Hi,

Please find the attached updated patch with the fixes. I have also
considered the suggestions given by Aditya on different email thread.

Thanks,
Khushboo


On Wed, Jul 25, 2018 at 6:01 PM, Akshay Joshi <akshay.jo...@enterprisedb.com
> wrote:

> Hi Khushboo
>
> Following are my initial review comments:
>
>    - Fix PEP-8 warnings.
>    - We should hide "Modulus" and "Remainder" columns for PG/EPAS 10 as
>    Hash partition is not supported in that.
>    - Primary key switch control on "columns" tab should be enabled for
>    partitioned table created on PG/EPAS 11.
>    - Please verify that Unique constraint are supported for PG/EPAS 11 or
>    not.
>
> Will do code review tomorrow.
>
> On Wed, Jul 25, 2018 at 4:29 PM, Khushboo Vashi <
> khushboo.va...@enterprisedb.com> wrote:
>
>> Hi,
>>
>>
>> Please find the attached patch for RM #3412.
>>
>> - Add support for PRIMARY KEY, FOREIGN KEY, indexes, and triggers on
>> partitioned tables for PG 11
>>
>> - Add support for partitioning by  HASH key.
>>
>>
>> Thanks,
>> Khushboo
>>
>
>
>
> --
> *Akshay Joshi*
>
> *Sr. Software Architect *
>
>
>
> *Phone: +91 20-3058-9517Mobile: +91 976-788-8246*
>
diff --git a/docs/en_US/images/table_partition.png b/docs/en_US/images/table_partition.png
new file mode 100644
index 0000000..2d6e999
Binary files /dev/null and b/docs/en_US/images/table_partition.png differ
diff --git a/docs/en_US/table_dialog.rst b/docs/en_US/table_dialog.rst
index 016fbd0..ba6a9b3 100644
--- a/docs/en_US/table_dialog.rst
+++ b/docs/en_US/table_dialog.rst
@@ -256,6 +256,31 @@ Use the fields in the **Like** box to specify which attributes of an existing ta
 * Move the *With storage?* switch to the *Yes* position to copy storage settings.
 * Move the *With comments?* switch to the *Yes* position to copy comments.
 
