diff --git a/docs/en_US/release_notes_4_17.rst b/docs/en_US/release_notes_4_17.rst index 3027e8cf9..37e622bd1 100644 --- a/docs/en_US/release_notes_4_17.rst +++ b/docs/en_US/release_notes_4_17.rst @@ -27,6 +27,7 @@ Bug fixes | `Issue #4198 `_ - Fix syntax highlighting in code mirror for backslash and escape constant. | `Issue #4506 `_ - Fix an issue where clicking on an empty textbox like fill factor or comments, considers it as change and enabled the save button. +| `Issue #4633 `_ - Added support to view multilevel partitioned tables. | `Issue #4842 `_ - Ensure that constraints, indexes, rules, triggers, and compound triggers should be created on partitions. | `Issue #4943 `_ - Added more information to the 'Database connected/disconnected' message. | `Issue #4999 `_ - Rename some internal environment variables that could conflict with Kubernetes. diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/__init__.py index 11180ad85..f32b878cf 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/__init__.py @@ -200,52 +200,27 @@ class PartitionsView(BaseTableView, DataTypeReader, VacuumSettings): }) - def children(self, **kwargs): - """Build a list of treeview nodes from the child nodes.""" - - if 'sid' not in kwargs: - return precondition_required( - gettext('Required properties are missing.') - ) - - from pgadmin.utils.driver import get_driver - manager = get_driver(PG_DEFAULT_DRIVER).connection_manager( - sid=kwargs['sid'] - ) - - did = None - if 'did' in kwargs: - did = kwargs['did'] - - conn = manager.connection(did=did) - - if not conn.connected(): - return precondition_required( - gettext( - "Connection to the server has been lost." - ) - ) - + def get_children_nodes(self, manager, **kwargs): nodes = [] + # treat partition table as normal table. + # replace tid with ptid and pop ptid from kwargs + if 'ptid' in kwargs: + ptid = kwargs.pop('ptid') + kwargs['tid'] = ptid + for module in self.blueprint.submodules: if isinstance(module, PGChildModule): if manager is not None and \ module.BackendSupported(manager, **kwargs): - # treat partition table as normal table. - # replace tid with ptid and pop ptid from kwargs - if 'ptid' in kwargs: - ptid = kwargs.pop('ptid') - kwargs['tid'] = ptid nodes.extend(module.get_nodes(**kwargs)) else: nodes.extend(module.get_nodes(**kwargs)) - # Return sorted nodes based on label - return make_json_response( - data=sorted( - nodes, key=lambda c: c['label'] - ) - ) + if manager is not None and \ + self.blueprint.BackendSupported(manager, **kwargs): + nodes.extend(self.blueprint.get_nodes(**kwargs)) + + return nodes @BaseTableView.check_precondition def list(self, gid, sid, did, scid, tid): @@ -295,7 +270,7 @@ class PartitionsView(BaseTableView, DataTypeReader, VacuumSettings): """ SQL = render_template( "/".join([self.partition_template_path, 'nodes.sql']), - scid=scid, tid=tid + scid=scid, tid=tid, ptid=ptid ) status, rset = self.conn.execute_2darray(SQL) if not status: diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js index 14e015210..04a693ead 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition.js @@ -49,8 +49,7 @@ function( sqlCreateHelp: 'sql-createtable.html', dialogHelp: url_for('help.static', {'filename': 'table_dialog.html'}), hasScriptTypes: ['create'], - height: '95%', - width: '85%', + width: '650px', Init: function() { /* Avoid mulitple registration of menus */ if (this.initialized) @@ -865,7 +864,7 @@ function( 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', 'is_default', 'values_from', 'values_to', 'values_in', 'values_modulus', 'values_remainder'], control: Backform.SubNodeCollectionControl.extend({ row: Backgrid.PartitionRow, initialize: function() { @@ -930,10 +929,30 @@ function( },{ id: 'partition_note', label: gettext('Partition'), type: 'note', group: 'partition', - text: gettext('The control above is used to Create/Attach/Detach partitions.
' + - '
  • Create Mode: User will be able to create N number of partitions. Mode switch control is disabled in this scenario.
  • ' + - '
  • Edit Mode: User will be able to create/attach/detach N number of partitions. ' + - 'In attach mode there will be list of suitable tables to be attached.
'), + text: [ + '
  • ', + '', 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: '), '', + 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: 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) && !_.isUndefined(m.node_info.server.version) && diff --git a/web/pgadmin/browser/utils.py b/web/pgadmin/browser/utils.py index 0cc061db5..0fda71a0e 100644 --- a/web/pgadmin/browser/utils.py +++ b/web/pgadmin/browser/utils.py @@ -343,10 +343,8 @@ class NodeView(with_metaclass(MethodViewType, View)): def children(self, *args, **kwargs): """Build a list of treeview nodes from the child nodes.""" - children = [] + children = self.get_children_nodes(*args, **kwargs) - for module in self.blueprint.submodules: - children.extend(module.get_nodes(*args, **kwargs)) # Return sorted nodes based on label return make_json_response( data=sorted( @@ -354,8 +352,46 @@ class NodeView(with_metaclass(MethodViewType, View)): ) ) + def get_children_nodes(self, *args, **kwargs): + """ + Returns the list of children nodes for the current nodes. Override this + function for special cases only. + + :param args: + :param kwargs: Parameters to generate the correct set of tree node. + :return: List of the children nodes + """ + children = [] + + for module in self.blueprint.submodules: + children.extend(module.get_nodes(*args, **kwargs)) + + return children + class PGChildNodeView(NodeView): + + def get_children_nodes(self, manager, **kwargs): + """ + Returns the list of children nodes for the current nodes. + + :param manager: Server Manager object + :param kwargs: Parameters to generate the correct set of browser tree + node + :return: + """ + nodes = [] + for module in self.blueprint.submodules: + if isinstance(module, PGChildModule): + if ( + manager is not None and + module.BackendSupported(manager, **kwargs) + ): + nodes.extend(module.get_nodes(**kwargs)) + else: + nodes.extend(module.get_nodes(**kwargs)) + return nodes + def children(self, **kwargs): """Build a list of treeview nodes from the child nodes.""" @@ -388,21 +424,11 @@ class PGChildNodeView(NodeView): ) ) - nodes = [] - for module in self.blueprint.submodules: - if isinstance(module, PGChildModule): - if ( - manager is not None and - module.BackendSupported(manager, **kwargs) - ): - nodes.extend(module.get_nodes(**kwargs)) - else: - nodes.extend(module.get_nodes(**kwargs)) - # Return sorted nodes based on label return make_json_response( data=sorted( - nodes, key=lambda c: c['label'] + self.get_children_nodes(manager, **kwargs), + key=lambda c: c['label'] ) )