public inbox for [email protected]  
help / color / mirror / Atom feed
From: Khushboo Vashi <[email protected]>
To: pgadmin-hackers <[email protected]>
Subject: pgAdmin4 PATCH: Domain Module
Date: Wed, 20 Jan 2016 10:18:59 +0530
Message-ID: <CAFOhELf-n8mM4h8RZuqxUs-Z+f97N6Ux5KT6aoAdWFiHVVURyw@mail.gmail.com> (raw)
List-Unsubscribe:  <mailto:[email protected]?body=unsub%20pgadmin-hackers>

Hi,

Please find attached patch for the Domain Module.

The patch will be modified after Types module implementation as we need to
populate Base Type  and some Type related validations from the Types module.

Please review it and let me know the feedback.

Thanks,
Khushboo


-- 
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers


Attachments:

  [text/x-patch] domains.patch (67.0K, 3-domains.patch)
  download | inline diff:
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..250a665
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py
@@ -0,0 +1,510 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+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 functools import wraps
+
+class DomainModule(CollectionNodeModule):
+    NODE_TYPE = 'domain'
+    COLLECTION_LABEL = gettext("Domains")
+
+    def __init__(self, *args, **kwargs):
+        self.min_ver = None
+        self.max_ver = None
+        super(DomainModule, self).__init__(*args, **kwargs)
+
+    # Before loading this module we need to make sure that scid is schema object
+    def BackendSupported(self, manager, **kwargs):
+        """
+        This function will validate schema name & scid against catalogs then allow us to
+        make decision if we want to load this module or not for the schema
+        """
+        if super(DomainModule, self).BackendSupported(manager, **kwargs):
+            conn = manager.connection()
+            # If DB is not connected then return error to browser
+            if not conn.connected():
+                return precondition_required(
+                    gettext(
+                            "Connection to the server has been lost!"
+                    )
+                )
+            ver = manager.version
+            server_type = manager.server_type
+            # we will set template path for sql scripts
+            if ver >= 90100:
+                template_path = 'domains/sql/9.1_plus'
+            else:
+                # Note: Domain is not supported below postgres version 9.1
+                # Hence we will not load this module
+                return False
+
+            SQL = render_template("/".join([template_path, 'backend_support.sql']), scid=kwargs['scid'])
+            status, res = conn.execute_scalar(SQL)
+            # check if any errors
+            if not status:
+                return internal_server_error(errormsg=res)
+            # Check scid is catalog and from 'sys', 'dbo', 'information_schema',
+            # then False (Do not load this module), othewise True
+            if res is True:
+                return False
+            else:
+                return True
+
+    def get_nodes(self, gid, sid, did, scid):
+        """
+        Generate the collection node
+        """
+        yield self.generate_browser_collection_node(scid)
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for database, when any of the database node is
+        initialized.
+        """
+        return schemas.SchemaModule.NODE_TYPE
+
+
+blueprint = DomainModule(__name__)
+
+
+class DomainView(PGChildNodeView):
+    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_schemas': [{'get': 'get_schemas'}, {'get': 'get_schemas'}],
+        'get_collations': [{'get': 'get_collations'}, {'get': 'get_collations'}],
+        'get_types': [{'get': 'get_types'}, {'get': 'get_types'}]
+    })
+
+
+    def module_js(self):
+        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):
+            # Here args[0] will hold self & kwargs will hold gid,sid,did
+            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 database is not connected then return error
+            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 >= 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."""
+
+        SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid)
+        status, res = self.conn.execute_dict(SQL)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+        return ajax_response(
+                response=res['rows'],
+                status=200
+                )
+
+
+    @check_precondition
+    def nodes(self, gid, sid, did, scid):
+        """Returns all the Domains."""
+
+        res = []
+        SQL = render_template("/".join([self.template_path, 'properties.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'],
+                        row['name'],
+                        icon="icon-domain"
+                    ))
+
+        return make_json_response(
+                data=res,
+                status=200
+                )
+
+
+    @check_precondition
+    def properties(self, gid, sid, did, scid, doid):
+        """Returns Domain properties."""
+
+        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 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']
+        return ajax_response(
+                response=data,
+                status=200
+                )
+
+
+    @check_precondition
+    def get_schemas(self, gid, sid, did, scid, doid=None):
+        """Returns Schemas for particular database."""
+
+        res = [{ 'label': '', 'value': '' }]
+        try:
+            SQL = render_template("/".join([self.template_path, 'get_schemas.sql']), scid=scid)
+            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['nspname'], 'value': row['nspname'] }
+                        )
+
+            return make_json_response(
+                    data=res,
+                    status=200
+                    )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+
+    @check_precondition
+    def get_collations(self, gid, sid, did, scid, doid=None):
+        """Returns Collations."""
+
+        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 get_types(self, gid, sid, did, scid, doid=None):
+        """Returns Types."""
+        # TODO: This function should be removed once Types module will be completed.
+
+        res = [{ 'label': '', 'value': '' }]
+        try:
+            SQL = render_template("/".join([self.template_path, 'get_types.sql']))
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            for row in rset['rows']:
+                res.append(
+                            { 'label': row['typname'], 'value': row['typname'] }
+                        )
+            return make_json_response(
+                    data=res,
+                    status=200
+                    )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+
+    @check_precondition
+    def create(self, gid, sid, did, scid):
+        """
+        Creates new domain object.
+
+        Expected Parameters:
+            name: Domain Name
+            owner: Owner Name
+            basensp: Schema Name
+            basetype: Domain Base Type
+
+        Expected Response:
+            Domain object in json format.
+        """
+
+        data = request.form if request.form else json.loads(request.data.decode())
+        required_args = [
+            'name',
+            'owner',
+            'basensp',
+            'basetype'
+        ]
+
+        for arg in required_args:
+            if arg not in data or data[arg] == '':
+                return make_json_response(
+                    status=410,
+                    success=0,
+                    errormsg=gettext(
+                        "Couldn't find the required parameter (%s)." % arg
+                    )
+                )
+
+        try:
+            SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, qtIdent=self.qtIdent)
+            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, doid = self.conn.execute_scalar(SQL)
+            if not status:
+                return internal_server_error(errormsg=doid)
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    doid,
+                    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):
+        """
+        Drop the Domain.
+        """
+        # Below will decide if it's simple drop or drop with cascade call
+        if self.cmd == 'delete':
+            # This is a cascade operation
+            cascade = True
+        else:
+            cascade = False
+
+        try:
+            SQL = render_template("/".join([self.template_path, '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
+    def update(self, gid, sid, did, scid, doid):
+        """
+        Update the Domain.
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        status, SQL = self.getSQL(gid, sid, data, scid, doid)
+        if not status:
+            return internal_server_error(errormsg=SQL)
+
+        try:
+            if SQL and SQL.strip('\n') and SQL.strip(' '):
+                status, res = self.conn.execute_scalar(SQL)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                return make_json_response(
+                    success=1,
+                    info="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 msql(self, gid, sid, did, scid, doid=None):
+        """
+        Returns the modified SQL.
+
+        Expected Parameters:
+            name: Domain Name
+            owner: Owner Name
+            basensp: Schema Name
+            basetype: Domain Base Type
+
+        Expected Response:
+            SQL statements to create/update the Domain.
+        """
+        data = request.args
+        if doid is None:
+            required_args = [
+                'name',
+                'owner',
+                'basensp',
+                'basetype'
+            ]
+
+
+            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
+                        )
+                    )
+        SQL = self.getSQL(gid, sid, data, scid, doid)
+        if isinstance(SQL, str) and SQL and SQL.strip('\n') and SQL.strip(' '):
+            return make_json_response(
+                    data=SQL,
+                    status=200
+                    )
+        else:
+            return SQL
+
+
+    def getSQL(self, gid, sid, data, scid, doid=None):
+        """
+        Genrates the SQL statements to create/update the Domain.
+        """
+        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, conn=self.conn, qtIdent=self.qtIdent
+                    )
+            else:
+                SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn, qtIdent=self.qtIdent)
+            return True,SQL
+
+        except Exception as e:
+            return False, e
+
+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..7f0575b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py
@@ -0,0 +1,390 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+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 NodeView
+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):
+    NODE_TYPE = 'domain-constraints'
+    COLLECTION_LABEL = gettext("Domain Constraints")
+
+    def __init__(self, *args, **kwargs):
+        self.min_ver = None
+        self.max_ver = None
+        super(DomainConstraintModule, self).__init__(*args, **kwargs)
+
+    def get_nodes(self, gid, sid, did, scid, doid):
+        """
+        Generate the collection node
+        """
+        yield self.generate_browser_collection_node(doid)
+
+    @property
+    def node_inode(self):
+        """
+        Override this property to make the node as leaf node.
+        """
+        return False
+
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for database, when any of the database node is
+        initialized.
+        """
+        return domains.DomainModule.NODE_TYPE
+
+
+blueprint = DomainConstraintModule(__name__)
+
+
+class DomainConstraintView(NodeView):
+    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):
+        """
+        This property defines (if javascript) exists for this node.
+        Override this property for your own logic.
+        """
+        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):
+            # Here args[0] will hold self & kwargs will hold gid,sid,did
+            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
+            server_type = self.manager.server_type
+
+            # we will set template path for sql scripts
+            if 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.
+        """
+        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.
+        """
+        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'],
+                        row['conname'],
+                        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.
+        """
+
+        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)
+
+        return ajax_response(
+                response=res['rows'][0],
+                status=200
+                )
+
+
+    @check_precondition
+    def create(self, gid, sid, did, scid, doid):
+        """
+        Creates new the Domain Constraints.
+
+        Expected Parameters:
+            conname: Constraints Name
+            consrc: Constraints Check
+
+        Expected Response:
+            Domain Constraint object in json format.
+        """
+
+        data = request.form if request.form else json.loads(request.data.decode())
+        required_args = [
+            'conname',
+            '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=name)
+
+            domain, schema = res['rows'][0]
+
+            SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, domain=domain, schema=schema, qtIdent=self.qtIdent)
+            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['conname'])
+            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,
+                    data['conname'],
+                    icon="icon-domain"
+                )
+            )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+
+    @check_precondition
+    def delete(self, gid, sid, did, scid, doid, coid):
+        """
+        Drop the Doman Constraints.
+        """
+        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 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):
+        """
+        Update the Domain Constraints.
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        SQL = self.getSQL(gid, sid, data, scid, doid, coid)
+        try:
+            if SQL and SQL.strip('\n') and SQL.strip(' '):
+                status, res = self.conn.execute_scalar(SQL)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                return make_json_response(
+                    success=1,
+                    info="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 msql(self, gid, sid, did, scid, doid=None):
+        """
+        Returns the modified SQL.
+
+        Expected Parameters:
+            conname: Constraints Name
+            consrc: Constraints Check
+
+        Expected Response:
+            Domain Constraint object in json format.
+        """
+        data = request.args
+        if doid is None:
+            required_args = [
+                'conname',
+                '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
+                        )
+                    )
+        SQL = self.getSQL(gid, sid, data, scid, doid)
+        if isinstance(SQL, str) and SQL and SQL.strip('\n') and SQL.strip(' '):
+            return make_json_response(
+                    data=SQL,
+                    status=200
+                    )
+        else:
+            return SQL
+
+
+    def getSQL(self, gid, sid, data, scid, doid, coid=None):
+        """
+        Genrates the SQL statements to create/update the Domain Constraint.
+        """
+        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 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, 'create.sql']), data=data, conn=self.conn)
+            return SQL
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+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 0000000..d62e137
Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png differ
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 0000000..32a045b
Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png differ
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 0000000..9d1d2a0
Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png differ
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css
new file mode 100644
index 0000000..68cd622
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css
@@ -0,0 +1,9 @@
+.pg-icon-domain-constraints {
+  background-image: url('{{ url_for('NODE-domain-constraints.static', filename='img/domain-constraints.png') }}') !important;
+  border-radius: 10px;
+  background-repeat: no-repeat;
+  align-content: center;
+  vertical-align: middle;
+  height: 1.3em;
+}
+
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..fb3de7d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js
@@ -0,0 +1,95 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+  if (!pgBrowser.Nodes['coll-domain-constraints']) {
+    var databases = pgAdmin.Browser.Nodes['coll-domain-constraints'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'domain-constraints',
+        label: '{{ _('Domain Constraints') }}',
+        type: 'coll-domain-constraints'
+      });
+  };
+
+  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,
+      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: {
+          conname: undefined,
+          description: undefined,
+          consrc: undefined,
+          connoinherit: undefined,
+          convalidated: undefined
+        },
+        schema: [{id: 'conname', label:'Name', type:'text', cell:'string'},
+        {id: 'description', label:'Comment', type: 'multiline', cell: 'string', mode: ['properties', 'create', 'edit'], disabled: function(m) { return !m.isNew(); }},
+        {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: function(m) { return !m.isNew(); }},
+        {id: 'convalidated', label:"Don't Validate", type: 'switch', cell: 'boolean', group: 'Definition'}
+        ],
+        validate: function() {
+          var err = {},
+              errmsg;
+
+          if (_.isUndefined(this.get('conname')) || String(this.get('conname')).replace(/^\s+|\s+$/g, '') == '') {
+            err['conname'] = '{{ _('Name can not be empty!') }}';
+            errmsg = errmsg || err['conname'];
+          }
+
+          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;
+
+        },
+      }),
+  });
+
+  }
+
+  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..ed2610d
--- /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,6 @@
+{% if data and schema and domain %}
+  ALTER DOMAIN {{ conn|qtIdent(schema, domain) }}
+  ADD CONSTRAINT {{ data.conname }} CHECK ({{ data.consrc }} )
+  {% if data.convalidated %} NOT VALID {% endif %} {% if data.connoinherit %} NO INHERIT {% endif %};
+{% 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..1a56583
--- /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 {{ data.conname }}
+{% endif %}
\ No newline at end of file
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..a199c5c
--- /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,7 @@
+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..344abcc
--- /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,4 @@
+SELECT oid, conname as name
+  FROM pg_constraint
+WHERE contypid = {{doid}}::oid
+  AND conname={{ name|qtLiteral }};
\ No newline at end of file
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..7ed21d9
--- /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,18 @@
+SELECT 'TABLE' AS objectkind, c.oid, conname, relname, nspname, description,
+  pg_get_expr(conbin, conrelid, true) as consrc, connoinherit, convalidated
+FROM pg_constraint c
+JOIN pg_class cl ON cl.oid=conrelid
+JOIN pg_namespace nl ON nl.oid=relnamespace
+LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass)
+WHERE contype = 'c' AND conrelid =  {{doid}}::oid
+UNION
+SELECT 'DOMAIN' AS objectkind, c.oid, 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
+{% 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..9a2c8d7
--- /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,4 @@
+{% if data.conname %}
+  ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
+    RENAME CONSTRAINT {{ o_data.conname }} TO {{ data.conname }};
+{% 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 0000000..5562152
Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png differ
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 0000000..7521cdd
Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png differ
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 0000000..42ca929
Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png differ
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css
new file mode 100644
index 0000000..7d4fad0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css
@@ -0,0 +1,9 @@
+.pg-icon-domain {
+  background-image: url('{{ url_for('NODE-domain.static', filename='img/domain.png') }}') !important;
+  border-radius: 10px;
+  background-repeat: no-repeat;
+  align-content: center;
+  vertical-align: middle;
+  height: 1.3em;
+}
+
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..8fcf507
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js
@@ -0,0 +1,236 @@
+/* Create and Register Domain Collection and Domain Node. */
+
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+  if (!pgBrowser.Nodes['coll-domain']) {
+    var databases = pgAdmin.Browser.Nodes['coll-domain'] =
+      pgAdmin.Browser.Collection.extend({
+        node: 'domain',
+        label: '{{ _('Domains') }}',
+        type: 'coll-domain'
+      });
+  };
+
+  var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({
+    defaults: {
+      provider: null,
+      security_label: null
+    },
+    schema: [{
+      id: 'provider', label: '{{ _('Provider') }}',
+      type: 'text'
+    },{
+      id: 'security_label', label: '{{ _('Security Label') }}',
+      type: 'text'
+    }],
+    validate: function() {
+      var err = {},
+          errmsg = null,
+          data = this.toJSON();
+
+      if (_.isUndefined(data.label) ||
+        _.isNull(data.label) ||
+        String(data.label).replace(/^\s+|\s+$/g, '') == '') {
+        return _("Please specify the value for all the security providers.");
+      }
+      return null;
+    }
+  });
+
+  var ConstraintsModel = pgAdmin.Browser.Node.Model.extend({
+    defaults: {
+      conname: undefined,
+      description: undefined,
+      slony: undefined,
+      consrc: undefined,
+      connoinherit: undefined,
+      convalidated: undefined
+    },
+    schema: [
+      {id: 'conname', label:'Name', type:'text', cell:'string'},
+      {id: 'description', label:'Comment', type: 'multiline', cell: 'string'},
+      {id: 'slony', label:'Use Slony', type: 'text', cell: 'string'},
+      {id: 'consrc', label:'Check', type: 'multiline', cel: 'string', group: 'definition'},
+      {id: 'connoinherit', label:'No Inherit', type: 'switch', cell: 'boolean', group: 'definition'},
+      {id: 'convalidated', label:"Don't Validate", type: 'switch', cell: 'boolean', group: 'definition'}
+    ],
+    validate: function() {
+      // TODO: Add validation here
+    },
+    toJSON: Backbone.Model.prototype.toJSON
+  });
+
+
+  if (!pgBrowser.Nodes['domain']) {
+    pgAdmin.Browser.Nodes['domain'] = pgBrowser.Node.extend({
+      type: 'domain',
+      label: '{{ _('Domain') }}',
+      collection_type: 'coll-domain',
+      hasSQL: 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,
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          oid: undefined, 
+          owner: undefined,
+          basensp: undefined,
+          description: undefined,
+          basetype: undefined,
+          typlen: undefined,
+          precision: undefined,
+          typdefault: undefined,
+          typnotnull: undefined,
+          collname: undefined,
+          constraints: [],
+          seclabels: []
+        },
+        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: 'string', control: 'node-ajax-options',
+          type: 'text', url: 'get_schemas'
+        },{
+          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(); }
+        },{
+          id: 'typlen', label:'{{ _('Length') }}', cell: 'string',
+          type: 'text', group: 'Definition', disabled: function(m) { return !m.isNew(); }
+        },{
+          id: 'precision', label:'{{ _('Precision') }}', cell: 'string',
+          type: 'text', group: 'Definition', disabled: function(m) { return !m.isNew(); }
+        },{
+          id: 'typdefault', label:'{{ _('Default') }}', cell: 'string',
+          type: 'text', group: 'Definition'
+        },{
+          id: 'typnotnull', label:'{{ _('Not Null') }}', cell: 'boolean',
+          type: 'switch', group: 'Definition'
+        },{
+          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', visible: false, mode: ['edit', 'create'],
+          model: ConstraintsModel, canAdd: true, canDelete: false, canEdit: false
+        },{
+          id: 'seclabels', label: '{{ _('Security Labels') }}',
+          model: SecurityModel, editable: false, type: 'collection',
+          group: '{{ _('Security') }}', mode: ['edit', 'create'],
+          min_version: 90100, canAdd: true,
+          canEdit: false, canDelete: true, control: 'unique-col-collection'
+        }
+        ],
+        validate: function()
+        {
+          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);
+
+          if (_.size(err)) {
+            this.trigger('on-status', {msg: errmsg});
+            return errmsg;
+          }
+
+          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;
+      }
+  });
+
+  }
+
+  return pgBrowser.Nodes['domain'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql
new file mode 100644
index 0000000..7417b43
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql
@@ -0,0 +1,20 @@
+SELECT
+  CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport
+FROM
+  pg_namespace nsp
+WHERE
+  nsp.oid={{scid}}::int
+  AND (
+    (nspname = 'pg_catalog' AND EXISTS 
+                                   (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1))
+    OR (nspname = 'pgagent' AND EXISTS
+                                   (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1))
+    OR (nspname = 'information_schema' AND EXISTS 
+                                   (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1))
+    OR (nspname LIKE '_%' AND EXISTS 
+                                   (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1))
+    )
+  AND
+   nspname NOT LIKE E'pg\\temp\\%'
+  AND
+   nspname NOT LIKE E'pg\\toast_temp\\%'
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/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..d4cdfe0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql
@@ -0,0 +1,32 @@
+{% import 'macros/security.macros' as SECLABLE %}
+
+{% if data %}
+  CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }}
+    AS {{ data.basetype }}
+  {% if data.collname %}
+    COLLATE {{ data.collname }}
+  {% endif %}
+
+  {% if data.typnotnull %}
+    NOT NULL
+  {% endif %}
+  ;
+
+  {% if data.constraints %}
+    {% for c in data.constraints %}
+      ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }}
+      ADD CONSTRAINT {{ c.conname }} CHECK ({{ c.consrc }} )
+      {% if c.convalidated %} NOT VALID {% endif %} {% if c.connoinherit %} NO INHERIT {% endif %};
+    {% endfor %}
+  {% endif %}
+
+  ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ data.owner }};
+  {% if data.description %}
+    COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }}
+    IS '{{ data.description }}';
+  {% endif %}
+
+  {% for r in data.seclabels %}
+    {{ SECLABLE.APPLY(conn, 'DOMAIN', data.name, r.provider, r.label) }}
+  {% endfor %}
+{% endif %}
\ No newline at end of file
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..c740f4f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql
@@ -0,0 +1,11 @@
+{% 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 %}
\ No newline at end of file
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..6a13a65
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql
@@ -0,0 +1,7 @@
+SELECT --nspname, collname,
+  CASE WHEN length(nspname) > 0 AND length(collname) > 0  THEN
+  concat(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..e26998f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql
@@ -0,0 +1,18 @@
+SELECT 'TABLE' AS objectkind, c.oid, conname, relname, nspname, description,
+  pg_get_expr(conbin, conrelid, true) as consrc, connoinherit, convalidated  FROM pg_constraint c
+JOIN pg_class cl ON cl.oid=conrelid
+JOIN pg_namespace nl ON nl.oid=relnamespace
+LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass)
+WHERE contype = 'c' AND conrelid =  {{doid}}::oid
+UNION
+SELECT 'DOMAIN' AS objectkind, c.oid, 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
\ No newline at end of file
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..1c26d6b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql
@@ -0,0 +1,10 @@
+SELECT
+  d.oid, d.typname as name
+FROM
+  pg_type d
+JOIN
+  pg_namespace bn ON bn.oid=d.typnamespace
+WHERE
+  bn.nspname = {{ basensp|qtLiteral }}
+  AND d.typname={{ name|qtLiteral }}
+
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql
new file mode 100644
index 0000000..82dcfcb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql
@@ -0,0 +1,21 @@
+SELECT
+  nsp.nspname
+FROM
+  pg_namespace nsp
+  LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass)
+  LEFT OUTER JOIN pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid)
+WHERE
+  NOT ((nspname = 'pg_catalog' AND EXISTS
+    (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR
+  (nspname = 'pgagent' AND EXISTS
+    (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR
+  (nspname = 'information_schema' AND EXISTS
+    (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR
+  (nspname LIKE '_%' AND EXISTS
+      (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1))
+  )AND
+  nspname NOT LIKE E'pg\\temp\\%' AND
+  nspname NOT LIKE E'pg\\toast_temp\\%' AND
+  -- ADDED: Because We need to omit system schema except the one on which we are trying to  create collation
+  ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' )
+ORDER BY nspname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql
new file mode 100644
index 0000000..e473056
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql
@@ -0,0 +1,7 @@
+SELECT * FROM (SELECT format_type(t.oid,NULL) AS typname, CASE WHEN typelem > 0 THEN typelem ELSE t.oid END as elemoid, typlen, typtype, t.oid, nspname,
+       (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+  FROM pg_type t
+  JOIN pg_namespace nsp ON typnamespace=nsp.oid
+ WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', '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')) 
+  ) AS dummy ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1
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..901d053
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql
@@ -0,0 +1,20 @@
+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,
+  CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN
+  concat(cn.nspname, '."', c.collname,'"')
+  ELSE '' END AS collname,
+  d.typlen, 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.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..eb9c08f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql
@@ -0,0 +1,61 @@
+{% 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 not 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|qtLiteral }};
+  {% endif %}
+
+  {% if data.owner %}
+    ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+      OWNER TO {{ data.owner }};
+  {% endif %}
+
+  {% if data.constraints %}
+    {% for c in data.constraints.added %}
+        ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
+        ADD CONSTRAINT {{ c.conname }} CHECK ({{ c.consrc }} )
+        {% if c.convalidated %} NOT VALID {% endif %} {% if c.connoinherit %} NO INHERIT {% endif %};
+    {% endfor %}
+  {% endif %}
+
+  {% set seclabels = data.seclabels %}
+  {% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+    {% for r in seclabels.deleted %}
+    {{ SECLABLE.DROP(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider) }}
+    {% endfor %}
+  {% endif %}
+  {% if 'added' in seclabels and seclabels.added|length > 0 %}
+    {% for r in seclabels.added %}
+    {{ SECLABLE.APPLY(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }}
+    {% endfor %}
+  {% endif %}
+  {% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+    {% for r in seclabels.changed %}
+    {{ SECLABLE.APPLY(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }}
+    {% 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/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js
index 8837fa9..67a8705 100644
--- a/web/pgadmin/static/js/backform.pgadmin.js
+++ b/web/pgadmin/static/js/backform.pgadmin.js
@@ -998,9 +998,9 @@
       }
 
       // Insert Edit Cell into Grid
-      if (data.disabled == false && data.canEdit) {
+      if (data.disabled == false && data.canEdit || data.canAdd) {
           var editCell = Backgrid.Extension.ObjectCell.extend({
-            schema: gridSchema.schema
+            schema: gridSchema.schema, editable: false
           });
 
           gridSchema.columns.unshift({
@@ -1043,6 +1043,7 @@
         e.preventDefault();
         grid.insertRow({});
         var newRow = $(grid.body.rows[collection.length - 1].$el);
+        console.log(newRow);
         newRow.attr("class", "new").click(function(e) {
           $(this).attr("class", "");
         });


view thread (29+ messages)  latest in thread

reply

Reply instructions:

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

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

  To: [email protected]
  Cc: [email protected]
  Subject: Re: pgAdmin4 PATCH: Domain Module
  In-Reply-To: <CAFOhELf-n8mM4h8RZuqxUs-Z+f97N6Ux5KT6aoAdWFiHVVURyw@mail.gmail.com>

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

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