public inbox for [email protected]help / color / mirror / Atom feed
pgAdmin4 PATCH: Domain Module 29+ messages / 4 participants [nested] [flat]
* pgAdmin4 PATCH: Domain Module @ 2016-01-20 04:48 Khushboo Vashi <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Khushboo Vashi @ 2016-01-20 04:48 UTC (permalink / raw) To: pgadmin-hackers Hi, Please find attached patch for the Domain Module. The patch will be modified after Types module implementation as we need to populate Base Type and some Type related validations from the Types module. Please review it and let me know the feedback. Thanks, Khushboo -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers Attachments: [text/x-patch] domains.patch (67.0K, 3-domains.patch) download | inline diff: diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py new file mode 100644 index 0000000..250a665 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py @@ -0,0 +1,510 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas as schemas +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + +class DomainModule(CollectionNodeModule): + NODE_TYPE = 'domain' + COLLECTION_LABEL = gettext("Domains") + + def __init__(self, *args, **kwargs): + self.min_ver = None + self.max_ver = None + super(DomainModule, self).__init__(*args, **kwargs) + + # Before loading this module we need to make sure that scid is schema object + def BackendSupported(self, manager, **kwargs): + """ + This function will validate schema name & scid against catalogs then allow us to + make decision if we want to load this module or not for the schema + """ + if super(DomainModule, self).BackendSupported(manager, **kwargs): + conn = manager.connection() + # If DB is not connected then return error to browser + if not conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + ver = manager.version + server_type = manager.server_type + # we will set template path for sql scripts + if ver >= 90100: + template_path = 'domains/sql/9.1_plus' + else: + # Note: Domain is not supported below postgres version 9.1 + # Hence we will not load this module + return False + + SQL = render_template("/".join([template_path, 'backend_support.sql']), scid=kwargs['scid']) + status, res = conn.execute_scalar(SQL) + # check if any errors + if not status: + return internal_server_error(errormsg=res) + # Check scid is catalog and from 'sys', 'dbo', 'information_schema', + # then False (Do not load this module), othewise True + if res is True: + return False + else: + return True + + def get_nodes(self, gid, sid, did, scid): + """ + Generate the collection node + """ + yield self.generate_browser_collection_node(scid) + + @property + def script_load(self): + """ + Load the module script for database, when any of the database node is + initialized. + """ + return schemas.SchemaModule.NODE_TYPE + + +blueprint = DomainModule(__name__) + + +class DomainView(PGChildNodeView): + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'doid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_schemas': [{'get': 'get_schemas'}, {'get': 'get_schemas'}], + 'get_collations': [{'get': 'get_collations'}, {'get': 'get_collations'}], + 'get_types': [{'get': 'get_types'}, {'get': 'get_types'}] + }) + + + def module_js(self): + return make_response( + render_template( + "domains/js/domains.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + # Get database connection + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If database is not connected then return error + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90100: + self.template_path = 'domains/sql/9.1_plus' + return f(*args, **kwargs) + + return wrap + + + @check_precondition + def list(self, gid, sid, did, scid): + """List the Domains.""" + + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + + @check_precondition + def nodes(self, gid, sid, did, scid): + """Returns all the Domains.""" + + res = [] + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + row['name'], + icon="icon-domain" + )) + + return make_json_response( + data=res, + status=200 + ) + + + @check_precondition + def properties(self, gid, sid, did, scid, doid): + """Returns Domain properties.""" + + SQL = render_template("/".join([self.template_path, 'properties.sql']),scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, 'get_constraints.sql']),doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + + data['constraints'] = res['rows'] + return ajax_response( + response=data, + status=200 + ) + + + @check_precondition + def get_schemas(self, gid, sid, did, scid, doid=None): + """Returns Schemas for particular database.""" + + res = [{ 'label': '', 'value': '' }] + try: + SQL = render_template("/".join([self.template_path, 'get_schemas.sql']), scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append( + { 'label': row['nspname'], 'value': row['nspname'] } + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def get_collations(self, gid, sid, did, scid, doid=None): + """Returns Collations.""" + + res = [{ 'label': '', 'value': '' }] + try: + SQL = render_template("/".join([self.template_path, 'get_collations.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append( + { 'label': row['copy_collation'], 'value': row['copy_collation'] } + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def get_types(self, gid, sid, did, scid, doid=None): + """Returns Types.""" + # TODO: This function should be removed once Types module will be completed. + + res = [{ 'label': '', 'value': '' }] + try: + SQL = render_template("/".join([self.template_path, 'get_types.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append( + { 'label': row['typname'], 'value': row['typname'] } + ) + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def create(self, gid, sid, did, scid): + """ + Creates new domain object. + + Expected Parameters: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Expected Response: + Domain object in json format. + """ + + data = request.form if request.form else json.loads(request.data.decode()) + required_args = [ + 'name', + 'owner', + 'basensp', + 'basetype' + ] + + for arg in required_args: + if arg not in data or data[arg] == '': + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + try: + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, qtIdent=self.qtIdent) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # we need oid to to add object in tree at browser, below sql will gives the same + SQL = render_template("/".join([self.template_path, 'get_oid.sql']), basensp=data['basensp'], name=data['name']) + status, doid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=doid) + + return jsonify( + node=self.blueprint.generate_browser_node( + doid, + data['name'], + icon="icon-domain" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def delete(self, gid, sid, did, scid, doid): + """ + Drop the Domain. + """ + # Below will decide if it's simple drop or drop with cascade call + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + SQL = render_template("/".join([self.template_path, 'delete.sql']), scid=scid, doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + name, basensp = res['rows'][0] + + SQL = render_template("/".join([self.template_path, 'delete.sql']), name=name, basensp=basensp, cascade=cascade) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, scid, doid): + """ + Update the Domain. + """ + data = request.form if request.form else json.loads(request.data.decode()) + + status, SQL = self.getSQL(gid, sid, data, scid, doid) + if not status: + return internal_server_error(errormsg=SQL) + + try: + if SQL and SQL.strip('\n') and SQL.strip(' '): + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Domain updated", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def msql(self, gid, sid, did, scid, doid=None): + """ + Returns the modified SQL. + + Expected Parameters: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Expected Response: + SQL statements to create/update the Domain. + """ + data = request.args + if doid is None: + required_args = [ + 'name', + 'owner', + 'basensp', + 'basetype' + ] + + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + SQL = self.getSQL(gid, sid, data, scid, doid) + if isinstance(SQL, str) and SQL and SQL.strip('\n') and SQL.strip(' '): + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + + def getSQL(self, gid, sid, data, scid, doid=None): + """ + Genrates the SQL statements to create/update the Domain. + """ + try: + if doid is not None: + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + SQL = render_template( + "/".join([self.template_path,'update.sql']), + data=data, o_data=old_data, conn=self.conn, qtIdent=self.qtIdent + ) + else: + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn, qtIdent=self.qtIdent) + return True,SQL + + except Exception as e: + return False, e + +DomainView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py new file mode 100644 index 0000000..7f0575b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py @@ -0,0 +1,390 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import NodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas.domains as domains +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + +class DomainConstraintModule(CollectionNodeModule): + NODE_TYPE = 'domain-constraints' + COLLECTION_LABEL = gettext("Domain Constraints") + + def __init__(self, *args, **kwargs): + self.min_ver = None + self.max_ver = None + super(DomainConstraintModule, self).__init__(*args, **kwargs) + + def get_nodes(self, gid, sid, did, scid, doid): + """ + Generate the collection node + """ + yield self.generate_browser_collection_node(doid) + + @property + def node_inode(self): + """ + Override this property to make the node as leaf node. + """ + return False + + + @property + def script_load(self): + """ + Load the module script for database, when any of the database node is + initialized. + """ + return domains.DomainModule.NODE_TYPE + + +blueprint = DomainConstraintModule(__name__) + + +class DomainConstraintView(NodeView): + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'}, + {'type': 'int', 'id': 'doid'} + ] + ids = [ + {'type': 'int', 'id': 'coid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def module_js(self): + """ + This property defines (if javascript) exists for this node. + Override this property for your own logic. + """ + return make_response( + render_template( + "domain-constraints/js/domain-constraints.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90100: + self.template_path = 'domain-constraints/sql/9.1_plus' + return f(*args, **kwargs) + + return wrap + + + @check_precondition + def list(self, gid, sid, did, scid, doid): + """ + List the Domain Constraints. + """ + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + + @check_precondition + def nodes(self, gid, sid, did, scid, doid): + """ + Returns all the Domain Constraints. + """ + res = [] + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid) + status, rset = self.conn.execute_2darray(SQL) + + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + row['conname'], + icon="icon-domain-constraints" + )) + + return make_json_response( + data=res, + status=200 + ) + + + @check_precondition + def properties(self, gid, sid, did, scid, doid, coid): + """ + Returns the Domain Constraints property. + """ + + SQL = render_template("/".join([self.template_path, 'properties.sql']),doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + return ajax_response( + response=res['rows'][0], + status=200 + ) + + + @check_precondition + def create(self, gid, sid, did, scid, doid): + """ + Creates new the Domain Constraints. + + Expected Parameters: + conname: Constraints Name + consrc: Constraints Check + + Expected Response: + Domain Constraint object in json format. + """ + + data = request.form if request.form else json.loads(request.data.decode()) + required_args = [ + 'conname', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + try: + # Get Schema and Domain. + SQL = render_template("/".join([self.template_path, 'get_domain.sql']), doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, domain=domain, schema=schema, qtIdent=self.qtIdent) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get the recently added constraints oid + SQL = render_template("/".join([self.template_path, 'get_oid.sql']), doid=doid, name=data['conname']) + status, coid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=doid) + + return jsonify( + node=self.blueprint.generate_browser_node( + coid, + data['conname'], + icon="icon-domain" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def delete(self, gid, sid, did, scid, doid, coid): + """ + Drop the Doman Constraints. + """ + try: + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, 'delete.sql']), data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def update(self, gid, sid, did, scid, doid, coid): + """ + Update the Domain Constraints. + """ + data = request.form if request.form else json.loads(request.data.decode()) + + SQL = self.getSQL(gid, sid, data, scid, doid, coid) + try: + if SQL and SQL.strip('\n') and SQL.strip(' '): + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Domain updated", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def msql(self, gid, sid, did, scid, doid=None): + """ + Returns the modified SQL. + + Expected Parameters: + conname: Constraints Name + consrc: Constraints Check + + Expected Response: + Domain Constraint object in json format. + """ + data = request.args + if doid is None: + required_args = [ + 'conname', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + SQL = self.getSQL(gid, sid, data, scid, doid) + if isinstance(SQL, str) and SQL and SQL.strip('\n') and SQL.strip(' '): + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + + def getSQL(self, gid, sid, data, scid, doid, coid=None): + """ + Genrates the SQL statements to create/update the Domain Constraint. + """ + try: + if coid is not None: + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + old_data = res['rows'][0] + + SQL = render_template( + "/".join([self.template_path,'update.sql']), + data=data, o_data=old_data, conn=self.conn + ) + else: + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn) + return SQL + except Exception as e: + return internal_server_error(errormsg=str(e)) + +DomainConstraintView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png new file mode 100644 index 0000000..d62e137 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png new file mode 100644 index 0000000..32a045b Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png new file mode 100644 index 0000000..9d1d2a0 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css new file mode 100644 index 0000000..68cd622 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css @@ -0,0 +1,9 @@ +.pg-icon-domain-constraints { + background-image: url('{{ url_for('NODE-domain-constraints.static', filename='img/domain-constraints.png') }}') !important; + border-radius: 10px; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js new file mode 100644 index 0000000..fb3de7d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js @@ -0,0 +1,95 @@ +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + if (!pgBrowser.Nodes['coll-domain-constraints']) { + var databases = pgAdmin.Browser.Nodes['coll-domain-constraints'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + type: 'coll-domain-constraints' + }); + }; + + if (!pgBrowser.Nodes['domain-constraints']) { + pgAdmin.Browser.Nodes['domain-constraints'] = pgBrowser.Node.extend({ + type: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + collection_type: 'coll-domain-constraints', + hasSQL: true, + parent_type: ['domain'], + Init: function() { + /* Avoid mulitple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + conname: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined + }, + schema: [{id: 'conname', label:'Name', type:'text', cell:'string'}, + {id: 'description', label:'Comment', type: 'multiline', cell: 'string', mode: ['properties', 'create', 'edit'], disabled: function(m) { return !m.isNew(); }}, + {id: 'consrc', label:'Check', type: 'multiline', cel: 'string', group: 'Definition', mode: ['properties', 'create', 'edit'], disabled: function(m) { return !m.isNew(); }}, + {id: 'connoinherit', label:'No Inherit', type: 'switch', cell: 'boolean', group: 'Definition', mode: ['properties', 'create', 'edit'], disabled: function(m) { return !m.isNew(); }}, + {id: 'convalidated', label:"Don't Validate", type: 'switch', cell: 'boolean', group: 'Definition'} + ], + validate: function() { + var err = {}, + errmsg; + + if (_.isUndefined(this.get('conname')) || String(this.get('conname')).replace(/^\s+|\s+$/g, '') == '') { + err['conname'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['conname']; + } + + if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') { + err['consrc'] = '{{ _('Check can not be empty!') }}'; + errmsg = errmsg || err['consrc']; + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + + }, + }), + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql new file mode 100644 index 0000000..ed2610d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql @@ -0,0 +1,6 @@ +{% if data and schema and domain %} + ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ data.conname }} CHECK ({{ data.consrc }} ) + {% if data.convalidated %} NOT VALID {% endif %} {% if data.connoinherit %} NO INHERIT {% endif %}; +{% endif %} + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..1a56583 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} + ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ data.conname }} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql new file mode 100644 index 0000000..a199c5c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql @@ -0,0 +1,7 @@ +SELECT d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..344abcc --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql @@ -0,0 +1,4 @@ +SELECT oid, conname as name + FROM pg_constraint +WHERE contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..7ed21d9 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql @@ -0,0 +1,18 @@ +SELECT 'TABLE' AS objectkind, c.oid, conname, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc, connoinherit, convalidated +FROM pg_constraint c +JOIN pg_class cl ON cl.oid=conrelid +JOIN pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT 'DOMAIN' AS objectkind, c.oid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc +, connoinherit, convalidated FROM pg_constraint c + JOIN pg_type t ON t.oid=contypid + JOIN pg_namespace nl ON nl.oid=typnamespace + LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) + WHERE contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql new file mode 100644 index 0000000..9a2c8d7 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql @@ -0,0 +1,4 @@ +{% if data.conname %} + ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ o_data.conname }} TO {{ data.conname }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png new file mode 100644 index 0000000..5562152 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png new file mode 100644 index 0000000..7521cdd Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png new file mode 100644 index 0000000..42ca929 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css new file mode 100644 index 0000000..7d4fad0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css @@ -0,0 +1,9 @@ +.pg-icon-domain { + background-image: url('{{ url_for('NODE-domain.static', filename='img/domain.png') }}') !important; + border-radius: 10px; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js new file mode 100644 index 0000000..8fcf507 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js @@ -0,0 +1,236 @@ +/* Create and Register Domain Collection and Domain Node. */ + +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + if (!pgBrowser.Nodes['coll-domain']) { + var databases = pgAdmin.Browser.Nodes['coll-domain'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain', + label: '{{ _('Domains') }}', + type: 'coll-domain' + }); + }; + + var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: null, + security_label: null + }, + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text' + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text' + }], + validate: function() { + var err = {}, + errmsg = null, + data = this.toJSON(); + + if (_.isUndefined(data.label) || + _.isNull(data.label) || + String(data.label).replace(/^\s+|\s+$/g, '') == '') { + return _("Please specify the value for all the security providers."); + } + return null; + } + }); + + var ConstraintsModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + conname: undefined, + description: undefined, + slony: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined + }, + schema: [ + {id: 'conname', label:'Name', type:'text', cell:'string'}, + {id: 'description', label:'Comment', type: 'multiline', cell: 'string'}, + {id: 'slony', label:'Use Slony', type: 'text', cell: 'string'}, + {id: 'consrc', label:'Check', type: 'multiline', cel: 'string', group: 'definition'}, + {id: 'connoinherit', label:'No Inherit', type: 'switch', cell: 'boolean', group: 'definition'}, + {id: 'convalidated', label:"Don't Validate", type: 'switch', cell: 'boolean', group: 'definition'} + ], + validate: function() { + // TODO: Add validation here + }, + toJSON: Backbone.Model.prototype.toJSON + }); + + + if (!pgBrowser.Nodes['domain']) { + pgAdmin.Browser.Nodes['domain'] = pgBrowser.Node.extend({ + type: 'domain', + label: '{{ _('Domain') }}', + collection_type: 'coll-domain', + hasSQL: true, + parent_type: ['schema'], + Init: function() { + /* Avoid mulitple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + canDropCascade: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + oid: undefined, + owner: undefined, + basensp: undefined, + description: undefined, + basetype: undefined, + typlen: undefined, + precision: undefined, + typdefault: undefined, + typnotnull: undefined, + collname: undefined, + constraints: [], + seclabels: [] + }, + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'] + },{ + id: 'oid', label:'{{ _('Oid') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl, + node: 'role', type: 'text', mode: ['edit', 'create', 'properties'] + },{ + id: 'basensp', label:'{{ _('Schema') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', url: 'get_schemas' + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'basetype', label:'{{ _('Base Type') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', mode:['properties', 'create', 'edit'], group: 'Definition', url: 'get_types', + disabled: function(m) { return !m.isNew(); } + },{ + id: 'typlen', label:'{{ _('Length') }}', cell: 'string', + type: 'text', group: 'Definition', disabled: function(m) { return !m.isNew(); } + },{ + id: 'precision', label:'{{ _('Precision') }}', cell: 'string', + type: 'text', group: 'Definition', disabled: function(m) { return !m.isNew(); } + },{ + id: 'typdefault', label:'{{ _('Default') }}', cell: 'string', + type: 'text', group: 'Definition' + },{ + id: 'typnotnull', label:'{{ _('Not Null') }}', cell: 'boolean', + type: 'switch', group: 'Definition' + },{ + id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', group: 'Definition', url: 'get_collations', disabled: function(m) { return !m.isNew(); } + },{ + id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string', + type: 'collection', group: 'Constraints', visible: false, mode: ['edit', 'create'], + model: ConstraintsModel, canAdd: true, canDelete: false, canEdit: false + },{ + id: 'seclabels', label: '{{ _('Security Labels') }}', + model: SecurityModel, editable: false, type: 'collection', + group: '{{ _('Security') }}', mode: ['edit', 'create'], + min_version: 90100, canAdd: true, + canEdit: false, canDelete: true, control: 'unique-col-collection' + } + ], + validate: function() + { + var err = {}, + errmsg, + seclabels = this.get('seclabels'); + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('basetype')) || String(this.get('basetype')).replace(/^\s+|\s+$/g, '') == '') { + err['basetype'] = '{{ _('Base Type can not be empty!') }}'; + errmsg = errmsg || err['basetype']; + } + + if (seclabels) { + var secLabelsErr; + for (var i = 0; i < seclabels.models.length && !secLabelsErr; i++) { + secLabelsErr = (seclabels.models[i]).validate.apply(seclabels.models[i]); + if (secLabelsErr) { + err['seclabels'] = secLabelsErr; + errmsg = errmsg || secLabelsErr; + } + } + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + } + }), + canCreate: function(itemData, item, data) { + //If check is false then , we will allow create menu + if (data && data.check == false) + return true; + + var t = pgBrowser.tree, i = item, d = itemData; + // To iterate over tree to check parent node + while (i) { + // If it is schema then allow user to create domain + if (_.indexOf(['schema'], d._type) > -1) + return true; + + if ('coll-domain' == d._type) { + //Check if we are not child of catalog + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + if( prev_d._type == 'catalog') { + return false; + } else { + return true; + } + } + i = t.hasParent(i) ? t.parent(i) : null; + d = i ? t.itemData(i) : null; + } + // by default we do not want to allow create menu + return true; + } + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql new file mode 100644 index 0000000..7417b43 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql @@ -0,0 +1,20 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM + pg_namespace nsp +WHERE + nsp.oid={{scid}}::int + AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + ) + AND + nspname NOT LIKE E'pg\\temp\\%' + AND + nspname NOT LIKE E'pg\\toast_temp\\%' \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql new file mode 100644 index 0000000..d4cdfe0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql @@ -0,0 +1,32 @@ +{% import 'macros/security.macros' as SECLABLE %} + +{% if data %} + CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ data.basetype }} + {% if data.collname %} + COLLATE {{ data.collname }} + {% endif %} + + {% if data.typnotnull %} + NOT NULL + {% endif %} + ; + + {% if data.constraints %} + {% for c in data.constraints %} + ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + ADD CONSTRAINT {{ c.conname }} CHECK ({{ c.consrc }} ) + {% if c.convalidated %} NOT VALID {% endif %} {% if c.connoinherit %} NO INHERIT {% endif %}; + {% endfor %} + {% endif %} + + ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ data.owner }}; + {% if data.description %} + COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}'; + {% endif %} + + {% for r in data.seclabels %} + {{ SECLABLE.APPLY(conn, 'DOMAIN', data.name, r.provider, r.label) }} + {% endfor %} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..c740f4f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql @@ -0,0 +1,11 @@ +{% if scid and doid %} + SELECT d.typname as name, bn.nspname as basensp + FROM pg_type d + JOIN pg_namespace bn ON bn.oid=d.typnamespace + WHERE d.typnamespace = {{scid}}::oid + AND d.oid={{doid}}::int; +{% endif %} + +{% if name %} + DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade%} CASCADE{% endif %} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql new file mode 100644 index 0000000..6a13a65 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql @@ -0,0 +1,7 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM pg_collation c, pg_namespace n +WHERE c.collnamespace=n.oid +ORDER BY nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql new file mode 100644 index 0000000..e26998f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql @@ -0,0 +1,18 @@ +SELECT 'TABLE' AS objectkind, c.oid, conname, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc, connoinherit, convalidated FROM pg_constraint c +JOIN pg_class cl ON cl.oid=conrelid +JOIN pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT 'DOMAIN' AS objectkind, c.oid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc +, connoinherit, convalidated +FROM pg_constraint c +JOIN pg_type t ON t.oid=contypid +JOIN pg_namespace nl ON nl.oid=typnamespace + +LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) + + WHERE contype = 'c' AND contypid = {{doid}}::oid +ORDER BY conname \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..1c26d6b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql @@ -0,0 +1,10 @@ +SELECT + d.oid, d.typname as name +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }} + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql new file mode 100644 index 0000000..82dcfcb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql @@ -0,0 +1,21 @@ +SELECT + nsp.nspname +FROM + pg_namespace nsp + LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) + LEFT OUTER JOIN pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid) +WHERE + NOT ((nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + )AND + nspname NOT LIKE E'pg\\temp\\%' AND + nspname NOT LIKE E'pg\\toast_temp\\%' AND + -- ADDED: Because We need to omit system schema except the one on which we are trying to create collation + ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' ) +ORDER BY nspname; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql new file mode 100644 index 0000000..e473056 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql @@ -0,0 +1,7 @@ +SELECT * FROM (SELECT format_type(t.oid,NULL) AS typname, CASE WHEN typelem > 0 THEN typelem ELSE t.oid END as elemoid, typlen, typtype, t.oid, nspname, + (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup + FROM pg_type t + JOIN pg_namespace nsp ON typnamespace=nsp.oid + WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'e', 'r')AND NOT EXISTS + (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) + ) AS dummy ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..901d053 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql @@ -0,0 +1,20 @@ +SELECT d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typlen, d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT array_agg(provider || '=' || label) FROM pg_shseclabel sl1 WHERE sl1.objoid=d.oid) AS seclabels +FROM pg_type d + JOIN pg_type b ON b.oid = d.typbasetype + JOIN pg_namespace bn ON bn.oid=d.typnamespace + LEFT OUTER JOIN pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) + LEFT OUTER JOIN pg_collation c ON d.typcollation=c.oid + LEFT OUTER JOIN pg_namespace cn ON c.collnamespace=cn.oid +WHERE d.typnamespace = {{scid}}::oid +{% if doid %} + AND d.oid={{doid}}::int +{% endif %} +ORDER BY d.typname diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql new file mode 100644 index 0000000..eb9c08f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql @@ -0,0 +1,61 @@ +{% if data %} + {% set name = o_data.name %} + {% if data.name %} + {% if data.name != o_data.name %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; + {% set name = data.name %} + {% endif %} + {% endif %} + + {% if not data.typnotnull %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; + {% endif %} + + {% if data.typdefault %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault|qtLiteral }}; + {% endif %} + + {% if data.owner %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ data.owner }}; + {% endif %} + + {% if data.constraints %} + {% for c in data.constraints.added %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ c.conname }} CHECK ({{ c.consrc }} ) + {% if c.convalidated %} NOT VALID {% endif %} {% if c.connoinherit %} NO INHERIT {% endif %}; + {% endfor %} + {% endif %} + + {% set seclabels = data.seclabels %} + {% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} + {% for r in seclabels.deleted %} + {{ SECLABLE.DROP(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider) }} + {% endfor %} + {% endif %} + {% if 'added' in seclabels and seclabels.added|length > 0 %} + {% for r in seclabels.added %} + {{ SECLABLE.APPLY(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }} + {% endfor %} + {% endif %} + {% if 'changed' in seclabels and seclabels.changed|length > 0 %} + {% for r in seclabels.changed %} + {{ SECLABLE.APPLY(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }} + {% endfor %} + {% endif %} + + {% if data.description %} + COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }}; + {% endif %} + + {% if data.basensp %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }}; + {% endif %} + +{% endif %} diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js index 8837fa9..67a8705 100644 --- a/web/pgadmin/static/js/backform.pgadmin.js +++ b/web/pgadmin/static/js/backform.pgadmin.js @@ -998,9 +998,9 @@ } // Insert Edit Cell into Grid - if (data.disabled == false && data.canEdit) { + if (data.disabled == false && data.canEdit || data.canAdd) { var editCell = Backgrid.Extension.ObjectCell.extend({ - schema: gridSchema.schema + schema: gridSchema.schema, editable: false }); gridSchema.columns.unshift({ @@ -1043,6 +1043,7 @@ e.preventDefault(); grid.insertRow({}); var newRow = $(grid.body.rows[collection.length - 1].$el); + console.log(newRow); newRow.attr("class", "new").click(function(e) { $(this).attr("class", ""); }); ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-01-20 07:07 Khushboo Vashi <[email protected]> parent: Khushboo Vashi <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Khushboo Vashi @ 2016-01-20 07:07 UTC (permalink / raw) To: pgadmin-hackers Resending patch with binary option. On Wed, Jan 20, 2016 at 10:18 AM, Khushboo Vashi < [email protected]> wrote: > Hi, > > Please find attached patch for the Domain Module. > > The patch will be modified after Types module implementation as we need to > populate Base Type and some Type related validations from the Types module. > > Please review it and let me know the feedback. > > Thanks, > Khushboo > -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers Attachments: [text/x-patch] domains.patch (70.0K, 3-domains.patch) download | inline diff: diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py new file mode 100644 index 0000000..250a665 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py @@ -0,0 +1,510 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas as schemas +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + +class DomainModule(CollectionNodeModule): + NODE_TYPE = 'domain' + COLLECTION_LABEL = gettext("Domains") + + def __init__(self, *args, **kwargs): + self.min_ver = None + self.max_ver = None + super(DomainModule, self).__init__(*args, **kwargs) + + # Before loading this module we need to make sure that scid is schema object + def BackendSupported(self, manager, **kwargs): + """ + This function will validate schema name & scid against catalogs then allow us to + make decision if we want to load this module or not for the schema + """ + if super(DomainModule, self).BackendSupported(manager, **kwargs): + conn = manager.connection() + # If DB is not connected then return error to browser + if not conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + ver = manager.version + server_type = manager.server_type + # we will set template path for sql scripts + if ver >= 90100: + template_path = 'domains/sql/9.1_plus' + else: + # Note: Domain is not supported below postgres version 9.1 + # Hence we will not load this module + return False + + SQL = render_template("/".join([template_path, 'backend_support.sql']), scid=kwargs['scid']) + status, res = conn.execute_scalar(SQL) + # check if any errors + if not status: + return internal_server_error(errormsg=res) + # Check scid is catalog and from 'sys', 'dbo', 'information_schema', + # then False (Do not load this module), othewise True + if res is True: + return False + else: + return True + + def get_nodes(self, gid, sid, did, scid): + """ + Generate the collection node + """ + yield self.generate_browser_collection_node(scid) + + @property + def script_load(self): + """ + Load the module script for database, when any of the database node is + initialized. + """ + return schemas.SchemaModule.NODE_TYPE + + +blueprint = DomainModule(__name__) + + +class DomainView(PGChildNodeView): + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'doid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_schemas': [{'get': 'get_schemas'}, {'get': 'get_schemas'}], + 'get_collations': [{'get': 'get_collations'}, {'get': 'get_collations'}], + 'get_types': [{'get': 'get_types'}, {'get': 'get_types'}] + }) + + + def module_js(self): + return make_response( + render_template( + "domains/js/domains.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + # Get database connection + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If database is not connected then return error + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90100: + self.template_path = 'domains/sql/9.1_plus' + return f(*args, **kwargs) + + return wrap + + + @check_precondition + def list(self, gid, sid, did, scid): + """List the Domains.""" + + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + + @check_precondition + def nodes(self, gid, sid, did, scid): + """Returns all the Domains.""" + + res = [] + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + row['name'], + icon="icon-domain" + )) + + return make_json_response( + data=res, + status=200 + ) + + + @check_precondition + def properties(self, gid, sid, did, scid, doid): + """Returns Domain properties.""" + + SQL = render_template("/".join([self.template_path, 'properties.sql']),scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, 'get_constraints.sql']),doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + + data['constraints'] = res['rows'] + return ajax_response( + response=data, + status=200 + ) + + + @check_precondition + def get_schemas(self, gid, sid, did, scid, doid=None): + """Returns Schemas for particular database.""" + + res = [{ 'label': '', 'value': '' }] + try: + SQL = render_template("/".join([self.template_path, 'get_schemas.sql']), scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append( + { 'label': row['nspname'], 'value': row['nspname'] } + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def get_collations(self, gid, sid, did, scid, doid=None): + """Returns Collations.""" + + res = [{ 'label': '', 'value': '' }] + try: + SQL = render_template("/".join([self.template_path, 'get_collations.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append( + { 'label': row['copy_collation'], 'value': row['copy_collation'] } + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def get_types(self, gid, sid, did, scid, doid=None): + """Returns Types.""" + # TODO: This function should be removed once Types module will be completed. + + res = [{ 'label': '', 'value': '' }] + try: + SQL = render_template("/".join([self.template_path, 'get_types.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append( + { 'label': row['typname'], 'value': row['typname'] } + ) + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def create(self, gid, sid, did, scid): + """ + Creates new domain object. + + Expected Parameters: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Expected Response: + Domain object in json format. + """ + + data = request.form if request.form else json.loads(request.data.decode()) + required_args = [ + 'name', + 'owner', + 'basensp', + 'basetype' + ] + + for arg in required_args: + if arg not in data or data[arg] == '': + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + try: + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, qtIdent=self.qtIdent) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # we need oid to to add object in tree at browser, below sql will gives the same + SQL = render_template("/".join([self.template_path, 'get_oid.sql']), basensp=data['basensp'], name=data['name']) + status, doid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=doid) + + return jsonify( + node=self.blueprint.generate_browser_node( + doid, + data['name'], + icon="icon-domain" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def delete(self, gid, sid, did, scid, doid): + """ + Drop the Domain. + """ + # Below will decide if it's simple drop or drop with cascade call + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + SQL = render_template("/".join([self.template_path, 'delete.sql']), scid=scid, doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + name, basensp = res['rows'][0] + + SQL = render_template("/".join([self.template_path, 'delete.sql']), name=name, basensp=basensp, cascade=cascade) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, scid, doid): + """ + Update the Domain. + """ + data = request.form if request.form else json.loads(request.data.decode()) + + status, SQL = self.getSQL(gid, sid, data, scid, doid) + if not status: + return internal_server_error(errormsg=SQL) + + try: + if SQL and SQL.strip('\n') and SQL.strip(' '): + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Domain updated", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def msql(self, gid, sid, did, scid, doid=None): + """ + Returns the modified SQL. + + Expected Parameters: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Expected Response: + SQL statements to create/update the Domain. + """ + data = request.args + if doid is None: + required_args = [ + 'name', + 'owner', + 'basensp', + 'basetype' + ] + + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + SQL = self.getSQL(gid, sid, data, scid, doid) + if isinstance(SQL, str) and SQL and SQL.strip('\n') and SQL.strip(' '): + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + + def getSQL(self, gid, sid, data, scid, doid=None): + """ + Genrates the SQL statements to create/update the Domain. + """ + try: + if doid is not None: + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + SQL = render_template( + "/".join([self.template_path,'update.sql']), + data=data, o_data=old_data, conn=self.conn, qtIdent=self.qtIdent + ) + else: + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn, qtIdent=self.qtIdent) + return True,SQL + + except Exception as e: + return False, e + +DomainView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py new file mode 100644 index 0000000..7f0575b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py @@ -0,0 +1,390 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import NodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas.domains as domains +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + +class DomainConstraintModule(CollectionNodeModule): + NODE_TYPE = 'domain-constraints' + COLLECTION_LABEL = gettext("Domain Constraints") + + def __init__(self, *args, **kwargs): + self.min_ver = None + self.max_ver = None + super(DomainConstraintModule, self).__init__(*args, **kwargs) + + def get_nodes(self, gid, sid, did, scid, doid): + """ + Generate the collection node + """ + yield self.generate_browser_collection_node(doid) + + @property + def node_inode(self): + """ + Override this property to make the node as leaf node. + """ + return False + + + @property + def script_load(self): + """ + Load the module script for database, when any of the database node is + initialized. + """ + return domains.DomainModule.NODE_TYPE + + +blueprint = DomainConstraintModule(__name__) + + +class DomainConstraintView(NodeView): + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'}, + {'type': 'int', 'id': 'doid'} + ] + ids = [ + {'type': 'int', 'id': 'coid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def module_js(self): + """ + This property defines (if javascript) exists for this node. + Override this property for your own logic. + """ + return make_response( + render_template( + "domain-constraints/js/domain-constraints.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90100: + self.template_path = 'domain-constraints/sql/9.1_plus' + return f(*args, **kwargs) + + return wrap + + + @check_precondition + def list(self, gid, sid, did, scid, doid): + """ + List the Domain Constraints. + """ + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + + @check_precondition + def nodes(self, gid, sid, did, scid, doid): + """ + Returns all the Domain Constraints. + """ + res = [] + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid) + status, rset = self.conn.execute_2darray(SQL) + + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + row['conname'], + icon="icon-domain-constraints" + )) + + return make_json_response( + data=res, + status=200 + ) + + + @check_precondition + def properties(self, gid, sid, did, scid, doid, coid): + """ + Returns the Domain Constraints property. + """ + + SQL = render_template("/".join([self.template_path, 'properties.sql']),doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + return ajax_response( + response=res['rows'][0], + status=200 + ) + + + @check_precondition + def create(self, gid, sid, did, scid, doid): + """ + Creates new the Domain Constraints. + + Expected Parameters: + conname: Constraints Name + consrc: Constraints Check + + Expected Response: + Domain Constraint object in json format. + """ + + data = request.form if request.form else json.loads(request.data.decode()) + required_args = [ + 'conname', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + try: + # Get Schema and Domain. + SQL = render_template("/".join([self.template_path, 'get_domain.sql']), doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, domain=domain, schema=schema, qtIdent=self.qtIdent) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get the recently added constraints oid + SQL = render_template("/".join([self.template_path, 'get_oid.sql']), doid=doid, name=data['conname']) + status, coid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=doid) + + return jsonify( + node=self.blueprint.generate_browser_node( + coid, + data['conname'], + icon="icon-domain" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def delete(self, gid, sid, did, scid, doid, coid): + """ + Drop the Doman Constraints. + """ + try: + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, 'delete.sql']), data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def update(self, gid, sid, did, scid, doid, coid): + """ + Update the Domain Constraints. + """ + data = request.form if request.form else json.loads(request.data.decode()) + + SQL = self.getSQL(gid, sid, data, scid, doid, coid) + try: + if SQL and SQL.strip('\n') and SQL.strip(' '): + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Domain updated", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def msql(self, gid, sid, did, scid, doid=None): + """ + Returns the modified SQL. + + Expected Parameters: + conname: Constraints Name + consrc: Constraints Check + + Expected Response: + Domain Constraint object in json format. + """ + data = request.args + if doid is None: + required_args = [ + 'conname', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + SQL = self.getSQL(gid, sid, data, scid, doid) + if isinstance(SQL, str) and SQL and SQL.strip('\n') and SQL.strip(' '): + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + + def getSQL(self, gid, sid, data, scid, doid, coid=None): + """ + Genrates the SQL statements to create/update the Domain Constraint. + """ + try: + if coid is not None: + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + old_data = res['rows'][0] + + SQL = render_template( + "/".join([self.template_path,'update.sql']), + data=data, o_data=old_data, conn=self.conn + ) + else: + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn) + return SQL + except Exception as e: + return internal_server_error(errormsg=str(e)) + +DomainConstraintView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..d62e13705c50e6c0cf8f19d680053e8643e28751 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=2TrO847{Jl`|t`Qpas zn<hS+l=rMY>RGPqvlydizRJ&>B`Om#V}R-yOM?7@862M7NCR>>3p^r=fwTu0yPeFo z12TL)T^vI=t|uoPU||ZF<tgaHG*QsQ!?m&Tq=?3mCu}J#Dx3x@mM}}^iE=5NIWXnk zkpnC4ai%a>@;Gg7=uums=9bIK=Egd~(us-3g@Iv02gfsK^JP^)gH=mhBT7;dOH!?p zi&B9UgOP!ufv%yEu7P2Qk%5(ov6YF5wt=aYfq}(LRXG$5x%nxXX_XKS29{tAAk|g| XW)KahriZQpYGCkm^>bP0l+XkKyyRU} literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png new file mode 100644 index 0000000000000000000000000000000000000000..32a045b8fafdc08640d53b2a86b1dcabcb0fe0fd GIT binary patch literal 579 zcmV-J0=)f+P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp) z=>Px$AW%$HMR1Z9#Q*@REHmdHAm<<;=p`lTDl6$LE15$|nM6yxJ3#6&F}^)Qx<p3n zH#h4zIK)Ou#79fTM@#HJKkY(8?L$QEL`CjJMeasM?ng(;QB>|oNbX2U?n+AUOH1!e zOz%xi?@dncPEXKRS?^Cz#amtQQBm+xQt(q#@KaRqR8;U)Rq<9<@mE*YVq(`~V)0vB z+GS<)VPW%PV%=$Jag!JHXlV6qZS`($_HuH3pd<EncJ_C7f1@XWqbGu>Du=8uhpjJ) zvN4agH<i3UrMYgWyK<eyN1Vq+p2tbA!G5C3PO!m$q|HvV#D$~JOsv;ftk_qv-CeQX zT*cC%$J3;><6_L$t<Kr8)!w|r@o>cPam4X*+~mi`^K{Glc|xh<NdN!<0d!JMQvg8b z*k%9#00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipf34<ROg z(p72z0056kL_t&-)1{2Z3c^qT1mDEody6IZuCW&s1V!`^4}$goe?(18@b2Db*j*w1 z%4M(p;&Zw?9ZaKAxo*x;COX}<8fzjqKRv^2kF1spymYHMjE2m7Hl|a~emCNwG8(i? z8I#``(kiBrEbh}(Qn8TL2+$}b3Hn^7p`K6R#_6zQ2$?ux;lXBYB>hkN@C%g?4vVBM R$bbL<002ovPDHLkV1min0OSAw literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..9d1d2a061c7948168d7b1c2474d769b31709f1cf GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE08{VY2nhHc^eMaU%j`d zaI$#+HuEKC{pKEZKYn>h(+aIMH^MjGjcs3}f9Cqyt&fvx7AT*)xv^`L;hf{Hi_iP4 zxgK%&V?q65_4c*;8}G%;JMMY<SLM___M4Bi9{E^!<YUpX&n1ga`7PgFwEkdS!(#P< zNn&@N9OqiK(i&(nV@Z%-FoVOh8)-leXMsm#F_88EW4Dvpc0fjir;B5V#O36K1sn!O zhRVf}5jSsGPH?f<xudc|vBoY<&5cb=uTGB9v187J4II+it5?jhn8F{TsCZIWRad$D zg1*K9W%cu2NsD(hEfVT#*wm#pYw;D04+0E(uQE?s`Z6*ZXoqTvYeY#(Vo9o1a#1Rf zVlXl=G|)9P(lsy)F*2|+F}5->(Kax(GBB{1sVaw}AvZrIGp!P$!N3x%0i@c>zzm|{ T)b!9bKn)C@u6{1-oD!M<=4O^k literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css new file mode 100644 index 0000000..68cd622 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css @@ -0,0 +1,9 @@ +.pg-icon-domain-constraints { + background-image: url('{{ url_for('NODE-domain-constraints.static', filename='img/domain-constraints.png') }}') !important; + border-radius: 10px; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js new file mode 100644 index 0000000..fb3de7d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js @@ -0,0 +1,95 @@ +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + if (!pgBrowser.Nodes['coll-domain-constraints']) { + var databases = pgAdmin.Browser.Nodes['coll-domain-constraints'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + type: 'coll-domain-constraints' + }); + }; + + if (!pgBrowser.Nodes['domain-constraints']) { + pgAdmin.Browser.Nodes['domain-constraints'] = pgBrowser.Node.extend({ + type: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + collection_type: 'coll-domain-constraints', + hasSQL: true, + parent_type: ['domain'], + Init: function() { + /* Avoid mulitple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + conname: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined + }, + schema: [{id: 'conname', label:'Name', type:'text', cell:'string'}, + {id: 'description', label:'Comment', type: 'multiline', cell: 'string', mode: ['properties', 'create', 'edit'], disabled: function(m) { return !m.isNew(); }}, + {id: 'consrc', label:'Check', type: 'multiline', cel: 'string', group: 'Definition', mode: ['properties', 'create', 'edit'], disabled: function(m) { return !m.isNew(); }}, + {id: 'connoinherit', label:'No Inherit', type: 'switch', cell: 'boolean', group: 'Definition', mode: ['properties', 'create', 'edit'], disabled: function(m) { return !m.isNew(); }}, + {id: 'convalidated', label:"Don't Validate", type: 'switch', cell: 'boolean', group: 'Definition'} + ], + validate: function() { + var err = {}, + errmsg; + + if (_.isUndefined(this.get('conname')) || String(this.get('conname')).replace(/^\s+|\s+$/g, '') == '') { + err['conname'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['conname']; + } + + if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') { + err['consrc'] = '{{ _('Check can not be empty!') }}'; + errmsg = errmsg || err['consrc']; + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + + }, + }), + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql new file mode 100644 index 0000000..ed2610d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql @@ -0,0 +1,6 @@ +{% if data and schema and domain %} + ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ data.conname }} CHECK ({{ data.consrc }} ) + {% if data.convalidated %} NOT VALID {% endif %} {% if data.connoinherit %} NO INHERIT {% endif %}; +{% endif %} + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..1a56583 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} + ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ data.conname }} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql new file mode 100644 index 0000000..a199c5c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql @@ -0,0 +1,7 @@ +SELECT d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..344abcc --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql @@ -0,0 +1,4 @@ +SELECT oid, conname as name + FROM pg_constraint +WHERE contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..7ed21d9 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql @@ -0,0 +1,18 @@ +SELECT 'TABLE' AS objectkind, c.oid, conname, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc, connoinherit, convalidated +FROM pg_constraint c +JOIN pg_class cl ON cl.oid=conrelid +JOIN pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT 'DOMAIN' AS objectkind, c.oid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc +, connoinherit, convalidated FROM pg_constraint c + JOIN pg_type t ON t.oid=contypid + JOIN pg_namespace nl ON nl.oid=typnamespace + LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) + WHERE contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql new file mode 100644 index 0000000..9a2c8d7 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql @@ -0,0 +1,4 @@ +{% if data.conname %} + ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ o_data.conname }} TO {{ data.conname }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png new file mode 100644 index 0000000000000000000000000000000000000000..55621528a1dba4928538fe5557b9b988ed78d6ab GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}U4T!BE0BJeoqfW=;gF{0PD8_o zi&pN_(K)B7`FPi%2ix`@v9LU(tNUQ*!K1czXOxuggoGT_(#p@zf4Jw!!zHV)`3F7T zd-Um}H}`iRIijcc@Wq#>&ptkR`SszG54Uz6zW4mg?MLsgZ9jbb>E~y!zTSB9`Re1( zjS1S99(_9h@YC4`A5Y)^_^>@J3+MvIk|4ie28U-i(tsS!0*}aIAngIhZYQ(tfQ)ue z7sn8Z%b|U@#hMgESUvs4wKoSxO>~{O=-Yq$8_!r)-^e%4-l6f-Jox1%8}FIVx>FCW zPMVwluQGbkP1bcSwVV0d_?PZbZVis%HeYTWyU*$OvYp$uTc3QLbD?2trOnDuhZa>f z=3d_{(zER9xt(<n3UsBI4SNC?Y!rBW<m2`n>yDRyg$uoE8Qx}bo1D0qw;t#u)e_f; zl9a@fRIB8oR3OD*WMF8ZYiOivU>IU#U}a)#Wn!XjU}|MxU@=ow4n;$5eoAIqB}9XP eC0GMUwUvPxM8m1+p=*E|7(8A5T-G@yGywoRCC2Lj literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png new file mode 100644 index 0000000000000000000000000000000000000000..7521cddeaaaf0ee4e3c60e948078d70e17e06893 GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}T7XZ8E0Deu9eu*V;gF{0K`pIg zc6K`r4IeIAc}Q3HxV`=3y+@xudUJpGkt2F~PoI5!^78A$Cm(L@JbcYR=;4bm_nv>b z{pkI*?T7D#gyiSvKYR7{_S4Tdo_xOg_;X`|_N7Ok&OiKg_QA)~_dlLo85{w$iLoTe zFPOpM*^M+HhqJ&VvKUBvfU(=jY&#$$$<xI#MB;Mq`IABp20RT9wUXkaL(1R(pOn?c z>38<+e>KaN2{%0Jd@sMbY$+13Z&%Z<%!P+*SNu+#({Rr={_KxUhswHdID0QWTCBvD z)^ky;@gu`E#RAC#l`JcnelS0rxkKN7b1B>H56gX)0&P<*ag8WRNi0dVN-jzTQVd20 zh6cKZM!E)uAw~vPCdO7KCfWw3Rt5$ZGgakKH00)|WTsU@G#FTdHGouG8JIydoSGiG Q2B?9-)78&qol`;+03_q3ga7~l literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png new file mode 100644 index 0000000000000000000000000000000000000000..42ca929325854b8f34787425e8094d08c75983bc GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}PJmB{E0BJeoqfW=;gF{0PD8^7 zi&s6`bL8oxH}`iRdHU?*lb2r~J^6Th=ix^$zTSWS<?f>oH+CHUa^}pVf`aWvMt7fm zx&8F>jfd~AY(HF`mUiRG=W9<sUwQB@KR>@QLHo+%PZuA3I{)y~*$1D_JotF({zvHt zo%ulf7)yfuf*Bm1-ADs+I14-?i-EKU7`vU!wgWPXJzX3_Brcbpe=XFcAmSFNeC3q& zl!Z+(RsYN12&-NW{P*^#<rx82_2-+ii&SqfGgO`Jcj@;01@HTH{&&7#v?j}I)=J6m ze`~hppZn7?WA|%z!}T6pf>z0IjbZrS5ICD*275*_bAo)r8t#Vg4A*Lz);xc4JKq03 zXXC}zd)YwiRZCnWN>UO_QmvAUQh^kMk%6IsuAz~xfnkV|ft87|m5GVAfvJ^&fyGQ! qITQ`K`6-!cl@JXEmS7Da)m8>(5DllMhpqu?VDNPHb6Mw<&;$VOR=Fqu literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css new file mode 100644 index 0000000..7d4fad0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css @@ -0,0 +1,9 @@ +.pg-icon-domain { + background-image: url('{{ url_for('NODE-domain.static', filename='img/domain.png') }}') !important; + border-radius: 10px; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js new file mode 100644 index 0000000..8fcf507 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js @@ -0,0 +1,236 @@ +/* Create and Register Domain Collection and Domain Node. */ + +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + if (!pgBrowser.Nodes['coll-domain']) { + var databases = pgAdmin.Browser.Nodes['coll-domain'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain', + label: '{{ _('Domains') }}', + type: 'coll-domain' + }); + }; + + var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: null, + security_label: null + }, + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text' + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text' + }], + validate: function() { + var err = {}, + errmsg = null, + data = this.toJSON(); + + if (_.isUndefined(data.label) || + _.isNull(data.label) || + String(data.label).replace(/^\s+|\s+$/g, '') == '') { + return _("Please specify the value for all the security providers."); + } + return null; + } + }); + + var ConstraintsModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + conname: undefined, + description: undefined, + slony: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined + }, + schema: [ + {id: 'conname', label:'Name', type:'text', cell:'string'}, + {id: 'description', label:'Comment', type: 'multiline', cell: 'string'}, + {id: 'slony', label:'Use Slony', type: 'text', cell: 'string'}, + {id: 'consrc', label:'Check', type: 'multiline', cel: 'string', group: 'definition'}, + {id: 'connoinherit', label:'No Inherit', type: 'switch', cell: 'boolean', group: 'definition'}, + {id: 'convalidated', label:"Don't Validate", type: 'switch', cell: 'boolean', group: 'definition'} + ], + validate: function() { + // TODO: Add validation here + }, + toJSON: Backbone.Model.prototype.toJSON + }); + + + if (!pgBrowser.Nodes['domain']) { + pgAdmin.Browser.Nodes['domain'] = pgBrowser.Node.extend({ + type: 'domain', + label: '{{ _('Domain') }}', + collection_type: 'coll-domain', + hasSQL: true, + parent_type: ['schema'], + Init: function() { + /* Avoid mulitple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + canDropCascade: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + oid: undefined, + owner: undefined, + basensp: undefined, + description: undefined, + basetype: undefined, + typlen: undefined, + precision: undefined, + typdefault: undefined, + typnotnull: undefined, + collname: undefined, + constraints: [], + seclabels: [] + }, + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'] + },{ + id: 'oid', label:'{{ _('Oid') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl, + node: 'role', type: 'text', mode: ['edit', 'create', 'properties'] + },{ + id: 'basensp', label:'{{ _('Schema') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', url: 'get_schemas' + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'basetype', label:'{{ _('Base Type') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', mode:['properties', 'create', 'edit'], group: 'Definition', url: 'get_types', + disabled: function(m) { return !m.isNew(); } + },{ + id: 'typlen', label:'{{ _('Length') }}', cell: 'string', + type: 'text', group: 'Definition', disabled: function(m) { return !m.isNew(); } + },{ + id: 'precision', label:'{{ _('Precision') }}', cell: 'string', + type: 'text', group: 'Definition', disabled: function(m) { return !m.isNew(); } + },{ + id: 'typdefault', label:'{{ _('Default') }}', cell: 'string', + type: 'text', group: 'Definition' + },{ + id: 'typnotnull', label:'{{ _('Not Null') }}', cell: 'boolean', + type: 'switch', group: 'Definition' + },{ + id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', group: 'Definition', url: 'get_collations', disabled: function(m) { return !m.isNew(); } + },{ + id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string', + type: 'collection', group: 'Constraints', visible: false, mode: ['edit', 'create'], + model: ConstraintsModel, canAdd: true, canDelete: false, canEdit: false + },{ + id: 'seclabels', label: '{{ _('Security Labels') }}', + model: SecurityModel, editable: false, type: 'collection', + group: '{{ _('Security') }}', mode: ['edit', 'create'], + min_version: 90100, canAdd: true, + canEdit: false, canDelete: true, control: 'unique-col-collection' + } + ], + validate: function() + { + var err = {}, + errmsg, + seclabels = this.get('seclabels'); + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('basetype')) || String(this.get('basetype')).replace(/^\s+|\s+$/g, '') == '') { + err['basetype'] = '{{ _('Base Type can not be empty!') }}'; + errmsg = errmsg || err['basetype']; + } + + if (seclabels) { + var secLabelsErr; + for (var i = 0; i < seclabels.models.length && !secLabelsErr; i++) { + secLabelsErr = (seclabels.models[i]).validate.apply(seclabels.models[i]); + if (secLabelsErr) { + err['seclabels'] = secLabelsErr; + errmsg = errmsg || secLabelsErr; + } + } + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + } + }), + canCreate: function(itemData, item, data) { + //If check is false then , we will allow create menu + if (data && data.check == false) + return true; + + var t = pgBrowser.tree, i = item, d = itemData; + // To iterate over tree to check parent node + while (i) { + // If it is schema then allow user to create domain + if (_.indexOf(['schema'], d._type) > -1) + return true; + + if ('coll-domain' == d._type) { + //Check if we are not child of catalog + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + if( prev_d._type == 'catalog') { + return false; + } else { + return true; + } + } + i = t.hasParent(i) ? t.parent(i) : null; + d = i ? t.itemData(i) : null; + } + // by default we do not want to allow create menu + return true; + } + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql new file mode 100644 index 0000000..7417b43 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql @@ -0,0 +1,20 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM + pg_namespace nsp +WHERE + nsp.oid={{scid}}::int + AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + ) + AND + nspname NOT LIKE E'pg\\temp\\%' + AND + nspname NOT LIKE E'pg\\toast_temp\\%' \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql new file mode 100644 index 0000000..d4cdfe0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql @@ -0,0 +1,32 @@ +{% import 'macros/security.macros' as SECLABLE %} + +{% if data %} + CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ data.basetype }} + {% if data.collname %} + COLLATE {{ data.collname }} + {% endif %} + + {% if data.typnotnull %} + NOT NULL + {% endif %} + ; + + {% if data.constraints %} + {% for c in data.constraints %} + ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + ADD CONSTRAINT {{ c.conname }} CHECK ({{ c.consrc }} ) + {% if c.convalidated %} NOT VALID {% endif %} {% if c.connoinherit %} NO INHERIT {% endif %}; + {% endfor %} + {% endif %} + + ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ data.owner }}; + {% if data.description %} + COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}'; + {% endif %} + + {% for r in data.seclabels %} + {{ SECLABLE.APPLY(conn, 'DOMAIN', data.name, r.provider, r.label) }} + {% endfor %} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..c740f4f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql @@ -0,0 +1,11 @@ +{% if scid and doid %} + SELECT d.typname as name, bn.nspname as basensp + FROM pg_type d + JOIN pg_namespace bn ON bn.oid=d.typnamespace + WHERE d.typnamespace = {{scid}}::oid + AND d.oid={{doid}}::int; +{% endif %} + +{% if name %} + DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade%} CASCADE{% endif %} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql new file mode 100644 index 0000000..6a13a65 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql @@ -0,0 +1,7 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM pg_collation c, pg_namespace n +WHERE c.collnamespace=n.oid +ORDER BY nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql new file mode 100644 index 0000000..e26998f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql @@ -0,0 +1,18 @@ +SELECT 'TABLE' AS objectkind, c.oid, conname, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc, connoinherit, convalidated FROM pg_constraint c +JOIN pg_class cl ON cl.oid=conrelid +JOIN pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT 'DOMAIN' AS objectkind, c.oid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc +, connoinherit, convalidated +FROM pg_constraint c +JOIN pg_type t ON t.oid=contypid +JOIN pg_namespace nl ON nl.oid=typnamespace + +LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) + + WHERE contype = 'c' AND contypid = {{doid}}::oid +ORDER BY conname \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..1c26d6b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql @@ -0,0 +1,10 @@ +SELECT + d.oid, d.typname as name +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }} + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql new file mode 100644 index 0000000..82dcfcb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql @@ -0,0 +1,21 @@ +SELECT + nsp.nspname +FROM + pg_namespace nsp + LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) + LEFT OUTER JOIN pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid) +WHERE + NOT ((nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + )AND + nspname NOT LIKE E'pg\\temp\\%' AND + nspname NOT LIKE E'pg\\toast_temp\\%' AND + -- ADDED: Because We need to omit system schema except the one on which we are trying to create collation + ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' ) +ORDER BY nspname; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql new file mode 100644 index 0000000..e473056 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql @@ -0,0 +1,7 @@ +SELECT * FROM (SELECT format_type(t.oid,NULL) AS typname, CASE WHEN typelem > 0 THEN typelem ELSE t.oid END as elemoid, typlen, typtype, t.oid, nspname, + (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup + FROM pg_type t + JOIN pg_namespace nsp ON typnamespace=nsp.oid + WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'e', 'r')AND NOT EXISTS + (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) + ) AS dummy ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..901d053 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql @@ -0,0 +1,20 @@ +SELECT d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typlen, d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT array_agg(provider || '=' || label) FROM pg_shseclabel sl1 WHERE sl1.objoid=d.oid) AS seclabels +FROM pg_type d + JOIN pg_type b ON b.oid = d.typbasetype + JOIN pg_namespace bn ON bn.oid=d.typnamespace + LEFT OUTER JOIN pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) + LEFT OUTER JOIN pg_collation c ON d.typcollation=c.oid + LEFT OUTER JOIN pg_namespace cn ON c.collnamespace=cn.oid +WHERE d.typnamespace = {{scid}}::oid +{% if doid %} + AND d.oid={{doid}}::int +{% endif %} +ORDER BY d.typname diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql new file mode 100644 index 0000000..eb9c08f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql @@ -0,0 +1,61 @@ +{% if data %} + {% set name = o_data.name %} + {% if data.name %} + {% if data.name != o_data.name %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; + {% set name = data.name %} + {% endif %} + {% endif %} + + {% if not data.typnotnull %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; + {% endif %} + + {% if data.typdefault %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault|qtLiteral }}; + {% endif %} + + {% if data.owner %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ data.owner }}; + {% endif %} + + {% if data.constraints %} + {% for c in data.constraints.added %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ c.conname }} CHECK ({{ c.consrc }} ) + {% if c.convalidated %} NOT VALID {% endif %} {% if c.connoinherit %} NO INHERIT {% endif %}; + {% endfor %} + {% endif %} + + {% set seclabels = data.seclabels %} + {% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} + {% for r in seclabels.deleted %} + {{ SECLABLE.DROP(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider) }} + {% endfor %} + {% endif %} + {% if 'added' in seclabels and seclabels.added|length > 0 %} + {% for r in seclabels.added %} + {{ SECLABLE.APPLY(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }} + {% endfor %} + {% endif %} + {% if 'changed' in seclabels and seclabels.changed|length > 0 %} + {% for r in seclabels.changed %} + {{ SECLABLE.APPLY(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }} + {% endfor %} + {% endif %} + + {% if data.description %} + COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }}; + {% endif %} + + {% if data.basensp %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }}; + {% endif %} + +{% endif %} diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js index 8837fa9..67a8705 100644 --- a/web/pgadmin/static/js/backform.pgadmin.js +++ b/web/pgadmin/static/js/backform.pgadmin.js @@ -998,9 +998,9 @@ } // Insert Edit Cell into Grid - if (data.disabled == false && data.canEdit) { + if (data.disabled == false && data.canEdit || data.canAdd) { var editCell = Backgrid.Extension.ObjectCell.extend({ - schema: gridSchema.schema + schema: gridSchema.schema, editable: false }); gridSchema.columns.unshift({ @@ -1043,6 +1043,7 @@ e.preventDefault(); grid.insertRow({}); var newRow = $(grid.body.rows[collection.length - 1].$el); + console.log(newRow); newRow.attr("class", "new").click(function(e) { $(this).attr("class", ""); }); ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-01-20 07:20 Neel Patel <[email protected]> parent: Khushboo Vashi <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Neel Patel @ 2016-01-20 07:20 UTC (permalink / raw) To: Khushboo Vashi <[email protected]>; +Cc: pgadmin-hackers Hi Khushboo, While applying the patch file, we are getting below warnings. ######################################### domains (1).patch:1340: trailing whitespace. oid: undefined, domains (1).patch:1483: trailing whitespace. (nspname = 'pg_catalog' AND EXISTS domains (1).patch:1487: trailing whitespace. OR (nspname = 'information_schema' AND EXISTS domains (1).patch:1489: trailing whitespace. OR (nspname LIKE '_%' AND EXISTS domains (1).patch:1642: trailing whitespace. (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) warning: squelched 4 whitespace errors warning: 9 lines add whitespace errors. ######################################### Can you please remove the whitespace and regenerate the patch ? Thanks, Neel Patel On Wed, Jan 20, 2016 at 12:37 PM, Khushboo Vashi < [email protected]> wrote: > Resending patch with binary option. > > On Wed, Jan 20, 2016 at 10:18 AM, Khushboo Vashi < > [email protected]> wrote: > >> Hi, >> >> Please find attached patch for the Domain Module. >> >> The patch will be modified after Types module implementation as we need >> to populate Base Type and some Type related validations from the Types >> module. >> >> Please review it and let me know the feedback. >> >> Thanks, >> Khushboo >> > > > > -- > Sent via pgadmin-hackers mailing list ([email protected]) > To make changes to your subscription: > http://www.postgresql.org/mailpref/pgadmin-hackers > > ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-01-20 09:20 Khushboo Vashi <[email protected]> parent: Neel Patel <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Khushboo Vashi @ 2016-01-20 09:20 UTC (permalink / raw) To: Neel Patel <[email protected]>; +Cc: pgadmin-hackers Hi Neel, Please find updated patch. Thanks, Khushboo On Wed, Jan 20, 2016 at 12:50 PM, Neel Patel <[email protected]> wrote: > Hi Khushboo, > > While applying the patch file, we are getting below warnings. > > ######################################### > domains (1).patch:1340: trailing whitespace. > oid: undefined, > domains (1).patch:1483: trailing whitespace. > (nspname = 'pg_catalog' AND EXISTS > domains (1).patch:1487: trailing whitespace. > OR (nspname = 'information_schema' AND EXISTS > domains (1).patch:1489: trailing whitespace. > OR (nspname LIKE '_%' AND EXISTS > domains (1).patch:1642: trailing whitespace. > (select 1 from pg_class where relnamespace=typnamespace and relname = > typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS > (select 1 from pg_class where relnamespace=typnamespace and relname = > substring(typname from 2)::name and relkind != 'c')) > warning: squelched 4 whitespace errors > warning: 9 lines add whitespace errors. > ######################################### > > Can you please remove the whitespace and regenerate the patch ? > > Thanks, > Neel Patel > > On Wed, Jan 20, 2016 at 12:37 PM, Khushboo Vashi < > [email protected]> wrote: > >> Resending patch with binary option. >> >> On Wed, Jan 20, 2016 at 10:18 AM, Khushboo Vashi < >> [email protected]> wrote: >> >>> Hi, >>> >>> Please find attached patch for the Domain Module. >>> >>> The patch will be modified after Types module implementation as we need >>> to populate Base Type and some Type related validations from the Types >>> module. >>> >>> Please review it and let me know the feedback. >>> >>> Thanks, >>> Khushboo >>> >> >> >> >> -- >> Sent via pgadmin-hackers mailing list ([email protected]) >> To make changes to your subscription: >> http://www.postgresql.org/mailpref/pgadmin-hackers >> >> > -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers Attachments: [text/x-patch] domains.patch (70.0K, 3-domains.patch) download | inline diff: diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py new file mode 100644 index 0000000..250a665 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py @@ -0,0 +1,510 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas as schemas +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + +class DomainModule(CollectionNodeModule): + NODE_TYPE = 'domain' + COLLECTION_LABEL = gettext("Domains") + + def __init__(self, *args, **kwargs): + self.min_ver = None + self.max_ver = None + super(DomainModule, self).__init__(*args, **kwargs) + + # Before loading this module we need to make sure that scid is schema object + def BackendSupported(self, manager, **kwargs): + """ + This function will validate schema name & scid against catalogs then allow us to + make decision if we want to load this module or not for the schema + """ + if super(DomainModule, self).BackendSupported(manager, **kwargs): + conn = manager.connection() + # If DB is not connected then return error to browser + if not conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + ver = manager.version + server_type = manager.server_type + # we will set template path for sql scripts + if ver >= 90100: + template_path = 'domains/sql/9.1_plus' + else: + # Note: Domain is not supported below postgres version 9.1 + # Hence we will not load this module + return False + + SQL = render_template("/".join([template_path, 'backend_support.sql']), scid=kwargs['scid']) + status, res = conn.execute_scalar(SQL) + # check if any errors + if not status: + return internal_server_error(errormsg=res) + # Check scid is catalog and from 'sys', 'dbo', 'information_schema', + # then False (Do not load this module), othewise True + if res is True: + return False + else: + return True + + def get_nodes(self, gid, sid, did, scid): + """ + Generate the collection node + """ + yield self.generate_browser_collection_node(scid) + + @property + def script_load(self): + """ + Load the module script for database, when any of the database node is + initialized. + """ + return schemas.SchemaModule.NODE_TYPE + + +blueprint = DomainModule(__name__) + + +class DomainView(PGChildNodeView): + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'doid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_schemas': [{'get': 'get_schemas'}, {'get': 'get_schemas'}], + 'get_collations': [{'get': 'get_collations'}, {'get': 'get_collations'}], + 'get_types': [{'get': 'get_types'}, {'get': 'get_types'}] + }) + + + def module_js(self): + return make_response( + render_template( + "domains/js/domains.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + # Get database connection + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If database is not connected then return error + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90100: + self.template_path = 'domains/sql/9.1_plus' + return f(*args, **kwargs) + + return wrap + + + @check_precondition + def list(self, gid, sid, did, scid): + """List the Domains.""" + + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + + @check_precondition + def nodes(self, gid, sid, did, scid): + """Returns all the Domains.""" + + res = [] + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + row['name'], + icon="icon-domain" + )) + + return make_json_response( + data=res, + status=200 + ) + + + @check_precondition + def properties(self, gid, sid, did, scid, doid): + """Returns Domain properties.""" + + SQL = render_template("/".join([self.template_path, 'properties.sql']),scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, 'get_constraints.sql']),doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + + data['constraints'] = res['rows'] + return ajax_response( + response=data, + status=200 + ) + + + @check_precondition + def get_schemas(self, gid, sid, did, scid, doid=None): + """Returns Schemas for particular database.""" + + res = [{ 'label': '', 'value': '' }] + try: + SQL = render_template("/".join([self.template_path, 'get_schemas.sql']), scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append( + { 'label': row['nspname'], 'value': row['nspname'] } + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def get_collations(self, gid, sid, did, scid, doid=None): + """Returns Collations.""" + + res = [{ 'label': '', 'value': '' }] + try: + SQL = render_template("/".join([self.template_path, 'get_collations.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append( + { 'label': row['copy_collation'], 'value': row['copy_collation'] } + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def get_types(self, gid, sid, did, scid, doid=None): + """Returns Types.""" + # TODO: This function should be removed once Types module will be completed. + + res = [{ 'label': '', 'value': '' }] + try: + SQL = render_template("/".join([self.template_path, 'get_types.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append( + { 'label': row['typname'], 'value': row['typname'] } + ) + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def create(self, gid, sid, did, scid): + """ + Creates new domain object. + + Expected Parameters: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Expected Response: + Domain object in json format. + """ + + data = request.form if request.form else json.loads(request.data.decode()) + required_args = [ + 'name', + 'owner', + 'basensp', + 'basetype' + ] + + for arg in required_args: + if arg not in data or data[arg] == '': + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + try: + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, qtIdent=self.qtIdent) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # we need oid to to add object in tree at browser, below sql will gives the same + SQL = render_template("/".join([self.template_path, 'get_oid.sql']), basensp=data['basensp'], name=data['name']) + status, doid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=doid) + + return jsonify( + node=self.blueprint.generate_browser_node( + doid, + data['name'], + icon="icon-domain" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def delete(self, gid, sid, did, scid, doid): + """ + Drop the Domain. + """ + # Below will decide if it's simple drop or drop with cascade call + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + SQL = render_template("/".join([self.template_path, 'delete.sql']), scid=scid, doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + name, basensp = res['rows'][0] + + SQL = render_template("/".join([self.template_path, 'delete.sql']), name=name, basensp=basensp, cascade=cascade) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, scid, doid): + """ + Update the Domain. + """ + data = request.form if request.form else json.loads(request.data.decode()) + + status, SQL = self.getSQL(gid, sid, data, scid, doid) + if not status: + return internal_server_error(errormsg=SQL) + + try: + if SQL and SQL.strip('\n') and SQL.strip(' '): + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Domain updated", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def msql(self, gid, sid, did, scid, doid=None): + """ + Returns the modified SQL. + + Expected Parameters: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Expected Response: + SQL statements to create/update the Domain. + """ + data = request.args + if doid is None: + required_args = [ + 'name', + 'owner', + 'basensp', + 'basetype' + ] + + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + SQL = self.getSQL(gid, sid, data, scid, doid) + if isinstance(SQL, str) and SQL and SQL.strip('\n') and SQL.strip(' '): + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + + def getSQL(self, gid, sid, data, scid, doid=None): + """ + Genrates the SQL statements to create/update the Domain. + """ + try: + if doid is not None: + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + SQL = render_template( + "/".join([self.template_path,'update.sql']), + data=data, o_data=old_data, conn=self.conn, qtIdent=self.qtIdent + ) + else: + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn, qtIdent=self.qtIdent) + return True,SQL + + except Exception as e: + return False, e + +DomainView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py new file mode 100644 index 0000000..7f0575b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py @@ -0,0 +1,390 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import NodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas.domains as domains +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + +class DomainConstraintModule(CollectionNodeModule): + NODE_TYPE = 'domain-constraints' + COLLECTION_LABEL = gettext("Domain Constraints") + + def __init__(self, *args, **kwargs): + self.min_ver = None + self.max_ver = None + super(DomainConstraintModule, self).__init__(*args, **kwargs) + + def get_nodes(self, gid, sid, did, scid, doid): + """ + Generate the collection node + """ + yield self.generate_browser_collection_node(doid) + + @property + def node_inode(self): + """ + Override this property to make the node as leaf node. + """ + return False + + + @property + def script_load(self): + """ + Load the module script for database, when any of the database node is + initialized. + """ + return domains.DomainModule.NODE_TYPE + + +blueprint = DomainConstraintModule(__name__) + + +class DomainConstraintView(NodeView): + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'}, + {'type': 'int', 'id': 'doid'} + ] + ids = [ + {'type': 'int', 'id': 'coid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def module_js(self): + """ + This property defines (if javascript) exists for this node. + Override this property for your own logic. + """ + return make_response( + render_template( + "domain-constraints/js/domain-constraints.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90100: + self.template_path = 'domain-constraints/sql/9.1_plus' + return f(*args, **kwargs) + + return wrap + + + @check_precondition + def list(self, gid, sid, did, scid, doid): + """ + List the Domain Constraints. + """ + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + + @check_precondition + def nodes(self, gid, sid, did, scid, doid): + """ + Returns all the Domain Constraints. + """ + res = [] + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid) + status, rset = self.conn.execute_2darray(SQL) + + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + row['conname'], + icon="icon-domain-constraints" + )) + + return make_json_response( + data=res, + status=200 + ) + + + @check_precondition + def properties(self, gid, sid, did, scid, doid, coid): + """ + Returns the Domain Constraints property. + """ + + SQL = render_template("/".join([self.template_path, 'properties.sql']),doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + return ajax_response( + response=res['rows'][0], + status=200 + ) + + + @check_precondition + def create(self, gid, sid, did, scid, doid): + """ + Creates new the Domain Constraints. + + Expected Parameters: + conname: Constraints Name + consrc: Constraints Check + + Expected Response: + Domain Constraint object in json format. + """ + + data = request.form if request.form else json.loads(request.data.decode()) + required_args = [ + 'conname', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + try: + # Get Schema and Domain. + SQL = render_template("/".join([self.template_path, 'get_domain.sql']), doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, domain=domain, schema=schema, qtIdent=self.qtIdent) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get the recently added constraints oid + SQL = render_template("/".join([self.template_path, 'get_oid.sql']), doid=doid, name=data['conname']) + status, coid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=doid) + + return jsonify( + node=self.blueprint.generate_browser_node( + coid, + data['conname'], + icon="icon-domain" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def delete(self, gid, sid, did, scid, doid, coid): + """ + Drop the Doman Constraints. + """ + try: + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, 'delete.sql']), data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def update(self, gid, sid, did, scid, doid, coid): + """ + Update the Domain Constraints. + """ + data = request.form if request.form else json.loads(request.data.decode()) + + SQL = self.getSQL(gid, sid, data, scid, doid, coid) + try: + if SQL and SQL.strip('\n') and SQL.strip(' '): + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Domain updated", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def msql(self, gid, sid, did, scid, doid=None): + """ + Returns the modified SQL. + + Expected Parameters: + conname: Constraints Name + consrc: Constraints Check + + Expected Response: + Domain Constraint object in json format. + """ + data = request.args + if doid is None: + required_args = [ + 'conname', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + SQL = self.getSQL(gid, sid, data, scid, doid) + if isinstance(SQL, str) and SQL and SQL.strip('\n') and SQL.strip(' '): + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + + def getSQL(self, gid, sid, data, scid, doid, coid=None): + """ + Genrates the SQL statements to create/update the Domain Constraint. + """ + try: + if coid is not None: + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + old_data = res['rows'][0] + + SQL = render_template( + "/".join([self.template_path,'update.sql']), + data=data, o_data=old_data, conn=self.conn + ) + else: + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn) + return SQL + except Exception as e: + return internal_server_error(errormsg=str(e)) + +DomainConstraintView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..d62e13705c50e6c0cf8f19d680053e8643e28751 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=2TrO847{Jl`|t`Qpas zn<hS+l=rMY>RGPqvlydizRJ&>B`Om#V}R-yOM?7@862M7NCR>>3p^r=fwTu0yPeFo z12TL)T^vI=t|uoPU||ZF<tgaHG*QsQ!?m&Tq=?3mCu}J#Dx3x@mM}}^iE=5NIWXnk zkpnC4ai%a>@;Gg7=uums=9bIK=Egd~(us-3g@Iv02gfsK^JP^)gH=mhBT7;dOH!?p zi&B9UgOP!ufv%yEu7P2Qk%5(ov6YF5wt=aYfq}(LRXG$5x%nxXX_XKS29{tAAk|g| XW)KahriZQpYGCkm^>bP0l+XkKyyRU} literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png new file mode 100644 index 0000000000000000000000000000000000000000..32a045b8fafdc08640d53b2a86b1dcabcb0fe0fd GIT binary patch literal 579 zcmV-J0=)f+P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp) z=>Px$AW%$HMR1Z9#Q*@REHmdHAm<<;=p`lTDl6$LE15$|nM6yxJ3#6&F}^)Qx<p3n zH#h4zIK)Ou#79fTM@#HJKkY(8?L$QEL`CjJMeasM?ng(;QB>|oNbX2U?n+AUOH1!e zOz%xi?@dncPEXKRS?^Cz#amtQQBm+xQt(q#@KaRqR8;U)Rq<9<@mE*YVq(`~V)0vB z+GS<)VPW%PV%=$Jag!JHXlV6qZS`($_HuH3pd<EncJ_C7f1@XWqbGu>Du=8uhpjJ) zvN4agH<i3UrMYgWyK<eyN1Vq+p2tbA!G5C3PO!m$q|HvV#D$~JOsv;ftk_qv-CeQX zT*cC%$J3;><6_L$t<Kr8)!w|r@o>cPam4X*+~mi`^K{Glc|xh<NdN!<0d!JMQvg8b z*k%9#00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipf34<ROg z(p72z0056kL_t&-)1{2Z3c^qT1mDEody6IZuCW&s1V!`^4}$goe?(18@b2Db*j*w1 z%4M(p;&Zw?9ZaKAxo*x;COX}<8fzjqKRv^2kF1spymYHMjE2m7Hl|a~emCNwG8(i? z8I#``(kiBrEbh}(Qn8TL2+$}b3Hn^7p`K6R#_6zQ2$?ux;lXBYB>hkN@C%g?4vVBM R$bbL<002ovPDHLkV1min0OSAw literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..9d1d2a061c7948168d7b1c2474d769b31709f1cf GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE08{VY2nhHc^eMaU%j`d zaI$#+HuEKC{pKEZKYn>h(+aIMH^MjGjcs3}f9Cqyt&fvx7AT*)xv^`L;hf{Hi_iP4 zxgK%&V?q65_4c*;8}G%;JMMY<SLM___M4Bi9{E^!<YUpX&n1ga`7PgFwEkdS!(#P< zNn&@N9OqiK(i&(nV@Z%-FoVOh8)-leXMsm#F_88EW4Dvpc0fjir;B5V#O36K1sn!O zhRVf}5jSsGPH?f<xudc|vBoY<&5cb=uTGB9v187J4II+it5?jhn8F{TsCZIWRad$D zg1*K9W%cu2NsD(hEfVT#*wm#pYw;D04+0E(uQE?s`Z6*ZXoqTvYeY#(Vo9o1a#1Rf zVlXl=G|)9P(lsy)F*2|+F}5->(Kax(GBB{1sVaw}AvZrIGp!P$!N3x%0i@c>zzm|{ T)b!9bKn)C@u6{1-oD!M<=4O^k literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css new file mode 100644 index 0000000..68cd622 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css @@ -0,0 +1,9 @@ +.pg-icon-domain-constraints { + background-image: url('{{ url_for('NODE-domain-constraints.static', filename='img/domain-constraints.png') }}') !important; + border-radius: 10px; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js new file mode 100644 index 0000000..fb3de7d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js @@ -0,0 +1,95 @@ +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + if (!pgBrowser.Nodes['coll-domain-constraints']) { + var databases = pgAdmin.Browser.Nodes['coll-domain-constraints'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + type: 'coll-domain-constraints' + }); + }; + + if (!pgBrowser.Nodes['domain-constraints']) { + pgAdmin.Browser.Nodes['domain-constraints'] = pgBrowser.Node.extend({ + type: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + collection_type: 'coll-domain-constraints', + hasSQL: true, + parent_type: ['domain'], + Init: function() { + /* Avoid mulitple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + conname: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined + }, + schema: [{id: 'conname', label:'Name', type:'text', cell:'string'}, + {id: 'description', label:'Comment', type: 'multiline', cell: 'string', mode: ['properties', 'create', 'edit'], disabled: function(m) { return !m.isNew(); }}, + {id: 'consrc', label:'Check', type: 'multiline', cel: 'string', group: 'Definition', mode: ['properties', 'create', 'edit'], disabled: function(m) { return !m.isNew(); }}, + {id: 'connoinherit', label:'No Inherit', type: 'switch', cell: 'boolean', group: 'Definition', mode: ['properties', 'create', 'edit'], disabled: function(m) { return !m.isNew(); }}, + {id: 'convalidated', label:"Don't Validate", type: 'switch', cell: 'boolean', group: 'Definition'} + ], + validate: function() { + var err = {}, + errmsg; + + if (_.isUndefined(this.get('conname')) || String(this.get('conname')).replace(/^\s+|\s+$/g, '') == '') { + err['conname'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['conname']; + } + + if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') { + err['consrc'] = '{{ _('Check can not be empty!') }}'; + errmsg = errmsg || err['consrc']; + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + + }, + }), + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql new file mode 100644 index 0000000..ed2610d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql @@ -0,0 +1,6 @@ +{% if data and schema and domain %} + ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ data.conname }} CHECK ({{ data.consrc }} ) + {% if data.convalidated %} NOT VALID {% endif %} {% if data.connoinherit %} NO INHERIT {% endif %}; +{% endif %} + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..1a56583 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} + ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ data.conname }} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql new file mode 100644 index 0000000..a199c5c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql @@ -0,0 +1,7 @@ +SELECT d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..344abcc --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql @@ -0,0 +1,4 @@ +SELECT oid, conname as name + FROM pg_constraint +WHERE contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..7ed21d9 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql @@ -0,0 +1,18 @@ +SELECT 'TABLE' AS objectkind, c.oid, conname, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc, connoinherit, convalidated +FROM pg_constraint c +JOIN pg_class cl ON cl.oid=conrelid +JOIN pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT 'DOMAIN' AS objectkind, c.oid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc +, connoinherit, convalidated FROM pg_constraint c + JOIN pg_type t ON t.oid=contypid + JOIN pg_namespace nl ON nl.oid=typnamespace + LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) + WHERE contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql new file mode 100644 index 0000000..9a2c8d7 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql @@ -0,0 +1,4 @@ +{% if data.conname %} + ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ o_data.conname }} TO {{ data.conname }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png new file mode 100644 index 0000000000000000000000000000000000000000..55621528a1dba4928538fe5557b9b988ed78d6ab GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}U4T!BE0BJeoqfW=;gF{0PD8_o zi&pN_(K)B7`FPi%2ix`@v9LU(tNUQ*!K1czXOxuggoGT_(#p@zf4Jw!!zHV)`3F7T zd-Um}H}`iRIijcc@Wq#>&ptkR`SszG54Uz6zW4mg?MLsgZ9jbb>E~y!zTSB9`Re1( zjS1S99(_9h@YC4`A5Y)^_^>@J3+MvIk|4ie28U-i(tsS!0*}aIAngIhZYQ(tfQ)ue z7sn8Z%b|U@#hMgESUvs4wKoSxO>~{O=-Yq$8_!r)-^e%4-l6f-Jox1%8}FIVx>FCW zPMVwluQGbkP1bcSwVV0d_?PZbZVis%HeYTWyU*$OvYp$uTc3QLbD?2trOnDuhZa>f z=3d_{(zER9xt(<n3UsBI4SNC?Y!rBW<m2`n>yDRyg$uoE8Qx}bo1D0qw;t#u)e_f; zl9a@fRIB8oR3OD*WMF8ZYiOivU>IU#U}a)#Wn!XjU}|MxU@=ow4n;$5eoAIqB}9XP eC0GMUwUvPxM8m1+p=*E|7(8A5T-G@yGywoRCC2Lj literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png new file mode 100644 index 0000000000000000000000000000000000000000..7521cddeaaaf0ee4e3c60e948078d70e17e06893 GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}T7XZ8E0Deu9eu*V;gF{0K`pIg zc6K`r4IeIAc}Q3HxV`=3y+@xudUJpGkt2F~PoI5!^78A$Cm(L@JbcYR=;4bm_nv>b z{pkI*?T7D#gyiSvKYR7{_S4Tdo_xOg_;X`|_N7Ok&OiKg_QA)~_dlLo85{w$iLoTe zFPOpM*^M+HhqJ&VvKUBvfU(=jY&#$$$<xI#MB;Mq`IABp20RT9wUXkaL(1R(pOn?c z>38<+e>KaN2{%0Jd@sMbY$+13Z&%Z<%!P+*SNu+#({Rr={_KxUhswHdID0QWTCBvD z)^ky;@gu`E#RAC#l`JcnelS0rxkKN7b1B>H56gX)0&P<*ag8WRNi0dVN-jzTQVd20 zh6cKZM!E)uAw~vPCdO7KCfWw3Rt5$ZGgakKH00)|WTsU@G#FTdHGouG8JIydoSGiG Q2B?9-)78&qol`;+03_q3ga7~l literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png new file mode 100644 index 0000000000000000000000000000000000000000..42ca929325854b8f34787425e8094d08c75983bc GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}PJmB{E0BJeoqfW=;gF{0PD8^7 zi&s6`bL8oxH}`iRdHU?*lb2r~J^6Th=ix^$zTSWS<?f>oH+CHUa^}pVf`aWvMt7fm zx&8F>jfd~AY(HF`mUiRG=W9<sUwQB@KR>@QLHo+%PZuA3I{)y~*$1D_JotF({zvHt zo%ulf7)yfuf*Bm1-ADs+I14-?i-EKU7`vU!wgWPXJzX3_Brcbpe=XFcAmSFNeC3q& zl!Z+(RsYN12&-NW{P*^#<rx82_2-+ii&SqfGgO`Jcj@;01@HTH{&&7#v?j}I)=J6m ze`~hppZn7?WA|%z!}T6pf>z0IjbZrS5ICD*275*_bAo)r8t#Vg4A*Lz);xc4JKq03 zXXC}zd)YwiRZCnWN>UO_QmvAUQh^kMk%6IsuAz~xfnkV|ft87|m5GVAfvJ^&fyGQ! qITQ`K`6-!cl@JXEmS7Da)m8>(5DllMhpqu?VDNPHb6Mw<&;$VOR=Fqu literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css new file mode 100644 index 0000000..7d4fad0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css @@ -0,0 +1,9 @@ +.pg-icon-domain { + background-image: url('{{ url_for('NODE-domain.static', filename='img/domain.png') }}') !important; + border-radius: 10px; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js new file mode 100644 index 0000000..aa1dd7c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js @@ -0,0 +1,236 @@ +/* Create and Register Domain Collection and Domain Node. */ + +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + if (!pgBrowser.Nodes['coll-domain']) { + var databases = pgAdmin.Browser.Nodes['coll-domain'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain', + label: '{{ _('Domains') }}', + type: 'coll-domain' + }); + }; + + var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: null, + security_label: null + }, + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text' + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text' + }], + validate: function() { + var err = {}, + errmsg = null, + data = this.toJSON(); + + if (_.isUndefined(data.label) || + _.isNull(data.label) || + String(data.label).replace(/^\s+|\s+$/g, '') == '') { + return _("Please specify the value for all the security providers."); + } + return null; + } + }); + + var ConstraintsModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + conname: undefined, + description: undefined, + slony: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined + }, + schema: [ + {id: 'conname', label:'Name', type:'text', cell:'string'}, + {id: 'description', label:'Comment', type: 'multiline', cell: 'string'}, + {id: 'slony', label:'Use Slony', type: 'text', cell: 'string'}, + {id: 'consrc', label:'Check', type: 'multiline', cel: 'string', group: 'definition'}, + {id: 'connoinherit', label:'No Inherit', type: 'switch', cell: 'boolean', group: 'definition'}, + {id: 'convalidated', label:"Don't Validate", type: 'switch', cell: 'boolean', group: 'definition'} + ], + validate: function() { + // TODO: Add validation here + }, + toJSON: Backbone.Model.prototype.toJSON + }); + + + if (!pgBrowser.Nodes['domain']) { + pgAdmin.Browser.Nodes['domain'] = pgBrowser.Node.extend({ + type: 'domain', + label: '{{ _('Domain') }}', + collection_type: 'coll-domain', + hasSQL: true, + parent_type: ['schema'], + Init: function() { + /* Avoid mulitple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + canDropCascade: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + oid: undefined, + owner: undefined, + basensp: undefined, + description: undefined, + basetype: undefined, + typlen: undefined, + precision: undefined, + typdefault: undefined, + typnotnull: undefined, + collname: undefined, + constraints: [], + seclabels: [] + }, + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'] + },{ + id: 'oid', label:'{{ _('Oid') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl, + node: 'role', type: 'text', mode: ['edit', 'create', 'properties'] + },{ + id: 'basensp', label:'{{ _('Schema') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', url: 'get_schemas' + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'basetype', label:'{{ _('Base Type') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', mode:['properties', 'create', 'edit'], group: 'Definition', url: 'get_types', + disabled: function(m) { return !m.isNew(); } + },{ + id: 'typlen', label:'{{ _('Length') }}', cell: 'string', + type: 'text', group: 'Definition', disabled: function(m) { return !m.isNew(); } + },{ + id: 'precision', label:'{{ _('Precision') }}', cell: 'string', + type: 'text', group: 'Definition', disabled: function(m) { return !m.isNew(); } + },{ + id: 'typdefault', label:'{{ _('Default') }}', cell: 'string', + type: 'text', group: 'Definition' + },{ + id: 'typnotnull', label:'{{ _('Not Null') }}', cell: 'boolean', + type: 'switch', group: 'Definition' + },{ + id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', group: 'Definition', url: 'get_collations', disabled: function(m) { return !m.isNew(); } + },{ + id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string', + type: 'collection', group: 'Constraints', visible: false, mode: ['edit', 'create'], + model: ConstraintsModel, canAdd: true, canDelete: false, canEdit: false + },{ + id: 'seclabels', label: '{{ _('Security Labels') }}', + model: SecurityModel, editable: false, type: 'collection', + group: '{{ _('Security') }}', mode: ['edit', 'create'], + min_version: 90100, canAdd: true, + canEdit: false, canDelete: true, control: 'unique-col-collection' + } + ], + validate: function() + { + var err = {}, + errmsg, + seclabels = this.get('seclabels'); + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('basetype')) || String(this.get('basetype')).replace(/^\s+|\s+$/g, '') == '') { + err['basetype'] = '{{ _('Base Type can not be empty!') }}'; + errmsg = errmsg || err['basetype']; + } + + if (seclabels) { + var secLabelsErr; + for (var i = 0; i < seclabels.models.length && !secLabelsErr; i++) { + secLabelsErr = (seclabels.models[i]).validate.apply(seclabels.models[i]); + if (secLabelsErr) { + err['seclabels'] = secLabelsErr; + errmsg = errmsg || secLabelsErr; + } + } + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + } + }), + canCreate: function(itemData, item, data) { + //If check is false then , we will allow create menu + if (data && data.check == false) + return true; + + var t = pgBrowser.tree, i = item, d = itemData; + // To iterate over tree to check parent node + while (i) { + // If it is schema then allow user to create domain + if (_.indexOf(['schema'], d._type) > -1) + return true; + + if ('coll-domain' == d._type) { + //Check if we are not child of catalog + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + if( prev_d._type == 'catalog') { + return false; + } else { + return true; + } + } + i = t.hasParent(i) ? t.parent(i) : null; + d = i ? t.itemData(i) : null; + } + // by default we do not want to allow create menu + return true; + } + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql new file mode 100644 index 0000000..64f0941 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql @@ -0,0 +1,20 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM + pg_namespace nsp +WHERE + nsp.oid={{scid}}::int + AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + ) + AND + nspname NOT LIKE E'pg\\temp\\%' + AND + nspname NOT LIKE E'pg\\toast_temp\\%' \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql new file mode 100644 index 0000000..d4cdfe0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql @@ -0,0 +1,32 @@ +{% import 'macros/security.macros' as SECLABLE %} + +{% if data %} + CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ data.basetype }} + {% if data.collname %} + COLLATE {{ data.collname }} + {% endif %} + + {% if data.typnotnull %} + NOT NULL + {% endif %} + ; + + {% if data.constraints %} + {% for c in data.constraints %} + ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + ADD CONSTRAINT {{ c.conname }} CHECK ({{ c.consrc }} ) + {% if c.convalidated %} NOT VALID {% endif %} {% if c.connoinherit %} NO INHERIT {% endif %}; + {% endfor %} + {% endif %} + + ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ data.owner }}; + {% if data.description %} + COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}'; + {% endif %} + + {% for r in data.seclabels %} + {{ SECLABLE.APPLY(conn, 'DOMAIN', data.name, r.provider, r.label) }} + {% endfor %} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..c740f4f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql @@ -0,0 +1,11 @@ +{% if scid and doid %} + SELECT d.typname as name, bn.nspname as basensp + FROM pg_type d + JOIN pg_namespace bn ON bn.oid=d.typnamespace + WHERE d.typnamespace = {{scid}}::oid + AND d.oid={{doid}}::int; +{% endif %} + +{% if name %} + DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade%} CASCADE{% endif %} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql new file mode 100644 index 0000000..6a13a65 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql @@ -0,0 +1,7 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM pg_collation c, pg_namespace n +WHERE c.collnamespace=n.oid +ORDER BY nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql new file mode 100644 index 0000000..e26998f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql @@ -0,0 +1,18 @@ +SELECT 'TABLE' AS objectkind, c.oid, conname, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc, connoinherit, convalidated FROM pg_constraint c +JOIN pg_class cl ON cl.oid=conrelid +JOIN pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT 'DOMAIN' AS objectkind, c.oid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc +, connoinherit, convalidated +FROM pg_constraint c +JOIN pg_type t ON t.oid=contypid +JOIN pg_namespace nl ON nl.oid=typnamespace + +LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) + + WHERE contype = 'c' AND contypid = {{doid}}::oid +ORDER BY conname \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..1c26d6b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql @@ -0,0 +1,10 @@ +SELECT + d.oid, d.typname as name +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }} + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql new file mode 100644 index 0000000..82dcfcb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql @@ -0,0 +1,21 @@ +SELECT + nsp.nspname +FROM + pg_namespace nsp + LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) + LEFT OUTER JOIN pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid) +WHERE + NOT ((nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + )AND + nspname NOT LIKE E'pg\\temp\\%' AND + nspname NOT LIKE E'pg\\toast_temp\\%' AND + -- ADDED: Because We need to omit system schema except the one on which we are trying to create collation + ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' ) +ORDER BY nspname; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql new file mode 100644 index 0000000..e473056 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql @@ -0,0 +1,7 @@ +SELECT * FROM (SELECT format_type(t.oid,NULL) AS typname, CASE WHEN typelem > 0 THEN typelem ELSE t.oid END as elemoid, typlen, typtype, t.oid, nspname, + (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup + FROM pg_type t + JOIN pg_namespace nsp ON typnamespace=nsp.oid + WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'e', 'r')AND NOT EXISTS + (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) + ) AS dummy ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..901d053 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql @@ -0,0 +1,20 @@ +SELECT d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typlen, d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT array_agg(provider || '=' || label) FROM pg_shseclabel sl1 WHERE sl1.objoid=d.oid) AS seclabels +FROM pg_type d + JOIN pg_type b ON b.oid = d.typbasetype + JOIN pg_namespace bn ON bn.oid=d.typnamespace + LEFT OUTER JOIN pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) + LEFT OUTER JOIN pg_collation c ON d.typcollation=c.oid + LEFT OUTER JOIN pg_namespace cn ON c.collnamespace=cn.oid +WHERE d.typnamespace = {{scid}}::oid +{% if doid %} + AND d.oid={{doid}}::int +{% endif %} +ORDER BY d.typname diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql new file mode 100644 index 0000000..eb9c08f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql @@ -0,0 +1,61 @@ +{% if data %} + {% set name = o_data.name %} + {% if data.name %} + {% if data.name != o_data.name %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; + {% set name = data.name %} + {% endif %} + {% endif %} + + {% if not data.typnotnull %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; + {% endif %} + + {% if data.typdefault %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault|qtLiteral }}; + {% endif %} + + {% if data.owner %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ data.owner }}; + {% endif %} + + {% if data.constraints %} + {% for c in data.constraints.added %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ c.conname }} CHECK ({{ c.consrc }} ) + {% if c.convalidated %} NOT VALID {% endif %} {% if c.connoinherit %} NO INHERIT {% endif %}; + {% endfor %} + {% endif %} + + {% set seclabels = data.seclabels %} + {% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} + {% for r in seclabels.deleted %} + {{ SECLABLE.DROP(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider) }} + {% endfor %} + {% endif %} + {% if 'added' in seclabels and seclabels.added|length > 0 %} + {% for r in seclabels.added %} + {{ SECLABLE.APPLY(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }} + {% endfor %} + {% endif %} + {% if 'changed' in seclabels and seclabels.changed|length > 0 %} + {% for r in seclabels.changed %} + {{ SECLABLE.APPLY(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }} + {% endfor %} + {% endif %} + + {% if data.description %} + COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }}; + {% endif %} + + {% if data.basensp %} + ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }}; + {% endif %} + +{% endif %} diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js index 8837fa9..67a8705 100644 --- a/web/pgadmin/static/js/backform.pgadmin.js +++ b/web/pgadmin/static/js/backform.pgadmin.js @@ -998,9 +998,9 @@ } // Insert Edit Cell into Grid - if (data.disabled == false && data.canEdit) { + if (data.disabled == false && data.canEdit || data.canAdd) { var editCell = Backgrid.Extension.ObjectCell.extend({ - schema: gridSchema.schema + schema: gridSchema.schema, editable: false }); gridSchema.columns.unshift({ @@ -1043,6 +1043,7 @@ e.preventDefault(); grid.insertRow({}); var newRow = $(grid.body.rows[collection.length - 1].$el); + console.log(newRow); newRow.attr("class", "new").click(function(e) { $(this).attr("class", ""); }); ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-01-20 17:26 Neel Patel <[email protected]> parent: Khushboo Vashi <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Neel Patel @ 2016-01-20 17:26 UTC (permalink / raw) To: Khushboo Vashi <[email protected]>; +Cc: pgadmin-hackers Hi Khushboo, Please find below review comments. - While creating new Domain and clicking on SQL tab, python side we are getting error saying "*TypeError: 'bool' object is not callable".* We are not able to create any domain. Fix this issue so that we can test other functionality. - Implement the reverse engineering SQL generation for the domain node. - As per the checklist, remove the "Use Slony" from Constraints tab, as it is not required. - No need to pass "*qtIdent=self.qtIdent*" as function argument in "create" and "getSQL" function in domains/__init__.py - In "Security" tab , provider and security label fields are not editable. - In PG version 9.1, when we update the existing domain name then "ALTER DOMAIN" is not supported. Currently there is no checking for the PG version 9.1 and 9.2_plus. It will fail when we connect to database 9.1 e.g. For PG version 9.1 - Update command should be as below. ALTER TYPE xyz RENAME TO abc; For PG version 9.2 onwards - Update command should be as below. ALTER DOMAIN xyz RENAME TO abc; - Some of the SQL file, qtIdent is not used. Please check all the related SQL files. e.g. - In update.sql file "data.owner" should be "conn|qtIdent(data.owner)" {% if data.owner %} ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} OWNER TO {{ data.owner }}; {% endif %} Let us know for any issues. Thanks, Neel Patel On Wed, Jan 20, 2016 at 2:50 PM, Khushboo Vashi < [email protected]> wrote: > Hi Neel, > > Please find updated patch. > > Thanks, > Khushboo > > On Wed, Jan 20, 2016 at 12:50 PM, Neel Patel <[email protected]> > wrote: > >> Hi Khushboo, >> >> While applying the patch file, we are getting below warnings. >> >> ######################################### >> domains (1).patch:1340: trailing whitespace. >> oid: undefined, >> domains (1).patch:1483: trailing whitespace. >> (nspname = 'pg_catalog' AND EXISTS >> domains (1).patch:1487: trailing whitespace. >> OR (nspname = 'information_schema' AND EXISTS >> domains (1).patch:1489: trailing whitespace. >> OR (nspname LIKE '_%' AND EXISTS >> domains (1).patch:1642: trailing whitespace. >> (select 1 from pg_class where relnamespace=typnamespace and relname = >> typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS >> (select 1 from pg_class where relnamespace=typnamespace and relname = >> substring(typname from 2)::name and relkind != 'c')) >> warning: squelched 4 whitespace errors >> warning: 9 lines add whitespace errors. >> ######################################### >> >> Can you please remove the whitespace and regenerate the patch ? >> >> Thanks, >> Neel Patel >> >> On Wed, Jan 20, 2016 at 12:37 PM, Khushboo Vashi < >> [email protected]> wrote: >> >>> Resending patch with binary option. >>> >>> On Wed, Jan 20, 2016 at 10:18 AM, Khushboo Vashi < >>> [email protected]> wrote: >>> >>>> Hi, >>>> >>>> Please find attached patch for the Domain Module. >>>> >>>> The patch will be modified after Types module implementation as we need >>>> to populate Base Type and some Type related validations from the Types >>>> module. >>>> >>>> Please review it and let me know the feedback. >>>> >>>> Thanks, >>>> Khushboo >>>> >>> >>> >>> >>> -- >>> Sent via pgadmin-hackers mailing list ([email protected]) >>> To make changes to your subscription: >>> http://www.postgresql.org/mailpref/pgadmin-hackers >>> >>> >> > ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-02-02 10:21 Khushboo Vashi <[email protected]> parent: Neel Patel <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Khushboo Vashi @ 2016-02-02 10:21 UTC (permalink / raw) To: Neel Patel <[email protected]>; +Cc: pgadmin-hackers Hi Neel, Thanks for reviewing my patch. I have modified the code as per your suggestions and also fixed some of the issues got while doing unit testing. Please find attached patch for the same. Thanks, Khushboo On Wed, Jan 20, 2016 at 10:56 PM, Neel Patel <[email protected]> wrote: > Hi Khushboo, > > Please find below review comments. > > - While creating new Domain and clicking on SQL tab, python side we are > getting error saying "*TypeError: 'bool' object is not callable".* > We are not able to create any domain. Fix this issue so that we can > test other functionality. > - Implement the reverse engineering SQL generation for the domain node. > - As per the checklist, remove the "Use Slony" from Constraints tab, as it > is not required. > - No need to pass "*qtIdent=self.qtIdent*" as function argument in > "create" and "getSQL" function in domains/__init__.py > - In "Security" tab , provider and security label fields are not editable. > - In PG version 9.1, when we update the existing domain name then "ALTER > DOMAIN" is not supported. > Currently there is no checking for the PG version 9.1 and 9.2_plus. It > will fail when we connect to database 9.1 > > e.g. > For PG version 9.1 - Update command should be as below. > ALTER TYPE xyz RENAME TO abc; > For PG version 9.2 onwards - Update command should be as below. > ALTER DOMAIN xyz RENAME TO abc; > > - Some of the SQL file, qtIdent is not used. Please check all the related > SQL files. > e.g. - In update.sql file "data.owner" should be > "conn|qtIdent(data.owner)" > > {% if data.owner %} > ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} > OWNER TO {{ data.owner }}; > {% endif %} > > Let us know for any issues. > > Thanks, > Neel Patel > > On Wed, Jan 20, 2016 at 2:50 PM, Khushboo Vashi < > [email protected]> wrote: > >> Hi Neel, >> >> Please find updated patch. >> >> Thanks, >> Khushboo >> >> On Wed, Jan 20, 2016 at 12:50 PM, Neel Patel <[email protected] >> > wrote: >> >>> Hi Khushboo, >>> >>> While applying the patch file, we are getting below warnings. >>> >>> ######################################### >>> domains (1).patch:1340: trailing whitespace. >>> oid: undefined, >>> domains (1).patch:1483: trailing whitespace. >>> (nspname = 'pg_catalog' AND EXISTS >>> domains (1).patch:1487: trailing whitespace. >>> OR (nspname = 'information_schema' AND EXISTS >>> domains (1).patch:1489: trailing whitespace. >>> OR (nspname LIKE '_%' AND EXISTS >>> domains (1).patch:1642: trailing whitespace. >>> (select 1 from pg_class where relnamespace=typnamespace and relname = >>> typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS >>> (select 1 from pg_class where relnamespace=typnamespace and relname = >>> substring(typname from 2)::name and relkind != 'c')) >>> warning: squelched 4 whitespace errors >>> warning: 9 lines add whitespace errors. >>> ######################################### >>> >>> Can you please remove the whitespace and regenerate the patch ? >>> >>> Thanks, >>> Neel Patel >>> >>> On Wed, Jan 20, 2016 at 12:37 PM, Khushboo Vashi < >>> [email protected]> wrote: >>> >>>> Resending patch with binary option. >>>> >>>> On Wed, Jan 20, 2016 at 10:18 AM, Khushboo Vashi < >>>> [email protected]> wrote: >>>> >>>>> Hi, >>>>> >>>>> Please find attached patch for the Domain Module. >>>>> >>>>> The patch will be modified after Types module implementation as we >>>>> need to populate Base Type and some Type related validations from the >>>>> Types module. >>>>> >>>>> Please review it and let me know the feedback. >>>>> >>>>> Thanks, >>>>> Khushboo >>>>> >>>> >>>> >>>> >>>> -- >>>> Sent via pgadmin-hackers mailing list ([email protected]) >>>> To make changes to your subscription: >>>> http://www.postgresql.org/mailpref/pgadmin-hackers >>>> >>>> >>> >> > -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers Attachments: [text/x-patch] Domain_ver_1.patch (91.5K, 3-Domain_ver_1.patch) download | inline diff: diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py new file mode 100644 index 0000000..0d029a4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py @@ -0,0 +1,533 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas as schemas +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + +class DomainModule(CollectionNodeModule): + NODE_TYPE = 'domain' + COLLECTION_LABEL = gettext("Domains") + + def __init__(self, *args, **kwargs): + self.min_ver = None + self.max_ver = None + super(DomainModule, self).__init__(*args, **kwargs) + + # Before loading this module we need to make sure that scid is schema object + def BackendSupported(self, manager, **kwargs): + """ + This function will validate schema name & scid against catalogs then allow us to + make decision if we want to load this module or not for the schema + """ + if super(DomainModule, self).BackendSupported(manager, **kwargs): + conn = manager.connection() + # If DB is not connected then return error to browser + if not conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + ver = manager.version + server_type = manager.server_type + # we will set template path for sql scripts + if ver >= 90100: + template_path = 'domains/sql/9.1_plus' + else: + # Note: Domain is not supported below postgres version 9.1 + # Hence we will not load this module + return False + + SQL = render_template("/".join([template_path, 'backend_support.sql']), scid=kwargs['scid']) + status, res = conn.execute_scalar(SQL) + # check if any errors + if not status: + return internal_server_error(errormsg=res) + # Check scid is catalog and from 'sys', 'dbo', 'information_schema', + # then False (Do not load this module), othewise True + if res is True: + return False + else: + return True + + def get_nodes(self, gid, sid, did, scid): + """ + Generate the collection node + """ + yield self.generate_browser_collection_node(scid) + + @property + def script_load(self): + """ + Load the module script for database, when any of the database node is + initialized. + """ + return schemas.SchemaModule.NODE_TYPE + + +blueprint = DomainModule(__name__) + + +class DomainView(PGChildNodeView): + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'doid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_schemas': [{'get': 'get_schemas'}, {'get': 'get_schemas'}], + 'get_collations': [{'get': 'get_collations'}, {'get': 'get_collations'}], + 'get_types': [{'get': 'get_types'}, {'get': 'get_types'}] + }) + + + def module_js(self): + return make_response( + render_template( + "domains/js/domains.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + # Get database connection + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If database is not connected then return error + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domains/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domains/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + + @check_precondition + def list(self, gid, sid, did, scid): + """List the Domains.""" + + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + + @check_precondition + def nodes(self, gid, sid, did, scid): + """Returns all the Domains.""" + + res = [] + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + row['name'], + icon="icon-domain" + )) + + return make_json_response( + data=res, + status=200 + ) + + + @check_precondition + def properties(self, gid, sid, did, scid, doid): + """Returns Domain properties.""" + + SQL = render_template("/".join([self.template_path, 'properties.sql']),scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, 'get_constraints.sql']),doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + + data['constraints'] = res['rows'] + return ajax_response( + response=data, + status=200 + ) + + + @check_precondition + def get_schemas(self, gid, sid, did, scid, doid=None): + """Returns Schemas for particular database.""" + + res = [{ 'label': '', 'value': '' }] + try: + SQL = render_template("/".join([self.template_path, 'get_schemas.sql']), scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append( + { 'label': row['nspname'], 'value': row['nspname'] } + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def get_collations(self, gid, sid, did, scid, doid=None): + """Returns Collations.""" + + res = [{ 'label': '', 'value': '' }] + try: + SQL = render_template("/".join([self.template_path, 'get_collations.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append( + { 'label': row['copy_collation'], 'value': row['copy_collation'] } + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def get_types(self, gid, sid, did, scid, doid=None): + """Returns Types.""" + # TODO: This function should be removed once Types module will be completed. + + res = [{ 'label': '', 'value': '' }] + try: + SQL = render_template("/".join([self.template_path, 'get_types.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append( + { 'label': row['typname'], 'value': row['typname'] } + ) + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def create(self, gid, sid, did, scid): + """ + Creates new domain object. + + Expected Parameters: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Expected Response: + Domain object in json format. + """ + + data = request.form if request.form else json.loads(request.data.decode()) + required_args = [ + 'name', + 'owner', + 'basensp', + 'basetype' + ] + + for arg in required_args: + if arg not in data or data[arg] == '': + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + try: + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # we need oid to to add object in tree at browser, below sql will gives the same + SQL = render_template("/".join([self.template_path, 'get_oid.sql']), basensp=data['basensp'], name=data['name']) + status, doid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=doid) + + return jsonify( + node=self.blueprint.generate_browser_node( + doid, + data['name'], + icon="icon-domain" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def delete(self, gid, sid, did, scid, doid): + """ + Drop the Domain. + """ + # Below will decide if it's simple drop or drop with cascade call + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + SQL = render_template("/".join([self.template_path, 'delete.sql']), scid=scid, doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + name, basensp = res['rows'][0] + + SQL = render_template("/".join([self.template_path, 'delete.sql']), name=name, basensp=basensp, cascade=cascade) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, scid, doid): + """ + Update the Domain. + """ + data = request.form if request.form else json.loads(request.data.decode()) + + domain_data = {} + for key in data: + if key in ['constraints', 'seclabels']: + domain_data[key] = json.loads(data[key]) + else: + domain_data[key] = data[key] + + print doid, domain_data + status, SQL = self.getSQL(gid, sid, domain_data, scid, doid) + + if not status: + return internal_server_error(errormsg=SQL) + + try: + if SQL: + print SQL + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Domain updated", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def msql(self, gid, sid, did, scid, doid=None): + """ + Returns the modified SQL. + + Expected Parameters: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Expected Response: + SQL statements to create/update the Domain. + """ + data = request.args + + if doid is None: + required_args = [ + 'name', + 'owner', + 'basensp', + 'basetype' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + domain_data = {} + for key in data: + if key in ['constraints', 'seclabels']: + domain_data[key] = json.loads(data[key]) + elif key == 'typnotnull': + domain_data[key] = True if data[key] == 'true' else (False if data['key'] == 'false' else '') + else: + domain_data[key] = data[key] + + status, SQL = self.getSQL(gid, sid, domain_data, scid, doid) + + if SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + + def getSQL(self, gid, sid, data, scid, doid=None): + """ + Genrates the SQL statements to create/update the Domain. + """ + try: + if doid is not None: + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + SQL = render_template( + "/".join([self.template_path,'update.sql']), + data=data, o_data=old_data) + else: + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data) + return True,SQL + + except Exception as e: + return False, e + +DomainView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py new file mode 100644 index 0000000..d701909 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py @@ -0,0 +1,400 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import NodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas.domains as domains +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + +class DomainConstraintModule(CollectionNodeModule): + NODE_TYPE = 'domain-constraints' + COLLECTION_LABEL = gettext("Domain Constraints") + + def __init__(self, *args, **kwargs): + self.min_ver = None + self.max_ver = None + super(DomainConstraintModule, self).__init__(*args, **kwargs) + + def get_nodes(self, gid, sid, did, scid, doid): + """ + Generate the collection node + """ + yield self.generate_browser_collection_node(doid) + + @property + def node_inode(self): + """ + Override this property to make the node as leaf node. + """ + return False + + + @property + def script_load(self): + """ + Load the module script for database, when any of the database node is + initialized. + """ + return domains.DomainModule.NODE_TYPE + + +blueprint = DomainConstraintModule(__name__) + + +class DomainConstraintView(NodeView): + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'}, + {'type': 'int', 'id': 'doid'} + ] + ids = [ + {'type': 'int', 'id': 'coid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def module_js(self): + """ + This property defines (if javascript) exists for this node. + Override this property for your own logic. + """ + return make_response( + render_template( + "domain-constraints/js/domain-constraints.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90100: + self.template_path = 'domain-constraints/sql/9.1_plus' + return f(*args, **kwargs) + + return wrap + + + @check_precondition + def list(self, gid, sid, did, scid, doid): + """ + List the Domain Constraints. + """ + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + + @check_precondition + def nodes(self, gid, sid, did, scid, doid): + """ + Returns all the Domain Constraints. + """ + res = [] + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid) + status, rset = self.conn.execute_2darray(SQL) + + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + row['name'], + icon="icon-domain-constraints" + )) + + return make_json_response( + data=res, + status=200 + ) + + + @check_precondition + def properties(self, gid, sid, did, scid, doid, coid): + """ + Returns the Domain Constraints property. + """ + + SQL = render_template("/".join([self.template_path, 'properties.sql']),doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + return ajax_response( + response=res['rows'][0], + status=200 + ) + + + @check_precondition + def create(self, gid, sid, did, scid, doid): + """ + Creates new the Domain Constraints. + + Expected Parameters: + name: Constraints Name + consrc: Constraints Check + + Expected Response: + Domain Constraint object in json format. + """ + + data = request.form if request.form else json.loads(request.data.decode()) + required_args = [ + 'name', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + try: + # Get Schema and Domain. + SQL = render_template("/".join([self.template_path, 'get_domain.sql']), doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, domain=domain, schema=schema) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get the recently added constraints oid + SQL = render_template("/".join([self.template_path, 'get_oid.sql']), doid=doid, name=data['name']) + status, coid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=doid) + + return jsonify( + node=self.blueprint.generate_browser_node( + coid, + data['name'], + icon="icon-domain-constraints" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def delete(self, gid, sid, did, scid, doid, coid): + """ + Drop the Doman Constraints. + """ + try: + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, 'delete.sql']), data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain Constraint dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def update(self, gid, sid, did, scid, doid, coid): + """ + Update the Domain Constraints. + """ + data = request.form if request.form else json.loads(request.data.decode()) + + status, SQL = self.getSQL(gid, sid, data, scid, doid, coid) + try: + if SQL and status: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Domain Constraint updated", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + + @check_precondition + def msql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the modified SQL. + + Expected Parameters: + name: Constraints Name + consrc: Constraints Check + + Expected Response: + Domain Constraint object in json format. + """ + data = request.args + + if coid is None: + required_args = [ + 'name', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + status, SQL = self.getSQL(gid, sid, data, scid, doid, coid) + if status and SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + + def getSQL(self, gid, sid, data, scid, doid, coid=None): + """ + Genrates the SQL statements to create/update the Domain Constraint. + """ + try: + if coid is not None: + SQL = render_template("/".join([self.template_path, 'properties.sql']), doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + old_data = res['rows'][0] + + SQL = render_template( + "/".join([self.template_path,'update.sql']), + data=data, o_data=old_data, conn=self.conn + ) + else: + SQL = render_template("/".join([self.template_path, 'get_domain.sql']), doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return False,internal_server_error(errormsg=name) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, domain=domain, schema=schema) + return True, SQL + except Exception as e: + return False, internal_server_error(errormsg=str(e)) + +DomainConstraintView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..d62e13705c50e6c0cf8f19d680053e8643e28751 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=2TrO847{Jl`|t`Qpas zn<hS+l=rMY>RGPqvlydizRJ&>B`Om#V}R-yOM?7@862M7NCR>>3p^r=fwTu0yPeFo z12TL)T^vI=t|uoPU||ZF<tgaHG*QsQ!?m&Tq=?3mCu}J#Dx3x@mM}}^iE=5NIWXnk zkpnC4ai%a>@;Gg7=uums=9bIK=Egd~(us-3g@Iv02gfsK^JP^)gH=mhBT7;dOH!?p zi&B9UgOP!ufv%yEu7P2Qk%5(ov6YF5wt=aYfq}(LRXG$5x%nxXX_XKS29{tAAk|g| XW)KahriZQpYGCkm^>bP0l+XkKyyRU} literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png new file mode 100644 index 0000000000000000000000000000000000000000..32a045b8fafdc08640d53b2a86b1dcabcb0fe0fd GIT binary patch literal 579 zcmV-J0=)f+P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp) z=>Px$AW%$HMR1Z9#Q*@REHmdHAm<<;=p`lTDl6$LE15$|nM6yxJ3#6&F}^)Qx<p3n zH#h4zIK)Ou#79fTM@#HJKkY(8?L$QEL`CjJMeasM?ng(;QB>|oNbX2U?n+AUOH1!e zOz%xi?@dncPEXKRS?^Cz#amtQQBm+xQt(q#@KaRqR8;U)Rq<9<@mE*YVq(`~V)0vB z+GS<)VPW%PV%=$Jag!JHXlV6qZS`($_HuH3pd<EncJ_C7f1@XWqbGu>Du=8uhpjJ) zvN4agH<i3UrMYgWyK<eyN1Vq+p2tbA!G5C3PO!m$q|HvV#D$~JOsv;ftk_qv-CeQX zT*cC%$J3;><6_L$t<Kr8)!w|r@o>cPam4X*+~mi`^K{Glc|xh<NdN!<0d!JMQvg8b z*k%9#00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipf34<ROg z(p72z0056kL_t&-)1{2Z3c^qT1mDEody6IZuCW&s1V!`^4}$goe?(18@b2Db*j*w1 z%4M(p;&Zw?9ZaKAxo*x;COX}<8fzjqKRv^2kF1spymYHMjE2m7Hl|a~emCNwG8(i? z8I#``(kiBrEbh}(Qn8TL2+$}b3Hn^7p`K6R#_6zQ2$?ux;lXBYB>hkN@C%g?4vVBM R$bbL<002ovPDHLkV1min0OSAw literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..9d1d2a061c7948168d7b1c2474d769b31709f1cf GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE08{VY2nhHc^eMaU%j`d zaI$#+HuEKC{pKEZKYn>h(+aIMH^MjGjcs3}f9Cqyt&fvx7AT*)xv^`L;hf{Hi_iP4 zxgK%&V?q65_4c*;8}G%;JMMY<SLM___M4Bi9{E^!<YUpX&n1ga`7PgFwEkdS!(#P< zNn&@N9OqiK(i&(nV@Z%-FoVOh8)-leXMsm#F_88EW4Dvpc0fjir;B5V#O36K1sn!O zhRVf}5jSsGPH?f<xudc|vBoY<&5cb=uTGB9v187J4II+it5?jhn8F{TsCZIWRad$D zg1*K9W%cu2NsD(hEfVT#*wm#pYw;D04+0E(uQE?s`Z6*ZXoqTvYeY#(Vo9o1a#1Rf zVlXl=G|)9P(lsy)F*2|+F}5->(Kax(GBB{1sVaw}AvZrIGp!P$!N3x%0i@c>zzm|{ T)b!9bKn)C@u6{1-oD!M<=4O^k literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css new file mode 100644 index 0000000..57b6612 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css @@ -0,0 +1,8 @@ +.pg-icon-domain-constraints { + background-image: url('{{ url_for('NODE-domain-constraints.static', filename='img/domain-constraints.png') }}') !important; + border-radius: 10px; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js new file mode 100644 index 0000000..c564ef8 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js @@ -0,0 +1,105 @@ +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + if (!pgBrowser.Nodes['coll-domain-constraints']) { + var databases = pgAdmin.Browser.Nodes['coll-domain-constraints'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + type: 'coll-domain-constraints' + }); + }; + + if (!pgBrowser.Nodes['domain-constraints']) { + pgAdmin.Browser.Nodes['domain-constraints'] = pgBrowser.Node.extend({ + type: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + collection_type: 'coll-domain-constraints', + hasSQL: true, + parent_type: ['domain'], + Init: function() { + /* Avoid mulitple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined + }, + schema: [{id: 'name', label:'Name', type:'text', cell:'string', disabled: 'isDisabled'}, + {id: 'description', label:'Comment', type: 'multiline', cell: 'string', mode: ['properties', 'create', 'edit'], disabled: function(m) { return !m.isNew(); }}, + {id: 'consrc', label:'Check', type: 'multiline', cel: 'string', group: 'Definition', mode: ['properties', 'create', 'edit'], disabled: function(m) { return !m.isNew(); }}, + {id: 'connoinherit', label:'No Inherit', type: 'switch', cell: 'boolean', group: 'Definition', mode: ['properties', 'create', 'edit'], disabled: 'isDisabled'}, + {id: 'convalidated', label:"Don't Validate", type: 'switch', cell: 'boolean', group: 'Definition', disabled: 'isDisabled'} + ], + validate: function() { + var err = {}, + errmsg; + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') { + err['consrc'] = '{{ _('Check can not be empty!') }}'; + errmsg = errmsg || err['consrc']; + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return true; + } + } + return false; + } + }), + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql new file mode 100644 index 0000000..af6a5e8 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql @@ -0,0 +1,4 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }} ) +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..1fa5bc4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql new file mode 100644 index 0000000..440c854 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql @@ -0,0 +1,7 @@ +SELECT d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..613d0f2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql @@ -0,0 +1,4 @@ +SELECT oid, conname as name + FROM pg_constraint +WHERE contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..bc66960 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql @@ -0,0 +1,18 @@ +SELECT 'TABLE' AS objectkind, c.oid, conname as name, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc +FROM pg_constraint c +JOIN pg_class cl ON cl.oid=conrelid +JOIN pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT 'DOMAIN' AS objectkind, c.oid, conname as name, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc + FROM pg_constraint c + JOIN pg_type t ON t.oid=contypid + JOIN pg_namespace nl ON nl.oid=typnamespace + LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) + WHERE contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql new file mode 100644 index 0000000..d574430 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql @@ -0,0 +1,4 @@ +{% if data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql new file mode 100644 index 0000000..9c2c5ef --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql @@ -0,0 +1,5 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }} ) + {% if data.convalidated %} NOT VALID {% endif %} {% if data.connoinherit %} NO INHERIT {% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..1fa5bc4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql new file mode 100644 index 0000000..440c854 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql @@ -0,0 +1,7 @@ +SELECT d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..613d0f2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql @@ -0,0 +1,4 @@ +SELECT oid, conname as name + FROM pg_constraint +WHERE contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..cb5c9ac --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql @@ -0,0 +1,18 @@ +SELECT 'TABLE' AS objectkind, c.oid, conname as name, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc, connoinherit, convalidated +FROM pg_constraint c +JOIN pg_class cl ON cl.oid=conrelid +JOIN pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT 'DOMAIN' AS objectkind, c.oid, conname as name, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc +, connoinherit, convalidated FROM pg_constraint c + JOIN pg_type t ON t.oid=contypid + JOIN pg_namespace nl ON nl.oid=typnamespace + LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) + WHERE contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql new file mode 100644 index 0000000..d574430 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql @@ -0,0 +1,4 @@ +{% if data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png new file mode 100644 index 0000000000000000000000000000000000000000..55621528a1dba4928538fe5557b9b988ed78d6ab GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}U4T!BE0BJeoqfW=;gF{0PD8_o zi&pN_(K)B7`FPi%2ix`@v9LU(tNUQ*!K1czXOxuggoGT_(#p@zf4Jw!!zHV)`3F7T zd-Um}H}`iRIijcc@Wq#>&ptkR`SszG54Uz6zW4mg?MLsgZ9jbb>E~y!zTSB9`Re1( zjS1S99(_9h@YC4`A5Y)^_^>@J3+MvIk|4ie28U-i(tsS!0*}aIAngIhZYQ(tfQ)ue z7sn8Z%b|U@#hMgESUvs4wKoSxO>~{O=-Yq$8_!r)-^e%4-l6f-Jox1%8}FIVx>FCW zPMVwluQGbkP1bcSwVV0d_?PZbZVis%HeYTWyU*$OvYp$uTc3QLbD?2trOnDuhZa>f z=3d_{(zER9xt(<n3UsBI4SNC?Y!rBW<m2`n>yDRyg$uoE8Qx}bo1D0qw;t#u)e_f; zl9a@fRIB8oR3OD*WMF8ZYiOivU>IU#U}a)#Wn!XjU}|MxU@=ow4n;$5eoAIqB}9XP eC0GMUwUvPxM8m1+p=*E|7(8A5T-G@yGywoRCC2Lj literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png new file mode 100644 index 0000000000000000000000000000000000000000..7521cddeaaaf0ee4e3c60e948078d70e17e06893 GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}T7XZ8E0Deu9eu*V;gF{0K`pIg zc6K`r4IeIAc}Q3HxV`=3y+@xudUJpGkt2F~PoI5!^78A$Cm(L@JbcYR=;4bm_nv>b z{pkI*?T7D#gyiSvKYR7{_S4Tdo_xOg_;X`|_N7Ok&OiKg_QA)~_dlLo85{w$iLoTe zFPOpM*^M+HhqJ&VvKUBvfU(=jY&#$$$<xI#MB;Mq`IABp20RT9wUXkaL(1R(pOn?c z>38<+e>KaN2{%0Jd@sMbY$+13Z&%Z<%!P+*SNu+#({Rr={_KxUhswHdID0QWTCBvD z)^ky;@gu`E#RAC#l`JcnelS0rxkKN7b1B>H56gX)0&P<*ag8WRNi0dVN-jzTQVd20 zh6cKZM!E)uAw~vPCdO7KCfWw3Rt5$ZGgakKH00)|WTsU@G#FTdHGouG8JIydoSGiG Q2B?9-)78&qol`;+03_q3ga7~l literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png new file mode 100644 index 0000000000000000000000000000000000000000..42ca929325854b8f34787425e8094d08c75983bc GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}PJmB{E0BJeoqfW=;gF{0PD8^7 zi&s6`bL8oxH}`iRdHU?*lb2r~J^6Th=ix^$zTSWS<?f>oH+CHUa^}pVf`aWvMt7fm zx&8F>jfd~AY(HF`mUiRG=W9<sUwQB@KR>@QLHo+%PZuA3I{)y~*$1D_JotF({zvHt zo%ulf7)yfuf*Bm1-ADs+I14-?i-EKU7`vU!wgWPXJzX3_Brcbpe=XFcAmSFNeC3q& zl!Z+(RsYN12&-NW{P*^#<rx82_2-+ii&SqfGgO`Jcj@;01@HTH{&&7#v?j}I)=J6m ze`~hppZn7?WA|%z!}T6pf>z0IjbZrS5ICD*275*_bAo)r8t#Vg4A*Lz);xc4JKq03 zXXC}zd)YwiRZCnWN>UO_QmvAUQh^kMk%6IsuAz~xfnkV|ft87|m5GVAfvJ^&fyGQ! qITQ`K`6-!cl@JXEmS7Da)m8>(5DllMhpqu?VDNPHb6Mw<&;$VOR=Fqu literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css new file mode 100644 index 0000000..ab9e776 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css @@ -0,0 +1,8 @@ +.pg-icon-domain { + background-image: url('{{ url_for('NODE-domain.static', filename='img/domain.png') }}') !important; + border-radius: 10px; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js new file mode 100644 index 0000000..89bb21b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js @@ -0,0 +1,256 @@ +/* Create and Register Domain Collection and Domain Node. */ + +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + if (!pgBrowser.Nodes['coll-domain']) { + var databases = pgAdmin.Browser.Nodes['coll-domain'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain', + label: '{{ _('Domains') }}', + type: 'coll-domain' + }); + }; + + var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: null, + security_label: null + }, + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text' + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text' + }], + validate: function() { + var err = {}, + errmsg = null, + data = this.toJSON(); + + if (_.isUndefined(data.label) || + _.isNull(data.label) || + String(data.label).replace(/^\s+|\s+$/g, '') == '') { + return _("Please specify the value for all the security providers."); + } + return null; + } + }); + + var ConstraintsModel = pgAdmin.Browser.Node.Model.extend({ + idAttribute: 'conname', + defaults: { + conname: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined + }, + schema: [ + {id: 'conname', label:'Name', type:'text', cell:'string'}, + {id: 'description', label:'Comment', type: 'multiline', cell: 'string'}, + {id: 'consrc', label:'Check', type: 'multiline', cel: 'string', group: 'definition'}, + {id: 'connoinherit', label:'No Inherit', type: 'switch', cell: 'boolean', group: 'definition'}, + {id: 'convalidated', label:"Don't Validate", type: 'switch', cell: 'boolean', group: 'definition'} + ], + validate: function() { + // TODO: Add validation here + }, + toJSON: Backbone.Model.prototype.toJSON + }); + + + if (!pgBrowser.Nodes['domain']) { + pgAdmin.Browser.Nodes['domain'] = pgBrowser.Node.extend({ + type: 'domain', + label: '{{ _('Domain') }}', + collection_type: 'coll-domain', + hasSQL: true, + parent_type: ['schema'], + Init: function() { + /* Avoid mulitple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + canDropCascade: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + oid: undefined, + owner: undefined, + basensp: undefined, + description: undefined, + basetype: undefined, + typlen: undefined, + precision: undefined, + typdefault: undefined, + typnotnull: undefined, + collname: undefined, + constraints: [], + seclabels: [] + }, + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'] + },{ + id: 'oid', label:'{{ _('Oid') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl, + node: 'role', type: 'text', mode: ['edit', 'create', 'properties'] + },{ + id: 'basensp', label:'{{ _('Schema') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', url: 'get_schemas' + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'basetype', label:'{{ _('Base Type') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', mode:['properties', 'create', 'edit'], group: 'Definition', url: 'get_types', + disabled: function(m) { return !m.isNew(); } + },{ + id: 'typlen', label:'{{ _('Length') }}', cell: 'string', + type: 'text', group: 'Definition', disabled: function(m) { return !m.isNew(); } + },{ + id: 'precision', label:'{{ _('Precision') }}', cell: 'string', + type: 'text', group: 'Definition', disabled: function(m) { return !m.isNew(); } + },{ + id: 'typdefault', label:'{{ _('Default') }}', cell: 'string', + type: 'text', group: 'Definition' + },{ + id: 'typnotnull', label:'{{ _('Not Null') }}', cell: 'boolean', + type: 'switch', group: 'Definition' + },{ + id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', group: 'Definition', url: 'get_collations', disabled: function(m) { + return !m.isNew(); + } + },{ + id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string', + type: 'collection', group: 'Constraints', visible: false, mode: ['edit', 'create'], + model: ConstraintsModel, canAdd: true, canDelete: true, canEdit: function(o) { + if (o instanceof Backbone.Model) { + if (o instanceof ConstraintsModel) { + return o.isNew(); + } + } + return true; + } + },{ + id: 'seclabels', label: '{{ _('Security Labels') }}', + model: SecurityModel, type: 'collection', + group: '{{ _('Security') }}', mode: ['edit', 'create'], + min_version: 90100, canAdd: true, + canEdit: true, canDelete: true + } + ], + validate: function() + { + var err = {}, + errmsg, + seclabels = this.get('seclabels'); + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('basetype')) || String(this.get('basetype')).replace(/^\s+|\s+$/g, '') == '') { + err['basetype'] = '{{ _('Base Type can not be empty!') }}'; + errmsg = errmsg || err['basetype']; + } + + if (seclabels) { + var secLabelsErr; + for (var i = 0; i < seclabels.models.length && !secLabelsErr; i++) { + secLabelsErr = (seclabels.models[i]).validate.apply(seclabels.models[i]); + if (secLabelsErr) { + err['seclabels'] = secLabelsErr; + errmsg = errmsg || secLabelsErr; + } + } + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + } + }), + canCreate: function(itemData, item, data) { + //If check is false then , we will allow create menu + if (data && data.check == false) + return true; + + var t = pgBrowser.tree, i = item, d = itemData; + // To iterate over tree to check parent node + while (i) { + // If it is schema then allow user to create domain + if (_.indexOf(['schema'], d._type) > -1) + return true; + + if ('coll-domain' == d._type) { + //Check if we are not child of catalog + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + if( prev_d._type == 'catalog') { + return false; + } else { + return true; + } + } + i = t.hasParent(i) ? t.parent(i) : null; + d = i ? t.itemData(i) : null; + } + // by default we do not want to allow create menu + return true; + }, + isDisabled: function(m){ + console.log("hee"); + console.log(m.isNew()); + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return false; + } + } + return true; + } + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql new file mode 100644 index 0000000..ca250ab --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql @@ -0,0 +1,20 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM + pg_namespace nsp +WHERE + nsp.oid={{scid}}::int + AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + ) + AND + nspname NOT LIKE E'pg\\temp\\%' + AND + nspname NOT LIKE E'pg\\toast_temp\\%' diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql new file mode 100644 index 0000000..06daeb9 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql @@ -0,0 +1,38 @@ +{% import 'macros/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }} +{% if data.collname %} + COLLATE {{ data.collname and data.collname != "pg_catalog.\"default\"" }} +{% endif %} +{% if data.typdefault %} + DEFAULT {{ data.typdefault }} +{% endif %} +{% if data.typnotnull == 'true' %} + NOT NULL +{% endif %}; + +{% if data.constraints %} +{% for c in data.constraints %} +{% if c.conname and c.consrc %}ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} ); +{% endif %} +{% endfor %} +{% endif %} + +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }}; + +{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}'; +{% endif %} + +{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} +{{ SECLABLE.APPLY(conn, 'DOMAIN', data.name, r.provider, r.security_label) }} +{% endif %} +{% endfor %} +{% endif %} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..137db11 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql @@ -0,0 +1,15 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid + AND d.oid={{doid}}::int; +{% endif %} + +{% if name %} +DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade%} CASCADE{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql new file mode 100644 index 0000000..cb490f5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql @@ -0,0 +1,7 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM pg_collation c, pg_namespace n +WHERE c.collnamespace=n.oid +ORDER BY nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql new file mode 100644 index 0000000..471ff08 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql @@ -0,0 +1,18 @@ +SELECT + 'TABLE' AS objectkind, c.oid, conname, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc +FROM pg_constraint c +JOIN pg_class cl ON cl.oid=conrelid +JOIN pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT + 'DOMAIN' AS objectkind, c.oid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as cons +FROM pg_constraint c +JOIN pg_type t ON t.oid=contypid +JOIN pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE contype = 'c' AND contypid = {{doid}}::oid +ORDER BY conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..828e35d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql @@ -0,0 +1,9 @@ +SELECT + d.oid, d.typname as name +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql new file mode 100644 index 0000000..386c803 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql @@ -0,0 +1,21 @@ +SELECT + nsp.nspname +FROM + pg_namespace nsp + LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) + LEFT OUTER JOIN pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid) +WHERE + NOT ((nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + )AND + nspname NOT LIKE E'pg\\temp\\%' AND + nspname NOT LIKE E'pg\\toast_temp\\%' AND + -- ADDED: Because We need to omit system schema except the one on which we are trying to create collation + ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' ) +ORDER BY nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql new file mode 100644 index 0000000..0ecefd7 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql @@ -0,0 +1,9 @@ +SELECT * FROM +(SELECT format_type(t.oid,NULL) AS typname, CASE WHEN typelem > 0 THEN typelem ELSE t.oid END as elemoid, typlen, typtype, t.oid, nspname, +(SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup +FROM pg_type t +JOIN pg_namespace nsp ON typnamespace=nsp.oid +WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'e', 'r')AND NOT EXISTS +(select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS +(select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) +) AS dummy ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..e8b71a5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql @@ -0,0 +1,19 @@ +SELECT d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typlen, d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup +FROM pg_type d + JOIN pg_type b ON b.oid = d.typbasetype + JOIN pg_namespace bn ON bn.oid=d.typnamespace + LEFT OUTER JOIN pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) + LEFT OUTER JOIN pg_collation c ON d.typcollation=c.oid + LEFT OUTER JOIN pg_namespace cn ON c.collnamespace=cn.oid +WHERE d.typnamespace = {{scid}}::oid +{% if doid %} + AND d.oid={{doid}}::int +{% endif %} +ORDER BY d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql new file mode 100644 index 0000000..602b2bf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql @@ -0,0 +1,71 @@ +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER TYPE {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} +{% endif %} + +{% if data.typnotnull and not o_data.typnotnull %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif not data.typnotnull and o_data.typnotnull%} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif %} + +{% if data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault|qtLiteral }}; +{% elif not data.typdefault and o_data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif %} +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }}; +{% endif %} + +{% if data.constraints %} +{% for c in data.constraints.deleted %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }} +{% endfor %} +{% for c in data.constraints.added %} +{% if c.conname and c.consrc %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} ); +{% endif %} +{% endfor %} +{% endif -%} + +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.DROP(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }} +{% endfor %} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.APPLY(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }} +{% endfor %} +{% endif -%} + +{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }}; +{% endif %} + +{% if data.basensp %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }}; +{% endif %} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/backend_support.sql new file mode 100644 index 0000000..ca250ab --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/backend_support.sql @@ -0,0 +1,20 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM + pg_namespace nsp +WHERE + nsp.oid={{scid}}::int + AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + ) + AND + nspname NOT LIKE E'pg\\temp\\%' + AND + nspname NOT LIKE E'pg\\toast_temp\\%' diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql new file mode 100644 index 0000000..44b6839 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql @@ -0,0 +1,40 @@ +{% import 'macros/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }} +{% if data.collname and data.collname != "pg_catalog.\"default\"" %} + COLLATE {{ data.collname }} +{% endif %} +{% if data.typdefault %} + DEFAULT {{ data.typdefault }} +{% endif %} +{% if data.typnotnull == 'true' %} + NOT NULL +{% endif %}; + +{% if data.constraints %} +{% for c in data.constraints %} +{% if c.conname and c.consrc %}ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} ) +{% if c.convalidated %} + NOT VALID {% endif %} {% if c.connoinherit %} NO INHERIT{% endif %}; +{% endif %} +{% endfor %} +{% endif %} + +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }}; + +{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}'; +{% endif %} + +{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} +{{ SECLABLE.APPLY(conn, 'DOMAIN', data.name, r.provider, r.security_label) }} +{% endif %} +{% endfor %} +{% endif %} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..8226003 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql @@ -0,0 +1,11 @@ +{% if scid and doid %} +SELECT d.typname as name, bn.nspname as basensp +FROM pg_type d +JOIN pg_namespace bn ON bn.oid=d.typnamespace +WHERE d.typnamespace = {{scid}}::oid +AND d.oid={{doid}}::int; +{% endif %} + +{% if name %} +DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade%} CASCADE{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql new file mode 100644 index 0000000..6a13a65 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql @@ -0,0 +1,7 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM pg_collation c, pg_namespace n +WHERE c.collnamespace=n.oid +ORDER BY nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql new file mode 100644 index 0000000..e2be522 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql @@ -0,0 +1,17 @@ +SELECT + 'TABLE' AS objectkind, c.oid, conname, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc, connoinherit, convalidated FROM pg_constraint c +JOIN pg_class cl ON cl.oid=conrelid +JOIN pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT + 'DOMAIN' AS objectkind, c.oid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc, connoinherit, convalidated +FROM pg_constraint c +JOIN pg_type t ON t.oid=contypid +JOIN pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE contype = 'c' AND contypid = {{doid}}::oid +ORDER BY conname diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..0c1aace --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql @@ -0,0 +1,9 @@ +SELECT + d.oid, d.typname as name +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_schemas.sql new file mode 100644 index 0000000..386c803 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_schemas.sql @@ -0,0 +1,21 @@ +SELECT + nsp.nspname +FROM + pg_namespace nsp + LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) + LEFT OUTER JOIN pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid) +WHERE + NOT ((nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + )AND + nspname NOT LIKE E'pg\\temp\\%' AND + nspname NOT LIKE E'pg\\toast_temp\\%' AND + -- ADDED: Because We need to omit system schema except the one on which we are trying to create collation + ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' ) +ORDER BY nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_types.sql new file mode 100644 index 0000000..0ecefd7 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_types.sql @@ -0,0 +1,9 @@ +SELECT * FROM +(SELECT format_type(t.oid,NULL) AS typname, CASE WHEN typelem > 0 THEN typelem ELSE t.oid END as elemoid, typlen, typtype, t.oid, nspname, +(SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup +FROM pg_type t +JOIN pg_namespace nsp ON typnamespace=nsp.oid +WHERE (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) AND typisdefined AND typtype IN ('b', 'c', 'e', 'r')AND NOT EXISTS +(select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS +(select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) +) AS dummy ORDER BY nspname <> 'pg_catalog', nspname <> 'public', nspname, 1 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..24ace08 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql @@ -0,0 +1,20 @@ +SELECT d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typlen, d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT array_agg(provider || '=' || label) FROM pg_shseclabel sl1 WHERE sl1.objoid=d.oid) AS seclabels +FROM pg_type d + JOIN pg_type b ON b.oid = d.typbasetype + JOIN pg_namespace bn ON bn.oid=d.typnamespace + LEFT OUTER JOIN pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) + LEFT OUTER JOIN pg_collation c ON d.typcollation=c.oid + LEFT OUTER JOIN pg_namespace cn ON c.collnamespace=cn.oid +WHERE d.typnamespace = {{scid}}::oid +{% if doid %} + AND d.oid={{doid}}::int +{% endif %} +ORDER BY d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql new file mode 100644 index 0000000..d40af4c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql @@ -0,0 +1,71 @@ +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} +{% endif %} + +{% if data.typnotnull and not o_data.typnotnull %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif not data.typnotnull and o_data.typnotnull%} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif %} + +{% if data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault|qtLiteral }}; +{% elif not data.typdefault and o_data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif %} + +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }}; +{% endif %} + +{% if data.constraints %} +{% for c in data.constraints.deleted %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }} +{% endfor %} +{% for c in data.constraints.added %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} ) +{% if c.convalidated %} NOT VALID {% endif %} {% if c.connoinherit %} NO INHERIT {% endif %}; +{% endfor %} +{% endif %} + +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.DROP(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }} +{% endfor %} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.APPLY(conn, 'DOMAIN', conn|qtIdent(o_data.basensp, name), r.provider, r.label) }} +{% endfor %} +{% endif %} + +{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }}; +{% endif %} + +{% if data.basensp %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }}; +{% endif %} + +{% endif %} diff --git a/web/pgadmin/browser/static/js/node.ui.js b/web/pgadmin/browser/static/js/node.ui.js index aa7314e..94d5205 100644 --- a/web/pgadmin/browser/static/js/node.ui.js +++ b/web/pgadmin/browser/static/js/node.ui.js @@ -52,7 +52,7 @@ function($, _, pgAdmin, Backbone, Backform, Alertify, Node) { ' <% } %>', ' <% for (var i=0; i < options.length; i++) { %>', ' <% var option = options[i]; %>', - ' <option <% if (option.image) { %> data-image=<%= option.image %> <% } %> value=<%= formatter.fromRaw(option.value) %> <%=option.value === rawValue ? "selected=\'selected\'" : "" %>><%-option.label%></option>', + ' <option <% if (option.image) { %> data-image=<%= option.image %> <% } %> <% if (typeof option.value == "string") { %>value=<%=formatter.fromRaw(option.value.replace(/"/g, """)) %> <% }else { %>value=<%=formatter.fromRaw(option.value)%> <% } %><%=option.value === rawValue ? "selected=\'selected\'" : "" %>><%-option.label%></option>', ' <% } %>', ' </select>', '</div>'].join("\n")), @@ -116,7 +116,6 @@ function($, _, pgAdmin, Backbone, Backform, Alertify, Node) { // // It is feasible that the data may not have been fetched. data = (data && data.data) || []; - /* * Transform the data */ @@ -129,6 +128,7 @@ function($, _, pgAdmin, Backbone, Backform, Alertify, Node) { } else { self.field.set('options', data); } + } }, render: function() { diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js index 8837fa9..be7d177 100644 --- a/web/pgadmin/static/js/backform.pgadmin.js +++ b/web/pgadmin/static/js/backform.pgadmin.js @@ -1000,12 +1000,14 @@ // Insert Edit Cell into Grid if (data.disabled == false && data.canEdit) { var editCell = Backgrid.Extension.ObjectCell.extend({ - schema: gridSchema.schema - }); + schema: gridSchema.schema + }), + canEdit = self.field.has('canEdit') && + self.field.get('canEdit') || true; gridSchema.columns.unshift({ name: "pg-backform-edit", label: "", cell : editCell, - cell_priority: -2 + cell_priority: -2, editable: canEdit }); } @@ -1044,8 +1046,9 @@ grid.insertRow({}); var newRow = $(grid.body.rows[collection.length - 1].$el); newRow.attr("class", "new").click(function(e) { - $(this).attr("class", ""); + $(this).attr("class", "editable"); }); + $(newRow).pgMakeVisible('backform-tab'); return false; }); ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-02-03 10:52 Neel Patel <[email protected]> parent: Khushboo Vashi <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Neel Patel @ 2016-02-03 10:52 UTC (permalink / raw) To: Khushboo Vashi <[email protected]>; +Cc: pgadmin-hackers Hi Khushboo, Please find below review comments. - Reverse engineering SQL generation is not implemented for domain node. - "Length" and "Precision" fields should be enabled/disabled based on the selection of "Base Type" value. - Query is not getting generated properly. Some of the parameters are not reflected in query. As we have provided Length and Precision value in below numeric base type. Also do proper indentation in generated query. * Wrong Query :- * CREATE DOMAIN my_schema.test_123 AS "numeric" DEFAULT 5 ; * Correct Query :- * CREATE DOMAIN my_schema.test_123 AS numeric(22,4) DEFAULT 5 NOT NULL; - After creation of new domain with base type "aclitem" , wrong "Length" field value is getting displayed. - We are getting error saying "*TypeError: the JSON object must be str, not 'dict*'" when we add constraint with "NOT VALID" and NO INHERIT. - We should add property "System Domain?" when we select any domain node. - We think for creation of Security Label, we should include the schema name along with domain name. *Wrong Query :- * SECURITY LABEL FOR pv_label ON DOMAIN test_123 IS 'label_val'; Correct Query :- SECURITY LABEL FOR pv_label ON DOMAIN <schema_name>.test_123 IS 'label_val'; Let us know in case of any issues. Thanks, Neel Patel On Tue, Feb 2, 2016 at 3:51 PM, Khushboo Vashi < [email protected]> wrote: > Hi Neel, > > Thanks for reviewing my patch. > > I have modified the code as per your suggestions and also fixed some of > the issues got while doing unit testing. > Please find attached patch for the same. > > > Thanks, > Khushboo > > > > On Wed, Jan 20, 2016 at 10:56 PM, Neel Patel <[email protected]> > wrote: > >> Hi Khushboo, >> >> Please find below review comments. >> >> - While creating new Domain and clicking on SQL tab, python side we are >> getting error saying "*TypeError: 'bool' object is not callable".* >> We are not able to create any domain. Fix this issue so that we can >> test other functionality. >> - Implement the reverse engineering SQL generation for the domain node. >> - As per the checklist, remove the "Use Slony" from Constraints tab, as >> it is not required. >> - No need to pass "*qtIdent=self.qtIdent*" as function argument in >> "create" and "getSQL" function in domains/__init__.py >> - In "Security" tab , provider and security label fields are not editable. >> - In PG version 9.1, when we update the existing domain name then "ALTER >> DOMAIN" is not supported. >> Currently there is no checking for the PG version 9.1 and 9.2_plus. It >> will fail when we connect to database 9.1 >> >> e.g. >> For PG version 9.1 - Update command should be as below. >> ALTER TYPE xyz RENAME TO abc; >> For PG version 9.2 onwards - Update command should be as below. >> ALTER DOMAIN xyz RENAME TO abc; >> >> - Some of the SQL file, qtIdent is not used. Please check all the related >> SQL files. >> e.g. - In update.sql file "data.owner" should be >> "conn|qtIdent(data.owner)" >> >> {% if data.owner %} >> ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} >> OWNER TO {{ data.owner }}; >> {% endif %} >> >> Let us know for any issues. >> >> Thanks, >> Neel Patel >> >> On Wed, Jan 20, 2016 at 2:50 PM, Khushboo Vashi < >> [email protected]> wrote: >> >>> Hi Neel, >>> >>> Please find updated patch. >>> >>> Thanks, >>> Khushboo >>> >>> On Wed, Jan 20, 2016 at 12:50 PM, Neel Patel < >>> [email protected]> wrote: >>> >>>> Hi Khushboo, >>>> >>>> While applying the patch file, we are getting below warnings. >>>> >>>> ######################################### >>>> domains (1).patch:1340: trailing whitespace. >>>> oid: undefined, >>>> domains (1).patch:1483: trailing whitespace. >>>> (nspname = 'pg_catalog' AND EXISTS >>>> domains (1).patch:1487: trailing whitespace. >>>> OR (nspname = 'information_schema' AND EXISTS >>>> domains (1).patch:1489: trailing whitespace. >>>> OR (nspname LIKE '_%' AND EXISTS >>>> domains (1).patch:1642: trailing whitespace. >>>> (select 1 from pg_class where relnamespace=typnamespace and relname = >>>> typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS >>>> (select 1 from pg_class where relnamespace=typnamespace and relname = >>>> substring(typname from 2)::name and relkind != 'c')) >>>> warning: squelched 4 whitespace errors >>>> warning: 9 lines add whitespace errors. >>>> ######################################### >>>> >>>> Can you please remove the whitespace and regenerate the patch ? >>>> >>>> Thanks, >>>> Neel Patel >>>> >>>> On Wed, Jan 20, 2016 at 12:37 PM, Khushboo Vashi < >>>> [email protected]> wrote: >>>> >>>>> Resending patch with binary option. >>>>> >>>>> On Wed, Jan 20, 2016 at 10:18 AM, Khushboo Vashi < >>>>> [email protected]> wrote: >>>>> >>>>>> Hi, >>>>>> >>>>>> Please find attached patch for the Domain Module. >>>>>> >>>>>> The patch will be modified after Types module implementation as we >>>>>> need to populate Base Type and some Type related validations from the >>>>>> Types module. >>>>>> >>>>>> Please review it and let me know the feedback. >>>>>> >>>>>> Thanks, >>>>>> Khushboo >>>>>> >>>>> >>>>> >>>>> >>>>> -- >>>>> Sent via pgadmin-hackers mailing list ([email protected]) >>>>> To make changes to your subscription: >>>>> http://www.postgresql.org/mailpref/pgadmin-hackers >>>>> >>>>> >>>> >>> >> > ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-02-23 07:07 Khushboo Vashi <[email protected]> parent: Neel Patel <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Khushboo Vashi @ 2016-02-23 07:07 UTC (permalink / raw) To: Neel Patel <[email protected]>; +Cc: pgadmin-hackers Hi, Please find attached Revised patch for the Domain module and also my comments inline as below. Thanks, Khushboo On Wed, Feb 3, 2016 at 4:22 PM, Neel Patel <[email protected]> wrote: > Hi Khushboo, > > Please find below review comments. > > - Reverse engineering SQL generation is not implemented for domain node. > Done > - "Length" and "Precision" fields should be enabled/disabled based on the > selection of "Base Type" value. > This implementation is dependent on the 'Type' module. Once that will be done, I will merge my code. > - Query is not getting generated properly. Some of the parameters are not > reflected in query. As we have provided Length and Precision value in > below numeric base type. Also do proper indentation in generated query. > > * Wrong Query :- * > CREATE DOMAIN my_schema.test_123 > AS "numeric" > DEFAULT 5 > ; > > * Correct Query :- * > CREATE DOMAIN my_schema.test_123 > AS numeric(22,4) > DEFAULT 5 > NOT NULL; > > Done > - After creation of new domain with base type "aclitem" , wrong "Length" > field value is getting displayed. > Done > - We are getting error saying "*TypeError: the JSON object must be str, > not 'dict*'" when we add constraint w > ith "NOT VALID" and NO INHERIT. > Done > - We should add property "System Domain?" when we select any domain node. > Done > - We think for creation of Security Label, we should include the schema > name along with domain name. > *Wrong Query :- * > SECURITY LABEL FOR pv_label ON DOMAIN test_123 IS 'label_val'; > Correct Query :- > SECURITY LABEL FOR pv_label ON DOMAIN <schema_name>.test_123 IS > 'label_val'; > > > Done > Let us know in case of any issues. > > Thanks, > Neel Patel > > On Tue, Feb 2, 2016 at 3:51 PM, Khushboo Vashi < > [email protected]> wrote: > >> Hi Neel, >> >> Thanks for reviewing my patch. >> >> I have modified the code as per your suggestions and also fixed some of >> the issues got while doing unit testing. >> Please find attached patch for the same. >> >> >> Thanks, >> Khushboo >> >> >> >> On Wed, Jan 20, 2016 at 10:56 PM, Neel Patel <[email protected] >> > wrote: >> >>> Hi Khushboo, >>> >>> Please find below review comments. >>> >>> - While creating new Domain and clicking on SQL tab, python side we are >>> getting error saying "*TypeError: 'bool' object is not callable".* >>> We are not able to create any domain. Fix this issue so that we can >>> test other functionality. >>> - Implement the reverse engineering SQL generation for the domain node. >>> - As per the checklist, remove the "Use Slony" from Constraints tab, as >>> it is not required. >>> - No need to pass "*qtIdent=self.qtIdent*" as function argument in >>> "create" and "getSQL" function in domains/__init__.py >>> - In "Security" tab , provider and security label fields are not >>> editable. >>> - In PG version 9.1, when we update the existing domain name then "ALTER >>> DOMAIN" is not supported. >>> Currently there is no checking for the PG version 9.1 and 9.2_plus. It >>> will fail when we connect to database 9.1 >>> >>> e.g. >>> For PG version 9.1 - Update command should be as below. >>> ALTER TYPE xyz RENAME TO abc; >>> For PG version 9.2 onwards - Update command should be as below. >>> ALTER DOMAIN xyz RENAME TO abc; >>> >>> - Some of the SQL file, qtIdent is not used. Please check all the >>> related SQL files. >>> e.g. - In update.sql file "data.owner" should be >>> "conn|qtIdent(data.owner)" >>> >>> {% if data.owner %} >>> ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} >>> OWNER TO {{ data.owner }}; >>> {% endif %} >>> >>> Let us know for any issues. >>> >>> Thanks, >>> Neel Patel >>> >>> On Wed, Jan 20, 2016 at 2:50 PM, Khushboo Vashi < >>> [email protected]> wrote: >>> >>>> Hi Neel, >>>> >>>> Please find updated patch. >>>> >>>> Thanks, >>>> Khushboo >>>> >>>> On Wed, Jan 20, 2016 at 12:50 PM, Neel Patel < >>>> [email protected]> wrote: >>>> >>>>> Hi Khushboo, >>>>> >>>>> While applying the patch file, we are getting below warnings. >>>>> >>>>> ######################################### >>>>> domains (1).patch:1340: trailing whitespace. >>>>> oid: undefined, >>>>> domains (1).patch:1483: trailing whitespace. >>>>> (nspname = 'pg_catalog' AND EXISTS >>>>> domains (1).patch:1487: trailing whitespace. >>>>> OR (nspname = 'information_schema' AND EXISTS >>>>> domains (1).patch:1489: trailing whitespace. >>>>> OR (nspname LIKE '_%' AND EXISTS >>>>> domains (1).patch:1642: trailing whitespace. >>>>> (select 1 from pg_class where relnamespace=typnamespace and relname = >>>>> typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS >>>>> (select 1 from pg_class where relnamespace=typnamespace and relname = >>>>> substring(typname from 2)::name and relkind != 'c')) >>>>> warning: squelched 4 whitespace errors >>>>> warning: 9 lines add whitespace errors. >>>>> ######################################### >>>>> >>>>> Can you please remove the whitespace and regenerate the patch ? >>>>> >>>>> Thanks, >>>>> Neel Patel >>>>> >>>>> On Wed, Jan 20, 2016 at 12:37 PM, Khushboo Vashi < >>>>> [email protected]> wrote: >>>>> >>>>>> Resending patch with binary option. >>>>>> >>>>>> On Wed, Jan 20, 2016 at 10:18 AM, Khushboo Vashi < >>>>>> [email protected]> wrote: >>>>>> >>>>>>> Hi, >>>>>>> >>>>>>> Please find attached patch for the Domain Module. >>>>>>> >>>>>>> The patch will be modified after Types module implementation as we >>>>>>> need to populate Base Type and some Type related validations from the >>>>>>> Types module. >>>>>>> >>>>>>> Please review it and let me know the feedback. >>>>>>> >>>>>>> Thanks, >>>>>>> Khushboo >>>>>>> >>>>>> >>>>>> >>>>>> >>>>>> -- >>>>>> Sent via pgadmin-hackers mailing list ([email protected] >>>>>> ) >>>>>> To make changes to your subscription: >>>>>> http://www.postgresql.org/mailpref/pgadmin-hackers >>>>>> >>>>>> >>>>> >>>> >>> >> > -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers Attachments: [text/x-patch] Domains_ver_2.patch (110.6K, 3-Domains_ver_2.patch) download | inline diff: diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py new file mode 100644 index 0000000..205b591 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py @@ -0,0 +1,805 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""The Domain Module.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas as schemas +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + + +class DomainModule(CollectionNodeModule): + """ + class DomainModule(CollectionNodeModule): + + This class represents The Domain Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Module. + + * get_nodes(gid, sid, did, scid) + - Generate the domain collection node. + + * script_load() + - Load the module script for domain, when schema node is + initialized. + """ + + NODE_TYPE = 'domain' + COLLECTION_LABEL = gettext("Domains") + + def __init__(self, *args, **kwargs): + super(DomainModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid): + """ + Generate the domain collection node. + """ + yield self.generate_browser_collection_node(scid) + + @property + def script_load(self): + """ + Load the module script for domain, when schema node is + initialized. + """ + return schemas.SchemaModule.NODE_TYPE + + +blueprint = DomainModule(__name__) + + +class DomainView(PGChildNodeView): + """ + class DomainView + + This class inherits PGChildNodeView to get the different routes for + the module. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain. + + Methods: + ------- + * validate_request(f): + - Works as a decorator. + Validating request on the request of create, update and modified SQL. + + * module_js(): + - Overrides this property to define javascript for Domain node. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domains. + + * nodes(gid, sid, did, scid): + - Returns all the Domains to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain properties. + + * get_schemas(gid, sid, did, scid, doid=None): + - Returns Schemas for the particular database. + + * get_collations(gid, sid, did, scid, doid=None): + - Returns Collations. + + * get_types(gid, sid, did, scid, doid=None): + - Returns Data Types. + + * create(gid, sid, did, scid): + - Creates a new Domain object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain object. + + * dependents(gid, sid, did, scid, doid): + - Returns the dependents for the Domain object. + + * dependencies(gid, sid, did, scid, doid): + - Returns the dependencies for the Domain object. + """ + + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'doid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_schemas': [{'get': 'get_schemas'}, {'get': 'get_schemas'}], + 'get_collations': [ + {'get': 'get_collations'}, + {'get': 'get_collations'} + ], + 'get_types': [{'get': 'get_types'}, {'get': 'get_types'}] + }) + + def validate_request(f): + """ + Works as a decorator. + Validating request on the request of create, update and modified SQL. + + Required Args: + name: Name of the Domain + owner: Domain Owner + basensp: Schema Name + basttype: Data Type of the Domain + + Above both the arguments will not be validated in the update action. + """ + + @wraps(f) + def wrap(self, **kwargs): + + data = {} + if request.data: + req = json.loads(request.data) + else: + req = request.args or request.form + + if 'doid' not in kwargs: + required_args = [ + 'name', + 'owner', + 'basensp', + 'basetype' + ] + + for arg in required_args: + if arg not in req or req[arg] == '': + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter \ + (%s)." % arg + ) + ) + + try: + list_params = [] + if request.method == 'GET': + list_params = ['constraints', 'seclabels'] + + for key in req: + if key in list_params and req[key] != '' \ + and req[key] is not None: + # Coverts string into python list as expected. + data[key] = json.loads(req[key]) + elif key == 'typnotnull': + data[key] = True if req[key] == 'true' or req[key] is\ + True else\ + (False if req[key] == 'false' or req[key] is + False else '') + else: + data[key] = req[key] + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + self.request = data + return f(self, **kwargs) + + return wrap + + def module_js(self): + """ + Overrides this property to define javascript for Domain node. + """ + return make_response( + render_template( + "domains/js/domains.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + # Get database connection + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domains/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domains/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid): + """ + List the Domains. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid): + """ + Returns all the Domains to generate Nodes in the browser. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + res = [] + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + scid, + row['name'], + icon="icon-domain" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid): + """ + Returns the Domain properties. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + # The Length and the precision of the Datatype should be separate. + # The Format we getting from database is: numeric(1,1) + # So, we need to separate Length: 1, Precision: 1 + if data['fulltype'] != '' and data['fulltype'].find("(") > 0: + substr = data['fulltype'][data['fulltype'].find("(") + 1:len( + data['fulltype']) - 1] + typlen = substr.split(",") + if len(typlen) > 1: + data['typlen'] = typlen[0] + data['precision'] = typlen[1] + else: + data['typlen'] = typlen + data['precision'] = '' + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + # Set System Domain Status + data['sysdomain'] = False + if doid <= self.manager.db_info[did]['datlastsysoid']: + data['sysdomain'] = True + + return ajax_response( + response=data, + status=200 + ) + + @check_precondition + def get_schemas(self, gid, sid, did, scid, doid=None): + """ + Returns Schemas for the particular database. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + res = [{'label': '', 'value': ''}] + try: + SQL = render_template("/".join([self.template_path, + 'get_schemas.sql']), + scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append({'label': row['nspname'], + 'value': row['nspname']}) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def get_collations(self, gid, sid, did, scid, doid=None): + """ + Returns Collations. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + res = [{'label': '', 'value': ''}] + try: + SQL = render_template("/".join([self.template_path, + 'get_collations.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append({'label': row['copy_collation'], + 'value': row['copy_collation']} + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def get_types(self, gid, sid, did, scid, doid=None): + """ + Returns Types. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + # TODO: This function should be removed once + # Types module will be completed. + + res = [{'label': '', 'value': ''}] + try: + SQL = render_template("/".join([self.template_path, + 'get_types.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append( + {'label': row['typname'], + 'value': row['typname']} + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + @validate_request + def create(self, gid, sid, did, scid): + """ + Creates a new Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + Domain object in json format. + """ + + data = self.request + try: + SQL = render_template("/".join([self.template_path, 'create.sql']), + data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # We need oid to to add object in tree at browser, below sql will + # gives the same + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + basensp=data['basensp'], + name=data['name']) + status, doid = self.conn.execute_scalar(SQL) + + if not status: + return internal_server_error(errormsg=doid) + + return jsonify( + node=self.blueprint.generate_browser_node( + doid, + scid, + data['name'], + icon="icon-domain" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid): + """ + Drops the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + name, basensp = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + name=name, basensp=basensp, cascade=cascade) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + @validate_request + def update(self, gid, sid, did, scid, doid): + """ + Updates the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if not status: + return internal_server_error(errormsg=SQL) + + try: + if SQL: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Domain updated", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid=None): + """ + Returns the SQL for the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return False, internal_server_error(errormsg=res) + data = res['rows'][0] + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + # Toggle Validate and inherit options for 'CREATE Query' + for c in data['constraints']: + c['convalidated'] = False if c['convalidated'] else True + c['connoinherit'] = False if c['connoinherit'] else True + + SQL = render_template("/".join([self.template_path, + 'create.sql']), data=data) + + return ajax_response(response=SQL) + + @check_precondition + @validate_request + def msql(self, gid, sid, did, scid, doid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + SQL statements to create/update the Domain. + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid=None): + """ + Generates the SQL statements to create/update the Domain. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + try: + if doid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data) + else: + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data) + return True, SQL + + except Exception as e: + return False, e + + @check_precondition + def dependents(self, gid, sid, did, scid, doid): + """ + This function get the dependents and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependents_result = self.get_dependents(self.conn, doid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid): + """ + This function get the dependencies and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependencies_result = self.get_dependencies(self.conn, doid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py new file mode 100644 index 0000000..370e585 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py @@ -0,0 +1,640 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""The Domain Constraint Module.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas.domains \ + as domains +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + + +class DomainConstraintModule(CollectionNodeModule): + """ + class DomainConstraintModule(CollectionNodeModule): + + This class represents The Domain Constraint Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Constraint Module. + + * get_nodes(gid, sid, did, scid) + - Generate the Domain Constraint collection node. + + * node_inode(gid, sid, did, scid) + - Override this property to make the Domain Constraint node as leaf node. + + * script_load() + - Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + NODE_TYPE = 'domain-constraints' + COLLECTION_LABEL = gettext("Domain Constraints") + + def __init__(self, *args, **kwargs): + super(DomainConstraintModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid, doid): + """ + Generate the Domain Constraint collection node. + """ + yield self.generate_browser_collection_node(doid) + + @property + def node_inode(self): + """ + Override this property to make the node as leaf node. + """ + return False + + @property + def script_load(self): + """ + Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + return domains.DomainModule.NODE_TYPE + + +blueprint = DomainConstraintModule(__name__) + + +class DomainConstraintView(PGChildNodeView): + """ + class DomainConstraintView(PGChildNodeView): + + This class inherits PGChildNodeView to get the different routes for + the module. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain Constraint. + + Methods: + ------- + + * module_js(): + - Overrides this property to define javascript for Domain Constraint node. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domain Constraints. + + * nodes(gid, sid, did, scid): + - Returns all the Domain Constraints to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain Constraint properties. + + * create(gid, sid, did, scid): + - Creates a new Domain Constraint object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain Constraint object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain Constraint object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain Constraint object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain Constraint. + object. + + * dependents(gid, sid, did, scid, doid, coid): + - Returns the dependents for the Domain Constraint object. + + * dependencies(gid, sid, did, scid, doid, coid): + - Returns the dependencies for the Domain Constraint object. + """ + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'}, + {'type': 'int', 'id': 'doid'} + ] + ids = [ + {'type': 'int', 'id': 'coid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def module_js(self): + """ + Overrides this property to define javascript for Domain Constraint node. + """ + return make_response( + render_template( + "domain-constraints/js/domain-constraints.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domain-constraints/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domain-constraints/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid, doid): + """ + List the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid, doid): + """ + Returns all the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + res = [] + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, rset = self.conn.execute_2darray(SQL) + + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + doid, + row['name'], + icon="icon-domain-constraints" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid, coid): + """ + Returns the Domain Constraints property. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + return ajax_response( + response=res['rows'][0], + status=200 + ) + + @check_precondition + def create(self, gid, sid, did, scid, doid): + """ + Creates a new Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Constraints Name + consrc: Constraints Check + + Returns: + Domain Constraint object in json format. + """ + + data = request.form if request.form else \ + json.loads(request.data.decode()) + required_args = [ + 'name', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + try: + # Get Schema and Domain. + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get the recently added constraints oid + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + doid=doid, name=data['name']) + status, coid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=doid) + + return jsonify( + node=self.blueprint.generate_browser_node( + coid, + doid, + data['name'], + icon="icon-domain-constraints" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid, coid): + """ + Drops the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain Constraint dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, scid, doid, coid): + """ + Updates the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + data = request.form if request.form else \ + json.loads(request.data.decode()) + + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + + try: + if SQL and status: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Domain Constraint updated", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the SQL for the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + # Get Schema and Domain. + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + data['convalidated'] = False if data['convalidated'] else True + data['connoinherit'] = False if data['connoinherit'] else True + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + + return ajax_response(response=SQL) + + @check_precondition + def msql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + + Required Args: + name: Constraints Name + consrc: Constraints Check + + Returns: + Domain Constraint object in json format. + """ + data = request.args + + if coid is None: + required_args = [ + 'name', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + if status and SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid, coid=None): + """ + Generates the SQL statements to create/update the Domain Constraint. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + if coid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data, conn=self.conn + ) + else: + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + + if not status: + return False, internal_server_error(errormsg=name) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + return True, SQL + except Exception as e: + return False, internal_server_error(errormsg=str(e)) + + @check_precondition + def dependents(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependents and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependents_result = self.get_dependents(self.conn, coid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependencies and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependencies_result = self.get_dependencies(self.conn, coid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainConstraintView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..d62e13705c50e6c0cf8f19d680053e8643e28751 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=2TrO847{Jl`|t`Qpas zn<hS+l=rMY>RGPqvlydizRJ&>B`Om#V}R-yOM?7@862M7NCR>>3p^r=fwTu0yPeFo z12TL)T^vI=t|uoPU||ZF<tgaHG*QsQ!?m&Tq=?3mCu}J#Dx3x@mM}}^iE=5NIWXnk zkpnC4ai%a>@;Gg7=uums=9bIK=Egd~(us-3g@Iv02gfsK^JP^)gH=mhBT7;dOH!?p zi&B9UgOP!ufv%yEu7P2Qk%5(ov6YF5wt=aYfq}(LRXG$5x%nxXX_XKS29{tAAk|g| XW)KahriZQpYGCkm^>bP0l+XkKyyRU} literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png new file mode 100644 index 0000000000000000000000000000000000000000..32a045b8fafdc08640d53b2a86b1dcabcb0fe0fd GIT binary patch literal 579 zcmV-J0=)f+P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp) z=>Px$AW%$HMR1Z9#Q*@REHmdHAm<<;=p`lTDl6$LE15$|nM6yxJ3#6&F}^)Qx<p3n zH#h4zIK)Ou#79fTM@#HJKkY(8?L$QEL`CjJMeasM?ng(;QB>|oNbX2U?n+AUOH1!e zOz%xi?@dncPEXKRS?^Cz#amtQQBm+xQt(q#@KaRqR8;U)Rq<9<@mE*YVq(`~V)0vB z+GS<)VPW%PV%=$Jag!JHXlV6qZS`($_HuH3pd<EncJ_C7f1@XWqbGu>Du=8uhpjJ) zvN4agH<i3UrMYgWyK<eyN1Vq+p2tbA!G5C3PO!m$q|HvV#D$~JOsv;ftk_qv-CeQX zT*cC%$J3;><6_L$t<Kr8)!w|r@o>cPam4X*+~mi`^K{Glc|xh<NdN!<0d!JMQvg8b z*k%9#00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipf34<ROg z(p72z0056kL_t&-)1{2Z3c^qT1mDEody6IZuCW&s1V!`^4}$goe?(18@b2Db*j*w1 z%4M(p;&Zw?9ZaKAxo*x;COX}<8fzjqKRv^2kF1spymYHMjE2m7Hl|a~emCNwG8(i? z8I#``(kiBrEbh}(Qn8TL2+$}b3Hn^7p`K6R#_6zQ2$?ux;lXBYB>hkN@C%g?4vVBM R$bbL<002ovPDHLkV1min0OSAw literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..9d1d2a061c7948168d7b1c2474d769b31709f1cf GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE08{VY2nhHc^eMaU%j`d zaI$#+HuEKC{pKEZKYn>h(+aIMH^MjGjcs3}f9Cqyt&fvx7AT*)xv^`L;hf{Hi_iP4 zxgK%&V?q65_4c*;8}G%;JMMY<SLM___M4Bi9{E^!<YUpX&n1ga`7PgFwEkdS!(#P< zNn&@N9OqiK(i&(nV@Z%-FoVOh8)-leXMsm#F_88EW4Dvpc0fjir;B5V#O36K1sn!O zhRVf}5jSsGPH?f<xudc|vBoY<&5cb=uTGB9v187J4II+it5?jhn8F{TsCZIWRad$D zg1*K9W%cu2NsD(hEfVT#*wm#pYw;D04+0E(uQE?s`Z6*ZXoqTvYeY#(Vo9o1a#1Rf zVlXl=G|)9P(lsy)F*2|+F}5->(Kax(GBB{1sVaw}AvZrIGp!P$!N3x%0i@c>zzm|{ T)b!9bKn)C@u6{1-oD!M<=4O^k literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css new file mode 100644 index 0000000..57b6612 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css @@ -0,0 +1,8 @@ +.pg-icon-domain-constraints { + background-image: url('{{ url_for('NODE-domain-constraints.static', filename='img/domain-constraints.png') }}') !important; + border-radius: 10px; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js new file mode 100644 index 0000000..d9e23a8 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js @@ -0,0 +1,129 @@ +// Domain Constraint Module: Collection and Node +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Constraint Collection Node + if (!pgBrowser.Nodes['coll-domain-constraints']) { + var domain_constraints = pgAdmin.Browser.Nodes['coll-domain-constraints'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + type: 'coll-domain-constraints' + }); + }; + + // Domain Constraint Node + if (!pgBrowser.Nodes['domain-constraints']) { + pgAdmin.Browser.Nodes['domain-constraints'] = pgBrowser.Node.extend({ + type: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + collection_type: 'coll-domain-constraints', + hasSQL: true, + hasDepends: true, + parent_type: ['domain'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined + }, + // Domain Constraint Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', type:'text', cell:'string', + disabled: 'isDisabled' + },{ + id: 'description', label: '{{ _('Comment') }}', type: 'multiline', cell: + 'string', mode: ['properties', 'create', 'edit'], disabled: function + (m) { return !m.isNew(); } + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', cel: + 'string', group: '{{ _('Definition') }}', mode: ['properties', + 'create', 'edit'], disabled: function(m) { return !m.isNew(); } + },{ + id: 'connoinherit', label: '{{ _('No Inherit') }}', type: + 'switch', cell: 'boolean', group: '{{ _('Definition') }}', mode: + ['properties', 'create', 'edit'], disabled: 'isDisabled' + },{ + id: 'convalidated', label: "Don't Validate", type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', disabled: 'isDisabled', + mode: ['create', 'edit'] + },{ + id: 'convalidated', label: '{{ _('Valid?') }}', type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', disabled: 'isDisabled', + mode: ['properties'] + }], + // Client Side Validation + validate: function() { + var err = {}, + errmsg; + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') { + err['consrc'] = '{{ _('Check can not be empty!') }}'; + errmsg = errmsg || err['consrc']; + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return true; + } + } + return false; + } + }), + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql new file mode 100644 index 0000000..22bad72 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql @@ -0,0 +1,4 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }}); +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..95b7310 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql @@ -0,0 +1,30 @@ +SELECT + 'TABLE' AS objectkind, c.oid, conname as name, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc +FROM + pg_constraint c +JOIN + pg_class cl ON cl.oid=conrelid +JOIN + pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT + 'DOMAIN' AS objectkind, c.oid, conname as name, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql new file mode 100644 index 0000000..d574430 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql @@ -0,0 +1,4 @@ +{% if data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql new file mode 100644 index 0000000..c50e486 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql @@ -0,0 +1,8 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }}){% if data.convalidated %} + + NOT VALID{% endif %}{% if data.connoinherit %} + + NO INHERIT{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..80b7ced --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql @@ -0,0 +1,29 @@ +SELECT + 'TABLE' AS objectkind, c.oid, conname as name, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc, connoinherit, convalidated +FROM + pg_constraint c +JOIN + pg_class cl ON cl.oid=conrelid +JOIN + pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT + 'DOMAIN' AS objectkind, c.oid, conname as name, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', + E'\\1') as consrc, connoinherit, convalidated FROM pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql new file mode 100644 index 0000000..d574430 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql @@ -0,0 +1,4 @@ +{% if data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png new file mode 100644 index 0000000000000000000000000000000000000000..55621528a1dba4928538fe5557b9b988ed78d6ab GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}U4T!BE0BJeoqfW=;gF{0PD8_o zi&pN_(K)B7`FPi%2ix`@v9LU(tNUQ*!K1czXOxuggoGT_(#p@zf4Jw!!zHV)`3F7T zd-Um}H}`iRIijcc@Wq#>&ptkR`SszG54Uz6zW4mg?MLsgZ9jbb>E~y!zTSB9`Re1( zjS1S99(_9h@YC4`A5Y)^_^>@J3+MvIk|4ie28U-i(tsS!0*}aIAngIhZYQ(tfQ)ue z7sn8Z%b|U@#hMgESUvs4wKoSxO>~{O=-Yq$8_!r)-^e%4-l6f-Jox1%8}FIVx>FCW zPMVwluQGbkP1bcSwVV0d_?PZbZVis%HeYTWyU*$OvYp$uTc3QLbD?2trOnDuhZa>f z=3d_{(zER9xt(<n3UsBI4SNC?Y!rBW<m2`n>yDRyg$uoE8Qx}bo1D0qw;t#u)e_f; zl9a@fRIB8oR3OD*WMF8ZYiOivU>IU#U}a)#Wn!XjU}|MxU@=ow4n;$5eoAIqB}9XP eC0GMUwUvPxM8m1+p=*E|7(8A5T-G@yGywoRCC2Lj literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png new file mode 100644 index 0000000000000000000000000000000000000000..7521cddeaaaf0ee4e3c60e948078d70e17e06893 GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}T7XZ8E0Deu9eu*V;gF{0K`pIg zc6K`r4IeIAc}Q3HxV`=3y+@xudUJpGkt2F~PoI5!^78A$Cm(L@JbcYR=;4bm_nv>b z{pkI*?T7D#gyiSvKYR7{_S4Tdo_xOg_;X`|_N7Ok&OiKg_QA)~_dlLo85{w$iLoTe zFPOpM*^M+HhqJ&VvKUBvfU(=jY&#$$$<xI#MB;Mq`IABp20RT9wUXkaL(1R(pOn?c z>38<+e>KaN2{%0Jd@sMbY$+13Z&%Z<%!P+*SNu+#({Rr={_KxUhswHdID0QWTCBvD z)^ky;@gu`E#RAC#l`JcnelS0rxkKN7b1B>H56gX)0&P<*ag8WRNi0dVN-jzTQVd20 zh6cKZM!E)uAw~vPCdO7KCfWw3Rt5$ZGgakKH00)|WTsU@G#FTdHGouG8JIydoSGiG Q2B?9-)78&qol`;+03_q3ga7~l literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png new file mode 100644 index 0000000000000000000000000000000000000000..42ca929325854b8f34787425e8094d08c75983bc GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}PJmB{E0BJeoqfW=;gF{0PD8^7 zi&s6`bL8oxH}`iRdHU?*lb2r~J^6Th=ix^$zTSWS<?f>oH+CHUa^}pVf`aWvMt7fm zx&8F>jfd~AY(HF`mUiRG=W9<sUwQB@KR>@QLHo+%PZuA3I{)y~*$1D_JotF({zvHt zo%ulf7)yfuf*Bm1-ADs+I14-?i-EKU7`vU!wgWPXJzX3_Brcbpe=XFcAmSFNeC3q& zl!Z+(RsYN12&-NW{P*^#<rx82_2-+ii&SqfGgO`Jcj@;01@HTH{&&7#v?j}I)=J6m ze`~hppZn7?WA|%z!}T6pf>z0IjbZrS5ICD*275*_bAo)r8t#Vg4A*Lz);xc4JKq03 zXXC}zd)YwiRZCnWN>UO_QmvAUQh^kMk%6IsuAz~xfnkV|ft87|m5GVAfvJ^&fyGQ! qITQ`K`6-!cl@JXEmS7Da)m8>(5DllMhpqu?VDNPHb6Mw<&;$VOR=Fqu literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css new file mode 100644 index 0000000..ab9e776 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css @@ -0,0 +1,8 @@ +.pg-icon-domain { + background-image: url('{{ url_for('NODE-domain.static', filename='img/domain.png') }}') !important; + border-radius: 10px; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js new file mode 100644 index 0000000..e2d1b01 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js @@ -0,0 +1,277 @@ +// Domain Module: Collection and Node. +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Collection Node + if (!pgBrowser.Nodes['coll-domain']) { + var domains = pgAdmin.Browser.Nodes['coll-domain'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain', + label: '{{ _('Domains') }}', + type: 'coll-domain', + columns: ['name', 'oid', 'owner', 'basensp', 'description', 'basetype', + 'typdefault', 'typnotnull', 'sysdomain'] + }); + }; + + // Security Model + var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: null, + security_label: null + }, + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text' + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text' + }], + validate: function() { + var err = {}, + errmsg = null, + data = this.toJSON(); + + if (_.isUndefined(data.label) || + _.isNull(data.label) || + String(data.label).replace(/^\s+|\s+$/g, '') == '') { + return _("Please specify the value for all the security providers."); + } + return null; + } + }); + + // Constraint Model + var ConstraintsModel = pgAdmin.Browser.Node.Model.extend({ + idAttribute: 'conname', + defaults: { + conname: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined + }, + schema: [{ + id: 'conname', label: '{{ _('Name') }}', type: 'text', cell: 'string' + },{ + id: 'description', label: '{{ _('Comment') }}', type: 'multiline', cell: 'string' + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', + cell: 'string', group: '{{ _('Definition') }}' + },{ + id: 'connoinherit', label: '{{ _('No Inherit') }}', type: 'switch', + cell:'boolean', group: '{{ _('Definition') }}' + },{ + id: 'convalidated', label: '{{ _('Valid?') }}', type: + 'switch', cell: 'boolean', group: '{{ _('Definition') }}', mode: + ['properties'] + },{ + id: 'convalidated', label: "Don't Validate", type: + 'switch', cell: 'boolean', group: '{{ _('Definition') }}', mode: + ['create', 'edit'] + }], + validate: function() { + // TODO: Add validation here + }, + toJSON: Backbone.Model.prototype.toJSON + }); + + // Domain Node + if (!pgBrowser.Nodes['domain']) { + pgAdmin.Browser.Nodes['domain'] = pgBrowser.Node.extend({ + type: 'domain', + label: '{{ _('Domain') }}', + collection_type: 'coll-domain', + hasSQL: true, + hasDepends: true, + parent_type: ['schema'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + canDropCascade: pgBrowser.Nodes['schema'].canChildDrop, + // Domain Node Model + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + oid: undefined, + owner: undefined, + basensp: undefined, + description: undefined, + basetype: undefined, + typlen: undefined, + precision: undefined, + typdefault: undefined, + typnotnull: undefined, + sysdomain: undefined, + collname: undefined, + constraints: [], + seclabels: [] + }, + // Domain Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'] + },{ + id: 'oid', label:'{{ _('Oid') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl, + node: 'role', type: 'text', mode: ['edit', 'create', 'properties'] + },{ + id: 'basensp', label:'{{ _('Schema') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', url: 'get_schemas' + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'basetype', label:'{{ _('Base Type') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', mode:['properties', 'create', 'edit'], group: '{{ _('Definition') }}', url: 'get_types', + disabled: function(m) { return !m.isNew(); } + },{ + id: 'typlen', label:'{{ _('Length') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', disabled: function(m) { return !m.isNew(); } + },{ + id: 'precision', label:'{{ _('Precision') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', disabled: function(m) { return !m.isNew(); } + },{ + id: 'typdefault', label:'{{ _('Default') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}' + },{ + id: 'typnotnull', label:'{{ _('Not Null') }}', cell: 'boolean', + type: 'switch', group: '{{ _('Definition') }}' + },{ + id: 'sysdomain', label:'{{ _('System Domain?') }}', cell: 'boolean', + type: 'switch', group: '{{ _('Definition') }}', mode: ['properties'] + },{ + id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', group: '{{ _('Definition') }}', url: 'get_collations', disabled: function(m) { + return !m.isNew(); + } + },{ + id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string', + type: 'collection', group: '{{ _('Constraints') }}', visible: false, mode: ['edit', 'create'], + model: ConstraintsModel, canAdd: true, canDelete: true, canEdit: function(o) { + if (o instanceof Backbone.Model) { + if (o instanceof ConstraintsModel) { + return o.isNew(); + } + } + return true; + } + },{ + id: 'seclabels', label: '{{ _('Security Labels') }}', + model: SecurityModel, type: 'collection', + group: '{{ _('Security') }}', mode: ['edit', 'create'], + min_version: 90100, canAdd: true, + canEdit: true, canDelete: true + } + ], + validate: function() // Client Side Validation + { + var err = {}, + errmsg, + seclabels = this.get('seclabels'); + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('basetype')) || String(this.get('basetype')).replace(/^\s+|\s+$/g, '') == '') { + err['basetype'] = '{{ _('Base Type can not be empty!') }}'; + errmsg = errmsg || err['basetype']; + } + + if (seclabels) { + var secLabelsErr; + for (var i = 0; i < seclabels.models.length && !secLabelsErr; i++) { + secLabelsErr = (seclabels.models[i]).validate.apply(seclabels.models[i]); + if (secLabelsErr) { + err['seclabels'] = secLabelsErr; + errmsg = errmsg || secLabelsErr; + } + } + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + } + }), + canCreate: function(itemData, item, data) { + //If check is false then , we will allow create menu + if (data && data.check == false) + return true; + + var t = pgBrowser.tree, i = item, d = itemData; + // To iterate over tree to check parent node + while (i) { + // If it is schema then allow user to create domain + if (_.indexOf(['schema'], d._type) > -1) + return true; + + if ('coll-domain' == d._type) { + //Check if we are not child of catalog + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + if( prev_d._type == 'catalog') { + return false; + } else { + return true; + } + } + i = t.hasParent(i) ? t.parent(i) : null; + d = i ? t.itemData(i) : null; + } + // by default we do not want to allow create menu + return true; + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return false; + } + } + return true; + } + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql new file mode 100644 index 0000000..8f3db8e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql @@ -0,0 +1,20 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM + pg_namespace nsp +WHERE + nsp.oid={{scid}}::int + AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + ) + AND + nspname NOT LIKE E'pg\\temp\\%' + AND + nspname NOT LIKE E'pg\\toast_temp\\%' diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql new file mode 100644 index 0000000..29ae115 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql @@ -0,0 +1,36 @@ +{% import 'macros/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}} {% if data.precision %}, {{data.precision}}{% endif %}){% endif %}{% if data.collname %} + + COLLATE {{ data.collname and data.collname != "pg_catalog.\"default\"" }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL +{% endif -%}; + +{% if data.constraints %} +{% for c in data.constraints %} +{% if c.conname and c.consrc %}ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}); +{% endif %} +{% endfor %} +{% endif %} + +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }}; + +{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}'; +{% endif %} + +{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} +{{ SECLABLE.APPLY(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }} +{% endif %} +{% endfor %} +{% endif %} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..4b1e49f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql @@ -0,0 +1,15 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid + AND d.oid={{doid}}::int; +{% endif %} + +{% if name %} + DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade%} CASCADE{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql new file mode 100644 index 0000000..819fdbb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql new file mode 100644 index 0000000..c448844 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql @@ -0,0 +1,29 @@ +SELECT + 'TABLE' AS objectkind, c.oid, conname, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc +FROM + pg_constraint c +JOIN + pg_class cl ON cl.oid=conrelid +JOIN + pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT + 'DOMAIN' AS objectkind, c.oid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as cons +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' +AND contypid = {{doid}}::oid +ORDER BY conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..b1eb768 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql @@ -0,0 +1,9 @@ +SELECT + d.oid, d.typname as name +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql new file mode 100644 index 0000000..adb3338 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql @@ -0,0 +1,24 @@ +SELECT + nsp.nspname +FROM + pg_namespace nsp +LEFT OUTER JOIN + pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) +LEFT OUTER JOIN + pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid) +WHERE + NOT ((nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + )AND + nspname NOT LIKE E'pg\\temp\\%' AND + nspname NOT LIKE E'pg\\toast_temp\\%' AND + -- ADDED: Because We need to omit system schema except the one on which we are trying to create collation + ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' ) +ORDER BY + nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql new file mode 100644 index 0000000..97621b0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql @@ -0,0 +1,22 @@ +SELECT + * +FROM + (SELECT + format_type(t.oid,NULL) AS typname, + CASE WHEN typelem > 0 THEN typelem ELSE t.oid END as elemoid, + typlen, typtype, t.oid, nspname, + (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup + FROM + pg_type t + JOIN + pg_namespace nsp ON typnamespace=nsp.oid + WHERE + (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) + AND typisdefined AND typtype IN ('b', 'c', 'e', 'r') + AND NOT EXISTS + (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') + AND (typname not like '_%' OR NOT EXISTS + (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) + ) AS dummy +ORDER BY + nspname <> 'pg_catalog', nspname <> 'public', nspname, 1 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..216fafe --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql @@ -0,0 +1,28 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid + {% if doid %} + AND d.oid={{doid}}::int + {% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql new file mode 100644 index 0000000..c80a20e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql @@ -0,0 +1,72 @@ +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER TYPE {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} +{% endif %} + +{% if data.typnotnull and not o_data.typnotnull %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif %} + +{% if data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault|qtLiteral }}; +{% elif not data.typdefault and o_data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif %} +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }}; +{% endif %} + +{% if data.constraints %} +{% for c in data.constraints.deleted %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }} +{% endfor %} +{% for c in data.constraints.added %} +{% if c.conname and c.consrc %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} ); +{% endif %} +{% endfor %} +{% endif -%} + +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.DROP(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'DOMAIN', name, r.provider, r +.security_label, o_data.basensp) }} +{% endfor %} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.APPLY(conn, 'DOMAIN', name, r.provider, r.label, o_data.basensp) }} +{% endfor %} +{% endif -%} + +{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }}; +{% endif -%} + +{% if data.basensp %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }}; +{% endif %} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/backend_support.sql new file mode 100644 index 0000000..8f3db8e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/backend_support.sql @@ -0,0 +1,20 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM + pg_namespace nsp +WHERE + nsp.oid={{scid}}::int + AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + ) + AND + nspname NOT LIKE E'pg\\temp\\%' + AND + nspname NOT LIKE E'pg\\toast_temp\\%' diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql new file mode 100644 index 0000000..6dfe5e5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql @@ -0,0 +1,37 @@ +{% import 'macros/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}} {% if data.precision %}, {{data.precision}}{% endif %}){% endif %}{% if data.collname and data.collname != "pg_catalog.\"default\"" %} + + COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL{% endif -%}; + +{% if data.constraints %} +{% for c in data.constraints %} +{% if c.conname and c.consrc %}ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if c.convalidated %} + + NOT VALID {% endif %}{% if c.connoinherit %} NO INHERIT{% endif %}; +{% endif -%} +{% endfor %} +{% endif %} + +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }}; + +{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}'; +{% endif %} + +{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} +{{ SECLABLE.APPLY(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }} +{% endif %} +{% endfor %} +{% endif %} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..7f437ef --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql @@ -0,0 +1,16 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +AND + d.oid={{doid}}::int; +{% endif %} + +{% if name %} + DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade%} CASCADE{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql new file mode 100644 index 0000000..e59c17d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql new file mode 100644 index 0000000..01c8475 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql @@ -0,0 +1,29 @@ +SELECT + 'TABLE' AS objectkind, c.oid, conname, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc, connoinherit, convalidated +FROM + pg_constraint c +JOIN + pg_class cl ON cl.oid=conrelid +JOIN + pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT + 'DOMAIN' AS objectkind, c.oid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc, connoinherit, convalidated +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +ORDER BY + conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..7f1afd1 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql @@ -0,0 +1,9 @@ +SELECT + d.oid, d.typname as name +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_schemas.sql new file mode 100644 index 0000000..ec62dd6 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_schemas.sql @@ -0,0 +1,24 @@ +SELECT + nsp.nspname +FROM + pg_namespace nsp +LEFT OUTER JOIN + pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) + LEFT OUTER JOIN + pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid) +WHERE + NOT ((nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + )AND + nspname NOT LIKE E'pg\\temp\\%' AND + nspname NOT LIKE E'pg\\toast_temp\\%' AND + -- ADDED: Because We need to omit system schema except the one on which we are trying to create collation + ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' ) +ORDER BY + nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_types.sql new file mode 100644 index 0000000..c4d46f7 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_types.sql @@ -0,0 +1,22 @@ +SELECT + * +FROM + (SELECT + format_type(t.oid,NULL) AS typname, + CASE WHEN typelem > 0 THEN typelem ELSE t.oid END as elemoid, + typlen, typtype, t.oid, nspname, + (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup + FROM + pg_type t + JOIN + pg_namespace nsp ON typnamespace=nsp.oid + WHERE + (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) + AND typisdefined AND typtype IN ('b', 'c', 'e', 'r') + AND NOT EXISTS + (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') + AND (typname not like '_%' OR NOT EXISTS + (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) + ) AS dummy +ORDER BY + nspname <> 'pg_catalog', nspname <> 'public', nspname, 1 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..5dd2d01 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql @@ -0,0 +1,29 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT array_agg(provider || '=' || label) FROM pg_shseclabel sl1 WHERE sl1.objoid=d.oid) AS seclabels +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid +{% if doid %} + AND d.oid={{doid}}::int +{% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql new file mode 100644 index 0000000..5d1987a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql @@ -0,0 +1,70 @@ +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} +{% endif %} +{% if data.typnotnull and not o_data.typnotnull %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif %} + +{% if data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault|qtLiteral }}; +{% elif not data.typdefault and o_data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif %} +{% import 'macros/security.macros' as SECLABLE %} +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }}; +{% endif %} + +{% if data.constraints %} +{% for c in data.constraints.deleted %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }} +{% endfor %} +{% for c in data.constraints.added %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} ) +{% if c.convalidated %} NOT VALID {% endif %} {% if c.connoinherit %} NO INHERIT {% endif %}; +{% endfor %} +{% endif %} + +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.DROP(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'DOMAIN', name, r.provider, r.label, o_data.basensp) }} +{% endfor %} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.APPLY(conn, 'DOMAIN', name, r.provider, r.label, o_data.basensp) }} +{% endfor %} +{% endif %} + +{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }}; +{% endif %} + +{% if data.basensp %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }}; +{% endif %} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/templates/macros/security.macros b/web/pgadmin/browser/server_groups/servers/templates/macros/security.macros index 83fb9d2..6ce4f30 100644 --- a/web/pgadmin/browser/server_groups/servers/templates/macros/security.macros +++ b/web/pgadmin/browser/server_groups/servers/templates/macros/security.macros @@ -1,6 +1,17 @@ -{% macro APPLY(conn, type, name, provider, label) -%} -SECURITY LABEL FOR {{ conn|qtIdent(provider) }} ON {{ type }} {{ conn|qtIdent(name) }} IS {{ label|qtLiteral }}; +{% macro APPLY(conn, type, name, provider, label, schema) -%} +{% if schema %} +{% set name = conn|qtIdent(schema, name) %} +{% else %} +{% set name = conn|qtIdent(name) %} +{% endif %} +SECURITY LABEL FOR {{ conn|qtIdent(provider) }} ON {{ type }} {{ name }} IS {{ +label|qtLiteral }}; {%- endmacro %} -{% macro DROP(conn, type, name, provider) -%} -SECURITY LABEL FOR {{ conn|qtIdent(provider) }} ON {{ type }} {{ conn|qtIdent(name) }} IS NULL; +{% macro DROP(conn, type, name, provider, schema) -%} +{% if schema %} +{% set name = conn|qtIdent(schema, name) %} +{% else %} +{% set name = conn|qtIdent(name) %} +{% endif %} +SECURITY LABEL FOR {{ conn|qtIdent(provider) }} ON {{ type }} {{ name }} IS NULL; {%- endmacro %} diff --git a/web/pgadmin/browser/static/js/datamodel.js b/web/pgadmin/browser/static/js/datamodel.js index 9385adf..61e81ed 100644 --- a/web/pgadmin/browser/static/js/datamodel.js +++ b/web/pgadmin/browser/static/js/datamodel.js @@ -340,7 +340,13 @@ function(_, pgAdmin, $, Backbone) { delete res[k]; } } else { - res[k] = (obj && obj.toJSON()); + if (obj && (obj instanceof Array)) { + res[k] = obj; + } + else + { + res[k] = obj && obj.toJSON(); + } } }); return res; diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js index 4346156..ae807cb 100644 --- a/web/pgadmin/static/js/backform.pgadmin.js +++ b/web/pgadmin/static/js/backform.pgadmin.js @@ -1058,11 +1058,13 @@ if (data.disabled == false && data.canEdit) { var editCell = Backgrid.Extension.ObjectCell.extend({ schema: gridSchema.schema - }); + }), + canEdit = self.field.has('canEdit') && + self.field.get('canEdit') || true; gridSchema.columns.unshift({ name: "pg-backform-edit", label: "", cell : editCell, - cell_priority: -2 + cell_priority: -2, editable: canEdit }); } @@ -1101,7 +1103,7 @@ grid.insertRow({}); var newRow = $(grid.body.rows[collection.length - 1].$el); newRow.attr("class", "new").click(function(e) { - $(this).attr("class", ""); + $(this).attr("class", "editable"); }); $(newRow).pgMakeVisible('backform-tab'); return false; @@ -1528,8 +1530,15 @@ /* * Add empty option as Select2 requires any empty '<option><option>' for * some of its functionality to work and initialize select2 control. + * + * But - not for multiple selection. */ - this.$el.find("select").prepend($('<option></option>')).select2(col.select2); + var s = this.$el.find("select"); + if (!col.select2.multiple) { + s.prepend($('<option></option>')); + } + s.select2(col.select2); + return this; } }); ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-02-24 09:24 Khushboo Vashi <[email protected]> parent: Khushboo Vashi <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Khushboo Vashi @ 2016-02-24 09:24 UTC (permalink / raw) To: pgadmin-hackers Hi, I have updated the Domain module as below: - Used 'NodeByListControl' to get schemas, in domains.js file as suggested by Ashesh to avoid code redundancy. - Applied *'Security Label Macro'* Patch (Implemented by Harshal) and removed same changes from the Domain Patch. To test Domain patch, 'Security Label Macro' patch must be applied first as that is not committed yet. Please find attached Domain Module Patch. Thanks, Khushboo On Tue, Feb 23, 2016 at 12:37 PM, Khushboo Vashi < [email protected]> wrote: > Hi, > > Please find attached Revised patch for the Domain module and also my > comments inline as below. > > Thanks, > Khushboo > > On Wed, Feb 3, 2016 at 4:22 PM, Neel Patel <[email protected]> > wrote: > >> Hi Khushboo, >> >> Please find below review comments. >> >> - Reverse engineering SQL generation is not implemented for domain node. >> > Done > >> - "Length" and "Precision" fields should be enabled/disabled based on >> the selection of "Base Type" value. >> > This implementation is dependent on the 'Type' module. Once that will be > done, I will merge my code. > >> - Query is not getting generated properly. Some of the parameters are >> not reflected in query. As we have provided Length and Precision value in >> below numeric base type. Also do proper indentation in generated query. >> >> * Wrong Query :- * >> CREATE DOMAIN my_schema.test_123 >> AS "numeric" >> DEFAULT 5 >> ; >> >> * Correct Query :- * >> CREATE DOMAIN my_schema.test_123 >> AS numeric(22,4) >> DEFAULT 5 >> NOT NULL; >> >> Done > >> - After creation of new domain with base type "aclitem" , wrong "Length" >> field value is getting displayed. >> > Done > >> - We are getting error saying "*TypeError: the JSON object must be str, >> not 'dict*'" when we add constraint w >> > ith "NOT VALID" and NO INHERIT. >> > Done > >> - We should add property "System Domain?" when we select any domain node. >> > Done > >> - We think for creation of Security Label, we should include the schema >> name along with domain name. >> *Wrong Query :- * >> SECURITY LABEL FOR pv_label ON DOMAIN test_123 IS 'label_val'; >> Correct Query :- >> SECURITY LABEL FOR pv_label ON DOMAIN <schema_name>.test_123 IS >> 'label_val'; >> >> >> Done > >> Let us know in case of any issues. >> >> Thanks, >> Neel Patel >> >> On Tue, Feb 2, 2016 at 3:51 PM, Khushboo Vashi < >> [email protected]> wrote: >> >>> Hi Neel, >>> >>> Thanks for reviewing my patch. >>> >>> I have modified the code as per your suggestions and also fixed some of >>> the issues got while doing unit testing. >>> Please find attached patch for the same. >>> >>> >>> Thanks, >>> Khushboo >>> >>> >>> >>> On Wed, Jan 20, 2016 at 10:56 PM, Neel Patel < >>> [email protected]> wrote: >>> >>>> Hi Khushboo, >>>> >>>> Please find below review comments. >>>> >>>> - While creating new Domain and clicking on SQL tab, python side we are >>>> getting error saying "*TypeError: 'bool' object is not callable".* >>>> We are not able to create any domain. Fix this issue so that we can >>>> test other functionality. >>>> - Implement the reverse engineering SQL generation for the domain node. >>>> - As per the checklist, remove the "Use Slony" from Constraints tab, as >>>> it is not required. >>>> - No need to pass "*qtIdent=self.qtIdent*" as function argument in >>>> "create" and "getSQL" function in domains/__init__.py >>>> - In "Security" tab , provider and security label fields are not >>>> editable. >>>> - In PG version 9.1, when we update the existing domain name then >>>> "ALTER DOMAIN" is not supported. >>>> Currently there is no checking for the PG version 9.1 and 9.2_plus. >>>> It will fail when we connect to database 9.1 >>>> >>>> e.g. >>>> For PG version 9.1 - Update command should be as below. >>>> ALTER TYPE xyz RENAME TO abc; >>>> For PG version 9.2 onwards - Update command should be as below. >>>> ALTER DOMAIN xyz RENAME TO abc; >>>> >>>> - Some of the SQL file, qtIdent is not used. Please check all the >>>> related SQL files. >>>> e.g. - In update.sql file "data.owner" should be >>>> "conn|qtIdent(data.owner)" >>>> >>>> {% if data.owner %} >>>> ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} >>>> OWNER TO {{ data.owner }}; >>>> {% endif %} >>>> >>>> Let us know for any issues. >>>> >>>> Thanks, >>>> Neel Patel >>>> >>>> On Wed, Jan 20, 2016 at 2:50 PM, Khushboo Vashi < >>>> [email protected]> wrote: >>>> >>>>> Hi Neel, >>>>> >>>>> Please find updated patch. >>>>> >>>>> Thanks, >>>>> Khushboo >>>>> >>>>> On Wed, Jan 20, 2016 at 12:50 PM, Neel Patel < >>>>> [email protected]> wrote: >>>>> >>>>>> Hi Khushboo, >>>>>> >>>>>> While applying the patch file, we are getting below warnings. >>>>>> >>>>>> ######################################### >>>>>> domains (1).patch:1340: trailing whitespace. >>>>>> oid: undefined, >>>>>> domains (1).patch:1483: trailing whitespace. >>>>>> (nspname = 'pg_catalog' AND EXISTS >>>>>> domains (1).patch:1487: trailing whitespace. >>>>>> OR (nspname = 'information_schema' AND EXISTS >>>>>> domains (1).patch:1489: trailing whitespace. >>>>>> OR (nspname LIKE '_%' AND EXISTS >>>>>> domains (1).patch:1642: trailing whitespace. >>>>>> (select 1 from pg_class where relnamespace=typnamespace and relname >>>>>> = typname and relkind != 'c') AND (typname not like '_%' OR NOT EXISTS >>>>>> (select 1 from pg_class where relnamespace=typnamespace and relname = >>>>>> substring(typname from 2)::name and relkind != 'c')) >>>>>> warning: squelched 4 whitespace errors >>>>>> warning: 9 lines add whitespace errors. >>>>>> ######################################### >>>>>> >>>>>> Can you please remove the whitespace and regenerate the patch ? >>>>>> >>>>>> Thanks, >>>>>> Neel Patel >>>>>> >>>>>> On Wed, Jan 20, 2016 at 12:37 PM, Khushboo Vashi < >>>>>> [email protected]> wrote: >>>>>> >>>>>>> Resending patch with binary option. >>>>>>> >>>>>>> On Wed, Jan 20, 2016 at 10:18 AM, Khushboo Vashi < >>>>>>> [email protected]> wrote: >>>>>>> >>>>>>>> Hi, >>>>>>>> >>>>>>>> Please find attached patch for the Domain Module. >>>>>>>> >>>>>>>> The patch will be modified after Types module implementation as we >>>>>>>> need to populate Base Type and some Type related validations from the >>>>>>>> Types module. >>>>>>>> >>>>>>>> Please review it and let me know the feedback. >>>>>>>> >>>>>>>> Thanks, >>>>>>>> Khushboo >>>>>>>> >>>>>>> >>>>>>> >>>>>>> >>>>>>> -- >>>>>>> Sent via pgadmin-hackers mailing list ( >>>>>>> [email protected]) >>>>>>> To make changes to your subscription: >>>>>>> http://www.postgresql.org/mailpref/pgadmin-hackers >>>>>>> >>>>>>> >>>>>> >>>>> >>>> >>> >> > -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers Attachments: [text/x-patch] Domains_ver_3.patch (109.6K, 3-Domains_ver_3.patch) download | inline diff: diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py new file mode 100644 index 0000000..4b419bb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py @@ -0,0 +1,778 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""The Domain Module.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas as schemas +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + + +class DomainModule(CollectionNodeModule): + """ + class DomainModule(CollectionNodeModule): + + This class represents The Domain Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Module. + + * get_nodes(gid, sid, did, scid) + - Generate the domain collection node. + + * script_load() + - Load the module script for domain, when schema node is + initialized. + """ + + NODE_TYPE = 'domain' + COLLECTION_LABEL = gettext("Domains") + + def __init__(self, *args, **kwargs): + super(DomainModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid): + """ + Generate the domain collection node. + """ + yield self.generate_browser_collection_node(scid) + + @property + def script_load(self): + """ + Load the module script for domain, when schema node is + initialized. + """ + return schemas.SchemaModule.NODE_TYPE + + +blueprint = DomainModule(__name__) + + +class DomainView(PGChildNodeView): + """ + class DomainView + + This class inherits PGChildNodeView to get the different routes for + the module. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain. + + Methods: + ------- + * validate_request(f): + - Works as a decorator. + Validating request on the request of create, update and modified SQL. + + * module_js(): + - Overrides this property to define javascript for Domain node. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domains. + + * nodes(gid, sid, did, scid): + - Returns all the Domains to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain properties. + + * get_collations(gid, sid, did, scid, doid=None): + - Returns Collations. + + * get_types(gid, sid, did, scid, doid=None): + - Returns Data Types. + + * create(gid, sid, did, scid): + - Creates a new Domain object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain object. + + * dependents(gid, sid, did, scid, doid): + - Returns the dependents for the Domain object. + + * dependencies(gid, sid, did, scid, doid): + - Returns the dependencies for the Domain object. + """ + + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'doid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_collations': [ + {'get': 'get_collations'}, + {'get': 'get_collations'} + ], + 'get_types': [{'get': 'get_types'}, {'get': 'get_types'}] + }) + + def validate_request(f): + """ + Works as a decorator. + Validating request on the request of create, update and modified SQL. + + Required Args: + name: Name of the Domain + owner: Domain Owner + basensp: Schema Name + basttype: Data Type of the Domain + + Above both the arguments will not be validated in the update action. + """ + + @wraps(f) + def wrap(self, **kwargs): + + data = {} + if request.data: + req = json.loads(request.data) + else: + req = request.args or request.form + + if 'doid' not in kwargs: + required_args = [ + 'name', + 'owner', + 'basensp', + 'basetype' + ] + + for arg in required_args: + if arg not in req or req[arg] == '': + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter \ + (%s)." % arg + ) + ) + + try: + list_params = [] + if request.method == 'GET': + list_params = ['constraints', 'seclabels'] + + for key in req: + if key in list_params and req[key] != '' \ + and req[key] is not None: + # Coverts string into python list as expected. + data[key] = json.loads(req[key]) + elif key == 'typnotnull': + data[key] = True if req[key] == 'true' or req[key] is\ + True else\ + (False if req[key] == 'false' or req[key] is + False else '') + else: + data[key] = req[key] + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + self.request = data + return f(self, **kwargs) + + return wrap + + def module_js(self): + """ + Overrides this property to define javascript for Domain node. + """ + return make_response( + render_template( + "domains/js/domains.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + # Get database connection + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domains/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domains/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid): + """ + List the Domains. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid): + """ + Returns all the Domains to generate Nodes in the browser. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + res = [] + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + scid, + row['name'], + icon="icon-domain" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid): + """ + Returns the Domain properties. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + # The Length and the precision of the Datatype should be separate. + # The Format we getting from database is: numeric(1,1) + # So, we need to separate Length: 1, Precision: 1 + if data['fulltype'] != '' and data['fulltype'].find("(") > 0: + substr = data['fulltype'][data['fulltype'].find("(") + 1:len( + data['fulltype']) - 1] + typlen = substr.split(",") + if len(typlen) > 1: + data['typlen'] = typlen[0] + data['precision'] = typlen[1] + else: + data['typlen'] = typlen + data['precision'] = '' + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + # Set System Domain Status + data['sysdomain'] = False + if doid <= self.manager.db_info[did]['datlastsysoid']: + data['sysdomain'] = True + + return ajax_response( + response=data, + status=200 + ) + + @check_precondition + def get_collations(self, gid, sid, did, scid, doid=None): + """ + Returns Collations. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + res = [{'label': '', 'value': ''}] + try: + SQL = render_template("/".join([self.template_path, + 'get_collations.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append({'label': row['copy_collation'], + 'value': row['copy_collation']} + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def get_types(self, gid, sid, did, scid, doid=None): + """ + Returns Types. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + # TODO: This function should be removed once + # Types module will be completed. + + res = [{'label': '', 'value': ''}] + try: + SQL = render_template("/".join([self.template_path, + 'get_types.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append( + {'label': row['typname'], + 'value': row['typname']} + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + @validate_request + def create(self, gid, sid, did, scid): + """ + Creates a new Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + Domain object in json format. + """ + + data = self.request + try: + SQL = render_template("/".join([self.template_path, 'create.sql']), + data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # We need oid to to add object in tree at browser, below sql will + # gives the same + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + basensp=data['basensp'], + name=data['name']) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + doid, scid = res['rows'][0] + + return jsonify( + node=self.blueprint.generate_browser_node( + doid, + scid, + data['name'], + icon="icon-domain" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid): + """ + Drops the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + name, basensp = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + name=name, basensp=basensp, cascade=cascade) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + @validate_request + def update(self, gid, sid, did, scid, doid): + """ + Updates the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if not status: + return internal_server_error(errormsg=SQL) + + try: + if SQL: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get Schema Id + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + scid = res['rows'][0]['scid'] + + return make_json_response( + success=1, + info="Domain updated", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid=None): + """ + Returns the SQL for the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return False, internal_server_error(errormsg=res) + data = res['rows'][0] + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + # Toggle Validate and inherit options for 'CREATE Query' + for c in data['constraints']: + c['convalidated'] = False if c['convalidated'] else True + c['connoinherit'] = False if c['connoinherit'] else True + + SQL = render_template("/".join([self.template_path, + 'create.sql']), data=data) + + return ajax_response(response=SQL) + + @check_precondition + @validate_request + def msql(self, gid, sid, did, scid, doid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + SQL statements to create/update the Domain. + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid=None): + """ + Generates the SQL statements to create/update the Domain. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + try: + if doid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data) + else: + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data) + return True, SQL + + except Exception as e: + return False, e + + @check_precondition + def dependents(self, gid, sid, did, scid, doid): + """ + This function get the dependents and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependents_result = self.get_dependents(self.conn, doid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid): + """ + This function get the dependencies and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependencies_result = self.get_dependencies(self.conn, doid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py new file mode 100644 index 0000000..370e585 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py @@ -0,0 +1,640 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""The Domain Constraint Module.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas.domains \ + as domains +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + + +class DomainConstraintModule(CollectionNodeModule): + """ + class DomainConstraintModule(CollectionNodeModule): + + This class represents The Domain Constraint Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Constraint Module. + + * get_nodes(gid, sid, did, scid) + - Generate the Domain Constraint collection node. + + * node_inode(gid, sid, did, scid) + - Override this property to make the Domain Constraint node as leaf node. + + * script_load() + - Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + NODE_TYPE = 'domain-constraints' + COLLECTION_LABEL = gettext("Domain Constraints") + + def __init__(self, *args, **kwargs): + super(DomainConstraintModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid, doid): + """ + Generate the Domain Constraint collection node. + """ + yield self.generate_browser_collection_node(doid) + + @property + def node_inode(self): + """ + Override this property to make the node as leaf node. + """ + return False + + @property + def script_load(self): + """ + Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + return domains.DomainModule.NODE_TYPE + + +blueprint = DomainConstraintModule(__name__) + + +class DomainConstraintView(PGChildNodeView): + """ + class DomainConstraintView(PGChildNodeView): + + This class inherits PGChildNodeView to get the different routes for + the module. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain Constraint. + + Methods: + ------- + + * module_js(): + - Overrides this property to define javascript for Domain Constraint node. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domain Constraints. + + * nodes(gid, sid, did, scid): + - Returns all the Domain Constraints to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain Constraint properties. + + * create(gid, sid, did, scid): + - Creates a new Domain Constraint object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain Constraint object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain Constraint object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain Constraint object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain Constraint. + object. + + * dependents(gid, sid, did, scid, doid, coid): + - Returns the dependents for the Domain Constraint object. + + * dependencies(gid, sid, did, scid, doid, coid): + - Returns the dependencies for the Domain Constraint object. + """ + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'}, + {'type': 'int', 'id': 'doid'} + ] + ids = [ + {'type': 'int', 'id': 'coid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def module_js(self): + """ + Overrides this property to define javascript for Domain Constraint node. + """ + return make_response( + render_template( + "domain-constraints/js/domain-constraints.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domain-constraints/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domain-constraints/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid, doid): + """ + List the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid, doid): + """ + Returns all the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + res = [] + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, rset = self.conn.execute_2darray(SQL) + + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + doid, + row['name'], + icon="icon-domain-constraints" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid, coid): + """ + Returns the Domain Constraints property. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + return ajax_response( + response=res['rows'][0], + status=200 + ) + + @check_precondition + def create(self, gid, sid, did, scid, doid): + """ + Creates a new Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Constraints Name + consrc: Constraints Check + + Returns: + Domain Constraint object in json format. + """ + + data = request.form if request.form else \ + json.loads(request.data.decode()) + required_args = [ + 'name', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + try: + # Get Schema and Domain. + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get the recently added constraints oid + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + doid=doid, name=data['name']) + status, coid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=doid) + + return jsonify( + node=self.blueprint.generate_browser_node( + coid, + doid, + data['name'], + icon="icon-domain-constraints" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid, coid): + """ + Drops the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain Constraint dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, scid, doid, coid): + """ + Updates the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + data = request.form if request.form else \ + json.loads(request.data.decode()) + + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + + try: + if SQL and status: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Domain Constraint updated", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the SQL for the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + # Get Schema and Domain. + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + data['convalidated'] = False if data['convalidated'] else True + data['connoinherit'] = False if data['connoinherit'] else True + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + + return ajax_response(response=SQL) + + @check_precondition + def msql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + + Required Args: + name: Constraints Name + consrc: Constraints Check + + Returns: + Domain Constraint object in json format. + """ + data = request.args + + if coid is None: + required_args = [ + 'name', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + if status and SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid, coid=None): + """ + Generates the SQL statements to create/update the Domain Constraint. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + if coid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data, conn=self.conn + ) + else: + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + + if not status: + return False, internal_server_error(errormsg=name) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + return True, SQL + except Exception as e: + return False, internal_server_error(errormsg=str(e)) + + @check_precondition + def dependents(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependents and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependents_result = self.get_dependents(self.conn, coid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependencies and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependencies_result = self.get_dependencies(self.conn, coid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainConstraintView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..d62e13705c50e6c0cf8f19d680053e8643e28751 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=2TrO847{Jl`|t`Qpas zn<hS+l=rMY>RGPqvlydizRJ&>B`Om#V}R-yOM?7@862M7NCR>>3p^r=fwTu0yPeFo z12TL)T^vI=t|uoPU||ZF<tgaHG*QsQ!?m&Tq=?3mCu}J#Dx3x@mM}}^iE=5NIWXnk zkpnC4ai%a>@;Gg7=uums=9bIK=Egd~(us-3g@Iv02gfsK^JP^)gH=mhBT7;dOH!?p zi&B9UgOP!ufv%yEu7P2Qk%5(ov6YF5wt=aYfq}(LRXG$5x%nxXX_XKS29{tAAk|g| XW)KahriZQpYGCkm^>bP0l+XkKyyRU} literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png new file mode 100644 index 0000000000000000000000000000000000000000..32a045b8fafdc08640d53b2a86b1dcabcb0fe0fd GIT binary patch literal 579 zcmV-J0=)f+P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp) z=>Px$AW%$HMR1Z9#Q*@REHmdHAm<<;=p`lTDl6$LE15$|nM6yxJ3#6&F}^)Qx<p3n zH#h4zIK)Ou#79fTM@#HJKkY(8?L$QEL`CjJMeasM?ng(;QB>|oNbX2U?n+AUOH1!e zOz%xi?@dncPEXKRS?^Cz#amtQQBm+xQt(q#@KaRqR8;U)Rq<9<@mE*YVq(`~V)0vB z+GS<)VPW%PV%=$Jag!JHXlV6qZS`($_HuH3pd<EncJ_C7f1@XWqbGu>Du=8uhpjJ) zvN4agH<i3UrMYgWyK<eyN1Vq+p2tbA!G5C3PO!m$q|HvV#D$~JOsv;ftk_qv-CeQX zT*cC%$J3;><6_L$t<Kr8)!w|r@o>cPam4X*+~mi`^K{Glc|xh<NdN!<0d!JMQvg8b z*k%9#00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipf34<ROg z(p72z0056kL_t&-)1{2Z3c^qT1mDEody6IZuCW&s1V!`^4}$goe?(18@b2Db*j*w1 z%4M(p;&Zw?9ZaKAxo*x;COX}<8fzjqKRv^2kF1spymYHMjE2m7Hl|a~emCNwG8(i? z8I#``(kiBrEbh}(Qn8TL2+$}b3Hn^7p`K6R#_6zQ2$?ux;lXBYB>hkN@C%g?4vVBM R$bbL<002ovPDHLkV1min0OSAw literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..9d1d2a061c7948168d7b1c2474d769b31709f1cf GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE08{VY2nhHc^eMaU%j`d zaI$#+HuEKC{pKEZKYn>h(+aIMH^MjGjcs3}f9Cqyt&fvx7AT*)xv^`L;hf{Hi_iP4 zxgK%&V?q65_4c*;8}G%;JMMY<SLM___M4Bi9{E^!<YUpX&n1ga`7PgFwEkdS!(#P< zNn&@N9OqiK(i&(nV@Z%-FoVOh8)-leXMsm#F_88EW4Dvpc0fjir;B5V#O36K1sn!O zhRVf}5jSsGPH?f<xudc|vBoY<&5cb=uTGB9v187J4II+it5?jhn8F{TsCZIWRad$D zg1*K9W%cu2NsD(hEfVT#*wm#pYw;D04+0E(uQE?s`Z6*ZXoqTvYeY#(Vo9o1a#1Rf zVlXl=G|)9P(lsy)F*2|+F}5->(Kax(GBB{1sVaw}AvZrIGp!P$!N3x%0i@c>zzm|{ T)b!9bKn)C@u6{1-oD!M<=4O^k literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css new file mode 100644 index 0000000..57b6612 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css @@ -0,0 +1,8 @@ +.pg-icon-domain-constraints { + background-image: url('{{ url_for('NODE-domain-constraints.static', filename='img/domain-constraints.png') }}') !important; + border-radius: 10px; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js new file mode 100644 index 0000000..d9e23a8 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js @@ -0,0 +1,129 @@ +// Domain Constraint Module: Collection and Node +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Constraint Collection Node + if (!pgBrowser.Nodes['coll-domain-constraints']) { + var domain_constraints = pgAdmin.Browser.Nodes['coll-domain-constraints'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + type: 'coll-domain-constraints' + }); + }; + + // Domain Constraint Node + if (!pgBrowser.Nodes['domain-constraints']) { + pgAdmin.Browser.Nodes['domain-constraints'] = pgBrowser.Node.extend({ + type: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + collection_type: 'coll-domain-constraints', + hasSQL: true, + hasDepends: true, + parent_type: ['domain'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined + }, + // Domain Constraint Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', type:'text', cell:'string', + disabled: 'isDisabled' + },{ + id: 'description', label: '{{ _('Comment') }}', type: 'multiline', cell: + 'string', mode: ['properties', 'create', 'edit'], disabled: function + (m) { return !m.isNew(); } + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', cel: + 'string', group: '{{ _('Definition') }}', mode: ['properties', + 'create', 'edit'], disabled: function(m) { return !m.isNew(); } + },{ + id: 'connoinherit', label: '{{ _('No Inherit') }}', type: + 'switch', cell: 'boolean', group: '{{ _('Definition') }}', mode: + ['properties', 'create', 'edit'], disabled: 'isDisabled' + },{ + id: 'convalidated', label: "Don't Validate", type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', disabled: 'isDisabled', + mode: ['create', 'edit'] + },{ + id: 'convalidated', label: '{{ _('Valid?') }}', type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', disabled: 'isDisabled', + mode: ['properties'] + }], + // Client Side Validation + validate: function() { + var err = {}, + errmsg; + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') { + err['consrc'] = '{{ _('Check can not be empty!') }}'; + errmsg = errmsg || err['consrc']; + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return true; + } + } + return false; + } + }), + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql new file mode 100644 index 0000000..22bad72 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql @@ -0,0 +1,4 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }}); +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..95b7310 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql @@ -0,0 +1,30 @@ +SELECT + 'TABLE' AS objectkind, c.oid, conname as name, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc +FROM + pg_constraint c +JOIN + pg_class cl ON cl.oid=conrelid +JOIN + pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT + 'DOMAIN' AS objectkind, c.oid, conname as name, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql new file mode 100644 index 0000000..d574430 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql @@ -0,0 +1,4 @@ +{% if data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql new file mode 100644 index 0000000..c50e486 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql @@ -0,0 +1,8 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }}){% if data.convalidated %} + + NOT VALID{% endif %}{% if data.connoinherit %} + + NO INHERIT{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..80b7ced --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql @@ -0,0 +1,29 @@ +SELECT + 'TABLE' AS objectkind, c.oid, conname as name, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc, connoinherit, convalidated +FROM + pg_constraint c +JOIN + pg_class cl ON cl.oid=conrelid +JOIN + pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT + 'DOMAIN' AS objectkind, c.oid, conname as name, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', + E'\\1') as consrc, connoinherit, convalidated FROM pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql new file mode 100644 index 0000000..d574430 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql @@ -0,0 +1,4 @@ +{% if data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png new file mode 100644 index 0000000000000000000000000000000000000000..55621528a1dba4928538fe5557b9b988ed78d6ab GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}U4T!BE0BJeoqfW=;gF{0PD8_o zi&pN_(K)B7`FPi%2ix`@v9LU(tNUQ*!K1czXOxuggoGT_(#p@zf4Jw!!zHV)`3F7T zd-Um}H}`iRIijcc@Wq#>&ptkR`SszG54Uz6zW4mg?MLsgZ9jbb>E~y!zTSB9`Re1( zjS1S99(_9h@YC4`A5Y)^_^>@J3+MvIk|4ie28U-i(tsS!0*}aIAngIhZYQ(tfQ)ue z7sn8Z%b|U@#hMgESUvs4wKoSxO>~{O=-Yq$8_!r)-^e%4-l6f-Jox1%8}FIVx>FCW zPMVwluQGbkP1bcSwVV0d_?PZbZVis%HeYTWyU*$OvYp$uTc3QLbD?2trOnDuhZa>f z=3d_{(zER9xt(<n3UsBI4SNC?Y!rBW<m2`n>yDRyg$uoE8Qx}bo1D0qw;t#u)e_f; zl9a@fRIB8oR3OD*WMF8ZYiOivU>IU#U}a)#Wn!XjU}|MxU@=ow4n;$5eoAIqB}9XP eC0GMUwUvPxM8m1+p=*E|7(8A5T-G@yGywoRCC2Lj literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png new file mode 100644 index 0000000000000000000000000000000000000000..7521cddeaaaf0ee4e3c60e948078d70e17e06893 GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}T7XZ8E0Deu9eu*V;gF{0K`pIg zc6K`r4IeIAc}Q3HxV`=3y+@xudUJpGkt2F~PoI5!^78A$Cm(L@JbcYR=;4bm_nv>b z{pkI*?T7D#gyiSvKYR7{_S4Tdo_xOg_;X`|_N7Ok&OiKg_QA)~_dlLo85{w$iLoTe zFPOpM*^M+HhqJ&VvKUBvfU(=jY&#$$$<xI#MB;Mq`IABp20RT9wUXkaL(1R(pOn?c z>38<+e>KaN2{%0Jd@sMbY$+13Z&%Z<%!P+*SNu+#({Rr={_KxUhswHdID0QWTCBvD z)^ky;@gu`E#RAC#l`JcnelS0rxkKN7b1B>H56gX)0&P<*ag8WRNi0dVN-jzTQVd20 zh6cKZM!E)uAw~vPCdO7KCfWw3Rt5$ZGgakKH00)|WTsU@G#FTdHGouG8JIydoSGiG Q2B?9-)78&qol`;+03_q3ga7~l literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png new file mode 100644 index 0000000000000000000000000000000000000000..42ca929325854b8f34787425e8094d08c75983bc GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}PJmB{E0BJeoqfW=;gF{0PD8^7 zi&s6`bL8oxH}`iRdHU?*lb2r~J^6Th=ix^$zTSWS<?f>oH+CHUa^}pVf`aWvMt7fm zx&8F>jfd~AY(HF`mUiRG=W9<sUwQB@KR>@QLHo+%PZuA3I{)y~*$1D_JotF({zvHt zo%ulf7)yfuf*Bm1-ADs+I14-?i-EKU7`vU!wgWPXJzX3_Brcbpe=XFcAmSFNeC3q& zl!Z+(RsYN12&-NW{P*^#<rx82_2-+ii&SqfGgO`Jcj@;01@HTH{&&7#v?j}I)=J6m ze`~hppZn7?WA|%z!}T6pf>z0IjbZrS5ICD*275*_bAo)r8t#Vg4A*Lz);xc4JKq03 zXXC}zd)YwiRZCnWN>UO_QmvAUQh^kMk%6IsuAz~xfnkV|ft87|m5GVAfvJ^&fyGQ! qITQ`K`6-!cl@JXEmS7Da)m8>(5DllMhpqu?VDNPHb6Mw<&;$VOR=Fqu literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css new file mode 100644 index 0000000..ab9e776 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/css/domains.css @@ -0,0 +1,8 @@ +.pg-icon-domain { + background-image: url('{{ url_for('NODE-domain.static', filename='img/domain.png') }}') !important; + border-radius: 10px; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js new file mode 100644 index 0000000..50440fc --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js @@ -0,0 +1,290 @@ +// Domain Module: Collection and Node. +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Collection Node + if (!pgBrowser.Nodes['coll-domain']) { + var domains = pgAdmin.Browser.Nodes['coll-domain'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain', + label: '{{ _('Domains') }}', + type: 'coll-domain', + columns: ['name', 'oid', 'owner', 'basensp', 'description', 'basetype', + 'typdefault', 'typnotnull', 'sysdomain'] + }); + }; + + // Security Model + var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: null, + security_label: null + }, + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text' + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text' + }], + validate: function() { + var err = {}, + errmsg = null, + data = this.toJSON(); + + if (_.isUndefined(data.label) || + _.isNull(data.label) || + String(data.label).replace(/^\s+|\s+$/g, '') == '') { + return _("Please specify the value for all the security providers."); + } + return null; + } + }); + + // Constraint Model + var ConstraintsModel = pgAdmin.Browser.Node.Model.extend({ + idAttribute: 'conname', + defaults: { + conname: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined + }, + schema: [{ + id: 'conname', label: '{{ _('Name') }}', type: 'text', cell: 'string' + },{ + id: 'description', label: '{{ _('Comment') }}', type: 'multiline', cell: 'string' + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', + cell: 'string', group: '{{ _('Definition') }}' + },{ + id: 'connoinherit', label: '{{ _('No Inherit') }}', type: 'switch', + cell:'boolean', group: '{{ _('Definition') }}' + },{ + id: 'convalidated', label: '{{ _('Valid?') }}', type: + 'switch', cell: 'boolean', group: '{{ _('Definition') }}', mode: + ['properties'] + },{ + id: 'convalidated', label: "Don't Validate", type: + 'switch', cell: 'boolean', group: '{{ _('Definition') }}', mode: + ['create', 'edit'] + }], + validate: function() { + // TODO: Add validation here + }, + toJSON: Backbone.Model.prototype.toJSON + }); + + // Domain Node + if (!pgBrowser.Nodes['domain']) { + pgAdmin.Browser.Nodes['domain'] = pgBrowser.Node.extend({ + type: 'domain', + label: '{{ _('Domain') }}', + collection_type: 'coll-domain', + hasSQL: true, + hasDepends: true, + parent_type: ['schema'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + canDropCascade: pgBrowser.Nodes['schema'].canChildDrop, + // Domain Node Model + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + oid: undefined, + owner: undefined, + basensp: undefined, + description: undefined, + basetype: undefined, + typlen: undefined, + precision: undefined, + typdefault: undefined, + typnotnull: undefined, + sysdomain: undefined, + collname: undefined, + constraints: [], + seclabels: [] + }, + // Domain Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'] + },{ + id: 'oid', label:'{{ _('Oid') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl, + node: 'role', type: 'text', mode: ['edit', 'create', 'properties'] + },{ + id: 'basensp', label:'{{ _('Schema') }}', cell: 'node-list-by-name', + control: 'node-list-by-name', cache_level: 'database', type: 'text', + node: 'schema' + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'basetype', label:'{{ _('Base Type') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', mode:['properties', 'create', 'edit'], group: '{{ _('Definition') }}', url: 'get_types', + disabled: function(m) { return !m.isNew(); } + },{ + id: 'typlen', label:'{{ _('Length') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', disabled: function(m) { return !m.isNew(); } + },{ + id: 'precision', label:'{{ _('Precision') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', disabled: function(m) { return !m.isNew(); } + },{ + id: 'typdefault', label:'{{ _('Default') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}' + },{ + id: 'typnotnull', label:'{{ _('Not Null') }}', cell: 'boolean', + type: 'switch', group: '{{ _('Definition') }}' + },{ + id: 'sysdomain', label:'{{ _('System Domain?') }}', cell: 'boolean', + type: 'switch', group: '{{ _('Definition') }}', mode: ['properties'] + },{ + id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', group: '{{ _('Definition') }}', url: 'get_collations', disabled: function(m) { + return !m.isNew(); + } + },{ + id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string', + type: 'collection', group: '{{ _('Constraints') }}', visible: false, mode: ['edit', 'create'], + model: ConstraintsModel, canAdd: true, canDelete: true, canEdit: function(o) { + if (o instanceof Backbone.Model) { + if (o instanceof ConstraintsModel) { + return o.isNew(); + } + } + return true; + } + },{ + id: 'seclabels', label: '{{ _('Security Labels') }}', + model: SecurityModel, type: 'collection', + group: '{{ _('Security') }}', mode: ['edit', 'create'], + min_version: 90100, canAdd: true, + canEdit: true, canDelete: true + } + ], + validate: function() // Client Side Validation + { + var err = {}, + errmsg, + seclabels = this.get('seclabels'); + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('owner')) || String(this.get('owner')) + .replace(/^\s+|\s+$/g, '') == '') { + err['owner'] = '{{ _('Owner can not be empty!') }}'; + errmsg = errmsg || err['owner']; + } + + if (_.isUndefined(this.get('basensp')) || String(this.get('basensp')) + .replace(/^\s+|\s+$/g, '') == '') { + err['basensp'] = '{{ _('Schema can not be empty!') }}'; + errmsg = errmsg || err['basensp']; + } + + if (_.isUndefined(this.get('basetype')) || String(this.get('basetype')).replace(/^\s+|\s+$/g, '') == '') { + err['basetype'] = '{{ _('Base Type can not be empty!') }}'; + errmsg = errmsg || err['basetype']; + } + + if (seclabels) { + var secLabelsErr; + for (var i = 0; i < seclabels.models.length && !secLabelsErr; i++) { + secLabelsErr = (seclabels.models[i]).validate.apply(seclabels.models[i]); + if (secLabelsErr) { + err['seclabels'] = secLabelsErr; + errmsg = errmsg || secLabelsErr; + } + } + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + } + }), + canCreate: function(itemData, item, data) { + //If check is false then , we will allow create menu + if (data && data.check == false) + return true; + + var t = pgBrowser.tree, i = item, d = itemData; + // To iterate over tree to check parent node + while (i) { + // If it is schema then allow user to create domain + if (_.indexOf(['schema'], d._type) > -1) + return true; + + if ('coll-domain' == d._type) { + //Check if we are not child of catalog + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + if( prev_d._type == 'catalog') { + return false; + } else { + return true; + } + } + i = t.hasParent(i) ? t.parent(i) : null; + d = i ? t.itemData(i) : null; + } + // by default we do not want to allow create menu + return true; + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return false; + } + } + return true; + } + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql new file mode 100644 index 0000000..8f3db8e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/backend_support.sql @@ -0,0 +1,20 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM + pg_namespace nsp +WHERE + nsp.oid={{scid}}::int + AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + ) + AND + nspname NOT LIKE E'pg\\temp\\%' + AND + nspname NOT LIKE E'pg\\toast_temp\\%' diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql new file mode 100644 index 0000000..1fe1585 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql @@ -0,0 +1,37 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}} {% if data.precision %}, {{data.precision}}{% endif %}){% endif %}{% if data.collname %} + + COLLATE {{ data.collname and data.collname != "pg_catalog.\"default\"" }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL +{% endif -%}; + +{% if data.constraints %} +{% for c in data.constraints %} +{% if c.conname and c.consrc %}ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}); +{% endif %} +{% endfor %} +{% endif -%} + +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }}; + +{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}'; +{% endif -%} + +{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} + +{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }} +{% endif %} +{% endfor %} +{% endif %} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..4b1e49f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql @@ -0,0 +1,15 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid + AND d.oid={{doid}}::int; +{% endif %} + +{% if name %} + DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade%} CASCADE{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql new file mode 100644 index 0000000..819fdbb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql new file mode 100644 index 0000000..c448844 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql @@ -0,0 +1,29 @@ +SELECT + 'TABLE' AS objectkind, c.oid, conname, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc +FROM + pg_constraint c +JOIN + pg_class cl ON cl.oid=conrelid +JOIN + pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT + 'DOMAIN' AS objectkind, c.oid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as cons +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' +AND contypid = {{doid}}::oid +ORDER BY conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..427b583 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql @@ -0,0 +1,19 @@ +{% if doid %} +SELECT + d.typnamespace as scid +FROM + pg_type d +WHERE + d.oid={{ doid }}::oid; + +{% else %} +SELECT + d.oid, d.typnamespace +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql new file mode 100644 index 0000000..adb3338 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_schemas.sql @@ -0,0 +1,24 @@ +SELECT + nsp.nspname +FROM + pg_namespace nsp +LEFT OUTER JOIN + pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) +LEFT OUTER JOIN + pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid) +WHERE + NOT ((nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + )AND + nspname NOT LIKE E'pg\\temp\\%' AND + nspname NOT LIKE E'pg\\toast_temp\\%' AND + -- ADDED: Because We need to omit system schema except the one on which we are trying to create collation + ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' ) +ORDER BY + nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql new file mode 100644 index 0000000..97621b0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_types.sql @@ -0,0 +1,22 @@ +SELECT + * +FROM + (SELECT + format_type(t.oid,NULL) AS typname, + CASE WHEN typelem > 0 THEN typelem ELSE t.oid END as elemoid, + typlen, typtype, t.oid, nspname, + (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup + FROM + pg_type t + JOIN + pg_namespace nsp ON typnamespace=nsp.oid + WHERE + (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) + AND typisdefined AND typtype IN ('b', 'c', 'e', 'r') + AND NOT EXISTS + (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') + AND (typname not like '_%' OR NOT EXISTS + (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) + ) AS dummy +ORDER BY + nspname <> 'pg_catalog', nspname <> 'public', nspname, 1 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..216fafe --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql @@ -0,0 +1,28 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid + {% if doid %} + AND d.oid={{doid}}::int + {% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql new file mode 100644 index 0000000..81a210b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql @@ -0,0 +1,84 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER TYPE {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} +{% endif -%} + +{% if data.typnotnull and not o_data.typnotnull %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif -%} + +{% if data.typdefault %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault|qtLiteral }}; +{% elif not data.typdefault and o_data.typdefault %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif -%} +{% if data.owner %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }}; +{% endif -%} + +{% if data.constraints %} +{% for c in data.constraints.deleted %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }} +{% endfor %} +{% for c in data.constraints.added %} +{% if c.conname and c.consrc %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} ); +{% endif %} +{% endfor %} +{% endif -%} + +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} + +{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif -%} + +{% if data.description %} + +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }}; +{% endif -%} + +{% if data.basensp %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }}; +{% endif %} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/backend_support.sql new file mode 100644 index 0000000..8f3db8e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/backend_support.sql @@ -0,0 +1,20 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM + pg_namespace nsp +WHERE + nsp.oid={{scid}}::int + AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + ) + AND + nspname NOT LIKE E'pg\\temp\\%' + AND + nspname NOT LIKE E'pg\\toast_temp\\%' diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql new file mode 100644 index 0000000..84819e3 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql @@ -0,0 +1,40 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}} {% if data.precision %}, {{data.precision}}{% endif %}){% endif %}{% if data.collname and data.collname != "pg_catalog.\"default\"" %} + + COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL{% endif -%}; + +{% if data.constraints %} +{% for c in data.constraints %} +{% if c.conname and c.consrc %}ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if c.convalidated %} + + NOT VALID {% endif %}{% if c.connoinherit %} NO INHERIT{% endif %}; +{% endif -%} +{% endfor %} +{% endif -%} + +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }}; + +{% if data.description %} + +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}'; +{% endif -%} + +{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} + +{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data +.basensp) }} +{% endif %} +{% endfor %} +{% endif %} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..7f437ef --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql @@ -0,0 +1,16 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +AND + d.oid={{doid}}::int; +{% endif %} + +{% if name %} + DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade%} CASCADE{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql new file mode 100644 index 0000000..e59c17d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql new file mode 100644 index 0000000..01c8475 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql @@ -0,0 +1,29 @@ +SELECT + 'TABLE' AS objectkind, c.oid, conname, relname, nspname, description, + pg_get_expr(conbin, conrelid, true) as consrc, connoinherit, convalidated +FROM + pg_constraint c +JOIN + pg_class cl ON cl.oid=conrelid +JOIN + pg_namespace nl ON nl.oid=relnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND conrelid = {{doid}}::oid +UNION +SELECT + 'DOMAIN' AS objectkind, c.oid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc, connoinherit, convalidated +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +ORDER BY + conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..427b583 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql @@ -0,0 +1,19 @@ +{% if doid %} +SELECT + d.typnamespace as scid +FROM + pg_type d +WHERE + d.oid={{ doid }}::oid; + +{% else %} +SELECT + d.oid, d.typnamespace +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_schemas.sql new file mode 100644 index 0000000..ec62dd6 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_schemas.sql @@ -0,0 +1,24 @@ +SELECT + nsp.nspname +FROM + pg_namespace nsp +LEFT OUTER JOIN + pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) + LEFT OUTER JOIN + pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid) +WHERE + NOT ((nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR + (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) + )AND + nspname NOT LIKE E'pg\\temp\\%' AND + nspname NOT LIKE E'pg\\toast_temp\\%' AND + -- ADDED: Because We need to omit system schema except the one on which we are trying to create collation + ( nsp.oid = {{ scid }} OR nspname NOT LIKE E'pg\\_%' ) +ORDER BY + nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_types.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_types.sql new file mode 100644 index 0000000..c4d46f7 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_types.sql @@ -0,0 +1,22 @@ +SELECT + * +FROM + (SELECT + format_type(t.oid,NULL) AS typname, + CASE WHEN typelem > 0 THEN typelem ELSE t.oid END as elemoid, + typlen, typtype, t.oid, nspname, + (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup + FROM + pg_type t + JOIN + pg_namespace nsp ON typnamespace=nsp.oid + WHERE + (NOT (typname = 'unknown' AND nspname = 'pg_catalog')) + AND typisdefined AND typtype IN ('b', 'c', 'e', 'r') + AND NOT EXISTS + (select 1 from pg_class where relnamespace=typnamespace and relname = typname and relkind != 'c') + AND (typname not like '_%' OR NOT EXISTS + (select 1 from pg_class where relnamespace=typnamespace and relname = substring(typname from 2)::name and relkind != 'c')) + ) AS dummy +ORDER BY + nspname <> 'pg_catalog', nspname <> 'public', nspname, 1 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..5dd2d01 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql @@ -0,0 +1,29 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT array_agg(provider || '=' || label) FROM pg_shseclabel sl1 WHERE sl1.objoid=d.oid) AS seclabels +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid +{% if doid %} + AND d.oid={{doid}}::int +{% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql new file mode 100644 index 0000000..657859c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql @@ -0,0 +1,82 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} +{% endif -%} +{% if data.typnotnull and not o_data.typnotnull %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif -%} + +{% if data.typdefault %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault|qtLiteral }}; +{% elif not data.typdefault and o_data.typdefault %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif -%} +{% if data.owner %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }}; +{% endif -%} + +{% if data.constraints %} +{% for c in data.constraints.deleted %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }} +{% endfor %} +{% for c in data.constraints.added %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} ) +{% if c.convalidated %} NOT VALID {% endif %} {% if c.connoinherit %} NO INHERIT {% endif %}; +{% endfor %} +{% endif -%} + +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} + +{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} +{% endfor %} +{% endif -%} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif -%} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif -%} + +{% if data.description %} + +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }}; +{% endif -%} + +{% if data.basensp %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }}; +{% endif %} + +{% endif %} diff --git a/web/pgadmin/browser/static/js/datamodel.js b/web/pgadmin/browser/static/js/datamodel.js index 9385adf..61e81ed 100644 --- a/web/pgadmin/browser/static/js/datamodel.js +++ b/web/pgadmin/browser/static/js/datamodel.js @@ -340,7 +340,13 @@ function(_, pgAdmin, $, Backbone) { delete res[k]; } } else { - res[k] = (obj && obj.toJSON()); + if (obj && (obj instanceof Array)) { + res[k] = obj; + } + else + { + res[k] = obj && obj.toJSON(); + } } }); return res; diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js index 4346156..ae807cb 100644 --- a/web/pgadmin/static/js/backform.pgadmin.js +++ b/web/pgadmin/static/js/backform.pgadmin.js @@ -1058,11 +1058,13 @@ if (data.disabled == false && data.canEdit) { var editCell = Backgrid.Extension.ObjectCell.extend({ schema: gridSchema.schema - }); + }), + canEdit = self.field.has('canEdit') && + self.field.get('canEdit') || true; gridSchema.columns.unshift({ name: "pg-backform-edit", label: "", cell : editCell, - cell_priority: -2 + cell_priority: -2, editable: canEdit }); } @@ -1101,7 +1103,7 @@ grid.insertRow({}); var newRow = $(grid.body.rows[collection.length - 1].$el); newRow.attr("class", "new").click(function(e) { - $(this).attr("class", ""); + $(this).attr("class", "editable"); }); $(newRow).pgMakeVisible('backform-tab'); return false; @@ -1528,8 +1530,15 @@ /* * Add empty option as Select2 requires any empty '<option><option>' for * some of its functionality to work and initialize select2 control. + * + * But - not for multiple selection. */ - this.$el.find("select").prepend($('<option></option>')).select2(col.select2); + var s = this.$el.find("select"); + if (!col.select2.multiple) { + s.prepend($('<option></option>')); + } + s.select2(col.select2); + return this; } }); ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-02-25 12:13 Dave Page <[email protected]> parent: Khushboo Vashi <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Dave Page @ 2016-02-25 12:13 UTC (permalink / raw) To: Khushboo Vashi <[email protected]>; +Cc: pgadmin-hackers Hi On Wed, Feb 24, 2016 at 9:24 AM, Khushboo Vashi < [email protected]> wrote: > Hi, > > I have updated the Domain module as below: > > - Used 'NodeByListControl' to get schemas, in domains.js file as suggested > by Ashesh to avoid code redundancy. > > - Applied *'Security Label Macro'* Patch (Implemented by Harshal) and > removed same changes from the Domain Patch. > To test Domain patch, 'Security Label Macro' patch must be applied first > as that is not committed yet. > > Please find attached Domain Module Patch. > Initial feedback: - Owner and schema should be allowed to be left blank (and then default to the current user/schema) - Length and Precision fields should only be enabled if appropriate for the data type. - SQL generation for new Domains doesn't work: - When adding constraints, I should be able to type directly into the grid. Expanding the row should be optional. - The comment column on the constraints grid expands when the text reaches ~50% of the width. It should be a fixed size (and use 100% of the space available, less appropriate margins). - Backend support checks should not special-case Slony schemas. - 4 character indentation not used consistently in SQL templates. - Error seen when saving a domain: "macros/schemas/security.macros" 016-02-25 11:55:10,728: INFO werkzeug: 127.0.0.1 - - [25/Feb/2016 11:55:10] "GET /browser/domain/msql/1/1/24587/2200/?name=email&owner=postgres&basensp=public&description=This+is+an+email+data+type&basetype=text&typlen=&precision=&typdefault=&typnotnull=true&collname=&constraints=%5B%5D&seclabels=%5B%5D&_=1456401124386 HTTP/1.1" 500 - Traceback (most recent call last): File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", line 1836, in __call__ return self.wsgi_app(environ, start_response) File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", line 1820, in wsgi_app response = self.make_response(self.handle_exception(e)) File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", line 1403, in handle_exception reraise(exc_type, exc_value, tb) File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", line 1817, in wsgi_app response = self.full_dispatch_request() File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", line 1477, in full_dispatch_request rv = self.handle_user_exception(e) File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", line 1381, in handle_user_exception reraise(exc_type, exc_value, tb) File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", line 1475, in full_dispatch_request rv = self.dispatch_request() File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", line 1461, in dispatch_request return self.view_functions[rule.endpoint](**req.view_args) File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/views.py", line 84, in view return self.dispatch_request(*args, **kwargs) File "/Users/dpage/git/pgadmin4/web/pgadmin/browser/utils.py", line 248, in dispatch_request return method(*args, **kwargs) File "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py", line 277, in wrap return f(*args, **kwargs) File "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py", line 232, in wrap return f(self, **kwargs) File "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py", line 700, in msql status=200 File "/Users/dpage/git/pgadmin4/web/pgadmin/utils/ajax.py", line 41, in make_json_response response=json.dumps(doc, cls=DataTypeJSONEncoder), File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/simplejson/__init__.py", line 386, in dumps **kw).encode(obj) File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/simplejson/encoder.py", line 269, in encode chunks = self.iterencode(o, _one_shot=True) File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/simplejson/encoder.py", line 348, in iterencode return _iterencode(o, 0) File "/Users/dpage/git/pgadmin4/web/pgadmin/utils/ajax.py", line 26, in default return json.JSONEncoder.default(self, obj) File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/simplejson/encoder.py", line 246, in default raise TypeError(repr(o) + " is not JSON serializable") TypeError: TemplateNotFound() is not JSON serializable -- Dave Page Blog: http://pgsnake.blogspot.com Twitter: @pgsnake EnterpriseDB UK: http://www.enterprisedb.com The Enterprise PostgreSQL Company ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-02-25 13:30 Dave Page <[email protected]> parent: Dave Page <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Dave Page @ 2016-02-25 13:30 UTC (permalink / raw) To: Khushboo Vashi <[email protected]>; +Cc: pgadmin-hackers Per discussion with Khushboo, the patch at http://www.postgresql.org/message-id/attachment/41939/schemas_macros_10_Feb_2.patch is a pre-req for this. Updated comments below... On Thu, Feb 25, 2016 at 12:13 PM, Dave Page <[email protected]> wrote: > Hi > > On Wed, Feb 24, 2016 at 9:24 AM, Khushboo Vashi < > [email protected]> wrote: > >> Hi, >> >> I have updated the Domain module as below: >> >> - Used 'NodeByListControl' to get schemas, in domains.js file as >> suggested by Ashesh to avoid code redundancy. >> >> - Applied *'Security Label Macro'* Patch (Implemented by Harshal) and >> removed same changes from the Domain Patch. >> To test Domain patch, 'Security Label Macro' patch must be applied >> first as that is not committed yet. >> >> Please find attached Domain Module Patch. >> > > Initial feedback: > > - Owner and schema should be allowed to be left blank (and then default to > the current user/schema) > > - Length and Precision fields should only be enabled if appropriate for > the data type. > The above still apply. > > - SQL generation for new Domains doesn't work: > This now works. > > - When adding constraints, I should be able to type directly into the > grid. Expanding the row should be optional. > > - The comment column on the constraints grid expands when the text reaches > ~50% of the width. It should be a fixed size (and use 100% of the space > available, less appropriate margins). > > - Backend support checks should not special-case Slony schemas. > > - 4 character indentation not used consistently in SQL templates. > These still apply. > > - Error seen when saving a domain: "macros/schemas/security.macros" > > 016-02-25 11:55:10,728: INFO werkzeug: 127.0.0.1 - - [25/Feb/2016 > 11:55:10] "GET > /browser/domain/msql/1/1/24587/2200/?name=email&owner=postgres&basensp=public&description=This+is+an+email+data+type&basetype=text&typlen=&precision=&typdefault=&typnotnull=true&collname=&constraints=%5B%5D&seclabels=%5B%5D&_=1456401124386 > HTTP/1.1" 500 - > Traceback (most recent call last): > File > "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", > line 1836, in __call__ > return self.wsgi_app(environ, start_response) > File > "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", > line 1820, in wsgi_app > response = self.make_response(self.handle_exception(e)) > File > "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", > line 1403, in handle_exception > reraise(exc_type, exc_value, tb) > File > "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", > line 1817, in wsgi_app > response = self.full_dispatch_request() > File > "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", > line 1477, in full_dispatch_request > rv = self.handle_user_exception(e) > File > "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", > line 1381, in handle_user_exception > reraise(exc_type, exc_value, tb) > File > "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", > line 1475, in full_dispatch_request > rv = self.dispatch_request() > File > "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", > line 1461, in dispatch_request > return self.view_functions[rule.endpoint](**req.view_args) > File > "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/views.py", > line 84, in view > return self.dispatch_request(*args, **kwargs) > File "/Users/dpage/git/pgadmin4/web/pgadmin/browser/utils.py", line 248, > in dispatch_request > return method(*args, **kwargs) > File > "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py", > line 277, in wrap > return f(*args, **kwargs) > File > "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py", > line 232, in wrap > return f(self, **kwargs) > File > "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py", > line 700, in msql > status=200 > File "/Users/dpage/git/pgadmin4/web/pgadmin/utils/ajax.py", line 41, in > make_json_response > response=json.dumps(doc, cls=DataTypeJSONEncoder), > File > "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/simplejson/__init__.py", > line 386, in dumps > **kw).encode(obj) > File > "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/simplejson/encoder.py", > line 269, in encode > chunks = self.iterencode(o, _one_shot=True) > File > "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/simplejson/encoder.py", > line 348, in iterencode > return _iterencode(o, 0) > File "/Users/dpage/git/pgadmin4/web/pgadmin/utils/ajax.py", line 26, in > default > return json.JSONEncoder.default(self, obj) > File > "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/simplejson/encoder.py", > line 246, in default > raise TypeError(repr(o) + " is not JSON serializable") > TypeError: TemplateNotFound() is not JSON serializable > This issue is resolved. Additional issues: - We can add a comment to constraints (and view them), however they are not saved. - The domain is not created as a single SQL statement, but by creating a domain over the base type, then adding constraints. Can this be done in one query? - Reverse engineered SQL doesn't include the normal header and commented-out drop statement. Thanks. -- Dave Page Blog: http://pgsnake.blogspot.com Twitter: @pgsnake EnterpriseDB UK: http://www.enterprisedb.com The Enterprise PostgreSQL Company ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-16 09:18 Khushboo Vashi <[email protected]> parent: Dave Page <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Khushboo Vashi @ 2016-03-16 09:18 UTC (permalink / raw) To: Dave Page <[email protected]>; +Cc: pgadmin-hackers Hi, Please find the updated patch for the Domain Module. To test this, the Data-type Reader patch needs to be applied first. Also, please find in-line comments below. Thanks, Khushboo On Thu, Feb 25, 2016 at 7:00 PM, Dave Page <[email protected]> wrote: > Per discussion with Khushboo, the patch at > http://www.postgresql.org/message-id/attachment/41939/schemas_macros_10_Feb_2.patch > is a pre-req for this. Updated comments below... > > On Thu, Feb 25, 2016 at 12:13 PM, Dave Page <[email protected]> wrote: > >> Hi >> >> On Wed, Feb 24, 2016 at 9:24 AM, Khushboo Vashi < >> [email protected]> wrote: >> >>> Hi, >>> >>> I have updated the Domain module as below: >>> >>> - Used 'NodeByListControl' to get schemas, in domains.js file as >>> suggested by Ashesh to avoid code redundancy. >>> >>> - Applied *'Security Label Macro'* Patch (Implemented by Harshal) and >>> removed same changes from the Domain Patch. >>> To test Domain patch, 'Security Label Macro' patch must be applied >>> first as that is not committed yet. >>> >>> Please find attached Domain Module Patch. >>> >> >> Initial feedback: >> >> - Owner and schema should be allowed to be left blank (and then default >> to the current user/schema) >> > Done > - Length and Precision fields should only be enabled if appropriate for >> the data type. >> > Done > The above still apply. > > >> >> - SQL generation for new Domains doesn't work: >> > > This now works. > > >> >> - When adding constraints, I should be able to type directly into the >> grid. Expanding the row should be optional. >> > I have made the grid non-editable explicitly as the Check constraint control is multi-line control and right now there is no support in the grid for the multi-line control. > - The comment column on the constraints grid expands when the text reaches >> ~50% of the width. It should be a fixed size (and use 100% of the space >> available, less appropriate margins) >> > I have applied the size for the each header of the grid, but if the given input will be without space in the grid then it will expand. For this, we can make table layout fixed. So, please suggest, should I do that or not? > - Backend support checks should not special-case Slony schemas. >> >> Done > - 4 character indentation not used consistently in SQL templates. >> > Done > These still apply. > > >> >> - Error seen when saving a domain: "macros/schemas/security.macros" >> >> 016-02-25 11:55:10,728: INFO werkzeug: 127.0.0.1 - - [25/Feb/2016 >> 11:55:10] "GET >> /browser/domain/msql/1/1/24587/2200/?name=email&owner=postgres&basensp=public&description=This+is+an+email+data+type&basetype=text&typlen=&precision=&typdefault=&typnotnull=true&collname=&constraints=%5B%5D&seclabels=%5B%5D&_=1456401124386 >> HTTP/1.1" 500 - >> Traceback (most recent call last): >> File >> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", >> line 1836, in __call__ >> return self.wsgi_app(environ, start_response) >> File >> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", >> line 1820, in wsgi_app >> response = self.make_response(self.handle_exception(e)) >> File >> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", >> line 1403, in handle_exception >> reraise(exc_type, exc_value, tb) >> File >> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", >> line 1817, in wsgi_app >> response = self.full_dispatch_request() >> File >> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", >> line 1477, in full_dispatch_request >> rv = self.handle_user_exception(e) >> File >> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", >> line 1381, in handle_user_exception >> reraise(exc_type, exc_value, tb) >> File >> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", >> line 1475, in full_dispatch_request >> rv = self.dispatch_request() >> File >> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py", >> line 1461, in dispatch_request >> return self.view_functions[rule.endpoint](**req.view_args) >> File >> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/views.py", >> line 84, in view >> return self.dispatch_request(*args, **kwargs) >> File "/Users/dpage/git/pgadmin4/web/pgadmin/browser/utils.py", line >> 248, in dispatch_request >> return method(*args, **kwargs) >> File >> "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py", >> line 277, in wrap >> return f(*args, **kwargs) >> File >> "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py", >> line 232, in wrap >> return f(self, **kwargs) >> File >> "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py", >> line 700, in msql >> status=200 >> File "/Users/dpage/git/pgadmin4/web/pgadmin/utils/ajax.py", line 41, in >> make_json_response >> response=json.dumps(doc, cls=DataTypeJSONEncoder), >> File >> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/simplejson/__init__.py", >> line 386, in dumps >> **kw).encode(obj) >> File >> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/simplejson/encoder.py", >> line 269, in encode >> chunks = self.iterencode(o, _one_shot=True) >> File >> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/simplejson/encoder.py", >> line 348, in iterencode >> return _iterencode(o, 0) >> File "/Users/dpage/git/pgadmin4/web/pgadmin/utils/ajax.py", line 26, in >> default >> return json.JSONEncoder.default(self, obj) >> File >> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/simplejson/encoder.py", >> line 246, in default >> raise TypeError(repr(o) + " is not JSON serializable") >> TypeError: TemplateNotFound() is not JSON serializable >> > > This issue is resolved. > > Additional issues: > > - We can add a comment to constraints (and view them), however they are > not saved. > > Done > - The domain is not created as a single SQL statement, but by creating a > domain over the base type, then adding constraints. Can this be done in one > query? > Done > - Reverse engineered SQL doesn't include the normal header and > commented-out drop statement. > Done > Thanks. > > -- > Dave Page > Blog: http://pgsnake.blogspot.com > Twitter: @pgsnake > > EnterpriseDB UK: http://www.enterprisedb.com > The Enterprise PostgreSQL Company > -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers Attachments: [text/x-patch] domains_ver_4.patch (99.2K, 3-domains_ver_4.patch) download | inline diff: diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py new file mode 100644 index 0000000..2c273e4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py @@ -0,0 +1,806 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements the Domain Node.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas as schemas +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from pgadmin.browser.server_groups.servers.databases.schemas.utils import \ + SchemaChildModule, DataTypeReader +from pgadmin.browser.server_groups.servers.databases.utils import \ + parse_sec_labels_from_db +from functools import wraps + + +class DomainModule(SchemaChildModule): + """ + class DomainModule(SchemaChildModule): + + This class represents The Domain Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Module. + + * get_nodes(gid, sid, did, scid) + - Generate the domain collection node. + + * script_load() + - Load the module script for domain, when schema node is + initialized. + """ + + NODE_TYPE = 'domain' + COLLECTION_LABEL = gettext("Domains") + + def __init__(self, *args, **kwargs): + super(DomainModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid): + """ + Generate the domain collection node. + """ + yield self.generate_browser_collection_node(scid) + + @property + def script_load(self): + """ + Load the module script for domain, when schema node is + initialized. + """ + return schemas.SchemaModule.NODE_TYPE + + +blueprint = DomainModule(__name__) + + +class DomainView(PGChildNodeView, DataTypeReader): + """ + class DomainView + + This class inherits PGChildNodeView to get the different routes for + the module. Also, inherits DataTypeReader to get data types. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain. + + Methods: + ------- + * validate_request(f): + - Works as a decorator. + Validating request on the request of create, update and modified SQL. + + * module_js(): + - Load JS file (domains.js) for this module. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domains. + + * nodes(gid, sid, did, scid): + - Returns all the Domains to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain properties. + + * get_collations(gid, sid, did, scid, doid=None): + - Returns Collations. + + * create(gid, sid, did, scid): + - Creates a new Domain object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain object. + + * dependents(gid, sid, did, scid, doid): + - Returns the dependents for the Domain object. + + * dependencies(gid, sid, did, scid, doid): + - Returns the dependencies for the Domain object. + + * types(gid, sid, did, scid, fnid=None): + - Returns Data Types. + """ + + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'doid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_types': [{'get': 'types'}, {'get': 'types'}], + 'get_collations': [ + {'get': 'get_collations'}, + {'get': 'get_collations'} + ] + }) + + def validate_request(f): + """ + Works as a decorator. + Validating request on the request of create, update and modified SQL. + + Required Args: + name: Name of the Domain + owner: Domain Owner + basensp: Schema Name + basetype: Data Type of the Domain + + Above both the arguments will not be validated in the update action. + """ + + @wraps(f) + def wrap(self, **kwargs): + + data = {} + if request.data: + req = json.loads(request.data.decode()) + else: + req = request.args or request.form + + if 'doid' not in kwargs: + required_args = [ + 'name', + 'basetype' + ] + + for arg in required_args: + if arg not in req or req[arg] == '': + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter \ + (%s)." % arg + ) + ) + + try: + list_params = [] + if request.method == 'GET': + list_params = ['constraints', 'seclabels'] + + for key in req: + if key in list_params and req[key] != '' \ + and req[key] is not None: + # Coverts string into python list as expected. + data[key] = json.loads(req[key]) + elif key == 'typnotnull': + data[key] = True if req[key] == 'true' or req[key] is\ + True else\ + (False if req[key] == 'false' or req[key] is + False else '') + else: + data[key] = req[key] + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + self.request = data + return f(self, **kwargs) + + return wrap + + def module_js(self): + """ + Load JS file (domains.js) for this module. + """ + return make_response( + render_template( + "domains/js/domains.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + # Get database connection + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domains/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domains/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid): + """ + List the Domains. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid): + """ + Returns all the Domains to generate Nodes in the browser. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + res = [] + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + scid, + row['name'], + icon="icon-domain" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid): + """ + Returns the Domain properties. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + # Get Type Length and Precision + data.update(self._parse_type(data['fulltype'])) + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + # Get formatted Security Labels + if 'seclabels' in data: + data.update(parse_sec_labels_from_db(data['seclabels'])) + + # Set System Domain Status + data['sysdomain'] = False + if doid <= self.manager.db_info[did]['datlastsysoid']: + data['sysdomain'] = True + + return ajax_response( + response=data, + status=200 + ) + + def _parse_type(self, basetype): + """ + Returns Type and Data Type from the basetype. + """ + typ_len = '' + typ_precision = '' + + # The Length and the precision of the Datatype should be separate. + # The Format we getting from database is: numeric(1,1) + # So, we need to separate Length: 1, Precision: 1 + + if basetype != '' and basetype.find("(") > 0: + substr = basetype[basetype.find("(") + 1:len( + basetype) - 1] + typlen = substr.split(",") + if len(typlen) > 1: + typ_len = typlen[0] + typ_precision = typlen[1] + else: + typ_len = typlen + typ_precision = '' + + return {'typlen': typ_len, 'precision': typ_precision} + + @check_precondition + def get_collations(self, gid, sid, did, scid, doid=None): + """ + Returns Collations. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + res = [{'label': '', 'value': ''}] + try: + SQL = render_template("/".join([self.template_path, + 'get_collations.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append({'label': row['copy_collation'], + 'value': row['copy_collation']} + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def types(self, gid, sid, did, scid, doid=None): + """ + Returns the Data Types. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + fnid: Function Id + """ + + condition = """typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r') +AND NOT EXISTS (SELECT 1 FROM pg_class WHERE relnamespace=typnamespace +AND relname = typname AND relkind != 'c') AND +(typname NOT LIKE '_%' OR NOT EXISTS (SELECT 1 FROM pg_class WHERE +relnamespace=typnamespace AND relname = substring(typname FROM 2)::name +AND relkind != 'c'))""" + + if self.blueprint.show_system_objects: + condition += " AND nsp.nspname != 'information_schema'" + + # Get Types + status, types = self.get_types(self.conn, condition) + + if not status: + return internal_server_error(errormsg=types) + + return make_json_response( + data=types, + status=200 + ) + + @check_precondition + @validate_request + def create(self, gid, sid, did, scid): + """ + Creates a new Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + Domain object in json format. + """ + + data = self.request + try: + SQL = render_template("/".join([self.template_path, 'create.sql']), + data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # We need oid to to add object in tree at browser, below sql will + # gives the same + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + basensp=data['basensp'], + name=data['name']) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + doid, scid = res['rows'][0] + + return jsonify( + node=self.blueprint.generate_browser_node( + doid, + scid, + data['name'], + icon="icon-domain" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid): + """ + Drops the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + name, basensp = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + name=name, basensp=basensp, cascade=cascade) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + @validate_request + def update(self, gid, sid, did, scid, doid): + """ + Updates the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if not status: + return internal_server_error(errormsg=SQL) + + try: + if SQL: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get Schema Id + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + scid = res['rows'][0]['scid'] + + return make_json_response( + success=1, + info="Domain updated", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid=None): + """ + Returns the SQL for the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return False, internal_server_error(errormsg=res) + data = res['rows'][0] + + # Get Type Length and Precision + data.update(self._parse_type(data['fulltype'])) + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + # Toggle Validate and inherit options for 'CREATE Query' + for c in data['constraints']: + if 'convalidated' in c: + c['convalidated'] = False if c['convalidated'] else True + if 'connoinherit' in c: + c['connoinherit'] = False if c['connoinherit'] else True + + SQL = render_template("/".join([self.template_path, + 'create.sql']), data=data) + + sql_header = """-- DOMAIN: {0} + +-- DROP DOMAIN {0}; + +""".format(data['basensp'] + '.' + data['name']) + + SQL = sql_header + SQL + + return ajax_response(response=SQL) + + @check_precondition + @validate_request + def msql(self, gid, sid, did, scid, doid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + SQL statements to create/update the Domain. + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid=None): + """ + Generates the SQL statements to create/update the Domain. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + try: + if doid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data) + else: + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data) + return True, SQL + + except Exception as e: + return False, e + + @check_precondition + def dependents(self, gid, sid, did, scid, doid): + """ + This function get the dependents and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependents_result = self.get_dependents(self.conn, doid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid): + """ + This function get the dependencies and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependencies_result = self.get_dependencies(self.conn, doid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py new file mode 100644 index 0000000..d16bb60 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py @@ -0,0 +1,653 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements the Domain Constraint Module.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas.domains \ + as domains +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + + +class DomainConstraintModule(CollectionNodeModule): + """ + class DomainConstraintModule(CollectionNodeModule): + + This class represents The Domain Constraint Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Constraint Module. + + * get_nodes(gid, sid, did, scid) + - Generate the Domain Constraint collection node. + + * node_inode(gid, sid, did, scid) + - Returns Domain Constraint node as leaf node. + + * script_load() + - Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + NODE_TYPE = 'domain-constraints' + COLLECTION_LABEL = gettext("Domain Constraints") + + def __init__(self, *args, **kwargs): + super(DomainConstraintModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid, doid): + """ + Generate the Domain Constraint collection node. + """ + yield self.generate_browser_collection_node(doid) + + @property + def node_inode(self): + """ + Returns Domain Constraint node as leaf node. + """ + return False + + @property + def script_load(self): + """ + Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + return domains.DomainModule.NODE_TYPE + + +blueprint = DomainConstraintModule(__name__) + + +class DomainConstraintView(PGChildNodeView): + """ + class DomainConstraintView(PGChildNodeView): + + This class inherits PGChildNodeView to get the different routes for + the module. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain Constraint. + + Methods: + ------- + + * module_js(): + - Load JS file (domain-constraints.js) for this module. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domain Constraints. + + * nodes(gid, sid, did, scid): + - Returns all the Domain Constraints to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain Constraint properties. + + * create(gid, sid, did, scid): + - Creates a new Domain Constraint object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain Constraint object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain Constraint object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain Constraint object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain Constraint. + object. + + * dependents(gid, sid, did, scid, doid, coid): + - Returns the dependents for the Domain Constraint object. + + * dependencies(gid, sid, did, scid, doid, coid): + - Returns the dependencies for the Domain Constraint object. + """ + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'}, + {'type': 'int', 'id': 'doid'} + ] + ids = [ + {'type': 'int', 'id': 'coid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def module_js(self): + """ + Load JS file (domain-constraints.js) for this module. + """ + return make_response( + render_template( + "domain-constraints/js/domain-constraints.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domain-constraints/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domain-constraints/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid, doid): + """ + List the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid, doid): + """ + Returns all the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + res = [] + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, rset = self.conn.execute_2darray(SQL) + + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + doid, + row['name'], + icon="icon-domain-constraints" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid, coid): + """ + Returns the Domain Constraints property. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + if 'convalidated' in data: + data['convalidated'] = not data['convalidated'] + return ajax_response( + response=data, + status=200 + ) + + @check_precondition + def create(self, gid, sid, did, scid, doid): + """ + Creates a new Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Constraints Name + consrc: Constraints Check + + Returns: + Domain Constraint object in json format. + """ + + data = request.form if request.form else \ + json.loads(request.data.decode()) + required_args = [ + 'name', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + try: + # Get Schema and Domain. + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get the recently added constraints oid + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + doid=doid, name=data['name']) + status, coid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=doid) + + return jsonify( + node=self.blueprint.generate_browser_node( + coid, + doid, + data['name'], + icon="icon-domain-constraints" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid, coid): + """ + Drops the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain Constraint dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, scid, doid, coid): + """ + Updates the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + data = request.form if request.form else \ + json.loads(request.data.decode()) + + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + + try: + if SQL and status: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Domain Constraint updated", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the SQL for the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + # Get Schema and Domain. + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + if 'convalidated' in data: + data['convalidated'] = False if data['convalidated'] else True + if 'connoinherit' in data: + data['connoinherit'] = False if data['connoinherit'] else True + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + + sql_header = """-- CHECK: {0} + +-- ALTER DOMAIN {1} DROP CONSTRAINT {0}; + +""".format(data['name'],schema + '.' + domain) + + SQL = sql_header + SQL + + return ajax_response(response=SQL) + + @check_precondition + def msql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + + Required Args: + name: Constraints Name + consrc: Constraints Check + + Returns: + Domain Constraint object in json format. + """ + data = request.args + + if coid is None: + required_args = [ + 'name', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + if status and SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid, coid=None): + """ + Generates the SQL statements to create/update the Domain Constraint. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + if coid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data, conn=self.conn + ) + else: + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + + if not status: + return False, internal_server_error(errormsg=name) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + return True, SQL + except Exception as e: + return False, internal_server_error(errormsg=str(e)) + + @check_precondition + def dependents(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependents and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependents_result = self.get_dependents(self.conn, coid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependencies and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependencies_result = self.get_dependencies(self.conn, coid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainConstraintView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..d62e13705c50e6c0cf8f19d680053e8643e28751 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=2TrO847{Jl`|t`Qpas zn<hS+l=rMY>RGPqvlydizRJ&>B`Om#V}R-yOM?7@862M7NCR>>3p^r=fwTu0yPeFo z12TL)T^vI=t|uoPU||ZF<tgaHG*QsQ!?m&Tq=?3mCu}J#Dx3x@mM}}^iE=5NIWXnk zkpnC4ai%a>@;Gg7=uums=9bIK=Egd~(us-3g@Iv02gfsK^JP^)gH=mhBT7;dOH!?p zi&B9UgOP!ufv%yEu7P2Qk%5(ov6YF5wt=aYfq}(LRXG$5x%nxXX_XKS29{tAAk|g| XW)KahriZQpYGCkm^>bP0l+XkKyyRU} literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png new file mode 100644 index 0000000000000000000000000000000000000000..32a045b8fafdc08640d53b2a86b1dcabcb0fe0fd GIT binary patch literal 579 zcmV-J0=)f+P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp) z=>Px$AW%$HMR1Z9#Q*@REHmdHAm<<;=p`lTDl6$LE15$|nM6yxJ3#6&F}^)Qx<p3n zH#h4zIK)Ou#79fTM@#HJKkY(8?L$QEL`CjJMeasM?ng(;QB>|oNbX2U?n+AUOH1!e zOz%xi?@dncPEXKRS?^Cz#amtQQBm+xQt(q#@KaRqR8;U)Rq<9<@mE*YVq(`~V)0vB z+GS<)VPW%PV%=$Jag!JHXlV6qZS`($_HuH3pd<EncJ_C7f1@XWqbGu>Du=8uhpjJ) zvN4agH<i3UrMYgWyK<eyN1Vq+p2tbA!G5C3PO!m$q|HvV#D$~JOsv;ftk_qv-CeQX zT*cC%$J3;><6_L$t<Kr8)!w|r@o>cPam4X*+~mi`^K{Glc|xh<NdN!<0d!JMQvg8b z*k%9#00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipf34<ROg z(p72z0056kL_t&-)1{2Z3c^qT1mDEody6IZuCW&s1V!`^4}$goe?(18@b2Db*j*w1 z%4M(p;&Zw?9ZaKAxo*x;COX}<8fzjqKRv^2kF1spymYHMjE2m7Hl|a~emCNwG8(i? z8I#``(kiBrEbh}(Qn8TL2+$}b3Hn^7p`K6R#_6zQ2$?ux;lXBYB>hkN@C%g?4vVBM R$bbL<002ovPDHLkV1min0OSAw literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..9d1d2a061c7948168d7b1c2474d769b31709f1cf GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE08{VY2nhHc^eMaU%j`d zaI$#+HuEKC{pKEZKYn>h(+aIMH^MjGjcs3}f9Cqyt&fvx7AT*)xv^`L;hf{Hi_iP4 zxgK%&V?q65_4c*;8}G%;JMMY<SLM___M4Bi9{E^!<YUpX&n1ga`7PgFwEkdS!(#P< zNn&@N9OqiK(i&(nV@Z%-FoVOh8)-leXMsm#F_88EW4Dvpc0fjir;B5V#O36K1sn!O zhRVf}5jSsGPH?f<xudc|vBoY<&5cb=uTGB9v187J4II+it5?jhn8F{TsCZIWRad$D zg1*K9W%cu2NsD(hEfVT#*wm#pYw;D04+0E(uQE?s`Z6*ZXoqTvYeY#(Vo9o1a#1Rf zVlXl=G|)9P(lsy)F*2|+F}5->(Kax(GBB{1sVaw}AvZrIGp!P$!N3x%0i@c>zzm|{ T)b!9bKn)C@u6{1-oD!M<=4O^k literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js new file mode 100644 index 0000000..166cabc --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js @@ -0,0 +1,149 @@ +// Domain Constraint Module: Collection and Node +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Constraint Collection Node + if (!pgBrowser.Nodes['coll-domain-constraints']) { + var domain_constraints = pgAdmin.Browser.Nodes['coll-domain-constraints'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + type: 'coll-domain-constraints', + columns: ['name', 'description'] + }); + }; + + // Domain Constraint Node + if (!pgBrowser.Nodes['domain-constraints']) { + pgAdmin.Browser.Nodes['domain-constraints'] = pgBrowser.Node.extend({ + type: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + collection_type: 'coll-domain-constraints', + hasSQL: true, + hasDepends: true, + parent_type: ['domain'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + oid: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined, + convalidated_p: undefined + }, + // Domain Constraint Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', type:'text', cell:'string', + disabled: 'isDisabled' + },{ + id: 'oid', label:'{{ _('OID') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'description', label: '{{ _('Comment') }}', type: 'multiline', cell: + 'string', mode: ['properties', 'create', 'edit'], min_version: 90500, + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', cel: + 'string', group: '{{ _('Definition') }}', mode: ['properties', + 'create', 'edit'], disabled: function(m) { return !m.isNew(); } + },{ + id: 'connoinherit', label: '{{ _('No Inherit') }}', type: + 'switch', cell: 'boolean', group: '{{ _('Definition') }}', mode: + ['properties', 'create', 'edit'], disabled: 'isDisabled', + visible: false + },{ + id: 'convalidated', label: "{{ _("Don't Validate") }}", type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', disabled: function(m) { + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return true; + } + else if(!m.get('convalidated')) + { + return true; + } + return false; + } + return true; + }, + mode: ['create', 'edit'], visible: function(m) { return !m.isNew() } + },{ + id: 'convalidated_p', label: '{{ _('Valid?') }}', type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', disabled: 'isDisabled', + mode: ['properties'] + }], + // Client Side Validation + validate: function() { + var err = {}, + errmsg; + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') { + err['consrc'] = '{{ _('Check can not be empty!') }}'; + errmsg = errmsg || err['consrc']; + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return true; + } + } + return false; + } + }), + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql new file mode 100644 index 0000000..be943d2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql @@ -0,0 +1,3 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }});{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..043f011 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql @@ -0,0 +1,14 @@ +SELECT + c.oid, conname AS name, typname AS relname, nspname, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql new file mode 100644 index 0000000..299ba6b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql @@ -0,0 +1,3 @@ +{% if data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql new file mode 100644 index 0000000..513c38d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql @@ -0,0 +1,9 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }}){% if data.convalidated %} + + NOT VALID{% endif %};{% if data.description %} + + +COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON DOMAIN {{ conn|qtIdent(schema, domain) }} + IS '{{ data.description }}';{% endif %}{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..34d8b34 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql @@ -0,0 +1,17 @@ +SELECT + c.oid, conname AS name, typname AS relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc, + connoinherit, convalidated, convalidated AS convalidated_p +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql new file mode 100644 index 0000000..b436f3a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql @@ -0,0 +1,13 @@ +{% set name = o_data.name %} +{% if data.name %} +{% set name = data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif -%}{% if data.convalidated %} + + +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + VALIDATE CONSTRAINT {{ conn|qtIdent(name) }}{% endif -%}{% if data.description %} + + +COMMENT ON CONSTRAINT {{ conn|qtIdent(name) }} ON DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + IS '{{ data.description }}';{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png new file mode 100644 index 0000000000000000000000000000000000000000..55621528a1dba4928538fe5557b9b988ed78d6ab GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}U4T!BE0BJeoqfW=;gF{0PD8_o zi&pN_(K)B7`FPi%2ix`@v9LU(tNUQ*!K1czXOxuggoGT_(#p@zf4Jw!!zHV)`3F7T zd-Um}H}`iRIijcc@Wq#>&ptkR`SszG54Uz6zW4mg?MLsgZ9jbb>E~y!zTSB9`Re1( zjS1S99(_9h@YC4`A5Y)^_^>@J3+MvIk|4ie28U-i(tsS!0*}aIAngIhZYQ(tfQ)ue z7sn8Z%b|U@#hMgESUvs4wKoSxO>~{O=-Yq$8_!r)-^e%4-l6f-Jox1%8}FIVx>FCW zPMVwluQGbkP1bcSwVV0d_?PZbZVis%HeYTWyU*$OvYp$uTc3QLbD?2trOnDuhZa>f z=3d_{(zER9xt(<n3UsBI4SNC?Y!rBW<m2`n>yDRyg$uoE8Qx}bo1D0qw;t#u)e_f; zl9a@fRIB8oR3OD*WMF8ZYiOivU>IU#U}a)#Wn!XjU}|MxU@=ow4n;$5eoAIqB}9XP eC0GMUwUvPxM8m1+p=*E|7(8A5T-G@yGywoRCC2Lj literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png new file mode 100644 index 0000000000000000000000000000000000000000..7521cddeaaaf0ee4e3c60e948078d70e17e06893 GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}T7XZ8E0Deu9eu*V;gF{0K`pIg zc6K`r4IeIAc}Q3HxV`=3y+@xudUJpGkt2F~PoI5!^78A$Cm(L@JbcYR=;4bm_nv>b z{pkI*?T7D#gyiSvKYR7{_S4Tdo_xOg_;X`|_N7Ok&OiKg_QA)~_dlLo85{w$iLoTe zFPOpM*^M+HhqJ&VvKUBvfU(=jY&#$$$<xI#MB;Mq`IABp20RT9wUXkaL(1R(pOn?c z>38<+e>KaN2{%0Jd@sMbY$+13Z&%Z<%!P+*SNu+#({Rr={_KxUhswHdID0QWTCBvD z)^ky;@gu`E#RAC#l`JcnelS0rxkKN7b1B>H56gX)0&P<*ag8WRNi0dVN-jzTQVd20 zh6cKZM!E)uAw~vPCdO7KCfWw3Rt5$ZGgakKH00)|WTsU@G#FTdHGouG8JIydoSGiG Q2B?9-)78&qol`;+03_q3ga7~l literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png new file mode 100644 index 0000000000000000000000000000000000000000..42ca929325854b8f34787425e8094d08c75983bc GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}PJmB{E0BJeoqfW=;gF{0PD8^7 zi&s6`bL8oxH}`iRdHU?*lb2r~J^6Th=ix^$zTSWS<?f>oH+CHUa^}pVf`aWvMt7fm zx&8F>jfd~AY(HF`mUiRG=W9<sUwQB@KR>@QLHo+%PZuA3I{)y~*$1D_JotF({zvHt zo%ulf7)yfuf*Bm1-ADs+I14-?i-EKU7`vU!wgWPXJzX3_Brcbpe=XFcAmSFNeC3q& zl!Z+(RsYN12&-NW{P*^#<rx82_2-+ii&SqfGgO`Jcj@;01@HTH{&&7#v?j}I)=J6m ze`~hppZn7?WA|%z!}T6pf>z0IjbZrS5ICD*275*_bAo)r8t#Vg4A*Lz);xc4JKq03 zXXC}zd)YwiRZCnWN>UO_QmvAUQh^kMk%6IsuAz~xfnkV|ft87|m5GVAfvJ^&fyGQ! qITQ`K`6-!cl@JXEmS7Da)m8>(5DllMhpqu?VDNPHb6Mw<&;$VOR=Fqu literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js new file mode 100644 index 0000000..5037fcf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js @@ -0,0 +1,330 @@ +// Domain Module: Collection and Node. +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', + 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Collection Node + if (!pgBrowser.Nodes['coll-domain']) { + var domains = pgAdmin.Browser.Nodes['coll-domain'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain', + label: '{{ _('Domains') }}', + type: 'coll-domain', + columns: ['name', 'owner', 'description'] + }); + }; + + // Security Model + var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: null, + security_label: null + }, + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text' + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text' + }], + validate: function() { + var err = {}, + errmsg = null, + data = this.toJSON(); + + if (_.isUndefined(data.label) || + _.isNull(data.label) || + String(data.label).replace(/^\s+|\s+$/g, '') == '') { + return _("Please specify the value for all the security providers."); + } + return null; + } + }); + + // Constraint Model + var ConstraintModel = pgAdmin.Browser.Node.Model.extend({ + idAttribute: 'conname', + defaults: { + conname: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined + }, + schema: [{ + id: 'conname', label: '{{ _('Name') }}', type: 'text', cell: 'string', + cellHeaderClasses: 'width_percent_40' + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', + cell: 'string', group: '{{ _('Definition') }}', + cellHeaderClasses: 'width_percent_60' + }], + isEditable: function(m) { + return true + //return _.isUndefined(m.isNew) ? true : m.isNew(); + }, + toJSON: Backbone.Model.prototype.toJSON + }); + + // Domain Node + if (!pgBrowser.Nodes['domain']) { + pgAdmin.Browser.Nodes['domain'] = pgBrowser.Node.extend({ + type: 'domain', + label: '{{ _('Domain') }}', + collection_type: 'coll-domain', + hasSQL: true, + hasDepends: true, + parent_type: ['schema'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + canDropCascade: pgBrowser.Nodes['schema'].canChildDrop, + // Domain Node Model + model: pgAdmin.Browser.Node.Model.extend({ + initialize: function(attrs, args) { + var isNew = (_.size(attrs) === 0); + if (isNew) { + // Set Selected Schema + schema = args.node_info.schema.label + this.set({'basensp': schema}, {silent: true}); + + // Set Current User + var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user; + this.set({'owner': userInfo.name}, {silent: true}); + } + pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments); + }, + defaults: { + name: undefined, + oid: undefined, + owner: undefined, + basensp: undefined, + description: undefined, + basetype: undefined, + typlen: undefined, + precision: undefined, + typdefault: undefined, + typnotnull: undefined, + sysdomain: undefined, + collname: undefined, + constraints: [], + seclabels: [] + }, + type_options: undefined, + // Domain Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'] + },{ + id: 'oid', label:'{{ _('OID') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl, + node: 'role', type: 'text', mode: ['edit', 'create', 'properties'] + },{ + id: 'basensp', label:'{{ _('Schema') }}', cell: 'node-list-by-name', + control: 'node-list-by-name', cache_level: 'database', type: 'text', + node: 'schema' + },{ + id: 'sysdomain', label:'{{ _('System Domain?') }}', cell: 'boolean', + type: 'switch', mode: ['properties'] + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'basetype', label:'{{ _('Base Type') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', mode:['properties', 'create', 'edit'], group: '{{ _('Definition') }}', url: 'get_types', + disabled: function(m) { return !m.isNew(); }, first_empty: true, + transform: function(d){ + this.model.type_options = d; + return d; + } + },{ + id: 'typlen', label:'{{ _('Length') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'], + disabled: function(m) { + // We will store type from selected from combobox + if (!m.isNew()) { + return true; + } + var of_type = m.get('basetype'); + if(m.type_options) { + // iterating over all the types + _.each(m.type_options, function(o) { + // if type from selected from combobox matches in options + if ( of_type == o.value ) { + // if length is allowed for selected type + if(o.length) + { + // set the values in model + m.set('is_tlength', true, {silent: true}); + m.set('min_val', o.min_val, {silent: true}); + m.set('max_val', o.max_val, {silent: true}); + } + } + }); + } + return !m.get('is_tlength'); + } + },{ + id: 'precision', label:'{{ _('Precision') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'], + disabled: function(m) { + // We will store type from selected from combobox + if (!m.isNew()) { + return true; + } + var of_type = m.get('basetype'); + if(m.type_options) { + // iterating over all the types + _.each(m.type_options, function(o) { + // if type from selected from combobox matches in options + if ( of_type == o.value ) { + // if precession is allowed for selected type + if(o.precision) + { + // set the values in model + m.set('is_precision', true, {silent: true}); + m.set('min_val', o.min_val, {silent: true}); + m.set('max_val', o.max_val, {silent: true}); + } + } + }); + } + return !m.get('is_precision'); + } + },{ + id: 'typdefault', label:'{{ _('Default') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}' + },{ + id: 'typnotnull', label:'{{ _('Not Null') }}', cell: 'boolean', + type: 'switch', group: '{{ _('Definition') }}' + },{ + id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', group: '{{ _('Definition') }}', url: 'get_collations', disabled: function(m) { + return !m.isNew(); + } + },{ + id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string', + type: 'collection', group: '{{ _('Constraints') }}', mode: ['edit', 'create'], + model: ConstraintModel, canAdd: true, canDelete: true, + canEdit: function(o){ + if (o instanceof Backbone.Model) { + if (o instanceof ConstraintModel) { + return o.isNew(); + } + } + return true; + } + },{ + id: 'seclabels', label: '{{ _('Security Labels') }}', + model: SecurityModel, type: 'collection', + group: '{{ _('Security') }}', mode: ['edit', 'create'], + min_version: 90100, canAdd: true, + canEdit: true, canDelete: true + } + ], + validate: function() // Client Side Validation + { + var err = {}, + errmsg, + seclabels = this.get('seclabels'); + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('basetype')) || String(this.get('basetype')).replace(/^\s+|\s+$/g, '') == '') { + err['basetype'] = '{{ _('Base Type can not be empty!') }}'; + errmsg = errmsg || err['basetype']; + } + + if (seclabels) { + var secLabelsErr; + for (var i = 0; i < seclabels.models.length && !secLabelsErr; i++) { + secLabelsErr = (seclabels.models[i]).validate.apply(seclabels.models[i]); + if (secLabelsErr) { + err['seclabels'] = secLabelsErr; + errmsg = errmsg || secLabelsErr; + } + } + } + + this.errorModel.clear().set(err); + + return null; + } + }), + canCreate: function(itemData, item, data) { + //If check is false then , we will allow create menu + if (data && data.check == false) + return true; + + var t = pgBrowser.tree, i = item, d = itemData; + // To iterate over tree to check parent node + while (i) { + // If it is schema then allow user to create domain + if (_.indexOf(['schema'], d._type) > -1) + return true; + + if ('coll-domain' == d._type) { + //Check if we are not child of catalog + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + if( prev_d._type == 'catalog') { + return false; + } else { + return true; + } + } + i = t.hasParent(i) ? t.parent(i) : null; + d = i ? t.itemData(i) : null; + } + // by default we do not want to allow create menu + return true; + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return false; + } + } + return true; + } + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql new file mode 100644 index 0000000..f8b0b75 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql @@ -0,0 +1,30 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname %} + + COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL{% endif %}{% if data.constraints %}{% for c in data.constraints %}{% if c.conname and c.consrc %} + + CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% endif -%} +{% endfor -%} +{% endif -%}; + +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.description %} + + +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}';{% endif -%}{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} + + +{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%} +{% endfor -%} +{% endif -%} + +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..7a12b50 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql @@ -0,0 +1,16 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +AND + d.oid={{doid}}::int; +{% endif %} + +{% if name %} +DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql new file mode 100644 index 0000000..819fdbb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql new file mode 100644 index 0000000..f17f0c5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql @@ -0,0 +1,15 @@ +SELECT + 'DOMAIN' AS objectkind, c.oid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as cons +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' + AND contypid = {{doid}}::oid +ORDER BY conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..8b5c891 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql @@ -0,0 +1,18 @@ +{% if doid %} +SELECT + d.typnamespace as scid +FROM + pg_type d +WHERE + d.oid={{ doid }}::oid; +{% else %} +SELECT + d.oid, d.typnamespace +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..42af39d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql @@ -0,0 +1,35 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, + pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT + array_agg(provider || '=' || label) + FROM + pg_seclabel sl1 + WHERE + sl1.objoid=d.oid) AS seclabels +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid + {% if doid %} + AND d.oid={{doid}}::int + {% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql new file mode 100644 index 0000000..858ae91 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql @@ -0,0 +1,65 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER TYPE {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} + +{% endif -%} +{% if data.typnotnull and not o_data.typnotnull %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif -%}{% if data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault }}; +{% elif not data.typdefault and o_data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif -%}{% if data.owner %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }};{% endif -%}{% if data.constraints %} +{% for c in data.constraints.deleted %} + + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }}; +{% endfor -%} +{% for c in data.constraints.added %} +{% if c.conname and c.consrc %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} );{% endif -%} +{% endfor -%}{% endif -%} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} + +{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} +{% endfor -%} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} + +{% endfor -%} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} + +{% endfor -%} +{% endif -%}{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }};{% endif %}{% if data.basensp %} + + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%} +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql new file mode 100644 index 0000000..e89369f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql @@ -0,0 +1,30 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname and data.collname != "pg_catalog.\"default\"" %} + + COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL{% endif %}{% if data.constraints %}{% for c in data.constraints %}{% if c.conname and c.consrc %} + + CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% endif -%} +{% endfor -%} +{% endif -%}; + +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.description %} + + +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}';{% endif -%}{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} + + +{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%} +{% endfor -%} +{% endif -%} + +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..7a12b50 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql @@ -0,0 +1,16 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +AND + d.oid={{doid}}::int; +{% endif %} + +{% if name %} +DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql new file mode 100644 index 0000000..e59c17d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql new file mode 100644 index 0000000..29a203c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql @@ -0,0 +1,15 @@ +SELECT + 'DOMAIN' AS objectkind, c.oid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc, connoinherit, convalidated +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +ORDER BY + conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..8b5c891 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql @@ -0,0 +1,18 @@ +{% if doid %} +SELECT + d.typnamespace as scid +FROM + pg_type d +WHERE + d.oid={{ doid }}::oid; +{% else %} +SELECT + d.oid, d.typnamespace +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..2892988 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql @@ -0,0 +1,34 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT + array_agg(provider || '=' || label) + FROM + pg_shseclabel sl1 + WHERE + sl1.objoid=d.oid) AS seclabels +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid +{% if doid %} + AND d.oid={{doid}}::int +{% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql new file mode 100644 index 0000000..a09cef5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql @@ -0,0 +1,66 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} + +{% endif -%} +{% if data.typnotnull and not o_data.typnotnull %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif -%}{% if data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault }}; +{% elif not data.typdefault and o_data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif -%}{% if data.owner %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }};{% endif -%}{% if data.constraints %} +{% for c in data.constraints.deleted %} + + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }}; +{% endfor -%} +{% for c in data.constraints.added %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if c.convalidated %} + NOT VALID{% endif %}{% if c.connoinherit %} NO INHERIT{% endif -%}; +{% endfor -%}{% endif -%} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} + +{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} +{% endfor %} +{% endif -%} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif -%} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif -%}{% if data.description %} + +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }};{% endif -%}{% if data.basensp %} + + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%} +{% endif -%} ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-16 09:25 Dave Page <[email protected]> parent: Khushboo Vashi <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Dave Page @ 2016-03-16 09:25 UTC (permalink / raw) To: Khushboo Vashi <[email protected]>; +Cc: pgadmin-hackers Hi On Wed, Mar 16, 2016 at 9:18 AM, Khushboo Vashi <[email protected]> wrote: > >>> - Owner and schema should be allowed to be left blank (and then default >>> to the current user/schema) > > Done Oh, sorry - that design changed a while back, and I've updated all the existing nodes already. I thought I'd mentioned that. All we do now is pre-set the default values for those two fields. >>> - When adding constraints, I should be able to type directly into the >>> grid. Expanding the row should be optional. > > I have made the grid non-editable explicitly as the Check constraint control > is multi-line control and right now there is no support in the grid for the > multi-line control. Not sure I follow - the mockup design you sent months ago allowed you to type into the grid, and expand a row to show all fields if you wanted. That is an *absolutely essential* feature enhancement for pgAdmin 4 - it's required by the table design (though this will change a little in other ways, like positioning of the expand row button), and should be used here: https://www.lucidchart.com/documents/edit/610ce42d-c397-48ff-a5e7-bd92c4995715/0 >>> - The comment column on the constraints grid expands when the text >>> reaches ~50% of the width. It should be a fixed size (and use 100% of the >>> space available, less appropriate margins) > > I have applied the size for the each header of the grid, but if the given > input will be without space in the grid then it will expand. For this, we > can make table layout fixed. So, please suggest, should I do that or not? Yes, I think it should be fixed. If the grid row is expanded, presumably it'll show in a multi-line field anyway? Plus the properties will use a multi-line field as well. -- Dave Page Blog: http://pgsnake.blogspot.com Twitter: @pgsnake EnterpriseDB UK: http://www.enterprisedb.com The Enterprise PostgreSQL Company -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-16 09:32 Khushboo Vashi <[email protected]> parent: Dave Page <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Khushboo Vashi @ 2016-03-16 09:32 UTC (permalink / raw) To: Dave Page <[email protected]>; +Cc: pgadmin-hackers Hi, On Wed, Mar 16, 2016 at 2:55 PM, Dave Page <[email protected]> wrote: > Hi > > On Wed, Mar 16, 2016 at 9:18 AM, Khushboo Vashi > <[email protected]> wrote: > > > >>> - Owner and schema should be allowed to be left blank (and then default > >>> to the current user/schema) > > > > Done > > Oh, sorry - that design changed a while back, and I've updated all the > existing nodes already. I thought I'd mentioned that. All we do now is > pre-set the default values for those two fields. > I have done in this way only. Sorry for the misunderstanding. > >>> - When adding constraints, I should be able to type directly into the > >>> grid. Expanding the row should be optional. > > > > I have made the grid non-editable explicitly as the Check constraint > control > > is multi-line control and right now there is no support in the grid for > the > > multi-line control. > > Not sure I follow - the mockup design you sent months ago allowed you > to type into the grid, and expand a row to show all fields if you > wanted. That is an *absolutely essential* feature enhancement for > pgAdmin 4 - it's required by the table design (though this will change > a little in other ways, like positioning of the expand row button), > and should be used here: > > https://www.lucidchart.com/documents/edit/610ce42d-c397-48ff-a5e7-bd92c4995715/0 > > All other controls other than text-area are supported in back-grid. I will try to incorporate text-area as well, so we can directly type into the grid for this control also. > >>> - The comment column on the constraints grid expands when the text > >>> reaches ~50% of the width. It should be a fixed size (and use 100% of > the > >>> space available, less appropriate margins) > > > > I have applied the size for the each header of the grid, but if the given > > input will be without space in the grid then it will expand. For this, we > > can make table layout fixed. So, please suggest, should I do that or not? > > Yes, I think it should be fixed. If the grid row is expanded, > presumably it'll show in a multi-line field anyway? Plus the > properties will use a multi-line field as well. > > Okay. > -- > Dave Page > Blog: http://pgsnake.blogspot.com > Twitter: @pgsnake > > EnterpriseDB UK: http://www.enterprisedb.com > The Enterprise PostgreSQL Company > ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-16 14:03 Khushboo Vashi <[email protected]> parent: Khushboo Vashi <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Khushboo Vashi @ 2016-03-16 14:03 UTC (permalink / raw) To: Dave Page <[email protected]>; +Cc: pgadmin-hackers Hi, Please find the updated Domain Module Patch. To test this patch, please apply Backgrid Textarea Cell Patch before this. Thanks, Khushboo On Wed, Mar 16, 2016 at 3:02 PM, Khushboo Vashi < [email protected]> wrote: > Hi, > > On Wed, Mar 16, 2016 at 2:55 PM, Dave Page <[email protected]> wrote: > >> Hi >> >> On Wed, Mar 16, 2016 at 9:18 AM, Khushboo Vashi >> <[email protected]> wrote: >> > >> >>> - Owner and schema should be allowed to be left blank (and then >> default >> >>> to the current user/schema) >> > >> > Done >> >> Oh, sorry - that design changed a while back, and I've updated all the >> existing nodes already. I thought I'd mentioned that. All we do now is >> pre-set the default values for those two fields. >> > I have done in this way only. Sorry for the misunderstanding. > >> >>> - When adding constraints, I should be able to type directly into the >> >>> grid. Expanding the row should be optional. >> > >> > I have made the grid non-editable explicitly as the Check constraint >> control >> > is multi-line control and right now there is no support in the grid for >> the >> > multi-line control. >> >> Not sure I follow - the mockup design you sent months ago allowed you >> to type into the grid, and expand a row to show all fields if you >> wanted. That is an *absolutely essential* feature enhancement for >> pgAdmin 4 - it's required by the table design (though this will change >> a little in other ways, like positioning of the expand row button), >> and should be used here: >> >> https://www.lucidchart.com/documents/edit/610ce42d-c397-48ff-a5e7-bd92c4995715/0 >> >> All other controls other than text-area are supported in back-grid. > I will try to incorporate text-area as well, so we can directly type into > the grid for this control also. > > >> >>> - The comment column on the constraints grid expands when the text >> >>> reaches ~50% of the width. It should be a fixed size (and use 100% of >> the >> >>> space available, less appropriate margins) >> > >> > I have applied the size for the each header of the grid, but if the >> given >> > input will be without space in the grid then it will expand. For this, >> we >> > can make table layout fixed. So, please suggest, should I do that or >> not? >> >> Yes, I think it should be fixed. If the grid row is expanded, >> presumably it'll show in a multi-line field anyway? Plus the >> properties will use a multi-line field as well. >> >> Okay. > >> -- >> Dave Page >> Blog: http://pgsnake.blogspot.com >> Twitter: @pgsnake >> >> EnterpriseDB UK: http://www.enterprisedb.com >> The Enterprise PostgreSQL Company >> > > -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers Attachments: [text/x-patch] Domains_ver_5.patch (99.3K, 3-Domains_ver_5.patch) download | inline diff: diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py new file mode 100644 index 0000000..2c273e4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py @@ -0,0 +1,806 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements the Domain Node.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas as schemas +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from pgadmin.browser.server_groups.servers.databases.schemas.utils import \ + SchemaChildModule, DataTypeReader +from pgadmin.browser.server_groups.servers.databases.utils import \ + parse_sec_labels_from_db +from functools import wraps + + +class DomainModule(SchemaChildModule): + """ + class DomainModule(SchemaChildModule): + + This class represents The Domain Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Module. + + * get_nodes(gid, sid, did, scid) + - Generate the domain collection node. + + * script_load() + - Load the module script for domain, when schema node is + initialized. + """ + + NODE_TYPE = 'domain' + COLLECTION_LABEL = gettext("Domains") + + def __init__(self, *args, **kwargs): + super(DomainModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid): + """ + Generate the domain collection node. + """ + yield self.generate_browser_collection_node(scid) + + @property + def script_load(self): + """ + Load the module script for domain, when schema node is + initialized. + """ + return schemas.SchemaModule.NODE_TYPE + + +blueprint = DomainModule(__name__) + + +class DomainView(PGChildNodeView, DataTypeReader): + """ + class DomainView + + This class inherits PGChildNodeView to get the different routes for + the module. Also, inherits DataTypeReader to get data types. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain. + + Methods: + ------- + * validate_request(f): + - Works as a decorator. + Validating request on the request of create, update and modified SQL. + + * module_js(): + - Load JS file (domains.js) for this module. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domains. + + * nodes(gid, sid, did, scid): + - Returns all the Domains to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain properties. + + * get_collations(gid, sid, did, scid, doid=None): + - Returns Collations. + + * create(gid, sid, did, scid): + - Creates a new Domain object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain object. + + * dependents(gid, sid, did, scid, doid): + - Returns the dependents for the Domain object. + + * dependencies(gid, sid, did, scid, doid): + - Returns the dependencies for the Domain object. + + * types(gid, sid, did, scid, fnid=None): + - Returns Data Types. + """ + + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'doid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_types': [{'get': 'types'}, {'get': 'types'}], + 'get_collations': [ + {'get': 'get_collations'}, + {'get': 'get_collations'} + ] + }) + + def validate_request(f): + """ + Works as a decorator. + Validating request on the request of create, update and modified SQL. + + Required Args: + name: Name of the Domain + owner: Domain Owner + basensp: Schema Name + basetype: Data Type of the Domain + + Above both the arguments will not be validated in the update action. + """ + + @wraps(f) + def wrap(self, **kwargs): + + data = {} + if request.data: + req = json.loads(request.data.decode()) + else: + req = request.args or request.form + + if 'doid' not in kwargs: + required_args = [ + 'name', + 'basetype' + ] + + for arg in required_args: + if arg not in req or req[arg] == '': + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter \ + (%s)." % arg + ) + ) + + try: + list_params = [] + if request.method == 'GET': + list_params = ['constraints', 'seclabels'] + + for key in req: + if key in list_params and req[key] != '' \ + and req[key] is not None: + # Coverts string into python list as expected. + data[key] = json.loads(req[key]) + elif key == 'typnotnull': + data[key] = True if req[key] == 'true' or req[key] is\ + True else\ + (False if req[key] == 'false' or req[key] is + False else '') + else: + data[key] = req[key] + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + self.request = data + return f(self, **kwargs) + + return wrap + + def module_js(self): + """ + Load JS file (domains.js) for this module. + """ + return make_response( + render_template( + "domains/js/domains.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + # Get database connection + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domains/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domains/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid): + """ + List the Domains. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid): + """ + Returns all the Domains to generate Nodes in the browser. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + res = [] + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + scid, + row['name'], + icon="icon-domain" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid): + """ + Returns the Domain properties. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + # Get Type Length and Precision + data.update(self._parse_type(data['fulltype'])) + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + # Get formatted Security Labels + if 'seclabels' in data: + data.update(parse_sec_labels_from_db(data['seclabels'])) + + # Set System Domain Status + data['sysdomain'] = False + if doid <= self.manager.db_info[did]['datlastsysoid']: + data['sysdomain'] = True + + return ajax_response( + response=data, + status=200 + ) + + def _parse_type(self, basetype): + """ + Returns Type and Data Type from the basetype. + """ + typ_len = '' + typ_precision = '' + + # The Length and the precision of the Datatype should be separate. + # The Format we getting from database is: numeric(1,1) + # So, we need to separate Length: 1, Precision: 1 + + if basetype != '' and basetype.find("(") > 0: + substr = basetype[basetype.find("(") + 1:len( + basetype) - 1] + typlen = substr.split(",") + if len(typlen) > 1: + typ_len = typlen[0] + typ_precision = typlen[1] + else: + typ_len = typlen + typ_precision = '' + + return {'typlen': typ_len, 'precision': typ_precision} + + @check_precondition + def get_collations(self, gid, sid, did, scid, doid=None): + """ + Returns Collations. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + res = [{'label': '', 'value': ''}] + try: + SQL = render_template("/".join([self.template_path, + 'get_collations.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append({'label': row['copy_collation'], + 'value': row['copy_collation']} + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def types(self, gid, sid, did, scid, doid=None): + """ + Returns the Data Types. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + fnid: Function Id + """ + + condition = """typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r') +AND NOT EXISTS (SELECT 1 FROM pg_class WHERE relnamespace=typnamespace +AND relname = typname AND relkind != 'c') AND +(typname NOT LIKE '_%' OR NOT EXISTS (SELECT 1 FROM pg_class WHERE +relnamespace=typnamespace AND relname = substring(typname FROM 2)::name +AND relkind != 'c'))""" + + if self.blueprint.show_system_objects: + condition += " AND nsp.nspname != 'information_schema'" + + # Get Types + status, types = self.get_types(self.conn, condition) + + if not status: + return internal_server_error(errormsg=types) + + return make_json_response( + data=types, + status=200 + ) + + @check_precondition + @validate_request + def create(self, gid, sid, did, scid): + """ + Creates a new Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + Domain object in json format. + """ + + data = self.request + try: + SQL = render_template("/".join([self.template_path, 'create.sql']), + data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # We need oid to to add object in tree at browser, below sql will + # gives the same + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + basensp=data['basensp'], + name=data['name']) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + doid, scid = res['rows'][0] + + return jsonify( + node=self.blueprint.generate_browser_node( + doid, + scid, + data['name'], + icon="icon-domain" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid): + """ + Drops the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + name, basensp = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + name=name, basensp=basensp, cascade=cascade) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + @validate_request + def update(self, gid, sid, did, scid, doid): + """ + Updates the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if not status: + return internal_server_error(errormsg=SQL) + + try: + if SQL: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get Schema Id + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + scid = res['rows'][0]['scid'] + + return make_json_response( + success=1, + info="Domain updated", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid=None): + """ + Returns the SQL for the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return False, internal_server_error(errormsg=res) + data = res['rows'][0] + + # Get Type Length and Precision + data.update(self._parse_type(data['fulltype'])) + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + # Toggle Validate and inherit options for 'CREATE Query' + for c in data['constraints']: + if 'convalidated' in c: + c['convalidated'] = False if c['convalidated'] else True + if 'connoinherit' in c: + c['connoinherit'] = False if c['connoinherit'] else True + + SQL = render_template("/".join([self.template_path, + 'create.sql']), data=data) + + sql_header = """-- DOMAIN: {0} + +-- DROP DOMAIN {0}; + +""".format(data['basensp'] + '.' + data['name']) + + SQL = sql_header + SQL + + return ajax_response(response=SQL) + + @check_precondition + @validate_request + def msql(self, gid, sid, did, scid, doid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + SQL statements to create/update the Domain. + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid=None): + """ + Generates the SQL statements to create/update the Domain. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + try: + if doid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data) + else: + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data) + return True, SQL + + except Exception as e: + return False, e + + @check_precondition + def dependents(self, gid, sid, did, scid, doid): + """ + This function get the dependents and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependents_result = self.get_dependents(self.conn, doid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid): + """ + This function get the dependencies and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependencies_result = self.get_dependencies(self.conn, doid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py new file mode 100644 index 0000000..d16bb60 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py @@ -0,0 +1,653 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements the Domain Constraint Module.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas.domains \ + as domains +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + + +class DomainConstraintModule(CollectionNodeModule): + """ + class DomainConstraintModule(CollectionNodeModule): + + This class represents The Domain Constraint Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Constraint Module. + + * get_nodes(gid, sid, did, scid) + - Generate the Domain Constraint collection node. + + * node_inode(gid, sid, did, scid) + - Returns Domain Constraint node as leaf node. + + * script_load() + - Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + NODE_TYPE = 'domain-constraints' + COLLECTION_LABEL = gettext("Domain Constraints") + + def __init__(self, *args, **kwargs): + super(DomainConstraintModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid, doid): + """ + Generate the Domain Constraint collection node. + """ + yield self.generate_browser_collection_node(doid) + + @property + def node_inode(self): + """ + Returns Domain Constraint node as leaf node. + """ + return False + + @property + def script_load(self): + """ + Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + return domains.DomainModule.NODE_TYPE + + +blueprint = DomainConstraintModule(__name__) + + +class DomainConstraintView(PGChildNodeView): + """ + class DomainConstraintView(PGChildNodeView): + + This class inherits PGChildNodeView to get the different routes for + the module. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain Constraint. + + Methods: + ------- + + * module_js(): + - Load JS file (domain-constraints.js) for this module. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domain Constraints. + + * nodes(gid, sid, did, scid): + - Returns all the Domain Constraints to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain Constraint properties. + + * create(gid, sid, did, scid): + - Creates a new Domain Constraint object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain Constraint object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain Constraint object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain Constraint object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain Constraint. + object. + + * dependents(gid, sid, did, scid, doid, coid): + - Returns the dependents for the Domain Constraint object. + + * dependencies(gid, sid, did, scid, doid, coid): + - Returns the dependencies for the Domain Constraint object. + """ + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'}, + {'type': 'int', 'id': 'doid'} + ] + ids = [ + {'type': 'int', 'id': 'coid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def module_js(self): + """ + Load JS file (domain-constraints.js) for this module. + """ + return make_response( + render_template( + "domain-constraints/js/domain-constraints.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domain-constraints/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domain-constraints/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid, doid): + """ + List the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid, doid): + """ + Returns all the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + res = [] + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, rset = self.conn.execute_2darray(SQL) + + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + doid, + row['name'], + icon="icon-domain-constraints" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid, coid): + """ + Returns the Domain Constraints property. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + if 'convalidated' in data: + data['convalidated'] = not data['convalidated'] + return ajax_response( + response=data, + status=200 + ) + + @check_precondition + def create(self, gid, sid, did, scid, doid): + """ + Creates a new Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Constraints Name + consrc: Constraints Check + + Returns: + Domain Constraint object in json format. + """ + + data = request.form if request.form else \ + json.loads(request.data.decode()) + required_args = [ + 'name', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + try: + # Get Schema and Domain. + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get the recently added constraints oid + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + doid=doid, name=data['name']) + status, coid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=doid) + + return jsonify( + node=self.blueprint.generate_browser_node( + coid, + doid, + data['name'], + icon="icon-domain-constraints" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid, coid): + """ + Drops the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain Constraint dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, scid, doid, coid): + """ + Updates the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + data = request.form if request.form else \ + json.loads(request.data.decode()) + + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + + try: + if SQL and status: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Domain Constraint updated", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the SQL for the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + # Get Schema and Domain. + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + if 'convalidated' in data: + data['convalidated'] = False if data['convalidated'] else True + if 'connoinherit' in data: + data['connoinherit'] = False if data['connoinherit'] else True + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + + sql_header = """-- CHECK: {0} + +-- ALTER DOMAIN {1} DROP CONSTRAINT {0}; + +""".format(data['name'],schema + '.' + domain) + + SQL = sql_header + SQL + + return ajax_response(response=SQL) + + @check_precondition + def msql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + + Required Args: + name: Constraints Name + consrc: Constraints Check + + Returns: + Domain Constraint object in json format. + """ + data = request.args + + if coid is None: + required_args = [ + 'name', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + if status and SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid, coid=None): + """ + Generates the SQL statements to create/update the Domain Constraint. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + if coid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data, conn=self.conn + ) + else: + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + + if not status: + return False, internal_server_error(errormsg=name) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + return True, SQL + except Exception as e: + return False, internal_server_error(errormsg=str(e)) + + @check_precondition + def dependents(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependents and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependents_result = self.get_dependents(self.conn, coid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependencies and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependencies_result = self.get_dependencies(self.conn, coid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainConstraintView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..d62e13705c50e6c0cf8f19d680053e8643e28751 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=2TrO847{Jl`|t`Qpas zn<hS+l=rMY>RGPqvlydizRJ&>B`Om#V}R-yOM?7@862M7NCR>>3p^r=fwTu0yPeFo z12TL)T^vI=t|uoPU||ZF<tgaHG*QsQ!?m&Tq=?3mCu}J#Dx3x@mM}}^iE=5NIWXnk zkpnC4ai%a>@;Gg7=uums=9bIK=Egd~(us-3g@Iv02gfsK^JP^)gH=mhBT7;dOH!?p zi&B9UgOP!ufv%yEu7P2Qk%5(ov6YF5wt=aYfq}(LRXG$5x%nxXX_XKS29{tAAk|g| XW)KahriZQpYGCkm^>bP0l+XkKyyRU} literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png new file mode 100644 index 0000000000000000000000000000000000000000..32a045b8fafdc08640d53b2a86b1dcabcb0fe0fd GIT binary patch literal 579 zcmV-J0=)f+P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp) z=>Px$AW%$HMR1Z9#Q*@REHmdHAm<<;=p`lTDl6$LE15$|nM6yxJ3#6&F}^)Qx<p3n zH#h4zIK)Ou#79fTM@#HJKkY(8?L$QEL`CjJMeasM?ng(;QB>|oNbX2U?n+AUOH1!e zOz%xi?@dncPEXKRS?^Cz#amtQQBm+xQt(q#@KaRqR8;U)Rq<9<@mE*YVq(`~V)0vB z+GS<)VPW%PV%=$Jag!JHXlV6qZS`($_HuH3pd<EncJ_C7f1@XWqbGu>Du=8uhpjJ) zvN4agH<i3UrMYgWyK<eyN1Vq+p2tbA!G5C3PO!m$q|HvV#D$~JOsv;ftk_qv-CeQX zT*cC%$J3;><6_L$t<Kr8)!w|r@o>cPam4X*+~mi`^K{Glc|xh<NdN!<0d!JMQvg8b z*k%9#00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipf34<ROg z(p72z0056kL_t&-)1{2Z3c^qT1mDEody6IZuCW&s1V!`^4}$goe?(18@b2Db*j*w1 z%4M(p;&Zw?9ZaKAxo*x;COX}<8fzjqKRv^2kF1spymYHMjE2m7Hl|a~emCNwG8(i? z8I#``(kiBrEbh}(Qn8TL2+$}b3Hn^7p`K6R#_6zQ2$?ux;lXBYB>hkN@C%g?4vVBM R$bbL<002ovPDHLkV1min0OSAw literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..9d1d2a061c7948168d7b1c2474d769b31709f1cf GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE08{VY2nhHc^eMaU%j`d zaI$#+HuEKC{pKEZKYn>h(+aIMH^MjGjcs3}f9Cqyt&fvx7AT*)xv^`L;hf{Hi_iP4 zxgK%&V?q65_4c*;8}G%;JMMY<SLM___M4Bi9{E^!<YUpX&n1ga`7PgFwEkdS!(#P< zNn&@N9OqiK(i&(nV@Z%-FoVOh8)-leXMsm#F_88EW4Dvpc0fjir;B5V#O36K1sn!O zhRVf}5jSsGPH?f<xudc|vBoY<&5cb=uTGB9v187J4II+it5?jhn8F{TsCZIWRad$D zg1*K9W%cu2NsD(hEfVT#*wm#pYw;D04+0E(uQE?s`Z6*ZXoqTvYeY#(Vo9o1a#1Rf zVlXl=G|)9P(lsy)F*2|+F}5->(Kax(GBB{1sVaw}AvZrIGp!P$!N3x%0i@c>zzm|{ T)b!9bKn)C@u6{1-oD!M<=4O^k literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js new file mode 100644 index 0000000..166cabc --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js @@ -0,0 +1,149 @@ +// Domain Constraint Module: Collection and Node +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Constraint Collection Node + if (!pgBrowser.Nodes['coll-domain-constraints']) { + var domain_constraints = pgAdmin.Browser.Nodes['coll-domain-constraints'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + type: 'coll-domain-constraints', + columns: ['name', 'description'] + }); + }; + + // Domain Constraint Node + if (!pgBrowser.Nodes['domain-constraints']) { + pgAdmin.Browser.Nodes['domain-constraints'] = pgBrowser.Node.extend({ + type: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + collection_type: 'coll-domain-constraints', + hasSQL: true, + hasDepends: true, + parent_type: ['domain'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + oid: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined, + convalidated_p: undefined + }, + // Domain Constraint Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', type:'text', cell:'string', + disabled: 'isDisabled' + },{ + id: 'oid', label:'{{ _('OID') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'description', label: '{{ _('Comment') }}', type: 'multiline', cell: + 'string', mode: ['properties', 'create', 'edit'], min_version: 90500, + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', cel: + 'string', group: '{{ _('Definition') }}', mode: ['properties', + 'create', 'edit'], disabled: function(m) { return !m.isNew(); } + },{ + id: 'connoinherit', label: '{{ _('No Inherit') }}', type: + 'switch', cell: 'boolean', group: '{{ _('Definition') }}', mode: + ['properties', 'create', 'edit'], disabled: 'isDisabled', + visible: false + },{ + id: 'convalidated', label: "{{ _("Don't Validate") }}", type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', disabled: function(m) { + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return true; + } + else if(!m.get('convalidated')) + { + return true; + } + return false; + } + return true; + }, + mode: ['create', 'edit'], visible: function(m) { return !m.isNew() } + },{ + id: 'convalidated_p', label: '{{ _('Valid?') }}', type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', disabled: 'isDisabled', + mode: ['properties'] + }], + // Client Side Validation + validate: function() { + var err = {}, + errmsg; + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') { + err['consrc'] = '{{ _('Check can not be empty!') }}'; + errmsg = errmsg || err['consrc']; + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return true; + } + } + return false; + } + }), + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql new file mode 100644 index 0000000..be943d2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql @@ -0,0 +1,3 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }});{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..043f011 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql @@ -0,0 +1,14 @@ +SELECT + c.oid, conname AS name, typname AS relname, nspname, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql new file mode 100644 index 0000000..299ba6b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql @@ -0,0 +1,3 @@ +{% if data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql new file mode 100644 index 0000000..513c38d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql @@ -0,0 +1,9 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }}){% if data.convalidated %} + + NOT VALID{% endif %};{% if data.description %} + + +COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON DOMAIN {{ conn|qtIdent(schema, domain) }} + IS '{{ data.description }}';{% endif %}{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..34d8b34 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql @@ -0,0 +1,17 @@ +SELECT + c.oid, conname AS name, typname AS relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc, + connoinherit, convalidated, convalidated AS convalidated_p +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql new file mode 100644 index 0000000..b436f3a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql @@ -0,0 +1,13 @@ +{% set name = o_data.name %} +{% if data.name %} +{% set name = data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif -%}{% if data.convalidated %} + + +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + VALIDATE CONSTRAINT {{ conn|qtIdent(name) }}{% endif -%}{% if data.description %} + + +COMMENT ON CONSTRAINT {{ conn|qtIdent(name) }} ON DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + IS '{{ data.description }}';{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png new file mode 100644 index 0000000000000000000000000000000000000000..55621528a1dba4928538fe5557b9b988ed78d6ab GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}U4T!BE0BJeoqfW=;gF{0PD8_o zi&pN_(K)B7`FPi%2ix`@v9LU(tNUQ*!K1czXOxuggoGT_(#p@zf4Jw!!zHV)`3F7T zd-Um}H}`iRIijcc@Wq#>&ptkR`SszG54Uz6zW4mg?MLsgZ9jbb>E~y!zTSB9`Re1( zjS1S99(_9h@YC4`A5Y)^_^>@J3+MvIk|4ie28U-i(tsS!0*}aIAngIhZYQ(tfQ)ue z7sn8Z%b|U@#hMgESUvs4wKoSxO>~{O=-Yq$8_!r)-^e%4-l6f-Jox1%8}FIVx>FCW zPMVwluQGbkP1bcSwVV0d_?PZbZVis%HeYTWyU*$OvYp$uTc3QLbD?2trOnDuhZa>f z=3d_{(zER9xt(<n3UsBI4SNC?Y!rBW<m2`n>yDRyg$uoE8Qx}bo1D0qw;t#u)e_f; zl9a@fRIB8oR3OD*WMF8ZYiOivU>IU#U}a)#Wn!XjU}|MxU@=ow4n;$5eoAIqB}9XP eC0GMUwUvPxM8m1+p=*E|7(8A5T-G@yGywoRCC2Lj literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png new file mode 100644 index 0000000000000000000000000000000000000000..7521cddeaaaf0ee4e3c60e948078d70e17e06893 GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}T7XZ8E0Deu9eu*V;gF{0K`pIg zc6K`r4IeIAc}Q3HxV`=3y+@xudUJpGkt2F~PoI5!^78A$Cm(L@JbcYR=;4bm_nv>b z{pkI*?T7D#gyiSvKYR7{_S4Tdo_xOg_;X`|_N7Ok&OiKg_QA)~_dlLo85{w$iLoTe zFPOpM*^M+HhqJ&VvKUBvfU(=jY&#$$$<xI#MB;Mq`IABp20RT9wUXkaL(1R(pOn?c z>38<+e>KaN2{%0Jd@sMbY$+13Z&%Z<%!P+*SNu+#({Rr={_KxUhswHdID0QWTCBvD z)^ky;@gu`E#RAC#l`JcnelS0rxkKN7b1B>H56gX)0&P<*ag8WRNi0dVN-jzTQVd20 zh6cKZM!E)uAw~vPCdO7KCfWw3Rt5$ZGgakKH00)|WTsU@G#FTdHGouG8JIydoSGiG Q2B?9-)78&qol`;+03_q3ga7~l literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png new file mode 100644 index 0000000000000000000000000000000000000000..42ca929325854b8f34787425e8094d08c75983bc GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}PJmB{E0BJeoqfW=;gF{0PD8^7 zi&s6`bL8oxH}`iRdHU?*lb2r~J^6Th=ix^$zTSWS<?f>oH+CHUa^}pVf`aWvMt7fm zx&8F>jfd~AY(HF`mUiRG=W9<sUwQB@KR>@QLHo+%PZuA3I{)y~*$1D_JotF({zvHt zo%ulf7)yfuf*Bm1-ADs+I14-?i-EKU7`vU!wgWPXJzX3_Brcbpe=XFcAmSFNeC3q& zl!Z+(RsYN12&-NW{P*^#<rx82_2-+ii&SqfGgO`Jcj@;01@HTH{&&7#v?j}I)=J6m ze`~hppZn7?WA|%z!}T6pf>z0IjbZrS5ICD*275*_bAo)r8t#Vg4A*Lz);xc4JKq03 zXXC}zd)YwiRZCnWN>UO_QmvAUQh^kMk%6IsuAz~xfnkV|ft87|m5GVAfvJ^&fyGQ! qITQ`K`6-!cl@JXEmS7Da)m8>(5DllMhpqu?VDNPHb6Mw<&;$VOR=Fqu literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js new file mode 100644 index 0000000..598e542 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js @@ -0,0 +1,323 @@ +// Domain Module: Collection and Node. +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', + 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Collection Node + if (!pgBrowser.Nodes['coll-domain']) { + var domains = pgAdmin.Browser.Nodes['coll-domain'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain', + label: '{{ _('Domains') }}', + type: 'coll-domain', + columns: ['name', 'owner', 'description'] + }); + }; + + // Security Model + var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: null, + security_label: null + }, + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text', editable: true, cellHeaderClasses:'width_percent_50' + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text', editable: true, cellHeaderClasses:'width_percent_50' + }], + validate: function() { + var err = {}, + errmsg = null; + + if (_.isUndefined(this.get('security_label')) || + _.isNull(this.get('security_label')) || + String(this.get('security_label')).replace(/^\s+|\s+$/g, '') == '') { + errmsg = '{{ _('Please specify the value for all the security providers.')}}'; + this.errorModel.set('security_label', errmsg); + return errmsg; + } else { + this.errorModel.unset('security_label'); + } + return null; + } + }); + + // Constraint Model + var ConstraintModel = pgAdmin.Browser.Node.Model.extend({ + idAttribute: 'conoid', + defaults: { + conoid: undefined, + conname: undefined, + consrc: undefined, + }, + schema: [{ + id: 'conname', label: '{{ _('Name') }}', type: 'text', cell: 'string', + cellHeaderClasses: 'width_percent_40', editable: 'isEditable' + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', + cell: Backgrid.Extension.TextareaCell, group: '{{ _('Definition') }}', + cellHeaderClasses: 'width_percent_60', editable: 'isEditable' + }], + isEditable: function(m) { + return _.isUndefined(m.isNew) ? true : m.isNew(); + }, + toJSON: Backbone.Model.prototype.toJSON + }); + + // Domain Node + if (!pgBrowser.Nodes['domain']) { + pgAdmin.Browser.Nodes['domain'] = pgBrowser.Node.extend({ + type: 'domain', + label: '{{ _('Domain') }}', + collection_type: 'coll-domain', + hasSQL: true, + hasDepends: true, + parent_type: ['schema'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + canDropCascade: pgBrowser.Nodes['schema'].canChildDrop, + // Domain Node Model + model: pgAdmin.Browser.Node.Model.extend({ + initialize: function(attrs, args) { + var isNew = (_.size(attrs) === 0); + if (isNew) { + // Set Selected Schema + schema = args.node_info.schema.label + this.set({'basensp': schema}, {silent: true}); + + // Set Current User + var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user; + this.set({'owner': userInfo.name}, {silent: true}); + } + pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments); + }, + defaults: { + name: undefined, + oid: undefined, + owner: undefined, + basensp: undefined, + description: undefined, + basetype: undefined, + typlen: undefined, + precision: undefined, + typdefault: undefined, + typnotnull: undefined, + sysdomain: undefined, + collname: undefined, + constraints: [], + seclabels: [] + }, + type_options: undefined, + // Domain Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'] + },{ + id: 'oid', label:'{{ _('OID') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl, + node: 'role', type: 'text', mode: ['edit', 'create', 'properties'] + },{ + id: 'basensp', label:'{{ _('Schema') }}', cell: 'node-list-by-name', + control: 'node-list-by-name', cache_level: 'database', type: 'text', + node: 'schema' + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'basetype', label:'{{ _('Base Type') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', mode:['properties', 'create', 'edit'], group: '{{ _('Definition') }}', url: 'get_types', + disabled: function(m) { return !m.isNew(); }, first_empty: true, + transform: function(d){ + this.model.type_options = d; + return d; + } + },{ + id: 'typlen', label:'{{ _('Length') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'], + disabled: function(m) { + // We will store type from selected from combobox + if (!m.isNew()) { + return true; + } + var of_type = m.get('basetype'); + if(m.type_options) { + // iterating over all the types + _.each(m.type_options, function(o) { + // if type from selected from combobox matches in options + if ( of_type == o.value ) { + // if length is allowed for selected type + if(o.length) + { + // set the values in model + m.set('is_tlength', true, {silent: true}); + m.set('min_val', o.min_val, {silent: true}); + m.set('max_val', o.max_val, {silent: true}); + } + } + }); + } + return !m.get('is_tlength'); + } + },{ + id: 'precision', label:'{{ _('Precision') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'], + disabled: function(m) { + // We will store type from selected from combobox + if (!m.isNew()) { + return true; + } + var of_type = m.get('basetype'); + if(m.type_options) { + // iterating over all the types + _.each(m.type_options, function(o) { + // if type from selected from combobox matches in options + if ( of_type == o.value ) { + // if precession is allowed for selected type + if(o.precision) + { + // set the values in model + m.set('is_precision', true, {silent: true}); + m.set('min_val', o.min_val, {silent: true}); + m.set('max_val', o.max_val, {silent: true}); + } + } + }); + } + return !m.get('is_precision'); + } + },{ + id: 'typdefault', label:'{{ _('Default') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}' + },{ + id: 'typnotnull', label:'{{ _('Not Null') }}', cell: 'boolean', + type: 'switch', group: '{{ _('Definition') }}' + },{ + id: 'sysdomain', label:'{{ _('System Domain?') }}', cell: 'boolean', + type: 'switch', group: '{{ _('Definition') }}', mode: ['properties'] + },{ + id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', group: '{{ _('Definition') }}', url: 'get_collations', disabled: function(m) { + return !m.isNew(); + } + },{ + id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string', + type: 'collection', group: '{{ _('Constraints') }}', mode: ['edit', 'create'], + model: ConstraintModel, canAdd: true, canDelete: true, + canEdit: false + },{ + id: 'seclabels', label: '{{ _('Security Labels') }}', + model: SecurityModel, type: 'collection', + group: '{{ _('Security') }}', mode: ['edit', 'create'], + min_version: 90100, canAdd: true, + canEdit: false, canDelete: true + } + ], + validate: function() // Client Side Validation + { + var err = {}, + errmsg, + seclabels = this.get('seclabels'); + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('basetype')) || String(this.get('basetype')).replace(/^\s+|\s+$/g, '') == '') { + err['basetype'] = '{{ _('Base Type can not be empty!') }}'; + errmsg = errmsg || err['basetype']; + } + + if (seclabels) { + var secLabelsErr; + for (var i = 0; i < seclabels.models.length && !secLabelsErr; i++) { + secLabelsErr = (seclabels.models[i]).validate.apply(seclabels.models[i]); + if (secLabelsErr) { + err['seclabels'] = secLabelsErr; + errmsg = errmsg || secLabelsErr; + } + } + } + + this.errorModel.clear().set(err); + + return null; + } + }), + canCreate: function(itemData, item, data) { + //If check is false then , we will allow create menu + if (data && data.check == false) + return true; + + var t = pgBrowser.tree, i = item, d = itemData; + // To iterate over tree to check parent node + while (i) { + // If it is schema then allow user to create domain + if (_.indexOf(['schema'], d._type) > -1) + return true; + + if ('coll-domain' == d._type) { + //Check if we are not child of catalog + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + if( prev_d._type == 'catalog') { + return false; + } else { + return true; + } + } + i = t.hasParent(i) ? t.parent(i) : null; + d = i ? t.itemData(i) : null; + } + // by default we do not want to allow create menu + return true; + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return false; + } + } + return true; + } + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql new file mode 100644 index 0000000..f8b0b75 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql @@ -0,0 +1,30 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname %} + + COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL{% endif %}{% if data.constraints %}{% for c in data.constraints %}{% if c.conname and c.consrc %} + + CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% endif -%} +{% endfor -%} +{% endif -%}; + +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.description %} + + +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}';{% endif -%}{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} + + +{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%} +{% endfor -%} +{% endif -%} + +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..7a12b50 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql @@ -0,0 +1,16 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +AND + d.oid={{doid}}::int; +{% endif %} + +{% if name %} +DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql new file mode 100644 index 0000000..819fdbb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql new file mode 100644 index 0000000..897fb24 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql @@ -0,0 +1,15 @@ +SELECT + 'DOMAIN' AS objectkind, c.oid as conoid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as cons +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' + AND contypid = {{doid}}::oid +ORDER BY conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..8b5c891 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql @@ -0,0 +1,18 @@ +{% if doid %} +SELECT + d.typnamespace as scid +FROM + pg_type d +WHERE + d.oid={{ doid }}::oid; +{% else %} +SELECT + d.oid, d.typnamespace +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..42af39d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql @@ -0,0 +1,35 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, + pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT + array_agg(provider || '=' || label) + FROM + pg_seclabel sl1 + WHERE + sl1.objoid=d.oid) AS seclabels +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid + {% if doid %} + AND d.oid={{doid}}::int + {% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql new file mode 100644 index 0000000..6ff4052 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql @@ -0,0 +1,64 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER TYPE {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} + +{% endif -%} +{% if data.typnotnull and not o_data.typnotnull %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif -%}{% if data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault }}; +{% elif not data.typdefault and o_data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif -%}{% if data.owner %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.constraints %} +{% for c in data.constraints.deleted %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }}; +{% endfor -%} +{% for c in data.constraints.added %} +{% if c.conname and c.consrc %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} );{% endif -%} +{% endfor -%}{% endif -%} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} + +{% endfor -%} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} + +{% endfor -%} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} + +{% endfor -%} +{% endif -%}{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }};{% endif %}{% if data.basensp %} + + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%} +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql new file mode 100644 index 0000000..e89369f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql @@ -0,0 +1,30 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname and data.collname != "pg_catalog.\"default\"" %} + + COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL{% endif %}{% if data.constraints %}{% for c in data.constraints %}{% if c.conname and c.consrc %} + + CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% endif -%} +{% endfor -%} +{% endif -%}; + +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.description %} + + +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}';{% endif -%}{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} + + +{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%} +{% endfor -%} +{% endif -%} + +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..7a12b50 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql @@ -0,0 +1,16 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +AND + d.oid={{doid}}::int; +{% endif %} + +{% if name %} +DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql new file mode 100644 index 0000000..e59c17d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql new file mode 100644 index 0000000..df956bf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql @@ -0,0 +1,15 @@ +SELECT + 'DOMAIN' AS objectkind, c.oid as conoid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc, connoinherit, convalidated +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +ORDER BY + conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..8b5c891 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql @@ -0,0 +1,18 @@ +{% if doid %} +SELECT + d.typnamespace as scid +FROM + pg_type d +WHERE + d.oid={{ doid }}::oid; +{% else %} +SELECT + d.oid, d.typnamespace +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..2892988 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql @@ -0,0 +1,34 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT + array_agg(provider || '=' || label) + FROM + pg_shseclabel sl1 + WHERE + sl1.objoid=d.oid) AS seclabels +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid +{% if doid %} + AND d.oid={{doid}}::int +{% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql new file mode 100644 index 0000000..ecc7e9a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql @@ -0,0 +1,64 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} + +{% endif -%} +{% if data.typnotnull and not o_data.typnotnull %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif -%}{% if data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault }}; +{% elif not data.typdefault and o_data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif -%}{% if data.owner %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.constraints %} +{% for c in data.constraints.deleted %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }}; +{% endfor -%} +{% for c in data.constraints.added %} +{% if c.conname and c.consrc %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if c.convalidated %} + NOT VALID{% endif %}{% if c.connoinherit %} NO INHERIT{% endif -%};{% endif -%} +{% endfor -%}{% endif -%} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} + +{% endfor %} +{% endif -%} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif -%}{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif -%}{% if data.description %} + +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }};{% endif -%}{% if data.basensp %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%} +{% endif -%} ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-16 16:10 Dave Page <[email protected]> parent: Khushboo Vashi <[email protected]> 0 siblings, 2 replies; 29+ messages in thread From: Dave Page @ 2016-03-16 16:10 UTC (permalink / raw) To: Khushboo Vashi <[email protected]>; +Cc: pgadmin-hackers Hi On Wed, Mar 16, 2016 at 2:03 PM, Khushboo Vashi <[email protected]> wrote: > Hi, > > Please find the updated Domain Module Patch. > > To test this patch, please apply Backgrid Textarea Cell Patch before this. Thanks. I believe with the following fixes, we'll be done :-) - Default values should be auto-quoted when necessary (ie. strings, on a text-based domain). - "System Domain?" should be in the General section, between owner and comment. - The switches should use the same colouring/styling as other objects, e.g. options: { 'onText': 'Yes', 'offText': 'No', 'onColor': 'success', 'offColor': 'primary', 'size': 'small' } - Please remove the Schema property from the main properties tab (not the properties dialogue). - No icon is show for Checks on the Dependents tab for a domain. - The add button on the Security Labels tab is spelt "Add". Why is that? Other instances of this grid use "ADD" which is the default in backform.pgadmin.js. - Dependencies on domain check constraints are listed as being on a "Type" not a "Domain". - If adding a domain constraint using the grid on the Domain dialogue, I cannot specify "NOT VALID". We need a checkbox for that in a narrow columns at the end. Unchecking it for an existing constraint should be the equivalent of doing "ALTER DOMAIN ... VALIDATE CONSTRAINT" - If I switch the "Don't Validate" switch on a constraint, there are leading blank lines in the generated SQL. The same occurs when adding a comment to a constraint. - I think we need to reverse the meaning of "Don't Validate" and rename to match the "Valid?" field that's on the properties list. Otherwise it's not clear they're the same thing. - s/Not Null/Not Null?/ -- Dave Page Blog: http://pgsnake.blogspot.com Twitter: @pgsnake EnterpriseDB UK: http://www.enterprisedb.com The Enterprise PostgreSQL Company -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-17 17:39 Khushboo Vashi <[email protected]> parent: Dave Page <[email protected]> 1 sibling, 1 reply; 29+ messages in thread From: Khushboo Vashi @ 2016-03-17 17:39 UTC (permalink / raw) To: Dave Page <[email protected]>; +Cc: pgadmin-hackers Hi Dave, I have a query regarding your below feedback : * - Default values should be auto-quoted when necessary (ie. strings, on a text-based domain).* To resolve this, I have checked the typcategory field from pg_type for the base_type selected for the Domain. If the typcategory is String type (i.e. S), then only I have used qtLiteral function to quote the default value. Is this right approach or not? Thanks, Khushboo On Wed, Mar 16, 2016 at 9:40 PM, Dave Page <[email protected]> wrote: > Hi > > On Wed, Mar 16, 2016 at 2:03 PM, Khushboo Vashi > <[email protected]> wrote: > > Hi, > > > > Please find the updated Domain Module Patch. > > > > To test this patch, please apply Backgrid Textarea Cell Patch before > this. > > Thanks. I believe with the following fixes, we'll be done :-) > > - Default values should be auto-quoted when necessary (ie. strings, on > a text-based domain). > > - "System Domain?" should be in the General section, between owner and > comment. > > - The switches should use the same colouring/styling as other objects, e.g. > > options: { > 'onText': 'Yes', 'offText': 'No', > 'onColor': 'success', 'offColor': 'primary', > 'size': 'small' > } > > - Please remove the Schema property from the main properties tab (not > the properties dialogue). > > - No icon is show for Checks on the Dependents tab for a domain. > > - The add button on the Security Labels tab is spelt "Add". Why is > that? Other instances of this grid use "ADD" which is the default in > backform.pgadmin.js. > > - Dependencies on domain check constraints are listed as being on a > "Type" not a "Domain". > > - If adding a domain constraint using the grid on the Domain dialogue, > I cannot specify "NOT VALID". We need a checkbox for that in a narrow > columns at the end. Unchecking it for an existing constraint should be > the equivalent of doing "ALTER DOMAIN ... VALIDATE CONSTRAINT" > > - If I switch the "Don't Validate" switch on a constraint, there are > leading blank lines in the generated SQL. The same occurs when adding > a comment to a constraint. > > - I think we need to reverse the meaning of "Don't Validate" and > rename to match the "Valid?" field that's on the properties list. > Otherwise it's not clear they're the same thing. > > - s/Not Null/Not Null?/ > > > -- > Dave Page > Blog: http://pgsnake.blogspot.com > Twitter: @pgsnake > > EnterpriseDB UK: http://www.enterprisedb.com > The Enterprise PostgreSQL Company > ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-18 15:01 Dave Page <[email protected]> parent: Khushboo Vashi <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Dave Page @ 2016-03-18 15:01 UTC (permalink / raw) To: Khushboo Vashi <[email protected]>; +Cc: pgadmin-hackers On Thu, Mar 17, 2016 at 5:39 PM, Khushboo Vashi <[email protected]> wrote: > Hi Dave, > > I have a query regarding your below feedback : > > - Default values should be auto-quoted when necessary (ie. strings, on a > text-based domain). > > To resolve this, I have checked the typcategory field from pg_type for the > base_type selected for the Domain. > If the typcategory is String type (i.e. S), then only I have used qtLiteral > function to quote the default value. > > Is this right approach or not? Yes, I think that's a good approach (at least, I don't see any downsides right now :-) ) -- Dave Page Blog: http://pgsnake.blogspot.com Twitter: @pgsnake EnterpriseDB UK: http://www.enterprisedb.com The Enterprise PostgreSQL Company -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-21 07:41 Khushboo Vashi <[email protected]> parent: Dave Page <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Khushboo Vashi @ 2016-03-21 07:41 UTC (permalink / raw) To: Dave Page <[email protected]>; +Cc: pgadmin-hackers Hi Dave, On Fri, Mar 18, 2016 at 8:31 PM, Dave Page <[email protected]> wrote: > On Thu, Mar 17, 2016 at 5:39 PM, Khushboo Vashi > <[email protected]> wrote: > > Hi Dave, > > > > I have a query regarding your below feedback : > > > > - Default values should be auto-quoted when necessary (ie. strings, on a > > text-based domain). > > > > To resolve this, I have checked the typcategory field from pg_type for > the > > base_type selected for the Domain. > > If the typcategory is String type (i.e. S), then only I have used > qtLiteral > > function to quote the default value. > > > > Is this right approach or not? > > Yes, I think that's a good approach (at least, I don't see any > downsides right now :-) ) > > After implementing above approach, I found some issues: 1> If I put quotes explicitly, user can not set any expression as a default value For example, CREATE OR REPLACE FUNCTION test_text_return() RETURNS TEXT AS $$ SELECT now()::text; $$ LANGUAGE 'sql'; CREATE DOMAIN text_domin AS TEXT DEFAULT public.test_text_return(); In this case, if I put quotes for default value, it will be set as a string rather than an expression. 2> When I set any string for the default value, it is getting stored with the datatype like 'test_default'::text So, in the Default Value (Properties Dialogue), it is showing 'test_default'::text I have tried pg_get_expr(typdefaultbin, 0) to fetch only default value (without datatype), but it returns the whole expression. As per my discussion with Ashesh, we feel - we should not quote the default value after looking at the above example. Please, let me know what should I do further? -- Dave Page Blog: http://pgsnake.blogspot.com Twitter: @pgsnake EnterpriseDB UK: http://www.enterprisedb.com The Enterprise PostgreSQL Company ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-22 17:10 Dave Page <[email protected]> parent: Khushboo Vashi <[email protected]> 0 siblings, 0 replies; 29+ messages in thread From: Dave Page @ 2016-03-22 17:10 UTC (permalink / raw) To: Khushboo Vashi <[email protected]>; +Cc: pgadmin-hackers On Mon, Mar 21, 2016 at 7:41 AM, Khushboo Vashi <[email protected]> wrote: > Hi Dave, > > > On Fri, Mar 18, 2016 at 8:31 PM, Dave Page <[email protected]> wrote: >> >> On Thu, Mar 17, 2016 at 5:39 PM, Khushboo Vashi >> <[email protected]> wrote: >> > Hi Dave, >> > >> > I have a query regarding your below feedback : >> > >> > - Default values should be auto-quoted when necessary (ie. strings, on a >> > text-based domain). >> > >> > To resolve this, I have checked the typcategory field from pg_type for >> > the >> > base_type selected for the Domain. >> > If the typcategory is String type (i.e. S), then only I have used >> > qtLiteral >> > function to quote the default value. >> > >> > Is this right approach or not? >> >> Yes, I think that's a good approach (at least, I don't see any >> downsides right now :-) ) >> > > After implementing above approach, I found some issues: > > 1> If I put quotes explicitly, user can not set any expression as a default > value > For example, > > CREATE OR REPLACE FUNCTION test_text_return() RETURNS TEXT AS > $$ > SELECT now()::text; > $$ LANGUAGE 'sql'; > > > CREATE DOMAIN text_domin AS TEXT DEFAULT public.test_text_return(); > > In this case, if I put quotes for default value, it will be set as a > string rather than an expression. > > > 2> When I set any string for the default value, it is getting stored with > the datatype like 'test_default'::text > So, in the Default Value (Properties Dialogue), it is showing > 'test_default'::text > > I have tried pg_get_expr(typdefaultbin, 0) to fetch only default value > (without datatype), but it returns the whole expression. > > > As per my discussion with Ashesh, we feel - we should not quote the default > value after looking at the above example. > > Please, let me know what should I do further? Urgh, yes - I hadn't thought of that. Leave it unquoted please. I guess we can add a hint to the field: Enter an expression or value. -- Dave Page Blog: http://pgsnake.blogspot.com Twitter: @pgsnake EnterpriseDB UK: http://www.enterprisedb.com The Enterprise PostgreSQL Company -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-23 07:18 Khushboo Vashi <[email protected]> parent: Dave Page <[email protected]> 1 sibling, 1 reply; 29+ messages in thread From: Khushboo Vashi @ 2016-03-23 07:18 UTC (permalink / raw) To: Dave Page <[email protected]>; +Cc: pgadmin-hackers Hi, Please find attached updated patch for the Domains module. On Wed, Mar 16, 2016 at 9:40 PM, Dave Page <[email protected]> wrote: > Hi > > On Wed, Mar 16, 2016 at 2:03 PM, Khushboo Vashi > <[email protected]> wrote: > > Hi, > > > > Please find the updated Domain Module Patch. > > > > To test this patch, please apply Backgrid Textarea Cell Patch before > this. > > Thanks. I believe with the following fixes, we'll be done :-) > > - Default values should be auto-quoted when necessary (ie. strings, on > a text-based domain). > As per our discussion, this should leave unquoted. > - "System Domain?" should be in the General section, between owner and > comment. > Done > - The switches should use the same colouring/styling as other objects, e.g. > > options: { > 'onText': 'Yes', 'offText': 'No', > 'onColor': 'success', 'offColor': 'primary', > 'size': 'small' > } > Done > - Please remove the Schema property from the main properties tab (not > the properties dialogue). > Done > - No icon is show for Checks on the Dependents tab for a domain. > Done > - The add button on the Security Labels tab is spelt "Add". Why is > that? Other instances of this grid use "ADD" which is the default in > backform.pgadmin.js. > Done > - Dependencies on domain check constraints are listed as being on a > "Type" not a "Domain". > Done > - If adding a domain constraint using the grid on the Domain dialogue, > I cannot specify "NOT VALID". We need a checkbox for that in a narrow > columns at the end. Unchecking it for an existing constraint should be > the equivalent of doing "ALTER DOMAIN ... VALIDATE CONSTRAINT" > Done > - If I switch the "Don't Validate" switch on a constraint, there are > leading blank lines in the generated SQL. The same occurs when adding > a comment to a constraint. > Done > - I think we need to reverse the meaning of "Don't Validate" and > rename to match the "Valid?" field that's on the properties list. > Otherwise it's not clear they're the same thing. > Done > - s/Not Null/Not Null?/ > Done > > > -- > Dave Page > Blog: http://pgsnake.blogspot.com > Twitter: @pgsnake > > EnterpriseDB UK: http://www.enterprisedb.com > The Enterprise PostgreSQL Company > -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers Attachments: [text/x-patch] Domains_ver_6.patch (102.7K, 3-Domains_ver_6.patch) download | inline diff: diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py new file mode 100644 index 0000000..2df600f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py @@ -0,0 +1,810 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements the Domain Node.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error, gone +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas as schemas +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from pgadmin.browser.server_groups.servers.databases.schemas.utils import \ + SchemaChildModule, DataTypeReader +from pgadmin.browser.server_groups.servers.databases.utils import \ + parse_sec_labels_from_db +from functools import wraps + + +class DomainModule(SchemaChildModule): + """ + class DomainModule(SchemaChildModule): + + This class represents The Domain Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Module. + + * get_nodes(gid, sid, did, scid) + - Generate the domain collection node. + + * script_load() + - Load the module script for domain, when schema node is + initialized. + """ + + NODE_TYPE = 'domain' + COLLECTION_LABEL = gettext("Domains") + + def __init__(self, *args, **kwargs): + super(DomainModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid): + """ + Generate the domain collection node. + """ + yield self.generate_browser_collection_node(scid) + + @property + def script_load(self): + """ + Load the module script for domain, when schema node is + initialized. + """ + return schemas.SchemaModule.NODE_TYPE + + +blueprint = DomainModule(__name__) + + +class DomainView(PGChildNodeView, DataTypeReader): + """ + class DomainView + + This class inherits PGChildNodeView to get the different routes for + the module. Also, inherits DataTypeReader to get data types. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain. + + Methods: + ------- + * validate_request(f): + - Works as a decorator. + Validating request on the request of create, update and modified SQL. + + * module_js(): + - Load JS file (domains.js) for this module. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domains. + + * nodes(gid, sid, did, scid): + - Returns all the Domains to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain properties. + + * get_collations(gid, sid, did, scid, doid=None): + - Returns Collations. + + * create(gid, sid, did, scid): + - Creates a new Domain object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain object. + + * dependents(gid, sid, did, scid, doid): + - Returns the dependents for the Domain object. + + * dependencies(gid, sid, did, scid, doid): + - Returns the dependencies for the Domain object. + + * types(gid, sid, did, scid, fnid=None): + - Returns Data Types. + """ + + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'doid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_types': [{'get': 'types'}, {'get': 'types'}], + 'get_collations': [ + {'get': 'get_collations'}, + {'get': 'get_collations'} + ] + }) + + def validate_request(f): + """ + Works as a decorator. + Validating request on the request of create, update and modified SQL. + + Required Args: + name: Name of the Domain + owner: Domain Owner + basensp: Schema Name + basetype: Data Type of the Domain + + Above both the arguments will not be validated in the update action. + """ + + @wraps(f) + def wrap(self, **kwargs): + + data = {} + if request.data: + req = json.loads(request.data.decode()) + else: + req = request.args or request.form + + if 'doid' not in kwargs: + required_args = [ + 'name', + 'basetype' + ] + + for arg in required_args: + if arg not in req or req[arg] == '': + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter \ + (%s)." % arg + ) + ) + + try: + list_params = [] + if request.method == 'GET': + list_params = ['constraints', 'seclabels'] + + for key in req: + if key in list_params and req[key] != '' \ + and req[key] is not None: + # Coverts string into python list as expected. + data[key] = json.loads(req[key]) + elif key == 'typnotnull': + data[key] = True if req[key] == 'true' or req[key] is\ + True else\ + (False if req[key] == 'false' or req[key] is + False else '') + else: + data[key] = req[key] + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + self.request = data + return f(self, **kwargs) + + return wrap + + def module_js(self): + """ + Load JS file (domains.js) for this module. + """ + return make_response( + render_template( + "domains/js/domains.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + # Get database connection + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domains/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domains/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid): + """ + List the Domains. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid): + """ + Returns all the Domains to generate Nodes in the browser. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + res = [] + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + scid, + row['name'], + icon="icon-domain" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid): + """ + Returns the Domain properties. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + if len(res['rows']) == 0: + return gone(gettext(""" +Could not find the domain in the database. +It may have been removed by another user or +shifted to the another schema. +""")) + + data = res['rows'][0] + + # Get Type Length and Precision + data.update(self._parse_type(data['fulltype'])) + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + # Get formatted Security Labels + if 'seclabels' in data: + data.update(parse_sec_labels_from_db(data['seclabels'])) + + # Set System Domain Status + data['sysdomain'] = False + if doid <= self.manager.db_info[did]['datlastsysoid']: + data['sysdomain'] = True + + return ajax_response( + response=data, + status=200 + ) + + def _parse_type(self, basetype): + """ + Returns Type and Data Type from the basetype. + """ + typ_len = '' + typ_precision = '' + + # The Length and the precision of the Datatype should be separate. + # The Format we getting from database is: numeric(1,1) + # So, we need to separate Length: 1, Precision: 1 + + if basetype != '' and basetype.find("(") > 0: + substr = basetype[basetype.find("(") + 1:len( + basetype) - 1] + typlen = substr.split(",") + if len(typlen) > 1: + typ_len = typlen[0] + typ_precision = typlen[1] + else: + typ_len = typlen + typ_precision = '' + + return {'typlen': typ_len, 'precision': typ_precision} + + @check_precondition + def get_collations(self, gid, sid, did, scid, doid=None): + """ + Returns Collations. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + res = [{'label': '', 'value': ''}] + try: + SQL = render_template("/".join([self.template_path, + 'get_collations.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append({'label': row['copy_collation'], + 'value': row['copy_collation']} + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def types(self, gid, sid, did, scid, doid=None): + """ + Returns the Data Types. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + fnid: Function Id + """ + + condition = """typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r') +AND NOT EXISTS (SELECT 1 FROM pg_class WHERE relnamespace=typnamespace +AND relname = typname AND relkind != 'c') AND +(typname NOT LIKE '_%' OR NOT EXISTS (SELECT 1 FROM pg_class WHERE +relnamespace=typnamespace AND relname = substring(typname FROM 2)::name +AND relkind != 'c'))""" + + if self.blueprint.show_system_objects: + condition += " AND nsp.nspname != 'information_schema'" + + # Get Types + status, types = self.get_types(self.conn, condition) + + if not status: + return internal_server_error(errormsg=types) + + return make_json_response( + data=types, + status=200 + ) + + @check_precondition + @validate_request + def create(self, gid, sid, did, scid): + """ + Creates a new Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + Domain object in json format. + """ + + data = self.request + try: + status, SQL = self.get_sql(gid, sid, data, scid) + + if not status: + return internal_server_error(errormsg=SQL) + + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # We need oid to to add object in tree at browser, below sql will + # gives the same + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + basensp=data['basensp'], + name=data['name']) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + doid, scid = res['rows'][0] + + return jsonify( + node=self.blueprint.generate_browser_node( + doid, + scid, + data['name'], + icon="icon-domain" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid): + """ + Drops the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + name, basensp = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + name=name, basensp=basensp, cascade=cascade) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + @validate_request + def update(self, gid, sid, did, scid, doid): + """ + Updates the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if not status: + return internal_server_error(errormsg=SQL) + + try: + if SQL: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get Schema Id + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + scid = res['rows'][0]['scid'] + + return make_json_response( + success=1, + info="Domain updated", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid=None): + """ + Returns the SQL for the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return False, internal_server_error(errormsg=res) + data = res['rows'][0] + + # Get Type Length and Precision + data.update(self._parse_type(data['fulltype'])) + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), data=data) + + sql_header = """-- DOMAIN: {0} + +-- DROP DOMAIN {0}; + +""".format(data['basensp'] + '.' + data['name']) + + SQL = sql_header + SQL + + return ajax_response(response=SQL.strip('\n')) + + @check_precondition + @validate_request + def msql(self, gid, sid, did, scid, doid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + SQL statements to create/update the Domain. + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid=None): + """ + Generates the SQL statements to create/update the Domain. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + try: + if doid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data) + else: + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data) + return True, SQL.strip('\n') + + except Exception as e: + return False, e + + @check_precondition + def dependents(self, gid, sid, did, scid, doid): + """ + This function get the dependents and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependents_result = self.get_dependents(self.conn, doid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid): + """ + This function get the dependencies and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependencies_result = self.get_dependencies(self.conn, doid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py new file mode 100644 index 0000000..d217e69 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py @@ -0,0 +1,687 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements the Domain Constraint Module.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas.domains \ + as domains +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + + +class DomainConstraintModule(CollectionNodeModule): + """ + class DomainConstraintModule(CollectionNodeModule): + + This class represents The Domain Constraint Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Constraint Module. + + * get_nodes(gid, sid, did, scid) + - Generate the Domain Constraint collection node. + + * node_inode(gid, sid, did, scid) + - Returns Domain Constraint node as leaf node. + + * script_load() + - Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + NODE_TYPE = 'domain-constraints' + COLLECTION_LABEL = gettext("Domain Constraints") + + def __init__(self, *args, **kwargs): + super(DomainConstraintModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid, doid): + """ + Generate the Domain Constraint collection node. + """ + yield self.generate_browser_collection_node(doid) + + @property + def node_inode(self): + """ + Returns Domain Constraint node as leaf node. + """ + return False + + @property + def script_load(self): + """ + Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + return domains.DomainModule.NODE_TYPE + + @property + def csssnippets(self): + """ + Returns a snippet of css to include in the page + """ + return [ + render_template( + "domain-constraints/css/domain-constraints.css", + node_type=self.node_type + ) + ] + + + +blueprint = DomainConstraintModule(__name__) + + +class DomainConstraintView(PGChildNodeView): + """ + class DomainConstraintView(PGChildNodeView): + + This class inherits PGChildNodeView to get the different routes for + the module. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain Constraint. + + Methods: + ------- + + * module_js(): + - Load JS file (domain-constraints.js) for this module. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domain Constraints. + + * nodes(gid, sid, did, scid): + - Returns all the Domain Constraints to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain Constraint properties. + + * create(gid, sid, did, scid): + - Creates a new Domain Constraint object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain Constraint object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain Constraint object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain Constraint object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain Constraint. + object. + + * dependents(gid, sid, did, scid, doid, coid): + - Returns the dependents for the Domain Constraint object. + + * dependencies(gid, sid, did, scid, doid, coid): + - Returns the dependencies for the Domain Constraint object. + """ + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'}, + {'type': 'int', 'id': 'doid'} + ] + ids = [ + {'type': 'int', 'id': 'coid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def validate_request(f): + """ + Works as a decorator. + Validating request on the request of create, update and modified SQL. + + Required Args: + name: Name of the Domain Constraint + consrc: Check Constraint Definition + + Above both the arguments will not be validated in the update action. + """ + + @wraps(f) + def wrap(self, **kwargs): + + data = {} + if request.data: + req = json.loads(request.data.decode()) + else: + req = request.args or request.form + + if 'coid' not in kwargs: + required_args = [ + 'name', + 'consrc' + ] + + for arg in required_args: + if arg not in req or req[arg] == '': + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter \ + (%s)." % arg + ) + ) + + try: + for key in req: + if key == 'convalidated': + data[key] = True if (req[key] == 'true' or req[key] is + True) else False + else: + data[key] = req[key] + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + self.request = data + return f(self, **kwargs) + + return wrap + + def module_js(self): + """ + Load JS file (domain-constraints.js) for this module. + """ + return make_response( + render_template( + "domain-constraints/js/domain-constraints.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domain-constraints/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domain-constraints/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid, doid): + """ + List the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid, doid): + """ + Returns all the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + res = [] + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, rset = self.conn.execute_2darray(SQL) + + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + if row['convalidated']: + icon = 'icon-domain-constraints' + else: + icon = 'icon-domain-constraints-bad' + res.append( + self.blueprint.generate_browser_node( + row['oid'], + doid, + row['name'], + icon=icon + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid, coid): + """ + Returns the Domain Constraints property. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + return ajax_response( + response=data, + status=200 + ) + + @check_precondition + @validate_request + def create(self, gid, sid, did, scid, doid): + """ + Creates a new Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Returns: + Domain Constraint object in json format. + """ + data = self.request + try: + status, SQL = self.get_sql(gid, sid, data, scid, doid) + if not status: + return internal_server_error(errormsg=SQL) + + status, res = self.conn.execute_scalar(SQL) + + if not status: + return internal_server_error(errormsg=res) + + # Get the recently added constraints oid + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + doid=doid, name=data['name']) + status, coid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=coid) + + if 'convalidated' in data and data['convalidated']: + icon = 'icon-domain-constraints' + else: + icon = 'icon-domain-constraints-bad' + + return jsonify( + node=self.blueprint.generate_browser_node( + coid, + doid, + data['name'], + icon=icon + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid, coid): + """ + Drops the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain Constraint dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + @validate_request + def update(self, gid, sid, did, scid, doid, coid): + """ + Updates the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + data = self.request + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + + try: + if SQL and status: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + if 'convalidated' in data and data['convalidated']: + icon = 'icon-domain-constraints' + elif 'convalidated' in data and not data['convalidated']: + icon = 'icon-domain-constraints-bad' + else: + icon = '' + + return make_json_response( + success=1, + info="Domain Constraint updated", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did, + 'icon': icon + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the SQL for the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + # Get Schema and Domain. + domain, schema = self._get_domain(doid) + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + + sql_header = """-- CHECK: {0} + +-- ALTER DOMAIN {1} DROP CONSTRAINT {0}; + +""".format(data['name'],schema + '.' + domain) + + SQL = sql_header + SQL + + return ajax_response(response=SQL) + + @check_precondition + @validate_request + def msql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + + Returns: + Domain Constraint object in json format. + """ + data = self.request + + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + if status and SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid, coid=None): + """ + Generates the SQL statements to create/update the Domain Constraint. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + if coid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data, conn=self.conn + ) + else: + domain, schema = self._get_domain(doid) + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + return True, SQL.strip('\n') + except Exception as e: + return False, internal_server_error(errormsg=str(e)) + + def _get_domain(self, doid): + """ + Returns Domain and Schema name. + + Args: + doid: Domain Id + + """ + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + return res['rows'][0] + + @check_precondition + def dependents(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependents and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependents_result = self.get_dependents(self.conn, coid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependencies and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependencies_result = self.get_dependencies(self.conn, coid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainConstraintView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..d62e13705c50e6c0cf8f19d680053e8643e28751 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=2TrO847{Jl`|t`Qpas zn<hS+l=rMY>RGPqvlydizRJ&>B`Om#V}R-yOM?7@862M7NCR>>3p^r=fwTu0yPeFo z12TL)T^vI=t|uoPU||ZF<tgaHG*QsQ!?m&Tq=?3mCu}J#Dx3x@mM}}^iE=5NIWXnk zkpnC4ai%a>@;Gg7=uums=9bIK=Egd~(us-3g@Iv02gfsK^JP^)gH=mhBT7;dOH!?p zi&B9UgOP!ufv%yEu7P2Qk%5(ov6YF5wt=aYfq}(LRXG$5x%nxXX_XKS29{tAAk|g| XW)KahriZQpYGCkm^>bP0l+XkKyyRU} literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png new file mode 100644 index 0000000000000000000000000000000000000000..32a045b8fafdc08640d53b2a86b1dcabcb0fe0fd GIT binary patch literal 579 zcmV-J0=)f+P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp) z=>Px$AW%$HMR1Z9#Q*@REHmdHAm<<;=p`lTDl6$LE15$|nM6yxJ3#6&F}^)Qx<p3n zH#h4zIK)Ou#79fTM@#HJKkY(8?L$QEL`CjJMeasM?ng(;QB>|oNbX2U?n+AUOH1!e zOz%xi?@dncPEXKRS?^Cz#amtQQBm+xQt(q#@KaRqR8;U)Rq<9<@mE*YVq(`~V)0vB z+GS<)VPW%PV%=$Jag!JHXlV6qZS`($_HuH3pd<EncJ_C7f1@XWqbGu>Du=8uhpjJ) zvN4agH<i3UrMYgWyK<eyN1Vq+p2tbA!G5C3PO!m$q|HvV#D$~JOsv;ftk_qv-CeQX zT*cC%$J3;><6_L$t<Kr8)!w|r@o>cPam4X*+~mi`^K{Glc|xh<NdN!<0d!JMQvg8b z*k%9#00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipf34<ROg z(p72z0056kL_t&-)1{2Z3c^qT1mDEody6IZuCW&s1V!`^4}$goe?(18@b2Db*j*w1 z%4M(p;&Zw?9ZaKAxo*x;COX}<8fzjqKRv^2kF1spymYHMjE2m7Hl|a~emCNwG8(i? z8I#``(kiBrEbh}(Qn8TL2+$}b3Hn^7p`K6R#_6zQ2$?ux;lXBYB>hkN@C%g?4vVBM R$bbL<002ovPDHLkV1min0OSAw literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..9d1d2a061c7948168d7b1c2474d769b31709f1cf GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE08{VY2nhHc^eMaU%j`d zaI$#+HuEKC{pKEZKYn>h(+aIMH^MjGjcs3}f9Cqyt&fvx7AT*)xv^`L;hf{Hi_iP4 zxgK%&V?q65_4c*;8}G%;JMMY<SLM___M4Bi9{E^!<YUpX&n1ga`7PgFwEkdS!(#P< zNn&@N9OqiK(i&(nV@Z%-FoVOh8)-leXMsm#F_88EW4Dvpc0fjir;B5V#O36K1sn!O zhRVf}5jSsGPH?f<xudc|vBoY<&5cb=uTGB9v187J4II+it5?jhn8F{TsCZIWRad$D zg1*K9W%cu2NsD(hEfVT#*wm#pYw;D04+0E(uQE?s`Z6*ZXoqTvYeY#(Vo9o1a#1Rf zVlXl=G|)9P(lsy)F*2|+F}5->(Kax(GBB{1sVaw}AvZrIGp!P$!N3x%0i@c>zzm|{ T)b!9bKn)C@u6{1-oD!M<=4O^k literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css new file mode 100644 index 0000000..4691c60 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css @@ -0,0 +1,23 @@ +.icon-coll-domain-constraints { + background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/coll-domain-constraints.png' )}}') !important; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} + +.icon-check-bad, .icon-domain-constraints-bad { + background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/domain-constraints-bad.png' )}}') !important; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} + +.icon-check, .icon-domain-constraints { + background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/domain-constraints.png' )}}') !important; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js new file mode 100644 index 0000000..c9f1a97 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js @@ -0,0 +1,150 @@ +// Domain Constraint Module: Collection and Node +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Constraint Collection Node + if (!pgBrowser.Nodes['coll-domain-constraints']) { + var domain_constraints = pgAdmin.Browser.Nodes['coll-domain-constraints'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + type: 'coll-domain-constraints', + columns: ['name', 'description'] + }); + }; + + // Domain Constraint Node + if (!pgBrowser.Nodes['domain-constraints']) { + pgAdmin.Browser.Nodes['domain-constraints'] = pgBrowser.Node.extend({ + type: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + collection_type: 'coll-domain-constraints', + hasSQL: true, + hasDepends: true, + parent_type: ['domain'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + oid: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined + }, + // Domain Constraint Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', type:'text', cell:'string', + disabled: 'isDisabled' + },{ + id: 'oid', label:'{{ _('OID') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'description', label: '{{ _('Comment') }}', type: 'multiline', cell: + 'string', mode: ['properties', 'create', 'edit'], min_version: 90500, + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', cel: + 'string', group: '{{ _('Definition') }}', mode: ['properties', + 'create', 'edit'], disabled: function(m) { return !m.isNew(); } + },{ + id: 'connoinherit', label: '{{ _('No Inherit') }}', type: + 'switch', cell: 'boolean', group: '{{ _('Definition') }}', mode: + ['properties', 'create', 'edit'], disabled: 'isDisabled', + visible: false + },{ + id: 'convalidated', label: "{{ _("Validate?") }}", type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', + options: { + 'onText': 'Yes', 'offText': 'No', + 'onColor': 'success', 'offColor': 'primary', + 'size': 'small' + }, + disabled: function(m) { + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return true; + } + else if(m.get('convalidated')) + { + return true; + } + return false; + } + return false; + }, + mode: ['properties', 'create', 'edit'] + }], + // Client Side Validation + validate: function() { + var err = {}, + errmsg; + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') { + err['consrc'] = '{{ _('Check can not be empty!') }}'; + errmsg = errmsg || err['consrc']; + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return true; + } + } + return false; + } + }), + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql new file mode 100644 index 0000000..be943d2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql @@ -0,0 +1,3 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }});{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..043f011 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql @@ -0,0 +1,14 @@ +SELECT + c.oid, conname AS name, typname AS relname, nspname, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql new file mode 100644 index 0000000..299ba6b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql @@ -0,0 +1,3 @@ +{% if data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql new file mode 100644 index 0000000..ad993f7 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql @@ -0,0 +1,10 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }}){% if not data.convalidated %} + + NOT VALID{% endif %};{% if data.description %} + + +COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON DOMAIN {{ conn|qtIdent(schema, domain) }} + IS '{{ data.description }}';{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_type_category.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_type_category.sql new file mode 100644 index 0000000..3e3b244 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_type_category.sql @@ -0,0 +1,5 @@ +SELECT + typcategory +FROM + pg_type +WHERE typname = {{datatype}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..34d8b34 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql @@ -0,0 +1,17 @@ +SELECT + c.oid, conname AS name, typname AS relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc, + connoinherit, convalidated, convalidated AS convalidated_p +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql new file mode 100644 index 0000000..628eb6e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql @@ -0,0 +1,13 @@ +{% set name = o_data.name %} +{% if data.name %} +{% set name = data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif -%}{% if data.convalidated %} + + +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + VALIDATE CONSTRAINT {{ conn|qtIdent(name) }};{% endif -%}{% if data.description %} + + +COMMENT ON CONSTRAINT {{ conn|qtIdent(name) }} ON DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + IS '{{ data.description }}';{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png new file mode 100644 index 0000000000000000000000000000000000000000..55621528a1dba4928538fe5557b9b988ed78d6ab GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}U4T!BE0BJeoqfW=;gF{0PD8_o zi&pN_(K)B7`FPi%2ix`@v9LU(tNUQ*!K1czXOxuggoGT_(#p@zf4Jw!!zHV)`3F7T zd-Um}H}`iRIijcc@Wq#>&ptkR`SszG54Uz6zW4mg?MLsgZ9jbb>E~y!zTSB9`Re1( zjS1S99(_9h@YC4`A5Y)^_^>@J3+MvIk|4ie28U-i(tsS!0*}aIAngIhZYQ(tfQ)ue z7sn8Z%b|U@#hMgESUvs4wKoSxO>~{O=-Yq$8_!r)-^e%4-l6f-Jox1%8}FIVx>FCW zPMVwluQGbkP1bcSwVV0d_?PZbZVis%HeYTWyU*$OvYp$uTc3QLbD?2trOnDuhZa>f z=3d_{(zER9xt(<n3UsBI4SNC?Y!rBW<m2`n>yDRyg$uoE8Qx}bo1D0qw;t#u)e_f; zl9a@fRIB8oR3OD*WMF8ZYiOivU>IU#U}a)#Wn!XjU}|MxU@=ow4n;$5eoAIqB}9XP eC0GMUwUvPxM8m1+p=*E|7(8A5T-G@yGywoRCC2Lj literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png new file mode 100644 index 0000000000000000000000000000000000000000..7521cddeaaaf0ee4e3c60e948078d70e17e06893 GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}T7XZ8E0Deu9eu*V;gF{0K`pIg zc6K`r4IeIAc}Q3HxV`=3y+@xudUJpGkt2F~PoI5!^78A$Cm(L@JbcYR=;4bm_nv>b z{pkI*?T7D#gyiSvKYR7{_S4Tdo_xOg_;X`|_N7Ok&OiKg_QA)~_dlLo85{w$iLoTe zFPOpM*^M+HhqJ&VvKUBvfU(=jY&#$$$<xI#MB;Mq`IABp20RT9wUXkaL(1R(pOn?c z>38<+e>KaN2{%0Jd@sMbY$+13Z&%Z<%!P+*SNu+#({Rr={_KxUhswHdID0QWTCBvD z)^ky;@gu`E#RAC#l`JcnelS0rxkKN7b1B>H56gX)0&P<*ag8WRNi0dVN-jzTQVd20 zh6cKZM!E)uAw~vPCdO7KCfWw3Rt5$ZGgakKH00)|WTsU@G#FTdHGouG8JIydoSGiG Q2B?9-)78&qol`;+03_q3ga7~l literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png new file mode 100644 index 0000000000000000000000000000000000000000..42ca929325854b8f34787425e8094d08c75983bc GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}PJmB{E0BJeoqfW=;gF{0PD8^7 zi&s6`bL8oxH}`iRdHU?*lb2r~J^6Th=ix^$zTSWS<?f>oH+CHUa^}pVf`aWvMt7fm zx&8F>jfd~AY(HF`mUiRG=W9<sUwQB@KR>@QLHo+%PZuA3I{)y~*$1D_JotF({zvHt zo%ulf7)yfuf*Bm1-ADs+I14-?i-EKU7`vU!wgWPXJzX3_Brcbpe=XFcAmSFNeC3q& zl!Z+(RsYN12&-NW{P*^#<rx82_2-+ii&SqfGgO`Jcj@;01@HTH{&&7#v?j}I)=J6m ze`~hppZn7?WA|%z!}T6pf>z0IjbZrS5ICD*275*_bAo)r8t#Vg4A*Lz);xc4JKq03 zXXC}zd)YwiRZCnWN>UO_QmvAUQh^kMk%6IsuAz~xfnkV|ft87|m5GVAfvJ^&fyGQ! qITQ`K`6-!cl@JXEmS7Da)m8>(5DllMhpqu?VDNPHb6Mw<&;$VOR=Fqu literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js new file mode 100644 index 0000000..1c6da2a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js @@ -0,0 +1,345 @@ +// Domain Module: Collection and Node. +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', + 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Collection Node + if (!pgBrowser.Nodes['coll-domain']) { + var domains = pgAdmin.Browser.Nodes['coll-domain'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain', + label: '{{ _('Domains') }}', + type: 'coll-domain', + columns: ['name', 'owner', 'description'] + }); + }; + + // Security Model + var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: null, + security_label: null + }, + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text', editable: true, cellHeaderClasses:'width_percent_50' + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text', editable: true, cellHeaderClasses:'width_percent_50' + }], + validate: function() { + var err = {}, + errmsg = null; + + if (_.isUndefined(this.get('security_label')) || + _.isNull(this.get('security_label')) || + String(this.get('security_label')).replace(/^\s+|\s+$/g, '') == '') { + errmsg = '{{ _('Please specify the value for all the security providers.')}}'; + this.errorModel.set('security_label', errmsg); + return errmsg; + } else { + this.errorModel.unset('security_label'); + } + return null; + } + }); + + // Constraint Model + var ConstraintModel = pgAdmin.Browser.Node.Model.extend({ + idAttribute: 'conoid', + defaults: { + conoid: undefined, + conname: undefined, + consrc: undefined, + convalidated: undefined + }, + schema: [{ + id: 'conname', label: '{{ _('Name') }}', type: 'text', cell: 'string', + cellHeaderClasses: 'width_percent_40', editable: 'isEditable' + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', + cell: Backgrid.Extension.TextareaCell, group: '{{ _('Definition') }}', + cellHeaderClasses: 'width_percent_60', editable: 'isEditable' + },{ + id: 'convalidated', label: '{{ _('Validate?') }}', type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', min_version: 90200, + editable: 'isEditable', + options: { + 'onText': 'Yes', 'offText': 'No', + 'onColor': 'success', 'offColor': 'primary', + 'size': 'small' + } + }], + isEditable: function(m) { + return _.isUndefined(m.isNew) ? true : m.isNew(); + }, + toJSON: Backbone.Model.prototype.toJSON + }); + + // Domain Node + if (!pgBrowser.Nodes['domain']) { + pgAdmin.Browser.Nodes['domain'] = pgBrowser.Node.extend({ + type: 'domain', + label: '{{ _('Domain') }}', + collection_type: 'coll-domain', + hasSQL: true, + hasDepends: true, + parent_type: ['schema'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + canDropCascade: pgBrowser.Nodes['schema'].canChildDrop, + // Domain Node Model + model: pgAdmin.Browser.Node.Model.extend({ + initialize: function(attrs, args) { + var isNew = (_.size(attrs) === 0); + if (isNew) { + // Set Selected Schema + schema = args.node_info.schema.label + this.set({'basensp': schema}, {silent: true}); + + // Set Current User + var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user; + this.set({'owner': userInfo.name}, {silent: true}); + } + pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments); + }, + defaults: { + name: undefined, + oid: undefined, + owner: undefined, + basensp: undefined, + description: undefined, + basetype: undefined, + typlen: undefined, + precision: undefined, + typdefault: undefined, + typnotnull: undefined, + sysdomain: undefined, + collname: undefined, + constraints: [], + seclabels: [] + }, + type_options: undefined, + // Domain Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'] + },{ + id: 'oid', label:'{{ _('OID') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl, + node: 'role', type: 'text', mode: ['edit', 'create', 'properties'] + },{ + id: 'basensp', label:'{{ _('Schema') }}', cell: 'node-list-by-name', + control: 'node-list-by-name', cache_level: 'database', type: 'text', + node: 'schema', mode: ['create', 'edit'] + },{ + id: 'sysdomain', label:'{{ _('System Domain?') }}', cell: 'boolean', + type: 'switch', mode: ['properties'], + options: { + 'onText': 'Yes', 'offText': 'No', + 'onColor': 'success', 'offColor': 'primary', + 'size': 'small' + } + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'basetype', label:'{{ _('Base Type') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', mode:['properties', 'create', 'edit'], group: '{{ _('Definition') }}', url: 'get_types', + disabled: function(m) { return !m.isNew(); }, first_empty: true, + transform: function(d){ + this.model.type_options = d; + return d; + } + },{ + id: 'typlen', label:'{{ _('Length') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'], + disabled: function(m) { + // We will store type from selected from combobox + if (!m.isNew()) { + return true; + } + var of_type = m.get('basetype'); + if(m.type_options) { + // iterating over all the types + _.each(m.type_options, function(o) { + // if type from selected from combobox matches in options + if ( of_type == o.value ) { + // if length is allowed for selected type + if(o.length) + { + // set the values in model + m.set('is_tlength', true, {silent: true}); + m.set('min_val', o.min_val, {silent: true}); + m.set('max_val', o.max_val, {silent: true}); + } + } + }); + } + return !m.get('is_tlength'); + } + },{ + id: 'precision', label:'{{ _('Precision') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'], + disabled: function(m) { + // We will store type from selected from combobox + if (!m.isNew()) { + return true; + } + var of_type = m.get('basetype'); + if(m.type_options) { + // iterating over all the types + _.each(m.type_options, function(o) { + // if type from selected from combobox matches in options + if ( of_type == o.value ) { + // if precession is allowed for selected type + if(o.precision) + { + // set the values in model + m.set('is_precision', true, {silent: true}); + m.set('min_val', o.min_val, {silent: true}); + m.set('max_val', o.max_val, {silent: true}); + } + } + }); + } + return !m.get('is_precision'); + } + },{ + id: 'typdefault', label:'{{ _('Default') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', + helpMessage: "Enter an expression or a value." + },{ + id: 'typnotnull', label:'{{ _('Not Null?') }}', cell: 'boolean', + type: 'switch', group: '{{ _('Definition') }}', + options: { + 'onText': 'Yes', 'offText': 'No', + 'onColor': 'success', 'offColor': 'primary', + 'size': 'small' + } + },{ + id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', group: '{{ _('Definition') }}', url: 'get_collations', disabled: function(m) { + return !m.isNew(); + } + },{ + id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string', + type: 'collection', group: '{{ _('Constraints') }}', mode: ['edit', 'create'], + model: ConstraintModel, canAdd: true, canDelete: true, + canEdit: false + },{ + id: 'seclabels', label: '{{ _('Security Labels') }}', + model: SecurityModel, type: 'collection', + group: '{{ _('Security') }}', mode: ['edit', 'create'], + min_version: 90100, canAdd: true, + canEdit: false, canDelete: true, + control: 'unique-col-collection', uniqueCol : ['provider'] + } + ], + validate: function() // Client Side Validation + { + var err = {}, + errmsg, + seclabels = this.get('seclabels'); + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('basetype')) || String(this.get('basetype')).replace(/^\s+|\s+$/g, '') == '') { + err['basetype'] = '{{ _('Base Type can not be empty!') }}'; + errmsg = errmsg || err['basetype']; + } + + if (seclabels) { + var secLabelsErr; + for (var i = 0; i < seclabels.models.length && !secLabelsErr; i++) { + secLabelsErr = (seclabels.models[i]).validate.apply(seclabels.models[i]); + if (secLabelsErr) { + err['seclabels'] = secLabelsErr; + errmsg = errmsg || secLabelsErr; + } + } + } + + this.errorModel.clear().set(err); + + return null; + } + }), + canCreate: function(itemData, item, data) { + //If check is false then , we will allow create menu + if (data && data.check == false) + return true; + + var t = pgBrowser.tree, i = item, d = itemData; + // To iterate over tree to check parent node + while (i) { + // If it is schema then allow user to create domain + if (_.indexOf(['schema'], d._type) > -1) + return true; + + if ('coll-domain' == d._type) { + //Check if we are not child of catalog + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + if( prev_d._type == 'catalog') { + return false; + } else { + return true; + } + } + i = t.hasParent(i) ? t.parent(i) : null; + d = i ? t.itemData(i) : null; + } + // by default we do not want to allow create menu + return true; + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return false; + } + } + return true; + } + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql new file mode 100644 index 0000000..f8b0b75 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql @@ -0,0 +1,30 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname %} + + COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL{% endif %}{% if data.constraints %}{% for c in data.constraints %}{% if c.conname and c.consrc %} + + CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% endif -%} +{% endfor -%} +{% endif -%}; + +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.description %} + + +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}';{% endif -%}{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} + + +{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%} +{% endfor -%} +{% endif -%} + +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..7a12b50 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql @@ -0,0 +1,16 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +AND + d.oid={{doid}}::int; +{% endif %} + +{% if name %} +DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql new file mode 100644 index 0000000..819fdbb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql new file mode 100644 index 0000000..897fb24 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql @@ -0,0 +1,15 @@ +SELECT + 'DOMAIN' AS objectkind, c.oid as conoid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as cons +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' + AND contypid = {{doid}}::oid +ORDER BY conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..8b5c891 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql @@ -0,0 +1,18 @@ +{% if doid %} +SELECT + d.typnamespace as scid +FROM + pg_type d +WHERE + d.oid={{ doid }}::oid; +{% else %} +SELECT + d.oid, d.typnamespace +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..42af39d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql @@ -0,0 +1,35 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, + pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT + array_agg(provider || '=' || label) + FROM + pg_seclabel sl1 + WHERE + sl1.objoid=d.oid) AS seclabels +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid + {% if doid %} + AND d.oid={{doid}}::int + {% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql new file mode 100644 index 0000000..6ff4052 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql @@ -0,0 +1,64 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER TYPE {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} + +{% endif -%} +{% if data.typnotnull and not o_data.typnotnull %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif -%}{% if data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault }}; +{% elif not data.typdefault and o_data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif -%}{% if data.owner %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.constraints %} +{% for c in data.constraints.deleted %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }}; +{% endfor -%} +{% for c in data.constraints.added %} +{% if c.conname and c.consrc %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} );{% endif -%} +{% endfor -%}{% endif -%} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} + +{% endfor -%} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} + +{% endfor -%} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} + +{% endfor -%} +{% endif -%}{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }};{% endif %}{% if data.basensp %} + + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%} +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql new file mode 100644 index 0000000..f71e5a3 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql @@ -0,0 +1,36 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname and data.collname != "pg_catalog.\"default\"" %} + + COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL{% endif %}; + +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %} + +{% if data.constraints %} +{% for c in data.constraints %}{% if c.conname and c.consrc %} + +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if not c.convalidated %} NOT VALID{% endif %}{% endif -%}; +{% endfor -%} +{% endif %} + +{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}';{% endif -%} + +{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} + + +{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%} +{% endfor -%} +{% endif -%} + +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..7a12b50 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql @@ -0,0 +1,16 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +AND + d.oid={{doid}}::int; +{% endif %} + +{% if name %} +DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql new file mode 100644 index 0000000..e59c17d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql new file mode 100644 index 0000000..df956bf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql @@ -0,0 +1,15 @@ +SELECT + 'DOMAIN' AS objectkind, c.oid as conoid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc, connoinherit, convalidated +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +ORDER BY + conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..8b5c891 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql @@ -0,0 +1,18 @@ +{% if doid %} +SELECT + d.typnamespace as scid +FROM + pg_type d +WHERE + d.oid={{ doid }}::oid; +{% else %} +SELECT + d.oid, d.typnamespace +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..2892988 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql @@ -0,0 +1,34 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT + array_agg(provider || '=' || label) + FROM + pg_shseclabel sl1 + WHERE + sl1.objoid=d.oid) AS seclabels +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid +{% if doid %} + AND d.oid={{doid}}::int +{% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql new file mode 100644 index 0000000..ecc7e9a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql @@ -0,0 +1,64 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} + +{% endif -%} +{% if data.typnotnull and not o_data.typnotnull %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif -%}{% if data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault }}; +{% elif not data.typdefault and o_data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif -%}{% if data.owner %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.constraints %} +{% for c in data.constraints.deleted %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }}; +{% endfor -%} +{% for c in data.constraints.added %} +{% if c.conname and c.consrc %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if c.convalidated %} + NOT VALID{% endif %}{% if c.connoinherit %} NO INHERIT{% endif -%};{% endif -%} +{% endfor -%}{% endif -%} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} + +{% endfor %} +{% endif -%} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif -%}{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif -%}{% if data.description %} + +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }};{% endif -%}{% if data.basensp %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%} +{% endif -%} ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-23 07:27 Khushboo Vashi <[email protected]> parent: Khushboo Vashi <[email protected]> 0 siblings, 2 replies; 29+ messages in thread From: Khushboo Vashi @ 2016-03-23 07:27 UTC (permalink / raw) To: Dave Page <[email protected]>; +Cc: pgadmin-hackers Updated one comment. On Wed, Mar 23, 2016 at 12:48 PM, Khushboo Vashi < [email protected]> wrote: > Hi, > > Please find attached updated patch for the Domains module. > > On Wed, Mar 16, 2016 at 9:40 PM, Dave Page <[email protected]> wrote: > >> Hi >> >> On Wed, Mar 16, 2016 at 2:03 PM, Khushboo Vashi >> <[email protected]> wrote: >> > Hi, >> > >> > Please find the updated Domain Module Patch. >> > >> > To test this patch, please apply Backgrid Textarea Cell Patch before >> this. >> >> Thanks. I believe with the following fixes, we'll be done :-) >> >> - Default values should be auto-quoted when necessary (ie. strings, on >> a text-based domain). >> > As per our discussion, this should leave unquoted. > And also added a hint to the field stated 'Enter an expression or a value.' > > - "System Domain?" should be in the General section, between owner and >> comment. >> > Done > >> - The switches should use the same colouring/styling as other objects, >> e.g. >> >> options: { >> 'onText': 'Yes', 'offText': 'No', >> 'onColor': 'success', 'offColor': 'primary', >> 'size': 'small' >> } >> > Done > >> - Please remove the Schema property from the main properties tab (not >> the properties dialogue). >> > Done > >> - No icon is show for Checks on the Dependents tab for a domain. >> > Done > >> - The add button on the Security Labels tab is spelt "Add". Why is >> that? Other instances of this grid use "ADD" which is the default in >> backform.pgadmin.js. >> > Done > >> - Dependencies on domain check constraints are listed as being on a >> "Type" not a "Domain". >> > Done > >> - If adding a domain constraint using the grid on the Domain dialogue, >> I cannot specify "NOT VALID". We need a checkbox for that in a narrow >> columns at the end. Unchecking it for an existing constraint should be >> the equivalent of doing "ALTER DOMAIN ... VALIDATE CONSTRAINT" >> > Done > >> - If I switch the "Don't Validate" switch on a constraint, there are >> leading blank lines in the generated SQL. The same occurs when adding >> a comment to a constraint. >> > Done > >> - I think we need to reverse the meaning of "Don't Validate" and >> rename to match the "Valid?" field that's on the properties list. >> Otherwise it's not clear they're the same thing. >> > Done > >> - s/Not Null/Not Null?/ >> > Done > >> >> >> -- >> Dave Page >> Blog: http://pgsnake.blogspot.com >> Twitter: @pgsnake >> >> EnterpriseDB UK: http://www.enterprisedb.com >> The Enterprise PostgreSQL Company >> > > ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-23 09:39 Dave Page <[email protected]> parent: Khushboo Vashi <[email protected]> 1 sibling, 1 reply; 29+ messages in thread From: Dave Page @ 2016-03-23 09:39 UTC (permalink / raw) To: Khushboo Vashi <[email protected]>; +Cc: pgadmin-hackers Did you forget to attach the latest patch? On Wed, Mar 23, 2016 at 7:27 AM, Khushboo Vashi <[email protected]> wrote: > Updated one comment. > > On Wed, Mar 23, 2016 at 12:48 PM, Khushboo Vashi > <[email protected]> wrote: >> >> Hi, >> >> Please find attached updated patch for the Domains module. >> >> On Wed, Mar 16, 2016 at 9:40 PM, Dave Page <[email protected]> wrote: >>> >>> Hi >>> >>> On Wed, Mar 16, 2016 at 2:03 PM, Khushboo Vashi >>> <[email protected]> wrote: >>> > Hi, >>> > >>> > Please find the updated Domain Module Patch. >>> > >>> > To test this patch, please apply Backgrid Textarea Cell Patch before >>> > this. >>> >>> Thanks. I believe with the following fixes, we'll be done :-) >>> >>> - Default values should be auto-quoted when necessary (ie. strings, on >>> a text-based domain). >> >> As per our discussion, this should leave unquoted. > > And also added a hint to the field stated 'Enter an expression or a value.' >> >> >>> - "System Domain?" should be in the General section, between owner and >>> comment. >> >> Done >>> >>> - The switches should use the same colouring/styling as other objects, >>> e.g. >>> >>> options: { >>> 'onText': 'Yes', 'offText': 'No', >>> 'onColor': 'success', 'offColor': 'primary', >>> 'size': 'small' >>> } >> >> Done >>> >>> - Please remove the Schema property from the main properties tab (not >>> the properties dialogue). >> >> Done >>> >>> - No icon is show for Checks on the Dependents tab for a domain. >> >> Done >>> >>> - The add button on the Security Labels tab is spelt "Add". Why is >>> that? Other instances of this grid use "ADD" which is the default in >>> backform.pgadmin.js. >> >> Done >>> >>> - Dependencies on domain check constraints are listed as being on a >>> "Type" not a "Domain". >> >> Done >>> >>> - If adding a domain constraint using the grid on the Domain dialogue, >>> I cannot specify "NOT VALID". We need a checkbox for that in a narrow >>> columns at the end. Unchecking it for an existing constraint should be >>> the equivalent of doing "ALTER DOMAIN ... VALIDATE CONSTRAINT" >> >> Done >>> >>> - If I switch the "Don't Validate" switch on a constraint, there are >>> leading blank lines in the generated SQL. The same occurs when adding >>> a comment to a constraint. >> >> Done >>> >>> - I think we need to reverse the meaning of "Don't Validate" and >>> rename to match the "Valid?" field that's on the properties list. >>> Otherwise it's not clear they're the same thing. >> >> Done >>> >>> - s/Not Null/Not Null?/ >> >> Done >>> >>> >>> >>> -- >>> Dave Page >>> Blog: http://pgsnake.blogspot.com >>> Twitter: @pgsnake >>> >>> EnterpriseDB UK: http://www.enterprisedb.com >>> The Enterprise PostgreSQL Company >> >> > -- Dave Page Blog: http://pgsnake.blogspot.com Twitter: @pgsnake EnterpriseDB UK: http://www.enterprisedb.com The Enterprise PostgreSQL Company -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-23 09:42 Ashesh Vashi <[email protected]> parent: Dave Page <[email protected]> 0 siblings, 0 replies; 29+ messages in thread From: Ashesh Vashi @ 2016-03-23 09:42 UTC (permalink / raw) To: Dave Page <[email protected]>; +Cc: Khushboo Vashi <[email protected]>; pgadmin-hackers On Wed, Mar 23, 2016 at 3:09 PM, Dave Page <[email protected]> wrote: > Did you forget to attach the latest patch? > She did it in the previous patch only. She forgot to mention about the help string, which she did in later mail. -- Thanks & Regards, Ashesh Vashi EnterpriseDB INDIA: Enterprise PostgreSQL Company <http://www.enterprisedb.com/; *http://www.linkedin.com/in/asheshvashi* <http://www.linkedin.com/in/asheshvashi; > > On Wed, Mar 23, 2016 at 7:27 AM, Khushboo Vashi > <[email protected]> wrote: > > Updated one comment. > > > > On Wed, Mar 23, 2016 at 12:48 PM, Khushboo Vashi > > <[email protected]> wrote: > >> > >> Hi, > >> > >> Please find attached updated patch for the Domains module. > >> > >> On Wed, Mar 16, 2016 at 9:40 PM, Dave Page <[email protected]> wrote: > >>> > >>> Hi > >>> > >>> On Wed, Mar 16, 2016 at 2:03 PM, Khushboo Vashi > >>> <[email protected]> wrote: > >>> > Hi, > >>> > > >>> > Please find the updated Domain Module Patch. > >>> > > >>> > To test this patch, please apply Backgrid Textarea Cell Patch before > >>> > this. > >>> > >>> Thanks. I believe with the following fixes, we'll be done :-) > >>> > >>> - Default values should be auto-quoted when necessary (ie. strings, on > >>> a text-based domain). > >> > >> As per our discussion, this should leave unquoted. > > > > And also added a hint to the field stated 'Enter an expression or a > value.' > >> > >> > >>> - "System Domain?" should be in the General section, between owner and > >>> comment. > >> > >> Done > >>> > >>> - The switches should use the same colouring/styling as other objects, > >>> e.g. > >>> > >>> options: { > >>> 'onText': 'Yes', 'offText': 'No', > >>> 'onColor': 'success', 'offColor': 'primary', > >>> 'size': 'small' > >>> } > >> > >> Done > >>> > >>> - Please remove the Schema property from the main properties tab (not > >>> the properties dialogue). > >> > >> Done > >>> > >>> - No icon is show for Checks on the Dependents tab for a domain. > >> > >> Done > >>> > >>> - The add button on the Security Labels tab is spelt "Add". Why is > >>> that? Other instances of this grid use "ADD" which is the default in > >>> backform.pgadmin.js. > >> > >> Done > >>> > >>> - Dependencies on domain check constraints are listed as being on a > >>> "Type" not a "Domain". > >> > >> Done > >>> > >>> - If adding a domain constraint using the grid on the Domain dialogue, > >>> I cannot specify "NOT VALID". We need a checkbox for that in a narrow > >>> columns at the end. Unchecking it for an existing constraint should be > >>> the equivalent of doing "ALTER DOMAIN ... VALIDATE CONSTRAINT" > >> > >> Done > >>> > >>> - If I switch the "Don't Validate" switch on a constraint, there are > >>> leading blank lines in the generated SQL. The same occurs when adding > >>> a comment to a constraint. > >> > >> Done > >>> > >>> - I think we need to reverse the meaning of "Don't Validate" and > >>> rename to match the "Valid?" field that's on the properties list. > >>> Otherwise it's not clear they're the same thing. > >> > >> Done > >>> > >>> - s/Not Null/Not Null?/ > >> > >> Done > >>> > >>> > >>> > >>> -- > >>> Dave Page > >>> Blog: http://pgsnake.blogspot.com > >>> Twitter: @pgsnake > >>> > >>> EnterpriseDB UK: http://www.enterprisedb.com > >>> The Enterprise PostgreSQL Company > >> > >> > > > > > > -- > Dave Page > Blog: http://pgsnake.blogspot.com > Twitter: @pgsnake > > EnterpriseDB UK: http://www.enterprisedb.com > The Enterprise PostgreSQL Company > > > -- > Sent via pgadmin-hackers mailing list ([email protected]) > To make changes to your subscription: > http://www.postgresql.org/mailpref/pgadmin-hackers > ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-23 13:05 Dave Page <[email protected]> parent: Khushboo Vashi <[email protected]> 1 sibling, 1 reply; 29+ messages in thread From: Dave Page @ 2016-03-23 13:05 UTC (permalink / raw) To: Khushboo Vashi <[email protected]>; +Cc: pgadmin-hackers Hi Almost there :-s - The hint for default should be a placeholder in the textbox itself (like combos have "Select from the list" - I should be able to check the "Validate?" option on one or more constraints from within the Domain dialogue - Please ensure the capitalisation of all property labels is consistent - it should be "Base type" not "Base Type", "System domain?" not "System Domain?" etc. - The check constraint reverse engineered SQL should include the path to the constraint, e.g. -- CHECK: schema.domain.check_at Once that's done, it can be committed I think. Thanks. On Wed, Mar 23, 2016 at 7:27 AM, Khushboo Vashi <[email protected]> wrote: > Updated one comment. > > On Wed, Mar 23, 2016 at 12:48 PM, Khushboo Vashi > <[email protected]> wrote: >> >> Hi, >> >> Please find attached updated patch for the Domains module. >> >> On Wed, Mar 16, 2016 at 9:40 PM, Dave Page <[email protected]> wrote: >>> >>> Hi >>> >>> On Wed, Mar 16, 2016 at 2:03 PM, Khushboo Vashi >>> <[email protected]> wrote: >>> > Hi, >>> > >>> > Please find the updated Domain Module Patch. >>> > >>> > To test this patch, please apply Backgrid Textarea Cell Patch before >>> > this. >>> >>> Thanks. I believe with the following fixes, we'll be done :-) >>> >>> - Default values should be auto-quoted when necessary (ie. strings, on >>> a text-based domain). >> >> As per our discussion, this should leave unquoted. > > And also added a hint to the field stated 'Enter an expression or a value.' >> >> >>> - "System Domain?" should be in the General section, between owner and >>> comment. >> >> Done >>> >>> - The switches should use the same colouring/styling as other objects, >>> e.g. >>> >>> options: { >>> 'onText': 'Yes', 'offText': 'No', >>> 'onColor': 'success', 'offColor': 'primary', >>> 'size': 'small' >>> } >> >> Done >>> >>> - Please remove the Schema property from the main properties tab (not >>> the properties dialogue). >> >> Done >>> >>> - No icon is show for Checks on the Dependents tab for a domain. >> >> Done >>> >>> - The add button on the Security Labels tab is spelt "Add". Why is >>> that? Other instances of this grid use "ADD" which is the default in >>> backform.pgadmin.js. >> >> Done >>> >>> - Dependencies on domain check constraints are listed as being on a >>> "Type" not a "Domain". >> >> Done >>> >>> - If adding a domain constraint using the grid on the Domain dialogue, >>> I cannot specify "NOT VALID". We need a checkbox for that in a narrow >>> columns at the end. Unchecking it for an existing constraint should be >>> the equivalent of doing "ALTER DOMAIN ... VALIDATE CONSTRAINT" >> >> Done >>> >>> - If I switch the "Don't Validate" switch on a constraint, there are >>> leading blank lines in the generated SQL. The same occurs when adding >>> a comment to a constraint. >> >> Done >>> >>> - I think we need to reverse the meaning of "Don't Validate" and >>> rename to match the "Valid?" field that's on the properties list. >>> Otherwise it's not clear they're the same thing. >> >> Done >>> >>> - s/Not Null/Not Null?/ >> >> Done >>> >>> >>> >>> -- >>> Dave Page >>> Blog: http://pgsnake.blogspot.com >>> Twitter: @pgsnake >>> >>> EnterpriseDB UK: http://www.enterprisedb.com >>> The Enterprise PostgreSQL Company >> >> > -- Dave Page Blog: http://pgsnake.blogspot.com Twitter: @pgsnake EnterpriseDB UK: http://www.enterprisedb.com The Enterprise PostgreSQL Company -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-24 09:54 Khushboo Vashi <[email protected]> parent: Dave Page <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Khushboo Vashi @ 2016-03-24 09:54 UTC (permalink / raw) To: Dave Page <[email protected]>; +Cc: pgadmin-hackers Hi, Please find the attached updated patch for the Domain module. Thanks, Khushboo On Wed, Mar 23, 2016 at 6:35 PM, Dave Page <[email protected]> wrote: > Hi > > Almost there :-s > > - The hint for default should be a placeholder in the textbox itself > (like combos have "Select from the list" > Done > - I should be able to check the "Validate?" option on one or more > constraints from within the Domain dialogue > Done. Constraint Name can also be changed through the Domain dialogue. > - Please ensure the capitalisation of all property labels is > consistent - it should be "Base type" not "Base Type", "System > domain?" not "System Domain?" etc. > Done > - The check constraint reverse engineered SQL should include the path > to the constraint, e.g. > > Done > -- CHECK: schema.domain.check_at > > Once that's done, it can be committed I think. > > Thanks. > > On Wed, Mar 23, 2016 at 7:27 AM, Khushboo Vashi > <[email protected]> wrote: > > Updated one comment. > > > > On Wed, Mar 23, 2016 at 12:48 PM, Khushboo Vashi > > <[email protected]> wrote: > >> > >> Hi, > >> > >> Please find attached updated patch for the Domains module. > >> > >> On Wed, Mar 16, 2016 at 9:40 PM, Dave Page <[email protected]> wrote: > >>> > >>> Hi > >>> > >>> On Wed, Mar 16, 2016 at 2:03 PM, Khushboo Vashi > >>> <[email protected]> wrote: > >>> > Hi, > >>> > > >>> > Please find the updated Domain Module Patch. > >>> > > >>> > To test this patch, please apply Backgrid Textarea Cell Patch before > >>> > this. > >>> > >>> Thanks. I believe with the following fixes, we'll be done :-) > >>> > >>> - Default values should be auto-quoted when necessary (ie. strings, on > >>> a text-based domain). > >> > >> As per our discussion, this should leave unquoted. > > > > And also added a hint to the field stated 'Enter an expression or a > value.' > >> > >> > >>> - "System Domain?" should be in the General section, between owner and > >>> comment. > >> > >> Done > >>> > >>> - The switches should use the same colouring/styling as other objects, > >>> e.g. > >>> > >>> options: { > >>> 'onText': 'Yes', 'offText': 'No', > >>> 'onColor': 'success', 'offColor': 'primary', > >>> 'size': 'small' > >>> } > >> > >> Done > >>> > >>> - Please remove the Schema property from the main properties tab (not > >>> the properties dialogue). > >> > >> Done > >>> > >>> - No icon is show for Checks on the Dependents tab for a domain. > >> > >> Done > >>> > >>> - The add button on the Security Labels tab is spelt "Add". Why is > >>> that? Other instances of this grid use "ADD" which is the default in > >>> backform.pgadmin.js. > >> > >> Done > >>> > >>> - Dependencies on domain check constraints are listed as being on a > >>> "Type" not a "Domain". > >> > >> Done > >>> > >>> - If adding a domain constraint using the grid on the Domain dialogue, > >>> I cannot specify "NOT VALID". We need a checkbox for that in a narrow > >>> columns at the end. Unchecking it for an existing constraint should be > >>> the equivalent of doing "ALTER DOMAIN ... VALIDATE CONSTRAINT" > >> > >> Done > >>> > >>> - If I switch the "Don't Validate" switch on a constraint, there are > >>> leading blank lines in the generated SQL. The same occurs when adding > >>> a comment to a constraint. > >> > >> Done > >>> > >>> - I think we need to reverse the meaning of "Don't Validate" and > >>> rename to match the "Valid?" field that's on the properties list. > >>> Otherwise it's not clear they're the same thing. > >> > >> Done > >>> > >>> - s/Not Null/Not Null?/ > >> > >> Done > >>> > >>> > >>> > >>> -- > >>> Dave Page > >>> Blog: http://pgsnake.blogspot.com > >>> Twitter: @pgsnake > >>> > >>> EnterpriseDB UK: http://www.enterprisedb.com > >>> The Enterprise PostgreSQL Company > >> > >> > > > > > > -- > Dave Page > Blog: http://pgsnake.blogspot.com > Twitter: @pgsnake > > EnterpriseDB UK: http://www.enterprisedb.com > The Enterprise PostgreSQL Company > -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers Attachments: [text/x-patch] Domains_ver_7.patch (104.6K, 3-Domains_ver_7.patch) download | inline diff: diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py new file mode 100644 index 0000000..c21b24a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py @@ -0,0 +1,824 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements the Domain Node.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error, gone +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas as schemas +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from pgadmin.browser.server_groups.servers.databases.schemas.utils import \ + SchemaChildModule, DataTypeReader +from pgadmin.browser.server_groups.servers.databases.utils import \ + parse_sec_labels_from_db +from functools import wraps + + +class DomainModule(SchemaChildModule): + """ + class DomainModule(SchemaChildModule): + + This class represents The Domain Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Module. + + * get_nodes(gid, sid, did, scid) + - Generate the domain collection node. + + * script_load() + - Load the module script for domain, when schema node is + initialized. + """ + + NODE_TYPE = 'domain' + COLLECTION_LABEL = gettext("Domains") + + def __init__(self, *args, **kwargs): + super(DomainModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid): + """ + Generate the domain collection node. + """ + yield self.generate_browser_collection_node(scid) + + @property + def script_load(self): + """ + Load the module script for domain, when schema node is + initialized. + """ + return schemas.SchemaModule.NODE_TYPE + + +blueprint = DomainModule(__name__) + + +class DomainView(PGChildNodeView, DataTypeReader): + """ + class DomainView + + This class inherits PGChildNodeView to get the different routes for + the module. Also, inherits DataTypeReader to get data types. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain. + + Methods: + ------- + * validate_request(f): + - Works as a decorator. + Validating request on the request of create, update and modified SQL. + + * module_js(): + - Load JS file (domains.js) for this module. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domains. + + * nodes(gid, sid, did, scid): + - Returns all the Domains to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain properties. + + * get_collations(gid, sid, did, scid, doid=None): + - Returns Collations. + + * create(gid, sid, did, scid): + - Creates a new Domain object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain object. + + * dependents(gid, sid, did, scid, doid): + - Returns the dependents for the Domain object. + + * dependencies(gid, sid, did, scid, doid): + - Returns the dependencies for the Domain object. + + * types(gid, sid, did, scid, fnid=None): + - Returns Data Types. + """ + + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'doid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_types': [{'get': 'types'}, {'get': 'types'}], + 'get_collations': [ + {'get': 'get_collations'}, + {'get': 'get_collations'} + ] + }) + + def validate_request(f): + """ + Works as a decorator. + Validating request on the request of create, update and modified SQL. + + Required Args: + name: Name of the Domain + owner: Domain Owner + basensp: Schema Name + basetype: Data Type of the Domain + + Above both the arguments will not be validated in the update action. + """ + + @wraps(f) + def wrap(self, **kwargs): + + data = {} + if request.data: + req = json.loads(request.data.decode()) + else: + req = request.args or request.form + + if 'doid' not in kwargs: + required_args = [ + 'name', + 'basetype' + ] + + for arg in required_args: + if arg not in req or req[arg] == '': + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter \ + (%s)." % arg + ) + ) + + try: + list_params = [] + if request.method == 'GET': + list_params = ['constraints', 'seclabels'] + + for key in req: + if key in list_params and req[key] != '' \ + and req[key] is not None: + # Coverts string into python list as expected. + data[key] = json.loads(req[key]) + elif key == 'typnotnull': + data[key] = True if req[key] == 'true' or req[key] is\ + True else\ + (False if req[key] == 'false' or req[key] is + False else '') + else: + data[key] = req[key] + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + self.request = data + return f(self, **kwargs) + + return wrap + + def module_js(self): + """ + Load JS file (domains.js) for this module. + """ + return make_response( + render_template( + "domains/js/domains.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + # Get database connection + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domains/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domains/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid): + """ + List the Domains. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid): + """ + Returns all the Domains to generate Nodes in the browser. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + res = [] + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + scid, + row['name'], + icon="icon-domain" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid): + """ + Returns the Domain properties. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + if len(res['rows']) == 0: + return gone(gettext(""" +Could not find the domain in the database. +It may have been removed by another user or +shifted to the another schema. +""")) + + data = res['rows'][0] + + # Get Type Length and Precision + data.update(self._parse_type(data['fulltype'])) + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + # Get formatted Security Labels + if 'seclabels' in data: + data.update(parse_sec_labels_from_db(data['seclabels'])) + + # Set System Domain Status + data['sysdomain'] = False + if doid <= self.manager.db_info[did]['datlastsysoid']: + data['sysdomain'] = True + + return ajax_response( + response=data, + status=200 + ) + + def _parse_type(self, basetype): + """ + Returns Type and Data Type from the basetype. + """ + typ_len = '' + typ_precision = '' + + # The Length and the precision of the Datatype should be separate. + # The Format we getting from database is: numeric(1,1) + # So, we need to separate Length: 1, Precision: 1 + + if basetype != '' and basetype.find("(") > 0: + substr = basetype[basetype.find("(") + 1:len( + basetype) - 1] + typlen = substr.split(",") + if len(typlen) > 1: + typ_len = typlen[0] + typ_precision = typlen[1] + else: + typ_len = typlen + typ_precision = '' + + return {'typlen': typ_len, 'precision': typ_precision} + + @check_precondition + def get_collations(self, gid, sid, did, scid, doid=None): + """ + Returns Collations. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + res = [{'label': '', 'value': ''}] + try: + SQL = render_template("/".join([self.template_path, + 'get_collations.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append({'label': row['copy_collation'], + 'value': row['copy_collation']} + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def types(self, gid, sid, did, scid, doid=None): + """ + Returns the Data Types. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + fnid: Function Id + """ + + condition = """typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r') +AND NOT EXISTS (SELECT 1 FROM pg_class WHERE relnamespace=typnamespace +AND relname = typname AND relkind != 'c') AND +(typname NOT LIKE '_%' OR NOT EXISTS (SELECT 1 FROM pg_class WHERE +relnamespace=typnamespace AND relname = substring(typname FROM 2)::name +AND relkind != 'c'))""" + + if self.blueprint.show_system_objects: + condition += " AND nsp.nspname != 'information_schema'" + + # Get Types + status, types = self.get_types(self.conn, condition) + + if not status: + return internal_server_error(errormsg=types) + + return make_json_response( + data=types, + status=200 + ) + + @check_precondition + @validate_request + def create(self, gid, sid, did, scid): + """ + Creates a new Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + Domain object in json format. + """ + + data = self.request + try: + status, SQL = self.get_sql(gid, sid, data, scid) + + if not status: + return internal_server_error(errormsg=SQL) + + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # We need oid to to add object in tree at browser, below sql will + # gives the same + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + basensp=data['basensp'], + name=data['name']) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + doid, scid = res['rows'][0] + + return jsonify( + node=self.blueprint.generate_browser_node( + doid, + scid, + data['name'], + icon="icon-domain" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid): + """ + Drops the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + name, basensp = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + name=name, basensp=basensp, cascade=cascade) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + @validate_request + def update(self, gid, sid, did, scid, doid): + """ + Updates the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if not status: + return internal_server_error(errormsg=SQL) + + try: + if SQL: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get Schema Id + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + scid = res['rows'][0]['scid'] + + return make_json_response( + success=1, + info="Domain updated", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid=None): + """ + Returns the SQL for the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return False, internal_server_error(errormsg=res) + data = res['rows'][0] + + # Get Type Length and Precision + data.update(self._parse_type(data['fulltype'])) + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), data=data) + + sql_header = """-- DOMAIN: {0} + +-- DROP DOMAIN {0}; + +""".format(data['basensp'] + '.' + data['name']) + + SQL = sql_header + SQL + + return ajax_response(response=SQL.strip('\n')) + + @check_precondition + @validate_request + def msql(self, gid, sid, did, scid, doid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + SQL statements to create/update the Domain. + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid=None): + """ + Generates the SQL statements to create/update the Domain. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + try: + if doid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + con_data = {} + for c in res['rows']: + con_data[c['conoid']] = c + + old_data['constraints'] = con_data + + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data) + else: + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data) + return True, SQL.strip('\n') + + except Exception as e: + return False, e + + @check_precondition + def dependents(self, gid, sid, did, scid, doid): + """ + This function get the dependents and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependents_result = self.get_dependents(self.conn, doid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid): + """ + This function get the dependencies and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependencies_result = self.get_dependencies(self.conn, doid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py new file mode 100644 index 0000000..8fcace1 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py @@ -0,0 +1,691 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements the Domain Constraint Module.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas.domains \ + as domains +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + + +class DomainConstraintModule(CollectionNodeModule): + """ + class DomainConstraintModule(CollectionNodeModule): + + This class represents The Domain Constraint Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Constraint Module. + + * get_nodes(gid, sid, did, scid) + - Generate the Domain Constraint collection node. + + * node_inode(gid, sid, did, scid) + - Returns Domain Constraint node as leaf node. + + * script_load() + - Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + NODE_TYPE = 'domain-constraints' + COLLECTION_LABEL = gettext("Domain Constraints") + + def __init__(self, *args, **kwargs): + super(DomainConstraintModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid, doid): + """ + Generate the Domain Constraint collection node. + """ + yield self.generate_browser_collection_node(doid) + + @property + def node_inode(self): + """ + Returns Domain Constraint node as leaf node. + """ + return False + + @property + def script_load(self): + """ + Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + return domains.DomainModule.NODE_TYPE + + @property + def csssnippets(self): + """ + Returns a snippet of css to include in the page + """ + return [ + render_template( + "domain-constraints/css/domain-constraints.css", + node_type=self.node_type + ) + ] + + + +blueprint = DomainConstraintModule(__name__) + + +class DomainConstraintView(PGChildNodeView): + """ + class DomainConstraintView(PGChildNodeView): + + This class inherits PGChildNodeView to get the different routes for + the module. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain Constraint. + + Methods: + ------- + + * module_js(): + - Load JS file (domain-constraints.js) for this module. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domain Constraints. + + * nodes(gid, sid, did, scid): + - Returns all the Domain Constraints to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain Constraint properties. + + * create(gid, sid, did, scid): + - Creates a new Domain Constraint object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain Constraint object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain Constraint object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain Constraint object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain Constraint. + object. + + * dependents(gid, sid, did, scid, doid, coid): + - Returns the dependents for the Domain Constraint object. + + * dependencies(gid, sid, did, scid, doid, coid): + - Returns the dependencies for the Domain Constraint object. + """ + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'}, + {'type': 'int', 'id': 'doid'} + ] + ids = [ + {'type': 'int', 'id': 'coid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def validate_request(f): + """ + Works as a decorator. + Validating request on the request of create, update and modified SQL. + + Required Args: + name: Name of the Domain Constraint + consrc: Check Constraint Definition + + Above both the arguments will not be validated in the update action. + """ + + @wraps(f) + def wrap(self, **kwargs): + + data = {} + if request.data: + req = json.loads(request.data.decode()) + else: + req = request.args or request.form + + if 'coid' not in kwargs: + required_args = [ + 'name', + 'consrc' + ] + + for arg in required_args: + if arg not in req or req[arg] == '': + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter \ + (%s)." % arg + ) + ) + + try: + for key in req: + if key == 'convalidated': + data[key] = True if (req[key] == 'true' or req[key] is + True) else False + else: + data[key] = req[key] + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + self.request = data + return f(self, **kwargs) + + return wrap + + def module_js(self): + """ + Load JS file (domain-constraints.js) for this module. + """ + return make_response( + render_template( + "domain-constraints/js/domain-constraints.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domain-constraints/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domain-constraints/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid, doid): + """ + List the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid, doid): + """ + Returns all the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + res = [] + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, rset = self.conn.execute_2darray(SQL) + + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + if 'convalidated' not in row: + icon = 'icon-domain-constraints' + elif row['convalidated']: + icon = 'icon-domain-constraints' + else: + icon = 'icon-domain-constraints-bad' + res.append( + self.blueprint.generate_browser_node( + row['oid'], + doid, + row['name'], + icon=icon + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid, coid): + """ + Returns the Domain Constraints property. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + return ajax_response( + response=data, + status=200 + ) + + @check_precondition + @validate_request + def create(self, gid, sid, did, scid, doid): + """ + Creates a new Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Returns: + Domain Constraint object in json format. + """ + data = self.request + try: + status, SQL = self.get_sql(gid, sid, data, scid, doid) + if not status: + return internal_server_error(errormsg=SQL) + + status, res = self.conn.execute_scalar(SQL) + + if not status: + return internal_server_error(errormsg=res) + + # Get the recently added constraints oid + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + doid=doid, name=data['name']) + status, coid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=coid) + + if 'convalidated' not in data: + icon = 'icon-domain-constraints' + elif 'convalidated' in data and data['convalidated']: + icon = 'icon-domain-constraints' + else: + icon = 'icon-domain-constraints-bad' + + return jsonify( + node=self.blueprint.generate_browser_node( + coid, + doid, + data['name'], + icon=icon + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid, coid): + """ + Drops the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain Constraint dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + @validate_request + def update(self, gid, sid, did, scid, doid, coid): + """ + Updates the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + data = self.request + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + + try: + if SQL and status: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + if 'convalidated' in data and data['convalidated']: + icon = 'icon-domain-constraints' + elif 'convalidated' in data and not data['convalidated']: + icon = 'icon-domain-constraints-bad' + else: + icon = '' + + return make_json_response( + success=1, + info="Domain Constraint updated", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did, + 'icon': icon + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the SQL for the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + # Get Schema and Domain. + domain, schema = self._get_domain(doid) + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + + sql_header = """-- CHECK: {1}.{0} + +-- ALTER DOMAIN {1} DROP CONSTRAINT {0}; + +""".format(data['name'], schema + '.' + domain) + + SQL = sql_header + SQL + + return ajax_response(response=SQL) + + @check_precondition + @validate_request + def msql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + + Returns: + Domain Constraint object in json format. + """ + data = self.request + + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + if status and SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid, coid=None): + """ + Generates the SQL statements to create/update the Domain Constraint. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + if coid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data, conn=self.conn + ) + else: + domain, schema = self._get_domain(doid) + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + return True, SQL.strip('\n') + except Exception as e: + return False, internal_server_error(errormsg=str(e)) + + def _get_domain(self, doid): + """ + Returns Domain and Schema name. + + Args: + doid: Domain Id + + """ + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + return res['rows'][0] + + @check_precondition + def dependents(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependents and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependents_result = self.get_dependents(self.conn, coid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependencies and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependencies_result = self.get_dependencies(self.conn, coid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainConstraintView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..d62e13705c50e6c0cf8f19d680053e8643e28751 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=2TrO847{Jl`|t`Qpas zn<hS+l=rMY>RGPqvlydizRJ&>B`Om#V}R-yOM?7@862M7NCR>>3p^r=fwTu0yPeFo z12TL)T^vI=t|uoPU||ZF<tgaHG*QsQ!?m&Tq=?3mCu}J#Dx3x@mM}}^iE=5NIWXnk zkpnC4ai%a>@;Gg7=uums=9bIK=Egd~(us-3g@Iv02gfsK^JP^)gH=mhBT7;dOH!?p zi&B9UgOP!ufv%yEu7P2Qk%5(ov6YF5wt=aYfq}(LRXG$5x%nxXX_XKS29{tAAk|g| XW)KahriZQpYGCkm^>bP0l+XkKyyRU} literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png new file mode 100644 index 0000000000000000000000000000000000000000..32a045b8fafdc08640d53b2a86b1dcabcb0fe0fd GIT binary patch literal 579 zcmV-J0=)f+P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp) z=>Px$AW%$HMR1Z9#Q*@REHmdHAm<<;=p`lTDl6$LE15$|nM6yxJ3#6&F}^)Qx<p3n zH#h4zIK)Ou#79fTM@#HJKkY(8?L$QEL`CjJMeasM?ng(;QB>|oNbX2U?n+AUOH1!e zOz%xi?@dncPEXKRS?^Cz#amtQQBm+xQt(q#@KaRqR8;U)Rq<9<@mE*YVq(`~V)0vB z+GS<)VPW%PV%=$Jag!JHXlV6qZS`($_HuH3pd<EncJ_C7f1@XWqbGu>Du=8uhpjJ) zvN4agH<i3UrMYgWyK<eyN1Vq+p2tbA!G5C3PO!m$q|HvV#D$~JOsv;ftk_qv-CeQX zT*cC%$J3;><6_L$t<Kr8)!w|r@o>cPam4X*+~mi`^K{Glc|xh<NdN!<0d!JMQvg8b z*k%9#00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipf34<ROg z(p72z0056kL_t&-)1{2Z3c^qT1mDEody6IZuCW&s1V!`^4}$goe?(18@b2Db*j*w1 z%4M(p;&Zw?9ZaKAxo*x;COX}<8fzjqKRv^2kF1spymYHMjE2m7Hl|a~emCNwG8(i? z8I#``(kiBrEbh}(Qn8TL2+$}b3Hn^7p`K6R#_6zQ2$?ux;lXBYB>hkN@C%g?4vVBM R$bbL<002ovPDHLkV1min0OSAw literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..9d1d2a061c7948168d7b1c2474d769b31709f1cf GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE08{VY2nhHc^eMaU%j`d zaI$#+HuEKC{pKEZKYn>h(+aIMH^MjGjcs3}f9Cqyt&fvx7AT*)xv^`L;hf{Hi_iP4 zxgK%&V?q65_4c*;8}G%;JMMY<SLM___M4Bi9{E^!<YUpX&n1ga`7PgFwEkdS!(#P< zNn&@N9OqiK(i&(nV@Z%-FoVOh8)-leXMsm#F_88EW4Dvpc0fjir;B5V#O36K1sn!O zhRVf}5jSsGPH?f<xudc|vBoY<&5cb=uTGB9v187J4II+it5?jhn8F{TsCZIWRad$D zg1*K9W%cu2NsD(hEfVT#*wm#pYw;D04+0E(uQE?s`Z6*ZXoqTvYeY#(Vo9o1a#1Rf zVlXl=G|)9P(lsy)F*2|+F}5->(Kax(GBB{1sVaw}AvZrIGp!P$!N3x%0i@c>zzm|{ T)b!9bKn)C@u6{1-oD!M<=4O^k literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css new file mode 100644 index 0000000..4691c60 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css @@ -0,0 +1,23 @@ +.icon-coll-domain-constraints { + background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/coll-domain-constraints.png' )}}') !important; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} + +.icon-check-bad, .icon-domain-constraints-bad { + background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/domain-constraints-bad.png' )}}') !important; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} + +.icon-check, .icon-domain-constraints { + background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/domain-constraints.png' )}}') !important; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js new file mode 100644 index 0000000..33d1a85 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js @@ -0,0 +1,142 @@ +// Domain Constraint Module: Collection and Node +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Constraint Collection Node + if (!pgBrowser.Nodes['coll-domain-constraints']) { + var domain_constraints = pgAdmin.Browser.Nodes['coll-domain-constraints'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + type: 'coll-domain-constraints', + columns: ['name', 'description'] + }); + }; + + // Domain Constraint Node + if (!pgBrowser.Nodes['domain-constraints']) { + pgAdmin.Browser.Nodes['domain-constraints'] = pgBrowser.Node.extend({ + type: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + collection_type: 'coll-domain-constraints', + hasSQL: true, + hasDepends: true, + parent_type: ['domain'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + oid: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: true + }, + // Domain Constraint Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', type:'text', cell:'string', + disabled: 'isDisabled' + },{ + id: 'oid', label:'{{ _('OID') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'description', label: '{{ _('Comment') }}', type: 'multiline', cell: + 'string', mode: ['properties', 'create', 'edit'], min_version: 90500, + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', cel: + 'string', group: '{{ _('Definition') }}', mode: ['properties', + 'create', 'edit'], disabled: function(m) { return !m.isNew(); } + },{ + id: 'connoinherit', label: '{{ _('No Inherit') }}', type: + 'switch', cell: 'boolean', group: '{{ _('Definition') }}', mode: + ['properties', 'create', 'edit'], disabled: 'isDisabled', + visible: false + },{ + id: 'convalidated', label: "{{ _("Validate?") }}", type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', min_version: 90200, + disabled: function(m) { + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) { return true; + } + else if(m.get('convalidated')) { + return true; + } + return false; + } + return false; + }, + mode: ['properties', 'create', 'edit'] + }], + // Client Side Validation + validate: function() { + var err = {}, + errmsg; + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') { + err['consrc'] = '{{ _('Check can not be empty!') }}'; + errmsg = errmsg || err['consrc']; + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return true; + } + } + return false; + } + }), + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql new file mode 100644 index 0000000..be943d2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql @@ -0,0 +1,3 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }});{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..043f011 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql @@ -0,0 +1,14 @@ +SELECT + c.oid, conname AS name, typname AS relname, nspname, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql new file mode 100644 index 0000000..299ba6b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql @@ -0,0 +1,3 @@ +{% if data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql new file mode 100644 index 0000000..ad993f7 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql @@ -0,0 +1,10 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }}){% if not data.convalidated %} + + NOT VALID{% endif %};{% if data.description %} + + +COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON DOMAIN {{ conn|qtIdent(schema, domain) }} + IS '{{ data.description }}';{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_type_category.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_type_category.sql new file mode 100644 index 0000000..3e3b244 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_type_category.sql @@ -0,0 +1,5 @@ +SELECT + typcategory +FROM + pg_type +WHERE typname = {{datatype}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..34d8b34 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql @@ -0,0 +1,17 @@ +SELECT + c.oid, conname AS name, typname AS relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc, + connoinherit, convalidated, convalidated AS convalidated_p +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql new file mode 100644 index 0000000..628eb6e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql @@ -0,0 +1,13 @@ +{% set name = o_data.name %} +{% if data.name %} +{% set name = data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif -%}{% if data.convalidated %} + + +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + VALIDATE CONSTRAINT {{ conn|qtIdent(name) }};{% endif -%}{% if data.description %} + + +COMMENT ON CONSTRAINT {{ conn|qtIdent(name) }} ON DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + IS '{{ data.description }}';{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png new file mode 100644 index 0000000000000000000000000000000000000000..55621528a1dba4928538fe5557b9b988ed78d6ab GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}U4T!BE0BJeoqfW=;gF{0PD8_o zi&pN_(K)B7`FPi%2ix`@v9LU(tNUQ*!K1czXOxuggoGT_(#p@zf4Jw!!zHV)`3F7T zd-Um}H}`iRIijcc@Wq#>&ptkR`SszG54Uz6zW4mg?MLsgZ9jbb>E~y!zTSB9`Re1( zjS1S99(_9h@YC4`A5Y)^_^>@J3+MvIk|4ie28U-i(tsS!0*}aIAngIhZYQ(tfQ)ue z7sn8Z%b|U@#hMgESUvs4wKoSxO>~{O=-Yq$8_!r)-^e%4-l6f-Jox1%8}FIVx>FCW zPMVwluQGbkP1bcSwVV0d_?PZbZVis%HeYTWyU*$OvYp$uTc3QLbD?2trOnDuhZa>f z=3d_{(zER9xt(<n3UsBI4SNC?Y!rBW<m2`n>yDRyg$uoE8Qx}bo1D0qw;t#u)e_f; zl9a@fRIB8oR3OD*WMF8ZYiOivU>IU#U}a)#Wn!XjU}|MxU@=ow4n;$5eoAIqB}9XP eC0GMUwUvPxM8m1+p=*E|7(8A5T-G@yGywoRCC2Lj literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png new file mode 100644 index 0000000000000000000000000000000000000000..7521cddeaaaf0ee4e3c60e948078d70e17e06893 GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}T7XZ8E0Deu9eu*V;gF{0K`pIg zc6K`r4IeIAc}Q3HxV`=3y+@xudUJpGkt2F~PoI5!^78A$Cm(L@JbcYR=;4bm_nv>b z{pkI*?T7D#gyiSvKYR7{_S4Tdo_xOg_;X`|_N7Ok&OiKg_QA)~_dlLo85{w$iLoTe zFPOpM*^M+HhqJ&VvKUBvfU(=jY&#$$$<xI#MB;Mq`IABp20RT9wUXkaL(1R(pOn?c z>38<+e>KaN2{%0Jd@sMbY$+13Z&%Z<%!P+*SNu+#({Rr={_KxUhswHdID0QWTCBvD z)^ky;@gu`E#RAC#l`JcnelS0rxkKN7b1B>H56gX)0&P<*ag8WRNi0dVN-jzTQVd20 zh6cKZM!E)uAw~vPCdO7KCfWw3Rt5$ZGgakKH00)|WTsU@G#FTdHGouG8JIydoSGiG Q2B?9-)78&qol`;+03_q3ga7~l literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png new file mode 100644 index 0000000000000000000000000000000000000000..42ca929325854b8f34787425e8094d08c75983bc GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}PJmB{E0BJeoqfW=;gF{0PD8^7 zi&s6`bL8oxH}`iRdHU?*lb2r~J^6Th=ix^$zTSWS<?f>oH+CHUa^}pVf`aWvMt7fm zx&8F>jfd~AY(HF`mUiRG=W9<sUwQB@KR>@QLHo+%PZuA3I{)y~*$1D_JotF({zvHt zo%ulf7)yfuf*Bm1-ADs+I14-?i-EKU7`vU!wgWPXJzX3_Brcbpe=XFcAmSFNeC3q& zl!Z+(RsYN12&-NW{P*^#<rx82_2-+ii&SqfGgO`Jcj@;01@HTH{&&7#v?j}I)=J6m ze`~hppZn7?WA|%z!}T6pf>z0IjbZrS5ICD*275*_bAo)r8t#Vg4A*Lz);xc4JKq03 zXXC}zd)YwiRZCnWN>UO_QmvAUQh^kMk%6IsuAz~xfnkV|ft87|m5GVAfvJ^&fyGQ! qITQ`K`6-!cl@JXEmS7Da)m8>(5DllMhpqu?VDNPHb6Mw<&;$VOR=Fqu literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js new file mode 100644 index 0000000..3dc3201 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js @@ -0,0 +1,370 @@ +// Domain Module: Collection and Node. +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', + 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Collection Node + if (!pgBrowser.Nodes['coll-domain']) { + var domains = pgAdmin.Browser.Nodes['coll-domain'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain', + label: '{{ _('Domains') }}', + type: 'coll-domain', + columns: ['name', 'owner', 'description'] + }); + }; + + // Security Model + var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: null, + security_label: null + }, + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text', editable: true, cellHeaderClasses:'width_percent_50' + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text', editable: true, cellHeaderClasses:'width_percent_50' + }], + validate: function() { + var err = {}, + errmsg = null; + + if (_.isUndefined(this.get('security_label')) || + _.isNull(this.get('security_label')) || + String(this.get('security_label')).replace(/^\s+|\s+$/g, '') == '') { + errmsg = '{{ _('Please specify the value for all the security providers.')}}'; + this.errorModel.set('security_label', errmsg); + return errmsg; + } else { + this.errorModel.unset('security_label'); + } + return null; + } + }); + + // Constraint Model + var ConstraintModel = pgAdmin.Browser.Node.Model.extend({ + idAttribute: 'conoid', + initialize: function(attrs, args) { + var isNew = (_.size(attrs) === 0); + if (!isNew) { + this.convalidated_default = this.get('convalidated') + console.log(this); + console.log(args); + } + pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments); + }, + defaults: { + conoid: undefined, + conname: undefined, + consrc: undefined, + convalidated: true + }, + convalidated_default: true, + schema: [{ + id: 'conname', label: '{{ _('Name') }}', type: 'text', cell: 'string', + cellHeaderClasses: 'width_percent_40', + editable: function(m) { + if (_.isUndefined(m.isNew)) { return true; } + if (!m.isNew()) { + var server = this.get('node_info').server; + if (server.version < 90200) { return false; + } + } + return true; + } + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', + cell: Backgrid.Extension.TextareaCell, group: '{{ _('Definition') }}', + cellHeaderClasses: 'width_percent_60', editable: function(m) { + return _.isUndefined(m.isNew) ? true : m.isNew(); + } + },{ + id: 'convalidated', label: '{{ _('Validate?') }}', type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', + editable: function(m) { + var server = this.get('node_info').server; + if (server.version < 90200) { return false; + } + if (_.isUndefined(m.isNew)) { return true; } + if (!m.isNew()) { + if(m.get('convalidated') && m.convalidated_default) { + return false; + } + return true; + } + return true; + } + }], + toJSON: Backbone.Model.prototype.toJSON + }); + + // Domain Node + if (!pgBrowser.Nodes['domain']) { + pgAdmin.Browser.Nodes['domain'] = pgBrowser.Node.extend({ + type: 'domain', + label: '{{ _('Domain') }}', + collection_type: 'coll-domain', + hasSQL: true, + hasDepends: true, + parent_type: ['schema'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + canDropCascade: pgBrowser.Nodes['schema'].canChildDrop, + // Domain Node Model + model: pgAdmin.Browser.Node.Model.extend({ + initialize: function(attrs, args) { + var isNew = (_.size(attrs) === 0); + if (isNew) { + // Set Selected Schema + schema = args.node_info.schema.label + this.set({'basensp': schema}, {silent: true}); + + // Set Current User + var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user; + this.set({'owner': userInfo.name}, {silent: true}); + } + pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments); + }, + defaults: { + name: undefined, + oid: undefined, + owner: undefined, + basensp: undefined, + description: undefined, + basetype: undefined, + typlen: undefined, + precision: undefined, + typdefault: undefined, + typnotnull: undefined, + sysdomain: undefined, + collname: undefined, + constraints: [], + seclabels: [] + }, + type_options: undefined, + // Domain Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'] + },{ + id: 'oid', label:'{{ _('OID') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl, + node: 'role', type: 'text', mode: ['edit', 'create', 'properties'] + },{ + id: 'basensp', label:'{{ _('Schema') }}', cell: 'node-list-by-name', + control: 'node-list-by-name', cache_level: 'database', type: 'text', + node: 'schema', mode: ['create', 'edit'] + },{ + id: 'sysdomain', label:'{{ _('System domain?') }}', cell: 'boolean', + type: 'switch', mode: ['properties'], + options: { + 'onText': 'Yes', 'offText': 'No', + 'onColor': 'success', 'offColor': 'primary', + 'size': 'small' + } + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'basetype', label:'{{ _('Base type') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', mode:['properties', 'create', 'edit'], group: '{{ _('Definition') }}', url: 'get_types', + disabled: function(m) { return !m.isNew(); }, first_empty: true, + transform: function(d){ + this.model.type_options = d; + return d; + } + },{ + id: 'typlen', label:'{{ _('Length') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'], + disabled: function(m) { + // We will store type from selected from combobox + if (!m.isNew()) { + return true; + } + var of_type = m.get('basetype'); + if(m.type_options) { + // iterating over all the types + _.each(m.type_options, function(o) { + // if type from selected from combobox matches in options + if ( of_type == o.value ) { + // if length is allowed for selected type + if(o.length) + { + // set the values in model + m.set('is_tlength', true, {silent: true}); + m.set('min_val', o.min_val, {silent: true}); + m.set('max_val', o.max_val, {silent: true}); + } + } + }); + } + return !m.get('is_tlength'); + } + },{ + id: 'precision', label:'{{ _('Precision') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'], + disabled: function(m) { + // We will store type from selected from combobox + if (!m.isNew()) { + return true; + } + var of_type = m.get('basetype'); + if(m.type_options) { + // iterating over all the types + _.each(m.type_options, function(o) { + // if type from selected from combobox matches in options + if ( of_type == o.value ) { + // if precession is allowed for selected type + if(o.precision) + { + // set the values in model + m.set('is_precision', true, {silent: true}); + m.set('min_val', o.min_val, {silent: true}); + m.set('max_val', o.max_val, {silent: true}); + } + } + }); + } + return !m.get('is_precision'); + } + },{ + id: 'typdefault', label:'{{ _('Default') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', + placeholder: "Enter an expression or a value." + },{ + id: 'typnotnull', label:'{{ _('Not Null?') }}', cell: 'boolean', + type: 'switch', group: '{{ _('Definition') }}', + options: { + 'onText': 'Yes', 'offText': 'No', + 'onColor': 'success', 'offColor': 'primary', + 'size': 'small' + } + },{ + id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', group: '{{ _('Definition') }}', url: 'get_collations', disabled: function(m) { + return !m.isNew(); + } + },{ + id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string', + type: 'collection', group: '{{ _('Constraints') }}', mode: ['edit', 'create'], + model: ConstraintModel, canAdd: true, canDelete: true, + canEdit: false + },{ + id: 'seclabels', label: '{{ _('Security Labels') }}', + model: SecurityModel, type: 'collection', + group: '{{ _('Security') }}', mode: ['edit', 'create'], + min_version: 90100, canAdd: true, + canEdit: false, canDelete: true, + control: 'unique-col-collection', uniqueCol : ['provider'] + } + ], + validate: function() // Client Side Validation + { + var err = {}, + errmsg, + seclabels = this.get('seclabels'); + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('basetype')) || String(this.get('basetype')).replace(/^\s+|\s+$/g, '') == '') { + err['basetype'] = '{{ _('Base Type can not be empty!') }}'; + errmsg = errmsg || err['basetype']; + } + + if (seclabels) { + var secLabelsErr; + for (var i = 0; i < seclabels.models.length && !secLabelsErr; i++) { + secLabelsErr = (seclabels.models[i]).validate.apply(seclabels.models[i]); + if (secLabelsErr) { + err['seclabels'] = secLabelsErr; + errmsg = errmsg || secLabelsErr; + } + } + } + + this.errorModel.clear().set(err); + + return null; + } + }), + canCreate: function(itemData, item, data) { + //If check is false then , we will allow create menu + if (data && data.check == false) + return true; + + var t = pgBrowser.tree, i = item, d = itemData; + // To iterate over tree to check parent node + while (i) { + // If it is schema then allow user to create domain + if (_.indexOf(['schema'], d._type) > -1) + return true; + + if ('coll-domain' == d._type) { + //Check if we are not child of catalog + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + if( prev_d._type == 'catalog') { + return false; + } else { + return true; + } + } + i = t.hasParent(i) ? t.parent(i) : null; + d = i ? t.itemData(i) : null; + } + // by default we do not want to allow create menu + return true; + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return false; + } + } + return true; + } + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql new file mode 100644 index 0000000..f8b0b75 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql @@ -0,0 +1,30 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname %} + + COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL{% endif %}{% if data.constraints %}{% for c in data.constraints %}{% if c.conname and c.consrc %} + + CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% endif -%} +{% endfor -%} +{% endif -%}; + +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.description %} + + +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}';{% endif -%}{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} + + +{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%} +{% endfor -%} +{% endif -%} + +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..7a12b50 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql @@ -0,0 +1,16 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +AND + d.oid={{doid}}::int; +{% endif %} + +{% if name %} +DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql new file mode 100644 index 0000000..819fdbb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql new file mode 100644 index 0000000..897fb24 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql @@ -0,0 +1,15 @@ +SELECT + 'DOMAIN' AS objectkind, c.oid as conoid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as cons +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' + AND contypid = {{doid}}::oid +ORDER BY conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..8b5c891 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql @@ -0,0 +1,18 @@ +{% if doid %} +SELECT + d.typnamespace as scid +FROM + pg_type d +WHERE + d.oid={{ doid }}::oid; +{% else %} +SELECT + d.oid, d.typnamespace +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..42af39d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql @@ -0,0 +1,35 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, + pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT + array_agg(provider || '=' || label) + FROM + pg_seclabel sl1 + WHERE + sl1.objoid=d.oid) AS seclabels +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid + {% if doid %} + AND d.oid={{doid}}::int + {% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql new file mode 100644 index 0000000..3c205dc --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql @@ -0,0 +1,69 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER TYPE {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} +{% endif -%} +{% if data.typnotnull and not o_data.typnotnull %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif -%}{% if data.typdefault %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault }}; +{% elif data.typdefault == '' and o_data.typdefault %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif -%}{% if data.owner %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }}; +{% endif -%}{% if data.constraints %} +{% for c in data.constraints.deleted %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }}; +{% endfor -%} +{% for c in data.constraints.added %} +{% if c.conname and c.consrc %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} );{% endif -%} +{% endfor -%}{% endif -%} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} + +{% endfor -%} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} + +{% endfor -%} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} + +{% endfor -%} +{% endif -%}{% if data.description %} + +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }}; +{% endif -%}{% if data.basensp %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%} +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql new file mode 100644 index 0000000..f71e5a3 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql @@ -0,0 +1,36 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname and data.collname != "pg_catalog.\"default\"" %} + + COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL{% endif %}; + +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %} + +{% if data.constraints %} +{% for c in data.constraints %}{% if c.conname and c.consrc %} + +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if not c.convalidated %} NOT VALID{% endif %}{% endif -%}; +{% endfor -%} +{% endif %} + +{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}';{% endif -%} + +{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} + + +{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%} +{% endfor -%} +{% endif -%} + +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..7a12b50 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql @@ -0,0 +1,16 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +AND + d.oid={{doid}}::int; +{% endif %} + +{% if name %} +DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql new file mode 100644 index 0000000..e59c17d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql new file mode 100644 index 0000000..df956bf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql @@ -0,0 +1,15 @@ +SELECT + 'DOMAIN' AS objectkind, c.oid as conoid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc, connoinherit, convalidated +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +ORDER BY + conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..8b5c891 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql @@ -0,0 +1,18 @@ +{% if doid %} +SELECT + d.typnamespace as scid +FROM + pg_type d +WHERE + d.oid={{ doid }}::oid; +{% else %} +SELECT + d.oid, d.typnamespace +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..2892988 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql @@ -0,0 +1,34 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT + array_agg(provider || '=' || label) + FROM + pg_shseclabel sl1 + WHERE + sl1.objoid=d.oid) AS seclabels +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid +{% if doid %} + AND d.oid={{doid}}::int +{% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql new file mode 100644 index 0000000..a9ae19e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql @@ -0,0 +1,80 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} +{% endif -%} +{% if data.typnotnull and not o_data.typnotnull %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif -%}{% if data.typdefault %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault }}; +{% elif data.typdefault == '' and o_data.typdefault %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif -%}{% if data.owner %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }}; +{% endif -%}{% if data.constraints %} +{% for c in data.constraints.deleted %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(o_data['constraints'][c.conoid]['conname']) }}; +{% endfor -%} +{% for c in data.constraints.changed %} +{% if c.conname and c.conname !=o_data['constraints'][c.conoid]['conname'] %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data['constraints'][c.conoid]['conname']) }} TO {{ conn|qtIdent(c.conname) }}; +{% endif %} +{% if c.convalidated and not o_data['constraints'][c.conoid]['convalidated'] %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + VALIDATE CONSTRAINT {{ conn|qtIdent(c.conname) }}; +{% endif %} +{% endfor -%} +{% for c in data.constraints.added %} +{% if c.conname and c.consrc %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if not c.convalidated %} NOT VALID{% endif %}{% if c.connoinherit %} NO INHERIT{% endif -%};{% endif -%} +{% endfor -%}{% endif -%} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} + +{% endfor %} +{% endif -%} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif -%}{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif -%}{% if data.description %} + +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }}; +{% endif -%}{% if data.basensp %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%} +{% endif -%} ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-24 11:59 Dave Page <[email protected]> parent: Khushboo Vashi <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Dave Page @ 2016-03-24 11:59 UTC (permalink / raw) To: Khushboo Vashi <[email protected]>; +Cc: pgadmin-hackers Hi You're going to hate me for this.... - I added an un-validated constraint to a domain, then opened the domain properties and clicked the Validate? option for it. The SQL is generated, but the Save button is not enabled. - If I right-click a domain, I get Create options for "Domain" (with a constraint icon) and "Domain..." with a domain icon. See attached screenshot. Thanks. On Thu, Mar 24, 2016 at 9:54 AM, Khushboo Vashi <[email protected]> wrote: > Hi, > > Please find the attached updated patch for the Domain module. > > Thanks, > Khushboo > > On Wed, Mar 23, 2016 at 6:35 PM, Dave Page <[email protected]> wrote: >> >> Hi >> >> Almost there :-s >> >> - The hint for default should be a placeholder in the textbox itself >> (like combos have "Select from the list" > > Done >> >> - I should be able to check the "Validate?" option on one or more >> constraints from within the Domain dialogue > > Done. > Constraint Name can also be changed through the Domain dialogue. >> >> - Please ensure the capitalisation of all property labels is >> consistent - it should be "Base type" not "Base Type", "System >> domain?" not "System Domain?" etc. > > Done >> >> - The check constraint reverse engineered SQL should include the path >> to the constraint, e.g. >> > Done >> >> -- CHECK: schema.domain.check_at >> >> Once that's done, it can be committed I think. >> >> Thanks. >> >> On Wed, Mar 23, 2016 at 7:27 AM, Khushboo Vashi >> <[email protected]> wrote: >> > Updated one comment. >> > >> > On Wed, Mar 23, 2016 at 12:48 PM, Khushboo Vashi >> > <[email protected]> wrote: >> >> >> >> Hi, >> >> >> >> Please find attached updated patch for the Domains module. >> >> >> >> On Wed, Mar 16, 2016 at 9:40 PM, Dave Page <[email protected]> wrote: >> >>> >> >>> Hi >> >>> >> >>> On Wed, Mar 16, 2016 at 2:03 PM, Khushboo Vashi >> >>> <[email protected]> wrote: >> >>> > Hi, >> >>> > >> >>> > Please find the updated Domain Module Patch. >> >>> > >> >>> > To test this patch, please apply Backgrid Textarea Cell Patch before >> >>> > this. >> >>> >> >>> Thanks. I believe with the following fixes, we'll be done :-) >> >>> >> >>> - Default values should be auto-quoted when necessary (ie. strings, on >> >>> a text-based domain). >> >> >> >> As per our discussion, this should leave unquoted. >> > >> > And also added a hint to the field stated 'Enter an expression or a >> > value.' >> >> >> >> >> >>> - "System Domain?" should be in the General section, between owner and >> >>> comment. >> >> >> >> Done >> >>> >> >>> - The switches should use the same colouring/styling as other objects, >> >>> e.g. >> >>> >> >>> options: { >> >>> 'onText': 'Yes', 'offText': 'No', >> >>> 'onColor': 'success', 'offColor': 'primary', >> >>> 'size': 'small' >> >>> } >> >> >> >> Done >> >>> >> >>> - Please remove the Schema property from the main properties tab (not >> >>> the properties dialogue). >> >> >> >> Done >> >>> >> >>> - No icon is show for Checks on the Dependents tab for a domain. >> >> >> >> Done >> >>> >> >>> - The add button on the Security Labels tab is spelt "Add". Why is >> >>> that? Other instances of this grid use "ADD" which is the default in >> >>> backform.pgadmin.js. >> >> >> >> Done >> >>> >> >>> - Dependencies on domain check constraints are listed as being on a >> >>> "Type" not a "Domain". >> >> >> >> Done >> >>> >> >>> - If adding a domain constraint using the grid on the Domain dialogue, >> >>> I cannot specify "NOT VALID". We need a checkbox for that in a narrow >> >>> columns at the end. Unchecking it for an existing constraint should be >> >>> the equivalent of doing "ALTER DOMAIN ... VALIDATE CONSTRAINT" >> >> >> >> Done >> >>> >> >>> - If I switch the "Don't Validate" switch on a constraint, there are >> >>> leading blank lines in the generated SQL. The same occurs when adding >> >>> a comment to a constraint. >> >> >> >> Done >> >>> >> >>> - I think we need to reverse the meaning of "Don't Validate" and >> >>> rename to match the "Valid?" field that's on the properties list. >> >>> Otherwise it's not clear they're the same thing. >> >> >> >> Done >> >>> >> >>> - s/Not Null/Not Null?/ >> >> >> >> Done >> >>> >> >>> >> >>> >> >>> -- >> >>> Dave Page >> >>> Blog: http://pgsnake.blogspot.com >> >>> Twitter: @pgsnake >> >>> >> >>> EnterpriseDB UK: http://www.enterprisedb.com >> >>> The Enterprise PostgreSQL Company >> >> >> >> >> > >> >> >> >> -- >> Dave Page >> Blog: http://pgsnake.blogspot.com >> Twitter: @pgsnake >> >> EnterpriseDB UK: http://www.enterprisedb.com >> The Enterprise PostgreSQL Company > > -- Dave Page Blog: http://pgsnake.blogspot.com Twitter: @pgsnake EnterpriseDB UK: http://www.enterprisedb.com The Enterprise PostgreSQL Company -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers Attachments: [image/png] Screen Shot 2016-03-24 at 11.57.51.png (75.1K, 2-Screen%20Shot%202016-03-24%20at%2011.57.51.png) download | view image ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-03-24 15:25 Khushboo Vashi <[email protected]> parent: Dave Page <[email protected]> 0 siblings, 1 reply; 29+ messages in thread From: Khushboo Vashi @ 2016-03-24 15:25 UTC (permalink / raw) To: Dave Page <[email protected]>; +Cc: pgadmin-hackers Hi, Please find the attached updated patch for the Domains Module. Thanks, Khushboo On Thu, Mar 24, 2016 at 5:29 PM, Dave Page <[email protected]> wrote: > Hi > > You're going to hate me for this.... > > - I added an un-validated constraint to a domain, then opened the > domain properties and clicked the Validate? option for it. The SQL is > generated, but the Save button is not enabled. > Done. > > - If I right-click a domain, I get Create options for "Domain" (with a > constraint icon) and "Domain..." with a domain icon. > > As per the discussion with Ashesh, He has updated the context menu JS file with the new version and that is causing the issue. He is going to fix this issue as this is generic for the all context menu. I will create a new task for this in kanban and assign it to Ashesh. See attached screenshot. > > Thanks. > > On Thu, Mar 24, 2016 at 9:54 AM, Khushboo Vashi > <[email protected]> wrote: > > Hi, > > > > Please find the attached updated patch for the Domain module. > > > > Thanks, > > Khushboo > > > > On Wed, Mar 23, 2016 at 6:35 PM, Dave Page <[email protected]> wrote: > >> > >> Hi > >> > >> Almost there :-s > >> > >> - The hint for default should be a placeholder in the textbox itself > >> (like combos have "Select from the list" > > > > Done > >> > >> - I should be able to check the "Validate?" option on one or more > >> constraints from within the Domain dialogue > > > > Done. > > Constraint Name can also be changed through the Domain dialogue. > >> > >> - Please ensure the capitalisation of all property labels is > >> consistent - it should be "Base type" not "Base Type", "System > >> domain?" not "System Domain?" etc. > > > > Done > >> > >> - The check constraint reverse engineered SQL should include the path > >> to the constraint, e.g. > >> > > Done > >> > >> -- CHECK: schema.domain.check_at > >> > >> Once that's done, it can be committed I think. > >> > >> Thanks. > >> > >> On Wed, Mar 23, 2016 at 7:27 AM, Khushboo Vashi > >> <[email protected]> wrote: > >> > Updated one comment. > >> > > >> > On Wed, Mar 23, 2016 at 12:48 PM, Khushboo Vashi > >> > <[email protected]> wrote: > >> >> > >> >> Hi, > >> >> > >> >> Please find attached updated patch for the Domains module. > >> >> > >> >> On Wed, Mar 16, 2016 at 9:40 PM, Dave Page <[email protected]> > wrote: > >> >>> > >> >>> Hi > >> >>> > >> >>> On Wed, Mar 16, 2016 at 2:03 PM, Khushboo Vashi > >> >>> <[email protected]> wrote: > >> >>> > Hi, > >> >>> > > >> >>> > Please find the updated Domain Module Patch. > >> >>> > > >> >>> > To test this patch, please apply Backgrid Textarea Cell Patch > before > >> >>> > this. > >> >>> > >> >>> Thanks. I believe with the following fixes, we'll be done :-) > >> >>> > >> >>> - Default values should be auto-quoted when necessary (ie. strings, > on > >> >>> a text-based domain). > >> >> > >> >> As per our discussion, this should leave unquoted. > >> > > >> > And also added a hint to the field stated 'Enter an expression or a > >> > value.' > >> >> > >> >> > >> >>> - "System Domain?" should be in the General section, between owner > and > >> >>> comment. > >> >> > >> >> Done > >> >>> > >> >>> - The switches should use the same colouring/styling as other > objects, > >> >>> e.g. > >> >>> > >> >>> options: { > >> >>> 'onText': 'Yes', 'offText': 'No', > >> >>> 'onColor': 'success', 'offColor': 'primary', > >> >>> 'size': 'small' > >> >>> } > >> >> > >> >> Done > >> >>> > >> >>> - Please remove the Schema property from the main properties tab > (not > >> >>> the properties dialogue). > >> >> > >> >> Done > >> >>> > >> >>> - No icon is show for Checks on the Dependents tab for a domain. > >> >> > >> >> Done > >> >>> > >> >>> - The add button on the Security Labels tab is spelt "Add". Why is > >> >>> that? Other instances of this grid use "ADD" which is the default in > >> >>> backform.pgadmin.js. > >> >> > >> >> Done > >> >>> > >> >>> - Dependencies on domain check constraints are listed as being on a > >> >>> "Type" not a "Domain". > >> >> > >> >> Done > >> >>> > >> >>> - If adding a domain constraint using the grid on the Domain > dialogue, > >> >>> I cannot specify "NOT VALID". We need a checkbox for that in a > narrow > >> >>> columns at the end. Unchecking it for an existing constraint should > be > >> >>> the equivalent of doing "ALTER DOMAIN ... VALIDATE CONSTRAINT" > >> >> > >> >> Done > >> >>> > >> >>> - If I switch the "Don't Validate" switch on a constraint, there are > >> >>> leading blank lines in the generated SQL. The same occurs when > adding > >> >>> a comment to a constraint. > >> >> > >> >> Done > >> >>> > >> >>> - I think we need to reverse the meaning of "Don't Validate" and > >> >>> rename to match the "Valid?" field that's on the properties list. > >> >>> Otherwise it's not clear they're the same thing. > >> >> > >> >> Done > >> >>> > >> >>> - s/Not Null/Not Null?/ > >> >> > >> >> Done > >> >>> > >> >>> > >> >>> > >> >>> -- > >> >>> Dave Page > >> >>> Blog: http://pgsnake.blogspot.com > >> >>> Twitter: @pgsnake > >> >>> > >> >>> EnterpriseDB UK: http://www.enterprisedb.com > >> >>> The Enterprise PostgreSQL Company > >> >> > >> >> > >> > > >> > >> > >> > >> -- > >> Dave Page > >> Blog: http://pgsnake.blogspot.com > >> Twitter: @pgsnake > >> > >> EnterpriseDB UK: http://www.enterprisedb.com > >> The Enterprise PostgreSQL Company > > > > > > > > -- > Dave Page > Blog: http://pgsnake.blogspot.com > Twitter: @pgsnake > > EnterpriseDB UK: http://www.enterprisedb.com > The Enterprise PostgreSQL Company > -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers Attachments: [text/x-patch] Domains_ver_8.patch (104.3K, 3-Domains_ver_8.patch) download | inline diff: diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py new file mode 100644 index 0000000..c21b24a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py @@ -0,0 +1,824 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements the Domain Node.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error, gone +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas as schemas +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from pgadmin.browser.server_groups.servers.databases.schemas.utils import \ + SchemaChildModule, DataTypeReader +from pgadmin.browser.server_groups.servers.databases.utils import \ + parse_sec_labels_from_db +from functools import wraps + + +class DomainModule(SchemaChildModule): + """ + class DomainModule(SchemaChildModule): + + This class represents The Domain Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Module. + + * get_nodes(gid, sid, did, scid) + - Generate the domain collection node. + + * script_load() + - Load the module script for domain, when schema node is + initialized. + """ + + NODE_TYPE = 'domain' + COLLECTION_LABEL = gettext("Domains") + + def __init__(self, *args, **kwargs): + super(DomainModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid): + """ + Generate the domain collection node. + """ + yield self.generate_browser_collection_node(scid) + + @property + def script_load(self): + """ + Load the module script for domain, when schema node is + initialized. + """ + return schemas.SchemaModule.NODE_TYPE + + +blueprint = DomainModule(__name__) + + +class DomainView(PGChildNodeView, DataTypeReader): + """ + class DomainView + + This class inherits PGChildNodeView to get the different routes for + the module. Also, inherits DataTypeReader to get data types. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain. + + Methods: + ------- + * validate_request(f): + - Works as a decorator. + Validating request on the request of create, update and modified SQL. + + * module_js(): + - Load JS file (domains.js) for this module. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domains. + + * nodes(gid, sid, did, scid): + - Returns all the Domains to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain properties. + + * get_collations(gid, sid, did, scid, doid=None): + - Returns Collations. + + * create(gid, sid, did, scid): + - Creates a new Domain object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain object. + + * dependents(gid, sid, did, scid, doid): + - Returns the dependents for the Domain object. + + * dependencies(gid, sid, did, scid, doid): + - Returns the dependencies for the Domain object. + + * types(gid, sid, did, scid, fnid=None): + - Returns Data Types. + """ + + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'doid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_types': [{'get': 'types'}, {'get': 'types'}], + 'get_collations': [ + {'get': 'get_collations'}, + {'get': 'get_collations'} + ] + }) + + def validate_request(f): + """ + Works as a decorator. + Validating request on the request of create, update and modified SQL. + + Required Args: + name: Name of the Domain + owner: Domain Owner + basensp: Schema Name + basetype: Data Type of the Domain + + Above both the arguments will not be validated in the update action. + """ + + @wraps(f) + def wrap(self, **kwargs): + + data = {} + if request.data: + req = json.loads(request.data.decode()) + else: + req = request.args or request.form + + if 'doid' not in kwargs: + required_args = [ + 'name', + 'basetype' + ] + + for arg in required_args: + if arg not in req or req[arg] == '': + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter \ + (%s)." % arg + ) + ) + + try: + list_params = [] + if request.method == 'GET': + list_params = ['constraints', 'seclabels'] + + for key in req: + if key in list_params and req[key] != '' \ + and req[key] is not None: + # Coverts string into python list as expected. + data[key] = json.loads(req[key]) + elif key == 'typnotnull': + data[key] = True if req[key] == 'true' or req[key] is\ + True else\ + (False if req[key] == 'false' or req[key] is + False else '') + else: + data[key] = req[key] + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + self.request = data + return f(self, **kwargs) + + return wrap + + def module_js(self): + """ + Load JS file (domains.js) for this module. + """ + return make_response( + render_template( + "domains/js/domains.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + # Get database connection + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domains/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domains/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid): + """ + List the Domains. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid): + """ + Returns all the Domains to generate Nodes in the browser. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + res = [] + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + scid, + row['name'], + icon="icon-domain" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid): + """ + Returns the Domain properties. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + if len(res['rows']) == 0: + return gone(gettext(""" +Could not find the domain in the database. +It may have been removed by another user or +shifted to the another schema. +""")) + + data = res['rows'][0] + + # Get Type Length and Precision + data.update(self._parse_type(data['fulltype'])) + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + # Get formatted Security Labels + if 'seclabels' in data: + data.update(parse_sec_labels_from_db(data['seclabels'])) + + # Set System Domain Status + data['sysdomain'] = False + if doid <= self.manager.db_info[did]['datlastsysoid']: + data['sysdomain'] = True + + return ajax_response( + response=data, + status=200 + ) + + def _parse_type(self, basetype): + """ + Returns Type and Data Type from the basetype. + """ + typ_len = '' + typ_precision = '' + + # The Length and the precision of the Datatype should be separate. + # The Format we getting from database is: numeric(1,1) + # So, we need to separate Length: 1, Precision: 1 + + if basetype != '' and basetype.find("(") > 0: + substr = basetype[basetype.find("(") + 1:len( + basetype) - 1] + typlen = substr.split(",") + if len(typlen) > 1: + typ_len = typlen[0] + typ_precision = typlen[1] + else: + typ_len = typlen + typ_precision = '' + + return {'typlen': typ_len, 'precision': typ_precision} + + @check_precondition + def get_collations(self, gid, sid, did, scid, doid=None): + """ + Returns Collations. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + res = [{'label': '', 'value': ''}] + try: + SQL = render_template("/".join([self.template_path, + 'get_collations.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append({'label': row['copy_collation'], + 'value': row['copy_collation']} + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def types(self, gid, sid, did, scid, doid=None): + """ + Returns the Data Types. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + fnid: Function Id + """ + + condition = """typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r') +AND NOT EXISTS (SELECT 1 FROM pg_class WHERE relnamespace=typnamespace +AND relname = typname AND relkind != 'c') AND +(typname NOT LIKE '_%' OR NOT EXISTS (SELECT 1 FROM pg_class WHERE +relnamespace=typnamespace AND relname = substring(typname FROM 2)::name +AND relkind != 'c'))""" + + if self.blueprint.show_system_objects: + condition += " AND nsp.nspname != 'information_schema'" + + # Get Types + status, types = self.get_types(self.conn, condition) + + if not status: + return internal_server_error(errormsg=types) + + return make_json_response( + data=types, + status=200 + ) + + @check_precondition + @validate_request + def create(self, gid, sid, did, scid): + """ + Creates a new Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + Domain object in json format. + """ + + data = self.request + try: + status, SQL = self.get_sql(gid, sid, data, scid) + + if not status: + return internal_server_error(errormsg=SQL) + + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # We need oid to to add object in tree at browser, below sql will + # gives the same + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + basensp=data['basensp'], + name=data['name']) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + doid, scid = res['rows'][0] + + return jsonify( + node=self.blueprint.generate_browser_node( + doid, + scid, + data['name'], + icon="icon-domain" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid): + """ + Drops the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + name, basensp = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + name=name, basensp=basensp, cascade=cascade) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + @validate_request + def update(self, gid, sid, did, scid, doid): + """ + Updates the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if not status: + return internal_server_error(errormsg=SQL) + + try: + if SQL: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get Schema Id + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + scid = res['rows'][0]['scid'] + + return make_json_response( + success=1, + info="Domain updated", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid=None): + """ + Returns the SQL for the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return False, internal_server_error(errormsg=res) + data = res['rows'][0] + + # Get Type Length and Precision + data.update(self._parse_type(data['fulltype'])) + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), data=data) + + sql_header = """-- DOMAIN: {0} + +-- DROP DOMAIN {0}; + +""".format(data['basensp'] + '.' + data['name']) + + SQL = sql_header + SQL + + return ajax_response(response=SQL.strip('\n')) + + @check_precondition + @validate_request + def msql(self, gid, sid, did, scid, doid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + SQL statements to create/update the Domain. + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid=None): + """ + Generates the SQL statements to create/update the Domain. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + try: + if doid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + con_data = {} + for c in res['rows']: + con_data[c['conoid']] = c + + old_data['constraints'] = con_data + + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data) + else: + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data) + return True, SQL.strip('\n') + + except Exception as e: + return False, e + + @check_precondition + def dependents(self, gid, sid, did, scid, doid): + """ + This function get the dependents and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependents_result = self.get_dependents(self.conn, doid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid): + """ + This function get the dependencies and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependencies_result = self.get_dependencies(self.conn, doid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py new file mode 100644 index 0000000..8fcace1 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py @@ -0,0 +1,691 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements the Domain Constraint Module.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas.domains \ + as domains +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + + +class DomainConstraintModule(CollectionNodeModule): + """ + class DomainConstraintModule(CollectionNodeModule): + + This class represents The Domain Constraint Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Constraint Module. + + * get_nodes(gid, sid, did, scid) + - Generate the Domain Constraint collection node. + + * node_inode(gid, sid, did, scid) + - Returns Domain Constraint node as leaf node. + + * script_load() + - Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + NODE_TYPE = 'domain-constraints' + COLLECTION_LABEL = gettext("Domain Constraints") + + def __init__(self, *args, **kwargs): + super(DomainConstraintModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid, doid): + """ + Generate the Domain Constraint collection node. + """ + yield self.generate_browser_collection_node(doid) + + @property + def node_inode(self): + """ + Returns Domain Constraint node as leaf node. + """ + return False + + @property + def script_load(self): + """ + Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + return domains.DomainModule.NODE_TYPE + + @property + def csssnippets(self): + """ + Returns a snippet of css to include in the page + """ + return [ + render_template( + "domain-constraints/css/domain-constraints.css", + node_type=self.node_type + ) + ] + + + +blueprint = DomainConstraintModule(__name__) + + +class DomainConstraintView(PGChildNodeView): + """ + class DomainConstraintView(PGChildNodeView): + + This class inherits PGChildNodeView to get the different routes for + the module. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain Constraint. + + Methods: + ------- + + * module_js(): + - Load JS file (domain-constraints.js) for this module. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domain Constraints. + + * nodes(gid, sid, did, scid): + - Returns all the Domain Constraints to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain Constraint properties. + + * create(gid, sid, did, scid): + - Creates a new Domain Constraint object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain Constraint object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain Constraint object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain Constraint object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain Constraint. + object. + + * dependents(gid, sid, did, scid, doid, coid): + - Returns the dependents for the Domain Constraint object. + + * dependencies(gid, sid, did, scid, doid, coid): + - Returns the dependencies for the Domain Constraint object. + """ + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'}, + {'type': 'int', 'id': 'doid'} + ] + ids = [ + {'type': 'int', 'id': 'coid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def validate_request(f): + """ + Works as a decorator. + Validating request on the request of create, update and modified SQL. + + Required Args: + name: Name of the Domain Constraint + consrc: Check Constraint Definition + + Above both the arguments will not be validated in the update action. + """ + + @wraps(f) + def wrap(self, **kwargs): + + data = {} + if request.data: + req = json.loads(request.data.decode()) + else: + req = request.args or request.form + + if 'coid' not in kwargs: + required_args = [ + 'name', + 'consrc' + ] + + for arg in required_args: + if arg not in req or req[arg] == '': + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter \ + (%s)." % arg + ) + ) + + try: + for key in req: + if key == 'convalidated': + data[key] = True if (req[key] == 'true' or req[key] is + True) else False + else: + data[key] = req[key] + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + self.request = data + return f(self, **kwargs) + + return wrap + + def module_js(self): + """ + Load JS file (domain-constraints.js) for this module. + """ + return make_response( + render_template( + "domain-constraints/js/domain-constraints.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domain-constraints/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domain-constraints/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid, doid): + """ + List the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid, doid): + """ + Returns all the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + res = [] + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, rset = self.conn.execute_2darray(SQL) + + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + if 'convalidated' not in row: + icon = 'icon-domain-constraints' + elif row['convalidated']: + icon = 'icon-domain-constraints' + else: + icon = 'icon-domain-constraints-bad' + res.append( + self.blueprint.generate_browser_node( + row['oid'], + doid, + row['name'], + icon=icon + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid, coid): + """ + Returns the Domain Constraints property. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + return ajax_response( + response=data, + status=200 + ) + + @check_precondition + @validate_request + def create(self, gid, sid, did, scid, doid): + """ + Creates a new Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Returns: + Domain Constraint object in json format. + """ + data = self.request + try: + status, SQL = self.get_sql(gid, sid, data, scid, doid) + if not status: + return internal_server_error(errormsg=SQL) + + status, res = self.conn.execute_scalar(SQL) + + if not status: + return internal_server_error(errormsg=res) + + # Get the recently added constraints oid + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + doid=doid, name=data['name']) + status, coid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=coid) + + if 'convalidated' not in data: + icon = 'icon-domain-constraints' + elif 'convalidated' in data and data['convalidated']: + icon = 'icon-domain-constraints' + else: + icon = 'icon-domain-constraints-bad' + + return jsonify( + node=self.blueprint.generate_browser_node( + coid, + doid, + data['name'], + icon=icon + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid, coid): + """ + Drops the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain Constraint dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + @validate_request + def update(self, gid, sid, did, scid, doid, coid): + """ + Updates the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + data = self.request + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + + try: + if SQL and status: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + if 'convalidated' in data and data['convalidated']: + icon = 'icon-domain-constraints' + elif 'convalidated' in data and not data['convalidated']: + icon = 'icon-domain-constraints-bad' + else: + icon = '' + + return make_json_response( + success=1, + info="Domain Constraint updated", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did, + 'icon': icon + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the SQL for the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + # Get Schema and Domain. + domain, schema = self._get_domain(doid) + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + + sql_header = """-- CHECK: {1}.{0} + +-- ALTER DOMAIN {1} DROP CONSTRAINT {0}; + +""".format(data['name'], schema + '.' + domain) + + SQL = sql_header + SQL + + return ajax_response(response=SQL) + + @check_precondition + @validate_request + def msql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + + Returns: + Domain Constraint object in json format. + """ + data = self.request + + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + if status and SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid, coid=None): + """ + Generates the SQL statements to create/update the Domain Constraint. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + if coid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data, conn=self.conn + ) + else: + domain, schema = self._get_domain(doid) + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + return True, SQL.strip('\n') + except Exception as e: + return False, internal_server_error(errormsg=str(e)) + + def _get_domain(self, doid): + """ + Returns Domain and Schema name. + + Args: + doid: Domain Id + + """ + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + return res['rows'][0] + + @check_precondition + def dependents(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependents and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependents_result = self.get_dependents(self.conn, coid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependencies and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependencies_result = self.get_dependencies(self.conn, coid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainConstraintView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..d62e13705c50e6c0cf8f19d680053e8643e28751 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=2TrO847{Jl`|t`Qpas zn<hS+l=rMY>RGPqvlydizRJ&>B`Om#V}R-yOM?7@862M7NCR>>3p^r=fwTu0yPeFo z12TL)T^vI=t|uoPU||ZF<tgaHG*QsQ!?m&Tq=?3mCu}J#Dx3x@mM}}^iE=5NIWXnk zkpnC4ai%a>@;Gg7=uums=9bIK=Egd~(us-3g@Iv02gfsK^JP^)gH=mhBT7;dOH!?p zi&B9UgOP!ufv%yEu7P2Qk%5(ov6YF5wt=aYfq}(LRXG$5x%nxXX_XKS29{tAAk|g| XW)KahriZQpYGCkm^>bP0l+XkKyyRU} literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png new file mode 100644 index 0000000000000000000000000000000000000000..32a045b8fafdc08640d53b2a86b1dcabcb0fe0fd GIT binary patch literal 579 zcmV-J0=)f+P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp) z=>Px$AW%$HMR1Z9#Q*@REHmdHAm<<;=p`lTDl6$LE15$|nM6yxJ3#6&F}^)Qx<p3n zH#h4zIK)Ou#79fTM@#HJKkY(8?L$QEL`CjJMeasM?ng(;QB>|oNbX2U?n+AUOH1!e zOz%xi?@dncPEXKRS?^Cz#amtQQBm+xQt(q#@KaRqR8;U)Rq<9<@mE*YVq(`~V)0vB z+GS<)VPW%PV%=$Jag!JHXlV6qZS`($_HuH3pd<EncJ_C7f1@XWqbGu>Du=8uhpjJ) zvN4agH<i3UrMYgWyK<eyN1Vq+p2tbA!G5C3PO!m$q|HvV#D$~JOsv;ftk_qv-CeQX zT*cC%$J3;><6_L$t<Kr8)!w|r@o>cPam4X*+~mi`^K{Glc|xh<NdN!<0d!JMQvg8b z*k%9#00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipf34<ROg z(p72z0056kL_t&-)1{2Z3c^qT1mDEody6IZuCW&s1V!`^4}$goe?(18@b2Db*j*w1 z%4M(p;&Zw?9ZaKAxo*x;COX}<8fzjqKRv^2kF1spymYHMjE2m7Hl|a~emCNwG8(i? z8I#``(kiBrEbh}(Qn8TL2+$}b3Hn^7p`K6R#_6zQ2$?ux;lXBYB>hkN@C%g?4vVBM R$bbL<002ovPDHLkV1min0OSAw literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..9d1d2a061c7948168d7b1c2474d769b31709f1cf GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE08{VY2nhHc^eMaU%j`d zaI$#+HuEKC{pKEZKYn>h(+aIMH^MjGjcs3}f9Cqyt&fvx7AT*)xv^`L;hf{Hi_iP4 zxgK%&V?q65_4c*;8}G%;JMMY<SLM___M4Bi9{E^!<YUpX&n1ga`7PgFwEkdS!(#P< zNn&@N9OqiK(i&(nV@Z%-FoVOh8)-leXMsm#F_88EW4Dvpc0fjir;B5V#O36K1sn!O zhRVf}5jSsGPH?f<xudc|vBoY<&5cb=uTGB9v187J4II+it5?jhn8F{TsCZIWRad$D zg1*K9W%cu2NsD(hEfVT#*wm#pYw;D04+0E(uQE?s`Z6*ZXoqTvYeY#(Vo9o1a#1Rf zVlXl=G|)9P(lsy)F*2|+F}5->(Kax(GBB{1sVaw}AvZrIGp!P$!N3x%0i@c>zzm|{ T)b!9bKn)C@u6{1-oD!M<=4O^k literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css new file mode 100644 index 0000000..4691c60 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/css/domain-constraints.css @@ -0,0 +1,23 @@ +.icon-coll-domain-constraints { + background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/coll-domain-constraints.png' )}}') !important; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} + +.icon-check-bad, .icon-domain-constraints-bad { + background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/domain-constraints-bad.png' )}}') !important; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} + +.icon-check, .icon-domain-constraints { + background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/domain-constraints.png' )}}') !important; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js new file mode 100644 index 0000000..104c107 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js @@ -0,0 +1,142 @@ +// Domain Constraint Module: Collection and Node +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Constraint Collection Node + if (!pgBrowser.Nodes['coll-domain-constraints']) { + var domain_constraints = pgAdmin.Browser.Nodes['coll-domain-constraints'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + type: 'coll-domain-constraints', + columns: ['name', 'description'] + }); + }; + + // Domain Constraint Node + if (!pgBrowser.Nodes['domain-constraints']) { + pgAdmin.Browser.Nodes['domain-constraints'] = pgBrowser.Node.extend({ + type: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + collection_type: 'coll-domain-constraints', + hasSQL: true, + hasDepends: true, + parent_type: ['domain'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 5, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 5, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 5, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + oid: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: true + }, + // Domain Constraint Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', type:'text', cell:'string', + disabled: 'isDisabled' + },{ + id: 'oid', label:'{{ _('OID') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'description', label: '{{ _('Comment') }}', type: 'multiline', cell: + 'string', mode: ['properties', 'create', 'edit'], min_version: 90500, + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', cel: + 'string', group: '{{ _('Definition') }}', mode: ['properties', + 'create', 'edit'], disabled: function(m) { return !m.isNew(); } + },{ + id: 'connoinherit', label: '{{ _('No Inherit') }}', type: + 'switch', cell: 'boolean', group: '{{ _('Definition') }}', mode: + ['properties', 'create', 'edit'], disabled: 'isDisabled', + visible: false + },{ + id: 'convalidated', label: "{{ _("Validate?") }}", type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', min_version: 90200, + disabled: function(m) { + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) { return true; + } + else if(m.get('convalidated')) { + return true; + } + return false; + } + return false; + }, + mode: ['properties', 'create', 'edit'] + }], + // Client Side Validation + validate: function() { + var err = {}, + errmsg; + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') { + err['consrc'] = '{{ _('Check can not be empty!') }}'; + errmsg = errmsg || err['consrc']; + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return true; + } + } + return false; + } + }), + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql new file mode 100644 index 0000000..be943d2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql @@ -0,0 +1,3 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }});{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..043f011 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql @@ -0,0 +1,14 @@ +SELECT + c.oid, conname AS name, typname AS relname, nspname, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql new file mode 100644 index 0000000..299ba6b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql @@ -0,0 +1,3 @@ +{% if data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql new file mode 100644 index 0000000..ad993f7 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql @@ -0,0 +1,10 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }}){% if not data.convalidated %} + + NOT VALID{% endif %};{% if data.description %} + + +COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON DOMAIN {{ conn|qtIdent(schema, domain) }} + IS '{{ data.description }}';{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_type_category.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_type_category.sql new file mode 100644 index 0000000..3e3b244 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_type_category.sql @@ -0,0 +1,5 @@ +SELECT + typcategory +FROM + pg_type +WHERE typname = {{datatype}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..34d8b34 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql @@ -0,0 +1,17 @@ +SELECT + c.oid, conname AS name, typname AS relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc, + connoinherit, convalidated, convalidated AS convalidated_p +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql new file mode 100644 index 0000000..628eb6e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql @@ -0,0 +1,13 @@ +{% set name = o_data.name %} +{% if data.name %} +{% set name = data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif -%}{% if data.convalidated %} + + +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + VALIDATE CONSTRAINT {{ conn|qtIdent(name) }};{% endif -%}{% if data.description %} + + +COMMENT ON CONSTRAINT {{ conn|qtIdent(name) }} ON DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + IS '{{ data.description }}';{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png new file mode 100644 index 0000000000000000000000000000000000000000..55621528a1dba4928538fe5557b9b988ed78d6ab GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}U4T!BE0BJeoqfW=;gF{0PD8_o zi&pN_(K)B7`FPi%2ix`@v9LU(tNUQ*!K1czXOxuggoGT_(#p@zf4Jw!!zHV)`3F7T zd-Um}H}`iRIijcc@Wq#>&ptkR`SszG54Uz6zW4mg?MLsgZ9jbb>E~y!zTSB9`Re1( zjS1S99(_9h@YC4`A5Y)^_^>@J3+MvIk|4ie28U-i(tsS!0*}aIAngIhZYQ(tfQ)ue z7sn8Z%b|U@#hMgESUvs4wKoSxO>~{O=-Yq$8_!r)-^e%4-l6f-Jox1%8}FIVx>FCW zPMVwluQGbkP1bcSwVV0d_?PZbZVis%HeYTWyU*$OvYp$uTc3QLbD?2trOnDuhZa>f z=3d_{(zER9xt(<n3UsBI4SNC?Y!rBW<m2`n>yDRyg$uoE8Qx}bo1D0qw;t#u)e_f; zl9a@fRIB8oR3OD*WMF8ZYiOivU>IU#U}a)#Wn!XjU}|MxU@=ow4n;$5eoAIqB}9XP eC0GMUwUvPxM8m1+p=*E|7(8A5T-G@yGywoRCC2Lj literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png new file mode 100644 index 0000000000000000000000000000000000000000..7521cddeaaaf0ee4e3c60e948078d70e17e06893 GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}T7XZ8E0Deu9eu*V;gF{0K`pIg zc6K`r4IeIAc}Q3HxV`=3y+@xudUJpGkt2F~PoI5!^78A$Cm(L@JbcYR=;4bm_nv>b z{pkI*?T7D#gyiSvKYR7{_S4Tdo_xOg_;X`|_N7Ok&OiKg_QA)~_dlLo85{w$iLoTe zFPOpM*^M+HhqJ&VvKUBvfU(=jY&#$$$<xI#MB;Mq`IABp20RT9wUXkaL(1R(pOn?c z>38<+e>KaN2{%0Jd@sMbY$+13Z&%Z<%!P+*SNu+#({Rr={_KxUhswHdID0QWTCBvD z)^ky;@gu`E#RAC#l`JcnelS0rxkKN7b1B>H56gX)0&P<*ag8WRNi0dVN-jzTQVd20 zh6cKZM!E)uAw~vPCdO7KCfWw3Rt5$ZGgakKH00)|WTsU@G#FTdHGouG8JIydoSGiG Q2B?9-)78&qol`;+03_q3ga7~l literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png new file mode 100644 index 0000000000000000000000000000000000000000..42ca929325854b8f34787425e8094d08c75983bc GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}PJmB{E0BJeoqfW=;gF{0PD8^7 zi&s6`bL8oxH}`iRdHU?*lb2r~J^6Th=ix^$zTSWS<?f>oH+CHUa^}pVf`aWvMt7fm zx&8F>jfd~AY(HF`mUiRG=W9<sUwQB@KR>@QLHo+%PZuA3I{)y~*$1D_JotF({zvHt zo%ulf7)yfuf*Bm1-ADs+I14-?i-EKU7`vU!wgWPXJzX3_Brcbpe=XFcAmSFNeC3q& zl!Z+(RsYN12&-NW{P*^#<rx82_2-+ii&SqfGgO`Jcj@;01@HTH{&&7#v?j}I)=J6m ze`~hppZn7?WA|%z!}T6pf>z0IjbZrS5ICD*275*_bAo)r8t#Vg4A*Lz);xc4JKq03 zXXC}zd)YwiRZCnWN>UO_QmvAUQh^kMk%6IsuAz~xfnkV|ft87|m5GVAfvJ^&fyGQ! qITQ`K`6-!cl@JXEmS7Da)m8>(5DllMhpqu?VDNPHb6Mw<&;$VOR=Fqu literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js new file mode 100644 index 0000000..20acde5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js @@ -0,0 +1,362 @@ +// Domain Module: Collection and Node. +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', + 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Collection Node + if (!pgBrowser.Nodes['coll-domain']) { + var domains = pgAdmin.Browser.Nodes['coll-domain'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain', + label: '{{ _('Domains') }}', + type: 'coll-domain', + columns: ['name', 'owner', 'description'] + }); + }; + + // Security Model + var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: null, + security_label: null + }, + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text', editable: true, cellHeaderClasses:'width_percent_50' + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text', editable: true, cellHeaderClasses:'width_percent_50' + }], + validate: function() { + var err = {}, + errmsg = null; + + if (_.isUndefined(this.get('security_label')) || + _.isNull(this.get('security_label')) || + String(this.get('security_label')).replace(/^\s+|\s+$/g, '') == '') { + errmsg = '{{ _('Please specify the value for all the security providers.')}}'; + this.errorModel.set('security_label', errmsg); + return errmsg; + } else { + this.errorModel.unset('security_label'); + } + return null; + } + }); + + // Constraint Model + var ConstraintModel = pgAdmin.Browser.Node.Model.extend({ + idAttribute: 'conoid', + initialize: function(attrs, args) { + var isNew = (_.size(attrs) === 0); + if (!isNew) { + this.convalidated_default = this.get('convalidated') + } + pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments); + }, + defaults: { + conoid: undefined, + conname: undefined, + consrc: undefined, + convalidated: true + }, + convalidated_default: true, + schema: [{ + id: 'conoid', type: 'text', cell: 'string', visible: false + },{ + id: 'conname', label: '{{ _('Name') }}', type: 'text', cell: 'string', + cellHeaderClasses: 'width_percent_40', + editable: function(m) { + if (_.isUndefined(m.isNew)) { return true; } + if (!m.isNew()) { + var server = this.get('node_info').server; + if (server.version < 90200) { return false; + } + } + return true; + } + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', + cell: Backgrid.Extension.TextareaCell, group: '{{ _('Definition') }}', + cellHeaderClasses: 'width_percent_60', editable: function(m) { + return _.isUndefined(m.isNew) ? true : m.isNew(); + } + },{ + id: 'convalidated', label: '{{ _('Validate?') }}', type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', + editable: function(m) { + var server = this.get('node_info').server; + if (server.version < 90200) { return false; + } + if (_.isUndefined(m.isNew)) { return true; } + if (!m.isNew()) { + if(m.get('convalidated') && m.convalidated_default) { + return false; + } + return true; + } + return true; + } + }], + toJSON: Backbone.Model.prototype.toJSON, + validate: function() { + return null; + } + }); + + // Domain Node + if (!pgBrowser.Nodes['domain']) { + pgAdmin.Browser.Nodes['domain'] = pgBrowser.Node.extend({ + type: 'domain', + label: '{{ _('Domain') }}', + collection_type: 'coll-domain', + hasSQL: true, + hasDepends: true, + parent_type: ['schema'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + canDropCascade: pgBrowser.Nodes['schema'].canChildDrop, + // Domain Node Model + model: pgAdmin.Browser.Node.Model.extend({ + initialize: function(attrs, args) { + var isNew = (_.size(attrs) === 0); + if (isNew) { + // Set Selected Schema + schema = args.node_info.schema.label + this.set({'basensp': schema}, {silent: true}); + + // Set Current User + var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user; + this.set({'owner': userInfo.name}, {silent: true}); + } + pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments); + }, + defaults: { + name: undefined, + oid: undefined, + owner: undefined, + basensp: undefined, + description: undefined, + basetype: undefined, + typlen: undefined, + precision: undefined, + typdefault: undefined, + typnotnull: undefined, + sysdomain: undefined, + collname: undefined, + constraints: [], + seclabels: [] + }, + type_options: undefined, + // Domain Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'] + },{ + id: 'oid', label:'{{ _('OID') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl, + node: 'role', type: 'text', mode: ['edit', 'create', 'properties'] + },{ + id: 'basensp', label:'{{ _('Schema') }}', cell: 'node-list-by-name', + control: 'node-list-by-name', cache_level: 'database', type: 'text', + node: 'schema', mode: ['create', 'edit'] + },{ + id: 'sysdomain', label:'{{ _('System domain?') }}', cell: 'boolean', + type: 'switch', mode: ['properties'], + options: { + 'onText': 'Yes', 'offText': 'No', + 'onColor': 'success', 'offColor': 'primary', + 'size': 'small' + } + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'basetype', label:'{{ _('Base type') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', mode:['properties', 'create', 'edit'], group: '{{ _('Definition') }}', url: 'get_types', + disabled: function(m) { return !m.isNew(); }, first_empty: true, + transform: function(d){ + this.model.type_options = d; + return d; + } + },{ + id: 'typlen', label:'{{ _('Length') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'], + disabled: function(m) { + // We will store type from selected from combobox + if (!m.isNew()) { + return true; + } + var of_type = m.get('basetype'); + if(m.type_options) { + // iterating over all the types + _.each(m.type_options, function(o) { + // if type from selected from combobox matches in options + if ( of_type == o.value ) { + // if length is allowed for selected type + if(o.length) + { + // set the values in model + m.set('is_tlength', true, {silent: true}); + m.set('min_val', o.min_val, {silent: true}); + m.set('max_val', o.max_val, {silent: true}); + } + } + }); + } + return !m.get('is_tlength'); + } + },{ + id: 'precision', label:'{{ _('Precision') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'], + disabled: function(m) { + // We will store type from selected from combobox + if (!m.isNew()) { + return true; + } + var of_type = m.get('basetype'); + if(m.type_options) { + // iterating over all the types + _.each(m.type_options, function(o) { + // if type from selected from combobox matches in options + if ( of_type == o.value ) { + // if precession is allowed for selected type + if(o.precision) + { + // set the values in model + m.set('is_precision', true, {silent: true}); + m.set('min_val', o.min_val, {silent: true}); + m.set('max_val', o.max_val, {silent: true}); + } + } + }); + } + return !m.get('is_precision'); + } + },{ + id: 'typdefault', label:'{{ _('Default') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', + placeholder: "Enter an expression or a value." + },{ + id: 'typnotnull', label:'{{ _('Not Null?') }}', cell: 'boolean', + type: 'switch', group: '{{ _('Definition') }}', + options: { + 'onText': 'Yes', 'offText': 'No', + 'onColor': 'success', 'offColor': 'primary', + 'size': 'small' + } + },{ + id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', group: '{{ _('Definition') }}', url: 'get_collations', disabled: function(m) { + return !m.isNew(); + } + },{ + id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string', + type: 'collection', group: '{{ _('Constraints') }}', mode: ['edit', 'create'], + model: ConstraintModel, canAdd: true, canDelete: true, + canEdit: false, columns: ['conname','consrc', 'convalidated'] + },{ + id: 'seclabels', label: '{{ _('Security Labels') }}', + model: SecurityModel, type: 'collection', + group: '{{ _('Security') }}', mode: ['edit', 'create'], + min_version: 90100, canAdd: true, + canEdit: false, canDelete: true, + control: 'unique-col-collection', uniqueCol : ['provider'] + } + ], + validate: function() // Client Side Validation + { + var err = {}, + errmsg, + seclabels = this.get('seclabels'); + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('basetype')) || String(this.get('basetype')).replace(/^\s+|\s+$/g, '') == '') { + err['basetype'] = '{{ _('Base Type can not be empty!') }}'; + errmsg = errmsg || err['basetype']; + } + + this.errorModel.clear().set(err); + + return null; + } + }), + canCreate: function(itemData, item, data) { + //If check is false then , we will allow create menu + if (data && data.check == false) + return true; + + var t = pgBrowser.tree, i = item, d = itemData; + // To iterate over tree to check parent node + while (i) { + // If it is schema then allow user to create domain + if (_.indexOf(['schema'], d._type) > -1) + return true; + + if ('coll-domain' == d._type) { + //Check if we are not child of catalog + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + if( prev_d._type == 'catalog') { + return false; + } else { + return true; + } + } + i = t.hasParent(i) ? t.parent(i) : null; + d = i ? t.itemData(i) : null; + } + // by default we do not want to allow create menu + return true; + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return false; + } + } + return true; + } + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql new file mode 100644 index 0000000..f8b0b75 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql @@ -0,0 +1,30 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname %} + + COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL{% endif %}{% if data.constraints %}{% for c in data.constraints %}{% if c.conname and c.consrc %} + + CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% endif -%} +{% endfor -%} +{% endif -%}; + +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.description %} + + +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}';{% endif -%}{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} + + +{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%} +{% endfor -%} +{% endif -%} + +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..7a12b50 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql @@ -0,0 +1,16 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +AND + d.oid={{doid}}::int; +{% endif %} + +{% if name %} +DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql new file mode 100644 index 0000000..819fdbb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql new file mode 100644 index 0000000..897fb24 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql @@ -0,0 +1,15 @@ +SELECT + 'DOMAIN' AS objectkind, c.oid as conoid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as cons +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' + AND contypid = {{doid}}::oid +ORDER BY conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..8b5c891 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql @@ -0,0 +1,18 @@ +{% if doid %} +SELECT + d.typnamespace as scid +FROM + pg_type d +WHERE + d.oid={{ doid }}::oid; +{% else %} +SELECT + d.oid, d.typnamespace +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..42af39d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql @@ -0,0 +1,35 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, + pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT + array_agg(provider || '=' || label) + FROM + pg_seclabel sl1 + WHERE + sl1.objoid=d.oid) AS seclabels +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid + {% if doid %} + AND d.oid={{doid}}::int + {% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql new file mode 100644 index 0000000..3c205dc --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql @@ -0,0 +1,69 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER TYPE {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} +{% endif -%} +{% if data.typnotnull and not o_data.typnotnull %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif -%}{% if data.typdefault %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault }}; +{% elif data.typdefault == '' and o_data.typdefault %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif -%}{% if data.owner %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }}; +{% endif -%}{% if data.constraints %} +{% for c in data.constraints.deleted %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }}; +{% endfor -%} +{% for c in data.constraints.added %} +{% if c.conname and c.consrc %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} );{% endif -%} +{% endfor -%}{% endif -%} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} + +{% endfor -%} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} + +{% endfor -%} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} + +{% endfor -%} +{% endif -%}{% if data.description %} + +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }}; +{% endif -%}{% if data.basensp %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%} +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql new file mode 100644 index 0000000..f71e5a3 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql @@ -0,0 +1,36 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname and data.collname != "pg_catalog.\"default\"" %} + + COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL{% endif %}; + +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %} + +{% if data.constraints %} +{% for c in data.constraints %}{% if c.conname and c.consrc %} + +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if not c.convalidated %} NOT VALID{% endif %}{% endif -%}; +{% endfor -%} +{% endif %} + +{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}';{% endif -%} + +{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} + + +{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%} +{% endfor -%} +{% endif -%} + +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..7a12b50 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql @@ -0,0 +1,16 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +AND + d.oid={{doid}}::int; +{% endif %} + +{% if name %} +DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql new file mode 100644 index 0000000..e59c17d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql new file mode 100644 index 0000000..df956bf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql @@ -0,0 +1,15 @@ +SELECT + 'DOMAIN' AS objectkind, c.oid as conoid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc, connoinherit, convalidated +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +ORDER BY + conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..8b5c891 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql @@ -0,0 +1,18 @@ +{% if doid %} +SELECT + d.typnamespace as scid +FROM + pg_type d +WHERE + d.oid={{ doid }}::oid; +{% else %} +SELECT + d.oid, d.typnamespace +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..2892988 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql @@ -0,0 +1,34 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT + array_agg(provider || '=' || label) + FROM + pg_shseclabel sl1 + WHERE + sl1.objoid=d.oid) AS seclabels +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid +{% if doid %} + AND d.oid={{doid}}::int +{% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql new file mode 100644 index 0000000..a9ae19e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql @@ -0,0 +1,80 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} +{% endif -%} +{% if data.typnotnull and not o_data.typnotnull %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif -%}{% if data.typdefault %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault }}; +{% elif data.typdefault == '' and o_data.typdefault %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif -%}{% if data.owner %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }}; +{% endif -%}{% if data.constraints %} +{% for c in data.constraints.deleted %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(o_data['constraints'][c.conoid]['conname']) }}; +{% endfor -%} +{% for c in data.constraints.changed %} +{% if c.conname and c.conname !=o_data['constraints'][c.conoid]['conname'] %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data['constraints'][c.conoid]['conname']) }} TO {{ conn|qtIdent(c.conname) }}; +{% endif %} +{% if c.convalidated and not o_data['constraints'][c.conoid]['convalidated'] %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + VALIDATE CONSTRAINT {{ conn|qtIdent(c.conname) }}; +{% endif %} +{% endfor -%} +{% for c in data.constraints.added %} +{% if c.conname and c.consrc %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if not c.convalidated %} NOT VALID{% endif %}{% if c.connoinherit %} NO INHERIT{% endif -%};{% endif -%} +{% endfor -%}{% endif -%} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} + +{% endfor %} +{% endif -%} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif -%}{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif -%}{% if data.description %} + +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }}; +{% endif -%}{% if data.basensp %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%} +{% endif -%} ^ permalink raw reply [nested|flat] 29+ messages in thread
* Re: pgAdmin4 PATCH: Domain Module @ 2016-04-05 15:32 Dave Page <[email protected]> parent: Khushboo Vashi <[email protected]> 0 siblings, 0 replies; 29+ messages in thread From: Dave Page @ 2016-04-05 15:32 UTC (permalink / raw) To: Khushboo Vashi <[email protected]>; +Cc: pgadmin-hackers Thanks - committed. On Thu, Mar 24, 2016 at 3:25 PM, Khushboo Vashi <[email protected]> wrote: > Hi, > > Please find the attached updated patch for the Domains Module. > > Thanks, > Khushboo > > On Thu, Mar 24, 2016 at 5:29 PM, Dave Page <[email protected]> wrote: >> >> Hi >> >> You're going to hate me for this.... >> >> - I added an un-validated constraint to a domain, then opened the >> domain properties and clicked the Validate? option for it. The SQL is >> generated, but the Save button is not enabled. > > Done. >> >> >> - If I right-click a domain, I get Create options for "Domain" (with a >> constraint icon) and "Domain..." with a domain icon. >> > As per the discussion with Ashesh, He has updated the context menu JS file > with the new version and that is causing the issue. > He is going to fix this issue as this is generic for the all context menu. > I will create a new task for this in kanban and assign it to Ashesh. > >> See attached screenshot. >> >> Thanks. >> >> On Thu, Mar 24, 2016 at 9:54 AM, Khushboo Vashi >> <[email protected]> wrote: >> > Hi, >> > >> > Please find the attached updated patch for the Domain module. >> > >> > Thanks, >> > Khushboo >> > >> > On Wed, Mar 23, 2016 at 6:35 PM, Dave Page <[email protected]> wrote: >> >> >> >> Hi >> >> >> >> Almost there :-s >> >> >> >> - The hint for default should be a placeholder in the textbox itself >> >> (like combos have "Select from the list" >> > >> > Done >> >> >> >> - I should be able to check the "Validate?" option on one or more >> >> constraints from within the Domain dialogue >> > >> > Done. >> > Constraint Name can also be changed through the Domain dialogue. >> >> >> >> - Please ensure the capitalisation of all property labels is >> >> consistent - it should be "Base type" not "Base Type", "System >> >> domain?" not "System Domain?" etc. >> > >> > Done >> >> >> >> - The check constraint reverse engineered SQL should include the path >> >> to the constraint, e.g. >> >> >> > Done >> >> >> >> -- CHECK: schema.domain.check_at >> >> >> >> Once that's done, it can be committed I think. >> >> >> >> Thanks. >> >> >> >> On Wed, Mar 23, 2016 at 7:27 AM, Khushboo Vashi >> >> <[email protected]> wrote: >> >> > Updated one comment. >> >> > >> >> > On Wed, Mar 23, 2016 at 12:48 PM, Khushboo Vashi >> >> > <[email protected]> wrote: >> >> >> >> >> >> Hi, >> >> >> >> >> >> Please find attached updated patch for the Domains module. >> >> >> >> >> >> On Wed, Mar 16, 2016 at 9:40 PM, Dave Page <[email protected]> >> >> >> wrote: >> >> >>> >> >> >>> Hi >> >> >>> >> >> >>> On Wed, Mar 16, 2016 at 2:03 PM, Khushboo Vashi >> >> >>> <[email protected]> wrote: >> >> >>> > Hi, >> >> >>> > >> >> >>> > Please find the updated Domain Module Patch. >> >> >>> > >> >> >>> > To test this patch, please apply Backgrid Textarea Cell Patch >> >> >>> > before >> >> >>> > this. >> >> >>> >> >> >>> Thanks. I believe with the following fixes, we'll be done :-) >> >> >>> >> >> >>> - Default values should be auto-quoted when necessary (ie. strings, >> >> >>> on >> >> >>> a text-based domain). >> >> >> >> >> >> As per our discussion, this should leave unquoted. >> >> > >> >> > And also added a hint to the field stated 'Enter an expression or a >> >> > value.' >> >> >> >> >> >> >> >> >>> - "System Domain?" should be in the General section, between owner >> >> >>> and >> >> >>> comment. >> >> >> >> >> >> Done >> >> >>> >> >> >>> - The switches should use the same colouring/styling as other >> >> >>> objects, >> >> >>> e.g. >> >> >>> >> >> >>> options: { >> >> >>> 'onText': 'Yes', 'offText': 'No', >> >> >>> 'onColor': 'success', 'offColor': 'primary', >> >> >>> 'size': 'small' >> >> >>> } >> >> >> >> >> >> Done >> >> >>> >> >> >>> - Please remove the Schema property from the main properties tab >> >> >>> (not >> >> >>> the properties dialogue). >> >> >> >> >> >> Done >> >> >>> >> >> >>> - No icon is show for Checks on the Dependents tab for a domain. >> >> >> >> >> >> Done >> >> >>> >> >> >>> - The add button on the Security Labels tab is spelt "Add". Why is >> >> >>> that? Other instances of this grid use "ADD" which is the default >> >> >>> in >> >> >>> backform.pgadmin.js. >> >> >> >> >> >> Done >> >> >>> >> >> >>> - Dependencies on domain check constraints are listed as being on a >> >> >>> "Type" not a "Domain". >> >> >> >> >> >> Done >> >> >>> >> >> >>> - If adding a domain constraint using the grid on the Domain >> >> >>> dialogue, >> >> >>> I cannot specify "NOT VALID". We need a checkbox for that in a >> >> >>> narrow >> >> >>> columns at the end. Unchecking it for an existing constraint should >> >> >>> be >> >> >>> the equivalent of doing "ALTER DOMAIN ... VALIDATE CONSTRAINT" >> >> >> >> >> >> Done >> >> >>> >> >> >>> - If I switch the "Don't Validate" switch on a constraint, there >> >> >>> are >> >> >>> leading blank lines in the generated SQL. The same occurs when >> >> >>> adding >> >> >>> a comment to a constraint. >> >> >> >> >> >> Done >> >> >>> >> >> >>> - I think we need to reverse the meaning of "Don't Validate" and >> >> >>> rename to match the "Valid?" field that's on the properties list. >> >> >>> Otherwise it's not clear they're the same thing. >> >> >> >> >> >> Done >> >> >>> >> >> >>> - s/Not Null/Not Null?/ >> >> >> >> >> >> Done >> >> >>> >> >> >>> >> >> >>> >> >> >>> -- >> >> >>> Dave Page >> >> >>> Blog: http://pgsnake.blogspot.com >> >> >>> Twitter: @pgsnake >> >> >>> >> >> >>> EnterpriseDB UK: http://www.enterprisedb.com >> >> >>> The Enterprise PostgreSQL Company >> >> >> >> >> >> >> >> > >> >> >> >> >> >> >> >> -- >> >> Dave Page >> >> Blog: http://pgsnake.blogspot.com >> >> Twitter: @pgsnake >> >> >> >> EnterpriseDB UK: http://www.enterprisedb.com >> >> The Enterprise PostgreSQL Company >> > >> > >> >> >> >> -- >> Dave Page >> Blog: http://pgsnake.blogspot.com >> Twitter: @pgsnake >> >> EnterpriseDB UK: http://www.enterprisedb.com >> The Enterprise PostgreSQL Company > > -- Dave Page Blog: http://pgsnake.blogspot.com Twitter: @pgsnake EnterpriseDB UK: http://www.enterprisedb.com The Enterprise PostgreSQL Company -- Sent via pgadmin-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers ^ permalink raw reply [nested|flat] 29+ messages in thread
end of thread, other threads:[~2016-04-05 15:32 UTC | newest] Thread overview: 29+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2016-01-20 04:48 pgAdmin4 PATCH: Domain Module Khushboo Vashi <[email protected]> 2016-01-20 07:07 ` Khushboo Vashi <[email protected]> 2016-01-20 07:20 ` Neel Patel <[email protected]> 2016-01-20 09:20 ` Khushboo Vashi <[email protected]> 2016-01-20 17:26 ` Neel Patel <[email protected]> 2016-02-02 10:21 ` Khushboo Vashi <[email protected]> 2016-02-03 10:52 ` Neel Patel <[email protected]> 2016-02-23 07:07 ` Khushboo Vashi <[email protected]> 2016-02-24 09:24 ` Khushboo Vashi <[email protected]> 2016-02-25 12:13 ` Dave Page <[email protected]> 2016-02-25 13:30 ` Dave Page <[email protected]> 2016-03-16 09:18 ` Khushboo Vashi <[email protected]> 2016-03-16 09:25 ` Dave Page <[email protected]> 2016-03-16 09:32 ` Khushboo Vashi <[email protected]> 2016-03-16 14:03 ` Khushboo Vashi <[email protected]> 2016-03-16 16:10 ` Dave Page <[email protected]> 2016-03-17 17:39 ` Khushboo Vashi <[email protected]> 2016-03-18 15:01 ` Dave Page <[email protected]> 2016-03-21 07:41 ` Khushboo Vashi <[email protected]> 2016-03-22 17:10 ` Dave Page <[email protected]> 2016-03-23 07:18 ` Khushboo Vashi <[email protected]> 2016-03-23 07:27 ` Khushboo Vashi <[email protected]> 2016-03-23 09:39 ` Dave Page <[email protected]> 2016-03-23 09:42 ` Ashesh Vashi <[email protected]> 2016-03-23 13:05 ` Dave Page <[email protected]> 2016-03-24 09:54 ` Khushboo Vashi <[email protected]> 2016-03-24 11:59 ` Dave Page <[email protected]> 2016-03-24 15:25 ` Khushboo Vashi <[email protected]> 2016-04-05 15:32 ` Dave Page <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox