diff --git a/docs/en_US/images/table_partition.png b/docs/en_US/images/table_partition.png
old mode 100755
new mode 100644
index 6d4cf7855..195ebf2b8
Binary files a/docs/en_US/images/table_partition.png and b/docs/en_US/images/table_partition.png differ
diff --git a/docs/en_US/release_notes_4_6.rst b/docs/en_US/release_notes_4_6.rst
index 64e90b9b2..e320adb63 100644
--- a/docs/en_US/release_notes_4_6.rst
+++ b/docs/en_US/release_notes_4_6.rst
@@ -14,5 +14,7 @@ Features
Bug fixes
*********
+| `Bug #3938 `_ - Added support for Default Partition.
+| `Bug #4104 `_ - Ensure that record should be add/edited for root partition table with primary keys.
| `Bug #4138 `_ - Fix an issue where the dropdown becomes misaligned/displaced.
| `Bug #4161 `_ - Ensure that parameters of procedures for EPAS server 10 and below should be set/reset properly.
\ No newline at end of file
diff --git a/docs/en_US/table_dialog.rst b/docs/en_US/table_dialog.rst
index 6752f8fde..3d0cc9772 100644
--- a/docs/en_US/table_dialog.rst
+++ b/docs/en_US/table_dialog.rst
@@ -442,6 +442,7 @@ icon (+) to add each partition:
* Move the *Operation* switch to *attach* to attach the partition, by default it
is *create*.
* Use the *Name* field to add the name of the partition.
+* If partition type is Range or List then *Default* field will be enabled.
* If partition type is Range then *From* and *To* fields will be enabled.
* If partition type is List then *In* field will be enabled.
* If partition type is Hash then *Modulus* and *Remainder* fields will be
@@ -523,4 +524,4 @@ three columns and a primary key constraint on the *category_id* column.
* Click the *Info* button (i) to access online help.
* Click the *Save* button to save work.
* Click the *Cancel* button to exit without saving work.
-* Click the *Reset* button to restore configuration parameters.
\ No newline at end of file
+* Click the *Reset* button to restore configuration parameters.
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/partition.utils.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/partition.utils.js
index 568556113..54c29619e 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/partition.utils.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/partition.utils.js
@@ -242,6 +242,7 @@ define('pgadmin.node.table_partition_utils', [
oid: undefined,
is_attach: false,
partition_name: undefined,
+ is_default: undefined,
values_from: undefined,
values_to: undefined,
values_in: undefined,
@@ -252,8 +253,8 @@ define('pgadmin.node.table_partition_utils', [
schema: [{
id: 'oid', label: gettext('OID'), type: 'text',
},{
- id: 'is_attach', label:gettext('Operation'), cell: 'switch',
- type: 'switch', options: { 'onText': gettext('Attach'), 'offText': gettext('Create')},
+ id: 'is_attach', label:gettext('Operation'), cell: 'switch', type: 'switch',
+ options: {'onText': gettext('Attach'), 'offText': gettext('Create'), 'width': 65},
cellHeaderClasses: 'width_percent_5',
editable: function(m) {
if (m instanceof Backbone.Model && m.isNew() && !m.top.isNew())
@@ -268,39 +269,53 @@ define('pgadmin.node.table_partition_utils', [
return true;
return false;
}, cellFunction: getPartitionCell,
+ },{
+ id: 'is_default', label: gettext('Default'), type: 'switch', cell:'switch',
+ cellHeaderClasses: 'width_percent_5', min_version: 110000,
+ options: {'onText': gettext('Yes'), 'offText': gettext('No')},
+ editable: function(m) {
+ if(m.handler && m.handler.top &&
+ m.handler.top.attributes &&
+ (m.handler.top.attributes.partition_type === 'range' ||
+ m.handler.top.attributes.partition_type === 'list') &&
+ m instanceof Backbone.Model && m.isNew() &&
+ m.handler.top.node_info.server.version >= 110000)
+ return true;
+ return false;
+ },
},{
id: 'values_from', label: gettext('From'), type:'text',
- cell:Backgrid.Extension.StringDepCell,
+ cell:Backgrid.Extension.StringDepCell, deps: ['is_default'],
cellHeaderClasses: 'width_percent_15',
editable: function(m) {
if(m.handler && m.handler.top &&
m.handler.top.attributes &&
m.handler.top.attributes.partition_type === 'range' &&
- m instanceof Backbone.Model && m.isNew())
+ m instanceof Backbone.Model && m.isNew() && m.get('is_default') !== true)
return true;
return false;
},
},{
id: 'values_to', label: gettext('To'), type:'text',
- cell:Backgrid.Extension.StringDepCell,
+ cell:Backgrid.Extension.StringDepCell, deps: ['is_default'],
cellHeaderClasses: 'width_percent_15',
editable: function(m) {
if(m.handler && m.handler.top &&
m.handler.top.attributes &&
m.handler.top.attributes.partition_type === 'range' &&
- m instanceof Backbone.Model && m.isNew())
+ m instanceof Backbone.Model && m.isNew() && m.get('is_default') !== true)
return true;
return false;
},
},{
id: 'values_in', label: gettext('In'), type:'text',
- cell:Backgrid.Extension.StringDepCell,
+ cell:Backgrid.Extension.StringDepCell, deps: ['is_default'],
cellHeaderClasses: 'width_percent_15',
editable: function(m) {
if(m.handler && m.handler.top &&
m.handler.top.attributes &&
m.handler.top.attributes.partition_type === 'list' &&
- m instanceof Backbone.Model && m.isNew())
+ m instanceof Backbone.Model && m.isNew() && m.get('is_default') !== true)
return true;
return false;
},
@@ -331,6 +346,7 @@ define('pgadmin.node.table_partition_utils', [
}],
validate: function() {
var partition_name = this.get('partition_name'),
+ is_default = this.get('is_default'),
values_from = this.get('values_from'),
values_to = this.get('values_to'),
values_in = this.get('values_in'),
@@ -350,20 +366,20 @@ define('pgadmin.node.table_partition_utils', [
}
if (this.top.get('partition_type') === 'range') {
- if (_.isUndefined(values_from) || _.isNull(values_from) ||
- String(values_from).replace(/^\s+|\s+$/g, '') === '') {
+ if (is_default !== true && (_.isUndefined(values_from) ||
+ _.isNull(values_from) || String(values_from).replace(/^\s+|\s+$/g, '') === '')) {
msg = gettext('For range partition From field cannot be empty.');
this.errorModel.set('values_from', msg);
return msg;
- } else if (_.isUndefined(values_to) || _.isNull(values_to) ||
- String(values_to).replace(/^\s+|\s+$/g, '') === '') {
+ } else if (is_default !== true && (_.isUndefined(values_to) || _.isNull(values_to) ||
+ String(values_to).replace(/^\s+|\s+$/g, '') === '')) {
msg = gettext('For range partition To field cannot be empty.');
this.errorModel.set('values_to', msg);
return msg;
}
} else if (this.top.get('partition_type') === 'list') {
- if (_.isUndefined(values_in) || _.isNull(values_in) ||
- String(values_in).replace(/^\s+|\s+$/g, '') === '') {
+ if (is_default !== true && (_.isUndefined(values_in) || _.isNull(values_in) ||
+ String(values_in).replace(/^\s+|\s+$/g, '') === '')) {
msg = gettext('For list partition In field cannot be empty.');
this.errorModel.set('values_in', msg);
return msg;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.js
index a76a4facb..10c0cfda7 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.js
@@ -960,17 +960,18 @@ define('pgadmin.node.table', [
id: 'partition_key_note', label: gettext('Partition Keys'),
type: 'note', group: 'partition', mode: ['create'],
text: [
- '
',
+ '- ',
gettext('Partition table supports two types of keys:'),
- '
- ',
- gettext('Column: User can select any column from the list of available columns.'),
'
- ',
- gettext('Expression: User can specify expression to create partition key.'),
- '
',
- gettext('Example'),
- ':',
+ '', gettext('Column: '), '',
+ gettext('User can select any column from the list of available columns.'),
+ '
- ',
+ '', gettext('Expression: '), '',
+ gettext('User can specify expression to create partition key.'),
+ '
- ',
+ '', gettext('Example: '), '',
gettext('Let\'s say, we want to create a partition table based per year for the column \'saledate\', having datatype \'date/timestamp\', then we need to specify the expression as \'extract(YEAR from saledate)\' as partition key.'),
- '
',
+ '',
].join(''),
visible: function(m) {
if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
@@ -990,7 +991,7 @@ define('pgadmin.node.table', [
canEdit: false, canDelete: true,
customDeleteTitle: gettext('Detach Partition'),
customDeleteMsg: gettext('Are you sure you wish to detach this partition?'),
- columns:['is_attach', 'partition_name', 'values_from', 'values_to', 'values_in', 'values_modulus', 'values_remainder'],
+ columns:['is_attach', 'partition_name', 'is_default', 'values_from', 'values_to', 'values_in', 'values_modulus', 'values_remainder'],
control: Backform.SubNodeCollectionControl.extend({
row: Backgrid.PartitionRow,
initialize: function() {
@@ -1053,22 +1054,28 @@ define('pgadmin.node.table', [
id: 'partition_note', label: gettext('Partitions'),
type: 'note', group: 'partition',
text: [
- '',
- ' - ',
- gettext('Create a table: User can create multiple partitions while creating new partitioned table. Operation switch is disabled in this scenario.'),
+ '
- ',
+ '', gettext('Create a table: '), '',
+ gettext('User can create multiple partitions while creating new partitioned table. Operation switch is disabled in this scenario.'),
'
- ',
- gettext('Edit existing table: User can create/attach/detach multiple partitions. In attach operation user can select table from the list of suitable tables to be attached.'),
+ '', gettext('Edit existing table: '), '',
+ gettext('User can create/attach/detach multiple partitions. In attach operation user can select table from the list of suitable tables to be attached.'),
'
- ',
+ '', gettext('Default: '), '',
+ gettext('The default partition can store rows that do not fall into any existing partition’s range or list.'),
+ '
- ',
+ '', gettext('From/To/In input: '), '',
gettext('From/To/In input: Values for these fields must be quoted with single quote. For more than one partition key values must be comma(,) separated.'),
- '
',
- gettext('Example'),
- ':- ',
- gettext('From/To: Enabled for range partition. Consider partitioned table with multiple keys of type Integer, then values should be specified like \'100\',\'200\'.'),
- '
- ',
- gettext('In: Enabled for list partition. Values must be comma(,) separated and quoted with single quote.'),
- '
',
- gettext('Modulus/Remainder: Enabled for hash partition.'),
- '
',
+ '',
+ '', gettext('Example: From/To: '), '',
+ gettext('Enabled for range partition. Consider partitioned table with multiple keys of type Integer, then values should be specified like \'100\',\'200\'.'),
+ '',
+ '', gettext('In: '), '',
+ gettext('Enabled for list partition. Values must be comma(,) separated and quoted with single quote.'),
+ '',
+ '', gettext('Modulus/Remainder: '), '',
+ gettext('Enabled for hash partition.'),
+ '',
].join(''),
visible: function(m) {
if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/tests/test_table_add.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/tests/test_table_add.py
index a0ee77a49..6b97b253f 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/tests/test_table_add.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/tests/test_table_add.py
@@ -31,6 +31,14 @@ class TableAddTestCase(BaseTestGenerator):
partition_type='range'
)
),
+ ('Create Range partitioned table with 1 default and 2'
+ ' value based partition',
+ dict(url='/browser/table/obj/',
+ server_min_version=110000,
+ partition_type='range',
+ is_default=True
+ )
+ ),
('Create List partitioned table with 2 partitions',
dict(url='/browser/table/obj/',
server_min_version=100000,
@@ -215,6 +223,24 @@ class TableAddTestCase(BaseTestGenerator):
'is_attach': False,
'partition_name': 'emp_2011'
}]
+ if hasattr(self, 'is_default'):
+ data['partitions'] = \
+ [{'values_from': "'2010-01-01'",
+ 'values_to': "'2010-12-31'",
+ 'is_attach': False,
+ 'partition_name': 'emp_2010_def'
+ },
+ {'values_from': "'2011-01-01'",
+ 'values_to': "'2011-12-31'",
+ 'is_attach': False,
+ 'partition_name': 'emp_2011_def'
+ },
+ {'values_from': "",
+ 'values_to': "",
+ 'is_attach': False,
+ 'is_default': True,
+ 'partition_name': 'emp_2012_def'
+ }]
data['partition_keys'] = \
[{'key_type': 'column', 'pt_column': 'DOJ'}]
elif self.partition_type == 'list':
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/utils.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/utils.py
index 78b250477..7815b3252 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/utils.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/utils.py
@@ -2184,16 +2184,23 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
partition_name = row['schema_name'] + '.' + row['name']
if data['partition_type'] == 'range':
- range_part = row['partition_value'].split(
- 'FOR VALUES FROM (')[1].split(') TO')
- range_from = range_part[0]
- range_to = range_part[1][2:-1]
+ if row['partition_value'] == 'DEFAULT':
+ is_default = True
+ range_from = None
+ range_to = None
+ else:
+ range_part = row['partition_value'].split(
+ 'FOR VALUES FROM (')[1].split(') TO')
+ range_from = range_part[0]
+ range_to = range_part[1][2:-1]
+ is_default = False
partitions.append({
'oid': row['oid'],
'partition_name': partition_name,
'values_from': range_from,
- 'values_to': range_to
+ 'values_to': range_to,
+ 'is_default': is_default
})
elif data['partition_type'] == 'list':
range_part = \
@@ -2251,15 +2258,22 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
part_data['relispartition'] = True
part_data['name'] = row['partition_name']
- if partitions['partition_type'] == 'range':
+ if 'is_default' in row and row['is_default'] and (
+ partitions['partition_type'] == 'range' or
+ partitions['partition_type'] == 'list'):
+ part_data['partition_value'] = 'DEFAULT'
+ elif partitions['partition_type'] == 'range':
range_from = row['values_from'].split(',')
range_to = row['values_to'].split(',')
- from_str = ', '.join("{0}".format(item) for item in range_from)
- to_str = ', '.join("{0}".format(item) for item in range_to)
+ from_str = ', '.join("{0}".format(item) for
+ item in range_from)
+ to_str = ', '.join("{0}".format(item) for
+ item in range_to)
- part_data['partition_value'] = 'FOR VALUES FROM (' + from_str \
- + ') TO (' + to_str + ')'
+ part_data['partition_value'] = 'FOR VALUES FROM (' +\
+ from_str + ') TO (' +\
+ to_str + ')'
elif partitions['partition_type'] == 'list':
range_in = row['values_in'].split(',')
diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/11_plus/primary_keys.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/11_plus/primary_keys.sql
new file mode 100644
index 000000000..1dfb094f7
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/11_plus/primary_keys.sql
@@ -0,0 +1,8 @@
+{# ============= Fetch the primary keys for given object id ============= #}
+{% if obj_id %}
+SELECT at.attname, ty.typname
+FROM pg_attribute at LEFT JOIN pg_type ty ON (ty.oid = at.atttypid)
+WHERE attrelid={{obj_id}}::oid AND attnum = ANY (
+ (SELECT con.conkey FROM pg_class rel LEFT OUTER JOIN pg_constraint con ON con.conrelid=rel.oid
+ AND con.contype='p' WHERE rel.relkind IN ('r','s','t', 'p') AND rel.oid = {{obj_id}}::oid)::oid[])
+{% endif %}