Hi,

PAF patch for exclusion constraint.

Note: This patch has dependency on Table, Column node and Index constraint
(I'll be sending patch for index constraint immediately after this).

-- 
*Harshal Dhumal*
*Software Engineer *



EenterpriseDB <http://www.enterprisedb.com>
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/__init__.py
new file mode 100644
index 0000000..d6a174f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/__init__.py
@@ -0,0 +1,918 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Implements Exclusion constraint Node"""
+
+import json
+from flask import render_template, make_response, request, jsonify
+from flask.ext.babel import gettext as _
+from pgadmin.utils.ajax import make_json_response, \
+    make_response as ajax_response, internal_server_error
+from pgadmin.browser.utils import PGChildNodeView
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+import pgadmin.browser.server_groups.servers.databases.schemas.tables as table
+from functools import wraps
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.constraints.type \
+    import ConstraintRegistry, ConstraintTypeModule
+
+
+class ExclusionConstraintModule(ConstraintTypeModule):
+    """
+    class ForeignKeyConstraintModule(CollectionNodeModule)
+
+        A module class for Exclusion constraint node derived from ConstraintTypeModule.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Method is used to initialize the ForeignKeyConstraintModule and it's base module.
+
+    * get_nodes(gid, sid, did)
+      - Method is used to generate the browser collection node.
+
+    * node_inode()
+      - Method is overridden from its base class to make the node as leaf node.
+
+    * script_load()
+      - Load the module script for language, when any of the database node is
+        initialized.
+    """
+
+    NODE_TYPE = 'exclusion_constraint'
+    COLLECTION_LABEL = _("Foreign Keys")
+
+    def __init__(self, *args, **kwargs):
+        """
+        Method is used to initialize the ForeignKeyConstraintModule and it's base module.
+
+        Args:
+          *args:
+          **kwargs:
+
+        Returns:
+
+        """
+        self.min_ver = None
+        self.max_ver = None
+        super(ExclusionConstraintModule, self).__init__(*args, **kwargs)
+
+    def get_nodes(self, gid, sid, did, scid, tid):
+        """
+        Generate the collection node
+        """
+        pass
+
+    @property
+    def node_inode(self):
+        """
+        Override this property to make the node a leaf node.
+
+        Returns: False as this is the leaf node
+        """
+        return False
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for exclusion_constraint, when any of the table node is
+        initialized.
+
+        Returns: node type of the server module.
+        """
+        return table.TableModule.NODE_TYPE
+
+blueprint = ExclusionConstraintModule(__name__)
+
+
+class ExclusionConstraintView(PGChildNodeView):
+    """
+    class ExclusionConstraintView(PGChildNodeView)
+
+        A view class for Exclusion constraint node derived from PGChildNodeView. This class is
+        responsible for all the stuff related to view like creating, updating Exclusion constraint
+        node, showing properties, showing sql in sql pane.
+
+    Methods:
+    -------
+    * __init__(**kwargs)
+      - Method is used to initialize the ForeignKeyConstraintView and it's base view.
+
+    * module_js()
+      - This property defines (if javascript) exists for this node.
+        Override this property for your own logic
+
+    * check_precondition()
+      - This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+
+    * end_transaction()
+      - To end any existing database transaction.
+
+    * list()
+      - This function returns Exclusion constraint nodes within that
+        collection as http response.
+
+    * get_list()
+      - This function is used to list all the language nodes within that collection
+        and return list of Exclusion constraint nodes.
+
+    * nodes()
+      - This function returns child node within that collection.
+        Here return all Exclusion constraint node as http response.
+
+    * get_nodes()
+      - returns all Exclusion constraint nodes' list.
+
+    * properties()
+      - This function will show the properties of the selected Exclusion.
+
+    * update()
+      - This function will update the data for the selected Exclusion.
+
+    * msql()
+      - This function is used to return modified SQL for the selected Exclusion.
+
+    * get_sql()
+      - This function will generate sql from model data.
+
+    * sql():
+      - This function will generate sql to show it in sql pane for the selected Exclusion.
+
+    * get_access_methods():
+      - Returns access methods for exclusion constraint.
+
+    * get_oper_class():
+      - Returns operator classes for selected access method.
+
+    * get_operator():
+      - Returns operators for selected column.
+
+    """
+
+    node_type = 'exclusion_constraint'
+
+    parent_ids = [
+            {'type': 'int', 'id': 'gid'},
+            {'type': 'int', 'id': 'sid'},
+            {'type': 'int', 'id': 'did'},
+            {'type': 'int', 'id': 'scid'},
+            {'type': 'int', 'id': 'tid'}
+            ]
+    ids = [{'type': 'int', 'id': 'exid'}
+            ]
+
+    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'}],
+        'module.js': [{}, {}, {'get': 'module_js'}],
+        'get_access_methods': [{}, {'get': 'get_access_methods'}],
+        'get_oper_class': [{}, {'get': 'get_oper_class'}],
+        'get_operator': [{}, {'get': 'get_operator'}]
+    })
+
+    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(
+                    "exclusion_constraint/js/exclusion_constraint.js",
+                    _=_
+                    ),
+                200, {'Content-Type': 'application/x-javascript'}
+                )
+
+    def check_precondition(f):
+        """
+        This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+        """
+        @wraps(f)
+        def wrap(*args, **kwargs):
+            # Here args[0] will hold self & kwargs will hold gid,sid,did
+            self = args[0]
+            self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
+                kwargs['sid']
+                )
+            self.conn = self.manager.connection(did=kwargs['did'])
+
+            # If DB not connected then return error to browser
+            if not self.conn.connected():
+                return precondition_required(
+                    _(
+                            "Connection to the server has been lost!"
+                    )
+                )
+
+            self.template_path = 'exclusion_constraint/sql'
+            # We need parent's name eg table name and schema name
+            SQL = render_template("/".join([self.template_path,
+                                            'get_parent.sql']),
+                                  tid=kwargs['tid'])
+            status, rset = self.conn.execute_2darray(SQL)
+            if not status:
+                return internal_server_error(errormsg=rset)
+
+            for row in rset['rows']:
+                self.schema = row['schema']
+                self.table = row['table']
+            return f(*args, **kwargs)
+
+        return wrap
+
+    def end_transaction(self):
+        SQL = render_template(
+                "/".join([self.template_path, 'end.sql']))
+        # End transaction if any.
+        self.conn.execute_scalar(SQL)
+
+    @check_precondition
+    def properties(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function is used to list all the Exclusion constraint
+        nodes within that collection.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        try:
+            sql = render_template("/".join([self.template_path, 'properties.sql']),
+                                  tid=tid, exid=exid)
+
+            status, res = self.conn.execute_dict(sql)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            result = res['rows'][0]
+
+            sql = render_template(
+                "/".join([self.template_path, 'get_constraint_cols.sql']),
+                cid=exid,
+                colcnt=result['indnatts'])
+            status, res = self.conn.execute_dict(sql)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            columns = []
+            for row in res['rows']:
+                if row['options'] & 1:
+                    order = False
+                    nulls_order = True if (row['options'] & 2) else False
+                else:
+                    order = True
+                    nulls_order = True if (row['options'] & 2) else False
+
+                columns.append({"column": row['coldef'].strip('"'),
+                                "oper_class": row['opcname'],
+                                "order": order,
+                                "nulls_order": nulls_order,
+                                "operator": row['oprname']
+                                })
+
+            result['columns'] = columns
+
+            return ajax_response(
+                    response=result,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def list(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function returns all exclusion constraints
+        nodes within that collection as a http response.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        try:
+            res = self.get_node_list(gid, sid, did, scid, tid, exid)
+            return ajax_response(
+                    response=res,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_node_list(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function returns all exclusion constraints
+        nodes within that collection as a list.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        SQL = render_template("/".join([self.template_path,
+                                        'properties.sql']),
+                                        tid=tid)
+        status, res = self.conn.execute_dict(SQL)
+
+        return res['rows']
+
+    @check_precondition
+    def nodes(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function returns all Exclusion constraint nodes as a
+        http response.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        try:
+            res = self.get_nodes(gid, sid, did, scid, tid, exid)
+            return make_json_response(
+                    data=res,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_nodes(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function returns all Exclusion constraint nodes as a list.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        res = []
+        SQL = render_template("/".join([self.template_path,
+                                        'nodes.sql']),
+                              tid=tid)
+        status, rset = self.conn.execute_2darray(SQL)
+
+        for row in rset['rows']:
+            res.append(
+                    self.blueprint.generate_browser_node(
+                        row['oid'],
+                        tid,
+                        row['name'],
+                        icon="icon-exclusion_constraint"
+                    ))
+        return res
+
+    @check_precondition
+    def create(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function will create a Exclusion constraint.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        required_args = ['columns']
+
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        for arg in required_args:
+            if arg not in data:
+                return make_json_response(
+                      status=400,
+                      success=0,
+                      errormsg=_(
+                          "Couldn't find required parameter (%s)." % str(arg)
+                      )
+                    )
+            elif isinstance(data[arg], list) and len(data[arg]) < 1:
+                return make_json_response(
+                      status=400,
+                      success=0,
+                      errormsg=_(
+                          "Couldn't find required parameter (%s)." % str(arg)
+                      )
+                    )
+
+        data['schema'] = self.schema
+        data['table'] = self.table
+        try:
+            if 'name' not in data or data['name'] == "":
+                SQL = render_template(
+                    "/".join([self.template_path, 'begin.sql']))
+                # Start transaction.
+                status, res = self.conn.execute_scalar(SQL)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=res)
+
+            # The below SQL will execute CREATE DDL only
+            SQL = render_template(
+                "/".join([self.template_path, 'create.sql']),
+                data=data, conn=self.conn
+                )
+            status, res = self.conn.execute_scalar(SQL)
+            if not status:
+                self.end_transaction()
+                return internal_server_error(errormsg=res)
+
+            if 'name' not in data or data['name'] == "":
+                sql = render_template(
+                    "/".join([self.template_path,
+                              'get_oid_with_transaction.sql']),
+                    tid=tid)
+
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=res)
+
+                self.end_transaction()
+
+                data['name'] = res['rows'][0]['name']
+
+            else:
+                sql = render_template("/".join([self.template_path, 'get_oid.sql']), name=data['name'])
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    self.end_transaction()
+                    return internal_server_error(errormsg=res)
+            if 'name' in data and data['name'] != '':
+                sql = render_template("/".join([self.template_path, 'grant.sql']), data=data, conn=self.conn)
+                sql = sql.strip('\n').strip(' ')
+
+                if sql != '':
+                    status, result = self.conn.execute_scalar(sql)
+                    if not status:
+                        self.end_transaction()
+                        return internal_server_error(errormsg=result)
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    res['rows'][0]['oid'],
+                    tid,
+                    data['name'],
+                    icon="icon-exclusion_constraint"
+                    )
+                )
+
+        except Exception as e:
+            self.end_transaction()
+
+            return make_json_response(
+                status=400,
+                success=0,
+                errormsg=e
+            )
+
+    @check_precondition
+    def update(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function will update the data for the selected
+        Exclusion constraint.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+
+        try:
+            data['schema'] = self.schema
+            data['table'] = self.table
+            sql = self.get_sql(data, tid, exid)
+            sql = sql.strip('\n').strip(' ')
+            if sql != "":
+                status, res = self.conn.execute_scalar(sql)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                sql = render_template("/".join([self.template_path, 'get_oid.sql']), name=data['name'])
+                status, res = self.conn.execute_dict(sql)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                return make_json_response(
+                    success=1,
+                    info="Exclusion constraint updated",
+                    data={
+                        'id': res['rows'][0]['oid'],
+                        'tid': tid,
+                        'scid': scid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did,
+                    }
+                )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': exid,
+                        'tid': tid,
+                        'scid': scid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did
+                    }
+                )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def delete(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function will delete an existing Exclusion.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        # Below code will decide if it's simple drop or drop with cascade call
+        if self.cmd == 'delete':
+            # This is a cascade operation
+            cascade = True
+        else:
+            cascade = False
+        try:
+            sql = render_template("/".join([self.template_path, 'get_name.sql']),
+                                  cid=exid)
+            status, res = self.conn.execute_dict(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            data = res['rows'][0]
+            data['schema'] = self.schema
+            data['table'] = self.table
+
+            sql = render_template("/".join([self.template_path, 'delete.sql']),
+                                  data=data,
+                                  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=_("Exclusion constraint dropped."),
+                data={
+                    'id': exid,
+                    'sid': sid,
+                    'gid': gid,
+                    'did': did
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def msql(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function returns modified SQL for the selected
+        Exclusion constraint.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        data = {}
+        for k, v in request.args.items():
+            try:
+                data[k] = json.loads(v)
+            except ValueError:
+                data[k] = v
+
+        data['schema'] = self.schema
+        data['table'] = self.table
+        try:
+            sql = self.get_sql(data, tid, exid)
+            sql = sql.strip('\n').strip(' ')
+
+            return make_json_response(
+                    data=sql,
+                    status=200
+                    )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    def get_sql(self, data, tid, exid=None):
+        """
+        This function will generate sql from model data.
+
+        Args:
+          data: Contains the data of the selected Exclusion constraint.
+          tid: Table ID.
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        if exid is not None:
+            sql = render_template("/".join([self.template_path, 'properties.sql']),
+                                  tid=tid,
+                                  exid=exid)
+            status, res = self.conn.execute_dict(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            old_data = res['rows'][0]
+            required_args = ['name']
+            for arg in required_args:
+                if arg not in data:
+                    data[arg] = old_data[arg]
+
+            sql = render_template("/".join([self.template_path, 'update.sql']),
+                                  data=data, o_data=old_data)
+        else:
+            required_args = ['columns']
+
+            for arg in required_args:
+                if arg not in data:
+                    return _('-- definition incomplete')
+                elif isinstance(data[arg], list) and len(data[arg]) < 1:
+                    return _('-- definition incomplete')
+
+            sql = render_template("/".join([self.template_path, 'create.sql']),
+                                  data=data, conn=self.conn)
+            sql += "\n"
+            sql += render_template("/".join([self.template_path, 'grant.sql']),
+                                   data=data, conn=self.conn)
+
+        return sql
+
+    @check_precondition
+    def sql(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function generates sql to show in the sql pane for the selected
+        Exclusion constraint.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        try:
+            SQL = render_template(
+                "/".join([self.template_path, 'properties.sql']),
+                tid=tid, conn=self.conn, exid=exid)
+            status, result = self.conn.execute_dict(SQL)
+            if not status:
+                return internal_server_error(errormsg=result)
+
+            data = result['rows'][0]
+            data['schema'] = self.schema
+            data['table'] = self.table
+
+            sql = render_template(
+                "/".join([self.template_path, 'get_constraint_cols.sql']),
+                cid=exid,
+                colcnt=data['indnatts'])
+            status, res = self.conn.execute_dict(sql)
+
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            columns = []
+            for row in res['rows']:
+                if row['options'] & 1:
+                    order = False
+                    nulls_order = True if (row['options'] & 2) else False
+                else:
+                    order = True
+                    nulls_order = True if (row['options'] & 2) else False
+
+                columns.append({"column": row['coldef'].strip('"'),
+                                "oper_class": row['opcname'],
+                                "order": order,
+                                "nulls_order": nulls_order,
+                                "operator": row['oprname']
+                                })
+
+            data['columns'] = columns
+
+            if not data['amname'] or data['amname'] == '':
+                data['amname'] = 'btree'
+
+            SQL = render_template(
+                "/".join([self.template_path, 'create.sql']), data=data)
+            SQL += "\n"
+            SQL += render_template(
+                "/".join([self.template_path, 'grant.sql']),
+                data=data, conn=self.conn)
+
+            sql_header = "-- Constraint: {0}\n\n-- ".format(data['name'])
+
+            sql_header += render_template(
+                    "/".join([self.template_path, 'delete.sql']),
+                    data=data)
+            sql_header += "\n"
+
+            SQL = sql_header + SQL
+
+            return ajax_response(response=SQL)
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_access_methods(self, gid, sid, did, scid, tid, exid=None):
+        """
+        This function returns access methods.
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        res = [{'label': '', 'value': ''}]
+        sql = render_template("/".join([self.template_path,
+                                        'get_access_methods.sql']))
+        status, rest = self.conn.execute_2darray(sql)
+
+        if not status:
+            return internal_server_error(errormsg=rest)
+
+        for row in rest['rows']:
+            res.append(
+                {'label': row['amname'], 'value': row['amname']}
+            )
+        return make_json_response(
+                data=res,
+                status=200
+                )
+
+
+    @check_precondition
+    def get_oper_class(self, gid, sid, did, scid, tid, exid=None):
+        """
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        data = request.args if request.args else None
+        try:
+            if data and 'indextype' in data:
+                SQL = render_template("/".join([self.template_path,
+                                                'get_oper_class.sql']),
+                                      indextype=data['indextype'])
+
+                status, res = self.conn.execute_2darray(SQL)
+
+                if not status:
+                    return internal_server_error(errormsg=res)
+                result = []
+                for row in res['rows']:
+                    result.append([row['opcname'], row['opcname']])
+                return make_json_response(
+                    data=result,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+
+    @check_precondition
+    def get_operator(self, gid, sid, did, scid, tid, exid=None):
+        """
+
+        Args:
+          gid: Server Group ID
+          sid: Server ID
+          did: Database ID
+          scid: Schema ID
+          tid: Table ID
+          exid: Exclusion constraint ID
+
+        Returns:
+
+        """
+        data = request.args if request.args else None
+        try:
+            if data and 'col' in data:
+                SQL = render_template("/".join([self.template_path,
+                                                'get_operator.sql']),
+                                      col=data['col'],
+                                      tid=tid)
+
+                status, res = self.conn.execute_2darray(SQL)
+
+                if not status:
+                    return internal_server_error(errormsg=res)
+                result = []
+                for row in res['rows']:
+                    result.append([row['oprname'], row['oprname']])
+                return make_json_response(
+                    data=result,
+                    status=200
+                    )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+constraint = ConstraintRegistry(
+    'exclusion_constraint', ExclusionConstraintModule, ExclusionConstraintView
+    )
+ExclusionConstraintView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/img/exclusion_constraint.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/img/exclusion_constraint.png
new file mode 100644
index 0000000000000000000000000000000000000000..bd62eef6410d9315857d2e6d246770e8b65b8f47
GIT binary patch
literal 725
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl47y|-)LR^8|wi}<ymTvVw_QrY1
zLC3?d^*UDhFWLA1|9`)wM`KREbvXQ5A-=(6%9fR9ZhU_6^4^pwS28lLWMw^=HS5>=
z_isLZP1<@%wQ`Qjk=K%*nNgG09JqAnY+~Zm($d$dsZT2^E~KQ~e)?Lsd6{J1L`ko#
z8QYH^XzhAZSorVPuP+-md|tBT-`~HFYif>6nkJPuNuqp?YIOD56Ib^8`rV6+d^K<0
zzkmPUtXQ!pGFqZ!hGg3swe*g4XK!wIaz5qe_HfFSrwbQu^AC`0S|K@Qk7Uk-*~hPM
zEhsq<82I=1?;BI6UhV1m_vg>S@bIm*jgk|0NG?2Of9R!T^@7Cpr%znEwc5~Vm%sly
zUEQ5NKC5l*c3rqFx8Q{2nrn7@9?A4<w%>5&&c`3`Z{FOOoV;9Hdxfs<!JM4Ox9?6m
zenWfBW$A4XY?hzYUUH^<`Bv54PXhWDq@H_cwegx-ao6F7A05CjWh@Eu3ubV5b|VeM
zN%D4g;b^-zwF=1LEbxdd2GSm2>~=ES4#-&V>Eak7aXC5R07H*Y2Gbdx45l?XZ+LiQ
zWMmY~)Ws(}c=qt=V{riyAu&Nw;pq&V9$ucOPn<e=Qd>hyv$&ZhB;@K9Q<JS*N=v?e
zImpD;=5|bN*|M}_&%~xBFluK@M_UI6S4XqEt8Zx7+`W4C?)K%=xA(7?_c&m$V4z{4
zVq&6Wqh!RTA|NX)Ek1w3j45*_&6>t1bmBw`i*--4qPDWM%7n?><t2V{=CjN{U1V)w
zV3-jtUHUai{xs0(swJ)wB`Jv|saDBFsX&Us$iUE0*8qr2Lkumf3@ojTjkFEStPBi}
hcbK)IXvob^$xN%ntzp~MJ}aOG22WQ%mvv4FO#q89Jv#sZ

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/js/exclusion_constraint.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/js/exclusion_constraint.js
new file mode 100644
index 0000000..fa2ab96
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/js/exclusion_constraint.js
@@ -0,0 +1,580 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin',
+        'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, Alertify) {
+
+  var ExclusionConstraintColumnModel = pgBrowser.Node.Model.extend({
+    defaults: {
+      column: undefined,
+      oper_class: undefined,
+      order: undefined,
+      nulls_order: undefined,
+      operator:undefined
+    },
+    schema: [
+      {
+        id: 'column', label:'{{ _('Column') }}', type:'text', editable: false,
+        cell:'string'
+      },{
+        id: 'oper_class', label:'{{ _('Operator class') }}', type:'text',
+        node: 'exclusion_constraint', url: 'get_oper_class', first_empty: true,
+        disabled: function(m) {
+          if (!m.top.isNew()) {
+            return true;
+          }
+
+          if (m.collection) {
+            var indexType = m.collection.top.get('amname')
+            return !(indexType == 'btree' || _.isUndefined(indexType) ||
+            _.isNull(indexType) || indexType == '');
+          } else {
+            return false;
+          }
+        },
+        select2: {
+          allowClear: true, width: 'style',
+          placeholder: 'Select operator class'
+        }, cell: Backgrid.Extension.Select2Cell.extend({
+          initialize: function () {
+          Backgrid.Extension.Select2Cell.prototype.initialize.apply(this, arguments);
+
+          var self = this,
+            url = self.column.get('url') || self.defaults.url,
+            m = self.model,
+            indextype = self.model.collection.handler.get('amname');
+
+            if (url && (indextype == 'btree' || _.isUndefined(indextype) ||
+                _.isNull(indextype) || indextype == '')) {
+              var node = this.column.get('schema_node'),
+                  eventHandler = m.top || m,
+                  node_info = this.column.get('node_info'),
+                  full_url = node.generate_url.apply(
+                    node, [
+                      null, url, this.column.get('node_data'),
+                      this.column.get('url_with_id') || false, node_info
+                    ]),
+                  data = [];
+
+              indextype = 'btree';
+
+              if (this.column.get('version_compatible')) {
+                eventHandler.trigger('pgadmin:view:fetching', m, self.column);
+                $.ajax({
+                  async: false,
+                  data : {indextype:indextype},
+                  url: full_url,
+                  success: function(res) {
+                    data = res.data;
+                  },
+                  error: function() {
+                    eventHandler.trigger('pgadmin:view:fetch:error', m, self.column);
+                  }
+                });
+                eventHandler.trigger('pgadmin:view:fetched', m, self.column);
+              }
+              /*
+               * Transform the data
+               */
+              transform = self.column.get('transform') || self.defaults.transform;
+              if (transform && _.isFunction(transform)) {
+                // We will transform the data later, when rendering.
+                // It will allow us to generate different data based on the
+                // dependencies.
+                self.column.set('options', transform.bind(self, data));
+              } else {
+                self.column.set('options', data);
+              }
+            } else {
+              self.column.set('options', []);
+            }
+          }
+        })
+      },{
+        id: 'order', label:'{{ _('DESC') }}', type: 'switch',
+        options: {
+          onText: 'ASC',
+          offText: 'DESC',
+        },disabled: function(m) {
+          return !m.top.isNew();
+        }
+      },{
+        id: 'nulls_order', label:'{{ _('NULLs order') }}', type:"switch",
+        options: {
+          onText: 'FIRST',
+          offText: 'LAST',
+        },disabled: function(m) {
+          return !m.top.isNew();
+        }
+      },{
+        id: 'operator', label:'{{ _('Operator') }}', type: 'text',
+        node: 'exclusion_constraint', url: 'get_operator', first_empty: false,
+        disabled: function(m) {
+          return !m.top.isNew();
+        },
+        select2: {
+          allowClear: false, width: 'style',
+        }, cell: Backgrid.Extension.Select2Cell.extend({
+        initialize: function () {
+          Backgrid.Extension.Select2Cell.prototype.initialize.apply(this, arguments);
+
+          var self = this,
+            url = self.column.get('url') || self.defaults.url,
+            m = self.model,
+            col = self.model.get('column');
+
+            if (url && !_.isUndefined(col) && !_.isNull(col) && col != '') {
+              var node = this.column.get('schema_node'),
+                  eventHandler = m.top || m,
+                  node_info = this.column.get('node_info'),
+                  full_url = node.generate_url.apply(
+                    node, [
+                      null, url, this.column.get('node_data'),
+                      this.column.get('url_with_id') || false, node_info
+                    ]),
+                  data = [];
+
+              if (this.column.get('version_compatible')) {
+                eventHandler.trigger('pgadmin:view:fetching', m, self.column);
+                $.ajax({
+                  async: false,
+                  data : {col:col},
+                  url: full_url,
+                  success: function(res) {
+                    data = res.data;
+                  },
+                  error: function() {
+                    eventHandler.trigger('pgadmin:view:fetch:error', m, self.column);
+                  }
+                });
+                eventHandler.trigger('pgadmin:view:fetched', m, self.column);
+              }
+              /*
+               * Transform the data
+               */
+              transform = self.column.get('transform') || self.defaults.transform;
+              if (transform && _.isFunction(transform)) {
+                // We will transform the data later, when rendering.
+                // It will allow us to generate different data based on the
+                // dependencies.
+                self.column.set('options', transform.bind(self, data));
+              } else {
+                self.column.set('options', data);
+              }
+            } else {
+              self.column.set('options', []);
+            }
+          }
+        })
+      }
+    ],
+    validate: function() {
+      var err = {},
+          errmsg = null,
+          data = this.toJSON();
+
+      if (_.isUndefined(data.operator) ||
+        _.isNull(data.operator) ||
+        String(data.operator).replace(/^\s+|\s+$/g, '') == '') {
+        return _("Please specify value for operator.");
+      }
+      return null;
+    }
+  });
+
+  var ExclusionConstraintColumnControl =  Backform.ExclusionConstraintColumnControl =
+    Backform.UniqueColCollectionControl.extend({
+
+    initialize: function(opts) {
+      Backform.UniqueColCollectionControl.prototype.initialize.apply(
+        this, arguments
+          );
+
+      var self = this,
+        node = self.field.get('node').type,
+        headerSchema = [{
+          id: 'column', label:'', type:'text',
+          node: 'column', control: Backform.NodeListByNameControl,
+          cache_level: 'server', canAdd: function(m) {
+            return m.isNew();
+          },
+          select2: {
+            allowClear: false, width: 'style',
+            placeholder: 'Select column'
+          }, first_empty: !self.model.isNew(),
+          disabled: function(m) {
+            return !self.model.isNew();
+          }
+        }],
+        headerDefaults = {column: null,
+                          oper_class: null,
+                          order:false,
+                          nulls_order:false,
+                          operator:null},
+
+        gridCols = ['column', 'oper_class', 'order', 'nulls_order', 'operator'];
+
+      self.headerData = new (Backbone.Model.extend({
+        defaults: headerDefaults,
+        schema: headerSchema
+      }))({});
+
+      var headerGroups = Backform.generateViewSchema(
+          self.field.get('node_info'), self.headerData, 'create',
+          node, self.field.get('node_data')
+          ),
+          fields = [];
+
+      _.each(headerGroups, function(o) {
+        fields = fields.concat(o.fields);
+      });
+
+      self.headerFields = new Backform.Fields(fields);
+      self.gridSchema = Backform.generateGridColumnsFromModel(
+          self.field.get('node_info'), self.field.get('model'), 'edit', gridCols, self.field.get('schema_node')
+          );
+
+      self.controls = [];
+      self.listenTo(self.headerData, "change", self.headerDataChanged);
+      self.listenTo(self.headerData, "select2", self.headerDataChanged);
+      self.listenTo(self.collection, "add", self.onAddorRemoveColumns);
+      self.listenTo(self.collection, "remove", self.onAddorRemoveColumns);
+    },
+
+    generateHeader: function(data) {
+      var header = [
+        '<div class="subnode-header-form">',
+        ' <div class="container-fluid">',
+        '  <div class="row">',
+        '   <div class="col-md-4">',
+        '    <label class="control-label"><%-column_label%></label>',
+        '   </div>',
+        '   <div class="col-md-4" header="column"></div>',
+        '   <div class="col-md-4">',
+        '     <button class="btn-sm btn-default add" <%=canAdd ? "" : "disabled=\'disabled\'"%> ><%-add_label%></buttton>',
+        '   </div>',
+        '  </div>',
+        ' </div>',
+        '</div>',].join("\n")
+
+      _.extend(data, {
+        column_label: '{{ _('Column')}}',
+        add_label: '{{ _('ADD')}}'
+      });
+
+      var self = this,
+          headerTmpl = _.template(header),
+          $header = $(headerTmpl(data)),
+          controls = this.controls;
+
+      this.headerFields.each(function(field) {
+        var control = new (field.get("control"))({
+          field: field,
+          model: self.headerData
+        });
+
+        $header.find('div[header="' + field.get('name') + '"]').append(
+          control.render().$el
+        );
+
+        controls.push(control);
+      });
+
+      // We should not show add but in properties mode
+      if (data.mode == 'properties') {
+        $header.find("button.add").remove();
+      }
+
+      self.$header = $header;
+
+      return $header;
+    },
+
+    events: _.extend(
+                  {}, Backform.UniqueColCollectionControl.prototype.events,
+                {'click button.add': 'addColumns'}
+                ),
+
+    showGridControl: function(data) {
+
+      var self = this,
+          titleTmpl = _.template([
+            "<div class='subnode-header'>",
+            "<label class='control-label'><%-label%></label>",
+            "</div>"].join("\n")),
+          $gridBody =
+            $("<div class='pgadmin-control-group backgrid form-group col-xs-12 object subnode'></div>").append(
+              titleTmpl({label: data.label})
+            );
+      if (self.model.isNew()) {
+        self.collection.reset();
+      }
+
+      $gridBody.append(self.generateHeader(data));
+
+      var gridColumns = _.clone(this.gridSchema.columns);
+
+      // Insert Delete Cell into Grid
+      if (data.disabled == false && data.canDelete) {
+          gridColumns.unshift({
+            name: "pg-backform-delete", label: "",
+            cell: Backgrid.Extension.DeleteCell,
+            editable: false, cell_priority: -1
+          });
+      }
+
+      if (self.grid) {
+        self.grid.remove();
+        self.grid.null;
+      }
+      // Initialize a new Grid instance
+      var grid = self.grid = new Backgrid.Grid({
+        columns: gridColumns,
+        collection: self.collection,
+        className: "backgrid table-bordered"
+      });
+      self.$grid = grid.render().$el;
+
+      $gridBody.append(self.$grid);
+
+      setTimeout(function() {
+        self.headerData.set({
+          'column': self.$header.find(
+            'div[header="column"] select'
+            ).val()
+            }, {silent:true}
+          );
+      }, 10);
+
+      // Render node grid
+      return $gridBody;
+    },
+
+    headerDataChanged: function() {
+      var self = this, val,
+          data = this.headerData.toJSON(),
+          inSelected = false,
+          checkVars = ['column'];
+
+      if (!self.$header) {
+        return;
+      }
+
+      if (self.control_data.canAdd) {
+        self.collection.each(function(m) {
+          if (!inSelected) {
+            _.each(checkVars, function(v) {
+              if (!inSelected) {
+                val = m.get(v);
+                inSelected = ((
+                  (_.isUndefined(val) || _.isNull(val)) &&
+                  (_.isUndefined(data[v]) || _.isNull(data[v]))
+                  ) ||
+                  (val == data[v]));
+              }
+            });
+          }
+        });
+      }
+      else {
+        inSelected = true;
+      }
+
+      self.$header.find('button.add').prop('disabled', inSelected);
+    },
+
+    addColumns: function(ev) {
+      ev.preventDefault();
+      var self = this,
+          column = self.headerData.get('column');
+
+      if (!column || column == '') {
+        return false;
+      }
+
+      var m = new (self.field.get('model'))(
+                self.headerData.toJSON()),
+          coll = self.model.get(self.field.get('name'));
+
+      coll.add(m);
+
+      var idx = coll.indexOf(m);
+
+      // idx may not be always > -1 because our UniqueColCollection may
+      // remove 'm' if duplicate value found.
+      if (idx > -1) {
+        self.$grid.find('.new').removeClass('new');
+
+        var newRow = self.grid.body.rows[idx].$el;
+
+        newRow.addClass("new");
+        $(newRow).pgMakeVisible('backform-tab');
+      } else {
+        delete m;
+      }
+
+      return false;
+    },
+
+    onAddorRemoveColumns: function() {
+      var self = this;
+
+      // Wait for collection to be updated before checking for the button to be
+      // enabled, or not.
+      setTimeout(function() {
+        self.headerDataChanged();
+      }, 10);
+    },
+
+    remove: function() {
+      /*
+       * Stop listening the events registered by this control.
+       */
+      this.stopListening(this.headerData, "change", this.headerDataChanged);
+      this.listenTo(this.headerData, "select2", this.headerDataChanged);
+      this.listenTo(this.collection, "remove", this.onAddorRemoveColumns);
+
+      ExclusionConstraintColumnControl.__super__.remove.apply(this, arguments);
+
+      // Remove the header model
+      delete (this.headerData);
+
+    }
+  });
+
+  // Extend the browser's node class for exclusion constraint node
+  if (!pgBrowser.Nodes['exclusion_constraint']) {
+    pgAdmin.Browser.Nodes['exclusion_constraint'] = pgBrowser.Node.extend({
+      type: 'exclusion_constraint',
+      label: '{{ _('Exclusion constraint') }}',
+      collection_type: 'coll-constraints',
+      hasSQL: true,
+      parent_type: 'table',
+      canDrop: true,
+      canDropCascade: true,
+      hasDepends: true,
+      Init: function() {
+        /* Avoid multiple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        pgBrowser.add_menus([{
+          name: 'create_exclusion_constraint_on_coll', node: 'coll-constraints', module: this,
+          applies: ['object', 'context'], callback: 'show_obj_properties',
+          category: 'create', priority: 4, label: '{{ _('Exclusion constraint...') }}',
+          icon: 'wcTabIcon icon-exclusion_constraint', data: {action: 'create', check: true},
+          enable: 'canCreate'
+        }]);
+      },
+      is_not_valid: function(node) {
+        return (node && !node.valid);
+      },
+      // Define the model for exclusion constraint node
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          oid: undefined,
+          comment: undefined,
+          spcname: "pg_default",
+          amname: "gist",
+          fillfactor: undefined,
+          condeferrable: undefined,
+          condeferred: undefined,
+          columns: []
+        },
+
+        // Define the schema for the exclusion constraint node
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', type: 'text',
+          mode: ['properties', 'create', 'edit']
+        },{
+          id: 'oid', label:'{{ _('OID') }}', cell: 'string',
+          type: 'text' , mode: ['properties']
+        },{
+          id: 'comment', label:'{{ _('Comment') }}', cell: 'string',
+          type: 'multiline', mode: ['properties', 'create', 'edit'],
+          deps:['name'], disabled:function(m) {
+            var name = m.get('name');
+            if (!(name && name != '')) {
+              setTimeout(function(){
+                m.set('comment', null);
+              },10);
+              return true;
+            } else {
+              return false;
+            }
+          }
+        },{
+          id: 'spcname', label: '{{ _('Tablespace') }}',
+          type: 'text', group: '{{ _('Definition') }}',
+          control: 'node-list-by-name', node: 'tablespace',
+          select2:{allowClear:false},
+          filter: function(m) {
+            // Don't show pg_global tablespace in selection.
+            if (m.label == "pg_global") return false;
+            else return true;
+          }
+        },{
+          id: 'amname', label: '{{ _('Access method') }}',
+          type: 'text', group: '{{ _('Definition') }}',
+          control: 'node-ajax-options', url:"get_access_methods",
+          select2:{allowClear:true},
+          disabled: function(m) {
+            return !m.isNew();
+          }
+        },{
+          id: 'fillfactor', label: '{{ _('Fill factor') }}',
+          type: 'int', group: '{{ _('Definition') }}', allowNull: true
+        },{
+          id: 'condeferrable', label: '{{ _('Deferrable') }}',
+          type: 'switch', group: '{{ _('Definition') }}', deps: ['index'],
+          disabled: function(m) {
+            return !m.isNew();
+          }
+        },{
+          id: 'condeferred', label: '{{ _('Deferred') }}',
+          type: 'switch', group: '{{ _('Definition') }}',
+          deps: ['condeferrable'],
+          disabled: function(m) {
+            return !m.isNew();
+          }
+        },{
+          id: 'constraint', label:'{{ _('Constraint') }}', cell: 'string',
+          type: 'multiline', mode: ['create', 'edit'],
+          group: '{{ _('Definition') }}', disabled: function(m) {
+            return !m.isNew();
+          }
+        },{
+          id: 'columns', label: '{{ _('Columns') }}',
+          type: 'collection', group: '{{ _('Columns') }}', disabled: false,
+          deps:['amname'],
+          canAdd: function(m) {
+            // We can't update columns of existing exclusion constraint.
+            return m.isNew();
+          }, canDelete: true,
+          control: ExclusionConstraintColumnControl,
+          model: ExclusionConstraintColumnModel,
+          disabled: function(m) {
+            return !m.isNew();
+          }
+        }
+        ],
+        validate: function() {
+          this.errorModel.clear();
+
+          var columns = this.get('columns');
+          if ((_.isUndefined(columns) || _.isNull(columns) || columns.length < 1)) {
+            var msg = '{{ _('Please specify columns.') }}';
+            this.errorModel.set('columns', msg);
+            return msg;
+          }
+
+          return null;
+        }
+      })
+    });
+  }
+
+  return pgBrowser.Nodes['exclusion_constraint'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/begin.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/begin.sql
new file mode 100644
index 0000000..58bfee1
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/begin.sql
@@ -0,0 +1 @@
+BEGIN;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/create.sql
new file mode 100644
index 0000000..c6a53cd
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/create.sql
@@ -0,0 +1,12 @@
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    ADD{% if data.name %} CONSTRAINT {{ conn|qtIdent(data.name) }}{% endif%} EXCLUDE {% if data.amname and data.amname != '' %}USING {{data.amname}}{% endif %} (
+    {% for col in data.columns %}{% if loop.index != 1 %},
+    {% endif %}{{ conn|qtIdent(col.column)}} {% if col.oper_class and col.oper_class != '' %}{{col.oper_class}} {% endif%}{% if col.order %}ASC{% else %}DESC{% endif %} NULLS {% if col.nulls_order %}FIRST{% else %}LAST{% endif %} WITH {{col.operator}}{% endfor %}){% if data.fillfactor %}
+    WITH (FILLFACTOR={{data.fillfactor}}){% endif %}{% if data.spcname and data.spcname != "pg_default" %}
+
+    USING INDEX TABLESPACE {{ conn|qtIdent(data.spcname) }}{% endif %}
+{% if data.condeferrable %}
+
+    DEFERRABLE{% if data.condeferred %}
+ INITIALLY DEFERRED{% endif%}
+{% endif%}{% if data.constraint %} WHERE ({{data.constraint}}){% endif%};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/delete.sql
new file mode 100644
index 0000000..2096795
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/delete.sql
@@ -0,0 +1,3 @@
+{% if data %}
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }} DROP CONSTRAINT {{ conn|qtIdent(data.name) }}{% if cascade%} CASCADE{% endif %};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/end.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/end.sql
new file mode 100644
index 0000000..92d09d5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/end.sql
@@ -0,0 +1 @@
+END;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_access_methods.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_access_methods.sql
new file mode 100644
index 0000000..5d74057
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_access_methods.sql
@@ -0,0 +1,6 @@
+SELECT amname
+FROM pg_am
+WHERE EXISTS (SELECT 1
+              FROM pg_proc
+              WHERE oid=amgettuple)
+ORDER BY amname;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_constraint_cols.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_constraint_cols.sql
new file mode 100644
index 0000000..f766a71
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_constraint_cols.sql
@@ -0,0 +1,19 @@
+{% for n in range(colcnt|int) %}
+{% if loop.index != 1 %}
+UNION
+{% endif %}
+SELECT
+  i.indoption[{{loop.index -1}}] AS options,
+  pg_get_indexdef(i.indexrelid, {{loop.index}}, true) AS coldef,
+  op.oprname,
+  CASE WHEN (o.opcdefault = FALSE) THEN o.opcname ELSE null END AS opcname
+,
+  coll.collname, nspc.nspname as collnspname
+FROM pg_index i
+JOIN pg_attribute a ON (a.attrelid = i.indexrelid AND attnum = {{loop.index}})
+LEFT OUTER JOIN pg_opclass o ON (o.oid = i.indclass[{{loop.index -1}}])
+LEFT OUTER JOIN pg_constraint c ON (c.conindid = i.indexrelid) LEFT OUTER JOIN pg_operator op ON (op.oid = c.conexclop[{{loop.index}}])
+LEFT OUTER JOIN pg_collation coll ON a.attcollation=coll.oid
+LEFT OUTER JOIN pg_namespace nspc ON coll.collnamespace=nspc.oid
+WHERE i.indexrelid = {{cid}}::oid
+{% endfor %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_name.sql
new file mode 100644
index 0000000..7aaa522
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_name.sql
@@ -0,0 +1,3 @@
+SELECT conname as name
+FROM pg_constraint ct
+WHERE ct.conindid = {{cid}}::oid
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_oid.sql
new file mode 100644
index 0000000..168e13b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_oid.sql
@@ -0,0 +1,4 @@
+SELECT ct.oid
+FROM pg_constraint ct
+WHERE contype='x' AND
+ct.conname = {{ name|qtLiteral }};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_oid_with_transaction.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_oid_with_transaction.sql
new file mode 100644
index 0000000..abe636a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_oid_with_transaction.sql
@@ -0,0 +1,6 @@
+SELECT ct.oid,
+    ct.conname as name,
+    NOT convalidated as convalidated
+FROM pg_constraint ct
+WHERE contype='f' AND
+    conrelid = {{tid}}::oid LIMIT 1;
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_oper_class.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_oper_class.sql
new file mode 100644
index 0000000..c6739d0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_oper_class.sql
@@ -0,0 +1,7 @@
+SELECT opcname
+FROM pg_opclass opc,
+pg_am am
+WHERE opcmethod=am.oid AND
+      am.amname ={{indextype|qtLiteral}} AND
+      NOT opcdefault
+ORDER BY 1
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_operator.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_operator.sql
new file mode 100644
index 0000000..692a963
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_operator.sql
@@ -0,0 +1,9 @@
+SELECT DISTINCT op.oprname as oprname
+FROM pg_operator op
+LEFT OUTER JOIN pg_attribute att ON (op.oprleft=att.atttypid OR op.oprright=att.atttypid)
+WHERE  oprcom > 0 AND
+    att.attrelid = {{tid}}::oid AND
+    att.attname = {{col|qtLiteral}} AND
+    att.attnum > 0 AND
+    att.attisdropped IS FALSE
+ORDER BY oprname
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_parent.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_parent.sql
new file mode 100644
index 0000000..a652857
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/get_parent.sql
@@ -0,0 +1,7 @@
+SELECT nsp.nspname AS schema,
+    rel.relname AS table
+FROM
+    pg_class rel
+JOIN pg_namespace nsp
+ON rel.relnamespace = nsp.oid::int
+WHERE rel.oid = {{tid}}::int
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/grant.sql
new file mode 100644
index 0000000..0fb0ea5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/grant.sql
@@ -0,0 +1,4 @@
+{% if data.comment %}
+COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
+    IS {{ data.comment|qtLiteral }};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/nodes.sql
new file mode 100644
index 0000000..c67c40d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/nodes.sql
@@ -0,0 +1,7 @@
+SELECT conindid as oid,
+    conname as name,
+    NOT convalidated as convalidated
+FROM pg_constraint ct
+WHERE contype='x' AND
+    conrelid = {{tid}}::oid
+ORDER BY conname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/properties.sql
new file mode 100644
index 0000000..3a1c897
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/properties.sql
@@ -0,0 +1,30 @@
+SELECT cls.oid,
+    cls.relname as name,
+    indnatts,
+    amname,
+    COALESCE(spcname, 'pg_default') as spcname,
+    CASE contype
+        WHEN 'p' THEN desp.description
+        WHEN 'u' THEN desp.description
+        WHEN 'x' THEN desp.description
+        ELSE des.description
+    END AS comment,
+    condeferrable,
+    condeferred,
+    substring(array_to_string(cls.reloptions, ',') from 'fillfactor=([0-9]*)') AS fillfactor
+FROM pg_index idx
+JOIN pg_class cls ON cls.oid=indexrelid
+JOIN pg_class tab ON tab.oid=indrelid
+LEFT OUTER JOIN pg_tablespace ta on ta.oid=cls.reltablespace
+JOIN pg_namespace n ON n.oid=tab.relnamespace
+JOIN pg_am am ON am.oid=cls.relam
+LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i')
+LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid)
+LEFT OUTER JOIN pg_description des ON (des.objoid=cls.oid AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0 AND desp.classoid='pg_constraint'::regclass)
+WHERE indrelid = {{tid}}::oid
+{% if cid %}
+AND cls.oid = {{cid}}::oid
+{% endif %}
+AND contype='x'
+ORDER BY cls.relname
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/update.sql
new file mode 100644
index 0000000..d062e8b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/templates/exclusion_constraint/sql/update.sql
@@ -0,0 +1,22 @@
+{### SQL to update exclusion constraint object ###}
+{% if data %}
+{# ==== To update exclusion constraint name ==== #}
+{% if data.name != o_data.name %}
+ALTER TABLE {{ conn|qtIdent(data.schema, data.table) }}
+    RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};
+{% endif %}
+{# ==== To update exclusion constraint tablespace ==== #}
+{% if data.spcname and data.spcname != o_data.spcname %}
+ALTER INDEX {{ conn|qtIdent(data.schema, data.name) }}
+    SET TABLESPACE {{ conn|qtIdent(data.spcname) }};
+{% endif %}
+{% if data.fillfactor and data.fillfactor != o_data.fillfactor %}
+ALTER INDEX {{ conn|qtIdent(data.schema, data.name) }}
+    SET (FILLFACTOR={{ data.fillfactor }});
+{% endif %}
+{# ==== To update exclusion constraint comments ==== #}
+{% if data.comment and data.comment != o_data.comment %}
+COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.table) }}
+    IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% endif %}
\ No newline at end of file
-- 
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