Hi, Please find the updated Domain Module Patch.
To test this patch, please apply Backgrid Textarea Cell Patch before this. Thanks, Khushboo On Wed, Mar 16, 2016 at 3:02 PM, Khushboo Vashi < khushboo.va...@enterprisedb.com> wrote: > Hi, > > On Wed, Mar 16, 2016 at 2:55 PM, Dave Page <dp...@pgadmin.org> wrote: > >> Hi >> >> On Wed, Mar 16, 2016 at 9:18 AM, Khushboo Vashi >> <khushboo.va...@enterprisedb.com> wrote: >> > >> >>> - Owner and schema should be allowed to be left blank (and then >> default >> >>> to the current user/schema) >> > >> > Done >> >> Oh, sorry - that design changed a while back, and I've updated all the >> existing nodes already. I thought I'd mentioned that. All we do now is >> pre-set the default values for those two fields. >> > I have done in this way only. Sorry for the misunderstanding. > >> >>> - When adding constraints, I should be able to type directly into the >> >>> grid. Expanding the row should be optional. >> > >> > I have made the grid non-editable explicitly as the Check constraint >> control >> > is multi-line control and right now there is no support in the grid for >> the >> > multi-line control. >> >> Not sure I follow - the mockup design you sent months ago allowed you >> to type into the grid, and expand a row to show all fields if you >> wanted. That is an *absolutely essential* feature enhancement for >> pgAdmin 4 - it's required by the table design (though this will change >> a little in other ways, like positioning of the expand row button), >> and should be used here: >> >> https://www.lucidchart.com/documents/edit/610ce42d-c397-48ff-a5e7-bd92c4995715/0 >> >> All other controls other than text-area are supported in back-grid. > I will try to incorporate text-area as well, so we can directly type into > the grid for this control also. > > >> >>> - The comment column on the constraints grid expands when the text >> >>> reaches ~50% of the width. It should be a fixed size (and use 100% of >> the >> >>> space available, less appropriate margins) >> > >> > I have applied the size for the each header of the grid, but if the >> given >> > input will be without space in the grid then it will expand. For this, >> we >> > can make table layout fixed. So, please suggest, should I do that or >> not? >> >> Yes, I think it should be fixed. If the grid row is expanded, >> presumably it'll show in a multi-line field anyway? Plus the >> properties will use a multi-line field as well. >> >> Okay. > >> -- >> Dave Page >> Blog: http://pgsnake.blogspot.com >> Twitter: @pgsnake >> >> EnterpriseDB UK: http://www.enterprisedb.com >> The Enterprise PostgreSQL Company >> > >
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py new file mode 100644 index 0000000..2c273e4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/__init__.py @@ -0,0 +1,806 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements the Domain Node.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas as schemas +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from pgadmin.browser.server_groups.servers.databases.schemas.utils import \ + SchemaChildModule, DataTypeReader +from pgadmin.browser.server_groups.servers.databases.utils import \ + parse_sec_labels_from_db +from functools import wraps + + +class DomainModule(SchemaChildModule): + """ + class DomainModule(SchemaChildModule): + + This class represents The Domain Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Module. + + * get_nodes(gid, sid, did, scid) + - Generate the domain collection node. + + * script_load() + - Load the module script for domain, when schema node is + initialized. + """ + + NODE_TYPE = 'domain' + COLLECTION_LABEL = gettext("Domains") + + def __init__(self, *args, **kwargs): + super(DomainModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid): + """ + Generate the domain collection node. + """ + yield self.generate_browser_collection_node(scid) + + @property + def script_load(self): + """ + Load the module script for domain, when schema node is + initialized. + """ + return schemas.SchemaModule.NODE_TYPE + + +blueprint = DomainModule(__name__) + + +class DomainView(PGChildNodeView, DataTypeReader): + """ + class DomainView + + This class inherits PGChildNodeView to get the different routes for + the module. Also, inherits DataTypeReader to get data types. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain. + + Methods: + ------- + * validate_request(f): + - Works as a decorator. + Validating request on the request of create, update and modified SQL. + + * module_js(): + - Load JS file (domains.js) for this module. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domains. + + * nodes(gid, sid, did, scid): + - Returns all the Domains to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain properties. + + * get_collations(gid, sid, did, scid, doid=None): + - Returns Collations. + + * create(gid, sid, did, scid): + - Creates a new Domain object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain object. + + * dependents(gid, sid, did, scid, doid): + - Returns the dependents for the Domain object. + + * dependencies(gid, sid, did, scid, doid): + - Returns the dependencies for the Domain object. + + * types(gid, sid, did, scid, fnid=None): + - Returns Data Types. + """ + + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'doid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_types': [{'get': 'types'}, {'get': 'types'}], + 'get_collations': [ + {'get': 'get_collations'}, + {'get': 'get_collations'} + ] + }) + + def validate_request(f): + """ + Works as a decorator. + Validating request on the request of create, update and modified SQL. + + Required Args: + name: Name of the Domain + owner: Domain Owner + basensp: Schema Name + basetype: Data Type of the Domain + + Above both the arguments will not be validated in the update action. + """ + + @wraps(f) + def wrap(self, **kwargs): + + data = {} + if request.data: + req = json.loads(request.data.decode()) + else: + req = request.args or request.form + + if 'doid' not in kwargs: + required_args = [ + 'name', + 'basetype' + ] + + for arg in required_args: + if arg not in req or req[arg] == '': + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter \ + (%s)." % arg + ) + ) + + try: + list_params = [] + if request.method == 'GET': + list_params = ['constraints', 'seclabels'] + + for key in req: + if key in list_params and req[key] != '' \ + and req[key] is not None: + # Coverts string into python list as expected. + data[key] = json.loads(req[key]) + elif key == 'typnotnull': + data[key] = True if req[key] == 'true' or req[key] is\ + True else\ + (False if req[key] == 'false' or req[key] is + False else '') + else: + data[key] = req[key] + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + self.request = data + return f(self, **kwargs) + + return wrap + + def module_js(self): + """ + Load JS file (domains.js) for this module. + """ + return make_response( + render_template( + "domains/js/domains.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + # Get database connection + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + server_type = self.manager.server_type + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domains/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domains/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid): + """ + List the Domains. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid): + """ + Returns all the Domains to generate Nodes in the browser. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + res = [] + SQL = render_template("/".join([self.template_path, 'node.sql']), + scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + scid, + row['name'], + icon="icon-domain" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid): + """ + Returns the Domain properties. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + # Get Type Length and Precision + data.update(self._parse_type(data['fulltype'])) + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + # Get formatted Security Labels + if 'seclabels' in data: + data.update(parse_sec_labels_from_db(data['seclabels'])) + + # Set System Domain Status + data['sysdomain'] = False + if doid <= self.manager.db_info[did]['datlastsysoid']: + data['sysdomain'] = True + + return ajax_response( + response=data, + status=200 + ) + + def _parse_type(self, basetype): + """ + Returns Type and Data Type from the basetype. + """ + typ_len = '' + typ_precision = '' + + # The Length and the precision of the Datatype should be separate. + # The Format we getting from database is: numeric(1,1) + # So, we need to separate Length: 1, Precision: 1 + + if basetype != '' and basetype.find("(") > 0: + substr = basetype[basetype.find("(") + 1:len( + basetype) - 1] + typlen = substr.split(",") + if len(typlen) > 1: + typ_len = typlen[0] + typ_precision = typlen[1] + else: + typ_len = typlen + typ_precision = '' + + return {'typlen': typ_len, 'precision': typ_precision} + + @check_precondition + def get_collations(self, gid, sid, did, scid, doid=None): + """ + Returns Collations. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + res = [{'label': '', 'value': ''}] + try: + SQL = render_template("/".join([self.template_path, + 'get_collations.sql'])) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in rset['rows']: + res.append({'label': row['copy_collation'], + 'value': row['copy_collation']} + ) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def types(self, gid, sid, did, scid, doid=None): + """ + Returns the Data Types. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + fnid: Function Id + """ + + condition = """typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r') +AND NOT EXISTS (SELECT 1 FROM pg_class WHERE relnamespace=typnamespace +AND relname = typname AND relkind != 'c') AND +(typname NOT LIKE '_%' OR NOT EXISTS (SELECT 1 FROM pg_class WHERE +relnamespace=typnamespace AND relname = substring(typname FROM 2)::name +AND relkind != 'c'))""" + + if self.blueprint.show_system_objects: + condition += " AND nsp.nspname != 'information_schema'" + + # Get Types + status, types = self.get_types(self.conn, condition) + + if not status: + return internal_server_error(errormsg=types) + + return make_json_response( + data=types, + status=200 + ) + + @check_precondition + @validate_request + def create(self, gid, sid, did, scid): + """ + Creates a new Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + Domain object in json format. + """ + + data = self.request + try: + SQL = render_template("/".join([self.template_path, 'create.sql']), + data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # We need oid to to add object in tree at browser, below sql will + # gives the same + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + basensp=data['basensp'], + name=data['name']) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + doid, scid = res['rows'][0] + + return jsonify( + node=self.blueprint.generate_browser_node( + doid, + scid, + data['name'], + icon="icon-domain" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid): + """ + Drops the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + name, basensp = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + name=name, basensp=basensp, cascade=cascade) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + @validate_request + def update(self, gid, sid, did, scid, doid): + """ + Updates the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if not status: + return internal_server_error(errormsg=SQL) + + try: + if SQL: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get Schema Id + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + scid = res['rows'][0]['scid'] + + return make_json_response( + success=1, + info="Domain updated", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid=None): + """ + Returns the SQL for the Domain object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return False, internal_server_error(errormsg=res) + data = res['rows'][0] + + # Get Type Length and Precision + data.update(self._parse_type(data['fulltype'])) + + # Get Domain Constraints + SQL = render_template("/".join([self.template_path, + 'get_constraints.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data['constraints'] = res['rows'] + + # Toggle Validate and inherit options for 'CREATE Query' + for c in data['constraints']: + if 'convalidated' in c: + c['convalidated'] = False if c['convalidated'] else True + if 'connoinherit' in c: + c['connoinherit'] = False if c['connoinherit'] else True + + SQL = render_template("/".join([self.template_path, + 'create.sql']), data=data) + + sql_header = """-- DOMAIN: {0} + +-- DROP DOMAIN {0}; + +""".format(data['basensp'] + '.' + data['name']) + + SQL = sql_header + SQL + + return ajax_response(response=SQL) + + @check_precondition + @validate_request + def msql(self, gid, sid, did, scid, doid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Domain Name + owner: Owner Name + basensp: Schema Name + basetype: Domain Base Type + + Returns: + SQL statements to create/update the Domain. + """ + + status, SQL = self.get_sql(gid, sid, self.request, scid, doid) + + if SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid=None): + """ + Generates the SQL statements to create/update the Domain. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + + try: + if doid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + scid=scid, doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data) + else: + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data) + return True, SQL + + except Exception as e: + return False, e + + @check_precondition + def dependents(self, gid, sid, did, scid, doid): + """ + This function get the dependents and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependents_result = self.get_dependents(self.conn, doid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid): + """ + This function get the dependencies and return ajax response + for the Domain node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + dependencies_result = self.get_dependencies(self.conn, doid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py new file mode 100644 index 0000000..d16bb60 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/__init__.py @@ -0,0 +1,653 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements the Domain Constraint Module.""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas.domains \ + as domains +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps + + +class DomainConstraintModule(CollectionNodeModule): + """ + class DomainConstraintModule(CollectionNodeModule): + + This class represents The Domain Constraint Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Domain Constraint Module. + + * get_nodes(gid, sid, did, scid) + - Generate the Domain Constraint collection node. + + * node_inode(gid, sid, did, scid) + - Returns Domain Constraint node as leaf node. + + * script_load() + - Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + NODE_TYPE = 'domain-constraints' + COLLECTION_LABEL = gettext("Domain Constraints") + + def __init__(self, *args, **kwargs): + super(DomainConstraintModule, self).__init__(*args, **kwargs) + self.min_ver = None + self.max_ver = None + + def get_nodes(self, gid, sid, did, scid, doid): + """ + Generate the Domain Constraint collection node. + """ + yield self.generate_browser_collection_node(doid) + + @property + def node_inode(self): + """ + Returns Domain Constraint node as leaf node. + """ + return False + + @property + def script_load(self): + """ + Load the module script for the Domain Constraint, when any of the + Domain node is initialized. + """ + return domains.DomainModule.NODE_TYPE + + +blueprint = DomainConstraintModule(__name__) + + +class DomainConstraintView(PGChildNodeView): + """ + class DomainConstraintView(PGChildNodeView): + + This class inherits PGChildNodeView to get the different routes for + the module. + + The class is responsible to Create, Read, Update and Delete operations for + the Domain Constraint. + + Methods: + ------- + + * module_js(): + - Load JS file (domain-constraints.js) for this module. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, doid): + - List the Domain Constraints. + + * nodes(gid, sid, did, scid): + - Returns all the Domain Constraints to generate Nodes in the browser. + + * properties(gid, sid, did, scid, doid): + - Returns the Domain Constraint properties. + + * create(gid, sid, did, scid): + - Creates a new Domain Constraint object. + + * update(gid, sid, did, scid, doid): + - Updates the Domain Constraint object. + + * delete(gid, sid, did, scid, doid): + - Drops the Domain Constraint object. + + * sql(gid, sid, did, scid, doid=None): + - Returns the SQL for the Domain Constraint object. + + * msql(gid, sid, did, scid, doid=None): + - Returns the modified SQL. + + * get_sql(gid, sid, data, scid, doid=None): + - Generates the SQL statements to create/update the Domain Constraint. + object. + + * dependents(gid, sid, did, scid, doid, coid): + - Returns the dependents for the Domain Constraint object. + + * dependencies(gid, sid, did, scid, doid, coid): + - Returns the dependencies for the Domain Constraint object. + """ + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'}, + {'type': 'int', 'id': 'doid'} + ] + ids = [ + {'type': 'int', 'id': 'coid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def module_js(self): + """ + Load JS file (domain-constraints.js) for this module. + """ + return make_response( + render_template( + "domain-constraints/js/domain-constraints.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks database connection status. + Attach connection object and template path. + """ + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + self.qtIdent = driver.qtIdent + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + ver = self.manager.version + + # we will set template path for sql scripts + if ver >= 90200: + self.template_path = 'domain-constraints/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'domain-constraints/sql/9.1_plus' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid, doid): + """ + List the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid, doid): + """ + Returns all the Domain Constraints. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + """ + res = [] + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid) + status, rset = self.conn.execute_2darray(SQL) + + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + doid, + row['name'], + icon="icon-domain-constraints" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, doid, coid): + """ + Returns the Domain Constraints property. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + if 'convalidated' in data: + data['convalidated'] = not data['convalidated'] + return ajax_response( + response=data, + status=200 + ) + + @check_precondition + def create(self, gid, sid, did, scid, doid): + """ + Creates a new Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + + Required Args: + name: Constraints Name + consrc: Constraints Check + + Returns: + Domain Constraint object in json format. + """ + + data = request.form if request.form else \ + json.loads(request.data.decode()) + required_args = [ + 'name', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + try: + # Get Schema and Domain. + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=res) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Get the recently added constraints oid + SQL = render_template("/".join([self.template_path, + 'get_oid.sql']), + doid=doid, name=data['name']) + status, coid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=doid) + + return jsonify( + node=self.blueprint.generate_browser_node( + coid, + doid, + data['name'], + icon="icon-domain-constraints" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid, doid, coid): + """ + Drops the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'delete.sql']), + data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Domain Constraint dropped"), + data={ + 'id': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, scid, doid, coid): + """ + Updates the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + data = request.form if request.form else \ + json.loads(request.data.decode()) + + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + + try: + if SQL and status: + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Domain Constraint updated", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': coid, + 'doid': doid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the SQL for the Domain Constraint object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + + # Get Schema and Domain. + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=name) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + if 'convalidated' in data: + data['convalidated'] = False if data['convalidated'] else True + if 'connoinherit' in data: + data['connoinherit'] = False if data['connoinherit'] else True + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + + sql_header = """-- CHECK: {0} + +-- ALTER DOMAIN {1} DROP CONSTRAINT {0}; + +""".format(data['name'],schema + '.' + domain) + + SQL = sql_header + SQL + + return ajax_response(response=SQL) + + @check_precondition + def msql(self, gid, sid, did, scid, doid, coid=None): + """ + Returns the modified SQL. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + + Required Args: + name: Constraints Name + consrc: Constraints Check + + Returns: + Domain Constraint object in json format. + """ + data = request.args + + if coid is None: + required_args = [ + 'name', + 'consrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + status, SQL = self.get_sql(gid, sid, data, scid, doid, coid) + if status and SQL: + return make_json_response( + data=SQL, + status=200 + ) + else: + return SQL + + def get_sql(self, gid, sid, data, scid, doid, coid=None): + """ + Generates the SQL statements to create/update the Domain Constraint. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + try: + if coid is not None: + SQL = render_template("/".join([self.template_path, + 'properties.sql']), + doid=doid, coid=coid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + old_data = res['rows'][0] + + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data, conn=self.conn + ) + else: + SQL = render_template("/".join([self.template_path, + 'get_domain.sql']), + doid=doid) + status, res = self.conn.execute_2darray(SQL) + + if not status: + return False, internal_server_error(errormsg=name) + + domain, schema = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + 'create.sql']), + data=data, domain=domain, schema=schema) + return True, SQL + except Exception as e: + return False, internal_server_error(errormsg=str(e)) + + @check_precondition + def dependents(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependents and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependents_result = self.get_dependents(self.conn, coid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, doid, coid): + """ + This function get the dependencies and return ajax response + for the Domain Constraint node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Domain Id + coid: Domain Constraint Id + """ + dependencies_result = self.get_dependencies(self.conn, coid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +DomainConstraintView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/coll-domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..d62e13705c50e6c0cf8f19d680053e8643e28751 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv3GfMV1=2TrO847{Jl`|t`Qpas zn<hS+l=rMY>RGPqvlydizRJ&>B`Om#V}R-yOM?7@862M7NCR>>3p^r=fwTu0yPeFo z12TL)T^vI=t|uoPU||ZF<tgaHG*QsQ!?m&Tq=?3mCu}J#Dx3x@mM}}^iE=5NIWXnk zkpnC4ai%a>@;Gg7=uums=9bIK=Egd~(us-3g@Iv02gfsK^JP^)gH=mhBT7;dOH!?p zi&B9UgOP!ufv%yEu7P2Qk%5(ov6YF5wt=aYfq}(LRXG$5x%nxXX_XKS29{tAAk|g| XW)KahriZQpYGCkm^>bP0l+XkKyyRU} literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints-bad.png new file mode 100644 index 0000000000000000000000000000000000000000..32a045b8fafdc08640d53b2a86b1dcabcb0fe0fd GIT binary patch literal 579 zcmV-J0=)f+P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp) z=>Px$AW%$HMR1Z9#Q*@REHmdHAm<<;=p`lTDl6$LE15$|nM6yxJ3#6&F}^)Qx<p3n zH#h4zIK)Ou#79fTM@#HJKkY(8?L$QEL`CjJMeasM?ng(;QB>|oNbX2U?n+AUOH1!e zOz%xi?@dncPEXKRS?^Cz#amtQQBm+xQt(q#@KaRqR8;U)Rq<9<@mE*YVq(`~V)0vB z+GS<)VPW%PV%=$Jag!JHXlV6qZS`($_HuH3pd<EncJ_C7f1@XWqbGu>Du=8uhpjJ) zvN4agH<i3UrMYgWyK<eyN1Vq+p2tbA!G5C3PO!m$q|HvV#D$~JOsv;ftk_qv-CeQX zT*cC%$J3;><6_L$t<Kr8)!w|r@o>cPam4X*+~mi`^K{Glc|xh<NdN!<0d!JMQvg8b z*k%9#00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipf34<ROg z(p72z0056kL_t&-)1{2Z3c^qT1mDEody6IZuCW&s1V!`^4}$goe?(18@b2Db*j*w1 z%4M(p;&Zw?9ZaKAxo*x;COX}<8fzjqKRv^2kF1spymYHMjE2m7Hl|a~emCNwG8(i? z8I#``(kiBrEbh}(Qn8TL2+$}b3Hn^7p`K6R#_6zQ2$?ux;lXBYB>hkN@C%g?4vVBM R$bbL<002ovPDHLkV1min0OSAw literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/static/img/domain-constraints.png new file mode 100644 index 0000000000000000000000000000000000000000..9d1d2a061c7948168d7b1c2474d769b31709f1cf GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE08{VY2nhHc^eMaU%j`d zaI$#+HuEKC{pKEZKYn>h(+aIMH^MjGjcs3}f9Cqyt&fvx7AT*)xv^`L;hf{Hi_iP4 zxgK%&V?q65_4c*;8}G%;JMMY<SLM___M4Bi9{E^!<YUpX&n1ga`7PgFwEkdS!(#P< zNn&@N9OqiK(i&(nV@Z%-FoVOh8)-leXMsm#F_88EW4Dvpc0fjir;B5V#O36K1sn!O zhRVf}5jSsGPH?f<xudc|vBoY<&5cb=uTGB9v187J4II+it5?jhn8F{TsCZIWRad$D zg1*K9W%cu2NsD(hEfVT#*wm#pYw;D04+0E(uQE?s`Z6*ZXoqTvYeY#(Vo9o1a#1Rf zVlXl=G|)9P(lsy)F*2|+F}5->(Kax(GBB{1sVaw}AvZrIGp!P$!N3x%0i@c>zzm|{ T)b!9bKn)C@u6{1-oD!M<=4O^k literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js new file mode 100644 index 0000000..166cabc --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/js/domain-constraints.js @@ -0,0 +1,149 @@ +// Domain Constraint Module: Collection and Node +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Constraint Collection Node + if (!pgBrowser.Nodes['coll-domain-constraints']) { + var domain_constraints = pgAdmin.Browser.Nodes['coll-domain-constraints'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + type: 'coll-domain-constraints', + columns: ['name', 'description'] + }); + }; + + // Domain Constraint Node + if (!pgBrowser.Nodes['domain-constraints']) { + pgAdmin.Browser.Nodes['domain-constraints'] = pgBrowser.Node.extend({ + type: 'domain-constraints', + label: '{{ _('Domain Constraints') }}', + collection_type: 'coll-domain-constraints', + hasSQL: true, + hasDepends: true, + parent_type: ['domain'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain-constraints', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain-constraints', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain Constraint...') }}', + icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + oid: undefined, + description: undefined, + consrc: undefined, + connoinherit: undefined, + convalidated: undefined, + convalidated_p: undefined + }, + // Domain Constraint Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', type:'text', cell:'string', + disabled: 'isDisabled' + },{ + id: 'oid', label:'{{ _('OID') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'description', label: '{{ _('Comment') }}', type: 'multiline', cell: + 'string', mode: ['properties', 'create', 'edit'], min_version: 90500, + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', cel: + 'string', group: '{{ _('Definition') }}', mode: ['properties', + 'create', 'edit'], disabled: function(m) { return !m.isNew(); } + },{ + id: 'connoinherit', label: '{{ _('No Inherit') }}', type: + 'switch', cell: 'boolean', group: '{{ _('Definition') }}', mode: + ['properties', 'create', 'edit'], disabled: 'isDisabled', + visible: false + },{ + id: 'convalidated', label: "{{ _("Don't Validate") }}", type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', disabled: function(m) { + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return true; + } + else if(!m.get('convalidated')) + { + return true; + } + return false; + } + return true; + }, + mode: ['create', 'edit'], visible: function(m) { return !m.isNew() } + },{ + id: 'convalidated_p', label: '{{ _('Valid?') }}', type: 'switch', cell: + 'boolean', group: '{{ _('Definition') }}', disabled: 'isDisabled', + mode: ['properties'] + }], + // Client Side Validation + validate: function() { + var err = {}, + errmsg; + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') { + err['consrc'] = '{{ _('Check can not be empty!') }}'; + errmsg = errmsg || err['consrc']; + } + + this.errorModel.clear().set(err); + + if (_.size(err)) { + this.trigger('on-status', {msg: errmsg}); + return errmsg; + } + + return null; + + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return true; + } + } + return false; + } + }), + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql new file mode 100644 index 0000000..be943d2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/create.sql @@ -0,0 +1,3 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }});{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..043f011 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/properties.sql @@ -0,0 +1,14 @@ +SELECT + c.oid, conname AS name, typname AS relname, nspname, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql new file mode 100644 index 0000000..299ba6b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.1_plus/update.sql @@ -0,0 +1,3 @@ +{% if data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql new file mode 100644 index 0000000..513c38d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/create.sql @@ -0,0 +1,9 @@ +{% if data and schema and domain %} +ALTER DOMAIN {{ conn|qtIdent(schema, domain) }} + ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }}){% if data.convalidated %} + + NOT VALID{% endif %};{% if data.description %} + + +COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON DOMAIN {{ conn|qtIdent(schema, domain) }} + IS '{{ data.description }}';{% endif %}{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..260c3c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/delete.sql @@ -0,0 +1,4 @@ +{% if data %} +ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }} + DROP CONSTRAINT {{ conn|qtIdent(data.name) }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql new file mode 100644 index 0000000..1040c0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_domain.sql @@ -0,0 +1,8 @@ +SELECT + d.typname as domain, bn.nspname as schema +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.oid = {{doid}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..f59e08c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/get_oid.sql @@ -0,0 +1,7 @@ +SELECT + oid, conname as name +FROM + pg_constraint +WHERE + contypid = {{doid}}::oid + AND conname={{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..34d8b34 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/properties.sql @@ -0,0 +1,17 @@ +SELECT + c.oid, conname AS name, typname AS relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc, + connoinherit, convalidated, convalidated AS convalidated_p +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +{% if coid %} + AND c.oid = {{ coid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql new file mode 100644 index 0000000..b436f3a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/domain-constraints/templates/domain-constraints/sql/9.2_plus/update.sql @@ -0,0 +1,13 @@ +{% set name = o_data.name %} +{% if data.name %} +{% set name = data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif -%}{% if data.convalidated %} + + +ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + VALIDATE CONSTRAINT {{ conn|qtIdent(name) }}{% endif -%}{% if data.description %} + + +COMMENT ON CONSTRAINT {{ conn|qtIdent(name) }} ON DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }} + IS '{{ data.description }}';{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/coll-domain.png new file mode 100644 index 0000000000000000000000000000000000000000..55621528a1dba4928538fe5557b9b988ed78d6ab GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}U4T!BE0BJeoqfW=;gF{0PD8_o zi&pN_(K)B7`FPi%2ix`@v9LU(tNUQ*!K1czXOxuggoGT_(#p@zf4Jw!!zHV)`3F7T zd-Um}H}`iRIijcc@Wq#>&ptkR`SszG54Uz6zW4mg?MLsgZ9jbb>E~y!zTSB9`Re1( zjS1S99(_9h@YC4`A5Y)^_^>@J3+MvIk|4ie28U-i(tsS!0*}aIAngIhZYQ(tfQ)ue z7sn8Z%b|U@#hMgESUvs4wKoSxO>~{O=-Yq$8_!r)-^e%4-l6f-Jox1%8}FIVx>FCW zPMVwluQGbkP1bcSwVV0d_?PZbZVis%HeYTWyU*$OvYp$uTc3QLbD?2trOnDuhZa>f z=3d_{(zER9xt(<n3UsBI4SNC?Y!rBW<m2`n>yDRyg$uoE8Qx}bo1D0qw;t#u)e_f; zl9a@fRIB8oR3OD*WMF8ZYiOivU>IU#U}a)#Wn!XjU}|MxU@=ow4n;$5eoAIqB}9XP eC0GMUwUvPxM8m1+p=*E|7(8A5T-G@yGywoRCC2Lj literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain-sm.png new file mode 100644 index 0000000000000000000000000000000000000000..7521cddeaaaf0ee4e3c60e948078d70e17e06893 GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}T7XZ8E0Deu9eu*V;gF{0K`pIg zc6K`r4IeIAc}Q3HxV`=3y+@xudUJpGkt2F~PoI5!^78A$Cm(L@JbcYR=;4bm_nv>b z{pkI*?T7D#gyiSvKYR7{_S4Tdo_xOg_;X`|_N7Ok&OiKg_QA)~_dlLo85{w$iLoTe zFPOpM*^M+HhqJ&VvKUBvfU(=jY&#$$$<xI#MB;Mq`IABp20RT9wUXkaL(1R(pOn?c z>38<+e>KaN2{%0Jd@sMbY$+13Z&%Z<%!P+*SNu+#({Rr={_KxUhswHdID0QWTCBvD z)^ky;@gu`E#RAC#l`JcnelS0rxkKN7b1B>H56gX)0&P<*ag8WRNi0dVN-jzTQVd20 zh6cKZM!E)uAw~vPCdO7KCfWw3Rt5$ZGgakKH00)|WTsU@G#FTdHGouG8JIydoSGiG Q2B?9-)78&qol`;+03_q3ga7~l literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/static/img/domain.png new file mode 100644 index 0000000000000000000000000000000000000000..42ca929325854b8f34787425e8094d08c75983bc GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}PJmB{E0BJeoqfW=;gF{0PD8^7 zi&s6`bL8oxH}`iRdHU?*lb2r~J^6Th=ix^$zTSWS<?f>oH+CHUa^}pVf`aWvMt7fm zx&8F>jfd~AY(HF`mUiRG=W9<sUwQB@KR>@QLHo+%PZuA3I{)y~*$1D_JotF({zvHt zo%ulf7)yfuf*Bm1-ADs+I14-?i-EKU7`vU!wgWPXJzX3_Brcbpe=XFcAmSFNeC3q& zl!Z+(RsYN12&-NW{P*^#<rx82_2-+ii&SqfGgO`Jcj@;01@HTH{&&7#v?j}I)=J6m ze`~hppZn7?WA|%z!}T6pf>z0IjbZrS5ICD*275*_bAo)r8t#Vg4A*Lz);xc4JKq03 zXXC}zd)YwiRZCnWN>UO_QmvAUQh^kMk%6IsuAz~xfnkV|ft87|m5GVAfvJ^&fyGQ! qITQ`K`6-!cl@JXEmS7Da)m8>(5DllMhpqu?VDNPHb6Mw<&;$VOR=Fqu literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js new file mode 100644 index 0000000..598e542 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/js/domains.js @@ -0,0 +1,323 @@ +// Domain Module: Collection and Node. +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', + 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Define Domain Collection Node + if (!pgBrowser.Nodes['coll-domain']) { + var domains = pgAdmin.Browser.Nodes['coll-domain'] = + pgAdmin.Browser.Collection.extend({ + node: 'domain', + label: '{{ _('Domains') }}', + type: 'coll-domain', + columns: ['name', 'owner', 'description'] + }); + }; + + // Security Model + var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: null, + security_label: null + }, + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text', editable: true, cellHeaderClasses:'width_percent_50' + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text', editable: true, cellHeaderClasses:'width_percent_50' + }], + validate: function() { + var err = {}, + errmsg = null; + + if (_.isUndefined(this.get('security_label')) || + _.isNull(this.get('security_label')) || + String(this.get('security_label')).replace(/^\s+|\s+$/g, '') == '') { + errmsg = '{{ _('Please specify the value for all the security providers.')}}'; + this.errorModel.set('security_label', errmsg); + return errmsg; + } else { + this.errorModel.unset('security_label'); + } + return null; + } + }); + + // Constraint Model + var ConstraintModel = pgAdmin.Browser.Node.Model.extend({ + idAttribute: 'conoid', + defaults: { + conoid: undefined, + conname: undefined, + consrc: undefined, + }, + schema: [{ + id: 'conname', label: '{{ _('Name') }}', type: 'text', cell: 'string', + cellHeaderClasses: 'width_percent_40', editable: 'isEditable' + },{ + id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', + cell: Backgrid.Extension.TextareaCell, group: '{{ _('Definition') }}', + cellHeaderClasses: 'width_percent_60', editable: 'isEditable' + }], + isEditable: function(m) { + return _.isUndefined(m.isNew) ? true : m.isNew(); + }, + toJSON: Backbone.Model.prototype.toJSON + }); + + // Domain Node + if (!pgBrowser.Nodes['domain']) { + pgAdmin.Browser.Nodes['domain'] = pgBrowser.Node.extend({ + type: 'domain', + label: '{{ _('Domain') }}', + collection_type: 'coll-domain', + hasSQL: true, + hasDepends: true, + parent_type: ['schema'], + Init: function() { + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_domain_on_coll', node: 'coll-domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'domain', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_domain', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Domain...') }}', + icon: 'wcTabIcon icon-domain', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + canDropCascade: pgBrowser.Nodes['schema'].canChildDrop, + // Domain Node Model + model: pgAdmin.Browser.Node.Model.extend({ + initialize: function(attrs, args) { + var isNew = (_.size(attrs) === 0); + if (isNew) { + // Set Selected Schema + schema = args.node_info.schema.label + this.set({'basensp': schema}, {silent: true}); + + // Set Current User + var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user; + this.set({'owner': userInfo.name}, {silent: true}); + } + pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments); + }, + defaults: { + name: undefined, + oid: undefined, + owner: undefined, + basensp: undefined, + description: undefined, + basetype: undefined, + typlen: undefined, + precision: undefined, + typdefault: undefined, + typnotnull: undefined, + sysdomain: undefined, + collname: undefined, + constraints: [], + seclabels: [] + }, + type_options: undefined, + // Domain Schema + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'] + },{ + id: 'oid', label:'{{ _('OID') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl, + node: 'role', type: 'text', mode: ['edit', 'create', 'properties'] + },{ + id: 'basensp', label:'{{ _('Schema') }}', cell: 'node-list-by-name', + control: 'node-list-by-name', cache_level: 'database', type: 'text', + node: 'schema' + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'basetype', label:'{{ _('Base Type') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', mode:['properties', 'create', 'edit'], group: '{{ _('Definition') }}', url: 'get_types', + disabled: function(m) { return !m.isNew(); }, first_empty: true, + transform: function(d){ + this.model.type_options = d; + return d; + } + },{ + id: 'typlen', label:'{{ _('Length') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'], + disabled: function(m) { + // We will store type from selected from combobox + if (!m.isNew()) { + return true; + } + var of_type = m.get('basetype'); + if(m.type_options) { + // iterating over all the types + _.each(m.type_options, function(o) { + // if type from selected from combobox matches in options + if ( of_type == o.value ) { + // if length is allowed for selected type + if(o.length) + { + // set the values in model + m.set('is_tlength', true, {silent: true}); + m.set('min_val', o.min_val, {silent: true}); + m.set('max_val', o.max_val, {silent: true}); + } + } + }); + } + return !m.get('is_tlength'); + } + },{ + id: 'precision', label:'{{ _('Precision') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'], + disabled: function(m) { + // We will store type from selected from combobox + if (!m.isNew()) { + return true; + } + var of_type = m.get('basetype'); + if(m.type_options) { + // iterating over all the types + _.each(m.type_options, function(o) { + // if type from selected from combobox matches in options + if ( of_type == o.value ) { + // if precession is allowed for selected type + if(o.precision) + { + // set the values in model + m.set('is_precision', true, {silent: true}); + m.set('min_val', o.min_val, {silent: true}); + m.set('max_val', o.max_val, {silent: true}); + } + } + }); + } + return !m.get('is_precision'); + } + },{ + id: 'typdefault', label:'{{ _('Default') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}' + },{ + id: 'typnotnull', label:'{{ _('Not Null') }}', cell: 'boolean', + type: 'switch', group: '{{ _('Definition') }}' + },{ + id: 'sysdomain', label:'{{ _('System Domain?') }}', cell: 'boolean', + type: 'switch', group: '{{ _('Definition') }}', mode: ['properties'] + },{ + id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options', + type: 'text', group: '{{ _('Definition') }}', url: 'get_collations', disabled: function(m) { + return !m.isNew(); + } + },{ + id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string', + type: 'collection', group: '{{ _('Constraints') }}', mode: ['edit', 'create'], + model: ConstraintModel, canAdd: true, canDelete: true, + canEdit: false + },{ + id: 'seclabels', label: '{{ _('Security Labels') }}', + model: SecurityModel, type: 'collection', + group: '{{ _('Security') }}', mode: ['edit', 'create'], + min_version: 90100, canAdd: true, + canEdit: false, canDelete: true + } + ], + validate: function() // Client Side Validation + { + var err = {}, + errmsg, + seclabels = this.get('seclabels'); + + if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _('Name can not be empty!') }}'; + errmsg = errmsg || err['name']; + } + + if (_.isUndefined(this.get('basetype')) || String(this.get('basetype')).replace(/^\s+|\s+$/g, '') == '') { + err['basetype'] = '{{ _('Base Type can not be empty!') }}'; + errmsg = errmsg || err['basetype']; + } + + if (seclabels) { + var secLabelsErr; + for (var i = 0; i < seclabels.models.length && !secLabelsErr; i++) { + secLabelsErr = (seclabels.models[i]).validate.apply(seclabels.models[i]); + if (secLabelsErr) { + err['seclabels'] = secLabelsErr; + errmsg = errmsg || secLabelsErr; + } + } + } + + this.errorModel.clear().set(err); + + return null; + } + }), + canCreate: function(itemData, item, data) { + //If check is false then , we will allow create menu + if (data && data.check == false) + return true; + + var t = pgBrowser.tree, i = item, d = itemData; + // To iterate over tree to check parent node + while (i) { + // If it is schema then allow user to create domain + if (_.indexOf(['schema'], d._type) > -1) + return true; + + if ('coll-domain' == d._type) { + //Check if we are not child of catalog + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + if( prev_d._type == 'catalog') { + return false; + } else { + return true; + } + } + i = t.hasParent(i) ? t.parent(i) : null; + d = i ? t.itemData(i) : null; + } + // by default we do not want to allow create menu + return true; + }, + isDisabled: function(m){ + if (!m.isNew()) { + var server = this.node_info.server; + if (server.version < 90200) + { + return false; + } + } + return true; + } + }); + + } + + return pgBrowser.Nodes['domain']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql new file mode 100644 index 0000000..f8b0b75 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/create.sql @@ -0,0 +1,30 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname %} + + COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL{% endif %}{% if data.constraints %}{% for c in data.constraints %}{% if c.conname and c.consrc %} + + CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% endif -%} +{% endfor -%} +{% endif -%}; + +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.description %} + + +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}';{% endif -%}{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} + + +{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%} +{% endfor -%} +{% endif -%} + +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..7a12b50 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/delete.sql @@ -0,0 +1,16 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +AND + d.oid={{doid}}::int; +{% endif %} + +{% if name %} +DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql new file mode 100644 index 0000000..819fdbb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql new file mode 100644 index 0000000..897fb24 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_constraints.sql @@ -0,0 +1,15 @@ +SELECT + 'DOMAIN' AS objectkind, c.oid as conoid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as cons +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' + AND contypid = {{doid}}::oid +ORDER BY conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql new file mode 100644 index 0000000..8b5c891 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/get_oid.sql @@ -0,0 +1,18 @@ +{% if doid %} +SELECT + d.typnamespace as scid +FROM + pg_type d +WHERE + d.oid={{ doid }}::oid; +{% else %} +SELECT + d.oid, d.typnamespace +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..42af39d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/properties.sql @@ -0,0 +1,35 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, + pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT + array_agg(provider || '=' || label) + FROM + pg_seclabel sl1 + WHERE + sl1.objoid=d.oid) AS seclabels +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid + {% if doid %} + AND d.oid={{doid}}::int + {% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql new file mode 100644 index 0000000..6ff4052 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.1_plus/update.sql @@ -0,0 +1,64 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER TYPE {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} + +{% endif -%} +{% if data.typnotnull and not o_data.typnotnull %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif -%}{% if data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault }}; +{% elif not data.typdefault and o_data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif -%}{% if data.owner %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.constraints %} +{% for c in data.constraints.deleted %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }}; +{% endfor -%} +{% for c in data.constraints.added %} +{% if c.conname and c.consrc %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} );{% endif -%} +{% endfor -%}{% endif -%} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} + +{% endfor -%} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} + +{% endfor -%} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} + +{% endfor -%} +{% endif -%}{% if data.description %} +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }};{% endif %}{% if data.basensp %} + + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%} +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql new file mode 100644 index 0000000..e89369f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/create.sql @@ -0,0 +1,30 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname and data.collname != "pg_catalog.\"default\"" %} + + COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %} + + DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %} + + NOT NULL{% endif %}{% if data.constraints %}{% for c in data.constraints %}{% if c.conname and c.consrc %} + + CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% endif -%} +{% endfor -%} +{% endif -%}; + +{% if data.owner %} +ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.description %} + + +COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} + IS '{{ data.description }}';{% endif -%}{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.security_label and r.provider %} + + +{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%} +{% endfor -%} +{% endif -%} + +{% endif -%} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..7a12b50 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/delete.sql @@ -0,0 +1,16 @@ +{% if scid and doid %} +SELECT + d.typname as name, bn.nspname as basensp +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +AND + d.oid={{doid}}::int; +{% endif %} + +{% if name %} +DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql new file mode 100644 index 0000000..e59c17d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_collations.sql @@ -0,0 +1,10 @@ +SELECT --nspname, collname, + CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN + concat(nspname, '."', collname,'"') + ELSE '' END AS copy_collation +FROM + pg_collation c, pg_namespace n +WHERE + c.collnamespace=n.oid +ORDER BY + nspname, collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql new file mode 100644 index 0000000..df956bf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_constraints.sql @@ -0,0 +1,15 @@ +SELECT + 'DOMAIN' AS objectkind, c.oid as conoid, conname, typname as relname, nspname, description, + regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc, connoinherit, convalidated +FROM + pg_constraint c +JOIN + pg_type t ON t.oid=contypid +JOIN + pg_namespace nl ON nl.oid=typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass) +WHERE + contype = 'c' AND contypid = {{doid}}::oid +ORDER BY + conname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql new file mode 100644 index 0000000..8b5c891 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/get_oid.sql @@ -0,0 +1,18 @@ +{% if doid %} +SELECT + d.typnamespace as scid +FROM + pg_type d +WHERE + d.oid={{ doid }}::oid; +{% else %} +SELECT + d.oid, d.typnamespace +FROM + pg_type d +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + bn.nspname = {{ basensp|qtLiteral }} + AND d.typname={{ name|qtLiteral }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql new file mode 100644 index 0000000..7bd3e5b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/node.sql @@ -0,0 +1,13 @@ +SELECT + d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner, + bn.nspname as basensp +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +WHERE + d.typnamespace = {{scid}}::oid +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..2892988 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/properties.sql @@ -0,0 +1,34 @@ +SELECT + d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner, + c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype, + CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN + concat(cn.nspname, '."', c.collname,'"') + ELSE '' END AS collname, + d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp, + description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup, + (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup, + (SELECT + array_agg(provider || '=' || label) + FROM + pg_shseclabel sl1 + WHERE + sl1.objoid=d.oid) AS seclabels +FROM + pg_type d +JOIN + pg_type b ON b.oid = d.typbasetype +JOIN + pg_namespace bn ON bn.oid=d.typnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass) +LEFT OUTER JOIN + pg_collation c ON d.typcollation=c.oid +LEFT OUTER JOIN + pg_namespace cn ON c.collnamespace=cn.oid +WHERE + d.typnamespace = {{scid}}::oid +{% if doid %} + AND d.oid={{doid}}::int +{% endif %} +ORDER BY + d.typname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql new file mode 100644 index 0000000..ecc7e9a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/9.2_plus/update.sql @@ -0,0 +1,64 @@ +{% import 'macros/schemas/security.macros' as SECLABLE %} +{% if data %} +{% set name = o_data.name %} +{% if data.name %} +{% if data.name != o_data.name %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} + +{% endif -%} +{% if data.typnotnull and not o_data.typnotnull %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET NOT NULL; +{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP NOT NULL; +{% endif -%}{% if data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET DEFAULT {{ data.typdefault }}; +{% elif not data.typdefault and o_data.typdefault %} +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP DEFAULT; +{% endif -%}{% if data.owner %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.constraints %} +{% for c in data.constraints.deleted %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + DROP CONSTRAINT {{ conn|qtIdent(c.conname) }}; +{% endfor -%} +{% for c in data.constraints.added %} +{% if c.conname and c.consrc %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if c.convalidated %} + NOT VALID{% endif %}{% if c.connoinherit %} NO INHERIT{% endif -%};{% endif -%} +{% endfor -%}{% endif -%} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }} + +{% endfor %} +{% endif -%} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif -%}{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} + +{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }} +{% endfor %} +{% endif -%}{% if data.description %} + +COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + IS {{ data.description|qtLiteral }};{% endif -%}{% if data.basensp %} + +ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }} + SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%} +{% endif -%}
-- Sent via pgadmin-hackers mailing list (pgadmin-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers