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 6a46bb0..8c6b157 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 @@ -16,7 +16,7 @@ from pgadmin.utils.ajax import make_json_response, \ make_response as ajax_response, internal_server_error from pgadmin.browser.utils import PGChildNodeView from pgadmin.browser.server_groups.servers.databases.schemas.utils \ - import SchemaChildModule + import SchemaChildModule, DataTypeReader import pgadmin.browser.server_groups.servers.databases as database from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \ parse_priv_to_db @@ -87,7 +87,7 @@ class TypeModule(SchemaChildModule): blueprint = TypeModule(__name__) -class TypeView(PGChildNodeView): +class TypeView(PGChildNodeView, DataTypeReader): """ This class is responsible for generating routes for Type node @@ -335,7 +335,13 @@ class TypeView(PGChildNodeView): composite_lst = [] for row in rset['rows']: - typelist = ' '.join([row['attname'], row['typname']]) + # We will fetch Full type name + fulltype = self.get_full_type( + row['collnspname'], row['typname'], + row['isdup'], row['attndims'], row['atttypmod'] + ) + + typelist = ' '.join([row['attname'], fulltype]) if not row['collname'] or (row['collname'] == 'default' and row['collnspname'] == 'pg_catalog'): full_collate = '' @@ -349,19 +355,28 @@ class TypeView(PGChildNodeView): # Below logic will allow us to split length, precision from type name for grid import re - matchObj = re.match( r'(.*)\((.*?),(.*?)\)', row['typname']) + # If we have length & precision both + matchObj = re.search(r'(\d+),(\d+)', fulltype) if matchObj: - t_name = matchObj.group(1) - t_len = matchObj.group(2) - t_prec = matchObj.group(3) + t_len = matchObj.group(1) + t_prec = matchObj.group(2) else: - t_name = row['typname'] - t_len = None - t_prec = None + # If we have length only + matchObj = re.search(r'(\d+)', fulltype) + if matchObj: + t_len = matchObj.group(1) + t_prec = None + else: + t_len = None + t_prec = None + + is_tlength = True if t_len else False + is_precision = True if t_prec else False composite_lst.append({ - 'attnum':row['attnum'], 'member_name': row['attname'], 'type': t_name, 'collation': full_collate, - 'tlength': t_len, 'precision': t_prec }) + 'attnum':row['attnum'], 'member_name': row['attname'], 'type': row['typname'], 'collation': full_collate, + 'tlength': t_len, 'precision': t_prec, + 'is_tlength': is_tlength, 'is_precision': is_precision }) # Adding both results res['member_list'] = ', '.join(properties_list) @@ -442,7 +457,6 @@ class TypeView(PGChildNodeView): # it from properties sql copy_dict['typacl'] = [] - for row in acl['rows']: priv = parse_priv_from_db(row) if row['deftype'] in copy_dict: diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/js/type.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/js/type.js index 571e0d5..e0182a8 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/js/type.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/js/type.js @@ -308,23 +308,15 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) { group: '{{ _('Definition') }}', mode: ['edit', 'create'], select2: { width: "50%" }, - options: function() { - if(!this.model.isNew()) { + options: function(obj) { return [ {label: "Composite", value: "c"}, {label: "Enumeration", value: "e"}, {label: "External", value: "b"}, {label: "Range", value: "r"}, + {label: "Shell", value: "s"} ] - } else { - return [ - {label: "Composite", value: "c"}, - {label: "Enumeration", value: "e"}, - {label: "Range", value: "r"}, - ] - - } - }, + }, disabled: 'inSchemaWithModelCheck', // If create mode then by default open composite type control: Backform.Select2Control.extend({ @@ -345,10 +337,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) { canAdd: true, canEdit: true, canDelete: true, disabled: 'inSchema', deps: ['typtype'], deps: ['typtype'], visible: function(m) { - if (m.get('typtype') === 'c') { - return true; - } - return false; + return m.get('typtype') === 'c'; } },{ id: 'enum', label: '{{ _('Enumeration Type') }}', @@ -524,7 +513,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) { },{ type: 'nested', control: 'tab', group: '{{ _('Definition') }}', label: '{{ _('External Type') }}', deps: ['typtype'], - mode: ['edit'], + mode: ['create', 'edit'], visible: function(m) { return m.get('typtype') === 'b'; }, @@ -731,14 +720,23 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) { id: 'typacl', label: 'Privileges', type: 'collection', group: '{{ _('Security') }}', control: 'unique-col-collection', model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend({privileges: ['U']}), - mode: ['edit', 'create'], canAdd: true, canDelete: true, - uniqueCol : ['grantee'] + mode: ['edit', 'create'], canDelete: true, + uniqueCol : ['grantee'], deps: ['typtype'], + canAdd: function(m) { + // Do not allow to add when shell type is selected + return !(m.get('typtype') === 's'); + } },{ id: 'seclabels', label: '{{ _('Security Labels') }}', model: SecurityModel, editable: false, type: 'collection', group: '{{ _('Security') }}', mode: ['edit', 'create'], - min_version: 90100, canAdd: true, - canEdit: false, canDelete: true, control: 'unique-col-collection' + min_version: 90100, canEdit: false, canDelete: true, + control: 'unique-col-collection', deps: ['typtype'], + canAdd: function(m) { + // Do not allow to add when shell type is selected + return !(m.get('typtype') === 's'); + } + }], validate: function() { // Validation code for required fields diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/create.sql index bd1f8b7..35403b6 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/create.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/create.sql @@ -1,9 +1,13 @@ {% import 'macros/schemas/security.macros' as SECLABLE %} {% import 'macros/schemas/privilege.macros' as PRIVILEGE %} +{## If user selected shell type then just create type template ##} +{% if data and data.typtype == 's' %} +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 - ({% if data.composite %}{% for d in data.composite %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(d.member_name) }} {{ d.type }}{% if d.is_tlength and d.tlength %}({{d.tlength}}{% if d.is_precision and d.precision %},{{d.precision}}{% endif %}){% endif %}{% if d.collation %} COLLATE {{d.collation}}{% endif %}{% endfor %}{% endif %}); +({{"\n\t"}}{% if data.composite %}{% for d in data.composite %}{% if loop.index != 1 %},{{"\n\t"}}{% endif %}{{ conn|qtIdent(d.member_name) }} {{ d.type }}{% if d.is_tlength and d.tlength %}({{d.tlength}}{% if d.is_precision and d.precision %},{{d.precision}}{% endif %}){% endif %}{% if d.collation %} COLLATE {{d.collation}}{% endif %}{% endfor %}{% endif %}{{"\n"}}); {% endif %} {### Enum Type ###} {% if data and data.typtype == 'e' %} @@ -76,4 +80,4 @@ COMMENT ON TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% {{ SECLABLE.SET(conn, 'TYPE', data.name, r.provider, r.security_label, data.schema) }} {% endif %} {% endfor %} -{% endif %} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_external_functions.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_external_functions.sql index 86648a2..809efbf 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_external_functions.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_external_functions.sql @@ -1,16 +1,18 @@ {### Input/Output/Send/Receive/Analyze function list also append into TypModeIN/TypModOUT ###} {% if extfunc %} SELECT proname, nspname, - CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN + CASE WHEN (length(nspname) > 0 AND nspname != 'public') and length(proname) > 0 THEN concat(quote_ident(nspname), '.', quote_ident(proname)) + WHEN length(proname) > 0 THEN + quote_ident(proname) ELSE '' END AS func FROM ( SELECT proname, nspname, max(proargtypes[0]) AS arg0, max(proargtypes[1]) AS arg1 FROM pg_proc p JOIN pg_namespace n ON n.oid=pronamespace GROUP BY proname, nspname -HAVING count(proname) = 1 ) AS uniquefunc -WHERE arg0 <> 0 AND arg1 IS NULL; +HAVING count(proname) = 1) AS uniquefunc +WHERE arg0 <> 0 AND (arg1 IS NULL OR arg1 <> 0); {% endif %} {### TypmodIN list ###} {% if typemodin %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py index 129c19e..29c7936 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py @@ -13,6 +13,7 @@ from pgadmin.browser.collection import CollectionNodeModule from pgadmin.browser.utils import PGChildNodeView from flask import render_template from pgadmin.utils.ajax import internal_server_error +import json class SchemaChildModule(CollectionNodeModule): """ @@ -141,6 +142,97 @@ class DataTypeReader: return True, res + def get_full_type(self, nsp, typname, isDup, numdims, typmod): + """ + Returns full type name with Length and Precision. + + Args: + conn: Connection Object + condition: condition to restrict SQL statement + """ + needSchema = isDup + schema = nsp if nsp is not None else '' + name = '' + array = '' + length = '' + + # Above 7.4, format_type also sends the schema name if it's not included + # in the search_path, so we need to skip it in the typname + if typname.find(schema + '".') >= 0: + name = typname[len(schema)+3] + elif typname.find(schema + '.') >= 0: + name = typname[len(schema)+1] + else: + name = typname + + if name.startswith('_'): + if not numdims: + numdims = 1 + name = name[1:] + + if name.endswith('[]'): + if not numdims: + numdims = 1 + name = name[:-2] + + if name.startswith('"') and name.endswith('"'): + name = name[1:-1] + + if numdims > 0: + while numdims: + array += '[]' + numdims -= 1 + + if typmod != -1: + length = '(' + if name == 'numeric': + _len = (typmod - 4) >> 16; + _prec = (typmod - 4) & 0xffff; + length += str(_len) + if(_prec): + length += ',' + str(_prec) + elif name == 'time' or \ + name == 'timetz' or \ + name == 'time without time zone' or \ + name == 'time with time zone' or \ + name == 'timestamp' or \ + name == 'timestamptz'or \ + name == 'timestamp without time zone' or \ + name == 'timestamp with time zone' or \ + name == 'bit' or \ + name == 'bit varying' or \ + name == 'varbit': + _prec = 0 + _len = typmod + length += str(_len) + elif name == 'interval': + _prec = 0 + _len = typmod & 0xffff + length += str(_len) + elif name == 'date': + # Clear length + length = '' + else: + _len = typmod - 4 + _prec = 0 + length += str(_len) + + if len(length) > 0: + length += ')' + + if name == 'char' and schema == 'pg_catalog': + return '"char"' + array + elif name == 'time with time zone': + return 'time' + length + ' with time zone' + array + elif name == 'time without time zone': + return 'time' + length + ' without time zone' + array + elif name == 'timestamp with time zone': + return 'timestamp' + length + ' with time zone' + array + elif name == 'timestamp without time zone': + return 'timestamp' + length + ' without time zone' + array + else: + return name + length + array + def trigger_definition(data): """ @@ -246,4 +338,4 @@ def parse_rule_definition(res): res_data['condition'] = condition except Exception as e: return internal_server_error(errormsg=str(e)) - return res_data + return res_data \ No newline at end of file