public inbox for [email protected]  
help / color / mirror / Atom feed
From: Dave Page <[email protected]>
To: Murtuza Zabuawala <[email protected]>
Cc: pgadmin-hackers <[email protected]>
Subject: Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
Date: Fri, 18 Mar 2016 16:52:45 +0000
Message-ID: <CA+OCxoz_rEVOMa1nfJxH16-ZB6YXozm-DAAV8HOE5EORqAwQLQ@mail.gmail.com> (raw)
In-Reply-To: <CAKKotZSrb=YrxbaasHQF9DXA-roFmzmA7cVC3tVPMdhYwL58cw@mail.gmail.com>
References: <CAKKotZSda6+wgyzckDJPXKK3dc8U-UXho9hmaaBp+LwZzdnhaQ@mail.gmail.com>
	<CA+OCxoxNx3ghQZ+YnWeNpyVs_BaJE+yL7C6H-PUFh6ddpr061Q@mail.gmail.com>
	<CA+OCxozEyaxygwg4KKNWKjKcLw=a-s6R64Tij9Z2JSVzzEKLrg@mail.gmail.com>
	<CAKKotZTGn2aUnkrotK5u=9hh+nD4gUC9mvKWo2FcDVuEhH5oyA@mail.gmail.com>
	<CA+OCxoyeSbbyKFm236agok7DNr4iRt16On9Kea+Z3tUj_iXa_g@mail.gmail.com>
	<CAKKotZQVJPRkHKzhix+s9zc+QnwUfWfOj+sVFfk6c7abKuyECg@mail.gmail.com>
	<CA+OCxowYVt2V-sr5YQm-YDxhmNiam-4iSudNSmydu_e2_T3eMA@mail.gmail.com>
	<CAKKotZSrb=YrxbaasHQF9DXA-roFmzmA7cVC3tVPMdhYwL58cw@mail.gmail.com>
List-Unsubscribe:  <mailto:[email protected]?body=unsub%20pgadmin-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


view thread (26+ messages)  latest in thread

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected]
  Subject: Re: PATCH: Added Node Type & Catalog objects [pgAdmin4]
  In-Reply-To: <CA+OCxoz_rEVOMa1nfJxH16-ZB6YXozm-DAAV8HOE5EORqAwQLQ@mail.gmail.com>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox