diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/__init__.py new file mode 100644 index 000000000..6d3259ec6 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/__init__.py @@ -0,0 +1,987 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +import json +import re +from flask import render_template, request, jsonify, current_app +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error, gone, \ + bad_request +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule, PGChildModule +import pgadmin.browser.server_groups.servers as servers +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.utils import parse_priv_from_db, \ + parse_priv_to_db +from functools import wraps + +""" + This module is responsible for generating two nodes + 1) Schema + 2) Catalog + + We have created single file because catalog & schema has same + functionality, the only difference is we can not perform DDL/DML operations + on catalog, also - it allows us to share the same submodules for both + catalog, and schema modules. + + This modules uses separate template paths for each respective node + - templates/catalog for Catalog node + - templates/schema for Schema node + + [Each path contains node specific js files as well as sql template files.] +""" + + +class SchemaModule(CollectionNodeModule): + """ + class SchemaModule(CollectionNodeModule) + + A module class for Schema node derived from CollectionNodeModule. + + Methods: + ------- + * __init__(*args, **kwargs) + - Method is used to initialize the Schema and it's base module. + + * get_nodes(gid, sid, did) + - Method is used to generate the browser collection node. + + * node_inode() + - Method is overridden from its base class to make the node as leaf node. + + * script_load() + - Load the module script for schema, when any of the server node is + initialized. + """ + NODE_TYPE = 'schema' + COLLECTION_LABEL = gettext("Schemas") + + def __init__(self, *args, **kwargs): + """ + Method is used to initialize the SchemaModule and it's base module. + + Args: + *args: + **kwargs: + """ + self.min_ver = None + self.max_ver = None + + super(SchemaModule, self).__init__(*args, **kwargs) + + def get_nodes(self, gid, sid, did): + """ + Generate the collection node + """ + yield self.generate_browser_collection_node(did) + + @property + def script_load(self): + """ + Load the module script for server, when any of the server-group node is + initialized. + """ + return servers.ServerModule.NODE_TYPE + + +class CatalogModule(SchemaModule): + """ + class CatalogModule(SchemaModule) + + A module class for the catalog schema node derived from SchemaModule. + """ + + NODE_TYPE = 'catalog' + COLLECTION_LABEL = gettext("Catalogs") + + +schema_blueprint = SchemaModule(__name__) +catalog_blueprint = CatalogModule(__name__) + + +def check_precondition(f): + """ + This function will behave as a decorator which will checks + database connection before running view, it will also attaches + manager,conn & template_path properties to instance of the method. + + Assumptions: + This function will always be used as decorator of a class method. + """ + @wraps(f) + def wrap(*args, **kwargs): + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager( + kwargs['sid'] + ) + self.conn = self.manager.connection(did=kwargs['did']) + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext("Connection to the server has been lost!") + ) + + # Set template path for sql scripts + self.template_path = self.template_initial + '/' + ( + self.ppas_template_path(self.manager.version) + if self.manager.server_type == 'ppas' else + self.pg_template_path(self.manager.version) + ) + + return f(*args, **kwargs) + + return wrap + + +class SchemaView(PGChildNodeView): + """ + This class is responsible for generating routes for schema node. + + Methods: + ------- + * __init__(**kwargs) + - Method is used to initialize the SchemaView and it's base view. + + * module_js() + - Request handler for module.js routes for the schema node module + javascript, which returns javscript for this module. + + * list() + - This function is used to list all the schema nodes within the + collection. + + * nodes() + - This function will used to create all the child node within the + collection, Here it will create all the schema node. + + * properties(gid, sid, did, scid) + - This function will show the properties of the selected schema node. + + * create(gid, sid, did, scid) + - This function will create the new schema object. + + * update(gid, sid, did, scid) + - This function will update the data for the selected schema node. + + * delete(self, gid, sid, scid): + - This function will drop the schema object + + * msql(gid, sid, did, scid) + - This function is used to return modified SQL for the selected schema + node. + + * get_sql(data, scid) + - This function will generate sql from model data + + * sql(gid, sid, did, scid): + - This function will generate sql to show it in sql pane for the schema + node. + + * dependency(gid, sid, did, scid): + - This function will generate dependency list show it in dependency + pane for the selected schema node. + + * dependent(gid, sid, did, scid): + - This function will generate dependent list to show it in dependent + pane for the selected schema node. + """ + node_type = schema_blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'} + ] + ids = [ + {'type': 'int', 'id': 'scid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'children': [{ 'get': 'children'}], + 'nodes': [{'get': 'nodes'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'delete': [{'delete': 'delete'}] + }) + + def __init__(self, *args, **kwargs): + """ + Initialize the variables used by methods of SchemaView. + """ + + super(SchemaView, self).__init__(*args, **kwargs) + + self.manager = None + self.conn = None + self.template_path = None + self.template_initial = 'schema' + + + @staticmethod + def ppas_template_path(ver): + """ + Returns the template path for PPAS servers. + """ + if ver >= 90200: + return 'ppas/9.2_plus' + return 'ppas/9.1_plus' + + @staticmethod + def pg_template_path(ver): + """ + Returns the template path for PostgreSQL servers. + """ + if ver >= 90200: + return 'pg/9.2_plus' + return 'pg/9.1_plus' + + def format_request_acls(self, data, modified=False, specific=None): + acls = {} + try: + acls = render_template( + "/".join([self.template_path, 'allowed_privs.json']) + ) + acls = json.loads(acls) + except Exception as e: + current_app.logger.exception(e) + + # Privileges + for aclcol in acls: + if specific is not None: + if aclcol not in specific: + continue + if aclcol in data: + allowedacl = acls[aclcol] + if modified: + for modifier in ['added', 'changed', 'deleted']: + if modifier in data[aclcol]: + data[aclcol][modifier] = parse_priv_to_db( + data[aclcol][modifier], allowedacl['acl'] + ) + else: + data[aclcol] = parse_priv_to_db(data[aclcol], allowedacl['acl']) + + return acls + + @staticmethod + def formatdbacl(acl): + """ + Args: + acl: Privileges from ACL query + + Returns: + Formatted output required for client side parsing + """ + # Reset any data for that acl if its already present in result set + data = dict() + for row in acl['rows']: + priv = parse_priv_from_db(row) + + if row['deftype'] in data: + data[row['deftype']].append(priv) + else: + data[row['deftype']] = [priv] + return data + + def _formatter_no_defacl(self, data, scid=None): + """ + Args: + data: Result of properties query + scid: Schema OID + + Returns: + It will return formatted output of collections like + security lables, privileges + """ + # Need to format security labels according to client js collection + seclabels = [] + if 'seclabels' in data and data['seclabels'] is not None: + for sec in data['seclabels']: + sec = re.search(r'([^=]+)=(.*$)', sec) + seclabels.append({ + 'provider': sec.group(1), + 'security_label': sec.group(2) + }) + + data['seclabels'] = seclabels + + # We need to parse & convert ACL coming from database to json format + SQL = render_template( + "/".join([self.template_path, 'sql/acl.sql']), + _=gettext, + scid=scid + ) + status, acl = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=acl) + + data.update(self.formatdbacl(acl)) + + return data + + def _formatter(self, data, scid=None): + + self._formatter_no_defacl(data, scid) + + # We need to parse & convert DEFAULT ACL coming from + # database to json format + SQL = render_template( + "/".join([self.template_path, 'sql/defacl.sql']), + _=gettext, + scid=scid + ) + + status, defacl = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=defacl) + + data.update(self.formatdbacl(defacl)) + + return data + + @check_precondition + def list(self, gid, sid, did): + """ + This function is used to list all the schema nodes within the collection. + + Args: + gid: Server group ID + sid: Server ID + did: Database ID + + Returns: + JSON of available schema nodes + """ + SQL = render_template( + "/".join([self.template_path, 'sql/properties.sql']), + _=gettext, + show_sysobj=self.blueprint.show_system_objects + ) + 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=None): + """ + This function will create all the child nodes within the collection + Here it will create all the schema node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + + Returns: + JSON of available schema child nodes + """ + res = [] + SQL = render_template( + "/".join([self.template_path, 'sql/nodes.sql']), + show_sysobj=self.blueprint.show_system_objects, + _=gettext, + scid=scid + ) + + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + if scid is not None: + if len(rset['rows']) == 0: + return gone(gettext(""" +Could not find the schema in the database. +It may have been removed by another user. +""")) + + icon = 'icon-{0}'.format(self.node_type) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + did, + row['name'], + icon=icon, + can_create=row['can_create'], + has_usage=row['has_usage'] + ) + ) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid): + """ + This function will show the properties of the selected schema node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + + Returns: + JSON of selected schema node + """ + SQL = render_template( + "/".join([self.template_path, 'sql/properties.sql']), + scid=scid, + _=gettext, + show_sysobj=self.blueprint.show_system_objects + ) + + 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 schema in the database. +It may have been removed by another user. +""")) + + # Making copy of output for future use + copy_data = dict(res['rows'][0]) + copy_data = self._formatter(copy_data, scid) + + return ajax_response( + response=copy_data, + status=200 + ) + + @check_precondition + def create(self, gid, sid, did): + """ + This function will create a schema object + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + """ + data = request.form if request.form else \ + json.loads(request.data.decode()) + + required_args = { + 'name': 'Name' + } + + 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)." % + required_args[arg] + ) + ) + try: + SQL = render_template( + "/".join([self.template_path, 'sql/create.sql']), + data=data, conn=self.conn, _=gettext + ) + status, res = self.conn.execute_scalar(SQL) + if not status: + return make_json_response( + status=410, + success=0, + errormsg=res + '\n' + + 'Operation failed while running create statement' + ) + self.format_request_acls(data, specific=['nspacl']) + + SQL = render_template( + "/".join([self.template_path, 'sql/alter.sql']), + data=data, conn=self.conn, _=gettext + ) + # Checking if we are not executing empty query + if SQL and SQL.strip('\n') and SQL.strip(' '): + status, res = self.conn.execute_scalar(SQL) + if not status: + return make_json_response( + status=410, + success=0, + errormsg=res + '\n' + + 'Operation failed while running alter statement' + ) + + # we need oid to to add object in tree at browser, + # below sql will gives the same + SQL = render_template( + "/".join([self.template_path, 'sql/oid.sql']), + schema=data['name'], _=gettext + ) + + status, scid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=scid) + + icon = 'icon-{0}'.format(self.node_type) + + return jsonify( + node=self.blueprint.generate_browser_node( + scid, + did, + data['name'], + icon=icon + ) + ) + except Exception as e: + current_app.logger.exception(e) + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, scid): + """ + This function will update an existing schema object + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + """ + data = request.form if request.form else json.loads( + request.data.decode() + ) + try: + SQL = self.get_sql(gid, sid, data, scid) + + 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="Updated", + data={ + 'id': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, scid): + """ + This function will delete an existing schema object + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + """ + + try: + # Get name for schema from did + SQL = render_template( + "/".join([self.template_path, 'sql/get_name.sql']), + _=gettext, + scid=scid + ) + + status, name = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=name) + # drop schema + SQL = render_template( + "/".join([self.template_path, 'sql/delete.sql']), + _=gettext, name=name, conn=self.conn, + cascade=True if self.cmd == 'delete' else False + ) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Schema dropped"), + data={ + 'id': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + current_app.logger.exception(e) + return internal_server_error(errormsg=str(e)) + + @check_precondition + def msql(self, gid, sid, did, scid=None): + """ + This function will generate modified sql for schema object based on + the input from the user. This route is used by the SQL tab in the + edit/create dialog. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID (When working with existing schema node) + """ + data = dict() + for k, v in request.args.items(): + try: + data[k] = json.loads(v) + except ValueError: + data[k] = v + + try: + SQL = self.get_sql(gid, sid, data, scid) + if SQL and SQL.strip('\n') and SQL.strip(' '): + return make_json_response( + data=SQL, + status=200 + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + def get_sql(self, gid, sid, data, scid=None): + """ + This function will generate sql from model data received from client + """ + if scid is not None: + SQL = render_template( + "/".join([self.template_path, 'sql/properties.sql']), + _=gettext, scid=scid, + show_sysobj=self.blueprint.show_system_objects + ) + + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + old_data = res['rows'][0] + # old_data contains all the existing data for requested schema + old_data = self._formatter(old_data, scid) + + # if name is not present in request data then use old name + if 'name' not in data: + data['name'] = old_data['name'] + + # Privileges and Default privileges + self.format_request_acls(data, True) + + SQL = render_template( + "/".join([self.template_path, 'sql/update.sql']), + _=gettext, data=data, o_data=old_data, conn=self.conn + ) + else: + required_args = ['name'] + + for arg in required_args: + if arg not in data: + return " -- " + gettext("definition incomplete") + + # Privileges + self.format_request_acls(data, specific=['nspacl']) + + SQL = render_template( + "/".join([self.template_path, 'sql/create.sql']), + data=data, conn=self.conn, _=gettext + ) + SQL += "\n" + SQL += render_template( + "/".join([self.template_path, 'sql/alter.sql']), + _=gettext, data=data, conn=self.conn + ) + + return SQL + + @check_precondition + def sql(self, gid, sid, did, scid): + """ + This function will generate reverse engineered sql for the schema + object. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + """ + SQL = render_template( + "/".join([self.template_path, 'sql/properties.sql']), + scid=scid, _=gettext + ) + + 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 schema in the database. +It may have been removed by another user. +""")) + + data = res['rows'][0] + data = self._formatter(data, scid) + + # Privileges and Default privileges + self.format_request_acls(data) + + # Render sql from create & alter sql using properties & acl data + SQL = '' + SQL = render_template( + "/".join([self.template_path, 'sql/create.sql']), + _=gettext, data=data, conn=self.conn + ) + SQL += "\n" + SQL += render_template( + "/".join([self.template_path, 'sql/alter.sql']), + _=gettext, data=data, conn=self.conn + ) + + sql_header = """ +-- SCHEMA: {0} + +-- DROP SCHEMA {0}; + +""".format(data['name']) + + SQL = sql_header + SQL + + return ajax_response(response=SQL.strip("\n")) + + @check_precondition + def dependents(self, gid, sid, did, scid): + """ + This function gets the dependencies and returns an ajax response. + for the schema node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + """ + dependents_result = self.get_dependents(self.conn, scid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid): + """ + This function get the dependencies and return ajax response + for the schema node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + """ + dependencies_result = self.get_dependencies(self.conn, scid) + return ajax_response( + response=dependencies_result, + status=200 + ) + + @check_precondition + def children(self, **kwargs): + """Build a list of treeview nodes from the child nodes.""" + + SQL = render_template( + "/".join([self.template_path, 'sql/is_catalog.sql']), + scid=kwargs['scid'], _=gettext + ) + + 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 schema in the database. +It may have been removed by another user. +""")) + + data = res['rows'][0] + backend_support_keywords = kwargs.copy() + backend_support_keywords['is_catalog'] = data['is_catalog'] + backend_support_keywords['db_support'] = data['db_support'] + backend_support_keywords['schema_name'] = data['schema_name'] + + nodes = [] + for module in self.blueprint.submodules: + if isinstance(module, PGChildModule): + if self.manager is not None and \ + module.BackendSupported( + self.manager, **backend_support_keywords + ): + nodes.extend(module.get_nodes(**kwargs)) + else: + nodes.extend(module.get_nodes(**kwargs)) + + return make_json_response(data=nodes) + + + +class CatalogView(SchemaView): + """ + This class is responsible for generating routes for catalog schema node. + + Methods: + ------- + * __init__(**kwargs) + - Method is used to initialize the CatalogView and it's base view. + + * module_js() + - This property defines (if javascript) exists for this node. + Override this property for your own logic + + * create(gid, sid, did, scid) + - Raise an error - we can not create a catalog. + + * update(gid, sid, did, scid) + - This function will update the data for the selected catalog node + + * delete(self, gid, sid, scid): + - Raise an error - we can not delete a catalog. + + * get_sql(data, scid) + - This function will generate sql from model data + + """ + + node_type = catalog_blueprint.node_type + + def __init__(self, *args, **kwargs): + """ + Initialize the variables used by methods of SchemaView. + """ + + super(CatalogView, self).__init__(*args, **kwargs) + + self.template_initial = 'catalog' + + + def _formatter(self, data, scid=None): + + """ + Overriding _formatter, because - we won't show the Default + privileges with the catalog schema. + """ + + self._formatter_no_defacl(data, scid) + + return data + + def get_sql(self, gid, sid, data, scid=None): + """ + This function will generate sql from model data + """ + if scid is None: + return bad_request('Cannot create a catalog schema!') + + return super(CatalogView, self).get_sql(gid, sid, data, scid) + + @check_precondition + def sql(self, gid, sid, did, scid): + """ + This function will generate reverse engineered sql for schema object + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + """ + SQL = render_template( + "/".join([self.template_path, 'sql/properties.sql']), + scid=scid, _=gettext + ) + + 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 schema in the database. +It may have been removed by another user. +""")) + + old_data = res['rows'][0] + old_data = self._formatter(old_data, scid) + + # Privileges + self.format_request_acls(old_data, specific=['nspacl']) + + # Render sql from create & alter sql using properties & acl data + SQL = '' + SQL = render_template( + "/".join([self.template_path, 'sql/create.sql']), + _=gettext, data=old_data, conn=self.conn + ) + SQL += "\n" + SQL += render_template( + "/".join([self.template_path, 'sql/alter.sql']), + _=gettext, data=old_data, conn=self.conn + ) + + sql_header = """ +-- CATALOG: {0} + +-- DROP SCHEMA {0}; + +""".format(old_data['name']) + + SQL = sql_header + SQL + + return ajax_response(response=SQL.strip("\n")) + + +SchemaView.register_node_view(schema_blueprint) +CatalogView.register_node_view(catalog_blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/static/img/catalog.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/img/catalog.png new file mode 100644 index 000000000..7c0ef24ff Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/img/catalog.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/static/img/coll-catalog.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/img/coll-catalog.png new file mode 100644 index 000000000..caa6d3d40 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/img/coll-catalog.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/static/img/coll-schema.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/img/coll-schema.png new file mode 100644 index 000000000..9733882b7 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/img/coll-schema.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/static/img/schema.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/img/schema.png new file mode 100644 index 000000000..4dacf1856 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/static/img/schema.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/js/catalog.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/js/catalog.js new file mode 100644 index 000000000..e9e60bd16 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/js/catalog.js @@ -0,0 +1,111 @@ + define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', + 'pgadmin.browser', 'backform', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, Backform, alertify) { + // Extend the browser's collection class for SecurityLabel control + var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: null, + security_label: null + }, + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text', disabled: false + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text', disabled: false + }], + 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; + } + }); + + // Extend the browser's collection class for catalog collection + if (!pgBrowser.Nodes['coll-catalog']) { + var databases = pgAdmin.Browser.Nodes['coll-catalog'] = + pgAdmin.Browser.Collection.extend({ + node: 'catalog', + label: '{{ _('Catalogs') }}', + type: 'coll-catalog', + columns: ['name', 'oid', 'description'] + }); + }; + // Extend the browser's node class for catalog node + if (!pgBrowser.Nodes['catalog']) { + pgAdmin.Browser.Nodes['catalog'] = pgAdmin.Browser.Node.extend({ + parent_type: 'database', + type: 'catalog', + label: '{{ _('Catalog') }}', + hasSQL: true, + hasDepends: true, + Init: function() { + /* Avoid mulitple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + + }, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + namespaceowner: undefined, + nspacl: undefined, + description: undefined, + securitylabel: [] + }, + initialize: function(attrs, args) { + var isNew = (_.size(attrs) === 0); + + if (isNew) { + var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user; + + this.set({'namespaceowner': userInfo.name}, {silent: true}); + } + pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments); + }, + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', disabled: true + },{ + id: 'oid', label:'{{ _('Oid') }}', cell: 'string', + type: 'text', disabled: true + },{ + id: 'namespaceowner', label:'{{ _('Owner') }}', cell: 'string', + type: 'text', disabled: true + },{ + id: 'acl', label: '{{ _('Privileges') }}', type: 'text', + mode: ['properties'], disabled: true + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'seclabels', label: '{{ _('Security Labels') }}', + model: SecurityModel, editable: false, type: 'collection', + group: '{{ _('Security') }}', mode: ['edit', 'create'], + min_version: 90200, canAdd: true, + canEdit: false, canDelete: true, control: 'unique-col-collection' + } + ], + validate: function() { + return null; + } + }) + }); + + } + + return pgBrowser.Nodes['catalog']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/allowed_privs.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/allowed_privs.json new file mode 100644 index 000000000..261915431 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/allowed_privs.json @@ -0,0 +1,26 @@ +{# List of allowed privileges for PostgreSQL 9.1 #} +{# + Format for allowed privileges is: + "acl_col": { + "type": "name", + "acl": [...] + } +#} +{ + "nspacl": { + "type": "DATABASE", + "acl": ["c", "C", "T"] + }, + "deftblacl": { + "type": "TABLE", + "acl": ["r", "a", "w", "d", "D", "x", "t"] + }, + "defseqacl": { + "type": "SEQUENCE", + "acl": ["U", "r", "a"] + }, + "deffuncacl": { + "type": "FUNCTION", + "acl": ["X"] + } +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/acl.sql new file mode 100644 index 000000000..500212d5c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/acl.sql @@ -0,0 +1,23 @@ +{# Fetch privileges for schema #} +SELECT + 'nspacl' as deftype, COALESCE(gt.rolname, 'public') AS grantee, + g.rolname AS grantor, array_agg(b.privilege_type) AS privileges, + array_agg(b.is_grantable) AS grantable +FROM + (SELECT + (d).grantee AS grantee, (d).grantor AS grantor, + (d).is_grantable AS is_grantable, + CASE (d).privilege_type + WHEN 'CREATE' THEN 'C' + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN - ' || (d).privilege_type + END AS privilege_type + FROM + (SELECT aclexplode(nsp.nspacl) as d + FROM pg_namespace nsp + WHERE nsp.oid = {{ scid|qtLiteral }}::OID + ) a + ) b + LEFT JOIN pg_catalog.pg_roles g ON (b.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (b.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/alter.sql new file mode 100644 index 000000000..937bab7ba --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/alter.sql @@ -0,0 +1 @@ +{# We have nothing to alter in the catalog #} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/create.sql new file mode 100644 index 000000000..12dd10251 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/create.sql @@ -0,0 +1,17 @@ +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data %} +CREATE SCHEMA {{ conn|qtIdent(data.name) }} +{% if data.namespaceowner %} + AUTHORIZATION {{ conn|qtIdent(data.namespaceowner) }}; + +{% endif %} +{% if data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{% if data.nspacl %} +{% for priv in data.nspacl %} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %} +{% endif %} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/get_name.sql new file mode 100644 index 000000000..3c5187dcf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/get_name.sql @@ -0,0 +1 @@ +SELECT nsp.nspname FROM pg_namespace nsp WHERE nsp.oid = {{ scid|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/is_catalog.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/is_catalog.sql new file mode 100644 index 000000000..9386acbf4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/is_catalog.sql @@ -0,0 +1,9 @@ +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.nspname as schema_name, + {{ CATALOGS.LIST('nsp') }} AS is_catalog, + {{ CATALOGS.DB_SUPPORT('nsp') }} AS db_support +FROM + pg_catalog.pg_namespace nsp +WHERE + nsp.oid = {{ scid|qtLiteral }}::OID; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/nodes.sql new file mode 100644 index 000000000..fccaccfad --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/nodes.sql @@ -0,0 +1,16 @@ +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.oid, +{{ CATALOGS.LABELS('nsp', _) }}, + has_schema_privilege(nsp.oid, 'CREATE') as can_create, + has_schema_privilege(nsp.oid, 'USAGE') as has_usage +FROM + pg_namespace nsp +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% endif %} + ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/properties.sql new file mode 100644 index 000000000..5f338a0a2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/properties.sql @@ -0,0 +1,27 @@ +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +SELECT + 2 AS nsptyp, + nsp.nspname AS name, + nsp.oid, + array_to_string(nsp.nspacl::text[], ', ') as acl, + r.rolname AS namespaceowner, description, + has_schema_privilege(nsp.oid, 'CREATE') AS can_create, + CASE + WHEN nspname LIKE E'pg\\_%' THEN true + ELSE false END AS is_sys_object, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deftblacl' AND defaclnamespace = nsp.oid) AS tblacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'defseqacl' AND defaclnamespace = nsp.oid) AS seqacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deffuncacl' AND defaclnamespace = nsp.oid) AS funcacl +FROM + pg_namespace nsp + LEFT OUTER JOIN pg_description des ON + (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) + LEFT JOIN pg_roles r ON (r.oid = nsp.nspowner) +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% endif %} + ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/update.sql new file mode 100644 index 000000000..829bfa25f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.1_plus/sql/update.sql @@ -0,0 +1,30 @@ +{% import 'macros/security.macros' as SECLABLE %} +{% if data %} +{# ==== To update catalog comments ==== #} +{% if data.description and data.description != o_data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(o_data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{# ==== To update catalog securitylabel ==== #} +{# The SQL generated below will change Security Label #} +{% if data.seclabels and data.seclabels|length > 0 %} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.DROP(conn, 'SCHEMA', o_data.name, r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'SCHEMA', o_data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.APPLY(conn, 'SCHEMA', o_data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} + +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/allowed_privs.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/allowed_privs.json new file mode 100644 index 000000000..2ad1e739d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/allowed_privs.json @@ -0,0 +1,30 @@ +{# List of allowed privileges for PostgreSQL 9.2 or later #} +{# + Format for allowed privileges is: + "acl_col": { + "type": "name", + "acl": [...] + } +#} +{ + "nspacl": { + "type": "DATABASE", + "acl": ["c", "C", "T"] + }, + "deftblacl": { + "type": "TABLE", + "acl": ["r", "a", "w", "d", "D", "x", "t"] + }, + "defseqacl": { + "type": "SEQUENCE", + "acl": ["U", "r", "a"] + }, + "deffuncacl": { + "type": "FUNCTION", + "acl": ["X"] + }, + "deftypeacl": { + "type": "TYPE", + "acl": ["U"] + } +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/acl.sql new file mode 100644 index 000000000..500212d5c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/acl.sql @@ -0,0 +1,23 @@ +{# Fetch privileges for schema #} +SELECT + 'nspacl' as deftype, COALESCE(gt.rolname, 'public') AS grantee, + g.rolname AS grantor, array_agg(b.privilege_type) AS privileges, + array_agg(b.is_grantable) AS grantable +FROM + (SELECT + (d).grantee AS grantee, (d).grantor AS grantor, + (d).is_grantable AS is_grantable, + CASE (d).privilege_type + WHEN 'CREATE' THEN 'C' + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN - ' || (d).privilege_type + END AS privilege_type + FROM + (SELECT aclexplode(nsp.nspacl) as d + FROM pg_namespace nsp + WHERE nsp.oid = {{ scid|qtLiteral }}::OID + ) a + ) b + LEFT JOIN pg_catalog.pg_roles g ON (b.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (b.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/alter.sql new file mode 100644 index 000000000..937bab7ba --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/alter.sql @@ -0,0 +1 @@ +{# We have nothing to alter in the catalog #} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/create.sql new file mode 100644 index 000000000..12dd10251 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/create.sql @@ -0,0 +1,17 @@ +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data %} +CREATE SCHEMA {{ conn|qtIdent(data.name) }} +{% if data.namespaceowner %} + AUTHORIZATION {{ conn|qtIdent(data.namespaceowner) }}; + +{% endif %} +{% if data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{% if data.nspacl %} +{% for priv in data.nspacl %} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %} +{% endif %} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/get_name.sql new file mode 100644 index 000000000..3c5187dcf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/get_name.sql @@ -0,0 +1 @@ +SELECT nsp.nspname FROM pg_namespace nsp WHERE nsp.oid = {{ scid|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/is_catalog.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/is_catalog.sql new file mode 100644 index 000000000..9386acbf4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/is_catalog.sql @@ -0,0 +1,9 @@ +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.nspname as schema_name, + {{ CATALOGS.LIST('nsp') }} AS is_catalog, + {{ CATALOGS.DB_SUPPORT('nsp') }} AS db_support +FROM + pg_catalog.pg_namespace nsp +WHERE + nsp.oid = {{ scid|qtLiteral }}::OID; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/nodes.sql new file mode 100644 index 000000000..fccaccfad --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/nodes.sql @@ -0,0 +1,16 @@ +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.oid, +{{ CATALOGS.LABELS('nsp', _) }}, + has_schema_privilege(nsp.oid, 'CREATE') as can_create, + has_schema_privilege(nsp.oid, 'USAGE') as has_usage +FROM + pg_namespace nsp +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% endif %} + ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/properties.sql new file mode 100644 index 000000000..692e4a09b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/properties.sql @@ -0,0 +1,28 @@ +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +SELECT + 2 AS nsptyp, + nsp.nspname AS name, + nsp.oid, + array_to_string(nsp.nspacl::text[], ', ') as acl, + r.rolname AS namespaceowner, description, + has_schema_privilege(nsp.oid, 'CREATE') AS can_create, + CASE + WHEN nspname LIKE E'pg\\_%' THEN true + ELSE false END AS is_sys_object, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deftblacl' AND defaclnamespace = nsp.oid) AS tblacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'defseqacl' AND defaclnamespace = nsp.oid) AS seqacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deffuncacl' AND defaclnamespace = nsp.oid) AS funcacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deftypeacl' AND defaclnamespace = nsp.oid) AS typeacl +FROM + pg_namespace nsp + LEFT OUTER JOIN pg_description des ON + (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) + LEFT JOIN pg_roles r ON (r.oid = nsp.nspowner) +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% endif %} + ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/update.sql new file mode 100644 index 000000000..829bfa25f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/9.2_plus/sql/update.sql @@ -0,0 +1,30 @@ +{% import 'macros/security.macros' as SECLABLE %} +{% if data %} +{# ==== To update catalog comments ==== #} +{% if data.description and data.description != o_data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(o_data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{# ==== To update catalog securitylabel ==== #} +{# The SQL generated below will change Security Label #} +{% if data.seclabels and data.seclabels|length > 0 %} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.DROP(conn, 'SCHEMA', o_data.name, r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'SCHEMA', o_data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.APPLY(conn, 'SCHEMA', o_data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} + +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/macros/catalogs.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/macros/catalogs.sql new file mode 100644 index 000000000..5f9bf9532 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/macros/catalogs.sql @@ -0,0 +1,25 @@ +{% macro LIST(tbl) -%} + ({{ tbl }}.nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND + relnamespace = {{ tbl }}.oid LIMIT 1)) OR + ({{ tbl }}.nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND + relnamespace = {{ tbl }}.oid LIMIT 1)) OR + ({{ tbl }}.nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND + relnamespace = {{ tbl }}.oid LIMIT 1)) +{%- endmacro %} +{% macro LABELS(tbl, _) -%} + CASE {{ tbl }}.nspname + WHEN 'pg_catalog' THEN '{{ _( 'PostgreSQL Catalog' ) }} (pg_catalog)' + WHEN 'pgagent' THEN '{{ _( 'pgAgent Job Scheduler' ) }} (pgagent)' + WHEN 'information_schema' THEN '{{ _( 'ANSI' ) }} (information_schema)' + ELSE {{ tbl }}.nspname + END AS name +{%- endmacro %} +{% macro DB_SUPPORT(tbl) -%} + CASE + WHEN {{ tbl }}.nspname = ANY('{information_schema}') + THEN false + ELSE true END +{%- endmacro %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/allowed_privs.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/allowed_privs.json new file mode 100644 index 000000000..4b8fb1258 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/allowed_privs.json @@ -0,0 +1,26 @@ +{# List of allowed privileges for PPAS 9.1 #} +{# + Format for allowed privileges is: + "acl_col": { + "type": "name", + "acl": [...] + } +#} +{ + "nspacl": { + "type": "DATABASE", + "acl": ["c", "C", "T"] + }, + "deftblacl": { + "type": "TABLE", + "acl": ["r", "a", "w", "d", "D", "x", "t"] + }, + "defseqacl": { + "type": "SEQUENCE", + "acl": ["U", "r", "a"] + }, + "deffuncacl": { + "type": "FUNCTION", + "acl": ["X"] + } +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/acl.sql new file mode 100644 index 000000000..500212d5c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/acl.sql @@ -0,0 +1,23 @@ +{# Fetch privileges for schema #} +SELECT + 'nspacl' as deftype, COALESCE(gt.rolname, 'public') AS grantee, + g.rolname AS grantor, array_agg(b.privilege_type) AS privileges, + array_agg(b.is_grantable) AS grantable +FROM + (SELECT + (d).grantee AS grantee, (d).grantor AS grantor, + (d).is_grantable AS is_grantable, + CASE (d).privilege_type + WHEN 'CREATE' THEN 'C' + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN - ' || (d).privilege_type + END AS privilege_type + FROM + (SELECT aclexplode(nsp.nspacl) as d + FROM pg_namespace nsp + WHERE nsp.oid = {{ scid|qtLiteral }}::OID + ) a + ) b + LEFT JOIN pg_catalog.pg_roles g ON (b.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (b.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/alter.sql new file mode 100644 index 000000000..937bab7ba --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/alter.sql @@ -0,0 +1 @@ +{# We have nothing to alter in the catalog #} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/create.sql new file mode 100644 index 000000000..38c553228 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/create.sql @@ -0,0 +1,17 @@ +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data %} +CREATE SCHEMA {{ conn|qtIdent(data.name) }} +{% if data.namespaceowner %} + AUTHORIZATION {{ conn|qtIdent(data.namespaceowner) }}; + +{% endif %} +{% if data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{% if data.nspacl %} +{% for priv in data.nspacl %} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %} +{% endif %} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/get_name.sql new file mode 100644 index 000000000..3c5187dcf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/get_name.sql @@ -0,0 +1 @@ +SELECT nsp.nspname FROM pg_namespace nsp WHERE nsp.oid = {{ scid|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/is_catalog.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/is_catalog.sql new file mode 100644 index 000000000..992b18c3b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/is_catalog.sql @@ -0,0 +1,9 @@ +{% import 'catalog/ppas/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.nspname as schema_name, + {{ CATALOGS.LIST('nsp') }} AS is_catalog, + {{ CATALOGS.DB_SUPPORT('nsp') }} AS db_support +FROM + pg_catalog.pg_namespace nsp +WHERE + nsp.oid = {{ scid|qtLiteral }}::OID; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/nodes.sql new file mode 100644 index 000000000..f7bfb16bf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/nodes.sql @@ -0,0 +1,17 @@ +{% import 'catalog/ppas/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.oid, +{{ CATALOGS.LABELS('nsp', _) }}, + has_schema_privilege(nsp.oid, 'CREATE') as can_create, + has_schema_privilege(nsp.oid, 'USAGE') as has_usage +FROM + pg_namespace nsp +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% endif %} + nsp.nspparent = 0 AND + ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/properties.sql new file mode 100644 index 000000000..bd494c5c2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/properties.sql @@ -0,0 +1,31 @@ +{% import 'catalog/ppas/macros/catalogs.sql' as CATALOGS %} +SELECT + CASE + WHEN (nspname LIKE E'pg\\_temp\\_%') THEN 1 + WHEN (nspname LIKE E'pg\\_%') THEN 0 + ELSE 3 END AS nsptyp, + nsp.nspname AS name, + nsp.oid, + array_to_string(nsp.nspacl::text[], ', ') as acl, + r.rolname AS namespaceowner, description, + has_schema_privilege(nsp.oid, 'CREATE') AS can_create, + CASE + WHEN nspname LIKE E'pg\\_%' THEN true + ELSE false END AS is_sys_object, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deftblacl' AND defaclnamespace = nsp.oid) AS tblacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'defseqacl' AND defaclnamespace = nsp.oid) AS seqacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deffuncacl' AND defaclnamespace = nsp.oid) AS funcacl +FROM + pg_namespace nsp + LEFT OUTER JOIN pg_description des ON + (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) + LEFT JOIN pg_roles r ON (r.oid = nsp.nspowner) +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% endif %} + nsp.nspparent = 0 AND + ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/update.sql new file mode 100644 index 000000000..829bfa25f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.1_plus/sql/update.sql @@ -0,0 +1,30 @@ +{% import 'macros/security.macros' as SECLABLE %} +{% if data %} +{# ==== To update catalog comments ==== #} +{% if data.description and data.description != o_data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(o_data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{# ==== To update catalog securitylabel ==== #} +{# The SQL generated below will change Security Label #} +{% if data.seclabels and data.seclabels|length > 0 %} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.DROP(conn, 'SCHEMA', o_data.name, r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'SCHEMA', o_data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.APPLY(conn, 'SCHEMA', o_data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} + +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/allowed_privs.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/allowed_privs.json new file mode 100644 index 000000000..8e650f27b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/allowed_privs.json @@ -0,0 +1,30 @@ +{# List of allowed privileges for PPAS 9.2 or later #} +{# + Format for allowed privileges is: + "acl_col": { + "type": "name", + "acl": [...] + } +#} +{ + "nspacl": { + "type": "DATABASE", + "acl": ["c", "C", "T"] + }, + "deftblacl": { + "type": "TABLE", + "acl": ["r", "a", "w", "d", "D", "x", "t"] + }, + "defseqacl": { + "type": "SEQUENCE", + "acl": ["U", "r", "a"] + }, + "deffuncacl": { + "type": "FUNCTION", + "acl": ["X"] + }, + "deftypeacl": { + "type": "TYPE", + "acl": ["U"] + } +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/acl.sql new file mode 100644 index 000000000..500212d5c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/acl.sql @@ -0,0 +1,23 @@ +{# Fetch privileges for schema #} +SELECT + 'nspacl' as deftype, COALESCE(gt.rolname, 'public') AS grantee, + g.rolname AS grantor, array_agg(b.privilege_type) AS privileges, + array_agg(b.is_grantable) AS grantable +FROM + (SELECT + (d).grantee AS grantee, (d).grantor AS grantor, + (d).is_grantable AS is_grantable, + CASE (d).privilege_type + WHEN 'CREATE' THEN 'C' + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN - ' || (d).privilege_type + END AS privilege_type + FROM + (SELECT aclexplode(nsp.nspacl) as d + FROM pg_namespace nsp + WHERE nsp.oid = {{ scid|qtLiteral }}::OID + ) a + ) b + LEFT JOIN pg_catalog.pg_roles g ON (b.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (b.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/alter.sql new file mode 100644 index 000000000..937bab7ba --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/alter.sql @@ -0,0 +1 @@ +{# We have nothing to alter in the catalog #} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/create.sql new file mode 100644 index 000000000..38c553228 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/create.sql @@ -0,0 +1,17 @@ +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data %} +CREATE SCHEMA {{ conn|qtIdent(data.name) }} +{% if data.namespaceowner %} + AUTHORIZATION {{ conn|qtIdent(data.namespaceowner) }}; + +{% endif %} +{% if data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{% if data.nspacl %} +{% for priv in data.nspacl %} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %} +{% endif %} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/get_name.sql new file mode 100644 index 000000000..3c5187dcf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/get_name.sql @@ -0,0 +1 @@ +SELECT nsp.nspname FROM pg_namespace nsp WHERE nsp.oid = {{ scid|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/is_catalog.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/is_catalog.sql new file mode 100644 index 000000000..992b18c3b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/is_catalog.sql @@ -0,0 +1,9 @@ +{% import 'catalog/ppas/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.nspname as schema_name, + {{ CATALOGS.LIST('nsp') }} AS is_catalog, + {{ CATALOGS.DB_SUPPORT('nsp') }} AS db_support +FROM + pg_catalog.pg_namespace nsp +WHERE + nsp.oid = {{ scid|qtLiteral }}::OID; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/nodes.sql new file mode 100644 index 000000000..70a6dd0d5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/nodes.sql @@ -0,0 +1,17 @@ +{% import 'catalog/ppas/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.oid, +{{ CATALOGS.LABELS('nsp', _) }}, + has_schema_privilege(nsp.oid, 'CREATE') as can_create, + has_schema_privilege(nsp.oid, 'USAGE') as has_usage +FROM + pg_namespace nsp +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% endif %} + nsp.nspparent = 0 AND + ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/properties.sql new file mode 100644 index 000000000..9fe9404d1 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/properties.sql @@ -0,0 +1,29 @@ +{% import 'catalog/ppas/macros/catalogs.sql' as CATALOGS %} +SELECT + 2 END AS nsptyp, + nsp.nspname AS name, + nsp.oid, + array_to_string(nsp.nspacl::text[], ', ') as acl, + r.rolname AS namespaceowner, description, + has_schema_privilege(nsp.oid, 'CREATE') AS can_create, + CASE + WHEN nspname LIKE E'pg\\_%' THEN true + ELSE false END AS is_sys_object, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deftblacl' AND defaclnamespace = nsp.oid) AS tblacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'defseqacl' AND defaclnamespace = nsp.oid) AS seqacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deffuncacl' AND defaclnamespace = nsp.oid) AS funcacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deftypeacl' AND defaclnamespace = nsp.oid) AS typeacl +FROM + pg_namespace nsp + LEFT OUTER JOIN pg_description des ON + (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) + LEFT JOIN pg_roles r ON (r.oid = nsp.nspowner) +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% endif %} + nsp.nspparent = 0 AND + ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/update.sql new file mode 100644 index 000000000..829bfa25f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/9.2_plus/sql/update.sql @@ -0,0 +1,30 @@ +{% import 'macros/security.macros' as SECLABLE %} +{% if data %} +{# ==== To update catalog comments ==== #} +{% if data.description and data.description != o_data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(o_data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{# ==== To update catalog securitylabel ==== #} +{# The SQL generated below will change Security Label #} +{% if data.seclabels and data.seclabels|length > 0 %} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.DROP(conn, 'SCHEMA', o_data.name, r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'SCHEMA', o_data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.APPLY(conn, 'SCHEMA', o_data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} + +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/macros/catalogs.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/macros/catalogs.sql new file mode 100644 index 000000000..d9095bddd --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/macros/catalogs.sql @@ -0,0 +1,29 @@ +{% macro LIST(tbl) -%} + ({{ tbl }}.nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class + WHERE relname = 'pg_class' AND relnamespace = {{ tbl }}.oid LIMIT 1)) OR + ({{ tbl }}.nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class + WHERE relname = 'pga_job' AND relnamespace = {{ tbl }}.oid LIMIT 1)) OR + ({{ tbl }}.nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class + WHERE relname = 'tables' AND relnamespace = {{ tbl }}.oid LIMIT 1)) OR + ({{ tbl }}.nspname = 'dbo' OR {{ tbl }}.nspname = 'sys') OR + {{ tbl }}.nspname = 'dbms_job_procedure' AND EXISTS + (SELECT 1 FROM pg_proc + WHERE pronamespace = {{ tbl }}.oid and proname = 'run_job' LIMIT 1)) +{%- endmacro %} +{% macro LABELS(tbl, _) -%} + CASE {{ tbl }}.nspname + WHEN 'pg_catalog' THEN '{{ _( 'PostgreSQL Catalog' ) }} (pg_catalog)' + WHEN 'pgagent' THEN '{{ _( 'pgAgent Job Scheduler' ) }} (pgagent)' + WHEN 'information_schema' THEN '{{ _( 'ANSI' ) }} (information_schema)' + ELSE {{ tbl }}.nspname + END AS name +{%- endmacro %} +{% macro DB_SUPPORT(tbl) -%} + CASE + WHEN {{ tbl }}.nspname = ANY('{information_schema,sys,dbo}') + THEN false + ELSE true END +{%- endmacro %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/js/schema.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/js/schema.js new file mode 100644 index 000000000..6ff637f61 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/js/schema.js @@ -0,0 +1,234 @@ +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', + 'pgadmin.browser', 'backform', 'alertify', + 'pgadmin.browser.collection', + 'pgadmin.browser.server.privilege'], +function($, _, S, pgAdmin, pgBrowser, Backform, alertify) { + // Extend the browser's collection class for SecurityLabel control + var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: null, + security_label: null + }, + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text', disabled: false + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text', disabled: false + }], + 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; + } + }); + + // Extend the browser's collection class for schema collection + if (!pgBrowser.Nodes['coll-schema']) { + var databases = pgAdmin.Browser.Nodes['coll-schema'] = + pgAdmin.Browser.Collection.extend({ + node: 'schema', + label: '{{ _('Schemas') }}', + type: 'coll-schema', + columns: ['name', 'oid', 'description'] + }); + }; + // Extend the browser's node class for schema node + if (!pgBrowser.Nodes['schema']) { + pgAdmin.Browser.Nodes['schema'] = pgAdmin.Browser.Node.extend({ + parent_type: 'database', + type: 'schema', + label: '{{ _('Schema') }}', + hasSQL: true, + canDrop: true, + canDropCascade: true, + hasDepends: true, + Init: function() { + /* Avoid mulitple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_schema_on_coll', node: 'coll-schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Schema...') }}', + icon: 'wcTabIcon icon-schema', data: {action: 'create'} + },{ + name: 'create_schema', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Schema...') }}', + icon: 'wcTabIcon icon-schema', data: {action: 'create'} + },{ + name: 'create_schema', node: 'database', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Schema...') }}', + icon: 'wcTabIcon icon-schema', data: {action: 'create'} + } + ]); + }, + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + namespaceowner: undefined, + description: undefined, + is_system_obj: undefined, + }, + initialize: function(attrs, args) { + var isNew = (_.size(attrs) === 0); + + if (isNew) { + var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user; + + this.set({'namespaceowner': userInfo.name}, {silent: true}); + } + pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments); + }, + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text' + },{ + id: 'oid', label:'{{ _('Oid') }}', cell: 'string', + type: 'text', disabled: true, mode: ['edit', 'properties'] + },{ + id: 'namespaceowner', label:'{{ _('Owner') }}', cell: 'string', + type: 'text', control: 'node-list-by-name', node: 'role', + select2: { allowClear: false } + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'is_sys_object', label:'{{ _('System schema?') }}', + cell: 'switch', type: 'switch', mode: ['properties'], disabled: true, + options: { + 'onText': 'Yes', 'offText': 'No', + 'onColor': 'success', 'offColor': 'primary', + 'size': 'small' + } + },{ + id: 'acl', label: '{{ _('Privileges') }}', type: 'text', + mode: ['properties'], disabled: true + },{ + id: 'tblacl', label: '{{ _('Default TABLE Privileges') }}', type: 'text', + mode: ['properties'], disabled: true + },{ + id: 'seqacl', label: '{{ _('Default SEQUENCE Privileges') }}', type: 'text', + mode: ['properties'], disabled: true + },{ + id: 'funcacl', label: '{{ _('Default FUNCTION Privileges') }}', + type: 'text', mode: ['properties'], disabled: true + },{ + id: 'typeacl', label: '{{ _('Default TYPE Privileges') }}', type: 'text', + mode: ['properties'], disabled: true, min_version: 90200, + visible: function() { + return this.version_compatible; + } + },{ + id: 'nspacl', label: '{{ _('Privileges') }}', + model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend( + {privileges: ['C', 'U']}), uniqueCol : ['grantee', 'grantor'], + editable: false, type: 'collection', group: '{{ _('Security') }}', + mode: ['edit', 'create'], + canAdd: true, canDelete: true, control: 'unique-col-collection', + },{ + id: 'seclabels', label: '{{ _('Security Labels') }}', + model: SecurityModel, editable: false, type: 'collection', + group: '{{ _('Security') }}', mode: ['edit', 'create'], + min_version: 90200, canAdd: true, + canEdit: false, canDelete: true, control: 'unique-col-collection' + },{ + type: 'nested', control: 'tab', group: '{{ _('Default Privileges') }}', + mode: ['edit'], + schema:[{ + id: 'deftblacl', model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend( + {privileges: ['a', 'r', 'w', 'd', 'D', 'x', 't']}), + label: '{{ _('Default Privileges: Tables') }}', + editable: false, type: 'collection', group: '{{ _('Tables') }}', + mode: ['edit', 'create'], control: 'unique-col-collection', + canAdd: true, canDelete: true, uniqueCol : ['grantee', 'grantor'] + },{ + id: 'defseqacl', model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend( + {privileges: ['r', 'w', 'U']}), + label: '{{ _('Default Privileges: Sequences') }}', + editable: false, type: 'collection', group: '{{ _('Sequences') }}', + mode: ['edit', 'create'], control: 'unique-col-collection', + canAdd: true, canDelete: true, uniqueCol : ['grantee', 'grantor'] + },{ + id: 'deffuncacl', model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend( + {privileges: ['X']}), + label: '{{ _('Default Privileges: Functions') }}', + editable: false, type: 'collection', group: '{{ _('Functions') }}', + mode: ['edit', 'create'], control: 'unique-col-collection', + canAdd: true, canDelete: true, uniqueCol : ['grantee', 'grantor'] + },{ + id: 'deftypeacl', model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend( + {privileges: ['U']}), + label: '{{ _('Default Privileges: Types') }}', + editable: false, type: 'collection', group: '{{ _('Types') }}', + mode: ['edit', 'create'], control: 'unique-col-collection', + canAdd: true, canDelete: true, uniqueCol : ['grantee', 'grantor'], + min_version: 90200 + }] + } + ], + validate: function() { + var err = {}, + errmsg = null; + // Validation of mandatory fields + this.errorModel.clear(); + if (_.isUndefined(this.get('name')) || + _.isNull(this.get('name')) || + String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + errmsg = '{{ _('Name can not be empty.')}}'; + this.errorModel.set('name', errmsg); + return errmsg; + } else if (_.isUndefined(this.get('namespaceowner')) || + _.isNull(this.get('namespaceowner')) || + String(this.get('namespaceowner')).replace(/^\s+|\s+$/g, '') == '') { + errmsg = '{{ _('Owner can not be empty.')}}'; + this.errorModel.set('namespaceowner', errmsg); + return errmsg; + } + return null; + } + }), + // This function will checks whether we can allow user to + // drop object or not based on location within schema & catalog + canChildDrop: function(itemData, item) { + 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 collation + if (_.indexOf(['schema'], d._type) > -1) + return true; + + //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; + } + 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['schema']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/allowed_privs.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/allowed_privs.json new file mode 100644 index 000000000..a3dda2641 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/allowed_privs.json @@ -0,0 +1,26 @@ +{# List of allowed privileges for PostgreSQL 9.1 #} +{# + Format for allowed privileges is: + "acl_col": { + "type": "name", + "acl": [...] + } +#} +{ + "nspacl": { + "type": "SCHEMA", + "acl": ["C", "U"] + }, + "deftblacl": { + "type": "TABLE", + "acl": ["r", "a", "w", "d", "D", "x", "t"] + }, + "defseqacl": { + "type": "SEQUENCE", + "acl": ["U", "r", "a"] + }, + "deffuncacl": { + "type": "FUNCTION", + "acl": ["X"] + } +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/acl.sql new file mode 100644 index 000000000..500212d5c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/acl.sql @@ -0,0 +1,23 @@ +{# Fetch privileges for schema #} +SELECT + 'nspacl' as deftype, COALESCE(gt.rolname, 'public') AS grantee, + g.rolname AS grantor, array_agg(b.privilege_type) AS privileges, + array_agg(b.is_grantable) AS grantable +FROM + (SELECT + (d).grantee AS grantee, (d).grantor AS grantor, + (d).is_grantable AS is_grantable, + CASE (d).privilege_type + WHEN 'CREATE' THEN 'C' + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN - ' || (d).privilege_type + END AS privilege_type + FROM + (SELECT aclexplode(nsp.nspacl) as d + FROM pg_namespace nsp + WHERE nsp.oid = {{ scid|qtLiteral }}::OID + ) a + ) b + LEFT JOIN pg_catalog.pg_roles g ON (b.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (b.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/alter.sql new file mode 100644 index 000000000..f0d601d1c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/alter.sql @@ -0,0 +1,35 @@ +{% import 'macros/security.macros' as SECLABLE %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% import 'macros/default_privilege.macros' as DEFAULT_PRIVILEGE %} +{# Alter the comment/description #} +{% if data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{# ACL for the schema #} +{% if data.nspacl %} +{% for priv in data.nspacl %} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %} +{% endif %} + +{# Default privileges on tables #} +{% for defacl, type in [ + ('deftblacl', 'TABLES'), ('defseqacl', 'SEQUENCES'), + ('deffuncacl', 'FUNCTIONS')] +%} +{% if data[defacl] %}{% set acl = data[defacl] %} +{% for priv in data.deftblacl %} +{{ DEFAULT_PRIVILEGE.SET( + conn, 'SCHEMA', data.name, type, priv.grantee, + priv.without_grant, priv.with_grant + ) }}{% endfor %} +{% endif %} +{% endfor %} + +{# Security Labels on schema #} +{% if data.seclabels and data.seclabels|length > 0 %} +{% for r in data.seclabels %} +{{ SECLABLE.APPLY(conn, 'SCHEMA', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/create.sql new file mode 100644 index 000000000..d3598ac16 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/create.sql @@ -0,0 +1,7 @@ +{% if data.name %} +CREATE SCHEMA {{ conn|qtIdent(data.name) }}{% if data.namespaceowner %} + + AUTHORIZATION {{ conn|qtIdent(data.namespaceowner) }}{% endif %}; +{% else %} +{{ -- _('Incomplete definition') }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/defacl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/defacl.sql new file mode 100644 index 000000000..c11bea642 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/defacl.sql @@ -0,0 +1,39 @@ +SELECT + CASE (a.deftype) + WHEN 'r' THEN 'deftblacl' + WHEN 'S' THEN 'defseqacl' + WHEN 'f' THEN 'deffuncacl' + ELSE 'UNKNOWN - ' || a.deftype + END AS deftype, + COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(a.privilege_type) as privileges, array_agg(a.is_grantable) as grantable +FROM + (SELECT + (acl).grantee as grantee, (acl).grantor AS grantor, (acl).is_grantable AS is_grantable, + CASE (acl).privilege_type + WHEN 'CONNECT' THEN 'c' + WHEN 'CREATE' THEN 'C' + WHEN 'DELETE' THEN 'd' + WHEN 'EXECUTE' THEN 'X' + WHEN 'INSERT' THEN 'a' + WHEN 'REFERENCES' THEN 'x' + WHEN 'SELECT' THEN 'r' + WHEN 'TEMPORARY' THEN 'T' + WHEN 'TRIGGER' THEN 't' + WHEN 'TRUNCATE' THEN 'D' + WHEN 'UPDATE' THEN 'w' + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN' + END AS privilege_type, + defaclobjtype as deftype + FROM + (SELECT defaclobjtype, aclexplode(defaclacl) as acl + FROM + pg_namespace nsp + LEFT OUTER JOIN pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid) + WHERE + nsp.oid={{scid}}::int + ) d) a + LEFT JOIN pg_catalog.pg_roles g ON (a.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (a.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname, a.deftype +ORDER BY a.deftype; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/delete.sql new file mode 100644 index 000000000..74e9126da --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/delete.sql @@ -0,0 +1 @@ +DROP SCHEMA {{ conn|qtIdent(name) }} {% if cascade %}CASCADE{%endif%}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/get_name.sql new file mode 100644 index 000000000..3c5187dcf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/get_name.sql @@ -0,0 +1 @@ +SELECT nsp.nspname FROM pg_namespace nsp WHERE nsp.oid = {{ scid|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/is_catalog.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/is_catalog.sql new file mode 100644 index 000000000..9386acbf4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/is_catalog.sql @@ -0,0 +1,9 @@ +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.nspname as schema_name, + {{ CATALOGS.LIST('nsp') }} AS is_catalog, + {{ CATALOGS.DB_SUPPORT('nsp') }} AS db_support +FROM + pg_catalog.pg_namespace nsp +WHERE + nsp.oid = {{ scid|qtLiteral }}::OID; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/nodes.sql new file mode 100644 index 000000000..3140c0921 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/nodes.sql @@ -0,0 +1,21 @@ +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.oid, + nsp.nspname as name, + has_schema_privilege(nsp.oid, 'CREATE') as can_create, + has_schema_privilege(nsp.oid, 'USAGE') as has_usage +FROM + pg_namespace nsp +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% else %} + {% if show_sysobj %} + nspname NOT LIKE E'pg\\_temp\\_%' AND + nspname NOT LIKE E'pg\\_toast\\_temp\\_%' AND + {% endif %} + {% endif %} + NOT ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/oid.sql new file mode 100644 index 000000000..c5329b390 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/oid.sql @@ -0,0 +1 @@ +SELECT nsp.oid FROM pg_namespace nsp WHERE nsp.nspname = {{ schema|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/properties.sql new file mode 100644 index 000000000..7d61503a7 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/properties.sql @@ -0,0 +1,35 @@ +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +SELECT + CASE + WHEN (nspname LIKE E'pg\\_temp\\_%') THEN 1 + WHEN (nspname LIKE E'pg\\_%') THEN 0 + ELSE 3 END AS nsptyp, + nsp.nspname AS name, + nsp.oid, + array_to_string(nsp.nspacl::text[], ', ') as acl, + r.rolname AS namespaceowner, description, + has_schema_privilege(nsp.oid, 'CREATE') AS can_create, + CASE + WHEN nspname LIKE E'pg\\_%' THEN true + ELSE false END AS is_sys_object, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deftblacl' AND defaclnamespace = nsp.oid) AS tblacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'defseqacl' AND defaclnamespace = nsp.oid) AS seqacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deffuncacl' AND defaclnamespace = nsp.oid) AS funcacl +FROM + pg_namespace nsp + LEFT OUTER JOIN pg_description des ON + (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) + LEFT JOIN pg_roles r ON (r.oid = nsp.nspowner) +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% else %} + {% if show_sysobj %} + nspname NOT LIKE E'pg\\_temp\\_%' AND + nspname NOT LIKE E'pg\\_toast\\_temp\\_%' AND + {% endif %} + {% endif %} + NOT ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/update.sql new file mode 100644 index 000000000..990b8c086 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.1_plus/sql/update.sql @@ -0,0 +1,83 @@ +{% import 'macros/security.macros' as SECLABEL %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% import 'macros/default_privilege.macros' as DEFAULT_PRIVILEGE %} +{# Rename the schema #} +{% if data.name and data.name != o_data.name %} +ALTER SCHEMA {{ conn|qtIdent(o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; + +{% endif %} +{# Change the owner #} +{% if data.namespaceowner and data.namespaceowner != o_data.namespaceowner %} +ALTER SCHEMA {{ conn|qtIdent(data.name) }} + OWNER TO {{ conn|qtIdent(data.namespaceowner) }}; + +{% endif %} +{# Update the comments/description #} +{% if data.description and data.description != o_data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{# Change the privileges #} +{% if data.nspacl %} +{% if 'deleted' in data.nspacl %} +{% for priv in data.nspacl.deleted %} +{{ PRIVILEGE.RESETALL(conn, 'SCHEMA', priv.grantee, data.name) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.nspacl %} +{% for priv in data.nspacl.changed %} +{{ PRIVILEGE.RESETALL(conn, 'SCHEMA', priv.grantee, data.name) }} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.nspacl %} +{% for priv in data.nspacl.added %} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% endif %} +{# Change the default privileges #} +{% for defacl, type in [ + ('deftblacl', 'TABLES'), ('defseqacl', 'SEQUENCES'), + ('deffuncacl', 'FUNCTIONS')] +%} +{% if data[defacl] %}{% set acl = data[defacl] %} +{% if 'deleted' in acl %} +{% for priv in acl.deleted %} +{{ DEFAULT_PRIVILEGE.UNSET(conn, 'SCHEMA', data.name, type, priv.grantee) }} +{% endfor %} +{% endif %} +{% if 'changed' in acl %} +{% for priv in acl.changed %} +{{ DEFAULT_PRIVILEGE.UNSET(conn, 'SCHEMA', data.name, type, priv.grantee) }} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, type, priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in acl %} +{% for priv in acl.added %} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, type, priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% endif %} +{% endfor %} +{# Change the security labels #} +{% if data.seclabels and data.seclabels|length > 0 %} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABEL.DROP(conn, 'SCHEMA', data.name, r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABEL.APPLY(conn, 'SCHEMA', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABEL.APPLY(conn, 'SCHEMA', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/allowed_privs.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/allowed_privs.json new file mode 100644 index 000000000..61dfdfe91 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/allowed_privs.json @@ -0,0 +1,30 @@ +{# List of allowed privileges for PostgreSQL 9.2 or later #} +{# + Format for allowed privileges is: + "acl_col": { + "type": "name", + "acl": [...] + } +#} +{ + "nspacl": { + "type": "SCHEMA", + "acl": ["C", "U"] + }, + "deftblacl": { + "type": "TABLE", + "acl": ["r", "a", "w", "d", "D", "x", "t"] + }, + "defseqacl": { + "type": "SEQUENCE", + "acl": ["U", "r", "a"] + }, + "deffuncacl": { + "type": "FUNCTION", + "acl": ["X"] + }, + "deftypeacl": { + "type": "TYPE", + "acl": ["U"] + } +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/acl.sql new file mode 100644 index 000000000..500212d5c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/acl.sql @@ -0,0 +1,23 @@ +{# Fetch privileges for schema #} +SELECT + 'nspacl' as deftype, COALESCE(gt.rolname, 'public') AS grantee, + g.rolname AS grantor, array_agg(b.privilege_type) AS privileges, + array_agg(b.is_grantable) AS grantable +FROM + (SELECT + (d).grantee AS grantee, (d).grantor AS grantor, + (d).is_grantable AS is_grantable, + CASE (d).privilege_type + WHEN 'CREATE' THEN 'C' + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN - ' || (d).privilege_type + END AS privilege_type + FROM + (SELECT aclexplode(nsp.nspacl) as d + FROM pg_namespace nsp + WHERE nsp.oid = {{ scid|qtLiteral }}::OID + ) a + ) b + LEFT JOIN pg_catalog.pg_roles g ON (b.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (b.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/alter.sql new file mode 100644 index 000000000..6ca29228e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/alter.sql @@ -0,0 +1,35 @@ +{% import 'macros/security.macros' as SECLABEL %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% import 'macros/default_privilege.macros' as DEFAULT_PRIVILEGE %} +{# Alter the comment/description #} +{% if data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{# ACL for the schema #} +{% if data.nspacl %} +{% for priv in data.nspacl %} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %} +{% endif %} + +{# Default privileges on tables #} +{% for defacl, type in [ + ('deftblacl', 'TABLES'), ('defseqacl', 'SEQUENCES'), + ('deffuncacl', 'FUNCTIONS'), ('deftypeacl', 'TYPES')] +%} +{% if data[defacl] %}{% set acl = data[defacl] %} +{% for priv in data.deftblacl %} +{{ DEFAULT_PRIVILEGE.SET( + conn, 'SCHEMA', data.name, type, priv.grantee, + priv.without_grant, priv.with_grant + ) }}{% endfor %} +{% endif %} +{% endfor %} + +{# Security Labels on schema #} +{% if data.seclabels and data.seclabels|length > 0 %} +{% for r in data.seclabels %} +{{ SECLABEL.APPLY(conn, 'SCHEMA', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/create.sql new file mode 100644 index 000000000..d3598ac16 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/create.sql @@ -0,0 +1,7 @@ +{% if data.name %} +CREATE SCHEMA {{ conn|qtIdent(data.name) }}{% if data.namespaceowner %} + + AUTHORIZATION {{ conn|qtIdent(data.namespaceowner) }}{% endif %}; +{% else %} +{{ -- _('Incomplete definition') }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/defacl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/defacl.sql new file mode 100644 index 000000000..9c37a1d48 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/defacl.sql @@ -0,0 +1,40 @@ +SELECT + CASE (a.deftype) + WHEN 'r' THEN 'deftblacl' + WHEN 'S' THEN 'defseqacl' + WHEN 'f' THEN 'deffuncacl' + WHEN 'T' THEN 'deftypeacl' + ELSE 'UNKNOWN - ' || a.deftype + END AS deftype, + COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(a.privilege_type) as privileges, array_agg(a.is_grantable) as grantable +FROM + (SELECT + (acl).grantee as grantee, (acl).grantor AS grantor, (acl).is_grantable AS is_grantable, + CASE (acl).privilege_type + WHEN 'CONNECT' THEN 'c' + WHEN 'CREATE' THEN 'C' + WHEN 'DELETE' THEN 'd' + WHEN 'EXECUTE' THEN 'X' + WHEN 'INSERT' THEN 'a' + WHEN 'REFERENCES' THEN 'x' + WHEN 'SELECT' THEN 'r' + WHEN 'TEMPORARY' THEN 'T' + WHEN 'TRIGGER' THEN 't' + WHEN 'TRUNCATE' THEN 'D' + WHEN 'UPDATE' THEN 'w' + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN - ' || (acl).privilege_type + END AS privilege_type, + defaclobjtype as deftype + FROM + (SELECT defaclobjtype, aclexplode(defaclacl) as acl + FROM + pg_namespace nsp + LEFT OUTER JOIN pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid) + WHERE + nsp.oid={{scid}}::int + ) d) a + LEFT JOIN pg_catalog.pg_roles g ON (a.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (a.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname, a.deftype +ORDER BY a.deftype; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/delete.sql new file mode 100644 index 000000000..74e9126da --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/delete.sql @@ -0,0 +1 @@ +DROP SCHEMA {{ conn|qtIdent(name) }} {% if cascade %}CASCADE{%endif%}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/get_name.sql new file mode 100644 index 000000000..3c5187dcf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/get_name.sql @@ -0,0 +1 @@ +SELECT nsp.nspname FROM pg_namespace nsp WHERE nsp.oid = {{ scid|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/is_catalog.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/is_catalog.sql new file mode 100644 index 000000000..9386acbf4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/is_catalog.sql @@ -0,0 +1,9 @@ +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.nspname as schema_name, + {{ CATALOGS.LIST('nsp') }} AS is_catalog, + {{ CATALOGS.DB_SUPPORT('nsp') }} AS db_support +FROM + pg_catalog.pg_namespace nsp +WHERE + nsp.oid = {{ scid|qtLiteral }}::OID; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/nodes.sql new file mode 100644 index 000000000..3140c0921 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/nodes.sql @@ -0,0 +1,21 @@ +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.oid, + nsp.nspname as name, + has_schema_privilege(nsp.oid, 'CREATE') as can_create, + has_schema_privilege(nsp.oid, 'USAGE') as has_usage +FROM + pg_namespace nsp +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% else %} + {% if show_sysobj %} + nspname NOT LIKE E'pg\\_temp\\_%' AND + nspname NOT LIKE E'pg\\_toast\\_temp\\_%' AND + {% endif %} + {% endif %} + NOT ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/oid.sql new file mode 100644 index 000000000..c5329b390 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/oid.sql @@ -0,0 +1 @@ +SELECT nsp.oid FROM pg_namespace nsp WHERE nsp.nspname = {{ schema|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/properties.sql new file mode 100644 index 000000000..56cd4f7ad --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/properties.sql @@ -0,0 +1,36 @@ +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +SELECT + CASE + WHEN (nspname LIKE E'pg\\_temp\\_%') THEN 1 + WHEN (nspname LIKE E'pg\\_%') THEN 0 + ELSE 3 END AS nsptyp, + nsp.nspname AS name, + nsp.oid, + array_to_string(nsp.nspacl::text[], ', ') as acl, + r.rolname AS namespaceowner, description, + has_schema_privilege(nsp.oid, 'CREATE') AS can_create, + CASE + WHEN nspname LIKE E'pg\\_%' THEN true + ELSE false END AS is_sys_object, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deftblacl' AND defaclnamespace = nsp.oid) AS tblacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'defseqacl' AND defaclnamespace = nsp.oid) AS seqacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deffuncacl' AND defaclnamespace = nsp.oid) AS funcacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deftypeacl' AND defaclnamespace = nsp.oid) AS typeacl +FROM + pg_namespace nsp + LEFT OUTER JOIN pg_description des ON + (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) + LEFT JOIN pg_roles r ON (r.oid = nsp.nspowner) +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% else %} + {% if show_sysobj %} + nspname NOT LIKE E'pg\\_temp\\_%' AND + nspname NOT LIKE E'pg\\_toast\\_temp\\_%' AND + {% endif %} + {% endif %} + NOT ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/update.sql new file mode 100644 index 000000000..1e8066185 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/pg/9.2_plus/sql/update.sql @@ -0,0 +1,83 @@ +{% import 'macros/security.macros' as SECLABEL %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% import 'macros/default_privilege.macros' as DEFAULT_PRIVILEGE %} +{# Rename the schema #} +{% if data.name and data.name != o_data.name %} +ALTER SCHEMA {{ conn|qtIdent(o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; + +{% endif %} +{# Change the owner #} +{% if data.namespaceowner and data.namespaceowner != o_data.namespaceowner %} +ALTER SCHEMA {{ conn|qtIdent(data.name) }} + OWNER TO {{ conn|qtIdent(data.namespaceowner) }}; + +{% endif %} +{# Update the comments/description #} +{% if data.description and data.description != o_data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{# Change the privileges #} +{% if data.nspacl %} +{% if 'deleted' in data.nspacl %} +{% for priv in data.nspacl.deleted %} +{{ PRIVILEGE.RESETALL(conn, 'SCHEMA', priv.grantee, data.name) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.nspacl %} +{% for priv in data.nspacl.changed %} +{{ PRIVILEGE.RESETALL(conn, 'SCHEMA', priv.grantee, data.name) }} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.nspacl %} +{% for priv in data.nspacl.added %} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% endif %} +{# Change the default privileges #} +{% for defacl, type in [ + ('deftblacl', 'TABLES'), ('defseqacl', 'SEQUENCES'), + ('deffuncacl', 'FUNCTIONS'), ('deftypeacl', 'TYPES')] +%} +{% if data[defacl] %}{% set acl = data[defacl] %} +{% if 'deleted' in acl %} +{% for priv in acl.deleted %} +{{ DEFAULT_PRIVILEGE.UNSET(conn, 'SCHEMA', data.name, type, priv.grantee) }} +{% endfor %} +{% endif %} +{% if 'changed' in acl %} +{% for priv in acl.changed %} +{{ DEFAULT_PRIVILEGE.UNSET(conn, 'SCHEMA', data.name, type, priv.grantee) }} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, type, priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in acl %} +{% for priv in acl.added %} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, type, priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% endif %} +{% endfor %} +{# Change the security labels #} +{% if data.seclabels and data.seclabels|length > 0 %} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABEL.DROP(conn, 'SCHEMA', data.name, r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABEL.APPLY(conn, 'SCHEMA', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABEL.APPLY(conn, 'SCHEMA', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/allowed_privs.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/allowed_privs.json new file mode 100644 index 000000000..f7815078e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/allowed_privs.json @@ -0,0 +1,26 @@ +{# List of allowed privileges for PPAS 9.1 #} +{# + Format for allowed privileges is: + "acl_col": { + "type": "name", + "acl": [...] + } +#} +{ + "nspacl": { + "type": "SCHEMA", + "acl": ["C", "U"] + }, + "deftblacl": { + "type": "TABLE", + "acl": ["r", "a", "w", "d", "D", "x", "t"] + }, + "defseqacl": { + "type": "SEQUENCE", + "acl": ["U", "r", "a"] + }, + "deffuncacl": { + "type": "FUNCTION", + "acl": ["X"] + } +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/acl.sql new file mode 100644 index 000000000..500212d5c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/acl.sql @@ -0,0 +1,23 @@ +{# Fetch privileges for schema #} +SELECT + 'nspacl' as deftype, COALESCE(gt.rolname, 'public') AS grantee, + g.rolname AS grantor, array_agg(b.privilege_type) AS privileges, + array_agg(b.is_grantable) AS grantable +FROM + (SELECT + (d).grantee AS grantee, (d).grantor AS grantor, + (d).is_grantable AS is_grantable, + CASE (d).privilege_type + WHEN 'CREATE' THEN 'C' + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN - ' || (d).privilege_type + END AS privilege_type + FROM + (SELECT aclexplode(nsp.nspacl) as d + FROM pg_namespace nsp + WHERE nsp.oid = {{ scid|qtLiteral }}::OID + ) a + ) b + LEFT JOIN pg_catalog.pg_roles g ON (b.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (b.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/alter.sql new file mode 100644 index 000000000..8d527d255 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/alter.sql @@ -0,0 +1,35 @@ +{% import 'macros/security.macros' as SECLABEL %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% import 'macros/default_privilege.macros' as DEFAULT_PRIVILEGE %} +{# Alter the comment/description #} +{% if data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{# ACL for the schema #} +{% if data.nspacl %} +{% for priv in data.nspacl %} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %} +{% endif %} + +{# Default privileges on tables #} +{% for defacl, type in [ + ('deftblacl', 'TABLES'), ('defseqacl', 'SEQUENCES'), + ('deffuncacl', 'FUNCTIONS')] +%} +{% if data[defacl] %}{% set acl = data[defacl] %} +{% for priv in data.deftblacl %} +{{ DEFAULT_PRIVILEGE.SET( + conn, 'SCHEMA', data.name, type, priv.grantee, + priv.without_grant, priv.with_grant + ) }}{% endfor %} +{% endif %} +{% endfor %} + +{# Security Labels on schema #} +{% if data.seclabels and data.seclabels|length > 0 %} +{% for r in data.seclabels %} +{{ SECLABEL.APPLY(conn, 'SCHEMA', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/create.sql new file mode 100644 index 000000000..d3598ac16 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/create.sql @@ -0,0 +1,7 @@ +{% if data.name %} +CREATE SCHEMA {{ conn|qtIdent(data.name) }}{% if data.namespaceowner %} + + AUTHORIZATION {{ conn|qtIdent(data.namespaceowner) }}{% endif %}; +{% else %} +{{ -- _('Incomplete definition') }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/defacl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/defacl.sql new file mode 100644 index 000000000..c2c10a3c3 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/defacl.sql @@ -0,0 +1,39 @@ +SELECT + CASE (a.deftype) + WHEN 'r' THEN 'deftblacl' + WHEN 'S' THEN 'defseqacl' + WHEN 'f' THEN 'deffuncacl' + ELSE 'UNKNOWN - ' || a.deftype + END AS deftype, + COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(a.privilege_type) as privileges, array_agg(a.is_grantable) as grantable +FROM + (SELECT + (acl).grantee as grantee, (acl).grantor AS grantor, (acl).is_grantable AS is_grantable, + CASE (acl).privilege_type + WHEN 'CONNECT' THEN 'c' + WHEN 'CREATE' THEN 'C' + WHEN 'DELETE' THEN 'd' + WHEN 'EXECUTE' THEN 'X' + WHEN 'INSERT' THEN 'a' + WHEN 'REFERENCES' THEN 'x' + WHEN 'SELECT' THEN 'r' + WHEN 'TEMPORARY' THEN 'T' + WHEN 'TRIGGER' THEN 't' + WHEN 'TRUNCATE' THEN 'D' + WHEN 'UPDATE' THEN 'w' + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN - ' || (acl).privilege_type + END AS privilege_type, + defaclobjtype as deftype + FROM + (SELECT defaclobjtype, aclexplode(defaclacl) as acl + FROM + pg_namespace nsp + LEFT OUTER JOIN pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid) + WHERE + nsp.oid={{scid}}::int + ) d) a + LEFT JOIN pg_catalog.pg_roles g ON (a.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (a.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname, a.deftype +ORDER BY a.deftype; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/delete.sql new file mode 100644 index 000000000..74e9126da --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/delete.sql @@ -0,0 +1 @@ +DROP SCHEMA {{ conn|qtIdent(name) }} {% if cascade %}CASCADE{%endif%}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/get_name.sql new file mode 100644 index 000000000..3c5187dcf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/get_name.sql @@ -0,0 +1 @@ +SELECT nsp.nspname FROM pg_namespace nsp WHERE nsp.oid = {{ scid|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/is_catalog.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/is_catalog.sql new file mode 100644 index 000000000..992b18c3b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/is_catalog.sql @@ -0,0 +1,9 @@ +{% import 'catalog/ppas/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.nspname as schema_name, + {{ CATALOGS.LIST('nsp') }} AS is_catalog, + {{ CATALOGS.DB_SUPPORT('nsp') }} AS db_support +FROM + pg_catalog.pg_namespace nsp +WHERE + nsp.oid = {{ scid|qtLiteral }}::OID; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/nodes.sql new file mode 100644 index 000000000..7823f421e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/nodes.sql @@ -0,0 +1,21 @@ +{% import 'catalog/ppas/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.oid, + nsp.nspname as name, + has_schema_privilege(nsp.oid, 'CREATE') as can_create, + has_schema_privilege(nsp.oid, 'USAGE') as has_usage +FROM + pg_namespace nsp +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% else %} + {% if show_sysobj %} + nspname NOT LIKE E'pg\\_temp\\_%' AND + nspname NOT LIKE E'pg\\_toast\\_temp\\_%' AND + {% endif %} + {% endif %} + NOT ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/oid.sql new file mode 100644 index 000000000..c5329b390 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/oid.sql @@ -0,0 +1 @@ +SELECT nsp.oid FROM pg_namespace nsp WHERE nsp.nspname = {{ schema|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/properties.sql new file mode 100644 index 000000000..17f268a3b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/properties.sql @@ -0,0 +1,36 @@ +{% import 'catalog/ppas/macros/catalogs.sql' as CATALOGS %} +SELECT + CASE + WHEN (nspname LIKE E'pg\\_temp\\_%') THEN 1 + WHEN (nspname LIKE E'pg\\_%') THEN 0 + ELSE 3 END AS nsptyp, + nsp.nspname AS name, + nsp.oid, + array_to_string(nsp.nspacl::text[], ', ') as acl, + r.rolname AS namespaceowner, description, + has_schema_privilege(nsp.oid, 'CREATE') AS can_create, + CASE + WHEN nspname LIKE E'pg\\_%' THEN true + ELSE false END AS is_sys_object, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deftblacl' AND defaclnamespace = nsp.oid) AS tblacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'defseqacl' AND defaclnamespace = nsp.oid) AS seqacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deffuncacl' AND defaclnamespace = nsp.oid) AS funcacl +FROM + pg_namespace nsp + LEFT OUTER JOIN pg_description des ON + (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) + LEFT JOIN pg_roles r ON (r.oid = nsp.nspowner) +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% else %} + {% if show_sysobj %} + nspname NOT LIKE E'pg\\_temp\\_%' AND + nspname NOT LIKE E'pg\\_toast\\_temp\\_%' AND + {% endif %} + {% endif %} + nsp.nspparent = 0 AND + NOT ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/update.sql new file mode 100644 index 000000000..64f8e808e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.1_plus/sql/update.sql @@ -0,0 +1,125 @@ +{% import 'macros/security.macros' as SECLABLE %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% import 'macros/default_privilege.macros' as DEFAULT_PRIVILEGE %} +{% if data %} +{### To update SCHEMA name ###} +{% if data.name and data.name != o_data.name %} +ALTER SCHEMA {{ conn|qtIdent(o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; + +{% endif %} +{### To update SCHEMA owner ###} +{% if data.namespaceowner and data.namespaceowner != o_data.namespaceowner %} +ALTER SCHEMA {{ conn|qtIdent(data.name) }} + OWNER TO {{ conn|qtIdent(data.namespaceowner) }}; + +{% endif %} +{### To update SCHEMA comments ###} +{% if data.description and data.description != o_data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{### Change the security labels ###} +{# The SQL generated below will change Security Label #} +{% if data.seclabels and data.seclabels|length > 0 %} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.DROP(conn, 'SCHEMA', data.name, r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'SCHEMA', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.APPLY(conn, 'SCHEMA', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} + +{% endif %} +{### Change the privileges ###} +{% if data.nspacl %} +{% if 'deleted' in data.nspacl %} +{% for priv in data.nspacl.deleted %} +{{ PRIVILEGE.RESETALL(conn, 'SCHEMA', priv.grantee, data.name) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.nspacl %} +{% for priv in data.nspacl.changed %} +{{ PRIVILEGE.RESETALL(conn, 'SCHEMA', priv.grantee, data.name) }} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.nspacl %} +{% for priv in data.nspacl.added %} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} + +{% endif %} +{### To change default privileges for tables ###} +{% if data.deftblacl %} +{% if 'deleted' in data.deftblacl %} +{% for priv in data.deftblacl.deleted %} +{{ DEFAULT_PRIVILEGE.UNSET(conn, 'SCHEMA', data.name, 'TABLES', priv.grantee) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.deftblacl %} +{% for priv in data.deftblacl.changed %} +{{ DEFAULT_PRIVILEGE.UNSET(conn, 'SCHEMA', data.name, 'TABLES', priv.grantee) }} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, 'TABLES', priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.deftblacl %} +{% for priv in data.deftblacl.added %} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, 'TABLES', priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} + +{% endif %} +{### To change default privileges for sequences ###} +{% if data.defseqacl %} +{% if 'deleted' in data.defseqacl %} +{% for priv in data.defseqacl.deleted %} +{{ DEFAULT_PRIVILEGE.UNSET(conn,'SCHEMA', data.name, 'SEQUENCES', priv.grantee) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.defseqacl %} +{% for priv in data.defseqacl.changed %} +{{ DEFAULT_PRIVILEGE.UNSET(conn,'SCHEMA', data.name, 'SEQUENCES', priv.grantee) }} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, 'SEQUENCES', priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.defseqacl %} +{% for priv in data.defseqacl.added %} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, 'SEQUENCES', priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} + +{% endif %} +{### To change default privileges for functions ###} +{% if data.deffuncacl %} +{% if 'deleted' in data.deffuncacl %} +{% for priv in data.deffuncacl.deleted %} +{{ DEFAULT_PRIVILEGE.UNSET(conn,'SCHEMA', data.name, 'FUNCTIONS', priv.grantee) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.deffuncacl %} +{% for priv in data.deffuncacl.changed %} +{{ DEFAULT_PRIVILEGE.UNSET(conn,'SCHEMA', data.name, 'FUNCTIONS', priv.grantee) }} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, 'FUNCTIONS', priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.deffuncacl %} +{% for priv in data.deffuncacl.added %} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, 'FUNCTIONS', priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} + +{% endif %} +{### End main if data ###} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/allowed_privs.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/allowed_privs.json new file mode 100644 index 000000000..576f75953 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/allowed_privs.json @@ -0,0 +1,30 @@ +{# List of allowed privileges for PPAS 9.2 or later #} +{# + Format for allowed privileges is: + "acl_col": { + "type": "name", + "acl": [...] + } +#} +{ + "nspacl": { + "type": "SCHEMA", + "acl": ["C", "U"] + }, + "deftblacl": { + "type": "TABLE", + "acl": ["r", "a", "w", "d", "D", "x", "t"] + }, + "defseqacl": { + "type": "SEQUENCE", + "acl": ["U", "r", "a"] + }, + "deffuncacl": { + "type": "FUNCTION", + "acl": ["X"] + }, + "deftypeacl": { + "type": "TYPE", + "acl": ["U"] + } +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/acl.sql new file mode 100644 index 000000000..500212d5c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/acl.sql @@ -0,0 +1,23 @@ +{# Fetch privileges for schema #} +SELECT + 'nspacl' as deftype, COALESCE(gt.rolname, 'public') AS grantee, + g.rolname AS grantor, array_agg(b.privilege_type) AS privileges, + array_agg(b.is_grantable) AS grantable +FROM + (SELECT + (d).grantee AS grantee, (d).grantor AS grantor, + (d).is_grantable AS is_grantable, + CASE (d).privilege_type + WHEN 'CREATE' THEN 'C' + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN - ' || (d).privilege_type + END AS privilege_type + FROM + (SELECT aclexplode(nsp.nspacl) as d + FROM pg_namespace nsp + WHERE nsp.oid = {{ scid|qtLiteral }}::OID + ) a + ) b + LEFT JOIN pg_catalog.pg_roles g ON (b.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (b.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/alter.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/alter.sql new file mode 100644 index 000000000..6ca29228e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/alter.sql @@ -0,0 +1,35 @@ +{% import 'macros/security.macros' as SECLABEL %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% import 'macros/default_privilege.macros' as DEFAULT_PRIVILEGE %} +{# Alter the comment/description #} +{% if data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{# ACL for the schema #} +{% if data.nspacl %} +{% for priv in data.nspacl %} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %} +{% endif %} + +{# Default privileges on tables #} +{% for defacl, type in [ + ('deftblacl', 'TABLES'), ('defseqacl', 'SEQUENCES'), + ('deffuncacl', 'FUNCTIONS'), ('deftypeacl', 'TYPES')] +%} +{% if data[defacl] %}{% set acl = data[defacl] %} +{% for priv in data.deftblacl %} +{{ DEFAULT_PRIVILEGE.SET( + conn, 'SCHEMA', data.name, type, priv.grantee, + priv.without_grant, priv.with_grant + ) }}{% endfor %} +{% endif %} +{% endfor %} + +{# Security Labels on schema #} +{% if data.seclabels and data.seclabels|length > 0 %} +{% for r in data.seclabels %} +{{ SECLABEL.APPLY(conn, 'SCHEMA', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/create.sql new file mode 100644 index 000000000..d3598ac16 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/create.sql @@ -0,0 +1,7 @@ +{% if data.name %} +CREATE SCHEMA {{ conn|qtIdent(data.name) }}{% if data.namespaceowner %} + + AUTHORIZATION {{ conn|qtIdent(data.namespaceowner) }}{% endif %}; +{% else %} +{{ -- _('Incomplete definition') }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/defacl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/defacl.sql new file mode 100644 index 000000000..9c37a1d48 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/defacl.sql @@ -0,0 +1,40 @@ +SELECT + CASE (a.deftype) + WHEN 'r' THEN 'deftblacl' + WHEN 'S' THEN 'defseqacl' + WHEN 'f' THEN 'deffuncacl' + WHEN 'T' THEN 'deftypeacl' + ELSE 'UNKNOWN - ' || a.deftype + END AS deftype, + COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(a.privilege_type) as privileges, array_agg(a.is_grantable) as grantable +FROM + (SELECT + (acl).grantee as grantee, (acl).grantor AS grantor, (acl).is_grantable AS is_grantable, + CASE (acl).privilege_type + WHEN 'CONNECT' THEN 'c' + WHEN 'CREATE' THEN 'C' + WHEN 'DELETE' THEN 'd' + WHEN 'EXECUTE' THEN 'X' + WHEN 'INSERT' THEN 'a' + WHEN 'REFERENCES' THEN 'x' + WHEN 'SELECT' THEN 'r' + WHEN 'TEMPORARY' THEN 'T' + WHEN 'TRIGGER' THEN 't' + WHEN 'TRUNCATE' THEN 'D' + WHEN 'UPDATE' THEN 'w' + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN - ' || (acl).privilege_type + END AS privilege_type, + defaclobjtype as deftype + FROM + (SELECT defaclobjtype, aclexplode(defaclacl) as acl + FROM + pg_namespace nsp + LEFT OUTER JOIN pg_catalog.pg_default_acl dacl ON (dacl.defaclnamespace = nsp.oid) + WHERE + nsp.oid={{scid}}::int + ) d) a + LEFT JOIN pg_catalog.pg_roles g ON (a.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (a.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname, a.deftype +ORDER BY a.deftype; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/delete.sql new file mode 100644 index 000000000..74e9126da --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/delete.sql @@ -0,0 +1 @@ +DROP SCHEMA {{ conn|qtIdent(name) }} {% if cascade %}CASCADE{%endif%}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/get_name.sql new file mode 100644 index 000000000..3c5187dcf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/get_name.sql @@ -0,0 +1 @@ +SELECT nsp.nspname FROM pg_namespace nsp WHERE nsp.oid = {{ scid|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/is_catalog.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/is_catalog.sql new file mode 100644 index 000000000..992b18c3b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/is_catalog.sql @@ -0,0 +1,9 @@ +{% import 'catalog/ppas/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.nspname as schema_name, + {{ CATALOGS.LIST('nsp') }} AS is_catalog, + {{ CATALOGS.DB_SUPPORT('nsp') }} AS db_support +FROM + pg_catalog.pg_namespace nsp +WHERE + nsp.oid = {{ scid|qtLiteral }}::OID; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/nodes.sql new file mode 100644 index 000000000..7823f421e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/nodes.sql @@ -0,0 +1,21 @@ +{% import 'catalog/ppas/macros/catalogs.sql' as CATALOGS %} +SELECT + nsp.oid, + nsp.nspname as name, + has_schema_privilege(nsp.oid, 'CREATE') as can_create, + has_schema_privilege(nsp.oid, 'USAGE') as has_usage +FROM + pg_namespace nsp +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% else %} + {% if show_sysobj %} + nspname NOT LIKE E'pg\\_temp\\_%' AND + nspname NOT LIKE E'pg\\_toast\\_temp\\_%' AND + {% endif %} + {% endif %} + NOT ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/oid.sql new file mode 100644 index 000000000..c5329b390 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/oid.sql @@ -0,0 +1 @@ +SELECT nsp.oid FROM pg_namespace nsp WHERE nsp.nspname = {{ schema|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/properties.sql new file mode 100644 index 000000000..535498328 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/properties.sql @@ -0,0 +1,37 @@ +{% import 'catalog/ppas/macros/catalogs.sql' as CATALOGS %} +SELECT + CASE + WHEN (nspname LIKE E'pg\\_temp\\_%') THEN 1 + WHEN (nspname LIKE E'pg\\_%') THEN 0 + ELSE 3 END AS nsptyp, + nsp.nspname AS name, + nsp.oid, + array_to_string(nsp.nspacl::text[], ', ') as acl, + r.rolname AS namespaceowner, description, + has_schema_privilege(nsp.oid, 'CREATE') AS can_create, + CASE + WHEN nspname LIKE E'pg\\_%' THEN true + ELSE false END AS is_sys_object, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deftblacl' AND defaclnamespace = nsp.oid) AS tblacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'defseqacl' AND defaclnamespace = nsp.oid) AS seqacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deffuncacl' AND defaclnamespace = nsp.oid) AS funcacl, + (SELECT array_to_string(defaclacl::text[], ', ') FROM pg_default_acl WHERE defaclobjtype = 'deftypeacl' AND defaclnamespace = nsp.oid) AS typeacl +FROM + pg_namespace nsp + LEFT OUTER JOIN pg_description des ON + (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) + LEFT JOIN pg_roles r ON (r.oid = nsp.nspowner) +WHERE + {% if scid %} + nsp.oid={{scid}}::int AND + {% else %} + {% if show_sysobj %} + nspname NOT LIKE E'pg\\_temp\\_%' AND + nspname NOT LIKE E'pg\\_toast\\_temp\\_%' AND + {% endif %} + {% endif %} + nsp.nspparent = 0 AND + NOT ( +{{ CATALOGS.LIST('nsp') }} + ) +ORDER BY 1, nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/update.sql new file mode 100644 index 000000000..b74ec3381 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/schema/ppas/9.2_plus/sql/update.sql @@ -0,0 +1,145 @@ +{% import 'macros/security.macros' as SECLABLE %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% import 'macros/default_privilege.macros' as DEFAULT_PRIVILEGE %} +{% if data %} +{### To update SCHEMA name ###} +{% if data.name and data.name != o_data.name %} +ALTER SCHEMA {{ conn|qtIdent(o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; + +{% endif %} +{### To update SCHEMA owner ###} +{% if data.namespaceowner and data.namespaceowner != o_data.namespaceowner %} +ALTER SCHEMA {{ conn|qtIdent(data.name) }} + OWNER TO {{ conn|qtIdent(data.namespaceowner) }}; + +{% endif %} +{### To update SCHEMA comments ###} +{% if data.description and data.description != o_data.description %} +COMMENT ON SCHEMA {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{### Change the security labels ###} +{# The SQL generated below will change Security Label #} +{% if data.seclabels and data.seclabels|length > 0 %} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} +{{ SECLABLE.DROP(conn, 'SCHEMA', data.name, r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'SCHEMA', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} +{{ SECLABLE.APPLY(conn, 'SCHEMA', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} + +{% endif %} +{### Change the privileges ###} +{% if data.nspacl %} +{% if 'deleted' in data.nspacl %} +{% for priv in data.nspacl.deleted %} +{{ PRIVILEGE.RESETALL(conn, 'SCHEMA', priv.grantee, data.name) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.nspacl %} +{% for priv in data.nspacl.changed %} +{{ PRIVILEGE.RESETALL(conn, 'SCHEMA', priv.grantee, data.name) }} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.nspacl %} +{% for priv in data.nspacl.added %} +{{ PRIVILEGE.APPLY(conn, 'SCHEMA', priv.grantee, data.name, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} + +{% endif %} +{### To change default privileges for tables ###} +{% if data.deftblacl %} +{% if 'deleted' in data.deftblacl %} +{% for priv in data.deftblacl.deleted %} +{{ DEFAULT_PRIVILEGE.UNSET(conn, 'SCHEMA', data.name, 'TABLES', priv.grantee) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.deftblacl %} +{% for priv in data.deftblacl.changed %} +{{ DEFAULT_PRIVILEGE.UNSET(conn, 'SCHEMA', data.name, 'TABLES', priv.grantee) }} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, 'TABLES', priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.deftblacl %} +{% for priv in data.deftblacl.added %} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, 'TABLES', priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} + +{% endif %} +{### To change default privileges for sequences ###} +{% if data.defseqacl %} +{% if 'deleted' in data.defseqacl %} +{% for priv in data.defseqacl.deleted %} +{{ DEFAULT_PRIVILEGE.UNSET(conn,'SCHEMA', data.name, 'SEQUENCES', priv.grantee) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.defseqacl %} +{% for priv in data.defseqacl.changed %} +{{ DEFAULT_PRIVILEGE.UNSET(conn,'SCHEMA', data.name, 'SEQUENCES', priv.grantee) }} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, 'SEQUENCES', priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.defseqacl %} +{% for priv in data.defseqacl.added %} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, 'SEQUENCES', priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} + +{% endif %} +{### To change default privileges for functions ###} +{% if data.deffuncacl %} +{% if 'deleted' in data.deffuncacl %} +{% for priv in data.deffuncacl.deleted %} +{{ DEFAULT_PRIVILEGE.UNSET(conn,'SCHEMA', data.name, 'FUNCTIONS', priv.grantee) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.deffuncacl %} +{% for priv in data.deffuncacl.changed %} +{{ DEFAULT_PRIVILEGE.UNSET(conn,'SCHEMA', data.name, 'FUNCTIONS', priv.grantee) }} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, 'FUNCTIONS', priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.deffuncacl %} +{% for priv in data.deffuncacl.added %} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, 'FUNCTIONS', priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} + +{% endif %} +{### To change default privileges for types ###} +{% if data.deftypeacl %} +{% if 'deleted' in data.deftypeacl %} +{% for priv in data.deftypeacl.deleted %} +{{ DEFAULT_PRIVILEGE.UNSET(conn,'SCHEMA', data.name, 'TYPES', priv.grantee) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.deftypeacl %} +{% for priv in data.deftypeacl.changed %} +{{ DEFAULT_PRIVILEGE.UNSET(conn,'SCHEMA', data.name, 'TYPES', priv.grantee) }} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, 'TYPES', priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.deftypeacl %} +{% for priv in data.deftypeacl.added %} +{{ DEFAULT_PRIVILEGE.SET(conn,'SCHEMA', data.name, 'TYPES', priv.grantee, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} + +{% endif %} +{### End main if data ###} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py new file mode 100644 index 000000000..6f78af5e8 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py @@ -0,0 +1,59 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Schema collection node helper class""" + +from pgadmin.browser.collection import CollectionNodeModule + + +class SchemaChildModule(CollectionNodeModule): + """ + Base class for the schema child node. + + Some of the node may be/may not be allowed in certain catalog nodes. + i.e. + Do not show the schema objects under pg_catalog, pgAgent, etc. + + Looks at two parameters CATALOG_DB_SUPPORTED, SUPPORTED_SCHEMAS. + + Schema child objects like catalog_objects are only supported for + 'pg_catalog', and objects like 'jobs' & 'schedules' are only supported for + the 'pgagent' schema. + + For catalog_objects, we should set: + CATALOG_DB_SUPPORTED = False + SUPPORTED_SCHEMAS = ['pg_catalog'] + + For jobs & schedules, we should set: + CATALOG_DB_SUPPORTED = False + SUPPORTED_SCHEMAS = ['pgagent'] + """ + CATALOG_DB_SUPPORTED = True + SUPPORTED_SCHEMAS = None + + def BackendSupported(self, manager, **kwargs): + return ( + ( + kwargs['is_catalog'] and (( + self.CATALOG_DB_SUPPORTED and kwargs['db_support'] + ) or ( + not self.CATALOG_DB_SUPPORTED and + not kwargs['db_support'] and + ( + self.SUPPORTED_SCHEMAS is None or ( + kwargs['schema_name'] in self.SUPPORTED_SCHEMAS + ) + ) + )) + ) or ( + not kwargs['is_catalog'] and self.CATALOG_DB_SUPPORTED + ) + ) and CollectionNodeModule.BackendSupported( + self, manager, **kwargs + )