+With PostgreSQL 10 forward, the *Partition* tab will be visible.
+
+Click the *Partition* tab to continue.
+
+.. image:: images/table_partition.png
+    :alt: Table dialog partition tab
+
+Use the fields in the *partition* tab to create the partitions for the table:
+
+* Select a partition type from the *Partition Type* selection box. There are 3 options available; Range, List and Hash. Hash option will only enable for PostgreSQL version >= 11.
+
+Use the *Partition Keys* panel to define the partition keys. Click the *Add* icon (+) to add each partition keys selection:
+
+* Select a partition key type in the *Keytype* field.
+* Select a partition column in the *Column* field if Column option selected for *Keytype* field .
+* Specify the expression in the *Expression* field if Expression option selected for the *Keytype* field.
+
+Use the *Partitions* panel to define the partitions of a table. Click the *Add* 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 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 enabled.
+
 Click the *Parameter* tab to continue.
 
 .. image:: images/table_parameter.png
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js
index 582167a..95506ba 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/column/static/js/column.js
@@ -205,7 +205,8 @@ define('pgadmin.node.column', [
                   m.top.get('primary_key').length > 0 &&
                   !_.isUndefined(m.top.get('primary_key').first().get('oid'))
               ) || (
-                m.top.has('is_partitioned') && m.top.get('is_partitioned')
+                m.top.has('is_partitioned') && m.top.get('is_partitioned') &&
+                m.top.node_info.server && m.top.node_info.server.version < 11000
               ))
             ) {
               return true;
@@ -235,7 +236,8 @@ define('pgadmin.node.column', [
 
             // If table is partitioned table then disable
             if (m.top && !_.isUndefined(m.top.get('is_partitioned')) &&
-              m.top.get('is_partitioned'))
+              m.top.get('is_partitioned') && m.top.node_info.server &&
+              m.top.node_info.server.version < 11000)
             {
               setTimeout(function () {
                 m.set('is_primary_key', false);
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js
index 9d11ec7..e3dd9dc 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.js
@@ -1061,13 +1061,15 @@ define('pgadmin.node.foreign_key', [
 
         var t = pgBrowser.tree, i = item, d = itemData, parents = [],
           immediate_parent_table_found = false,
-          is_immediate_parent_table_partitioned = false;
+          is_immediate_parent_table_partitioned = false,
+          s_version = this.getTreeNodeHierarchy(i).server.version;
+
           // To iterate over tree to check parent node
         while (i) {
             // If table is partitioned table then return false
           if (!immediate_parent_table_found && (d._type == 'table' || d._type == 'partition')) {
             immediate_parent_table_found = true;
-            if ('is_partitioned' in d && d.is_partitioned) {
+            if ('is_partitioned' in d && d.is_partitioned && s_version < 110000) {
               is_immediate_parent_table_partitioned = true;
             }
           }
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js
index e599b28..a9307d4 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.js
@@ -44,14 +44,15 @@ define('pgadmin.node.primary_key', [
 
         var t = pgBrowser.tree, i = item, d = itemData, parents = [],
           immediate_parent_table_found = false,
-          is_immediate_parent_table_partitioned = false;
+          is_immediate_parent_table_partitioned = false,
+          s_version = this.getTreeNodeHierarchy(i).server.version;
 
         // To iterate over tree to check parent node
         while (i) {
           // If table is partitioned table then return false
           if (!immediate_parent_table_found && (d._type == 'table' || d._type == 'partition')) {
             immediate_parent_table_found = true;
-            if ('is_partitioned' in d && d.is_partitioned) {
+            if ('is_partitioned' in d && d.is_partitioned && s_version < 110000) {
               is_immediate_parent_table_partitioned = true;
             }
           }
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js
index f8b8c2c..ae40561 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/unique_constraint.js
@@ -44,14 +44,15 @@ define('pgadmin.node.unique_constraint', [
 
         var t = pgBrowser.tree, i = item, d = itemData, parents = [],
           immediate_parent_table_found = false,
-          is_immediate_parent_table_partitioned = false;
+          is_immediate_parent_table_partitioned = false,
+          s_version = this.getTreeNodeHierarchy(i).server.version;
 
         // To iterate over tree to check parent node
         while (i) {
           // If table is partitioned table then return false
           if (!immediate_parent_table_found && (d._type == 'table' || d._type == 'partition')) {
             immediate_parent_table_found = true;
-            if ('is_partitioned' in d && d.is_partitioned) {
+            if ('is_partitioned' in d && d.is_partitioned && s_version < 110000) {
               is_immediate_parent_table_partitioned = true;
             }
           }
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/__init__.py
index 1acdb8e..7279b15 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/__init__.py
@@ -75,8 +75,9 @@ class IndexesModule(CollectionNodeModule):
         if super(IndexesModule, self).BackendSupported(manager, **kwargs):
             conn = manager.connection(did=kwargs['did'])
 
-            # In case of partitioned table return false.
-            if 'tid' in kwargs and manager.version >= 100000:
+            # If PG version > 100000 and < 110000 then index is
+            # not supported for partitioned table.
+            if 'tid' in kwargs and 100000 <= manager.version < 110000:
                 return not backend_supported(self, manager, **kwargs)
 
             if 'vid' not in kwargs:
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js
index 1da58ae..5732013 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.js
@@ -560,14 +560,15 @@ define('pgadmin.node.index', [
 
         var t = pgBrowser.tree, i = item, d = itemData, parents = [],
           immediate_parent_table_found = false,
-          is_immediate_parent_table_partitioned = false;
+          is_immediate_parent_table_partitioned = false,
+          s_version = this.getTreeNodeHierarchy(i).server.version;
         // To iterate over tree to check parent node
         while (i) {
           // Do not allow creating index on partitioned tables.
           if (!immediate_parent_table_found &&
             _.indexOf(['table', 'partition'], d._type) > -1) {
             immediate_parent_table_found = true;
-            if ('is_partitioned' in d && d.is_partitioned) {
+            if ('is_partitioned' in d && d.is_partitioned && s_version < 110000) {
               is_immediate_parent_table_partitioned = true;
             }
           }
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 80610cf..bd0cbf4 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
@@ -237,6 +237,8 @@ define('pgadmin.node.table_partition_utils', [
       values_from: undefined,
       values_to: undefined,
       values_in: undefined,
+      values_modulus: undefined,
+      values_remainder: undefined,
     },
     keys:['partition_name'],
     schema: [{
@@ -252,7 +254,7 @@ define('pgadmin.node.table_partition_utils', [
       },
     },{
       id: 'partition_name', label: gettext('Name'), type: 'text', cell:'string',
-      cellHeaderClasses: 'width_percent_25',
+      cellHeaderClasses: 'width_percent_15',
       editable: function(m) {
         if (m instanceof Backbone.Model && m.isNew())
           return true;
@@ -261,11 +263,11 @@ define('pgadmin.node.table_partition_utils', [
     },{
       id: 'values_from', label: gettext('From'), type:'text',
       cell:Backgrid.Extension.StringDepCell,
-      cellHeaderClasses: 'width_percent_20',
+      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.handler.top.attributes.partition_type === 'range' &&
           m instanceof Backbone.Model && m.isNew())
           return true;
         return false;
@@ -273,11 +275,11 @@ define('pgadmin.node.table_partition_utils', [
     },{
       id: 'values_to', label: gettext('To'), type:'text',
       cell:Backgrid.Extension.StringDepCell,
-      cellHeaderClasses: 'width_percent_20',
+      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.handler.top.attributes.partition_type === 'range' &&
           m instanceof Backbone.Model && m.isNew())
           return true;
         return false;
@@ -285,11 +287,35 @@ define('pgadmin.node.table_partition_utils', [
     },{
       id: 'values_in', label: gettext('In'), type:'text',
       cell:Backgrid.Extension.StringDepCell,
-      cellHeaderClasses: 'width_percent_25',
+      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.handler.top.attributes.partition_type === 'list' &&
+          m instanceof Backbone.Model && m.isNew())
+          return true;
+        return false;
+      },
+    },{
+      id: 'values_modulus', label: gettext('Modulus'), type:'int',
+      cell:Backgrid.Extension.StringDepCell,
+      cellHeaderClasses: 'width_percent_15',
+      editable: function(m) {
+        if(m.handler && m.handler.top &&
+          m.handler.top.attributes &&
+          m.handler.top.attributes.partition_type === 'hash' &&
+          m instanceof Backbone.Model && m.isNew())
+          return true;
+        return false;
+      },
+    },{
+      id: 'values_remainder', label: gettext('Remainder'), type:'int',
+      cell:Backgrid.Extension.StringDepCell,
+      cellHeaderClasses: 'width_percent_15 width_percent_20',
+      editable: function(m) {
+        if(m.handler && m.handler.top &&
+          m.handler.top.attributes &&
+          m.handler.top.attributes.partition_type === 'hash' &&
           m instanceof Backbone.Model && m.isNew())
           return true;
         return false;
@@ -300,6 +326,8 @@ define('pgadmin.node.table_partition_utils', [
         values_from = this.get('values_from'),
         values_to = this.get('values_to'),
         values_in = this.get('values_in'),
+        values_modulus = this.get('values_modulus'),
+        values_remainder = this.get('values_remainder'),
         msg;
 
       // Have to clear existing validation before initiating current state
@@ -307,31 +335,43 @@ define('pgadmin.node.table_partition_utils', [
       this.errorModel.clear();
 
       if (_.isUndefined(partition_name) || _.isNull(partition_name) ||
-       String(partition_name).replace(/^\s+|\s+$/g, '') == '') {
+       String(partition_name).replace(/^\s+|\s+$/g, '') === '') {
         msg = gettext('Partition name cannot be empty.');
         this.errorModel.set('partition_name', msg);
         return msg;
       }
 
-      if (this.top.get('partition_type') == 'range') {
+      if (this.top.get('partition_type') === 'range') {
         if (_.isUndefined(values_from) || _.isNull(values_from) ||
-          String(values_from).replace(/^\s+|\s+$/g, '') == '') {
+          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, '') == '') {
+          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') {
+      } else if (this.top.get('partition_type') === 'list') {
         if (_.isUndefined(values_in) || _.isNull(values_in) ||
-          String(values_in).replace(/^\s+|\s+$/g, '') == '') {
+          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;
         }
+      } else if (this.top.get('partition_type') === 'hash') {
+        if (_.isUndefined(values_modulus) || _.isNull(values_modulus) ||
+          String(values_modulus).replace(/^\s+|\s+$/g, '') === '') {
+          msg = gettext('For hash partition Modulus field cannot be empty.');
+          this.errorModel.set('values_modulus', msg);
+          return msg;
+        } else if (_.isUndefined(values_remainder) || _.isNull(values_remainder) ||
+          String(values_remainder).replace(/^\s+|\s+$/g, '') === '') {
+          msg = gettext('For hash partition Remainder field cannot be empty.');
+          this.errorModel.set('values_remainder', msg);
+          return msg;
+        }
       }
 
       return null;
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 530cd11..94b9ae1 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
@@ -593,7 +593,9 @@ define('pgadmin.node.table', [
             control: 'unique-col-collection',
             columns : ['name', 'columns'],
             canAdd: function(m) {
-              if (m.get('is_partitioned')) {
+              if (m.get('is_partitioned') && !_.isUndefined(m.top.node_info) && !_.isUndefined(m.top.node_info.server)
+              && !_.isUndefined(m.top.node_info.server.version) &&
+                m.top.node_info.server.version < 110000) {
                 setTimeout(function() {
                   var coll = m.get('primary_key');
                   coll.remove(coll.filter(function() { return true; }));
@@ -620,7 +622,9 @@ define('pgadmin.node.table', [
             canEdit: true, canDelete: true, deps:['is_partitioned'],
             control: 'unique-col-collection',
             canAdd: function(m) {
-              if (m.get('is_partitioned')) {
+              if (m.get('is_partitioned') && !_.isUndefined(m.top.node_info) && !_.isUndefined(m.top.node_info.server)
+              && !_.isUndefined(m.top.node_info.server.version) &&
+                m.top.node_info.server.version < 110000) {
                 setTimeout(function() {
                   var coll = m.get('foreign_key');
                   coll.remove(coll.filter(function() { return true; }));
@@ -656,7 +660,8 @@ define('pgadmin.node.table', [
             control: 'unique-col-collection',
             columns : ['name', 'columns'],
             canAdd: function(m) {
-              if (m.get('is_partitioned')) {
+              if (m.get('is_partitioned') && !_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
+              && !_.isUndefined(m.node_info.server.version) && m.node_info.server.version < 110000) {
                 setTimeout(function() {
                   var coll = m.get('unique_constraint');
                   coll.remove(coll.filter(function() { return true; }));
@@ -834,11 +839,22 @@ define('pgadmin.node.table', [
           id: 'partition_type', label:gettext('Partition Type'),
           editable: false, type: 'select2', select2: {allowClear: false},
           group: 'partition', deps: ['is_partitioned'],
-          options:[{
-            label: gettext('Range'), value: 'range',
-          },{
-            label: gettext('List'), value: 'list',
-          }],
+          options: function() {
+            var options = [{
+              label: gettext('Range'), value: 'range',
+            },{
+              label: gettext('List'), value: 'list',
+            }];
+
+            if(!_.isUndefined(this.node_info) && !_.isUndefined(this.node_info.server)
+              && !_.isUndefined(this.node_info.server.version) &&
+                this.node_info.server.version >= 110000) {
+              options.push({
+                label: gettext('Hash'), value: 'hash',
+              });
+            }
+            return options;
+          },
           mode:['create'],
           visible: function(m) {
             if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server)
@@ -963,7 +979,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'],
+          columns:['is_attach', 'partition_name', 'values_from', 'values_to', 'values_in', 'values_modulus', 'values_remainder'],
           control: Backform.SubNodeCollectionControl.extend({
             row: Backgrid.PartitionRow,
             initialize: function() {
@@ -1040,6 +1056,8 @@ define('pgadmin.node.table', [
             '</li><li> ',
             gettext('In: Enabled for list partition. Values must be comma(,) separated and quoted with single quote.'),
             '</li></ul></li></ul>',
+            gettext('Modulus/Remainder: Enabled for hash partition.'),
+            '</li></ul></li></ul>',
           ].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 4c3cde3..da30142 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
@@ -36,6 +36,12 @@ class TableAddTestCase(BaseTestGenerator):
               server_min_version=100000,
               partition_type='list'
               )
+         ),
+        ('Create Hash partitioned table with 2 partitions',
+         dict(url='/browser/table/obj/',
+              server_min_version=110000,
+              partition_type='hash'
+              )
          )
     ]
 
@@ -93,13 +99,14 @@ class TableAddTestCase(BaseTestGenerator):
                     "attoptions": [],
                     "seclabels": []
                 },
-                {"name": "DOJ",
-                 "cltype": "date",
-                 "attacl": [],
-                 "is_primary_key": False,
-                 "attoptions": [],
-                 "seclabels": []
-                 }
+                {
+                    "name": "DOJ",
+                    "cltype": "date",
+                    "attacl": [],
+                    "is_primary_key": False,
+                    "attoptions": [],
+                    "seclabels": []
+                }
             ],
             "exclude_constraint": [],
             "fillfactor": "",
@@ -208,7 +215,9 @@ class TableAddTestCase(BaseTestGenerator):
                       'is_attach': False,
                       'partition_name': 'emp_2011'
                       }]
-            else:
+                data['partition_keys'] = \
+                    [{'key_type': 'column', 'pt_column': 'DOJ'}]
+            elif self.partition_type == 'list':
                 data['partitions'] = \
                     [{'values_in': "'2012-01-01', '2012-12-31'",
                       'is_attach': False,
@@ -218,8 +227,22 @@ class TableAddTestCase(BaseTestGenerator):
                       'is_attach': False,
                       'partition_name': 'emp_2013'
                       }]
-            data['partition_keys'] = \
-                [{'key_type': 'column', 'pt_column': 'DOJ'}]
+                data['partition_keys'] = \
+                    [{'key_type': 'column', 'pt_column': 'DOJ'}]
+            else:
+                data['partitions'] = \
+                    [{'values_modulus': "24",
+                      'values_remainder': "3",
+                      'is_attach': False,
+                      'partition_name': 'emp_2016'
+                      },
+                     {'values_modulus': "8",
+                      'values_remainder': "2",
+                      'is_attach': False,
+                      'partition_name': 'emp_2017'
+                      }]
+                data['partition_keys'] = \
+                    [{'key_type': 'column', 'pt_column': 'empno'}]
 
         # Add table
         response = self.tester.post(
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js
index 85aab40..95dcc96 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/triggers/static/js/trigger.js
@@ -204,7 +204,9 @@ define('pgadmin.node.trigger', [
           disabled: function(m) {
             // Disabled if table is a partitioned table.
             if (_.has(m, 'node_info') && _.has(m.node_info, 'table') &&
-              _.has(m.node_info.table, 'is_partitioned') && m.node_info.table.is_partitioned)
+              _.has(m.node_info.table, 'is_partitioned') &&
+               m.node_info.table.is_partitioned && m.node_info.server.version < 110000
+               )
             {
               setTimeout(function(){
                 m.set('is_row_trigger', false);
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 7264dd7..f210456 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
@@ -874,6 +874,7 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
         # Table & Schema declaration so that we can use them in child nodes
         schema = data['schema']
         table = data['name']
+        is_partitioned = 'is_partitioned' in data and data['is_partitioned']
 
         data = self._formatter(did, scid, tid, data)
 
@@ -1139,7 +1140,7 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
         # 5) Reverse engineered sql for PARTITIONS
         ##########################################
         """
-        if 'is_partitioned' in data and data['is_partitioned']:
+        if is_partitioned:
             SQL = render_template("/".join([self.partition_template_path,
                                             'nodes.sql']),
                                   scid=scid, tid=tid)
@@ -1211,6 +1212,9 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
         elif 'partition_type' in data \
                 and data['partition_type'] == 'list':
             partition_scheme = 'LIST ('
+        elif 'partition_type' in data \
+                and data['partition_type'] == 'hash':
+            partition_scheme = 'HASH ('
 
         for row in data['partition_keys']:
             if row['key_type'] == 'column':
@@ -2205,7 +2209,7 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
                         'values_from': range_from,
                         'values_to': range_to
                     })
-                else:
+                elif data['partition_type'] == 'list':
                     range_part = \
                         row['partition_value'].split('FOR VALUES IN (')[1]
 
@@ -2215,6 +2219,20 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
                         'partition_name': partition_name,
                         'values_in': range_in
                     })
+                else:
+                    range_part = row['partition_value'].split(
+                        'FOR VALUES WITH (')[1].split(",")
+                    range_modulus = range_part[0].strip().strip(
+                        "modulus").strip()
+                    range_remainder = range_part[1].strip().\
+                        strip(" remainder").strip(")").strip()
+
+                    partitions.append({
+                        'oid': row['oid'],
+                        'partition_name': partition_name,
+                        'values_modulus': range_modulus,
+                        'values_remainder': range_remainder
+                    })
 
             data['partitions'] = partitions
 
@@ -2256,10 +2274,26 @@ class BaseTableView(PGChildNodeView, BasePartitionTable):
 
                 part_data['partition_value'] = 'FOR VALUES FROM (' + from_str \
                                                + ') TO (' + to_str + ')'
-            else:
+
+            elif partitions['partition_type'] == 'list':
                 range_in = row['values_in'].split(',')
                 in_str = ', '.join("{0}".format(item) for item in range_in)
-                part_data['partition_value'] = 'FOR VALUES IN (' + in_str + ')'
+                part_data['partition_value'] = 'FOR VALUES IN (' + in_str\
+                                               + ')'
+
+            else:
+                range_modulus = row['values_modulus'].split(',')
+                range_remainder = row['values_remainder'].split(',')
+
+                modulus_str = ', '.join("{0}".format(item) for item in
+                                        range_modulus)
+                remainder_str = ', '.join("{0}".format(item) for item in
+                                          range_remainder)
+
+                part_data['partition_value'] = 'FOR VALUES WITH (MODULUS '\
+                                               + modulus_str \
+                                               + ', REMAINDER ' +\
+                                               remainder_str + ')'
 
             if 'is_attach' in row and row['is_attach']:
                 partition_sql = render_template(

Reply via email to