From 3e66b6f7beb073a88980ddd05cf4700c4e5afd5e Mon Sep 17 00:00:00 2001 From: Akshay Joshi Date: Thu, 11 Sep 2025 14:27:27 +0530 Subject: [PATCH] 1) Added "MULTIRANGE_TYPE_NAME" option while creating a Range Type. #6394 2) Added "SUBSCRIPT" option while creating an External Type. #6395 --- docs/en_US/images/type_range.png | Bin 39891 -> 73950 bytes docs/en_US/release_notes.rst | 1 + docs/en_US/release_notes_9_9.rst | 33 ++ docs/en_US/type_dialog.rst | 13 +- .../databases/schemas/types/__init__.py | 43 ++- .../databases/schemas/types/static/js/type.js | 3 + .../schemas/types/static/js/type.ui.js | 93 ++--- .../pg/sql/14_plus/additional_properties.sql | 45 +++ .../templates/types/pg/sql/14_plus/create.sql | 86 +++++ .../templates/types/pg/sql/14_plus/nodes.sql | 19 + .../types/pg/sql/14_plus/properties.sql | 45 +++ .../templates/types/pg/sql/14_plus/update.sql | 183 ++++++++++ .../templates/types/pg/sql/default/count.sql | 2 +- .../pg/sql/default/get_external_functions.sql | 2 +- .../types/pg/sql/default/properties.sql | 21 +- .../templates/types/pg/sql/default/update.sql | 21 ++ .../sql/14_plus/additional_properties.sql | 64 ++++ .../types/ppas/sql/14_plus/create.sql | 96 ++++++ .../types/ppas/sql/14_plus/nodes.sql | 19 + .../types/ppas/sql/14_plus/properties.sql | 45 +++ .../types/ppas/sql/14_plus/update.sql | 183 ++++++++++ .../sql/default/additional_properties.sql | 7 +- .../sql/default/get_external_functions.sql | 2 +- .../types/ppas/sql/default/properties.sql | 21 +- .../types/ppas/sql/default/update.sql | 21 ++ .../pg/14_plus/alter_acl_grantee_change.sql | 18 + .../alter_range_type_acl_description.sql | 20 ++ .../pg/14_plus/create_multi_range_type.msql | 5 + .../pg/14_plus/create_multi_range_type.sql | 13 + .../tests/pg/14_plus/create_range_type.sql | 13 + .../schemas/types/tests/pg/14_plus/tests.json | 324 ++++++++++++++++++ .../default/alter_enum_type_rename_enum.msql | 4 + .../default/alter_enum_type_rename_enum.sql | 16 + .../schemas/types/tests/pg/default/tests.json | 12 + .../ppas/14_plus/alter_acl_grantee_change.sql | 18 + .../alter_range_type_acl_description.sql | 20 ++ .../ppas/14_plus/create_multi_range_type.msql | 5 + .../ppas/14_plus/create_multi_range_type.sql | 13 + .../tests/ppas/14_plus/create_range_type.sql | 13 + .../types/tests/ppas/14_plus/tests.json | 324 ++++++++++++++++++ .../default/alter_enum_type_rename_enum.msql | 4 + .../default/alter_enum_type_rename_enum.sql | 16 + .../types/tests/ppas/default/tests.json | 12 + web/pgadmin/browser/static/js/node.js | 11 +- web/pgadmin/static/js/tree/tree.js | 10 +- 45 files changed, 1868 insertions(+), 71 deletions(-) create mode 100644 docs/en_US/release_notes_9_9.rst create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/additional_properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/nodes.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/update.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/additional_properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/nodes.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/update.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/alter_acl_grantee_change.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/alter_range_type_acl_description.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/create_multi_range_type.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/create_multi_range_type.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/create_range_type.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/tests.json create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/default/alter_enum_type_rename_enum.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/default/alter_enum_type_rename_enum.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/alter_acl_grantee_change.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/alter_range_type_acl_description.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/create_multi_range_type.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/create_multi_range_type.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/create_range_type.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/tests.json create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/default/alter_enum_type_rename_enum.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/default/alter_enum_type_rename_enum.sql diff --git a/docs/en_US/images/type_range.png b/docs/en_US/images/type_range.png index 43eeac5623fcf3e2d4598bfb7693c8c5bc88db07..f063cbff87ac190e9b32c93f5981a979f51ccb13 100644 GIT binary patch literal 73950 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 B-i-hN literal 39891 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 diff --git a/docs/en_US/release_notes.rst b/docs/en_US/release_notes.rst index 79e899148..7db716697 100644 --- a/docs/en_US/release_notes.rst +++ b/docs/en_US/release_notes.rst @@ -12,6 +12,7 @@ notes for it. :maxdepth: 1 + release_notes_9_9 release_notes_9_8 release_notes_9_7 release_notes_9_6 diff --git a/docs/en_US/release_notes_9_9.rst b/docs/en_US/release_notes_9_9.rst new file mode 100644 index 000000000..1236a37a3 --- /dev/null +++ b/docs/en_US/release_notes_9_9.rst @@ -0,0 +1,33 @@ +*********** +Version 9.9 +*********** + +Release date: 2025-10-16 + +This release contains a number of bug fixes and new features since the release of pgAdmin 4 v9.8. + +Supported Database Servers +************************** +**PostgreSQL**: 13, 14, 15, 16 and 17 + +**EDB Advanced Server**: 13, 14, 15, 16 and 17 + +Bundled PostgreSQL Utilities +**************************** +**psql**, **pg_dump**, **pg_dumpall**, **pg_restore**: 17.5 + + +New features +************ + + | `Issue #6394 `_ - Added "MULTIRANGE_TYPE_NAME" option while creating a Range Type. + | `Issue #6395 `_ - Added "SUBSCRIPT" option while creating a External Type. + + +Housekeeping +************ + + +Bug fixes +********* + diff --git a/docs/en_US/type_dialog.rst b/docs/en_US/type_dialog.rst index 9cc9567ba..db3e29340 100644 --- a/docs/en_US/type_dialog.rst +++ b/docs/en_US/type_dialog.rst @@ -109,17 +109,17 @@ On the *Optional-1* tab: to select a type_modifier_output_function. It is allowed to omit the type_modifier_output_function, in which case the default display format is the stored typmod integer value enclosed in parentheses. +* Use the drop-down listbox next to the optional *Analyze function* field to + select a function for performing type-specific statistics collection for + columns of the data type. +* Use the drop-down listbox next to the optional *Subscript function* field to + select a function for allows the data type to be subscripted in SQL commands. * Use the optional *Internal length* to specify a value for internal representation. -* Move the *Variable?* switch to specify the internal representation is of - variable length (VARIABLE). The default is a fixed length positive integer. * Specify a default value in the optional *Default* field in cases where a column of the data type defaults to something other than the null value. Specify the default with the DEFAULT key word. (A default can be overridden by an explicit DEFAULT clause attached to a particular column.) -* Use the drop-down listbox next to the optional *Analyze function* field to - select a function for performing type-specific statistics collection for - columns of the data type. * Use the drop-down listbox next to the optional *Category type* field to help control which implicit cast will be applied in ambiguous situations. * Move the *Preferred?* switch to *Yes* to specify the selected category type is @@ -162,6 +162,9 @@ disabled. values to a canonical form. * Use the drop-down listbox next to *Sub-type diff function* to select a user-defined subtype_diff function. +* Specify the optional *Multirange type name* parameter to specifies the + name of the corresponding multirange type. If not specified, this name is + chosen automatically. If you select *Shell* in the *Type* field, the *Definition* tab displays the *Shell* panel: diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/__init__.py index a757b825e..758a150d8 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/__init__.py @@ -249,6 +249,15 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): return wrap + def has_dependent_type(self, data): + """ + This function is used to check the type has dependent + on another type. + """ + return ('rngmultirangetype' in data and + data['rngmultirangetype'] is not None and + data['rngmultirangetype'] != '') + @check_precondition def list(self, gid, sid, did, scid): """ @@ -317,7 +326,8 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): rset['rows'][0]['oid'], scid, rset['rows'][0]['name'], - icon=self.icon_str % self.node_type + icon=self.icon_str % self.node_type, + has_dependent=self.has_dependent_type(rset['rows'][0]) ) return make_json_response( @@ -359,7 +369,8 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): scid, row['name'], icon=self.icon_str % self.node_type, - description=row['description'] + description=row['description'], + has_dependent=self.has_dependent_type(row) )) return make_json_response( @@ -536,7 +547,8 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): enum_list = [] for row in rset['rows']: properties_list.append(row['enumlabel']) - enum_list.append({'label': row['enumlabel']}) + enum_list.append({'label': row['enumlabel'], + 'old_label': row['enumlabel']}) # Adding both results in ouput res['enum_list'] = ', '.join(properties_list) @@ -1063,7 +1075,8 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): tid, scid, data['name'], - icon="icon-type" + icon="icon-type", + has_dependent=self.has_dependent_type(data) ) ) except Exception as e: @@ -1086,7 +1099,7 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): request.data ) try: - SQL, name = self.get_sql(gid, sid, data, scid, tid) + SQL, name, has_dependent = self.get_sql(gid, sid, data, scid, tid) # Most probably this is due to error if not isinstance(SQL, str): return SQL @@ -1114,6 +1127,7 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): scid, name, icon=self.icon_str % self.node_type, + has_dependent=has_dependent, **other_node_info ) ) @@ -1226,11 +1240,15 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): for key, val in req.items(): if key in ['composite', 'enum', 'seclabels', 'typacl']: data[key] = json.loads(val) + elif key in ['typreceive', 'typsend', 'typmodin', 'typmodout', + 'typanalyze', 'typsubscript','typstorage'] and \ + val == 'null': + data[key] = json.loads(val) else: data[key] = val try: - sql, _ = self.get_sql(gid, sid, data, scid, tid) + sql, _, _ = self.get_sql(gid, sid, data, scid, tid) # Most probably this is due to error if not isinstance(sql, str): return sql @@ -1330,7 +1348,7 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): self._CREATE_SQL]), data=data, conn=self.conn, is_sql=is_sql) - return SQL, data['name'] + return SQL, data['name'], False def get_sql(self, gid, sid, data, scid, tid=None, is_sql=False): """ @@ -1339,6 +1357,8 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): if tid is None: return self._get_new_sql(data, is_sql) + data = self._convert_for_sql(data) + for key in ['added', 'changed', 'deleted']: if key in data.get('typacl', []): data['typacl'][key] = parse_priv_to_db( @@ -1411,7 +1431,8 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): data=data, o_data=old_data, conn=self.conn ) - return SQL, old_data['name'] + return (SQL, data['name'] if 'name' in data else old_data['name'], + self.has_dependent_type(old_data)) @check_precondition def sql(self, gid, sid, did, scid, tid, **kwargs): @@ -1478,7 +1499,7 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): if data[k] == '-': data[k] = None - SQL, _ = self.get_sql(gid, sid, data, scid, tid=None, is_sql=True) + SQL, _, _ = self.get_sql(gid, sid, data, scid, tid=None, is_sql=True) # Most probably this is due to error if not isinstance(SQL, str): return SQL @@ -1588,8 +1609,8 @@ class TypeView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): if data: if target_schema: data['schema'] = target_schema - sql, _ = self.get_sql(gid=gid, sid=sid, scid=scid, data=data, - tid=oid) + sql, _, _ = self.get_sql(gid=gid, sid=sid, scid=scid, data=data, + tid=oid) else: if drop_sql: sql = self.delete(gid=gid, sid=sid, did=did, diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js index d1027da26..eedebd680 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.js @@ -42,6 +42,9 @@ define('pgadmin.node.type', [ hasSQL: true, hasDepends: true, width: pgBrowser.stdW.md + 'px', + refreshParent: function(node) { + return node?._metadata?.data?.has_dependent; + }, Init: function() { /* Avoid multiple registration of menus */ if (this.initialized) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.ui.js index 57cfa78f3..94611ab04 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.ui.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/js/type.ui.js @@ -192,7 +192,6 @@ function isVisible(state, type) { } class EnumerationSchema extends BaseUISchema { - constructor() { super({ oid: undefined, @@ -201,18 +200,14 @@ class EnumerationSchema extends BaseUISchema { } get idAttribute() { - return 'oid'; + return 'old_label'; } get baseFields() { - let obj = this; return [ { id: 'label', label: gettext('Label'), type: 'text', cell: 'text', minWidth: 620, - editable: (state) => { - return _.isUndefined(obj.isNew) ? true : obj.isNew(state); - } } ]; } @@ -395,6 +390,15 @@ class RangeSchema extends BaseUISchema { } }; }, + }, { + id: 'rngmultirangetype', label: gettext('Multirange type name'), + cell: 'string', group: gettext('Range Type'), + type: 'text', mode: ['properties', 'create', 'edit'], + disabled: () => obj.inCatalog(), + readonly: function (state) { + return !obj.isNew(state); + }, + min_version: 140000, } ]; } @@ -562,9 +566,6 @@ class ExternalSchema extends BaseUISchema { group: gettext('Optional-1'), mode: ['properties', 'create', 'edit'], disabled: () => obj.inCatalog(), - readonly: function (state) { - return !obj.isNew(state); - }, controlProps: { allowClear: true, placeholder: '', width: '100%' }, },{ id: 'typsend', label: gettext('Send function'), @@ -586,9 +587,6 @@ class ExternalSchema extends BaseUISchema { }, mode: ['properties', 'create', 'edit'], disabled: () => obj.inCatalog(), - readonly: function (state) { - return !obj.isNew(state); - }, controlProps: { allowClear: true, placeholder: '', width: '100%' }, },{ id: 'typmodin', label: gettext('Typmod in function'), @@ -623,9 +621,6 @@ class ExternalSchema extends BaseUISchema { }, mode: ['properties', 'create', 'edit'], group: gettext('Optional-1'), disabled: () => obj.inCatalog(), - readonly: function (state) { - return !obj.isNew(state); - }, controlProps: { allowClear: true, placeholder: '', width: '100%' }, },{ id: 'typmodout', label: gettext('Typmod out function'), @@ -661,9 +656,24 @@ class ExternalSchema extends BaseUISchema { }, mode: ['properties', 'create', 'edit'], disabled: () => obj.inCatalog(), - readonly: function (state) { - return !obj.isNew(state); + },{ + id: 'typanalyze', label: gettext('Analyze function'), + group: gettext('Optional-1'), + type: (state) => { + return obj.getFunctionType(state); }, + mode: ['properties', 'create','edit'], + disabled: () => obj.inCatalog(), + controlProps: { allowClear: true, placeholder: '', width: '100%' }, + },{ + id: 'typsubscript', label: gettext('Subscript function'), + group: gettext('Optional-1'), min_version: 140000, + type: (state) => { + return obj.getFunctionType(state); + }, + mode: ['properties', 'create','edit'], + disabled: () => obj.inCatalog(), + controlProps: { allowClear: true, placeholder: '', width: '100%' }, },{ id: 'typlen', label: gettext('Internal length'), cell: 'integer', group: gettext('Optional-1'), @@ -672,14 +682,6 @@ class ExternalSchema extends BaseUISchema { readonly: function (state) { return !obj.isNew(state); }, - },{ - id: 'variable', label: gettext('Variable?'), cell: 'switch', - group: gettext('Optional-1'), type: 'switch', - mode: ['create','edit'], - disabled: () => obj.inCatalog(), - readonly: function (state) { - return !obj.isNew(state); - }, },{ id: 'typdefault', label: gettext('Default?'), cell: 'string', group: gettext('Optional-1'), @@ -688,18 +690,6 @@ class ExternalSchema extends BaseUISchema { readonly: function (state) { return !obj.isNew(state); }, - },{ - id: 'typanalyze', label: gettext('Analyze function'), - group: gettext('Optional-1'), - type: (state) => { - return obj.getFunctionType(state); - }, - mode: ['properties', 'create','edit'], - disabled: () => obj.inCatalog(), - readonly: function (state) { - return !obj.isNew(state); - }, - controlProps: { allowClear: true, placeholder: '', width: '100%' }, },{ id: 'typcategory', label: gettext('Category type'), cell: 'string', @@ -792,9 +782,6 @@ class ExternalSchema extends BaseUISchema { type: 'select', mode: ['properties', 'create', 'edit'], group: gettext('Optional-2'), cell: 'string', disabled: () => obj.inCatalog(), - readonly: function (state) { - return !obj.isNew(state); - }, controlProps: { allowClear: true, placeholder: '', width: '100%' }, options: obj.fieldOptions.typStorageOptions, },{ @@ -1258,14 +1245,13 @@ export default class TypeSchema extends BaseUISchema { }, { id: 'typtype', label: gettext('Type'), - mode: ['create','edit'], group: gettext('Definition'), - type: 'select', + mode: ['create','edit', 'properties'], group: gettext('Definition'), disabled: () => obj.inCatalog(), readonly: function (state) { return !obj.isNew(state); }, controlProps: { allowClear: false }, - options: function() { + type: (state)=>{ let typetype = [ {label: gettext('Composite'), value: 'c'}, {label: gettext('Enumeration'), value: 'e'}, @@ -1273,6 +1259,13 @@ export default class TypeSchema extends BaseUISchema { {label: gettext('Range'), value: 'r'}, {label: gettext('Shell'), value: 'p'}, ]; + + if (!obj.isNew(state)) { + typetype.push( + {label: gettext('Multirange'), value: 'm'} + ); + } + if (obj.fieldOptions.server_info.server_type === 'ppas' && obj.fieldOptions.server_info.version >= 90500){ typetype.push( @@ -1280,7 +1273,12 @@ export default class TypeSchema extends BaseUISchema { {label: gettext('Varying Array'), value: 'V'} ); } - return typetype; + + return { + type: 'select', + options: typetype, + optionsReloadBasis: state.typtype, + }; }, }, { @@ -1305,6 +1303,7 @@ export default class TypeSchema extends BaseUISchema { schema: new EnumerationSchema(), type: 'collection', group: gettext('Definition'), mode: ['edit', 'create'], + uniqueCol: ['label'], canAddRow: function(state) { return !obj.isInvalidColumnAdded(state); }, @@ -1389,6 +1388,12 @@ export default class TypeSchema extends BaseUISchema { disabled: () => obj.inCatalog(), visible: (state) => isVisible(state, 'r'), }, + { + id: 'rngmultirangetype', label: gettext('Multirange type name'), cell: 'string', + type: 'text', mode: ['properties'], group: gettext('Definition'), + disabled: () => obj.inCatalog(), + visible: (state) => isVisible(state, 'r'), + }, { id: 'typinput', label: gettext('Input function'), cell: 'string', type: 'text', mode: ['properties'], group: gettext('Definition'), diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/additional_properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/additional_properties.sql new file mode 100644 index 000000000..0f5ae3994 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/additional_properties.sql @@ -0,0 +1,45 @@ +{# The SQL given below will fetch composite type#} +{% if typtype == 'c' %} +SELECT attnum, attname, pg_catalog.format_type(t.oid,NULL) AS typname, attndims, atttypmod, nsp.nspname, + (SELECT COUNT(1) from pg_catalog.pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup, + collname, nspc.nspname as collnspname, att.attrelid, + pg_catalog.format_type(t.oid, att.atttypmod) AS fulltype, + CASE WHEN t.typelem > 0 THEN t.typelem ELSE t.oid END as elemoid +FROM pg_catalog.pg_attribute att + JOIN pg_catalog.pg_type t ON t.oid=atttypid + JOIN pg_catalog.pg_namespace nsp ON t.typnamespace=nsp.oid + LEFT OUTER JOIN pg_catalog.pg_type b ON t.typelem=b.oid + LEFT OUTER JOIN pg_catalog.pg_collation c ON att.attcollation=c.oid + LEFT OUTER JOIN pg_catalog.pg_namespace nspc ON c.collnamespace=nspc.oid + WHERE att.attrelid = {{typrelid}}::oid + ORDER by attnum; +{% endif %} + +{# The SQL given below will fetch enum type#} +{% if typtype == 'e' %} +SELECT enumlabel +FROM pg_catalog.pg_enum + WHERE enumtypid={{tid}}::oid + ORDER by enumsortorder +{% endif %} + +{# The SQL given below will fetch range type#} +{% if typtype == 'r' %} +SELECT rngsubtype, st.typname, + rngcollation, mt.typname as rngmultirangetype, + CASE WHEN n.nspname IS NOT NULL THEN pg_catalog.concat(pg_catalog.quote_ident(n.nspname), '.', pg_catalog.quote_ident(col.collname)) ELSE col.collname END AS collname, + rngsubopc, opc.opcname, + rngcanonical, rngsubdiff as rngsubdiff_proc, + CASE WHEN length(ns.nspname::text) > 0 AND length(pgpr.proname::text) > 0 THEN + pg_catalog.concat(quote_ident(ns.nspname), '.', pg_catalog.quote_ident(pgpr.proname)) + ELSE '' END AS rngsubdiff +FROM pg_catalog.pg_range + LEFT JOIN pg_catalog.pg_type st ON st.oid=rngsubtype + LEFT JOIN pg_catalog.pg_type mt ON mt.oid=rngmultitypid + LEFT JOIN pg_catalog.pg_collation col ON col.oid=rngcollation + LEFT JOIN pg_catalog.pg_namespace n ON col.collnamespace=n.oid + LEFT JOIN pg_catalog.pg_opclass opc ON opc.oid=rngsubopc + LEFT JOIN pg_catalog.pg_proc pgpr ON pgpr.oid = rngsubdiff + LEFT JOIN pg_catalog.pg_namespace ns ON ns.oid=pgpr.pronamespace + WHERE rngtypid={{tid}}::oid; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/create.sql new file mode 100644 index 000000000..924eaa483 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/create.sql @@ -0,0 +1,86 @@ +{% import 'macros/schemas/security.macros' as SECLABEL %} +{% import 'macros/schemas/privilege.macros' as PRIVILEGE %} +{% import 'types/macros/get_full_type_sql_format.macros' as GET_TYPE %} +{## If user selected shell type then just create type template ##} +{% if data and data.typtype == 'p' %} +CREATE TYPE {{ conn|qtIdent(data.schema, data.name) }}; +{% endif %} +{### Composite Type ###} +{% if data and data.typtype == 'c' %} +CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS +({{"\n\t"}}{% if data.composite %}{% for d in data.composite %}{% if loop.index != 1 %},{{"\n\t"}}{% endif %}{{ conn|qtIdent(d.member_name) }} {% if is_sql %}{{ d.fulltype }}{% else %}{{ GET_TYPE.CREATE_TYPE_SQL(conn, d.cltype, d.tlength, d.precision, d.hasSqrBracket) }}{% endif %}{% if d.collation %} COLLATE {{d.collation}}{% endif %}{% endfor %}{% endif %}{{"\n"}}); +{% endif %} +{### Enum Type ###} +{% if data and data.typtype == 'e' %} +CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS ENUM + ({% for e in data.enum %}{% if loop.index != 1 %}, {% endif %}{{ e.label|qtLiteral(conn) }}{% endfor %}); +{% endif %} +{### Range Type ###} +{% if data and data.typtype == 'r' %} +CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS RANGE +( + {% if data.typname %}SUBTYPE={{ conn|qtTypeIdent(data.typname) }}{% endif %}{% if data.collname %}, + COLLATION = {{ data.collname }}{% endif %}{% if data.opcname %}, + SUBTYPE_OPCLASS = {{ data.opcname }}{% endif %}{% if data.rngcanonical %}, + CANONICAL = {{ data.rngcanonical }}{% endif %}{% if data.rngsubdiff %}, + SUBTYPE_DIFF = {{ data.rngsubdiff }}{% endif %}{% if data.rngmultirangetype %}, + MULTIRANGE_TYPE_NAME = {{ data.rngmultirangetype }}{% endif %} + +); +{% endif %} +{### External Type ###} +{% if data and data.typtype == 'b' %} +CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} + +( + {% if data.typinput %}INPUT = {{data.typinput}}{% endif %}{% if data.typoutput %}, + OUTPUT = {{ data.typoutput }}{% endif %}{% if data.typreceive %}, + RECEIVE = {{data.typreceive}}{% endif %}{% if data.typsend %}, + SEND = {{data.typsend}}{% endif %}{% if data.typmodin %}, + TYPMOD_IN = {{data.typmodin}}{% endif %}{% if data.typmodout %}, + TYPMOD_OUT = {{data.typmodout}}{% endif %}{% if data.typanalyze %}, + ANALYZE = {{data.typanalyze}}{% endif %}{% if data.typsubscript %}, + SUBSCRIPT = {{data.typsubscript}}{% endif %}{% if data.typlen %}, + INTERNALLENGTH = {{data.typlen}}{% endif %}{% if data.typbyval %}, + PASSEDBYVALUE{% endif %}{% if data.typalign %}, + ALIGNMENT = {{data.typalign}}{% endif %}{% if data.typstorage %}, + STORAGE = {{data.typstorage}}{% endif %}{% if data.typcategory %}, + CATEGORY = {{data.typcategory|qtLiteral(conn)}}{% endif %}{% if data.typispreferred %}, + PREFERRED = {{data.typispreferred}}{% endif %}{% if data.typdefault %}, + DEFAULT = {{data.typdefault|qtLiteral(conn)}}{% endif %}{% if data.element %}, + ELEMENT = {{data.element}}{% endif %}{% if data.typdelim %}, + DELIMITER = {{data.typdelim|qtLiteral(conn)}}{% endif %}{% if data.is_collatable %}, + COLLATABLE = {{data.is_collatable}}{% endif %} + +); +{% endif %} +{### Type Owner ###} +{% if data and data.typeowner %} + +ALTER TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} + + OWNER TO {{ conn|qtIdent(data.typeowner) }}; +{% endif %} +{### Type Comments ###} +{% if data and data.description %} + +COMMENT ON TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} + + IS {{data.description|qtLiteral(conn)}}; +{% endif %} +{### ACL ###} +{% if data.typacl %} + +{% for priv in data.typacl %} +{{ PRIVILEGE.SET(conn, 'TYPE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }} +{% endfor %} +{% endif %} +{### Security Lables ###} +{% if data.seclabels %} + +{% for r in data.seclabels %} +{% if r.provider and r.label %} +{{ SECLABEL.SET(conn, 'TYPE', data.name, r.provider, r.label, data.schema) }} +{% endif %} +{% endfor %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/nodes.sql new file mode 100644 index 000000000..9469379f5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/nodes.sql @@ -0,0 +1,19 @@ +SELECT t.oid, t.typname AS name, des.description, ra.rngmultitypid as rngmultirangetype +FROM pg_catalog.pg_type t + LEFT OUTER JOIN pg_catalog.pg_type e ON e.oid=t.typelem + LEFT OUTER JOIN pg_catalog.pg_range ra ON ra.rngtypid=t.oid + LEFT OUTER JOIN pg_catalog.pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c' + LEFT OUTER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = t.typnamespace + LEFT OUTER JOIN pg_catalog.pg_description des ON (des.objoid=t.oid AND des.classoid='pg_type'::regclass) +WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' AND t.typnamespace = {{scid}}::oid +{% if tid %} + AND t.oid = {{tid}}::oid +{% endif %} +{% if not show_system_objects %} + AND ct.oid is NULL +{% endif %} +{% if schema_diff %} + AND CASE WHEN (SELECT COUNT(*) FROM pg_catalog.pg_depend + WHERE objid = t.oid AND deptype = 'e') > 0 THEN FALSE ELSE TRUE END +{% endif %} +ORDER BY t.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/properties.sql new file mode 100644 index 000000000..66d66e56c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/properties.sql @@ -0,0 +1,45 @@ +SELECT t.oid, t.typname AS name, + (CASE WHEN CAST(coalesce(t.typcollation, '0') AS integer) = 100 THEN true ElSE false END) AS is_collatable, + t.typacl AS type_acl, + t.typnamespace, t.typowner, t.typlen, t.typbyval, t.typtype, + t.typcategory, t.typispreferred, t.typisdefined, t.typdelim, + t.typrelid, t.typelem, t.typarray, t.typalign, t.typstorage, + t.typnotnull, t.typbasetype, t.typtypmod, t.typndims, + t.typcollation, t.typdefaultbin, t.typdefault, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typinput::oid) AS typinput, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typoutput::oid) AS typoutput, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typreceive::oid) AS typreceive, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typsend::oid) AS typsend, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typmodin::oid) AS typmodin, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typmodout::oid) AS typmodout, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typanalyze::oid) AS typanalyze, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typsubscript::oid) AS typsubscript, + pg_catalog.format_type(t.oid, null) AS alias, + pg_catalog.pg_get_userbyid(t.typowner) as typeowner, e.typname as element, + description, ct.oid AS taboid, + nsp.nspname AS schema, + --MinimumVersion 9.1 START + (SELECT pg_catalog.array_agg(provider || '=' || label) FROM pg_catalog.pg_seclabel sl1 WHERE sl1.objoid=t.oid) AS seclabels, + -- END + (CASE WHEN (t.oid <= {{ datlastsysoid}}::oid OR ct.oid != 0) THEN true ElSE false END) AS is_sys_type +FROM pg_catalog.pg_type t + LEFT OUTER JOIN pg_catalog.pg_type e ON e.oid=t.typelem + LEFT OUTER JOIN pg_catalog.pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c' + LEFT OUTER JOIN pg_catalog.pg_description des ON (des.objoid=t.oid AND des.classoid='pg_type'::regclass) + LEFT OUTER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = t.typnamespace +WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' AND t.typnamespace = {{scid}}::oid +{% if tid %} + AND t.oid = {{tid}}::oid +{% endif %} +{% if not show_system_objects %} + AND ct.oid is NULL +{% endif %} +ORDER BY t.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/update.sql new file mode 100644 index 000000000..008b3e8e1 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/14_plus/update.sql @@ -0,0 +1,183 @@ +{% import 'macros/schemas/security.macros' as SECLABEL %} +{% import 'macros/schemas/privilege.macros' as PRIVILEGE %} +{% import 'types/macros/get_full_type_sql_format.macros' as GET_TYPE %} +{#======================================#} +{# Below will change object owner #} +{% if data.typeowner and data.typeowner != o_data.typeowner %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + OWNER TO {{ conn|qtIdent(data.typeowner) }}; + +{% endif %} +{#======================================#} +{# Below will change objects comment #} +{% if data.description is defined and data.description != o_data.description %} +COMMENT ON TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + IS {{ data.description|qtLiteral(conn) }}; + +{% endif %} +{#======================================#} +{### The sql given below will update composite type ###} +{% if data.composite and data.composite|length > 0 %} +{% set composite = data.composite %} +{% if 'deleted' in composite and composite.deleted|length > 0 %} +{% for r in composite.deleted %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + DROP ATTRIBUTE {{conn|qtIdent(r.member_name)}}; +{% endfor %} +{% endif %} +{% if 'added' in composite and composite.added|length > 0 %} +{% for r in composite.added %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + ADD ATTRIBUTE {{conn|qtIdent(r.member_name)}} {{ GET_TYPE.CREATE_TYPE_SQL(conn, r.cltype, r.tlength, r.precision, r.hasSqrBracket) }}{% if r.collation %} + COLLATE {{r.collation}}{% endif %}; +{% endfor %} +{% endif %} +{% if 'changed' in composite and composite.changed|length > 0 %} +{% for r in composite.changed %} +{% for o in o_data.composite %} +{##### Variables for the loop #####} +{% set member_name = o.member_name %} +{% set cltype = o.cltype %} +{% set tlength = o.tlength %} +{% set precision = o.precision %} +{% set hasSqrBracket = o.hasSqrBracket %} +{##### If member name changed #####} +{% if o.attnum == r.attnum %} +{% if r.member_name and o.member_name != r.member_name %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + RENAME ATTRIBUTE {{o.member_name}} TO {{r.member_name}}; +{% set member_name = r.member_name %} +{% endif %} +{##### If type changed #####} +{% if r.cltype and cltype != r.cltype %} +{% set cltype = r.cltype %} +{% set hasSqrBracket = r.hasSqrBracket %} +{##### If length is not allowed on type #####} +{% if not r.is_tlength %} +{% set tlength = 0 %} +{% set precision = 0 %} +{% endif %} +{% endif %} +{##### If length changed #####} +{% if r.tlength and tlength != r.tlength %} +{% set tlength = r.tlength %} +{% endif %} +{##### If precision changed #####} +{% if tlength and r.precision and precision != r.precision %} +{% set precision = r.precision %} +{% endif %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + ALTER ATTRIBUTE {{conn|qtIdent(member_name)}} SET DATA TYPE {{ GET_TYPE.CREATE_TYPE_SQL(conn, cltype, tlength, precision, hasSqrBracket) }}{% if r.collation %} + COLLATE {{r.collation}}{% endif %}; +{% endif%} +{% endfor %} +{% endfor %} +{% endif %} +{% endif %} +{#======================================#} +{### The sql given below will update enum type ###} +{% if data.enum and data.enum|length > 0 %} +{% set enum = data.enum %} +{% set o_enum_len = o_data.enum|length %} +{# We need actual list index from length #} +{% set o_enum_len = o_enum_len - 1 %} +{% if 'added' in enum and enum.added|length > 0 %} +{% for r in enum.added %} +{% set c_idx = loop.index %} +{% if c_idx == 1 %} +{# if first new element then add it after old data enum list#} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + ADD VALUE {{r.label|qtLiteral(conn)}} {% if o_enum_len > 0 %}AFTER {{o_data.enum[o_enum_len].label|qtLiteral(conn) }}{% endif %}; +{% else %} +{# if first new element then add it after new data enum list#} +{% set p_idx = loop.index - 2 %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + ADD VALUE {{r.label|qtLiteral(conn)}} AFTER {{enum.added[p_idx].label|qtLiteral(conn)}}; +{% endif %} +{% endfor %} +{% endif %} + +{% if 'changed' in enum and enum.changed|length > 0 %} +{% for r in enum.changed %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + RENAME VALUE {{r.old_label|qtLiteral(conn)}} TO {{r.label|qtLiteral(conn)}}; +{% endfor %} +{% endif %} + +{% endif %} +{#======================================#} +{### The sql given below will update External type ###} +{% if (data.typreceive is defined and data.typreceive != o_data.typreceive) or (data.typsend is defined and data.typsend != o_data.typsend) or (data.typmodin is defined and data.typmodin != o_data.typmodin) or (data.typmodout is defined and data.typmodout != o_data.typmodout) or (data.typanalyze is defined and data.typanalyze != o_data.typanalyze) or (data.typsubscript is defined and data.typsubscript != o_data.typsubscript) or (data.typstorage is defined and data.typstorage != o_data.typstorage)%} +{% set ns = namespace(add_comma=false) %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} SET ( + {% if data.typreceive is defined %}RECEIVE = {{data.typreceive}}{% set ns.add_comma = true%}{% endif %}{% if data.typsend is defined %}{% if ns.add_comma %}, + {% endif %}SEND = {{data.typsend}}{% set ns.add_comma = true%}{% endif %}{% if data.typmodin is defined %}{% if ns.add_comma %}, + {% endif %}TYPMOD_IN = {{data.typmodin}}{% set ns.add_comma = true%}{% endif %}{% if data.typmodout is defined %}{% if ns.add_comma %}, + {% endif %}TYPMOD_OUT = {{data.typmodout}}{% set ns.add_comma = true%}{% endif %}{% if data.typanalyze is defined %}{% if ns.add_comma %}, + {% endif %}ANALYZE = {{data.typanalyze}}{% set ns.add_comma = true%}{% endif %}{% if data.typsubscript is defined %}{% if ns.add_comma %}, + {% endif %}SUBSCRIPT = {{data.typsubscript}}{% set ns.add_comma = true%}{% endif %}{% if data.typstorage is defined %}{% if ns.add_comma %}, + {% endif %}STORAGE = {{data.typstorage}}{% endif %} + +); +{% endif %} +{#======================================#} +{# The SQL generated below will change Security Label #} +{% if data.seclabels and data.seclabels|length > 0 %} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABEL.UNSET(conn, 'TYPE', o_data.name, r.provider, o_data.schema) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABEL.SET(conn, 'TYPE', o_data.name, r.provider, r.label, o_data.schema) }} +{% endfor %} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABEL.SET(conn, 'TYPE', o_data.name, r.provider, r.label, o_data.schema) }} +{% endfor %} +{% endif %} + +{% endif %} +{#======================================#} +{# Change the privileges #} +{% if data.typacl and data.typacl|length > 0 %} +{% if 'deleted' in data.typacl %} +{% for priv in data.typacl.deleted %} +{{ PRIVILEGE.UNSETALL(conn, 'TYPE', priv.grantee, o_data.name, o_data.schema) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.typacl %} +{% for priv in data.typacl.changed %} +{% if priv.grantee != priv.old_grantee %} +{{ PRIVILEGE.UNSETALL(conn, 'TYPE', priv.old_grantee, o_data.name, o_data.schema) }} +{% else %} +{{ PRIVILEGE.UNSETALL(conn, 'TYPE', priv.grantee, o_data.name, o_data.schema) }} +{% endif %} +{{ PRIVILEGE.SET(conn, 'TYPE', priv.grantee, o_data.name, priv.without_grant, priv.with_grant, o_data.schema) }} +{% endfor %} +{% endif %} +{% if 'added' in data.typacl %} +{% for priv in data.typacl.added %} +{{ PRIVILEGE.SET(conn, 'TYPE', priv.grantee, o_data.name, priv.without_grant, priv.with_grant, o_data.schema) }} +{% endfor %} +{% endif %} +{% endif %} +{#======================================#} +{# Below will change object name #} +{% if data.name and data.name != o_data.name %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; + +{% endif %} +{#======================================#} +{# Below will change the schema for object #} +{# with extra if condition we will also make sure that object has correct name #} +{% if data.schema and data.schema != o_data.schema %} +ALTER TYPE {% if data.name and data.name != o_data.name %}{{ conn|qtIdent(o_data.schema, data.name) }} +{% else %}{{ conn|qtIdent(o_data.schema, o_data.name) }} +{% endif %} + SET SCHEMA {{ conn|qtIdent(data.schema) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/count.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/count.sql index 20d3b8b25..670982a2e 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/count.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/count.sql @@ -1,6 +1,6 @@ SELECT COUNT(*) FROM pg_catalog.pg_type t -LEFT OUTER JOIN pg_catalog.pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c' + LEFT OUTER JOIN pg_catalog.pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c' WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' AND t.typnamespace = {{scid}}::oid {% if not showsysobj %} AND ct.oid is NULL diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/get_external_functions.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/get_external_functions.sql index 425df8845..003bf0487 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/get_external_functions.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/get_external_functions.sql @@ -1,7 +1,7 @@ {### Input/Output/Send/Receive/Analyze function list also append into TypModeIN/TypModOUT ###} {% if extfunc %} SELECT proname, nspname, - CASE WHEN (length(nspname::text) > 0 AND nspname != 'public') and length(proname::text) > 0 THEN + CASE WHEN length(nspname::text) > 0 and length(proname::text) > 0 THEN pg_catalog.concat(pg_catalog.quote_ident(nspname), '.', pg_catalog.quote_ident(proname)) WHEN length(proname::text) > 0 THEN pg_catalog.quote_ident(proname) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/properties.sql index 09728bf20..a52fbaeb8 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/properties.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/properties.sql @@ -1,7 +1,26 @@ SELECT t.oid, t.typname AS name, (CASE WHEN CAST(coalesce(t.typcollation, '0') AS integer) = 100 THEN true ElSE false END) AS is_collatable, t.typacl AS type_acl, - t.*, pg_catalog.format_type(t.oid, null) AS alias, + t.typnamespace, t.typowner, t.typlen, t.typbyval, t.typtype, + t.typcategory, t.typispreferred, t.typisdefined, t.typdelim, + t.typrelid, t.typelem, t.typarray, t.typalign, t.typstorage, + t.typnotnull, t.typbasetype, t.typtypmod, t.typndims, + t.typcollation, t.typdefaultbin, t.typdefault, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typinput::oid) AS typinput, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typoutput::oid) AS typoutput, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typreceive::oid) AS typreceive, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typsend::oid) AS typsend, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typmodin::oid) AS typmodin, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typmodout::oid) AS typmodout, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typanalyze::oid) AS typanalyze, + pg_catalog.format_type(t.oid, null) AS alias, pg_catalog.pg_get_userbyid(t.typowner) as typeowner, e.typname as element, description, ct.oid AS taboid, nsp.nspname AS schema, diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/update.sql index fd40a44a0..20200cc76 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/update.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/pg/sql/default/update.sql @@ -97,6 +97,27 @@ ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} {% endfor %} {% endif %} +{% if 'changed' in enum and enum.changed|length > 0 %} +{% for r in enum.changed %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + RENAME VALUE {{r.old_label|qtLiteral(conn)}} TO {{r.label|qtLiteral(conn)}}; +{% endfor %} +{% endif %} + +{% endif %} +{#======================================#} +{### The sql given below will update External type ###} +{% if (data.typreceive is defined and data.typreceive != o_data.typreceive) or (data.typsend is defined and data.typsend != o_data.typsend) or (data.typmodin is defined and data.typmodin != o_data.typmodin) or (data.typmodout is defined and data.typmodout != o_data.typmodout) or (data.typanalyze is defined and data.typanalyze != o_data.typanalyze) or (data.typstorage is defined and data.typstorage != o_data.typstorage)%} +{% set ns = namespace(add_comma=false) %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} SET ( + {% if data.typreceive is defined %}RECEIVE = {{data.typreceive}}{% set ns.add_comma = true%}{% endif %}{% if data.typsend is defined %}{% if ns.add_comma %}, + {% endif %}SEND = {{data.typsend}}{% set ns.add_comma = true%}{% endif %}{% if data.typmodin is defined %}{% if ns.add_comma %}, + {% endif %}TYPMOD_IN = {{data.typmodin}}{% set ns.add_comma = true%}{% endif %}{% if data.typmodout is defined %}{% if ns.add_comma %}, + {% endif %}TYPMOD_OUT = {{data.typmodout}}{% set ns.add_comma = true%}{% endif %}{% if data.typanalyze is defined %}{% if ns.add_comma %}, + {% endif %}ANALYZE = {{data.typanalyze}}{% set ns.add_comma = true%}{% endif %}{% if data.typstorage is defined %}{% if ns.add_comma %}, + {% endif %}STORAGE = {{data.typstorage}}{% endif %} + +); {% endif %} {#======================================#} {# The SQL generated below will change Security Label #} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/additional_properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/additional_properties.sql new file mode 100644 index 000000000..b186b762f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/additional_properties.sql @@ -0,0 +1,64 @@ +{# The SQL given below will fetch composite type#} +{% if typtype == 'c' %} +SELECT attnum, attname, pg_catalog.format_type(t.oid,NULL) AS typname, attndims, atttypmod, nsp.nspname, + (SELECT COUNT(1) from pg_catalog.pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup, + collname, nspc.nspname as collnspname, att.attrelid, + pg_catalog.format_type(t.oid, att.atttypmod) AS fulltype, + CASE WHEN t.typelem > 0 THEN t.typelem ELSE t.oid END as elemoid +FROM pg_catalog.pg_attribute att + JOIN pg_catalog.pg_type t ON t.oid=atttypid + JOIN pg_catalog.pg_namespace nsp ON t.typnamespace=nsp.oid + LEFT OUTER JOIN pg_catalog.pg_type b ON t.typelem=b.oid + LEFT OUTER JOIN pg_catalog.pg_collation c ON att.attcollation=c.oid + LEFT OUTER JOIN pg_catalog.pg_namespace nspc ON c.collnamespace=nspc.oid + WHERE att.attrelid = {{typrelid}}::oid + ORDER by attnum; +{% endif %} + +{# The SQL given below will fetch enum type#} +{% if typtype == 'e' %} +SELECT enumlabel +FROM pg_catalog.pg_enum + WHERE enumtypid={{tid}}::oid + ORDER by enumsortorder +{% endif %} + +{# The SQL given below will fetch range type#} +{% if typtype == 'r' %} +SELECT rngsubtype, st.typname, + rngcollation, mt.typname as rngmultirangetype, + CASE WHEN n.nspname IS NOT NULL THEN pg_catalog.concat(pg_catalog.quote_ident(n.nspname), '.', pg_catalog.quote_ident(col.collname)) ELSE col.collname END AS collname, + rngsubopc, opc.opcname, + rngcanonical, rngsubdiff as rngsubdiff_proc, + CASE WHEN length(ns.nspname::text) > 0 AND length(pgpr.proname::text) > 0 THEN + pg_catalog.concat(quote_ident(ns.nspname), '.', pg_catalog.quote_ident(pgpr.proname)) + ELSE '' END AS rngsubdiff +FROM pg_catalog.pg_range + LEFT JOIN pg_catalog.pg_type st ON st.oid=rngsubtype + LEFT JOIN pg_catalog.pg_type mt ON mt.oid=rngmultitypid + LEFT JOIN pg_catalog.pg_collation col ON col.oid=rngcollation + LEFT JOIN pg_catalog.pg_namespace n ON col.collnamespace=n.oid + LEFT JOIN pg_catalog.pg_opclass opc ON opc.oid=rngsubopc + LEFT JOIN pg_catalog.pg_proc pgpr ON pgpr.oid = rngsubdiff + LEFT JOIN pg_catalog.pg_namespace ns ON ns.oid=pgpr.pronamespace + WHERE rngtypid={{tid}}::oid; +{% endif %} + +{# The SQL given below will fetch enum type#} +{% if typtype == 'N' or typtype == 'V' %} +SELECT t.typname AS typname, + CASE WHEN t.typelem > 0 THEN t.typelem ELSE t.oid END AS elemoid, + t.typtypmod, + t.typtype, + t.typndims, + pg_catalog.format_type(e.oid,NULL) AS type, + pg_catalog.format_type(e.oid, t.typtypmod) AS fulltype, + nsp.nspname as typnspname, + e.typname as type, + (SELECT COUNT(1) FROM pg_catalog.pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup, + CASE WHEN t.typcollation != 0 THEN TRUE ELSE FALSE END AS is_collatable +FROM pg_catalog.pg_type t + LEFT OUTER JOIN pg_catalog.pg_namespace nsp ON typnamespace=nsp.oid + LEFT OUTER JOIN pg_catalog.pg_type e ON e.oid=t.typelem +WHERE t.oid={{tid}}::oid; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/create.sql new file mode 100644 index 000000000..17c46f6eb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/create.sql @@ -0,0 +1,96 @@ +{% import 'macros/schemas/security.macros' as SECLABEL %} +{% import 'macros/schemas/privilege.macros' as PRIVILEGE %} +{% import 'types/macros/get_full_type_sql_format.macros' as GET_TYPE %} +{## If user selected shell type then just create type template ##} +{% if data and data.typtype == 'p' %} +CREATE TYPE {{ conn|qtIdent(data.schema, data.name) }}; +{% endif %} +{### Composite Type ###} +{% if data and data.typtype == 'c' %} +CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS +({{"\n\t"}}{% if data.composite %}{% for d in data.composite %}{% if loop.index != 1 %},{{"\n\t"}}{% endif %}{{ conn|qtIdent(d.member_name) }} {% if is_sql %}{{ d.fulltype }}{% else %}{{ GET_TYPE.CREATE_TYPE_SQL(conn, d.cltype, d.tlength, d.precision, d.hasSqrBracket) }}{% endif %}{% if d.collation %} COLLATE {{d.collation}}{% endif %}{% endfor %}{% endif %}{{"\n"}}); +{% endif %} +{### Enum Type ###} +{% if data and data.typtype == 'e' %} +CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS ENUM + ({% for e in data.enum %}{% if loop.index != 1 %}, {% endif %}{{ e.label|qtLiteral(conn) }}{% endfor %}); +{% endif %} +{### Range Type ###} +{% if data and data.typtype == 'r' %} +CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS RANGE +( + {% if data.typname %}SUBTYPE={{ conn|qtTypeIdent(data.typname) }}{% endif %}{% if data.collname %}, + COLLATION = {{ data.collname }}{% endif %}{% if data.opcname %}, + SUBTYPE_OPCLASS = {{ data.opcname }}{% endif %}{% if data.rngcanonical %}, + CANONICAL = {{ data.rngcanonical }}{% endif %}{% if data.rngsubdiff %}, + SUBTYPE_DIFF = {{ data.rngsubdiff }}{% endif %}{% if data.rngmultirangetype %}, + MULTIRANGE_TYPE_NAME = {{ data.rngmultirangetype }}{% endif %} + +); +{% endif %} +{### External Type ###} +{% if data and data.typtype == 'b' %} +CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} + +( + {% if data.typinput %}INPUT = {{data.typinput}}{% endif %}{% if data.typoutput %}, + OUTPUT = {{ data.typoutput }}{% endif %}{% if data.typreceive %}, + RECEIVE = {{data.typreceive}}{% endif %}{% if data.typsend %}, + SEND = {{data.typsend}}{% endif %}{% if data.typmodin %}, + TYPMOD_IN = {{data.typmodin}}{% endif %}{% if data.typmodout %}, + TYPMOD_OUT = {{data.typmodout}}{% endif %}{% if data.typanalyze %}, + ANALYZE = {{data.typanalyze}}{% endif %}{% if data.typsubscript %}, + SUBSCRIPT = {{data.typsubscript}}{% endif %}{% if data.typlen %}, + INTERNALLENGTH = {{data.typlen}}{% endif %}{% if data.typbyval %}, + PASSEDBYVALUE{% endif %}{% if data.typalign %}, + ALIGNMENT = {{data.typalign}}{% endif %}{% if data.typstorage %}, + STORAGE = {{data.typstorage}}{% endif %}{% if data.typcategory %}, + CATEGORY = {{data.typcategory|qtLiteral(conn)}}{% endif %}{% if data.typispreferred %}, + PREFERRED = {{data.typispreferred}}{% endif %}{% if data.typdefault %}, + DEFAULT = {{data.typdefault|qtLiteral(conn)}}{% endif %}{% if data.element %}, + ELEMENT = {{data.element}}{% endif %}{% if data.typdelim %}, + DELIMITER = {{data.typdelim|qtLiteral(conn)}}{% endif %}{% if data.is_collatable %}, + COLLATABLE = {{data.is_collatable}}{% endif %} + +); +{% endif %} +{### Nested-table Type ###} +{% if data and data.typtype == 'N' %} +CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS TABLE OF + {% if is_sql %}{{ data.fulltype }}{% else %}{{ GET_TYPE.CREATE_TYPE_SQL(conn, data.cltype, data.tlength, data.precision, data.hasSqrBracket) }}{% endif %}; +{% endif %} +{### VARRAY Type ###} +{% if data and data.typtype == 'V' %} +CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS VARRAY({{data.maxsize}}) OF + {% if is_sql %}{{ data.fulltype }}{% else %}{{ GET_TYPE.CREATE_TYPE_SQL(conn, data.cltype, data.tlength, data.precision, data.hasSqrBracket) }}{% endif %}; +{% endif %} + +{### Type Owner ###} +{% if data and data.typeowner %} +ALTER TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} + + OWNER TO {{ conn|qtIdent(data.typeowner) }}; +{% endif %} +{### Type Comments ###} +{% if data and data.description %} + +COMMENT ON TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} + + IS {{data.description|qtLiteral(conn)}}; +{% endif %} +{### ACL ###} +{% if data.typacl %} + +{% for priv in data.typacl %} +{{ PRIVILEGE.SET(conn, 'TYPE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }} +{% endfor %} +{% endif %} +{### Security Lables ###} +{% if data.seclabels %} + +{% for r in data.seclabels %} +{% if r.provider and r.label %} +{{ SECLABEL.SET(conn, 'TYPE', data.name, r.provider, r.label, data.schema) }} +{% endif %} +{% endfor %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/nodes.sql new file mode 100644 index 000000000..9469379f5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/nodes.sql @@ -0,0 +1,19 @@ +SELECT t.oid, t.typname AS name, des.description, ra.rngmultitypid as rngmultirangetype +FROM pg_catalog.pg_type t + LEFT OUTER JOIN pg_catalog.pg_type e ON e.oid=t.typelem + LEFT OUTER JOIN pg_catalog.pg_range ra ON ra.rngtypid=t.oid + LEFT OUTER JOIN pg_catalog.pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c' + LEFT OUTER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = t.typnamespace + LEFT OUTER JOIN pg_catalog.pg_description des ON (des.objoid=t.oid AND des.classoid='pg_type'::regclass) +WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' AND t.typnamespace = {{scid}}::oid +{% if tid %} + AND t.oid = {{tid}}::oid +{% endif %} +{% if not show_system_objects %} + AND ct.oid is NULL +{% endif %} +{% if schema_diff %} + AND CASE WHEN (SELECT COUNT(*) FROM pg_catalog.pg_depend + WHERE objid = t.oid AND deptype = 'e') > 0 THEN FALSE ELSE TRUE END +{% endif %} +ORDER BY t.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/properties.sql new file mode 100644 index 000000000..d50619e61 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/properties.sql @@ -0,0 +1,45 @@ +SELECT t.oid, t.typname AS name, + (CASE WHEN CAST(coalesce(t.typcollation, '0') AS integer) = 100 THEN true ElSE false END) AS is_collatable, + t.typacl AS type_acl, + t.typnamespace, t.typowner, t.typlen, t.typbyval, t.typtype, + t.typcategory, t.typispreferred, t.typisdefined, t.typdelim, + t.typrelid, t.typelem, t.typarray, t.typalign, t.typstorage, + t.typnotnull, t.typbasetype, t.typtypmod, t.typndims, + t.typcollation, t.typdefaultbin, t.typdefault, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typinput::oid) AS typinput, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typoutput::oid) AS typoutput, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typreceive::oid) AS typreceive, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typsend::oid) AS typsend, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typmodin::oid) AS typmodin, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typmodout::oid) AS typmodout, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typanalyze::oid) AS typanalyze, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typsubscript::oid) AS typsubscript, + pg_catalog.format_type(t.oid, null) AS alias, + pg_catalog.pg_get_userbyid(t.typowner) as typeowner, e.typname as element, + description, ct.oid AS taboid, + nsp.nspname AS schema, + --MinimumVersion 9.1 START + (SELECT pg_catalog.array_agg(provider || '=' || label) FROM pg_catalog.pg_seclabel sl1 WHERE sl1.objoid=t.oid) AS seclabels, + -- END + (CASE WHEN (t.oid <= {{ datlastsysoid}}::oid OR ct.oid != 0) THEN true ElSE false END) AS is_sys_type +FROM pg_catalog.pg_type t + LEFT OUTER JOIN pg_catalog.pg_type e ON e.oid=t.typelem + LEFT OUTER JOIN pg_catalog.pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c' + LEFT OUTER JOIN pg_catalog.pg_description des ON (des.objoid=t.oid AND des.classoid='pg_type'::regclass) + LEFT OUTER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = t.typnamespace +WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' AND t.typnamespace = {{scid}}::oid +{% if tid %} + AND t.oid = {{tid}}::oid +{% endif %} +{% if not show_system_objects %} + AND ct.oid is NULL +{% endif %} +ORDER BY t.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/update.sql new file mode 100644 index 000000000..008b3e8e1 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/14_plus/update.sql @@ -0,0 +1,183 @@ +{% import 'macros/schemas/security.macros' as SECLABEL %} +{% import 'macros/schemas/privilege.macros' as PRIVILEGE %} +{% import 'types/macros/get_full_type_sql_format.macros' as GET_TYPE %} +{#======================================#} +{# Below will change object owner #} +{% if data.typeowner and data.typeowner != o_data.typeowner %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + OWNER TO {{ conn|qtIdent(data.typeowner) }}; + +{% endif %} +{#======================================#} +{# Below will change objects comment #} +{% if data.description is defined and data.description != o_data.description %} +COMMENT ON TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + IS {{ data.description|qtLiteral(conn) }}; + +{% endif %} +{#======================================#} +{### The sql given below will update composite type ###} +{% if data.composite and data.composite|length > 0 %} +{% set composite = data.composite %} +{% if 'deleted' in composite and composite.deleted|length > 0 %} +{% for r in composite.deleted %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + DROP ATTRIBUTE {{conn|qtIdent(r.member_name)}}; +{% endfor %} +{% endif %} +{% if 'added' in composite and composite.added|length > 0 %} +{% for r in composite.added %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + ADD ATTRIBUTE {{conn|qtIdent(r.member_name)}} {{ GET_TYPE.CREATE_TYPE_SQL(conn, r.cltype, r.tlength, r.precision, r.hasSqrBracket) }}{% if r.collation %} + COLLATE {{r.collation}}{% endif %}; +{% endfor %} +{% endif %} +{% if 'changed' in composite and composite.changed|length > 0 %} +{% for r in composite.changed %} +{% for o in o_data.composite %} +{##### Variables for the loop #####} +{% set member_name = o.member_name %} +{% set cltype = o.cltype %} +{% set tlength = o.tlength %} +{% set precision = o.precision %} +{% set hasSqrBracket = o.hasSqrBracket %} +{##### If member name changed #####} +{% if o.attnum == r.attnum %} +{% if r.member_name and o.member_name != r.member_name %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + RENAME ATTRIBUTE {{o.member_name}} TO {{r.member_name}}; +{% set member_name = r.member_name %} +{% endif %} +{##### If type changed #####} +{% if r.cltype and cltype != r.cltype %} +{% set cltype = r.cltype %} +{% set hasSqrBracket = r.hasSqrBracket %} +{##### If length is not allowed on type #####} +{% if not r.is_tlength %} +{% set tlength = 0 %} +{% set precision = 0 %} +{% endif %} +{% endif %} +{##### If length changed #####} +{% if r.tlength and tlength != r.tlength %} +{% set tlength = r.tlength %} +{% endif %} +{##### If precision changed #####} +{% if tlength and r.precision and precision != r.precision %} +{% set precision = r.precision %} +{% endif %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + ALTER ATTRIBUTE {{conn|qtIdent(member_name)}} SET DATA TYPE {{ GET_TYPE.CREATE_TYPE_SQL(conn, cltype, tlength, precision, hasSqrBracket) }}{% if r.collation %} + COLLATE {{r.collation}}{% endif %}; +{% endif%} +{% endfor %} +{% endfor %} +{% endif %} +{% endif %} +{#======================================#} +{### The sql given below will update enum type ###} +{% if data.enum and data.enum|length > 0 %} +{% set enum = data.enum %} +{% set o_enum_len = o_data.enum|length %} +{# We need actual list index from length #} +{% set o_enum_len = o_enum_len - 1 %} +{% if 'added' in enum and enum.added|length > 0 %} +{% for r in enum.added %} +{% set c_idx = loop.index %} +{% if c_idx == 1 %} +{# if first new element then add it after old data enum list#} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + ADD VALUE {{r.label|qtLiteral(conn)}} {% if o_enum_len > 0 %}AFTER {{o_data.enum[o_enum_len].label|qtLiteral(conn) }}{% endif %}; +{% else %} +{# if first new element then add it after new data enum list#} +{% set p_idx = loop.index - 2 %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + ADD VALUE {{r.label|qtLiteral(conn)}} AFTER {{enum.added[p_idx].label|qtLiteral(conn)}}; +{% endif %} +{% endfor %} +{% endif %} + +{% if 'changed' in enum and enum.changed|length > 0 %} +{% for r in enum.changed %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + RENAME VALUE {{r.old_label|qtLiteral(conn)}} TO {{r.label|qtLiteral(conn)}}; +{% endfor %} +{% endif %} + +{% endif %} +{#======================================#} +{### The sql given below will update External type ###} +{% if (data.typreceive is defined and data.typreceive != o_data.typreceive) or (data.typsend is defined and data.typsend != o_data.typsend) or (data.typmodin is defined and data.typmodin != o_data.typmodin) or (data.typmodout is defined and data.typmodout != o_data.typmodout) or (data.typanalyze is defined and data.typanalyze != o_data.typanalyze) or (data.typsubscript is defined and data.typsubscript != o_data.typsubscript) or (data.typstorage is defined and data.typstorage != o_data.typstorage)%} +{% set ns = namespace(add_comma=false) %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} SET ( + {% if data.typreceive is defined %}RECEIVE = {{data.typreceive}}{% set ns.add_comma = true%}{% endif %}{% if data.typsend is defined %}{% if ns.add_comma %}, + {% endif %}SEND = {{data.typsend}}{% set ns.add_comma = true%}{% endif %}{% if data.typmodin is defined %}{% if ns.add_comma %}, + {% endif %}TYPMOD_IN = {{data.typmodin}}{% set ns.add_comma = true%}{% endif %}{% if data.typmodout is defined %}{% if ns.add_comma %}, + {% endif %}TYPMOD_OUT = {{data.typmodout}}{% set ns.add_comma = true%}{% endif %}{% if data.typanalyze is defined %}{% if ns.add_comma %}, + {% endif %}ANALYZE = {{data.typanalyze}}{% set ns.add_comma = true%}{% endif %}{% if data.typsubscript is defined %}{% if ns.add_comma %}, + {% endif %}SUBSCRIPT = {{data.typsubscript}}{% set ns.add_comma = true%}{% endif %}{% if data.typstorage is defined %}{% if ns.add_comma %}, + {% endif %}STORAGE = {{data.typstorage}}{% endif %} + +); +{% endif %} +{#======================================#} +{# The SQL generated below will change Security Label #} +{% if data.seclabels and data.seclabels|length > 0 %} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABEL.UNSET(conn, 'TYPE', o_data.name, r.provider, o_data.schema) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABEL.SET(conn, 'TYPE', o_data.name, r.provider, r.label, o_data.schema) }} +{% endfor %} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABEL.SET(conn, 'TYPE', o_data.name, r.provider, r.label, o_data.schema) }} +{% endfor %} +{% endif %} + +{% endif %} +{#======================================#} +{# Change the privileges #} +{% if data.typacl and data.typacl|length > 0 %} +{% if 'deleted' in data.typacl %} +{% for priv in data.typacl.deleted %} +{{ PRIVILEGE.UNSETALL(conn, 'TYPE', priv.grantee, o_data.name, o_data.schema) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.typacl %} +{% for priv in data.typacl.changed %} +{% if priv.grantee != priv.old_grantee %} +{{ PRIVILEGE.UNSETALL(conn, 'TYPE', priv.old_grantee, o_data.name, o_data.schema) }} +{% else %} +{{ PRIVILEGE.UNSETALL(conn, 'TYPE', priv.grantee, o_data.name, o_data.schema) }} +{% endif %} +{{ PRIVILEGE.SET(conn, 'TYPE', priv.grantee, o_data.name, priv.without_grant, priv.with_grant, o_data.schema) }} +{% endfor %} +{% endif %} +{% if 'added' in data.typacl %} +{% for priv in data.typacl.added %} +{{ PRIVILEGE.SET(conn, 'TYPE', priv.grantee, o_data.name, priv.without_grant, priv.with_grant, o_data.schema) }} +{% endfor %} +{% endif %} +{% endif %} +{#======================================#} +{# Below will change object name #} +{% if data.name and data.name != o_data.name %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; + +{% endif %} +{#======================================#} +{# Below will change the schema for object #} +{# with extra if condition we will also make sure that object has correct name #} +{% if data.schema and data.schema != o_data.schema %} +ALTER TYPE {% if data.name and data.name != o_data.name %}{{ conn|qtIdent(o_data.schema, data.name) }} +{% else %}{{ conn|qtIdent(o_data.schema, o_data.name) }} +{% endif %} + SET SCHEMA {{ conn|qtIdent(data.schema) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/additional_properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/additional_properties.sql index 8379a23e8..683cca92a 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/additional_properties.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/additional_properties.sql @@ -29,12 +29,17 @@ SELECT rngsubtype, st.typname, rngcollation, CASE WHEN n.nspname IS NOT NULL THEN pg_catalog.concat(pg_catalog.quote_ident(n.nspname), '.', pg_catalog.quote_ident(col.collname)) ELSE col.collname END AS collname, rngsubopc, opc.opcname, - rngcanonical, rngsubdiff + rngcanonical, rngsubdiff as rngsubdiff_proc, + CASE WHEN length(ns.nspname::text) > 0 AND length(pgpr.proname::text) > 0 THEN + pg_catalog.concat(quote_ident(ns.nspname), '.', pg_catalog.quote_ident(pgpr.proname)) + ELSE '' END AS rngsubdiff FROM pg_catalog.pg_range LEFT JOIN pg_catalog.pg_type st ON st.oid=rngsubtype LEFT JOIN pg_catalog.pg_collation col ON col.oid=rngcollation LEFT JOIN pg_catalog.pg_namespace n ON col.collnamespace=n.oid LEFT JOIN pg_catalog.pg_opclass opc ON opc.oid=rngsubopc + LEFT JOIN pg_catalog.pg_proc pgpr ON pgpr.oid = rngsubdiff + LEFT JOIN pg_catalog.pg_namespace ns ON ns.oid=pgpr.pronamespace WHERE rngtypid={{tid}}::oid; {% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/get_external_functions.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/get_external_functions.sql index 425df8845..003bf0487 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/get_external_functions.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/get_external_functions.sql @@ -1,7 +1,7 @@ {### Input/Output/Send/Receive/Analyze function list also append into TypModeIN/TypModOUT ###} {% if extfunc %} SELECT proname, nspname, - CASE WHEN (length(nspname::text) > 0 AND nspname != 'public') and length(proname::text) > 0 THEN + CASE WHEN length(nspname::text) > 0 and length(proname::text) > 0 THEN pg_catalog.concat(pg_catalog.quote_ident(nspname), '.', pg_catalog.quote_ident(proname)) WHEN length(proname::text) > 0 THEN pg_catalog.quote_ident(proname) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/properties.sql index 09728bf20..a52fbaeb8 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/properties.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/properties.sql @@ -1,7 +1,26 @@ SELECT t.oid, t.typname AS name, (CASE WHEN CAST(coalesce(t.typcollation, '0') AS integer) = 100 THEN true ElSE false END) AS is_collatable, t.typacl AS type_acl, - t.*, pg_catalog.format_type(t.oid, null) AS alias, + t.typnamespace, t.typowner, t.typlen, t.typbyval, t.typtype, + t.typcategory, t.typispreferred, t.typisdefined, t.typdelim, + t.typrelid, t.typelem, t.typarray, t.typalign, t.typstorage, + t.typnotnull, t.typbasetype, t.typtypmod, t.typndims, + t.typcollation, t.typdefaultbin, t.typdefault, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typinput::oid) AS typinput, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typoutput::oid) AS typoutput, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typreceive::oid) AS typreceive, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typsend::oid) AS typsend, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typmodin::oid) AS typmodin, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typmodout::oid) AS typmodout, + (SELECT pg_catalog.concat(nspname, '.', proname,'') FROM pg_proc pr JOIN pg_namespace nsp ON pr.pronamespace = nsp.oid + WHERE pr.oid = t.typanalyze::oid) AS typanalyze, + pg_catalog.format_type(t.oid, null) AS alias, pg_catalog.pg_get_userbyid(t.typowner) as typeowner, e.typname as element, description, ct.oid AS taboid, nsp.nspname AS schema, diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/update.sql index fd40a44a0..20200cc76 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/update.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/types/ppas/sql/default/update.sql @@ -97,6 +97,27 @@ ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} {% endfor %} {% endif %} +{% if 'changed' in enum and enum.changed|length > 0 %} +{% for r in enum.changed %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} + RENAME VALUE {{r.old_label|qtLiteral(conn)}} TO {{r.label|qtLiteral(conn)}}; +{% endfor %} +{% endif %} + +{% endif %} +{#======================================#} +{### The sql given below will update External type ###} +{% if (data.typreceive is defined and data.typreceive != o_data.typreceive) or (data.typsend is defined and data.typsend != o_data.typsend) or (data.typmodin is defined and data.typmodin != o_data.typmodin) or (data.typmodout is defined and data.typmodout != o_data.typmodout) or (data.typanalyze is defined and data.typanalyze != o_data.typanalyze) or (data.typstorage is defined and data.typstorage != o_data.typstorage)%} +{% set ns = namespace(add_comma=false) %} +ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }} SET ( + {% if data.typreceive is defined %}RECEIVE = {{data.typreceive}}{% set ns.add_comma = true%}{% endif %}{% if data.typsend is defined %}{% if ns.add_comma %}, + {% endif %}SEND = {{data.typsend}}{% set ns.add_comma = true%}{% endif %}{% if data.typmodin is defined %}{% if ns.add_comma %}, + {% endif %}TYPMOD_IN = {{data.typmodin}}{% set ns.add_comma = true%}{% endif %}{% if data.typmodout is defined %}{% if ns.add_comma %}, + {% endif %}TYPMOD_OUT = {{data.typmodout}}{% set ns.add_comma = true%}{% endif %}{% if data.typanalyze is defined %}{% if ns.add_comma %}, + {% endif %}ANALYZE = {{data.typanalyze}}{% set ns.add_comma = true%}{% endif %}{% if data.typstorage is defined %}{% if ns.add_comma %}, + {% endif %}STORAGE = {{data.typstorage}}{% endif %} + +); {% endif %} {#======================================#} {# The SQL generated below will change Security Label #} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/alter_acl_grantee_change.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/alter_acl_grantee_change.sql new file mode 100644 index 000000000..91520ae0d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/alter_acl_grantee_change.sql @@ -0,0 +1,18 @@ +-- Type: range_type_$%{}[]()&*^!@"'`\/# + +-- DROP TYPE IF EXISTS public."range_type_$%{}[]()&*^!@""'`\/#"; + +CREATE TYPE public."range_type_$%{}[]()&*^!@""'`\/#" AS RANGE +( + SUBTYPE=bool, + SUBTYPE_OPCLASS = bool_ops, + MULTIRANGE_TYPE_NAME = multirange_type_$%{}[]()&*^!@"'`\/# +); + +ALTER TYPE public."range_type_$%{}[]()&*^!@""'`\/#" + OWNER TO ; + +COMMENT ON TYPE public."range_type_$%{}[]()&*^!@""'`\/#" + IS 'this is test'; + +GRANT USAGE ON TYPE public."range_type_$%{}[]()&*^!@""'`\/#" TO PUBLIC; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/alter_range_type_acl_description.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/alter_range_type_acl_description.sql new file mode 100644 index 000000000..094785d2f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/alter_range_type_acl_description.sql @@ -0,0 +1,20 @@ +-- Type: range_type_$%{}[]()&*^!@"'`\/# + +-- DROP TYPE IF EXISTS public."range_type_$%{}[]()&*^!@""'`\/#"; + +CREATE TYPE public."range_type_$%{}[]()&*^!@""'`\/#" AS RANGE +( + SUBTYPE=bool, + SUBTYPE_OPCLASS = bool_ops, + MULTIRANGE_TYPE_NAME = multirange_type_$%{}[]()&*^!@"'`\/# +); + +ALTER TYPE public."range_type_$%{}[]()&*^!@""'`\/#" + OWNER TO ; + +COMMENT ON TYPE public."range_type_$%{}[]()&*^!@""'`\/#" + IS 'this is test'; + +GRANT USAGE ON TYPE public."range_type_$%{}[]()&*^!@""'`\/#" TO PUBLIC; + +GRANT USAGE ON TYPE public."range_type_$%{}[]()&*^!@""'`\/#" TO ; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/create_multi_range_type.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/create_multi_range_type.msql new file mode 100644 index 000000000..6584c082a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/create_multi_range_type.msql @@ -0,0 +1,5 @@ +CREATE TYPE public."range_type_$%{}[]()&*^!@""'`\/#" AS RANGE +( + SUBTYPE=bool, + MULTIRANGE_TYPE_NAME = multirange_type +); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/create_multi_range_type.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/create_multi_range_type.sql new file mode 100644 index 000000000..718440994 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/create_multi_range_type.sql @@ -0,0 +1,13 @@ +-- Type: range_type_$%{}[]()&*^!@"'`\/# + +-- DROP TYPE IF EXISTS public."range_type_$%{}[]()&*^!@""'`\/#"; + +CREATE TYPE public."range_type_$%{}[]()&*^!@""'`\/#" AS RANGE +( + SUBTYPE=bool, + SUBTYPE_OPCLASS = bool_ops, + MULTIRANGE_TYPE_NAME = multirange_type +); + +ALTER TYPE public."range_type_$%{}[]()&*^!@""'`\/#" + OWNER TO ; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/create_range_type.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/create_range_type.sql new file mode 100644 index 000000000..ac57c8cbb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/create_range_type.sql @@ -0,0 +1,13 @@ +-- Type: range_type_$%{}[]()&*^!@"'`\/# + +-- DROP TYPE IF EXISTS public."range_type_$%{}[]()&*^!@""'`\/#"; + +CREATE TYPE public."range_type_$%{}[]()&*^!@""'`\/#" AS RANGE +( + SUBTYPE=bool, + SUBTYPE_OPCLASS = bool_ops, + MULTIRANGE_TYPE_NAME = multirange_type_$%{}[]()&*^!@"'`\/# +); + +ALTER TYPE public."range_type_$%{}[]()&*^!@""'`\/#" + OWNER TO ; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/tests.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/tests.json new file mode 100644 index 000000000..3c7527248 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/14_plus/tests.json @@ -0,0 +1,324 @@ +{ + "scenarios": [ + { + "type": "create", + "name": "Create ENUM type", + "endpoint": "NODE-type.obj", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql", + "data": { + "name": "enum_type_$%{}[]()&*^!@\"'`\\/#", + "is_sys_type":false, + "typtype":"e", + "schema":"public", + "composite":[], + "enum":[{"label":"a"},{"label":"b"},{"label":"c"}], + "typacl":[], + "seclabels":[], + "description":"" + }, + "expected_sql_file": "create_enum_type.sql", + "expected_msql_file": "create_enum_type.msql" + }, + { + "type": "alter", + "name": "alter ENUM type and add new label", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "enum": {"added": [{"label": "d"}]} + }, + "expected_sql_file": "alter_enum_type_label.sql", + "expected_msql_file": "alter_enum_type_label.msql" + }, + { + "type": "alter", + "name": "alter ENUM type and add ACL, description", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "description":"this is test", + "typacl": {"added": [{"grantee":"PUBLIC","grantor":"postgres","privileges":[{"privilege_type":"U","privilege":true,"with_grant":false}]}]} + }, + "expected_sql_file": "alter_enum_type_acl_description.sql", + "expected_msql_file": "alter_enum_type_acl_description.msql" + }, + { + "type": "alter", + "name": "alter ENUM type and rename ENUM", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "enum": {"changed": [{"label": "a_new", "old_label": "a"}, {"label": "d_new", "old_label": "d"}]} + }, + "expected_sql_file": "alter_enum_type_rename_enum.sql", + "expected_msql_file": "alter_enum_type_rename_enum.msql" + }, + { + "type": "delete", + "name": "Drop ENUM type", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "data": {} + }, + + + { + "type": "create", + "name": "Create Composite type", + "endpoint": "NODE-type.obj", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql", + "data": { + "name": "composite_type_$%{}[]()&*^!@\"'`\\/#", + "is_sys_type":false, + "typtype":"c", + "schema":"public", + "composite":[ + {"member_name":"mname1","type":"bigint","is_tlength":false,"is_precision":false}, + {"member_name":"mname2","type":"character varying","tlength":50,"is_tlength":true,"is_precision":false,"collation":"pg_catalog.\"C\"","min_val":1,"max_val":2147483647,"collspcname":""}, + {"member_name":"mname3","type":"text[]","is_tlength":false,"is_precision":false,"collation":"pg_catalog.\"C\"","collspcname":""} + ], + "typacl":[], + "seclabels":[], + "description":"" + }, + "expected_sql_file": "create_composite_type.sql", + "expected_msql_file": "create_composite_type.msql" + }, + { + "type": "alter", + "name": "alter Composite type and add new label", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "composite": {"added": [{"member_name":"mname4","type":"bigint","is_tlength":false,"is_precision":false}]} + }, + "expected_sql_file": "alter_composite_type_add_member.sql", + "expected_msql_file": "alter_composite_type_add_member.msql" + }, + { + "type": "alter", + "name": "alter Composite type and delete label", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "composite": {"deleted":[{"attnum":1,"member_name":"mname1","type":"bigint","tlength":null,"is_tlength":false,"precision":null,"is_precision":false,"collation":"","cltype":"bigint","hasSqrBracket":false,"fulltype":"bigint"}]} + }, + "expected_sql_file": "alter_composite_type_remove_member.sql", + "expected_msql_file": "alter_composite_type_remove_member.msql" + }, + { + "type": "alter", + "name": "alter Composite type and add ACL, description", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "description":"this is test", + "typacl": {"added": [{"grantee":"PUBLIC","grantor":"postgres","privileges":[{"privilege_type":"U","privilege":true,"with_grant":false}]}]} + }, + "expected_sql_file": "alter_composite_type_acl_description.sql", + "expected_msql_file": "alter_composite_type_acl_description.msql" + }, + { + "type": "delete", + "name": "Drop Composite type", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "data": {} + }, + + { + "type": "create", + "name": "Create Composite type - one field", + "endpoint": "NODE-type.obj", + "sql_endpoint": "NODE-type.sql_id", + "data": { + "name": "composite_type_$%{}[]()&*^!@\"'`\\/#", + "is_sys_type":false, + "typtype":"c", + "schema":"public", + "composite":[ + {"member_name":"mname1","type":"bigint","is_tlength":false,"is_precision":false} + ], + "typacl":[], + "seclabels":[], + "description":"" + }, + "expected_sql_file": "create_composite_type_one_field.sql" + }, + { + "type": "delete", + "name": "Drop Composite type - one field", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "data": {} + }, + + { + "type": "create", + "name": "Create SHELL type", + "endpoint": "NODE-type.obj", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql", + "data": { + "name": "shell_type_$%{}[]()&*^!@\"'`\\/#", + "is_sys_type":false, + "typtype":"p", + "schema":"public", + "composite":[], + "enum":[], + "typacl":[], + "seclabels":[] + }, + "expected_sql_file": "create_shell_type.sql", + "expected_msql_file": "create_shell_type.msql" + }, + { + "type": "alter", + "name": "alter SHELL type and add description", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "description":"this is test" + }, + "expected_sql_file": "alter_shell_type_acl_description.sql", + "expected_msql_file": "alter_shell_type_acl_description.msql" + }, + { + "type": "delete", + "name": "Drop SHELL type", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "data": {} + }, + + + { + "type": "create", + "name": "Create RANGE type", + "endpoint": "NODE-type.obj", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql", + "data": { + "name": "range_type_$%{}[]()&*^!@\"'`\\/#", + "is_sys_type":false, + "typtype":"r", + "schema":"public", + "composite":[], + "enum":[], + "typacl":[], + "seclabels":[], + "typname":"bool" + }, + "expected_sql_file": "create_range_type.sql", + "expected_msql_file": "create_range_type.msql" + }, + { + "type": "alter", + "name": "alter RANGE type and add ACL, description", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "description":"this is test", + "typacl": {"added": [{"grantee":"PUBLIC","grantor":"postgres","privileges":[{"privilege_type":"U","privilege":true,"with_grant":false}]}]} + }, + "expected_sql_file": "alter_range_type_acl_description.sql", + "expected_msql_file": "alter_range_type_acl_description.msql" + }, + { + "type": "alter", + "name": "alter ACL (Remove privilege)", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "typacl": { + "deleted": [ + { + "grantee": "PUBLIC", + "grantor": "postgres", + "privileges": [ + { + "privilege_type": "U", + "privilege": true, + "with_grant": false + } + ] + } + ] + } + } + }, + { + "type": "alter", + "name": "alter ACL (change grantee)", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "typacl": { + "changed": [ + { + "grantee": "PUBLIC", + "grantor": "postgres", + "old_grantee": "postgres", + "privileges": [ + { + "privilege_type": "U", + "privilege": true, + "with_grant": false + } + ] + } + ] + } + }, + "expected_sql_file": "alter_acl_grantee_change.sql", + "expected_msql_file": "alter_acl_grantee_change_msql.sql" + }, + { + "type": "delete", + "name": "Drop RANGE type", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "data": {} + }, + { + "type": "create", + "name": "Create Multi RANGE type", + "endpoint": "NODE-type.obj", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql", + "data": { + "name": "range_type_$%{}[]()&*^!@\"'`\\/#", + "is_sys_type":false, + "typtype":"r", + "schema":"public", + "composite":[], + "enum":[], + "typacl":[], + "seclabels":[], + "typname":"bool", + "rngmultirangetype": "multirange_type" + }, + "expected_sql_file": "create_multi_range_type.sql", + "expected_msql_file": "create_multi_range_type.msql" + }, + { + "type": "delete", + "name": "Drop RANGE type", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "data": {} + } + ] +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/default/alter_enum_type_rename_enum.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/default/alter_enum_type_rename_enum.msql new file mode 100644 index 000000000..9ca45582c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/default/alter_enum_type_rename_enum.msql @@ -0,0 +1,4 @@ +ALTER TYPE public."enum_type_$%{}[]()&*^!@""'`\/#" + RENAME VALUE 'a' TO 'a_new'; +ALTER TYPE public."enum_type_$%{}[]()&*^!@""'`\/#" + RENAME VALUE 'd' TO 'd_new'; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/default/alter_enum_type_rename_enum.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/default/alter_enum_type_rename_enum.sql new file mode 100644 index 000000000..02f31566c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/default/alter_enum_type_rename_enum.sql @@ -0,0 +1,16 @@ +-- Type: enum_type_$%{}[]()&*^!@"'`\/# + +-- DROP TYPE IF EXISTS public."enum_type_$%{}[]()&*^!@""'`\/#"; + +CREATE TYPE public."enum_type_$%{}[]()&*^!@""'`\/#" AS ENUM + ('a_new', 'b', 'c', 'd_new'); + +ALTER TYPE public."enum_type_$%{}[]()&*^!@""'`\/#" + OWNER TO ; + +COMMENT ON TYPE public."enum_type_$%{}[]()&*^!@""'`\/#" + IS 'this is test'; + +GRANT USAGE ON TYPE public."enum_type_$%{}[]()&*^!@""'`\/#" TO PUBLIC; + +GRANT USAGE ON TYPE public."enum_type_$%{}[]()&*^!@""'`\/#" TO ; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/default/tests.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/default/tests.json index f44f970d3..31bd5dde5 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/default/tests.json +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/pg/default/tests.json @@ -45,6 +45,18 @@ "expected_sql_file": "alter_enum_type_acl_description.sql", "expected_msql_file": "alter_enum_type_acl_description.msql" }, + { + "type": "alter", + "name": "alter ENUM type and rename ENUM", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "enum": {"changed": [{"label": "a_new", "old_label": "a"}, {"label": "d_new", "old_label": "d"}]} + }, + "expected_sql_file": "alter_enum_type_rename_enum.sql", + "expected_msql_file": "alter_enum_type_rename_enum.msql" + }, { "type": "delete", "name": "Drop ENUM type", diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/alter_acl_grantee_change.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/alter_acl_grantee_change.sql new file mode 100644 index 000000000..91520ae0d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/alter_acl_grantee_change.sql @@ -0,0 +1,18 @@ +-- Type: range_type_$%{}[]()&*^!@"'`\/# + +-- DROP TYPE IF EXISTS public."range_type_$%{}[]()&*^!@""'`\/#"; + +CREATE TYPE public."range_type_$%{}[]()&*^!@""'`\/#" AS RANGE +( + SUBTYPE=bool, + SUBTYPE_OPCLASS = bool_ops, + MULTIRANGE_TYPE_NAME = multirange_type_$%{}[]()&*^!@"'`\/# +); + +ALTER TYPE public."range_type_$%{}[]()&*^!@""'`\/#" + OWNER TO ; + +COMMENT ON TYPE public."range_type_$%{}[]()&*^!@""'`\/#" + IS 'this is test'; + +GRANT USAGE ON TYPE public."range_type_$%{}[]()&*^!@""'`\/#" TO PUBLIC; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/alter_range_type_acl_description.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/alter_range_type_acl_description.sql new file mode 100644 index 000000000..094785d2f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/alter_range_type_acl_description.sql @@ -0,0 +1,20 @@ +-- Type: range_type_$%{}[]()&*^!@"'`\/# + +-- DROP TYPE IF EXISTS public."range_type_$%{}[]()&*^!@""'`\/#"; + +CREATE TYPE public."range_type_$%{}[]()&*^!@""'`\/#" AS RANGE +( + SUBTYPE=bool, + SUBTYPE_OPCLASS = bool_ops, + MULTIRANGE_TYPE_NAME = multirange_type_$%{}[]()&*^!@"'`\/# +); + +ALTER TYPE public."range_type_$%{}[]()&*^!@""'`\/#" + OWNER TO ; + +COMMENT ON TYPE public."range_type_$%{}[]()&*^!@""'`\/#" + IS 'this is test'; + +GRANT USAGE ON TYPE public."range_type_$%{}[]()&*^!@""'`\/#" TO PUBLIC; + +GRANT USAGE ON TYPE public."range_type_$%{}[]()&*^!@""'`\/#" TO ; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/create_multi_range_type.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/create_multi_range_type.msql new file mode 100644 index 000000000..6584c082a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/create_multi_range_type.msql @@ -0,0 +1,5 @@ +CREATE TYPE public."range_type_$%{}[]()&*^!@""'`\/#" AS RANGE +( + SUBTYPE=bool, + MULTIRANGE_TYPE_NAME = multirange_type +); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/create_multi_range_type.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/create_multi_range_type.sql new file mode 100644 index 000000000..718440994 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/create_multi_range_type.sql @@ -0,0 +1,13 @@ +-- Type: range_type_$%{}[]()&*^!@"'`\/# + +-- DROP TYPE IF EXISTS public."range_type_$%{}[]()&*^!@""'`\/#"; + +CREATE TYPE public."range_type_$%{}[]()&*^!@""'`\/#" AS RANGE +( + SUBTYPE=bool, + SUBTYPE_OPCLASS = bool_ops, + MULTIRANGE_TYPE_NAME = multirange_type +); + +ALTER TYPE public."range_type_$%{}[]()&*^!@""'`\/#" + OWNER TO ; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/create_range_type.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/create_range_type.sql new file mode 100644 index 000000000..ac57c8cbb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/create_range_type.sql @@ -0,0 +1,13 @@ +-- Type: range_type_$%{}[]()&*^!@"'`\/# + +-- DROP TYPE IF EXISTS public."range_type_$%{}[]()&*^!@""'`\/#"; + +CREATE TYPE public."range_type_$%{}[]()&*^!@""'`\/#" AS RANGE +( + SUBTYPE=bool, + SUBTYPE_OPCLASS = bool_ops, + MULTIRANGE_TYPE_NAME = multirange_type_$%{}[]()&*^!@"'`\/# +); + +ALTER TYPE public."range_type_$%{}[]()&*^!@""'`\/#" + OWNER TO ; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/tests.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/tests.json new file mode 100644 index 000000000..3fb712c93 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/14_plus/tests.json @@ -0,0 +1,324 @@ +{ + "scenarios": [ + { + "type": "create", + "name": "Create ENUM type", + "endpoint": "NODE-type.obj", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql", + "data": { + "name": "enum_type_$%{}[]()&*^!@\"'`\\/#", + "is_sys_type":false, + "typtype":"e", + "schema":"public", + "composite":[], + "enum":[{"label":"a"},{"label":"b"},{"label":"c"}], + "typacl":[], + "seclabels":[], + "description":"" + }, + "expected_sql_file": "create_enum_type.sql", + "expected_msql_file": "create_enum_type.msql" + }, + { + "type": "alter", + "name": "alter ENUM type and add new label", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "enum": {"added": [{"label": "d"}]} + }, + "expected_sql_file": "alter_enum_type_label.sql", + "expected_msql_file": "alter_enum_type_label.msql" + }, + { + "type": "alter", + "name": "alter ENUM type and add ACL, description", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "description":"this is test", + "typacl": {"added": [{"grantee":"PUBLIC","grantor":"enterprisedb","privileges":[{"privilege_type":"U","privilege":true,"with_grant":false}]}]} + }, + "expected_sql_file": "alter_enum_type_acl_description.sql", + "expected_msql_file": "alter_enum_type_acl_description.msql" + }, + { + "type": "alter", + "name": "alter ENUM type and rename ENUM", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "enum": {"changed": [{"label": "a_new", "old_label": "a"}, {"label": "d_new", "old_label": "d"}]} + }, + "expected_sql_file": "alter_enum_type_rename_enum.sql", + "expected_msql_file": "alter_enum_type_rename_enum.msql" + }, + { + "type": "delete", + "name": "Drop ENUM type", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "data": {} + }, + + + { + "type": "create", + "name": "Create Composite type", + "endpoint": "NODE-type.obj", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql", + "data": { + "name": "composite_type_$%{}[]()&*^!@\"'`\\/#", + "is_sys_type":false, + "typtype":"c", + "schema":"public", + "composite":[ + {"member_name":"mname1","type":"bigint","is_tlength":false,"is_precision":false}, + {"member_name":"mname2","type":"character varying","tlength":50,"is_tlength":true,"is_precision":false,"collation":"pg_catalog.\"C\"","min_val":1,"max_val":2147483647,"collspcname":""}, + {"member_name":"mname3","type":"text[]","is_tlength":false,"is_precision":false,"collation":"pg_catalog.\"C\"","collspcname":""} + ], + "typacl":[], + "seclabels":[], + "description":"" + }, + "expected_sql_file": "create_composite_type.sql", + "expected_msql_file": "create_composite_type.msql" + }, + { + "type": "alter", + "name": "alter Composite type and add new label", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "composite": {"added": [{"member_name":"mname4","type":"bigint","is_tlength":false,"is_precision":false}]} + }, + "expected_sql_file": "alter_composite_type_add_member.sql", + "expected_msql_file": "alter_composite_type_add_member.msql" + }, + { + "type": "alter", + "name": "alter Composite type and delete label", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "composite": {"deleted":[{"attnum":1,"member_name":"mname1","type":"bigint","tlength":null,"is_tlength":false,"precision":null,"is_precision":false,"collation":"","cltype":"bigint","hasSqrBracket":false,"fulltype":"bigint"}]} + }, + "expected_sql_file": "alter_composite_type_remove_member.sql", + "expected_msql_file": "alter_composite_type_remove_member.msql" + }, + { + "type": "alter", + "name": "alter Composite type and add ACL, description", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "description":"this is test", + "typacl": {"added": [{"grantee":"PUBLIC","grantor":"enterprisedb","privileges":[{"privilege_type":"U","privilege":true,"with_grant":false}]}]} + }, + "expected_sql_file": "alter_composite_type_acl_description.sql", + "expected_msql_file": "alter_composite_type_acl_description.msql" + }, + { + "type": "delete", + "name": "Drop Composite type", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "data": {} + }, + + { + "type": "create", + "name": "Create Composite type - one field", + "endpoint": "NODE-type.obj", + "sql_endpoint": "NODE-type.sql_id", + "data": { + "name": "composite_type_$%{}[]()&*^!@\"'`\\/#", + "is_sys_type":false, + "typtype":"c", + "schema":"public", + "composite":[ + {"member_name":"mname1","type":"bigint","is_tlength":false,"is_precision":false} + ], + "typacl":[], + "seclabels":[], + "description":"" + }, + "expected_sql_file": "create_composite_type_one_field.sql" + }, + { + "type": "delete", + "name": "Drop Composite type - one field", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "data": {} + }, + + { + "type": "create", + "name": "Create SHELL type", + "endpoint": "NODE-type.obj", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql", + "data": { + "name": "shell_type_$%{}[]()&*^!@\"'`\\/#", + "is_sys_type":false, + "typtype":"p", + "schema":"public", + "composite":[], + "enum":[], + "typacl":[], + "seclabels":[] + }, + "expected_sql_file": "create_shell_type.sql", + "expected_msql_file": "create_shell_type.msql" + }, + { + "type": "alter", + "name": "alter SHELL type and add description", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "description":"this is test" + }, + "expected_sql_file": "alter_shell_type_acl_description.sql", + "expected_msql_file": "alter_shell_type_acl_description.msql" + }, + { + "type": "delete", + "name": "Drop SHELL type", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "data": {} + }, + + + { + "type": "create", + "name": "Create RANGE type", + "endpoint": "NODE-type.obj", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql", + "data": { + "name": "range_type_$%{}[]()&*^!@\"'`\\/#", + "is_sys_type":false, + "typtype":"r", + "schema":"public", + "composite":[], + "enum":[], + "typacl":[], + "seclabels":[], + "typname":"bool" + }, + "expected_sql_file": "create_range_type.sql", + "expected_msql_file": "create_range_type.msql" + }, + { + "type": "alter", + "name": "alter RANGE type and add ACL, description", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "description":"this is test", + "typacl": {"added": [{"grantee":"PUBLIC","grantor":"enterprisedb","privileges":[{"privilege_type":"U","privilege":true,"with_grant":false}]}]} + }, + "expected_sql_file": "alter_range_type_acl_description.sql", + "expected_msql_file": "alter_range_type_acl_description.msql" + }, + { + "type": "alter", + "name": "alter ACL (Remove privilege)", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "typacl": { + "deleted": [ + { + "grantee": "PUBLIC", + "grantor": "enterprisedb", + "privileges": [ + { + "privilege_type": "U", + "privilege": true, + "with_grant": false + } + ] + } + ] + } + } + }, + { + "type": "alter", + "name": "alter ACL (change grantee)", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "typacl": { + "changed": [ + { + "grantee": "PUBLIC", + "grantor": "enterprisedb", + "old_grantee": "enterprisedb", + "privileges": [ + { + "privilege_type": "U", + "privilege": true, + "with_grant": false + } + ] + } + ] + } + }, + "expected_sql_file": "alter_acl_grantee_change.sql", + "expected_msql_file": "alter_acl_grantee_change_msql.sql" + }, + { + "type": "delete", + "name": "Drop RANGE type", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "data": {} + }, + { + "type": "create", + "name": "Create Multi RANGE type", + "endpoint": "NODE-type.obj", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql", + "data": { + "name": "range_type_$%{}[]()&*^!@\"'`\\/#", + "is_sys_type":false, + "typtype":"r", + "schema":"public", + "composite":[], + "enum":[], + "typacl":[], + "seclabels":[], + "typname":"bool", + "rngmultirangetype": "multirange_type" + }, + "expected_sql_file": "create_multi_range_type.sql", + "expected_msql_file": "create_multi_range_type.msql" + }, + { + "type": "delete", + "name": "Drop RANGE type", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "data": {} + } + ] +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/default/alter_enum_type_rename_enum.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/default/alter_enum_type_rename_enum.msql new file mode 100644 index 000000000..9ca45582c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/default/alter_enum_type_rename_enum.msql @@ -0,0 +1,4 @@ +ALTER TYPE public."enum_type_$%{}[]()&*^!@""'`\/#" + RENAME VALUE 'a' TO 'a_new'; +ALTER TYPE public."enum_type_$%{}[]()&*^!@""'`\/#" + RENAME VALUE 'd' TO 'd_new'; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/default/alter_enum_type_rename_enum.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/default/alter_enum_type_rename_enum.sql new file mode 100644 index 000000000..02f31566c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/default/alter_enum_type_rename_enum.sql @@ -0,0 +1,16 @@ +-- Type: enum_type_$%{}[]()&*^!@"'`\/# + +-- DROP TYPE IF EXISTS public."enum_type_$%{}[]()&*^!@""'`\/#"; + +CREATE TYPE public."enum_type_$%{}[]()&*^!@""'`\/#" AS ENUM + ('a_new', 'b', 'c', 'd_new'); + +ALTER TYPE public."enum_type_$%{}[]()&*^!@""'`\/#" + OWNER TO ; + +COMMENT ON TYPE public."enum_type_$%{}[]()&*^!@""'`\/#" + IS 'this is test'; + +GRANT USAGE ON TYPE public."enum_type_$%{}[]()&*^!@""'`\/#" TO PUBLIC; + +GRANT USAGE ON TYPE public."enum_type_$%{}[]()&*^!@""'`\/#" TO ; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/default/tests.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/default/tests.json index 94018e027..2aa4cbd34 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/default/tests.json +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/tests/ppas/default/tests.json @@ -45,6 +45,18 @@ "expected_sql_file": "alter_enum_type_acl_description.sql", "expected_msql_file": "alter_enum_type_acl_description.msql" }, + { + "type": "alter", + "name": "alter ENUM type and rename ENUM", + "endpoint": "NODE-type.obj_id", + "sql_endpoint": "NODE-type.sql_id", + "msql_endpoint": "NODE-type.msql_id", + "data": { + "enum": {"changed": [{"label": "a_new", "old_label": "a"}, {"label": "d_new", "old_label": "d"}]} + }, + "expected_sql_file": "alter_enum_type_rename_enum.sql", + "expected_msql_file": "alter_enum_type_rename_enum.msql" + }, { "type": "delete", "name": "Drop ENUM type", diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js index 3a6af0d9f..a574e768e 100644 --- a/web/pgadmin/browser/static/js/node.js +++ b/web/pgadmin/browser/static/js/node.js @@ -113,7 +113,7 @@ define('pgadmin.browser.node', [ if (self.node_initialized) return; self.node_initialized = true; - + pgAdmin.Browser.add_menus([{ name: 'refresh', node: self.type, @@ -122,7 +122,7 @@ define('pgadmin.browser.node', [ callback: 'refresh', priority: 2, label: gettext('Refresh...'), - shortcut_preference: ['browser', 'sub_menu_refresh'], + shortcut_preference: ['browser', 'sub_menu_refresh'], enable: true, }]); @@ -208,7 +208,7 @@ define('pgadmin.browser.node', [ label: gettext('Query Tool'), enable: enable, permission: AllPermissionTypes.TOOLS_QUERY_TOOL, - shortcut_preference: ['browser', 'sub_menu_query_tool'], + shortcut_preference: ['browser', 'sub_menu_query_tool'], }]); // show search objects same as query tool @@ -587,6 +587,11 @@ define('pgadmin.browser.node', [ }, 10); selectNextNode = false; } + if (obj.refreshParent?.(i)) { + obj.callbacks.refresh('refresh', t.parent(i)); + return; + } + pgBrowser.removeTreeNode(i, selectNextNode); } return true; diff --git a/web/pgadmin/static/js/tree/tree.js b/web/pgadmin/static/js/tree/tree.js index becb5093d..e848a7509 100644 --- a/web/pgadmin/static/js/tree/tree.js +++ b/web/pgadmin/static/js/tree/tree.js @@ -84,7 +84,7 @@ export class Tree { this.rootNode = manageTree.tempTree; this.Nodes = pgBrowser ? pgBrowser.Nodes : pgAdmin.Browser.Nodes; - // Flag to suppress added and opened tree event being called during object search operations, + // Flag to suppress added and opened tree event being called during object search operations, // tree.select of search object clashes with other tree.select. this.suppressEventsForPath = null; this.draggableTypes = {}; @@ -479,7 +479,13 @@ export class Tree { let node_cnt = 0; let result = {}; if (!identifier) return; - let item = TreeNode.prototype.isPrototypeOf(identifier) ? identifier : this.findNode(identifier.path); + let item; + try { + item = TreeNode.prototype.isPrototypeOf(identifier) ? identifier : this.findNode(identifier.path); + } catch { + // It is possible that the requested path is not present in the tree anymore + item = null; + } if (!item) return; do { const currentNodeData = item.getData();