Hi,

Please find the updated Domain Module Patch.

To test this patch, please apply Backgrid Textarea Cell Patch before this.

Thanks,
Khushboo

On Wed, Mar 16, 2016 at 3:02 PM, Khushboo Vashi <
khushboo.va...@enterprisedb.com> wrote:

> Hi,
>
> On Wed, Mar 16, 2016 at 2:55 PM, Dave Page <dp...@pgadmin.org> wrote:
>
>> Hi
>>
>> On Wed, Mar 16, 2016 at 9:18 AM, Khushboo Vashi
>> <khushboo.va...@enterprisedb.com> wrote:
>> >
>> >>> - Owner and schema should be allowed to be left blank (and then
>> default
>> >>> to the current user/schema)
>> >
>> > Done
>>
>> Oh, sorry - that design changed a while back, and I've updated all the
>> existing nodes already. I thought I'd mentioned that. All we do now is
>> pre-set the default values for those two fields.
>>
> I have done in this way only. Sorry for the misunderstanding.
>
>> >>> - When adding constraints, I should be able to type directly into the
>> >>> grid. Expanding the row should be optional.
>> >
>> > I have made the grid non-editable explicitly as the Check constraint
>> control
>> > is multi-line control and right now there is no support in the grid for
>> the
>> > multi-line control.
>>
>> Not sure I follow - the mockup design you sent months ago allowed you
>> to type into the grid, and expand a row to show all fields if you
>> wanted. That is an *absolutely essential* feature enhancement for
>> pgAdmin 4 - it's required by the table design (though this will change
>> a little in other ways, like positioning of the expand row button),
>> and should be used here:
>>
>> https://www.lucidchart.com/documents/edit/610ce42d-c397-48ff-a5e7-bd92c4995715/0
>>
>> All other controls other than text-area are supported in back-grid.
> I will try to incorporate text-area as well, so we can directly type into
> the grid for this control also.
>
>
>> >>> - The comment column on the constraints grid expands when the text
>> >>> reaches ~50% of the width. It should be a fixed size (and use 100% of
>> the
>> >>> space available, less appropriate margins)
>> >
>> > I have applied the size for the each header of the grid, but if the
>> given
>> > input will be without space in the grid then it will expand. For this,
>> we
>> > can make table layout fixed. So, please suggest, should I do that or
>> not?
>>
>> Yes, I think it should be fixed. If the grid row is expanded,
>> presumably it'll show in a multi-line field anyway? Plus the
>> properties will use a multi-line field as well.
>>
>> Okay.
>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>
>
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py
new file mode 100644
index 0000000..2c273e4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py
@@ -0,0 +1,806 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Implements the Domain 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.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases.schemas as schemas
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from pgadmin.browser.server_groups.servers.databases.schemas.utils import \
+    SchemaChildModule, DataTypeReader
+from pgadmin.browser.server_groups.servers.databases.utils import \
+    parse_sec_labels_from_db
+from functools import wraps
+
+
+class DomainModule(SchemaChildModule):
+    """
+    class DomainModule(SchemaChildModule):
+
+        This class represents The Domain Module.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Initialize the Domain Module.
+
+    * get_nodes(gid, sid, did, scid)
+      - Generate the domain collection node.
+
+    * script_load()
+      - Load the module script for domain, when schema node is
+        initialized.
+    """
+
+    NODE_TYPE = 'domain'
+    COLLECTION_LABEL = gettext("Domains")
+
+    def __init__(self, *args, **kwargs):
+        super(DomainModule, self).__init__(*args, **kwargs)
+        self.min_ver = None
+        self.max_ver = None
+
+    def get_nodes(self, gid, sid, did, scid):
+        """
+        Generate the domain collection node.
+        """
+        yield self.generate_browser_collection_node(scid)
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for domain, when schema node is
+        initialized.
+        """
+        return schemas.SchemaModule.NODE_TYPE
+
+
+blueprint = DomainModule(__name__)
+
+
+class DomainView(PGChildNodeView, DataTypeReader):
+    """
+    class DomainView
+
+    This class inherits PGChildNodeView to get the different routes for
+    the module. Also, inherits DataTypeReader to get data types.
+
+    The class is responsible to Create, Read, Update and Delete operations for
+    the Domain.
+
+    Methods:
+    -------
+    * validate_request(f):
+      - Works as a decorator.
+        Validating request on the request of create, update and modified SQL.
+
+    * module_js():
+      - Load JS file (domains.js) for this module.
+
+    * check_precondition(f):
+      - Works as a decorator.
+      -  Checks database connection status.
+      -  Attach connection object and template path.
+
+    * list(gid, sid, did, scid, doid):
+      - List the Domains.
+
+    * nodes(gid, sid, did, scid):
+      - Returns all the Domains to generate Nodes in the browser.
+
+    * properties(gid, sid, did, scid, doid):
+      - Returns the Domain properties.
+
+    * get_collations(gid, sid, did, scid, doid=None):
+      - Returns Collations.
+
+    * create(gid, sid, did, scid):
+      - Creates a new Domain object.
+
+    * update(gid, sid, did, scid, doid):
+      - Updates the Domain object.
+
+    * delete(gid, sid, did, scid, doid):
+      - Drops the Domain object.
+
+    * sql(gid, sid, did, scid, doid=None):
+      - Returns the SQL for the Domain object.
+
+    * msql(gid, sid, did, scid, doid=None):
+      - Returns the modified SQL.
+
+    * get_sql(gid, sid, data, scid, doid=None):
+      - Generates the SQL statements to create/update the Domain object.
+
+    * dependents(gid, sid, did, scid, doid):
+      - Returns the dependents for the Domain object.
+
+    * dependencies(gid, sid, did, scid, doid):
+      - Returns the dependencies for the Domain object.
+
+    * types(gid, sid, did, scid, fnid=None):
+      - Returns Data Types.
+    """
+
+    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': 'doid'}
+    ]
+
+    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': 'types'}, {'get': 'types'}],
+        'get_collations': [
+            {'get': 'get_collations'},
+            {'get': 'get_collations'}
+        ]
+    })
+
+    def validate_request(f):
+        """
+        Works as a decorator.
+        Validating request on the request of create, update and modified SQL.
+
+        Required Args:
+                    name: Name of the Domain
+                    owner: Domain Owner
+                    basensp: Schema Name
+                    basetype: Data Type of the Domain
+
+        Above both the arguments will not be validated in the update action.
+        """
+
+        @wraps(f)
+        def wrap(self, **kwargs):
+
+            data = {}
+            if request.data:
+                req = json.loads(request.data.decode())
+            else:
+                req = request.args or request.form
+
+            if 'doid' not in kwargs:
+                required_args = [
+                    'name',
+                    'basetype'
+                ]
+
+                for arg in required_args:
+                    if arg not in req or req[arg] == '':
+                        return make_json_response(
+                            status=410,
+                            success=0,
+                            errormsg=gettext(
+                                "Couldn't find the required parameter \
+                                (%s)." % arg
+                            )
+                        )
+
+            try:
+                list_params = []
+                if request.method == 'GET':
+                    list_params = ['constraints', 'seclabels']
+
+                for key in req:
+                    if key in list_params and req[key] != '' \
+                            and req[key] is not None:
+                        # Coverts string into python list as expected.
+                        data[key] = json.loads(req[key])
+                    elif key == 'typnotnull':
+                        data[key] = True if req[key] == 'true' or req[key] is\
+                                                                  True else\
+                            (False if req[key] == 'false' or req[key] is
+                                                             False else '')
+                    else:
+                        data[key] = req[key]
+
+            except Exception as e:
+                return internal_server_error(errormsg=str(e))
+
+            self.request = data
+            return f(self, **kwargs)
+
+        return wrap
+
+    def module_js(self):
+        """
+        Load JS file (domains.js) for this module.
+        """
+        return make_response(
+            render_template(
+                "domains/js/domains.js",
+                _=gettext
+            ),
+            200, {'Content-Type': 'application/x-javascript'}
+        )
+
+    def check_precondition(f):
+        """
+        Works as a decorator.
+        Checks database connection status.
+        Attach connection object and template path.
+        """
+        @wraps(f)
+        def wrap(*args, **kwargs):
+            self = args[0]
+            driver = get_driver(PG_DEFAULT_DRIVER)
+            self.manager = driver.connection_manager(kwargs['sid'])
+            # Get database connection
+            self.conn = self.manager.connection(did=kwargs['did'])
+            self.qtIdent = driver.qtIdent
+
+            if not self.conn.connected():
+                return precondition_required(
+                    gettext("Connection to the server has been lost!")
+                )
+
+            ver = self.manager.version
+            server_type = self.manager.server_type
+
+            # we will set template path for sql scripts
+            if ver >= 90200:
+                self.template_path = 'domains/sql/9.2_plus'
+            elif ver >= 90100:
+                self.template_path = 'domains/sql/9.1_plus'
+
+            return f(*args, **kwargs)
+
+        return wrap
+
+    @check_precondition
+    def list(self, gid, sid, did, scid):
+        """
+        List the Domains.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+        """
+
+        SQL = render_template("/".join([self.template_path, 'node.sql']),
+                              scid=scid)
+        status, res = self.conn.execute_dict(SQL)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+        return ajax_response(
+            response=res['rows'],
+            status=200
+        )
+
+    @check_precondition
+    def nodes(self, gid, sid, did, scid):
+        """
+        Returns all the Domains to generate Nodes in the browser.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+        """
+
+        res = []
+        SQL = render_template("/".join([self.template_path, 'node.sql']),
+                              scid=scid)
+        status, rset = self.conn.execute_2darray(SQL)
+        if not status:
+            return internal_server_error(errormsg=rset)
+
+        for row in rset['rows']:
+            res.append(
+                self.blueprint.generate_browser_node(
+                    row['oid'],
+                    scid,
+                    row['name'],
+                    icon="icon-domain"
+                ))
+
+        return make_json_response(
+            data=res,
+            status=200
+        )
+
+    @check_precondition
+    def properties(self, gid, sid, did, scid, doid):
+        """
+        Returns the Domain properties.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+        """
+
+        SQL = render_template("/".join([self.template_path, 'properties.sql']),
+                              scid=scid, doid=doid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        data = res['rows'][0]
+
+        # Get Type Length and Precision
+        data.update(self._parse_type(data['fulltype']))
+
+        # Get Domain Constraints
+        SQL = render_template("/".join([self.template_path,
+                                        'get_constraints.sql']),
+                              doid=doid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        data['constraints'] = res['rows']
+
+        # Get formatted Security Labels
+        if 'seclabels' in data:
+            data.update(parse_sec_labels_from_db(data['seclabels']))
+
+        # Set System Domain Status
+        data['sysdomain'] = False
+        if doid <= self.manager.db_info[did]['datlastsysoid']:
+            data['sysdomain'] = True
+
+        return ajax_response(
+            response=data,
+            status=200
+        )
+
+    def _parse_type(self, basetype):
+        """
+        Returns Type and Data Type from the basetype.
+        """
+        typ_len = ''
+        typ_precision = ''
+
+        # The Length and the precision of the Datatype should be separate.
+        # The Format we getting from database is: numeric(1,1)
+        # So, we need to separate Length: 1, Precision: 1
+
+        if basetype != '' and basetype.find("(") > 0:
+            substr = basetype[basetype.find("(") + 1:len(
+               basetype) - 1]
+            typlen = substr.split(",")
+            if len(typlen) > 1:
+                typ_len = typlen[0]
+                typ_precision = typlen[1]
+            else:
+                typ_len = typlen
+                typ_precision = ''
+
+        return {'typlen': typ_len, 'precision': typ_precision}
+
+    @check_precondition
+    def get_collations(self, gid, sid, did, scid, doid=None):
+        """
+        Returns Collations.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+        """
+
+        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['copy_collation'],
+                            'value': row['copy_collation']}
+                           )
+
+            return make_json_response(
+                data=res,
+                status=200
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def types(self, gid, sid, did, scid, doid=None):
+        """
+        Returns the Data Types.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            fnid: Function Id
+        """
+
+        condition = """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'))"""
+
+        if self.blueprint.show_system_objects:
+            condition += " AND nsp.nspname != 'information_schema'"
+
+        # Get Types
+        status, types = self.get_types(self.conn, condition)
+
+        if not status:
+            return internal_server_error(errormsg=types)
+
+        return make_json_response(
+            data=types,
+            status=200
+        )
+
+    @check_precondition
+    @validate_request
+    def create(self, gid, sid, did, scid):
+        """
+        Creates a new Domain object.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+
+        Required Args:
+            name: Domain Name
+            owner: Owner Name
+            basensp: Schema Name
+            basetype: Domain Base Type
+
+        Returns:
+            Domain object in json format.
+        """
+
+        data = self.request
+        try:
+            SQL = render_template("/".join([self.template_path, 'create.sql']),
+                                  data=data)
+            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, below sql will
+            # gives the same
+            SQL = render_template("/".join([self.template_path,
+                                            'get_oid.sql']),
+                                  basensp=data['basensp'],
+                                  name=data['name'])
+            status, res = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            doid, scid = res['rows'][0]
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    doid,
+                    scid,
+                    data['name'],
+                    icon="icon-domain"
+                )
+            )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def delete(self, gid, sid, did, scid, doid):
+        """
+        Drops the Domain object.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+        """
+
+        if self.cmd == 'delete':
+            # This is a cascade operation
+            cascade = True
+        else:
+            cascade = False
+
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'delete.sql']),
+                                  scid=scid, doid=doid)
+            status, res = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=name)
+
+            name, basensp = res['rows'][0]
+
+            SQL = render_template("/".join([self.template_path,
+                                            'delete.sql']),
+                                  name=name, basensp=basensp, cascade=cascade)
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            return make_json_response(
+                success=1,
+                info=gettext("Domain dropped"),
+                data={
+                    'id': doid,
+                    'scid': scid,
+                    'sid': sid,
+                    'gid': gid,
+                    'did': did
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    @validate_request
+    def update(self, gid, sid, did, scid, doid):
+        """
+        Updates the Domain object.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+        """
+
+        status, SQL = self.get_sql(gid, sid, self.request, scid, doid)
+
+        if not status:
+            return internal_server_error(errormsg=SQL)
+
+        try:
+            if SQL:
+                status, res = self.conn.execute_scalar(SQL)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                # Get Schema Id
+                SQL = render_template("/".join([self.template_path,
+                                                'get_oid.sql']),
+                                      doid=doid)
+                status, res = self.conn.execute_2darray(SQL)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                scid = res['rows'][0]['scid']
+
+                return make_json_response(
+                    success=1,
+                    info="Domain updated",
+                    data={
+                        'id': doid,
+                        'scid': scid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did
+                    }
+                )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': doid,
+                        'scid': scid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did
+                    }
+                )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def sql(self, gid, sid, did, scid, doid=None):
+        """
+        Returns the SQL for the Domain object.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+        """
+
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              scid=scid, doid=doid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return False, internal_server_error(errormsg=res)
+        data = res['rows'][0]
+
+        # Get Type Length and Precision
+        data.update(self._parse_type(data['fulltype']))
+
+        # Get Domain Constraints
+        SQL = render_template("/".join([self.template_path,
+                                        'get_constraints.sql']),
+                              doid=doid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        data['constraints'] = res['rows']
+
+        # Toggle Validate and inherit options for 'CREATE Query'
+        for c in data['constraints']:
+            if 'convalidated' in c:
+                c['convalidated'] = False if c['convalidated'] else True
+            if 'connoinherit' in c:
+                c['connoinherit'] = False if c['connoinherit'] else True
+
+        SQL = render_template("/".join([self.template_path,
+                                        'create.sql']), data=data)
+
+        sql_header = """-- DOMAIN: {0}
+
+-- DROP DOMAIN {0};
+
+""".format(data['basensp'] + '.' + data['name'])
+
+        SQL = sql_header + SQL
+
+        return ajax_response(response=SQL)
+
+    @check_precondition
+    @validate_request
+    def msql(self, gid, sid, did, scid, doid=None):
+        """
+        Returns the modified SQL.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+
+        Required Args:
+            name: Domain Name
+            owner: Owner Name
+            basensp: Schema Name
+            basetype: Domain Base Type
+
+        Returns:
+            SQL statements to create/update the Domain.
+        """
+
+        status, SQL = self.get_sql(gid, sid, self.request, scid, doid)
+
+        if SQL:
+            return make_json_response(
+                data=SQL,
+                status=200
+            )
+        else:
+            return SQL
+
+    def get_sql(self, gid, sid, data, scid, doid=None):
+        """
+        Generates the SQL statements to create/update the Domain.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+        """
+
+        try:
+            if doid is not None:
+                SQL = render_template("/".join([self.template_path,
+                                                'properties.sql']),
+                                      scid=scid, doid=doid)
+                status, res = self.conn.execute_dict(SQL)
+
+                if not status:
+                    return False, internal_server_error(errormsg=res)
+
+                old_data = res['rows'][0]
+                SQL = render_template(
+                    "/".join([self.template_path, 'update.sql']),
+                    data=data, o_data=old_data)
+            else:
+                SQL = render_template("/".join([self.template_path,
+                                                'create.sql']),
+                                      data=data)
+            return True, SQL
+
+        except Exception as e:
+            return False, e
+
+    @check_precondition
+    def dependents(self, gid, sid, did, scid, doid):
+        """
+        This function get the dependents and return ajax response
+        for the Domain node.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+        """
+        dependents_result = self.get_dependents(self.conn, doid)
+        return ajax_response(
+                response=dependents_result,
+                status=200
+                )
+
+    @check_precondition
+    def dependencies(self, gid, sid, did, scid, doid):
+        """
+        This function get the dependencies and return ajax response
+        for the Domain node.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+        """
+        dependencies_result = self.get_dependencies(self.conn, doid)
+        return ajax_response(
+                response=dependencies_result,
+                status=200
+                )
+
+DomainView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py
new file mode 100644
index 0000000..d16bb60
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py
@@ -0,0 +1,653 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Implements the Domain Constraint Module."""
+
+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.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases.schemas.domains \
+    as domains
+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 DomainConstraintModule(CollectionNodeModule):
+    """
+    class DomainConstraintModule(CollectionNodeModule):
+
+        This class represents The Domain Constraint Module.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Initialize the Domain Constraint Module.
+
+    * get_nodes(gid, sid, did, scid)
+      - Generate the Domain Constraint collection node.
+
+    * node_inode(gid, sid, did, scid)
+      - Returns Domain Constraint node as leaf node.
+
+    * script_load()
+      - Load the module script for the Domain Constraint, when any of the
+        Domain node is initialized.
+    """
+    NODE_TYPE = 'domain-constraints'
+    COLLECTION_LABEL = gettext("Domain Constraints")
+
+    def __init__(self, *args, **kwargs):
+        super(DomainConstraintModule, self).__init__(*args, **kwargs)
+        self.min_ver = None
+        self.max_ver = None
+
+    def get_nodes(self, gid, sid, did, scid, doid):
+        """
+        Generate the Domain Constraint collection node.
+        """
+        yield self.generate_browser_collection_node(doid)
+
+    @property
+    def node_inode(self):
+        """
+        Returns Domain Constraint node as leaf node.
+        """
+        return False
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for the Domain Constraint, when any of the
+        Domain node is initialized.
+        """
+        return domains.DomainModule.NODE_TYPE
+
+
+blueprint = DomainConstraintModule(__name__)
+
+
+class DomainConstraintView(PGChildNodeView):
+    """
+    class DomainConstraintView(PGChildNodeView):
+
+    This class inherits PGChildNodeView to get the different routes for
+    the module.
+
+    The class is responsible to Create, Read, Update and Delete operations for
+    the Domain Constraint.
+
+    Methods:
+    -------
+
+    * module_js():
+      - Load JS file (domain-constraints.js) for this module.
+
+    * check_precondition(f):
+      - Works as a decorator.
+      -  Checks database connection status.
+      -  Attach connection object and template path.
+
+    * list(gid, sid, did, scid, doid):
+      - List the Domain Constraints.
+
+    * nodes(gid, sid, did, scid):
+      - Returns all the Domain Constraints to generate Nodes in the browser.
+
+    * properties(gid, sid, did, scid, doid):
+      - Returns the Domain Constraint properties.
+
+    * create(gid, sid, did, scid):
+      - Creates a new Domain Constraint object.
+
+    * update(gid, sid, did, scid, doid):
+      - Updates the Domain Constraint object.
+
+    * delete(gid, sid, did, scid, doid):
+      - Drops the Domain Constraint object.
+
+    * sql(gid, sid, did, scid, doid=None):
+      - Returns the SQL for the Domain Constraint object.
+
+    * msql(gid, sid, did, scid, doid=None):
+      - Returns the modified SQL.
+
+    * get_sql(gid, sid, data, scid, doid=None):
+      - Generates the SQL statements to create/update the Domain Constraint.
+        object.
+
+    * dependents(gid, sid, did, scid, doid, coid):
+      - Returns the dependents for the Domain Constraint object.
+
+    * dependencies(gid, sid, did, scid, doid, coid):
+      - Returns the dependencies for the Domain Constraint object.
+    """
+    node_type = blueprint.node_type
+
+    parent_ids = [
+        {'type': 'int', 'id': 'gid'},
+        {'type': 'int', 'id': 'sid'},
+        {'type': 'int', 'id': 'did'},
+        {'type': 'int', 'id': 'scid'},
+        {'type': 'int', 'id': 'doid'}
+    ]
+    ids = [
+        {'type': 'int', 'id': 'coid'}
+    ]
+
+    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'}]
+    })
+
+    def module_js(self):
+        """
+        Load JS file (domain-constraints.js) for this module.
+        """
+        return make_response(
+            render_template(
+                "domain-constraints/js/domain-constraints.js",
+                _=gettext
+            ),
+            200, {'Content-Type': 'application/x-javascript'}
+        )
+
+    def check_precondition(f):
+        """
+        Works as a decorator.
+        Checks database connection status.
+        Attach connection object and template path.
+        """
+        @wraps(f)
+        def wrap(*args, **kwargs):
+            self = args[0]
+            driver = get_driver(PG_DEFAULT_DRIVER)
+            self.manager = driver.connection_manager(kwargs['sid'])
+            self.conn = self.manager.connection(did=kwargs['did'])
+            self.qtIdent = driver.qtIdent
+
+            # 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!")
+                )
+
+            ver = self.manager.version
+
+            # we will set template path for sql scripts
+            if ver >= 90200:
+                self.template_path = 'domain-constraints/sql/9.2_plus'
+            elif ver >= 90100:
+                self.template_path = 'domain-constraints/sql/9.1_plus'
+
+            return f(*args, **kwargs)
+
+        return wrap
+
+    @check_precondition
+    def list(self, gid, sid, did, scid, doid):
+        """
+        List the Domain Constraints.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+        """
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              doid=doid)
+        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, doid):
+        """
+        Returns all the Domain Constraints.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+        """
+        res = []
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              doid=doid)
+        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'],
+                    doid,
+                    row['name'],
+                    icon="icon-domain-constraints"
+                ))
+
+        return make_json_response(
+            data=res,
+            status=200
+        )
+
+    @check_precondition
+    def properties(self, gid, sid, did, scid, doid, coid):
+        """
+        Returns the Domain Constraints property.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+            coid: Domain Constraint Id
+        """
+
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              doid=doid, coid=coid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        data = res['rows'][0]
+        if 'convalidated' in data:
+            data['convalidated'] = not data['convalidated']
+        return ajax_response(
+            response=data,
+            status=200
+        )
+
+    @check_precondition
+    def create(self, gid, sid, did, scid, doid):
+        """
+        Creates a new Domain Constraint object.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+
+        Required Args:
+            name:  Constraints Name
+            consrc: Constraints Check
+
+        Returns:
+            Domain Constraint object in json format.
+        """
+
+        data = request.form if request.form else \
+            json.loads(request.data.decode())
+        required_args = [
+            'name',
+            'consrc'
+        ]
+
+        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)." % arg
+                    )
+                )
+
+        try:
+            # Get Schema and Domain.
+            SQL = render_template("/".join([self.template_path,
+                                            'get_domain.sql']),
+                                  doid=doid)
+            status, res = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            domain, schema = res['rows'][0]
+
+            SQL = render_template("/".join([self.template_path,
+                                            'create.sql']),
+                                  data=data, domain=domain, schema=schema)
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            # Get the recently added constraints oid
+            SQL = render_template("/".join([self.template_path,
+                                            'get_oid.sql']),
+                                  doid=doid, name=data['name'])
+            status, coid = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=doid)
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    coid,
+                    doid,
+                    data['name'],
+                    icon="icon-domain-constraints"
+                )
+            )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def delete(self, gid, sid, did, scid, doid, coid):
+        """
+        Drops the Domain Constraint object.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+            coid: Domain Constraint Id
+        """
+        try:
+            SQL = render_template("/".join([self.template_path,
+                                            'properties.sql']),
+                                  doid=doid, coid=coid)
+            status, res = self.conn.execute_dict(SQL)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data = res['rows'][0]
+
+            SQL = render_template("/".join([self.template_path,
+                                            'delete.sql']),
+                                  data=data)
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            return make_json_response(
+                success=1,
+                info=gettext("Domain Constraint dropped"),
+                data={
+                    'id': doid,
+                    'scid': scid,
+                    'sid': sid,
+                    'gid': gid,
+                    'did': did
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def update(self, gid, sid, did, scid, doid, coid):
+        """
+        Updates the Domain Constraint object.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+            coid: Domain Constraint Id
+        """
+        data = request.form if request.form else \
+            json.loads(request.data.decode())
+
+        status, SQL = self.get_sql(gid, sid, data, scid, doid, coid)
+
+        try:
+            if SQL and status:
+                status, res = self.conn.execute_scalar(SQL)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                return make_json_response(
+                    success=1,
+                    info="Domain Constraint updated",
+                    data={
+                        'id': coid,
+                        'doid': doid,
+                        'scid': scid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did
+                    }
+                )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': coid,
+                        'doid': doid,
+                        'scid': scid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did
+                    }
+                )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def sql(self, gid, sid, did, scid, doid, coid=None):
+        """
+        Returns the SQL for the Domain Constraint object.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+            coid: Domain Constraint Id
+        """
+
+        # Get Schema and Domain.
+        SQL = render_template("/".join([self.template_path,
+                                        'get_domain.sql']),
+                              doid=doid)
+        status, res = self.conn.execute_2darray(SQL)
+        if not status:
+            return internal_server_error(errormsg=name)
+
+        domain, schema = res['rows'][0]
+
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                              doid=doid, coid=coid)
+        status, res = self.conn.execute_dict(SQL)
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        data = res['rows'][0]
+        if 'convalidated' in data:
+            data['convalidated'] = False if data['convalidated'] else True
+        if 'connoinherit' in data:
+            data['connoinherit'] = False if data['connoinherit'] else True
+
+        SQL = render_template("/".join([self.template_path,
+                                        'create.sql']),
+                              data=data, domain=domain, schema=schema)
+
+        sql_header = """-- CHECK: {0}
+
+-- ALTER DOMAIN {1} DROP CONSTRAINT {0};
+
+""".format(data['name'],schema + '.' + domain)
+
+        SQL = sql_header + SQL
+
+        return ajax_response(response=SQL)
+
+    @check_precondition
+    def msql(self, gid, sid, did, scid, doid, coid=None):
+        """
+        Returns the modified SQL.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+            coid: Domain Constraint Id
+
+        Required Args:
+            name:   Constraints Name
+            consrc: Constraints Check
+
+        Returns:
+            Domain Constraint object in json format.
+        """
+        data = request.args
+
+        if coid is None:
+            required_args = [
+                'name',
+                'consrc'
+            ]
+
+            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)." % arg
+                        )
+                    )
+        status, SQL = self.get_sql(gid, sid, data, scid, doid, coid)
+        if status and SQL:
+            return make_json_response(
+                data=SQL,
+                status=200
+            )
+        else:
+            return SQL
+
+    def get_sql(self, gid, sid, data, scid, doid, coid=None):
+        """
+        Generates the SQL statements to create/update the Domain Constraint.
+
+         Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+            coid: Domain Constraint Id
+        """
+        try:
+            if coid is not None:
+                SQL = render_template("/".join([self.template_path,
+                                                'properties.sql']),
+                                      doid=doid, coid=coid)
+                status, res = self.conn.execute_dict(SQL)
+
+                if not status:
+                    return False, internal_server_error(errormsg=res)
+
+                old_data = res['rows'][0]
+
+                SQL = render_template(
+                    "/".join([self.template_path, 'update.sql']),
+                    data=data, o_data=old_data, conn=self.conn
+                    )
+            else:
+                SQL = render_template("/".join([self.template_path,
+                                                'get_domain.sql']),
+                                      doid=doid)
+                status, res = self.conn.execute_2darray(SQL)
+
+                if not status:
+                    return False, internal_server_error(errormsg=name)
+
+                domain, schema = res['rows'][0]
+
+                SQL = render_template("/".join([self.template_path,
+                                                'create.sql']),
+                                      data=data, domain=domain, schema=schema)
+            return True, SQL
+        except Exception as e:
+            return False, internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def dependents(self, gid, sid, did, scid, doid, coid):
+        """
+        This function get the dependents and return ajax response
+        for the Domain Constraint node.
+
+        Args:
+            gid:  Server Group Id
+            sid:  Server Id
+            did:  Database Id
+            scid: Schema Id
+            doid: Domain Id
+            coid: Domain Constraint Id
+        """
+        dependents_result = self.get_dependents(self.conn, coid)
+        return ajax_response(
+                response=dependents_result,
+                status=200
+                )
+
+    @check_precondition
+    def dependencies(self, gid, sid, did, scid, doid, coid):
+        """
+        This function get the dependencies and return ajax response
+        for the Domain Constraint node.
+
+        Args:
+            gid: Server Group Id
+            sid: Server Id
+            did: Database Id
+            scid: Schema Id
+            doid: Domain Id
+            coid: Domain Constraint Id
+        """
+        dependencies_result = self.get_dependencies(self.conn, coid)
+        return ajax_response(
+                response=dependencies_result,
+                status=200
+                )
+
+DomainConstraintView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png
new file mode 100644
index 0000000000000000000000000000000000000000..d62e13705c50e6c0cf8f19d680053e8643e28751
GIT binary patch
literal 314
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=2TrO847{Jl`|t`Qpas
zn<hS+l=rMY>RGPqvlydizRJ&>B`Om#V}R-yOM?7@862M7NCR>>3p^r=fwTu0yPeFo
z12TL)T^vI=t|uoPU||ZF<tgaHG*QsQ!?m&Tq=?3mCu}J#Dx3x@mM}}^iE=5NIWXnk
zkpnC4ai%a>@;Gg7=uums=9bIK=Egd~(us-3g@Iv02gfsK^JP^)gH=mhBT7;dOH!?p
zi&B9UgOP!ufv%yEu7P2Qk%5(ov6YF5wt=aYfq}(LRXG$5x%nxXX_XKS29{tAAk|g|
XW)KahriZQpYGCkm^>bP0l+XkKyyRU}

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png
new file mode 100644
index 0000000000000000000000000000000000000000..32a045b8fafdc08640d53b2a86b1dcabcb0fe0fd
GIT binary patch
literal 579
zcmV-J0=)f+P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp)
z=>Px$AW%$HMR1Z9#Q*@REHmdHAm<<;=p`lTDl6$LE15$|nM6yxJ3#6&F}^)Qx<p3n
zH#h4zIK)Ou#79fTM@#HJKkY(8?L$QEL`CjJMeasM?ng(;QB>|oNbX2U?n+AUOH1!e
zOz%xi?@dncPEXKRS?^Cz#amtQQBm+xQt(q#@KaRqR8;U)Rq<9<@mE*YVq(`~V)0vB
z+GS<)VPW%PV%=$Jag!JHXlV6qZS`($_HuH3pd<EncJ_C7f1@XWqbGu>Du=8uhpjJ)
zvN4agH<i3UrMYgWyK<eyN1Vq+p2tbA!G5C3PO!m$q|HvV#D$~JOsv;ftk_qv-CeQX
zT*cC%$J3;><6_L$t<Kr8)!w|r@o>cPam4X*+~mi`^K{Glc|xh<NdN!<0d!JMQvg8b
z*k%9#00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipf34<ROg
z(p72z0056kL_t&-)1{2Z3c^qT1mDEody6IZuCW&s1V!`^4}$goe?(18@b2Db*j*w1
z%4M(p;&Zw?9ZaKAxo*x;COX}<8fzjqKRv^2kF1spymYHMjE2m7Hl|a~emCNwG8(i?
z8I#``(kiBrEbh}(Qn8TL2+$}b3Hn^7p`K6R#_6zQ2$?ux;lXBYB>hkN@C%g?4vVBM
R$bbL<002ovPDHLkV1min0OSAw

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png
new file mode 100644
index 0000000000000000000000000000000000000000..9d1d2a061c7948168d7b1c2474d769b31709f1cf
GIT binary patch
literal 406
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE08{VY2nhHc^eMaU%j`d
zaI$#+HuEKC{pKEZKYn>h(+aIMH^MjGjcs3}f9Cqyt&fvx7AT*)xv^`L;hf{Hi_iP4
zxgK%&V?q65_4c*;8}G%;JMMY<SLM___M4Bi9{E^!<YUpX&n1ga`7PgFwEkdS!(#P<
zNn&@N9OqiK(i&(nV@Z%-FoVOh8)-leXMsm#F_88EW4Dvpc0fjir;B5V#O36K1sn!O
zhRVf}5jSsGPH?f<xudc|vBoY<&5cb=uTGB9v187J4II+it5?jhn8F{TsCZIWRad$D
zg1*K9W%cu2NsD(hEfVT#*wm#pYw;D04+0E(uQE?s`Z6*ZXoqTvYeY#(Vo9o1a#1Rf
zVlXl=G|)9P(lsy)F*2|+F}5->(Kax(GBB{1sVaw}AvZrIGp!P$!N3x%0i@c>zzm|{
T)b!9bKn)C@u6{1-oD!M<=4O^k

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js
new file mode 100644
index 0000000..166cabc
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js
@@ -0,0 +1,149 @@
+// Domain Constraint Module: Collection and Node
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+  // Define Domain Constraint Collection Node
+  if (!pgBrowser.Nodes['coll-domain-constraints']) {
+    var domain_constraints = pgAdmin.Browser.Nodes['coll-domain-constraints'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'domain-constraints',
+        label: '{{ _('Domain Constraints') }}',
+        type: 'coll-domain-constraints',
+        columns: ['name', 'description']
+      });
+  };
+
+  // Domain Constraint Node
+  if (!pgBrowser.Nodes['domain-constraints']) {
+    pgAdmin.Browser.Nodes['domain-constraints'] = pgBrowser.Node.extend({
+      type: 'domain-constraints',
+      label: '{{ _('Domain Constraints') }}',
+      collection_type: 'coll-domain-constraints',
+      hasSQL: true,
+      hasDepends: true,
+      parent_type: ['domain'],
+      Init: function() {
+        // Avoid mulitple registration of menus
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_domain_on_coll', node: 'coll-domain-constraints', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}',
+          icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_domain-constraints', node: 'domain-constraints', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}',
+          icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_domain-constraints', node: 'domain', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}',
+          icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: false},
+          enable: 'canCreate'
+        }
+        ]);
+
+      },
+      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          description: undefined,
+          consrc: undefined,
+          connoinherit: undefined,
+          convalidated: undefined,
+          convalidated_p: undefined
+        },
+        // Domain Constraint Schema
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', type:'text', cell:'string',
+          disabled: 'isDisabled'
+        },{
+          id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'description', label: '{{ _('Comment') }}', type: 'multiline', cell:
+          'string', mode: ['properties', 'create', 'edit'], min_version: 90500,
+        },{
+          id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', cel:
+          'string', group: '{{ _('Definition') }}', mode: ['properties',
+          'create', 'edit'], disabled: function(m) { return !m.isNew(); }
+        },{
+          id: 'connoinherit', label: '{{ _('No Inherit') }}', type:
+          'switch', cell: 'boolean', group: '{{ _('Definition') }}', mode:
+          ['properties', 'create', 'edit'], disabled: 'isDisabled',
+          visible: false
+        },{
+          id: 'convalidated', label: "{{ _("Don't Validate") }}", type: 'switch', cell:
+          'boolean', group: '{{ _('Definition') }}', disabled: function(m) {
+          if (!m.isNew()) {
+            var server = this.node_info.server;
+            if (server.version < 90200)
+            {
+              return true;
+            }
+            else if(!m.get('convalidated'))
+            {
+                return true;
+            }
+            return false;
+          }
+          return true;
+          },
+          mode: ['create', 'edit'], visible: function(m) { return !m.isNew() }
+        },{
+          id: 'convalidated_p', label: '{{ _('Valid?') }}', type: 'switch', cell:
+          'boolean', group: '{{ _('Definition') }}', disabled: 'isDisabled',
+          mode: ['properties']
+        }],
+        // Client Side Validation
+        validate: function() {
+          var err = {},
+              errmsg;
+
+          if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+            err['name'] = '{{ _('Name can not be empty!') }}';
+            errmsg = errmsg || err['name'];
+          }
+
+          if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') {
+            err['consrc'] = '{{ _('Check can not be empty!') }}';
+            errmsg = errmsg || err['consrc'];
+          }
+
+          this.errorModel.clear().set(err);
+
+          if (_.size(err)) {
+            this.trigger('on-status', {msg: errmsg});
+            return errmsg;
+          }
+
+          return null;
+
+        },
+        isDisabled: function(m){
+          if (!m.isNew()) {
+            var server = this.node_info.server;
+            if (server.version < 90200)
+            {
+              return true;
+            }
+          }
+          return false;
+        }
+      }),
+  });
+
+  }
+
+  return pgBrowser.Nodes['domain'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql
new file mode 100644
index 0000000..be943d2
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql
@@ -0,0 +1,3 @@
+{% if data and schema and domain %}
+ALTER DOMAIN {{ conn|qtIdent(schema, domain) }}
+    ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }});{% endif -%}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..260c3c0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql
@@ -0,0 +1,4 @@
+{% if data %}
+ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }}
+    DROP CONSTRAINT {{ conn|qtIdent(data.name) }};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql
new file mode 100644
index 0000000..1040c0e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql
@@ -0,0 +1,8 @@
+SELECT
+    d.typname as domain, bn.nspname as schema
+FROM
+    pg_type d
+JOIN
+    pg_namespace bn ON bn.oid=d.typnamespace
+WHERE
+    d.oid = {{doid}};
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..f59e08c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,7 @@
+SELECT
+    oid, conname as name
+FROM
+    pg_constraint
+WHERE
+    contypid = {{doid}}::oid
+    AND conname={{ name|qtLiteral }};
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..043f011
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql
@@ -0,0 +1,14 @@
+SELECT
+    c.oid, conname AS name, typname AS relname, nspname,
+    regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc
+FROM
+    pg_constraint c
+JOIN
+    pg_type t ON t.oid=contypid
+JOIN
+    pg_namespace nl ON nl.oid=typnamespace
+WHERE
+    contype = 'c' AND contypid =  {{doid}}::oid
+{% if coid %}
+    AND c.oid = {{ coid }}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..299ba6b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql
@@ -0,0 +1,3 @@
+{% if data.name %}
+ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
+    RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql
new file mode 100644
index 0000000..513c38d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql
@@ -0,0 +1,9 @@
+{% if data and schema and domain %}
+ALTER DOMAIN {{ conn|qtIdent(schema, domain) }}
+    ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }}){% if data.convalidated %}
+
+    NOT VALID{% endif %};{% if data.description %}
+
+
+COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON DOMAIN {{ conn|qtIdent(schema, domain) }}
+    IS '{{ data.description }}';{% endif %}{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql
new file mode 100644
index 0000000..260c3c0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql
@@ -0,0 +1,4 @@
+{% if data %}
+ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }}
+    DROP CONSTRAINT {{ conn|qtIdent(data.name) }};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql
new file mode 100644
index 0000000..1040c0e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql
@@ -0,0 +1,8 @@
+SELECT
+    d.typname as domain, bn.nspname as schema
+FROM
+    pg_type d
+JOIN
+    pg_namespace bn ON bn.oid=d.typnamespace
+WHERE
+    d.oid = {{doid}};
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql
new file mode 100644
index 0000000..f59e08c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql
@@ -0,0 +1,7 @@
+SELECT
+    oid, conname as name
+FROM
+    pg_constraint
+WHERE
+    contypid = {{doid}}::oid
+    AND conname={{ name|qtLiteral }};
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql
new file mode 100644
index 0000000..34d8b34
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql
@@ -0,0 +1,17 @@
+SELECT
+    c.oid, conname AS name, typname AS relname, nspname, description,
+    regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc,
+    connoinherit, convalidated, convalidated AS convalidated_p
+FROM
+    pg_constraint c
+JOIN
+    pg_type t ON t.oid=contypid
+JOIN
+    pg_namespace nl ON nl.oid=typnamespace
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass)
+WHERE
+    contype = 'c' AND contypid =  {{doid}}::oid
+{% if coid %}
+    AND c.oid = {{ coid }}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql
new file mode 100644
index 0000000..b436f3a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql
@@ -0,0 +1,13 @@
+{% set name = o_data.name %}
+{% if data.name %}
+{% set name = data.name %}
+ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
+    RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif -%}{% if data.convalidated %}
+
+
+ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
+    VALIDATE CONSTRAINT {{ conn|qtIdent(name) }}{% endif -%}{% if data.description %}
+
+
+COMMENT ON CONSTRAINT {{ conn|qtIdent(name) }} ON DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
+    IS '{{ data.description }}';{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png
new file mode 100644
index 0000000000000000000000000000000000000000..55621528a1dba4928538fe5557b9b988ed78d6ab
GIT binary patch
literal 462
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}U4T!BE0BJeoqfW=;gF{0PD8_o
zi&pN_(K)B7`FPi%2ix`@v9LU(tNUQ*!K1czXOxuggoGT_(#p@zf4Jw!!zHV)`3F7T
zd-Um}H}`iRIijcc@Wq#>&ptkR`SszG54Uz6zW4mg?MLsgZ9jbb>E~y!zTSB9`Re1(
zjS1S99(_9h@YC4`A5Y)^_^>@J3+MvIk|4ie28U-i(tsS!0*}aIAngIhZYQ(tfQ)ue
z7sn8Z%b|U@#hMgESUvs4wKoSxO>~{O=-Yq$8_!r)-^e%4-l6f-Jox1%8}FIVx>FCW
zPMVwluQGbkP1bcSwVV0d_?PZbZVis%HeYTWyU*$OvYp$uTc3QLbD?2trOnDuhZa>f
z=3d_{(zER9xt(<n3UsBI4SNC?Y!rBW<m2`n>yDRyg$uoE8Qx}bo1D0qw;t#u)e_f;
zl9a@fRIB8oR3OD*WMF8ZYiOivU>IU#U}a)#Wn!XjU}|MxU@=ow4n;$5eoAIqB}9XP
eC0GMUwUvPxM8m1+p=*E|7(8A5T-G@yGywoRCC2Lj

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png
new file mode 100644
index 0000000000000000000000000000000000000000..7521cddeaaaf0ee4e3c60e948078d70e17e06893
GIT binary patch
literal 401
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}T7XZ8E0Deu9eu*V;gF{0K`pIg
zc6K`r4IeIAc}Q3HxV`=3y+@xudUJpGkt2F~PoI5!^78A$Cm(L@JbcYR=;4bm_nv>b
z{pkI*?T7D#gyiSvKYR7{_S4Tdo_xOg_;X`|_N7Ok&OiKg_QA)~_dlLo85{w$iLoTe
zFPOpM*^M+HhqJ&VvKUBvfU(=jY&#$$$<xI#MB;Mq`IABp20RT9wUXkaL(1R(pOn?c
z>38<+e>KaN2{%0Jd@sMbY$+13Z&%Z<%!P+*SNu+#({Rr={_KxUhswHdID0QWTCBvD
z)^ky;@gu`E#RAC#l`JcnelS0rxkKN7b1B>H56gX)0&P<*ag8WRNi0dVN-jzTQVd20
zh6cKZM!E)uAw~vPCdO7KCfWw3Rt5$ZGgakKH00)|WTsU@G#FTdHGouG8JIydoSGiG
Q2B?9-)78&qol`;+03_q3ga7~l

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png
new file mode 100644
index 0000000000000000000000000000000000000000..42ca929325854b8f34787425e8094d08c75983bc
GIT binary patch
literal 424
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}PJmB{E0BJeoqfW=;gF{0PD8^7
zi&s6`bL8oxH}`iRdHU?*lb2r~J^6Th=ix^$zTSWS<?f>oH+CHUa^}pVf`aWvMt7fm
zx&8F>jfd~AY(HF`mUiRG=W9<sUwQB@KR>@QLHo+%PZuA3I{)y~*$1D_JotF({zvHt
zo%ulf7)yfuf*Bm1-ADs+I14-?i-EKU7`vU!wgWPXJzX3_Brcbpe=XFcAmSFNeC3q&
zl!Z+(RsYN12&-NW{P*^#<rx82_2-+ii&SqfGgO`Jcj@;01@HTH{&&7#v?j}I)=J6m
ze`~hppZn7?WA|%z!}T6pf>z0IjbZrS5ICD*275*_bAo)r8t#Vg4A*Lz);xc4JKq03
zXXC}zd)YwiRZCnWN>UO_QmvAUQh^kMk%6IsuAz~xfnkV|ft87|m5GVAfvJ^&fyGQ!
qITQ`K`6-!cl@JXEmS7Da)m8>(5DllMhpqu?VDNPHb6Mw<&;$VOR=Fqu

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js
new file mode 100644
index 0000000..598e542
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js
@@ -0,0 +1,323 @@
+// Domain Module: Collection and Node.
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin',
+         'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+  // Define Domain Collection Node
+  if (!pgBrowser.Nodes['coll-domain']) {
+    var domains = pgAdmin.Browser.Nodes['coll-domain'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'domain',
+        label: '{{ _('Domains') }}',
+        type: 'coll-domain',
+        columns: ['name', 'owner', 'description']
+      });
+  };
+
+  // Security Model
+  var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({
+    defaults: {
+      provider: null,
+      security_label: null
+    },
+    schema: [{
+      id: 'provider', label: '{{ _('Provider') }}',
+      type: 'text', editable: true, cellHeaderClasses:'width_percent_50'
+    },{
+      id: 'security_label', label: '{{ _('Security Label') }}',
+      type: 'text', editable: true, cellHeaderClasses:'width_percent_50'
+    }],
+    validate: function() {
+      var err = {},
+          errmsg = null;
+
+      if (_.isUndefined(this.get('security_label')) ||
+        _.isNull(this.get('security_label')) ||
+        String(this.get('security_label')).replace(/^\s+|\s+$/g, '') == '') {
+            errmsg =  '{{ _('Please specify the value for all the security providers.')}}';
+            this.errorModel.set('security_label', errmsg);
+            return errmsg;
+          } else {
+            this.errorModel.unset('security_label');
+          }
+      return null;
+    }
+  });
+
+  // Constraint Model
+  var ConstraintModel = pgAdmin.Browser.Node.Model.extend({
+    idAttribute: 'conoid',
+    defaults: {
+      conoid: undefined,
+      conname: undefined,
+      consrc: undefined,
+    },
+    schema: [{
+      id: 'conname', label: '{{ _('Name') }}', type: 'text', cell: 'string',
+      cellHeaderClasses: 'width_percent_40', editable: 'isEditable'
+    },{
+      id: 'consrc', label: '{{ _('Check') }}', type: 'multiline',
+      cell: Backgrid.Extension.TextareaCell, group: '{{ _('Definition') }}',
+      cellHeaderClasses: 'width_percent_60', editable: 'isEditable'
+    }],
+    isEditable: function(m) {
+      return _.isUndefined(m.isNew) ? true : m.isNew();
+    },
+    toJSON: Backbone.Model.prototype.toJSON
+  });
+
+  // Domain Node
+  if (!pgBrowser.Nodes['domain']) {
+    pgAdmin.Browser.Nodes['domain'] = pgBrowser.Node.extend({
+      type: 'domain',
+      label: '{{ _('Domain') }}',
+      collection_type: 'coll-domain',
+      hasSQL: true,
+      hasDepends: true,
+      parent_type: ['schema'],
+      Init: function() {
+        // Avoid mulitple registration of menus
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_domain_on_coll', node: 'coll-domain', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Domain...') }}',
+          icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_domain', node: 'domain', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Domain...') }}',
+          icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        },{
+          name: 'create_domain', node: 'schema', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Domain...') }}',
+          icon: 'wcTabIcon icon-domain', data: {action: 'create', check: false},
+          enable: 'canCreate'
+        }
+        ]);
+
+      },
+      canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+      canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+      // Domain Node Model
+      model: pgAdmin.Browser.Node.Model.extend({
+        initialize: function(attrs, args) {
+          var isNew = (_.size(attrs) === 0);
+          if (isNew) {
+            // Set Selected Schema
+            schema = args.node_info.schema.label
+            this.set({'basensp': schema}, {silent: true});
+
+            // Set Current User
+            var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
+            this.set({'owner': userInfo.name}, {silent: true});
+          }
+          pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+        },
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          owner: undefined,
+          basensp: undefined,
+          description: undefined,
+          basetype: undefined,
+          typlen: undefined,
+          precision: undefined,
+          typdefault: undefined,
+          typnotnull: undefined,
+          sysdomain: undefined,
+          collname: undefined,
+          constraints: [],
+          seclabels: []
+        },
+        type_options: undefined,
+        // Domain Schema
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', cell: 'string',
+          type: 'text', mode: ['properties', 'create', 'edit']
+        },{
+          id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl,
+          node: 'role',  type: 'text', mode: ['edit', 'create', 'properties']
+        },{
+          id: 'basensp', label:'{{ _('Schema') }}', cell: 'node-list-by-name',
+           control: 'node-list-by-name', cache_level: 'database', type: 'text',
+           node: 'schema'
+        },{
+          id: 'description', label:'{{ _('Comment') }}', cell: 'string',
+          type: 'multiline'
+        },{
+          id: 'basetype', label:'{{ _('Base Type') }}', cell: 'string', control: 'node-ajax-options',
+          type: 'text', mode:['properties', 'create', 'edit'], group: '{{ _('Definition') }}', url: 'get_types',
+          disabled: function(m) { return !m.isNew(); }, first_empty: true,
+          transform: function(d){
+            this.model.type_options =  d;
+            return d;
+          }
+        },{
+          id: 'typlen', label:'{{ _('Length') }}', cell: 'string',
+          type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'],
+          disabled: function(m) {
+            // We will store type from selected from combobox
+            if (!m.isNew()) {
+              return true;
+            }
+            var of_type = m.get('basetype');
+            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') }}', cell: 'string',
+          type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'],
+          disabled: function(m) {
+            // We will store type from selected from combobox
+            if (!m.isNew()) {
+              return true;
+            }
+            var of_type = m.get('basetype');
+            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: 'typdefault', label:'{{ _('Default') }}', cell: 'string',
+          type: 'text', group: '{{ _('Definition') }}'
+        },{
+          id: 'typnotnull', label:'{{ _('Not Null') }}', cell: 'boolean',
+          type: 'switch', group: '{{ _('Definition') }}'
+        },{
+          id: 'sysdomain', label:'{{ _('System Domain?') }}', cell: 'boolean',
+          type: 'switch', group: '{{ _('Definition') }}', mode: ['properties']
+        },{
+          id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options',
+          type: 'text', group: '{{ _('Definition') }}', url: 'get_collations', disabled: function(m) {
+            return !m.isNew();
+          }
+        },{
+          id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string',
+          type: 'collection', group: '{{ _('Constraints') }}', mode: ['edit', 'create'],
+          model: ConstraintModel, canAdd: true, canDelete: true,
+          canEdit: false
+         },{
+          id: 'seclabels', label: '{{ _('Security Labels') }}',
+          model: SecurityModel, type: 'collection',
+          group: '{{ _('Security') }}', mode: ['edit', 'create'],
+          min_version: 90100, canAdd: true,
+          canEdit: false, canDelete: true
+        }
+        ],
+        validate: function() // Client Side Validation
+        {
+          var err = {},
+              errmsg,
+              seclabels = this.get('seclabels');
+
+          if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+            err['name'] = '{{ _('Name can not be empty!') }}';
+            errmsg = errmsg || err['name'];
+          }
+
+          if (_.isUndefined(this.get('basetype')) || String(this.get('basetype')).replace(/^\s+|\s+$/g, '') == '') {
+            err['basetype'] = '{{ _('Base Type can not be empty!') }}';
+            errmsg = errmsg || err['basetype'];
+          }
+
+          if (seclabels) {
+            var secLabelsErr;
+            for (var i = 0; i < seclabels.models.length && !secLabelsErr; i++) {
+              secLabelsErr = (seclabels.models[i]).validate.apply(seclabels.models[i]);
+              if (secLabelsErr) {
+                err['seclabels'] = secLabelsErr;
+                errmsg = errmsg || secLabelsErr;
+              }
+            }
+          }
+
+          this.errorModel.clear().set(err);
+
+          return null;
+        }
+      }),
+      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 domain
+          if (_.indexOf(['schema'], d._type) > -1)
+            return true;
+
+          if ('coll-domain' == 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;
+      },
+      isDisabled: function(m){
+          if (!m.isNew()) {
+            var server = this.node_info.server;
+            if (server.version < 90200)
+            {
+              return false;
+            }
+          }
+          return true;
+        }
+  });
+
+  }
+
+  return pgBrowser.Nodes['domain'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql
new file mode 100644
index 0000000..f8b0b75
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql
@@ -0,0 +1,30 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% if data %}
+CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }}
+    AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname %}
+
+    COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %}
+
+    DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %}
+
+    NOT NULL{% endif %}{% if data.constraints %}{% for c in data.constraints %}{% if c.conname and c.consrc %}
+
+    CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% endif -%}
+{% endfor -%}
+{% endif -%};
+
+{% if data.owner %}
+ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.description %}
+
+
+COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }}
+    IS '{{ data.description }}';{% endif -%}{% if data.seclabels %}
+{% for r in data.seclabels %}
+{% if r.security_label and r.provider %}
+
+
+{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%}
+{% endfor -%}
+{% endif -%}
+
+{% endif -%}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..7a12b50
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql
@@ -0,0 +1,16 @@
+{% if scid and doid %}
+SELECT
+    d.typname as name, bn.nspname as basensp
+FROM
+    pg_type d
+JOIN
+    pg_namespace bn ON bn.oid=d.typnamespace
+WHERE
+    d.typnamespace = {{scid}}::oid
+AND
+    d.oid={{doid}}::int;
+{% endif %}
+
+{% if name %}
+DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql
new file mode 100644
index 0000000..819fdbb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql
@@ -0,0 +1,10 @@
+SELECT --nspname, collname,
+    CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN
+    concat(nspname, '."', collname,'"')
+    ELSE '' END AS copy_collation
+FROM
+    pg_collation c, pg_namespace n
+WHERE
+    c.collnamespace=n.oid
+ORDER BY
+    nspname, collname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql
new file mode 100644
index 0000000..897fb24
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql
@@ -0,0 +1,15 @@
+SELECT
+    'DOMAIN' AS objectkind, c.oid as conoid, conname, typname as relname, nspname, description,
+    regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as cons
+FROM
+    pg_constraint c
+JOIN
+    pg_type t ON t.oid=contypid
+JOIN
+    pg_namespace nl ON nl.oid=typnamespace
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass)
+WHERE
+    contype = 'c'
+    AND contypid =  {{doid}}::oid
+ORDER BY conname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql
new file mode 100644
index 0000000..8b5c891
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,18 @@
+{% if doid %}
+SELECT
+    d.typnamespace as scid
+FROM
+    pg_type d
+WHERE
+    d.oid={{ doid }}::oid;
+{% else %}
+SELECT
+    d.oid, d.typnamespace
+FROM
+    pg_type d
+JOIN
+    pg_namespace bn ON bn.oid=d.typnamespace
+WHERE
+    bn.nspname = {{ basensp|qtLiteral }}
+    AND d.typname={{ name|qtLiteral }};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql
new file mode 100644
index 0000000..7bd3e5b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql
@@ -0,0 +1,13 @@
+SELECT
+    d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner,
+    bn.nspname as basensp
+FROM
+    pg_type d
+JOIN
+    pg_type b ON b.oid = d.typbasetype
+JOIN
+    pg_namespace bn ON bn.oid=d.typnamespace
+WHERE
+    d.typnamespace = {{scid}}::oid
+ORDER BY
+    d.typname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..42af39d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql
@@ -0,0 +1,35 @@
+SELECT
+    d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype,
+    pg_get_userbyid(d.typowner) as owner,
+    c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype,
+    CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN
+    concat(cn.nspname, '."', c.collname,'"')
+    ELSE '' END AS collname,
+    d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp,
+    description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup,
+    (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup,
+    (SELECT
+        array_agg(provider || '=' || label)
+    FROM
+        pg_seclabel sl1
+    WHERE
+        sl1.objoid=d.oid) AS seclabels
+FROM
+    pg_type d
+JOIN
+    pg_type b ON b.oid = d.typbasetype
+JOIN
+    pg_namespace bn ON bn.oid=d.typnamespace
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass)
+LEFT OUTER JOIN
+    pg_collation c ON d.typcollation=c.oid
+LEFT OUTER JOIN
+    pg_namespace cn ON c.collnamespace=cn.oid
+WHERE
+    d.typnamespace = {{scid}}::oid
+    {% if doid %}
+    AND d.oid={{doid}}::int
+    {% endif %}
+ORDER BY
+    d.typname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..6ff4052
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql
@@ -0,0 +1,64 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% if data %}
+{% set name = o_data.name %}
+{% if data.name %}
+{% if data.name != o_data.name %}
+ALTER TYPE {{ conn|qtIdent(o_data.basensp, o_data.name) }}
+    RENAME TO {{ conn|qtIdent(data.name) }};
+{% set name = data.name %}
+{% endif %}
+
+{% endif -%}
+{% if data.typnotnull and not o_data.typnotnull %}
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    SET NOT NULL;
+{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%}
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    DROP NOT NULL;
+{% endif -%}{% if data.typdefault %}
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    SET DEFAULT {{ data.typdefault }};
+{% elif not data.typdefault and o_data.typdefault %}
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    DROP DEFAULT;
+{% endif -%}{% if data.owner %}
+
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.constraints %}
+{% for c in data.constraints.deleted %}
+
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    DROP CONSTRAINT {{ conn|qtIdent(c.conname) }};
+{% endfor -%}
+{% for c in data.constraints.added %}
+{% if c.conname and c.consrc %}
+
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} );{% endif -%}
+{% endfor -%}{% endif -%}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }}
+
+{% endfor -%}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }}
+
+{% endfor -%}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }}
+
+{% endfor -%}
+{% endif -%}{% if data.description %}
+COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    IS {{ data.description|qtLiteral }};{% endif %}{% if data.basensp %}
+
+
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%}
+{% endif -%}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql
new file mode 100644
index 0000000..e89369f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql
@@ -0,0 +1,30 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% if data %}
+CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }}
+    AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname and data.collname != "pg_catalog.\"default\"" %}
+
+    COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %}
+
+    DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %}
+
+    NOT NULL{% endif %}{% if data.constraints %}{% for c in data.constraints %}{% if c.conname and c.consrc %}
+
+    CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% endif -%}
+{% endfor -%}
+{% endif -%};
+
+{% if data.owner %}
+ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.description %}
+
+
+COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }}
+    IS '{{ data.description }}';{% endif -%}{% if data.seclabels %}
+{% for r in data.seclabels %}
+{% if r.security_label and r.provider %}
+
+
+{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%}
+{% endfor -%}
+{% endif -%}
+
+{% endif -%}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql
new file mode 100644
index 0000000..7a12b50
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql
@@ -0,0 +1,16 @@
+{% if scid and doid %}
+SELECT
+    d.typname as name, bn.nspname as basensp
+FROM
+    pg_type d
+JOIN
+    pg_namespace bn ON bn.oid=d.typnamespace
+WHERE
+    d.typnamespace = {{scid}}::oid
+AND
+    d.oid={{doid}}::int;
+{% endif %}
+
+{% if name %}
+DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql
new file mode 100644
index 0000000..e59c17d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql
@@ -0,0 +1,10 @@
+SELECT --nspname, collname,
+    CASE WHEN length(nspname) > 0 AND length(collname) > 0  THEN
+    concat(nspname, '."', collname,'"')
+    ELSE '' END AS copy_collation
+FROM
+    pg_collation c, pg_namespace n
+WHERE
+    c.collnamespace=n.oid
+ORDER BY
+    nspname, collname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql
new file mode 100644
index 0000000..df956bf
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql
@@ -0,0 +1,15 @@
+SELECT
+    'DOMAIN' AS objectkind, c.oid as conoid, conname, typname as relname, nspname, description,
+    regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc, connoinherit, convalidated
+FROM
+    pg_constraint c
+JOIN
+    pg_type t ON t.oid=contypid
+JOIN
+    pg_namespace nl ON nl.oid=typnamespace
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass)
+WHERE
+    contype = 'c' AND contypid =  {{doid}}::oid
+ORDER BY
+    conname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql
new file mode 100644
index 0000000..8b5c891
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql
@@ -0,0 +1,18 @@
+{% if doid %}
+SELECT
+    d.typnamespace as scid
+FROM
+    pg_type d
+WHERE
+    d.oid={{ doid }}::oid;
+{% else %}
+SELECT
+    d.oid, d.typnamespace
+FROM
+    pg_type d
+JOIN
+    pg_namespace bn ON bn.oid=d.typnamespace
+WHERE
+    bn.nspname = {{ basensp|qtLiteral }}
+    AND d.typname={{ name|qtLiteral }};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql
new file mode 100644
index 0000000..7bd3e5b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql
@@ -0,0 +1,13 @@
+SELECT
+    d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner,
+    bn.nspname as basensp
+FROM
+    pg_type d
+JOIN
+    pg_type b ON b.oid = d.typbasetype
+JOIN
+    pg_namespace bn ON bn.oid=d.typnamespace
+WHERE
+    d.typnamespace = {{scid}}::oid
+ORDER BY
+    d.typname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql
new file mode 100644
index 0000000..2892988
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql
@@ -0,0 +1,34 @@
+SELECT
+    d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner,
+    c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype,
+    CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN
+    concat(cn.nspname, '."', c.collname,'"')
+    ELSE '' END AS collname,
+    d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp,
+    description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup,
+    (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup,
+    (SELECT
+        array_agg(provider || '=' || label)
+    FROM
+        pg_shseclabel sl1
+    WHERE
+        sl1.objoid=d.oid) AS seclabels
+FROM
+    pg_type d
+JOIN
+    pg_type b ON b.oid = d.typbasetype
+JOIN
+    pg_namespace bn ON bn.oid=d.typnamespace
+LEFT OUTER JOIN
+    pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass)
+LEFT OUTER JOIN
+    pg_collation c ON d.typcollation=c.oid
+LEFT OUTER JOIN
+    pg_namespace cn ON c.collnamespace=cn.oid
+WHERE
+    d.typnamespace = {{scid}}::oid
+{% if doid %}
+  AND d.oid={{doid}}::int
+{% endif %}
+ORDER BY
+    d.typname;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql
new file mode 100644
index 0000000..ecc7e9a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql
@@ -0,0 +1,64 @@
+{% import 'macros/schemas/security.macros' as SECLABLE %}
+{% if data %}
+{% set name = o_data.name %}
+{% if data.name %}
+{% if data.name != o_data.name %}
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }}
+    RENAME TO {{ conn|qtIdent(data.name) }};
+{% set name = data.name %}
+{% endif %}
+
+{% endif -%}
+{% if data.typnotnull and not o_data.typnotnull %}
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    SET NOT NULL;
+{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%}
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    DROP NOT NULL;
+{% endif -%}{% if data.typdefault %}
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    SET DEFAULT {{ data.typdefault }};
+{% elif not data.typdefault and o_data.typdefault %}
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    DROP DEFAULT;
+{% endif -%}{% if data.owner %}
+
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.constraints %}
+{% for c in data.constraints.deleted %}
+
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    DROP CONSTRAINT {{ conn|qtIdent(c.conname) }};
+{% endfor -%}
+{% for c in data.constraints.added %}
+{% if c.conname and c.consrc %}
+
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if c.convalidated %}
+    NOT VALID{% endif %}{% if c.connoinherit %} NO INHERIT{% endif -%};{% endif -%}
+{% endfor -%}{% endif -%}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }}
+
+{% endfor %}
+{% endif -%}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+
+{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }}
+{% endfor %}
+{% endif -%}{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+
+{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }}
+{% endfor %}
+{% endif -%}{% if data.description %}
+
+COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    IS {{ data.description|qtLiteral }};{% endif -%}{% if data.basensp %}
+
+ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+    SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%}
+{% endif -%}
-- 
Sent via pgadmin-hackers mailing list (pgadmin-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers

Reply via email to