diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/__init__.py new file mode 100644 index 0000000..fb4d692 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/__init__.py @@ -0,0 +1,727 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements View Node""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas as schemas +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps +from .parse_definition import parse_definition + + +try: + unicode = unicode +except NameError: + # 'unicode' is undefined, must be Python 3 + str = str + unicode = str + bytes = bytes + basestring = (str, bytes) +else: + # 'unicode' exists, must be Python 2 + str = str + unicode = unicode + bytes = str + basestring = basestring + + +class ViewModule(CollectionNodeModule): + """ + class ViewModule(Object): + + A view collection Node which inherits CollectionNodeModule + class and define methods: + get_nodes - To generate collection node. + script_load - tells when to load js file. + csssnppets - add css to page + """ + NODE_TYPE = 'view' + COLLECTION_LABEL = gettext("Views") + + def __init__(self, *args, **kwargs): + self.min_ver = None + self.max_ver = None + + super(ViewModule, self).__init__(*args, **kwargs) + + # Before loading this module we need to make sure that scid is catalog \ + # and schema object and catalog name is 'sys', 'dbo', + # 'information_schema' then only we load this module + def BackendSupported(self, manager, **kwargs): + """ + This function will validate schema name & scid against catalogs \ + then allow us to make dission if we want to load this module or \ + not for that schema + """ + if super(ViewModule, self).BackendSupported(manager, **kwargs): + conn = manager.connection() + # If DB is not connected then return error to browser + if not conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + ver = manager.version + server_type = manager.server_type + # we will set template path for sql scripts + if server_type == 'ppas' and ver >= 90100: + template_path = 'templates/views/sql/edb_9.1_plus' + elif ver >= 90100: + template_path = 'templates/views/sql/9.4_plus' + else: + # Note: View is not supported below postgres version 9.1 + # Hence we will not load this module + return False + + SQL = render_template("/".join([ + template_path, 'backend_support.sql']), scid=kwargs['scid']) + status, res = conn.execute_scalar(SQL) + # check if any errors + if not status: + return internal_server_error(errormsg=res) + # Check scid is catalog and from 'sys', 'dbo', \ + # 'information_schema',then False (Do not load this module), \ + # othewise True + if res is True: + return False + else: + return True + + def get_nodes(self, gid, sid, did, scid): + """ + Generate the collection node + """ + yield self.generate_browser_collection_node(scid) + + @property + def script_load(self): + """ + Load the module script for view, when any of the database node is + initialized. it is because views are under catalogs and schemas . + """ + return schemas.SchemaModule.NODE_TYPE + + @property + def csssnippets(self): + """ + Returns a snippet of css to include in the page + """ + snippets = [ + render_template( + "browser/css/collection.css", + node_type=self.node_type, + _=gettext + ), + render_template( + "templates/views/css/view.css", + node_type=self.node_type, + _=gettext + ) + ] + + for submodule in self.submodules: + snippets.extend(submodule.csssnippets) + + return snippets + +# Set template_folder to views,as view also uses rules sql templates. +blueprint = ViewModule(__name__, template_folder="../views") + + +class ViewNode(PGChildNodeView): + """ + It is a class for view node which inherits the + properties and methods from PGChildNodeView class and define + various methods to list, create, update and delete view. + + Variables: + --------- + * node_type - tells which type of node it is + * parent_ids - id with its type and name of parent nodes + * ids - id with type and name of extension module being used. + * operations - function routes mappings defined. + """ + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'vid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'children': [{ + 'get': 'children' + }], + 'delete': [{'delete': 'delete'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'configs': [{'get': 'configs'}] + }) + + def module_js(self): + """ + This property defines (if javascript) exists for this node. + Override this property for your own logic. + """ + return make_response( + render_template( + "templates/views/js/views.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + This function will behave as a decorator which will checks + database connection before running view, it will also attaches + manager,conn & template_path properties to self + """ + @wraps(f) + def wrap(*args, **kwargs): + + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + self.manager = get_driver( + PG_DEFAULT_DRIVER).connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + self.datlastsysoid = self.manager.db_info[ + kwargs['did']]['datlastsysoid'] + ver = self.manager.version + + # we will set template path for sql scripts + if ver >= 90400: + self.template_path = 'templates/views/sql/9.4_plus' + elif ver >= 90300: + self.template_path = 'templates/views/sql/9.3_plus' + elif ver >= 90200: + self.template_path = 'templates/views/sql/9.2_plus' + elif ver >= 90100: + self.template_path = 'templates/views/sql/9.1_plus' + else: + self.template_path = 'templates/views/sql/pre_9.1' + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid): + """ + It fetches all views properties and render into properties + tab + """ + SQL = render_template("/".join( + [self.template_path, 'properties.sql']), scid=scid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, scid): + """ + It lists down the all views under the Views Collection node + """ + res = [] + SQL = render_template("/".join( + [self.template_path, 'properties.sql']), scid=scid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + scid, + row['name'], + icon="icon-view" + )) + + return make_json_response( + data=res, + status=200 + ) + + def parse_views_privileges(self, db_privileges): + """ + This function forms separate list of grantable + and non grantable privileges. + """ + acl = {'grantor': db_privileges['grantor'], + 'grantee': db_privileges['grantee'], + 'privileges': [] + } + + privileges = [] + for idx, priv in enumerate(db_privileges['privileges']): + if db_privileges['grantable'][idx]: + privileges.append({"privilege_type": priv, + "privilege": True, + "with_grant": True + }) + else: + privileges.append({"privilege_type": priv, + "privilege": True, + "with_grant": False + }) + + acl['privileges'] = privileges + + return acl + + @check_precondition + def properties(self, gid, sid, did, scid, vid): + """ + It fetches the properties of an individual view + and render in properties tab + + """ + SQL = render_template("/".join( + [self.template_path, 'properties.sql'] + ), vid=vid, datlastsysoid=self.datlastsysoid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + SQL = render_template("/".join( + [self.template_path, 'acl.sql']), vid=vid) + status, dataclres = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in dataclres['rows']: + priv = self.parse_views_privileges(row) + if row['deftype'] in res['rows'][0]: + res['rows'][0][row['deftype']].append(priv) + else: + res['rows'][0][row['deftype']] = [priv] + + result = res['rows'][0] + + # sending result to formtter + frmtd_reslt = self.formatter(result) + + # merging formated result with main result again + result.update(frmtd_reslt) + return ajax_response( + response=result, + status=200 + ) + + @staticmethod + def formatter(result): + """ This function formats output for + security label & variables""" + frmtd_result = dict() + sec_lbls = [] + if 'seclabels' in result and result['seclabels'] is not None: + for sec in result['seclabels']: + import re + sec = re.search(r'([^=]+)=(.*$)', sec) + sec_lbls.append({ + 'provider': sec.group(1), + 'security_label': sec.group(2) + }) + + frmtd_result.update({"seclabels": sec_lbls}) + return frmtd_result + + @check_precondition + def create(self, gid, sid, did, scid): + """ + This function will creates new view object + """ + required_args = [ + 'name', + 'definition' + ] + + data = request.form if request.form else \ + json.loads(request.data.decode()) + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + try: + SQL = render_template("/".join( + [self.template_path, 'create.sql']), data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + SQL = render_template("/".join( + [self.template_path, 'view_id.sql']), data=data) + status, view_id = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=view_id) + return jsonify( + node=self.blueprint.generate_browser_node( + view_id, + scid, + data['name'], + icon="icon-view" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, scid, vid): + """ + This function will update view object + """ + data = request.form if request.form else \ + json.loads(request.data.decode()) + SQL = self.getSQL(gid, sid, data, vid) + try: + if SQL and isinstance(SQL, basestring) 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=gettext("View updated"), + data={ + 'id': vid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info=gettext("Nothing to update"), + data={ + 'id': vid, + '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, vid): + """ + This function will drop the view object + """ + + # Below will decide if it's simple drop or drop with cascade call + cascade = True if self.cmd == 'delete' else False + + try: + + # Get name for view from did + SQL = render_template("/".join( + [self.template_path, 'delete.sql']), vid=vid) + status, res_data = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res_data) + + # drop view + SQL = render_template("/".join( + [self.template_path, 'delete.sql']), + nspname=res_data['rows'][0]['nspname'], + name=res_data['rows'][0]['name'], cascade=cascade + ) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("View dropped"), + data={ + 'id': vid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def msql(self, gid, sid, did, scid, vid=None): + """ + This function to return modified SQL + """ + data = {} + for k, v in request.args.items(): + try: + data[k] = json.loads(v) + except ValueError: + data[k] = v + + SQL = self.getSQL(gid, sid, data, vid) + if SQL and isinstance(SQL, basestring) and \ + SQL.strip('\n') and SQL.strip(' '): + return make_json_response( + data=SQL, + status=200 + ) + else: + return make_json_response( + data=gettext("--modified SQL"), + status=200 + ) + + @staticmethod + def parse_privileges(str_privileges, object_type='VIEW'): + """Parse Privileges.""" + db_privileges = { + 'a': 'INSERT', + 'r': 'SELECT', + 'w': 'UPDATE', + 'd': 'DELETE', + 'x': 'REFERENCE', + 't': 'TRIGGER' + } + privileges = [] + for priv in str_privileges: + priv_with_grant = [] + priv_without_grant = [] + for privilege in priv['privileges']: + if privilege['with_grant']: + priv_with_grant.append( + db_privileges[privilege['privilege_type']]) + elif privilege['privilege']: + priv_without_grant.append( + db_privileges[privilege['privilege_type']]) + priv_with_grant = ", ".join(priv_with_grant) + priv_without_grant = ", ".join(priv_without_grant) + + # Server Level validation + if 'grantee' in priv: + privileges.append( + { + 'grantee': priv['grantee'] if 'grantee' in priv else '', + 'with_grant': priv_with_grant, + 'without_grant': priv_without_grant + } + ) + else: + return '' + return privileges + + def getSQL(self, gid, sid, data, vid=None): + """ + This function will genrate sql from model data + """ + try: + if vid is not None: + SQL = render_template("/".join( + [self.template_path, 'properties.sql']), + vid=vid, + datlastsysoid=self.datlastsysoid + ) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + old_data = res['rows'][0] + + if 'name' not in data: + data['name'] = res['rows'][0]['name'] + if 'schema' not in data: + data['schema'] = res['rows'][0]['schema'] + + key = 'datacl' + if key in data and data[key] is not None: + if 'added' in data[key]: + data[key]['added'] = self.parse_privileges( + data[key]['added']) + if 'changed' in data[key]: + data[key]['changed'] = self.parse_privileges( + data[key]['changed']) + if 'deleted' in data[key]: + data[key]['deleted'] = self.parse_privileges( + data[key]['deleted']) + SQL = render_template("/".join( + [self.template_path, 'update.sql']), data=data, + o_data=old_data, conn=self.conn) + else: + required_args = [ + 'name', + 'schema', + 'definition' + ] + for arg in required_args: + if arg not in data: + return " -- definition incomplete" + + if 'datacl' in data and data['datacl'] is not None: + data['datacl'] = self.parse_privileges(data['datacl']) + SQL = render_template("/".join( + [self.template_path, 'create.sql']), data=data) + if data['definition']: + SQL += "\n" + SQL += render_template("/".join( + [self.template_path, 'grant.sql']), data=data) + return SQL + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, scid, vid): + """ + This function will generate sql to render into sql panel + """ + SQL_data = '' + SQL = render_template("/".join( + [self.template_path, 'properties.sql']), + vid=vid, + datlastsysoid=self.datlastsysoid + ) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + # Fetch all privileges for view + SQL = render_template("/".join( + [self.template_path, 'acl.sql']), vid=vid) + status, dataclres = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + for row in dataclres['rows']: + priv = self.parse_views_privileges(row) + if row['deftype'] in res['rows'][0]: + res['rows'][0]['datacl'].append(priv) + else: + res['rows'][0]['datacl'] = [priv] + + result = res['rows'][0] + if 'datacl' in result: + result['datacl'] = self.parse_privileges(result['datacl']) + SQL = render_template("/".join( + [self.template_path, 'create.sql']), + data=result, + conn=self.conn, + display_comments=True + ) + SQL += "\n" + SQL += render_template("/".join( + [self.template_path, 'grant.sql']), data=result) + + SQL_data += SQL + + """Get all non system rules of view node, generate their sql and render + into sql tab""" + self.temp_path = 'rules/templates/rules/sql' + SQL = render_template("/".join( + [self.temp_path, 'properties.sql']), vid=vid) + status, data_prop = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + for rule in data_prop['rows']: + + # Generate SQL only for non system rule + if rule['name'] != '_RETURN': + res = [] + SQL = render_template("/".join( + [self.temp_path, 'properties.sql']), + rid=rule['oid'] + ) + status, res = self.conn.execute_dict(SQL) + res_data = parse_definition(res) + SQL = render_template("/".join( + [self.temp_path, 'create.sql']), + data=res_data, display_comments=True) + SQL_data += '\n' + SQL_data += SQL + + return ajax_response(response=SQL_data) + + @check_precondition + def dependents(self, gid, sid, did, scid, vid): + """ + This function get the dependents and return ajax response + for the view node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + vid: View ID + """ + dependents_result = self.get_dependents(self.conn, vid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, vid): + """ + This function get the dependencies and return ajax response + for the view node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + vid: View ID + """ + dependencies_result = self.get_dependencies(self.conn, vid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +ViewNode.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/parse_definition.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/parse_definition.py new file mode 100644 index 0000000..f317acf --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/parse_definition.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 +# +########################################################################## + +""" Parse definition field data """ + +from pgadmin.utils.ajax import internal_server_error + + +def parse_definition(res): + """ + This function extracts: + - events + - do_instead + - statements + - condition + from the defintion row, form an array with + fields and return it. + """ + res_data = [] + try: + res_data = res['rows'][0] + data_def = res_data['definition'] + import re + # Parse data for event + e_match = re.search(r"ON\s+(.*)\s+TO", data_def) + event_data = e_match.group(1) if e_match is not None else None + event = event_data if event_data is not None else '' + + # Parse data for do instead + inst_match = re.search(r"\s+(INSTEAD)\s+", data_def) + instead_data = inst_match.group(1) if inst_match is not None else None + instead = True if instead_data is not None else False + + # Parse data for condition + condition_match = re.search(r"(?:WHERE)\s+(.*)\s+(?:DO)", data_def) + condition_data = condition_match.group(1) \ + if condition_match is not None else None + condition = condition_data if condition_data is not None else '' + + # Parse data for statements + statement_match = re.search( + r"(?:DO\s+)(?:INSTEAD\s+)?((.|\n)*)", data_def) + statement_data = statement_match.group(1) if statement_match else None + statement = statement_data if statement_data is not None else '' + + # set columns parse data + res_data['event'] = event.lower().capitalize() + res_data['do_instead'] = instead + res_data['statements'] = statement + res_data['condition'] = condition + except Exception as e: + return internal_server_error(errormsg=str(e)) + return res_data diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/__init__.py new file mode 100644 index 0000000..2d958e2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/__init__.py @@ -0,0 +1,526 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +""" Implements Rule Node """ + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases.schemas.views as views +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER +from functools import wraps +from ..parse_definition import parse_definition + + +try: + unicode = unicode +except NameError: + # 'unicode' is undefined, must be Python 3 + str = str + unicode = str + bytes = bytes + basestring = (str, bytes) +else: + # 'unicode' exists, must be Python 2 + str = str + unicode = unicode + bytes = str + basestring = basestring + + +class RuleModule(CollectionNodeModule): + """ + class RuleModule(Object): + + A rule collection Node which inherits CollectionNodeModule + class and define methods: + get_nodes - To generate collection node. + script_load - tells when to load js file. + csssnppets - add css to page + """ + NODE_TYPE = 'rule' + COLLECTION_LABEL = gettext("Rules") + + def __init__(self, *args, **kwargs): + self.min_ver = None + self.max_ver = None + + super(RuleModule, self).__init__(*args, **kwargs) + + # Before loading this module we need to make sure that scid is catalog \ + # and schema object and catalog name is 'sys', 'dbo', + # 'information_schema' then only we load this module + def BackendSupported(self, manager, **kwargs): + """ + This function will validate schema name & scid against catalogs \ + then allow us to make dission if we want to load this module or \ + not for that schema + """ + if super(RuleModule, self).BackendSupported(manager, **kwargs): + conn = manager.connection() + # If DB is not connected then return error to browser + if not conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + ver = manager.version + server_type = manager.server_type + template_path = 'rules/sql' + SQL = render_template("/".join( + [template_path, 'backend_support.sql'] + ), scid=kwargs['scid']) + status, res = conn.execute_scalar(SQL) + # check if any errors + if not status: + return internal_server_error(errormsg=res) + # Check scid is catalog and from 'sys', 'dbo', \ + # 'information_schema',then False (Do not load this module), \ + # othewise True + if res is True: + return False + else: + return True + + def get_nodes(self, gid, sid, did, scid, vid): + """ + Generate the collection node + """ + yield self.generate_browser_collection_node(vid) + + @property + def node_inode(self): + """ + If a node have child return True otherwise False + """ + return False + + @property + def script_load(self): + """ + Load the module script for rule, when any of the database node is + initialized. + """ + return views.ViewModule.NODE_TYPE + + @property + def csssnippets(self): + """ + Returns a snippet of css to include in the page + """ + snippets = [ + render_template( + "browser/css/collection.css", + node_type=self.node_type, + _=gettext + ), + render_template( + "rules/css/rule.css", + node_type=self.node_type, + _=gettext + ) + ] + + for submodule in self.submodules: + snippets.extend(submodule.csssnippets) + + return snippets + + +# Create blueprint of RuleModule. +blueprint = RuleModule(__name__) + + +class RuleView(PGChildNodeView): + """ + It is a class for rule node which inherits the + properties and methods from PGChildNodeView class and define + various methods to list, create, update and delete rule. + + Variables: + --------- + * node_type - tells which type of node it is + * parent_ids - id with its type and name of parent nodes + * ids - id with type and name of extension module being used. + * operations - function routes mappings defined. + """ + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'}, + {'type': 'int', 'id': 'vid'} + ] + ids = [ + {'type': 'int', 'id': 'rid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'children': [{ + 'get': 'children' + }], + 'delete': [{'delete': 'delete'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'configs': [{'get': 'configs'}] + }) + + def module_js(self): + """ + This property defines (if javascript) exists for this node. + Override this property for your own logic. + """ + return make_response( + render_template( + "rules/js/rules.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + This function will behave as a decorator which will checks + database connection before running rule, it will also attaches + manager,conn & template_path properties to self + """ + @wraps(f) + def wrap(*args, **kwargs): + + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + self.manager = get_driver( + PG_DEFAULT_DRIVER).connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + self.datlastsysoid = self.manager.db_info[kwargs['did']]['datlastsysoid'] + ver = self.manager.version + self.template_path = 'rules/sql' + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid, vid): + """ + It fetches all rules properties and render into properties + tab + """ + + # fetch schema name by schema id + SQL = render_template("/".join( + [self.template_path, 'properties.sql']), vid=vid) + 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, vid): + """ + It lists down the all rules under the Rules Collection node + """ + res = [] + SQL = render_template("/".join( + [self.template_path, 'properties.sql']), vid=vid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + vid, + row['name'], + icon="icon-rule" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, vid, rid): + """ + It fetches the properties of an individual rule + and render in properties tab + + """ + SQL = render_template("/".join( + [self.template_path, 'properties.sql'] + ), rid=rid, datlastsysoid=self.datlastsysoid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + + return ajax_response( + response=parse_definition(res), + status=200 + ) + + @check_precondition + def create(self, gid, sid, did, scid, vid): + """ + This function will creates new the rule object + """ + required_args = [ + 'name', + ] + + data = request.form if request.form else \ + json.loads(request.data.decode()) + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + try: + SQL = render_template("/".join( + [self.template_path, 'create.sql']), data=data) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + """ It fetches rule id against rule name to display node + in tree browser""" + SQL = render_template("/".join( + [self.template_path, 'rule_id.sql']), rule_name=data['name']) + status, rule_id = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=rule_id) + return jsonify( + node=self.blueprint.generate_browser_node( + rule_id, + vid, + data['name'], + icon="icon-rule" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, scid, vid, rid): + """ + This function will update rule object + """ + data = request.form if request.form else \ + json.loads(request.data.decode()) + SQL = self.getSQL(gid, sid, data, vid, rid) + try: + if SQL and isinstance(SQL, basestring) 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=gettext("Rule updated"), + data={ + 'id': vid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info=gettext("Nothing to update"), + data={ + 'id': vid, + '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, vid, rid): + """ + This function will drop the rule object + """ + # Below will decide if it's simple drop or drop with cascade call + cascade = True if self.cmd == 'delete' else False + + try: + # Get name for rule from did + SQL = render_template("/".join( + [self.template_path, 'delete.sql']), rid=rid) + status, res_data = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res_data) + # drop rule + rset = res_data['rows'][0] + SQL = render_template("/".join( + [self.template_path, 'delete.sql']), + rulename=rset['rulename'], + relname=rset['relname'], + nspname=rset['nspname'], + cascade=cascade + ) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Rule dropped"), + data={ + 'id': vid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def msql(self, gid, sid, did, scid, vid, rid=None): + """ + This function to return modified SQL + """ + data = request.args + SQL = self.getSQL(gid, sid, data, vid, rid) + if SQL and isinstance(SQL, basestring) and \ + SQL.strip('\n') and SQL.strip(' '): + return make_json_response( + data=SQL, + status=200 + ) + else: + return make_json_response( + data=gettext("--modified SQL"), + status=200 + ) + + @check_precondition + def sql(self, gid, sid, did, scid, vid, rid): + """ + This function will generate sql to render into sql panel + """ + SQL = render_template("/".join( + [self.template_path, 'properties.sql']), rid=rid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + res_data = parse_definition(res) + SQL = render_template("/".join( + [self.template_path, 'create.sql']), + data=res_data, display_comments=True) + + return ajax_response(response=SQL) + + def getSQL(self, gid, sid, data, vid, rid): + """ + This function will genrate sql from model data + """ + try: + if rid is not None: + SQL = render_template("/".join( + [self.template_path, 'properties.sql']), rid=rid) + status, res = self.conn.execute_dict(SQL) + res_data = [] + res_data = parse_definition(res) + if not status: + return internal_server_error(errormsg=res) + old_data = res_data + SQL = render_template( + "/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data + ) + else: + SQL = render_template("/".join( + [self.template_path, 'create.sql']), data=data) + return SQL + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def dependents(self, gid, sid, did, scid, vid, rid): + """ + This function get the dependents and return ajax response + for the rule node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + vid: View ID + rid: Rule ID + """ + dependents_result = self.get_dependents(self.conn, rid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, vid, rid): + """ + This function get the dependencies and return ajax response + for the rule node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + vid: View ID + rid: Rule ID + """ + dependencies_result = self.get_dependencies(self.conn, rid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +RuleView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/static/img/coll-rule.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/static/img/coll-rule.png new file mode 100644 index 0000000000000000000000000000000000000000..cfe6ed27d98351a03e98451fe83e785aeb51351f GIT binary patch literal 357 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Q-Dv1E0BJDrt0oW+5b=H{C_&@ z|C4F|pG^AyxbOed8ULTo{{Ljk|Hl*lKkj*b(Es0~_J5B$|2=B`_pssL!=`@^>;65g z`S+m8Y=`zEpec+cL4Lsu4$p3+0Xdun9+AaB+5?Q;PG;Ky8Bv}tjv*44r}laCH7M}7 zJY3k(rBU~;KGd5<=#Js+WtXq}O?@?enTE6ko10E>S;3Z`GiP+(9r|ThaVJc2?wlhg ziC9V-ADTyViR>?)F zK#IZ0z|cU~&`8(7FvQ5f%EZ{p#8lhB)XKnM-aEZjC>nC}Q!>*kAsP%U!5V<7O{@&e WAR10h4_yP)z~JfX=d#Wzp$P!7cZ;e3 literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/static/img/rule.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/static/img/rule.png new file mode 100644 index 0000000000000000000000000000000000000000..8b4978090e33bbe3d5f7ed249fd93c2da34f52d6 GIT binary patch literal 373 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}cz{ocE0DgsQug(ks{c>t{C_(0 z|C1^IANT%y)b{UT{r{)4{y&-e|8f7nNA3R}HvW51{r~Cg|4*j>e>~yeqt1U1oBlnh z`S+mw|I-=&pG^AyxclFumVXax|2?Sq_n>kK%jYzp(TpWQe!&b5&u)M?oCO|{#X#Bv zjNMLV+W{G&o-U3d5|`KZnTs_T@Hi`bDEDUl`g^`}GN)YjKDBz@O#-F|X1Becro3=L zVnUz`J9~da#}rq6_MWOkIpP6gt8f32>#JDS-4zfcy)J)|(ya4dQ}_S3nP9`r$iw)~ zA83JUiEBhjN@7W>RdP`(kYX@0Ff`CLG}1LN3^6jWGBLI?G1WFOwK6c6_fBsWiiX_$ ml+3hBhz0{oum+%N6DtEVh=x -1) + return true; + + if ('coll-rule' == d._type) { + + //Check if we are not child of rule + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + prev_j = t.hasParent(prev_i) ? t.parent(prev_i) : null; + prev_e = prev_j ? t.itemData(prev_j) : null; + prev_k = t.hasParent(prev_j) ? t.parent(prev_j) : null; + prev_f = prev_k ? t.itemData(prev_k) : null; + if( prev_f._type == 'catalog') { + return false; + } else { + return true; + } + } + + /** + Check if it is view and its parent node is schema + then allow to create Rule + */ + else if('view' == d._type){ + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + prev_j = t.hasParent(prev_i) ? t.parent(prev_i) : null; + prev_e = prev_j ? t.itemData(prev_j) : null; + if(prev_e._type == 'schema') { + return true; + }else{ + 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['coll-rule']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/backend_support.sql new file mode 100644 index 0000000..e3f8f4f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/backend_support.sql @@ -0,0 +1,20 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM + pg_namespace nsp +WHERE + nsp.oid={{scid}}::int +AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) +) +AND + nspname NOT LIKE E'pg\\temp\\%' +AND + nspname NOT LIKE E'pg\\toast_temp\\%' diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/create.sql new file mode 100644 index 0000000..3c7436f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/create.sql @@ -0,0 +1,26 @@ +{#============Create Rule=============#} +{% if display_comments %} +-- Rule: {{ data.name }} ON {{ conn|qtIdent(data.schema, data.name) }} + +-- DROP Rule {{ data.name }} ON {{ conn|qtIdent(data.schema, data.name) }}; + +{% endif %} +{% if data.name and data.schema and data.view %} +CREATE OR REPLACE RULE {{ conn|qtIdent(data.name) }} AS + ON {{ data.event|upper if data.event else 'SELECT' }} TO {{ conn|qtIdent(data.schema, data.view) }} +{% if data.condition %} + WHERE {{ data.condition }} +{% endif %} + DO{% if data.is_instead == True %} +{{ ' INSTEAD' }} + {% else %} +{{ ' ' }} + {% endif %} +{% if data.statements %} +{{ data.statements.rstrip(';') }}; +{% else %} +NOTHING; +{% endif %} +{% if data.comment %} +COMMENT ON RULE {{ conn|qtIdent(data.name) }} ON {{ conn|qtIdent(data.schema, data.view) }} IS {{ data.comment|qtLiteral }};{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/delete.sql new file mode 100644 index 0000000..7b8318b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/delete.sql @@ -0,0 +1,16 @@ +{#======== Drop/Cascade Rule =========#} +{% if rid %} +SELECT + rw.rulename, cl.relname, nsp.nspname +FROM + pg_rewrite rw +JOIN + pg_class cl ON cl.oid=rw.ev_class +JOIN + pg_namespace nsp ON nsp.oid=cl.relnamespace +WHERE + rw.oid={{ rid }}; +{% endif %} +{% if rulename and relname and nspname %} +DROP RULE {{ conn|qtIdent(rulename) }} ON {{ conn|qtIdent(nspname, relname) }} {% if cascade %} CASCADE {% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/properties.sql new file mode 100644 index 0000000..eda1025 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/properties.sql @@ -0,0 +1,27 @@ +{#===================Fetch Rules====================#} +{% if vid or rid %} +SELECT + rw.oid AS oid, rw.rulename AS name, rw.*, relname AS view, + CASE WHEN relkind = 'r' THEN TRUE ELSE FALSE END AS parentistable, + nspname AS schema, description AS comment, + {#==Checks whether it is system rule or not===#} + CASE WHEN rw.rulename = '_RETURN' THEN True ELSE False END AS system_rule, + CASE WHEN rw.ev_enabled != 'D' THEN True ELSE False END AS enabled, + pg_get_ruledef(rw.oid, true) AS definition +FROM + pg_rewrite rw +JOIN + pg_class cl ON cl.oid=rw.ev_class +JOIN + pg_namespace nsp ON nsp.oid=cl.relnamespace +LEFT OUTER JOIN + pg_description des ON (des.objoid=rw.oid AND des.classoid='pg_rewrite'::regclass) +WHERE + {% if vid %} + ev_class = {{ vid }} + {% elif rid %} + rw.oid = {{ rid }} + {% endif %} +ORDER BY + rw.rulename + {% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/rule_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/rule_id.sql new file mode 100644 index 0000000..3dc6dba --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/rule_id.sql @@ -0,0 +1,9 @@ +{#========Below will provide rule id for last created rule========#} +{% if rule_name %} +SELECT + rw.oid +FROM + pg_rewrite rw +WHERE + rw.rulename={{ rule_name|qtLiteral }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/update.sql new file mode 100644 index 0000000..b50369a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/rules/templates/rules/sql/update.sql @@ -0,0 +1,22 @@ +{#============Update Rule=============#} +{% if data.event or data.do_instead or data.condition %} +CREATE OR REPLACE RULE {{ conn|qtIdent(o_data.name) }} AS + ON {% if data.event and data.event != o_data.event %}{{ data.event|upper }}{% else %}{{ o_data.event|upper }}{% endif %} + TO {{ conn|qtIdent(o_data.schema, o_data.view) }} +{% if data.condition and o_data.condition != data.condition %} + WHERE {{ data.condition }} +{% elif data.condition is not defined and o_data.condition %} + WHERE {{ o_data.condition }} +{% endif %} + DO{% if data.do_instead in ['true', True] %}{{ ' INSTEAD' }}{% else %}{{ ' ' }} +{% endif %} +{% if data.statements and data.statements != o_data.statements %} + {{ data.statements.rstrip(';') }}; + {% elif data.statements is not defined and o_data.statements %} + {{ o_data.statements.rstrip(';') }}; +{% else %} + NOTHING; +{% endif %} +{% endif %} +{% if data.comment and data.comment != o_data.comment %} +COMMENT ON RULE {{ conn|qtIdent(o_data.name) }} ON {{ conn|qtIdent(o_data.schema, o_data.view) }} IS {{ data.comment|qtLiteral }};{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/img/coll-view.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/img/coll-view.png new file mode 100644 index 0000000000000000000000000000000000000000..078d533684b8bd7e185749c4f1c9620af23d114b GIT binary patch literal 418 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}Y=BRQE08|>xOdKRnRzD^9{rj2 z_}lVF@Af@;b@}P@d(WRgefI3xqo=p7KVP}!V)Uc`-T(gmd-%V5`%AU||Nn1)p?>Gh zn(Z&tPJa$N{=xgfzqUJn>TdojJ^s=A@Oy`SZ_IYR((AHW{03+NV@Z%-FoVOh8)+a; zlDE5yQ1zZ1Ae(`+z$3C4NPB>>+sSM@AS2(?#W6(Ua%g{~P=f-Gv+27P9V>QJ|4;U6 zbe!GKH+kPSMNh%0YD;wVPp!&p$=H2Qa>4_iC-Ws`4oVu`kXvDIe5l_eV|jwW;+5+S zYgy6~PYdy+-cS45)~k2Z!$WAAUct+ok^krC+MlVs+ppfyc`@F`nC<>l-VMq?t5r)} zBT7;dOH!?pi&B9UgOP!up{{|Eu910&p^25DrIo3vwt<xOdKRnaBToAO4>7 z;On9XZ+AU@dGYD<`_G?0efI3xZ-1%w|NsB(FVt^;p7P|y zjoU9bT=|i6;cMdQ&tb_QLRH3SlxI5?abofwq#PpGhn z3y3`waupO1o<6}vaKfZ%6BvUwB`{h}XxPzW6T>ijp+R<{)&<#R3sx-A(NkQ!Xcb#n z<0DHqpYzNNJzLm||5$yx0JK%L#5JNMC9x#cD!C{XNHG{07#ipr8tEDsh8P)GnHXD{ zm}(oCS{WG3d#ASwMMG|WN@iLmM1z4PSOZYCiIssFM8m1+p=*E|7(8A5T-G@yGywnx Cm9(({ literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/css/view.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/css/view.css new file mode 100644 index 0000000..88a2f43 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/css/view.css @@ -0,0 +1,8 @@ +.icon-view{ + background-image: url('{{ url_for('NODE-view.static', filename='img/view.png') }}') !important; + border-radius: 10px; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/js/views.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/js/views.js new file mode 100644 index 0000000..9a4ebc2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/js/views.js @@ -0,0 +1,310 @@ +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', + 'pgadmin.browser', 'codemirror', 'pgadmin.browser.server.privilege'], + +function($, _, S, pgAdmin, pgBrowser, CodeMirror) { + + // Define Security Model with fields and validation + var SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: undefined, + securitylabel: undefined + }, + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text', editable: true + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text', editable: true + }], + + validate: function() { + var err = {}, + errmsg = null, + field_name = this.get('provider'), + field_security_label = this.get('security_label'); + + if (_.isUndefined(field_name) || _.isNull(field_name) || + String(field_name).replace(/^\s+|\s+$/g, '') == '') { + err['provider'] = '{{ _("Provider cannot be empty.") }}'; + errmsg = errmsg || err['provider']; + this.errorModel.set('provider', errmsg); + return errmsg; + }else{ + this.errorModel.unset('provider'); + } + + if (_.isUndefined(field_security_label) || _.isNull(field_security_label) || + String(field_security_label).replace(/^\s+|\s+$/g, '') == '') { + err['security_label'] = '{{ _("Security Label cannot be empty.") }}'; + errmsg = errmsg || err['security_label']; + this.errorModel.set('security_label', errmsg); + return errmsg; + }else{ + this.errorModel.unset('security_label'); + } + return null; + } + }); + + + /** + Create and add a view collection into nodes + @param {variable} label - Label for Node + @param {variable} type - Type of Node + @param {variable} columns - List of columns to + display under under properties. + */ + if (!pgBrowser.Nodes['coll-view']) { + var views= pgAdmin.Browser.Nodes['coll-view'] = + pgAdmin.Browser.Collection.extend({ + node: 'view', + label: '{{ _('Views') }}', + type: 'coll-view', + columns: ['name', 'owner'] + }); + }; + + /** + Create and Add an View Node into nodes + @param {variable} parent_type - The list of nodes + under which this node to display + @param {variable} type - Type of Node + @param {variable} hasSQL - To show SQL tab + @param {variable} canDrop - Adds drop view option + in the context menu + @param {variable} canDropCascade - Adds drop Cascade + view option in the context menu + */ + if (!pgBrowser.Nodes['view']) { + pgAdmin.Browser.Nodes['view'] = pgAdmin.Browser.Node.extend({ + parent_type: ['schema', 'catalog'], + type: 'view', + label: '{{ _('View') }}', + hasSQL: true, + hasDepends: true, + collection_type: 'coll-view', + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + canDropCascade: pgBrowser.Nodes['schema'].canChildDrop, + Init: function() { + + // Avoid mulitple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + /** + Add "create view" menu option into context and object menu + for the following nodes: + coll-view, view and schema. + @property {data} - Allow create view option on schema node or + system view nodes. + */ + pgBrowser.add_menus([{ + name: 'create_view_on_coll', node: 'coll-view', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('View...') }}', + icon: 'wcTabIcon icon-view', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_view', node: 'view', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('View...') }}', + icon: 'wcTabIcon icon-view', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_view', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('View...') }}', + icon: 'wcTabIcon icon-view', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + }, + + /** + Define model for the view node and specify the + properties of the model in schema. + */ + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + }, + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text' + }, + { + id: 'oid', label:'{{ _('Oid') }}', cell: 'string', + type: 'text', disabled: true + }, + { + id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: 'node-list-by-name', + node: 'role' + }, + { + id: 'schema', label:'{{ _('Schema') }}', cell: 'string', first_empty: false, + type: 'options', node: 'schema', + control: Backform.NodeListByNameControl.extend({ + render: function(){ + + // Initialize parent's render method + Backform.NodeListByNameControl.prototype.render.apply(this, arguments); + + // Set schema default value to its parent Schema + if(this.model.isNew()){ + this.model.set({'schema': this.model.node_info.schema.label}); + } + return this; + } + }), + + // Don't show schema starts with "pg_" + filter: function(d){ + if(d.label && d.label.match(/^pg_/)){ + return false; + } + return true; + } + }, + { + id: 'comment', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + }, + { + id: 'system_view', label:'{{ _('System view?') }}', cell: 'string', + type: 'switch', disabled: true, mode: ['properties'], 'options': { + 'onText': 'Yes', 'offText': 'No', 'onColor': 'success', + 'offColor': 'default', 'size': 'small' + } + }, + { + id: 'security_barrier', label:'{{ _('Security Barrier') }}', cell: 'string', + type: 'switch', min_version: '90200', + group: 'Definition', options: { + 'onText': 'Yes', 'offText': 'No', 'onColor': 'success', + 'offColor': 'default', 'size': 'small' + } + }, + { + id: 'check_option', label:'{{ _('Check Options') }}', + control: 'select2', group: 'Definition', type: 'text', + min_version: '90400', mode:['create', 'edit'], + select2: { + + // set select2 option width to 100% + width: '100%' + }, + options:[ + {label: "No", value: "no"}, + {label: "Local", value: "local"}, + {label: "Cascaded", value: "cascaded"} + ] + }, + { + id: 'definition', label:'{{ _('Definition') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'], group: 'Definition', + control: 'sql-field' + }, + { + id: 'm_view', label:'{{ _('Materialized view?') }}', cell: 'string', + type: 'switch', mode: ['properties'], min_version: '90300', + group: 'Materialization', options: { + 'onText': 'Yes', 'offText': 'No', 'onColor': 'success', + 'offColor': 'default', 'size': 'small' + } + }, + + // Add Security Labels Control + { + id: 'seclabels', label: '{{ _('Securitiy Labels') }}', + model: SecurityModel, editable: false, type: 'collection', + canEdit: false, group: '{{ _('Security') }}', canDelete: true, + mode: ['edit', 'create'], canAdd: true, + control: 'unique-col-collection', uniqueCol : ['provider'] + }, + + // Add Privilege Control + { + id: 'datacl', label: '{{ _('Privileges') }}', + model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend( + {privileges: ['a', 'r', 'w', 'd', 'x', 't']}), uniqueCol : ['grantee'], + editable: false, type: 'collection', group: '{{ _('Security') }}', + mode: ['properties', 'edit', 'create'], canAdd: true, canDelete: true, + control: 'unique-col-collection', + } + ], + validate: function() { + + // Triggers specific error messages for fields + var err = {}, + errmsg, + field_name = this.get('name'), + field_def = this.get('definition'); + if (_.isUndefined(field_name) || _.isNull(field_name) || + String(field_name).replace(/^\s+|\s+$/g, '') == '') { + err['name'] = '{{ _("Please specify name.") }}'; + errmsg = errmsg || err['name']; + this.errorModel.set('name', errmsg); + return errmsg; + }else{ + this.errorModel.unset('name'); + } + if(this.isNew()){ + if (_.isUndefined(field_def) || _.isNull(field_def) || + String(field_def).replace(/^\s+|\s+$/g, '') == '') { + err['definition'] = '{{ _("Please enter function definition.") }}'; + errmsg = errmsg || err['definition']; + this.errorModel.set('definition', errmsg); + return errmsg; + }else{ + this.errorModel.unset('definition'); + } + } + return null; + } + }), + + /** + Show or hide create view menu option on parent node + and hide for system view in catalogs. + */ + canCreate: function(itemData, item, data) { + + // If check is false then, we will allow create menu + if (data && data.check == false) + return true; + + var t = pgBrowser.tree, i = item, d = itemData; + + // To iterate over tree to check parent node + while (i) { + + // If it is schema then allow user to create view + if (_.indexOf(['schema'], d._type) > -1) + return true; + + if ('coll-view' == d._type) { + + // Check if we are not child of view + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + if( prev_d._type == 'catalog') { + return false; + } else { + return true; + } + } + i = t.hasParent(i) ? t.parent(i) : null; + d = i ? t.itemData(i) : null; + } + + // by default we do not want to allow create menu + return true; + + } + }); + } + + return pgBrowser.Nodes['coll-view']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/acl.sql new file mode 100644 index 0000000..807fb04 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/acl.sql @@ -0,0 +1,35 @@ +{#============================Get ACLs=========================#} +{% if vid %} +SELECT + 'datacl' as deftype, + COALESCE(gt.rolname, 'public') grantee, + g.rolname grantor, + array_agg(privilege_type) as privileges, + array_agg(is_grantable) as grantable +FROM + (SELECT + d.grantee, d.grantor, d.is_grantable, + CASE d.privilege_type + WHEN 'DELETE' THEN 'd' + WHEN 'INSERT' THEN 'a' + WHEN 'REFERENCES' THEN 'x' + WHEN 'SELECT' THEN 'r' + WHEN 'TRIGGER' THEN 't' + WHEN 'UPDATE' THEN 'w' + ELSE 'UNKNOWN' + END AS privilege_type + FROM + (SELECT relacl FROM pg_class cl + LEFT OUTER JOIN pg_shdescription descr ON ( + cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass) + WHERE cl.oid = {{ vid }}::OID AND relkind = 'v' + ) acl, + (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable AS is_grantable, + (d).privilege_type AS privilege_type + FROM (SELECT aclexplode(relacl) as d FROM pg_class cl1 WHERE cl1.oid = {{vid}}) a) d + ) d + LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid) +GROUP BY + g.rolname, gt.rolname +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/backend_support.sql new file mode 100644 index 0000000..e3f8f4f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/backend_support.sql @@ -0,0 +1,20 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM + pg_namespace nsp +WHERE + nsp.oid={{scid}}::int +AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) +) +AND + nspname NOT LIKE E'pg\\temp\\%' +AND + nspname NOT LIKE E'pg\\toast_temp\\%' diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/create.sql new file mode 100644 index 0000000..2ac1e3a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/create.sql @@ -0,0 +1,25 @@ +{#============================Create new view=========================#} +{% if display_comments %} +-- View: {{ conn|qtIdent(data.schema, data.name) }} + +-- DROP VIEW {{ conn|qtIdent(data.schema, data.name) }}; + +{% endif %} +{% if data.name and data.schema and data.definition %} +CREATE OR REPLACE VIEW {{ conn|qtIdent(data.schema, data.name) }} +{% if ((data.check_option and data.check_option.lower() != 'no') or data.security_barrier) %} +WITH ({% if data.check_option and data.check_option.lower() != 'no' %} +check_option={{ data.check_option }}{% endif %}{{ ', ' if data.check_option and +data.check_option.lower() != 'no' and data.security_barrier else '' }}{% if data.security_barrier %} +security_barrier={{ data.security_barrier }}{% endif %}){% endif %} AS +{{ data.definition.rstrip(';') }}; +{% if data.owner %} + +ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }} + OWNER TO {{ data.owner }}; +{% endif %} +{% if data.comment %} +COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }} + IS {{ data.comment|qtLiteral }}; +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/delete.sql new file mode 100644 index 0000000..4e290a2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/delete.sql @@ -0,0 +1,12 @@ +{#============================Drop/Cascade view by name=========================#} +{% if vid %} +SELECT + c.relname As name, nsp.nspname +FROM + pg_class c + LEFT JOIN pg_namespace nsp on c.relnamespace = nsp.oid +WHERE + c.relfilenode = {{ vid }}; +{% elif (name and nspname) %} +DROP VIEW {{nspname}}.{{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/grant.sql new file mode 100644 index 0000000..9fa7181 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/grant.sql @@ -0,0 +1,14 @@ +{#=====Grant Permissions to User Role on Views/Tables====#} +{% import 'macros/security.macros' as SECLABLE %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{# We will generate Security Label SQL using macro #} +{% if data.seclabels %} +{% for r in data.seclabels %} +{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% if data.datacl %} +{% for priv in data.datacl %} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..9487458 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/properties.sql @@ -0,0 +1,29 @@ +{% if (vid and datlastsysoid) or scid %} +SELECT c.oid, c.xmin, c.relname AS name, c.reltablespace AS spcoid, spc.spcname, + pg_get_userbyid(c.relowner) AS owner, description AS comment, + pg_get_viewdef(c.oid, true) AS definition, nsp.nspname AS schema, + (SELECT array_agg(label) FROM pg_seclabels sl1 WHERE sl1.objoid=c.oid AND sl1.objsubid=0) AS labels, + (SELECT array_agg(provider) FROM pg_seclabels sl2 WHERE sl2.objoid=c.oid AND sl2.objsubid=0) AS providers +FROM pg_class c + LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace + LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace + LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass) + WHERE ((c.relhasrules AND (EXISTS ( + SELECT r.rulename FROM pg_rewrite r + WHERE ((r.ev_class = c.oid) + AND (bpchar(r.ev_type) = '1'::bpchar)) ))) OR (c.relkind = 'v'::char)) +{% if (vid and datlastsysoid) %} + AND c.oid = {{vid}}::oid +{% elif scid %} + AND c.relnamespace = {{scid}}::oid ORDER BY c.relname +{% endif %} + +{% elif type == 'roles' %} +SELECT pr.rolname FROM pg_roles pr WHERE pr.rolcanlogin ORDER BY pr.rolname + +{% elif type == 'schemas' %} +SELECT nsp.nspname + FROM pg_namespace nsp +WHERE (nsp.nspname NOT LIKE E'pg\\_%' + AND nsp.nspname != 'information_schema') +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/update.sql new file mode 100644 index 0000000..d258632 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/update.sql @@ -0,0 +1,81 @@ +{#============================Update view=========================#} +{% import 'macros/security.macros' as SECLABLE %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data %} +{% set view_name = data.name if data.name else o_data.name %} +{% set view_schema = data.schema if data.schema else o_data.schema %} +{% set def = data.definition.rstrip(';') if data.definition %} +{% if data.name and data.name != o_data.name %} +ALTER VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% endif %} +{% if data.schema and data.schema != o_data.schema %} +ALTER VIEW {{ conn|qtIdent(o_data.schema, view_name ) }} + SET SCHEMA {{ data.schema }}; +{% endif %} +{% if data.owner and data.owner != o_data.owner %} +ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }} + OWNER TO {{ data.owner }}; +{% endif %} +{% if def and def != o_data.definition.rstrip(';') %} +CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }} +{% if (data.check_option and data.check_option != o_data.check_option) + or (data.security_barrier and data.security_barrier != o_data.security_barrier) %} + WITH ({% if (data.check_option and data.check_option != 'no') %}check_option={{ data.check_option }}{% endif %}{{', ' if data.check_option and data.check_option != 'no' and data.security_barrier }}{% if data.security_barrier %}security_barrier={{ data.security_barrier }}{% endif %}) +{% endif %} + AS{{ def }}; +{% else %} +{% if (data.security_barrier and data.security_barrier != o_data.security_barrier) %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} + SET (security_barrier={{ data.security_barrier }}); +{% endif %} +{% if (data.check_option and data.check_option != o_data.check_option and data.check_option != 'no') %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} + SET (check_option={{ data.check_option }}); +{% elif (data.check_option and data.check_option != o_data.check_option and data.check_option == 'no') %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} RESET (check_option); +{% endif %} +{% endif %} +{% if data.comment and data.comment != o_data.comment %} +COMMENT ON VIEW {{ conn|qtIdent(view_schema, view_name) }} + IS {{ data.comment|qtLiteral }}; +{% endif %} +{# The SQL generated below will change privileges #} +{% if data.datacl %} +{% if 'deleted' in data.datacl %} +{% for priv in data.datacl.deleted %} +{{ PRIVILEGE.RESETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.datacl %} +{% for priv in data.datacl.changed %} +{{ PRIVILEGE.RESETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.datacl %} +{% for priv in data.datacl.added %} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% endif %} +{# The SQL generated below will change Security Label #} +{% if data.seclabels is not none 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, 'VIEW', data.name, r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'VIEW', 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, 'VIEW', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/view_id.sql new file mode 100644 index 0000000..6dff04e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.1_plus/view_id.sql @@ -0,0 +1,4 @@ +{# Below will provide view id for newly created view #} +{% if data %} +SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}'; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/acl.sql new file mode 100644 index 0000000..2a3fbca --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/acl.sql @@ -0,0 +1,35 @@ +{#============================Get ACLs=========================#} +{% if vid %} +SELECT + 'datacl' as deftype, + COALESCE(gt.rolname, 'public') grantee, + g.rolname grantor, + array_agg(privilege_type) as privileges, + array_agg(is_grantable) as grantable +FROM + (SELECT + d.grantee, d.grantor, d.is_grantable, + CASE d.privilege_type + WHEN 'DELETE' THEN 'd' + WHEN 'INSERT' THEN 'a' + WHEN 'REFERENCES' THEN 'x' + WHEN 'SELECT' THEN 'r' + WHEN 'TRIGGER' THEN 't' + WHEN 'UPDATE' THEN 'w' + ELSE 'UNKNOWN' + END AS privilege_type + FROM + (SELECT relacl FROM pg_class cl + LEFT OUTER JOIN pg_shdescription descr ON ( + cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass) + WHERE cl.oid = {{ vid }}::OID AND relkind = 'v' + ) acl, + (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable AS is_grantable, + (d).privilege_type AS privilege_type + FROM (SELECT aclexplode(relacl) as d FROM pg_class cl1 WHERE cl1.oid = {{ vid }}::OID) a) d + ) d + LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid) +GROUP BY + g.rolname, gt.rolname +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/backend_support.sql new file mode 100644 index 0000000..7d19b68 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/backend_support.sql @@ -0,0 +1,18 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM pg_namespace nsp +WHERE nsp.oid={{scid}}::int +AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXIST + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) +) +AND + nspname NOT LIKE E'pg\\temp\\%' +AND + nspname NOT LIKE E'pg\\toast_temp\\%' diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/create.sql new file mode 100644 index 0000000..a345e15 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/create.sql @@ -0,0 +1,25 @@ +{#============================Create new view=========================#} +{% if display_comments %} +-- View: {{ conn|qtIdent(data.schema, data.name) }} + +-- DROP VIEW {{ conn|qtIdent(data.schema, data.name) }}; + +{% endif %} +{% if data.name and data.schema and data.definition %} +CREATE OR REPLACE VIEW {{ conn|qtIdent(data.schema, data.name) }} +{% if ((data.check_option and data.check_option.lower() != 'no') or data.security_barrier) %} +WITH ({% if data.check_option and data.check_option.lower() != 'no' %} +check_option={{ data.check_option }}{% endif %}{{ ', ' if data.check_option and +data.check_option.lower() != 'no' and data.security_barrier else '' }}{% if data.security_barrier %} +security_barrier={{ data.security_barrier }}{% endif %}){% endif %} AS +{{ data.definition.rstrip(';') }}; +{% if data.owner %} + +ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }} + OWNER TO {{ data.owner }}; +{% endif %} +{% if data.comment %} +COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }} + IS {{ data.comment|qtLiteral }}; +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/delete.sql new file mode 100644 index 0000000..4e290a2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/delete.sql @@ -0,0 +1,12 @@ +{#============================Drop/Cascade view by name=========================#} +{% if vid %} +SELECT + c.relname As name, nsp.nspname +FROM + pg_class c + LEFT JOIN pg_namespace nsp on c.relnamespace = nsp.oid +WHERE + c.relfilenode = {{ vid }}; +{% elif (name and nspname) %} +DROP VIEW {{nspname}}.{{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/grant.sql new file mode 100644 index 0000000..9fa7181 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/grant.sql @@ -0,0 +1,14 @@ +{#=====Grant Permissions to User Role on Views/Tables====#} +{% import 'macros/security.macros' as SECLABLE %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{# We will generate Security Label SQL using macro #} +{% if data.seclabels %} +{% for r in data.seclabels %} +{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% if data.datacl %} +{% for priv in data.datacl %} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/properties.sql new file mode 100644 index 0000000..3da7801 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/properties.sql @@ -0,0 +1,39 @@ +{% if (vid and datlastsysoid) or scid %} +SELECT c.oid, c.xmin, c.relname AS name, c.reltablespace AS spcoid, spc.spcname, + pg_get_userbyid(c.relowner) AS owner, c.relacl, description As comment, + pg_get_viewdef(c.oid, true) AS definition, nsp.nspname AS schema, + {#=============Checks if it is system view================#} + {% if vid and datlastsysoid %} + CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view, + {% endif %} + {#=============Checks if it is materialized view========#} + CASE WHEN c.relkind = 'm' THEN True ELSE False END As m_view, + (SELECT array_agg(label) FROM pg_seclabels sl1 WHERE sl1.objoid=c.oid AND sl1.objsubid=0) AS labels, + (SELECT array_agg(provider) FROM pg_seclabels sl2 WHERE sl2.objoid=c.oid AND sl2.objsubid=0) AS providers + , substring(array_to_string(c.reloptions, ',') FROM 'security_barrier=([a-z|0-9]*)') AS security_barrier +FROM pg_class c + LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace + LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace + LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass) + WHERE ((c.relhasrules AND (EXISTS ( + SELECT r.rulename FROM pg_rewrite r + WHERE ((r.ev_class = c.oid) + AND (bpchar(r.ev_type) = '1'::bpchar)) ))) OR (c.relkind = 'v'::char)) +{% if (vid and datlastsysoid) %} + AND c.oid = {{vid}}::oid +{% elif scid %} + AND c.relnamespace = {{scid}}::oid ORDER BY c.relname +{% endif %} + +{% elif type == 'roles' %} +SELECT pr.rolname FROM pg_roles pr WHERE pr.rolcanlogin ORDER BY pr.rolname + +{% elif type == 'schemas' %} +SELECT + nsp.nspname +FROM + pg_namespace nsp +WHERE + (nsp.nspname NOT LIKE E'pg\\_%' + AND nsp.nspname != 'information_schema') +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/update.sql new file mode 100644 index 0000000..d258632 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/update.sql @@ -0,0 +1,81 @@ +{#============================Update view=========================#} +{% import 'macros/security.macros' as SECLABLE %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data %} +{% set view_name = data.name if data.name else o_data.name %} +{% set view_schema = data.schema if data.schema else o_data.schema %} +{% set def = data.definition.rstrip(';') if data.definition %} +{% if data.name and data.name != o_data.name %} +ALTER VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% endif %} +{% if data.schema and data.schema != o_data.schema %} +ALTER VIEW {{ conn|qtIdent(o_data.schema, view_name ) }} + SET SCHEMA {{ data.schema }}; +{% endif %} +{% if data.owner and data.owner != o_data.owner %} +ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }} + OWNER TO {{ data.owner }}; +{% endif %} +{% if def and def != o_data.definition.rstrip(';') %} +CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }} +{% if (data.check_option and data.check_option != o_data.check_option) + or (data.security_barrier and data.security_barrier != o_data.security_barrier) %} + WITH ({% if (data.check_option and data.check_option != 'no') %}check_option={{ data.check_option }}{% endif %}{{', ' if data.check_option and data.check_option != 'no' and data.security_barrier }}{% if data.security_barrier %}security_barrier={{ data.security_barrier }}{% endif %}) +{% endif %} + AS{{ def }}; +{% else %} +{% if (data.security_barrier and data.security_barrier != o_data.security_barrier) %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} + SET (security_barrier={{ data.security_barrier }}); +{% endif %} +{% if (data.check_option and data.check_option != o_data.check_option and data.check_option != 'no') %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} + SET (check_option={{ data.check_option }}); +{% elif (data.check_option and data.check_option != o_data.check_option and data.check_option == 'no') %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} RESET (check_option); +{% endif %} +{% endif %} +{% if data.comment and data.comment != o_data.comment %} +COMMENT ON VIEW {{ conn|qtIdent(view_schema, view_name) }} + IS {{ data.comment|qtLiteral }}; +{% endif %} +{# The SQL generated below will change privileges #} +{% if data.datacl %} +{% if 'deleted' in data.datacl %} +{% for priv in data.datacl.deleted %} +{{ PRIVILEGE.RESETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.datacl %} +{% for priv in data.datacl.changed %} +{{ PRIVILEGE.RESETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.datacl %} +{% for priv in data.datacl.added %} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% endif %} +{# The SQL generated below will change Security Label #} +{% if data.seclabels is not none 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, 'VIEW', data.name, r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'VIEW', 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, 'VIEW', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/view_id.sql new file mode 100644 index 0000000..6dff04e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.2_plus/view_id.sql @@ -0,0 +1,4 @@ +{# Below will provide view id for newly created view #} +{% if data %} +SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}'; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/acl.sql new file mode 100644 index 0000000..b60bfa8 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/acl.sql @@ -0,0 +1,30 @@ +{#============================Get ACLs=========================#} +{% if vid %} +SELECT + 'datacl' as deftype, COALESCE(gt.rolname, 'public') grantee, + g.rolname grantor, array_agg(privilege_type) as privileges, + array_agg(is_grantable) as grantable +FROM + (SELECT + d.grantee, d.grantor, d.is_grantable, + CASE d.privilege_type + WHEN 'DELETE' THEN 'd' + WHEN 'INSERT' THEN 'a' + WHEN 'REFERENCES' THEN 'x' + WHEN 'SELECT' THEN 'r' + WHEN 'TRIGGER' THEN 't' + WHEN 'UPDATE' THEN 'w' + ELSE 'UNKNOWN' + END AS privilege_type + FROM + (SELECT relacl FROM pg_class cl + LEFT OUTER JOIN pg_shdescription descr ON ( + cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass) + WHERE cl.oid = {{ vid }}::OID AND relkind = 'v' + ) acl, + aclexplode(relacl) d + ) d + LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/backend_support.sql new file mode 100644 index 0000000..ab39cac --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/backend_support.sql @@ -0,0 +1,18 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM pg_namespace nsp +WHERE nsp.oid={{scid}}::int +AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) +) +AND + nspname NOT LIKE E'pg\\temp\\%' +AND + nspname NOT LIKE E'pg\\toast_temp\\%' diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/create.sql new file mode 100644 index 0000000..a345e15 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/create.sql @@ -0,0 +1,25 @@ +{#============================Create new view=========================#} +{% if display_comments %} +-- View: {{ conn|qtIdent(data.schema, data.name) }} + +-- DROP VIEW {{ conn|qtIdent(data.schema, data.name) }}; + +{% endif %} +{% if data.name and data.schema and data.definition %} +CREATE OR REPLACE VIEW {{ conn|qtIdent(data.schema, data.name) }} +{% if ((data.check_option and data.check_option.lower() != 'no') or data.security_barrier) %} +WITH ({% if data.check_option and data.check_option.lower() != 'no' %} +check_option={{ data.check_option }}{% endif %}{{ ', ' if data.check_option and +data.check_option.lower() != 'no' and data.security_barrier else '' }}{% if data.security_barrier %} +security_barrier={{ data.security_barrier }}{% endif %}){% endif %} AS +{{ data.definition.rstrip(';') }}; +{% if data.owner %} + +ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }} + OWNER TO {{ data.owner }}; +{% endif %} +{% if data.comment %} +COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }} + IS {{ data.comment|qtLiteral }}; +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/delete.sql new file mode 100644 index 0000000..044ff71 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/delete.sql @@ -0,0 +1,12 @@ +{#============================Drop/Cascade view by name=========================#} +{% if vid %} +SELECT + c.relname AS name, nsp.nspname +FROM + pg_class c + LEFT JOIN pg_namespace nsp on c.relnamespace = nsp.oid +WHERE + c.relfilenode = {{ vid }}; +{% elif (name and nspname) %} +DROP VIEW {{nspname}}.{{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/grant.sql new file mode 100644 index 0000000..9fa7181 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/grant.sql @@ -0,0 +1,14 @@ +{#=====Grant Permissions to User Role on Views/Tables====#} +{% import 'macros/security.macros' as SECLABLE %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{# We will generate Security Label SQL using macro #} +{% if data.seclabels %} +{% for r in data.seclabels %} +{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% if data.datacl %} +{% for priv in data.datacl %} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/properties.sql new file mode 100644 index 0000000..f4e3498 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/properties.sql @@ -0,0 +1,60 @@ +{% if (vid and datlastsysoid) or scid %} +SELECT c.oid, c.xmin, c.relname AS name, c.reltablespace AS spcoid, c.relkind, nsp.nspname AS schema, + c.relispopulated AS ispopulated, spc.spcname, pg_get_userbyid(c.relowner) + AS owner, c.relacl, description, pg_get_viewdef(c.oid, true) AS definition, + {#=============Checks if it is system view================#} + {% if vid and datlastsysoid %} + CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view, + {% endif %} + {#=============Checks if it is materialized view========#} + CASE WHEN c.relkind = 'm' THEN True ELSE False END As m_view, + (SELECT array_agg(label) FROM pg_seclabels sl1 WHERE sl1.objoid=c.oid AND sl1.objsubid=0) AS labels, + (SELECT array_agg(provider) FROM pg_seclabels sl2 WHERE sl2.objoid=c.oid AND sl2.objsubid=0) AS providers + , substring(array_to_string(c.reloptions, ',') FROM 'security_barrier=([a-z|0-9]*)') AS security_barrier + , substring(array_to_string(c.reloptions, ',') FROM 'fillfactor=([0-9]*)') AS fillfactor + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') AS autovacuum_enabled + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS autovacuum_vacuum_threshold + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_vacuum_scale_factor + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS autovacuum_analyze_threshold + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_analyze_scale_factor + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS autovacuum_vacuum_cost_delay + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS autovacuum_vacuum_cost_limit + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS autovacuum_freeze_min_age + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS autovacuum_freeze_max_age + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS autovacuum_freeze_table_age + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') AS toast_autovacuum_enabled + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS toast_autovacuum_vacuum_threshold + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_vacuum_scale_factor + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS toast_autovacuum_analyze_threshold + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_analyze_scale_factor + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS toast_autovacuum_vacuum_cost_delay + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS toast_autovacuum_vacuum_cost_limit + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS toast_autovacuum_freeze_min_age + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS toast_autovacuum_freeze_max_age + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS toast_autovacuum_freeze_table_age + , c.reloptions AS reloptions, tst.reloptions AS toast_reloptions + , (CASE WHEN c.reltoastrelid = 0 THEN false ELSE true END) AS hastoasttable +FROM pg_class c + LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace + LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace + LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass) + LEFT OUTER JOIN pg_class tst ON tst.oid = c.reltoastrelid + WHERE ((c.relhasrules AND (EXISTS ( + SELECT r.rulename FROM pg_rewrite r + WHERE ((r.ev_class = c.oid) + AND (bpchar(r.ev_type) = '1'::bpchar)) ))) OR (c.relkind = 'v'::char)) +{% if (vid and datlastsysoid) %} + AND c.oid = {{vid}}::oid +{% elif scid %} + AND c.relnamespace = {{scid}}::oid ORDER BY c.relname +{% endif %} + +{% elif type == 'roles' %} +SELECT pr.rolname FROM pg_roles pr WHERE pr.rolcanlogin ORDER BY pr.rolname + +{% elif type == 'schemas' %} +SELECT nsp.nspname + FROM pg_namespace nsp +WHERE (nsp.nspname NOT LIKE E'pg\\_%' + AND nsp.nspname != 'information_schema') +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/update.sql new file mode 100644 index 0000000..d258632 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/update.sql @@ -0,0 +1,81 @@ +{#============================Update view=========================#} +{% import 'macros/security.macros' as SECLABLE %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data %} +{% set view_name = data.name if data.name else o_data.name %} +{% set view_schema = data.schema if data.schema else o_data.schema %} +{% set def = data.definition.rstrip(';') if data.definition %} +{% if data.name and data.name != o_data.name %} +ALTER VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% endif %} +{% if data.schema and data.schema != o_data.schema %} +ALTER VIEW {{ conn|qtIdent(o_data.schema, view_name ) }} + SET SCHEMA {{ data.schema }}; +{% endif %} +{% if data.owner and data.owner != o_data.owner %} +ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }} + OWNER TO {{ data.owner }}; +{% endif %} +{% if def and def != o_data.definition.rstrip(';') %} +CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }} +{% if (data.check_option and data.check_option != o_data.check_option) + or (data.security_barrier and data.security_barrier != o_data.security_barrier) %} + WITH ({% if (data.check_option and data.check_option != 'no') %}check_option={{ data.check_option }}{% endif %}{{', ' if data.check_option and data.check_option != 'no' and data.security_barrier }}{% if data.security_barrier %}security_barrier={{ data.security_barrier }}{% endif %}) +{% endif %} + AS{{ def }}; +{% else %} +{% if (data.security_barrier and data.security_barrier != o_data.security_barrier) %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} + SET (security_barrier={{ data.security_barrier }}); +{% endif %} +{% if (data.check_option and data.check_option != o_data.check_option and data.check_option != 'no') %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} + SET (check_option={{ data.check_option }}); +{% elif (data.check_option and data.check_option != o_data.check_option and data.check_option == 'no') %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} RESET (check_option); +{% endif %} +{% endif %} +{% if data.comment and data.comment != o_data.comment %} +COMMENT ON VIEW {{ conn|qtIdent(view_schema, view_name) }} + IS {{ data.comment|qtLiteral }}; +{% endif %} +{# The SQL generated below will change privileges #} +{% if data.datacl %} +{% if 'deleted' in data.datacl %} +{% for priv in data.datacl.deleted %} +{{ PRIVILEGE.RESETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.datacl %} +{% for priv in data.datacl.changed %} +{{ PRIVILEGE.RESETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.datacl %} +{% for priv in data.datacl.added %} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% endif %} +{# The SQL generated below will change Security Label #} +{% if data.seclabels is not none 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, 'VIEW', data.name, r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'VIEW', 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, 'VIEW', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/view_id.sql new file mode 100644 index 0000000..6dff04e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.3_plus/view_id.sql @@ -0,0 +1,4 @@ +{# Below will provide view id for newly created view #} +{% if data %} +SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}'; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/acl.sql new file mode 100644 index 0000000..f0c4c01 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/acl.sql @@ -0,0 +1,35 @@ +{#============================Get ACLs=========================#} +{% if vid %} +SELECT + 'datacl' as deftype, COALESCE(gt.rolname, 'public') grantee, + g.rolname grantor, array_agg(privilege_type) as privileges, + array_agg(is_grantable) as grantable +FROM + (SELECT + d.grantee, d.grantor, d.is_grantable, + CASE d.privilege_type + WHEN 'DELETE' THEN 'd' + WHEN 'INSERT' THEN 'a' + WHEN 'REFERENCES' THEN 'x' + WHEN 'SELECT' THEN 'r' + WHEN 'TRIGGER' THEN 't' + WHEN 'UPDATE' THEN 'w' + ELSE 'UNKNOWN' + END AS privilege_type + FROM + (SELECT + relacl FROM pg_class cl + LEFT OUTER JOIN + pg_shdescription descr + ON + (cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass) + WHERE + cl.oid = {{ vid }}::OID AND relkind = 'v' + ) acl, + aclexplode(relacl) d + ) d +LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid) +LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid) +GROUP BY + g.rolname, gt.rolname +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/backend_support.sql new file mode 100644 index 0000000..e3f8f4f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/backend_support.sql @@ -0,0 +1,20 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM + pg_namespace nsp +WHERE + nsp.oid={{scid}}::int +AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) +) +AND + nspname NOT LIKE E'pg\\temp\\%' +AND + nspname NOT LIKE E'pg\\toast_temp\\%' diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/create.sql new file mode 100644 index 0000000..2ac1e3a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/create.sql @@ -0,0 +1,25 @@ +{#============================Create new view=========================#} +{% if display_comments %} +-- View: {{ conn|qtIdent(data.schema, data.name) }} + +-- DROP VIEW {{ conn|qtIdent(data.schema, data.name) }}; + +{% endif %} +{% if data.name and data.schema and data.definition %} +CREATE OR REPLACE VIEW {{ conn|qtIdent(data.schema, data.name) }} +{% if ((data.check_option and data.check_option.lower() != 'no') or data.security_barrier) %} +WITH ({% if data.check_option and data.check_option.lower() != 'no' %} +check_option={{ data.check_option }}{% endif %}{{ ', ' if data.check_option and +data.check_option.lower() != 'no' and data.security_barrier else '' }}{% if data.security_barrier %} +security_barrier={{ data.security_barrier }}{% endif %}){% endif %} AS +{{ data.definition.rstrip(';') }}; +{% if data.owner %} + +ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }} + OWNER TO {{ data.owner }}; +{% endif %} +{% if data.comment %} +COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }} + IS {{ data.comment|qtLiteral }}; +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/delete.sql new file mode 100644 index 0000000..53a9bdb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/delete.sql @@ -0,0 +1,13 @@ +{#============================Drop/Cascade view by name=========================#} +{% if vid %} +SELECT + c.relname As name, nsp.nspname +FROM + pg_class c +LEFT JOIN + pg_namespace nsp ON c.relnamespace = nsp.oid +WHERE + c.relfilenode = {{ vid }}; +{% elif (name and nspname) %} +DROP VIEW {{ conn|qtIdent(nspname, name) }} {% if cascade %} CASCADE {% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/grant.sql new file mode 100644 index 0000000..9fa7181 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/grant.sql @@ -0,0 +1,14 @@ +{#=====Grant Permissions to User Role on Views/Tables====#} +{% import 'macros/security.macros' as SECLABLE %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{# We will generate Security Label SQL using macro #} +{% if data.seclabels %} +{% for r in data.seclabels %} +{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% if data.datacl %} +{% for priv in data.datacl %} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/properties.sql new file mode 100644 index 0000000..fa4dcf6 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/properties.sql @@ -0,0 +1,65 @@ +{#============================Fetch View Properties=========================#} +{% if (vid and datlastsysoid) or scid %} +SELECT c.oid, c.xmin, c.relname AS name, c.reltablespace AS spcoid, + c.relispopulated AS ispopulated, spc.spcname, nsp.nspname as schema, + pg_get_userbyid(c.relowner) AS owner, description AS comment, + pg_get_viewdef(c.oid, true) AS definition, + {#=============Checks if it is system view================#} + {% if vid and datlastsysoid %} + CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view, + {% endif %} + {#=============Checks if it is materialized view========#} + CASE WHEN c.relkind = 'm' THEN True ELSE False END As m_view, + (SELECT array_agg(provider || '=' || label) FROM pg_seclabels sl1 WHERE sl1.objoid=c.oid AND sl1.objsubid=0) AS seclabels + , substring(array_to_string(c.reloptions, ',') FROM 'check_option=([a-z]*)') AS check_option + , substring(array_to_string(c.reloptions, ',') FROM 'security_barrier=([a-z|0-9]*)') AS security_barrier + , substring(array_to_string(c.reloptions, ',') FROM 'fillfactor=([0-9]*)') AS fillfactor + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') AS autovacuum_enabled + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS autovacuum_vacuum_threshold + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_vacuum_scale_factor + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS autovacuum_analyze_threshold + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_analyze_scale_factor + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS autovacuum_vacuum_cost_delay + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS autovacuum_vacuum_cost_limit + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS autovacuum_freeze_min_age + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS autovacuum_freeze_max_age + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS autovacuum_freeze_table_age + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') AS toast_autovacuum_enabled + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS toast_autovacuum_vacuum_threshold + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_vacuum_scale_factor + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS toast_autovacuum_analyze_threshold + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_analyze_scale_factor + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS toast_autovacuum_vacuum_cost_delay + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS toast_autovacuum_vacuum_cost_limit + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS toast_autovacuum_freeze_min_age + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS toast_autovacuum_freeze_max_age + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS toast_autovacuum_freeze_table_age + , c.reloptions AS reloptions, tst.reloptions AS toast_reloptions + , (CASE WHEN c.reltoastrelid = 0 THEN false ELSE true END) AS hastoasttable +FROM pg_class c + LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace + LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace + LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass) + LEFT OUTER JOIN pg_class tst ON tst.oid = c.reltoastrelid + WHERE ((c.relhasrules AND (EXISTS ( + SELECT r.rulename FROM pg_rewrite r + WHERE ((r.ev_class = c.oid) + AND (bpchar(r.ev_type) = '1'::bpchar)) ))) OR (c.relkind = 'v'::char)) +{% if (vid and datlastsysoid) %} + AND c.oid = {{vid}}::oid +{% elif scid %} + AND c.relnamespace = {{scid}}::oid ORDER BY c.relname +{% endif %} + +{% elif type == 'roles' %} +SELECT pr.rolname FROM pg_roles pr WHERE pr.rolcanlogin ORDER BY pr.rolname + +{% elif type == 'schemas' %} +SELECT + nsp.nspname +FROM + pg_namespace nsp +WHERE + (nsp.nspname NOT LIKE E'pg\\_%' + AND nsp.nspname != 'information_schema') +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/update.sql new file mode 100644 index 0000000..1867038 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/update.sql @@ -0,0 +1,81 @@ +{#============================Update View=========================#} +{% import 'macros/security.macros' as SECLABLE %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data %} +{% set view_name = data.name if data.name else o_data.name %} +{% set view_schema = data.schema if data.schema else o_data.schema %} +{% set def = data.definition.rstrip(';') if data.definition %} +{% if data.name and data.name != o_data.name %} +ALTER VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% endif %} +{% if data.schema and data.schema != o_data.schema %} +ALTER VIEW {{ conn|qtIdent(o_data.schema, view_name ) }} + SET SCHEMA {{ data.schema }}; +{% endif %} +{% if data.owner and data.owner != o_data.owner %} +ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }} + OWNER TO {{ data.owner }}; +{% endif %} +{% if def and def != o_data.definition.rstrip(';') %} +CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }} +{% if (data.check_option and data.check_option != o_data.check_option) + or (data.security_barrier and data.security_barrier != o_data.security_barrier) %} + WITH ({% if (data.check_option and data.check_option != 'no') %}check_option={{ data.check_option }}{% endif %}{{', ' if data.check_option and data.check_option != 'no' and data.security_barrier }}{% if data.security_barrier %}security_barrier={{ data.security_barrier }}{% endif %}) +{% endif %} + AS{{ def }}; +{% else %} +{% if (data.security_barrier and data.security_barrier != o_data.security_barrier) %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} + SET (security_barrier={{ data.security_barrier }}); +{% endif %} +{% if (data.check_option and data.check_option != o_data.check_option and data.check_option != 'no') %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} + SET (check_option={{ data.check_option }}); +{% elif (data.check_option and data.check_option != o_data.check_option and data.check_option == 'no') %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} RESET (check_option); +{% endif %} +{% endif %} +{% if data.comment and data.comment != o_data.comment %} +COMMENT ON VIEW {{ conn|qtIdent(view_schema, view_name) }} + IS {{ data.comment|qtLiteral }}; +{% endif %} +{# The SQL generated below will change privileges #} +{% if data.datacl %} +{% if 'deleted' in data.datacl %} +{% for priv in data.datacl.deleted %} +{{ PRIVILEGE.RESETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.datacl %} +{% for priv in data.datacl.changed %} +{{ PRIVILEGE.RESETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.datacl %} +{% for priv in data.datacl.added %} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% endif %} +{# The SQL generated below will change Security Label #} +{% if data.seclabels is not none 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, 'VIEW', data.name, r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'VIEW', 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, 'VIEW', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/view_id.sql new file mode 100644 index 0000000..65648c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/9.4_plus/view_id.sql @@ -0,0 +1,4 @@ +{#==Below will provide view id for last created view==#} +{% if data %} +SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}'; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/acl.sql new file mode 100644 index 0000000..b5d2225 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/acl.sql @@ -0,0 +1,33 @@ +{#============================Get ACLs=========================#} +{% if vid %} +SELECT + 'datacl' as deftype, + COALESCE(gt.rolname, 'public') grantee, + g.rolname grantor, + array_agg(privilege_type) as privileges, + array_agg(is_grantable) as grantable +FROM + (SELECT + d.grantee, d.grantor, d.is_grantable, + CASE d.privilege_type + WHEN 'DELETE' THEN 'd' + WHEN 'INSERT' THEN 'a' + WHEN 'REFERENCES' THEN 'x' + WHEN 'SELECT' THEN 'r' + WHEN 'TRIGGER' THEN 't' + WHEN 'UPDATE' THEN 'w' + ELSE 'UNKNOWN' + END AS privilege_type + FROM + (SELECT relacl FROM pg_class cl + LEFT OUTER JOIN pg_shdescription descr ON ( + cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass) + WHERE cl.oid = {{ vid }}::OID AND relkind = 'v' + ) acl, + aclexplode(relacl) d + ) d + LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid) +GROUP BY + g.rolname, gt.rolname +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/backend_support.sql new file mode 100644 index 0000000..5c0648d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/backend_support.sql @@ -0,0 +1,20 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM + pg_namespace nsp +WHERE + nsp.oid={{scid}}::int +AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) +) +AND + nspname NOT LIKE E'pg\\temp\\%' +AND + nspname NOT LIKE E'pg\\toast_temp\\%' diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/create.sql new file mode 100644 index 0000000..2ac1e3a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/create.sql @@ -0,0 +1,25 @@ +{#============================Create new view=========================#} +{% if display_comments %} +-- View: {{ conn|qtIdent(data.schema, data.name) }} + +-- DROP VIEW {{ conn|qtIdent(data.schema, data.name) }}; + +{% endif %} +{% if data.name and data.schema and data.definition %} +CREATE OR REPLACE VIEW {{ conn|qtIdent(data.schema, data.name) }} +{% if ((data.check_option and data.check_option.lower() != 'no') or data.security_barrier) %} +WITH ({% if data.check_option and data.check_option.lower() != 'no' %} +check_option={{ data.check_option }}{% endif %}{{ ', ' if data.check_option and +data.check_option.lower() != 'no' and data.security_barrier else '' }}{% if data.security_barrier %} +security_barrier={{ data.security_barrier }}{% endif %}){% endif %} AS +{{ data.definition.rstrip(';') }}; +{% if data.owner %} + +ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }} + OWNER TO {{ data.owner }}; +{% endif %} +{% if data.comment %} +COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }} + IS {{ data.comment|qtLiteral }}; +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/delete.sql new file mode 100644 index 0000000..8fc4e01 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/delete.sql @@ -0,0 +1,9 @@ +{#============================Drop/Cascade view by name=========================#} +{% if vid %} +SELECT c.relname As name, nsp.nspname + FROM pg_class c + LEFT JOIN pg_namespace nsp on c.relnamespace = nsp.oid + WHERE c.relfilenode = {{ vid }}; +{% elif (name and nspname) %} +DROP VIEW {{ conn|qtIdent(nspname, name) }} {% if cascade %} CASCADE {% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/grant.sql new file mode 100644 index 0000000..9fa7181 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/grant.sql @@ -0,0 +1,14 @@ +{#=====Grant Permissions to User Role on Views/Tables====#} +{% import 'macros/security.macros' as SECLABLE %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{# We will generate Security Label SQL using macro #} +{% if data.seclabels %} +{% for r in data.seclabels %} +{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% if data.datacl %} +{% for priv in data.datacl %} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/properties.sql new file mode 100644 index 0000000..f926027 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/properties.sql @@ -0,0 +1,63 @@ +{#============================Fetch View Properties=========================#} +{% if (vid and datlastsysoid) or scid %} +SELECT c.oid, c.xmin, c.relname AS name, c.reltablespace AS spcoid, + c.relispopulated AS ispopulated, spc.spcname, nsp.nspname as schema, + pg_get_userbyid(c.relowner) AS owner, description AS comment, + pg_get_viewdef(c.oid, true) AS definition, + {#=============Checks if it is system view================#} + {% if vid and datlastsysoid %} + CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view, + {% endif %} + {#=============Checks if it is materialized view========#} + CASE WHEN c.relkind = 'm' THEN True ELSE False END As m_view, + (SELECT array_agg(provider || '=' || label) FROM pg_seclabels sl1 WHERE sl1.objoid=c.oid AND sl1.objsubid=0) AS seclabels + , substring(array_to_string(c.reloptions, ',') FROM 'check_option=([a-z]*)') AS check_option + , substring(array_to_string(c.reloptions, ',') FROM 'security_barrier=([a-z|0-9]*)') AS security_barrier + , substring(array_to_string(c.reloptions, ',') FROM 'fillfactor=([0-9]*)') AS fillfactor + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') AS autovacuum_enabled + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS autovacuum_vacuum_threshold + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_vacuum_scale_factor + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS autovacuum_analyze_threshold + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_analyze_scale_factor + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS autovacuum_vacuum_cost_delay + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS autovacuum_vacuum_cost_limit + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS autovacuum_freeze_min_age + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS autovacuum_freeze_max_age + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS autovacuum_freeze_table_age + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') AS toast_autovacuum_enabled + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS toast_autovacuum_vacuum_threshold + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_vacuum_scale_factor + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS toast_autovacuum_analyze_threshold + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_analyze_scale_factor + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS toast_autovacuum_vacuum_cost_delay + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS toast_autovacuum_vacuum_cost_limit + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS toast_autovacuum_freeze_min_age + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS toast_autovacuum_freeze_max_age + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS toast_autovacuum_freeze_table_age + , c.reloptions AS reloptions, tst.reloptions AS toast_reloptions + , (CASE WHEN c.reltoastrelid = 0 THEN false ELSE true END) AS hastoasttable +FROM pg_class c + LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace + LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace + LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass) + LEFT OUTER JOIN pg_class tst ON tst.oid = c.reltoastrelid + WHERE ((c.relhasrules AND (EXISTS ( + SELECT r.rulename FROM pg_rewrite r + WHERE ((r.ev_class = c.oid) + AND (bpchar(r.ev_type) = '1'::bpchar)) ))) OR (c.relkind = 'v'::char)) +{% if (vid and datlastsysoid) %} + AND c.oid = {{vid}}::oid +{% elif scid %} + AND c.relnamespace = {{scid}}::oid ORDER BY c.relname +{% endif %} + +{% elif type == 'roles' %} +SELECT pr.rolname FROM pg_roles pr WHERE pr.rolcanlogin ORDER BY pr.rolname + +{% elif type == 'schemas' %} +SELECT nsp.nspname + FROM pg_namespace nsp +WHERE + (nsp.nspname NOT LIKE E'pg\\_%' + AND nsp.nspname != 'information_schema') +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/update.sql new file mode 100644 index 0000000..1867038 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/update.sql @@ -0,0 +1,81 @@ +{#============================Update View=========================#} +{% import 'macros/security.macros' as SECLABLE %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data %} +{% set view_name = data.name if data.name else o_data.name %} +{% set view_schema = data.schema if data.schema else o_data.schema %} +{% set def = data.definition.rstrip(';') if data.definition %} +{% if data.name and data.name != o_data.name %} +ALTER VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% endif %} +{% if data.schema and data.schema != o_data.schema %} +ALTER VIEW {{ conn|qtIdent(o_data.schema, view_name ) }} + SET SCHEMA {{ data.schema }}; +{% endif %} +{% if data.owner and data.owner != o_data.owner %} +ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }} + OWNER TO {{ data.owner }}; +{% endif %} +{% if def and def != o_data.definition.rstrip(';') %} +CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }} +{% if (data.check_option and data.check_option != o_data.check_option) + or (data.security_barrier and data.security_barrier != o_data.security_barrier) %} + WITH ({% if (data.check_option and data.check_option != 'no') %}check_option={{ data.check_option }}{% endif %}{{', ' if data.check_option and data.check_option != 'no' and data.security_barrier }}{% if data.security_barrier %}security_barrier={{ data.security_barrier }}{% endif %}) +{% endif %} + AS{{ def }}; +{% else %} +{% if (data.security_barrier and data.security_barrier != o_data.security_barrier) %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} + SET (security_barrier={{ data.security_barrier }}); +{% endif %} +{% if (data.check_option and data.check_option != o_data.check_option and data.check_option != 'no') %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} + SET (check_option={{ data.check_option }}); +{% elif (data.check_option and data.check_option != o_data.check_option and data.check_option == 'no') %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} RESET (check_option); +{% endif %} +{% endif %} +{% if data.comment and data.comment != o_data.comment %} +COMMENT ON VIEW {{ conn|qtIdent(view_schema, view_name) }} + IS {{ data.comment|qtLiteral }}; +{% endif %} +{# The SQL generated below will change privileges #} +{% if data.datacl %} +{% if 'deleted' in data.datacl %} +{% for priv in data.datacl.deleted %} +{{ PRIVILEGE.RESETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.datacl %} +{% for priv in data.datacl.changed %} +{{ PRIVILEGE.RESETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.datacl %} +{% for priv in data.datacl.added %} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% endif %} +{# The SQL generated below will change Security Label #} +{% if data.seclabels is not none 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, 'VIEW', data.name, r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'VIEW', 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, 'VIEW', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/view_id.sql new file mode 100644 index 0000000..65648c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/edb_9.1_plus/view_id.sql @@ -0,0 +1,4 @@ +{#==Below will provide view id for last created view==#} +{% if data %} +SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}'; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/acl.sql new file mode 100644 index 0000000..46c5e5f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/acl.sql @@ -0,0 +1,32 @@ +{#============================Get ACLs=========================#} +{% if vid %} +SELECT + 'datacl' as deftype, COALESCE(gt.rolname, 'public') grantee, + g.rolname grantor, array_agg(privilege_type) as privileges, + array_agg(is_grantable) as grantable +FROM + (SELECT + d.grantee, d.grantor, d.is_grantable, + CASE d.privilege_type + WHEN 'DELETE' THEN 'd' + WHEN 'INSERT' THEN 'a' + WHEN 'REFERENCES' THEN 'x' + WHEN 'SELECT' THEN 'r' + WHEN 'TRIGGER' THEN 't' + WHEN 'UPDATE' THEN 'w' + ELSE 'UNKNOWN' + END AS privilege_type + FROM + (SELECT relacl FROM pg_class cl + LEFT OUTER JOIN pg_shdescription descr ON ( + cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass) + WHERE cl.oid = {{ vid }}::OID AND relkind = 'v' + ) acl, + (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable AS is_grantable, + (d).privilege_type AS privilege_type + FROM (SELECT aclexplode(relacl) as d FROM pg_class cl1 WHERE cl1.oid = {{ vid }}::OID) a) d + ) d + LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/backend_support.sql new file mode 100644 index 0000000..e3f8f4f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/backend_support.sql @@ -0,0 +1,20 @@ +SELECT + CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport +FROM + pg_namespace nsp +WHERE + nsp.oid={{scid}}::int +AND ( + (nspname = 'pg_catalog' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'pgagent' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname = 'information_schema' AND EXISTS + (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) + OR (nspname LIKE '_%' AND EXISTS + (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) +) +AND + nspname NOT LIKE E'pg\\temp\\%' +AND + nspname NOT LIKE E'pg\\toast_temp\\%' diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/create.sql new file mode 100644 index 0000000..2ac1e3a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/create.sql @@ -0,0 +1,25 @@ +{#============================Create new view=========================#} +{% if display_comments %} +-- View: {{ conn|qtIdent(data.schema, data.name) }} + +-- DROP VIEW {{ conn|qtIdent(data.schema, data.name) }}; + +{% endif %} +{% if data.name and data.schema and data.definition %} +CREATE OR REPLACE VIEW {{ conn|qtIdent(data.schema, data.name) }} +{% if ((data.check_option and data.check_option.lower() != 'no') or data.security_barrier) %} +WITH ({% if data.check_option and data.check_option.lower() != 'no' %} +check_option={{ data.check_option }}{% endif %}{{ ', ' if data.check_option and +data.check_option.lower() != 'no' and data.security_barrier else '' }}{% if data.security_barrier %} +security_barrier={{ data.security_barrier }}{% endif %}){% endif %} AS +{{ data.definition.rstrip(';') }}; +{% if data.owner %} + +ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }} + OWNER TO {{ data.owner }}; +{% endif %} +{% if data.comment %} +COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }} + IS {{ data.comment|qtLiteral }}; +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/delete.sql new file mode 100644 index 0000000..8fc4e01 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/delete.sql @@ -0,0 +1,9 @@ +{#============================Drop/Cascade view by name=========================#} +{% if vid %} +SELECT c.relname As name, nsp.nspname + FROM pg_class c + LEFT JOIN pg_namespace nsp on c.relnamespace = nsp.oid + WHERE c.relfilenode = {{ vid }}; +{% elif (name and nspname) %} +DROP VIEW {{ conn|qtIdent(nspname, name) }} {% if cascade %} CASCADE {% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/grant.sql new file mode 100644 index 0000000..9fa7181 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/grant.sql @@ -0,0 +1,14 @@ +{#=====Grant Permissions to User Role on Views/Tables====#} +{% import 'macros/security.macros' as SECLABLE %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{# We will generate Security Label SQL using macro #} +{% if data.seclabels %} +{% for r in data.seclabels %} +{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% if data.datacl %} +{% for priv in data.datacl %} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/properties.sql new file mode 100644 index 0000000..61c4a0e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/properties.sql @@ -0,0 +1,65 @@ +{#============================Fetch View Properties=========================#} +{% if (vid and datlastsysoid) or scid %} +SELECT c.oid, c.xmin, c.relname AS name, c.reltablespace AS spcoid, + c.relispopulated AS ispopulated, spc.spcname, nsp.nspname as schema, + pg_get_userbyid(c.relowner) AS owner, description AS comment, + pg_get_viewdef(c.oid, true) AS definition, + {#=============Checks if it is system view================#} + {% if vid and datlastsysoid %} + CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view, + {% endif %} + {#=============Checks if it is materialized view========#} + CASE WHEN c.relkind = 'm' THEN True ELSE False END As m_view, + (SELECT array_agg(provider || '=' || label) FROM pg_seclabels sl1 WHERE sl1.objoid=c.oid AND sl1.objsubid=0) AS seclabels + , substring(array_to_string(c.reloptions, ',') FROM 'check_option=([a-z]*)') AS check_option + , substring(array_to_string(c.reloptions, ',') FROM 'security_barrier=([a-z|0-9]*)') AS security_barrier + , substring(array_to_string(c.reloptions, ',') FROM 'fillfactor=([0-9]*)') AS fillfactor + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') AS autovacuum_enabled + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS autovacuum_vacuum_threshold + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_vacuum_scale_factor + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS autovacuum_analyze_threshold + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_analyze_scale_factor + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS autovacuum_vacuum_cost_delay + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS autovacuum_vacuum_cost_limit + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS autovacuum_freeze_min_age + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS autovacuum_freeze_max_age + , substring(array_to_string(c.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS autovacuum_freeze_table_age + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_enabled=([a-z|0-9]*)') AS toast_autovacuum_enabled + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS toast_autovacuum_vacuum_threshold + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_vacuum_scale_factor + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_threshold=([0-9]*)') AS toast_autovacuum_analyze_threshold + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_analyze_scale_factor + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS toast_autovacuum_vacuum_cost_delay + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS toast_autovacuum_vacuum_cost_limit + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_min_age=([0-9]*)') AS toast_autovacuum_freeze_min_age + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_max_age=([0-9]*)') AS toast_autovacuum_freeze_max_age + , substring(array_to_string(tst.reloptions, ',') FROM 'autovacuum_freeze_table_age=([0-9]*)') AS toast_autovacuum_freeze_table_age + , c.reloptions AS reloptions, tst.reloptions AS toast_reloptions + , (CASE WHEN c.reltoastrelid = 0 THEN false ELSE true END) AS hastoasttable +FROM pg_class c + LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace + LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace + LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass) + LEFT OUTER JOIN pg_class tst ON tst.oid = c.reltoastrelid + WHERE ((c.relhasrules AND (EXISTS ( + SELECT r.rulename FROM pg_rewrite r + WHERE ((r.ev_class = c.oid) + AND (bpchar(r.ev_type) = '1'::bpchar)) ))) OR (c.relkind = 'v'::char)) +{% if (vid and datlastsysoid) %} + AND c.oid = {{vid}}::oid +{% elif scid %} + AND c.relnamespace = {{scid}}::oid ORDER BY c.relname +{% endif %} + +{% elif type == 'roles' %} +SELECT pr.rolname FROM pg_roles pr WHERE pr.rolcanlogin ORDER BY pr.rolname + +{% elif type == 'schemas' %} +SELECT + nsp.nspname +FROM + pg_namespace nsp +WHERE + (nsp.nspname NOT LIKE E'pg\\_%' + AND nsp.nspname != 'information_schema') +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/update.sql new file mode 100644 index 0000000..1867038 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/update.sql @@ -0,0 +1,81 @@ +{#============================Update View=========================#} +{% import 'macros/security.macros' as SECLABLE %} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data %} +{% set view_name = data.name if data.name else o_data.name %} +{% set view_schema = data.schema if data.schema else o_data.schema %} +{% set def = data.definition.rstrip(';') if data.definition %} +{% if data.name and data.name != o_data.name %} +ALTER VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% endif %} +{% if data.schema and data.schema != o_data.schema %} +ALTER VIEW {{ conn|qtIdent(o_data.schema, view_name ) }} + SET SCHEMA {{ data.schema }}; +{% endif %} +{% if data.owner and data.owner != o_data.owner %} +ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }} + OWNER TO {{ data.owner }}; +{% endif %} +{% if def and def != o_data.definition.rstrip(';') %} +CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }} +{% if (data.check_option and data.check_option != o_data.check_option) + or (data.security_barrier and data.security_barrier != o_data.security_barrier) %} + WITH ({% if (data.check_option and data.check_option != 'no') %}check_option={{ data.check_option }}{% endif %}{{', ' if data.check_option and data.check_option != 'no' and data.security_barrier }}{% if data.security_barrier %}security_barrier={{ data.security_barrier }}{% endif %}) +{% endif %} + AS{{ def }}; +{% else %} +{% if (data.security_barrier and data.security_barrier != o_data.security_barrier) %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} + SET (security_barrier={{ data.security_barrier }}); +{% endif %} +{% if (data.check_option and data.check_option != o_data.check_option and data.check_option != 'no') %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} + SET (check_option={{ data.check_option }}); +{% elif (data.check_option and data.check_option != o_data.check_option and data.check_option == 'no') %} +ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} RESET (check_option); +{% endif %} +{% endif %} +{% if data.comment and data.comment != o_data.comment %} +COMMENT ON VIEW {{ conn|qtIdent(view_schema, view_name) }} + IS {{ data.comment|qtLiteral }}; +{% endif %} +{# The SQL generated below will change privileges #} +{% if data.datacl %} +{% if 'deleted' in data.datacl %} +{% for priv in data.datacl.deleted %} +{{ PRIVILEGE.RESETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.datacl %} +{% for priv in data.datacl.changed %} +{{ PRIVILEGE.RESETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.datacl %} +{% for priv in data.datacl.added %} +{{ PRIVILEGE.APPLY(conn, 'TABLE', priv.grantee, data.name, data.schema, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% endif %} +{# The SQL generated below will change Security Label #} +{% if data.seclabels is not none 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, 'VIEW', data.name, r.provider) }} +{% endfor %} +{% endif %} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} +{{ SECLABLE.APPLY(conn, 'VIEW', 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, 'VIEW', data.name, r.provider, r.security_label) }} +{% endfor %} +{% endif %} +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/view_id.sql new file mode 100644 index 0000000..65648c0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/views/sql/pre_9.1/view_id.sql @@ -0,0 +1,4 @@ +{#==Below will provide view id for last created view==#} +{% if data %} +SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}'; +{% endif %} diff --git a/web/pgadmin/browser/utils.py b/web/pgadmin/browser/utils.py index 6549851..db44224 100644 --- a/web/pgadmin/browser/utils.py +++ b/web/pgadmin/browser/utils.py @@ -491,7 +491,7 @@ class PGChildNodeView(NodeView): type_name = 'table' elif type_str[0] == 'R': type_name = 'rule' - ref_name = _ref_name + ' ON ' + _ref_name + row['ownertable'] + ref_name = _ref_name + ' ON ' + ref_name + row['ownertable'] _ref_name = None elif type_str[0] == 'C': if type_str[1] == 'c':