public inbox for [email protected]
help / color / mirror / Atom feedPATCH: Added Node Type & Catalog objects [pgAdmin4]
26+ messages / 4 participants
[nested] [flat]
* PATCH: Added Node Type & Catalog objects [pgAdmin4]
@ 2016-03-08 13:38 Murtuza Zabuawala <[email protected]>
2016-03-10 19:41 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Ashesh Vashi <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
0 siblings, 2 replies; 26+ messages in thread
From: Murtuza Zabuawala @ 2016-03-08 13:38 UTC (permalink / raw)
To: pgadmin-hackers
Hi,
PFA patch to add new nodes in pgAdmin4.
1) Type node
2) Catalog objects
*Note:* Both above nodes depended on schema/catalog node, Please apply them
after latest patch of schema/catalog from Ashesh.
- Type node also depends on parse_priv_function_templates.patch (which I
sent in separate email today)
--
Regards,
Murtuza Zabuawala
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/octet-stream] type_node_v1.patch (129.9K, 3-type_node_v1.patch)
download | inline diff:
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
new file mode 100644
index 0000000..f9f962b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/__init__.py
@@ -0,0 +1,1170 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+""" Implements Type Node """
+
+import json
+from flask import render_template, make_response, request, jsonify
+from flask.ext.babel import gettext
+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 pgadmin.browser.server_groups.servers.databases as database
+from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
+ parse_priv_to_db
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class TypeModule(SchemaChildModule):
+ """
+ class TypeModule(SchemaChildModule)
+
+ A module class for Type node derived from SchemaChildModule
+
+ Methods:
+ -------
+ * __init__(*args, **kwargs)
+ - Method is used to initialize the Type and it's base module.
+
+ * get_nodes(gid, sid, did, scid, tid)
+ - Method is used to generate the browser collection node.
+
+ * node_inode()
+ - Method is overridden from its base class to make the node as leaf node.
+
+ * script_load()
+ - Load the module script for schema, when any of the server node is
+ initialized.
+ """
+
+ NODE_TYPE = 'type'
+ COLLECTION_LABEL = gettext("Types")
+
+ def __init__(self, *args, **kwargs):
+ """
+ Method is used to initialize the TypeModule and it's base module.
+
+ Args:
+ *args:
+ **kwargs:
+ """
+ super(TypeModule, self).__init__(*args, **kwargs)
+ self.min_ver = None
+ self.max_ver = None
+
+ def get_nodes(self, gid, sid, did, scid):
+ """
+ Generate the collection node
+ """
+ yield self.generate_browser_collection_node(scid)
+
+ @property
+ def script_load(self):
+ """
+ Load the module script for database, when any of the database node is
+ initialized.
+ """
+ return database.DatabaseModule.NODE_TYPE
+
+ @property
+ def node_inode(self):
+ """
+ Load the module node as a leaf node
+ """
+ return False
+
+ @property
+ def show_system_objects(self):
+ """
+ Flag to set show system Type
+ """
+ return True
+
+blueprint = TypeModule(__name__)
+
+
+class TypeView(PGChildNodeView):
+ """
+ This class is responsible for generating routes for Type node
+
+ Methods:
+ -------
+ * __init__(**kwargs)
+ - Method is used to initialize the TypeView and it's base view.
+
+ * check_precondition()
+ - This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+
+ * list()
+ - This function is used to list all the Type nodes within that
+ collection.
+
+ * nodes()
+ - This function will used to create all the child node within that
+ collection, Here it will create all the Type node.
+
+ * properties(gid, sid, did, scid, tid)
+ - This function will show the properties of the selected Type node
+
+ * create(gid, sid, did, scid)
+ - This function will create the new Type object
+
+ * update(gid, sid, did, scid, tid)
+ - This function will update the data for the selected Type node
+
+ * delete(self, gid, sid, scid, tid):
+ - This function will drop the Type object
+
+ * msql(gid, sid, did, scid, tid)
+ - This function is used to return modified SQL for the selected
+ Type node
+
+ * get_sql(data, scid, tid)
+ - This function will generate sql from model data
+
+ * sql(gid, sid, did, scid):
+ - This function will generate sql to show it in sql pane for the
+ selected Type node.
+
+ * dependency(gid, sid, did, scid, tid):
+ - This function will generate dependency list show it in dependency
+ pane for the selected Type node.
+
+ * dependent(gid, sid, did, scid, tid):
+ - This function will generate dependent list to show it in dependent
+ pane for the selected Type node.
+
+ * additional_properties(copy_dict, tid):
+ - This function will add additional properties in response
+
+ * get_collations(gid, sid, did, scid, tid):
+ - This function will return list of collation in ajax response
+
+ * get_types(gid, sid, did, scid, tid):
+ - This function will return list of types in ajax response
+
+ * get_subtypes(gid, sid, did, scid, tid):
+ - This function will return list of subtypes in ajax response
+
+ * get_subtype_opclass(gid, sid, did, scid, tid):
+ - This function will return list of subtype opclass in ajax response
+
+ * get_subtype_diff(gid, sid, did, scid, tid):
+ - This function will return list of subtype diff functions
+ in ajax response
+
+ * get_canonical(gid, sid, did, scid, tid):
+ - This function will return list of canonical functions
+ in ajax response
+
+ * get_external_functions_list(gid, sid, did, scid, tid):
+ - This function will return list of external functions
+ in ajax response
+ """
+
+ node_type = blueprint.node_type
+
+ parent_ids = [
+ {'type': 'int', 'id': 'gid'},
+ {'type': 'int', 'id': 'sid'},
+ {'type': 'int', 'id': 'did'},
+ {'type': 'int', 'id': 'scid'}
+ ]
+ ids = [
+ {'type': 'int', 'id': 'tid'}
+ ]
+
+ operations = dict({
+ 'obj': [
+ {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+ {'get': 'list', 'post': 'create'}
+ ],
+ 'delete': [{'delete': 'delete'}],
+ 'children': [{'get': 'children'}],
+ 'nodes': [{'get': 'node'}, {'get': 'nodes'}],
+ 'sql': [{'get': 'sql'}],
+ 'msql': [{'get': 'msql'}, {'get': 'msql'}],
+ 'stats': [{'get': 'statistics'}],
+ 'dependency': [{'get': 'dependencies'}],
+ 'dependent': [{'get': 'dependents'}],
+ 'module.js': [{}, {}, {'get': 'module_js'}],
+ 'get_types': [{'get': 'get_types'}, {'get': 'get_types'}],
+ 'get_stypes': [{'get': 'get_subtypes'}, {'get': 'get_subtypes'}],
+ 'get_subopclass': [{'get': 'get_subtype_opclass'},
+ {'get': 'get_subtype_opclass'}],
+ 'get_stypediff': [{'get': 'get_subtype_diff'}, {'get': 'get_subtype_diff'}],
+ 'get_canonical': [{'get': 'get_canonical'}, {'get': 'get_canonical'}],
+ 'get_collations': [{'get': 'get_collations'}, {'get': 'get_collations'}],
+ 'get_external_functions': [{'get': 'get_external_functions_list'},
+ {'get': 'get_external_functions_list'}]
+ })
+
+ def check_precondition(f):
+ """
+ This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+ """
+ @wraps(f)
+ def wrap(*args, **kwargs):
+ # Here args[0] will hold self & kwargs will hold gid,sid,did
+ self = args[0]
+ self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(kwargs['sid'])
+ self.conn = self.manager.connection(did=kwargs['did'])
+
+ # We need datlastsysoid to check if current type is system type
+ self.datlastsysoid = self.manager.db_info[kwargs['did']]['datlastsysoid']
+
+ # If DB not connected then return error to browser
+ if not self.conn.connected():
+ return precondition_required(
+ gettext(
+ "Connection to the server has been lost!"
+ )
+ )
+
+ # Declare allows acl on type
+ self.acl = ['U']
+
+ # we will set template path for sql scripts
+ ver = self.manager.version
+ if 90000 >= ver < 90100:
+ self.template_path = 'type/sql/pre_9.1'
+ else:
+ self.template_path = 'type/sql/9.1_plus'
+ return f(*args, **kwargs)
+
+ return wrap
+
+ @check_precondition
+ def list(self, gid, sid, did, scid):
+ """
+ This function is used to list all the schema nodes within that collection.
+
+ Args:
+ gid: Server group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of available schema nodes
+ """
+
+ SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid,
+ datlastsysoid=self.datlastsysoid)
+ status, res = self.conn.execute_dict(SQL)
+
+ if not status:
+ return internal_server_error(errormsg=res)
+ return ajax_response(
+ response=res['rows'],
+ status=200
+ )
+
+ @check_precondition
+ def nodes(self, gid, sid, did, scid):
+ """
+ This function will used to create all the child node within that collection.
+ Here it will create all the schema node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of available type child nodes
+ """
+
+ res = []
+ SQL = render_template("/".join([self.template_path,
+ 'nodes.sql']), scid=scid)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=rset)
+
+ for row in rset['rows']:
+ res.append(
+ self.blueprint.generate_browser_node(
+ row['oid'],
+ scid,
+ row['name'],
+ icon="icon-type"
+ ))
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ def additional_properties(self, copy_dict, tid):
+ """
+ We will use this function to add additional properties according to type
+
+ Returns:
+ additional properties for type like range/composite/enum
+
+ """
+ # Fetching type of type
+ of_type = copy_dict['typtype']
+ res = dict()
+ # If type is of Composite then we need to add members list in our output
+ if of_type == 'c':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='c',
+ typrelid=copy_dict['typrelid'])
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # To display in properties
+ properties_list = []
+ # To display in composite collection grid
+ composite_lst = []
+
+ for row in rset['rows']:
+ typelist = ' '.join([row['attname'], row['typname']])
+ if not row['collname'] or (row['collname'] == 'default'
+ and row['collnspname'] == 'pg_catalog'):
+ full_collate = ''
+ collate = ''
+ else:
+ full_collate = get_driver(PG_DEFAULT_DRIVER).qtIdent(
+ self.conn, row['collnspname'], row['collname'])
+ collate = ' COLLATE ' + full_collate
+ typelist += collate
+ properties_list.append(typelist)
+
+ # Below logic will allow us to split length, precision from type name for grid
+ import re
+ matchObj = re.match( r'(.*)\((.*?),(.*?)\)', row['typname'])
+ if matchObj:
+ t_name = matchObj.group(1)
+ t_len = matchObj.group(2)
+ t_prec = matchObj.group(3)
+ else:
+ t_name = row['typname']
+ t_len = None
+ t_prec = None
+
+ composite_lst.append({
+ 'member_name': row['attname'], 'type': t_name, 'collation': full_collate,
+ 'tlength': t_len, 'precision': t_prec })
+
+ # Adding both results
+ res['member_list'] = ','.join(properties_list)
+ res['composite'] = composite_lst
+
+ # If type is of ENUM then we need to add labels in our output
+ if of_type == 'e':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='e', tid=tid)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ # To display in properties
+ properties_list = []
+ # To display in enum grid
+ enum_list = []
+ for row in rset['rows']:
+ properties_list.append(row['enumlabel'])
+ enum_list.append({'label': row['enumlabel']})
+
+ # Adding both results in ouput
+ res['enum_list'] = ','.join(properties_list)
+ res['enum'] = enum_list
+
+ # If type is of Range then we need to add collation,subtype etc in our output
+ if of_type == 'r':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='r', tid=tid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ range_dict = dict(res['rows'][0])
+ res.update(range_dict)
+
+ # Returning only additional properties only
+ return res
+
+ @check_precondition
+ def properties(self, gid, sid, did, scid, tid):
+ """
+ This function will show the properties of the selected schema node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of selected schema node
+ """
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ copy_dict = dict(res['rows'][0])
+
+ # We need to parse & convert ACL coming from database to json format
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ copy_dict['typacl'] = []
+
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in copy_dict:
+ copy_dict[row['deftype']].append(priv)
+ else:
+ copy_dict[row['deftype']] = [priv]
+
+ # Calling function to check and additional properties if available
+ copy_dict.update(self.additional_properties(copy_dict, tid))
+
+ return ajax_response(
+ response=copy_dict,
+ status=200
+ )
+
+ @check_precondition
+ def get_collations(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of collation available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_collations.sql']))
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['collation'],
+ 'value': row['collation']}
+ )
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_types(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of types available
+ as AJAX response.
+ """
+ res = []
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_types.sql']))
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ # Attaching properties for precession
+ # & length validation for current type
+ precision = False
+ length = False
+ min_val = 0
+ max_val = 0
+
+ # Check against PGOID for specific type
+ if row['elemoid']:
+ if row['elemoid'] in (1560, 1561, 1562, 1563, 1042, 1043,
+ 1014, 1015):
+ typeval = 'L'
+ elif row['elemoid'] in (1083, 1114, 1115, 1183, 1184, 1185,
+ 1186, 1187, 1266, 1270):
+ typeval = 'D'
+ elif row['elemoid'] in (1231, 1700):
+ typeval = 'P'
+ else:
+ typeval = ' '
+
+ # Logic to set precision & length/min/max values
+ if typeval == 'P':
+ precision = True
+
+ if precision or typeval in ('L', 'D'):
+ length = True
+ min_val = 0 if typeval == 'D' else 1
+ if precision:
+ max_val = 1000
+ elif min_val:
+ # Max of integer value
+ max_val = 2147483647
+ else:
+ max_val = 10
+
+ res.append(
+ {'label': row['typname'], 'value': row['typname'],
+ 'typval': typeval, 'precision': precision,
+ 'length': length, 'min_val': min_val, 'max_val': max_val
+ }
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtypes(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtypes available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ subtype=True)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['stype'], 'value': row['stype'],
+ 'is_collate': row['is_collate']}
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtype_opclass(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtype opclass available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ subtype_opclass=True, data=data)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['opcname'],
+ 'value': row['opcname']})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtype_diff(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtypes diff functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ get_opcintype=True, data=data)
+ if SQL:
+ status, opcintype = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=opcintype)
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ opcintype=opcintype, conn=self.conn)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['stypdiff'],
+ 'value': row['stypdiff']}
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_canonical(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of canonical functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+ canonical = True
+
+ try:
+ # We want to send data only if in we are in edit mode
+ # else we will disable the combobox
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ getoid=True, data=data)
+ if SQL:
+ status, oid = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=oid)
+ # If oid is None then do not run SQL
+ if oid is None:
+ canonical = False
+
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ canonical=canonical, conn=self.conn, oid=oid)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['canonical'],
+ 'value': row['canonical']})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def get_external_functions_list(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of external functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': '', 'cbtype': 'all'}]
+
+ try:
+ # The SQL generated below will populate Input/Output/Send/
+ # Receive/Analyze/TypModeIN/TypModOUT combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ extfunc=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'all'})
+
+ # The SQL generated below will populate TypModeIN combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ typemodin=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'typmodin'})
+
+ # The SQL generated below will populate TypModeIN combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ typemodout=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'typmodout'})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def create(self, gid, sid, did, scid):
+ """
+ This function will creates new the schema object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ data = request.form if request.form else json.loads(request.data.decode())
+ required_args = {
+ 'name': 'Name',
+ 'typtype': 'Type'
+ }
+
+ for arg in required_args:
+ if arg not in data:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ "Couldn't find the required parameter (%s)." %
+ required_args[arg]
+ )
+ )
+ # Additional checks goes here
+ # If type is composite then check if it has two members
+ if data and data[arg] == 'c':
+ if len(data['composite']) < 2:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Composite type requires at least two members'
+ )
+ )
+ # If type is enum then check if it has minimum one label
+ if data and data[arg] == 'e':
+ if len(data['enum']) < 1:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Enumeration type requires at least one label'
+ )
+ )
+ # If type is enum then check if it has minimum one label
+ if data and data[arg] == 'r':
+ if data['typname'] is None:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Subtype must be defined for range type'
+ )
+ )
+ # If type is external then check if input/output
+ # conversion function is defined
+ if data and data[arg] == 'x':
+ if data['typinput'] is None or \
+ data['typoutput'] is None:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'External type requires both Input & Output conversion function.'
+ )
+ )
+
+ # To format privileges coming from client
+ if 'typacl' in data and data['typacl'] is not None:
+ data['typacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ try:
+ SQL = render_template("/".join([self.template_path, 'create.sql']),
+ data=data, conn=self.conn)
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # we need oid to to add object in tree at browser
+ SQL = render_template("/".join([self.template_path,
+ 'get_oid.sql']),
+ scid=scid, data=data)
+ status, tid = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=tid)
+
+ return jsonify(
+ node=self.blueprint.generate_browser_node(
+ tid,
+ scid,
+ data['name'],
+ icon="icon-type"
+ )
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def update(self, gid, sid, did, scid, tid):
+ """
+ This function will updates existing the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+
+ data = request.form if request.form else json.loads(request.data.decode())
+ try:
+ SQL = self.get_sql(gid, sid, data, scid, tid)
+ if SQL and SQL.strip('\n') and SQL.strip(' '):
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return make_json_response(
+ success=1,
+ info="Type updated",
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+ else:
+ return make_json_response(
+ success=1,
+ info="Nothing to update",
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def delete(self, gid, sid, did, scid, tid):
+ """
+ This function will updates existing the schema object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+
+ # Below will decide if it's simple drop or drop with cascade call
+ if self.cmd == 'delete':
+ # This is a cascade operation
+ cascade = True
+ else:
+ cascade = False
+
+ try:
+ SQL = render_template("/".join([self.template_path, 'get_name.sql']),
+ scid=scid, tid=tid)
+ status, name = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=name)
+
+ SQL = render_template("/".join([self.template_path, 'delete.sql']),
+ name=name, cascade=cascade, conn=self.conn)
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return make_json_response(
+ success=1,
+ info=gettext("Type dropped"),
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def msql(self, gid, sid, did, scid, tid=None):
+ """
+ This function will generates modified sql for type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ req = request.args
+ data = dict()
+
+ # converting nested request data in proper json format
+ for key,val in req.items():
+ if key in ['composite', 'enum', 'seclabels', 'typacl']:
+ data[key] = json.loads(val)
+ else:
+ data[key] = val
+
+ try:
+ SQL = self.get_sql(gid, sid, data, scid, tid)
+
+ if SQL and SQL.strip('\n') and SQL.strip(' '):
+ return make_json_response(
+ data=SQL,
+ status=200
+ )
+ except Exception as e:
+ internal_server_error(errormsg=str(e))
+
+ def get_sql(self, gid, sid, data, scid, tid=None):
+ """
+ This function will genrate sql from model data
+ """
+ if tid is not None:
+
+ for key in ['typacl']:
+ if key in data and data[key] is not None:
+ if 'added' in data[key]:
+ data[key]['added'] = parse_priv_to_db(data[key]['added'], self.acl)
+ if 'changed' in data[key]:
+ data[key]['changed'] = parse_priv_to_db(data[key]['changed'], self.acl)
+ if 'deleted' in data[key]:
+ data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], self.acl)
+
+ SQL = render_template("/".join([self.template_path, 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ old_data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ old_data['typacl'] = []
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in old_data:
+ old_data[row['deftype']].append(priv)
+ else:
+ old_data[row['deftype']] = [priv]
+
+ # Calling function to check and additional properties if available
+ old_data.update(self.additional_properties(old_data, tid))
+
+ SQL = render_template(
+ "/".join([self.template_path, 'update.sql']),
+ data=data, o_data=old_data, conn=self.conn
+ )
+ else:
+ required_args = [
+ 'name',
+ 'typtype'
+ ]
+
+ for arg in required_args:
+ if arg not in data:
+ return " --definition incomplete"
+
+ # Privileges
+ if 'typacl' in data and data['typacl'] is not None:
+ data['typacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ SQL = render_template("/".join([self.template_path,
+ 'create.sql']),
+ data=data, conn=self.conn)
+
+ return SQL
+
+
+ @check_precondition
+ def sql(self, gid, sid, did, scid, tid):
+ """
+ This function will generates reverse engineered sql for schema object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ SQL = render_template("/".join([self.template_path, 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ data['typacl'] = []
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in data:
+ data[row['deftype']].append(priv)
+ else:
+ data[row['deftype']] = [priv]
+
+ # Privileges
+ if 'typacl' in data and data['typacl'] is not None:
+ data['nspacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ # Calling function to check and additional properties if available
+ data.update(self.additional_properties(data, tid))
+
+ # We do not want to display table which has '-' value
+ # setting them to None so that jinja avoid displaying them
+ for k in data:
+ if data[k] == '-':
+ data[k] = None
+
+ SQL = self.get_sql(gid, sid, data, scid, tid=None)
+
+ # We are appending headers here for sql panel
+ sql_header = "-- Type: {0}\n\n-- ".format(data['name'])
+ sql_header += render_template("/".join([self.template_path,
+ 'delete.sql']),
+ name=data['name'], conn=self.conn)
+ SQL = sql_header + '\n' + SQL
+
+ return ajax_response(response=SQL)
+
+ @check_precondition
+ def dependents(self, gid, sid, did, scid, tid):
+ """
+ This function get the dependents and return ajax response
+ for the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ dependents_result = self.get_dependents(
+ self.conn, tid
+ )
+
+ return ajax_response(
+ response=dependents_result,
+ status=200
+ )
+
+ @check_precondition
+ def dependencies(self, gid, sid, did, scid, tid):
+ """
+ This function get the dependencies and return ajax response
+ for the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ dependencies_result = self.get_dependencies(
+ self.conn, tid
+ )
+
+ return ajax_response(
+ response=dependencies_result,
+ status=200
+ )
+
+TypeView.register_node_view(blueprint)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/coll-type.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/coll-type.png
new file mode 100644
index 0000000000000000000000000000000000000000..fb020d7d99f84439046d288615e865ee1fbdb815
GIT binary patch
literal 329
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=3NcXI=aM>fHaQT@Q91
z`hS1R{~OExU!3;;MEm~(mH&6<{olN3-cDJdI>wS9zhDN3XE)M-9L@rd$YLPv0mg18
zv+aP47*7|+5RU7%XQO!=40zlgo^wo$EU!My#3j(ks*}LT9dUr^nFV*x`<LfEXLU&{
zNT_QZ){p11jr8BRJf*J9t1q$D%X6FK(q}uIk4`OV_&v+tAZF)<c~_)!|NM{V=e#WX
zN#J9vG0+~>64!{5l*E!$tK_0oAjM#0U}&IgXryak7-D2#Wnye)VybOmYGq(B@15Q%
q6b-rgDVb@N5Df;FU=2XkCRPS!5DllMhpqu?VDNPHb6Mw<&;$UZ$8*O3
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/type.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/type.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c16764e7d08c56922a97a2f0c6cc06455c86a56
GIT binary patch
literal 325
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv5AX?b1=8~#9EmzT>)QWU=l(xE
z`v2js|F_rtzdY~%nF;@oHvQjQ{(pPk|IMk)0@*;Nj3q&S!3+-1ZlnP@oCO|{#X#Bv
zjNMLV+W{G&o-U3d9M_W*4zM_RIV|8v(U4|t6r8Z|5fh7_LtB=H01KmJR;I%QmXsCK
znH_-=7a3W69onAxD9m6<$ym$O<m%A&El=SFOUjEmj7`o4+9Dfn@G_j<Bfar~Z-)!e
z0@V`Nh?11Vl2ohYqEsNoU}RuuplfKPYhV~+WME}tY-M7qZD49;U@-5U-YOIgx%nxX
kX_XKS29{tAK-DHz24)Zqr>2Ll0cv3IboFyt=akR{01h5&qyPW_
literal 0
HcmV?d00001
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
new file mode 100644
index 0000000..f90e9eb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/js/type.js
@@ -0,0 +1,841 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'pgadmin',
+ 'pgadmin.browser', 'alertify', 'backgrid', 'pgadmin.backgrid',
+ 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
+
+ if (!pgBrowser.Nodes['coll-type']) {
+ var databases = pgAdmin.Browser.Nodes['coll-type'] =
+ pgAdmin.Browser.Collection.extend({
+ node: 'type',
+ label: '{{ _('Types') }}',
+ type: 'coll-type',
+ columns: ['name', 'oid', 'description']
+ });
+ };
+
+ // Switch options to save space in model's field
+ var switchOptions = {
+ 'onText': 'Yes', 'offText': 'No',
+ 'onColor': 'success', 'offColor': 'default',
+ 'size': 'small'
+ };
+
+ // Security label model declaration
+ var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ provider: null,
+ security_label: null
+ },
+ schema: [{
+ id: 'provider', label: '{{ _('Provider') }}',
+ type: 'text', disabled: false
+ },{
+ id: 'security_label', label: '{{ _('Security Label') }}',
+ type: 'text', disabled: false
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+
+ if (_.isUndefined(data.label) ||
+ _.isNull(data.label) ||
+ String(data.label).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = _("Please specify the value for all the security providers.");
+ this.errorModel.set('security_label', errmsg);
+ } else {
+ this.errorModel.unset('security_label');
+ }
+ return null;
+ }
+ });
+
+ // Composite type model declaration
+ var CompositeModel = Backform.CompositeModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ member_name: undefined,
+ type: undefined,
+ tlength: undefined,
+ is_tlength: false,
+ precision: undefined,
+ is_precision: false,
+ collation: undefined,
+ min_val: undefined,
+ max_val: undefined,
+ },
+ type_options: undefined,
+ subtypes: undefined,
+ schema: [{
+ id: 'member_name', label: '{{ _('Member Name') }}',
+ type: 'text', disabled: false, editable: false
+ },{
+ id: 'type', label: '{{ _('Type') }}', control: 'node-ajax-options',
+ type: 'text', url: 'get_types', disabled: false, node: 'type',
+ editable: false,
+ transform: function(d){
+ this.model.type_options = d;
+ return d;
+ }
+ },{
+ id: 'tlength', label: '{{ _('Length') }}', deps: ['type'], type: 'text',
+ editable: false,
+ disabled: function(m) {
+ // We will store type from selected from combobox
+ var of_type = m.get('type');
+ if(m.type_options) {
+ // iterating over all the types
+ _.each(m.type_options, function(o) {
+ // if type from selected from combobox matches in options
+ if ( of_type == o.value ) {
+ // if length is allowed for selected type
+ if(o.length)
+ {
+ // set the values in model
+ m.set('is_tlength', true, {silent: true});
+ m.set('min_val', o.min_val, {silent: true});
+ m.set('max_val', o.max_val, {silent: true});
+ }
+ }
+ });
+ }
+ return !m.get('is_tlength');
+ }
+ },{
+ id: 'precision', label: '{{ _('Precision') }}', deps: ['type'],
+ type: 'text', editable: false,
+ disabled: function(m) {
+ // We will store type from selected from combobox
+ var of_type = m.get('type');
+ if(m.type_options) {
+ // iterating over all the types
+ _.each(m.type_options, function(o) {
+ // if type from selected from combobox matches in options
+ if ( of_type == o.value ) {
+ // if precession is allowed for selected type
+ if(o.precision)
+ {
+ // set the values in model
+ m.set('is_precision', true, {silent: true});
+ m.set('min_val', o.min_val, {silent: true});
+ m.set('max_val', o.max_val, {silent: true});
+ }
+ }
+ });
+ }
+ return !m.get('is_precision');
+ }
+ },{
+ id: 'collation', label: '{{ _('Collation') }}',
+ control: 'node-ajax-options', editable: false,
+ type: 'text', disabled: false, url: 'get_collations', node: 'type'
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+ // Validation for member name
+ if (_.isUndefined(data.member_name) ||
+ _.isNull(data.member_name) ||
+ String(data.member_name).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = _("Please specify the value for member name.")
+ this.errorModel.set('member_name', errmsg)
+ return errmsg;
+ } else {
+ this.errorModel.unset('member_name');
+ }
+
+ // Validation for Length field
+ if (data.is_tlength && !_.isUndefined(data.tlength)) {
+ if (data.tlength < data.min_val )
+ errmsg = _("Length should not be less than " + data.min_val)
+ if (data.tlength > data.max_val )
+ errmsg = _("Length should not be greater than " + data.max_val)
+ // If we have any error set then throw it to user
+ if(errmsg) {
+ this.errorModel.set('tlength', errmsg)
+ return errmsg;
+ }
+ } else {
+ this.errorModel.unset('tlength');
+ }
+
+ // Validation for precision field
+ if (data.is_precision && !_.isUndefined(data.precision)) {
+ if (data.precision < data.min_val )
+ errmsg = _("Precision should not be less than " + data.min_val)
+ if (data.precision > data.max_val )
+ errmsg = _("Precision should not be greater than " + data.max_val)
+ // If we have any error set then throw it to user
+ if(errmsg) {
+ this.errorModel.set('precision', errmsg)
+ return errmsg;
+ }
+ } else {
+ this.errorModel.unset('precision');
+ }
+
+ return null;
+ }
+ });
+
+ var EnumModel = Backform.EnumModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ label: null,
+ },
+ schema: [{
+ id: 'label', label: '{{ _('Label') }}',type: 'text', disabled: false,
+ cellHeaderClasses: 'width_percent_99'
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+
+ if (_.isUndefined(data.label) ||
+ _.isNull(data.label) ||
+ String(data.label).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = _("Please specify the value for label.");
+ this.errorModel.set('label', errmsg)
+ return errmsg;
+ } else {
+ this.errorModel.unset('label');
+ }
+
+
+ return null;
+ }
+ });
+
+ if (!pgBrowser.Nodes['type']) {
+ pgAdmin.Browser.Nodes['type'] = pgBrowser.Node.extend({
+ type: 'type',
+ label: '{{ _('Type') }}',
+ collection_type: 'coll-type',
+ hasSQL: true,
+ hasDepends: true,
+ parent_type: ['schema', 'catalog'],
+ Init: function() {
+ /* Avoid mulitple registration of menus */
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+
+ pgBrowser.add_menus([{
+ name: 'create_type_on_coll', node: 'coll-type', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_type', node: 'type', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_type', node: 'schema', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: false},
+ enable: 'canCreate'
+ }
+ ]);
+
+ },
+ canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+ canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+ ext_funcs: undefined,
+ model: pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ name: undefined,
+ oid: undefined,
+ is_sys_type: false,
+ typtype: undefined
+ },
+ schema: [{
+ id: 'name', label: '{{ _('Name') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchema'
+ },{
+ id: 'oid', label:'{{ _('Oid') }}', cell: 'string',
+ type: 'text' , mode: ['properties', 'edit'], disabled: true
+ },{
+ id: 'typeowner', label:'{{ _('Owner') }}', cell: 'string',
+ control: 'node-list-by-name',
+ type: 'text', mode: ['properties', 'create', 'edit'], node: 'role',
+ disabled: 'inSchema'
+ },{
+ id: 'schema', label:'{{ _('Schema') }}', cell: 'string',
+ type: 'text', mode: ['create', 'edit'], node: 'schema',
+ disabled: 'inSchema', filter: function(d) {
+ // If schema name start with pg_* then we need to exclude them
+ if(d && d.label.match(/^pg_/))
+ {
+ return false;
+ }
+ return true;
+ },
+ control: Backform.NodeListByNameControl.extend({
+ render: function(){
+ // Initialize parent's render method
+ Backform.NodeListByNameControl.prototype.render.apply(this, arguments);
+
+ // Set schema default value to its parent Schema
+ if(this.model.isNew()){
+ this.model.set({'schema': this.model.node_info.schema.label});
+ }
+ return this;
+ }
+ })
+ },{
+ id: 'typtype', label:'{{ _('Type') }}',
+ mode: ['create','edit'], disabled: 'inSchemaWithModelCheck',
+ group: '{{ _('Type Definition') }}',
+ mode: ['edit', 'create'],
+ select2: { width: "50%" },
+ options: [
+ {label: "Composite", value: "c"},
+ {label: "Enumration", value: "e"},
+ {label: "External", value: "x"},
+ {label: "Range", value: "r"},
+ ],
+ disabled: 'inSchemaWithModelCheck',
+ // If create mode then by default open composite type
+ control: Backform.Select2Control.extend({
+ render: function(){
+ // Initialize parent's render method
+ Backform.Select2Control.prototype.render.apply(this, arguments);
+ if(this.model.isNew()) {
+ this.model.set({'typtype': 'c'});
+ }
+ return this;
+ }
+ })
+ },{
+ id: 'composite', label: '{{ _('Composite Type') }}',
+ model: CompositeModel, editable: true, type: 'collection',
+ group: '{{ _('Type Definition') }}', mode: ['edit', 'create'],
+ control: 'unique-col-collection', uniqueCol : ['member_name'],
+ canAdd: true, canEdit: true, canDelete: true, disabled: 'inSchema',
+ deps: ['typtype'], deps: ['typtype'],
+ visible: function(m) {
+ if (m.get('typtype') === 'c') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'enum', label: '{{ _('Enumration Type') }}',
+ model: EnumModel, editable: true, type: 'collection',
+ group: '{{ _('Type Definition') }}', mode: ['edit', 'create'],
+ canAdd: true, canEdit: false, canDelete: function(m) {
+ // We will disable it if it's in 'edit' mode
+ if (m.isNew()) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ disabled: 'inSchema', deps: ['typtype'],
+ control: 'unique-col-collection', uniqueCol : ['label'],
+ visible: function(m) {
+ return m.get('typtype') === 'e';
+ }
+ },{
+ // We will disable range type control in edit mode
+ type: 'nested', control: 'fieldset', group: '{{ _('Type Definition') }}',
+ mode: ['edit', 'create'],
+ visible: function(m) {
+ return m.get('typtype') === 'r';
+ }, deps: ['typtype'], label: '{{ _('Range Type') }}',
+ schema:[{
+ id: 'typname', label:'{{ _('Subtype') }}', cell: 'string',
+ control: 'node-ajax-options',
+ url: 'get_stypes', type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}', disabled: 'inSchemaWithModelCheck',
+ transform: function(d){
+ this.model.subtypes = d;
+ return d;
+ }
+ },{
+ id: 'opcname', label:'{{ _('Subtype OpClass') }}', cell: 'string',
+ mode: ['properties', 'create', 'edit'], group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['typname'],
+ control: 'select', options: function() {
+ var l_typname = this.model.get('typname'),
+ self = this,
+ result = [];
+ if(!_.isUndefined(l_typname) && l_typname != '')
+ {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_subopclass', this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {'typname' : l_typname},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error', self.model, self.field);
+ }
+ });
+ //
+ }
+ return result;
+ }
+ },{
+ id: 'collname', label:'{{ _('Collation') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ deps: ['typname'], control: 'node-ajax-options', url: 'get_collations',
+ disabled: function(m) {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+
+ // Disbale in edit mode
+ if (!m.isNew()) {
+ return true;
+ }
+
+ // To check if collation is allowed?
+ var of_subtype = m.get('typname'),
+ is_collate = undefined;
+ if(!_.isUndefined(of_subtype)) {
+ // iterating over all the types
+ _.each(m.subtypes, function(s) {
+ // if subtype from selected from combobox matches
+ if ( of_subtype === s.label ) {
+ // if collation is allowed for selected subtype
+ // then enable it else disable it
+ is_collate = s.is_collate;
+ }
+ });
+ }
+ // If is_collate is true then do not disable
+ return is_collate ? false : true;
+ }
+ },{
+ id: 'rngcanonical', label:'{{ _('Canonical') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['name', 'typname'],
+ control: 'select', options: function() {
+ var name = this.model.get('name'),
+ self = this,
+ result = [];
+
+ if(!_.isUndefined(name) && name != '')
+ {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_canonical', this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {"name" : name},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error',
+ self.model, self.field);
+ }
+ });
+ }
+ return result;
+ }
+ },{
+ id: 'rngsubdiff', label:'{{ _('SubType diff') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['opcname'],
+ control: 'select', options: function() {
+ var l_typname = this.model.get('typname'),
+ l_opcname = this.model.get('opcname'),
+ self = this,
+ result = [];
+
+ if(!_.isUndefined(l_typname) && l_typname != '' &&
+ !_.isUndefined(l_opcname) && l_opcname != '') {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_stypediff',
+ this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {'typname' : l_typname, 'opcname': l_opcname},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error',
+ self.model, self.field);
+ }
+ });
+ }
+ return result;
+ }
+ }]
+ },{
+ type: 'nested', control: 'tab', group: '{{ _('Type Definition') }}',
+ label: '{{ _('External Type') }}', deps: ['typtype'],
+ mode: ['create', 'edit'],
+ visible: function(m) {
+ return m.get('typtype') === 'x';
+ },
+ schema:[{
+ id: 'typinput', label:'{{ _('Input function') }}',
+ cell: 'string',type: 'text',
+ mode: ['properties', 'create', 'edit'], group: 'Required',
+ disabled: 'inSchemaWithModelCheck',
+ control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo'
+ },{
+ id: 'typoutput', label:'{{ _('Output function') }}',
+ cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Required',
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo'
+ },{
+ id: 'typreceive', label:'{{ _('Receive function') }}',
+ cell: 'string', type: 'text', group: 'Optional-1',
+ mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo'
+ },{
+ id: 'typsend', label:'{{ _('Send function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo'
+ },{
+ id: 'typmodin', label:'{{ _('Typmod in function') }}',
+ cell: 'string', type: 'text',
+ mode: ['properties', 'create', 'edit'], group: 'Optional-1',
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: function(d) {
+ var result = [{label :"", value : ""}];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype === 'typmodin' || item.cbtype === 'all') {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ },{
+ id: 'typmodout', label:'{{ _('Typmod out function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: function(d) {
+ var result = [{label :"", value : ""}];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype === 'typmodout' || item.cbtype === 'all') {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ },{
+ id: 'typlen', label:'{{ _('Internal length') }}',
+ cell: 'integer', group: 'Optional-1',
+ type: 'int', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'variable', label:'{{ _('Variable') }}', cell: 'switch',
+ group: 'Optional-1', type: 'switch',
+ mode: ['create','edit'], options: switchOptions,
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typdefault', label:'{{ _('Default') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typanalyze', label:'{{ _('Analyze function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo'
+ },{
+ id: 'typcategory', label:'{{ _('Category') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { placeholder: "Select category", allowClear: true,
+ width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label :"Array types", value : "A"},
+ {label :"Boolean types", value : "B"},
+ {label :"Composite types", value : "C"},
+ {label :"Date/time types", value : "D"},
+ {label :"Enum types", value : "E"},
+ {label :"Geometric types", value : "G"},
+ {label :"Network address types", value : "I"},
+ {label :"Numeric types", value : "N"},
+ {label :"Pseudo-types", value : "P"},
+ {label :"String types", value : "S"},
+ {label :"Timespan types", value : "T"},
+ {label :"User-defined types", value : "U"},
+ {label :"Bit-string types", value : "V"},
+ {label :"unknown type", value : "X"}
+ ]
+ },{
+ id: 'typispreferred', label:'{{ _('Prefered') }}', cell: 'switch',
+ type: 'switch', mode: ['properties', 'create','edit'],
+ options: switchOptions, disabled: 'inSchemaWithModelCheck',
+ group: 'Optional-1'
+ },{
+ id: 'element', label:'{{ _('Element') }}', cell: 'string',
+ control: 'node-ajax-options', group: 'Optional-2',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', url: 'get_types'
+ },{
+ id: 'typdelim', label:'{{ _('Delimiter') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Optional-2', disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typalign', label:'{{ _('Alignment') }}',
+ cell: 'string', group: 'Optional-2',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { placeholder: "Select alignment", allowClear: true,
+ width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label: "char", value: "c"},
+ {label: "int2", value: "s"},
+ {label: "in4", value: "i"},
+ {label: "double", value: "d"},
+ ]
+ },{
+ id: 'typstorage', label:'{{ _('Storage') }}',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Optional-2', cell: 'string',
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { placeholder: "Select storage", allowClear: true,
+ width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label: "PLAIN", value: "p"},
+ {label: "MAIN", value: "e"},
+ {label: "EXTERNAL", value: "m"},
+ {label: "EXTENDED", value: "x"},
+ ]
+ },{
+ id: 'typbyval', label:'{{ _('Passed by Value?') }}',
+ cell: 'switch', options: switchOptions,
+ type: 'switch', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', group: 'Optional-2',
+ },{
+ id: 'is_collatable', label:'{{ _('Collatable?') }}',
+ cell: 'switch', min_version: 90100, group: 'Optional-2',
+ type: 'switch', mode: ['properties', 'create', 'edit'],
+ options: switchOptions, disabled: 'inSchemaWithModelCheck'
+ // End of extension tab
+ }]
+ },{
+ id: 'alias', label:'{{ _('Alias') }}', cell: 'string',
+ type: 'text', mode: ['properties'],
+ disabled: 'inSchema'
+ },{
+ id: 'type_acl', label:'{{ _('ACL') }}', cell: 'string',
+ type: 'text', mode: ['properties'],
+ disabled: 'inSchema'
+ },{
+ id: 'member_list', label:'{{ _('Members') }}', cell: 'string',
+ type: 'text', mode: ['properties'],
+ disabled: 'inSchema', visible: function(m) {
+ if(m.get('typtype') === 'c') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'enum_list', label:'{{ _('Labels') }}', cell: 'string',
+ type: 'text', mode: ['properties'],
+ disabled: 'inSchema', visible: function(m) {
+ if(m.get('typtype') === 'e') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'is_sys_type', label:'{{ _('System type?') }}', cell: 'switch',
+ type: 'switch', mode: ['properties'], options: switchOptions,
+ disabled: 'inSchema'
+ },{
+ id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+ type: 'multiline', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchema'
+ },{
+ 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'],
+ columns: ['grantee', 'grantor', 'privileges']
+ },{
+ id: 'seclabels', label: '{{ _('Security Labels') }}',
+ model: SecurityModel, editable: false, type: 'collection',
+ group: '{{ _('Security') }}', mode: ['edit', 'create'],
+ min_version: 90200, canAdd: true,
+ canEdit: false, canDelete: true, control: 'unique-col-collection'
+ }],
+ validate: function() {
+ // Validation code for required fields
+ var changedAttrs = this.sessAttrs,
+ msg = undefined;
+
+ if (_.has(changedAttrs, 'name') &&
+ (_.isUndefined(this.get('name'))
+ || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Name can not be empty!') }}';
+ this.errorModel.set('name', msg);
+ } else if (_.has(changedAttrs, 'schema') &&
+ (_.isUndefined(this.get('schema'))
+ || String(this.get('schema')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Schema can not be empty!') }}';
+ this.errorModel.set('schema', msg);
+ } else if (_.has(changedAttrs, 'typtype') &&
+ (_.isUndefined(this.get('typtype'))
+ || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Type can not be empty!') }}';
+ this.errorModel.set('typtype', msg);
+ } else if (this.get('typtype') == 'r' &&
+ _.has(changedAttrs, 'typname')
+ && (_.isUndefined(this.get('typname'))
+ || String(this.get('typname')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Subtype Name can not be empty!') }}';
+ this.errorModel.set('typname', msg);
+ } else if (this.get('typtype') == 'x' &&
+ _.has(changedAttrs, 'typinput')
+ && (_.isUndefined(this.get('typinput'))
+ || String(this.get('typinput')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Input function can not be empty!') }}';
+ this.errorModel.set('typinput', msg);
+ } else if (this.get('typtype') == 'x' &&
+ _.has(changedAttrs, 'typoutput')
+ && (_.isUndefined(this.get('typoutput'))
+ || String(this.get('typoutput')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Output function can not be empty!') }}';
+ this.errorModel.set('typoutput', msg);
+ } else {
+ this.errorModel.unset('name');
+ this.errorModel.unset('schema');
+ this.errorModel.unset('typtype');
+ this.errorModel.unset('typname');
+ this.errorModel.unset('typinput');
+ this.errorModel.unset('typoutput');
+ }
+ return null;
+ },
+ // We will disable everything if we are under catalog node
+ inSchema: function() {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+ return false;
+ },
+ // We will check if we are under schema node & in 'create' mode
+ inSchemaWithModelCheck: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ // We will disbale control if it's in 'edit' mode
+ if (m.isNew()) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+ return true;
+ },
+ // We want to enable only in edit mode
+ inSchemaWithEditMode: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ // We will disbale control if it's in 'edit' mode
+ if (m.isNew()) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+ return true;
+ },
+ // Function will help us to fill combobox
+ external_func_combo: function(d) {
+ var result = [];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype == 'all' ) {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ }),
+ canCreate: function(itemData, item, data) {
+ //If check is false then , we will allow create menu
+ if (data && data.check == false)
+ return true;
+
+ var t = pgBrowser.tree, i = item, d = itemData;
+ // To iterate over tree to check parent node
+ while (i) {
+ // If it is schema then allow user to create table
+ if (_.indexOf(['schema'], d._type) > -1)
+ return true;
+
+ if ('coll-type' == d._type) {
+ //Check if we are not child of catalog
+ prev_i = t.hasParent(i) ? t.parent(i) : null;
+ prev_d = prev_i ? t.itemData(prev_i) : null;
+ if( prev_d._type == 'catalog') {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ i = t.hasParent(i) ? t.parent(i) : null;
+ d = i ? t.itemData(i) : null;
+ }
+ // by default we do not want to allow create menu
+ return true;
+ }
+ });
+ }
+ return pgBrowser.Nodes['type'];
+});
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql
new file mode 100644
index 0000000..3ccf8e7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql
@@ -0,0 +1,29 @@
+SELECT 'typacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'USAGE' THEN 'U'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT t.typacl
+ FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 %}
+ ) acl,
+ (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
+ AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
+ aclexplode(t.typacl) as d FROM pg_type t WHERE t.oid = {{tid}}::oid) a) d
+ ) d
+ LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+ LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY g.rolname, gt.rolname
+
+
+
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql
new file mode 100644
index 0000000..d72874f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql
@@ -0,0 +1,40 @@
+{# The SQL given below will fetch composite type#}
+{% if type == 'c' %}
+SELECT attname, format_type(t.oid,NULL) AS typname, attndims, atttypmod, nsp.nspname,
+(SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup
+-- Min 9.1 start
+,collname, nspc.nspname as collnspname
+-- End
+, att.attrelid
+FROM pg_attribute att
+JOIN pg_type t ON t.oid=atttypid
+JOIN pg_namespace nsp ON t.typnamespace=nsp.oid
+LEFT OUTER JOIN pg_type b ON t.typelem=b.oid
+-- Min 9.1 start
+LEFT OUTER JOIN pg_collation c ON att.attcollation=c.oid
+LEFT OUTER JOIN pg_namespace nspc ON c.collnamespace=nspc.oid
+-- End
+WHERE att.attrelid = {{typrelid}}::oid
+ORDER by attnum;
+{% endif %}
+{# The SQL given below will fetch enum type#}
+{% if type == 'e' %}
+SELECT enumlabel FROM pg_enum WHERE enumtypid={{tid}}::oid
+-- Min 9.1
+ORDER by enumsortorder
+--End
+-- else
+-- ORDER by oid
+{% endif %}
+{# The SQL given below will fetch range type#}
+{% if type == 'r' %}
+SELECT rngsubtype, st.typname,
+rngcollation, col.collname,
+rngsubopc, opc.opcname,
+rngcanonical, rngsubdiff
+FROM pg_range
+LEFT JOIN pg_type st ON st.oid=rngsubtype
+LEFT JOIN pg_collation col ON col.oid=rngcollation
+LEFT JOIN pg_opclass opc ON opc.oid=rngsubopc
+WHERE rngtypid={{tid}}::oid;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/backend_support.sql
new file mode 100644
index 0000000..00b7fb4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/backend_support.sql
@@ -0,0 +1,18 @@
+SELECT
+ CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport
+FROM pg_namespace nsp
+WHERE nsp.oid={{scid}}::int
+AND (
+ (nspname = 'pg_catalog' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1))
+ OR (nspname = 'pgagent' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1))
+ OR (nspname = 'information_schema' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1))
+ OR (nspname LIKE '_%' AND EXISTS
+ (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1))
+)
+AND
+ nspname NOT LIKE E'pg\\temp\\%'
+AND
+ nspname NOT LIKE E'pg\\toast_temp\\%'
\ No newline at end of file
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
new file mode 100644
index 0000000..706f264
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/create.sql
@@ -0,0 +1,69 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/privilege.macros' as PRIVILEGE %}
+{### 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 %});
+{% 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 }}{% 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 %} );
+{% endif %}
+{### External Type ###}
+{% if data and data.typtype == 'x' %}
+CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS
+( {% if data.typinput %}
+INPUT = {{data.typinput}}{% endif %}{% if data.typoutput %}
+,OUTPUT = {{ data.typoutput }}{% endif %}{% if data.typreceive %}
+,RECEIVE = {{data.typreceive}}{% endif %}{% if data.typname %}
+,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.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}}{% endif %}{% if data.typispreferred %}
+,PREFERRED = {{data.typispreferred}}{% endif %}{% if data.typdefault %}
+,DEFAULT = {{data.typdefault}}{% endif %}{% if data.element %}
+,ELEMENT = {{data.element}}{% endif %}{% if data.typdelim %}
+,DELIMITER = {{data.typdelim|qtLiteral}}{% 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 {{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}};
+{% endif %}
+{### Security Lables ###}
+{% if data.seclabels %}
+
+{% for r in data.seclabels %}
+{{ SECLABLE.APPLY(conn, 'TYPE', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{### ACL ###}
+{% if data.typacl %}
+
+{% for priv in data.typacl %}
+{{ PRIVILEGE.APPLY(conn, 'TYPE', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% 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/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..17d4da1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql
@@ -0,0 +1 @@
+DROP TYPE {{ conn|qtIdent(name) }}{% if cascade%} CASCADE{% 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_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_collations.sql
new file mode 100644
index 0000000..d1c29bb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_collations.sql
@@ -0,0 +1,7 @@
+SELECT --nspname, collname,
+ CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(collname))
+ ELSE '' END AS collation
+FROM pg_collation c, pg_namespace n
+WHERE c.collnamespace=n.oid
+ORDER BY nspname, collname;
\ 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
new file mode 100644
index 0000000..7b76ed5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_external_functions.sql
@@ -0,0 +1,40 @@
+{### 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
+ concat(quote_ident(nspname), '.', 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 = 0;
+{% endif %}
+{### TypmodIN list ###}
+{% if typemodin %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+ FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+ WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='_cstring')
+ AND proargtypes[1] IS NULL
+ ORDER BY nspname, proname;
+{% endif %}
+{### TypmodOUT list ###}
+{% if typemodout %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+ FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+ WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='cstring')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[1] IS NULL
+ ORDER BY nspname, proname;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_name.sql
new file mode 100644
index 0000000..d368f0f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_name.sql
@@ -0,0 +1,3 @@
+SELECT t.typname AS name
+FROM pg_type t
+WHERE t.oid = {{tid}}::oid;
\ 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_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..2dd81c0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,11 @@
+{# Below will provide oid for newly created type #}
+SELECT t.oid
+FROM pg_type t
+LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+LEFT OUTER JOIN 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 data %}
+AND t.typname = {{data.name|qtLiteral}}
+{% endif %}
+ORDER BY t.typname;
\ 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_subtypes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_subtypes.sql
new file mode 100644
index 0000000..1138ba4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_subtypes.sql
@@ -0,0 +1,56 @@
+{### To fill subtype combobox ###}
+{% if subtype %}
+SELECT DISTINCT typ.typname AS stype,
+ (CASE WHEN typ.typcollation > 0 THEN true ELSE false END) AS is_collate
+FROM pg_opclass opc
+JOIN pg_type typ ON opc.opcintype = typ.oid
+WHERE opc.opcmethod = 403
+ORDER BY 1
+{% endif %}
+{### To fill subtype opclass combobox ###}
+{% if subtype_opclass and data and data.typname %}
+SELECT opc.opcname
+FROM pg_opclass opc
+JOIN pg_type typ ON opc.opcintype=typ.oid
+AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ORDER BY opcname;
+{% endif %}
+{### To fetch opcinttype from subtype opclass ###}
+{% if get_opcintype and data and data.typname and data.opcname %}
+SELECT opc.opcintype
+FROM pg_opclass opc
+JOIN pg_type typ ON opc.opcintype=typ.oid
+AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+AND opc.opcname = {{ data.opcname|qtLiteral }}
+ORDER BY opcname;
+{% endif %}
+{### To fill subtype diff function combobox ###}
+{% if opcintype %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS stypdiff
+FROM pg_proc
+JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype = 701
+AND proargtypes = '{{opcintype}} {{opcintype}}'
+ORDER BY proname;
+{% endif %}
+{### To fill canonical combobox ###}
+{% if getoid %}
+SELECT oid FROM pg_type
+WHERE typname = {{ data.name|qtLiteral }}
+{% endif %}
+{% if canonical and oid %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS canonical
+FROM pg_proc
+JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype= {{ oid }}
+AND proargtypes = '{{ oid }}'
+ORDER BY proname;
+{% 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_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_types.sql
new file mode 100644
index 0000000..2f2ca70
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_types.sql
@@ -0,0 +1,10 @@
+SELECT * FROM
+ (SELECT format_type(t.oid,NULL) AS typname,
+ CASE WHEN typelem > 0 THEN typelem ELSE t.oid END AS elemoid
+ ,typlen, typtype, t.oid, nspname,
+ (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+FROM pg_type t
+ JOIN pg_namespace nsp ON typnamespace=nsp.oid
+WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r')AND NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) AND nsp.nspname != 'information_schema'
+ ) AS dummy
+ ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..88cc34a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql
@@ -0,0 +1,7 @@
+SELECT t.oid, t.typname AS name
+FROM pg_type t
+LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+LEFT OUTER JOIN pg_namespace nsp ON nsp.oid = t.typnamespace
+WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' AND t.typnamespace = {{scid}}::oid
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..56ab793
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql
@@ -0,0 +1,21 @@
+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.*, format_type(t.oid, null) AS alias
+ ,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 array_agg(provider || '=' || label) FROM pg_shseclabel 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_type t
+LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_type'::regclass)
+LEFT OUTER JOIN 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 %}
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..16b72bc
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
@@ -0,0 +1,129 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{#======================================#}
+{# 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 {{ data.typeowner }};
+
+{% endif %}
+{#======================================#}
+{# Below will change objects comment #}
+{% if data.description and data.description != o_data.description %}
+COMMENT ON TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ IS {{ data.description|qtLiteral }};
+
+{% 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)}} {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% endfor %}
+{% endif %}
+{% if 'changed' in composite and composite.changed|length > 0 %}
+{% for r in composite.changed %}
+ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ ALTER ATTRIBUTE {{conn|qtIdent(r.member_name)}} SET DATA TYPE {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% 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}} AFTER {{o_data.enum[o_enum_len].label|qtLiteral }};
+{% 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}} AFTER {{enum.added[p_idx].label|qtLiteral}};
+{% endif %}
+{% endfor %}
+{% endif %}
+
+{% endif %}
+{#======================================#}
+{# The SQL generated below will change Security Label #}
+{% if data.seclabels and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% set name = conn|qtIdent(o_data.schema, o_data.name) %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'TYPE', name, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'TYPE', name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'TYPE', name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+
+{% endif %}
+{#======================================#}
+{# Change the privileges #}
+{% if data.typacl and data.typacl|length > 0 %}
+{% set name = conn|qtIdent(o_data.schema, o_data.name) %}
+{% if 'deleted' in data.typacl %}
+{% for priv in data.typacl.deleted %}
+{{ PRIVILEGE.RESETALL(conn, 'TYPE', priv.grantee, name) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.typacl %}
+{% for priv in data.typacl.changed %}
+{{ PRIVILEGE.RESETALL(conn, 'TYPE', priv.grantee, data.name) }}
+{{ PRIVILEGE.APPLY(conn, 'TYPE', priv.grantee, name, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.typacl %}
+{% for priv in data.typacl.added %}
+{{ PRIVILEGE.APPLY(conn, 'TYPE', priv.grantee, name, priv.without_grant, priv.with_grant) }}
+{% 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 != 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 %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt
new file mode 100644
index 0000000..4a616b6
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt
@@ -0,0 +1,53 @@
+"""
+Here are the list of Postgres inbuilt types & their OID's
+We will use these type to check for validations
+
+## PGOID_TYPE_SERIAL -42L
+## PGOID_TYPE_SERIAL8 -43L
+## PGOID_TYPE_SERIAL2 -44L
+## PGOID_TYPE_BOOL 16L
+## PGOID_TYPE_BYTEA 17L
+## PGOID_TYPE_CHAR 18L
+## PGOID_TYPE_NAME 19L
+## PGOID_TYPE_INT8 20L
+## PGOID_TYPE_INT2 21L
+## PGOID_TYPE_INT4 23L
+## PGOID_TYPE_TEXT 25L
+## PGOID_TYPE_OID 26L
+## PGOID_TYPE_TID 27L
+## PGOID_TYPE_XID 28L
+## PGOID_TYPE_CID 29L
+## PGOID_TYPE_FLOAT4 700L
+## PGOID_TYPE_FLOAT8 701L
+## PGOID_TYPE_MONEY 790L
+## PGOID_TYPE_CHAR_ARRAY 1002L
+## PGOID_TYPE_TEXT_ARRAY 1009L
+## PGOID_TYPE_BPCHAR_ARRAY 1014L
+## PGOID_TYPE_VARCHAR_ARRAY 1015L
+## PGOID_TYPE_BPCHAR 1042L
+## PGOID_TYPE_VARCHAR 1043L
+## PGOID_TYPE_DATE 1082L
+## PGOID_TYPE_TIME 1083L
+## PGOID_TYPE_TIMESTAMP 1114L
+## PGOID_TYPE_TIMESTAMP_ARRAY 1115L
+## PGOID_TYPE_TIME_ARRAY 1183L
+## PGOID_TYPE_TIMESTAMPTZ 1184L
+## PGOID_TYPE_TIMESTAMPTZ_ARRAY 1185L
+## PGOID_TYPE_INTERVAL 1186L
+## PGOID_TYPE_INTERVAL_ARRAY 1187L
+## PGOID_TYPE_NUMERIC_ARRAY 1231L
+## PGOID_TYPE_TIMETZ 1266L
+## PGOID_TYPE_TIMETZ_ARRAY 1270L
+## PGOID_TYPE_BIT 1560L
+## PGOID_TYPE_BIT_ARRAY 1561L
+## PGOID_TYPE_VARBIT 1562L
+## PGOID_TYPE_VARBIT_ARRAY 1563L
+## PGOID_TYPE_NUMERIC 1700L
+## PGOID_TYPE_CSTRING 2275L
+## PGOID_TYPE_ANY 2276L
+## PGOID_TYPE_VOID 2278L
+## PGOID_TYPE_TRIGGER 2279L
+## PGOID_TYPE_LANGUAGE_HANDLER 2280L
+## PGOID_TYPE_INTERNAL 2281L
+## PGOID_TYPE_HANDLER 3115L
+"""
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/acl.sql
new file mode 100644
index 0000000..3ccf8e7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/acl.sql
@@ -0,0 +1,29 @@
+SELECT 'typacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'USAGE' THEN 'U'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT t.typacl
+ FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 %}
+ ) acl,
+ (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
+ AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
+ aclexplode(t.typacl) as d FROM pg_type t WHERE t.oid = {{tid}}::oid) a) d
+ ) d
+ LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+ LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY g.rolname, gt.rolname
+
+
+
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/additional_properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/additional_properties.sql
new file mode 100644
index 0000000..d72874f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/additional_properties.sql
@@ -0,0 +1,40 @@
+{# The SQL given below will fetch composite type#}
+{% if type == 'c' %}
+SELECT attname, format_type(t.oid,NULL) AS typname, attndims, atttypmod, nsp.nspname,
+(SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup
+-- Min 9.1 start
+,collname, nspc.nspname as collnspname
+-- End
+, att.attrelid
+FROM pg_attribute att
+JOIN pg_type t ON t.oid=atttypid
+JOIN pg_namespace nsp ON t.typnamespace=nsp.oid
+LEFT OUTER JOIN pg_type b ON t.typelem=b.oid
+-- Min 9.1 start
+LEFT OUTER JOIN pg_collation c ON att.attcollation=c.oid
+LEFT OUTER JOIN pg_namespace nspc ON c.collnamespace=nspc.oid
+-- End
+WHERE att.attrelid = {{typrelid}}::oid
+ORDER by attnum;
+{% endif %}
+{# The SQL given below will fetch enum type#}
+{% if type == 'e' %}
+SELECT enumlabel FROM pg_enum WHERE enumtypid={{tid}}::oid
+-- Min 9.1
+ORDER by enumsortorder
+--End
+-- else
+-- ORDER by oid
+{% endif %}
+{# The SQL given below will fetch range type#}
+{% if type == 'r' %}
+SELECT rngsubtype, st.typname,
+rngcollation, col.collname,
+rngsubopc, opc.opcname,
+rngcanonical, rngsubdiff
+FROM pg_range
+LEFT JOIN pg_type st ON st.oid=rngsubtype
+LEFT JOIN pg_collation col ON col.oid=rngcollation
+LEFT JOIN pg_opclass opc ON opc.oid=rngsubopc
+WHERE rngtypid={{tid}}::oid;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/backend_support.sql
new file mode 100644
index 0000000..00b7fb4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/backend_support.sql
@@ -0,0 +1,18 @@
+SELECT
+ CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport
+FROM pg_namespace nsp
+WHERE nsp.oid={{scid}}::int
+AND (
+ (nspname = 'pg_catalog' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1))
+ OR (nspname = 'pgagent' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1))
+ OR (nspname = 'information_schema' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1))
+ OR (nspname LIKE '_%' AND EXISTS
+ (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1))
+)
+AND
+ nspname NOT LIKE E'pg\\temp\\%'
+AND
+ nspname NOT LIKE E'pg\\toast_temp\\%'
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/create.sql
new file mode 100644
index 0000000..4136d91
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/create.sql
@@ -0,0 +1,61 @@
+{% import 'macros/privilege.macros' as PRIVILEGE %}
+{### 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 %});
+{% 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 }}{% 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 %} );
+{% endif %}
+{### External Type ###}
+{% if data and data.typtype == 'x' %}
+CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS
+( {% if data.typinput %}
+INPUT = {{data.typinput}}{% endif %}{% if data.typoutput %}
+,OUTPUT = {{ data.typoutput }}{% endif %}{% if data.typreceive %}
+,RECEIVE = {{data.typreceive}}{% endif %}{% if data.typname %}
+,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.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}}{% endif %}{% if data.typispreferred %}
+,PREFERRED = {{data.typispreferred}}{% endif %}{% if data.typdefault %}
+,DEFAULT = {{data.typdefault}}{% endif %}{% if data.element %}
+,ELEMENT = {{data.element}}{% endif %}{% if data.typdelim %}
+,DELIMITER = {{data.typdelim|qtLiteral}}{% 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 {{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}};
+{% endif %}
+{### ACL ###}
+{% if data.typacl %}
+
+{% for priv in data.typacl %}
+{{ PRIVILEGE.APPLY(conn, 'TYPE', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/delete.sql
new file mode 100644
index 0000000..06a279e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/delete.sql
@@ -0,0 +1,8 @@
+{% if scid and tid %}
+SELECT t.typname AS name
+FROM pg_type t
+WHERE t.oid = {{tid}}::oid
+{% endif %}
+{% if name %}
+DROP TYPE {{ conn|qtIdent(name) }}{% if cascade%} CASCADE{% endif %};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_collations.sql
new file mode 100644
index 0000000..d1c29bb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_collations.sql
@@ -0,0 +1,7 @@
+SELECT --nspname, collname,
+ CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(collname))
+ ELSE '' END AS collation
+FROM pg_collation c, pg_namespace n
+WHERE c.collnamespace=n.oid
+ORDER BY nspname, collname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_external_functions.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_external_functions.sql
new file mode 100644
index 0000000..7b76ed5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_external_functions.sql
@@ -0,0 +1,40 @@
+{### 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
+ concat(quote_ident(nspname), '.', 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 = 0;
+{% endif %}
+{### TypmodIN list ###}
+{% if typemodin %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+ FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+ WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='_cstring')
+ AND proargtypes[1] IS NULL
+ ORDER BY nspname, proname;
+{% endif %}
+{### TypmodOUT list ###}
+{% if typemodout %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+ FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+ WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='cstring')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[1] IS NULL
+ ORDER BY nspname, proname;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_name.sql
new file mode 100644
index 0000000..d368f0f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_name.sql
@@ -0,0 +1,3 @@
+SELECT t.typname AS name
+FROM pg_type t
+WHERE t.oid = {{tid}}::oid;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_oid.sql
new file mode 100644
index 0000000..2dd81c0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_oid.sql
@@ -0,0 +1,11 @@
+{# Below will provide oid for newly created type #}
+SELECT t.oid
+FROM pg_type t
+LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+LEFT OUTER JOIN 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 data %}
+AND t.typname = {{data.name|qtLiteral}}
+{% endif %}
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_schemas.sql
new file mode 100644
index 0000000..f64b68a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_schemas.sql
@@ -0,0 +1,25 @@
+SELECT
+ nsp.nspname
+FROM
+ pg_namespace nsp
+ LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass)
+ LEFT OUTER JOIN pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid)
+WHERE
+ NOT ((nspname = 'pg_catalog' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR
+ (nspname = 'pgagent' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR
+ (nspname = 'information_schema' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR
+ (nspname LIKE '_%' AND EXISTS
+ (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) OR
+ (nspname = 'dbo' OR nspname = 'sys')
+ )AND
+ nspname NOT LIKE E'pg\\temp\\%' AND
+ nspname NOT LIKE E'pg\\toast_temp\\%' AND
+ -- ADDED: Because We need to omit system schema except the one on which we are trying to create collation
+ ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' ) AND
+ -- Specific code for EDB PPAS
+ nsp.nspparent = 0 AND NOT
+ (nspname = 'dbms_job_procedure' AND EXISTS(SELECT 1 FROM pg_proc WHERE pronamespace = nsp.oid and proname = 'run_job' LIMIT 1))
+ORDER BY nspname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_subtypes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_subtypes.sql
new file mode 100644
index 0000000..1138ba4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_subtypes.sql
@@ -0,0 +1,56 @@
+{### To fill subtype combobox ###}
+{% if subtype %}
+SELECT DISTINCT typ.typname AS stype,
+ (CASE WHEN typ.typcollation > 0 THEN true ELSE false END) AS is_collate
+FROM pg_opclass opc
+JOIN pg_type typ ON opc.opcintype = typ.oid
+WHERE opc.opcmethod = 403
+ORDER BY 1
+{% endif %}
+{### To fill subtype opclass combobox ###}
+{% if subtype_opclass and data and data.typname %}
+SELECT opc.opcname
+FROM pg_opclass opc
+JOIN pg_type typ ON opc.opcintype=typ.oid
+AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ORDER BY opcname;
+{% endif %}
+{### To fetch opcinttype from subtype opclass ###}
+{% if get_opcintype and data and data.typname and data.opcname %}
+SELECT opc.opcintype
+FROM pg_opclass opc
+JOIN pg_type typ ON opc.opcintype=typ.oid
+AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+AND opc.opcname = {{ data.opcname|qtLiteral }}
+ORDER BY opcname;
+{% endif %}
+{### To fill subtype diff function combobox ###}
+{% if opcintype %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS stypdiff
+FROM pg_proc
+JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype = 701
+AND proargtypes = '{{opcintype}} {{opcintype}}'
+ORDER BY proname;
+{% endif %}
+{### To fill canonical combobox ###}
+{% if getoid %}
+SELECT oid FROM pg_type
+WHERE typname = {{ data.name|qtLiteral }}
+{% endif %}
+{% if canonical and oid %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS canonical
+FROM pg_proc
+JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype= {{ oid }}
+AND proargtypes = '{{ oid }}'
+ORDER BY proname;
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_types.sql
new file mode 100644
index 0000000..2f2ca70
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_types.sql
@@ -0,0 +1,10 @@
+SELECT * FROM
+ (SELECT format_type(t.oid,NULL) AS typname,
+ CASE WHEN typelem > 0 THEN typelem ELSE t.oid END AS elemoid
+ ,typlen, typtype, t.oid, nspname,
+ (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+FROM pg_type t
+ JOIN pg_namespace nsp ON typnamespace=nsp.oid
+WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r')AND NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) AND nsp.nspname != 'information_schema'
+ ) AS dummy
+ ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/nodes.sql
new file mode 100644
index 0000000..88cc34a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/nodes.sql
@@ -0,0 +1,7 @@
+SELECT t.oid, t.typname AS name
+FROM pg_type t
+LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+LEFT OUTER JOIN pg_namespace nsp ON nsp.oid = t.typnamespace
+WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' AND t.typnamespace = {{scid}}::oid
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/properties.sql
new file mode 100644
index 0000000..f26ac7c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/properties.sql
@@ -0,0 +1,16 @@
+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.*, format_type(t.oid, null) AS alias
+ ,pg_get_userbyid(t.typowner) as typeowner, e.typname as element
+ ,description, ct.oid AS taboid
+ ,(CASE WHEN (t.oid <= {{ datlastsysoid}}::oid OR ct.oid != 0) THEN true ElSE false END) AS is_sys_type
+FROM pg_type t
+LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+LEFT OUTER JOIN 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 %}
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/update.sql
new file mode 100644
index 0000000..3f2663a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/update.sql
@@ -0,0 +1,107 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{#======================================#}
+{# 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 {{ data.typeowner }};
+
+{% endif %}
+{#======================================#}
+{# Below will change objects comment #}
+{% if data.description and data.description != o_data.description %}
+COMMENT ON TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ IS {{ data.description|qtLiteral }};
+
+{% 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)}} {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% endfor %}
+{% endif %}
+{% if 'changed' in composite and composite.changed|length > 0 %}
+{% for r in composite.changed %}
+ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ ALTER ATTRIBUTE {{conn|qtIdent(r.member_name)}} SET DATA TYPE {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% 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}} AFTER {{o_data.enum[o_enum_len].label|qtLiteral }};
+{% 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}} AFTER {{enum.added[p_idx].label|qtLiteral}};
+{% endif %}
+{% endfor %}
+{% endif %}
+
+{% endif %}
+{#======================================#}
+{# Change the privileges #}
+{% if data.typacl %}
+{% if 'deleted' in data.typacl %}
+{% for priv in data.typacl.deleted %}
+{{ PRIVILEGE.RESETALL(conn, 'TYPE', priv.grantee, o_data.name) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.typacl %}
+{% for priv in data.typacl.changed %}
+{{ PRIVILEGE.RESETALL(conn, 'TYPE', priv.grantee, data.name) }}
+{{ PRIVILEGE.APPLY(conn, 'TYPE', priv.grantee, o_data.name, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.typacl %}
+{% for priv in data.typacl.added %}
+{{ PRIVILEGE.APPLY(conn, 'TYPE', priv.grantee, o_data.name, priv.without_grant, priv.with_grant) }}
+{% 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 != 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 %}
+{% endif %}
\ No newline at end of file
[application/octet-stream] catalog_objects_node_v4.patch (38.0K, 4-catalog_objects_node_v4.patch)
download | inline diff:
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/__init__.py
new file mode 100644
index 0000000..408ecd3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/__init__.py
@@ -0,0 +1,314 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+""" Implements Catalog objects Node """
+
+from flask import render_template
+from flask.ext.babel import gettext
+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 pgadmin.browser.server_groups.servers.databases as database
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class CatalogObjectModule(SchemaChildModule):
+ """
+ class CatalogObjectModule(CollectionNodeModule)
+
+ A module class for Catalog objects node derived from CollectionNodeModule.
+
+ Methods:
+ -------
+ * __init__(*args, **kwargs)
+ - Method is used to initialize the Catalog objects and it's base module.
+
+ * get_nodes(gid, sid, did, scid, coid)
+ - Method is used to generate the browser collection node.
+
+ * node_inode()
+ - Method is overridden from its base class to make the node as leaf node.
+
+ * script_load()
+ - Load the module script for Catalog objects, when any of the server node is
+ initialized.
+ """
+ NODE_TYPE = 'catalogobject'
+ COLLECTION_LABEL = gettext("Catalog Objects")
+
+ # Flag for not to show node under Schema/Catalog node
+ # By default its set to True to display node in schema/catalog
+ # We do not want to display 'Catalog Objects' under Schema/Catalog
+ # but only in information_schema/sys/dbo
+ CATALOG_DB_SUPPORTED = False
+ SUPPORTED_SCHEMAS = ['information_schema', 'sys', 'dbo']
+
+ def __init__(self, *args, **kwargs):
+ """
+ Method is used to initialize the CatalogObjectModule and it's base module.
+
+ Args:
+ *args:
+ **kwargs:
+ """
+ super(CatalogObjectModule, self).__init__(*args, **kwargs)
+ self.min_ver = None
+ self.max_ver = None
+
+ def get_nodes(self, gid, sid, did, scid):
+ """
+ Generate the collection node
+ """
+ yield self.generate_browser_collection_node(scid)
+
+ @property
+ def script_load(self):
+ """
+ Load the module script for server, when any of the database node is
+ initialized.
+ """
+ return database.DatabaseModule.NODE_TYPE
+
+blueprint = CatalogObjectModule(__name__)
+
+
+class CatalogObjectView(PGChildNodeView):
+ """
+ This class is responsible for generating routes for Catalog objects node
+
+ Methods:
+ -------
+ * __init__(**kwargs)
+ - Method is used to initialize the CatalogObjectView and it's base view.
+
+ * check_precondition()
+ - This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+
+ * list()
+ - This function is used to list all the Catalog objects nodes within that
+ collection.
+
+ * nodes()
+ - This function will used to create all the child node within that
+ collection, Here it will create all the Catalog objects node.
+
+ * properties(gid, sid, did, scid, coid)
+ - This function will show the properties of the selected
+ Catalog objects node
+
+ * dependency(gid, sid, did, scid):
+ - This function will generate dependency list show it in dependency
+ pane for the selected Catalog objects node.
+
+ * dependent(gid, sid, did, scid):
+ - This function will generate dependent list to show it in dependent
+ pane for the selected Catalog objects node.
+
+ """
+ node_type = blueprint.node_type
+
+ parent_ids = [
+ {'type': 'int', 'id': 'gid'},
+ {'type': 'int', 'id': 'sid'},
+ {'type': 'int', 'id': 'did'},
+ {'type': 'int', 'id': 'scid'}
+ ]
+ ids = [
+ {'type': 'int', 'id': 'coid'}
+ ]
+
+ operations = dict({
+ 'obj': [
+ {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+ {'get': 'list', 'post': 'create'}
+ ],
+ 'children': [{'get': 'children'}],
+ 'nodes': [{'get': 'node'}, {'get': 'nodes'}],
+ 'sql': [{'get': 'sql'}],
+ 'msql': [{'get': 'msql'}, {'get': 'msql'}],
+ 'stats': [{'get': 'statistics'}],
+ 'dependency': [{'get': 'dependencies'}],
+ 'dependent': [{'get': 'dependents'}],
+ 'module.js': [{}, {}, {'get': 'module_js'}],
+ 'configs': [{'get': 'configs'}]
+ })
+
+ def check_precondition(f):
+ """
+ This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+ """
+ @wraps(f)
+ def wrap(*args, **kwargs):
+ # Here args[0] will hold self & kwargs will hold gid,sid,did
+ self = args[0]
+ self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
+ kwargs['sid']
+ )
+ self.conn = self.manager.connection(did=kwargs['did'])
+ # If DB not connected then return error to browser
+ if not self.conn.connected():
+ return precondition_required(
+ gettext(
+ "Connection to the server has been lost!"
+ )
+ )
+
+ self.template_path = 'catalogobject/sql/9.1_plus'
+
+ return f(*args, **kwargs)
+
+ return wrap
+
+ @check_precondition
+ def list(self, gid, sid, did, scid):
+ """
+ This function is used to list all the catalog objects
+ nodes within that collection.
+
+ Args:
+ gid: Server group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+
+ Returns:
+ JSON of available catalog objects nodes
+ """
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']), scid=scid)
+ status, res = self.conn.execute_dict(SQL)
+
+ if not status:
+ return internal_server_error(errormsg=res)
+ return ajax_response(
+ response=res['rows'],
+ status=200
+ )
+
+ @check_precondition
+ def nodes(self, gid, sid, did, scid):
+ """
+ This function will used to create all the child node within that collection.
+ Here it will create all the catalog objects node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+
+ Returns:
+ JSON of available catalog objects child nodes
+ """
+ res = []
+ SQL = render_template("/".join([self.template_path,
+ 'nodes.sql']), scid=scid)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=rset)
+
+ for row in rset['rows']:
+ res.append(
+ self.blueprint.generate_browser_node(
+ row['oid'],
+ scid,
+ row['name'],
+ icon="icon-catalogobject"
+ ))
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ @check_precondition
+ def properties(self, gid, sid, did, scid, coid):
+ """
+ This function will show the properties of the selected
+ catalog objects node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ scid: Schema ID
+ coid: Catalog object ID
+
+ Returns:
+ JSON of selected catalog objects node
+ """
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, coid=coid)
+ status, res = self.conn.execute_dict(SQL)
+
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return ajax_response(
+ response=res['rows'][0],
+ status=200
+ )
+
+ @check_precondition
+ def dependents(self, gid, sid, did, scid, coid):
+ """
+ This function get the dependents and return ajax response
+ for the catalog objects node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ coid: catalog objects ID
+ """
+ dependents_result = self.get_dependents(
+ self.conn, coid
+ )
+
+ return ajax_response(
+ response=dependents_result,
+ status=200
+ )
+
+ @check_precondition
+ def dependencies(self, gid, sid, did, scid, coid):
+ """
+ This function get the dependencies and return ajax response
+ for the catalog objects node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ coid: catalog objects ID
+ """
+ dependencies_result = self.get_dependencies(
+ self.conn, coid
+ )
+
+ return ajax_response(
+ response=dependencies_result,
+ status=200
+ )
+
+CatalogObjectView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/__init__.py
new file mode 100644
index 0000000..d7d92aa
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/__init__.py
@@ -0,0 +1,355 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+""" Implements Columns Node (For Catalog objects) """
+
+from flask import render_template, make_response, request, jsonify
+from flask.ext.babel import gettext
+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.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases as database
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class ColumnsModule(CollectionNodeModule):
+ """
+ class ColumnModule(CollectionNodeModule)
+
+ A module class for column node derived from CollectionNodeModule.
+
+ Methods:
+ -------
+ * __init__(*args, **kwargs)
+ - Method is used to initialize the column and it's base module.
+
+ * get_nodes(gid, sid, did, scid, coid)
+ - Method is used to generate the browser collection node.
+
+ * node_inode()
+ - Method is overridden from its base class to make the node as leaf node.
+
+ * script_load()
+ - Load the module script for column, when any of the server node is
+ initialized.
+ """
+
+ NODE_TYPE = 'columns'
+ COLLECTION_LABEL = gettext("Columns")
+
+ def __init__(self, *args, **kwargs):
+ """
+ Method is used to initialize the ColumnModule and it's base module.
+
+ Args:
+ *args:
+ **kwargs:
+ """
+ super(ColumnsModule, self).__init__(*args, **kwargs)
+ self.min_ver = None
+ self.max_ver = None
+
+ def get_nodes(self, gid, sid, did, scid, coid):
+ """
+ Generate the collection node
+ """
+ yield self.generate_browser_collection_node(coid)
+
+ @property
+ def script_load(self):
+ """
+ Load the module script for server, when any of the database node is
+ initialized.
+ """
+ return database.DatabaseModule.NODE_TYPE
+
+ @property
+ def node_inode(self):
+ """
+ Load the module node as a leaf node
+ """
+ return False
+
+blueprint = ColumnsModule(__name__)
+
+
+class ColumnsView(PGChildNodeView):
+ """
+ This class is responsible for generating routes for column node
+
+ Methods:
+ -------
+ * __init__(**kwargs)
+ - Method is used to initialize the ColumnView and it's base view.
+
+ * check_precondition()
+ - This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+
+ * list()
+ - This function is used to list all the column nodes within that
+ collection.
+
+ * nodes()
+ - This function will used to create all the child node within that
+ collection, Here it will create all the column node.
+
+ * properties(gid, sid, did, scid, coid, clid)
+ - This function will show the properties of the selected
+ column node
+
+ * dependency(gid, sid, did, scid, coid, clid):
+ - This function will generate dependency list show it in dependency
+ pane for the selected column node.
+
+ * dependent(gid, sid, did, scid, coid, clid):
+ - This function will generate dependent list to show it in dependent
+ pane for the selected column node.
+
+ """
+
+ node_type = blueprint.node_type
+
+ parent_ids = [
+ {'type': 'int', 'id': 'gid'},
+ {'type': 'int', 'id': 'sid'},
+ {'type': 'int', 'id': 'did'},
+ {'type': 'int', 'id': 'scid'},
+ {'type': 'int', 'id': 'coid'}
+ ]
+ ids = [
+ {'type': 'int', 'id': 'clid'}
+ ]
+
+ operations = dict({
+ 'obj': [
+ {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+ {'get': 'list', 'post': 'create'}
+ ],
+ 'children': [{'get': 'children'}],
+ 'nodes': [{'get': 'node'}, {'get': 'nodes'}],
+ 'sql': [{'get': 'sql'}],
+ 'msql': [{'get': 'msql'}, {'get': 'msql'}],
+ 'stats': [{'get': 'statistics'}],
+ 'dependency': [{'get': 'dependencies'}],
+ 'dependent': [{'get': 'dependents'}],
+ 'module.js': [{}, {}, {'get': 'module_js'}],
+ 'configs': [{'get': 'configs'}]
+ })
+
+ def check_precondition(f):
+ """
+ This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+ """
+ @wraps(f)
+ def wrap(*args, **kwargs):
+ # Here args[0] will hold self & kwargs will hold gid,sid,did
+ self = args[0]
+ self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
+ kwargs['sid']
+ )
+ self.conn = self.manager.connection(did=kwargs['did'])
+ # If DB not connected then return error to browser
+ if not self.conn.connected():
+ return precondition_required(
+ gettext(
+ "Connection to the server has been lost!"
+ )
+ )
+
+ self.template_path = 'columns/sql/9.1_plus'
+
+ return f(*args, **kwargs)
+
+ return wrap
+
+ @check_precondition
+ def list(self, gid, sid, did, scid, coid):
+ """
+ This function is used to list all the column
+ nodes within that collection.
+
+ Args:
+ gid: Server group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ coid: Catalog objects ID
+
+ Returns:
+ JSON of available column nodes
+ """
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']), coid=coid)
+ status, res = self.conn.execute_dict(SQL)
+
+ if not status:
+ return internal_server_error(errormsg=res)
+ return ajax_response(
+ response=res['rows'],
+ status=200
+ )
+
+ @check_precondition
+ def nodes(self, gid, sid, did, scid, coid):
+ """
+ This function will used to create all the child node within that collection.
+ Here it will create all the column node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ coid: Catalog objects ID
+
+ Returns:
+ JSON of available column child nodes
+ """
+ res = []
+ SQL = render_template("/".join([self.template_path,
+ 'nodes.sql']), coid=coid)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=rset)
+
+ for row in rset['rows']:
+ res.append(
+ self.blueprint.generate_browser_node(
+ row['atttypid'],
+ coid,
+ row['attname'],
+ icon="icon-column"
+ ))
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ @check_precondition
+ def properties(self, gid, sid, did, scid, coid, clid):
+ """
+ This function will show the properties of the selected
+ column node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ scid: Schema ID
+ coid: Catalog object ID
+ clid: Column ID
+
+ Returns:
+ JSON of selected column node
+ """
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),coid=coid, clid=clid)
+ status, res = self.conn.execute_dict(SQL)
+
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return ajax_response(
+ response=res['rows'][0],
+ status=200
+ )
+
+ @check_precondition
+ def dependents(self, gid, sid, did, scid, coid, clid):
+ """
+ This function get the dependents and return ajax response
+ for the column node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ coid: Catalog object ID
+ clid: Column ID
+ """
+ # Specific condition for column which we need to append
+ where = "WHERE dep.refobjid={0}::OID AND dep.refobjsubid={1}".format(
+ coid, clid
+ )
+
+ dependents_result = self.get_dependents(
+ self.conn, clid, where=where
+ )
+
+ # Specific sql to run againt column to fetch dependents
+ SQL = render_template("/".join([self.template_path,
+ 'depend.sql']), where=where)
+
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in res['rows']:
+ ref_name = row['refname']
+ if ref_name is None:
+ continue
+
+ dep_type = ''
+ dep_str = row['deptype']
+ if dep_str == 'a':
+ dep_type = 'auto'
+ elif dep_str == 'n':
+ dep_type = 'normal'
+ elif dep_str == 'i':
+ dep_type = 'internal'
+
+ dependents_result.append({'type': 'sequence', 'name': ref_name, 'field': dep_type})
+
+ return ajax_response(
+ response=dependents_result,
+ status=200
+ )
+
+ @check_precondition
+ def dependencies(self, gid, sid, did, scid, coid, clid):
+ """
+ This function get the dependencies and return ajax response
+ for the column node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ coid: Catalog objects ID
+ clid: Column ID
+
+ """
+ # Specific condition for column which we need to append
+ where = "WHERE dep.objid={0}::OID AND dep.objsubid={1}".format(
+ coid, clid
+ )
+
+ dependencies_result = self.get_dependencies(
+ self.conn, clid, where=where
+ )
+
+ return ajax_response(
+ response=dependencies_result,
+ status=200
+ )
+
+
+ColumnsView.register_node_view(blueprint)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/static/img/coll-columns.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/static/img/coll-columns.png
new file mode 100644
index 0000000000000000000000000000000000000000..89d758834d4176c1df2548db10b46b1f6b2e4ec5
GIT binary patch
literal 400
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}cz{ocE09*4`h3Ev&s(m&yL<QU
z)2C0LJ$u%^|9SG<<M-~}d-(9-vSrKOym_;3-MW`AU#?uaa_!o+>({S;_3G90=g%KJ
zc<}o5>kl73eEaro$BrEvHf-3qapU*z-`~D{TatgM6KFJJNswPKgTu2MX+REVfk$L9
zkoEv$x0Bg+Kt_S5i(`ny<=FG?VhsvBt`}W4E@ZQg__p6qm@nby;qrG1j0_I@c^(;r
zuhP)2X<D_P?dti2$vzyv(k(V*o?f?H<i?gXiRA)mhTUpwofQn*P8`|LWw=McNaXQ!
zy+q@@Yd0m1ylF3f{(An_-EV$!P4L^#_`09vh+=cW8=&2)C9V-ADTyViR>?)FK#IZ0
zz|cU~&`8(7FvQ5f%EZ{p#6;V`)XKoXVy3DbiiX_$l+3hBhz0{oum+H7D+4o#hEvl+
R*8nvzc)I$ztaD0e0s#BYr!@co
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/static/img/columns.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/static/img/columns.png
new file mode 100644
index 0000000000000000000000000000000000000000..bd9f81df98fe27d81ade5144d66b3b09b96123c1
GIT binary patch
literal 435
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE09*4`h3Ev&s(m&yL<QU
zy?giW-@pI#>C<P=p0)3PzHHgD=g*(Nc=6)Rn>Xv$t$X?M<;s;SSFBjEcJ12r>({?}
z_3FWc2M-@UeDvti>eZ{)tXZ>Z)20s}KD>VY`qQURpFe;8`t|F#Z{K$8*s)>5hK(CH
ze*gac?c2BS-o49eKe!ZVF=I)PUoeBivm0qZ4rhT!WHFHT0Ash4*>*risi%u$h{WaE
z^B0Ah6a-om4Rm-iuV3+X<o2rm?|w$}?!!eN_y0*dcT_ORb6x7A={6cu<^6X}kY2Ib
zZ0TMHrih1I^CUh^d%@<y)3TvOv&K6iH~-RCTdVU6czMe9*h~1l{VS1_(D3~9P6_q4
zgXi+))!6E^|9xfrB*GzI-TA)^=m6Cc*NBpo#FA92<f2p{#b9J$XrOCoq-$UpVq{=t
zVr*q%qHSPmWnf@2Q&kQ{LvDUbW?CgggMlSj14y-%ff+=@sp+9>fEpM)UHx3vIVCg!
E0NRqnZ~y=R
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/templates/columns/js/columns.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/templates/columns/js/columns.js
new file mode 100644
index 0000000..f4a6ab7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/templates/columns/js/columns.js
@@ -0,0 +1,72 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+ if (!pgBrowser.Nodes['coll-columns']) {
+ var databases = pgAdmin.Browser.Nodes['coll-columns'] =
+ pgAdmin.Browser.Collection.extend({
+ node: 'columns',
+ label: '{{ _('Columns') }}',
+ type: 'coll-columns'
+ });
+ };
+
+ if (!pgBrowser.Nodes['columns']) {
+ pgAdmin.Browser.Nodes['columns'] = pgAdmin.Browser.Node.extend({
+ parent_type: 'catalogobject',
+ type: 'columns',
+ label: '{{ _('Columns') }}',
+ hasSQL: false,
+ hasDepends: true,
+ Init: function() {
+ /* Avoid mulitple registration of menus */
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+
+ },
+ model: pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ attname: undefined,
+ attowner: undefined,
+ atttypid: undefined,
+ attnum: undefined,
+ cltype: undefined,
+ collspcname: undefined,
+ attacl: undefined,
+ description: undefined
+ },
+ schema: [{
+ id: 'attname', label: '{{ _('Column') }}', cell: 'string',
+ type: 'text', disabled: true
+ },{
+ id: 'atttypid', label: '{{ _('Oid') }}', cell: 'string',
+ type: 'text', disabled: true
+ },{
+ id: 'attowner', label: '{{ _('Owner') }}', cell: 'string',
+ type: 'text', disabled: true
+ },{
+ id: 'attnum', label:'{{ _('Position') }}', cell: 'string',
+ type: 'text', disabled: true
+ },{
+ id: 'cltype', label:'{{ _('Data type') }}', cell: 'string',
+ type: 'text', disabled: true
+ },{
+ id: 'collspcname', label:'{{ _('Collation') }}', cell: 'string',
+ type: 'text', disabled: true
+ },{
+ id: 'attacl', label:'{{ _('ACL') }}', cell: 'string',
+ type: 'text', disabled: true
+ },{
+ id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+ type: 'multiline', disabled: true
+ }
+ ]
+ })
+ });
+
+ }
+
+ return pgBrowser.Nodes['columns'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/templates/columns/sql/9.1_plus/depend.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/templates/columns/sql/9.1_plus/depend.sql
new file mode 100644
index 0000000..13c9229
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/templates/columns/sql/9.1_plus/depend.sql
@@ -0,0 +1,9 @@
+SELECT
+ ref.relname AS refname, d2.refclassid, dep.deptype AS deptype
+FROM pg_depend dep
+LEFT JOIN pg_depend d2 ON dep.objid=d2.objid AND dep.refobjid != d2.refobjid
+LEFT JOIN pg_class ref ON ref.oid=d2.refobjid
+LEFT JOIN pg_attribute att ON d2.refclassid=att.attrelid AND d2.refobjsubid=att.attnum
+{{ where }} AND
+dep.classid=(SELECT oid FROM pg_class WHERE relname='pg_attrdef') AND
+dep.refobjid NOT IN (SELECT d3.refobjid FROM pg_depend d3 WHERE d3.objid=d2.refobjid)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/templates/columns/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/templates/columns/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..f319b7f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/templates/columns/sql/9.1_plus/nodes.sql
@@ -0,0 +1,10 @@
+SELECT atttypid, attname
+FROM pg_attribute att
+ JOIN pg_type ty ON ty.oid=atttypid
+ JOIN pg_namespace tn ON tn.oid=ty.typnamespace
+ JOIN pg_class cl ON cl.oid=att.attrelid
+ JOIN pg_namespace na ON na.oid=cl.relnamespace
+WHERE att.attrelid = {{coid}}::oid
+ AND att.attnum > 0
+ AND att.attisdropped IS FALSE
+ORDER BY att.attnum
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/templates/columns/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/templates/columns/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..8362bcb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/columns/templates/columns/sql/9.1_plus/properties.sql
@@ -0,0 +1,39 @@
+SELECT att.*, def.*, pg_catalog.pg_get_expr(def.adbin, def.adrelid) AS defval,
+ CASE WHEN att.attndims > 0 THEN 1 ELSE 0 END AS isarray,
+ format_type(ty.oid,NULL) AS typname,
+ format_type(ty.oid,att.atttypmod) AS displaytypname,
+ tn.nspname as typnspname, et.typname as elemtypname,
+ ty.typstorage AS defaultstorage, cl.relname, na.nspname,
+ att.attstattarget, description, cs.relname AS sername,
+ ns.nspname AS serschema,
+ (SELECT count(1) FROM pg_type t2 WHERE t2.typname=ty.typname) > 1 AS isdup,
+ indkey, coll.collname, nspc.nspname as collnspname , attoptions,
+ -- Start pgAdmin4, added to save time on client side parsing
+ CASE WHEN length(coll.collname) > 0 AND length(nspc.nspname) > 0 THEN
+ concat(coll.collname,'."',nspc.nspname,'"')
+ ELSE '' END AS collspcname,
+ CASE WHEN strpos(format_type(ty.oid,att.atttypmod), '.') > 0 THEN
+ split_part(format_type(ty.oid,att.atttypmod), '.', 2)
+ ELSE format_type(ty.oid,att.atttypmod) END AS cltype,
+ -- End pgAdmin4
+ EXISTS(SELECT 1 FROM pg_constraint WHERE conrelid=att.attrelid AND contype='f' AND att.attnum=ANY(conkey)) As isfk,
+ (SELECT array_agg(label) FROM pg_seclabels sl1 WHERE sl1.objoid=att.attrelid AND sl1.objsubid=att.attnum) AS labels,
+ (SELECT array_agg(provider) FROM pg_seclabels sl2 WHERE sl2.objoid=att.attrelid AND sl2.objsubid=att.attnum) AS providers
+FROM pg_attribute att
+ JOIN pg_type ty ON ty.oid=atttypid
+ JOIN pg_namespace tn ON tn.oid=ty.typnamespace
+ JOIN pg_class cl ON cl.oid=att.attrelid
+ JOIN pg_namespace na ON na.oid=cl.relnamespace
+ LEFT OUTER JOIN pg_type et ON et.oid=ty.typelem
+ LEFT OUTER JOIN pg_attrdef def ON adrelid=att.attrelid AND adnum=att.attnum
+ LEFT OUTER JOIN pg_description des ON (des.objoid=att.attrelid AND des.objsubid=att.attnum AND des.classoid='pg_class'::regclass)
+ LEFT OUTER JOIN (pg_depend JOIN pg_class cs ON objid=cs.oid AND cs.relkind='S') ON refobjid=att.attrelid AND refobjsubid=att.attnum
+ LEFT OUTER JOIN pg_namespace ns ON ns.oid=cs.relnamespace
+ LEFT OUTER JOIN pg_index pi ON pi.indrelid=att.attrelid AND indisprimary
+ LEFT OUTER JOIN pg_collation coll ON att.attcollation=coll.oid
+ LEFT OUTER JOIN pg_namespace nspc ON coll.collnamespace=nspc.oid
+WHERE att.attrelid = {{coid}}::oid
+ {% if clid %}AND att.atttypid = {{clid}}::oid{% endif %}
+ AND att.attnum > 0
+ AND att.attisdropped IS FALSE
+ORDER BY att.attnum
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/static/img/catalogobject.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/static/img/catalogobject.png
new file mode 100644
index 0000000000000000000000000000000000000000..54ed7389c128fdcdcd86bc504311b9ed62e890ff
GIT binary patch
literal 409
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}QGic~E09*4`n>nT|4HY6FS`77
z*NwLq@4vkN`1#Xk&!0VecI)BOHRqluZ+xLQ`$hNp|E*{K)t&fLdiYn)z8{G@zJ+c2
z?7ilL!}53L3*P9?d8IbvW$T&0+53JtE_-LT@QvP_S6i;V`}u8J`~K&*j@bWvKj-(&
zo4+qyn4y_82WTr}NswPKgTu2MX+REVfk$L9koEv$x0Bg+Kt`OWi(`ny<>Z6}7e*c)
zo*thwI%{&?$jq4&bH`?n0;63WqoZP?V4z_kr{@MPJ55H%%mocSYh+v;;u!fv4l;3h
zFz78{aWi04RS0O9aOL`<HH&)Ixr&B%Z_;vMxcZwR(@;29_qOf^piQbJt`Q|Ei6yC4
z$wjF^iowXh&_LJFNY}tH#K^$P#MsKjMBBjB%D}*4rm7r@hTQy=%(O~~1_Mj529Rni
Y12c$*Q`1A&05vdpy85}Sb4q9e0Lf^e3;+NC
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/static/img/coll-catalogobject.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/static/img/coll-catalogobject.png
new file mode 100644
index 0000000000000000000000000000000000000000..85b89f1e75c3ba919e0f3f63235d14fb7bc58940
GIT binary patch
literal 419
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}W`IwKE09*4`n>DhzbWT`uD$x{
z)SXxN9zTEf{Q0wI&yL=BR<`qn!JHQzXa5Cl__X=z+n?X2HJ|=hb?kTkfuG4czlCl3
z?78}b?b5e~^IogZeA#^ZuNRQJ<gLccm+c3h-#TJH;ne49km4U2GhaM;`TYdYGRBf1
zzhDN3XE)M-9L@rd$YLPv0mg18v+aP4LQfaR5Q)pNeVlv^3Op<;)wR0J=T-dqKXF=D
z;7x%`K53IvKePoN_}pyJW$AzE4V%(`YXup0uH%d^EjTV{uHKcoJB~Mj!D=z%<uzj5
zT$!7r8kR3pmtU}z@dxAWZMQcV-k!sy{5*K!&;MLji|+8>Dm=t0E5ucyF7nz8Xt`>M
zYeY#(Vo9o1a#1RfVlXl=G|)9P(lsy)F*2|+F}5->(Kax(GBB{1sVaw}AvZrIGp!P$
f!N3x%0i@c>zzm|{)b!9bKn)C@u6{1-oD!M<aEhuj
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/templates/catalogobject/js/catalogobject.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/templates/catalogobject/js/catalogobject.js
new file mode 100644
index 0000000..d6f601a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/templates/catalogobject/js/catalogobject.js
@@ -0,0 +1,56 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+ if (!pgBrowser.Nodes['coll-catalogobject']) {
+ var databases = pgAdmin.Browser.Nodes['coll-catalogobject'] =
+ pgAdmin.Browser.Collection.extend({
+ node: 'catalogobject',
+ label: '{{ _('Catalog Objects') }}',
+ type: 'coll-catalogobject'
+ });
+ };
+
+ if (!pgBrowser.Nodes['catalogobject']) {
+ pgAdmin.Browser.Nodes['catalogobject'] = pgAdmin.Browser.Node.extend({
+ parent_type: 'catalog',
+ type: 'catalogobject',
+ label: '{{ _('Catalog Object') }}',
+ hasSQL: false,
+ hasDepends: true,
+ Init: function() {
+ /* Avoid mulitple registration of menus */
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+
+ },
+ model: pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ name: undefined,
+ namespaceowner: undefined,
+ nspacl: undefined,
+ description: undefined,
+ },
+ schema: [{
+ id: 'name', label: '{{ _('Name') }}', cell: 'string',
+ type: 'text', disabled: true
+ },{
+ id: 'oid', label:'{{ _('Oid') }}', cell: 'string',
+ type: 'text', disabled: true
+ },{
+ id: 'owner', label:'{{ _('Owner') }}', cell: 'string',
+ type: 'text', disabled: true
+ },{
+ id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+ type: 'multiline' , disabled: true
+ }
+ ]
+ })
+ });
+
+ }
+
+ return pgBrowser.Nodes['catalogobject'];
+});
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/templates/catalogobject/sql/9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/templates/catalogobject/sql/9.1_plus/backend_support.sql
new file mode 100644
index 0000000..f9b9564
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/templates/catalogobject/sql/9.1_plus/backend_support.sql
@@ -0,0 +1,18 @@
+SELECT
+ CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport
+FROM pg_namespace nsp
+WHERE nsp.oid={{scid}}::int
+AND (
+ (nspname = 'pg_catalog' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1))
+ OR (nspname = 'pgagent' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1))
+ OR (nspname = 'information_schema' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1))
+ OR (nspname LIKE '_%' AND EXISTS
+ (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1))
+)
+AND
+ nspname NOT LIKE E'pg\\temp\\%'
+AND
+ nspname NOT LIKE E'pg\\toast_temp\\%'
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/templates/catalogobject/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/templates/catalogobject/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..13a6db7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/templates/catalogobject/sql/9.1_plus/nodes.sql
@@ -0,0 +1,9 @@
+SELECT c.oid, c.relname as name
+ FROM pg_class c
+WHERE relnamespace = {{scid}}::int
+OR (-- On EnterpriseDB we need to ignore some objects in the catalog, namely, _*, dual and type_object_source.
+ select 'sys' ~ (SELECT nsp.nspname FROM pg_namespace nsp WHERE nsp.oid = {{scid}}::int)
+ AND
+ (c.relname NOT LIKE '\\_%' AND c.relname = 'dual' AND c.relname = 'type_object_source')
+ )
+ORDER BY relname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/templates/catalogobject/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/templates/catalogobject/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..5cf3e96
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalogobjects/templates/catalogobject/sql/9.1_plus/properties.sql
@@ -0,0 +1,13 @@
+SELECT c.oid, c.relname as name, pg_get_userbyid(relowner) AS owner, description
+ FROM pg_class c
+LEFT OUTER JOIN pg_description d ON (d.objoid=c.oid AND d.classoid='pg_class'::regclass)
+WHERE relnamespace = {{scid}}::int
+{% if coid %}
+AND c.oid = {{coid}}::int
+{% endif %}
+OR (-- On EnterpriseDB we need to ignore some objects in the catalog, namely, _*, dual and type_object_source.
+ select 'sys' ~ (SELECT nsp.nspname FROM pg_namespace nsp WHERE nsp.oid = {{scid}}::int)
+ AND
+ (c.relname NOT LIKE '\\_%' AND c.relname = 'dual' AND c.relname = 'type_object_source')
+ )
+ORDER BY relname;
\ No newline at end of file
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
@ 2016-03-10 19:41 ` Ashesh Vashi <[email protected]>
1 sibling, 0 replies; 26+ messages in thread
From: Ashesh Vashi @ 2016-03-10 19:41 UTC (permalink / raw)
To: Murtuza Zabuawala <[email protected]>; +Cc: pgadmin-hackers
Committed the catalog_objects with few changes:
- Renamed to catalog_objects
- columns under it were renamed to catalog_object_column
- Some of the query were specific to PPAS moved them to specific SQL
templates.
--
Thanks & Regards,
Ashesh Vashi
EnterpriseDB INDIA: Enterprise PostgreSQL Company
<http://www.enterprisedb.com;
*http://www.linkedin.com/in/asheshvashi*
<http://www.linkedin.com/in/asheshvashi;
On Tue, Mar 8, 2016 at 7:08 PM, Murtuza Zabuawala <
[email protected]> wrote:
> Hi,
>
> PFA patch to add new nodes in pgAdmin4.
> 1) Type node
> 2) Catalog objects
>
> *Note:* Both above nodes depended on schema/catalog node, Please apply
> them after latest patch of schema/catalog from Ashesh.
> - Type node also depends on parse_priv_function_templates.patch (which I
> sent in separate email today)
>
>
> --
> Regards,
> Murtuza Zabuawala
> EnterpriseDB: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
>
> --
> Sent via pgadmin-hackers mailing list ([email protected])
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgadmin-hackers
>
>
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
@ 2016-03-11 16:11 ` Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
1 sibling, 1 reply; 26+ messages in thread
From: Dave Page @ 2016-03-11 16:11 UTC (permalink / raw)
To: Murtuza Zabuawala <[email protected]>; +Cc: pgadmin-hackers
On Tue, Mar 8, 2016 at 1:38 PM, Murtuza Zabuawala
<[email protected]> wrote:
> Hi,
>
> PFA patch to add new nodes in pgAdmin4.
> 1) Type node
> 2) Catalog objects
>
> Note: Both above nodes depended on schema/catalog node, Please apply them
> after latest patch of schema/catalog from Ashesh.
> - Type node also depends on parse_priv_function_templates.patch (which I
> sent in separate email today)
The type node seems to need quite a bit of work - please review and
test it carefully before re-submitting. There's an updated patch
attached, and some review info below:
Changed in the attached patch:
- s/Oid/OID
- Set defaults for schema and owner
- Rename the Type Defintion group to Definition.
- Moved some properties (acl, members) into the appropriate group
- s/Enumration/Enumeration
To be fixed:
- This module is derived from SchemaChildModule, so why does it still
have the backend support SQL?
- I cleaned up most of the SQL, which was improperly indented in many
places. However I have not event tried to fix up the create.sql
scripts. These need reformatting to:
- Use 4 character indents
- Not start a line with a comma - e.g. " ,FOO=bar", which should be
" FOO=bar," (obviously the commas need to trail from the line
before).
- The "Show System Objects" option is not honoured.
- The members list should be delimited with ", " not ","
- The ACL columns don't match other objects on the subnode panel -
Grantee/Granter/Privileges should be Grantee/Privileges/Granter
- Attempting to add a security label with empty values gives: SECURITY
LABEL FOR None ON TYPE foo_enum IS NULL;
- The schema is omitted from GRANT statements when creating an object, e.g.
CREATE TYPE pem.foo_enum AS ENUM
('foo', 'bar', 'whizz');
ALTER TYPE pem.foo_enum OWNER TO postgres;
COMMENT ON TYPE pem.foo_enum IS 'This is the foo enum';
GRANT ALL ON TYPE foo_enum TO pem_admin;
- Move remaining Properties display properties to the appropriate
group - e.g. ENUM labels should be in the Definition group. Only Name,
OID, Owner, Alias, System Type and Comment should be under General.
- The schema name is omitted when dropping a type, leading to: type
"foo_enum" does not exist
- The dependencies and dependents tabs don't display any data.
Thanks.
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
@ 2016-03-11 16:14 ` Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
0 siblings, 1 reply; 26+ messages in thread
From: Dave Page @ 2016-03-11 16:14 UTC (permalink / raw)
To: Murtuza Zabuawala <[email protected]>; +Cc: pgadmin-hackers
And this time with the patch.
On Fri, Mar 11, 2016 at 4:11 PM, Dave Page <[email protected]> wrote:
> On Tue, Mar 8, 2016 at 1:38 PM, Murtuza Zabuawala
> <[email protected]> wrote:
>> Hi,
>>
>> PFA patch to add new nodes in pgAdmin4.
>> 1) Type node
>> 2) Catalog objects
>>
>> Note: Both above nodes depended on schema/catalog node, Please apply them
>> after latest patch of schema/catalog from Ashesh.
>> - Type node also depends on parse_priv_function_templates.patch (which I
>> sent in separate email today)
>
> The type node seems to need quite a bit of work - please review and
> test it carefully before re-submitting. There's an updated patch
> attached, and some review info below:
>
> Changed in the attached patch:
>
> - s/Oid/OID
> - Set defaults for schema and owner
> - Rename the Type Defintion group to Definition.
> - Moved some properties (acl, members) into the appropriate group
> - s/Enumration/Enumeration
>
> To be fixed:
>
> - This module is derived from SchemaChildModule, so why does it still
> have the backend support SQL?
>
> - I cleaned up most of the SQL, which was improperly indented in many
> places. However I have not event tried to fix up the create.sql
> scripts. These need reformatting to:
> - Use 4 character indents
> - Not start a line with a comma - e.g. " ,FOO=bar", which should be
> " FOO=bar," (obviously the commas need to trail from the line
> before).
>
> - The "Show System Objects" option is not honoured.
>
> - The members list should be delimited with ", " not ","
>
> - The ACL columns don't match other objects on the subnode panel -
> Grantee/Granter/Privileges should be Grantee/Privileges/Granter
>
> - Attempting to add a security label with empty values gives: SECURITY
> LABEL FOR None ON TYPE foo_enum IS NULL;
>
> - The schema is omitted from GRANT statements when creating an object, e.g.
>
> CREATE TYPE pem.foo_enum AS ENUM
> ('foo', 'bar', 'whizz');
>
> ALTER TYPE pem.foo_enum OWNER TO postgres;
>
> COMMENT ON TYPE pem.foo_enum IS 'This is the foo enum';
>
> GRANT ALL ON TYPE foo_enum TO pem_admin;
>
> - Move remaining Properties display properties to the appropriate
> group - e.g. ENUM labels should be in the Definition group. Only Name,
> OID, Owner, Alias, System Type and Comment should be under General.
>
> - The schema name is omitted when dropping a type, leading to: type
> "foo_enum" does not exist
>
> - The dependencies and dependents tabs don't display any data.
>
> Thanks.
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/octet-stream] type_node_v1-dave.patch (130.6K, 2-type_node_v1-dave.patch)
download | inline diff:
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
new file mode 100644
index 0000000..1b34ece
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/__init__.py
@@ -0,0 +1,1170 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+""" Implements Type Node """
+
+import json
+from flask import render_template, make_response, request, jsonify
+from flask.ext.babel import gettext
+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 pgadmin.browser.server_groups.servers.databases as database
+from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
+ parse_priv_to_db
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class TypeModule(SchemaChildModule):
+ """
+ class TypeModule(SchemaChildModule)
+
+ A module class for Type node derived from SchemaChildModule
+
+ Methods:
+ -------
+ * __init__(*args, **kwargs)
+ - Method is used to initialize the Type and it's base module.
+
+ * get_nodes(gid, sid, did, scid, tid)
+ - Method is used to generate the browser collection node.
+
+ * node_inode()
+ - Method is overridden from its base class to make the node as leaf node.
+
+ * script_load()
+ - Load the module script for schema, when any of the server node is
+ initialized.
+ """
+
+ NODE_TYPE = 'type'
+ COLLECTION_LABEL = gettext("Types")
+
+ def __init__(self, *args, **kwargs):
+ """
+ Method is used to initialize the TypeModule and it's base module.
+
+ Args:
+ *args:
+ **kwargs:
+ """
+ super(TypeModule, self).__init__(*args, **kwargs)
+ self.min_ver = None
+ self.max_ver = None
+
+ def get_nodes(self, gid, sid, did, scid):
+ """
+ Generate the collection node
+ """
+ yield self.generate_browser_collection_node(scid)
+
+ @property
+ def script_load(self):
+ """
+ Load the module script for database, when any of the database node is
+ initialized.
+ """
+ return database.DatabaseModule.NODE_TYPE
+
+ @property
+ def node_inode(self):
+ """
+ Load the module node as a leaf node
+ """
+ return False
+
+ @property
+ def show_system_objects(self):
+ """
+ Flag to set show system Type
+ """
+ return True
+
+blueprint = TypeModule(__name__)
+
+
+class TypeView(PGChildNodeView):
+ """
+ This class is responsible for generating routes for Type node
+
+ Methods:
+ -------
+ * __init__(**kwargs)
+ - Method is used to initialize the TypeView and it's base view.
+
+ * check_precondition()
+ - This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+
+ * list()
+ - This function is used to list all the Type nodes within that
+ collection.
+
+ * nodes()
+ - This function will used to create all the child node within that
+ collection, Here it will create all the Type node.
+
+ * properties(gid, sid, did, scid, tid)
+ - This function will show the properties of the selected Type node
+
+ * create(gid, sid, did, scid)
+ - This function will create the new Type object
+
+ * update(gid, sid, did, scid, tid)
+ - This function will update the data for the selected Type node
+
+ * delete(self, gid, sid, scid, tid):
+ - This function will drop the Type object
+
+ * msql(gid, sid, did, scid, tid)
+ - This function is used to return modified SQL for the selected
+ Type node
+
+ * get_sql(data, scid, tid)
+ - This function will generate sql from model data
+
+ * sql(gid, sid, did, scid):
+ - This function will generate sql to show it in sql pane for the
+ selected Type node.
+
+ * dependency(gid, sid, did, scid, tid):
+ - This function will generate dependency list show it in dependency
+ pane for the selected Type node.
+
+ * dependent(gid, sid, did, scid, tid):
+ - This function will generate dependent list to show it in dependent
+ pane for the selected Type node.
+
+ * additional_properties(copy_dict, tid):
+ - This function will add additional properties in response
+
+ * get_collations(gid, sid, did, scid, tid):
+ - This function will return list of collation in ajax response
+
+ * get_types(gid, sid, did, scid, tid):
+ - This function will return list of types in ajax response
+
+ * get_subtypes(gid, sid, did, scid, tid):
+ - This function will return list of subtypes in ajax response
+
+ * get_subtype_opclass(gid, sid, did, scid, tid):
+ - This function will return list of subtype opclass in ajax response
+
+ * get_subtype_diff(gid, sid, did, scid, tid):
+ - This function will return list of subtype diff functions
+ in ajax response
+
+ * get_canonical(gid, sid, did, scid, tid):
+ - This function will return list of canonical functions
+ in ajax response
+
+ * get_external_functions_list(gid, sid, did, scid, tid):
+ - This function will return list of external functions
+ in ajax response
+ """
+
+ node_type = blueprint.node_type
+
+ parent_ids = [
+ {'type': 'int', 'id': 'gid'},
+ {'type': 'int', 'id': 'sid'},
+ {'type': 'int', 'id': 'did'},
+ {'type': 'int', 'id': 'scid'}
+ ]
+ ids = [
+ {'type': 'int', 'id': 'tid'}
+ ]
+
+ operations = dict({
+ 'obj': [
+ {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+ {'get': 'list', 'post': 'create'}
+ ],
+ 'delete': [{'delete': 'delete'}],
+ 'children': [{'get': 'children'}],
+ 'nodes': [{'get': 'node'}, {'get': 'nodes'}],
+ 'sql': [{'get': 'sql'}],
+ 'msql': [{'get': 'msql'}, {'get': 'msql'}],
+ 'stats': [{'get': 'statistics'}],
+ 'dependency': [{'get': 'dependencies'}],
+ 'dependent': [{'get': 'dependents'}],
+ 'module.js': [{}, {}, {'get': 'module_js'}],
+ 'get_types': [{'get': 'get_types'}, {'get': 'get_types'}],
+ 'get_stypes': [{'get': 'get_subtypes'}, {'get': 'get_subtypes'}],
+ 'get_subopclass': [{'get': 'get_subtype_opclass'},
+ {'get': 'get_subtype_opclass'}],
+ 'get_stypediff': [{'get': 'get_subtype_diff'}, {'get': 'get_subtype_diff'}],
+ 'get_canonical': [{'get': 'get_canonical'}, {'get': 'get_canonical'}],
+ 'get_collations': [{'get': 'get_collations'}, {'get': 'get_collations'}],
+ 'get_external_functions': [{'get': 'get_external_functions_list'},
+ {'get': 'get_external_functions_list'}]
+ })
+
+ def check_precondition(f):
+ """
+ This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+ """
+ @wraps(f)
+ def wrap(*args, **kwargs):
+ # Here args[0] will hold self & kwargs will hold gid,sid,did
+ self = args[0]
+ self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(kwargs['sid'])
+ self.conn = self.manager.connection(did=kwargs['did'])
+
+ # We need datlastsysoid to check if current type is system type
+ self.datlastsysoid = self.manager.db_info[kwargs['did']]['datlastsysoid']
+
+ # If DB not connected then return error to browser
+ if not self.conn.connected():
+ return precondition_required(
+ gettext(
+ "Connection to the server has been lost!"
+ )
+ )
+
+ # Declare allows acl on type
+ self.acl = ['U']
+
+ # we will set template path for sql scripts
+ ver = self.manager.version
+ if 90000 >= ver < 90100:
+ self.template_path = 'type/sql/pre_9.1'
+ else:
+ self.template_path = 'type/sql/9.1_plus'
+ return f(*args, **kwargs)
+
+ return wrap
+
+ @check_precondition
+ def list(self, gid, sid, did, scid):
+ """
+ This function is used to list all the schema nodes within that collection.
+
+ Args:
+ gid: Server group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of available schema nodes
+ """
+
+ SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid,
+ datlastsysoid=self.datlastsysoid)
+ status, res = self.conn.execute_dict(SQL)
+
+ if not status:
+ return internal_server_error(errormsg=res)
+ return ajax_response(
+ response=res['rows'],
+ status=200
+ )
+
+ @check_precondition
+ def nodes(self, gid, sid, did, scid):
+ """
+ This function will used to create all the child node within that collection.
+ Here it will create all the schema node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of available type child nodes
+ """
+
+ res = []
+ SQL = render_template("/".join([self.template_path,
+ 'nodes.sql']), scid=scid)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=rset)
+
+ for row in rset['rows']:
+ res.append(
+ self.blueprint.generate_browser_node(
+ row['oid'],
+ scid,
+ row['name'],
+ icon="icon-type"
+ ))
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ def additional_properties(self, copy_dict, tid):
+ """
+ We will use this function to add additional properties according to type
+
+ Returns:
+ additional properties for type like range/composite/enum
+
+ """
+ # Fetching type of type
+ of_type = copy_dict['typtype']
+ res = dict()
+ # If type is of Composite then we need to add members list in our output
+ if of_type == 'c':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='c',
+ typrelid=copy_dict['typrelid'])
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # To display in properties
+ properties_list = []
+ # To display in composite collection grid
+ composite_lst = []
+
+ for row in rset['rows']:
+ typelist = ' '.join([row['attname'], row['typname']])
+ if not row['collname'] or (row['collname'] == 'default'
+ and row['collnspname'] == 'pg_catalog'):
+ full_collate = ''
+ collate = ''
+ else:
+ full_collate = get_driver(PG_DEFAULT_DRIVER).qtIdent(
+ self.conn, row['collnspname'], row['collname'])
+ collate = ' COLLATE ' + full_collate
+ typelist += collate
+ properties_list.append(typelist)
+
+ # Below logic will allow us to split length, precision from type name for grid
+ import re
+ matchObj = re.match( r'(.*)\((.*?),(.*?)\)', row['typname'])
+ if matchObj:
+ t_name = matchObj.group(1)
+ t_len = matchObj.group(2)
+ t_prec = matchObj.group(3)
+ else:
+ t_name = row['typname']
+ t_len = None
+ t_prec = None
+
+ composite_lst.append({
+ 'member_name': row['attname'], 'type': t_name, 'collation': full_collate,
+ 'tlength': t_len, 'precision': t_prec })
+
+ # Adding both results
+ res['member_list'] = ','.join(properties_list)
+ res['composite'] = composite_lst
+
+ # If type is of ENUM then we need to add labels in our output
+ if of_type == 'e':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='e', tid=tid)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ # To display in properties
+ properties_list = []
+ # To display in enum grid
+ enum_list = []
+ for row in rset['rows']:
+ properties_list.append(row['enumlabel'])
+ enum_list.append({'label': row['enumlabel']})
+
+ # Adding both results in ouput
+ res['enum_list'] = ','.join(properties_list)
+ res['enum'] = enum_list
+
+ # If type is of Range then we need to add collation,subtype etc in our output
+ if of_type == 'r':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='r', tid=tid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ range_dict = dict(res['rows'][0])
+ res.update(range_dict)
+
+ # Returning only additional properties only
+ return res
+
+ @check_precondition
+ def properties(self, gid, sid, did, scid, tid):
+ """
+ This function will show the properties of the selected schema node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of selected schema node
+ """
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ copy_dict = dict(res['rows'][0])
+
+ # We need to parse & convert ACL coming from database to json format
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ copy_dict['typacl'] = []
+
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in copy_dict:
+ copy_dict[row['deftype']].append(priv)
+ else:
+ copy_dict[row['deftype']] = [priv]
+
+ # Calling function to check and additional properties if available
+ copy_dict.update(self.additional_properties(copy_dict, tid))
+
+ return ajax_response(
+ response=copy_dict,
+ status=200
+ )
+
+ @check_precondition
+ def get_collations(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of collation available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_collations.sql']))
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['collation'],
+ 'value': row['collation']}
+ )
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_types(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of types available
+ as AJAX response.
+ """
+ res = []
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_types.sql']))
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ # Attaching properties for precession
+ # & length validation for current type
+ precision = False
+ length = False
+ min_val = 0
+ max_val = 0
+
+ # Check against PGOID for specific type
+ if row['elemoid']:
+ if row['elemoid'] in (1560, 1561, 1562, 1563, 1042, 1043,
+ 1014, 1015):
+ typeval = 'L'
+ elif row['elemoid'] in (1083, 1114, 1115, 1183, 1184, 1185,
+ 1186, 1187, 1266, 1270):
+ typeval = 'D'
+ elif row['elemoid'] in (1231, 1700):
+ typeval = 'P'
+ else:
+ typeval = ' '
+
+ # Logic to set precision & length/min/max values
+ if typeval == 'P':
+ precision = True
+
+ if precision or typeval in ('L', 'D'):
+ length = True
+ min_val = 0 if typeval == 'D' else 1
+ if precision:
+ max_val = 1000
+ elif min_val:
+ # Max of integer value
+ max_val = 2147483647
+ else:
+ max_val = 10
+
+ res.append(
+ {'label': row['typname'], 'value': row['typname'],
+ 'typval': typeval, 'precision': precision,
+ 'length': length, 'min_val': min_val, 'max_val': max_val
+ }
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtypes(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtypes available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ subtype=True)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['stype'], 'value': row['stype'],
+ 'is_collate': row['is_collate']}
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtype_opclass(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtype opclass available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ subtype_opclass=True, data=data)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['opcname'],
+ 'value': row['opcname']})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtype_diff(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtypes diff functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ get_opcintype=True, data=data)
+ if SQL:
+ status, opcintype = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=opcintype)
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ opcintype=opcintype, conn=self.conn)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['stypdiff'],
+ 'value': row['stypdiff']}
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_canonical(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of canonical functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+ canonical = True
+
+ try:
+ # We want to send data only if in we are in edit mode
+ # else we will disable the combobox
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ getoid=True, data=data)
+ if SQL:
+ status, oid = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=oid)
+ # If oid is None then do not run SQL
+ if oid is None:
+ canonical = False
+
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ canonical=canonical, conn=self.conn, oid=oid)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['canonical'],
+ 'value': row['canonical']})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def get_external_functions_list(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of external functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': '', 'cbtype': 'all'}]
+
+ try:
+ # The SQL generated below will populate Input/Output/Send/
+ # Receive/Analyze/TypModeIN/TypModOUT combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ extfunc=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'all'})
+
+ # The SQL generated below will populate TypModeIN combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ typemodin=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'typmodin'})
+
+ # The SQL generated below will populate TypModeIN combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ typemodout=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'typmodout'})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def create(self, gid, sid, did, scid):
+ """
+ This function will creates new the schema object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ data = request.form if request.form else json.loads(request.data.decode())
+ required_args = {
+ 'name': 'Name',
+ 'typtype': 'Type'
+ }
+
+ for arg in required_args:
+ if arg not in data:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ "Couldn't find the required parameter (%s)." %
+ required_args[arg]
+ )
+ )
+ # Additional checks goes here
+ # If type is composite then check if it has two members
+ if data and data[arg] == 'c':
+ if len(data['composite']) < 2:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Composite type requires at least two members'
+ )
+ )
+ # If type is enum then check if it has minimum one label
+ if data and data[arg] == 'e':
+ if len(data['enum']) < 1:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Enumeration type requires at least one label'
+ )
+ )
+ # If type is enum then check if it has minimum one label
+ if data and data[arg] == 'r':
+ if data['typname'] is None:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Subtype must be defined for range type'
+ )
+ )
+ # If type is external then check if input/output
+ # conversion function is defined
+ if data and data[arg] == 'x':
+ if data['typinput'] is None or \
+ data['typoutput'] is None:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'External type requires both Input & Output conversion function.'
+ )
+ )
+
+ # To format privileges coming from client
+ if 'typacl' in data and data['typacl'] is not None:
+ data['typacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ try:
+ SQL = render_template("/".join([self.template_path, 'create.sql']),
+ data=data, conn=self.conn)
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # we need oid to to add object in tree at browser
+ SQL = render_template("/".join([self.template_path,
+ 'get_oid.sql']),
+ scid=scid, data=data)
+ status, tid = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=tid)
+
+ return jsonify(
+ node=self.blueprint.generate_browser_node(
+ tid,
+ scid,
+ data['name'],
+ icon="icon-type"
+ )
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def update(self, gid, sid, did, scid, tid):
+ """
+ This function will updates existing the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+
+ data = request.form if request.form else json.loads(request.data.decode())
+ try:
+ SQL = self.get_sql(gid, sid, data, scid, tid)
+ if SQL and SQL.strip('\n') and SQL.strip(' '):
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return make_json_response(
+ success=1,
+ info="Type updated",
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+ else:
+ return make_json_response(
+ success=1,
+ info="Nothing to update",
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def delete(self, gid, sid, did, scid, tid):
+ """
+ This function will updates existing the schema object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+
+ # Below will decide if it's simple drop or drop with cascade call
+ if self.cmd == 'delete':
+ # This is a cascade operation
+ cascade = True
+ else:
+ cascade = False
+
+ try:
+ SQL = render_template("/".join([self.template_path, 'get_name.sql']),
+ scid=scid, tid=tid)
+ status, name = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=name)
+
+ SQL = render_template("/".join([self.template_path, 'delete.sql']),
+ name=name, cascade=cascade, conn=self.conn)
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return make_json_response(
+ success=1,
+ info=gettext("Type dropped"),
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def msql(self, gid, sid, did, scid, tid=None):
+ """
+ This function will generates modified sql for type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ req = request.args
+ data = dict()
+
+ # converting nested request data in proper json format
+ for key,val in req.items():
+ if key in ['composite', 'enum', 'seclabels', 'typacl']:
+ data[key] = json.loads(val)
+ else:
+ data[key] = val
+
+ try:
+ SQL = self.get_sql(gid, sid, data, scid, tid)
+
+ if SQL and SQL.strip('\n') and SQL.strip(' '):
+ return make_json_response(
+ data=SQL,
+ status=200
+ )
+ except Exception as e:
+ internal_server_error(errormsg=str(e))
+
+ def get_sql(self, gid, sid, data, scid, tid=None):
+ """
+ This function will genrate sql from model data
+ """
+ if tid is not None:
+
+ for key in ['typacl']:
+ if key in data and data[key] is not None:
+ if 'added' in data[key]:
+ data[key]['added'] = parse_priv_to_db(data[key]['added'], self.acl)
+ if 'changed' in data[key]:
+ data[key]['changed'] = parse_priv_to_db(data[key]['changed'], self.acl)
+ if 'deleted' in data[key]:
+ data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], self.acl)
+
+ SQL = render_template("/".join([self.template_path, 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ old_data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ old_data['typacl'] = []
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in old_data:
+ old_data[row['deftype']].append(priv)
+ else:
+ old_data[row['deftype']] = [priv]
+
+ # Calling function to check and additional properties if available
+ old_data.update(self.additional_properties(old_data, tid))
+
+ SQL = render_template(
+ "/".join([self.template_path, 'update.sql']),
+ data=data, o_data=old_data, conn=self.conn
+ )
+ else:
+ required_args = [
+ 'name',
+ 'typtype'
+ ]
+
+ for arg in required_args:
+ if arg not in data:
+ return " --definition incomplete"
+
+ # Privileges
+ if 'typacl' in data and data['typacl'] is not None:
+ data['typacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ SQL = render_template("/".join([self.template_path,
+ 'create.sql']),
+ data=data, conn=self.conn)
+
+ return SQL
+
+
+ @check_precondition
+ def sql(self, gid, sid, did, scid, tid):
+ """
+ This function will generates reverse engineered sql for schema object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ SQL = render_template("/".join([self.template_path, 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ data['typacl'] = []
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in data:
+ data[row['deftype']].append(priv)
+ else:
+ data[row['deftype']] = [priv]
+
+ # Privileges
+ if 'typacl' in data and data['typacl'] is not None:
+ data['nspacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ # Calling function to check and additional properties if available
+ data.update(self.additional_properties(data, tid))
+
+ # We do not want to display table which has '-' value
+ # setting them to None so that jinja avoid displaying them
+ for k in data:
+ if data[k] == '-':
+ data[k] = None
+
+ SQL = self.get_sql(gid, sid, data, scid, tid=None)
+
+ # We are appending headers here for sql panel
+ sql_header = "-- Type: {0}\n\n-- ".format(data['name'])
+ sql_header += render_template("/".join([self.template_path,
+ 'delete.sql']),
+ name=data['name'], conn=self.conn)
+ SQL = sql_header + '\n' + SQL
+
+ return ajax_response(response=SQL)
+
+ @check_precondition
+ def dependents(self, gid, sid, did, scid, tid):
+ """
+ This function get the dependents and return ajax response
+ for the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ dependents_result = self.get_dependents(
+ self.conn, tid
+ )
+
+ return ajax_response(
+ response=dependents_result,
+ status=200
+ )
+
+ @check_precondition
+ def dependencies(self, gid, sid, did, scid, tid):
+ """
+ This function get the dependencies and return ajax response
+ for the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ dependencies_result = self.get_dependencies(
+ self.conn, tid
+ )
+
+ return ajax_response(
+ response=dependencies_result,
+ status=200
+ )
+
+TypeView.register_node_view(blueprint)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/coll-type.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/coll-type.png
new file mode 100644
index 0000000000000000000000000000000000000000..fb020d7d99f84439046d288615e865ee1fbdb815
GIT binary patch
literal 329
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=3NcXI=aM>fHaQT@Q91
z`hS1R{~OExU!3;;MEm~(mH&6<{olN3-cDJdI>wS9zhDN3XE)M-9L@rd$YLPv0mg18
zv+aP47*7|+5RU7%XQO!=40zlgo^wo$EU!My#3j(ks*}LT9dUr^nFV*x`<LfEXLU&{
zNT_QZ){p11jr8BRJf*J9t1q$D%X6FK(q}uIk4`OV_&v+tAZF)<c~_)!|NM{V=e#WX
zN#J9vG0+~>64!{5l*E!$tK_0oAjM#0U}&IgXryak7-D2#Wnye)VybOmYGq(B@15Q%
q6b-rgDVb@N5Df;FU=2XkCRPS!5DllMhpqu?VDNPHb6Mw<&;$UZ$8*O3
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/type.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/type.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c16764e7d08c56922a97a2f0c6cc06455c86a56
GIT binary patch
literal 325
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv5AX?b1=8~#9EmzT>)QWU=l(xE
z`v2js|F_rtzdY~%nF;@oHvQjQ{(pPk|IMk)0@*;Nj3q&S!3+-1ZlnP@oCO|{#X#Bv
zjNMLV+W{G&o-U3d9M_W*4zM_RIV|8v(U4|t6r8Z|5fh7_LtB=H01KmJR;I%QmXsCK
znH_-=7a3W69onAxD9m6<$ym$O<m%A&El=SFOUjEmj7`o4+9Dfn@G_j<Bfar~Z-)!e
z0@V`Nh?11Vl2ohYqEsNoU}RuuplfKPYhV~+WME}tY-M7qZD49;U@-5U-YOIgx%nxX
kX_XKS29{tAK-DHz24)Zqr>2Ll0cv3IboFyt=akR{01h5&qyPW_
literal 0
HcmV?d00001
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
new file mode 100644
index 0000000..eecce4e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/js/type.js
@@ -0,0 +1,856 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'pgadmin',
+ 'pgadmin.browser', 'alertify', 'backgrid', 'pgadmin.backgrid',
+ 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
+
+ if (!pgBrowser.Nodes['coll-type']) {
+ var databases = pgAdmin.Browser.Nodes['coll-type'] =
+ pgAdmin.Browser.Collection.extend({
+ node: 'type',
+ label: '{{ _('Types') }}',
+ type: 'coll-type',
+ columns: ['name', 'typeowner', 'description']
+ });
+ };
+
+ // Switch options to save space in model's field
+ var switchOptions = {
+ 'onText': 'Yes', 'offText': 'No',
+ 'onColor': 'success', 'offColor': 'default',
+ 'size': 'small'
+ };
+
+ // Security label model declaration
+ var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ provider: null,
+ security_label: null
+ },
+ schema: [{
+ id: 'provider', label: '{{ _('Provider') }}',
+ type: 'text', disabled: false
+ },{
+ id: 'security_label', label: '{{ _('Security Label') }}',
+ type: 'text', disabled: false
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+
+ if (_.isUndefined(data.label) ||
+ _.isNull(data.label) ||
+ String(data.label).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = _("Please specify the value for all the security providers.");
+ this.errorModel.set('security_label', errmsg);
+ } else {
+ this.errorModel.unset('security_label');
+ }
+ return null;
+ }
+ });
+
+ // Composite type model declaration
+ var CompositeModel = Backform.CompositeModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ member_name: undefined,
+ type: undefined,
+ tlength: undefined,
+ is_tlength: false,
+ precision: undefined,
+ is_precision: false,
+ collation: undefined,
+ min_val: undefined,
+ max_val: undefined,
+ },
+ type_options: undefined,
+ subtypes: undefined,
+ schema: [{
+ id: 'member_name', label: '{{ _('Member Name') }}',
+ type: 'text', disabled: false, editable: false
+ },{
+ id: 'type', label: '{{ _('Type') }}', control: 'node-ajax-options',
+ type: 'text', url: 'get_types', disabled: false, node: 'type',
+ editable: false,
+ transform: function(d){
+ this.model.type_options = d;
+ return d;
+ }
+ },{
+ id: 'tlength', label: '{{ _('Length') }}', deps: ['type'], type: 'text',
+ editable: false,
+ disabled: function(m) {
+ // We will store type from selected from combobox
+ var of_type = m.get('type');
+ if(m.type_options) {
+ // iterating over all the types
+ _.each(m.type_options, function(o) {
+ // if type from selected from combobox matches in options
+ if ( of_type == o.value ) {
+ // if length is allowed for selected type
+ if(o.length)
+ {
+ // set the values in model
+ m.set('is_tlength', true, {silent: true});
+ m.set('min_val', o.min_val, {silent: true});
+ m.set('max_val', o.max_val, {silent: true});
+ }
+ }
+ });
+ }
+ return !m.get('is_tlength');
+ }
+ },{
+ id: 'precision', label: '{{ _('Precision') }}', deps: ['type'],
+ type: 'text', editable: false,
+ disabled: function(m) {
+ // We will store type from selected from combobox
+ var of_type = m.get('type');
+ if(m.type_options) {
+ // iterating over all the types
+ _.each(m.type_options, function(o) {
+ // if type from selected from combobox matches in options
+ if ( of_type == o.value ) {
+ // if precession is allowed for selected type
+ if(o.precision)
+ {
+ // set the values in model
+ m.set('is_precision', true, {silent: true});
+ m.set('min_val', o.min_val, {silent: true});
+ m.set('max_val', o.max_val, {silent: true});
+ }
+ }
+ });
+ }
+ return !m.get('is_precision');
+ }
+ },{
+ id: 'collation', label: '{{ _('Collation') }}',
+ control: 'node-ajax-options', editable: false,
+ type: 'text', disabled: false, url: 'get_collations', node: 'type'
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+ // Validation for member name
+ if (_.isUndefined(data.member_name) ||
+ _.isNull(data.member_name) ||
+ String(data.member_name).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = _("Please specify the value for member name.")
+ this.errorModel.set('member_name', errmsg)
+ return errmsg;
+ } else {
+ this.errorModel.unset('member_name');
+ }
+
+ // Validation for Length field
+ if (data.is_tlength && !_.isUndefined(data.tlength)) {
+ if (data.tlength < data.min_val )
+ errmsg = _("Length should not be less than " + data.min_val)
+ if (data.tlength > data.max_val )
+ errmsg = _("Length should not be greater than " + data.max_val)
+ // If we have any error set then throw it to user
+ if(errmsg) {
+ this.errorModel.set('tlength', errmsg)
+ return errmsg;
+ }
+ } else {
+ this.errorModel.unset('tlength');
+ }
+
+ // Validation for precision field
+ if (data.is_precision && !_.isUndefined(data.precision)) {
+ if (data.precision < data.min_val )
+ errmsg = _("Precision should not be less than " + data.min_val)
+ if (data.precision > data.max_val )
+ errmsg = _("Precision should not be greater than " + data.max_val)
+ // If we have any error set then throw it to user
+ if(errmsg) {
+ this.errorModel.set('precision', errmsg)
+ return errmsg;
+ }
+ } else {
+ this.errorModel.unset('precision');
+ }
+
+ return null;
+ }
+ });
+
+ var EnumModel = Backform.EnumModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ label: null,
+ },
+ schema: [{
+ id: 'label', label: '{{ _('Label') }}',type: 'text', disabled: false,
+ cellHeaderClasses: 'width_percent_99'
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+
+ if (_.isUndefined(data.label) ||
+ _.isNull(data.label) ||
+ String(data.label).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = _("Please specify the value for label.");
+ this.errorModel.set('label', errmsg)
+ return errmsg;
+ } else {
+ this.errorModel.unset('label');
+ }
+
+
+ return null;
+ }
+ });
+
+ if (!pgBrowser.Nodes['type']) {
+ pgAdmin.Browser.Nodes['type'] = pgBrowser.Node.extend({
+ type: 'type',
+ label: '{{ _('Type') }}',
+ collection_type: 'coll-type',
+ hasSQL: true,
+ hasDepends: true,
+ parent_type: ['schema', 'catalog'],
+ Init: function() {
+ /* Avoid mulitple registration of menus */
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+
+ pgBrowser.add_menus([{
+ name: 'create_type_on_coll', node: 'coll-type', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_type', node: 'type', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_type', node: 'schema', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: false},
+ enable: 'canCreate'
+ }
+ ]);
+
+ },
+ canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+ canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+ ext_funcs: undefined,
+ model: pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ name: undefined,
+ oid: undefined,
+ is_sys_type: false,
+ typtype: undefined
+ },
+
+ // Default values!
+ initialize: function(attrs, args) {
+ var isNew = (_.size(attrs) === 0);
+
+ if (isNew) {
+ var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
+ var schemaInfo = args.node_info.schema;
+
+ this.set({'typeowner': userInfo.name}, {silent: true});
+ this.set({'schema': schemaInfo.label}, {silent: true});
+ }
+ pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+ },
+
+ schema: [{
+ id: 'name', label: '{{ _('Name') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchema'
+ },{
+ id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+ type: 'text' , mode: ['properties'], disabled: true
+ },{
+ id: 'typeowner', label:'{{ _('Owner') }}', cell: 'string',
+ control: 'node-list-by-name',
+ type: 'text', mode: ['properties', 'create', 'edit'], node: 'role',
+ disabled: 'inSchema'
+ },{
+ id: 'schema', label:'{{ _('Schema') }}', cell: 'string',
+ type: 'text', mode: ['create', 'edit'], node: 'schema',
+ disabled: 'inSchema', filter: function(d) {
+ // If schema name start with pg_* then we need to exclude them
+ if(d && d.label.match(/^pg_/))
+ {
+ return false;
+ }
+ return true;
+ },
+ control: Backform.NodeListByNameControl.extend({
+ render: function(){
+ // Initialize parent's render method
+ Backform.NodeListByNameControl.prototype.render.apply(this, arguments);
+
+ // Set schema default value to its parent Schema
+ if(this.model.isNew()){
+ this.model.set({'schema': this.model.node_info.schema.label});
+ }
+ return this;
+ }
+ })
+ },{
+ id: 'typtype', label:'{{ _('Type') }}',
+ mode: ['create','edit'], disabled: 'inSchemaWithModelCheck',
+ group: '{{ _('Definition') }}',
+ mode: ['edit', 'create'],
+ select2: { width: "50%" },
+ options: [
+ {label: "Composite", value: "c"},
+ {label: "Enumeration", value: "e"},
+ {label: "External", value: "x"},
+ {label: "Range", value: "r"},
+ ],
+ disabled: 'inSchemaWithModelCheck',
+ // If create mode then by default open composite type
+ control: Backform.Select2Control.extend({
+ render: function(){
+ // Initialize parent's render method
+ Backform.Select2Control.prototype.render.apply(this, arguments);
+ if(this.model.isNew()) {
+ this.model.set({'typtype': 'c'});
+ }
+ return this;
+ }
+ })
+ },{
+ id: 'composite', label: '{{ _('Composite Type') }}',
+ model: CompositeModel, editable: true, type: 'collection',
+ group: '{{ _('Definition') }}', mode: ['edit', 'create'],
+ control: 'unique-col-collection', uniqueCol : ['member_name'],
+ canAdd: true, canEdit: true, canDelete: true, disabled: 'inSchema',
+ deps: ['typtype'], deps: ['typtype'],
+ visible: function(m) {
+ if (m.get('typtype') === 'c') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'enum', label: '{{ _('Enumeration Type') }}',
+ model: EnumModel, editable: true, type: 'collection',
+ group: '{{ _('Definition') }}', mode: ['edit', 'create'],
+ canAdd: true, canEdit: false, canDelete: function(m) {
+ // We will disable it if it's in 'edit' mode
+ if (m.isNew()) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ disabled: 'inSchema', deps: ['typtype'],
+ control: 'unique-col-collection', uniqueCol : ['label'],
+ visible: function(m) {
+ return m.get('typtype') === 'e';
+ }
+ },{
+ // We will disable range type control in edit mode
+ type: 'nested', control: 'fieldset', group: '{{ _('Definition') }}',
+ mode: ['edit', 'create'],
+ visible: function(m) {
+ return m.get('typtype') === 'r';
+ }, deps: ['typtype'], label: '{{ _('Range Type') }}',
+ schema:[{
+ id: 'typname', label:'{{ _('Subtype') }}', cell: 'string',
+ control: 'node-ajax-options',
+ url: 'get_stypes', type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}', disabled: 'inSchemaWithModelCheck',
+ transform: function(d){
+ this.model.subtypes = d;
+ return d;
+ }
+ },{
+ id: 'opcname', label:'{{ _('Subtype OpClass') }}', cell: 'string',
+ mode: ['properties', 'create', 'edit'], group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['typname'],
+ control: 'select', options: function() {
+ var l_typname = this.model.get('typname'),
+ self = this,
+ result = [];
+ if(!_.isUndefined(l_typname) && l_typname != '')
+ {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_subopclass', this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {'typname' : l_typname},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error', self.model, self.field);
+ }
+ });
+ //
+ }
+ return result;
+ }
+ },{
+ id: 'collname', label:'{{ _('Collation') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ deps: ['typname'], control: 'node-ajax-options', url: 'get_collations',
+ disabled: function(m) {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+
+ // Disbale in edit mode
+ if (!m.isNew()) {
+ return true;
+ }
+
+ // To check if collation is allowed?
+ var of_subtype = m.get('typname'),
+ is_collate = undefined;
+ if(!_.isUndefined(of_subtype)) {
+ // iterating over all the types
+ _.each(m.subtypes, function(s) {
+ // if subtype from selected from combobox matches
+ if ( of_subtype === s.label ) {
+ // if collation is allowed for selected subtype
+ // then enable it else disable it
+ is_collate = s.is_collate;
+ }
+ });
+ }
+ // If is_collate is true then do not disable
+ return is_collate ? false : true;
+ }
+ },{
+ id: 'rngcanonical', label:'{{ _('Canonical') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['name', 'typname'],
+ control: 'select', options: function() {
+ var name = this.model.get('name'),
+ self = this,
+ result = [];
+
+ if(!_.isUndefined(name) && name != '')
+ {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_canonical', this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {"name" : name},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error',
+ self.model, self.field);
+ }
+ });
+ }
+ return result;
+ }
+ },{
+ id: 'rngsubdiff', label:'{{ _('SubType diff') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['opcname'],
+ control: 'select', options: function() {
+ var l_typname = this.model.get('typname'),
+ l_opcname = this.model.get('opcname'),
+ self = this,
+ result = [];
+
+ if(!_.isUndefined(l_typname) && l_typname != '' &&
+ !_.isUndefined(l_opcname) && l_opcname != '') {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_stypediff',
+ this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {'typname' : l_typname, 'opcname': l_opcname},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error',
+ self.model, self.field);
+ }
+ });
+ }
+ return result;
+ }
+ }]
+ },{
+ type: 'nested', control: 'tab', group: '{{ _('Definition') }}',
+ label: '{{ _('External Type') }}', deps: ['typtype'],
+ mode: ['create', 'edit'],
+ visible: function(m) {
+ return m.get('typtype') === 'x';
+ },
+ schema:[{
+ id: 'typinput', label:'{{ _('Input function') }}',
+ cell: 'string',type: 'text',
+ mode: ['properties', 'create', 'edit'], group: 'Required',
+ disabled: 'inSchemaWithModelCheck',
+ control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo'
+ },{
+ id: 'typoutput', label:'{{ _('Output function') }}',
+ cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Required',
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo'
+ },{
+ id: 'typreceive', label:'{{ _('Receive function') }}',
+ cell: 'string', type: 'text', group: 'Optional-1',
+ mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo'
+ },{
+ id: 'typsend', label:'{{ _('Send function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo'
+ },{
+ id: 'typmodin', label:'{{ _('Typmod in function') }}',
+ cell: 'string', type: 'text',
+ mode: ['properties', 'create', 'edit'], group: 'Optional-1',
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: function(d) {
+ var result = [{label :"", value : ""}];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype === 'typmodin' || item.cbtype === 'all') {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ },{
+ id: 'typmodout', label:'{{ _('Typmod out function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: function(d) {
+ var result = [{label :"", value : ""}];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype === 'typmodout' || item.cbtype === 'all') {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ },{
+ id: 'typlen', label:'{{ _('Internal length') }}',
+ cell: 'integer', group: 'Optional-1',
+ type: 'int', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'variable', label:'{{ _('Variable') }}', cell: 'switch',
+ group: 'Optional-1', type: 'switch',
+ mode: ['create','edit'], options: switchOptions,
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typdefault', label:'{{ _('Default') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typanalyze', label:'{{ _('Analyze function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo'
+ },{
+ id: 'typcategory', label:'{{ _('Category') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { placeholder: "Select category", allowClear: true,
+ width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label :"Array types", value : "A"},
+ {label :"Boolean types", value : "B"},
+ {label :"Composite types", value : "C"},
+ {label :"Date/time types", value : "D"},
+ {label :"Enum types", value : "E"},
+ {label :"Geometric types", value : "G"},
+ {label :"Network address types", value : "I"},
+ {label :"Numeric types", value : "N"},
+ {label :"Pseudo-types", value : "P"},
+ {label :"String types", value : "S"},
+ {label :"Timespan types", value : "T"},
+ {label :"User-defined types", value : "U"},
+ {label :"Bit-string types", value : "V"},
+ {label :"unknown type", value : "X"}
+ ]
+ },{
+ id: 'typispreferred', label:'{{ _('Prefered') }}', cell: 'switch',
+ type: 'switch', mode: ['properties', 'create','edit'],
+ options: switchOptions, disabled: 'inSchemaWithModelCheck',
+ group: 'Optional-1'
+ },{
+ id: 'element', label:'{{ _('Element') }}', cell: 'string',
+ control: 'node-ajax-options', group: 'Optional-2',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', url: 'get_types'
+ },{
+ id: 'typdelim', label:'{{ _('Delimiter') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Optional-2', disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typalign', label:'{{ _('Alignment') }}',
+ cell: 'string', group: 'Optional-2',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { placeholder: "Select alignment", allowClear: true,
+ width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label: "char", value: "c"},
+ {label: "int2", value: "s"},
+ {label: "in4", value: "i"},
+ {label: "double", value: "d"},
+ ]
+ },{
+ id: 'typstorage', label:'{{ _('Storage') }}',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Optional-2', cell: 'string',
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { placeholder: "Select storage", allowClear: true,
+ width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label: "PLAIN", value: "p"},
+ {label: "MAIN", value: "e"},
+ {label: "EXTERNAL", value: "m"},
+ {label: "EXTENDED", value: "x"},
+ ]
+ },{
+ id: 'typbyval', label:'{{ _('Passed by Value?') }}',
+ cell: 'switch', options: switchOptions,
+ type: 'switch', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', group: 'Optional-2',
+ },{
+ id: 'is_collatable', label:'{{ _('Collatable?') }}',
+ cell: 'switch', min_version: 90100, group: 'Optional-2',
+ type: 'switch', mode: ['properties', 'create', 'edit'],
+ options: switchOptions, disabled: 'inSchemaWithModelCheck'
+ // End of extension tab
+ }]
+ },{
+ id: 'alias', label:'{{ _('Alias') }}', cell: 'string',
+ type: 'text', mode: ['properties'],
+ disabled: 'inSchema'
+ },{
+ id: 'type_acl', label:'{{ _('Privileges') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Security') }}',
+ disabled: 'inSchema'
+ },{
+ id: 'member_list', label:'{{ _('Members') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Definition') }}',
+ disabled: 'inSchema', visible: function(m) {
+ if(m.get('typtype') === 'c') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'enum_list', label:'{{ _('Labels') }}', cell: 'string',
+ type: 'text', mode: ['properties'],
+ disabled: 'inSchema', visible: function(m) {
+ if(m.get('typtype') === 'e') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'is_sys_type', label:'{{ _('System type?') }}', cell: 'switch',
+ type: 'switch', mode: ['properties'], options: switchOptions,
+ disabled: 'inSchema'
+ },{
+ id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+ type: 'multiline', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchema'
+ },{
+ 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'],
+ columns: ['grantee', 'grantor', 'privileges']
+ },{
+ id: 'seclabels', label: '{{ _('Security Labels') }}',
+ model: SecurityModel, editable: false, type: 'collection',
+ group: '{{ _('Security') }}', mode: ['edit', 'create'],
+ min_version: 90200, canAdd: true,
+ canEdit: false, canDelete: true, control: 'unique-col-collection'
+ }],
+ validate: function() {
+ // Validation code for required fields
+ var changedAttrs = this.sessAttrs,
+ msg = undefined;
+
+ if (_.has(changedAttrs, 'name') &&
+ (_.isUndefined(this.get('name'))
+ || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Name can not be empty!') }}';
+ this.errorModel.set('name', msg);
+ } else if (_.has(changedAttrs, 'schema') &&
+ (_.isUndefined(this.get('schema'))
+ || String(this.get('schema')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Schema can not be empty!') }}';
+ this.errorModel.set('schema', msg);
+ } else if (_.has(changedAttrs, 'typtype') &&
+ (_.isUndefined(this.get('typtype'))
+ || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Type can not be empty!') }}';
+ this.errorModel.set('typtype', msg);
+ } else if (this.get('typtype') == 'r' &&
+ _.has(changedAttrs, 'typname')
+ && (_.isUndefined(this.get('typname'))
+ || String(this.get('typname')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Subtype Name can not be empty!') }}';
+ this.errorModel.set('typname', msg);
+ } else if (this.get('typtype') == 'x' &&
+ _.has(changedAttrs, 'typinput')
+ && (_.isUndefined(this.get('typinput'))
+ || String(this.get('typinput')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Input function can not be empty!') }}';
+ this.errorModel.set('typinput', msg);
+ } else if (this.get('typtype') == 'x' &&
+ _.has(changedAttrs, 'typoutput')
+ && (_.isUndefined(this.get('typoutput'))
+ || String(this.get('typoutput')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Output function can not be empty!') }}';
+ this.errorModel.set('typoutput', msg);
+ } else {
+ this.errorModel.unset('name');
+ this.errorModel.unset('schema');
+ this.errorModel.unset('typtype');
+ this.errorModel.unset('typname');
+ this.errorModel.unset('typinput');
+ this.errorModel.unset('typoutput');
+ }
+ return null;
+ },
+ // We will disable everything if we are under catalog node
+ inSchema: function() {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+ return false;
+ },
+ // We will check if we are under schema node & in 'create' mode
+ inSchemaWithModelCheck: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ // We will disbale control if it's in 'edit' mode
+ if (m.isNew()) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+ return true;
+ },
+ // We want to enable only in edit mode
+ inSchemaWithEditMode: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ // We will disbale control if it's in 'edit' mode
+ if (m.isNew()) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+ return true;
+ },
+ // Function will help us to fill combobox
+ external_func_combo: function(d) {
+ var result = [];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype == 'all' ) {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ }),
+ canCreate: function(itemData, item, data) {
+ //If check is false then , we will allow create menu
+ if (data && data.check == false)
+ return true;
+
+ var t = pgBrowser.tree, i = item, d = itemData;
+ // To iterate over tree to check parent node
+ while (i) {
+ // If it is schema then allow user to create table
+ if (_.indexOf(['schema'], d._type) > -1)
+ return true;
+
+ if ('coll-type' == d._type) {
+ //Check if we are not child of catalog
+ prev_i = t.hasParent(i) ? t.parent(i) : null;
+ prev_d = prev_i ? t.itemData(prev_i) : null;
+ if( prev_d._type == 'catalog') {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ i = t.hasParent(i) ? t.parent(i) : null;
+ d = i ? t.itemData(i) : null;
+ }
+ // by default we do not want to allow create menu
+ return true;
+ }
+ });
+ }
+ return pgBrowser.Nodes['type'];
+});
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql
new file mode 100644
index 0000000..2915e3a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql
@@ -0,0 +1,29 @@
+SELECT 'typacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'USAGE' THEN 'U'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT t.typacl
+ FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 %}
+ ) acl,
+ (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
+ AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
+ aclexplode(t.typacl) as d FROM pg_type t WHERE t.oid = {{tid}}::oid) a) d
+ ) d
+ LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+ LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY g.rolname, gt.rolname
+
+
+
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql
new file mode 100644
index 0000000..eebc0bb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql
@@ -0,0 +1,40 @@
+{# The SQL given below will fetch composite type#}
+{% if type == 'c' %}
+SELECT attname, format_type(t.oid,NULL) AS typname, attndims, atttypmod, nsp.nspname,
+(SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup
+-- Min 9.1 start
+,collname, nspc.nspname as collnspname
+-- End
+, att.attrelid
+FROM pg_attribute att
+JOIN pg_type t ON t.oid=atttypid
+JOIN pg_namespace nsp ON t.typnamespace=nsp.oid
+LEFT OUTER JOIN pg_type b ON t.typelem=b.oid
+-- Min 9.1 start
+LEFT OUTER JOIN pg_collation c ON att.attcollation=c.oid
+LEFT OUTER JOIN pg_namespace nspc ON c.collnamespace=nspc.oid
+-- End
+WHERE att.attrelid = {{typrelid}}::oid
+ORDER by attnum;
+{% endif %}
+{# The SQL given below will fetch enum type#}
+{% if type == 'e' %}
+SELECT enumlabel FROM pg_enum WHERE enumtypid={{tid}}::oid
+-- Min 9.1
+ORDER by enumsortorder
+--End
+-- else
+-- ORDER by oid
+{% endif %}
+{# The SQL given below will fetch range type#}
+{% if type == 'r' %}
+SELECT rngsubtype, st.typname,
+rngcollation, col.collname,
+rngsubopc, opc.opcname,
+rngcanonical, rngsubdiff
+FROM pg_range
+LEFT JOIN pg_type st ON st.oid=rngsubtype
+LEFT JOIN pg_collation col ON col.oid=rngcollation
+LEFT JOIN pg_opclass opc ON opc.oid=rngsubopc
+WHERE rngtypid={{tid}}::oid;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/backend_support.sql
new file mode 100644
index 0000000..f31a2c1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/backend_support.sql
@@ -0,0 +1,14 @@
+SELECT
+ CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport
+FROM pg_namespace nsp
+WHERE nsp.oid={{scid}}::int
+ AND (
+ (nspname = 'pg_catalog' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1))
+ OR (nspname = 'pgagent' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1))
+ OR (nspname = 'information_schema' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1))
+ )
+ AND nspname NOT LIKE E'pg\\temp\\%'
+ AND nspname NOT LIKE E'pg\\toast_temp\\%'
\ No newline at end of file
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
new file mode 100644
index 0000000..7f5c030
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/create.sql
@@ -0,0 +1,68 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/privilege.macros' as PRIVILEGE %}
+{### 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 %});
+{% 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 }}{% 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 %} );
+{% endif %}
+{### External Type ###}
+{% if data and data.typtype == 'x' %}
+CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS
+( {% if data.typinput %}
+INPUT = {{data.typinput}}{% endif %}{% if data.typoutput %}
+,OUTPUT = {{ data.typoutput }}{% endif %}{% if data.typreceive %}
+,RECEIVE = {{data.typreceive}}{% endif %}{% if data.typname %}
+,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.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}}{% endif %}{% if data.typispreferred %}
+,PREFERRED = {{data.typispreferred}}{% endif %}{% if data.typdefault %}
+,DEFAULT = {{data.typdefault}}{% endif %}{% if data.element %}
+,ELEMENT = {{data.element}}{% endif %}{% if data.typdelim %}
+,DELIMITER = {{data.typdelim|qtLiteral}}{% 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 {{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}};
+{% endif %}
+{### Security Lables ###}
+{% if data.seclabels %}
+
+{% for r in data.seclabels %}
+{{ SECLABLE.APPLY(conn, 'TYPE', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{### ACL ###}
+{% if data.typacl %}
+
+{% for priv in data.typacl %}
+{{ PRIVILEGE.APPLY(conn, 'TYPE', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% 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/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..17d4da1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql
@@ -0,0 +1 @@
+DROP TYPE {{ conn|qtIdent(name) }}{% if cascade%} CASCADE{% 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_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_collations.sql
new file mode 100644
index 0000000..4b0169b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_collations.sql
@@ -0,0 +1,7 @@
+SELECT --nspname, collname,
+ CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(collname))
+ ELSE '' END AS collation
+FROM pg_collation c, pg_namespace n
+WHERE c.collnamespace=n.oid
+ORDER BY nspname, collname;
\ 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
new file mode 100644
index 0000000..85494db
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_external_functions.sql
@@ -0,0 +1,40 @@
+{### 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
+ concat(quote_ident(nspname), '.', 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 = 0;
+{% endif %}
+{### TypmodIN list ###}
+{% if typemodin %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='_cstring')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
+{### TypmodOUT list ###}
+{% if typemodout %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='cstring')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_name.sql
new file mode 100644
index 0000000..d368f0f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_name.sql
@@ -0,0 +1,3 @@
+SELECT t.typname AS name
+FROM pg_type t
+WHERE t.oid = {{tid}}::oid;
\ 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_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..14f7950
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,11 @@
+{# Below will provide oid for newly created type #}
+SELECT t.oid
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 data %}
+ AND t.typname = {{data.name|qtLiteral}}
+{% endif %}
+ORDER BY t.typname;
\ 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_subtypes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_subtypes.sql
new file mode 100644
index 0000000..75271fe
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_subtypes.sql
@@ -0,0 +1,56 @@
+{### To fill subtype combobox ###}
+{% if subtype %}
+SELECT DISTINCT typ.typname AS stype,
+ (CASE WHEN typ.typcollation > 0 THEN true ELSE false END) AS is_collate
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype = typ.oid
+WHERE opc.opcmethod = 403
+ORDER BY 1
+{% endif %}
+{### To fill subtype opclass combobox ###}
+{% if subtype_opclass and data and data.typname %}
+SELECT opc.opcname
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ORDER BY opcname;
+{% endif %}
+{### To fetch opcinttype from subtype opclass ###}
+{% if get_opcintype and data and data.typname and data.opcname %}
+SELECT opc.opcintype
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ AND opc.opcname = {{ data.opcname|qtLiteral }}
+ORDER BY opcname;
+{% endif %}
+{### To fill subtype diff function combobox ###}
+{% if opcintype %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS stypdiff
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype = 701
+ AND proargtypes = '{{opcintype}} {{opcintype}}'
+ORDER BY proname;
+{% endif %}
+{### To fill canonical combobox ###}
+{% if getoid %}
+SELECT oid FROM pg_type
+WHERE typname = {{ data.name|qtLiteral }}
+{% endif %}
+{% if canonical and oid %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS canonical
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype= {{ oid }}
+ AND proargtypes = '{{ oid }}'
+ORDER BY proname;
+{% 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_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_types.sql
new file mode 100644
index 0000000..a5d352d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_types.sql
@@ -0,0 +1,10 @@
+SELECT * FROM
+ (SELECT format_type(t.oid,NULL) AS typname,
+ CASE WHEN typelem > 0 THEN typelem ELSE t.oid END AS elemoid,
+ typlen, typtype, t.oid, nspname,
+ (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+FROM pg_type t
+ JOIN pg_namespace nsp ON typnamespace=nsp.oid
+WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r')AND NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) AND nsp.nspname != 'information_schema'
+ ) AS dummy
+ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..ddca1b5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql
@@ -0,0 +1,7 @@
+SELECT t.oid, t.typname AS name
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN pg_namespace nsp ON nsp.oid = t.typnamespace
+WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' AND t.typnamespace = {{scid}}::oid
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..6e81a25
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql
@@ -0,0 +1,21 @@
+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.*, format_type(t.oid, null) AS alias,
+ 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 array_agg(provider || '=' || label) FROM pg_shseclabel 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_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_type'::regclass)
+ LEFT OUTER JOIN 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 %}
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..16b72bc
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
@@ -0,0 +1,129 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{#======================================#}
+{# 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 {{ data.typeowner }};
+
+{% endif %}
+{#======================================#}
+{# Below will change objects comment #}
+{% if data.description and data.description != o_data.description %}
+COMMENT ON TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ IS {{ data.description|qtLiteral }};
+
+{% 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)}} {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% endfor %}
+{% endif %}
+{% if 'changed' in composite and composite.changed|length > 0 %}
+{% for r in composite.changed %}
+ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ ALTER ATTRIBUTE {{conn|qtIdent(r.member_name)}} SET DATA TYPE {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% 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}} AFTER {{o_data.enum[o_enum_len].label|qtLiteral }};
+{% 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}} AFTER {{enum.added[p_idx].label|qtLiteral}};
+{% endif %}
+{% endfor %}
+{% endif %}
+
+{% endif %}
+{#======================================#}
+{# The SQL generated below will change Security Label #}
+{% if data.seclabels and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% set name = conn|qtIdent(o_data.schema, o_data.name) %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'TYPE', name, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'TYPE', name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'TYPE', name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+
+{% endif %}
+{#======================================#}
+{# Change the privileges #}
+{% if data.typacl and data.typacl|length > 0 %}
+{% set name = conn|qtIdent(o_data.schema, o_data.name) %}
+{% if 'deleted' in data.typacl %}
+{% for priv in data.typacl.deleted %}
+{{ PRIVILEGE.RESETALL(conn, 'TYPE', priv.grantee, name) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.typacl %}
+{% for priv in data.typacl.changed %}
+{{ PRIVILEGE.RESETALL(conn, 'TYPE', priv.grantee, data.name) }}
+{{ PRIVILEGE.APPLY(conn, 'TYPE', priv.grantee, name, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.typacl %}
+{% for priv in data.typacl.added %}
+{{ PRIVILEGE.APPLY(conn, 'TYPE', priv.grantee, name, priv.without_grant, priv.with_grant) }}
+{% 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 != 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 %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt
new file mode 100644
index 0000000..631037f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt
@@ -0,0 +1,53 @@
+"""
+Here is the list of Postgres inbuilt types & their OID's
+We will use these types to check for validations
+
+## PGOID_TYPE_SERIAL -42L
+## PGOID_TYPE_SERIAL8 -43L
+## PGOID_TYPE_SERIAL2 -44L
+## PGOID_TYPE_BOOL 16L
+## PGOID_TYPE_BYTEA 17L
+## PGOID_TYPE_CHAR 18L
+## PGOID_TYPE_NAME 19L
+## PGOID_TYPE_INT8 20L
+## PGOID_TYPE_INT2 21L
+## PGOID_TYPE_INT4 23L
+## PGOID_TYPE_TEXT 25L
+## PGOID_TYPE_OID 26L
+## PGOID_TYPE_TID 27L
+## PGOID_TYPE_XID 28L
+## PGOID_TYPE_CID 29L
+## PGOID_TYPE_FLOAT4 700L
+## PGOID_TYPE_FLOAT8 701L
+## PGOID_TYPE_MONEY 790L
+## PGOID_TYPE_CHAR_ARRAY 1002L
+## PGOID_TYPE_TEXT_ARRAY 1009L
+## PGOID_TYPE_BPCHAR_ARRAY 1014L
+## PGOID_TYPE_VARCHAR_ARRAY 1015L
+## PGOID_TYPE_BPCHAR 1042L
+## PGOID_TYPE_VARCHAR 1043L
+## PGOID_TYPE_DATE 1082L
+## PGOID_TYPE_TIME 1083L
+## PGOID_TYPE_TIMESTAMP 1114L
+## PGOID_TYPE_TIMESTAMP_ARRAY 1115L
+## PGOID_TYPE_TIME_ARRAY 1183L
+## PGOID_TYPE_TIMESTAMPTZ 1184L
+## PGOID_TYPE_TIMESTAMPTZ_ARRAY 1185L
+## PGOID_TYPE_INTERVAL 1186L
+## PGOID_TYPE_INTERVAL_ARRAY 1187L
+## PGOID_TYPE_NUMERIC_ARRAY 1231L
+## PGOID_TYPE_TIMETZ 1266L
+## PGOID_TYPE_TIMETZ_ARRAY 1270L
+## PGOID_TYPE_BIT 1560L
+## PGOID_TYPE_BIT_ARRAY 1561L
+## PGOID_TYPE_VARBIT 1562L
+## PGOID_TYPE_VARBIT_ARRAY 1563L
+## PGOID_TYPE_NUMERIC 1700L
+## PGOID_TYPE_CSTRING 2275L
+## PGOID_TYPE_ANY 2276L
+## PGOID_TYPE_VOID 2278L
+## PGOID_TYPE_TRIGGER 2279L
+## PGOID_TYPE_LANGUAGE_HANDLER 2280L
+## PGOID_TYPE_INTERNAL 2281L
+## PGOID_TYPE_HANDLER 3115L
+"""
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/acl.sql
new file mode 100644
index 0000000..9b34240
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/acl.sql
@@ -0,0 +1,29 @@
+SELECT 'typacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'USAGE' THEN 'U'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT t.typacl
+ FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 %}
+ ) acl,
+ (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
+ AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
+ aclexplode(t.typacl) as d FROM pg_type t WHERE t.oid = {{tid}}::oid) a) d
+ ) d
+ LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+ LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY g.rolname, gt.rolname
+
+
+
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/additional_properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/additional_properties.sql
new file mode 100644
index 0000000..0406afd
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/additional_properties.sql
@@ -0,0 +1,40 @@
+{# The SQL given below will fetch composite type#}
+{% if type == 'c' %}
+SELECT attname, format_type(t.oid,NULL) AS typname, attndims, atttypmod, nsp.nspname,
+ (SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup
+-- Min 9.1 start
+ ,collname, nspc.nspname as collnspname
+-- End
+ , att.attrelid
+FROM pg_attribute att
+ JOIN pg_type t ON t.oid=atttypid
+ JOIN pg_namespace nsp ON t.typnamespace=nsp.oid
+ LEFT OUTER JOIN pg_type b ON t.typelem=b.oid
+-- Min 9.1 start
+ LEFT OUTER JOIN pg_collation c ON att.attcollation=c.oid
+ LEFT OUTER JOIN pg_namespace nspc ON c.collnamespace=nspc.oid
+-- End
+WHERE att.attrelid = {{typrelid}}::oid
+ORDER by attnum;
+{% endif %}
+{# The SQL given below will fetch enum type#}
+{% if type == 'e' %}
+SELECT enumlabel FROM pg_enum WHERE enumtypid={{tid}}::oid
+-- Min 9.1
+ORDER by enumsortorder
+--End
+-- else
+-- ORDER by oid
+{% endif %}
+{# The SQL given below will fetch range type#}
+{% if type == 'r' %}
+SELECT rngsubtype, st.typname,
+ rngcollation, col.collname,
+ rngsubopc, opc.opcname,
+ rngcanonical, rngsubdiff
+FROM pg_range
+ LEFT JOIN pg_type st ON st.oid=rngsubtype
+ LEFT JOIN pg_collation col ON col.oid=rngcollation
+ LEFT JOIN pg_opclass opc ON opc.oid=rngsubopc
+WHERE rngtypid={{tid}}::oid;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/backend_support.sql
new file mode 100644
index 0000000..f31a2c1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/backend_support.sql
@@ -0,0 +1,14 @@
+SELECT
+ CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport
+FROM pg_namespace nsp
+WHERE nsp.oid={{scid}}::int
+ AND (
+ (nspname = 'pg_catalog' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1))
+ OR (nspname = 'pgagent' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1))
+ OR (nspname = 'information_schema' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1))
+ )
+ AND nspname NOT LIKE E'pg\\temp\\%'
+ AND nspname NOT LIKE E'pg\\toast_temp\\%'
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/create.sql
new file mode 100644
index 0000000..27116a8
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/create.sql
@@ -0,0 +1,60 @@
+{% import 'macros/privilege.macros' as PRIVILEGE %}
+{### 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 %});
+{% 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 }}{% 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 %} );
+{% endif %}
+{### External Type ###}
+{% if data and data.typtype == 'x' %}
+CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS
+( {% if data.typinput %}
+INPUT = {{data.typinput}}{% endif %}{% if data.typoutput %}
+,OUTPUT = {{ data.typoutput }}{% endif %}{% if data.typreceive %}
+,RECEIVE = {{data.typreceive}}{% endif %}{% if data.typname %}
+,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.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}}{% endif %}{% if data.typispreferred %}
+,PREFERRED = {{data.typispreferred}}{% endif %}{% if data.typdefault %}
+,DEFAULT = {{data.typdefault}}{% endif %}{% if data.element %}
+,ELEMENT = {{data.element}}{% endif %}{% if data.typdelim %}
+,DELIMITER = {{data.typdelim|qtLiteral}}{% 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 {{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}};
+{% endif %}
+{### ACL ###}
+{% if data.typacl %}
+
+{% for priv in data.typacl %}
+{{ PRIVILEGE.APPLY(conn, 'TYPE', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/delete.sql
new file mode 100644
index 0000000..06a279e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/delete.sql
@@ -0,0 +1,8 @@
+{% if scid and tid %}
+SELECT t.typname AS name
+FROM pg_type t
+WHERE t.oid = {{tid}}::oid
+{% endif %}
+{% if name %}
+DROP TYPE {{ conn|qtIdent(name) }}{% if cascade%} CASCADE{% endif %};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_collations.sql
new file mode 100644
index 0000000..4b0169b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_collations.sql
@@ -0,0 +1,7 @@
+SELECT --nspname, collname,
+ CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(collname))
+ ELSE '' END AS collation
+FROM pg_collation c, pg_namespace n
+WHERE c.collnamespace=n.oid
+ORDER BY nspname, collname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_external_functions.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_external_functions.sql
new file mode 100644
index 0000000..53c3c85
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_external_functions.sql
@@ -0,0 +1,40 @@
+{### 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
+ concat(quote_ident(nspname), '.', 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 = 0;
+{% endif %}
+{### TypmodIN list ###}
+{% if typemodin %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='_cstring')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
+{### TypmodOUT list ###}
+{% if typemodout %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='cstring')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_name.sql
new file mode 100644
index 0000000..d368f0f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_name.sql
@@ -0,0 +1,3 @@
+SELECT t.typname AS name
+FROM pg_type t
+WHERE t.oid = {{tid}}::oid;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_oid.sql
new file mode 100644
index 0000000..3219b2a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_oid.sql
@@ -0,0 +1,11 @@
+{# Below will provide oid for newly created type #}
+SELECT t.oid
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 data %}
+ AND t.typname = {{data.name|qtLiteral}}
+{% endif %}
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_schemas.sql
new file mode 100644
index 0000000..a38db60
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_schemas.sql
@@ -0,0 +1,25 @@
+SELECT
+ nsp.nspname
+FROM
+ pg_namespace nsp
+ LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass)
+ LEFT OUTER JOIN pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid)
+WHERE
+ NOT ((nspname = 'pg_catalog' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR
+ (nspname = 'pgagent' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR
+ (nspname = 'information_schema' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR
+ (nspname LIKE '_%' AND EXISTS
+ (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) OR
+ (nspname = 'dbo' OR nspname = 'sys')
+ ) AND
+ nspname NOT LIKE E'pg\\temp\\%' AND
+ nspname NOT LIKE E'pg\\toast_temp\\%' AND
+ -- ADDED: Because We need to omit system schema except the one on which we are trying to create collation
+ ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' ) AND
+ -- Specific code for EDB PPAS
+ nsp.nspparent = 0 AND NOT
+ (nspname = 'dbms_job_procedure' AND EXISTS(SELECT 1 FROM pg_proc WHERE pronamespace = nsp.oid and proname = 'run_job' LIMIT 1))
+ORDER BY nspname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_subtypes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_subtypes.sql
new file mode 100644
index 0000000..5161a99
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_subtypes.sql
@@ -0,0 +1,56 @@
+{### To fill subtype combobox ###}
+{% if subtype %}
+SELECT DISTINCT typ.typname AS stype,
+ (CASE WHEN typ.typcollation > 0 THEN true ELSE false END) AS is_collate
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype = typ.oid
+WHERE opc.opcmethod = 403
+ORDER BY 1
+{% endif %}
+{### To fill subtype opclass combobox ###}
+{% if subtype_opclass and data and data.typname %}
+SELECT opc.opcname
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ORDER BY opcname;
+{% endif %}
+{### To fetch opcinttype from subtype opclass ###}
+{% if get_opcintype and data and data.typname and data.opcname %}
+SELECT opc.opcintype
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ AND opc.opcname = {{ data.opcname|qtLiteral }}
+ORDER BY opcname;
+{% endif %}
+{### To fill subtype diff function combobox ###}
+{% if opcintype %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS stypdiff
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype = 701
+AND proargtypes = '{{opcintype}} {{opcintype}}'
+ORDER BY proname;
+{% endif %}
+{### To fill canonical combobox ###}
+{% if getoid %}
+SELECT oid FROM pg_type
+WHERE typname = {{ data.name|qtLiteral }}
+{% endif %}
+{% if canonical and oid %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS canonical
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype= {{ oid }}
+AND proargtypes = '{{ oid }}'
+ORDER BY proname;
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_types.sql
new file mode 100644
index 0000000..b430e8a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_types.sql
@@ -0,0 +1,10 @@
+SELECT * FROM
+ (SELECT format_type(t.oid,NULL) AS typname,
+ CASE WHEN typelem > 0 THEN typelem ELSE t.oid END AS elemoid,
+ typlen, typtype, t.oid, nspname,
+ (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+FROM pg_type t
+ JOIN pg_namespace nsp ON typnamespace=nsp.oid
+WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r')AND NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) AND nsp.nspname != 'information_schema'
+ ) AS dummy
+ ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/nodes.sql
new file mode 100644
index 0000000..ddca1b5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/nodes.sql
@@ -0,0 +1,7 @@
+SELECT t.oid, t.typname AS name
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN pg_namespace nsp ON nsp.oid = t.typnamespace
+WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' AND t.typnamespace = {{scid}}::oid
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/properties.sql
new file mode 100644
index 0000000..385b906
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/properties.sql
@@ -0,0 +1,16 @@
+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.*, format_type(t.oid, null) AS alias,
+ pg_get_userbyid(t.typowner) as typeowner, e.typname as element,
+ description, ct.oid AS taboid,
+ (CASE WHEN (t.oid <= {{ datlastsysoid}}::oid OR ct.oid != 0) THEN true ElSE false END) AS is_sys_type
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 %}
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/update.sql
new file mode 100644
index 0000000..3f2663a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/update.sql
@@ -0,0 +1,107 @@
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{#======================================#}
+{# 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 {{ data.typeowner }};
+
+{% endif %}
+{#======================================#}
+{# Below will change objects comment #}
+{% if data.description and data.description != o_data.description %}
+COMMENT ON TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ IS {{ data.description|qtLiteral }};
+
+{% 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)}} {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% endfor %}
+{% endif %}
+{% if 'changed' in composite and composite.changed|length > 0 %}
+{% for r in composite.changed %}
+ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ ALTER ATTRIBUTE {{conn|qtIdent(r.member_name)}} SET DATA TYPE {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% 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}} AFTER {{o_data.enum[o_enum_len].label|qtLiteral }};
+{% 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}} AFTER {{enum.added[p_idx].label|qtLiteral}};
+{% endif %}
+{% endfor %}
+{% endif %}
+
+{% endif %}
+{#======================================#}
+{# Change the privileges #}
+{% if data.typacl %}
+{% if 'deleted' in data.typacl %}
+{% for priv in data.typacl.deleted %}
+{{ PRIVILEGE.RESETALL(conn, 'TYPE', priv.grantee, o_data.name) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.typacl %}
+{% for priv in data.typacl.changed %}
+{{ PRIVILEGE.RESETALL(conn, 'TYPE', priv.grantee, data.name) }}
+{{ PRIVILEGE.APPLY(conn, 'TYPE', priv.grantee, o_data.name, priv.without_grant, priv.with_grant) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.typacl %}
+{% for priv in data.typacl.added %}
+{{ PRIVILEGE.APPLY(conn, 'TYPE', priv.grantee, o_data.name, priv.without_grant, priv.with_grant) }}
+{% 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 != 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 %}
+{% endif %}
\ No newline at end of file
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
@ 2016-03-14 12:21 ` Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
0 siblings, 1 reply; 26+ messages in thread
From: Murtuza Zabuawala @ 2016-03-14 12:21 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi Dave,
Thank you for reviewing my patch.
I have made changes according to your feedback.
Please review updated patch.
Regards,
Murtuza
On Fri, Mar 11, 2016 at 9:44 PM, Dave Page <[email protected]> wrote:
> And this time with the patch.
>
> On Fri, Mar 11, 2016 at 4:11 PM, Dave Page <[email protected]> wrote:
> > On Tue, Mar 8, 2016 at 1:38 PM, Murtuza Zabuawala
> > <[email protected]> wrote:
> >> Hi,
> >>
> >> PFA patch to add new nodes in pgAdmin4.
> >> 1) Type node
> >> 2) Catalog objects
> >>
> >> Note: Both above nodes depended on schema/catalog node, Please apply
> them
> >> after latest patch of schema/catalog from Ashesh.
> >> - Type node also depends on parse_priv_function_templates.patch (which I
> >> sent in separate email today)
> >
> > The type node seems to need quite a bit of work - please review and
> > test it carefully before re-submitting. There's an updated patch
> > attached, and some review info below:
> >
> > Changed in the attached patch:
> >
> > - s/Oid/OID
> > - Set defaults for schema and owner
> > - Rename the Type Defintion group to Definition.
> > - Moved some properties (acl, members) into the appropriate group
> > - s/Enumration/Enumeration
> >
> > To be fixed:
> >
> > - This module is derived from SchemaChildModule, so why does it still
> > have the backend support SQL?
> >
>
Done (Apologies I forgot to delete them)
> > - I cleaned up most of the SQL, which was improperly indented in many
> > places. However I have not event tried to fix up the create.sql
> > scripts. These need reformatting to:
> > - Use 4 character indents
> > - Not start a line with a comma - e.g. " ,FOO=bar", which should be
> > " FOO=bar," (obviously the commas need to trail from the line
> > before).
>
Done
> >
> > - The "Show System Objects" option is not honoured.
> >
>
Done
> > - The members list should be delimited with ", " not ","
> >
>
Done
> > - The ACL columns don't match other objects on the subnode panel -
> > Grantee/Granter/Privileges should be Grantee/Privileges/Granter
> >
>
Done
> > - Attempting to add a security label with empty values gives: SECURITY
> > LABEL FOR None ON TYPE foo_enum IS NULL;
> >
> > - The schema is omitted from GRANT statements when creating an object,
> e.g.
> >
> > CREATE TYPE pem.foo_enum AS ENUM
> > ('foo', 'bar', 'whizz');
> >
> > ALTER TYPE pem.foo_enum OWNER TO postgres;
> >
> > COMMENT ON TYPE pem.foo_enum IS 'This is the foo enum';
> >
> > GRANT ALL ON TYPE foo_enum TO pem_admin;
> >
>
Done
> > - Move remaining Properties display properties to the appropriate
> > group - e.g. ENUM labels should be in the Definition group. Only Name,
> > OID, Owner, Alias, System Type and Comment should be under General.
> >
>
Done
> > - The schema name is omitted when dropping a type, leading to: type
> > "foo_enum" does not exist
> >
>
Done
> > - The dependencies and dependents tabs don't display any data.
> >
>
Done
> Thanks.
> >
> > --
> > Dave Page
> > Blog: http://pgsnake.blogspot.com
> > Twitter: @pgsnake
> >
> > EnterpriseDB UK: http://www.enterprisedb.com
> > The Enterprise PostgreSQL Company
>
>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/octet-stream] types_node_v2.patch (128.8K, 3-types_node_v2.patch)
download | inline diff:
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
new file mode 100644
index 0000000..47ac42a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/__init__.py
@@ -0,0 +1,1180 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+""" Implements Type Node """
+
+import json
+from flask import render_template, make_response, request, jsonify
+from flask.ext.babel import gettext
+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 pgadmin.browser.server_groups.servers.databases as database
+from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
+ parse_priv_to_db
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class TypeModule(SchemaChildModule):
+ """
+ class TypeModule(SchemaChildModule)
+
+ A module class for Type node derived from SchemaChildModule
+
+ Methods:
+ -------
+ * __init__(*args, **kwargs)
+ - Method is used to initialize the Type and it's base module.
+
+ * get_nodes(gid, sid, did, scid, tid)
+ - Method is used to generate the browser collection node.
+
+ * node_inode()
+ - Method is overridden from its base class to make the node as leaf node.
+
+ * script_load()
+ - Load the module script for type, when any of the server node is
+ initialized.
+ """
+
+ NODE_TYPE = 'type'
+ COLLECTION_LABEL = gettext("Types")
+
+ def __init__(self, *args, **kwargs):
+ """
+ Method is used to initialize the TypeModule and it's base module.
+
+ Args:
+ *args:
+ **kwargs:
+ """
+ super(TypeModule, self).__init__(*args, **kwargs)
+ self.min_ver = None
+ self.max_ver = None
+
+ def get_nodes(self, gid, sid, did, scid):
+ """
+ Generate the collection node
+ """
+ yield self.generate_browser_collection_node(scid)
+
+ @property
+ def script_load(self):
+ """
+ Load the module script for database, when any of the database node is
+ initialized.
+ """
+ return database.DatabaseModule.NODE_TYPE
+
+ @property
+ def node_inode(self):
+ """
+ Load the module node as a leaf node
+ """
+ return False
+
+blueprint = TypeModule(__name__)
+
+
+class TypeView(PGChildNodeView):
+ """
+ This class is responsible for generating routes for Type node
+
+ Methods:
+ -------
+ * __init__(**kwargs)
+ - Method is used to initialize the TypeView and it's base view.
+
+ * check_precondition()
+ - This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+
+ * list()
+ - This function is used to list all the Type nodes within that
+ collection.
+
+ * nodes()
+ - This function will used to create all the child node within that
+ collection, Here it will create all the Type node.
+
+ * properties(gid, sid, did, scid, tid)
+ - This function will show the properties of the selected Type node
+
+ * create(gid, sid, did, scid)
+ - This function will create the new Type object
+
+ * update(gid, sid, did, scid, tid)
+ - This function will update the data for the selected Type node
+
+ * delete(self, gid, sid, scid, tid):
+ - This function will drop the Type object
+
+ * msql(gid, sid, did, scid, tid)
+ - This function is used to return modified SQL for the selected
+ Type node
+
+ * get_sql(data, scid, tid)
+ - This function will generate sql from model data
+
+ * sql(gid, sid, did, scid):
+ - This function will generate sql to show it in sql pane for the
+ selected Type node.
+
+ * dependency(gid, sid, did, scid, tid):
+ - This function will generate dependency list show it in dependency
+ pane for the selected Type node.
+
+ * dependent(gid, sid, did, scid, tid):
+ - This function will generate dependent list to show it in dependent
+ pane for the selected Type node.
+
+ * additional_properties(copy_dict, tid):
+ - This function will add additional properties in response
+
+ * get_collations(gid, sid, did, scid, tid):
+ - This function will return list of collation in ajax response
+
+ * get_types(gid, sid, did, scid, tid):
+ - This function will return list of types in ajax response
+
+ * get_subtypes(gid, sid, did, scid, tid):
+ - This function will return list of subtypes in ajax response
+
+ * get_subtype_opclass(gid, sid, did, scid, tid):
+ - This function will return list of subtype opclass in ajax response
+
+ * get_subtype_diff(gid, sid, did, scid, tid):
+ - This function will return list of subtype diff functions
+ in ajax response
+
+ * get_canonical(gid, sid, did, scid, tid):
+ - This function will return list of canonical functions
+ in ajax response
+
+ * get_external_functions_list(gid, sid, did, scid, tid):
+ - This function will return list of external functions
+ in ajax response
+ """
+
+ node_type = blueprint.node_type
+
+ parent_ids = [
+ {'type': 'int', 'id': 'gid'},
+ {'type': 'int', 'id': 'sid'},
+ {'type': 'int', 'id': 'did'},
+ {'type': 'int', 'id': 'scid'}
+ ]
+ ids = [
+ {'type': 'int', 'id': 'tid'}
+ ]
+
+ operations = dict({
+ 'obj': [
+ {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+ {'get': 'list', 'post': 'create'}
+ ],
+ 'delete': [{'delete': 'delete'}],
+ 'children': [{'get': 'children'}],
+ 'nodes': [{'get': 'node'}, {'get': 'nodes'}],
+ 'sql': [{'get': 'sql'}],
+ 'msql': [{'get': 'msql'}, {'get': 'msql'}],
+ 'stats': [{'get': 'statistics'}],
+ 'dependency': [{'get': 'dependencies'}],
+ 'dependent': [{'get': 'dependents'}],
+ 'module.js': [{}, {}, {'get': 'module_js'}],
+ 'get_types': [{'get': 'get_types'}, {'get': 'get_types'}],
+ 'get_stypes': [{'get': 'get_subtypes'}, {'get': 'get_subtypes'}],
+ 'get_subopclass': [{'get': 'get_subtype_opclass'},
+ {'get': 'get_subtype_opclass'}],
+ 'get_stypediff': [{'get': 'get_subtype_diff'}, {'get': 'get_subtype_diff'}],
+ 'get_canonical': [{'get': 'get_canonical'}, {'get': 'get_canonical'}],
+ 'get_collations': [{'get': 'get_collations'}, {'get': 'get_collations'}],
+ 'get_external_functions': [{'get': 'get_external_functions_list'},
+ {'get': 'get_external_functions_list'}]
+ })
+
+ def check_precondition(f):
+ """
+ This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+ """
+ @wraps(f)
+ def wrap(*args, **kwargs):
+ # Here args[0] will hold self & kwargs will hold gid,sid,did
+ self = args[0]
+ self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(kwargs['sid'])
+ self.conn = self.manager.connection(did=kwargs['did'])
+
+ # We need datlastsysoid to check if current type is system type
+ self.datlastsysoid = self.manager.db_info[kwargs['did']]['datlastsysoid']
+
+ # If DB not connected then return error to browser
+ if not self.conn.connected():
+ return precondition_required(
+ gettext(
+ "Connection to the server has been lost!"
+ )
+ )
+
+ # Declare allows acl on type
+ self.acl = ['U']
+
+ # we will set template path for sql scripts
+ ver = self.manager.version
+ if 90000 >= ver < 90100:
+ self.template_path = 'type/sql/pre_9.1'
+ else:
+ self.template_path = 'type/sql/9.1_plus'
+ return f(*args, **kwargs)
+
+ return wrap
+
+ @check_precondition
+ def list(self, gid, sid, did, scid):
+ """
+ This function is used to list all the type nodes within that collection.
+
+ Args:
+ gid: Server group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of available type nodes
+ """
+
+ SQL = render_template("/".join([self.template_path, 'properties.sql']),
+ scid=scid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects)
+
+ status, res = self.conn.execute_dict(SQL)
+
+ if not status:
+ return internal_server_error(errormsg=res)
+ return ajax_response(
+ response=res['rows'],
+ status=200
+ )
+
+ @check_precondition
+ def nodes(self, gid, sid, did, scid):
+ """
+ This function will used to create all the child node within that collection.
+ Here it will create all the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of available type child nodes
+ """
+
+ res = []
+ SQL = render_template("/".join([self.template_path,
+ 'nodes.sql']), scid=scid,
+ show_system_objects=self.blueprint.show_system_objects)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=rset)
+
+ for row in rset['rows']:
+ res.append(
+ self.blueprint.generate_browser_node(
+ row['oid'],
+ scid,
+ row['name'],
+ icon="icon-type"
+ ))
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ def additional_properties(self, copy_dict, tid):
+ """
+ We will use this function to add additional properties according to type
+
+ Returns:
+ additional properties for type like range/composite/enum
+
+ """
+ # Fetching type of type
+ of_type = copy_dict['typtype']
+ res = dict()
+ # If type is of Composite then we need to add members list in our output
+ if of_type == 'c':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='c',
+ typrelid=copy_dict['typrelid'])
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # To display in properties
+ properties_list = []
+ # To display in composite collection grid
+ composite_lst = []
+
+ for row in rset['rows']:
+ typelist = ' '.join([row['attname'], row['typname']])
+ if not row['collname'] or (row['collname'] == 'default'
+ and row['collnspname'] == 'pg_catalog'):
+ full_collate = ''
+ collate = ''
+ else:
+ full_collate = get_driver(PG_DEFAULT_DRIVER).qtIdent(
+ self.conn, row['collnspname'], row['collname'])
+ collate = ' COLLATE ' + full_collate
+ typelist += collate
+ properties_list.append(typelist)
+
+ # Below logic will allow us to split length, precision from type name for grid
+ import re
+ matchObj = re.match( r'(.*)\((.*?),(.*?)\)', row['typname'])
+ if matchObj:
+ t_name = matchObj.group(1)
+ t_len = matchObj.group(2)
+ t_prec = matchObj.group(3)
+ else:
+ t_name = row['typname']
+ t_len = None
+ t_prec = None
+
+ composite_lst.append({
+ 'member_name': row['attname'], 'type': t_name, 'collation': full_collate,
+ 'tlength': t_len, 'precision': t_prec })
+
+ # Adding both results
+ res['member_list'] = ', '.join(properties_list)
+ res['composite'] = composite_lst
+
+ # If type is of ENUM then we need to add labels in our output
+ if of_type == 'e':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='e', tid=tid)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ # To display in properties
+ properties_list = []
+ # To display in enum grid
+ enum_list = []
+ for row in rset['rows']:
+ properties_list.append(row['enumlabel'])
+ enum_list.append({'label': row['enumlabel']})
+
+ # Adding both results in ouput
+ res['enum_list'] = ', '.join(properties_list)
+ res['enum'] = enum_list
+
+ # If type is of Range then we need to add collation,subtype etc in our output
+ if of_type == 'r':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='r', tid=tid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ range_dict = dict(res['rows'][0])
+ res.update(range_dict)
+
+ # Returning only additional properties only
+ return res
+
+ @check_precondition
+ def properties(self, gid, sid, did, scid, tid):
+ """
+ This function will show the properties of the selected type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of selected type node
+ """
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ copy_dict = dict(res['rows'][0])
+
+ # We need to parse & convert ACL coming from database to json format
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ copy_dict['typacl'] = []
+
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in copy_dict:
+ copy_dict[row['deftype']].append(priv)
+ else:
+ copy_dict[row['deftype']] = [priv]
+
+ # Calling function to check and additional properties if available
+ copy_dict.update(self.additional_properties(copy_dict, tid))
+
+ return ajax_response(
+ response=copy_dict,
+ status=200
+ )
+
+ @check_precondition
+ def get_collations(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of collation available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_collations.sql']))
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['collation'],
+ 'value': row['collation']}
+ )
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_types(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of types available
+ as AJAX response.
+ """
+ res = []
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_types.sql']))
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ # Attaching properties for precession
+ # & length validation for current type
+ precision = False
+ length = False
+ min_val = 0
+ max_val = 0
+
+ # Check against PGOID for specific type
+ if row['elemoid']:
+ if row['elemoid'] in (1560, 1561, 1562, 1563, 1042, 1043,
+ 1014, 1015):
+ typeval = 'L'
+ elif row['elemoid'] in (1083, 1114, 1115, 1183, 1184, 1185,
+ 1186, 1187, 1266, 1270):
+ typeval = 'D'
+ elif row['elemoid'] in (1231, 1700):
+ typeval = 'P'
+ else:
+ typeval = ' '
+
+ # Logic to set precision & length/min/max values
+ if typeval == 'P':
+ precision = True
+
+ if precision or typeval in ('L', 'D'):
+ length = True
+ min_val = 0 if typeval == 'D' else 1
+ if precision:
+ max_val = 1000
+ elif min_val:
+ # Max of integer value
+ max_val = 2147483647
+ else:
+ max_val = 10
+
+ res.append(
+ {'label': row['typname'], 'value': row['typname'],
+ 'typval': typeval, 'precision': precision,
+ 'length': length, 'min_val': min_val, 'max_val': max_val
+ }
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtypes(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtypes available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ subtype=True)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['stype'], 'value': row['stype'],
+ 'is_collate': row['is_collate']}
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtype_opclass(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtype opclass available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ subtype_opclass=True, data=data)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['opcname'],
+ 'value': row['opcname']})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtype_diff(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtypes diff functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ get_opcintype=True, data=data)
+ if SQL:
+ status, opcintype = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=opcintype)
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ opcintype=opcintype, conn=self.conn)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['stypdiff'],
+ 'value': row['stypdiff']}
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_canonical(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of canonical functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+ canonical = True
+
+ try:
+ # We want to send data only if in we are in edit mode
+ # else we will disable the combobox
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ getoid=True, data=data)
+ if SQL:
+ status, oid = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=oid)
+ # If oid is None then do not run SQL
+ if oid is None:
+ canonical = False
+
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ canonical=canonical, conn=self.conn, oid=oid)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['canonical'],
+ 'value': row['canonical']})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def get_external_functions_list(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of external functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': '', 'cbtype': 'all'}]
+
+ try:
+ # The SQL generated below will populate Input/Output/Send/
+ # Receive/Analyze/TypModeIN/TypModOUT combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ extfunc=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'all'})
+
+ # The SQL generated below will populate TypModeIN combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ typemodin=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'typmodin'})
+
+ # The SQL generated below will populate TypModeIN combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ typemodout=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'typmodout'})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def create(self, gid, sid, did, scid):
+ """
+ This function will creates new the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ data = request.form if request.form else json.loads(request.data.decode())
+ required_args = {
+ 'name': 'Name',
+ 'typtype': 'Type'
+ }
+
+ for arg in required_args:
+ if arg not in data:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ "Couldn't find the required parameter (%s)." %
+ required_args[arg]
+ )
+ )
+ # Additional checks goes here
+ # If type is composite then check if it has two members
+ if data and data[arg] == 'c':
+ if len(data['composite']) < 2:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Composite type requires at least two members'
+ )
+ )
+ # If type is enum then check if it has minimum one label
+ if data and data[arg] == 'e':
+ if len(data['enum']) < 1:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Enumeration type requires at least one label'
+ )
+ )
+ # If type is enum then check if it has minimum one label
+ if data and data[arg] == 'r':
+ if data['typname'] is None:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Subtype must be defined for range type'
+ )
+ )
+ # If type is external then check if input/output
+ # conversion function is defined
+ if data and data[arg] == 'x':
+ if data['typinput'] is None or \
+ data['typoutput'] is None:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'External type requires both Input & Output conversion function.'
+ )
+ )
+
+ # To format privileges coming from client
+ if 'typacl' in data and data['typacl'] is not None:
+ data['typacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ try:
+ SQL = render_template("/".join([self.template_path, 'create.sql']),
+ data=data, conn=self.conn)
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # we need oid to to add object in tree at browser
+ SQL = render_template("/".join([self.template_path,
+ 'get_oid.sql']),
+ scid=scid, data=data)
+ status, tid = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=tid)
+
+ return jsonify(
+ node=self.blueprint.generate_browser_node(
+ tid,
+ scid,
+ data['name'],
+ icon="icon-type"
+ )
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def update(self, gid, sid, did, scid, tid):
+ """
+ This function will updates existing the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+
+ data = request.form if request.form else json.loads(request.data.decode())
+ try:
+ SQL = self.get_sql(gid, sid, data, scid, tid)
+ if SQL and SQL.strip('\n') and SQL.strip(' '):
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return make_json_response(
+ success=1,
+ info="Type updated",
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+ else:
+ return make_json_response(
+ success=1,
+ info="Nothing to update",
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def delete(self, gid, sid, did, scid, tid):
+ """
+ This function will updates existing the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+
+ # Below will decide if it's simple drop or drop with cascade call
+ if self.cmd == 'delete':
+ # This is a cascade operation
+ cascade = True
+ else:
+ cascade = False
+
+ try:
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'delete.sql']),
+ data=data, cascade=cascade, conn=self.conn)
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return make_json_response(
+ success=1,
+ info=gettext("Type dropped"),
+ data={
+ 'id': tid,
+ 'scid': scid
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def msql(self, gid, sid, did, scid, tid=None):
+ """
+ This function will generates modified sql for type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ req = request.args
+ data = dict()
+
+ # converting nested request data in proper json format
+ for key,val in req.items():
+ if key in ['composite', 'enum', 'seclabels', 'typacl']:
+ data[key] = json.loads(val)
+ else:
+ data[key] = val
+
+ try:
+ SQL = self.get_sql(gid, sid, data, scid, tid)
+
+ if SQL and SQL.strip('\n') and SQL.strip(' '):
+ return make_json_response(
+ data=SQL,
+ status=200
+ )
+ except Exception as e:
+ internal_server_error(errormsg=str(e))
+
+ def get_sql(self, gid, sid, data, scid, tid=None):
+ """
+ This function will genrate sql from model data
+ """
+ if tid is not None:
+
+ for key in ['typacl']:
+ if key in data and data[key] is not None:
+ if 'added' in data[key]:
+ data[key]['added'] = parse_priv_to_db(data[key]['added'], self.acl)
+ if 'changed' in data[key]:
+ data[key]['changed'] = parse_priv_to_db(data[key]['changed'], self.acl)
+ if 'deleted' in data[key]:
+ data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], self.acl)
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ old_data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ old_data['typacl'] = []
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in old_data:
+ old_data[row['deftype']].append(priv)
+ else:
+ old_data[row['deftype']] = [priv]
+
+ # Calling function to check and additional properties if available
+ old_data.update(self.additional_properties(old_data, tid))
+
+ SQL = render_template(
+ "/".join([self.template_path, 'update.sql']),
+ data=data, o_data=old_data, conn=self.conn
+ )
+ else:
+ required_args = [
+ 'name',
+ 'typtype'
+ ]
+
+ for arg in required_args:
+ if arg not in data:
+ return " --definition incomplete"
+
+ # Privileges
+ if 'typacl' in data and data['typacl'] is not None:
+ data['typacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ SQL = render_template("/".join([self.template_path,
+ 'create.sql']),
+ data=data, conn=self.conn)
+
+ return SQL
+
+
+ @check_precondition
+ def sql(self, gid, sid, did, scid, tid):
+ """
+ This function will generates reverse engineered sql for type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ data['typacl'] = []
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in data:
+ data[row['deftype']].append(priv)
+ else:
+ data[row['deftype']] = [priv]
+
+ # Privileges
+ if 'typacl' in data and data['typacl'] is not None:
+ data['nspacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ # Calling function to check and additional properties if available
+ data.update(self.additional_properties(data, tid))
+
+ # We do not want to display table which has '-' value
+ # setting them to None so that jinja avoid displaying them
+ for k in data:
+ if data[k] == '-':
+ data[k] = None
+
+ SQL = self.get_sql(gid, sid, data, scid, tid=None)
+
+ # We are appending headers here for sql panel
+ sql_header = "-- Type: {0}\n\n-- ".format(data['name'])
+ sql_header += render_template("/".join([self.template_path,
+ 'delete.sql']),
+ data=data, conn=self.conn)
+ SQL = sql_header + '\n\n' + SQL
+
+ return ajax_response(response=SQL)
+
+ @check_precondition
+ def dependents(self, gid, sid, did, scid, tid):
+ """
+ This function get the dependents and return ajax response
+ for the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ dependents_result = self.get_dependents(
+ self.conn, tid
+ )
+
+ return ajax_response(
+ response=dependents_result,
+ status=200
+ )
+
+ @check_precondition
+ def dependencies(self, gid, sid, did, scid, tid):
+ """
+ This function get the dependencies and return ajax response
+ for the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ dependencies_result = self.get_dependencies(
+ self.conn, tid
+ )
+
+ return ajax_response(
+ response=dependencies_result,
+ status=200
+ )
+
+TypeView.register_node_view(blueprint)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/coll-type.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/coll-type.png
new file mode 100644
index 0000000000000000000000000000000000000000..fb020d7d99f84439046d288615e865ee1fbdb815
GIT binary patch
literal 329
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=3NcXI=aM>fHaQT@Q91
z`hS1R{~OExU!3;;MEm~(mH&6<{olN3-cDJdI>wS9zhDN3XE)M-9L@rd$YLPv0mg18
zv+aP47*7|+5RU7%XQO!=40zlgo^wo$EU!My#3j(ks*}LT9dUr^nFV*x`<LfEXLU&{
zNT_QZ){p11jr8BRJf*J9t1q$D%X6FK(q}uIk4`OV_&v+tAZF)<c~_)!|NM{V=e#WX
zN#J9vG0+~>64!{5l*E!$tK_0oAjM#0U}&IgXryak7-D2#Wnye)VybOmYGq(B@15Q%
q6b-rgDVb@N5Df;FU=2XkCRPS!5DllMhpqu?VDNPHb6Mw<&;$UZ$8*O3
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/type.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/type.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c16764e7d08c56922a97a2f0c6cc06455c86a56
GIT binary patch
literal 325
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv5AX?b1=8~#9EmzT>)QWU=l(xE
z`v2js|F_rtzdY~%nF;@oHvQjQ{(pPk|IMk)0@*;Nj3q&S!3+-1ZlnP@oCO|{#X#Bv
zjNMLV+W{G&o-U3d9M_W*4zM_RIV|8v(U4|t6r8Z|5fh7_LtB=H01KmJR;I%QmXsCK
znH_-=7a3W69onAxD9m6<$ym$O<m%A&El=SFOUjEmj7`o4+9Dfn@G_j<Bfar~Z-)!e
z0@V`Nh?11Vl2ohYqEsNoU}RuuplfKPYhV~+WME}tY-M7qZD49;U@-5U-YOIgx%nxX
kX_XKS29{tAK-DHz24)Zqr>2Ll0cv3IboFyt=akR{01h5&qyPW_
literal 0
HcmV?d00001
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
new file mode 100644
index 0000000..206168f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/js/type.js
@@ -0,0 +1,856 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'pgadmin',
+ 'pgadmin.browser', 'alertify', 'backgrid', 'pgadmin.backgrid',
+ 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
+
+ if (!pgBrowser.Nodes['coll-type']) {
+ var databases = pgAdmin.Browser.Nodes['coll-type'] =
+ pgAdmin.Browser.Collection.extend({
+ node: 'type',
+ label: '{{ _('Types') }}',
+ type: 'coll-type',
+ columns: ['name', 'typeowner', 'description']
+ });
+ };
+
+ // Switch options to save space in model's field
+ var switchOptions = {
+ 'onText': 'Yes', 'offText': 'No',
+ 'onColor': 'success', 'offColor': 'default',
+ 'size': 'small'
+ };
+
+ // Security label model declaration
+ var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ provider: undefined,
+ security_label: undefined
+ },
+ schema: [{
+ id: 'provider', label: '{{ _('Provider') }}',
+ type: 'text', disabled: false
+ },{
+ id: 'security_label', label: '{{ _('Security Label') }}',
+ type: 'text', disabled: false
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+
+ if (_.isUndefined(this.get('security_label')) ||
+ _.isNull(this.get('security_label')) ||
+ String(this.get('security_label')).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = '{{ _('Please provide the value for security label.') }}';
+ this.errorModel.set('security_label', errmsg);
+ return errmsg;
+ } else {
+ this.errorModel.unset('security_label');
+ }
+ return null;
+ }
+ });
+
+ // Composite type model declaration
+ var CompositeModel = Backform.CompositeModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ member_name: undefined,
+ type: undefined,
+ tlength: undefined,
+ is_tlength: false,
+ precision: undefined,
+ is_precision: false,
+ collation: undefined,
+ min_val: undefined,
+ max_val: undefined,
+ },
+ type_options: undefined,
+ subtypes: undefined,
+ schema: [{
+ id: 'member_name', label: '{{ _('Member Name') }}',
+ type: 'text', disabled: false, editable: false
+ },{
+ id: 'type', label: '{{ _('Type') }}', control: 'node-ajax-options',
+ type: 'text', url: 'get_types', disabled: false, node: 'type',
+ editable: false,
+ transform: function(d){
+ this.model.type_options = d;
+ return d;
+ }
+ },{
+ id: 'tlength', label: '{{ _('Length') }}', deps: ['type'], type: 'text',
+ editable: false,
+ disabled: function(m) {
+ // We will store type from selected from combobox
+ var of_type = m.get('type');
+ if(m.type_options) {
+ // iterating over all the types
+ _.each(m.type_options, function(o) {
+ // if type from selected from combobox matches in options
+ if ( of_type == o.value ) {
+ // if length is allowed for selected type
+ if(o.length)
+ {
+ // set the values in model
+ m.set('is_tlength', true, {silent: true});
+ m.set('min_val', o.min_val, {silent: true});
+ m.set('max_val', o.max_val, {silent: true});
+ }
+ }
+ });
+ }
+ return !m.get('is_tlength');
+ }
+ },{
+ id: 'precision', label: '{{ _('Precision') }}', deps: ['type'],
+ type: 'text', editable: false,
+ disabled: function(m) {
+ // We will store type from selected from combobox
+ var of_type = m.get('type');
+ if(m.type_options) {
+ // iterating over all the types
+ _.each(m.type_options, function(o) {
+ // if type from selected from combobox matches in options
+ if ( of_type == o.value ) {
+ // if precession is allowed for selected type
+ if(o.precision)
+ {
+ // set the values in model
+ m.set('is_precision', true, {silent: true});
+ m.set('min_val', o.min_val, {silent: true});
+ m.set('max_val', o.max_val, {silent: true});
+ }
+ }
+ });
+ }
+ return !m.get('is_precision');
+ }
+ },{
+ id: 'collation', label: '{{ _('Collation') }}',
+ control: 'node-ajax-options', editable: false,
+ type: 'text', disabled: false, url: 'get_collations', node: 'type'
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+ // Validation for member name
+ if (_.isUndefined(data.member_name) ||
+ _.isNull(data.member_name) ||
+ String(data.member_name).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = _("Please specify the value for member name.")
+ this.errorModel.set('member_name', errmsg)
+ return errmsg;
+ } else {
+ this.errorModel.unset('member_name');
+ }
+
+ // Validation for Length field
+ if (data.is_tlength && !_.isUndefined(data.tlength)) {
+ if (data.tlength < data.min_val )
+ errmsg = _("Length should not be less than " + data.min_val)
+ if (data.tlength > data.max_val )
+ errmsg = _("Length should not be greater than " + data.max_val)
+ // If we have any error set then throw it to user
+ if(errmsg) {
+ this.errorModel.set('tlength', errmsg)
+ return errmsg;
+ }
+ } else {
+ this.errorModel.unset('tlength');
+ }
+
+ // Validation for precision field
+ if (data.is_precision && !_.isUndefined(data.precision)) {
+ if (data.precision < data.min_val )
+ errmsg = _("Precision should not be less than " + data.min_val)
+ if (data.precision > data.max_val )
+ errmsg = _("Precision should not be greater than " + data.max_val)
+ // If we have any error set then throw it to user
+ if(errmsg) {
+ this.errorModel.set('precision', errmsg)
+ return errmsg;
+ }
+ } else {
+ this.errorModel.unset('precision');
+ }
+
+ return null;
+ }
+ });
+
+ var EnumModel = Backform.EnumModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ label: null,
+ },
+ schema: [{
+ id: 'label', label: '{{ _('Label') }}',type: 'text', disabled: false,
+ cellHeaderClasses: 'width_percent_99'
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+
+ if (_.isUndefined(data.label) ||
+ _.isNull(data.label) ||
+ String(data.label).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = _("Please specify the value for label.");
+ this.errorModel.set('label', errmsg)
+ return errmsg;
+ } else {
+ this.errorModel.unset('label');
+ }
+
+
+ return null;
+ }
+ });
+
+ if (!pgBrowser.Nodes['type']) {
+ pgAdmin.Browser.Nodes['type'] = pgBrowser.Node.extend({
+ type: 'type',
+ label: '{{ _('Type') }}',
+ collection_type: 'coll-type',
+ hasSQL: true,
+ hasDepends: true,
+ parent_type: ['schema', 'catalog'],
+ Init: function() {
+ /* Avoid mulitple registration of menus */
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+
+ pgBrowser.add_menus([{
+ name: 'create_type_on_coll', node: 'coll-type', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_type', node: 'type', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_type', node: 'schema', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: false},
+ enable: 'canCreate'
+ }
+ ]);
+
+ },
+ canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+ canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+ ext_funcs: undefined,
+ model: pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ name: undefined,
+ oid: undefined,
+ is_sys_type: false,
+ typtype: undefined
+ },
+
+ // Default values!
+ initialize: function(attrs, args) {
+ var isNew = (_.size(attrs) === 0);
+
+ if (isNew) {
+ var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
+ var schemaInfo = args.node_info.schema;
+
+ this.set({'typeowner': userInfo.name}, {silent: true});
+ this.set({'schema': schemaInfo.label}, {silent: true});
+ }
+ pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+ },
+
+ schema: [{
+ id: 'name', label: '{{ _('Name') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchema'
+ },{
+ id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+ type: 'text' , mode: ['properties'], disabled: true
+ },{
+ id: 'typeowner', label:'{{ _('Owner') }}', cell: 'string',
+ control: 'node-list-by-name',
+ type: 'text', mode: ['properties', 'create', 'edit'], node: 'role',
+ disabled: 'inSchema'
+ },{
+ id: 'schema', label:'{{ _('Schema') }}', cell: 'string',
+ type: 'text', mode: ['create', 'edit'], node: 'schema',
+ disabled: 'inSchema', filter: function(d) {
+ // If schema name start with pg_* then we need to exclude them
+ if(d && d.label.match(/^pg_/))
+ {
+ return false;
+ }
+ return true;
+ },
+ control: Backform.NodeListByNameControl.extend({
+ render: function(){
+ // Initialize parent's render method
+ Backform.NodeListByNameControl.prototype.render.apply(this, arguments);
+
+ // Set schema default value to its parent Schema
+ if(this.model.isNew()){
+ this.model.set({'schema': this.model.node_info.schema.label});
+ }
+ return this;
+ }
+ })
+ },{
+ id: 'typtype', label:'{{ _('Type') }}',
+ mode: ['create','edit'], disabled: 'inSchemaWithModelCheck',
+ group: '{{ _('Definition') }}',
+ mode: ['edit', 'create'],
+ select2: { width: "50%" },
+ options: [
+ {label: "Composite", value: "c"},
+ {label: "Enumeration", value: "e"},
+ {label: "External", value: "x"},
+ {label: "Range", value: "r"},
+ ],
+ disabled: 'inSchemaWithModelCheck',
+ // If create mode then by default open composite type
+ control: Backform.Select2Control.extend({
+ render: function(){
+ // Initialize parent's render method
+ Backform.Select2Control.prototype.render.apply(this, arguments);
+ if(this.model.isNew()) {
+ this.model.set({'typtype': 'c'});
+ }
+ return this;
+ }
+ })
+ },{
+ id: 'composite', label: '{{ _('Composite Type') }}',
+ model: CompositeModel, editable: true, type: 'collection',
+ group: '{{ _('Definition') }}', mode: ['edit', 'create'],
+ control: 'unique-col-collection', uniqueCol : ['member_name'],
+ canAdd: true, canEdit: true, canDelete: true, disabled: 'inSchema',
+ deps: ['typtype'], deps: ['typtype'],
+ visible: function(m) {
+ if (m.get('typtype') === 'c') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'enum', label: '{{ _('Enumeration Type') }}',
+ model: EnumModel, editable: true, type: 'collection',
+ group: '{{ _('Definition') }}', mode: ['edit', 'create'],
+ canAdd: true, canEdit: false, canDelete: function(m) {
+ // We will disable it if it's in 'edit' mode
+ if (m.isNew()) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ disabled: 'inSchema', deps: ['typtype'],
+ control: 'unique-col-collection', uniqueCol : ['label'],
+ visible: function(m) {
+ return m.get('typtype') === 'e';
+ }
+ },{
+ // We will disable range type control in edit mode
+ type: 'nested', control: 'fieldset', group: '{{ _('Definition') }}',
+ mode: ['edit', 'create'],
+ visible: function(m) {
+ return m.get('typtype') === 'r';
+ }, deps: ['typtype'], label: '{{ _('Range Type') }}',
+ schema:[{
+ id: 'typname', label:'{{ _('Subtype') }}', cell: 'string',
+ control: 'node-ajax-options',
+ url: 'get_stypes', type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}', disabled: 'inSchemaWithModelCheck',
+ transform: function(d){
+ this.model.subtypes = d;
+ return d;
+ }
+ },{
+ id: 'opcname', label:'{{ _('Subtype OpClass') }}', cell: 'string',
+ mode: ['properties', 'create', 'edit'], group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['typname'],
+ control: 'select', options: function() {
+ var l_typname = this.model.get('typname'),
+ self = this,
+ result = [];
+ if(!_.isUndefined(l_typname) && l_typname != '')
+ {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_subopclass', this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {'typname' : l_typname},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error', self.model, self.field);
+ }
+ });
+ //
+ }
+ return result;
+ }
+ },{
+ id: 'collname', label:'{{ _('Collation') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ deps: ['typname'], control: 'node-ajax-options', url: 'get_collations',
+ disabled: function(m) {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+
+ // Disbale in edit mode
+ if (!m.isNew()) {
+ return true;
+ }
+
+ // To check if collation is allowed?
+ var of_subtype = m.get('typname'),
+ is_collate = undefined;
+ if(!_.isUndefined(of_subtype)) {
+ // iterating over all the types
+ _.each(m.subtypes, function(s) {
+ // if subtype from selected from combobox matches
+ if ( of_subtype === s.label ) {
+ // if collation is allowed for selected subtype
+ // then enable it else disable it
+ is_collate = s.is_collate;
+ }
+ });
+ }
+ // If is_collate is true then do not disable
+ return is_collate ? false : true;
+ }
+ },{
+ id: 'rngcanonical', label:'{{ _('Canonical') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['name', 'typname'],
+ control: 'select', options: function() {
+ var name = this.model.get('name'),
+ self = this,
+ result = [];
+
+ if(!_.isUndefined(name) && name != '')
+ {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_canonical', this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {"name" : name},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error',
+ self.model, self.field);
+ }
+ });
+ }
+ return result;
+ }
+ },{
+ id: 'rngsubdiff', label:'{{ _('SubType diff') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['opcname'],
+ control: 'select', options: function() {
+ var l_typname = this.model.get('typname'),
+ l_opcname = this.model.get('opcname'),
+ self = this,
+ result = [];
+
+ if(!_.isUndefined(l_typname) && l_typname != '' &&
+ !_.isUndefined(l_opcname) && l_opcname != '') {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_stypediff',
+ this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {'typname' : l_typname, 'opcname': l_opcname},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error',
+ self.model, self.field);
+ }
+ });
+ }
+ return result;
+ }
+ }]
+ },{
+ type: 'nested', control: 'tab', group: '{{ _('Definition') }}',
+ label: '{{ _('External Type') }}', deps: ['typtype'],
+ mode: ['create', 'edit'],
+ visible: function(m) {
+ return m.get('typtype') === 'x';
+ },
+ schema:[{
+ id: 'typinput', label:'{{ _('Input function') }}',
+ cell: 'string',type: 'text',
+ mode: ['properties', 'create', 'edit'], group: 'Required',
+ disabled: 'inSchemaWithModelCheck',
+ control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo'
+ },{
+ id: 'typoutput', label:'{{ _('Output function') }}',
+ cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Required',
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo'
+ },{
+ id: 'typreceive', label:'{{ _('Receive function') }}',
+ cell: 'string', type: 'text', group: 'Optional-1',
+ mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo'
+ },{
+ id: 'typsend', label:'{{ _('Send function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo'
+ },{
+ id: 'typmodin', label:'{{ _('Typmod in function') }}',
+ cell: 'string', type: 'text',
+ mode: ['properties', 'create', 'edit'], group: 'Optional-1',
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: function(d) {
+ var result = [{label :"", value : ""}];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype === 'typmodin' || item.cbtype === 'all') {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ },{
+ id: 'typmodout', label:'{{ _('Typmod out function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: function(d) {
+ var result = [{label :"", value : ""}];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype === 'typmodout' || item.cbtype === 'all') {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ },{
+ id: 'typlen', label:'{{ _('Internal length') }}',
+ cell: 'integer', group: 'Optional-1',
+ type: 'int', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'variable', label:'{{ _('Variable') }}', cell: 'switch',
+ group: 'Optional-1', type: 'switch',
+ mode: ['create','edit'], options: switchOptions,
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typdefault', label:'{{ _('Default') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typanalyze', label:'{{ _('Analyze function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo'
+ },{
+ id: 'typcategory', label:'{{ _('Category') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { placeholder: "Select category", allowClear: true,
+ width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label :"Array types", value : "A"},
+ {label :"Boolean types", value : "B"},
+ {label :"Composite types", value : "C"},
+ {label :"Date/time types", value : "D"},
+ {label :"Enum types", value : "E"},
+ {label :"Geometric types", value : "G"},
+ {label :"Network address types", value : "I"},
+ {label :"Numeric types", value : "N"},
+ {label :"Pseudo-types", value : "P"},
+ {label :"String types", value : "S"},
+ {label :"Timespan types", value : "T"},
+ {label :"User-defined types", value : "U"},
+ {label :"Bit-string types", value : "V"},
+ {label :"unknown type", value : "X"}
+ ]
+ },{
+ id: 'typispreferred', label:'{{ _('Prefered') }}', cell: 'switch',
+ type: 'switch', mode: ['properties', 'create','edit'],
+ options: switchOptions, disabled: 'inSchemaWithModelCheck',
+ group: 'Optional-1'
+ },{
+ id: 'element', label:'{{ _('Element') }}', cell: 'string',
+ control: 'node-ajax-options', group: 'Optional-2',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', url: 'get_types'
+ },{
+ id: 'typdelim', label:'{{ _('Delimiter') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Optional-2', disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typalign', label:'{{ _('Alignment') }}',
+ cell: 'string', group: 'Optional-2',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { placeholder: "Select alignment", allowClear: true,
+ width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label: "char", value: "c"},
+ {label: "int2", value: "s"},
+ {label: "in4", value: "i"},
+ {label: "double", value: "d"},
+ ]
+ },{
+ id: 'typstorage', label:'{{ _('Storage') }}',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Optional-2', cell: 'string',
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { placeholder: "Select storage", allowClear: true,
+ width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label: "PLAIN", value: "p"},
+ {label: "MAIN", value: "e"},
+ {label: "EXTERNAL", value: "m"},
+ {label: "EXTENDED", value: "x"},
+ ]
+ },{
+ id: 'typbyval', label:'{{ _('Passed by Value?') }}',
+ cell: 'switch', options: switchOptions,
+ type: 'switch', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', group: 'Optional-2',
+ },{
+ id: 'is_collatable', label:'{{ _('Collatable?') }}',
+ cell: 'switch', min_version: 90100, group: 'Optional-2',
+ type: 'switch', mode: ['properties', 'create', 'edit'],
+ options: switchOptions, disabled: 'inSchemaWithModelCheck'
+ // End of extension tab
+ }]
+ },{
+ id: 'alias', label:'{{ _('Alias') }}', cell: 'string',
+ type: 'text', mode: ['properties'],
+ disabled: 'inSchema'
+ },{
+ id: 'type_acl', label:'{{ _('Privileges') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Security') }}',
+ disabled: 'inSchema'
+ },{
+ id: 'member_list', label:'{{ _('Members') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Definition') }}',
+ disabled: 'inSchema', visible: function(m) {
+ if(m.get('typtype') === 'c') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'enum_list', label:'{{ _('Labels') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Definition') }}',
+ disabled: 'inSchema', visible: function(m) {
+ if(m.get('typtype') === 'e') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'is_sys_type', label:'{{ _('System type?') }}', cell: 'switch',
+ type: 'switch', mode: ['properties'], options: switchOptions,
+ disabled: 'inSchema'
+ },{
+ id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+ type: 'multiline', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchema'
+ },{
+ 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']
+ },{
+ id: 'seclabels', label: '{{ _('Security Labels') }}',
+ model: SecurityModel, editable: false, type: 'collection',
+ group: '{{ _('Security') }}', mode: ['edit', 'create'],
+ min_version: 90200, canAdd: true,
+ canEdit: false, canDelete: true, control: 'unique-col-collection'
+ }],
+ validate: function() {
+ // Validation code for required fields
+ var changedAttrs = this.sessAttrs,
+ msg = undefined;
+
+ if (_.has(changedAttrs, 'name') &&
+ (_.isUndefined(this.get('name'))
+ || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Name can not be empty!') }}';
+ this.errorModel.set('name', msg);
+ } else if (_.has(changedAttrs, 'schema') &&
+ (_.isUndefined(this.get('schema'))
+ || String(this.get('schema')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Schema can not be empty!') }}';
+ this.errorModel.set('schema', msg);
+ } else if (_.has(changedAttrs, 'typtype') &&
+ (_.isUndefined(this.get('typtype'))
+ || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Type can not be empty!') }}';
+ this.errorModel.set('typtype', msg);
+ } else if (this.get('typtype') == 'r' &&
+ _.has(changedAttrs, 'typname')
+ && (_.isUndefined(this.get('typname'))
+ || String(this.get('typname')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Subtype Name can not be empty!') }}';
+ this.errorModel.set('typname', msg);
+ } else if (this.get('typtype') == 'x' &&
+ _.has(changedAttrs, 'typinput')
+ && (_.isUndefined(this.get('typinput'))
+ || String(this.get('typinput')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Input function can not be empty!') }}';
+ this.errorModel.set('typinput', msg);
+ } else if (this.get('typtype') == 'x' &&
+ _.has(changedAttrs, 'typoutput')
+ && (_.isUndefined(this.get('typoutput'))
+ || String(this.get('typoutput')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Output function can not be empty!') }}';
+ this.errorModel.set('typoutput', msg);
+ } else {
+ this.errorModel.unset('name');
+ this.errorModel.unset('schema');
+ this.errorModel.unset('typtype');
+ this.errorModel.unset('typname');
+ this.errorModel.unset('typinput');
+ this.errorModel.unset('typoutput');
+ }
+ return null;
+ },
+ // We will disable everything if we are under catalog node
+ inSchema: function() {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+ return false;
+ },
+ // We will check if we are under schema node & in 'create' mode
+ inSchemaWithModelCheck: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ // We will disbale control if it's in 'edit' mode
+ if (m.isNew()) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+ return true;
+ },
+ // We want to enable only in edit mode
+ inSchemaWithEditMode: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ // We will disbale control if it's in 'edit' mode
+ if (m.isNew()) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+ return true;
+ },
+ // Function will help us to fill combobox
+ external_func_combo: function(d) {
+ var result = [];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype == 'all' ) {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ }),
+ canCreate: function(itemData, item, data) {
+ //If check is false then , we will allow create menu
+ if (data && data.check == false)
+ return true;
+
+ var t = pgBrowser.tree, i = item, d = itemData;
+ // To iterate over tree to check parent node
+ while (i) {
+ // If it is schema then allow user to create table
+ if (_.indexOf(['schema'], d._type) > -1)
+ return true;
+
+ if ('coll-type' == d._type) {
+ //Check if we are not child of catalog
+ prev_i = t.hasParent(i) ? t.parent(i) : null;
+ prev_d = prev_i ? t.itemData(prev_i) : null;
+ if( prev_d._type == 'catalog') {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ i = t.hasParent(i) ? t.parent(i) : null;
+ d = i ? t.itemData(i) : null;
+ }
+ // by default we do not want to allow create menu
+ return true;
+ }
+ });
+ }
+ return pgBrowser.Nodes['type'];
+});
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql
new file mode 100644
index 0000000..60eab25
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql
@@ -0,0 +1,26 @@
+SELECT 'typacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'USAGE' THEN 'U'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT t.typacl
+ FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 %}
+ ) acl,
+ (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
+ AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
+ aclexplode(t.typacl) as d FROM pg_type t WHERE t.oid = {{tid}}::oid) a) d
+ ) d
+ LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+ LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY g.rolname, gt.rolname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql
new file mode 100644
index 0000000..eebc0bb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql
@@ -0,0 +1,40 @@
+{# The SQL given below will fetch composite type#}
+{% if type == 'c' %}
+SELECT attname, format_type(t.oid,NULL) AS typname, attndims, atttypmod, nsp.nspname,
+(SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup
+-- Min 9.1 start
+,collname, nspc.nspname as collnspname
+-- End
+, att.attrelid
+FROM pg_attribute att
+JOIN pg_type t ON t.oid=atttypid
+JOIN pg_namespace nsp ON t.typnamespace=nsp.oid
+LEFT OUTER JOIN pg_type b ON t.typelem=b.oid
+-- Min 9.1 start
+LEFT OUTER JOIN pg_collation c ON att.attcollation=c.oid
+LEFT OUTER JOIN pg_namespace nspc ON c.collnamespace=nspc.oid
+-- End
+WHERE att.attrelid = {{typrelid}}::oid
+ORDER by attnum;
+{% endif %}
+{# The SQL given below will fetch enum type#}
+{% if type == 'e' %}
+SELECT enumlabel FROM pg_enum WHERE enumtypid={{tid}}::oid
+-- Min 9.1
+ORDER by enumsortorder
+--End
+-- else
+-- ORDER by oid
+{% endif %}
+{# The SQL given below will fetch range type#}
+{% if type == 'r' %}
+SELECT rngsubtype, st.typname,
+rngcollation, col.collname,
+rngsubopc, opc.opcname,
+rngcanonical, rngsubdiff
+FROM pg_range
+LEFT JOIN pg_type st ON st.oid=rngsubtype
+LEFT JOIN pg_collation col ON col.oid=rngcollation
+LEFT JOIN pg_opclass opc ON opc.oid=rngsubopc
+WHERE rngtypid={{tid}}::oid;
+{% endif %}
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
new file mode 100644
index 0000000..6a53532
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/create.sql
@@ -0,0 +1,78 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{### 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 %});
+{% 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 }}{% 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 %}
+
+ );
+{% endif %}
+{### External Type ###}
+{% if data and data.typtype == 'x' %}
+CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS
+ (
+ {% if data.typinput %}INPUT = {{data.typinput}}{% endif %}{% if data.typoutput %},
+ OUTPUT = {{ data.typoutput }}{% endif %}{% if data.typreceive %},
+ RECEIVE = {{data.typreceive}}{% endif %}{% if data.typname %},
+ 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.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}}{% endif %}{% if data.typispreferred %},
+ PREFERRED = {{data.typispreferred}}{% endif %}{% if data.typdefault %},
+ DEFAULT = {{data.typdefault}}{% endif %}{% if data.element %},
+ ELEMENT = {{data.element}}{% endif %}{% if data.typdelim %},
+ DELIMITER = {{data.typdelim|qtLiteral}}{% 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 {{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}};
+{% 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.security_label %}
+{{ SECLABLE.SET(conn, 'TYPE', data.name, r.provider, r.security_label, data.schema) }}
+{% endif %}
+{% endfor %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..c258827
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql
@@ -0,0 +1 @@
+DROP TYPE {{ conn|qtIdent(data.schema, data.name) }}{% if cascade%} CASCADE{% 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_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_collations.sql
new file mode 100644
index 0000000..4b0169b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_collations.sql
@@ -0,0 +1,7 @@
+SELECT --nspname, collname,
+ CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(collname))
+ ELSE '' END AS collation
+FROM pg_collation c, pg_namespace n
+WHERE c.collnamespace=n.oid
+ORDER BY nspname, collname;
\ 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
new file mode 100644
index 0000000..85494db
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_external_functions.sql
@@ -0,0 +1,40 @@
+{### 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
+ concat(quote_ident(nspname), '.', 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 = 0;
+{% endif %}
+{### TypmodIN list ###}
+{% if typemodin %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='_cstring')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
+{### TypmodOUT list ###}
+{% if typemodout %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='cstring')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..14f7950
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,11 @@
+{# Below will provide oid for newly created type #}
+SELECT t.oid
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 data %}
+ AND t.typname = {{data.name|qtLiteral}}
+{% endif %}
+ORDER BY t.typname;
\ 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_subtypes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_subtypes.sql
new file mode 100644
index 0000000..75271fe
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_subtypes.sql
@@ -0,0 +1,56 @@
+{### To fill subtype combobox ###}
+{% if subtype %}
+SELECT DISTINCT typ.typname AS stype,
+ (CASE WHEN typ.typcollation > 0 THEN true ELSE false END) AS is_collate
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype = typ.oid
+WHERE opc.opcmethod = 403
+ORDER BY 1
+{% endif %}
+{### To fill subtype opclass combobox ###}
+{% if subtype_opclass and data and data.typname %}
+SELECT opc.opcname
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ORDER BY opcname;
+{% endif %}
+{### To fetch opcinttype from subtype opclass ###}
+{% if get_opcintype and data and data.typname and data.opcname %}
+SELECT opc.opcintype
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ AND opc.opcname = {{ data.opcname|qtLiteral }}
+ORDER BY opcname;
+{% endif %}
+{### To fill subtype diff function combobox ###}
+{% if opcintype %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS stypdiff
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype = 701
+ AND proargtypes = '{{opcintype}} {{opcintype}}'
+ORDER BY proname;
+{% endif %}
+{### To fill canonical combobox ###}
+{% if getoid %}
+SELECT oid FROM pg_type
+WHERE typname = {{ data.name|qtLiteral }}
+{% endif %}
+{% if canonical and oid %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS canonical
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype= {{ oid }}
+ AND proargtypes = '{{ oid }}'
+ORDER BY proname;
+{% 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_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_types.sql
new file mode 100644
index 0000000..a5d352d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_types.sql
@@ -0,0 +1,10 @@
+SELECT * FROM
+ (SELECT format_type(t.oid,NULL) AS typname,
+ CASE WHEN typelem > 0 THEN typelem ELSE t.oid END AS elemoid,
+ typlen, typtype, t.oid, nspname,
+ (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+FROM pg_type t
+ JOIN pg_namespace nsp ON typnamespace=nsp.oid
+WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r')AND NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) AND nsp.nspname != 'information_schema'
+ ) AS dummy
+ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..6abcb19
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql
@@ -0,0 +1,10 @@
+SELECT t.oid, t.typname AS name
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN pg_namespace nsp ON nsp.oid = t.typnamespace
+WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' AND t.typnamespace = {{scid}}::oid
+{% if not show_system_objects %}
+ AND ct.oid is NULL
+{% endif %}
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..de261a5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql
@@ -0,0 +1,24 @@
+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.*, format_type(t.oid, null) AS alias,
+ 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 array_agg(provider || '=' || label) FROM pg_shseclabel 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_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_type'::regclass)
+ LEFT OUTER JOIN 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;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..939597e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
@@ -0,0 +1,127 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{#======================================#}
+{# 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 {{ data.typeowner }};
+
+{% endif %}
+{#======================================#}
+{# Below will change objects comment #}
+{% if data.description and data.description != o_data.description %}
+COMMENT ON TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ IS {{ data.description|qtLiteral }};
+
+{% 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)}} {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% endfor %}
+{% endif %}
+{% if 'changed' in composite and composite.changed|length > 0 %}
+{% for r in composite.changed %}
+ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ ALTER ATTRIBUTE {{conn|qtIdent(r.member_name)}} SET DATA TYPE {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% 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}} AFTER {{o_data.enum[o_enum_len].label|qtLiteral }};
+{% 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}} AFTER {{enum.added[p_idx].label|qtLiteral}};
+{% endif %}
+{% endfor %}
+{% 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 %}
+{{ SECLABLE.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 %}
+{{ SECLABLE.SET(conn, 'TYPE', o_data.name, r.provider, r.security_label, o_data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.SET(conn, 'TYPE', o_data.name, r.provider, r.security_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 %}
+{{ PRIVILEGE.UNSETALL(conn, 'TYPE', priv.grantee, o_data.name, o_data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TYPE', priv.grantee, 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, 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 != 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 %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt
new file mode 100644
index 0000000..631037f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt
@@ -0,0 +1,53 @@
+"""
+Here is the list of Postgres inbuilt types & their OID's
+We will use these types to check for validations
+
+## PGOID_TYPE_SERIAL -42L
+## PGOID_TYPE_SERIAL8 -43L
+## PGOID_TYPE_SERIAL2 -44L
+## PGOID_TYPE_BOOL 16L
+## PGOID_TYPE_BYTEA 17L
+## PGOID_TYPE_CHAR 18L
+## PGOID_TYPE_NAME 19L
+## PGOID_TYPE_INT8 20L
+## PGOID_TYPE_INT2 21L
+## PGOID_TYPE_INT4 23L
+## PGOID_TYPE_TEXT 25L
+## PGOID_TYPE_OID 26L
+## PGOID_TYPE_TID 27L
+## PGOID_TYPE_XID 28L
+## PGOID_TYPE_CID 29L
+## PGOID_TYPE_FLOAT4 700L
+## PGOID_TYPE_FLOAT8 701L
+## PGOID_TYPE_MONEY 790L
+## PGOID_TYPE_CHAR_ARRAY 1002L
+## PGOID_TYPE_TEXT_ARRAY 1009L
+## PGOID_TYPE_BPCHAR_ARRAY 1014L
+## PGOID_TYPE_VARCHAR_ARRAY 1015L
+## PGOID_TYPE_BPCHAR 1042L
+## PGOID_TYPE_VARCHAR 1043L
+## PGOID_TYPE_DATE 1082L
+## PGOID_TYPE_TIME 1083L
+## PGOID_TYPE_TIMESTAMP 1114L
+## PGOID_TYPE_TIMESTAMP_ARRAY 1115L
+## PGOID_TYPE_TIME_ARRAY 1183L
+## PGOID_TYPE_TIMESTAMPTZ 1184L
+## PGOID_TYPE_TIMESTAMPTZ_ARRAY 1185L
+## PGOID_TYPE_INTERVAL 1186L
+## PGOID_TYPE_INTERVAL_ARRAY 1187L
+## PGOID_TYPE_NUMERIC_ARRAY 1231L
+## PGOID_TYPE_TIMETZ 1266L
+## PGOID_TYPE_TIMETZ_ARRAY 1270L
+## PGOID_TYPE_BIT 1560L
+## PGOID_TYPE_BIT_ARRAY 1561L
+## PGOID_TYPE_VARBIT 1562L
+## PGOID_TYPE_VARBIT_ARRAY 1563L
+## PGOID_TYPE_NUMERIC 1700L
+## PGOID_TYPE_CSTRING 2275L
+## PGOID_TYPE_ANY 2276L
+## PGOID_TYPE_VOID 2278L
+## PGOID_TYPE_TRIGGER 2279L
+## PGOID_TYPE_LANGUAGE_HANDLER 2280L
+## PGOID_TYPE_INTERNAL 2281L
+## PGOID_TYPE_HANDLER 3115L
+"""
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/acl.sql
new file mode 100644
index 0000000..46da500
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/acl.sql
@@ -0,0 +1,26 @@
+SELECT 'typacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'USAGE' THEN 'U'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT t.typacl
+ FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 %}
+ ) acl,
+ (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
+ AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
+ aclexplode(t.typacl) as d FROM pg_type t WHERE t.oid = {{tid}}::oid) a) d
+ ) d
+ LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+ LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY g.rolname, gt.rolname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/additional_properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/additional_properties.sql
new file mode 100644
index 0000000..0406afd
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/additional_properties.sql
@@ -0,0 +1,40 @@
+{# The SQL given below will fetch composite type#}
+{% if type == 'c' %}
+SELECT attname, format_type(t.oid,NULL) AS typname, attndims, atttypmod, nsp.nspname,
+ (SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup
+-- Min 9.1 start
+ ,collname, nspc.nspname as collnspname
+-- End
+ , att.attrelid
+FROM pg_attribute att
+ JOIN pg_type t ON t.oid=atttypid
+ JOIN pg_namespace nsp ON t.typnamespace=nsp.oid
+ LEFT OUTER JOIN pg_type b ON t.typelem=b.oid
+-- Min 9.1 start
+ LEFT OUTER JOIN pg_collation c ON att.attcollation=c.oid
+ LEFT OUTER JOIN pg_namespace nspc ON c.collnamespace=nspc.oid
+-- End
+WHERE att.attrelid = {{typrelid}}::oid
+ORDER by attnum;
+{% endif %}
+{# The SQL given below will fetch enum type#}
+{% if type == 'e' %}
+SELECT enumlabel FROM pg_enum WHERE enumtypid={{tid}}::oid
+-- Min 9.1
+ORDER by enumsortorder
+--End
+-- else
+-- ORDER by oid
+{% endif %}
+{# The SQL given below will fetch range type#}
+{% if type == 'r' %}
+SELECT rngsubtype, st.typname,
+ rngcollation, col.collname,
+ rngsubopc, opc.opcname,
+ rngcanonical, rngsubdiff
+FROM pg_range
+ LEFT JOIN pg_type st ON st.oid=rngsubtype
+ LEFT JOIN pg_collation col ON col.oid=rngcollation
+ LEFT JOIN pg_opclass opc ON opc.oid=rngsubopc
+WHERE rngtypid={{tid}}::oid;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/create.sql
new file mode 100644
index 0000000..dcb6451
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/create.sql
@@ -0,0 +1,69 @@
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{### 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 %});
+{% 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 }}{% endfor %});
+{% endif %}
+{### Range Type ###}
+{### 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 %}
+
+ );
+{% endif %}
+{### External Type ###}
+{% if data and data.typtype == 'x' %}
+CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS
+ (
+ {% if data.typinput %}INPUT = {{data.typinput}}{% endif %}{% if data.typoutput %},
+ OUTPUT = {{ data.typoutput }}{% endif %}{% if data.typreceive %},
+ RECEIVE = {{data.typreceive}}{% endif %}{% if data.typname %},
+ 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.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}}{% endif %}{% if data.typispreferred %},
+ PREFERRED = {{data.typispreferred}}{% endif %}{% if data.typdefault %},
+ DEFAULT = {{data.typdefault}}{% endif %}{% if data.element %},
+ ELEMENT = {{data.element}}{% endif %}{% if data.typdelim %},
+ DELIMITER = {{data.typdelim|qtLiteral}}{% 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 {{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}};
+{% 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 %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/delete.sql
new file mode 100644
index 0000000..c258827
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/delete.sql
@@ -0,0 +1 @@
+DROP TYPE {{ conn|qtIdent(data.schema, data.name) }}{% if cascade%} CASCADE{% endif %};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_collations.sql
new file mode 100644
index 0000000..4b0169b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_collations.sql
@@ -0,0 +1,7 @@
+SELECT --nspname, collname,
+ CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(collname))
+ ELSE '' END AS collation
+FROM pg_collation c, pg_namespace n
+WHERE c.collnamespace=n.oid
+ORDER BY nspname, collname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_external_functions.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_external_functions.sql
new file mode 100644
index 0000000..53c3c85
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_external_functions.sql
@@ -0,0 +1,40 @@
+{### 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
+ concat(quote_ident(nspname), '.', 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 = 0;
+{% endif %}
+{### TypmodIN list ###}
+{% if typemodin %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='_cstring')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
+{### TypmodOUT list ###}
+{% if typemodout %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='cstring')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_oid.sql
new file mode 100644
index 0000000..3219b2a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_oid.sql
@@ -0,0 +1,11 @@
+{# Below will provide oid for newly created type #}
+SELECT t.oid
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 data %}
+ AND t.typname = {{data.name|qtLiteral}}
+{% endif %}
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_schemas.sql
new file mode 100644
index 0000000..a38db60
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_schemas.sql
@@ -0,0 +1,25 @@
+SELECT
+ nsp.nspname
+FROM
+ pg_namespace nsp
+ LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass)
+ LEFT OUTER JOIN pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid)
+WHERE
+ NOT ((nspname = 'pg_catalog' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR
+ (nspname = 'pgagent' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR
+ (nspname = 'information_schema' AND EXISTS
+ (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR
+ (nspname LIKE '_%' AND EXISTS
+ (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) OR
+ (nspname = 'dbo' OR nspname = 'sys')
+ ) AND
+ nspname NOT LIKE E'pg\\temp\\%' AND
+ nspname NOT LIKE E'pg\\toast_temp\\%' AND
+ -- ADDED: Because We need to omit system schema except the one on which we are trying to create collation
+ ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' ) AND
+ -- Specific code for EDB PPAS
+ nsp.nspparent = 0 AND NOT
+ (nspname = 'dbms_job_procedure' AND EXISTS(SELECT 1 FROM pg_proc WHERE pronamespace = nsp.oid and proname = 'run_job' LIMIT 1))
+ORDER BY nspname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_subtypes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_subtypes.sql
new file mode 100644
index 0000000..5161a99
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_subtypes.sql
@@ -0,0 +1,56 @@
+{### To fill subtype combobox ###}
+{% if subtype %}
+SELECT DISTINCT typ.typname AS stype,
+ (CASE WHEN typ.typcollation > 0 THEN true ELSE false END) AS is_collate
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype = typ.oid
+WHERE opc.opcmethod = 403
+ORDER BY 1
+{% endif %}
+{### To fill subtype opclass combobox ###}
+{% if subtype_opclass and data and data.typname %}
+SELECT opc.opcname
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ORDER BY opcname;
+{% endif %}
+{### To fetch opcinttype from subtype opclass ###}
+{% if get_opcintype and data and data.typname and data.opcname %}
+SELECT opc.opcintype
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ AND opc.opcname = {{ data.opcname|qtLiteral }}
+ORDER BY opcname;
+{% endif %}
+{### To fill subtype diff function combobox ###}
+{% if opcintype %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS stypdiff
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype = 701
+AND proargtypes = '{{opcintype}} {{opcintype}}'
+ORDER BY proname;
+{% endif %}
+{### To fill canonical combobox ###}
+{% if getoid %}
+SELECT oid FROM pg_type
+WHERE typname = {{ data.name|qtLiteral }}
+{% endif %}
+{% if canonical and oid %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS canonical
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype= {{ oid }}
+AND proargtypes = '{{ oid }}'
+ORDER BY proname;
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_types.sql
new file mode 100644
index 0000000..b430e8a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/get_types.sql
@@ -0,0 +1,10 @@
+SELECT * FROM
+ (SELECT format_type(t.oid,NULL) AS typname,
+ CASE WHEN typelem > 0 THEN typelem ELSE t.oid END AS elemoid,
+ typlen, typtype, t.oid, nspname,
+ (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+FROM pg_type t
+ JOIN pg_namespace nsp ON typnamespace=nsp.oid
+WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r')AND NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) AND nsp.nspname != 'information_schema'
+ ) AS dummy
+ ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/nodes.sql
new file mode 100644
index 0000000..6abcb19
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/nodes.sql
@@ -0,0 +1,10 @@
+SELECT t.oid, t.typname AS name
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN pg_namespace nsp ON nsp.oid = t.typnamespace
+WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' AND t.typnamespace = {{scid}}::oid
+{% if not show_system_objects %}
+ AND ct.oid is NULL
+{% endif %}
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/properties.sql
new file mode 100644
index 0000000..94284d6
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/properties.sql
@@ -0,0 +1,21 @@
+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.*, format_type(t.oid, null) AS alias,
+ pg_get_userbyid(t.typowner) as typeowner, e.typname as element,
+ description, ct.oid AS taboid,
+ nsp.nspname AS schema,
+ (CASE WHEN (t.oid <= {{ datlastsysoid}}::oid OR ct.oid != 0) THEN true ElSE false END) AS is_sys_type
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_type'::regclass)
+ LEFT OUTER JOIN 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;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/update.sql
new file mode 100644
index 0000000..082f944
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/pre_9.1/update.sql
@@ -0,0 +1,106 @@
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{#======================================#}
+{# 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 {{ data.typeowner }};
+
+{% endif %}
+{#======================================#}
+{# Below will change objects comment #}
+{% if data.description and data.description != o_data.description %}
+COMMENT ON TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ IS {{ data.description|qtLiteral }};
+
+{% 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)}} {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% endfor %}
+{% endif %}
+{% if 'changed' in composite and composite.changed|length > 0 %}
+{% for r in composite.changed %}
+ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ ALTER ATTRIBUTE {{conn|qtIdent(r.member_name)}} SET DATA TYPE {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% 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}} AFTER {{o_data.enum[o_enum_len].label|qtLiteral }};
+{% 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}} AFTER {{enum.added[p_idx].label|qtLiteral}};
+{% endif %}
+{% endfor %}
+{% endif %}
+
+{% endif %}
+{#======================================#}
+{# Change the privileges #}
+{% if data.typacl %}
+{% 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 %}
+{{ PRIVILEGE.UNSETALL(conn, 'TYPE', priv.grantee, o_data.name, o_data.schema) }}
+{{ 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 != 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 %}
+{% endif %}
\ No newline at end of file
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
@ 2016-03-16 13:02 ` Dave Page <[email protected]>
2016-03-16 13:18 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Ashesh Vashi <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
0 siblings, 2 replies; 26+ messages in thread
From: Dave Page @ 2016-03-16 13:02 UTC (permalink / raw)
To: Murtuza Zabuawala <[email protected]>; +Cc: pgadmin-hackers
Hi
On Mon, Mar 14, 2016 at 12:21 PM, Murtuza Zabuawala
<[email protected]> wrote:
> Hi Dave,
>
> Thank you for reviewing my patch.
>
> I have made changes according to your feedback.
>
> Please review updated patch.
OK, I think it's in pretty good shape now. Here's what I hope are the
last of the review comments, mostly minor things :-)
- I'm allowed to edit ENUM label names, but PostgreSQL doesn't support that.
- Security label fields resize in Edit mode. See recent changes by
Arun to fix that elsewhere.
- When editing a type, I cannot select a Grantee on the Privileges tab.
- When adding a composite type, I cannot type/select directly in the
grid, I have to expand the row.
- If I click ADD to add a new member to a composite type whilst the
previous row is expanded, the new row is added, but the expanded panel
remains linked to the first row.
- If I then click on the Edit button on the new row, I see two
expanded panels, neither of which is attached to their row (and
they're in the wrong order). I would only expect to see one panel at a
time.
- When creating an External type, the Input, Output, Send, Receive and
Analyze function lists are all blank. Is this expected? I would have
thought there would be system functions listed as there are for Typmod
in/out.
- Labels for Yes/No fields should typically end in a '?', e.g.
"Variable?", "Preferred?"
- s/Prefered/Preferred
- Why does "Range" type have a frame around it?
- If a field is there to select/display a function, it should have
"function" at the end of its label, e.g. "Canonical function"
- As above, but with type. E.g. "Element type"
- Some of the combo boxes have hint text of "Select from the list",
whilst others don't. This should be consistent.
- The colouring of "No" on the switches should be blue (see "System Schema?")
- s/Subtype/Sub-type/
- s/SubType diff/Sub-type diff function/
- Please remove the pre-9.1 support.
- In def create(self, gid, sid, did, scid), ensure error messages are
properly pluralised, e.g. 'Composite types require at least two
members' or 'External types require both Input & Output conversion
functions.'
- additional_features.sql is poorly formatted (lack of proper indentation)
Thanks!
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
@ 2016-03-16 13:18 ` Ashesh Vashi <[email protected]>
2016-03-16 14:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
1 sibling, 1 reply; 26+ messages in thread
From: Ashesh Vashi @ 2016-03-16 13:18 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: Murtuza Zabuawala <[email protected]>; pgadmin-hackers
On Wed, Mar 16, 2016 at 6:32 PM, Dave Page <[email protected]> wrote:
> Hi
>
> On Mon, Mar 14, 2016 at 12:21 PM, Murtuza Zabuawala
> <[email protected]> wrote:
> > Hi Dave,
> >
> > Thank you for reviewing my patch.
> >
> > I have made changes according to your feedback.
> >
> > Please review updated patch.
>
> OK, I think it's in pretty good shape now. Here's what I hope are the
> last of the review comments, mostly minor things :-)
>
> - I'm allowed to edit ENUM label names, but PostgreSQL doesn't support
> that.
>
> - Security label fields resize in Edit mode. See recent changes by
> Arun to fix that elsewhere.
>
> - When editing a type, I cannot select a Grantee on the Privileges tab.
>
Is the grantor same as the current user?
If yes - then it is a bug, otherwise it is by design.
Because - you can not change privileges of a grantee, when it was given by
some other grantor.
But - if you delete the privileges for its grantor, it will remove
privileges for the privileges given by it too. (which is though, we have
not yet take care, and we will do it in later.)
--
Thanks & Regards,
Ashesh Vashi
EnterpriseDB INDIA: Enterprise PostgreSQL Company
<http://www.enterprisedb.com/;
*http://www.linkedin.com/in/asheshvashi*
<http://www.linkedin.com/in/asheshvashi;
>
> - When adding a composite type, I cannot type/select directly in the
> grid, I have to expand the row.
>
> - If I click ADD to add a new member to a composite type whilst the
> previous row is expanded, the new row is added, but the expanded panel
> remains linked to the first row.
>
> - If I then click on the Edit button on the new row, I see two
> expanded panels, neither of which is attached to their row (and
> they're in the wrong order). I would only expect to see one panel at a
> time.
>
> - When creating an External type, the Input, Output, Send, Receive and
> Analyze function lists are all blank. Is this expected? I would have
> thought there would be system functions listed as there are for Typmod
> in/out.
>
> - Labels for Yes/No fields should typically end in a '?', e.g.
> "Variable?", "Preferred?"
>
> - s/Prefered/Preferred
>
> - Why does "Range" type have a frame around it?
>
> - If a field is there to select/display a function, it should have
> "function" at the end of its label, e.g. "Canonical function"
>
> - As above, but with type. E.g. "Element type"
>
> - Some of the combo boxes have hint text of "Select from the list",
> whilst others don't. This should be consistent.
>
> - The colouring of "No" on the switches should be blue (see "System
> Schema?")
>
> - s/Subtype/Sub-type/
>
> - s/SubType diff/Sub-type diff function/
>
> - Please remove the pre-9.1 support.
>
> - In def create(self, gid, sid, did, scid), ensure error messages are
> properly pluralised, e.g. 'Composite types require at least two
> members' or 'External types require both Input & Output conversion
> functions.'
>
> - additional_features.sql is poorly formatted (lack of proper indentation)
>
> Thanks!
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
>
> --
> Sent via pgadmin-hackers mailing list ([email protected])
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgadmin-hackers
>
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-16 13:18 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Ashesh Vashi <[email protected]>
@ 2016-03-16 14:52 ` Dave Page <[email protected]>
0 siblings, 0 replies; 26+ messages in thread
From: Dave Page @ 2016-03-16 14:52 UTC (permalink / raw)
To: Ashesh Vashi <[email protected]>; +Cc: Murtuza Zabuawala <[email protected]>; pgadmin-hackers
On Wed, Mar 16, 2016 at 1:18 PM, Ashesh Vashi <[email protected]
> wrote:
> On Wed, Mar 16, 2016 at 6:32 PM, Dave Page <[email protected]> wrote:
>
>> Hi
>>
>> On Mon, Mar 14, 2016 at 12:21 PM, Murtuza Zabuawala
>> <[email protected]> wrote:
>> > Hi Dave,
>> >
>> > Thank you for reviewing my patch.
>> >
>> > I have made changes according to your feedback.
>> >
>> > Please review updated patch.
>>
>> OK, I think it's in pretty good shape now. Here's what I hope are the
>> last of the review comments, mostly minor things :-)
>>
>> - I'm allowed to edit ENUM label names, but PostgreSQL doesn't support
>> that.
>>
>> - Security label fields resize in Edit mode. See recent changes by
>> Arun to fix that elsewhere.
>>
>> - When editing a type, I cannot select a Grantee on the Privileges tab.
>>
> Is the grantor same as the current user?
>
> If yes - then it is a bug, otherwise it is by design.
> Because - you can not change privileges of a grantee, when it was given by
> some other grantor.
>
This is when adding new privileges - which basically doesn't work as the
form won't validate because there is no grantee selected.
>
> But - if you delete the privileges for its grantor, it will remove
> privileges for the privileges given by it too. (which is though, we have
> not yet take care, and we will do it in later.)
>
> --
>
> Thanks & Regards,
>
> Ashesh Vashi
> EnterpriseDB INDIA: Enterprise PostgreSQL Company
> <http://www.enterprisedb.com/;
>
>
> *http://www.linkedin.com/in/asheshvashi*
> <http://www.linkedin.com/in/asheshvashi;
>
>>
>> - When adding a composite type, I cannot type/select directly in the
>> grid, I have to expand the row.
>>
>> - If I click ADD to add a new member to a composite type whilst the
>> previous row is expanded, the new row is added, but the expanded panel
>> remains linked to the first row.
>>
>> - If I then click on the Edit button on the new row, I see two
>> expanded panels, neither of which is attached to their row (and
>> they're in the wrong order). I would only expect to see one panel at a
>> time.
>>
>> - When creating an External type, the Input, Output, Send, Receive and
>> Analyze function lists are all blank. Is this expected? I would have
>> thought there would be system functions listed as there are for Typmod
>> in/out.
>>
>> - Labels for Yes/No fields should typically end in a '?', e.g.
>> "Variable?", "Preferred?"
>>
>> - s/Prefered/Preferred
>>
>> - Why does "Range" type have a frame around it?
>>
>> - If a field is there to select/display a function, it should have
>> "function" at the end of its label, e.g. "Canonical function"
>>
>> - As above, but with type. E.g. "Element type"
>>
>> - Some of the combo boxes have hint text of "Select from the list",
>> whilst others don't. This should be consistent.
>>
>> - The colouring of "No" on the switches should be blue (see "System
>> Schema?")
>>
>> - s/Subtype/Sub-type/
>>
>> - s/SubType diff/Sub-type diff function/
>>
>> - Please remove the pre-9.1 support.
>>
>> - In def create(self, gid, sid, did, scid), ensure error messages are
>> properly pluralised, e.g. 'Composite types require at least two
>> members' or 'External types require both Input & Output conversion
>> functions.'
>>
>> - additional_features.sql is poorly formatted (lack of proper indentation)
>>
>> Thanks!
>>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>>
>> --
>> Sent via pgadmin-hackers mailing list ([email protected])
>> To make changes to your subscription:
>> http://www.postgresql.org/mailpref/pgadmin-hackers
>>
>
>
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
@ 2016-03-17 10:08 ` Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
1 sibling, 1 reply; 26+ messages in thread
From: Murtuza Zabuawala @ 2016-03-17 10:08 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi Dave,
Please find updated patch.
On 16-Mar-2016, at 6:32 pm, Dave Page <[email protected]> wrote:
Hi
OK, I think it's in pretty good shape now. Here's what I hope are the
last of the review comments, mostly minor things :-)
- I'm allowed to edit ENUM label names, but PostgreSQL doesn't support that.
Done
- Security label fields resize in Edit mode. See recent changes by
Arun to fix that elsewhere.
Done
- When editing a type, I cannot select a Grantee on the Privileges tab.
This is general issue with privilege control.
[We will add it in TODO list]
- When adding a composite type, I cannot type/select directly in the
grid, I have to expand the row.
Because currently in Backgrid we do not have functionality like if I edit
one cell second cell re-renders according the value of first cell,
If we expand and select the type the other two fields (length &
precision)are dependent on the value of type for composite types they
become enable/disable accordingly.
- If I click ADD to add a new member to a composite type whilst the
previous row is expanded, the new row is added, but the expanded panel
remains linked to the first row.
- If I then click on the Edit button on the new row, I see two
expanded panels, neither of which is attached to their row (and
they're in the wrong order). I would only expect to see one panel at a
time.
This is general with sub-node collection control.
[We will add it in TODO list]
- When creating an External type, the Input, Output, Send, Receive and
Analyze function lists are all blank. Is this expected? I would have
thought there would be system functions listed as there are for Typmod
in/out.
Dave, I was not able to test External type functionality completely because
it requires external type which is not inbuilt in postgres.
Once you create that custom type using C/C++ code, it will be listed in
those combobox by sql we are using to fetch external function types added
by user in postgres.
- Labels for Yes/No fields should typically end in a '?', e.g.
"Variable?", "Preferred?”
Done
- s/Prefered/Preferred
Done
- Why does "Range" type have a frame around it?
So that we can make visible/hide all the control/elements related to Range
type as whole instead having separate code for each control/element.
That frame is because we used ‘FieldsetControl’ of backform to group all
the elements of Range type.
- If a field is there to select/display a function, it should have
"function" at the end of its label, e.g. "Canonical function”
Done
- As above, but with type. E.g. "Element type”
Done
- Some of the combo boxes have hint text of "Select from the list",
whilst others don't. This should be consistent.
Done
- The colouring of "No" on the switches should be blue (see "System
Schema?")
Done
- s/Subtype/Sub-type/
Done
- s/SubType diff/Sub-type diff function/
Done
- Please remove the pre-9.1 support.
Done
- In def create(self, gid, sid, did, scid), ensure error messages are
properly pluralised, e.g. 'Composite types require at least two
members' or 'External types require both Input & Output conversion
functions.’
Done
- additional_features.sql is poorly formatted (lack of proper indentation)
Done
Thanks!
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/octet-stream] types_node_v3.patch (105.7K, 3-types_node_v3.patch)
download | inline diff:
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
new file mode 100644
index 0000000..ea77a6e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/__init__.py
@@ -0,0 +1,1178 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+""" Implements Type Node """
+
+import json
+from flask import render_template, make_response, request, jsonify
+from flask.ext.babel import gettext
+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 pgadmin.browser.server_groups.servers.databases as database
+from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
+ parse_priv_to_db
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class TypeModule(SchemaChildModule):
+ """
+ class TypeModule(SchemaChildModule)
+
+ A module class for Type node derived from SchemaChildModule
+
+ Methods:
+ -------
+ * __init__(*args, **kwargs)
+ - Method is used to initialize the Type and it's base module.
+
+ * get_nodes(gid, sid, did, scid, tid)
+ - Method is used to generate the browser collection node.
+
+ * node_inode()
+ - Method is overridden from its base class to make the node as leaf node.
+
+ * script_load()
+ - Load the module script for type, when any of the server node is
+ initialized.
+ """
+
+ NODE_TYPE = 'type'
+ COLLECTION_LABEL = gettext("Types")
+
+ def __init__(self, *args, **kwargs):
+ """
+ Method is used to initialize the TypeModule and it's base module.
+
+ Args:
+ *args:
+ **kwargs:
+ """
+ super(TypeModule, self).__init__(*args, **kwargs)
+ self.min_ver = None
+ self.max_ver = None
+
+ def get_nodes(self, gid, sid, did, scid):
+ """
+ Generate the collection node
+ """
+ yield self.generate_browser_collection_node(scid)
+
+ @property
+ def script_load(self):
+ """
+ Load the module script for database, when any of the database node is
+ initialized.
+ """
+ return database.DatabaseModule.NODE_TYPE
+
+ @property
+ def node_inode(self):
+ """
+ Load the module node as a leaf node
+ """
+ return False
+
+blueprint = TypeModule(__name__)
+
+
+class TypeView(PGChildNodeView):
+ """
+ This class is responsible for generating routes for Type node
+
+ Methods:
+ -------
+ * __init__(**kwargs)
+ - Method is used to initialize the TypeView and it's base view.
+
+ * check_precondition()
+ - This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+
+ * list()
+ - This function is used to list all the Type nodes within that
+ collection.
+
+ * nodes()
+ - This function will used to create all the child node within that
+ collection, Here it will create all the Type node.
+
+ * properties(gid, sid, did, scid, tid)
+ - This function will show the properties of the selected Type node
+
+ * create(gid, sid, did, scid)
+ - This function will create the new Type object
+
+ * update(gid, sid, did, scid, tid)
+ - This function will update the data for the selected Type node
+
+ * delete(self, gid, sid, scid, tid):
+ - This function will drop the Type object
+
+ * msql(gid, sid, did, scid, tid)
+ - This function is used to return modified SQL for the selected
+ Type node
+
+ * get_sql(data, scid, tid)
+ - This function will generate sql from model data
+
+ * sql(gid, sid, did, scid):
+ - This function will generate sql to show it in sql pane for the
+ selected Type node.
+
+ * dependency(gid, sid, did, scid, tid):
+ - This function will generate dependency list show it in dependency
+ pane for the selected Type node.
+
+ * dependent(gid, sid, did, scid, tid):
+ - This function will generate dependent list to show it in dependent
+ pane for the selected Type node.
+
+ * additional_properties(copy_dict, tid):
+ - This function will add additional properties in response
+
+ * get_collations(gid, sid, did, scid, tid):
+ - This function will return list of collation in ajax response
+
+ * get_types(gid, sid, did, scid, tid):
+ - This function will return list of types in ajax response
+
+ * get_subtypes(gid, sid, did, scid, tid):
+ - This function will return list of subtypes in ajax response
+
+ * get_subtype_opclass(gid, sid, did, scid, tid):
+ - This function will return list of subtype opclass in ajax response
+
+ * get_subtype_diff(gid, sid, did, scid, tid):
+ - This function will return list of subtype diff functions
+ in ajax response
+
+ * get_canonical(gid, sid, did, scid, tid):
+ - This function will return list of canonical functions
+ in ajax response
+
+ * get_external_functions_list(gid, sid, did, scid, tid):
+ - This function will return list of external functions
+ in ajax response
+ """
+
+ node_type = blueprint.node_type
+
+ parent_ids = [
+ {'type': 'int', 'id': 'gid'},
+ {'type': 'int', 'id': 'sid'},
+ {'type': 'int', 'id': 'did'},
+ {'type': 'int', 'id': 'scid'}
+ ]
+ ids = [
+ {'type': 'int', 'id': 'tid'}
+ ]
+
+ operations = dict({
+ 'obj': [
+ {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+ {'get': 'list', 'post': 'create'}
+ ],
+ 'delete': [{'delete': 'delete'}],
+ 'children': [{'get': 'children'}],
+ 'nodes': [{'get': 'node'}, {'get': 'nodes'}],
+ 'sql': [{'get': 'sql'}],
+ 'msql': [{'get': 'msql'}, {'get': 'msql'}],
+ 'stats': [{'get': 'statistics'}],
+ 'dependency': [{'get': 'dependencies'}],
+ 'dependent': [{'get': 'dependents'}],
+ 'module.js': [{}, {}, {'get': 'module_js'}],
+ 'get_types': [{'get': 'get_types'}, {'get': 'get_types'}],
+ 'get_stypes': [{'get': 'get_subtypes'}, {'get': 'get_subtypes'}],
+ 'get_subopclass': [{'get': 'get_subtype_opclass'},
+ {'get': 'get_subtype_opclass'}],
+ 'get_stypediff': [{'get': 'get_subtype_diff'}, {'get': 'get_subtype_diff'}],
+ 'get_canonical': [{'get': 'get_canonical'}, {'get': 'get_canonical'}],
+ 'get_collations': [{'get': 'get_collations'}, {'get': 'get_collations'}],
+ 'get_external_functions': [{'get': 'get_external_functions_list'},
+ {'get': 'get_external_functions_list'}]
+ })
+
+ def check_precondition(f):
+ """
+ This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+ """
+ @wraps(f)
+ def wrap(*args, **kwargs):
+ # Here args[0] will hold self & kwargs will hold gid,sid,did
+ self = args[0]
+ self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(kwargs['sid'])
+ self.conn = self.manager.connection(did=kwargs['did'])
+
+ # We need datlastsysoid to check if current type is system type
+ self.datlastsysoid = self.manager.db_info[kwargs['did']]['datlastsysoid']
+
+ # If DB not connected then return error to browser
+ if not self.conn.connected():
+ return precondition_required(
+ gettext(
+ "Connection to the server has been lost!"
+ )
+ )
+
+ # Declare allows acl on type
+ self.acl = ['U']
+
+ # we will set template path for sql scripts
+ self.template_path = 'type/sql/9.1_plus'
+
+ return f(*args, **kwargs)
+
+ return wrap
+
+ @check_precondition
+ def list(self, gid, sid, did, scid):
+ """
+ This function is used to list all the type nodes within that collection.
+
+ Args:
+ gid: Server group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of available type nodes
+ """
+
+ SQL = render_template("/".join([self.template_path, 'properties.sql']),
+ scid=scid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects)
+
+ status, res = self.conn.execute_dict(SQL)
+
+ if not status:
+ return internal_server_error(errormsg=res)
+ return ajax_response(
+ response=res['rows'],
+ status=200
+ )
+
+ @check_precondition
+ def nodes(self, gid, sid, did, scid):
+ """
+ This function will used to create all the child node within that collection.
+ Here it will create all the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of available type child nodes
+ """
+
+ res = []
+ SQL = render_template("/".join([self.template_path,
+ 'nodes.sql']), scid=scid,
+ show_system_objects=self.blueprint.show_system_objects)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=rset)
+
+ for row in rset['rows']:
+ res.append(
+ self.blueprint.generate_browser_node(
+ row['oid'],
+ scid,
+ row['name'],
+ icon="icon-type"
+ ))
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ def additional_properties(self, copy_dict, tid):
+ """
+ We will use this function to add additional properties according to type
+
+ Returns:
+ additional properties for type like range/composite/enum
+
+ """
+ # Fetching type of type
+ of_type = copy_dict['typtype']
+ res = dict()
+ # If type is of Composite then we need to add members list in our output
+ if of_type == 'c':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='c',
+ typrelid=copy_dict['typrelid'])
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # To display in properties
+ properties_list = []
+ # To display in composite collection grid
+ composite_lst = []
+
+ for row in rset['rows']:
+ typelist = ' '.join([row['attname'], row['typname']])
+ if not row['collname'] or (row['collname'] == 'default'
+ and row['collnspname'] == 'pg_catalog'):
+ full_collate = ''
+ collate = ''
+ else:
+ full_collate = get_driver(PG_DEFAULT_DRIVER).qtIdent(
+ self.conn, row['collnspname'], row['collname'])
+ collate = ' COLLATE ' + full_collate
+ typelist += collate
+ properties_list.append(typelist)
+
+ # Below logic will allow us to split length, precision from type name for grid
+ import re
+ matchObj = re.match( r'(.*)\((.*?),(.*?)\)', row['typname'])
+ if matchObj:
+ t_name = matchObj.group(1)
+ t_len = matchObj.group(2)
+ t_prec = matchObj.group(3)
+ else:
+ t_name = row['typname']
+ t_len = None
+ t_prec = None
+
+ composite_lst.append({
+ 'member_name': row['attname'], 'type': t_name, 'collation': full_collate,
+ 'tlength': t_len, 'precision': t_prec })
+
+ # Adding both results
+ res['member_list'] = ', '.join(properties_list)
+ res['composite'] = composite_lst
+
+ # If type is of ENUM then we need to add labels in our output
+ if of_type == 'e':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='e', tid=tid)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ # To display in properties
+ properties_list = []
+ # To display in enum grid
+ enum_list = []
+ for row in rset['rows']:
+ properties_list.append(row['enumlabel'])
+ enum_list.append({'label': row['enumlabel']})
+
+ # Adding both results in ouput
+ res['enum_list'] = ', '.join(properties_list)
+ res['enum'] = enum_list
+
+ # If type is of Range then we need to add collation,subtype etc in our output
+ if of_type == 'r':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='r', tid=tid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ range_dict = dict(res['rows'][0])
+ res.update(range_dict)
+
+ # Returning only additional properties only
+ return res
+
+ @check_precondition
+ def properties(self, gid, sid, did, scid, tid):
+ """
+ This function will show the properties of the selected type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of selected type node
+ """
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ copy_dict = dict(res['rows'][0])
+
+ # We need to parse & convert ACL coming from database to json format
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ copy_dict['typacl'] = []
+
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in copy_dict:
+ copy_dict[row['deftype']].append(priv)
+ else:
+ copy_dict[row['deftype']] = [priv]
+
+ # Calling function to check and additional properties if available
+ copy_dict.update(self.additional_properties(copy_dict, tid))
+
+ return ajax_response(
+ response=copy_dict,
+ status=200
+ )
+
+ @check_precondition
+ def get_collations(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of collation available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_collations.sql']))
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['collation'],
+ 'value': row['collation']}
+ )
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_types(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of types available
+ as AJAX response.
+ """
+ res = []
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_types.sql']))
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ # Attaching properties for precession
+ # & length validation for current type
+ precision = False
+ length = False
+ min_val = 0
+ max_val = 0
+
+ # Check against PGOID for specific type
+ if row['elemoid']:
+ if row['elemoid'] in (1560, 1561, 1562, 1563, 1042, 1043,
+ 1014, 1015):
+ typeval = 'L'
+ elif row['elemoid'] in (1083, 1114, 1115, 1183, 1184, 1185,
+ 1186, 1187, 1266, 1270):
+ typeval = 'D'
+ elif row['elemoid'] in (1231, 1700):
+ typeval = 'P'
+ else:
+ typeval = ' '
+
+ # Logic to set precision & length/min/max values
+ if typeval == 'P':
+ precision = True
+
+ if precision or typeval in ('L', 'D'):
+ length = True
+ min_val = 0 if typeval == 'D' else 1
+ if precision:
+ max_val = 1000
+ elif min_val:
+ # Max of integer value
+ max_val = 2147483647
+ else:
+ max_val = 10
+
+ res.append(
+ {'label': row['typname'], 'value': row['typname'],
+ 'typval': typeval, 'precision': precision,
+ 'length': length, 'min_val': min_val, 'max_val': max_val
+ }
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtypes(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtypes available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ subtype=True)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['stype'], 'value': row['stype'],
+ 'is_collate': row['is_collate']}
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtype_opclass(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtype opclass available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ subtype_opclass=True, data=data)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['opcname'],
+ 'value': row['opcname']})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtype_diff(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtypes diff functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ get_opcintype=True, data=data)
+ if SQL:
+ status, opcintype = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=opcintype)
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ opcintype=opcintype, conn=self.conn)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['stypdiff'],
+ 'value': row['stypdiff']}
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_canonical(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of canonical functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+ canonical = True
+
+ try:
+ # We want to send data only if in we are in edit mode
+ # else we will disable the combobox
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ getoid=True, data=data)
+ if SQL:
+ status, oid = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=oid)
+ # If oid is None then do not run SQL
+ if oid is None:
+ canonical = False
+
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ canonical=canonical, conn=self.conn, oid=oid)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['canonical'],
+ 'value': row['canonical']})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def get_external_functions_list(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of external functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': '', 'cbtype': 'all'}]
+
+ try:
+ # The SQL generated below will populate Input/Output/Send/
+ # Receive/Analyze/TypModeIN/TypModOUT combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ extfunc=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'all'})
+
+ # The SQL generated below will populate TypModeIN combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ typemodin=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'typmodin'})
+
+ # The SQL generated below will populate TypModeIN combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ typemodout=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'typmodout'})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def create(self, gid, sid, did, scid):
+ """
+ This function will creates new the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ data = request.form if request.form else json.loads(request.data.decode())
+ required_args = {
+ 'name': 'Name',
+ 'typtype': 'Type'
+ }
+
+ for arg in required_args:
+ if arg not in data:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ "Couldn't find the required parameter (%s)." %
+ required_args[arg]
+ )
+ )
+ # Additional checks goes here
+ # If type is composite then check if it has two members
+ if data and data[arg] == 'c':
+ if len(data['composite']) < 2:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Composite types requires at least two members'
+ )
+ )
+ # If type is enum then check if it has minimum one label
+ if data and data[arg] == 'e':
+ if len(data['enum']) < 1:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Enumeration types requires at least one label'
+ )
+ )
+ # If type is range then check if subtype is defined or not
+ if data and data[arg] == 'r':
+ if data['typname'] is None:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Subtype must be defined for range types'
+ )
+ )
+ # If type is external then check if input/output
+ # conversion function is defined
+ if data and data[arg] == 'x':
+ if data['typinput'] is None or \
+ data['typoutput'] is None:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'External types requires both Input & \
+ Output conversion function.'
+ )
+ )
+
+ # To format privileges coming from client
+ if 'typacl' in data and data['typacl'] is not None:
+ data['typacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ try:
+ SQL = render_template("/".join([self.template_path, 'create.sql']),
+ data=data, conn=self.conn)
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # we need oid to to add object in tree at browser
+ SQL = render_template("/".join([self.template_path,
+ 'get_oid.sql']),
+ scid=scid, data=data)
+ status, tid = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=tid)
+
+ return jsonify(
+ node=self.blueprint.generate_browser_node(
+ tid,
+ scid,
+ data['name'],
+ icon="icon-type"
+ )
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def update(self, gid, sid, did, scid, tid):
+ """
+ This function will updates existing the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+
+ data = request.form if request.form else json.loads(request.data.decode())
+ try:
+ SQL = self.get_sql(gid, sid, data, scid, tid)
+ if SQL and SQL.strip('\n') and SQL.strip(' '):
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return make_json_response(
+ success=1,
+ info="Type updated",
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+ else:
+ return make_json_response(
+ success=1,
+ info="Nothing to update",
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def delete(self, gid, sid, did, scid, tid):
+ """
+ This function will updates existing the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+
+ # Below will decide if it's simple drop or drop with cascade call
+ if self.cmd == 'delete':
+ # This is a cascade operation
+ cascade = True
+ else:
+ cascade = False
+
+ try:
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'delete.sql']),
+ data=data, cascade=cascade, conn=self.conn)
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return make_json_response(
+ success=1,
+ info=gettext("Type dropped"),
+ data={
+ 'id': tid,
+ 'scid': scid
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def msql(self, gid, sid, did, scid, tid=None):
+ """
+ This function will generates modified sql for type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ req = request.args
+ data = dict()
+
+ # converting nested request data in proper json format
+ for key,val in req.items():
+ if key in ['composite', 'enum', 'seclabels', 'typacl']:
+ data[key] = json.loads(val)
+ else:
+ data[key] = val
+
+ try:
+ SQL = self.get_sql(gid, sid, data, scid, tid)
+
+ if SQL and SQL.strip('\n') and SQL.strip(' '):
+ return make_json_response(
+ data=SQL,
+ status=200
+ )
+ except Exception as e:
+ internal_server_error(errormsg=str(e))
+
+ def get_sql(self, gid, sid, data, scid, tid=None):
+ """
+ This function will genrate sql from model data
+ """
+ if tid is not None:
+
+ for key in ['typacl']:
+ if key in data and data[key] is not None:
+ if 'added' in data[key]:
+ data[key]['added'] = parse_priv_to_db(data[key]['added'], self.acl)
+ if 'changed' in data[key]:
+ data[key]['changed'] = parse_priv_to_db(data[key]['changed'], self.acl)
+ if 'deleted' in data[key]:
+ data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], self.acl)
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ old_data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ old_data['typacl'] = []
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in old_data:
+ old_data[row['deftype']].append(priv)
+ else:
+ old_data[row['deftype']] = [priv]
+
+ # Calling function to check and additional properties if available
+ old_data.update(self.additional_properties(old_data, tid))
+
+ SQL = render_template(
+ "/".join([self.template_path, 'update.sql']),
+ data=data, o_data=old_data, conn=self.conn
+ )
+ else:
+ required_args = [
+ 'name',
+ 'typtype'
+ ]
+
+ for arg in required_args:
+ if arg not in data:
+ return " --definition incomplete"
+
+ # Privileges
+ if 'typacl' in data and data['typacl'] is not None:
+ data['typacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ SQL = render_template("/".join([self.template_path,
+ 'create.sql']),
+ data=data, conn=self.conn)
+
+ return SQL
+
+
+ @check_precondition
+ def sql(self, gid, sid, did, scid, tid):
+ """
+ This function will generates reverse engineered sql for type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ data['typacl'] = []
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in data:
+ data[row['deftype']].append(priv)
+ else:
+ data[row['deftype']] = [priv]
+
+ # Privileges
+ if 'typacl' in data and data['typacl'] is not None:
+ data['nspacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ # Calling function to check and additional properties if available
+ data.update(self.additional_properties(data, tid))
+
+ # We do not want to display table which has '-' value
+ # setting them to None so that jinja avoid displaying them
+ for k in data:
+ if data[k] == '-':
+ data[k] = None
+
+ SQL = self.get_sql(gid, sid, data, scid, tid=None)
+
+ # We are appending headers here for sql panel
+ sql_header = "-- Type: {0}\n\n-- ".format(data['name'])
+ sql_header += render_template("/".join([self.template_path,
+ 'delete.sql']),
+ data=data, conn=self.conn)
+ SQL = sql_header + '\n\n' + SQL
+
+ return ajax_response(response=SQL)
+
+ @check_precondition
+ def dependents(self, gid, sid, did, scid, tid):
+ """
+ This function get the dependents and return ajax response
+ for the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ dependents_result = self.get_dependents(
+ self.conn, tid
+ )
+
+ return ajax_response(
+ response=dependents_result,
+ status=200
+ )
+
+ @check_precondition
+ def dependencies(self, gid, sid, did, scid, tid):
+ """
+ This function get the dependencies and return ajax response
+ for the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ dependencies_result = self.get_dependencies(
+ self.conn, tid
+ )
+
+ return ajax_response(
+ response=dependencies_result,
+ status=200
+ )
+
+TypeView.register_node_view(blueprint)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/coll-type.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/coll-type.png
new file mode 100644
index 0000000000000000000000000000000000000000..fb020d7d99f84439046d288615e865ee1fbdb815
GIT binary patch
literal 329
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=3NcXI=aM>fHaQT@Q91
z`hS1R{~OExU!3;;MEm~(mH&6<{olN3-cDJdI>wS9zhDN3XE)M-9L@rd$YLPv0mg18
zv+aP47*7|+5RU7%XQO!=40zlgo^wo$EU!My#3j(ks*}LT9dUr^nFV*x`<LfEXLU&{
zNT_QZ){p11jr8BRJf*J9t1q$D%X6FK(q}uIk4`OV_&v+tAZF)<c~_)!|NM{V=e#WX
zN#J9vG0+~>64!{5l*E!$tK_0oAjM#0U}&IgXryak7-D2#Wnye)VybOmYGq(B@15Q%
q6b-rgDVb@N5Df;FU=2XkCRPS!5DllMhpqu?VDNPHb6Mw<&;$UZ$8*O3
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/type.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/type.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c16764e7d08c56922a97a2f0c6cc06455c86a56
GIT binary patch
literal 325
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv5AX?b1=8~#9EmzT>)QWU=l(xE
z`v2js|F_rtzdY~%nF;@oHvQjQ{(pPk|IMk)0@*;Nj3q&S!3+-1ZlnP@oCO|{#X#Bv
zjNMLV+W{G&o-U3d9M_W*4zM_RIV|8v(U4|t6r8Z|5fh7_LtB=H01KmJR;I%QmXsCK
znH_-=7a3W69onAxD9m6<$ym$O<m%A&El=SFOUjEmj7`o4+9Dfn@G_j<Bfar~Z-)!e
z0@V`Nh?11Vl2ohYqEsNoU}RuuplfKPYhV~+WME}tY-M7qZD49;U@-5U-YOIgx%nxX
kX_XKS29{tAK-DHz24)Zqr>2Ll0cv3IboFyt=akR{01h5&qyPW_
literal 0
HcmV?d00001
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
new file mode 100644
index 0000000..6e5dda0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/js/type.js
@@ -0,0 +1,864 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'pgadmin',
+ 'pgadmin.browser', 'alertify', 'backgrid', 'pgadmin.backgrid',
+ 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
+
+ if (!pgBrowser.Nodes['coll-type']) {
+ var databases = pgAdmin.Browser.Nodes['coll-type'] =
+ pgAdmin.Browser.Collection.extend({
+ node: 'type',
+ label: '{{ _('Types') }}',
+ type: 'coll-type',
+ columns: ['name', 'typeowner', 'description']
+ });
+ };
+
+ // Switch options to save space in model's field
+ var switchOptions = {
+ 'onText': 'Yes', 'offText': 'No',
+ 'onColor': 'success', 'offColor': 'primary',
+ 'size': 'small'
+ };
+
+ // Security label model declaration
+ var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ provider: undefined,
+ security_label: undefined
+ },
+ schema: [{
+ id: 'provider', label: '{{ _('Provider') }}',
+ type: 'text', disabled: false, cellHeaderClasses:'width_percent_50'
+ },{
+ id: 'security_label', label: '{{ _('Security Label') }}',
+ type: 'text', disabled: false, cellHeaderClasses:'width_percent_50'
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+
+ if (_.isUndefined(this.get('security_label')) ||
+ _.isNull(this.get('security_label')) ||
+ String(this.get('security_label')).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = '{{ _('Please provide the value for security label.') }}';
+ this.errorModel.set('security_label', errmsg);
+ return errmsg;
+ } else {
+ this.errorModel.unset('security_label');
+ }
+ return null;
+ }
+ });
+
+ // Composite type model declaration
+ var CompositeModel = Backform.CompositeModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ member_name: undefined,
+ type: undefined,
+ tlength: undefined,
+ is_tlength: false,
+ precision: undefined,
+ is_precision: false,
+ collation: undefined,
+ min_val: undefined,
+ max_val: undefined,
+ },
+ type_options: undefined,
+ subtypes: undefined,
+ schema: [{
+ id: 'member_name', label: '{{ _('Member Name') }}',
+ type: 'text', disabled: false, editable: false
+ },{
+ id: 'type', label: '{{ _('Type') }}', control: 'node-ajax-options',
+ type: 'text', url: 'get_types', disabled: false, node: 'type',
+ editable: false,
+ transform: function(d){
+ this.model.type_options = d;
+ return d;
+ }
+ },{
+ id: 'tlength', label: '{{ _('Length') }}', deps: ['type'], type: 'text',
+ editable: false,
+ disabled: function(m) {
+ // We will store type from selected from combobox
+ var of_type = m.get('type');
+ if(m.type_options) {
+ // iterating over all the types
+ _.each(m.type_options, function(o) {
+ // if type from selected from combobox matches in options
+ if ( of_type == o.value ) {
+ // if length is allowed for selected type
+ if(o.length)
+ {
+ // set the values in model
+ m.set('is_tlength', true, {silent: true});
+ m.set('min_val', o.min_val, {silent: true});
+ m.set('max_val', o.max_val, {silent: true});
+ }
+ }
+ });
+ }
+ return !m.get('is_tlength');
+ }
+ },{
+ id: 'precision', label: '{{ _('Precision') }}', deps: ['type'],
+ type: 'text', editable: false,
+ disabled: function(m) {
+ // We will store type from selected from combobox
+ var of_type = m.get('type');
+ if(m.type_options) {
+ // iterating over all the types
+ _.each(m.type_options, function(o) {
+ // if type from selected from combobox matches in options
+ if ( of_type == o.value ) {
+ // if precession is allowed for selected type
+ if(o.precision)
+ {
+ // set the values in model
+ m.set('is_precision', true, {silent: true});
+ m.set('min_val', o.min_val, {silent: true});
+ m.set('max_val', o.max_val, {silent: true});
+ }
+ }
+ });
+ }
+ return !m.get('is_precision');
+ }
+ },{
+ id: 'collation', label: '{{ _('Collation') }}',
+ control: 'node-ajax-options', editable: false,
+ type: 'text', disabled: false, url: 'get_collations', node: 'type'
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+ // Validation for member name
+ if (_.isUndefined(data.member_name) ||
+ _.isNull(data.member_name) ||
+ String(data.member_name).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = _("Please specify the value for member name.")
+ this.errorModel.set('member_name', errmsg)
+ return errmsg;
+ } else {
+ this.errorModel.unset('member_name');
+ }
+
+ // Validation for Length field
+ if (data.is_tlength && !_.isUndefined(data.tlength)) {
+ if (data.tlength < data.min_val )
+ errmsg = _("Length should not be less than " + data.min_val)
+ if (data.tlength > data.max_val )
+ errmsg = _("Length should not be greater than " + data.max_val)
+ // If we have any error set then throw it to user
+ if(errmsg) {
+ this.errorModel.set('tlength', errmsg)
+ return errmsg;
+ }
+ } else {
+ this.errorModel.unset('tlength');
+ }
+
+ // Validation for precision field
+ if (data.is_precision && !_.isUndefined(data.precision)) {
+ if (data.precision < data.min_val )
+ errmsg = _("Precision should not be less than " + data.min_val)
+ if (data.precision > data.max_val )
+ errmsg = _("Precision should not be greater than " + data.max_val)
+ // If we have any error set then throw it to user
+ if(errmsg) {
+ this.errorModel.set('precision', errmsg)
+ return errmsg;
+ }
+ } else {
+ this.errorModel.unset('precision');
+ }
+
+ return null;
+ }
+ });
+
+ var EnumModel = Backform.EnumModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ label: undefined,
+ },
+ schema: [{
+ id: 'label', label: '{{ _('Label') }}',type: 'text', disabled: false,
+ cellHeaderClasses: 'width_percent_99', editable: function(m) {
+ return _.isUndefined(m.get('label'));
+ }
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+
+ if (_.isUndefined(data.label) ||
+ _.isNull(data.label) ||
+ String(data.label).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = _("Please specify the value for label.");
+ this.errorModel.set('label', errmsg)
+ return errmsg;
+ } else {
+ this.errorModel.unset('label');
+ }
+
+
+ return null;
+ }
+ });
+
+ if (!pgBrowser.Nodes['type']) {
+ pgAdmin.Browser.Nodes['type'] = pgBrowser.Node.extend({
+ type: 'type',
+ label: '{{ _('Type') }}',
+ collection_type: 'coll-type',
+ hasSQL: true,
+ hasDepends: true,
+ parent_type: ['schema', 'catalog'],
+ Init: function() {
+ /* Avoid mulitple registration of menus */
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+
+ pgBrowser.add_menus([{
+ name: 'create_type_on_coll', node: 'coll-type', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_type', node: 'type', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_type', node: 'schema', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: false},
+ enable: 'canCreate'
+ }
+ ]);
+
+ },
+ canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+ canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+ ext_funcs: undefined,
+ model: pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ name: undefined,
+ oid: undefined,
+ is_sys_type: false,
+ typtype: undefined
+ },
+
+ // Default values!
+ initialize: function(attrs, args) {
+ var isNew = (_.size(attrs) === 0);
+
+ if (isNew) {
+ var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
+ var schemaInfo = args.node_info.schema;
+
+ this.set({'typeowner': userInfo.name}, {silent: true});
+ this.set({'schema': schemaInfo.label}, {silent: true});
+ }
+ pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+ },
+
+ schema: [{
+ id: 'name', label: '{{ _('Name') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchema'
+ },{
+ id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+ type: 'text' , mode: ['properties'], disabled: true
+ },{
+ id: 'typeowner', label:'{{ _('Owner') }}', cell: 'string',
+ control: 'node-list-by-name',
+ type: 'text', mode: ['properties', 'create', 'edit'], node: 'role',
+ disabled: 'inSchema'
+ },{
+ id: 'schema', label:'{{ _('Schema') }}', cell: 'string',
+ type: 'text', mode: ['create', 'edit'], node: 'schema',
+ disabled: 'inSchema', filter: function(d) {
+ // If schema name start with pg_* then we need to exclude them
+ if(d && d.label.match(/^pg_/))
+ {
+ return false;
+ }
+ return true;
+ },
+ control: Backform.NodeListByNameControl.extend({
+ render: function(){
+ // Initialize parent's render method
+ Backform.NodeListByNameControl.prototype.render.apply(this, arguments);
+
+ // Set schema default value to its parent Schema
+ if(this.model.isNew()){
+ this.model.set({'schema': this.model.node_info.schema.label});
+ }
+ return this;
+ }
+ })
+ },{
+ id: 'typtype', label:'{{ _('Type') }}',
+ mode: ['create','edit'], disabled: 'inSchemaWithModelCheck',
+ group: '{{ _('Definition') }}',
+ mode: ['edit', 'create'],
+ select2: { width: "50%" },
+ options: [
+ {label: "Composite", value: "c"},
+ {label: "Enumeration", value: "e"},
+ {label: "External", value: "x"},
+ {label: "Range", value: "r"},
+ ],
+ disabled: 'inSchemaWithModelCheck',
+ // If create mode then by default open composite type
+ control: Backform.Select2Control.extend({
+ render: function(){
+ // Initialize parent's render method
+ Backform.Select2Control.prototype.render.apply(this, arguments);
+ if(this.model.isNew()) {
+ this.model.set({'typtype': 'c'});
+ }
+ return this;
+ }
+ })
+ },{
+ id: 'composite', label: '{{ _('Composite Type') }}',
+ model: CompositeModel, editable: true, type: 'collection',
+ group: '{{ _('Definition') }}', mode: ['edit', 'create'],
+ control: 'unique-col-collection', uniqueCol : ['member_name'],
+ canAdd: true, canEdit: true, canDelete: true, disabled: 'inSchema',
+ deps: ['typtype'], deps: ['typtype'],
+ visible: function(m) {
+ if (m.get('typtype') === 'c') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'enum', label: '{{ _('Enumeration Type') }}',
+ model: EnumModel, editable: true, type: 'collection',
+ group: '{{ _('Definition') }}', mode: ['edit', 'create'],
+ canAdd: true, canEdit: false, canDelete: function(m) {
+ // We will disable it if it's in 'edit' mode
+ if (m.isNew()) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ disabled: 'inSchema', deps: ['typtype'],
+ control: 'unique-col-collection', uniqueCol : ['label'],
+ visible: function(m) {
+ return m.get('typtype') === 'e';
+ }
+ },{
+ // We will disable range type control in edit mode
+ type: 'nested', control: 'fieldset', group: '{{ _('Definition') }}',
+ mode: ['edit', 'create'],
+ visible: function(m) {
+ return m.get('typtype') === 'r';
+ }, deps: ['typtype'], label: '{{ _('Range Type') }}',
+ schema:[{
+ id: 'typname', label:'{{ _('Sub-type') }}', cell: 'string',
+ control: 'node-ajax-options',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ url: 'get_stypes', type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}', disabled: 'inSchemaWithModelCheck',
+ transform: function(d){
+ this.model.subtypes = d;
+ return d;
+ }
+ },{
+ id: 'opcname', label:'{{ _('Sub-type OpClass') }}', cell: 'string',
+ mode: ['properties', 'create', 'edit'], group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['typname'],
+ control: 'select', options: function() {
+ var l_typname = this.model.get('typname'),
+ self = this,
+ result = [];
+ if(!_.isUndefined(l_typname) && l_typname != '')
+ {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_subopclass', this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {'typname' : l_typname},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error', self.model, self.field);
+ }
+ });
+ //
+ }
+ return result;
+ }
+ },{
+ id: 'collname', label:'{{ _('Collation') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ deps: ['typname'], control: 'node-ajax-options', url: 'get_collations',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ disabled: function(m) {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+
+ // Disbale in edit mode
+ if (!m.isNew()) {
+ return true;
+ }
+
+ // To check if collation is allowed?
+ var of_subtype = m.get('typname'),
+ is_collate = undefined;
+ if(!_.isUndefined(of_subtype)) {
+ // iterating over all the types
+ _.each(m.subtypes, function(s) {
+ // if subtype from selected from combobox matches
+ if ( of_subtype === s.label ) {
+ // if collation is allowed for selected subtype
+ // then enable it else disable it
+ is_collate = s.is_collate;
+ }
+ });
+ }
+ // If is_collate is true then do not disable
+ return is_collate ? false : true;
+ }
+ },{
+ id: 'rngcanonical', label:'{{ _('Canonical function') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['name', 'typname'],
+ control: 'select', options: function() {
+ var name = this.model.get('name'),
+ self = this,
+ result = [];
+
+ if(!_.isUndefined(name) && name != '')
+ {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_canonical', this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {"name" : name},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error',
+ self.model, self.field);
+ }
+ });
+ }
+ return result;
+ }
+ },{
+ id: 'rngsubdiff', label:'{{ _('Sub-Type diff function') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['opcname'],
+ control: 'select', options: function() {
+ var l_typname = this.model.get('typname'),
+ l_opcname = this.model.get('opcname'),
+ self = this,
+ result = [];
+
+ if(!_.isUndefined(l_typname) && l_typname != '' &&
+ !_.isUndefined(l_opcname) && l_opcname != '') {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_stypediff',
+ this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {'typname' : l_typname, 'opcname': l_opcname},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error',
+ self.model, self.field);
+ }
+ });
+ }
+ return result;
+ }
+ }]
+ },{
+ type: 'nested', control: 'tab', group: '{{ _('Definition') }}',
+ label: '{{ _('External Type') }}', deps: ['typtype'],
+ mode: ['create', 'edit'],
+ visible: function(m) {
+ return m.get('typtype') === 'x';
+ },
+ schema:[{
+ id: 'typinput', label:'{{ _('Input function') }}',
+ cell: 'string',type: 'text',
+ mode: ['properties', 'create', 'edit'], group: 'Required',
+ disabled: 'inSchemaWithModelCheck',
+ control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typoutput', label:'{{ _('Output function') }}',
+ cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Required',
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typreceive', label:'{{ _('Receive function') }}',
+ cell: 'string', type: 'text', group: 'Optional-1',
+ mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typsend', label:'{{ _('Send function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typmodin', label:'{{ _('Typmod in function') }}',
+ cell: 'string', type: 'text',
+ mode: ['properties', 'create', 'edit'], group: 'Optional-1',
+ disabled: 'inSchemaWithModelCheck',
+ control: 'node-ajax-options', url: 'get_external_functions',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ transform: function(d) {
+ var result = [{label :"", value : ""}];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype === 'typmodin' || item.cbtype === 'all') {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ },{
+ id: 'typmodout', label:'{{ _('Typmod out function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck',
+ control: 'node-ajax-options', url: 'get_external_functions',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ transform: function(d) {
+ var result = [{label :"", value : ""}];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype === 'typmodout' || item.cbtype === 'all') {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ },{
+ id: 'typlen', label:'{{ _('Internal length') }}',
+ cell: 'integer', group: 'Optional-1',
+ type: 'int', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'variable', label:'{{ _('Variable?') }}', cell: 'switch',
+ group: 'Optional-1', type: 'switch',
+ mode: ['create','edit'], options: switchOptions,
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typdefault', label:'{{ _('Default?') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typanalyze', label:'{{ _('Analyze function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typcategory', label:'{{ _('Category type') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label :"Array types", value : "A"},
+ {label :"Boolean types", value : "B"},
+ {label :"Composite types", value : "C"},
+ {label :"Date/time types", value : "D"},
+ {label :"Enum types", value : "E"},
+ {label :"Geometric types", value : "G"},
+ {label :"Network address types", value : "I"},
+ {label :"Numeric types", value : "N"},
+ {label :"Pseudo-types", value : "P"},
+ {label :"String types", value : "S"},
+ {label :"Timespan types", value : "T"},
+ {label :"User-defined types", value : "U"},
+ {label :"Bit-string types", value : "V"},
+ {label :"unknown type", value : "X"}
+ ]
+ },{
+ id: 'typispreferred', label:'{{ _('Preferred?') }}', cell: 'switch',
+ type: 'switch', mode: ['properties', 'create','edit'],
+ options: switchOptions, disabled: 'inSchemaWithModelCheck',
+ group: 'Optional-1'
+ },{
+ id: 'element', label:'{{ _('Element type') }}', cell: 'string',
+ control: 'node-ajax-options', group: 'Optional-2',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', url: 'get_types'
+ },{
+ id: 'typdelim', label:'{{ _('Delimiter') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Optional-2', disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typalign', label:'{{ _('Alignment type') }}',
+ cell: 'string', group: 'Optional-2',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label: "char", value: "c"},
+ {label: "int2", value: "s"},
+ {label: "in4", value: "i"},
+ {label: "double", value: "d"},
+ ]
+ },{
+ id: 'typstorage', label:'{{ _('Storage type') }}',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Optional-2', cell: 'string',
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label: "PLAIN", value: "p"},
+ {label: "MAIN", value: "e"},
+ {label: "EXTERNAL", value: "m"},
+ {label: "EXTENDED", value: "x"},
+ ]
+ },{
+ id: 'typbyval', label:'{{ _('Passed by Value?') }}',
+ cell: 'switch', options: switchOptions,
+ type: 'switch', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', group: 'Optional-2',
+ },{
+ id: 'is_collatable', label:'{{ _('Collatable?') }}',
+ cell: 'switch', min_version: 90100, group: 'Optional-2',
+ type: 'switch', mode: ['properties', 'create', 'edit'],
+ options: switchOptions, disabled: 'inSchemaWithModelCheck'
+ // End of extension tab
+ }]
+ },{
+ id: 'alias', label:'{{ _('Alias') }}', cell: 'string',
+ type: 'text', mode: ['properties'],
+ disabled: 'inSchema'
+ },{
+ id: 'type_acl', label:'{{ _('Privileges') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Security') }}',
+ disabled: 'inSchema'
+ },{
+ id: 'member_list', label:'{{ _('Members') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Definition') }}',
+ disabled: 'inSchema', visible: function(m) {
+ if(m.get('typtype') === 'c') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'enum_list', label:'{{ _('Labels') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Definition') }}',
+ disabled: 'inSchema', visible: function(m) {
+ if(m.get('typtype') === 'e') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'is_sys_type', label:'{{ _('System type?') }}', cell: 'switch',
+ type: 'switch', mode: ['properties'], options: switchOptions,
+ disabled: 'inSchema'
+ },{
+ id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+ type: 'multiline', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchema'
+ },{
+ 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']
+ },{
+ 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'
+ }],
+ validate: function() {
+ // Validation code for required fields
+ var changedAttrs = this.sessAttrs,
+ msg = undefined;
+
+ if (_.has(changedAttrs, 'name') &&
+ (_.isUndefined(this.get('name'))
+ || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Name can not be empty!') }}';
+ this.errorModel.set('name', msg);
+ } else if (_.has(changedAttrs, 'schema') &&
+ (_.isUndefined(this.get('schema'))
+ || String(this.get('schema')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Schema can not be empty!') }}';
+ this.errorModel.set('schema', msg);
+ } else if (_.has(changedAttrs, 'typtype') &&
+ (_.isUndefined(this.get('typtype'))
+ || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Type can not be empty!') }}';
+ this.errorModel.set('typtype', msg);
+ } else if (this.get('typtype') == 'r' &&
+ _.has(changedAttrs, 'typname')
+ && (_.isUndefined(this.get('typname'))
+ || String(this.get('typname')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Subtype Name can not be empty!') }}';
+ this.errorModel.set('typname', msg);
+ } else if (this.get('typtype') == 'x' &&
+ _.has(changedAttrs, 'typinput')
+ && (_.isUndefined(this.get('typinput'))
+ || String(this.get('typinput')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Input function can not be empty!') }}';
+ this.errorModel.set('typinput', msg);
+ } else if (this.get('typtype') == 'x' &&
+ _.has(changedAttrs, 'typoutput')
+ && (_.isUndefined(this.get('typoutput'))
+ || String(this.get('typoutput')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Output function can not be empty!') }}';
+ this.errorModel.set('typoutput', msg);
+ } else {
+ this.errorModel.unset('name');
+ this.errorModel.unset('schema');
+ this.errorModel.unset('typtype');
+ this.errorModel.unset('typname');
+ this.errorModel.unset('typinput');
+ this.errorModel.unset('typoutput');
+ }
+ return null;
+ },
+ // We will disable everything if we are under catalog node
+ inSchema: function() {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+ return false;
+ },
+ // We will check if we are under schema node & in 'create' mode
+ inSchemaWithModelCheck: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ // We will disbale control if it's in 'edit' mode
+ if (m.isNew()) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+ return true;
+ },
+ // We want to enable only in edit mode
+ inSchemaWithEditMode: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ // We will disbale control if it's in 'edit' mode
+ if (m.isNew()) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+ return true;
+ },
+ // Function will help us to fill combobox
+ external_func_combo: function(d) {
+ var result = [];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype == 'all' ) {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ }),
+ canCreate: function(itemData, item, data) {
+ //If check is false then , we will allow create menu
+ if (data && data.check == false)
+ return true;
+
+ var t = pgBrowser.tree, i = item, d = itemData;
+ // To iterate over tree to check parent node
+ while (i) {
+ // If it is schema then allow user to create table
+ if (_.indexOf(['schema'], d._type) > -1)
+ return true;
+
+ if ('coll-type' == d._type) {
+ //Check if we are not child of catalog
+ prev_i = t.hasParent(i) ? t.parent(i) : null;
+ prev_d = prev_i ? t.itemData(prev_i) : null;
+ if( prev_d._type == 'catalog') {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ i = t.hasParent(i) ? t.parent(i) : null;
+ d = i ? t.itemData(i) : null;
+ }
+ // by default we do not want to allow create menu
+ return true;
+ }
+ });
+ }
+ return pgBrowser.Nodes['type'];
+});
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql
new file mode 100644
index 0000000..60eab25
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql
@@ -0,0 +1,26 @@
+SELECT 'typacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'USAGE' THEN 'U'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT t.typacl
+ FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 %}
+ ) acl,
+ (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
+ AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
+ aclexplode(t.typacl) as d FROM pg_type t WHERE t.oid = {{tid}}::oid) a) d
+ ) d
+ LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+ LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY g.rolname, gt.rolname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql
new file mode 100644
index 0000000..74fd922
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql
@@ -0,0 +1,35 @@
+{# The SQL given below will fetch composite type#}
+{% if type == 'c' %}
+SELECT attname, format_type(t.oid,NULL) AS typname, attndims, atttypmod, nsp.nspname,
+ (SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup,
+ collname, nspc.nspname as collnspname, att.attrelid
+FROM pg_attribute att
+ JOIN pg_type t ON t.oid=atttypid
+ JOIN pg_namespace nsp ON t.typnamespace=nsp.oid
+ LEFT OUTER JOIN pg_type b ON t.typelem=b.oid
+ LEFT OUTER JOIN pg_collation c ON att.attcollation=c.oid
+ LEFT OUTER JOIN 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 type == 'e' %}
+SELECT enumlabel
+FROM pg_enum
+ WHERE enumtypid={{tid}}::oid
+ ORDER by enumsortorder
+{% endif %}
+
+{# The SQL given below will fetch range type#}
+{% if type == 'r' %}
+SELECT rngsubtype, st.typname,
+ rngcollation, col.collname,
+ rngsubopc, opc.opcname,
+ rngcanonical, rngsubdiff
+FROM pg_range
+ LEFT JOIN pg_type st ON st.oid=rngsubtype
+ LEFT JOIN pg_collation col ON col.oid=rngcollation
+ LEFT JOIN pg_opclass opc ON opc.oid=rngsubopc
+ WHERE rngtypid={{tid}}::oid;
+{% endif %}
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
new file mode 100644
index 0000000..6a53532
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/create.sql
@@ -0,0 +1,78 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{### 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 %});
+{% 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 }}{% 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 %}
+
+ );
+{% endif %}
+{### External Type ###}
+{% if data and data.typtype == 'x' %}
+CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS
+ (
+ {% if data.typinput %}INPUT = {{data.typinput}}{% endif %}{% if data.typoutput %},
+ OUTPUT = {{ data.typoutput }}{% endif %}{% if data.typreceive %},
+ RECEIVE = {{data.typreceive}}{% endif %}{% if data.typname %},
+ 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.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}}{% endif %}{% if data.typispreferred %},
+ PREFERRED = {{data.typispreferred}}{% endif %}{% if data.typdefault %},
+ DEFAULT = {{data.typdefault}}{% endif %}{% if data.element %},
+ ELEMENT = {{data.element}}{% endif %}{% if data.typdelim %},
+ DELIMITER = {{data.typdelim|qtLiteral}}{% 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 {{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}};
+{% 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.security_label %}
+{{ SECLABLE.SET(conn, 'TYPE', data.name, r.provider, r.security_label, data.schema) }}
+{% endif %}
+{% endfor %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..c258827
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql
@@ -0,0 +1 @@
+DROP TYPE {{ conn|qtIdent(data.schema, data.name) }}{% if cascade%} CASCADE{% 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_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_collations.sql
new file mode 100644
index 0000000..4b0169b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_collations.sql
@@ -0,0 +1,7 @@
+SELECT --nspname, collname,
+ CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(collname))
+ ELSE '' END AS collation
+FROM pg_collation c, pg_namespace n
+WHERE c.collnamespace=n.oid
+ORDER BY nspname, collname;
\ 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
new file mode 100644
index 0000000..85494db
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_external_functions.sql
@@ -0,0 +1,40 @@
+{### 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
+ concat(quote_ident(nspname), '.', 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 = 0;
+{% endif %}
+{### TypmodIN list ###}
+{% if typemodin %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='_cstring')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
+{### TypmodOUT list ###}
+{% if typemodout %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='cstring')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..14f7950
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,11 @@
+{# Below will provide oid for newly created type #}
+SELECT t.oid
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 data %}
+ AND t.typname = {{data.name|qtLiteral}}
+{% endif %}
+ORDER BY t.typname;
\ 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_subtypes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_subtypes.sql
new file mode 100644
index 0000000..75271fe
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_subtypes.sql
@@ -0,0 +1,56 @@
+{### To fill subtype combobox ###}
+{% if subtype %}
+SELECT DISTINCT typ.typname AS stype,
+ (CASE WHEN typ.typcollation > 0 THEN true ELSE false END) AS is_collate
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype = typ.oid
+WHERE opc.opcmethod = 403
+ORDER BY 1
+{% endif %}
+{### To fill subtype opclass combobox ###}
+{% if subtype_opclass and data and data.typname %}
+SELECT opc.opcname
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ORDER BY opcname;
+{% endif %}
+{### To fetch opcinttype from subtype opclass ###}
+{% if get_opcintype and data and data.typname and data.opcname %}
+SELECT opc.opcintype
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ AND opc.opcname = {{ data.opcname|qtLiteral }}
+ORDER BY opcname;
+{% endif %}
+{### To fill subtype diff function combobox ###}
+{% if opcintype %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS stypdiff
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype = 701
+ AND proargtypes = '{{opcintype}} {{opcintype}}'
+ORDER BY proname;
+{% endif %}
+{### To fill canonical combobox ###}
+{% if getoid %}
+SELECT oid FROM pg_type
+WHERE typname = {{ data.name|qtLiteral }}
+{% endif %}
+{% if canonical and oid %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS canonical
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype= {{ oid }}
+ AND proargtypes = '{{ oid }}'
+ORDER BY proname;
+{% 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_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_types.sql
new file mode 100644
index 0000000..a5d352d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_types.sql
@@ -0,0 +1,10 @@
+SELECT * FROM
+ (SELECT format_type(t.oid,NULL) AS typname,
+ CASE WHEN typelem > 0 THEN typelem ELSE t.oid END AS elemoid,
+ typlen, typtype, t.oid, nspname,
+ (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+FROM pg_type t
+ JOIN pg_namespace nsp ON typnamespace=nsp.oid
+WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r')AND NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) AND nsp.nspname != 'information_schema'
+ ) AS dummy
+ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..6abcb19
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql
@@ -0,0 +1,10 @@
+SELECT t.oid, t.typname AS name
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN pg_namespace nsp ON nsp.oid = t.typnamespace
+WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' AND t.typnamespace = {{scid}}::oid
+{% if not show_system_objects %}
+ AND ct.oid is NULL
+{% endif %}
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..de261a5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql
@@ -0,0 +1,24 @@
+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.*, format_type(t.oid, null) AS alias,
+ 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 array_agg(provider || '=' || label) FROM pg_shseclabel 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_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_type'::regclass)
+ LEFT OUTER JOIN 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;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..939597e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
@@ -0,0 +1,127 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{#======================================#}
+{# 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 {{ data.typeowner }};
+
+{% endif %}
+{#======================================#}
+{# Below will change objects comment #}
+{% if data.description and data.description != o_data.description %}
+COMMENT ON TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ IS {{ data.description|qtLiteral }};
+
+{% 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)}} {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% endfor %}
+{% endif %}
+{% if 'changed' in composite and composite.changed|length > 0 %}
+{% for r in composite.changed %}
+ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ ALTER ATTRIBUTE {{conn|qtIdent(r.member_name)}} SET DATA TYPE {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% 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}} AFTER {{o_data.enum[o_enum_len].label|qtLiteral }};
+{% 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}} AFTER {{enum.added[p_idx].label|qtLiteral}};
+{% endif %}
+{% endfor %}
+{% 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 %}
+{{ SECLABLE.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 %}
+{{ SECLABLE.SET(conn, 'TYPE', o_data.name, r.provider, r.security_label, o_data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.SET(conn, 'TYPE', o_data.name, r.provider, r.security_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 %}
+{{ PRIVILEGE.UNSETALL(conn, 'TYPE', priv.grantee, o_data.name, o_data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TYPE', priv.grantee, 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, 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 != 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 %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt
new file mode 100644
index 0000000..631037f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt
@@ -0,0 +1,53 @@
+"""
+Here is the list of Postgres inbuilt types & their OID's
+We will use these types to check for validations
+
+## PGOID_TYPE_SERIAL -42L
+## PGOID_TYPE_SERIAL8 -43L
+## PGOID_TYPE_SERIAL2 -44L
+## PGOID_TYPE_BOOL 16L
+## PGOID_TYPE_BYTEA 17L
+## PGOID_TYPE_CHAR 18L
+## PGOID_TYPE_NAME 19L
+## PGOID_TYPE_INT8 20L
+## PGOID_TYPE_INT2 21L
+## PGOID_TYPE_INT4 23L
+## PGOID_TYPE_TEXT 25L
+## PGOID_TYPE_OID 26L
+## PGOID_TYPE_TID 27L
+## PGOID_TYPE_XID 28L
+## PGOID_TYPE_CID 29L
+## PGOID_TYPE_FLOAT4 700L
+## PGOID_TYPE_FLOAT8 701L
+## PGOID_TYPE_MONEY 790L
+## PGOID_TYPE_CHAR_ARRAY 1002L
+## PGOID_TYPE_TEXT_ARRAY 1009L
+## PGOID_TYPE_BPCHAR_ARRAY 1014L
+## PGOID_TYPE_VARCHAR_ARRAY 1015L
+## PGOID_TYPE_BPCHAR 1042L
+## PGOID_TYPE_VARCHAR 1043L
+## PGOID_TYPE_DATE 1082L
+## PGOID_TYPE_TIME 1083L
+## PGOID_TYPE_TIMESTAMP 1114L
+## PGOID_TYPE_TIMESTAMP_ARRAY 1115L
+## PGOID_TYPE_TIME_ARRAY 1183L
+## PGOID_TYPE_TIMESTAMPTZ 1184L
+## PGOID_TYPE_TIMESTAMPTZ_ARRAY 1185L
+## PGOID_TYPE_INTERVAL 1186L
+## PGOID_TYPE_INTERVAL_ARRAY 1187L
+## PGOID_TYPE_NUMERIC_ARRAY 1231L
+## PGOID_TYPE_TIMETZ 1266L
+## PGOID_TYPE_TIMETZ_ARRAY 1270L
+## PGOID_TYPE_BIT 1560L
+## PGOID_TYPE_BIT_ARRAY 1561L
+## PGOID_TYPE_VARBIT 1562L
+## PGOID_TYPE_VARBIT_ARRAY 1563L
+## PGOID_TYPE_NUMERIC 1700L
+## PGOID_TYPE_CSTRING 2275L
+## PGOID_TYPE_ANY 2276L
+## PGOID_TYPE_VOID 2278L
+## PGOID_TYPE_TRIGGER 2279L
+## PGOID_TYPE_LANGUAGE_HANDLER 2280L
+## PGOID_TYPE_INTERNAL 2281L
+## PGOID_TYPE_HANDLER 3115L
+"""
\ No newline at end of file
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
@ 2016-03-17 10:43 ` Dave Page <[email protected]>
2016-03-17 11:20 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Harshal Dhumal <[email protected]>
2016-03-18 07:15 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
0 siblings, 2 replies; 26+ messages in thread
From: Dave Page @ 2016-03-17 10:43 UTC (permalink / raw)
To: Murtuza Zabuawala <[email protected]>; +Cc: pgadmin-hackers
Hi
On Thu, Mar 17, 2016 at 10:08 AM, Murtuza Zabuawala
<[email protected]> wrote:
>
> - When editing a type, I cannot select a Grantee on the Privileges tab.
>
> This is general issue with privilege control.
> [We will add it in TODO list]
OK.
> - When adding a composite type, I cannot type/select directly in the
> grid, I have to expand the row.
>
> Because currently in Backgrid we do not have functionality like if I edit
> one cell second cell re-renders according the value of first cell,
> If we expand and select the type the other two fields (length &
> precision)are dependent on the value of type for composite types they become
> enable/disable accordingly.
That's must-have functionality. Please add a TODO for it.
> - If I click ADD to add a new member to a composite type whilst the
> previous row is expanded, the new row is added, but the expanded panel
> remains linked to the first row.
>
> - If I then click on the Edit button on the new row, I see two
> expanded panels, neither of which is attached to their row (and
> they're in the wrong order). I would only expect to see one panel at a
> time.
>
> This is general with sub-node collection control.
> [We will add it in TODO list]
OK.
> - When creating an External type, the Input, Output, Send, Receive and
> Analyze function lists are all blank. Is this expected? I would have
> thought there would be system functions listed as there are for Typmod
> in/out.
>
> Dave, I was not able to test External type functionality completely because
> it requires external type which is not inbuilt in postgres.
> Once you create that custom type using C/C++ code, it will be listed in
> those combobox by sql we are using to fetch external function types added by
> user in postgres.
I wonder if installing PostGIS would allow that testing to be done.
> - Why does "Range" type have a frame around it?
>
> So that we can make visible/hide all the control/elements related to Range
> type as whole instead having separate code for each control/element.
> That frame is because we used ‘FieldsetControl’ of backform to group all the
> elements of Range type.
Can you make it a plain div with no border/title? That would fit in
better with the existing styling, assuming the columns line up
properly.
Thanks.
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
@ 2016-03-17 11:20 ` Harshal Dhumal <[email protected]>
1 sibling, 0 replies; 26+ messages in thread
From: Harshal Dhumal @ 2016-03-17 11:20 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: Murtuza Zabuawala <[email protected]>; pgadmin-hackers
Hi Dave,
--
*Harshal Dhumal*
*Software Engineer *
EenterpriseDB <http://www.enterprisedb.com;
On Thu, Mar 17, 2016 at 4:13 PM, Dave Page <[email protected]> wrote:
> Hi
>
> On Thu, Mar 17, 2016 at 10:08 AM, Murtuza Zabuawala
> <[email protected]> wrote:
> >
> - If I click ADD to add a new member to a composite type whilst the
> > previous row is expanded, the new row is added, but the expanded panel
> > remains linked to the first row.
> >
> > - If I then click on the Edit button on the new row, I see two
> > expanded panels, neither of which is attached to their row (and
> > they're in the wrong order). I would only expect to see one panel at a
> > time.
> >
> > This is general with sub-node collection control.
> > [We will add it in TODO list]
>
I just sent a patch for this issue.
>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
>
> --
> Sent via pgadmin-hackers mailing list ([email protected])
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgadmin-hackers
>
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
@ 2016-03-18 07:15 ` Murtuza Zabuawala <[email protected]>
2016-03-18 07:26 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-18 16:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
1 sibling, 2 replies; 26+ messages in thread
From: Murtuza Zabuawala @ 2016-03-18 07:15 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi Dave,
Even after installing PosGIS I’m not able to create new custom external
type because the SQL does not return any value (Same behaviour as is in
pgAdmin3) , But I’m able to list all of it’s properties of external types
same as pgAdmin3 (PFA screenshots).
I have also attached patch for plain fields control (without lable &
border) for backform. (Please apply it before testing new type patch)
Thanks,
--
Regards,
Murtuza Zabuawala
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Thu, Mar 17, 2016 at 4:13 PM, Dave Page <[email protected]> wrote:
> Hi
>
> On Thu, Mar 17, 2016 at 10:08 AM, Murtuza Zabuawala
> <[email protected]> wrote:
> >
> > - When editing a type, I cannot select a Grantee on the Privileges tab.
> >
> > This is general issue with privilege control.
> > [We will add it in TODO list]
>
> OK.
>
> > - When adding a composite type, I cannot type/select directly in the
> > grid, I have to expand the row.
> >
> > Because currently in Backgrid we do not have functionality like if I edit
> > one cell second cell re-renders according the value of first cell,
> > If we expand and select the type the other two fields (length &
> > precision)are dependent on the value of type for composite types they
> become
> > enable/disable accordingly.
>
> That's must-have functionality. Please add a TODO for it.
>
> > - If I click ADD to add a new member to a composite type whilst the
> > previous row is expanded, the new row is added, but the expanded panel
> > remains linked to the first row.
> >
> > - If I then click on the Edit button on the new row, I see two
> > expanded panels, neither of which is attached to their row (and
> > they're in the wrong order). I would only expect to see one panel at a
> > time.
> >
> > This is general with sub-node collection control.
> > [We will add it in TODO list]
>
> OK.
>
> > - When creating an External type, the Input, Output, Send, Receive and
> > Analyze function lists are all blank. Is this expected? I would have
> > thought there would be system functions listed as there are for Typmod
> > in/out.
> >
> > Dave, I was not able to test External type functionality completely
> because
> > it requires external type which is not inbuilt in postgres.
> > Once you create that custom type using C/C++ code, it will be listed in
> > those combobox by sql we are using to fetch external function types
> added by
> > user in postgres.
>
> I wonder if installing PostGIS would allow that testing to be done.
>
> > - Why does "Range" type have a frame around it?
> >
> > So that we can make visible/hide all the control/elements related to
> Range
> > type as whole instead having separate code for each control/element.
> > That frame is because we used ‘FieldsetControl’ of backform to group all
> the
> > elements of Range type.
>
> Can you make it a plain div with no border/title? That would fit in
> better with the existing styling, assuming the columns line up
> properly.
>
> Thanks.
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[image/png] Screen Shot 2016-03-18 at 11.44.35 am.png (287.1K, 3-Screen%20Shot%202016-03-18%20at%2011.44.35%20am.png)
download | view image
[application/octet-stream] types_node_v4.patch (106.9K, 4-types_node_v4.patch)
download | inline diff:
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
new file mode 100644
index 0000000..19dc7b3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/__init__.py
@@ -0,0 +1,1208 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+""" Implements Type Node """
+
+import json
+from flask import render_template, make_response, request, jsonify
+from flask.ext.babel import gettext
+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 pgadmin.browser.server_groups.servers.databases as database
+from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
+ parse_priv_to_db
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class TypeModule(SchemaChildModule):
+ """
+ class TypeModule(SchemaChildModule)
+
+ A module class for Type node derived from SchemaChildModule
+
+ Methods:
+ -------
+ * __init__(*args, **kwargs)
+ - Method is used to initialize the Type and it's base module.
+
+ * get_nodes(gid, sid, did, scid, tid)
+ - Method is used to generate the browser collection node.
+
+ * node_inode()
+ - Method is overridden from its base class to make the node as leaf node.
+
+ * script_load()
+ - Load the module script for type, when any of the server node is
+ initialized.
+ """
+
+ NODE_TYPE = 'type'
+ COLLECTION_LABEL = gettext("Types")
+
+ def __init__(self, *args, **kwargs):
+ """
+ Method is used to initialize the TypeModule and it's base module.
+
+ Args:
+ *args:
+ **kwargs:
+ """
+ super(TypeModule, self).__init__(*args, **kwargs)
+ self.min_ver = None
+ self.max_ver = None
+
+ def get_nodes(self, gid, sid, did, scid):
+ """
+ Generate the collection node
+ """
+ yield self.generate_browser_collection_node(scid)
+
+ @property
+ def script_load(self):
+ """
+ Load the module script for database, when any of the database node is
+ initialized.
+ """
+ return database.DatabaseModule.NODE_TYPE
+
+ @property
+ def node_inode(self):
+ """
+ Load the module node as a leaf node
+ """
+ return False
+
+blueprint = TypeModule(__name__)
+
+
+class TypeView(PGChildNodeView):
+ """
+ This class is responsible for generating routes for Type node
+
+ Methods:
+ -------
+ * __init__(**kwargs)
+ - Method is used to initialize the TypeView and it's base view.
+
+ * check_precondition()
+ - This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+
+ * list()
+ - This function is used to list all the Type nodes within that
+ collection.
+
+ * nodes()
+ - This function will used to create all the child node within that
+ collection, Here it will create all the Type node.
+
+ * properties(gid, sid, did, scid, tid)
+ - This function will show the properties of the selected Type node
+
+ * create(gid, sid, did, scid)
+ - This function will create the new Type object
+
+ * update(gid, sid, did, scid, tid)
+ - This function will update the data for the selected Type node
+
+ * delete(self, gid, sid, scid, tid):
+ - This function will drop the Type object
+
+ * msql(gid, sid, did, scid, tid)
+ - This function is used to return modified SQL for the selected
+ Type node
+
+ * get_sql(data, scid, tid)
+ - This function will generate sql from model data
+
+ * sql(gid, sid, did, scid):
+ - This function will generate sql to show it in sql pane for the
+ selected Type node.
+
+ * dependency(gid, sid, did, scid, tid):
+ - This function will generate dependency list show it in dependency
+ pane for the selected Type node.
+
+ * dependent(gid, sid, did, scid, tid):
+ - This function will generate dependent list to show it in dependent
+ pane for the selected Type node.
+
+ * additional_properties(copy_dict, tid):
+ - This function will add additional properties in response
+
+ * get_collations(gid, sid, did, scid, tid):
+ - This function will return list of collation in ajax response
+
+ * get_types(gid, sid, did, scid, tid):
+ - This function will return list of types in ajax response
+
+ * get_subtypes(gid, sid, did, scid, tid):
+ - This function will return list of subtypes in ajax response
+
+ * get_subtype_opclass(gid, sid, did, scid, tid):
+ - This function will return list of subtype opclass in ajax response
+
+ * get_subtype_diff(gid, sid, did, scid, tid):
+ - This function will return list of subtype diff functions
+ in ajax response
+
+ * get_canonical(gid, sid, did, scid, tid):
+ - This function will return list of canonical functions
+ in ajax response
+
+ * get_external_functions_list(gid, sid, did, scid, tid):
+ - This function will return list of external functions
+ in ajax response
+ """
+
+ node_type = blueprint.node_type
+
+ parent_ids = [
+ {'type': 'int', 'id': 'gid'},
+ {'type': 'int', 'id': 'sid'},
+ {'type': 'int', 'id': 'did'},
+ {'type': 'int', 'id': 'scid'}
+ ]
+ ids = [
+ {'type': 'int', 'id': 'tid'}
+ ]
+
+ operations = dict({
+ 'obj': [
+ {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+ {'get': 'list', 'post': 'create'}
+ ],
+ 'delete': [{'delete': 'delete'}],
+ 'children': [{'get': 'children'}],
+ 'nodes': [{'get': 'node'}, {'get': 'nodes'}],
+ 'sql': [{'get': 'sql'}],
+ 'msql': [{'get': 'msql'}, {'get': 'msql'}],
+ 'stats': [{'get': 'statistics'}],
+ 'dependency': [{'get': 'dependencies'}],
+ 'dependent': [{'get': 'dependents'}],
+ 'module.js': [{}, {}, {'get': 'module_js'}],
+ 'get_types': [{'get': 'get_types'}, {'get': 'get_types'}],
+ 'get_stypes': [{'get': 'get_subtypes'}, {'get': 'get_subtypes'}],
+ 'get_subopclass': [{'get': 'get_subtype_opclass'},
+ {'get': 'get_subtype_opclass'}],
+ 'get_stypediff': [{'get': 'get_subtype_diff'}, {'get': 'get_subtype_diff'}],
+ 'get_canonical': [{'get': 'get_canonical'}, {'get': 'get_canonical'}],
+ 'get_collations': [{'get': 'get_collations'}, {'get': 'get_collations'}],
+ 'get_external_functions': [{'get': 'get_external_functions_list'},
+ {'get': 'get_external_functions_list'}]
+ })
+
+ def check_precondition(f):
+ """
+ This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+ """
+ @wraps(f)
+ def wrap(*args, **kwargs):
+ # Here args[0] will hold self & kwargs will hold gid,sid,did
+ self = args[0]
+ self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(kwargs['sid'])
+ self.conn = self.manager.connection(did=kwargs['did'])
+
+ # We need datlastsysoid to check if current type is system type
+ self.datlastsysoid = self.manager.db_info[kwargs['did']]['datlastsysoid']
+
+ # If DB not connected then return error to browser
+ if not self.conn.connected():
+ return precondition_required(
+ gettext(
+ "Connection to the server has been lost!"
+ )
+ )
+
+ # Declare allows acl on type
+ self.acl = ['U']
+
+ # we will set template path for sql scripts
+ self.template_path = 'type/sql/9.1_plus'
+
+ return f(*args, **kwargs)
+
+ return wrap
+
+ @check_precondition
+ def list(self, gid, sid, did, scid):
+ """
+ This function is used to list all the type nodes within that collection.
+
+ Args:
+ gid: Server group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of available type nodes
+ """
+
+ SQL = render_template("/".join([self.template_path, 'properties.sql']),
+ scid=scid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects)
+
+ status, res = self.conn.execute_dict(SQL)
+
+ if not status:
+ return internal_server_error(errormsg=res)
+ return ajax_response(
+ response=res['rows'],
+ status=200
+ )
+
+ @check_precondition
+ def nodes(self, gid, sid, did, scid):
+ """
+ This function will used to create all the child node within that collection.
+ Here it will create all the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of available type child nodes
+ """
+
+ res = []
+ SQL = render_template("/".join([self.template_path,
+ 'nodes.sql']), scid=scid,
+ show_system_objects=self.blueprint.show_system_objects)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=rset)
+
+ for row in rset['rows']:
+ res.append(
+ self.blueprint.generate_browser_node(
+ row['oid'],
+ scid,
+ row['name'],
+ icon="icon-type"
+ ))
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ def additional_properties(self, copy_dict, tid):
+ """
+ We will use this function to add additional properties according to type
+
+ Returns:
+ additional properties for type like range/composite/enum
+
+ """
+ # Fetching type of type
+ of_type = copy_dict['typtype']
+ res = dict()
+ # If type is of Composite then we need to add members list in our output
+ if of_type == 'c':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='c',
+ typrelid=copy_dict['typrelid'])
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # To display in properties
+ properties_list = []
+ # To display in composite collection grid
+ composite_lst = []
+
+ for row in rset['rows']:
+ typelist = ' '.join([row['attname'], row['typname']])
+ if not row['collname'] or (row['collname'] == 'default'
+ and row['collnspname'] == 'pg_catalog'):
+ full_collate = ''
+ collate = ''
+ else:
+ full_collate = get_driver(PG_DEFAULT_DRIVER).qtIdent(
+ self.conn, row['collnspname'], row['collname'])
+ collate = ' COLLATE ' + full_collate
+ typelist += collate
+ properties_list.append(typelist)
+
+ # Below logic will allow us to split length, precision from type name for grid
+ import re
+ matchObj = re.match( r'(.*)\((.*?),(.*?)\)', row['typname'])
+ if matchObj:
+ t_name = matchObj.group(1)
+ t_len = matchObj.group(2)
+ t_prec = matchObj.group(3)
+ else:
+ t_name = row['typname']
+ t_len = None
+ t_prec = None
+
+ composite_lst.append({
+ 'member_name': row['attname'], 'type': t_name, 'collation': full_collate,
+ 'tlength': t_len, 'precision': t_prec })
+
+ # Adding both results
+ res['member_list'] = ', '.join(properties_list)
+ res['composite'] = composite_lst
+
+ # If type is of ENUM then we need to add labels in our output
+ if of_type == 'e':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='e', tid=tid)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ # To display in properties
+ properties_list = []
+ # To display in enum grid
+ enum_list = []
+ for row in rset['rows']:
+ properties_list.append(row['enumlabel'])
+ enum_list.append({'label': row['enumlabel']})
+
+ # Adding both results in ouput
+ res['enum_list'] = ', '.join(properties_list)
+ res['enum'] = enum_list
+
+ # If type is of Range then we need to add collation,subtype etc in our output
+ if of_type == 'r':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='r', tid=tid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ range_dict = dict(res['rows'][0])
+ res.update(range_dict)
+
+ # Returning only additional properties only
+ return res
+
+ @check_precondition
+ def properties(self, gid, sid, did, scid, tid):
+ """
+ This function will show the properties of the selected type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of selected type node
+ """
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ copy_dict = dict(res['rows'][0])
+
+ # We need to parse & convert ACL coming from database to json format
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ copy_dict['typacl'] = []
+
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in copy_dict:
+ copy_dict[row['deftype']].append(priv)
+ else:
+ copy_dict[row['deftype']] = [priv]
+
+ # Calling function to check and additional properties if available
+ copy_dict.update(self.additional_properties(copy_dict, tid))
+
+ return ajax_response(
+ response=copy_dict,
+ status=200
+ )
+
+ @check_precondition
+ def get_collations(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of collation available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_collations.sql']))
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['collation'],
+ 'value': row['collation']}
+ )
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_types(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of types available
+ as AJAX response.
+ """
+ res = []
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_types.sql']))
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ # Attaching properties for precession
+ # & length validation for current type
+ precision = False
+ length = False
+ min_val = 0
+ max_val = 0
+
+ # Check against PGOID for specific type
+ if row['elemoid']:
+ if row['elemoid'] in (1560, 1561, 1562, 1563, 1042, 1043,
+ 1014, 1015):
+ typeval = 'L'
+ elif row['elemoid'] in (1083, 1114, 1115, 1183, 1184, 1185,
+ 1186, 1187, 1266, 1270):
+ typeval = 'D'
+ elif row['elemoid'] in (1231, 1700):
+ typeval = 'P'
+ else:
+ typeval = ' '
+
+ # Logic to set precision & length/min/max values
+ if typeval == 'P':
+ precision = True
+
+ if precision or typeval in ('L', 'D'):
+ length = True
+ min_val = 0 if typeval == 'D' else 1
+ if precision:
+ max_val = 1000
+ elif min_val:
+ # Max of integer value
+ max_val = 2147483647
+ else:
+ max_val = 10
+
+ res.append(
+ {'label': row['typname'], 'value': row['typname'],
+ 'typval': typeval, 'precision': precision,
+ 'length': length, 'min_val': min_val, 'max_val': max_val
+ }
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtypes(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtypes available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ subtype=True)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['stype'], 'value': row['stype'],
+ 'is_collate': row['is_collate']}
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtype_opclass(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtype opclass available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ subtype_opclass=True, data=data)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['opcname'],
+ 'value': row['opcname']})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtype_diff(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtypes diff functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ get_opcintype=True, data=data)
+ if SQL:
+ status, opcintype = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=opcintype)
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ opcintype=opcintype, conn=self.conn)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['stypdiff'],
+ 'value': row['stypdiff']}
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_canonical(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of canonical functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+ canonical = True
+
+ try:
+ # We want to send data only if in we are in edit mode
+ # else we will disable the combobox
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ getoid=True, data=data)
+ if SQL:
+ status, oid = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=oid)
+ # If oid is None then do not run SQL
+ if oid is None:
+ canonical = False
+
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ canonical=canonical, conn=self.conn, oid=oid)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['canonical'],
+ 'value': row['canonical']})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def get_external_functions_list(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of external functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': '', 'cbtype': 'all'}]
+
+ try:
+ # The SQL generated below will populate Input/Output/Send/
+ # Receive/Analyze/TypModeIN/TypModOUT combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ extfunc=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'all'})
+
+ # The SQL generated below will populate TypModeIN combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ typemodin=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'typmodin'})
+
+ # The SQL generated below will populate TypModeIN combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ typemodout=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'typmodout'})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def create(self, gid, sid, did, scid):
+ """
+ This function will creates new the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ data = request.form if request.form else json.loads(request.data.decode())
+ required_args = {
+ 'name': 'Name',
+ 'typtype': 'Type'
+ }
+
+ for arg in required_args:
+ if arg not in data:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ "Couldn't find the required parameter (%s)." %
+ required_args[arg]
+ )
+ )
+ # Additional checks goes here
+ # If type is composite then check if it has two members
+ if data and data[arg] == 'c':
+ if len(data['composite']) < 2:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Composite types requires at least two members'
+ )
+ )
+ # If type is enum then check if it has minimum one label
+ if data and data[arg] == 'e':
+ if len(data['enum']) < 1:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Enumeration types requires at least one label'
+ )
+ )
+ # If type is range then check if subtype is defined or not
+ if data and data[arg] == 'r':
+ if data['typname'] is None:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Subtype must be defined for range types'
+ )
+ )
+ # If type is external then check if input/output
+ # conversion function is defined
+ if data and data[arg] == 'b':
+ if data['typinput'] is None or \
+ data['typoutput'] is None:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'External types requires both Input & \
+ Output conversion function.'
+ )
+ )
+
+ # To format privileges coming from client
+ if 'typacl' in data and data['typacl'] is not None:
+ data['typacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ try:
+ SQL = render_template("/".join([self.template_path, 'create.sql']),
+ data=data, conn=self.conn)
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # we need oid to to add object in tree at browser
+ SQL = render_template("/".join([self.template_path,
+ 'get_oid.sql']),
+ scid=scid, data=data)
+ status, tid = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=tid)
+
+ return jsonify(
+ node=self.blueprint.generate_browser_node(
+ tid,
+ scid,
+ data['name'],
+ icon="icon-type"
+ )
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def update(self, gid, sid, did, scid, tid):
+ """
+ This function will updates existing the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+
+ data = request.form if request.form else json.loads(request.data.decode())
+ try:
+ SQL = self.get_sql(gid, sid, data, scid, tid)
+ if SQL and SQL.strip('\n') and SQL.strip(' '):
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return make_json_response(
+ success=1,
+ info="Type updated",
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+ else:
+ return make_json_response(
+ success=1,
+ info="Nothing to update",
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def delete(self, gid, sid, did, scid, tid):
+ """
+ This function will updates existing the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+
+ # Below will decide if it's simple drop or drop with cascade call
+ if self.cmd == 'delete':
+ # This is a cascade operation
+ cascade = True
+ else:
+ cascade = False
+
+ try:
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'delete.sql']),
+ data=data, cascade=cascade, conn=self.conn)
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return make_json_response(
+ success=1,
+ info=gettext("Type dropped"),
+ data={
+ 'id': tid,
+ 'scid': scid
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def msql(self, gid, sid, did, scid, tid=None):
+ """
+ This function will generates modified sql for type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ req = request.args
+ data = dict()
+
+ # converting nested request data in proper json format
+ for key,val in req.items():
+ if key in ['composite', 'enum', 'seclabels', 'typacl']:
+ data[key] = json.loads(val)
+ else:
+ data[key] = val
+
+ try:
+ SQL = self.get_sql(gid, sid, data, scid, tid)
+
+ if SQL and SQL.strip('\n') and SQL.strip(' '):
+ return make_json_response(
+ data=SQL,
+ status=200
+ )
+ except Exception as e:
+ internal_server_error(errormsg=str(e))
+
+ def _convert_for_sql(self, data):
+ """
+ This function will convert combobox values into
+ readable format for sql & msql function
+ """
+ # Convert combobox value into readable format
+
+ if 'typstorage' in data and data['typstorage'] is not None:
+ if data['typstorage'] == 'p':
+ data['typstorage'] = 'PLAIN'
+ elif data['typstorage'] == 'e':
+ data['typstorage'] = 'EXTERNAL'
+ elif data['typstorage'] == 'm':
+ data['typstorage'] = 'MAIN'
+ elif data['typstorage'] == 'x':
+ data['typstorage'] = 'EXTENDED'
+
+ if 'typalign' in data and data['typalign'] is not None:
+ if data['typalign'] == 'c':
+ data['typalign'] = 'char'
+ elif data['typalign'] == 's':
+ data['typalign'] = 'int2'
+ elif data['typalign'] == 'i':
+ data['typalign'] = 'int4'
+ elif data['typalign'] == 'd':
+ data['typalign'] = 'double'
+
+ return data
+
+ def get_sql(self, gid, sid, data, scid, tid=None):
+ """
+ This function will genrate sql from model data
+ """
+ if tid is not None:
+
+ for key in ['typacl']:
+ if key in data and data[key] is not None:
+ if 'added' in data[key]:
+ data[key]['added'] = parse_priv_to_db(data[key]['added'], self.acl)
+ if 'changed' in data[key]:
+ data[key]['changed'] = parse_priv_to_db(data[key]['changed'], self.acl)
+ if 'deleted' in data[key]:
+ data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], self.acl)
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ old_data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ old_data['typacl'] = []
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in old_data:
+ old_data[row['deftype']].append(priv)
+ else:
+ old_data[row['deftype']] = [priv]
+
+ # Calling function to check and additional properties if available
+ old_data.update(self.additional_properties(old_data, tid))
+ old_data = self._convert_for_sql(old_data)
+
+ SQL = render_template(
+ "/".join([self.template_path, 'update.sql']),
+ data=data, o_data=old_data, conn=self.conn
+ )
+ else:
+ required_args = [
+ 'name',
+ 'typtype'
+ ]
+
+ for arg in required_args:
+ if arg not in data:
+ return " --definition incomplete"
+
+ # Privileges
+ if 'typacl' in data and data['typacl'] is not None:
+ data['typacl'] = parse_priv_to_db(data['typacl'], self.acl)
+ data = self._convert_for_sql(data)
+ SQL = render_template("/".join([self.template_path,
+ 'create.sql']),
+ data=data, conn=self.conn)
+
+ return SQL
+
+
+ @check_precondition
+ def sql(self, gid, sid, did, scid, tid):
+ """
+ This function will generates reverse engineered sql for type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ data['typacl'] = []
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in data:
+ data[row['deftype']].append(priv)
+ else:
+ data[row['deftype']] = [priv]
+
+ # Privileges
+ if 'typacl' in data and data['typacl'] is not None:
+ data['nspacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ # Calling function to check and additional properties if available
+ data.update(self.additional_properties(data, tid))
+
+ # We do not want to display table which has '-' value
+ # setting them to None so that jinja avoid displaying them
+ for k in data:
+ if data[k] == '-':
+ data[k] = None
+
+ SQL = self.get_sql(gid, sid, data, scid, tid=None)
+
+ # We are appending headers here for sql panel
+ sql_header = "-- Type: {0}\n\n-- ".format(data['name'])
+ sql_header += render_template("/".join([self.template_path,
+ 'delete.sql']),
+ data=data, conn=self.conn)
+ SQL = sql_header + '\n\n' + SQL
+
+ return ajax_response(response=SQL)
+
+ @check_precondition
+ def dependents(self, gid, sid, did, scid, tid):
+ """
+ This function get the dependents and return ajax response
+ for the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ dependents_result = self.get_dependents(
+ self.conn, tid
+ )
+
+ return ajax_response(
+ response=dependents_result,
+ status=200
+ )
+
+ @check_precondition
+ def dependencies(self, gid, sid, did, scid, tid):
+ """
+ This function get the dependencies and return ajax response
+ for the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ dependencies_result = self.get_dependencies(
+ self.conn, tid
+ )
+
+ return ajax_response(
+ response=dependencies_result,
+ status=200
+ )
+
+TypeView.register_node_view(blueprint)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/coll-type.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/coll-type.png
new file mode 100644
index 0000000000000000000000000000000000000000..fb020d7d99f84439046d288615e865ee1fbdb815
GIT binary patch
literal 329
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=3NcXI=aM>fHaQT@Q91
z`hS1R{~OExU!3;;MEm~(mH&6<{olN3-cDJdI>wS9zhDN3XE)M-9L@rd$YLPv0mg18
zv+aP47*7|+5RU7%XQO!=40zlgo^wo$EU!My#3j(ks*}LT9dUr^nFV*x`<LfEXLU&{
zNT_QZ){p11jr8BRJf*J9t1q$D%X6FK(q}uIk4`OV_&v+tAZF)<c~_)!|NM{V=e#WX
zN#J9vG0+~>64!{5l*E!$tK_0oAjM#0U}&IgXryak7-D2#Wnye)VybOmYGq(B@15Q%
q6b-rgDVb@N5Df;FU=2XkCRPS!5DllMhpqu?VDNPHb6Mw<&;$UZ$8*O3
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/type.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/type.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c16764e7d08c56922a97a2f0c6cc06455c86a56
GIT binary patch
literal 325
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv5AX?b1=8~#9EmzT>)QWU=l(xE
z`v2js|F_rtzdY~%nF;@oHvQjQ{(pPk|IMk)0@*;Nj3q&S!3+-1ZlnP@oCO|{#X#Bv
zjNMLV+W{G&o-U3d9M_W*4zM_RIV|8v(U4|t6r8Z|5fh7_LtB=H01KmJR;I%QmXsCK
znH_-=7a3W69onAxD9m6<$ym$O<m%A&El=SFOUjEmj7`o4+9Dfn@G_j<Bfar~Z-)!e
z0@V`Nh?11Vl2ohYqEsNoU}RuuplfKPYhV~+WME}tY-M7qZD49;U@-5U-YOIgx%nxX
kX_XKS29{tAK-DHz24)Zqr>2Ll0cv3IboFyt=akR{01h5&qyPW_
literal 0
HcmV?d00001
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
new file mode 100644
index 0000000..b6ea7f4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/js/type.js
@@ -0,0 +1,864 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'pgadmin',
+ 'pgadmin.browser', 'alertify', 'backgrid', 'pgadmin.backgrid',
+ 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
+
+ if (!pgBrowser.Nodes['coll-type']) {
+ var databases = pgAdmin.Browser.Nodes['coll-type'] =
+ pgAdmin.Browser.Collection.extend({
+ node: 'type',
+ label: '{{ _('Types') }}',
+ type: 'coll-type',
+ columns: ['name', 'typeowner', 'description']
+ });
+ };
+
+ // Switch options to save space in model's field
+ var switchOptions = {
+ 'onText': 'Yes', 'offText': 'No',
+ 'onColor': 'success', 'offColor': 'primary',
+ 'size': 'small'
+ };
+
+ // Security label model declaration
+ var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ provider: undefined,
+ security_label: undefined
+ },
+ schema: [{
+ id: 'provider', label: '{{ _('Provider') }}',
+ type: 'text', disabled: false, cellHeaderClasses:'width_percent_50'
+ },{
+ id: 'security_label', label: '{{ _('Security Label') }}',
+ type: 'text', disabled: false, cellHeaderClasses:'width_percent_50'
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+
+ if (_.isUndefined(this.get('security_label')) ||
+ _.isNull(this.get('security_label')) ||
+ String(this.get('security_label')).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = '{{ _('Please provide the value for security label.') }}';
+ this.errorModel.set('security_label', errmsg);
+ return errmsg;
+ } else {
+ this.errorModel.unset('security_label');
+ }
+ return null;
+ }
+ });
+
+ // Composite type model declaration
+ var CompositeModel = Backform.CompositeModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ member_name: undefined,
+ type: undefined,
+ tlength: undefined,
+ is_tlength: false,
+ precision: undefined,
+ is_precision: false,
+ collation: undefined,
+ min_val: undefined,
+ max_val: undefined,
+ },
+ type_options: undefined,
+ subtypes: undefined,
+ schema: [{
+ id: 'member_name', label: '{{ _('Member Name') }}',
+ type: 'text', disabled: false, editable: false
+ },{
+ id: 'type', label: '{{ _('Type') }}', control: 'node-ajax-options',
+ type: 'text', url: 'get_types', disabled: false, node: 'type',
+ editable: false,
+ transform: function(d){
+ this.model.type_options = d;
+ return d;
+ }
+ },{
+ id: 'tlength', label: '{{ _('Length') }}', deps: ['type'], type: 'text',
+ editable: false,
+ disabled: function(m) {
+ // We will store type from selected from combobox
+ var of_type = m.get('type');
+ if(m.type_options) {
+ // iterating over all the types
+ _.each(m.type_options, function(o) {
+ // if type from selected from combobox matches in options
+ if ( of_type == o.value ) {
+ // if length is allowed for selected type
+ if(o.length)
+ {
+ // set the values in model
+ m.set('is_tlength', true, {silent: true});
+ m.set('min_val', o.min_val, {silent: true});
+ m.set('max_val', o.max_val, {silent: true});
+ }
+ }
+ });
+ }
+ return !m.get('is_tlength');
+ }
+ },{
+ id: 'precision', label: '{{ _('Precision') }}', deps: ['type'],
+ type: 'text', editable: false,
+ disabled: function(m) {
+ // We will store type from selected from combobox
+ var of_type = m.get('type');
+ if(m.type_options) {
+ // iterating over all the types
+ _.each(m.type_options, function(o) {
+ // if type from selected from combobox matches in options
+ if ( of_type == o.value ) {
+ // if precession is allowed for selected type
+ if(o.precision)
+ {
+ // set the values in model
+ m.set('is_precision', true, {silent: true});
+ m.set('min_val', o.min_val, {silent: true});
+ m.set('max_val', o.max_val, {silent: true});
+ }
+ }
+ });
+ }
+ return !m.get('is_precision');
+ }
+ },{
+ id: 'collation', label: '{{ _('Collation') }}',
+ control: 'node-ajax-options', editable: false,
+ type: 'text', disabled: false, url: 'get_collations', node: 'type'
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+ // Validation for member name
+ if (_.isUndefined(data.member_name) ||
+ _.isNull(data.member_name) ||
+ String(data.member_name).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = _("Please specify the value for member name.")
+ this.errorModel.set('member_name', errmsg)
+ return errmsg;
+ } else {
+ this.errorModel.unset('member_name');
+ }
+
+ // Validation for Length field
+ if (data.is_tlength && !_.isUndefined(data.tlength)) {
+ if (data.tlength < data.min_val )
+ errmsg = _("Length should not be less than " + data.min_val)
+ if (data.tlength > data.max_val )
+ errmsg = _("Length should not be greater than " + data.max_val)
+ // If we have any error set then throw it to user
+ if(errmsg) {
+ this.errorModel.set('tlength', errmsg)
+ return errmsg;
+ }
+ } else {
+ this.errorModel.unset('tlength');
+ }
+
+ // Validation for precision field
+ if (data.is_precision && !_.isUndefined(data.precision)) {
+ if (data.precision < data.min_val )
+ errmsg = _("Precision should not be less than " + data.min_val)
+ if (data.precision > data.max_val )
+ errmsg = _("Precision should not be greater than " + data.max_val)
+ // If we have any error set then throw it to user
+ if(errmsg) {
+ this.errorModel.set('precision', errmsg)
+ return errmsg;
+ }
+ } else {
+ this.errorModel.unset('precision');
+ }
+
+ return null;
+ }
+ });
+
+ var EnumModel = Backform.EnumModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ label: undefined,
+ },
+ schema: [{
+ id: 'label', label: '{{ _('Label') }}',type: 'text', disabled: false,
+ cellHeaderClasses: 'width_percent_99', editable: function(m) {
+ return _.isUndefined(m.get('label'));
+ }
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+
+ if (_.isUndefined(data.label) ||
+ _.isNull(data.label) ||
+ String(data.label).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = _("Please specify the value for label.");
+ this.errorModel.set('label', errmsg)
+ return errmsg;
+ } else {
+ this.errorModel.unset('label');
+ }
+
+
+ return null;
+ }
+ });
+
+ if (!pgBrowser.Nodes['type']) {
+ pgAdmin.Browser.Nodes['type'] = pgBrowser.Node.extend({
+ type: 'type',
+ label: '{{ _('Type') }}',
+ collection_type: 'coll-type',
+ hasSQL: true,
+ hasDepends: true,
+ parent_type: ['schema', 'catalog'],
+ Init: function() {
+ /* Avoid mulitple registration of menus */
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+
+ pgBrowser.add_menus([{
+ name: 'create_type_on_coll', node: 'coll-type', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_type', node: 'type', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_type', node: 'schema', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: false},
+ enable: 'canCreate'
+ }
+ ]);
+
+ },
+ canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+ canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+ ext_funcs: undefined,
+ model: pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ name: undefined,
+ oid: undefined,
+ is_sys_type: false,
+ typtype: undefined
+ },
+
+ // Default values!
+ initialize: function(attrs, args) {
+ var isNew = (_.size(attrs) === 0);
+
+ if (isNew) {
+ var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
+ var schemaInfo = args.node_info.schema;
+
+ this.set({'typeowner': userInfo.name}, {silent: true});
+ this.set({'schema': schemaInfo.label}, {silent: true});
+ }
+ pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+ },
+
+ schema: [{
+ id: 'name', label: '{{ _('Name') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchema'
+ },{
+ id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+ type: 'text' , mode: ['properties'], disabled: true
+ },{
+ id: 'typeowner', label:'{{ _('Owner') }}', cell: 'string',
+ control: 'node-list-by-name',
+ type: 'text', mode: ['properties', 'create', 'edit'], node: 'role',
+ disabled: 'inSchema'
+ },{
+ id: 'schema', label:'{{ _('Schema') }}', cell: 'string',
+ type: 'text', mode: ['create', 'edit'], node: 'schema',
+ disabled: 'inSchema', filter: function(d) {
+ // If schema name start with pg_* then we need to exclude them
+ if(d && d.label.match(/^pg_/))
+ {
+ return false;
+ }
+ return true;
+ },
+ control: Backform.NodeListByNameControl.extend({
+ render: function(){
+ // Initialize parent's render method
+ Backform.NodeListByNameControl.prototype.render.apply(this, arguments);
+
+ // Set schema default value to its parent Schema
+ if(this.model.isNew()){
+ this.model.set({'schema': this.model.node_info.schema.label});
+ }
+ return this;
+ }
+ })
+ },{
+ id: 'typtype', label:'{{ _('Type') }}',
+ mode: ['create','edit'], disabled: 'inSchemaWithModelCheck',
+ group: '{{ _('Definition') }}',
+ mode: ['edit', 'create'],
+ select2: { width: "50%" },
+ options: [
+ {label: "Composite", value: "c"},
+ {label: "Enumeration", value: "e"},
+ {label: "External", value: "b"},
+ {label: "Range", value: "r"},
+ ],
+ disabled: 'inSchemaWithModelCheck',
+ // If create mode then by default open composite type
+ control: Backform.Select2Control.extend({
+ render: function(){
+ // Initialize parent's render method
+ Backform.Select2Control.prototype.render.apply(this, arguments);
+ if(this.model.isNew()) {
+ this.model.set({'typtype': 'c'});
+ }
+ return this;
+ }
+ })
+ },{
+ id: 'composite', label: '{{ _('Composite Type') }}',
+ model: CompositeModel, editable: true, type: 'collection',
+ group: '{{ _('Definition') }}', mode: ['edit', 'create'],
+ control: 'unique-col-collection', uniqueCol : ['member_name'],
+ canAdd: true, canEdit: true, canDelete: true, disabled: 'inSchema',
+ deps: ['typtype'], deps: ['typtype'],
+ visible: function(m) {
+ if (m.get('typtype') === 'c') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'enum', label: '{{ _('Enumeration Type') }}',
+ model: EnumModel, editable: true, type: 'collection',
+ group: '{{ _('Definition') }}', mode: ['edit', 'create'],
+ canAdd: true, canEdit: false, canDelete: function(m) {
+ // We will disable it if it's in 'edit' mode
+ if (m.isNew()) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ disabled: 'inSchema', deps: ['typtype'],
+ control: 'unique-col-collection', uniqueCol : ['label'],
+ visible: function(m) {
+ return m.get('typtype') === 'e';
+ }
+ },{
+ // We will disable range type control in edit mode
+ type: 'nested', control: 'plain-fieldset', group: '{{ _('Definition') }}',
+ mode: ['edit', 'create'],
+ visible: function(m) {
+ return m.get('typtype') === 'r';
+ }, deps: ['typtype'], label: '{{ _('') }}',
+ schema:[{
+ id: 'typname', label:'{{ _('Sub-type') }}', cell: 'string',
+ control: 'node-ajax-options',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ url: 'get_stypes', type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}', disabled: 'inSchemaWithModelCheck',
+ transform: function(d){
+ this.model.subtypes = d;
+ return d;
+ }
+ },{
+ id: 'opcname', label:'{{ _('Sub-type OpClass') }}', cell: 'string',
+ mode: ['properties', 'create', 'edit'], group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['typname'],
+ control: 'select', options: function() {
+ var l_typname = this.model.get('typname'),
+ self = this,
+ result = [];
+ if(!_.isUndefined(l_typname) && l_typname != '')
+ {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_subopclass', this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {'typname' : l_typname},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error', self.model, self.field);
+ }
+ });
+ //
+ }
+ return result;
+ }
+ },{
+ id: 'collname', label:'{{ _('Collation') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ deps: ['typname'], control: 'node-ajax-options', url: 'get_collations',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ disabled: function(m) {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+
+ // Disbale in edit mode
+ if (!m.isNew()) {
+ return true;
+ }
+
+ // To check if collation is allowed?
+ var of_subtype = m.get('typname'),
+ is_collate = undefined;
+ if(!_.isUndefined(of_subtype)) {
+ // iterating over all the types
+ _.each(m.subtypes, function(s) {
+ // if subtype from selected from combobox matches
+ if ( of_subtype === s.label ) {
+ // if collation is allowed for selected subtype
+ // then enable it else disable it
+ is_collate = s.is_collate;
+ }
+ });
+ }
+ // If is_collate is true then do not disable
+ return is_collate ? false : true;
+ }
+ },{
+ id: 'rngcanonical', label:'{{ _('Canonical function') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['name', 'typname'],
+ control: 'select', options: function() {
+ var name = this.model.get('name'),
+ self = this,
+ result = [];
+
+ if(!_.isUndefined(name) && name != '')
+ {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_canonical', this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {"name" : name},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error',
+ self.model, self.field);
+ }
+ });
+ }
+ return result;
+ }
+ },{
+ id: 'rngsubdiff', label:'{{ _('Sub-Type diff function') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['opcname'],
+ control: 'select', options: function() {
+ var l_typname = this.model.get('typname'),
+ l_opcname = this.model.get('opcname'),
+ self = this,
+ result = [];
+
+ if(!_.isUndefined(l_typname) && l_typname != '' &&
+ !_.isUndefined(l_opcname) && l_opcname != '') {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_stypediff',
+ this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {'typname' : l_typname, 'opcname': l_opcname},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error',
+ self.model, self.field);
+ }
+ });
+ }
+ return result;
+ }
+ }]
+ },{
+ type: 'nested', control: 'tab', group: '{{ _('Definition') }}',
+ label: '{{ _('External Type') }}', deps: ['typtype'],
+ mode: ['create', 'edit'],
+ visible: function(m) {
+ return m.get('typtype') === 'b';
+ },
+ schema:[{
+ id: 'typinput', label:'{{ _('Input function') }}',
+ cell: 'string',type: 'text',
+ mode: ['properties', 'create', 'edit'], group: 'Required',
+ disabled: 'inSchemaWithModelCheck',
+ control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typoutput', label:'{{ _('Output function') }}',
+ cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Required',
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typreceive', label:'{{ _('Receive function') }}',
+ cell: 'string', type: 'text', group: 'Optional-1',
+ mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typsend', label:'{{ _('Send function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typmodin', label:'{{ _('Typmod in function') }}',
+ cell: 'string', type: 'text',
+ mode: ['properties', 'create', 'edit'], group: 'Optional-1',
+ disabled: 'inSchemaWithModelCheck',
+ control: 'node-ajax-options', url: 'get_external_functions',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ transform: function(d) {
+ var result = [{label :"", value : ""}];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype === 'typmodin' || item.cbtype === 'all') {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ },{
+ id: 'typmodout', label:'{{ _('Typmod out function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck',
+ control: 'node-ajax-options', url: 'get_external_functions',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ transform: function(d) {
+ var result = [{label :"", value : ""}];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype === 'typmodout' || item.cbtype === 'all') {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ },{
+ id: 'typlen', label:'{{ _('Internal length') }}',
+ cell: 'integer', group: 'Optional-1',
+ type: 'int', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'variable', label:'{{ _('Variable?') }}', cell: 'switch',
+ group: 'Optional-1', type: 'switch',
+ mode: ['create','edit'], options: switchOptions,
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typdefault', label:'{{ _('Default?') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typanalyze', label:'{{ _('Analyze function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typcategory', label:'{{ _('Category type') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label :"Array types", value : "A"},
+ {label :"Boolean types", value : "B"},
+ {label :"Composite types", value : "C"},
+ {label :"Date/time types", value : "D"},
+ {label :"Enum types", value : "E"},
+ {label :"Geometric types", value : "G"},
+ {label :"Network address types", value : "I"},
+ {label :"Numeric types", value : "N"},
+ {label :"Pseudo-types", value : "P"},
+ {label :"String types", value : "S"},
+ {label :"Timespan types", value : "T"},
+ {label :"User-defined types", value : "U"},
+ {label :"Bit-string types", value : "V"},
+ {label :"unknown type", value : "X"}
+ ]
+ },{
+ id: 'typispreferred', label:'{{ _('Preferred?') }}', cell: 'switch',
+ type: 'switch', mode: ['properties', 'create','edit'],
+ options: switchOptions, disabled: 'inSchemaWithModelCheck',
+ group: 'Optional-1'
+ },{
+ id: 'element', label:'{{ _('Element type') }}', cell: 'string',
+ control: 'node-ajax-options', group: 'Optional-2',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', url: 'get_types'
+ },{
+ id: 'typdelim', label:'{{ _('Delimiter') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Optional-2', disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typalign', label:'{{ _('Alignment type') }}',
+ cell: 'string', group: 'Optional-2',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label: "char", value: "c"},
+ {label: "int2", value: "s"},
+ {label: "int4", value: "i"},
+ {label: "double", value: "d"},
+ ]
+ },{
+ id: 'typstorage', label:'{{ _('Storage type') }}',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Optional-2', cell: 'string',
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label: "PLAIN", value: "p"},
+ {label: "EXTERNAL", value: "e"},
+ {label: "MAIN", value: "m"},
+ {label: "EXTENDED", value: "x"},
+ ]
+ },{
+ id: 'typbyval', label:'{{ _('Passed by Value?') }}',
+ cell: 'switch', options: switchOptions,
+ type: 'switch', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', group: 'Optional-2',
+ },{
+ id: 'is_collatable', label:'{{ _('Collatable?') }}',
+ cell: 'switch', min_version: 90100, group: 'Optional-2',
+ type: 'switch', mode: ['properties', 'create', 'edit'],
+ options: switchOptions, disabled: 'inSchemaWithModelCheck'
+ // End of extension tab
+ }]
+ },{
+ id: 'alias', label:'{{ _('Alias') }}', cell: 'string',
+ type: 'text', mode: ['properties'],
+ disabled: 'inSchema'
+ },{
+ id: 'type_acl', label:'{{ _('Privileges') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Security') }}',
+ disabled: 'inSchema'
+ },{
+ id: 'member_list', label:'{{ _('Members') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Definition') }}',
+ disabled: 'inSchema', visible: function(m) {
+ if(m.get('typtype') === 'c') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'enum_list', label:'{{ _('Labels') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Definition') }}',
+ disabled: 'inSchema', visible: function(m) {
+ if(m.get('typtype') === 'e') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'is_sys_type', label:'{{ _('System type?') }}', cell: 'switch',
+ type: 'switch', mode: ['properties'], options: switchOptions,
+ disabled: 'inSchema'
+ },{
+ id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+ type: 'multiline', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchema'
+ },{
+ 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']
+ },{
+ 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'
+ }],
+ validate: function() {
+ // Validation code for required fields
+ var changedAttrs = this.sessAttrs,
+ msg = undefined;
+
+ if (_.has(changedAttrs, 'name') &&
+ (_.isUndefined(this.get('name'))
+ || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Name can not be empty!') }}';
+ this.errorModel.set('name', msg);
+ } else if (_.has(changedAttrs, 'schema') &&
+ (_.isUndefined(this.get('schema'))
+ || String(this.get('schema')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Schema can not be empty!') }}';
+ this.errorModel.set('schema', msg);
+ } else if (_.has(changedAttrs, 'typtype') &&
+ (_.isUndefined(this.get('typtype'))
+ || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Type can not be empty!') }}';
+ this.errorModel.set('typtype', msg);
+ } else if (this.get('typtype') == 'r' &&
+ _.has(changedAttrs, 'typname')
+ && (_.isUndefined(this.get('typname'))
+ || String(this.get('typname')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Subtype Name can not be empty!') }}';
+ this.errorModel.set('typname', msg);
+ } else if (this.get('typtype') == 'x' &&
+ _.has(changedAttrs, 'typinput')
+ && (_.isUndefined(this.get('typinput'))
+ || String(this.get('typinput')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Input function can not be empty!') }}';
+ this.errorModel.set('typinput', msg);
+ } else if (this.get('typtype') == 'x' &&
+ _.has(changedAttrs, 'typoutput')
+ && (_.isUndefined(this.get('typoutput'))
+ || String(this.get('typoutput')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Output function can not be empty!') }}';
+ this.errorModel.set('typoutput', msg);
+ } else {
+ this.errorModel.unset('name');
+ this.errorModel.unset('schema');
+ this.errorModel.unset('typtype');
+ this.errorModel.unset('typname');
+ this.errorModel.unset('typinput');
+ this.errorModel.unset('typoutput');
+ }
+ return null;
+ },
+ // We will disable everything if we are under catalog node
+ inSchema: function() {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+ return false;
+ },
+ // We will check if we are under schema node & in 'create' mode
+ inSchemaWithModelCheck: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ // We will disbale control if it's in 'edit' mode
+ if (m.isNew()) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+ return true;
+ },
+ // We want to enable only in edit mode
+ inSchemaWithEditMode: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ // We will disbale control if it's in 'edit' mode
+ if (m.isNew()) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+ return true;
+ },
+ // Function will help us to fill combobox
+ external_func_combo: function(d) {
+ var result = [];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype == 'all' ) {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ }),
+ canCreate: function(itemData, item, data) {
+ //If check is false then , we will allow create menu
+ if (data && data.check == false)
+ return true;
+
+ var t = pgBrowser.tree, i = item, d = itemData;
+ // To iterate over tree to check parent node
+ while (i) {
+ // If it is schema then allow user to create table
+ if (_.indexOf(['schema'], d._type) > -1)
+ return true;
+
+ if ('coll-type' == d._type) {
+ //Check if we are not child of catalog
+ prev_i = t.hasParent(i) ? t.parent(i) : null;
+ prev_d = prev_i ? t.itemData(prev_i) : null;
+ if( prev_d._type == 'catalog') {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ i = t.hasParent(i) ? t.parent(i) : null;
+ d = i ? t.itemData(i) : null;
+ }
+ // by default we do not want to allow create menu
+ return true;
+ }
+ });
+ }
+ return pgBrowser.Nodes['type'];
+});
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql
new file mode 100644
index 0000000..60eab25
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql
@@ -0,0 +1,26 @@
+SELECT 'typacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'USAGE' THEN 'U'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT t.typacl
+ FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 %}
+ ) acl,
+ (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
+ AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
+ aclexplode(t.typacl) as d FROM pg_type t WHERE t.oid = {{tid}}::oid) a) d
+ ) d
+ LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+ LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY g.rolname, gt.rolname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql
new file mode 100644
index 0000000..74fd922
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql
@@ -0,0 +1,35 @@
+{# The SQL given below will fetch composite type#}
+{% if type == 'c' %}
+SELECT attname, format_type(t.oid,NULL) AS typname, attndims, atttypmod, nsp.nspname,
+ (SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup,
+ collname, nspc.nspname as collnspname, att.attrelid
+FROM pg_attribute att
+ JOIN pg_type t ON t.oid=atttypid
+ JOIN pg_namespace nsp ON t.typnamespace=nsp.oid
+ LEFT OUTER JOIN pg_type b ON t.typelem=b.oid
+ LEFT OUTER JOIN pg_collation c ON att.attcollation=c.oid
+ LEFT OUTER JOIN 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 type == 'e' %}
+SELECT enumlabel
+FROM pg_enum
+ WHERE enumtypid={{tid}}::oid
+ ORDER by enumsortorder
+{% endif %}
+
+{# The SQL given below will fetch range type#}
+{% if type == 'r' %}
+SELECT rngsubtype, st.typname,
+ rngcollation, col.collname,
+ rngsubopc, opc.opcname,
+ rngcanonical, rngsubdiff
+FROM pg_range
+ LEFT JOIN pg_type st ON st.oid=rngsubtype
+ LEFT JOIN pg_collation col ON col.oid=rngcollation
+ LEFT JOIN pg_opclass opc ON opc.oid=rngsubopc
+ WHERE rngtypid={{tid}}::oid;
+{% endif %}
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
new file mode 100644
index 0000000..245477c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/create.sql
@@ -0,0 +1,78 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{### 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 %});
+{% 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 }}{% 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 %}
+
+ );
+{% 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 %} AS
+ (
+ {% 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.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}}{% endif %}{% if data.typispreferred %},
+ PREFERRED = {{data.typispreferred}}{% endif %}{% if data.typdefault %},
+ DEFAULT = {{data.typdefault|qtLiteral}}{% endif %}{% if data.element %},
+ ELEMENT = {{data.element}}{% endif %}{% if data.typdelim %},
+ DELIMITER = {{data.typdelim|qtLiteral}}{% 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 {{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}};
+{% 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.security_label %}
+{{ SECLABLE.SET(conn, 'TYPE', data.name, r.provider, r.security_label, data.schema) }}
+{% endif %}
+{% endfor %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..c258827
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql
@@ -0,0 +1 @@
+DROP TYPE {{ conn|qtIdent(data.schema, data.name) }}{% if cascade%} CASCADE{% 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_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_collations.sql
new file mode 100644
index 0000000..4b0169b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_collations.sql
@@ -0,0 +1,7 @@
+SELECT --nspname, collname,
+ CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(collname))
+ ELSE '' END AS collation
+FROM pg_collation c, pg_namespace n
+WHERE c.collnamespace=n.oid
+ORDER BY nspname, collname;
\ 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
new file mode 100644
index 0000000..85494db
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_external_functions.sql
@@ -0,0 +1,40 @@
+{### 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
+ concat(quote_ident(nspname), '.', 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 = 0;
+{% endif %}
+{### TypmodIN list ###}
+{% if typemodin %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='_cstring')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
+{### TypmodOUT list ###}
+{% if typemodout %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='cstring')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..14f7950
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,11 @@
+{# Below will provide oid for newly created type #}
+SELECT t.oid
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 data %}
+ AND t.typname = {{data.name|qtLiteral}}
+{% endif %}
+ORDER BY t.typname;
\ 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_subtypes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_subtypes.sql
new file mode 100644
index 0000000..75271fe
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_subtypes.sql
@@ -0,0 +1,56 @@
+{### To fill subtype combobox ###}
+{% if subtype %}
+SELECT DISTINCT typ.typname AS stype,
+ (CASE WHEN typ.typcollation > 0 THEN true ELSE false END) AS is_collate
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype = typ.oid
+WHERE opc.opcmethod = 403
+ORDER BY 1
+{% endif %}
+{### To fill subtype opclass combobox ###}
+{% if subtype_opclass and data and data.typname %}
+SELECT opc.opcname
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ORDER BY opcname;
+{% endif %}
+{### To fetch opcinttype from subtype opclass ###}
+{% if get_opcintype and data and data.typname and data.opcname %}
+SELECT opc.opcintype
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ AND opc.opcname = {{ data.opcname|qtLiteral }}
+ORDER BY opcname;
+{% endif %}
+{### To fill subtype diff function combobox ###}
+{% if opcintype %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS stypdiff
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype = 701
+ AND proargtypes = '{{opcintype}} {{opcintype}}'
+ORDER BY proname;
+{% endif %}
+{### To fill canonical combobox ###}
+{% if getoid %}
+SELECT oid FROM pg_type
+WHERE typname = {{ data.name|qtLiteral }}
+{% endif %}
+{% if canonical and oid %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS canonical
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype= {{ oid }}
+ AND proargtypes = '{{ oid }}'
+ORDER BY proname;
+{% 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_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_types.sql
new file mode 100644
index 0000000..a5d352d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_types.sql
@@ -0,0 +1,10 @@
+SELECT * FROM
+ (SELECT format_type(t.oid,NULL) AS typname,
+ CASE WHEN typelem > 0 THEN typelem ELSE t.oid END AS elemoid,
+ typlen, typtype, t.oid, nspname,
+ (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+FROM pg_type t
+ JOIN pg_namespace nsp ON typnamespace=nsp.oid
+WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r')AND NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) AND nsp.nspname != 'information_schema'
+ ) AS dummy
+ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..6abcb19
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql
@@ -0,0 +1,10 @@
+SELECT t.oid, t.typname AS name
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN pg_namespace nsp ON nsp.oid = t.typnamespace
+WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' AND t.typnamespace = {{scid}}::oid
+{% if not show_system_objects %}
+ AND ct.oid is NULL
+{% endif %}
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..de261a5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql
@@ -0,0 +1,24 @@
+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.*, format_type(t.oid, null) AS alias,
+ 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 array_agg(provider || '=' || label) FROM pg_shseclabel 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_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_type'::regclass)
+ LEFT OUTER JOIN 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;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..939597e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
@@ -0,0 +1,127 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{#======================================#}
+{# 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 {{ data.typeowner }};
+
+{% endif %}
+{#======================================#}
+{# Below will change objects comment #}
+{% if data.description and data.description != o_data.description %}
+COMMENT ON TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ IS {{ data.description|qtLiteral }};
+
+{% 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)}} {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% endfor %}
+{% endif %}
+{% if 'changed' in composite and composite.changed|length > 0 %}
+{% for r in composite.changed %}
+ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ ALTER ATTRIBUTE {{conn|qtIdent(r.member_name)}} SET DATA TYPE {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% 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}} AFTER {{o_data.enum[o_enum_len].label|qtLiteral }};
+{% 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}} AFTER {{enum.added[p_idx].label|qtLiteral}};
+{% endif %}
+{% endfor %}
+{% 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 %}
+{{ SECLABLE.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 %}
+{{ SECLABLE.SET(conn, 'TYPE', o_data.name, r.provider, r.security_label, o_data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.SET(conn, 'TYPE', o_data.name, r.provider, r.security_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 %}
+{{ PRIVILEGE.UNSETALL(conn, 'TYPE', priv.grantee, o_data.name, o_data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TYPE', priv.grantee, 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, 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 != 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 %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt
new file mode 100644
index 0000000..631037f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt
@@ -0,0 +1,53 @@
+"""
+Here is the list of Postgres inbuilt types & their OID's
+We will use these types to check for validations
+
+## PGOID_TYPE_SERIAL -42L
+## PGOID_TYPE_SERIAL8 -43L
+## PGOID_TYPE_SERIAL2 -44L
+## PGOID_TYPE_BOOL 16L
+## PGOID_TYPE_BYTEA 17L
+## PGOID_TYPE_CHAR 18L
+## PGOID_TYPE_NAME 19L
+## PGOID_TYPE_INT8 20L
+## PGOID_TYPE_INT2 21L
+## PGOID_TYPE_INT4 23L
+## PGOID_TYPE_TEXT 25L
+## PGOID_TYPE_OID 26L
+## PGOID_TYPE_TID 27L
+## PGOID_TYPE_XID 28L
+## PGOID_TYPE_CID 29L
+## PGOID_TYPE_FLOAT4 700L
+## PGOID_TYPE_FLOAT8 701L
+## PGOID_TYPE_MONEY 790L
+## PGOID_TYPE_CHAR_ARRAY 1002L
+## PGOID_TYPE_TEXT_ARRAY 1009L
+## PGOID_TYPE_BPCHAR_ARRAY 1014L
+## PGOID_TYPE_VARCHAR_ARRAY 1015L
+## PGOID_TYPE_BPCHAR 1042L
+## PGOID_TYPE_VARCHAR 1043L
+## PGOID_TYPE_DATE 1082L
+## PGOID_TYPE_TIME 1083L
+## PGOID_TYPE_TIMESTAMP 1114L
+## PGOID_TYPE_TIMESTAMP_ARRAY 1115L
+## PGOID_TYPE_TIME_ARRAY 1183L
+## PGOID_TYPE_TIMESTAMPTZ 1184L
+## PGOID_TYPE_TIMESTAMPTZ_ARRAY 1185L
+## PGOID_TYPE_INTERVAL 1186L
+## PGOID_TYPE_INTERVAL_ARRAY 1187L
+## PGOID_TYPE_NUMERIC_ARRAY 1231L
+## PGOID_TYPE_TIMETZ 1266L
+## PGOID_TYPE_TIMETZ_ARRAY 1270L
+## PGOID_TYPE_BIT 1560L
+## PGOID_TYPE_BIT_ARRAY 1561L
+## PGOID_TYPE_VARBIT 1562L
+## PGOID_TYPE_VARBIT_ARRAY 1563L
+## PGOID_TYPE_NUMERIC 1700L
+## PGOID_TYPE_CSTRING 2275L
+## PGOID_TYPE_ANY 2276L
+## PGOID_TYPE_VOID 2278L
+## PGOID_TYPE_TRIGGER 2279L
+## PGOID_TYPE_LANGUAGE_HANDLER 2280L
+## PGOID_TYPE_INTERNAL 2281L
+## PGOID_TYPE_HANDLER 3115L
+"""
\ No newline at end of file
[application/octet-stream] plain_fieldset_control_v1.patch (2.3K, 5-plain_fieldset_control_v1.patch)
download | inline diff:
diff --git a/web/pgadmin/static/css/overrides.css b/web/pgadmin/static/css/overrides.css
index fdefc74..4dcff33 100755
--- a/web/pgadmin/static/css/overrides.css
+++ b/web/pgadmin/static/css/overrides.css
@@ -685,6 +685,12 @@ fieldset.inline-fieldset {
border: 2px solid; border-radius: 5px;
}
+fieldset.inline-fieldset-without-border {
+ margin: 0px; margin-left: 0px; margin-right: 0px;
+ padding-right: 0px; padding-left: 0px; padding-top: 0px; padding-bottom: 0px;
+ border: 0px solid; border-radius: 0px; display: inline-block;
+}
+
fieldset.inline-fieldset > legend {
display: block; border: 0px solid black; box-shadow: none;
box-sizing: content-box; top: auto; bottom: auto; left: auto;
@@ -694,6 +700,9 @@ fieldset.inline-fieldset > legend {
vertical-align: middle; width: auto;
}
+fieldset.inline-fieldset-without-border legend {
+ border: none; margin-bottom: 0px !important;
+ }
fieldset.inline-fieldset > div {
padding: 0px; margin: 0px; border: 0px;
diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js
index 39e3233..66d8dab 100644
--- a/web/pgadmin/static/js/backform.pgadmin.js
+++ b/web/pgadmin/static/js/backform.pgadmin.js
@@ -1721,6 +1721,30 @@
template: Backform.Dialog.prototype.template
});
+
+ // Backform Tab Control (in bootstrap tabbular)
+ // A collection of field models.
+ var PlainFieldsetControl = Backform.PlainFieldsetControl = Backform.FieldsetControl.extend({
+ initialize: function(opts) {
+ Backform.FieldsetControl.prototype.initialize.apply(
+ this, arguments
+ );
+ },
+ template: {
+ 'header': _.template([
+ '<fieldset class="<%=fieldsetClass%>" <%=disabled ? "disabled" : ""%>>',
+ ' <% if (legend != false) { %>',
+ ' <legend class="<%=legendClass%>" <%=collapse ? "data-toggle=\'collapse\'" : ""%> data-target="#<%=cId%>"><%=collapse ? "<span class=\'caret\'></span>" : "" %></legend>',
+ ' <% } %>',
+ '</fieldset>'
+ ].join("\n")),
+ 'content': _.template(
+ ' <div id="<%= cId %>" class="<%=contentClass%>"></div>'
+ )},
+ fieldsetClass: 'inline-fieldset-without-border',
+ legend: false,
+ });
+
/*
* Control For Code Mirror SQL text area.
*/
[image/png] Screenshot at 2016-03-18 11_38_34.png (66.3K, 6-Screenshot%20at%202016-03-18%2011_38_34.png)
download | view image
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-18 07:15 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
@ 2016-03-18 07:26 ` Murtuza Zabuawala <[email protected]>
1 sibling, 0 replies; 26+ messages in thread
From: Murtuza Zabuawala @ 2016-03-18 07:26 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi,
Compressed everything in Zip file and sending it again, Because got below
email for my last sent email.
*"Your message to pgadmin-hackers has been delayed, and requires the
approvalof the moderators, for the following reason(s):The message body is
too long (481753 > 204800)"*
*Hi Dave,*
*Even after installing PosGIS I’m not able to create new custom external
type because the SQL does not return any value (Same behaviour as is in
pgAdmin3) , But I’m able to list all of it’s properties of external types
same as pgAdmin3 (PFA screenshots).*
*I have also attached patch for plain fields control (without lable &
border) for backform. (Please apply it before testing new type patch)*
*Thanks,*
*--*
*Regards,*
*Murtuza ZabuawalaEnterpriseDB: http://www.enterprisedb.com
<http://www.enterprisedb.com/>The Enterprise PostgreSQL Company*
--
Regards,
Murtuza Zabuawala
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Fri, Mar 18, 2016 at 12:45 PM, Murtuza Zabuawala <
[email protected]> wrote:
> Hi Dave,
>
> Even after installing PosGIS I’m not able to create new custom external
> type because the SQL does not return any value (Same behaviour as is in
> pgAdmin3) , But I’m able to list all of it’s properties of external types
> same as pgAdmin3 (PFA screenshots).
>
> I have also attached patch for plain fields control (without lable &
> border) for backform. (Please apply it before testing new type patch)
>
> Thanks,
>
> --
> Regards,
> Murtuza Zabuawala
> EnterpriseDB: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
> On Thu, Mar 17, 2016 at 4:13 PM, Dave Page <[email protected]> wrote:
>
>> Hi
>>
>> On Thu, Mar 17, 2016 at 10:08 AM, Murtuza Zabuawala
>> <[email protected]> wrote:
>> >
>> > - When editing a type, I cannot select a Grantee on the Privileges tab.
>> >
>> > This is general issue with privilege control.
>> > [We will add it in TODO list]
>>
>> OK.
>>
>> > - When adding a composite type, I cannot type/select directly in the
>> > grid, I have to expand the row.
>> >
>> > Because currently in Backgrid we do not have functionality like if I
>> edit
>> > one cell second cell re-renders according the value of first cell,
>> > If we expand and select the type the other two fields (length &
>> > precision)are dependent on the value of type for composite types they
>> become
>> > enable/disable accordingly.
>>
>> That's must-have functionality. Please add a TODO for it.
>>
>> > - If I click ADD to add a new member to a composite type whilst the
>> > previous row is expanded, the new row is added, but the expanded panel
>> > remains linked to the first row.
>> >
>> > - If I then click on the Edit button on the new row, I see two
>> > expanded panels, neither of which is attached to their row (and
>> > they're in the wrong order). I would only expect to see one panel at a
>> > time.
>> >
>> > This is general with sub-node collection control.
>> > [We will add it in TODO list]
>>
>> OK.
>>
>> > - When creating an External type, the Input, Output, Send, Receive and
>> > Analyze function lists are all blank. Is this expected? I would have
>> > thought there would be system functions listed as there are for Typmod
>> > in/out.
>> >
>> > Dave, I was not able to test External type functionality completely
>> because
>> > it requires external type which is not inbuilt in postgres.
>> > Once you create that custom type using C/C++ code, it will be listed in
>> > those combobox by sql we are using to fetch external function types
>> added by
>> > user in postgres.
>>
>> I wonder if installing PostGIS would allow that testing to be done.
>>
>> > - Why does "Range" type have a frame around it?
>> >
>> > So that we can make visible/hide all the control/elements related to
>> Range
>> > type as whole instead having separate code for each control/element.
>> > That frame is because we used ‘FieldsetControl’ of backform to group
>> all the
>> > elements of Range type.
>>
>> Can you make it a plain div with no border/title? That would fit in
>> better with the existing styling, assuming the columns line up
>> properly.
>>
>> Thanks.
>>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>
>
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/zip] types node_v4.zip (112.9K, 3-types%20node_v4.zip)
download
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-18 07:15 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
@ 2016-03-18 16:52 ` Dave Page <[email protected]>
2016-03-22 08:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
1 sibling, 1 reply; 26+ messages in thread
From: Dave Page @ 2016-03-18 16:52 UTC (permalink / raw)
To: Murtuza Zabuawala <[email protected]>; +Cc: pgadmin-hackers
Hi
On Fri, Mar 18, 2016 at 7:15 AM, Murtuza Zabuawala
<[email protected]> wrote:
> Hi Dave,
>
> Even after installing PosGIS I’m not able to create new custom external type
> because the SQL does not return any value (Same behaviour as is in pgAdmin3)
> , But I’m able to list all of it’s properties of external types same as
> pgAdmin3 (PFA screenshots).
Hmm, I wonder if that's a pgAdmin 3 bug. Please check the CREATE TYPE
docs, and make sure the queries we're using are appropriate to find
suitable functions. It's possible we've messed that up.
> I have also attached patch for plain fields control (without lable & border)
> for backform. (Please apply it before testing new type patch)
That looks better. I'll commit that patch.
> - I'm allowed to edit ENUM label names, but PostgreSQL doesn't support that.
This issue still exists.
I also noticed some weirdness with the error message display - please
see the attached screenshots. The one complaining about the missing
name is as I'd expect. The second one is showing the error message an
additional time. Please look at that.
I've attached an updated patch - I cleaned up some strings and
localisation tags in this version.
Thanks.
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[image/png] Screen Shot 2016-03-18 at 16.44.14.png (71.9K, 2-Screen%20Shot%202016-03-18%20at%2016.44.14.png)
download | view image
[image/png] Screen Shot 2016-03-18 at 16.44.41.png (63.5K, 3-Screen%20Shot%202016-03-18%20at%2016.44.41.png)
download | view image
[application/octet-stream] types_node_v4-dave.patch (106.9K, 4-types_node_v4-dave.patch)
download | inline diff:
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
new file mode 100644
index 0000000..6ad50e8
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/__init__.py
@@ -0,0 +1,1208 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+""" Implements Type Node """
+
+import json
+from flask import render_template, make_response, request, jsonify
+from flask.ext.babel import gettext
+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 pgadmin.browser.server_groups.servers.databases as database
+from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
+ parse_priv_to_db
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class TypeModule(SchemaChildModule):
+ """
+ class TypeModule(SchemaChildModule)
+
+ A module class for Type node derived from SchemaChildModule
+
+ Methods:
+ -------
+ * __init__(*args, **kwargs)
+ - Method is used to initialize the Type and it's base module.
+
+ * get_nodes(gid, sid, did, scid, tid)
+ - Method is used to generate the browser collection node.
+
+ * node_inode()
+ - Method is overridden from its base class to make the node as leaf node.
+
+ * script_load()
+ - Load the module script for type, when any of the server node is
+ initialized.
+ """
+
+ NODE_TYPE = 'type'
+ COLLECTION_LABEL = gettext("Types")
+
+ def __init__(self, *args, **kwargs):
+ """
+ Method is used to initialize the TypeModule and it's base module.
+
+ Args:
+ *args:
+ **kwargs:
+ """
+ super(TypeModule, self).__init__(*args, **kwargs)
+ self.min_ver = None
+ self.max_ver = None
+
+ def get_nodes(self, gid, sid, did, scid):
+ """
+ Generate the collection node
+ """
+ yield self.generate_browser_collection_node(scid)
+
+ @property
+ def script_load(self):
+ """
+ Load the module script for database, when any of the database node is
+ initialized.
+ """
+ return database.DatabaseModule.NODE_TYPE
+
+ @property
+ def node_inode(self):
+ """
+ Load the module node as a leaf node
+ """
+ return False
+
+blueprint = TypeModule(__name__)
+
+
+class TypeView(PGChildNodeView):
+ """
+ This class is responsible for generating routes for Type node
+
+ Methods:
+ -------
+ * __init__(**kwargs)
+ - Method is used to initialize the TypeView and it's base view.
+
+ * check_precondition()
+ - This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+
+ * list()
+ - This function is used to list all the Type nodes within that
+ collection.
+
+ * nodes()
+ - This function will used to create all the child node within that
+ collection, Here it will create all the Type node.
+
+ * properties(gid, sid, did, scid, tid)
+ - This function will show the properties of the selected Type node
+
+ * create(gid, sid, did, scid)
+ - This function will create the new Type object
+
+ * update(gid, sid, did, scid, tid)
+ - This function will update the data for the selected Type node
+
+ * delete(self, gid, sid, scid, tid):
+ - This function will drop the Type object
+
+ * msql(gid, sid, did, scid, tid)
+ - This function is used to return modified SQL for the selected
+ Type node
+
+ * get_sql(data, scid, tid)
+ - This function will generate sql from model data
+
+ * sql(gid, sid, did, scid):
+ - This function will generate sql to show it in sql pane for the
+ selected Type node.
+
+ * dependency(gid, sid, did, scid, tid):
+ - This function will generate dependency list show it in dependency
+ pane for the selected Type node.
+
+ * dependent(gid, sid, did, scid, tid):
+ - This function will generate dependent list to show it in dependent
+ pane for the selected Type node.
+
+ * additional_properties(copy_dict, tid):
+ - This function will add additional properties in response
+
+ * get_collations(gid, sid, did, scid, tid):
+ - This function will return list of collation in ajax response
+
+ * get_types(gid, sid, did, scid, tid):
+ - This function will return list of types in ajax response
+
+ * get_subtypes(gid, sid, did, scid, tid):
+ - This function will return list of subtypes in ajax response
+
+ * get_subtype_opclass(gid, sid, did, scid, tid):
+ - This function will return list of subtype opclass in ajax response
+
+ * get_subtype_diff(gid, sid, did, scid, tid):
+ - This function will return list of subtype diff functions
+ in ajax response
+
+ * get_canonical(gid, sid, did, scid, tid):
+ - This function will return list of canonical functions
+ in ajax response
+
+ * get_external_functions_list(gid, sid, did, scid, tid):
+ - This function will return list of external functions
+ in ajax response
+ """
+
+ node_type = blueprint.node_type
+
+ parent_ids = [
+ {'type': 'int', 'id': 'gid'},
+ {'type': 'int', 'id': 'sid'},
+ {'type': 'int', 'id': 'did'},
+ {'type': 'int', 'id': 'scid'}
+ ]
+ ids = [
+ {'type': 'int', 'id': 'tid'}
+ ]
+
+ operations = dict({
+ 'obj': [
+ {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+ {'get': 'list', 'post': 'create'}
+ ],
+ 'delete': [{'delete': 'delete'}],
+ 'children': [{'get': 'children'}],
+ 'nodes': [{'get': 'node'}, {'get': 'nodes'}],
+ 'sql': [{'get': 'sql'}],
+ 'msql': [{'get': 'msql'}, {'get': 'msql'}],
+ 'stats': [{'get': 'statistics'}],
+ 'dependency': [{'get': 'dependencies'}],
+ 'dependent': [{'get': 'dependents'}],
+ 'module.js': [{}, {}, {'get': 'module_js'}],
+ 'get_types': [{'get': 'get_types'}, {'get': 'get_types'}],
+ 'get_stypes': [{'get': 'get_subtypes'}, {'get': 'get_subtypes'}],
+ 'get_subopclass': [{'get': 'get_subtype_opclass'},
+ {'get': 'get_subtype_opclass'}],
+ 'get_stypediff': [{'get': 'get_subtype_diff'}, {'get': 'get_subtype_diff'}],
+ 'get_canonical': [{'get': 'get_canonical'}, {'get': 'get_canonical'}],
+ 'get_collations': [{'get': 'get_collations'}, {'get': 'get_collations'}],
+ 'get_external_functions': [{'get': 'get_external_functions_list'},
+ {'get': 'get_external_functions_list'}]
+ })
+
+ def check_precondition(f):
+ """
+ This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+ """
+ @wraps(f)
+ def wrap(*args, **kwargs):
+ # Here args[0] will hold self & kwargs will hold gid,sid,did
+ self = args[0]
+ self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(kwargs['sid'])
+ self.conn = self.manager.connection(did=kwargs['did'])
+
+ # We need datlastsysoid to check if current type is system type
+ self.datlastsysoid = self.manager.db_info[kwargs['did']]['datlastsysoid']
+
+ # If DB not connected then return error to browser
+ if not self.conn.connected():
+ return precondition_required(
+ gettext(
+ "Connection to the server has been lost!"
+ )
+ )
+
+ # Declare allows acl on type
+ self.acl = ['U']
+
+ # we will set template path for sql scripts
+ self.template_path = 'type/sql/9.1_plus'
+
+ return f(*args, **kwargs)
+
+ return wrap
+
+ @check_precondition
+ def list(self, gid, sid, did, scid):
+ """
+ This function is used to list all the type nodes within that collection.
+
+ Args:
+ gid: Server group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of available type nodes
+ """
+
+ SQL = render_template("/".join([self.template_path, 'properties.sql']),
+ scid=scid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects)
+
+ status, res = self.conn.execute_dict(SQL)
+
+ if not status:
+ return internal_server_error(errormsg=res)
+ return ajax_response(
+ response=res['rows'],
+ status=200
+ )
+
+ @check_precondition
+ def nodes(self, gid, sid, did, scid):
+ """
+ This function will used to create all the child node within that collection.
+ Here it will create all the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of available type child nodes
+ """
+
+ res = []
+ SQL = render_template("/".join([self.template_path,
+ 'nodes.sql']), scid=scid,
+ show_system_objects=self.blueprint.show_system_objects)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=rset)
+
+ for row in rset['rows']:
+ res.append(
+ self.blueprint.generate_browser_node(
+ row['oid'],
+ scid,
+ row['name'],
+ icon="icon-type"
+ ))
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ def additional_properties(self, copy_dict, tid):
+ """
+ We will use this function to add additional properties according to type
+
+ Returns:
+ additional properties for type like range/composite/enum
+
+ """
+ # Fetching type of type
+ of_type = copy_dict['typtype']
+ res = dict()
+ # If type is of Composite then we need to add members list in our output
+ if of_type == 'c':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='c',
+ typrelid=copy_dict['typrelid'])
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # To display in properties
+ properties_list = []
+ # To display in composite collection grid
+ composite_lst = []
+
+ for row in rset['rows']:
+ typelist = ' '.join([row['attname'], row['typname']])
+ if not row['collname'] or (row['collname'] == 'default'
+ and row['collnspname'] == 'pg_catalog'):
+ full_collate = ''
+ collate = ''
+ else:
+ full_collate = get_driver(PG_DEFAULT_DRIVER).qtIdent(
+ self.conn, row['collnspname'], row['collname'])
+ collate = ' COLLATE ' + full_collate
+ typelist += collate
+ properties_list.append(typelist)
+
+ # Below logic will allow us to split length, precision from type name for grid
+ import re
+ matchObj = re.match( r'(.*)\((.*?),(.*?)\)', row['typname'])
+ if matchObj:
+ t_name = matchObj.group(1)
+ t_len = matchObj.group(2)
+ t_prec = matchObj.group(3)
+ else:
+ t_name = row['typname']
+ t_len = None
+ t_prec = None
+
+ composite_lst.append({
+ 'member_name': row['attname'], 'type': t_name, 'collation': full_collate,
+ 'tlength': t_len, 'precision': t_prec })
+
+ # Adding both results
+ res['member_list'] = ', '.join(properties_list)
+ res['composite'] = composite_lst
+
+ # If type is of ENUM then we need to add labels in our output
+ if of_type == 'e':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='e', tid=tid)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ # To display in properties
+ properties_list = []
+ # To display in enum grid
+ enum_list = []
+ for row in rset['rows']:
+ properties_list.append(row['enumlabel'])
+ enum_list.append({'label': row['enumlabel']})
+
+ # Adding both results in ouput
+ res['enum_list'] = ', '.join(properties_list)
+ res['enum'] = enum_list
+
+ # If type is of Range then we need to add collation,subtype etc in our output
+ if of_type == 'r':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='r', tid=tid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ range_dict = dict(res['rows'][0])
+ res.update(range_dict)
+
+ # Returning only additional properties only
+ return res
+
+ @check_precondition
+ def properties(self, gid, sid, did, scid, tid):
+ """
+ This function will show the properties of the selected type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of selected type node
+ """
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ copy_dict = dict(res['rows'][0])
+
+ # We need to parse & convert ACL coming from database to json format
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ copy_dict['typacl'] = []
+
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in copy_dict:
+ copy_dict[row['deftype']].append(priv)
+ else:
+ copy_dict[row['deftype']] = [priv]
+
+ # Calling function to check and additional properties if available
+ copy_dict.update(self.additional_properties(copy_dict, tid))
+
+ return ajax_response(
+ response=copy_dict,
+ status=200
+ )
+
+ @check_precondition
+ def get_collations(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of collation available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_collations.sql']))
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['collation'],
+ 'value': row['collation']}
+ )
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_types(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of types available
+ as AJAX response.
+ """
+ res = []
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_types.sql']))
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ # Attaching properties for precession
+ # & length validation for current type
+ precision = False
+ length = False
+ min_val = 0
+ max_val = 0
+
+ # Check against PGOID for specific type
+ if row['elemoid']:
+ if row['elemoid'] in (1560, 1561, 1562, 1563, 1042, 1043,
+ 1014, 1015):
+ typeval = 'L'
+ elif row['elemoid'] in (1083, 1114, 1115, 1183, 1184, 1185,
+ 1186, 1187, 1266, 1270):
+ typeval = 'D'
+ elif row['elemoid'] in (1231, 1700):
+ typeval = 'P'
+ else:
+ typeval = ' '
+
+ # Logic to set precision & length/min/max values
+ if typeval == 'P':
+ precision = True
+
+ if precision or typeval in ('L', 'D'):
+ length = True
+ min_val = 0 if typeval == 'D' else 1
+ if precision:
+ max_val = 1000
+ elif min_val:
+ # Max of integer value
+ max_val = 2147483647
+ else:
+ max_val = 10
+
+ res.append(
+ {'label': row['typname'], 'value': row['typname'],
+ 'typval': typeval, 'precision': precision,
+ 'length': length, 'min_val': min_val, 'max_val': max_val
+ }
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtypes(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtypes available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ subtype=True)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['stype'], 'value': row['stype'],
+ 'is_collate': row['is_collate']}
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtype_opclass(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtype opclass available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ subtype_opclass=True, data=data)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['opcname'],
+ 'value': row['opcname']})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtype_diff(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtypes diff functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ get_opcintype=True, data=data)
+ if SQL:
+ status, opcintype = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=opcintype)
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ opcintype=opcintype, conn=self.conn)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['stypdiff'],
+ 'value': row['stypdiff']}
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_canonical(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of canonical functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+ canonical = True
+
+ try:
+ # We want to send data only if in we are in edit mode
+ # else we will disable the combobox
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ getoid=True, data=data)
+ if SQL:
+ status, oid = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=oid)
+ # If oid is None then do not run SQL
+ if oid is None:
+ canonical = False
+
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ canonical=canonical, conn=self.conn, oid=oid)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['canonical'],
+ 'value': row['canonical']})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def get_external_functions_list(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of external functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': '', 'cbtype': 'all'}]
+
+ try:
+ # The SQL generated below will populate Input/Output/Send/
+ # Receive/Analyze/TypModeIN/TypModOUT combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ extfunc=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'all'})
+
+ # The SQL generated below will populate TypModeIN combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ typemodin=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'typmodin'})
+
+ # The SQL generated below will populate TypModeIN combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ typemodout=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'typmodout'})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def create(self, gid, sid, did, scid):
+ """
+ This function will creates new the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ data = request.form if request.form else json.loads(request.data.decode())
+ required_args = {
+ 'name': 'Name',
+ 'typtype': 'Type'
+ }
+
+ for arg in required_args:
+ if arg not in data:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ "Couldn't find the required parameter (%s)." %
+ required_args[arg]
+ )
+ )
+ # Additional checks goes here
+ # If type is composite then check if it has two members
+ if data and data[arg] == 'c':
+ if len(data['composite']) < 2:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Composite types requires at least two members'
+ )
+ )
+ # If type is enum then check if it has minimum one label
+ if data and data[arg] == 'e':
+ if len(data['enum']) < 1:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Enumeration types requires at least one label'
+ )
+ )
+ # If type is range then check if subtype is defined or not
+ if data and data[arg] == 'r':
+ if data['typname'] is None:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Subtype must be defined for range types'
+ )
+ )
+ # If type is external then check if input/output
+ # conversion function is defined
+ if data and data[arg] == 'b':
+ if data['typinput'] is None or \
+ data['typoutput'] is None:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'External types require both Input & \
+ Output conversion function.'
+ )
+ )
+
+ # To format privileges coming from client
+ if 'typacl' in data and data['typacl'] is not None:
+ data['typacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ try:
+ SQL = render_template("/".join([self.template_path, 'create.sql']),
+ data=data, conn=self.conn)
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # we need oid to to add object in tree at browser
+ SQL = render_template("/".join([self.template_path,
+ 'get_oid.sql']),
+ scid=scid, data=data)
+ status, tid = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=tid)
+
+ return jsonify(
+ node=self.blueprint.generate_browser_node(
+ tid,
+ scid,
+ data['name'],
+ icon="icon-type"
+ )
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def update(self, gid, sid, did, scid, tid):
+ """
+ This function will updates existing the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+
+ data = request.form if request.form else json.loads(request.data.decode())
+ try:
+ SQL = self.get_sql(gid, sid, data, scid, tid)
+ if SQL and SQL.strip('\n') and SQL.strip(' '):
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return make_json_response(
+ success=1,
+ info="Type updated",
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+ else:
+ return make_json_response(
+ success=1,
+ info="Nothing to update",
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def delete(self, gid, sid, did, scid, tid):
+ """
+ This function will updates existing the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+
+ # Below will decide if it's simple drop or drop with cascade call
+ if self.cmd == 'delete':
+ # This is a cascade operation
+ cascade = True
+ else:
+ cascade = False
+
+ try:
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'delete.sql']),
+ data=data, cascade=cascade, conn=self.conn)
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return make_json_response(
+ success=1,
+ info=gettext("Type dropped"),
+ data={
+ 'id': tid,
+ 'scid': scid
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def msql(self, gid, sid, did, scid, tid=None):
+ """
+ This function will generates modified sql for type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ req = request.args
+ data = dict()
+
+ # converting nested request data in proper json format
+ for key,val in req.items():
+ if key in ['composite', 'enum', 'seclabels', 'typacl']:
+ data[key] = json.loads(val)
+ else:
+ data[key] = val
+
+ try:
+ SQL = self.get_sql(gid, sid, data, scid, tid)
+
+ if SQL and SQL.strip('\n') and SQL.strip(' '):
+ return make_json_response(
+ data=SQL,
+ status=200
+ )
+ except Exception as e:
+ internal_server_error(errormsg=str(e))
+
+ def _convert_for_sql(self, data):
+ """
+ This function will convert combobox values into
+ readable format for sql & msql function
+ """
+ # Convert combobox value into readable format
+
+ if 'typstorage' in data and data['typstorage'] is not None:
+ if data['typstorage'] == 'p':
+ data['typstorage'] = 'PLAIN'
+ elif data['typstorage'] == 'e':
+ data['typstorage'] = 'EXTERNAL'
+ elif data['typstorage'] == 'm':
+ data['typstorage'] = 'MAIN'
+ elif data['typstorage'] == 'x':
+ data['typstorage'] = 'EXTENDED'
+
+ if 'typalign' in data and data['typalign'] is not None:
+ if data['typalign'] == 'c':
+ data['typalign'] = 'char'
+ elif data['typalign'] == 's':
+ data['typalign'] = 'int2'
+ elif data['typalign'] == 'i':
+ data['typalign'] = 'int4'
+ elif data['typalign'] == 'd':
+ data['typalign'] = 'double'
+
+ return data
+
+ def get_sql(self, gid, sid, data, scid, tid=None):
+ """
+ This function will genrate sql from model data
+ """
+ if tid is not None:
+
+ for key in ['typacl']:
+ if key in data and data[key] is not None:
+ if 'added' in data[key]:
+ data[key]['added'] = parse_priv_to_db(data[key]['added'], self.acl)
+ if 'changed' in data[key]:
+ data[key]['changed'] = parse_priv_to_db(data[key]['changed'], self.acl)
+ if 'deleted' in data[key]:
+ data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], self.acl)
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ old_data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ old_data['typacl'] = []
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in old_data:
+ old_data[row['deftype']].append(priv)
+ else:
+ old_data[row['deftype']] = [priv]
+
+ # Calling function to check and additional properties if available
+ old_data.update(self.additional_properties(old_data, tid))
+ old_data = self._convert_for_sql(old_data)
+
+ SQL = render_template(
+ "/".join([self.template_path, 'update.sql']),
+ data=data, o_data=old_data, conn=self.conn
+ )
+ else:
+ required_args = [
+ 'name',
+ 'typtype'
+ ]
+
+ for arg in required_args:
+ if arg not in data:
+ return " --definition incomplete"
+
+ # Privileges
+ if 'typacl' in data and data['typacl'] is not None:
+ data['typacl'] = parse_priv_to_db(data['typacl'], self.acl)
+ data = self._convert_for_sql(data)
+ SQL = render_template("/".join([self.template_path,
+ 'create.sql']),
+ data=data, conn=self.conn)
+
+ return SQL
+
+
+ @check_precondition
+ def sql(self, gid, sid, did, scid, tid):
+ """
+ This function will generates reverse engineered sql for type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ data['typacl'] = []
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in data:
+ data[row['deftype']].append(priv)
+ else:
+ data[row['deftype']] = [priv]
+
+ # Privileges
+ if 'typacl' in data and data['typacl'] is not None:
+ data['nspacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ # Calling function to check and additional properties if available
+ data.update(self.additional_properties(data, tid))
+
+ # We do not want to display table which has '-' value
+ # setting them to None so that jinja avoid displaying them
+ for k in data:
+ if data[k] == '-':
+ data[k] = None
+
+ SQL = self.get_sql(gid, sid, data, scid, tid=None)
+
+ # We are appending headers here for sql panel
+ sql_header = "-- Type: {0}\n\n-- ".format(data['name'])
+ sql_header += render_template("/".join([self.template_path,
+ 'delete.sql']),
+ data=data, conn=self.conn)
+ SQL = sql_header + '\n\n' + SQL
+
+ return ajax_response(response=SQL)
+
+ @check_precondition
+ def dependents(self, gid, sid, did, scid, tid):
+ """
+ This function get the dependents and return ajax response
+ for the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ dependents_result = self.get_dependents(
+ self.conn, tid
+ )
+
+ return ajax_response(
+ response=dependents_result,
+ status=200
+ )
+
+ @check_precondition
+ def dependencies(self, gid, sid, did, scid, tid):
+ """
+ This function get the dependencies and return ajax response
+ for the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ dependencies_result = self.get_dependencies(
+ self.conn, tid
+ )
+
+ return ajax_response(
+ response=dependencies_result,
+ status=200
+ )
+
+TypeView.register_node_view(blueprint)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/coll-type.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/coll-type.png
new file mode 100644
index 0000000000000000000000000000000000000000..fb020d7d99f84439046d288615e865ee1fbdb815
GIT binary patch
literal 329
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=3NcXI=aM>fHaQT@Q91
z`hS1R{~OExU!3;;MEm~(mH&6<{olN3-cDJdI>wS9zhDN3XE)M-9L@rd$YLPv0mg18
zv+aP47*7|+5RU7%XQO!=40zlgo^wo$EU!My#3j(ks*}LT9dUr^nFV*x`<LfEXLU&{
zNT_QZ){p11jr8BRJf*J9t1q$D%X6FK(q}uIk4`OV_&v+tAZF)<c~_)!|NM{V=e#WX
zN#J9vG0+~>64!{5l*E!$tK_0oAjM#0U}&IgXryak7-D2#Wnye)VybOmYGq(B@15Q%
q6b-rgDVb@N5Df;FU=2XkCRPS!5DllMhpqu?VDNPHb6Mw<&;$UZ$8*O3
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/type.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/type.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c16764e7d08c56922a97a2f0c6cc06455c86a56
GIT binary patch
literal 325
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv5AX?b1=8~#9EmzT>)QWU=l(xE
z`v2js|F_rtzdY~%nF;@oHvQjQ{(pPk|IMk)0@*;Nj3q&S!3+-1ZlnP@oCO|{#X#Bv
zjNMLV+W{G&o-U3d9M_W*4zM_RIV|8v(U4|t6r8Z|5fh7_LtB=H01KmJR;I%QmXsCK
znH_-=7a3W69onAxD9m6<$ym$O<m%A&El=SFOUjEmj7`o4+9Dfn@G_j<Bfar~Z-)!e
z0@V`Nh?11Vl2ohYqEsNoU}RuuplfKPYhV~+WME}tY-M7qZD49;U@-5U-YOIgx%nxX
kX_XKS29{tAK-DHz24)Zqr>2Ll0cv3IboFyt=akR{01h5&qyPW_
literal 0
HcmV?d00001
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
new file mode 100644
index 0000000..834b4e4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/js/type.js
@@ -0,0 +1,864 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'pgadmin',
+ 'pgadmin.browser', 'alertify', 'backgrid', 'pgadmin.backgrid',
+ 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
+
+ if (!pgBrowser.Nodes['coll-type']) {
+ var databases = pgAdmin.Browser.Nodes['coll-type'] =
+ pgAdmin.Browser.Collection.extend({
+ node: 'type',
+ label: '{{ _('Types') }}',
+ type: 'coll-type',
+ columns: ['name', 'typeowner', 'description']
+ });
+ };
+
+ // Switch options to save space in model's field
+ var switchOptions = {
+ 'onText': 'Yes', 'offText': 'No',
+ 'onColor': 'success', 'offColor': 'primary',
+ 'size': 'small'
+ };
+
+ // Security label model declaration
+ var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ provider: undefined,
+ security_label: undefined
+ },
+ schema: [{
+ id: 'provider', label: '{{ _('Provider') }}',
+ type: 'text', disabled: false, cellHeaderClasses:'width_percent_50'
+ },{
+ id: 'security_label', label: '{{ _('Security Label') }}',
+ type: 'text', disabled: false, cellHeaderClasses:'width_percent_50'
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+
+ if (_.isUndefined(this.get('security_label')) ||
+ _.isNull(this.get('security_label')) ||
+ String(this.get('security_label')).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = '{{ _('Please provide the value for security label.') }}';
+ this.errorModel.set('security_label', errmsg);
+ return errmsg;
+ } else {
+ this.errorModel.unset('security_label');
+ }
+ return null;
+ }
+ });
+
+ // Composite type model declaration
+ var CompositeModel = Backform.CompositeModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ member_name: undefined,
+ type: undefined,
+ tlength: undefined,
+ is_tlength: false,
+ precision: undefined,
+ is_precision: false,
+ collation: undefined,
+ min_val: undefined,
+ max_val: undefined,
+ },
+ type_options: undefined,
+ subtypes: undefined,
+ schema: [{
+ id: 'member_name', label: '{{ _('Member Name') }}',
+ type: 'text', disabled: false, editable: false
+ },{
+ id: 'type', label: '{{ _('Type') }}', control: 'node-ajax-options',
+ type: 'text', url: 'get_types', disabled: false, node: 'type',
+ editable: false,
+ transform: function(d){
+ this.model.type_options = d;
+ return d;
+ }
+ },{
+ id: 'tlength', label: '{{ _('Length') }}', deps: ['type'], type: 'text',
+ editable: false,
+ disabled: function(m) {
+ // We will store type from selected from combobox
+ var of_type = m.get('type');
+ if(m.type_options) {
+ // iterating over all the types
+ _.each(m.type_options, function(o) {
+ // if type from selected from combobox matches in options
+ if ( of_type == o.value ) {
+ // if length is allowed for selected type
+ if(o.length)
+ {
+ // set the values in model
+ m.set('is_tlength', true, {silent: true});
+ m.set('min_val', o.min_val, {silent: true});
+ m.set('max_val', o.max_val, {silent: true});
+ }
+ }
+ });
+ }
+ return !m.get('is_tlength');
+ }
+ },{
+ id: 'precision', label: '{{ _('Precision') }}', deps: ['type'],
+ type: 'text', editable: false,
+ disabled: function(m) {
+ // We will store type from selected from combobox
+ var of_type = m.get('type');
+ if(m.type_options) {
+ // iterating over all the types
+ _.each(m.type_options, function(o) {
+ // if type from selected from combobox matches in options
+ if ( of_type == o.value ) {
+ // if precession is allowed for selected type
+ if(o.precision)
+ {
+ // set the values in model
+ m.set('is_precision', true, {silent: true});
+ m.set('min_val', o.min_val, {silent: true});
+ m.set('max_val', o.max_val, {silent: true});
+ }
+ }
+ });
+ }
+ return !m.get('is_precision');
+ }
+ },{
+ id: 'collation', label: '{{ _('Collation') }}',
+ control: 'node-ajax-options', editable: false,
+ type: 'text', disabled: false, url: 'get_collations', node: 'type'
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+ // Validation for member name
+ if (_.isUndefined(data.member_name) ||
+ _.isNull(data.member_name) ||
+ String(data.member_name).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = '{{ _('Please specify a value for the member name.') }}'
+ this.errorModel.set('member_name', errmsg)
+ return errmsg;
+ } else {
+ this.errorModel.unset('member_name');
+ }
+
+ // Validation for Length field
+ if (data.is_tlength && !_.isUndefined(data.tlength)) {
+ if (data.tlength < data.min_val )
+ errmsg = '{{ _('Length should not be less than: ') }}' + data.min_val
+ if (data.tlength > data.max_val )
+ errmsg = '{{ _('Length should not be greater than: ') }}' + data.max_val
+ // If we have any error set then throw it to user
+ if(errmsg) {
+ this.errorModel.set('tlength', errmsg)
+ return errmsg;
+ }
+ } else {
+ this.errorModel.unset('tlength');
+ }
+
+ // Validation for precision field
+ if (data.is_precision && !_.isUndefined(data.precision)) {
+ if (data.precision < data.min_val )
+ errmsg = '{{ _('Precision should not be less than: ') }}' + data.min_val
+ if (data.precision > data.max_val )
+ errmsg = '{{ _('Precision should not be greater than: ') }}' + data.max_val
+ // If we have any error set then throw it to user
+ if(errmsg) {
+ this.errorModel.set('precision', errmsg)
+ return errmsg;
+ }
+ } else {
+ this.errorModel.unset('precision');
+ }
+
+ return null;
+ }
+ });
+
+ var EnumModel = Backform.EnumModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ label: undefined,
+ },
+ schema: [{
+ id: 'label', label: '{{ _('Label') }}',type: 'text', disabled: false,
+ cellHeaderClasses: 'width_percent_99', editable: function(m) {
+ return _.isUndefined(m.get('label'));
+ }
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+
+ if (_.isUndefined(data.label) ||
+ _.isNull(data.label) ||
+ String(data.label).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = '{{ _('Please specify a value for the label.') }}';
+ this.errorModel.set('label', errmsg)
+ return errmsg;
+ } else {
+ this.errorModel.unset('label');
+ }
+
+
+ return null;
+ }
+ });
+
+ if (!pgBrowser.Nodes['type']) {
+ pgAdmin.Browser.Nodes['type'] = pgBrowser.Node.extend({
+ type: 'type',
+ label: '{{ _('Type') }}',
+ collection_type: 'coll-type',
+ hasSQL: true,
+ hasDepends: true,
+ parent_type: ['schema', 'catalog'],
+ Init: function() {
+ /* Avoid mulitple registration of menus */
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+
+ pgBrowser.add_menus([{
+ name: 'create_type_on_coll', node: 'coll-type', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_type', node: 'type', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_type', node: 'schema', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: false},
+ enable: 'canCreate'
+ }
+ ]);
+
+ },
+ canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+ canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+ ext_funcs: undefined,
+ model: pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ name: undefined,
+ oid: undefined,
+ is_sys_type: false,
+ typtype: undefined
+ },
+
+ // Default values!
+ initialize: function(attrs, args) {
+ var isNew = (_.size(attrs) === 0);
+
+ if (isNew) {
+ var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
+ var schemaInfo = args.node_info.schema;
+
+ this.set({'typeowner': userInfo.name}, {silent: true});
+ this.set({'schema': schemaInfo.label}, {silent: true});
+ }
+ pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+ },
+
+ schema: [{
+ id: 'name', label: '{{ _('Name') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchema'
+ },{
+ id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+ type: 'text' , mode: ['properties'], disabled: true
+ },{
+ id: 'typeowner', label:'{{ _('Owner') }}', cell: 'string',
+ control: 'node-list-by-name',
+ type: 'text', mode: ['properties', 'create', 'edit'], node: 'role',
+ disabled: 'inSchema'
+ },{
+ id: 'schema', label:'{{ _('Schema') }}', cell: 'string',
+ type: 'text', mode: ['create', 'edit'], node: 'schema',
+ disabled: 'inSchema', filter: function(d) {
+ // If schema name start with pg_* then we need to exclude them
+ if(d && d.label.match(/^pg_/))
+ {
+ return false;
+ }
+ return true;
+ },
+ control: Backform.NodeListByNameControl.extend({
+ render: function(){
+ // Initialize parent's render method
+ Backform.NodeListByNameControl.prototype.render.apply(this, arguments);
+
+ // Set schema default value to its parent Schema
+ if(this.model.isNew()){
+ this.model.set({'schema': this.model.node_info.schema.label});
+ }
+ return this;
+ }
+ })
+ },{
+ id: 'typtype', label:'{{ _('Type') }}',
+ mode: ['create','edit'], disabled: 'inSchemaWithModelCheck',
+ group: '{{ _('Definition') }}',
+ mode: ['edit', 'create'],
+ select2: { width: "50%" },
+ options: [
+ {label: "Composite", value: "c"},
+ {label: "Enumeration", value: "e"},
+ {label: "External", value: "b"},
+ {label: "Range", value: "r"},
+ ],
+ disabled: 'inSchemaWithModelCheck',
+ // If create mode then by default open composite type
+ control: Backform.Select2Control.extend({
+ render: function(){
+ // Initialize parent's render method
+ Backform.Select2Control.prototype.render.apply(this, arguments);
+ if(this.model.isNew()) {
+ this.model.set({'typtype': 'c'});
+ }
+ return this;
+ }
+ })
+ },{
+ id: 'composite', label: '{{ _('Composite Type') }}',
+ model: CompositeModel, editable: true, type: 'collection',
+ group: '{{ _('Definition') }}', mode: ['edit', 'create'],
+ control: 'unique-col-collection', uniqueCol : ['member_name'],
+ canAdd: true, canEdit: true, canDelete: true, disabled: 'inSchema',
+ deps: ['typtype'], deps: ['typtype'],
+ visible: function(m) {
+ if (m.get('typtype') === 'c') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'enum', label: '{{ _('Enumeration Type') }}',
+ model: EnumModel, editable: true, type: 'collection',
+ group: '{{ _('Definition') }}', mode: ['edit', 'create'],
+ canAdd: true, canEdit: false, canDelete: function(m) {
+ // We will disable it if it's in 'edit' mode
+ if (m.isNew()) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ disabled: 'inSchema', deps: ['typtype'],
+ control: 'unique-col-collection', uniqueCol : ['label'],
+ visible: function(m) {
+ return m.get('typtype') === 'e';
+ }
+ },{
+ // We will disable range type control in edit mode
+ type: 'nested', control: 'plain-fieldset', group: '{{ _('Definition') }}',
+ mode: ['edit', 'create'],
+ visible: function(m) {
+ return m.get('typtype') === 'r';
+ }, deps: ['typtype'], label: '{{ _('') }}',
+ schema:[{
+ id: 'typname', label:'{{ _('Sub-type') }}', cell: 'string',
+ control: 'node-ajax-options',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ url: 'get_stypes', type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}', disabled: 'inSchemaWithModelCheck',
+ transform: function(d){
+ this.model.subtypes = d;
+ return d;
+ }
+ },{
+ id: 'opcname', label:'{{ _('Sub-type operator class') }}', cell: 'string',
+ mode: ['properties', 'create', 'edit'], group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['typname'],
+ control: 'select', options: function() {
+ var l_typname = this.model.get('typname'),
+ self = this,
+ result = [];
+ if(!_.isUndefined(l_typname) && l_typname != '')
+ {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_subopclass', this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {'typname' : l_typname},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error', self.model, self.field);
+ }
+ });
+ //
+ }
+ return result;
+ }
+ },{
+ id: 'collname', label:'{{ _('Collation') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ deps: ['typname'], control: 'node-ajax-options', url: 'get_collations',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ disabled: function(m) {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+
+ // Disbale in edit mode
+ if (!m.isNew()) {
+ return true;
+ }
+
+ // To check if collation is allowed?
+ var of_subtype = m.get('typname'),
+ is_collate = undefined;
+ if(!_.isUndefined(of_subtype)) {
+ // iterating over all the types
+ _.each(m.subtypes, function(s) {
+ // if subtype from selected from combobox matches
+ if ( of_subtype === s.label ) {
+ // if collation is allowed for selected subtype
+ // then enable it else disable it
+ is_collate = s.is_collate;
+ }
+ });
+ }
+ // If is_collate is true then do not disable
+ return is_collate ? false : true;
+ }
+ },{
+ id: 'rngcanonical', label:'{{ _('Canonical function') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['name', 'typname'],
+ control: 'select', options: function() {
+ var name = this.model.get('name'),
+ self = this,
+ result = [];
+
+ if(!_.isUndefined(name) && name != '')
+ {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_canonical', this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {"name" : name},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error',
+ self.model, self.field);
+ }
+ });
+ }
+ return result;
+ }
+ },{
+ id: 'rngsubdiff', label:'{{ _('Sub-type diff function') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['opcname'],
+ control: 'select', options: function() {
+ var l_typname = this.model.get('typname'),
+ l_opcname = this.model.get('opcname'),
+ self = this,
+ result = [];
+
+ if(!_.isUndefined(l_typname) && l_typname != '' &&
+ !_.isUndefined(l_opcname) && l_opcname != '') {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_stypediff',
+ this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {'typname' : l_typname, 'opcname': l_opcname},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error',
+ self.model, self.field);
+ }
+ });
+ }
+ return result;
+ }
+ }]
+ },{
+ type: 'nested', control: 'tab', group: '{{ _('Definition') }}',
+ label: '{{ _('External Type') }}', deps: ['typtype'],
+ mode: ['create', 'edit'],
+ visible: function(m) {
+ return m.get('typtype') === 'b';
+ },
+ schema:[{
+ id: 'typinput', label:'{{ _('Input function') }}',
+ cell: 'string',type: 'text',
+ mode: ['properties', 'create', 'edit'], group: 'Required',
+ disabled: 'inSchemaWithModelCheck',
+ control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typoutput', label:'{{ _('Output function') }}',
+ cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Required',
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typreceive', label:'{{ _('Receive function') }}',
+ cell: 'string', type: 'text', group: 'Optional-1',
+ mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typsend', label:'{{ _('Send function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typmodin', label:'{{ _('Typmod in function') }}',
+ cell: 'string', type: 'text',
+ mode: ['properties', 'create', 'edit'], group: 'Optional-1',
+ disabled: 'inSchemaWithModelCheck',
+ control: 'node-ajax-options', url: 'get_external_functions',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ transform: function(d) {
+ var result = [{label :"", value : ""}];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype === 'typmodin' || item.cbtype === 'all') {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ },{
+ id: 'typmodout', label:'{{ _('Typmod out function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck',
+ control: 'node-ajax-options', url: 'get_external_functions',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ transform: function(d) {
+ var result = [{label :"", value : ""}];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype === 'typmodout' || item.cbtype === 'all') {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ },{
+ id: 'typlen', label:'{{ _('Internal length') }}',
+ cell: 'integer', group: 'Optional-1',
+ type: 'int', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'variable', label:'{{ _('Variable?') }}', cell: 'switch',
+ group: 'Optional-1', type: 'switch',
+ mode: ['create','edit'], options: switchOptions,
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typdefault', label:'{{ _('Default?') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typanalyze', label:'{{ _('Analyze function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typcategory', label:'{{ _('Category type') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label :"Array types", value : "A"},
+ {label :"Boolean types", value : "B"},
+ {label :"Composite types", value : "C"},
+ {label :"Date/time types", value : "D"},
+ {label :"Enum types", value : "E"},
+ {label :"Geometric types", value : "G"},
+ {label :"Network address types", value : "I"},
+ {label :"Numeric types", value : "N"},
+ {label :"Pseudo-types", value : "P"},
+ {label :"String types", value : "S"},
+ {label :"Timespan types", value : "T"},
+ {label :"User-defined types", value : "U"},
+ {label :"Bit-string types", value : "V"},
+ {label :"unknown type", value : "X"}
+ ]
+ },{
+ id: 'typispreferred', label:'{{ _('Preferred?') }}', cell: 'switch',
+ type: 'switch', mode: ['properties', 'create','edit'],
+ options: switchOptions, disabled: 'inSchemaWithModelCheck',
+ group: 'Optional-1'
+ },{
+ id: 'element', label:'{{ _('Element type') }}', cell: 'string',
+ control: 'node-ajax-options', group: 'Optional-2',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', url: 'get_types'
+ },{
+ id: 'typdelim', label:'{{ _('Delimiter') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Optional-2', disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typalign', label:'{{ _('Alignment type') }}',
+ cell: 'string', group: 'Optional-2',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label: "char", value: "c"},
+ {label: "int2", value: "s"},
+ {label: "int4", value: "i"},
+ {label: "double", value: "d"},
+ ]
+ },{
+ id: 'typstorage', label:'{{ _('Storage type') }}',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Optional-2', cell: 'string',
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label: "PLAIN", value: "p"},
+ {label: "EXTERNAL", value: "e"},
+ {label: "MAIN", value: "m"},
+ {label: "EXTENDED", value: "x"},
+ ]
+ },{
+ id: 'typbyval', label:'{{ _('Passed by value?') }}',
+ cell: 'switch', options: switchOptions,
+ type: 'switch', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', group: 'Optional-2',
+ },{
+ id: 'is_collatable', label:'{{ _('Collatable?') }}',
+ cell: 'switch', min_version: 90100, group: 'Optional-2',
+ type: 'switch', mode: ['properties', 'create', 'edit'],
+ options: switchOptions, disabled: 'inSchemaWithModelCheck'
+ // End of extension tab
+ }]
+ },{
+ id: 'alias', label:'{{ _('Alias') }}', cell: 'string',
+ type: 'text', mode: ['properties'],
+ disabled: 'inSchema'
+ },{
+ id: 'type_acl', label:'{{ _('Privileges') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Security') }}',
+ disabled: 'inSchema'
+ },{
+ id: 'member_list', label:'{{ _('Members') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Definition') }}',
+ disabled: 'inSchema', visible: function(m) {
+ if(m.get('typtype') === 'c') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'enum_list', label:'{{ _('Labels') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Definition') }}',
+ disabled: 'inSchema', visible: function(m) {
+ if(m.get('typtype') === 'e') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'is_sys_type', label:'{{ _('System type?') }}', cell: 'switch',
+ type: 'switch', mode: ['properties'], options: switchOptions,
+ disabled: 'inSchema'
+ },{
+ id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+ type: 'multiline', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchema'
+ },{
+ 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']
+ },{
+ 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'
+ }],
+ validate: function() {
+ // Validation code for required fields
+ var changedAttrs = this.sessAttrs,
+ msg = undefined;
+
+ if (_.has(changedAttrs, 'name') &&
+ (_.isUndefined(this.get('name'))
+ || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Name can not be empty!') }}';
+ this.errorModel.set('name', msg);
+ } else if (_.has(changedAttrs, 'schema') &&
+ (_.isUndefined(this.get('schema'))
+ || String(this.get('schema')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Schema can not be empty!') }}';
+ this.errorModel.set('schema', msg);
+ } else if (_.has(changedAttrs, 'typtype') &&
+ (_.isUndefined(this.get('typtype'))
+ || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Type can not be empty!') }}';
+ this.errorModel.set('typtype', msg);
+ } else if (this.get('typtype') == 'r' &&
+ _.has(changedAttrs, 'typname')
+ && (_.isUndefined(this.get('typname'))
+ || String(this.get('typname')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Subtype Name can not be empty!') }}';
+ this.errorModel.set('typname', msg);
+ } else if (this.get('typtype') == 'x' &&
+ _.has(changedAttrs, 'typinput')
+ && (_.isUndefined(this.get('typinput'))
+ || String(this.get('typinput')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Input function can not be empty!') }}';
+ this.errorModel.set('typinput', msg);
+ } else if (this.get('typtype') == 'x' &&
+ _.has(changedAttrs, 'typoutput')
+ && (_.isUndefined(this.get('typoutput'))
+ || String(this.get('typoutput')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Output function can not be empty!') }}';
+ this.errorModel.set('typoutput', msg);
+ } else {
+ this.errorModel.unset('name');
+ this.errorModel.unset('schema');
+ this.errorModel.unset('typtype');
+ this.errorModel.unset('typname');
+ this.errorModel.unset('typinput');
+ this.errorModel.unset('typoutput');
+ }
+ return null;
+ },
+ // We will disable everything if we are under catalog node
+ inSchema: function() {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+ return false;
+ },
+ // We will check if we are under schema node & in 'create' mode
+ inSchemaWithModelCheck: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ // We will disbale control if it's in 'edit' mode
+ if (m.isNew()) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+ return true;
+ },
+ // We want to enable only in edit mode
+ inSchemaWithEditMode: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ // We will disbale control if it's in 'edit' mode
+ if (m.isNew()) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+ return true;
+ },
+ // Function will help us to fill combobox
+ external_func_combo: function(d) {
+ var result = [];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype == 'all' ) {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ }),
+ canCreate: function(itemData, item, data) {
+ //If check is false then , we will allow create menu
+ if (data && data.check == false)
+ return true;
+
+ var t = pgBrowser.tree, i = item, d = itemData;
+ // To iterate over tree to check parent node
+ while (i) {
+ // If it is schema then allow user to create table
+ if (_.indexOf(['schema'], d._type) > -1)
+ return true;
+
+ if ('coll-type' == d._type) {
+ //Check if we are not child of catalog
+ prev_i = t.hasParent(i) ? t.parent(i) : null;
+ prev_d = prev_i ? t.itemData(prev_i) : null;
+ if( prev_d._type == 'catalog') {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ i = t.hasParent(i) ? t.parent(i) : null;
+ d = i ? t.itemData(i) : null;
+ }
+ // by default we do not want to allow create menu
+ return true;
+ }
+ });
+ }
+ return pgBrowser.Nodes['type'];
+});
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql
new file mode 100644
index 0000000..60eab25
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql
@@ -0,0 +1,26 @@
+SELECT 'typacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'USAGE' THEN 'U'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT t.typacl
+ FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 %}
+ ) acl,
+ (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
+ AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
+ aclexplode(t.typacl) as d FROM pg_type t WHERE t.oid = {{tid}}::oid) a) d
+ ) d
+ LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+ LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY g.rolname, gt.rolname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql
new file mode 100644
index 0000000..74fd922
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql
@@ -0,0 +1,35 @@
+{# The SQL given below will fetch composite type#}
+{% if type == 'c' %}
+SELECT attname, format_type(t.oid,NULL) AS typname, attndims, atttypmod, nsp.nspname,
+ (SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup,
+ collname, nspc.nspname as collnspname, att.attrelid
+FROM pg_attribute att
+ JOIN pg_type t ON t.oid=atttypid
+ JOIN pg_namespace nsp ON t.typnamespace=nsp.oid
+ LEFT OUTER JOIN pg_type b ON t.typelem=b.oid
+ LEFT OUTER JOIN pg_collation c ON att.attcollation=c.oid
+ LEFT OUTER JOIN 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 type == 'e' %}
+SELECT enumlabel
+FROM pg_enum
+ WHERE enumtypid={{tid}}::oid
+ ORDER by enumsortorder
+{% endif %}
+
+{# The SQL given below will fetch range type#}
+{% if type == 'r' %}
+SELECT rngsubtype, st.typname,
+ rngcollation, col.collname,
+ rngsubopc, opc.opcname,
+ rngcanonical, rngsubdiff
+FROM pg_range
+ LEFT JOIN pg_type st ON st.oid=rngsubtype
+ LEFT JOIN pg_collation col ON col.oid=rngcollation
+ LEFT JOIN pg_opclass opc ON opc.oid=rngsubopc
+ WHERE rngtypid={{tid}}::oid;
+{% endif %}
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
new file mode 100644
index 0000000..245477c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/create.sql
@@ -0,0 +1,78 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{### 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 %});
+{% 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 }}{% 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 %}
+
+ );
+{% 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 %} AS
+ (
+ {% 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.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}}{% endif %}{% if data.typispreferred %},
+ PREFERRED = {{data.typispreferred}}{% endif %}{% if data.typdefault %},
+ DEFAULT = {{data.typdefault|qtLiteral}}{% endif %}{% if data.element %},
+ ELEMENT = {{data.element}}{% endif %}{% if data.typdelim %},
+ DELIMITER = {{data.typdelim|qtLiteral}}{% 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 {{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}};
+{% 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.security_label %}
+{{ SECLABLE.SET(conn, 'TYPE', data.name, r.provider, r.security_label, data.schema) }}
+{% endif %}
+{% endfor %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..c258827
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql
@@ -0,0 +1 @@
+DROP TYPE {{ conn|qtIdent(data.schema, data.name) }}{% if cascade%} CASCADE{% 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_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_collations.sql
new file mode 100644
index 0000000..4b0169b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_collations.sql
@@ -0,0 +1,7 @@
+SELECT --nspname, collname,
+ CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(collname))
+ ELSE '' END AS collation
+FROM pg_collation c, pg_namespace n
+WHERE c.collnamespace=n.oid
+ORDER BY nspname, collname;
\ 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
new file mode 100644
index 0000000..85494db
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_external_functions.sql
@@ -0,0 +1,40 @@
+{### 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
+ concat(quote_ident(nspname), '.', 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 = 0;
+{% endif %}
+{### TypmodIN list ###}
+{% if typemodin %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='_cstring')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
+{### TypmodOUT list ###}
+{% if typemodout %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='cstring')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..14f7950
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,11 @@
+{# Below will provide oid for newly created type #}
+SELECT t.oid
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 data %}
+ AND t.typname = {{data.name|qtLiteral}}
+{% endif %}
+ORDER BY t.typname;
\ 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_subtypes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_subtypes.sql
new file mode 100644
index 0000000..75271fe
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_subtypes.sql
@@ -0,0 +1,56 @@
+{### To fill subtype combobox ###}
+{% if subtype %}
+SELECT DISTINCT typ.typname AS stype,
+ (CASE WHEN typ.typcollation > 0 THEN true ELSE false END) AS is_collate
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype = typ.oid
+WHERE opc.opcmethod = 403
+ORDER BY 1
+{% endif %}
+{### To fill subtype opclass combobox ###}
+{% if subtype_opclass and data and data.typname %}
+SELECT opc.opcname
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ORDER BY opcname;
+{% endif %}
+{### To fetch opcinttype from subtype opclass ###}
+{% if get_opcintype and data and data.typname and data.opcname %}
+SELECT opc.opcintype
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ AND opc.opcname = {{ data.opcname|qtLiteral }}
+ORDER BY opcname;
+{% endif %}
+{### To fill subtype diff function combobox ###}
+{% if opcintype %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS stypdiff
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype = 701
+ AND proargtypes = '{{opcintype}} {{opcintype}}'
+ORDER BY proname;
+{% endif %}
+{### To fill canonical combobox ###}
+{% if getoid %}
+SELECT oid FROM pg_type
+WHERE typname = {{ data.name|qtLiteral }}
+{% endif %}
+{% if canonical and oid %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS canonical
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype= {{ oid }}
+ AND proargtypes = '{{ oid }}'
+ORDER BY proname;
+{% 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_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_types.sql
new file mode 100644
index 0000000..a5d352d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_types.sql
@@ -0,0 +1,10 @@
+SELECT * FROM
+ (SELECT format_type(t.oid,NULL) AS typname,
+ CASE WHEN typelem > 0 THEN typelem ELSE t.oid END AS elemoid,
+ typlen, typtype, t.oid, nspname,
+ (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+FROM pg_type t
+ JOIN pg_namespace nsp ON typnamespace=nsp.oid
+WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r')AND NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) AND nsp.nspname != 'information_schema'
+ ) AS dummy
+ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..6abcb19
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql
@@ -0,0 +1,10 @@
+SELECT t.oid, t.typname AS name
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN pg_namespace nsp ON nsp.oid = t.typnamespace
+WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' AND t.typnamespace = {{scid}}::oid
+{% if not show_system_objects %}
+ AND ct.oid is NULL
+{% endif %}
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..de261a5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql
@@ -0,0 +1,24 @@
+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.*, format_type(t.oid, null) AS alias,
+ 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 array_agg(provider || '=' || label) FROM pg_shseclabel 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_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_type'::regclass)
+ LEFT OUTER JOIN 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;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..939597e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
@@ -0,0 +1,127 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{#======================================#}
+{# 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 {{ data.typeowner }};
+
+{% endif %}
+{#======================================#}
+{# Below will change objects comment #}
+{% if data.description and data.description != o_data.description %}
+COMMENT ON TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ IS {{ data.description|qtLiteral }};
+
+{% 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)}} {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% endfor %}
+{% endif %}
+{% if 'changed' in composite and composite.changed|length > 0 %}
+{% for r in composite.changed %}
+ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ ALTER ATTRIBUTE {{conn|qtIdent(r.member_name)}} SET DATA TYPE {{conn|qtIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% 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}} AFTER {{o_data.enum[o_enum_len].label|qtLiteral }};
+{% 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}} AFTER {{enum.added[p_idx].label|qtLiteral}};
+{% endif %}
+{% endfor %}
+{% 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 %}
+{{ SECLABLE.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 %}
+{{ SECLABLE.SET(conn, 'TYPE', o_data.name, r.provider, r.security_label, o_data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.SET(conn, 'TYPE', o_data.name, r.provider, r.security_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 %}
+{{ PRIVILEGE.UNSETALL(conn, 'TYPE', priv.grantee, o_data.name, o_data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TYPE', priv.grantee, 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, 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 != 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 %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt
new file mode 100644
index 0000000..631037f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt
@@ -0,0 +1,53 @@
+"""
+Here is the list of Postgres inbuilt types & their OID's
+We will use these types to check for validations
+
+## PGOID_TYPE_SERIAL -42L
+## PGOID_TYPE_SERIAL8 -43L
+## PGOID_TYPE_SERIAL2 -44L
+## PGOID_TYPE_BOOL 16L
+## PGOID_TYPE_BYTEA 17L
+## PGOID_TYPE_CHAR 18L
+## PGOID_TYPE_NAME 19L
+## PGOID_TYPE_INT8 20L
+## PGOID_TYPE_INT2 21L
+## PGOID_TYPE_INT4 23L
+## PGOID_TYPE_TEXT 25L
+## PGOID_TYPE_OID 26L
+## PGOID_TYPE_TID 27L
+## PGOID_TYPE_XID 28L
+## PGOID_TYPE_CID 29L
+## PGOID_TYPE_FLOAT4 700L
+## PGOID_TYPE_FLOAT8 701L
+## PGOID_TYPE_MONEY 790L
+## PGOID_TYPE_CHAR_ARRAY 1002L
+## PGOID_TYPE_TEXT_ARRAY 1009L
+## PGOID_TYPE_BPCHAR_ARRAY 1014L
+## PGOID_TYPE_VARCHAR_ARRAY 1015L
+## PGOID_TYPE_BPCHAR 1042L
+## PGOID_TYPE_VARCHAR 1043L
+## PGOID_TYPE_DATE 1082L
+## PGOID_TYPE_TIME 1083L
+## PGOID_TYPE_TIMESTAMP 1114L
+## PGOID_TYPE_TIMESTAMP_ARRAY 1115L
+## PGOID_TYPE_TIME_ARRAY 1183L
+## PGOID_TYPE_TIMESTAMPTZ 1184L
+## PGOID_TYPE_TIMESTAMPTZ_ARRAY 1185L
+## PGOID_TYPE_INTERVAL 1186L
+## PGOID_TYPE_INTERVAL_ARRAY 1187L
+## PGOID_TYPE_NUMERIC_ARRAY 1231L
+## PGOID_TYPE_TIMETZ 1266L
+## PGOID_TYPE_TIMETZ_ARRAY 1270L
+## PGOID_TYPE_BIT 1560L
+## PGOID_TYPE_BIT_ARRAY 1561L
+## PGOID_TYPE_VARBIT 1562L
+## PGOID_TYPE_VARBIT_ARRAY 1563L
+## PGOID_TYPE_NUMERIC 1700L
+## PGOID_TYPE_CSTRING 2275L
+## PGOID_TYPE_ANY 2276L
+## PGOID_TYPE_VOID 2278L
+## PGOID_TYPE_TRIGGER 2279L
+## PGOID_TYPE_LANGUAGE_HANDLER 2280L
+## PGOID_TYPE_INTERNAL 2281L
+## PGOID_TYPE_HANDLER 3115L
+"""
\ No newline at end of file
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-18 07:15 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-18 16:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
@ 2016-03-22 08:14 ` Murtuza Zabuawala <[email protected]>
2016-03-22 09:22 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
0 siblings, 1 reply; 26+ messages in thread
From: Murtuza Zabuawala @ 2016-03-22 08:14 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi Dave,
We can create new external type using below method, By running all of below
queries at the same time , we can not create separate external type by only
using create type statement.
So as per my discussion with Ashesh, We should not allow user to create
external type in pgAdmin4 but only show definition in edit mode.
CREATE OR REPLACE FUNCTION geometry_1_in(cstring)
RETURNS geometry_1
AS '$libdir/postgis-2.2','LWGEOM_in'
LANGUAGE 'c' IMMUTABLE STRICT;
CREATE OR REPLACE FUNCTION geometry_1_out(geometry_1)
RETURNS cstring
AS '$libdir/postgis-2.2','LWGEOM_out'
LANGUAGE 'c' IMMUTABLE STRICT;
CREATE OR REPLACE FUNCTION geometry_1_typmod_in(cstring[])
RETURNS integer
AS '$libdir/postgis-2.2','geometry_typmod_in'
LANGUAGE 'c' IMMUTABLE STRICT;
CREATE OR REPLACE FUNCTION geometry_1_typmod_out(integer)
RETURNS cstring
AS '$libdir/postgis-2.2','postgis_typmod_out'
LANGUAGE 'c' IMMUTABLE STRICT;
CREATE OR REPLACE FUNCTION geometry_1_analyze(internal)
RETURNS bool
AS '$libdir/postgis-2.2', 'gserialized_analyze_nd'
LANGUAGE 'c' VOLATILE STRICT;
CREATE OR REPLACE FUNCTION geometry_1_recv(internal)
RETURNS geometry_1
AS '$libdir/postgis-2.2','LWGEOM_recv'
LANGUAGE 'c' IMMUTABLE STRICT;
CREATE OR REPLACE FUNCTION geometry_1_send(geometry_1)
RETURNS bytea
AS '$libdir/postgis-2.2','LWGEOM_send'
LANGUAGE 'c' IMMUTABLE STRICT;
CREATE TYPE public.geometry_1
(INPUT=geometry_1_in,
OUTPUT=geometry_1_out,
RECEIVE=geometry_1_recv,
SEND=geometry_1_send,
TYPMOD_IN=geometry_1_typmod_in,
TYPMOD_OUT=geometry_1_typmod_out,
ANALYZE=geometry_1_analyze,
CATEGORY='U', DEFAULT='',
INTERNALLENGTH=-1, ALIGNMENT=double, STORAGE=MAIN);
Please find inline comment for other two issues & updated patch attached.
Regards,
Murtuza
On Friday 18 March 2016 10:22 PM, Dave Page wrote:
Hi
On Fri, Mar 18, 2016 at 7:15 AM, Murtuza Zabuawala
<[email protected]> wrote:
Hi Dave,
Even after installing PosGIS I’m not able to create new custom external type
because the SQL does not return any value (Same behaviour as is in pgAdmin3)
, But I’m able to list all of it’s properties of external types same as
pgAdmin3 (PFA screenshots).
Hmm, I wonder if that's a pgAdmin 3 bug. Please check the CREATE TYPE
docs, and make sure the queries we're using are appropriate to find
suitable functions. It's possible we've messed that up.
I have also attached patch for plain fields control (without lable & border)
for backform. (Please apply it before testing new type patch)
That looks better. I'll commit that patch.
- I'm allowed to edit ENUM label names, but PostgreSQL doesn't support that.
This issue still exists.
This is working fine for me with same patch, labels coming from server are
not editable but we can add new labels (PFA screenshot)
I also noticed some weirdness with the error message display - please
see the attached screenshots. The one complaining about the missing
name is as I'd expect. The second one is showing the error message an
additional time. Please look at that.
Done
(PFA patch for sub node control also, there was an issue with subnode
control error handling which I've fixed it, please apply patch then test
Type node)
I've attached an updated patch - I cleaned up some strings and
localisation tags in this version.
Thanks.
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[image/png] Screen Shot 2016-03-21 at 12.47.44 pm.png (98.1K, 3-Screen%20Shot%202016-03-21%20at%2012.47.44%20pm.png)
download | view image
[application/octet-stream] fixed_sub_node_error_issue_v1.patch (2.0K, 4-fixed_sub_node_error_issue_v1.patch)
download | inline diff:
diff --git a/web/pgadmin/static/css/overrides.css b/web/pgadmin/static/css/overrides.css
index 4dcff33..770eac4 100755
--- a/web/pgadmin/static/css/overrides.css
+++ b/web/pgadmin/static/css/overrides.css
@@ -662,6 +662,10 @@ table.backgrid tr.new {
margin-right: 0px;
}
+.subnode-error .help-block {
+ color: #a94442;
+}
+
.select2-container--default .select2-search--inline .select2-search__field {
background: transparent none repeat scroll 0% 0%;
border: medium none;
diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js
index 66d8dab..cabfd53 100644
--- a/web/pgadmin/static/js/backform.pgadmin.js
+++ b/web/pgadmin/static/js/backform.pgadmin.js
@@ -1039,6 +1039,11 @@
return $dialog;
},
+ clearInvalid: function() {
+ this.$el.removeClass("subnode-error");
+ this.$el.find(".pgadmin-control-error-message").remove();
+ return this;
+ },
updateInvalid: function() {
var self = this,
errorModel = this.model.errorModel;
@@ -1052,7 +1057,7 @@
if (_.isEmpty(error)) return;
- self.$el.addClass(Backform.errorClassName).append(
+ self.$el.addClass("subnode-error").append(
$("<div></div>").addClass('pgadmin-control-error-message col-xs-offset-4 col-xs-8 help-block').text(error)
);
});
@@ -1108,10 +1113,15 @@
if (_.isEmpty(error)) return;
- self.$el.addClass(Backform.errorClassName).append(
+ self.$el.addClass('subnode-error').append(
$("<div></div>").addClass('pgadmin-control-error-message col-xs-offset-4 col-xs-8 help-block').text(error)
);
},
+ clearInvalid: function() {
+ this.$el.removeClass('subnode-error');
+ this.$el.find(".pgadmin-control-error-message").remove();
+ return this;
+ },
showGridControl: function(data) {
var gridHeader = ["<div class='subnode-header'>",
" <label class='control-label col-sm-4'>" + data.label + "</label>" ,
[application/octet-stream] types_node_v5.patch (107.9K, 5-types_node_v5.patch)
download | inline diff:
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
new file mode 100644
index 0000000..6a46bb0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/__init__.py
@@ -0,0 +1,1210 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+""" Implements Type Node """
+
+import json
+from flask import render_template, make_response, request, jsonify
+from flask.ext.babel import gettext
+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 pgadmin.browser.server_groups.servers.databases as database
+from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
+ parse_priv_to_db
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class TypeModule(SchemaChildModule):
+ """
+ class TypeModule(SchemaChildModule)
+
+ A module class for Type node derived from SchemaChildModule
+
+ Methods:
+ -------
+ * __init__(*args, **kwargs)
+ - Method is used to initialize the Type and it's base module.
+
+ * get_nodes(gid, sid, did, scid, tid)
+ - Method is used to generate the browser collection node.
+
+ * node_inode()
+ - Method is overridden from its base class to make the node as leaf node.
+
+ * script_load()
+ - Load the module script for type, when any of the server node is
+ initialized.
+ """
+
+ NODE_TYPE = 'type'
+ COLLECTION_LABEL = gettext("Types")
+
+ def __init__(self, *args, **kwargs):
+ """
+ Method is used to initialize the TypeModule and it's base module.
+
+ Args:
+ *args:
+ **kwargs:
+ """
+ super(TypeModule, self).__init__(*args, **kwargs)
+ self.min_ver = None
+ self.max_ver = None
+
+ def get_nodes(self, gid, sid, did, scid):
+ """
+ Generate the collection node
+ """
+ yield self.generate_browser_collection_node(scid)
+
+ @property
+ def script_load(self):
+ """
+ Load the module script for database, when any of the database node is
+ initialized.
+ """
+ return database.DatabaseModule.NODE_TYPE
+
+ @property
+ def node_inode(self):
+ """
+ Load the module node as a leaf node
+ """
+ return False
+
+blueprint = TypeModule(__name__)
+
+
+class TypeView(PGChildNodeView):
+ """
+ This class is responsible for generating routes for Type node
+
+ Methods:
+ -------
+ * __init__(**kwargs)
+ - Method is used to initialize the TypeView and it's base view.
+
+ * check_precondition()
+ - This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+
+ * list()
+ - This function is used to list all the Type nodes within that
+ collection.
+
+ * nodes()
+ - This function will used to create all the child node within that
+ collection, Here it will create all the Type node.
+
+ * properties(gid, sid, did, scid, tid)
+ - This function will show the properties of the selected Type node
+
+ * create(gid, sid, did, scid)
+ - This function will create the new Type object
+
+ * update(gid, sid, did, scid, tid)
+ - This function will update the data for the selected Type node
+
+ * delete(self, gid, sid, scid, tid):
+ - This function will drop the Type object
+
+ * msql(gid, sid, did, scid, tid)
+ - This function is used to return modified SQL for the selected
+ Type node
+
+ * get_sql(data, scid, tid)
+ - This function will generate sql from model data
+
+ * sql(gid, sid, did, scid):
+ - This function will generate sql to show it in sql pane for the
+ selected Type node.
+
+ * dependency(gid, sid, did, scid, tid):
+ - This function will generate dependency list show it in dependency
+ pane for the selected Type node.
+
+ * dependent(gid, sid, did, scid, tid):
+ - This function will generate dependent list to show it in dependent
+ pane for the selected Type node.
+
+ * additional_properties(copy_dict, tid):
+ - This function will add additional properties in response
+
+ * get_collations(gid, sid, did, scid, tid):
+ - This function will return list of collation in ajax response
+
+ * get_types(gid, sid, did, scid, tid):
+ - This function will return list of types in ajax response
+
+ * get_subtypes(gid, sid, did, scid, tid):
+ - This function will return list of subtypes in ajax response
+
+ * get_subtype_opclass(gid, sid, did, scid, tid):
+ - This function will return list of subtype opclass in ajax response
+
+ * get_subtype_diff(gid, sid, did, scid, tid):
+ - This function will return list of subtype diff functions
+ in ajax response
+
+ * get_canonical(gid, sid, did, scid, tid):
+ - This function will return list of canonical functions
+ in ajax response
+
+ * get_external_functions_list(gid, sid, did, scid, tid):
+ - This function will return list of external functions
+ in ajax response
+ """
+
+ node_type = blueprint.node_type
+
+ parent_ids = [
+ {'type': 'int', 'id': 'gid'},
+ {'type': 'int', 'id': 'sid'},
+ {'type': 'int', 'id': 'did'},
+ {'type': 'int', 'id': 'scid'}
+ ]
+ ids = [
+ {'type': 'int', 'id': 'tid'}
+ ]
+
+ operations = dict({
+ 'obj': [
+ {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+ {'get': 'list', 'post': 'create'}
+ ],
+ 'delete': [{'delete': 'delete'}],
+ 'children': [{'get': 'children'}],
+ 'nodes': [{'get': 'node'}, {'get': 'nodes'}],
+ 'sql': [{'get': 'sql'}],
+ 'msql': [{'get': 'msql'}, {'get': 'msql'}],
+ 'stats': [{'get': 'statistics'}],
+ 'dependency': [{'get': 'dependencies'}],
+ 'dependent': [{'get': 'dependents'}],
+ 'module.js': [{}, {}, {'get': 'module_js'}],
+ 'get_types': [{'get': 'get_types'}, {'get': 'get_types'}],
+ 'get_stypes': [{'get': 'get_subtypes'}, {'get': 'get_subtypes'}],
+ 'get_subopclass': [{'get': 'get_subtype_opclass'},
+ {'get': 'get_subtype_opclass'}],
+ 'get_stypediff': [{'get': 'get_subtype_diff'}, {'get': 'get_subtype_diff'}],
+ 'get_canonical': [{'get': 'get_canonical'}, {'get': 'get_canonical'}],
+ 'get_collations': [{'get': 'get_collations'}, {'get': 'get_collations'}],
+ 'get_external_functions': [{'get': 'get_external_functions_list'},
+ {'get': 'get_external_functions_list'}]
+ })
+
+ def check_precondition(f):
+ """
+ This function will behave as a decorator which will checks
+ database connection before running view, it will also attaches
+ manager,conn & template_path properties to self
+ """
+ @wraps(f)
+ def wrap(*args, **kwargs):
+ # Here args[0] will hold self & kwargs will hold gid,sid,did
+ self = args[0]
+ self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(kwargs['sid'])
+ self.conn = self.manager.connection(did=kwargs['did'])
+
+ # We need datlastsysoid to check if current type is system type
+ self.datlastsysoid = self.manager.db_info[kwargs['did']]['datlastsysoid']
+
+ # If DB not connected then return error to browser
+ if not self.conn.connected():
+ return precondition_required(
+ gettext(
+ "Connection to the server has been lost!"
+ )
+ )
+
+ # Declare allows acl on type
+ self.acl = ['U']
+
+ # we will set template path for sql scripts
+ self.template_path = 'type/sql/9.1_plus'
+
+ return f(*args, **kwargs)
+
+ return wrap
+
+ @check_precondition
+ def list(self, gid, sid, did, scid):
+ """
+ This function is used to list all the type nodes within that collection.
+
+ Args:
+ gid: Server group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of available type nodes
+ """
+
+ SQL = render_template("/".join([self.template_path, 'properties.sql']),
+ scid=scid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects)
+
+ status, res = self.conn.execute_dict(SQL)
+
+ if not status:
+ return internal_server_error(errormsg=res)
+ return ajax_response(
+ response=res['rows'],
+ status=200
+ )
+
+ @check_precondition
+ def nodes(self, gid, sid, did, scid):
+ """
+ This function will used to create all the child node within that collection.
+ Here it will create all the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of available type child nodes
+ """
+
+ res = []
+ SQL = render_template("/".join([self.template_path,
+ 'nodes.sql']), scid=scid,
+ show_system_objects=self.blueprint.show_system_objects)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=rset)
+
+ for row in rset['rows']:
+ res.append(
+ self.blueprint.generate_browser_node(
+ row['oid'],
+ scid,
+ row['name'],
+ icon="icon-type"
+ ))
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ def additional_properties(self, copy_dict, tid):
+ """
+ We will use this function to add additional properties according to type
+
+ Returns:
+ additional properties for type like range/composite/enum
+
+ """
+ # Fetching type of type
+ of_type = copy_dict['typtype']
+ res = dict()
+ # If type is of Composite then we need to add members list in our output
+ if of_type == 'c':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='c',
+ typrelid=copy_dict['typrelid'])
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # To display in properties
+ properties_list = []
+ # To display in composite collection grid
+ composite_lst = []
+
+ for row in rset['rows']:
+ typelist = ' '.join([row['attname'], row['typname']])
+ if not row['collname'] or (row['collname'] == 'default'
+ and row['collnspname'] == 'pg_catalog'):
+ full_collate = ''
+ collate = ''
+ else:
+ full_collate = get_driver(PG_DEFAULT_DRIVER).qtIdent(
+ self.conn, row['collnspname'], row['collname'])
+ collate = ' COLLATE ' + full_collate
+ typelist += collate
+ properties_list.append(typelist)
+
+ # Below logic will allow us to split length, precision from type name for grid
+ import re
+ matchObj = re.match( r'(.*)\((.*?),(.*?)\)', row['typname'])
+ if matchObj:
+ t_name = matchObj.group(1)
+ t_len = matchObj.group(2)
+ t_prec = matchObj.group(3)
+ else:
+ t_name = row['typname']
+ t_len = None
+ t_prec = None
+
+ composite_lst.append({
+ 'attnum':row['attnum'], 'member_name': row['attname'], 'type': t_name, 'collation': full_collate,
+ 'tlength': t_len, 'precision': t_prec })
+
+ # Adding both results
+ res['member_list'] = ', '.join(properties_list)
+ res['composite'] = composite_lst
+
+ # If type is of ENUM then we need to add labels in our output
+ if of_type == 'e':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='e', tid=tid)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ # To display in properties
+ properties_list = []
+ # To display in enum grid
+ enum_list = []
+ for row in rset['rows']:
+ properties_list.append(row['enumlabel'])
+ enum_list.append({'label': row['enumlabel']})
+
+ # Adding both results in ouput
+ res['enum_list'] = ', '.join(properties_list)
+ res['enum'] = enum_list
+
+ # If type is of Range then we need to add collation,subtype etc in our output
+ if of_type == 'r':
+ SQL = render_template("/".join([self.template_path,
+ 'additional_properties.sql']),
+ type='r', tid=tid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ range_dict = dict(res['rows'][0])
+ res.update(range_dict)
+
+ # Returning only additional properties only
+ return res
+
+ @check_precondition
+ def properties(self, gid, sid, did, scid, tid):
+ """
+ This function will show the properties of the selected type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ scid: Schema ID
+ tid: Type ID
+
+ Returns:
+ JSON of selected type node
+ """
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ copy_dict = dict(res['rows'][0])
+
+ # We need to parse & convert ACL coming from database to json format
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ copy_dict['typacl'] = []
+
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in copy_dict:
+ copy_dict[row['deftype']].append(priv)
+ else:
+ copy_dict[row['deftype']] = [priv]
+
+ # Calling function to check and additional properties if available
+ copy_dict.update(self.additional_properties(copy_dict, tid))
+
+ return ajax_response(
+ response=copy_dict,
+ status=200
+ )
+
+ @check_precondition
+ def get_collations(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of collation available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_collations.sql']))
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['collation'],
+ 'value': row['collation']}
+ )
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_types(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of types available
+ as AJAX response.
+ """
+ res = []
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_types.sql']))
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ # Attaching properties for precession
+ # & length validation for current type
+ precision = False
+ length = False
+ min_val = 0
+ max_val = 0
+
+ # Check against PGOID for specific type
+ if row['elemoid']:
+ if row['elemoid'] in (1560, 1561, 1562, 1563, 1042, 1043,
+ 1014, 1015):
+ typeval = 'L'
+ elif row['elemoid'] in (1083, 1114, 1115, 1183, 1184, 1185,
+ 1186, 1187, 1266, 1270):
+ typeval = 'D'
+ elif row['elemoid'] in (1231, 1700):
+ typeval = 'P'
+ else:
+ typeval = ' '
+
+ # Logic to set precision & length/min/max values
+ if typeval == 'P':
+ precision = True
+
+ if precision or typeval in ('L', 'D'):
+ length = True
+ min_val = 0 if typeval == 'D' else 1
+ if precision:
+ max_val = 1000
+ elif min_val:
+ # Max of integer value
+ max_val = 2147483647
+ else:
+ max_val = 10
+
+ res.append(
+ {'label': row['typname'], 'value': row['typname'],
+ 'typval': typeval, 'precision': precision,
+ 'length': length, 'min_val': min_val, 'max_val': max_val
+ }
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtypes(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtypes available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ subtype=True)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['stype'], 'value': row['stype'],
+ 'is_collate': row['is_collate']}
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtype_opclass(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtype opclass available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ subtype_opclass=True, data=data)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['opcname'],
+ 'value': row['opcname']})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_subtype_diff(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of subtypes diff functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+
+ try:
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ get_opcintype=True, data=data)
+ if SQL:
+ status, opcintype = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=opcintype)
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ opcintype=opcintype, conn=self.conn)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['stypdiff'],
+ 'value': row['stypdiff']}
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def get_canonical(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of canonical functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': ''}]
+ data = request.args
+ canonical = True
+
+ try:
+ # We want to send data only if in we are in edit mode
+ # else we will disable the combobox
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ getoid=True, data=data)
+ if SQL:
+ status, oid = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=oid)
+ # If oid is None then do not run SQL
+ if oid is None:
+ canonical = False
+
+ SQL = render_template("/".join([self.template_path,
+ 'get_subtypes.sql']),
+ canonical=canonical, conn=self.conn, oid=oid)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['canonical'],
+ 'value': row['canonical']})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def get_external_functions_list(self, gid, sid, did, scid, tid=None):
+ """
+ This function will return list of external functions available
+ as AJAX response.
+ """
+ res = [{'label': '', 'value': '', 'cbtype': 'all'}]
+
+ try:
+ # The SQL generated below will populate Input/Output/Send/
+ # Receive/Analyze/TypModeIN/TypModOUT combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ extfunc=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'all'})
+
+ # The SQL generated below will populate TypModeIN combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ typemodin=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'typmodin'})
+
+ # The SQL generated below will populate TypModeIN combo box
+ SQL = render_template("/".join([self.template_path,
+ 'get_external_functions.sql']),
+ typemodout=True)
+ if SQL:
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['func'], 'value': row['func'],
+ 'cbtype': 'typmodout'})
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def create(self, gid, sid, did, scid):
+ """
+ This function will creates new the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ data = request.form if request.form else json.loads(request.data.decode())
+ required_args = {
+ 'name': 'Name',
+ 'typtype': 'Type'
+ }
+
+ for arg in required_args:
+ if arg not in data:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ "Couldn't find the required parameter (%s)." %
+ required_args[arg]
+ )
+ )
+ # Additional checks goes here
+ # If type is composite then check if it has two members
+ if data and data[arg] == 'c':
+ if len(data['composite']) < 2:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Composite types requires at least two members'
+ )
+ )
+ # If type is enum then check if it has minimum one label
+ if data and data[arg] == 'e':
+ if len(data['enum']) < 1:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Enumeration types requires at least one label'
+ )
+ )
+ # If type is range then check if subtype is defined or not
+ if data and data[arg] == 'r':
+ if data['typname'] is None:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'Subtype must be defined for range types'
+ )
+ )
+ # If type is external then check if input/output
+ # conversion function is defined
+ if data and data[arg] == 'b':
+ if data['typinput'] is None or \
+ data['typoutput'] is None:
+ return make_json_response(
+ status=410,
+ success=0,
+ errormsg=gettext(
+ 'External types require both Input & \
+ Output conversion function.'
+ )
+ )
+
+ # To format privileges coming from client
+ if 'typacl' in data and data['typacl'] is not None:
+ data['typacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ data = self._convert_for_sql(data)
+
+ try:
+ SQL = render_template("/".join([self.template_path, 'create.sql']),
+ data=data, conn=self.conn)
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # we need oid to to add object in tree at browser
+ SQL = render_template("/".join([self.template_path,
+ 'get_oid.sql']),
+ scid=scid, data=data)
+ status, tid = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=tid)
+
+ return jsonify(
+ node=self.blueprint.generate_browser_node(
+ tid,
+ scid,
+ data['name'],
+ icon="icon-type"
+ )
+ )
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def update(self, gid, sid, did, scid, tid):
+ """
+ This function will updates existing the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+
+ data = request.form if request.form else json.loads(request.data.decode())
+ try:
+ SQL = self.get_sql(gid, sid, data, scid, tid)
+ if SQL and SQL.strip('\n') and SQL.strip(' '):
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return make_json_response(
+ success=1,
+ info="Type updated",
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+ else:
+ return make_json_response(
+ success=1,
+ info="Nothing to update",
+ data={
+ 'id': tid,
+ 'scid': scid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def delete(self, gid, sid, did, scid, tid):
+ """
+ This function will updates existing the type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+
+ # Below will decide if it's simple drop or drop with cascade call
+ if self.cmd == 'delete':
+ # This is a cascade operation
+ cascade = True
+ else:
+ cascade = False
+
+ try:
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'delete.sql']),
+ data=data, cascade=cascade, conn=self.conn)
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return make_json_response(
+ success=1,
+ info=gettext("Type dropped"),
+ data={
+ 'id': tid,
+ 'scid': scid
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+
+ @check_precondition
+ def msql(self, gid, sid, did, scid, tid=None):
+ """
+ This function will generates modified sql for type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ req = request.args
+ data = dict()
+
+ # converting nested request data in proper json format
+ for key,val in req.items():
+ if key in ['composite', 'enum', 'seclabels', 'typacl']:
+ data[key] = json.loads(val)
+ else:
+ data[key] = val
+
+ try:
+ SQL = self.get_sql(gid, sid, data, scid, tid)
+
+ if SQL and SQL.strip('\n') and SQL.strip(' '):
+ return make_json_response(
+ data=SQL,
+ status=200
+ )
+ except Exception as e:
+ internal_server_error(errormsg=str(e))
+
+ def _convert_for_sql(self, data):
+ """
+ This function will convert combobox values into
+ readable format for sql & msql function
+ """
+ # Convert combobox value into readable format
+
+ if 'typstorage' in data and data['typstorage'] is not None:
+ if data['typstorage'] == 'p':
+ data['typstorage'] = 'PLAIN'
+ elif data['typstorage'] == 'e':
+ data['typstorage'] = 'EXTERNAL'
+ elif data['typstorage'] == 'm':
+ data['typstorage'] = 'MAIN'
+ elif data['typstorage'] == 'x':
+ data['typstorage'] = 'EXTENDED'
+
+ if 'typalign' in data and data['typalign'] is not None:
+ if data['typalign'] == 'c':
+ data['typalign'] = 'char'
+ elif data['typalign'] == 's':
+ data['typalign'] = 'int2'
+ elif data['typalign'] == 'i':
+ data['typalign'] = 'int4'
+ elif data['typalign'] == 'd':
+ data['typalign'] = 'double'
+
+ return data
+
+ def get_sql(self, gid, sid, data, scid, tid=None):
+ """
+ This function will genrate sql from model data
+ """
+ if tid is not None:
+
+ for key in ['typacl']:
+ if key in data and data[key] is not None:
+ if 'added' in data[key]:
+ data[key]['added'] = parse_priv_to_db(data[key]['added'], self.acl)
+ if 'changed' in data[key]:
+ data[key]['changed'] = parse_priv_to_db(data[key]['changed'], self.acl)
+ if 'deleted' in data[key]:
+ data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], self.acl)
+
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ old_data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ old_data['typacl'] = []
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in old_data:
+ old_data[row['deftype']].append(priv)
+ else:
+ old_data[row['deftype']] = [priv]
+
+ # Calling function to check and additional properties if available
+ old_data.update(self.additional_properties(old_data, tid))
+ old_data = self._convert_for_sql(old_data)
+
+ SQL = render_template(
+ "/".join([self.template_path, 'update.sql']),
+ data=data, o_data=old_data, conn=self.conn
+ )
+ else:
+ required_args = [
+ 'name',
+ 'typtype'
+ ]
+
+ for arg in required_args:
+ if arg not in data:
+ return " --definition incomplete"
+
+ # Privileges
+ if 'typacl' in data and data['typacl'] is not None:
+ data['typacl'] = parse_priv_to_db(data['typacl'], self.acl)
+ data = self._convert_for_sql(data)
+ SQL = render_template("/".join([self.template_path,
+ 'create.sql']),
+ data=data, conn=self.conn)
+
+ return SQL
+
+
+ @check_precondition
+ def sql(self, gid, sid, did, scid, tid):
+ """
+ This function will generates reverse engineered sql for type object
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ SQL = render_template("/".join([self.template_path,
+ 'properties.sql']),
+ scid=scid, tid=tid,
+ datlastsysoid=self.datlastsysoid,
+ show_system_objects=self.blueprint.show_system_objects
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Making copy of output for future use
+ data = dict(res['rows'][0])
+
+ SQL = render_template("/".join([self.template_path, 'acl.sql']),
+ scid=scid, tid=tid)
+ status, acl = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=acl)
+
+ # We will set get privileges from acl sql so we don't need
+ # it from properties sql
+ data['typacl'] = []
+
+ for row in acl['rows']:
+ priv = parse_priv_from_db(row)
+ if row['deftype'] in data:
+ data[row['deftype']].append(priv)
+ else:
+ data[row['deftype']] = [priv]
+
+ # Privileges
+ if 'typacl' in data and data['typacl'] is not None:
+ data['nspacl'] = parse_priv_to_db(data['typacl'], self.acl)
+
+ # Calling function to check and additional properties if available
+ data.update(self.additional_properties(data, tid))
+
+ # We do not want to display table which has '-' value
+ # setting them to None so that jinja avoid displaying them
+ for k in data:
+ if data[k] == '-':
+ data[k] = None
+
+ SQL = self.get_sql(gid, sid, data, scid, tid=None)
+
+ # We are appending headers here for sql panel
+ sql_header = "-- Type: {0}\n\n-- ".format(data['name'])
+ sql_header += render_template("/".join([self.template_path,
+ 'delete.sql']),
+ data=data, conn=self.conn)
+ SQL = sql_header + '\n\n' + SQL
+
+ return ajax_response(response=SQL)
+
+ @check_precondition
+ def dependents(self, gid, sid, did, scid, tid):
+ """
+ This function get the dependents and return ajax response
+ for the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ dependents_result = self.get_dependents(
+ self.conn, tid
+ )
+
+ return ajax_response(
+ response=dependents_result,
+ status=200
+ )
+
+ @check_precondition
+ def dependencies(self, gid, sid, did, scid, tid):
+ """
+ This function get the dependencies and return ajax response
+ for the type node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ scid: Schema ID
+ tid: Type ID
+ """
+ dependencies_result = self.get_dependencies(
+ self.conn, tid
+ )
+
+ return ajax_response(
+ response=dependencies_result,
+ status=200
+ )
+
+TypeView.register_node_view(blueprint)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/coll-type.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/coll-type.png
new file mode 100644
index 0000000000000000000000000000000000000000..fb020d7d99f84439046d288615e865ee1fbdb815
GIT binary patch
literal 329
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=3NcXI=aM>fHaQT@Q91
z`hS1R{~OExU!3;;MEm~(mH&6<{olN3-cDJdI>wS9zhDN3XE)M-9L@rd$YLPv0mg18
zv+aP47*7|+5RU7%XQO!=40zlgo^wo$EU!My#3j(ks*}LT9dUr^nFV*x`<LfEXLU&{
zNT_QZ){p11jr8BRJf*J9t1q$D%X6FK(q}uIk4`OV_&v+tAZF)<c~_)!|NM{V=e#WX
zN#J9vG0+~>64!{5l*E!$tK_0oAjM#0U}&IgXryak7-D2#Wnye)VybOmYGq(B@15Q%
q6b-rgDVb@N5Df;FU=2XkCRPS!5DllMhpqu?VDNPHb6Mw<&;$UZ$8*O3
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/type.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/type.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c16764e7d08c56922a97a2f0c6cc06455c86a56
GIT binary patch
literal 325
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv5AX?b1=8~#9EmzT>)QWU=l(xE
z`v2js|F_rtzdY~%nF;@oHvQjQ{(pPk|IMk)0@*;Nj3q&S!3+-1ZlnP@oCO|{#X#Bv
zjNMLV+W{G&o-U3d9M_W*4zM_RIV|8v(U4|t6r8Z|5fh7_LtB=H01KmJR;I%QmXsCK
znH_-=7a3W69onAxD9m6<$ym$O<m%A&El=SFOUjEmj7`o4+9Dfn@G_j<Bfar~Z-)!e
z0@V`Nh?11Vl2ohYqEsNoU}RuuplfKPYhV~+WME}tY-M7qZD49;U@-5U-YOIgx%nxX
kX_XKS29{tAK-DHz24)Zqr>2Ll0cv3IboFyt=akR{01h5&qyPW_
literal 0
HcmV?d00001
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
new file mode 100644
index 0000000..e7211f2
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/js/type.js
@@ -0,0 +1,864 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'pgadmin',
+ 'pgadmin.browser', 'alertify', 'backgrid', 'pgadmin.backgrid',
+ 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
+
+ if (!pgBrowser.Nodes['coll-type']) {
+ var databases = pgAdmin.Browser.Nodes['coll-type'] =
+ pgAdmin.Browser.Collection.extend({
+ node: 'type',
+ label: '{{ _('Types') }}',
+ type: 'coll-type',
+ columns: ['name', 'typeowner', 'description']
+ });
+ };
+
+ // Switch options to save space in model's field
+ var switchOptions = {
+ 'onText': 'Yes', 'offText': 'No',
+ 'onColor': 'success', 'offColor': 'primary',
+ 'size': 'small'
+ };
+
+ // Security label model declaration
+ var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ provider: undefined,
+ security_label: undefined
+ },
+ schema: [{
+ id: 'provider', label: '{{ _('Provider') }}',
+ type: 'text', disabled: false, cellHeaderClasses:'width_percent_50'
+ },{
+ id: 'security_label', label: '{{ _('Security Label') }}',
+ type: 'text', disabled: false, cellHeaderClasses:'width_percent_50'
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ data = this.toJSON();
+
+ if (_.isUndefined(this.get('security_label')) ||
+ _.isNull(this.get('security_label')) ||
+ String(this.get('security_label')).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = '{{ _('Please provide the value for security label.') }}';
+ this.errorModel.set('security_label', errmsg);
+ return errmsg;
+ } else {
+ this.errorModel.unset('security_label');
+ }
+ return null;
+ }
+ });
+
+ // Composite type model declaration
+ var CompositeModel = Backform.CompositeModel = pgAdmin.Browser.Node.Model.extend({
+ idAttribute: 'attnum',
+ defaults: {
+ attnum: undefined,
+ member_name: undefined,
+ type: undefined,
+ tlength: undefined,
+ is_tlength: false,
+ precision: undefined,
+ is_precision: false,
+ collation: undefined,
+ min_val: undefined,
+ max_val: undefined,
+ },
+ type_options: undefined,
+ subtypes: undefined,
+ schema: [{
+ id: 'member_name', label: '{{ _('Member Name') }}',
+ type: 'text', disabled: false, editable: false
+ },{
+ id: 'type', label: '{{ _('Type') }}', control: 'node-ajax-options',
+ type: 'text', url: 'get_types', disabled: false, node: 'type',
+ editable: false,
+ transform: function(d){
+ this.model.type_options = d;
+ return d;
+ }
+ },{
+ id: 'tlength', label: '{{ _('Length') }}', deps: ['type'], type: 'text',
+ editable: false,
+ disabled: function(m) {
+ // We will store type from selected from combobox
+ var of_type = m.get('type');
+ if(m.type_options) {
+ // iterating over all the types
+ _.each(m.type_options, function(o) {
+ // if type from selected from combobox matches in options
+ if ( of_type == o.value ) {
+ // if length is allowed for selected type
+ if(o.length)
+ {
+ // set the values in model
+ m.set('is_tlength', true, {silent: true});
+ m.set('min_val', o.min_val, {silent: true});
+ m.set('max_val', o.max_val, {silent: true});
+ }
+ }
+ });
+ }
+ return !m.get('is_tlength');
+ }
+ },{
+ id: 'precision', label: '{{ _('Precision') }}', deps: ['type'],
+ type: 'text', editable: false,
+ disabled: function(m) {
+ // We will store type from selected from combobox
+ var of_type = m.get('type');
+ if(m.type_options) {
+ // iterating over all the types
+ _.each(m.type_options, function(o) {
+ // if type from selected from combobox matches in options
+ if ( of_type == o.value ) {
+ // if precession is allowed for selected type
+ if(o.precision)
+ {
+ // set the values in model
+ m.set('is_precision', true, {silent: true});
+ m.set('min_val', o.min_val, {silent: true});
+ m.set('max_val', o.max_val, {silent: true});
+ }
+ }
+ });
+ }
+ return !m.get('is_precision');
+ }
+ },{
+ id: 'collation', label: '{{ _('Collation') }}',
+ control: 'node-ajax-options', editable: false,
+ type: 'text', disabled: false, url: 'get_collations', node: 'type'
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null,
+ changedAttrs = this.sessAttrs;
+ // Clearing previous errors first.
+ this.errorModel.clear();
+ // Validation for member name
+ if ( _.has(changedAttrs, 'member_name') && _.isUndefined(this.get('member_name')) ||
+ _.isNull(this.get('member_name')) ||
+ String(this.get('member_name')).replace(/^\s+|\s+$/g, '') == '') {
+ errmsg = '{{ _('Please specify the value for member name.') }}';
+ this.errorModel.set('member_name', errmsg)
+ return errmsg;
+ }
+ // Validation for Length field
+ else if (_.has(changedAttrs, 'tlength') && this.get('is_tlength')
+ && !_.isUndefined(this.get('tlength'))) {
+ if (this.get('tlength') < this.get('min_val'))
+ errmsg = '{{ _('Length should not be less than ') }}' + this.get('min_val');
+ if (this.get('tlength') > this.get('max_val') )
+ errmsg = '{{ _('Length should not be greater than ') }}' + this.get('max_val');
+ // If we have any error set then throw it to user
+ if(errmsg) {
+ this.errorModel.set('tlength', errmsg)
+ return errmsg;
+ }
+ }
+ // Validation for precision field
+ else if (_.has(changedAttrs, 'precision') && this.get('is_precision')
+ && !_.isUndefined(this.get('precision'))) {
+ if (this.get('precision') < this.get('min_val'))
+ errmsg = '{{ _('Precision should not be less than ') }}' + this.get('min_val');
+ if (this.get('precision') > this.get('max_val'))
+ errmsg = '{{ _('Precision should not be greater than ') }}' + this.get('max_val');
+ // If we have any error set then throw it to user
+ if(errmsg) {
+ this.errorModel.set('precision', errmsg)
+ return errmsg;
+ }
+ }
+ return null;
+ }
+ });
+
+ var EnumModel = Backform.EnumModel = pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ label: undefined,
+ },
+ schema: [{
+ id: 'label', label: '{{ _('Label') }}',type: 'text', disabled: false,
+ cellHeaderClasses: 'width_percent_99', editable: function(m) {
+ return _.isUndefined(m.get('label'));
+ }
+ }],
+ validate: function() {
+ var err = {},
+ errmsg = null;
+
+ if (_.isUndefined(this.get('label') ||
+ _.isNull(this.get('label')) ||
+ String(this.get('label')).replace(/^\s+|\s+$/g, '') == '')) {
+ errmsg = '{{ _('Please specify the value for label.') }}';
+ this.errorModel.set('label', errmsg)
+ return errmsg;
+ } else {
+ this.errorModel.unset('label');
+ }
+ return null;
+ }
+ });
+
+ if (!pgBrowser.Nodes['type']) {
+ pgAdmin.Browser.Nodes['type'] = pgBrowser.Node.extend({
+ type: 'type',
+ label: '{{ _('Type') }}',
+ collection_type: 'coll-type',
+ hasSQL: true,
+ hasDepends: true,
+ parent_type: ['schema', 'catalog'],
+ Init: function() {
+ /* Avoid mulitple registration of menus */
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+
+ pgBrowser.add_menus([{
+ name: 'create_type_on_coll', node: 'coll-type', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_type', node: 'type', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_type', node: 'schema', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 4, label: '{{ _('Type...') }}',
+ icon: 'wcTabIcon icon-type', data: {action: 'create', check: false},
+ enable: 'canCreate'
+ }
+ ]);
+
+ },
+ canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+ canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+ ext_funcs: undefined,
+ model: pgAdmin.Browser.Node.Model.extend({
+ defaults: {
+ name: undefined,
+ oid: undefined,
+ is_sys_type: false,
+ typtype: undefined
+ },
+
+ // Default values!
+ initialize: function(attrs, args) {
+ var isNew = (_.size(attrs) === 0);
+
+ if (isNew) {
+ var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
+ var schemaInfo = args.node_info.schema;
+
+ this.set({'typeowner': userInfo.name}, {silent: true});
+ this.set({'schema': schemaInfo.label}, {silent: true});
+ }
+ pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+ },
+
+ schema: [{
+ id: 'name', label: '{{ _('Name') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchema'
+ },{
+ id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+ type: 'text' , mode: ['properties'], disabled: true
+ },{
+ id: 'typeowner', label:'{{ _('Owner') }}', cell: 'string',
+ control: 'node-list-by-name',
+ type: 'text', mode: ['properties', 'create', 'edit'], node: 'role',
+ disabled: 'inSchema'
+ },{
+ id: 'schema', label:'{{ _('Schema') }}', cell: 'string',
+ type: 'text', mode: ['create', 'edit'], node: 'schema',
+ disabled: 'inSchema', filter: function(d) {
+ // If schema name start with pg_* then we need to exclude them
+ if(d && d.label.match(/^pg_/))
+ {
+ return false;
+ }
+ return true;
+ },
+ control: Backform.NodeListByNameControl.extend({
+ render: function(){
+ // Initialize parent's render method
+ Backform.NodeListByNameControl.prototype.render.apply(this, arguments);
+
+ // Set schema default value to its parent Schema
+ if(this.model.isNew()){
+ this.model.set({'schema': this.model.node_info.schema.label});
+ }
+ return this;
+ }
+ })
+ },{
+ id: 'typtype', label:'{{ _('Type') }}',
+ mode: ['create','edit'], disabled: 'inSchemaWithModelCheck',
+ group: '{{ _('Definition') }}',
+ mode: ['edit', 'create'],
+ select2: { width: "50%" },
+ options: function() {
+ if(!this.model.isNew()) {
+ return [
+ {label: "Composite", value: "c"},
+ {label: "Enumeration", value: "e"},
+ {label: "External", value: "b"},
+ {label: "Range", value: "r"},
+ ]
+ } 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({
+ render: function(){
+ // Initialize parent's render method
+ Backform.Select2Control.prototype.render.apply(this, arguments);
+ if(this.model.isNew()) {
+ this.model.set({'typtype': 'c'});
+ }
+ return this;
+ }
+ })
+ },{
+ id: 'composite', label: '{{ _('Composite Type') }}',
+ model: CompositeModel, editable: true, type: 'collection',
+ group: '{{ _('Definition') }}', mode: ['edit', 'create'],
+ control: 'unique-col-collection', uniqueCol : ['member_name'],
+ canAdd: true, canEdit: true, canDelete: true, disabled: 'inSchema',
+ deps: ['typtype'], deps: ['typtype'],
+ visible: function(m) {
+ if (m.get('typtype') === 'c') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'enum', label: '{{ _('Enumeration Type') }}',
+ model: EnumModel, editable: true, type: 'collection',
+ group: '{{ _('Definition') }}', mode: ['edit', 'create'],
+ canAdd: true, canEdit: false, canDelete: function(m) {
+ // We will disable it if it's in 'edit' mode
+ if (m.isNew()) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ disabled: 'inSchema', deps: ['typtype'],
+ control: 'unique-col-collection', uniqueCol : ['label'],
+ visible: function(m) {
+ return m.get('typtype') === 'e';
+ }
+ },{
+ // We will disable range type control in edit mode
+ type: 'nested', control: 'plain-fieldset', group: '{{ _('Definition') }}',
+ mode: ['edit', 'create'],
+ visible: function(m) {
+ return m.get('typtype') === 'r';
+ }, deps: ['typtype'], label: '{{ _('') }}',
+ schema:[{
+ id: 'typname', label:'{{ _('Sub-type') }}', cell: 'string',
+ control: 'node-ajax-options',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ url: 'get_stypes', type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}', disabled: 'inSchemaWithModelCheck',
+ transform: function(d){
+ this.model.subtypes = d;
+ return d;
+ }
+ },{
+ id: 'opcname', label:'{{ _('Sub-type operator class') }}', cell: 'string',
+ mode: ['properties', 'create', 'edit'], group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['typname'],
+ control: 'select', options: function() {
+ var l_typname = this.model.get('typname'),
+ self = this,
+ result = [];
+ if(!_.isUndefined(l_typname) && l_typname != '')
+ {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_subopclass', this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {'typname' : l_typname},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error', self.model, self.field);
+ }
+ });
+ //
+ }
+ return result;
+ }
+ },{
+ id: 'collname', label:'{{ _('Collation') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ deps: ['typname'], control: 'node-ajax-options', url: 'get_collations',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ disabled: function(m) {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+
+ // Disbale in edit mode
+ if (!m.isNew()) {
+ return true;
+ }
+
+ // To check if collation is allowed?
+ var of_subtype = m.get('typname'),
+ is_collate = undefined;
+ if(!_.isUndefined(of_subtype)) {
+ // iterating over all the types
+ _.each(m.subtypes, function(s) {
+ // if subtype from selected from combobox matches
+ if ( of_subtype === s.label ) {
+ // if collation is allowed for selected subtype
+ // then enable it else disable it
+ is_collate = s.is_collate;
+ }
+ });
+ }
+ // If is_collate is true then do not disable
+ return is_collate ? false : true;
+ }
+ },{
+ id: 'rngcanonical', label:'{{ _('Canonical function') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['name', 'typname'],
+ control: 'select', options: function() {
+ var name = this.model.get('name'),
+ self = this,
+ result = [];
+
+ if(!_.isUndefined(name) && name != '')
+ {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_canonical', this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {"name" : name},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error',
+ self.model, self.field);
+ }
+ });
+ }
+ return result;
+ }
+ },{
+ id: 'rngsubdiff', label:'{{ _('Sub-type diff function') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: '{{ _('Range Type') }}',
+ disabled: 'inSchemaWithModelCheck', deps: ['opcname'],
+ control: 'select', options: function() {
+ var l_typname = this.model.get('typname'),
+ l_opcname = this.model.get('opcname'),
+ self = this,
+ result = [];
+
+ if(!_.isUndefined(l_typname) && l_typname != '' &&
+ !_.isUndefined(l_opcname) && l_opcname != '') {
+ var node = this.field.get('schema_node'),
+ _url = node.generate_url.apply(
+ node, [
+ null, 'get_stypediff',
+ this.field.get('node_data'), false,
+ this.field.get('node_info')
+ ]);
+ $.ajax({
+ async: false,
+ url: _url,
+ cache: false,
+ data: {'typname' : l_typname, 'opcname': l_opcname},
+ success: function(res) {
+ result = res.data;
+ },
+ error: function() {
+ self.model.trigger('pgadmin:view:fetch:error',
+ self.model, self.field);
+ }
+ });
+ }
+ return result;
+ }
+ }]
+ },{
+ type: 'nested', control: 'tab', group: '{{ _('Definition') }}',
+ label: '{{ _('External Type') }}', deps: ['typtype'],
+ mode: ['edit'],
+ visible: function(m) {
+ return m.get('typtype') === 'b';
+ },
+ schema:[{
+ id: 'typinput', label:'{{ _('Input function') }}',
+ cell: 'string',type: 'text',
+ mode: ['properties', 'create', 'edit'], group: 'Required',
+ disabled: 'inSchemaWithModelCheck',
+ control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typoutput', label:'{{ _('Output function') }}',
+ cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Required',
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typreceive', label:'{{ _('Receive function') }}',
+ cell: 'string', type: 'text', group: 'Optional-1',
+ mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typsend', label:'{{ _('Send function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typmodin', label:'{{ _('Typmod in function') }}',
+ cell: 'string', type: 'text',
+ mode: ['properties', 'create', 'edit'], group: 'Optional-1',
+ disabled: 'inSchemaWithModelCheck',
+ control: 'node-ajax-options', url: 'get_external_functions',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ transform: function(d) {
+ var result = [{label :"", value : ""}];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype === 'typmodin' || item.cbtype === 'all') {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ },{
+ id: 'typmodout', label:'{{ _('Typmod out function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck',
+ control: 'node-ajax-options', url: 'get_external_functions',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ transform: function(d) {
+ var result = [{label :"", value : ""}];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype === 'typmodout' || item.cbtype === 'all') {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ },{
+ id: 'typlen', label:'{{ _('Internal length') }}',
+ cell: 'integer', group: 'Optional-1',
+ type: 'int', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'variable', label:'{{ _('Variable?') }}', cell: 'switch',
+ group: 'Optional-1', type: 'switch',
+ mode: ['create','edit'], options: switchOptions,
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typdefault', label:'{{ _('Default?') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typanalyze', label:'{{ _('Analyze function') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck'
+ ,control: 'node-ajax-options', url: 'get_external_functions',
+ transform: 'external_func_combo',
+ select2: { allowClear: true, placeholder: "", width: "100%" }
+ },{
+ id: 'typcategory', label:'{{ _('Category type') }}',
+ cell: 'string', group: 'Optional-1',
+ type: 'text', mode: ['properties', 'create','edit'],
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label :"Array types", value : "A"},
+ {label :"Boolean types", value : "B"},
+ {label :"Composite types", value : "C"},
+ {label :"Date/time types", value : "D"},
+ {label :"Enum types", value : "E"},
+ {label :"Geometric types", value : "G"},
+ {label :"Network address types", value : "I"},
+ {label :"Numeric types", value : "N"},
+ {label :"Pseudo-types", value : "P"},
+ {label :"String types", value : "S"},
+ {label :"Timespan types", value : "T"},
+ {label :"User-defined types", value : "U"},
+ {label :"Bit-string types", value : "V"},
+ {label :"unknown type", value : "X"}
+ ]
+ },{
+ id: 'typispreferred', label:'{{ _('Preferred?') }}', cell: 'switch',
+ type: 'switch', mode: ['properties', 'create','edit'],
+ options: switchOptions, disabled: 'inSchemaWithModelCheck',
+ group: 'Optional-1'
+ },{
+ id: 'element', label:'{{ _('Element type') }}', cell: 'string',
+ control: 'node-ajax-options', group: 'Optional-2',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', url: 'get_types'
+ },{
+ id: 'typdelim', label:'{{ _('Delimiter') }}', cell: 'string',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Optional-2', disabled: 'inSchemaWithModelCheck'
+ },{
+ id: 'typalign', label:'{{ _('Alignment type') }}',
+ cell: 'string', group: 'Optional-2',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label: "char", value: "c"},
+ {label: "int2", value: "s"},
+ {label: "int4", value: "i"},
+ {label: "double", value: "d"},
+ ]
+ },{
+ id: 'typstorage', label:'{{ _('Storage type') }}',
+ type: 'text', mode: ['properties', 'create', 'edit'],
+ group: 'Optional-2', cell: 'string',
+ disabled: 'inSchemaWithModelCheck', control: 'select2',
+ select2: { allowClear: true, placeholder: "", width: "100%" },
+ options: [
+ {label :"", value : ""},
+ {label: "PLAIN", value: "p"},
+ {label: "EXTERNAL", value: "e"},
+ {label: "MAIN", value: "m"},
+ {label: "EXTENDED", value: "x"},
+ ]
+ },{
+ id: 'typbyval', label:'{{ _('Passed by value?') }}',
+ cell: 'switch', options: switchOptions,
+ type: 'switch', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchemaWithModelCheck', group: 'Optional-2',
+ },{
+ id: 'is_collatable', label:'{{ _('Collatable?') }}',
+ cell: 'switch', min_version: 90100, group: 'Optional-2',
+ type: 'switch', mode: ['properties', 'create', 'edit'],
+ options: switchOptions, disabled: 'inSchemaWithModelCheck'
+ // End of extension tab
+ }]
+ },{
+ id: 'alias', label:'{{ _('Alias') }}', cell: 'string',
+ type: 'text', mode: ['properties'],
+ disabled: 'inSchema'
+ },{
+ id: 'type_acl', label:'{{ _('Privileges') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Security') }}',
+ disabled: 'inSchema'
+ },{
+ id: 'member_list', label:'{{ _('Members') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Definition') }}',
+ disabled: 'inSchema', visible: function(m) {
+ if(m.get('typtype') === 'c') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'enum_list', label:'{{ _('Labels') }}', cell: 'string',
+ type: 'text', mode: ['properties'], group: '{{ _('Definition') }}',
+ disabled: 'inSchema', visible: function(m) {
+ if(m.get('typtype') === 'e') {
+ return true;
+ }
+ return false;
+ }
+ },{
+ id: 'is_sys_type', label:'{{ _('System type?') }}', cell: 'switch',
+ type: 'switch', mode: ['properties'], options: switchOptions,
+ disabled: 'inSchema'
+ },{
+ id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+ type: 'multiline', mode: ['properties', 'create', 'edit'],
+ disabled: 'inSchema'
+ },{
+ 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']
+ },{
+ 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'
+ }],
+ validate: function() {
+ // Validation code for required fields
+ var changedAttrs = this.sessAttrs,
+ msg = undefined;
+
+ this.errorModel.clear();
+
+ if (_.has(changedAttrs, 'name') &&
+ (_.isUndefined(this.get('name'))
+ || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Name can not be empty!') }}';
+ this.errorModel.set('name', msg);
+ } else if (_.has(changedAttrs, 'schema') &&
+ (_.isUndefined(this.get('schema'))
+ || String(this.get('schema')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Schema can not be empty!') }}';
+ this.errorModel.set('schema', msg);
+ } else if (_.has(changedAttrs, 'typtype') &&
+ (_.isUndefined(this.get('typtype'))
+ || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Type can not be empty!') }}';
+ this.errorModel.set('typtype', msg);
+ } else if (this.get('typtype') == 'r' &&
+ _.has(changedAttrs, 'typname')
+ && (_.isUndefined(this.get('typname'))
+ || String(this.get('typname')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Subtype Name can not be empty!') }}';
+ this.errorModel.set('typname', msg);
+ } else if (this.get('typtype') == 'x' &&
+ _.has(changedAttrs, 'typinput')
+ && (_.isUndefined(this.get('typinput'))
+ || String(this.get('typinput')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Input function can not be empty!') }}';
+ this.errorModel.set('typinput', msg);
+ } else if (this.get('typtype') == 'x' &&
+ _.has(changedAttrs, 'typoutput')
+ && (_.isUndefined(this.get('typoutput'))
+ || String(this.get('typoutput')).replace(/^\s+|\s+$/g, '') == '')) {
+ msg = '{{ _('Output function can not be empty!') }}';
+ this.errorModel.set('typoutput', msg);
+ }
+ return null;
+ },
+ // We will disable everything if we are under catalog node
+ inSchema: function() {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+ return false;
+ },
+ // We will check if we are under schema node & in 'create' mode
+ inSchemaWithModelCheck: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ // We will disbale control if it's in 'edit' mode
+ if (m.isNew()) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+ return true;
+ },
+ // We want to enable only in edit mode
+ inSchemaWithEditMode: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ // We will disbale control if it's in 'edit' mode
+ if (m.isNew()) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+ return true;
+ },
+ // Function will help us to fill combobox
+ external_func_combo: function(d) {
+ var result = [];
+ _.each(d, function(item) {
+ // if type from selected from combobox matches in options
+ if ( item.cbtype == 'all' ) {
+ result.push(item);
+ }
+ });
+ return result;
+ }
+ }),
+ canCreate: function(itemData, item, data) {
+ //If check is false then , we will allow create menu
+ if (data && data.check == false)
+ return true;
+
+ var t = pgBrowser.tree, i = item, d = itemData;
+ // To iterate over tree to check parent node
+ while (i) {
+ // If it is schema then allow user to create table
+ if (_.indexOf(['schema'], d._type) > -1)
+ return true;
+
+ if ('coll-type' == d._type) {
+ //Check if we are not child of catalog
+ prev_i = t.hasParent(i) ? t.parent(i) : null;
+ prev_d = prev_i ? t.itemData(prev_i) : null;
+ if( prev_d._type == 'catalog') {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ i = t.hasParent(i) ? t.parent(i) : null;
+ d = i ? t.itemData(i) : null;
+ }
+ // by default we do not want to allow create menu
+ return true;
+ }
+ });
+ }
+ return pgBrowser.Nodes['type'];
+});
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql
new file mode 100644
index 0000000..60eab25
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/acl.sql
@@ -0,0 +1,26 @@
+SELECT 'typacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'USAGE' THEN 'U'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT t.typacl
+ FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 %}
+ ) acl,
+ (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable
+ AS is_grantable, (d).privilege_type AS privilege_type FROM (SELECT
+ aclexplode(t.typacl) as d FROM pg_type t WHERE t.oid = {{tid}}::oid) a) d
+ ) d
+ LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+ LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY g.rolname, gt.rolname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql
new file mode 100644
index 0000000..172f6bb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/additional_properties.sql
@@ -0,0 +1,35 @@
+{# The SQL given below will fetch composite type#}
+{% if type == 'c' %}
+SELECT attnum, attname, format_type(t.oid,NULL) AS typname, attndims, atttypmod, nsp.nspname,
+ (SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup,
+ collname, nspc.nspname as collnspname, att.attrelid
+FROM pg_attribute att
+ JOIN pg_type t ON t.oid=atttypid
+ JOIN pg_namespace nsp ON t.typnamespace=nsp.oid
+ LEFT OUTER JOIN pg_type b ON t.typelem=b.oid
+ LEFT OUTER JOIN pg_collation c ON att.attcollation=c.oid
+ LEFT OUTER JOIN 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 type == 'e' %}
+SELECT enumlabel
+FROM pg_enum
+ WHERE enumtypid={{tid}}::oid
+ ORDER by enumsortorder
+{% endif %}
+
+{# The SQL given below will fetch range type#}
+{% if type == 'r' %}
+SELECT rngsubtype, st.typname,
+ rngcollation, col.collname,
+ rngsubopc, opc.opcname,
+ rngcanonical, rngsubdiff
+FROM pg_range
+ LEFT JOIN pg_type st ON st.oid=rngsubtype
+ LEFT JOIN pg_collation col ON col.oid=rngcollation
+ LEFT JOIN pg_opclass opc ON opc.oid=rngsubopc
+ WHERE rngtypid={{tid}}::oid;
+{% endif %}
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
new file mode 100644
index 0000000..85274b6
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/create.sql
@@ -0,0 +1,79 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{### 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 %});
+{% 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 }}{% 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 %}
+
+ );
+{% 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.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}}{% endif %}{% if data.typispreferred %},
+ PREFERRED = {{data.typispreferred}}{% endif %}{% if data.typdefault %},
+ DEFAULT = {{data.typdefault|qtLiteral}}{% endif %}{% if data.element %},
+ ELEMENT = {{data.element}}{% endif %}{% if data.typdelim %},
+ DELIMITER = {{data.typdelim|qtLiteral}}{% 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 {{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}};
+{% 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.security_label %}
+{{ SECLABLE.SET(conn, 'TYPE', data.name, r.provider, r.security_label, data.schema) }}
+{% endif %}
+{% endfor %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..c258827
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/delete.sql
@@ -0,0 +1 @@
+DROP TYPE {{ conn|qtIdent(data.schema, data.name) }}{% if cascade%} CASCADE{% 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_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_collations.sql
new file mode 100644
index 0000000..4b0169b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_collations.sql
@@ -0,0 +1,7 @@
+SELECT --nspname, collname,
+ CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(collname))
+ ELSE '' END AS collation
+FROM pg_collation c, pg_namespace n
+WHERE c.collnamespace=n.oid
+ORDER BY nspname, collname;
\ 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
new file mode 100644
index 0000000..86648a2
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_external_functions.sql
@@ -0,0 +1,40 @@
+{### 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
+ concat(quote_ident(nspname), '.', 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;
+{% endif %}
+{### TypmodIN list ###}
+{% if typemodin %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='_cstring')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
+{### TypmodOUT list ###}
+{% if typemodout %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS func
+FROM pg_proc p
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype=(SELECT oid FROM pg_type WHERE typname='cstring')
+ AND proargtypes[0]=(SELECT oid FROM pg_type WHERE typname='int4')
+ AND proargtypes[1] IS NULL
+ORDER BY nspname, proname;
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..14f7950
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,11 @@
+{# Below will provide oid for newly created type #}
+SELECT t.oid
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN 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 data %}
+ AND t.typname = {{data.name|qtLiteral}}
+{% endif %}
+ORDER BY t.typname;
\ 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_subtypes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_subtypes.sql
new file mode 100644
index 0000000..75271fe
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_subtypes.sql
@@ -0,0 +1,56 @@
+{### To fill subtype combobox ###}
+{% if subtype %}
+SELECT DISTINCT typ.typname AS stype,
+ (CASE WHEN typ.typcollation > 0 THEN true ELSE false END) AS is_collate
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype = typ.oid
+WHERE opc.opcmethod = 403
+ORDER BY 1
+{% endif %}
+{### To fill subtype opclass combobox ###}
+{% if subtype_opclass and data and data.typname %}
+SELECT opc.opcname
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ORDER BY opcname;
+{% endif %}
+{### To fetch opcinttype from subtype opclass ###}
+{% if get_opcintype and data and data.typname and data.opcname %}
+SELECT opc.opcintype
+FROM pg_opclass opc
+ JOIN pg_type typ ON opc.opcintype=typ.oid
+ AND typ.typname = {{ data.typname|qtLiteral }}
+WHERE opc.opcmethod = 403
+ AND opc.opcname = {{ data.opcname|qtLiteral }}
+ORDER BY opcname;
+{% endif %}
+{### To fill subtype diff function combobox ###}
+{% if opcintype %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS stypdiff
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype = 701
+ AND proargtypes = '{{opcintype}} {{opcintype}}'
+ORDER BY proname;
+{% endif %}
+{### To fill canonical combobox ###}
+{% if getoid %}
+SELECT oid FROM pg_type
+WHERE typname = {{ data.name|qtLiteral }}
+{% endif %}
+{% if canonical and oid %}
+SELECT proname, nspname,
+ CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
+ concat(quote_ident(nspname), '.', quote_ident(proname))
+ ELSE '' END AS canonical
+FROM pg_proc
+ JOIN pg_namespace n ON n.oid=pronamespace
+WHERE prorettype= {{ oid }}
+ AND proargtypes = '{{ oid }}'
+ORDER BY proname;
+{% 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_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_types.sql
new file mode 100644
index 0000000..a5d352d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/get_types.sql
@@ -0,0 +1,10 @@
+SELECT * FROM
+ (SELECT format_type(t.oid,NULL) AS typname,
+ CASE WHEN typelem > 0 THEN typelem ELSE t.oid END AS elemoid,
+ typlen, typtype, t.oid, nspname,
+ (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+FROM pg_type t
+ JOIN pg_namespace nsp ON typnamespace=nsp.oid
+WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r')AND NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) AND nsp.nspname != 'information_schema'
+ ) AS dummy
+ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..6abcb19
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/nodes.sql
@@ -0,0 +1,10 @@
+SELECT t.oid, t.typname AS name
+FROM pg_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN pg_namespace nsp ON nsp.oid = t.typnamespace
+WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' AND t.typnamespace = {{scid}}::oid
+{% if not show_system_objects %}
+ AND ct.oid is NULL
+{% endif %}
+ORDER BY t.typname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..de261a5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/properties.sql
@@ -0,0 +1,24 @@
+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.*, format_type(t.oid, null) AS alias,
+ 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 array_agg(provider || '=' || label) FROM pg_shseclabel 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_type t
+ LEFT OUTER JOIN pg_type e ON e.oid=t.typelem
+ LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c'
+ LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_type'::regclass)
+ LEFT OUTER JOIN 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;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..170b03a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
@@ -0,0 +1,139 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{#======================================#}
+{# 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 {{ data.typeowner }};
+
+{% endif %}
+{#======================================#}
+{# Below will change objects comment #}
+{% if data.description and data.description != o_data.description %}
+COMMENT ON TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ IS {{ data.description|qtLiteral }};
+
+{% 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)}} {{conn|qtTypeIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% 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 %}
+{% if o.attnum == r.attnum and 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}};
+{% if r.type and o.type != r.type %}
+ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ ALTER ATTRIBUTE {{conn|qtIdent(r.member_name)}} SET DATA TYPE {{conn|qtTypeIdent(r.type)}}{% if r.is_tlength and r.tlength %}
+({{r.tlength}}{% if r.is_precision and r.precision %},{{r.precision}}{% endif %}){% endif %}{% if r.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% else %}
+ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ ALTER ATTRIBUTE {{conn|qtIdent(r.member_name)}} SET DATA TYPE {{conn|qtTypeIdent(o.type)}}{% if o.is_tlength and o.tlength %}
+({{o.tlength}}{% if o.is_precision and o.precision %},{{o.precision}}{% endif %}){% endif %}{% if o.collation %}
+ COLLATE {{r.collation}}{% endif %};
+{% 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}} AFTER {{o_data.enum[o_enum_len].label|qtLiteral }};
+{% 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}} AFTER {{enum.added[p_idx].label|qtLiteral}};
+{% endif %}
+{% endfor %}
+{% 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 %}
+{{ SECLABLE.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 %}
+{{ SECLABLE.SET(conn, 'TYPE', o_data.name, r.provider, r.security_label, o_data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.SET(conn, 'TYPE', o_data.name, r.provider, r.security_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 %}
+{{ PRIVILEGE.UNSETALL(conn, 'TYPE', priv.grantee, o_data.name, o_data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TYPE', priv.grantee, 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, 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 != 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 %}
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt
new file mode 100644
index 0000000..631037f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/postgres_inbuit_types.txt
@@ -0,0 +1,53 @@
+"""
+Here is the list of Postgres inbuilt types & their OID's
+We will use these types to check for validations
+
+## PGOID_TYPE_SERIAL -42L
+## PGOID_TYPE_SERIAL8 -43L
+## PGOID_TYPE_SERIAL2 -44L
+## PGOID_TYPE_BOOL 16L
+## PGOID_TYPE_BYTEA 17L
+## PGOID_TYPE_CHAR 18L
+## PGOID_TYPE_NAME 19L
+## PGOID_TYPE_INT8 20L
+## PGOID_TYPE_INT2 21L
+## PGOID_TYPE_INT4 23L
+## PGOID_TYPE_TEXT 25L
+## PGOID_TYPE_OID 26L
+## PGOID_TYPE_TID 27L
+## PGOID_TYPE_XID 28L
+## PGOID_TYPE_CID 29L
+## PGOID_TYPE_FLOAT4 700L
+## PGOID_TYPE_FLOAT8 701L
+## PGOID_TYPE_MONEY 790L
+## PGOID_TYPE_CHAR_ARRAY 1002L
+## PGOID_TYPE_TEXT_ARRAY 1009L
+## PGOID_TYPE_BPCHAR_ARRAY 1014L
+## PGOID_TYPE_VARCHAR_ARRAY 1015L
+## PGOID_TYPE_BPCHAR 1042L
+## PGOID_TYPE_VARCHAR 1043L
+## PGOID_TYPE_DATE 1082L
+## PGOID_TYPE_TIME 1083L
+## PGOID_TYPE_TIMESTAMP 1114L
+## PGOID_TYPE_TIMESTAMP_ARRAY 1115L
+## PGOID_TYPE_TIME_ARRAY 1183L
+## PGOID_TYPE_TIMESTAMPTZ 1184L
+## PGOID_TYPE_TIMESTAMPTZ_ARRAY 1185L
+## PGOID_TYPE_INTERVAL 1186L
+## PGOID_TYPE_INTERVAL_ARRAY 1187L
+## PGOID_TYPE_NUMERIC_ARRAY 1231L
+## PGOID_TYPE_TIMETZ 1266L
+## PGOID_TYPE_TIMETZ_ARRAY 1270L
+## PGOID_TYPE_BIT 1560L
+## PGOID_TYPE_BIT_ARRAY 1561L
+## PGOID_TYPE_VARBIT 1562L
+## PGOID_TYPE_VARBIT_ARRAY 1563L
+## PGOID_TYPE_NUMERIC 1700L
+## PGOID_TYPE_CSTRING 2275L
+## PGOID_TYPE_ANY 2276L
+## PGOID_TYPE_VOID 2278L
+## PGOID_TYPE_TRIGGER 2279L
+## PGOID_TYPE_LANGUAGE_HANDLER 2280L
+## PGOID_TYPE_INTERNAL 2281L
+## PGOID_TYPE_HANDLER 3115L
+"""
\ No newline at end of file
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-18 07:15 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-18 16:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-22 08:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
@ 2016-03-22 09:22 ` Dave Page <[email protected]>
2016-04-07 11:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
0 siblings, 1 reply; 26+ messages in thread
From: Dave Page @ 2016-03-22 09:22 UTC (permalink / raw)
To: Murtuza Zabuawala <[email protected]>; +Cc: pgadmin-hackers
Hi
On Tue, Mar 22, 2016 at 8:14 AM, Murtuza Zabuawala
<[email protected]> wrote:
> Hi Dave,
>
> We can create new external type using below method, By running all of below
> queries at the same time , we can not create separate external type by only
> using create type statement.
>
> So as per my discussion with Ashesh, We should not allow user to create
> external type in pgAdmin4 but only show definition in edit mode.
Hmm, would it not make sense to allow the user to create the shell
type as well (perhaps, with a new type of "SHELL")? Then they could do
what is needed (and that should be easy, as it's just CREATE TYPE
foo;)
For example:
CREATE TYPE box;
CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ;
CREATE TYPE box (
INTERNALLENGTH = 16,
INPUT = my_box_in_function,
OUTPUT = my_box_out_function
);
CREATE TABLE myboxes (
id integer,
description box
);
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-18 07:15 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-18 16:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-22 08:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-22 09:22 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
@ 2016-04-07 11:36 ` Dave Page <[email protected]>
2016-04-07 11:41 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-04-08 13:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
0 siblings, 2 replies; 26+ messages in thread
From: Dave Page @ 2016-04-07 11:36 UTC (permalink / raw)
To: Murtuza Zabuawala <[email protected]>; +Cc: pgadmin-hackers
Hi Murtuza
On Tue, Mar 22, 2016 at 9:22 AM, Dave Page <[email protected]> wrote:
> Hi
>
> On Tue, Mar 22, 2016 at 8:14 AM, Murtuza Zabuawala
> <[email protected]> wrote:
>> Hi Dave,
>>
>> We can create new external type using below method, By running all of below
>> queries at the same time , we can not create separate external type by only
>> using create type statement.
>>
>> So as per my discussion with Ashesh, We should not allow user to create
>> external type in pgAdmin4 but only show definition in edit mode.
>
> Hmm, would it not make sense to allow the user to create the shell
> type as well (perhaps, with a new type of "SHELL")? Then they could do
> what is needed (and that should be easy, as it's just CREATE TYPE
> foo;)
>
> For example:
>
> CREATE TYPE box;
>
> CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
> CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ;
>
> CREATE TYPE box (
> INTERNALLENGTH = 16,
> INPUT = my_box_in_function,
> OUTPUT = my_box_out_function
> );
>
> CREATE TABLE myboxes (
> id integer,
> description box
> );
In the interests of making progress, I've committed the most recent
patch, with a number of minor changes most significantly, the Postgres
docs and system catalogs seem to have different ideas about what to
call length, precision and scale. pgAdmin 3 followed the catalogs and
used length and precision, however I've updated pgAdmin 4 to use
"Length/precision" and "Scale" which is inline with the Postgres docs.
That's only in the UI though - the code follows the catalogs.
There are still a couple of issues - please provide fixes ASAP:
1) If you create a composite type that contains a sized type (e.g.
numeric(5, 4), the precision and scale are not shown if you later open
the properties dialogue, or in the reverse engineered SQL.
E.g. what pgAdmin3 shows as:
CREATE TYPE pem.blergh AS
(c1 text COLLATE pg_catalog."C",
c2 numeric(5));
Is shown by pgAdmin4 as:
CREATE TYPE pem.blergh AS
(c1 text COLLATE pg_catalog."C", c2 numeric);
(adding the \n's would be good too).
2) If you select a different type of type in create mode, the new
options are shown below those for the previously selected type,
instead of replacing them. Please see the attached screenshot.
3) I would still like us to support External types. I believe the
simple option here is to re-add the code you had previously, and to
add a new type of type called "SHELL" as discussed in my previous
email above. The user would then be able to create a SHELL type, add
the required functions, then come back and create the EXTERNAL type.
I'll add cards to our internal kanban chart for these issues.
Thanks.
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[image/png] Screen Shot 2016-04-07 at 12.33.11.png (52.6K, 2-Screen%20Shot%202016-04-07%20at%2012.33.11.png)
download | view image
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-18 07:15 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-18 16:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-22 08:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-22 09:22 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-07 11:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
@ 2016-04-07 11:41 ` Murtuza Zabuawala <[email protected]>
1 sibling, 0 replies; 26+ messages in thread
From: Murtuza Zabuawala @ 2016-04-07 11:41 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Thanks Dave.
Sure will look in it ASAP.
Regards,
Murtuza
> On 07-Apr-2016, at 5:06 pm, Dave Page <[email protected]> wrote:
>
> Hi Murtuza
>
> On Tue, Mar 22, 2016 at 9:22 AM, Dave Page <[email protected]> wrote:
>> Hi
>>
>> On Tue, Mar 22, 2016 at 8:14 AM, Murtuza Zabuawala
>> <[email protected]> wrote:
>>> Hi Dave,
>>>
>>> We can create new external type using below method, By running all of below
>>> queries at the same time , we can not create separate external type by only
>>> using create type statement.
>>>
>>> So as per my discussion with Ashesh, We should not allow user to create
>>> external type in pgAdmin4 but only show definition in edit mode.
>>
>> Hmm, would it not make sense to allow the user to create the shell
>> type as well (perhaps, with a new type of "SHELL")? Then they could do
>> what is needed (and that should be easy, as it's just CREATE TYPE
>> foo;)
>>
>> For example:
>>
>> CREATE TYPE box;
>>
>> CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
>> CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ;
>>
>> CREATE TYPE box (
>> INTERNALLENGTH = 16,
>> INPUT = my_box_in_function,
>> OUTPUT = my_box_out_function
>> );
>>
>> CREATE TABLE myboxes (
>> id integer,
>> description box
>> );
>
> In the interests of making progress, I've committed the most recent
> patch, with a number of minor changes most significantly, the Postgres
> docs and system catalogs seem to have different ideas about what to
> call length, precision and scale. pgAdmin 3 followed the catalogs and
> used length and precision, however I've updated pgAdmin 4 to use
> "Length/precision" and "Scale" which is inline with the Postgres docs.
> That's only in the UI though - the code follows the catalogs.
>
> There are still a couple of issues - please provide fixes ASAP:
>
> 1) If you create a composite type that contains a sized type (e.g.
> numeric(5, 4), the precision and scale are not shown if you later open
> the properties dialogue, or in the reverse engineered SQL.
>
> E.g. what pgAdmin3 shows as:
>
> CREATE TYPE pem.blergh AS
> (c1 text COLLATE pg_catalog."C",
> c2 numeric(5));
>
> Is shown by pgAdmin4 as:
>
> CREATE TYPE pem.blergh AS
> (c1 text COLLATE pg_catalog."C", c2 numeric);
>
> (adding the \n's would be good too).
>
> 2) If you select a different type of type in create mode, the new
> options are shown below those for the previously selected type,
> instead of replacing them. Please see the attached screenshot.
>
> 3) I would still like us to support External types. I believe the
> simple option here is to re-add the code you had previously, and to
> add a new type of type called "SHELL" as discussed in my previous
> email above. The user would then be able to create a SHELL type, add
> the required functions, then come back and create the EXTERNAL type.
>
> I'll add cards to our internal kanban chart for these issues.
>
> Thanks.
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
> <Screen Shot 2016-04-07 at 12.33.11.png>
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-18 07:15 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-18 16:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-22 08:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-22 09:22 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-07 11:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
@ 2016-04-08 13:52 ` Murtuza Zabuawala <[email protected]>
2016-04-13 12:48 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
1 sibling, 1 reply; 26+ messages in thread
From: Murtuza Zabuawala @ 2016-04-08 13:52 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi Dave,
Please find updated patches to fix all the mentioned issues.
There are three patches,
1) One for fixing rendering issue when selecting type
2) Added utility function to fetch full type name with length & precision
3) Added new Shell type for External types.
Regards,
Murtuza
--
Regards,
Murtuza Zabuawala
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Thu, Apr 7, 2016 at 5:06 PM, Dave Page <[email protected]> wrote:
> Hi Murtuza
>
> On Tue, Mar 22, 2016 at 9:22 AM, Dave Page <[email protected]> wrote:
> > Hi
> >
> > On Tue, Mar 22, 2016 at 8:14 AM, Murtuza Zabuawala
> > <[email protected]> wrote:
> >> Hi Dave,
> >>
> >> We can create new external type using below method, By running all of
> below
> >> queries at the same time , we can not create separate external type by
> only
> >> using create type statement.
> >>
> >> So as per my discussion with Ashesh, We should not allow user to create
> >> external type in pgAdmin4 but only show definition in edit mode.
> >
> > Hmm, would it not make sense to allow the user to create the shell
> > type as well (perhaps, with a new type of "SHELL")? Then they could do
> > what is needed (and that should be easy, as it's just CREATE TYPE
> > foo;)
> >
> > For example:
> >
> > CREATE TYPE box;
> >
> > CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
> > CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ;
> >
> > CREATE TYPE box (
> > INTERNALLENGTH = 16,
> > INPUT = my_box_in_function,
> > OUTPUT = my_box_out_function
> > );
> >
> > CREATE TABLE myboxes (
> > id integer,
> > description box
> > );
>
> In the interests of making progress, I've committed the most recent
> patch, with a number of minor changes most significantly, the Postgres
> docs and system catalogs seem to have different ideas about what to
> call length, precision and scale. pgAdmin 3 followed the catalogs and
> used length and precision, however I've updated pgAdmin 4 to use
> "Length/precision" and "Scale" which is inline with the Postgres docs.
> That's only in the UI though - the code follows the catalogs.
>
> There are still a couple of issues - please provide fixes ASAP:
>
> 1) If you create a composite type that contains a sized type (e.g.
> numeric(5, 4), the precision and scale are not shown if you later open
> the properties dialogue, or in the reverse engineered SQL.
>
> E.g. what pgAdmin3 shows as:
>
> CREATE TYPE pem.blergh AS
> (c1 text COLLATE pg_catalog."C",
> c2 numeric(5));
>
> Is shown by pgAdmin4 as:
>
> CREATE TYPE pem.blergh AS
> (c1 text COLLATE pg_catalog."C", c2 numeric);
>
> (adding the \n's would be good too).
>
> 2) If you select a different type of type in create mode, the new
> options are shown below those for the previously selected type,
> instead of replacing them. Please see the attached screenshot.
>
> 3) I would still like us to support External types. I believe the
> simple option here is to re-add the code you had previously, and to
> add a new type of type called "SHELL" as discussed in my previous
> email above. The user would then be able to create a SHELL type, add
> the required functions, then come back and create the EXTERNAL type.
>
> I'll add cards to our internal kanban chart for these issues.
>
> Thanks.
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/octet-stream] added_fulltype_function_v1.patch (3.6K, 3-added_fulltype_function_v1.patch)
download | inline diff:
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..1072467 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py
@@ -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):
"""
[application/octet-stream] Fixed_rendering_issue.patch (2.1K, 4-Fixed_rendering_issue.patch)
download | inline diff:
diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js
index 8eaed6d..5c62b58 100644
--- a/web/pgadmin/static/js/backform.pgadmin.js
+++ b/web/pgadmin/static/js/backform.pgadmin.js
@@ -900,6 +900,7 @@
render: function() {
// Clean up existing elements
this.undelegateEvents();
+ this.$el.empty();
var field = _.defaults(this.field.toJSON(), this.defaults),
attributes = this.model.toJSON(),
@@ -978,7 +979,8 @@
gridSchema.columns.unshift({
name: "pg-backform-delete", label: "",
cell: Backgrid.Extension.DeleteCell,
- editable: false, cell_priority: -1
+ editable: false, cell_priority: -1,
+ canDeleteRow: data.canDeleteRow
});
}
@@ -990,7 +992,7 @@
gridSchema.columns.unshift({
name: "pg-backform-edit", label: "", cell : editCell,
- cell_priority: -2
+ cell_priority: -2, canEditRow: data.canEditRow
});
}
@@ -1200,7 +1202,8 @@
gridSchema.columns.unshift({
name: "pg-backform-delete", label: "",
cell: Backgrid.Extension.DeleteCell,
- editable: false, cell_priority: -1
+ editable: false, cell_priority: -1,
+ canDeleteRow: data.canDeleteRow
});
}
@@ -1214,7 +1217,8 @@
gridSchema.columns.unshift({
name: "pg-backform-edit", label: "", cell : editCell,
- cell_priority: -2, editable: canEdit
+ cell_priority: -2, editable: canEdit,
+ canEditRow: data.canEditRow
});
}
@@ -1664,6 +1668,8 @@
canAdd: (disabled ? false : evalASFunc(s.canAdd)),
canEdit: (disabled ? false : evalASFunc(s.canEdit)),
canDelete: (disabled ? false : evalASFunc(s.canDelete)),
+ canEditRow: (disabled ? false : evalASFunc(s.canEditRow)),
+ canDeleteRow: (disabled ? false : evalASFunc(s.canDeleteRow)),
transform: evalASFunc(s.transform),
mode: mode,
control: control,
[application/octet-stream] updated_type_node.patch (8.8K, 5-updated_type_node.patch)
download | inline diff:
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..7e05fae 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,21 @@ 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'])
+ 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
+ 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)
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 5d79449..1be3447 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
@@ -306,23 +306,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 (External type only)", 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({
@@ -343,10 +335,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') }}',
@@ -522,7 +511,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';
},
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..4c4929e 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,19 @@
{% 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' %}
+-- This is a shell type for creating 'External' type.
+-- In order to use, You need to create respective Input/Output function
+-- Once you created them, Use pgAdmin4 to create 'External' type.
+
+-- Refer http://www.postgresql.org/docs/[PG_VERSION]/static/sql-createtype.html
+
+CREATE TYPE {{ conn|qtIdent(data.schema, data.name) }};
+{% else %}
{### 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' %}
@@ -77,3 +87,5 @@ COMMENT ON TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{%
{% endif %}
{% endfor %}
{% endif %}
+{## End of shell type if ##}
+{% 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..148256e 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,8 +1,10 @@
{### 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 and length(nspname) != 'public' and length(proname) > 0 THEN
concat(quote_ident(nspname), '.', quote_ident(proname))
+ CASE 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
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-18 07:15 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-18 16:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-22 08:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-22 09:22 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-07 11:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-08 13:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
@ 2016-04-13 12:48 ` Dave Page <[email protected]>
2016-04-14 07:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
0 siblings, 1 reply; 26+ messages in thread
From: Dave Page @ 2016-04-13 12:48 UTC (permalink / raw)
To: Murtuza Zabuawala <[email protected]>; +Cc: pgadmin-hackers
Hi
I'm seeing some more issues now, some related to new functionality:
- Please don't include the descriptive header for shell types in create.sql.
- Shell types can (and should) have owner/comments set if specified.
- The Type dropdown should just say "Shell" for shell types (no
additional text).
- Privileges and labels grids should be disabled for shell types.
- When I select External type, I see the following error (and a 500
error in the browser console):
2016-04-13 13:44:49,953: ERROR pgadmin: Failed to execute query
(execute_2darray) for the server #1 - DB:pem (Query-id: 4020898):
Error Message:ERROR: syntax error at or near "and"
LINE 2: CASE WHEN length(nspname) > 0 AND and length(nspname) !=...
- Length/Precision and scale are not shown in the dialogue for existing types.
Thanks.
On Fri, Apr 8, 2016 at 2:52 PM, Murtuza Zabuawala
<[email protected]> wrote:
> Hi Dave,
>
> Please find updated patches to fix all the mentioned issues.
>
> There are three patches,
> 1) One for fixing rendering issue when selecting type
> 2) Added utility function to fetch full type name with length & precision
> 3) Added new Shell type for External types.
>
>
> Regards,
> Murtuza
>
>
> --
> Regards,
> Murtuza Zabuawala
> EnterpriseDB: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
> On Thu, Apr 7, 2016 at 5:06 PM, Dave Page <[email protected]> wrote:
>>
>> Hi Murtuza
>>
>> On Tue, Mar 22, 2016 at 9:22 AM, Dave Page <[email protected]> wrote:
>> > Hi
>> >
>> > On Tue, Mar 22, 2016 at 8:14 AM, Murtuza Zabuawala
>> > <[email protected]> wrote:
>> >> Hi Dave,
>> >>
>> >> We can create new external type using below method, By running all of
>> >> below
>> >> queries at the same time , we can not create separate external type by
>> >> only
>> >> using create type statement.
>> >>
>> >> So as per my discussion with Ashesh, We should not allow user to create
>> >> external type in pgAdmin4 but only show definition in edit mode.
>> >
>> > Hmm, would it not make sense to allow the user to create the shell
>> > type as well (perhaps, with a new type of "SHELL")? Then they could do
>> > what is needed (and that should be easy, as it's just CREATE TYPE
>> > foo;)
>> >
>> > For example:
>> >
>> > CREATE TYPE box;
>> >
>> > CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
>> > CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ;
>> >
>> > CREATE TYPE box (
>> > INTERNALLENGTH = 16,
>> > INPUT = my_box_in_function,
>> > OUTPUT = my_box_out_function
>> > );
>> >
>> > CREATE TABLE myboxes (
>> > id integer,
>> > description box
>> > );
>>
>> In the interests of making progress, I've committed the most recent
>> patch, with a number of minor changes most significantly, the Postgres
>> docs and system catalogs seem to have different ideas about what to
>> call length, precision and scale. pgAdmin 3 followed the catalogs and
>> used length and precision, however I've updated pgAdmin 4 to use
>> "Length/precision" and "Scale" which is inline with the Postgres docs.
>> That's only in the UI though - the code follows the catalogs.
>>
>> There are still a couple of issues - please provide fixes ASAP:
>>
>> 1) If you create a composite type that contains a sized type (e.g.
>> numeric(5, 4), the precision and scale are not shown if you later open
>> the properties dialogue, or in the reverse engineered SQL.
>>
>> E.g. what pgAdmin3 shows as:
>>
>> CREATE TYPE pem.blergh AS
>> (c1 text COLLATE pg_catalog."C",
>> c2 numeric(5));
>>
>> Is shown by pgAdmin4 as:
>>
>> CREATE TYPE pem.blergh AS
>> (c1 text COLLATE pg_catalog."C", c2 numeric);
>>
>> (adding the \n's would be good too).
>>
>> 2) If you select a different type of type in create mode, the new
>> options are shown below those for the previously selected type,
>> instead of replacing them. Please see the attached screenshot.
>>
>> 3) I would still like us to support External types. I believe the
>> simple option here is to re-add the code you had previously, and to
>> add a new type of type called "SHELL" as discussed in my previous
>> email above. The user would then be able to create a SHELL type, add
>> the required functions, then come back and create the EXTERNAL type.
>>
>> I'll add cards to our internal kanban chart for these issues.
>>
>> Thanks.
>>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>
>
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-18 07:15 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-18 16:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-22 08:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-22 09:22 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-07 11:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-08 13:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-04-13 12:48 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
@ 2016-04-14 07:36 ` Murtuza Zabuawala <[email protected]>
2016-04-14 11:41 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
0 siblings, 1 reply; 26+ messages in thread
From: Murtuza Zabuawala @ 2016-04-14 07:36 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi Dave,
PFA updated patch which will fix mentioned issues.
Hopefully the last one :-)
--
Regards,
Murtuza Zabuawala
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Wed, Apr 13, 2016 at 6:18 PM, Dave Page <[email protected]> wrote:
> Hi
>
> I'm seeing some more issues now, some related to new functionality:
>
> - Please don't include the descriptive header for shell types in
> create.sql.
>
Done
>
> - Shell types can (and should) have owner/comments set if specified.
>
> Done
> - The Type dropdown should just say "Shell" for shell types (no
> additional text).
>
> Done
> - Privileges and labels grids should be disabled for shell types.
>
> Done
> - When I select External type, I see the following error (and a 500
> error in the browser console):
>
> 2016-04-13 13:44:49,953: ERROR pgadmin: Failed to execute query
> (execute_2darray) for the server #1 - DB:pem (Query-id: 4020898):
> Error Message:ERROR: syntax error at or near "and"
> LINE 2: CASE WHEN length(nspname) > 0 AND and length(nspname) !=...
>
> Done
> - Length/Precision and scale are not shown in the dialogue for existing
> types.
>
> Done (PFA screenshot)
> Thanks.
>
>
> On Fri, Apr 8, 2016 at 2:52 PM, Murtuza Zabuawala
> <[email protected]> wrote:
> > Hi Dave,
> >
> > Please find updated patches to fix all the mentioned issues.
> >
> > There are three patches,
> > 1) One for fixing rendering issue when selecting type
> > 2) Added utility function to fetch full type name with length & precision
> > 3) Added new Shell type for External types.
> >
> >
> > Regards,
> > Murtuza
> >
> >
> > --
> > Regards,
> > Murtuza Zabuawala
> > EnterpriseDB: http://www.enterprisedb.com
> > The Enterprise PostgreSQL Company
> >
> > On Thu, Apr 7, 2016 at 5:06 PM, Dave Page <[email protected]> wrote:
> >>
> >> Hi Murtuza
> >>
> >> On Tue, Mar 22, 2016 at 9:22 AM, Dave Page <[email protected]> wrote:
> >> > Hi
> >> >
> >> > On Tue, Mar 22, 2016 at 8:14 AM, Murtuza Zabuawala
> >> > <[email protected]> wrote:
> >> >> Hi Dave,
> >> >>
> >> >> We can create new external type using below method, By running all of
> >> >> below
> >> >> queries at the same time , we can not create separate external type
> by
> >> >> only
> >> >> using create type statement.
> >> >>
> >> >> So as per my discussion with Ashesh, We should not allow user to
> create
> >> >> external type in pgAdmin4 but only show definition in edit mode.
> >> >
> >> > Hmm, would it not make sense to allow the user to create the shell
> >> > type as well (perhaps, with a new type of "SHELL")? Then they could do
> >> > what is needed (and that should be easy, as it's just CREATE TYPE
> >> > foo;)
> >> >
> >> > For example:
> >> >
> >> > CREATE TYPE box;
> >> >
> >> > CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
> >> > CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ;
> >> >
> >> > CREATE TYPE box (
> >> > INTERNALLENGTH = 16,
> >> > INPUT = my_box_in_function,
> >> > OUTPUT = my_box_out_function
> >> > );
> >> >
> >> > CREATE TABLE myboxes (
> >> > id integer,
> >> > description box
> >> > );
> >>
> >> In the interests of making progress, I've committed the most recent
> >> patch, with a number of minor changes most significantly, the Postgres
> >> docs and system catalogs seem to have different ideas about what to
> >> call length, precision and scale. pgAdmin 3 followed the catalogs and
> >> used length and precision, however I've updated pgAdmin 4 to use
> >> "Length/precision" and "Scale" which is inline with the Postgres docs.
> >> That's only in the UI though - the code follows the catalogs.
> >>
> >> There are still a couple of issues - please provide fixes ASAP:
> >>
> >> 1) If you create a composite type that contains a sized type (e.g.
> >> numeric(5, 4), the precision and scale are not shown if you later open
> >> the properties dialogue, or in the reverse engineered SQL.
> >>
> >> E.g. what pgAdmin3 shows as:
> >>
> >> CREATE TYPE pem.blergh AS
> >> (c1 text COLLATE pg_catalog."C",
> >> c2 numeric(5));
> >>
> >> Is shown by pgAdmin4 as:
> >>
> >> CREATE TYPE pem.blergh AS
> >> (c1 text COLLATE pg_catalog."C", c2 numeric);
> >>
> >> (adding the \n's would be good too).
> >>
> >> 2) If you select a different type of type in create mode, the new
> >> options are shown below those for the previously selected type,
> >> instead of replacing them. Please see the attached screenshot.
> >>
> >> 3) I would still like us to support External types. I believe the
> >> simple option here is to re-add the code you had previously, and to
> >> add a new type of type called "SHELL" as discussed in my previous
> >> email above. The user would then be able to create a SHELL type, add
> >> the required functions, then come back and create the EXTERNAL type.
> >>
> >> I'll add cards to our internal kanban chart for these issues.
> >>
> >> Thanks.
> >>
> >> --
> >> Dave Page
> >> Blog: http://pgsnake.blogspot.com
> >> Twitter: @pgsnake
> >>
> >> EnterpriseDB UK: http://www.enterprisedb.com
> >> The Enterprise PostgreSQL Company
> >
> >
>
>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/octet-stream] updates_type_node_v6.patch (14.9K, 3-updates_type_node_v6.patch)
download | inline diff:
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
[image/png] Screen Shot 2016-04-14 at 1.03.43 pm.png (69.4K, 4-Screen%20Shot%202016-04-14%20at%201.03.43%20pm.png)
download | view image
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-18 07:15 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-18 16:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-22 08:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-22 09:22 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-07 11:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-08 13:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-04-13 12:48 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-14 07:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
@ 2016-04-14 11:41 ` Dave Page <[email protected]>
2016-04-14 12:57 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
0 siblings, 1 reply; 26+ messages in thread
From: Dave Page @ 2016-04-14 11:41 UTC (permalink / raw)
To: Murtuza Zabuawala <[email protected]>; +Cc: pgadmin-hackers
Thanks - patch applied. Unfortunately there are 3 more minor issues
for you to fix:
- Renaming or changing the schema for a shell type should not be allowed.
- I'm allowed to try to add ACL entries or security labels to an
existing shell type. This should be disallowed.
- Changing the schema on a (non-shell) type doesn't work - the type
name is omitted, e.g.
ALTER TYPE pem
SET SCHEMA pemhistory;
Which should be:
ALTER TYPE pem.foo
SET SCHEMA pemhistory;
On Thu, Apr 14, 2016 at 8:36 AM, Murtuza Zabuawala
<[email protected]> wrote:
> Hi Dave,
>
> PFA updated patch which will fix mentioned issues.
>
> Hopefully the last one :-)
>
>
> --
> Regards,
> Murtuza Zabuawala
> EnterpriseDB: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
> On Wed, Apr 13, 2016 at 6:18 PM, Dave Page <[email protected]> wrote:
>>
>> Hi
>>
>> I'm seeing some more issues now, some related to new functionality:
>>
>> - Please don't include the descriptive header for shell types in
>> create.sql.
>
> Done
>>
>>
>> - Shell types can (and should) have owner/comments set if specified.
>>
> Done
>>
>> - The Type dropdown should just say "Shell" for shell types (no
>> additional text).
>>
> Done
>>
>> - Privileges and labels grids should be disabled for shell types.
>>
> Done
>>
>> - When I select External type, I see the following error (and a 500
>> error in the browser console):
>>
>> 2016-04-13 13:44:49,953: ERROR pgadmin: Failed to execute query
>> (execute_2darray) for the server #1 - DB:pem (Query-id: 4020898):
>> Error Message:ERROR: syntax error at or near "and"
>> LINE 2: CASE WHEN length(nspname) > 0 AND and length(nspname) !=...
>>
> Done
>>
>> - Length/Precision and scale are not shown in the dialogue for existing
>> types.
>>
> Done (PFA screenshot)
>
>>
>> Thanks.
>>
>>
>> On Fri, Apr 8, 2016 at 2:52 PM, Murtuza Zabuawala
>> <[email protected]> wrote:
>> > Hi Dave,
>> >
>> > Please find updated patches to fix all the mentioned issues.
>> >
>> > There are three patches,
>> > 1) One for fixing rendering issue when selecting type
>> > 2) Added utility function to fetch full type name with length &
>> > precision
>> > 3) Added new Shell type for External types.
>> >
>> >
>> > Regards,
>> > Murtuza
>> >
>> >
>> > --
>> > Regards,
>> > Murtuza Zabuawala
>> > EnterpriseDB: http://www.enterprisedb.com
>> > The Enterprise PostgreSQL Company
>> >
>> > On Thu, Apr 7, 2016 at 5:06 PM, Dave Page <[email protected]> wrote:
>> >>
>> >> Hi Murtuza
>> >>
>> >> On Tue, Mar 22, 2016 at 9:22 AM, Dave Page <[email protected]> wrote:
>> >> > Hi
>> >> >
>> >> > On Tue, Mar 22, 2016 at 8:14 AM, Murtuza Zabuawala
>> >> > <[email protected]> wrote:
>> >> >> Hi Dave,
>> >> >>
>> >> >> We can create new external type using below method, By running all
>> >> >> of
>> >> >> below
>> >> >> queries at the same time , we can not create separate external type
>> >> >> by
>> >> >> only
>> >> >> using create type statement.
>> >> >>
>> >> >> So as per my discussion with Ashesh, We should not allow user to
>> >> >> create
>> >> >> external type in pgAdmin4 but only show definition in edit mode.
>> >> >
>> >> > Hmm, would it not make sense to allow the user to create the shell
>> >> > type as well (perhaps, with a new type of "SHELL")? Then they could
>> >> > do
>> >> > what is needed (and that should be easy, as it's just CREATE TYPE
>> >> > foo;)
>> >> >
>> >> > For example:
>> >> >
>> >> > CREATE TYPE box;
>> >> >
>> >> > CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
>> >> > CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ;
>> >> >
>> >> > CREATE TYPE box (
>> >> > INTERNALLENGTH = 16,
>> >> > INPUT = my_box_in_function,
>> >> > OUTPUT = my_box_out_function
>> >> > );
>> >> >
>> >> > CREATE TABLE myboxes (
>> >> > id integer,
>> >> > description box
>> >> > );
>> >>
>> >> In the interests of making progress, I've committed the most recent
>> >> patch, with a number of minor changes most significantly, the Postgres
>> >> docs and system catalogs seem to have different ideas about what to
>> >> call length, precision and scale. pgAdmin 3 followed the catalogs and
>> >> used length and precision, however I've updated pgAdmin 4 to use
>> >> "Length/precision" and "Scale" which is inline with the Postgres docs.
>> >> That's only in the UI though - the code follows the catalogs.
>> >>
>> >> There are still a couple of issues - please provide fixes ASAP:
>> >>
>> >> 1) If you create a composite type that contains a sized type (e.g.
>> >> numeric(5, 4), the precision and scale are not shown if you later open
>> >> the properties dialogue, or in the reverse engineered SQL.
>> >>
>> >> E.g. what pgAdmin3 shows as:
>> >>
>> >> CREATE TYPE pem.blergh AS
>> >> (c1 text COLLATE pg_catalog."C",
>> >> c2 numeric(5));
>> >>
>> >> Is shown by pgAdmin4 as:
>> >>
>> >> CREATE TYPE pem.blergh AS
>> >> (c1 text COLLATE pg_catalog."C", c2 numeric);
>> >>
>> >> (adding the \n's would be good too).
>> >>
>> >> 2) If you select a different type of type in create mode, the new
>> >> options are shown below those for the previously selected type,
>> >> instead of replacing them. Please see the attached screenshot.
>> >>
>> >> 3) I would still like us to support External types. I believe the
>> >> simple option here is to re-add the code you had previously, and to
>> >> add a new type of type called "SHELL" as discussed in my previous
>> >> email above. The user would then be able to create a SHELL type, add
>> >> the required functions, then come back and create the EXTERNAL type.
>> >>
>> >> I'll add cards to our internal kanban chart for these issues.
>> >>
>> >> Thanks.
>> >>
>> >> --
>> >> Dave Page
>> >> Blog: http://pgsnake.blogspot.com
>> >> Twitter: @pgsnake
>> >>
>> >> EnterpriseDB UK: http://www.enterprisedb.com
>> >> The Enterprise PostgreSQL Company
>> >
>> >
>>
>>
>>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>
>
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-18 07:15 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-18 16:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-22 08:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-22 09:22 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-07 11:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-08 13:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-04-13 12:48 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-14 07:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-04-14 11:41 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
@ 2016-04-14 12:57 ` Murtuza Zabuawala <[email protected]>
2016-04-14 13:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
0 siblings, 1 reply; 26+ messages in thread
From: Murtuza Zabuawala @ 2016-04-14 12:57 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi Dave,
PFA patch to fixed given issues.
--
Regards,
Murtuza Zabuawala
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Thu, Apr 14, 2016 at 5:11 PM, Dave Page <[email protected]> wrote:
> Thanks - patch applied. Unfortunately there are 3 more minor issues
> for you to fix:
>
> - Renaming or changing the schema for a shell type should not be allowed.
>
> Done
> - I'm allowed to try to add ACL entries or security labels to an
> existing shell type. This should be disallowed.
>
> Done
> - Changing the schema on a (non-shell) type doesn't work - the type
> name is omitted, e.g.
>
> Done
> ALTER TYPE pem
> SET SCHEMA pemhistory;
>
> Which should be:
>
> ALTER TYPE pem.foo
> SET SCHEMA pemhistory;
>
> On Thu, Apr 14, 2016 at 8:36 AM, Murtuza Zabuawala
> <[email protected]> wrote:
> > Hi Dave,
> >
> > PFA updated patch which will fix mentioned issues.
> >
> > Hopefully the last one :-)
> >
> >
> > --
> > Regards,
> > Murtuza Zabuawala
> > EnterpriseDB: http://www.enterprisedb.com
> > The Enterprise PostgreSQL Company
> >
> > On Wed, Apr 13, 2016 at 6:18 PM, Dave Page <[email protected]> wrote:
> >>
> >> Hi
> >>
> >> I'm seeing some more issues now, some related to new functionality:
> >>
> >> - Please don't include the descriptive header for shell types in
> >> create.sql.
> >
> > Done
> >>
> >>
> >> - Shell types can (and should) have owner/comments set if specified.
> >>
> > Done
> >>
> >> - The Type dropdown should just say "Shell" for shell types (no
> >> additional text).
> >>
> > Done
> >>
> >> - Privileges and labels grids should be disabled for shell types.
> >>
> > Done
> >>
> >> - When I select External type, I see the following error (and a 500
> >> error in the browser console):
> >>
> >> 2016-04-13 13:44:49,953: ERROR pgadmin: Failed to execute query
> >> (execute_2darray) for the server #1 - DB:pem (Query-id: 4020898):
> >> Error Message:ERROR: syntax error at or near "and"
> >> LINE 2: CASE WHEN length(nspname) > 0 AND and length(nspname) !=...
> >>
> > Done
> >>
> >> - Length/Precision and scale are not shown in the dialogue for existing
> >> types.
> >>
> > Done (PFA screenshot)
> >
> >>
> >> Thanks.
> >>
> >>
> >> On Fri, Apr 8, 2016 at 2:52 PM, Murtuza Zabuawala
> >> <[email protected]> wrote:
> >> > Hi Dave,
> >> >
> >> > Please find updated patches to fix all the mentioned issues.
> >> >
> >> > There are three patches,
> >> > 1) One for fixing rendering issue when selecting type
> >> > 2) Added utility function to fetch full type name with length &
> >> > precision
> >> > 3) Added new Shell type for External types.
> >> >
> >> >
> >> > Regards,
> >> > Murtuza
> >> >
> >> >
> >> > --
> >> > Regards,
> >> > Murtuza Zabuawala
> >> > EnterpriseDB: http://www.enterprisedb.com
> >> > The Enterprise PostgreSQL Company
> >> >
> >> > On Thu, Apr 7, 2016 at 5:06 PM, Dave Page <[email protected]> wrote:
> >> >>
> >> >> Hi Murtuza
> >> >>
> >> >> On Tue, Mar 22, 2016 at 9:22 AM, Dave Page <[email protected]>
> wrote:
> >> >> > Hi
> >> >> >
> >> >> > On Tue, Mar 22, 2016 at 8:14 AM, Murtuza Zabuawala
> >> >> > <[email protected]> wrote:
> >> >> >> Hi Dave,
> >> >> >>
> >> >> >> We can create new external type using below method, By running all
> >> >> >> of
> >> >> >> below
> >> >> >> queries at the same time , we can not create separate external
> type
> >> >> >> by
> >> >> >> only
> >> >> >> using create type statement.
> >> >> >>
> >> >> >> So as per my discussion with Ashesh, We should not allow user to
> >> >> >> create
> >> >> >> external type in pgAdmin4 but only show definition in edit mode.
> >> >> >
> >> >> > Hmm, would it not make sense to allow the user to create the shell
> >> >> > type as well (perhaps, with a new type of "SHELL")? Then they could
> >> >> > do
> >> >> > what is needed (and that should be easy, as it's just CREATE TYPE
> >> >> > foo;)
> >> >> >
> >> >> > For example:
> >> >> >
> >> >> > CREATE TYPE box;
> >> >> >
> >> >> > CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
> >> >> > CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ;
> >> >> >
> >> >> > CREATE TYPE box (
> >> >> > INTERNALLENGTH = 16,
> >> >> > INPUT = my_box_in_function,
> >> >> > OUTPUT = my_box_out_function
> >> >> > );
> >> >> >
> >> >> > CREATE TABLE myboxes (
> >> >> > id integer,
> >> >> > description box
> >> >> > );
> >> >>
> >> >> In the interests of making progress, I've committed the most recent
> >> >> patch, with a number of minor changes most significantly, the
> Postgres
> >> >> docs and system catalogs seem to have different ideas about what to
> >> >> call length, precision and scale. pgAdmin 3 followed the catalogs and
> >> >> used length and precision, however I've updated pgAdmin 4 to use
> >> >> "Length/precision" and "Scale" which is inline with the Postgres
> docs.
> >> >> That's only in the UI though - the code follows the catalogs.
> >> >>
> >> >> There are still a couple of issues - please provide fixes ASAP:
> >> >>
> >> >> 1) If you create a composite type that contains a sized type (e.g.
> >> >> numeric(5, 4), the precision and scale are not shown if you later
> open
> >> >> the properties dialogue, or in the reverse engineered SQL.
> >> >>
> >> >> E.g. what pgAdmin3 shows as:
> >> >>
> >> >> CREATE TYPE pem.blergh AS
> >> >> (c1 text COLLATE pg_catalog."C",
> >> >> c2 numeric(5));
> >> >>
> >> >> Is shown by pgAdmin4 as:
> >> >>
> >> >> CREATE TYPE pem.blergh AS
> >> >> (c1 text COLLATE pg_catalog."C", c2 numeric);
> >> >>
> >> >> (adding the \n's would be good too).
> >> >>
> >> >> 2) If you select a different type of type in create mode, the new
> >> >> options are shown below those for the previously selected type,
> >> >> instead of replacing them. Please see the attached screenshot.
> >> >>
> >> >> 3) I would still like us to support External types. I believe the
> >> >> simple option here is to re-add the code you had previously, and to
> >> >> add a new type of type called "SHELL" as discussed in my previous
> >> >> email above. The user would then be able to create a SHELL type, add
> >> >> the required functions, then come back and create the EXTERNAL type.
> >> >>
> >> >> I'll add cards to our internal kanban chart for these issues.
> >> >>
> >> >> Thanks.
> >> >>
> >> >> --
> >> >> Dave Page
> >> >> Blog: http://pgsnake.blogspot.com
> >> >> Twitter: @pgsnake
> >> >>
> >> >> EnterpriseDB UK: http://www.enterprisedb.com
> >> >> The Enterprise PostgreSQL Company
> >> >
> >> >
> >>
> >>
> >>
> >> --
> >> Dave Page
> >> Blog: http://pgsnake.blogspot.com
> >> Twitter: @pgsnake
> >>
> >> EnterpriseDB UK: http://www.enterprisedb.com
> >> The Enterprise PostgreSQL Company
> >
> >
>
>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/octet-stream] fixed_type_node_v7.patch (5.4K, 3-fixed_type_node_v7.patch)
download | inline diff:
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 7a41179..2d00877 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
@@ -282,7 +282,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
},{
id: 'schema', label:'{{ _('Schema') }}', cell: 'string',
type: 'text', mode: ['create', 'edit'], node: 'schema',
- disabled: 'inSchema', filter: function(d) {
+ disabled: 'schemaCheck', filter: function(d) {
// If schema name start with pg_* then we need to exclude them
if(d && d.label.match(/^pg_/))
{
@@ -290,18 +290,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
}
return true;
},
- control: Backform.NodeListByNameControl.extend({
- render: function(){
- // Initialize parent's render method
- Backform.NodeListByNameControl.prototype.render.apply(this, arguments);
-
- // Set schema default value to its parent Schema
- if(this.model.isNew()){
- this.model.set({'schema': this.model.node_info.schema.label});
- }
- return this;
- }
- })
+ control: 'node-list-by-name'
},{
id: 'typtype', label:'{{ _('Type') }}',
mode: ['create','edit'], disabled: 'inSchemaWithModelCheck',
@@ -314,7 +303,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
{label: "Enumeration", value: "e"},
{label: "External", value: "b"},
{label: "Range", value: "r"},
- {label: "Shell", value: "s"}
+ {label: "Shell", value: "p"}
]
},
disabled: 'inSchemaWithModelCheck',
@@ -724,7 +713,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
uniqueCol : ['grantee'], deps: ['typtype'],
canAdd: function(m) {
// Do not allow to add when shell type is selected
- return !(m.get('typtype') === 's');
+ return !(m.get('typtype') === 'p');
}
},{
id: 'seclabels', label: '{{ _('Security Labels') }}',
@@ -734,7 +723,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
control: 'unique-col-collection', deps: ['typtype'],
canAdd: function(m) {
// Do not allow to add when shell type is selected
- return !(m.get('typtype') === 's');
+ return !(m.get('typtype') === 'p');
}
}],
@@ -789,6 +778,17 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
}
return false;
},
+ schemaCheck: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ if (m.isNew()) {
+ return false;
+ } else {
+ return m.get('typtype') === 'p';
+ }
+ }
+ return false;
+ },
// We will check if we are under schema node & in 'create' mode
inSchemaWithModelCheck: function(m) {
if(this.node_info && 'schema' in this.node_info)
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 35403b6..b4a44d4 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,7 +1,7 @@
{% 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' %}
+{% if data and data.typtype == 'p' %}
CREATE TYPE {{ conn|qtIdent(data.schema, data.name) }};
{% endif %}
{### Composite Type ###}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
index 170b03a..ab617b0 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
@@ -131,9 +131,9 @@ ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
{# 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 != o_data.name %}{{ conn|qtIdent(o_data.schema, data.name) }}
-{% else %}{{ conn|qtIdent(o_data.schema, o_data.name) }}{% endif %}
+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 %}
{% endif %}
\ No newline at end of file
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-18 07:15 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-18 16:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-22 08:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-22 09:22 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-07 11:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-08 13:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-04-13 12:48 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-14 07:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-04-14 11:41 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-14 12:57 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
@ 2016-04-14 13:14 ` Dave Page <[email protected]>
2016-04-14 13:31 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
0 siblings, 1 reply; 26+ messages in thread
From: Dave Page @ 2016-04-14 13:14 UTC (permalink / raw)
To: Murtuza Zabuawala <[email protected]>; +Cc: pgadmin-hackers
Hi,
The first issue is only partially fixed - it'll still let me try to
rename a shell type.
Also, I notice when hitting Save on my changes, they're applied (if
they're valid), but the dialogue doesn't close. I see the following in
the browser console:
Uncaught TypeError: Cannot read property 'sessChanged' of
null(anonymous function) @ datamodel.js:333
_.each._.forEach @ underscore.js:153
pgBrowser.DataModel.Backbone.Model.extend.toJSON @ datamodel.js:322
(anonymous function) @ node.js:850
jQuery.event.dispatch @ jquery-1.11.2.js:4665
elemData.handle @ jquery-1.11.2.js:4333
On Thu, Apr 14, 2016 at 1:57 PM, Murtuza Zabuawala
<[email protected]> wrote:
> Hi Dave,
>
> PFA patch to fixed given issues.
>
>
> --
> Regards,
> Murtuza Zabuawala
> EnterpriseDB: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
> On Thu, Apr 14, 2016 at 5:11 PM, Dave Page <[email protected]> wrote:
>>
>> Thanks - patch applied. Unfortunately there are 3 more minor issues
>> for you to fix:
>>
>> - Renaming or changing the schema for a shell type should not be allowed.
>>
> Done
>>
>> - I'm allowed to try to add ACL entries or security labels to an
>> existing shell type. This should be disallowed.
>>
> Done
>>
>> - Changing the schema on a (non-shell) type doesn't work - the type
>> name is omitted, e.g.
>>
> Done
>>
>> ALTER TYPE pem
>> SET SCHEMA pemhistory;
>>
>> Which should be:
>>
>> ALTER TYPE pem.foo
>> SET SCHEMA pemhistory;
>>
>> On Thu, Apr 14, 2016 at 8:36 AM, Murtuza Zabuawala
>> <[email protected]> wrote:
>> > Hi Dave,
>> >
>> > PFA updated patch which will fix mentioned issues.
>> >
>> > Hopefully the last one :-)
>> >
>> >
>> > --
>> > Regards,
>> > Murtuza Zabuawala
>> > EnterpriseDB: http://www.enterprisedb.com
>> > The Enterprise PostgreSQL Company
>> >
>> > On Wed, Apr 13, 2016 at 6:18 PM, Dave Page <[email protected]> wrote:
>> >>
>> >> Hi
>> >>
>> >> I'm seeing some more issues now, some related to new functionality:
>> >>
>> >> - Please don't include the descriptive header for shell types in
>> >> create.sql.
>> >
>> > Done
>> >>
>> >>
>> >> - Shell types can (and should) have owner/comments set if specified.
>> >>
>> > Done
>> >>
>> >> - The Type dropdown should just say "Shell" for shell types (no
>> >> additional text).
>> >>
>> > Done
>> >>
>> >> - Privileges and labels grids should be disabled for shell types.
>> >>
>> > Done
>> >>
>> >> - When I select External type, I see the following error (and a 500
>> >> error in the browser console):
>> >>
>> >> 2016-04-13 13:44:49,953: ERROR pgadmin: Failed to execute query
>> >> (execute_2darray) for the server #1 - DB:pem (Query-id: 4020898):
>> >> Error Message:ERROR: syntax error at or near "and"
>> >> LINE 2: CASE WHEN length(nspname) > 0 AND and length(nspname) !=...
>> >>
>> > Done
>> >>
>> >> - Length/Precision and scale are not shown in the dialogue for existing
>> >> types.
>> >>
>> > Done (PFA screenshot)
>> >
>> >>
>> >> Thanks.
>> >>
>> >>
>> >> On Fri, Apr 8, 2016 at 2:52 PM, Murtuza Zabuawala
>> >> <[email protected]> wrote:
>> >> > Hi Dave,
>> >> >
>> >> > Please find updated patches to fix all the mentioned issues.
>> >> >
>> >> > There are three patches,
>> >> > 1) One for fixing rendering issue when selecting type
>> >> > 2) Added utility function to fetch full type name with length &
>> >> > precision
>> >> > 3) Added new Shell type for External types.
>> >> >
>> >> >
>> >> > Regards,
>> >> > Murtuza
>> >> >
>> >> >
>> >> > --
>> >> > Regards,
>> >> > Murtuza Zabuawala
>> >> > EnterpriseDB: http://www.enterprisedb.com
>> >> > The Enterprise PostgreSQL Company
>> >> >
>> >> > On Thu, Apr 7, 2016 at 5:06 PM, Dave Page <[email protected]> wrote:
>> >> >>
>> >> >> Hi Murtuza
>> >> >>
>> >> >> On Tue, Mar 22, 2016 at 9:22 AM, Dave Page <[email protected]>
>> >> >> wrote:
>> >> >> > Hi
>> >> >> >
>> >> >> > On Tue, Mar 22, 2016 at 8:14 AM, Murtuza Zabuawala
>> >> >> > <[email protected]> wrote:
>> >> >> >> Hi Dave,
>> >> >> >>
>> >> >> >> We can create new external type using below method, By running
>> >> >> >> all
>> >> >> >> of
>> >> >> >> below
>> >> >> >> queries at the same time , we can not create separate external
>> >> >> >> type
>> >> >> >> by
>> >> >> >> only
>> >> >> >> using create type statement.
>> >> >> >>
>> >> >> >> So as per my discussion with Ashesh, We should not allow user to
>> >> >> >> create
>> >> >> >> external type in pgAdmin4 but only show definition in edit mode.
>> >> >> >
>> >> >> > Hmm, would it not make sense to allow the user to create the shell
>> >> >> > type as well (perhaps, with a new type of "SHELL")? Then they
>> >> >> > could
>> >> >> > do
>> >> >> > what is needed (and that should be easy, as it's just CREATE TYPE
>> >> >> > foo;)
>> >> >> >
>> >> >> > For example:
>> >> >> >
>> >> >> > CREATE TYPE box;
>> >> >> >
>> >> >> > CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
>> >> >> > CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ;
>> >> >> >
>> >> >> > CREATE TYPE box (
>> >> >> > INTERNALLENGTH = 16,
>> >> >> > INPUT = my_box_in_function,
>> >> >> > OUTPUT = my_box_out_function
>> >> >> > );
>> >> >> >
>> >> >> > CREATE TABLE myboxes (
>> >> >> > id integer,
>> >> >> > description box
>> >> >> > );
>> >> >>
>> >> >> In the interests of making progress, I've committed the most recent
>> >> >> patch, with a number of minor changes most significantly, the
>> >> >> Postgres
>> >> >> docs and system catalogs seem to have different ideas about what to
>> >> >> call length, precision and scale. pgAdmin 3 followed the catalogs
>> >> >> and
>> >> >> used length and precision, however I've updated pgAdmin 4 to use
>> >> >> "Length/precision" and "Scale" which is inline with the Postgres
>> >> >> docs.
>> >> >> That's only in the UI though - the code follows the catalogs.
>> >> >>
>> >> >> There are still a couple of issues - please provide fixes ASAP:
>> >> >>
>> >> >> 1) If you create a composite type that contains a sized type (e.g.
>> >> >> numeric(5, 4), the precision and scale are not shown if you later
>> >> >> open
>> >> >> the properties dialogue, or in the reverse engineered SQL.
>> >> >>
>> >> >> E.g. what pgAdmin3 shows as:
>> >> >>
>> >> >> CREATE TYPE pem.blergh AS
>> >> >> (c1 text COLLATE pg_catalog."C",
>> >> >> c2 numeric(5));
>> >> >>
>> >> >> Is shown by pgAdmin4 as:
>> >> >>
>> >> >> CREATE TYPE pem.blergh AS
>> >> >> (c1 text COLLATE pg_catalog."C", c2 numeric);
>> >> >>
>> >> >> (adding the \n's would be good too).
>> >> >>
>> >> >> 2) If you select a different type of type in create mode, the new
>> >> >> options are shown below those for the previously selected type,
>> >> >> instead of replacing them. Please see the attached screenshot.
>> >> >>
>> >> >> 3) I would still like us to support External types. I believe the
>> >> >> simple option here is to re-add the code you had previously, and to
>> >> >> add a new type of type called "SHELL" as discussed in my previous
>> >> >> email above. The user would then be able to create a SHELL type, add
>> >> >> the required functions, then come back and create the EXTERNAL type.
>> >> >>
>> >> >> I'll add cards to our internal kanban chart for these issues.
>> >> >>
>> >> >> Thanks.
>> >> >>
>> >> >> --
>> >> >> Dave Page
>> >> >> Blog: http://pgsnake.blogspot.com
>> >> >> Twitter: @pgsnake
>> >> >>
>> >> >> EnterpriseDB UK: http://www.enterprisedb.com
>> >> >> The Enterprise PostgreSQL Company
>> >> >
>> >> >
>> >>
>> >>
>> >>
>> >> --
>> >> Dave Page
>> >> Blog: http://pgsnake.blogspot.com
>> >> Twitter: @pgsnake
>> >>
>> >> EnterpriseDB UK: http://www.enterprisedb.com
>> >> The Enterprise PostgreSQL Company
>> >
>> >
>>
>>
>>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>
>
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-18 07:15 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-18 16:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-22 08:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-22 09:22 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-07 11:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-08 13:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-04-13 12:48 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-14 07:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-04-14 11:41 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-14 12:57 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-04-14 13:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
@ 2016-04-14 13:31 ` Murtuza Zabuawala <[email protected]>
2016-04-14 14:46 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
0 siblings, 1 reply; 26+ messages in thread
From: Murtuza Zabuawala @ 2016-04-14 13:31 UTC (permalink / raw)
To: Dave Page <[email protected]>; +Cc: pgadmin-hackers
Hi Dave,
Sorry my bad I misinterpreted, I thought you meant rename/changing schema
only.
PFA updated patch.
And For Dialog not closing issue is generic and I have already created
internal ticket for it.
--
Regards,
Murtuza Zabuawala
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Thu, Apr 14, 2016 at 6:44 PM, Dave Page <[email protected]> wrote:
> Hi,
>
> The first issue is only partially fixed - it'll still let me try to
> rename a shell type.
>
> Also, I notice when hitting Save on my changes, they're applied (if
> they're valid), but the dialogue doesn't close. I see the following in
> the browser console:
>
> Uncaught TypeError: Cannot read property 'sessChanged' of
> null(anonymous function) @ datamodel.js:333
> _.each._.forEach @ underscore.js:153
> pgBrowser.DataModel.Backbone.Model.extend.toJSON @ datamodel.js:322
> (anonymous function) @ node.js:850
> jQuery.event.dispatch @ jquery-1.11.2.js:4665
> elemData.handle @ jquery-1.11.2.js:4333
>
> On Thu, Apr 14, 2016 at 1:57 PM, Murtuza Zabuawala
> <[email protected]> wrote:
> > Hi Dave,
> >
> > PFA patch to fixed given issues.
> >
> >
> > --
> > Regards,
> > Murtuza Zabuawala
> > EnterpriseDB: http://www.enterprisedb.com
> > The Enterprise PostgreSQL Company
> >
> > On Thu, Apr 14, 2016 at 5:11 PM, Dave Page <[email protected]> wrote:
> >>
> >> Thanks - patch applied. Unfortunately there are 3 more minor issues
> >> for you to fix:
> >>
> >> - Renaming or changing the schema for a shell type should not be
> allowed.
> >>
> > Done
> >>
> >> - I'm allowed to try to add ACL entries or security labels to an
> >> existing shell type. This should be disallowed.
> >>
> > Done
> >>
> >> - Changing the schema on a (non-shell) type doesn't work - the type
> >> name is omitted, e.g.
> >>
> > Done
> >>
> >> ALTER TYPE pem
> >> SET SCHEMA pemhistory;
> >>
> >> Which should be:
> >>
> >> ALTER TYPE pem.foo
> >> SET SCHEMA pemhistory;
> >>
> >> On Thu, Apr 14, 2016 at 8:36 AM, Murtuza Zabuawala
> >> <[email protected]> wrote:
> >> > Hi Dave,
> >> >
> >> > PFA updated patch which will fix mentioned issues.
> >> >
> >> > Hopefully the last one :-)
> >> >
> >> >
> >> > --
> >> > Regards,
> >> > Murtuza Zabuawala
> >> > EnterpriseDB: http://www.enterprisedb.com
> >> > The Enterprise PostgreSQL Company
> >> >
> >> > On Wed, Apr 13, 2016 at 6:18 PM, Dave Page <[email protected]> wrote:
> >> >>
> >> >> Hi
> >> >>
> >> >> I'm seeing some more issues now, some related to new functionality:
> >> >>
> >> >> - Please don't include the descriptive header for shell types in
> >> >> create.sql.
> >> >
> >> > Done
> >> >>
> >> >>
> >> >> - Shell types can (and should) have owner/comments set if specified.
> >> >>
> >> > Done
> >> >>
> >> >> - The Type dropdown should just say "Shell" for shell types (no
> >> >> additional text).
> >> >>
> >> > Done
> >> >>
> >> >> - Privileges and labels grids should be disabled for shell types.
> >> >>
> >> > Done
> >> >>
> >> >> - When I select External type, I see the following error (and a 500
> >> >> error in the browser console):
> >> >>
> >> >> 2016-04-13 13:44:49,953: ERROR pgadmin: Failed to execute query
> >> >> (execute_2darray) for the server #1 - DB:pem (Query-id: 4020898):
> >> >> Error Message:ERROR: syntax error at or near "and"
> >> >> LINE 2: CASE WHEN length(nspname) > 0 AND and length(nspname)
> !=...
> >> >>
> >> > Done
> >> >>
> >> >> - Length/Precision and scale are not shown in the dialogue for
> existing
> >> >> types.
> >> >>
> >> > Done (PFA screenshot)
> >> >
> >> >>
> >> >> Thanks.
> >> >>
> >> >>
> >> >> On Fri, Apr 8, 2016 at 2:52 PM, Murtuza Zabuawala
> >> >> <[email protected]> wrote:
> >> >> > Hi Dave,
> >> >> >
> >> >> > Please find updated patches to fix all the mentioned issues.
> >> >> >
> >> >> > There are three patches,
> >> >> > 1) One for fixing rendering issue when selecting type
> >> >> > 2) Added utility function to fetch full type name with length &
> >> >> > precision
> >> >> > 3) Added new Shell type for External types.
> >> >> >
> >> >> >
> >> >> > Regards,
> >> >> > Murtuza
> >> >> >
> >> >> >
> >> >> > --
> >> >> > Regards,
> >> >> > Murtuza Zabuawala
> >> >> > EnterpriseDB: http://www.enterprisedb.com
> >> >> > The Enterprise PostgreSQL Company
> >> >> >
> >> >> > On Thu, Apr 7, 2016 at 5:06 PM, Dave Page <[email protected]>
> wrote:
> >> >> >>
> >> >> >> Hi Murtuza
> >> >> >>
> >> >> >> On Tue, Mar 22, 2016 at 9:22 AM, Dave Page <[email protected]>
> >> >> >> wrote:
> >> >> >> > Hi
> >> >> >> >
> >> >> >> > On Tue, Mar 22, 2016 at 8:14 AM, Murtuza Zabuawala
> >> >> >> > <[email protected]> wrote:
> >> >> >> >> Hi Dave,
> >> >> >> >>
> >> >> >> >> We can create new external type using below method, By running
> >> >> >> >> all
> >> >> >> >> of
> >> >> >> >> below
> >> >> >> >> queries at the same time , we can not create separate external
> >> >> >> >> type
> >> >> >> >> by
> >> >> >> >> only
> >> >> >> >> using create type statement.
> >> >> >> >>
> >> >> >> >> So as per my discussion with Ashesh, We should not allow user
> to
> >> >> >> >> create
> >> >> >> >> external type in pgAdmin4 but only show definition in edit
> mode.
> >> >> >> >
> >> >> >> > Hmm, would it not make sense to allow the user to create the
> shell
> >> >> >> > type as well (perhaps, with a new type of "SHELL")? Then they
> >> >> >> > could
> >> >> >> > do
> >> >> >> > what is needed (and that should be easy, as it's just CREATE
> TYPE
> >> >> >> > foo;)
> >> >> >> >
> >> >> >> > For example:
> >> >> >> >
> >> >> >> > CREATE TYPE box;
> >> >> >> >
> >> >> >> > CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
> >> >> >> > CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ...
> ;
> >> >> >> >
> >> >> >> > CREATE TYPE box (
> >> >> >> > INTERNALLENGTH = 16,
> >> >> >> > INPUT = my_box_in_function,
> >> >> >> > OUTPUT = my_box_out_function
> >> >> >> > );
> >> >> >> >
> >> >> >> > CREATE TABLE myboxes (
> >> >> >> > id integer,
> >> >> >> > description box
> >> >> >> > );
> >> >> >>
> >> >> >> In the interests of making progress, I've committed the most
> recent
> >> >> >> patch, with a number of minor changes most significantly, the
> >> >> >> Postgres
> >> >> >> docs and system catalogs seem to have different ideas about what
> to
> >> >> >> call length, precision and scale. pgAdmin 3 followed the catalogs
> >> >> >> and
> >> >> >> used length and precision, however I've updated pgAdmin 4 to use
> >> >> >> "Length/precision" and "Scale" which is inline with the Postgres
> >> >> >> docs.
> >> >> >> That's only in the UI though - the code follows the catalogs.
> >> >> >>
> >> >> >> There are still a couple of issues - please provide fixes ASAP:
> >> >> >>
> >> >> >> 1) If you create a composite type that contains a sized type (e.g.
> >> >> >> numeric(5, 4), the precision and scale are not shown if you later
> >> >> >> open
> >> >> >> the properties dialogue, or in the reverse engineered SQL.
> >> >> >>
> >> >> >> E.g. what pgAdmin3 shows as:
> >> >> >>
> >> >> >> CREATE TYPE pem.blergh AS
> >> >> >> (c1 text COLLATE pg_catalog."C",
> >> >> >> c2 numeric(5));
> >> >> >>
> >> >> >> Is shown by pgAdmin4 as:
> >> >> >>
> >> >> >> CREATE TYPE pem.blergh AS
> >> >> >> (c1 text COLLATE pg_catalog."C", c2 numeric);
> >> >> >>
> >> >> >> (adding the \n's would be good too).
> >> >> >>
> >> >> >> 2) If you select a different type of type in create mode, the new
> >> >> >> options are shown below those for the previously selected type,
> >> >> >> instead of replacing them. Please see the attached screenshot.
> >> >> >>
> >> >> >> 3) I would still like us to support External types. I believe the
> >> >> >> simple option here is to re-add the code you had previously, and
> to
> >> >> >> add a new type of type called "SHELL" as discussed in my previous
> >> >> >> email above. The user would then be able to create a SHELL type,
> add
> >> >> >> the required functions, then come back and create the EXTERNAL
> type.
> >> >> >>
> >> >> >> I'll add cards to our internal kanban chart for these issues.
> >> >> >>
> >> >> >> Thanks.
> >> >> >>
> >> >> >> --
> >> >> >> Dave Page
> >> >> >> Blog: http://pgsnake.blogspot.com
> >> >> >> Twitter: @pgsnake
> >> >> >>
> >> >> >> EnterpriseDB UK: http://www.enterprisedb.com
> >> >> >> The Enterprise PostgreSQL Company
> >> >> >
> >> >> >
> >> >>
> >> >>
> >> >>
> >> >> --
> >> >> Dave Page
> >> >> Blog: http://pgsnake.blogspot.com
> >> >> Twitter: @pgsnake
> >> >>
> >> >> EnterpriseDB UK: http://www.enterprisedb.com
> >> >> The Enterprise PostgreSQL Company
> >> >
> >> >
> >>
> >>
> >>
> >> --
> >> Dave Page
> >> Blog: http://pgsnake.blogspot.com
> >> Twitter: @pgsnake
> >>
> >> EnterpriseDB UK: http://www.enterprisedb.com
> >> The Enterprise PostgreSQL Company
> >
> >
>
>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/octet-stream] fixed_type_node_v8.patch (5.8K, 3-fixed_type_node_v8.patch)
download | inline diff:
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 7a41179..8da8648 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
@@ -270,7 +270,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
schema: [{
id: 'name', label: '{{ _('Name') }}', cell: 'string',
type: 'text', mode: ['properties', 'create', 'edit'],
- disabled: 'inSchema'
+ disabled: 'schemaCheck'
},{
id: 'oid', label:'{{ _('OID') }}', cell: 'string',
type: 'text' , mode: ['properties'], disabled: true
@@ -282,7 +282,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
},{
id: 'schema', label:'{{ _('Schema') }}', cell: 'string',
type: 'text', mode: ['create', 'edit'], node: 'schema',
- disabled: 'inSchema', filter: function(d) {
+ disabled: 'schemaCheck', filter: function(d) {
// If schema name start with pg_* then we need to exclude them
if(d && d.label.match(/^pg_/))
{
@@ -290,18 +290,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
}
return true;
},
- control: Backform.NodeListByNameControl.extend({
- render: function(){
- // Initialize parent's render method
- Backform.NodeListByNameControl.prototype.render.apply(this, arguments);
-
- // Set schema default value to its parent Schema
- if(this.model.isNew()){
- this.model.set({'schema': this.model.node_info.schema.label});
- }
- return this;
- }
- })
+ control: 'node-list-by-name'
},{
id: 'typtype', label:'{{ _('Type') }}',
mode: ['create','edit'], disabled: 'inSchemaWithModelCheck',
@@ -314,7 +303,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
{label: "Enumeration", value: "e"},
{label: "External", value: "b"},
{label: "Range", value: "r"},
- {label: "Shell", value: "s"}
+ {label: "Shell", value: "p"}
]
},
disabled: 'inSchemaWithModelCheck',
@@ -724,7 +713,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
uniqueCol : ['grantee'], deps: ['typtype'],
canAdd: function(m) {
// Do not allow to add when shell type is selected
- return !(m.get('typtype') === 's');
+ return !(m.get('typtype') === 'p');
}
},{
id: 'seclabels', label: '{{ _('Security Labels') }}',
@@ -734,7 +723,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
control: 'unique-col-collection', deps: ['typtype'],
canAdd: function(m) {
// Do not allow to add when shell type is selected
- return !(m.get('typtype') === 's');
+ return !(m.get('typtype') === 'p');
}
}],
@@ -789,6 +778,17 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
}
return false;
},
+ schemaCheck: function(m) {
+ if(this.node_info && 'schema' in this.node_info)
+ {
+ if (m.isNew()) {
+ return false;
+ } else {
+ return m.get('typtype') === 'p';
+ }
+ }
+ return true;
+ },
// We will check if we are under schema node & in 'create' mode
inSchemaWithModelCheck: function(m) {
if(this.node_info && 'schema' in this.node_info)
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 35403b6..b4a44d4 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,7 +1,7 @@
{% 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' %}
+{% if data and data.typtype == 'p' %}
CREATE TYPE {{ conn|qtIdent(data.schema, data.name) }};
{% endif %}
{### Composite Type ###}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
index 170b03a..ab617b0 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/types/templates/type/sql/9.1_plus/update.sql
@@ -131,9 +131,9 @@ ALTER TYPE {{ conn|qtIdent(o_data.schema, o_data.name) }}
{# 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 != o_data.name %}{{ conn|qtIdent(o_data.schema, data.name) }}
-{% else %}{{ conn|qtIdent(o_data.schema, o_data.name) }}{% endif %}
+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 %}
{% endif %}
\ No newline at end of file
^ permalink raw reply [nested|flat] 26+ messages in thread
* Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-11 16:11 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-11 16:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-14 12:21 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-17 10:08 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-18 07:15 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-18 16:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-03-22 08:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-22 09:22 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-07 11:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-08 13:52 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-04-13 12:48 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-14 07:36 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-04-14 11:41 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-14 12:57 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-04-14 13:14 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Dave Page <[email protected]>
2016-04-14 13:31 ` Re: PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
@ 2016-04-14 14:46 ` Dave Page <[email protected]>
0 siblings, 0 replies; 26+ messages in thread
From: Dave Page @ 2016-04-14 14:46 UTC (permalink / raw)
To: Murtuza Zabuawala <[email protected]>; +Cc: pgadmin-hackers
Thanks - patch applied!
On Thu, Apr 14, 2016 at 2:31 PM, Murtuza Zabuawala
<[email protected]> wrote:
> Hi Dave,
>
> Sorry my bad I misinterpreted, I thought you meant rename/changing schema
> only.
>
> PFA updated patch.
>
> And For Dialog not closing issue is generic and I have already created
> internal ticket for it.
>
>
>
> --
> Regards,
> Murtuza Zabuawala
> EnterpriseDB: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
> On Thu, Apr 14, 2016 at 6:44 PM, Dave Page <[email protected]> wrote:
>>
>> Hi,
>>
>> The first issue is only partially fixed - it'll still let me try to
>> rename a shell type.
>>
>> Also, I notice when hitting Save on my changes, they're applied (if
>> they're valid), but the dialogue doesn't close. I see the following in
>> the browser console:
>>
>> Uncaught TypeError: Cannot read property 'sessChanged' of
>> null(anonymous function) @ datamodel.js:333
>> _.each._.forEach @ underscore.js:153
>> pgBrowser.DataModel.Backbone.Model.extend.toJSON @ datamodel.js:322
>> (anonymous function) @ node.js:850
>> jQuery.event.dispatch @ jquery-1.11.2.js:4665
>> elemData.handle @ jquery-1.11.2.js:4333
>>
>> On Thu, Apr 14, 2016 at 1:57 PM, Murtuza Zabuawala
>> <[email protected]> wrote:
>> > Hi Dave,
>> >
>> > PFA patch to fixed given issues.
>> >
>> >
>> > --
>> > Regards,
>> > Murtuza Zabuawala
>> > EnterpriseDB: http://www.enterprisedb.com
>> > The Enterprise PostgreSQL Company
>> >
>> > On Thu, Apr 14, 2016 at 5:11 PM, Dave Page <[email protected]> wrote:
>> >>
>> >> Thanks - patch applied. Unfortunately there are 3 more minor issues
>> >> for you to fix:
>> >>
>> >> - Renaming or changing the schema for a shell type should not be
>> >> allowed.
>> >>
>> > Done
>> >>
>> >> - I'm allowed to try to add ACL entries or security labels to an
>> >> existing shell type. This should be disallowed.
>> >>
>> > Done
>> >>
>> >> - Changing the schema on a (non-shell) type doesn't work - the type
>> >> name is omitted, e.g.
>> >>
>> > Done
>> >>
>> >> ALTER TYPE pem
>> >> SET SCHEMA pemhistory;
>> >>
>> >> Which should be:
>> >>
>> >> ALTER TYPE pem.foo
>> >> SET SCHEMA pemhistory;
>> >>
>> >> On Thu, Apr 14, 2016 at 8:36 AM, Murtuza Zabuawala
>> >> <[email protected]> wrote:
>> >> > Hi Dave,
>> >> >
>> >> > PFA updated patch which will fix mentioned issues.
>> >> >
>> >> > Hopefully the last one :-)
>> >> >
>> >> >
>> >> > --
>> >> > Regards,
>> >> > Murtuza Zabuawala
>> >> > EnterpriseDB: http://www.enterprisedb.com
>> >> > The Enterprise PostgreSQL Company
>> >> >
>> >> > On Wed, Apr 13, 2016 at 6:18 PM, Dave Page <[email protected]> wrote:
>> >> >>
>> >> >> Hi
>> >> >>
>> >> >> I'm seeing some more issues now, some related to new functionality:
>> >> >>
>> >> >> - Please don't include the descriptive header for shell types in
>> >> >> create.sql.
>> >> >
>> >> > Done
>> >> >>
>> >> >>
>> >> >> - Shell types can (and should) have owner/comments set if specified.
>> >> >>
>> >> > Done
>> >> >>
>> >> >> - The Type dropdown should just say "Shell" for shell types (no
>> >> >> additional text).
>> >> >>
>> >> > Done
>> >> >>
>> >> >> - Privileges and labels grids should be disabled for shell types.
>> >> >>
>> >> > Done
>> >> >>
>> >> >> - When I select External type, I see the following error (and a 500
>> >> >> error in the browser console):
>> >> >>
>> >> >> 2016-04-13 13:44:49,953: ERROR pgadmin: Failed to execute query
>> >> >> (execute_2darray) for the server #1 - DB:pem (Query-id: 4020898):
>> >> >> Error Message:ERROR: syntax error at or near "and"
>> >> >> LINE 2: CASE WHEN length(nspname) > 0 AND and length(nspname)
>> >> >> !=...
>> >> >>
>> >> > Done
>> >> >>
>> >> >> - Length/Precision and scale are not shown in the dialogue for
>> >> >> existing
>> >> >> types.
>> >> >>
>> >> > Done (PFA screenshot)
>> >> >
>> >> >>
>> >> >> Thanks.
>> >> >>
>> >> >>
>> >> >> On Fri, Apr 8, 2016 at 2:52 PM, Murtuza Zabuawala
>> >> >> <[email protected]> wrote:
>> >> >> > Hi Dave,
>> >> >> >
>> >> >> > Please find updated patches to fix all the mentioned issues.
>> >> >> >
>> >> >> > There are three patches,
>> >> >> > 1) One for fixing rendering issue when selecting type
>> >> >> > 2) Added utility function to fetch full type name with length &
>> >> >> > precision
>> >> >> > 3) Added new Shell type for External types.
>> >> >> >
>> >> >> >
>> >> >> > Regards,
>> >> >> > Murtuza
>> >> >> >
>> >> >> >
>> >> >> > --
>> >> >> > Regards,
>> >> >> > Murtuza Zabuawala
>> >> >> > EnterpriseDB: http://www.enterprisedb.com
>> >> >> > The Enterprise PostgreSQL Company
>> >> >> >
>> >> >> > On Thu, Apr 7, 2016 at 5:06 PM, Dave Page <[email protected]>
>> >> >> > wrote:
>> >> >> >>
>> >> >> >> Hi Murtuza
>> >> >> >>
>> >> >> >> On Tue, Mar 22, 2016 at 9:22 AM, Dave Page <[email protected]>
>> >> >> >> wrote:
>> >> >> >> > Hi
>> >> >> >> >
>> >> >> >> > On Tue, Mar 22, 2016 at 8:14 AM, Murtuza Zabuawala
>> >> >> >> > <[email protected]> wrote:
>> >> >> >> >> Hi Dave,
>> >> >> >> >>
>> >> >> >> >> We can create new external type using below method, By running
>> >> >> >> >> all
>> >> >> >> >> of
>> >> >> >> >> below
>> >> >> >> >> queries at the same time , we can not create separate external
>> >> >> >> >> type
>> >> >> >> >> by
>> >> >> >> >> only
>> >> >> >> >> using create type statement.
>> >> >> >> >>
>> >> >> >> >> So as per my discussion with Ashesh, We should not allow user
>> >> >> >> >> to
>> >> >> >> >> create
>> >> >> >> >> external type in pgAdmin4 but only show definition in edit
>> >> >> >> >> mode.
>> >> >> >> >
>> >> >> >> > Hmm, would it not make sense to allow the user to create the
>> >> >> >> > shell
>> >> >> >> > type as well (perhaps, with a new type of "SHELL")? Then they
>> >> >> >> > could
>> >> >> >> > do
>> >> >> >> > what is needed (and that should be easy, as it's just CREATE
>> >> >> >> > TYPE
>> >> >> >> > foo;)
>> >> >> >> >
>> >> >> >> > For example:
>> >> >> >> >
>> >> >> >> > CREATE TYPE box;
>> >> >> >> >
>> >> >> >> > CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ...
>> >> >> >> > ;
>> >> >> >> > CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ...
>> >> >> >> > ;
>> >> >> >> >
>> >> >> >> > CREATE TYPE box (
>> >> >> >> > INTERNALLENGTH = 16,
>> >> >> >> > INPUT = my_box_in_function,
>> >> >> >> > OUTPUT = my_box_out_function
>> >> >> >> > );
>> >> >> >> >
>> >> >> >> > CREATE TABLE myboxes (
>> >> >> >> > id integer,
>> >> >> >> > description box
>> >> >> >> > );
>> >> >> >>
>> >> >> >> In the interests of making progress, I've committed the most
>> >> >> >> recent
>> >> >> >> patch, with a number of minor changes most significantly, the
>> >> >> >> Postgres
>> >> >> >> docs and system catalogs seem to have different ideas about what
>> >> >> >> to
>> >> >> >> call length, precision and scale. pgAdmin 3 followed the catalogs
>> >> >> >> and
>> >> >> >> used length and precision, however I've updated pgAdmin 4 to use
>> >> >> >> "Length/precision" and "Scale" which is inline with the Postgres
>> >> >> >> docs.
>> >> >> >> That's only in the UI though - the code follows the catalogs.
>> >> >> >>
>> >> >> >> There are still a couple of issues - please provide fixes ASAP:
>> >> >> >>
>> >> >> >> 1) If you create a composite type that contains a sized type
>> >> >> >> (e.g.
>> >> >> >> numeric(5, 4), the precision and scale are not shown if you later
>> >> >> >> open
>> >> >> >> the properties dialogue, or in the reverse engineered SQL.
>> >> >> >>
>> >> >> >> E.g. what pgAdmin3 shows as:
>> >> >> >>
>> >> >> >> CREATE TYPE pem.blergh AS
>> >> >> >> (c1 text COLLATE pg_catalog."C",
>> >> >> >> c2 numeric(5));
>> >> >> >>
>> >> >> >> Is shown by pgAdmin4 as:
>> >> >> >>
>> >> >> >> CREATE TYPE pem.blergh AS
>> >> >> >> (c1 text COLLATE pg_catalog."C", c2 numeric);
>> >> >> >>
>> >> >> >> (adding the \n's would be good too).
>> >> >> >>
>> >> >> >> 2) If you select a different type of type in create mode, the new
>> >> >> >> options are shown below those for the previously selected type,
>> >> >> >> instead of replacing them. Please see the attached screenshot.
>> >> >> >>
>> >> >> >> 3) I would still like us to support External types. I believe the
>> >> >> >> simple option here is to re-add the code you had previously, and
>> >> >> >> to
>> >> >> >> add a new type of type called "SHELL" as discussed in my previous
>> >> >> >> email above. The user would then be able to create a SHELL type,
>> >> >> >> add
>> >> >> >> the required functions, then come back and create the EXTERNAL
>> >> >> >> type.
>> >> >> >>
>> >> >> >> I'll add cards to our internal kanban chart for these issues.
>> >> >> >>
>> >> >> >> Thanks.
>> >> >> >>
>> >> >> >> --
>> >> >> >> Dave Page
>> >> >> >> Blog: http://pgsnake.blogspot.com
>> >> >> >> Twitter: @pgsnake
>> >> >> >>
>> >> >> >> EnterpriseDB UK: http://www.enterprisedb.com
>> >> >> >> The Enterprise PostgreSQL Company
>> >> >> >
>> >> >> >
>> >> >>
>> >> >>
>> >> >>
>> >> >> --
>> >> >> Dave Page
>> >> >> Blog: http://pgsnake.blogspot.com
>> >> >> Twitter: @pgsnake
>> >> >>
>> >> >> EnterpriseDB UK: http://www.enterprisedb.com
>> >> >> The Enterprise PostgreSQL Company
>> >> >
>> >> >
>> >>
>> >>
>> >>
>> >> --
>> >> Dave Page
>> >> Blog: http://pgsnake.blogspot.com
>> >> Twitter: @pgsnake
>> >>
>> >> EnterpriseDB UK: http://www.enterprisedb.com
>> >> The Enterprise PostgreSQL Company
>> >
>> >
>>
>>
>>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>
>
--
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake
EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
^ permalink raw reply [nested|flat] 26+ messages in thread
end of thread, other threads:[~2016-04-14 14:46 UTC | newest]
Thread overview: 26+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2016-03-08 13:38 PATCH: Added Node Type & Catalog objects [pgAdmin4] Murtuza Zabuawala <[email protected]>
2016-03-10 19:41 ` Ashesh Vashi <[email protected]>
2016-03-11 16:11 ` Dave Page <[email protected]>
2016-03-11 16:14 ` Dave Page <[email protected]>
2016-03-14 12:21 ` Murtuza Zabuawala <[email protected]>
2016-03-16 13:02 ` Dave Page <[email protected]>
2016-03-16 13:18 ` Ashesh Vashi <[email protected]>
2016-03-16 14:52 ` Dave Page <[email protected]>
2016-03-17 10:08 ` Murtuza Zabuawala <[email protected]>
2016-03-17 10:43 ` Dave Page <[email protected]>
2016-03-17 11:20 ` Harshal Dhumal <[email protected]>
2016-03-18 07:15 ` Murtuza Zabuawala <[email protected]>
2016-03-18 07:26 ` Murtuza Zabuawala <[email protected]>
2016-03-18 16:52 ` Dave Page <[email protected]>
2016-03-22 08:14 ` Murtuza Zabuawala <[email protected]>
2016-03-22 09:22 ` Dave Page <[email protected]>
2016-04-07 11:36 ` Dave Page <[email protected]>
2016-04-07 11:41 ` Murtuza Zabuawala <[email protected]>
2016-04-08 13:52 ` Murtuza Zabuawala <[email protected]>
2016-04-13 12:48 ` Dave Page <[email protected]>
2016-04-14 07:36 ` Murtuza Zabuawala <[email protected]>
2016-04-14 11:41 ` Dave Page <[email protected]>
2016-04-14 12:57 ` Murtuza Zabuawala <[email protected]>
2016-04-14 13:14 ` Dave Page <[email protected]>
2016-04-14 13:31 ` Murtuza Zabuawala <[email protected]>
2016-04-14 14:46 ` Dave Page <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox