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