diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/columns/templates/catalog_object_column/js/catalog_object_column.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/columns/templates/catalog_object_column/js/catalog_object_column.js index c6a796601..88b0be512 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/columns/templates/catalog_object_column/js/catalog_object_column.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/columns/templates/catalog_object_column/js/catalog_object_column.js @@ -19,6 +19,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) { type: 'catalog_object_column', label: '{{ _('catalog_object_column') }}', hasSQL: false, + hasScriptTypes: [], hasDepends: true, Init: function() { /* Avoid mulitple registration of menus */ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/templates/catalog_object/js/catalog_object.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/templates/catalog_object/js/catalog_object.js index aeffb0417..9e361c6dd 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/templates/catalog_object/js/catalog_object.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/templates/catalog_object/js/catalog_object.js @@ -18,6 +18,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) { type: 'catalog_object', label: '{{ _('Catalog Object') }}', hasSQL: false, + hasScriptTypes: [], hasDepends: true, Init: function() { /* Avoid mulitple registration of menus */ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/__init__.py new file mode 100644 index 000000000..1cdc2c0ae --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/__init__.py @@ -0,0 +1,677 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements Package Node""" + +import simplejson as json +from functools import wraps + +import pgadmin.browser.server_groups.servers.databases.schemas as schemas +from flask import render_template, make_response, request, jsonify +from flask_babel import gettext as _ +from pgadmin.browser.server_groups.servers.databases.schemas.utils \ + import SchemaChildModule +from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \ + parse_priv_to_db +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver + +from config import PG_DEFAULT_DRIVER + + +class PackageModule(SchemaChildModule): + """ + class PackageModule(CollectionNodeModule) + + A module class for Package node derived from CollectionNodeModule. + + Methods: + ------- + * __init__(*args, **kwargs) + - Method is used to initialize the PackageModule and it's base module. + + * get_nodes(gid, sid, did) + - Method is used to generate the browser collection node. + + * script_load() + - Load the module script for package, when any of the database node is + initialized. + + * node_inode() + - Method is overridden from its base class to make the node as leaf node. + + """ + + NODE_TYPE = 'package' + COLLECTION_LABEL = _("Packages") + + def __init__(self, *args, **kwargs): + super(PackageModule, self).__init__(*args, **kwargs) + self.min_ver = 90100 + self.max_ver = None + self.server_type = ['ppas'] + + def get_nodes(self, gid, sid, did, scid): + """ + Generate the package node + """ + yield self.generate_browser_collection_node(scid) + + @property + def script_load(self): + """ + Load the module script for schema, when any of the database node is + initialized. + """ + return schemas.SchemaModule.NODE_TYPE + + +blueprint = PackageModule(__name__) + + +class PackageView(PGChildNodeView): + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'pkgid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}, {'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def module_js(self): + """ + This property defines whether javascript exists for this node. + """ + return make_response( + render_template( + "package/js/package.js", + _=_ + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(action=None): + """ + 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 + """ + + def wrap(f): + @wraps(f) + def wrapped(self, *args, **kwargs): + + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + self.qtIdent = driver.qtIdent + + if 'did' in kwargs: + self.conn = self.manager.connection(did=kwargs['did']) + else: + self.conn = self.manager.connection() + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + _( + "Connection to the server has been lost!" + ) + ) + self.template_path = 'package/ppas/9.2_plus' + if self.manager.version < 90200: + self.template_path = 'package/ppas/9.1_plus' + + SQL = render_template("/".join([self.template_path, + 'get_schema.sql']), + scid=kwargs['scid']) + status, rset = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=rset) + + self.schema = rset + # Allowed ACL on package + self.acl = ['X'] + + return f(self, *args, **kwargs) + + return wrapped + + return wrap + + @check_precondition(action='list') + def list(self, gid, sid, did, scid): + """ + This function is used to list all the package nodes within the collection. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + + Returns: + + """ + 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(action='nodes') + def nodes(self, gid, sid, did, scid): + """ + This function is used to create all the child nodes within the collection. + Here it will create all the package nodes. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + + Returns: + + """ + res = [] + SQL = render_template("/".join([self.template_path, 'nodes.sql']), scid=scid) + status, rset = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + sid, + row['name'], + icon="icon-%s" % self.node_type + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition(action='properties') + def properties(self, gid, sid, did, scid, pkgid): + """ + This function will show the properties of the selected package node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + pkgid: Package ID + + Returns: + + """ + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid, pkgid=pkgid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=res) + + res['rows'][0]['pkgheadsrc'] = self.get_inner(res['rows'][0]['pkgheadsrc']) + res['rows'][0]['pkgbodysrc'] = self.get_inner(res['rows'][0]['pkgbodysrc']) + + SQL = render_template("/".join([self.template_path, 'acl.sql']), + scid=scid, + pkgid=pkgid) + status, rset1 = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=rset1) + + for row in rset1['rows']: + priv = parse_priv_from_db(row) + res['rows'][0].setdefault(row['deftype'], []).append(priv) + + return ajax_response( + response=res['rows'][0], + status=200 + ) + + @check_precondition(action="create") + def create(self, gid, sid, did, scid): + """ + Create the package. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + + Returns: + + """ + required_args = [ + u'name', + u'pkgheadsrc' + ] + + data = request.form if request.form else json.loads( + request.data, encoding='utf-8' + ) + + for arg in required_args: + if arg not in data: + return make_json_response( + status=400, + success=0, + errormsg=_( + "Could not find the required parameter (%s)." % arg + ) + ) + try: + data['schema'] = self.schema + # The SQL below will execute CREATE DDL only + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn) + status, msg = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=msg) + + # We need oid of newly created package. + SQL = render_template("/".join([self.template_path, 'get_oid.sql']), + name=data['name'], scid=scid) + + SQL = SQL.strip('\n').strip(' ') + if SQL and SQL != "": + status, pkgid = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=pkgid) + + return jsonify( + node=self.blueprint.generate_browser_node( + pkgid, + scid, + data['name'], + icon="icon-%s" % self.node_type + ) + ) + + except Exception as e: + return make_json_response( + status=500, + success=0, + errormsg=str(e) + ) + + @check_precondition(action='delete') + def delete(self, gid, sid, did, scid, pkgid): + """ + This function will drop the object + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + pkgid: Package ID + + Returns: + + """ + # Below will decide if it's simple drop or drop with cascade call + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid, pkgid=pkgid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + if not res['rows']: + return make_json_response( + success=0, + errormsg=_( + 'Error: Object not found.' + ), + info=_( + 'The specified package could not be found.\n' + ) + ) + + res['rows'][0]['schema'] = self.schema + + SQL = render_template("/".join([self.template_path, 'delete.sql']), + data=res['rows'][0], + 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=_("Package dropped"), + data={ + 'id': pkgid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition(action='update') + def update(self, gid, sid, did, scid, pkgid): + """ + This function will update the object + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + pkgid: Package ID + + Returns: + + """ + data = request.form if request.form else json.loads( + request.data, encoding='utf-8' + ) + try: + SQL = self.getSQL(gid, sid, did, data, scid, pkgid) + SQL = SQL.strip('\n').strip(' ') + if SQL != "": + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Package updated", + data={ + 'id': pkgid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': pkgid, + 'scid': scid, + 'sid': sid, + 'gid': gid, + 'did': did + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition(action='msql') + def msql(self, gid, sid, did, scid, pkgid=None): + """ + This function to return modified SQL. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + pkgid: Package ID + """ + + data = {} + for k, v in request.args.items(): + try: + data[k] = json.loads(v, encoding='utf-8') + except (ValueError, TypeError, KeyError): + data[k] = v + + if pkgid is None: + required_args = [ + 'name', + 'pkgheadsrc' + ] + + for arg in required_args: + if arg not in data: + return make_json_response( + status=400, + success=0, + errormsg=_( + "Could not find the required parameter (%s)." % arg + ) + ) + try: + SQL = self.getSQL(gid, sid, did, data, scid, pkgid) + SQL = SQL.strip('\n').strip(' ') + return make_json_response( + data=SQL, + status=200 + ) + except Exception as e: + return make_json_response( + data="-- modified SQL", + status=200 + ) + + def getSQL(self, gid, sid, did, data, scid, pkgid=None): + """ + This function will generate sql from model data. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + pkgid: Package ID + """ + + required_args = [ + u'name' + ] + + data['schema'] = self.schema + + if pkgid is not None: + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid, pkgid=pkgid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + res['rows'][0]['pkgheadsrc'] = self.get_inner(res['rows'][0]['pkgheadsrc']) + res['rows'][0]['pkgbodysrc'] = self.get_inner(res['rows'][0]['pkgbodysrc']) + + SQL = render_template("/".join([self.template_path, 'acl.sql']), + scid=scid, + pkgid=pkgid) + + status, rset1 = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=rset1) + + for row in rset1['rows']: + priv = parse_priv_from_db(row) + res['rows'][0].setdefault(row['deftype'], []).append(priv) + + # Making copy of output for further processing + old_data = dict(res['rows'][0]) + + # To format privileges data coming from client + for key in ['pkgacl']: + if key in data and data[key] is not None: + if 'added' in data[key]: + data[key]['added'] = parse_priv_to_db(data[key]['added'], self.acl) + if 'changed' in data[key]: + data[key]['changed'] = parse_priv_to_db(data[key]['changed'], self.acl) + if 'deleted' in data[key]: + data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], self.acl) + + # If name is not present with in update data then copy it + # from old data + for arg in required_args: + if arg not in data: + data[arg] = old_data[arg] + + SQL = render_template("/".join([self.template_path, 'update.sql']), + data=data, o_data=old_data, conn=self.conn) + else: + # To format privileges coming from client + if 'pkgacl' in data: + data['pkgacl'] = parse_priv_to_db(data['pkgacl'], self.acl) + + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn) + + return SQL + + @check_precondition(action="sql") + def sql(self, gid, sid, did, scid, pkgid): + """ + This function will generate sql for sql panel + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + pkgid: Package ID + """ + + SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid, pkgid=pkgid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + res['rows'][0]['pkgheadsrc'] = self.get_inner(res['rows'][0]['pkgheadsrc']) + res['rows'][0]['pkgbodysrc'] = self.get_inner(res['rows'][0]['pkgbodysrc']) + + SQL = render_template("/".join([self.template_path, 'acl.sql']), + scid=scid, + pkgid=pkgid) + status, rset1 = self.conn.execute_dict(SQL) + + if not status: + return internal_server_error(errormsg=rset1) + + for row in rset1['rows']: + priv = parse_priv_from_db(row) + res['rows'][0].setdefault(row['deftype'], []).append(priv) + + result = res['rows'][0] + sql = self.getSQL(gid, sid, did, result, scid) + sql = sql.strip('\n').strip(' ') + + sql_header = "-- Package: {}\n\n-- ".format(self.qtIdent(self.conn, + self.schema, + result['name'])) + if hasattr(str, 'decode'): + sql_header = sql_header.decode('utf-8') + + sql_header += render_template( + "/".join([self.template_path, 'delete.sql']), + data=result) + sql_header += "\n\n" + + sql = sql_header + sql + + return ajax_response(response=sql) + + @check_precondition(action="dependents") + def dependents(self, gid, sid, did, scid, pkgid): + """ + This function gets the dependents and returns an ajax response + for the package node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + pkgid: Package ID + """ + dependents_result = self.get_dependents(self.conn, pkgid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition(action="dependencies") + def dependencies(self, gid, sid, did, scid, pkgid): + """ + This function gets the dependencies and returns an ajax response + for the package node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + pkgid: Package ID + """ + dependencies_result = self.get_dependencies(self.conn, pkgid) + + return ajax_response( + response=dependencies_result, + status=200 + ) + + @staticmethod + def get_inner(sql): + if sql is None: + return None + + sql = sql.lower() + start = sql.find('is') + if start == -1: + start = sql.find('as') + + end = max(sql.rfind('end;'), sql.rfind('end')) + + if start == -1: + return sql[0: end].strip("\n") + return sql[start+2: end].strip("\n") + +PackageView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/__init__.py new file mode 100644 index 000000000..2c95c9cff --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/__init__.py @@ -0,0 +1,664 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements Edb Functions/Edb Procedures Node.""" + +import copy +from functools import wraps + +import pgadmin.browser.server_groups.servers.databases.schemas.packages as packages +from flask import render_template, make_response +from flask_babel import gettext +from pgadmin.browser.collection import CollectionNodeModule +from pgadmin.browser.server_groups.servers.databases.schemas.utils import \ + DataTypeReader +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error, gone +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver + +from config import PG_DEFAULT_DRIVER + + +class EdbFuncModule(CollectionNodeModule): + """ + class EdbFuncModule(CollectionNodeModule): + + This class represents The Functions Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Functions Module. + + * get_nodes(gid, sid, did, scid) + - Generate the Functions collection node. + + * node_inode(): + - Returns Functions node as leaf node. + + * script_load() + - Load the module script for Functions, when schema node is + initialized. + + * csssnippets() + - Returns a snippet of css. + """ + + NODE_TYPE = 'edbfunc' + COLLECTION_LABEL = gettext("Functions") + + def __init__(self, *args, **kwargs): + """ + Initialize the Function Module. + Args: + *args: + **kwargs: + """ + super(EdbFuncModule, self).__init__(*args, **kwargs) + + self.min_ver = 90100 + self.max_ver = None + self.server_type = ['ppas'] + + def get_nodes(self, gid, sid, did, scid, pkgid): + """ + Generate Functions collection node. + """ + yield self.generate_browser_collection_node(pkgid) + + @property + def script_load(self): + """ + Load the module script for Functions, when the + package node is initialized. + """ + return packages.PackageModule.NODE_TYPE + + @property + def node_inode(self): + """ + Make the node as leaf node. + Returns: + False as this node doesn't have child nodes. + """ + return False + +blueprint = EdbFuncModule(__name__) + + +class EdbFuncView(PGChildNodeView, DataTypeReader): + """ + class EdbFuncView(PGChildNodeView, DataTypeReader) + + This class inherits PGChildNodeView and DataTypeReader to get the different routes for + the module. + + The class is responsible to Create, Read, Update and Delete operations for + the Functions. + + Methods: + ------- + * validate_request(f): + - Works as a decorator. + Validating request on the request of create, update and modified SQL. + + * module_js(): + - Overrides this property to define javascript for Functions node. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, pkgid): + - List the Functions. + + * nodes(gid, sid, did, scid, pkgid): + - Returns all the Functions to generate Nodes in the browser. + + * properties(gid, sid, did, scid, pkgid, edbfnid): + - Returns the Functions properties. + + * sql(gid, sid, did, scid, pkgid, edbfnid): + - Returns the SQL for the Functions object. + + * dependents(gid, sid, did, scid, ,pkgid, edbfnid): + - Returns the dependents for the Functions object. + + * dependencies(gid, sid, did, scid, pkgid, edbfnid): + - Returns the dependencies for the Functions object. + + """ + + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'}, + {'type': 'int', 'id': 'pkgid'} + ] + ids = [ + {'type': 'int', 'id': 'edbfnid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties'}, + {'get': 'list'} + ], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def module_js(self): + """ + Load JS file (functions.js) for this module. + """ + + return make_response( + render_template( + "edbfunc/js/edbfunc.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks the database connection status. + Attaches the connection object and template path to the class object. + """ + + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + + # Get database connection + self.conn = self.manager.connection(did=kwargs['did']) + + self.qtIdent = driver.qtIdent + self.qtLiteral = driver.qtLiteral + + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + # Set template path for sql scripts depending + # on the server version. + + ver = self.manager.version + + # Set template path for sql scripts depending + # on the server version. + + self.sql_template_path = "/".join([ + self.node_type, + self.manager.server_type, + '9.2_plus' if ver >= 90200 else + '9.1_plus' + ]) + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid, pkgid): + """ + List all the Functions. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + SQL = render_template("/".join([self.sql_template_path, 'node.sql']), + pkgid=pkgid) + 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, pkgid): + """ + Returns all the Functions to generate the Nodes. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + """ + + res = [] + SQL = render_template("/".join([self.sql_template_path, + 'node.sql']), pkgid=pkgid) + 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'], + pkgid, + row['name'], + icon="icon-" + self.node_type + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, pkgid, edbfnid=None): + """ + Returns the Function properties. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + pkgid: Package Id + edbfnid: Function Id + """ + SQL = render_template("/".join([self.sql_template_path, + 'properties.sql']), + pkgid=pkgid, edbfnid=edbfnid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + if len(res['rows']) == 0: + return gone(gettext(""" +Could not find the function in the database.\n +It may have been removed by another user or moved to another schema. +""")) + + resp_data = res['rows'][0] + + # Get formatted Arguments + frmtd_params, frmtd_proargs = self._format_arguments_from_db(resp_data) + resp_data.update(frmtd_params) + resp_data.update(frmtd_proargs) + + return ajax_response( + response=resp_data, + status=200 + ) + + def _format_arguments_from_db(self, data): + """ + Create Argument list of the Function. + + Args: + data: Function Data + + Returns: + Function Arguments in the following format. + [ + {'proargtypes': 'integer', 'proargmodes: 'IN', + 'proargnames': 'column1', 'proargdefaultvals': 1}, {...} + ] + Where + Arguments: + proargtypes: Argument Types (Data Type) + proargmodes: Argument Modes [IN, OUT, INOUT, VARIADIC] + proargnames: Argument Name + proargdefaultvals: Default Value of the Argument + """ + proargtypes = [ptype for ptype in data['proargtypenames'].split(",")] \ + if data['proargtypenames'] else [] + proargmodes = data['proargmodes'] if data['proargmodes'] else [] + proargnames = data['proargnames'] if data['proargnames'] else [] + proargdefaultvals = [ptype for ptype in + data['proargdefaultvals'].split(",")] \ + if data['proargdefaultvals'] else [] + proallargtypes = data['proallargtypes'] \ + if data['proallargtypes'] else [] + + proargmodenames = {'i': 'IN', 'o': 'OUT', 'b': 'INOUT', + 'v': 'VARIADIC', 't': 'TABLE'} + + # The proargtypes doesn't give OUT params, so we need to fetch + # those from database explicitly, below code is written for this + # purpose. + # + # proallargtypes gives all the Function's argument including OUT, + # but we have not used that column; as the data type of this + # column (i.e. oid[]) is not supported by oidvectortypes(oidvector) + # function which we have used to fetch the datatypes + # of the other parameters. + + proargmodes_fltrd = copy.deepcopy(proargmodes) + proargnames_fltrd = [] + cnt = 0 + for m in proargmodes: + if m == 'o': # Out Mode + SQL = render_template("/".join([self.sql_template_path, + 'get_out_types.sql']), + out_arg_oid=proallargtypes[cnt]) + status, out_arg_type = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=out_arg_type) + + # Insert out parameter datatype + proargtypes.insert(cnt, out_arg_type) + proargdefaultvals.insert(cnt, '') + elif m == 'v': # Variadic Mode + proargdefaultvals.insert(cnt, '') + elif m == 't': # Table Mode + proargmodes_fltrd.remove(m) + proargnames_fltrd.append(proargnames[cnt]) + + cnt += 1 + + cnt = 0 + # Map param's short form to its actual name. (ex: 'i' to 'IN') + for m in proargmodes_fltrd: + proargmodes_fltrd[cnt] = proargmodenames[m] + cnt += 1 + + # Removes Argument Names from the list if that argument is removed + # from the list + for i in proargnames_fltrd: + proargnames.remove(i) + + # Insert null value against the parameters which do not have + # default values. + if len(proargmodes_fltrd) > len(proargdefaultvals): + dif = len(proargmodes_fltrd) - len(proargdefaultvals) + while (dif > 0): + proargdefaultvals.insert(0, '') + dif = dif - 1 + + # Prepare list of Argument list dict to be displayed in the Data Grid. + params = {"arguments": [ + self._map_arguments_dict( + i, proargmodes_fltrd[i] if len(proargmodes_fltrd) > i else '', + proargtypes[i] if len(proargtypes) > i else '', + proargnames[i] if len(proargnames) > i else '', + proargdefaultvals[i] if len(proargdefaultvals) > i else '' + ) + for i in range(len(proargtypes))]} + + # Prepare string formatted Argument to be displayed in the Properties + # panel. + + proargs = [self._map_arguments_list( + proargmodes_fltrd[i] if len(proargmodes_fltrd) > i else '', + proargtypes[i] if len(proargtypes) > i else '', + proargnames[i] if len(proargnames) > i else '', + proargdefaultvals[i] if len(proargdefaultvals) > i else '' + ) + for i in range(len(proargtypes))] + + proargs = {"proargs": ", ".join(proargs)} + + return params, proargs + + def _map_arguments_dict(self, argid, argmode, argtype, argname, argdefval): + """ + Returns Dict of formatted Arguments. + Args: + argid: Argument Sequence Number + argmode: Argument Mode + argname: Argument Name + argtype: Argument Type + argdef: Argument Default Value + """ + # The pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) SQL + # statement gives us '-' as a default value for INOUT mode. + # so, replacing it with empty string. + if argmode == 'INOUT' and argdefval.strip() == '-': + argdefval = '' + + return {"argid": argid, + "argtype": argtype.strip() if argtype is not None else '', + "argmode": argmode, + "argname": argname, + "argdefval": argdefval} + + def _map_arguments_list(self, argmode, argtype, argname, argdef): + """ + Returns List of formatted Arguments. + Args: + argmode: Argument Mode + argname: Argument Name + argtype: Argument Type + argdef: Argument Default Value + """ + # The pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) SQL + # statement gives us '-' as a default value for INOUT mode. + # so, replacing it with empty string. + if argmode == 'INOUT' and argdef.strip() == '-': + argdef = '' + + arg = '' + + if argmode and argmode: + arg += argmode + " " + if argname: + arg += argname + " " + if argtype: + arg += argtype + " " + if argdef: + arg += " DEFAULT " + argdef + + return arg.strip(" ") + + @check_precondition + def sql(self, gid, sid, did, scid, pkgid, edbfnid=None): + """ + Returns the SQL for the Function object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + fnid: Function Id + """ + SQL = render_template("/".join([self.sql_template_path, 'get_body.sql']), + scid=scid, + pkgid=pkgid) + + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + body = self.get_inner(res['rows'][0]['pkgbodysrc']) + + if body is None: + body = '' + + SQL = render_template("/".join([self.sql_template_path, + 'get_name.sql']), + edbfnid=edbfnid) + + status, name = self.conn.execute_scalar(SQL) + + if not status: + return internal_server_error(errormsg=res) + + sql = "-- Package {}: {}".format( + 'Function' if self.node_type == 'edbfunc' else 'Procedure', + name) + if hasattr(str, 'decode'): + sql = sql.decode('utf-8') + if body != '': + sql += "\n\n" + sql += body + + return ajax_response(response=sql) + + @check_precondition + def dependents(self, gid, sid, did, scid, pkgid, edbfnid): + """ + This function get the dependents and return ajax response + for the Function node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Function Id + """ + dependents_result = self.get_dependents(self.conn, edbfnid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, scid, pkgid, edbfnid): + """ + This function get the dependencies and return ajax response + for the Function node. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + doid: Function Id + """ + dependencies_result = self.get_dependencies(self.conn, edbfnid) + return ajax_response( + response=dependencies_result, + status=200 + ) + + @staticmethod + def get_inner(sql): + if sql is None: + return None + + sql = sql.lower() + start = sql.find('is') + if start == -1: + start = sql.find('as') + + end = max(sql.rfind('end;'), sql.rfind('end')) + + if start == -1: + return sql[0: end].strip("\n") + return sql[start+2: end].strip("\n") + +EdbFuncView.register_node_view(blueprint) + + +class EdbProcModule(CollectionNodeModule): + """ + class EdbProcModule(CollectionNodeModule): + + This class represents The Procedures Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Procedures Module. + + * get_nodes(gid, sid, did, scid) + - Generate the Procedures collection node. + + * node_inode(): + - Returns Procedures node as leaf node. + + * script_load() + - Load the module script for Procedures, when schema node is + initialized. + + """ + + NODE_TYPE = 'edbproc' + COLLECTION_LABEL = gettext("Procedures") + + def __init__(self, *args, **kwargs): + """ + Initialize the Procedure Module. + Args: + *args: + **kwargs: + """ + super(EdbProcModule, self).__init__(*args, **kwargs) + + self.min_ver = 90100 + self.max_ver = None + self.server_type = ['ppas'] + + def get_nodes(self, gid, sid, did, scid, pkgid): + """ + Generate Procedures collection node. + """ + yield self.generate_browser_collection_node(pkgid) + + @property + def node_inode(self): + """ + Make the node as leaf node. + Returns: + False as this node doesn't have child nodes. + """ + return False + + @property + def script_load(self): + """ + Load the module script for Procedures, when the + database node is initialized. + """ + return packages.PackageModule.NODE_TYPE + + +procedure_blueprint = EdbProcModule(__name__) + + +class EdbProcView(EdbFuncView): + node_type = procedure_blueprint.node_type + + def module_js(self): + """ + Load JS file (procedures.js) for this module. + """ + + return make_response( + render_template( + "edbproc/js/edbproc.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + +EdbProcView.register_node_view(procedure_blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/img/coll-edbfunc.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/img/coll-edbfunc.png new file mode 100644 index 000000000..c44874e55 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/img/coll-edbfunc.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/img/coll-edbproc.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/img/coll-edbproc.png new file mode 100644 index 000000000..7c13a9bc7 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/img/coll-edbproc.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/img/edbfunc.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/img/edbfunc.png new file mode 100644 index 000000000..656854a01 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/img/edbfunc.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/img/edbproc.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/img/edbproc.png new file mode 100644 index 000000000..8b65dff9e Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/static/img/edbproc.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/css/edbfunc.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/css/edbfunc.css new file mode 100644 index 000000000..16b2e7198 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/css/edbfunc.css @@ -0,0 +1,3 @@ +.functions_code { + height: 130px !important; +} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/js/edbfunc.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/js/edbfunc.js new file mode 100644 index 000000000..88c3408a1 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/js/edbfunc.js @@ -0,0 +1,112 @@ +/* Create and Register Function Collection and Node. */ +define( + ['jquery', 'underscore', 'underscore.string', + 'pgadmin', 'pgadmin.browser', 'alertify', + 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + if (!pgBrowser.Nodes['coll-edbfunc']) { + pgBrowser.Nodes['coll-edbfunc'] = + pgBrowser.Collection.extend({ + node: 'edbfunc', + label: '{{ _('Functions') }}', + type: 'coll-edbfunc', + columns: ['name', 'funcowner', 'description'] + }); + }; + + if (!pgBrowser.Nodes['edbfunc']) { + pgBrowser.Nodes['edbfunc'] = pgBrowser.Node.extend({ + type: 'edbfunc', + dialogHelp: '{{ url_for('help.static', filename='edbfunc_dialog.html') }}', + label: '{{ _('Function') }}', + collection_type: 'coll-edbfunc', + hasDepends: true, + canEdit: false, + hasSQL: true, + hasScriptTypes: [], + parent_type: ['package'], + Init: function(args) { + /* Avoid multiple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + + }, + canDrop: false, + canDropCascade: false, + model: pgBrowser.Node.Model.extend({ + defaults: { + name: undefined, + oid: undefined, + funcowner: undefined, + pronargs: undefined, /* Argument Count */ + proargs: undefined, /* Arguments */ + proargtypenames: undefined, /* Argument Signature */ + prorettypename: undefined, /* Return Type */ + lanname: 'sql', /* Language Name in which function is being written */ + prosrc: undefined, + proacl: undefined, + visibility: 'Unknown' + }, + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties'], + disabled: true + },{ + id: 'oid', label: '{{ _('OID') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'funcowner', label: '{{ _('Owner') }}', cell: 'string', + type: 'text', disabled: true + },{ + id: 'pronargs', label: '{{ _('Argument count') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', mode: ['properties'] + },{ + id: 'proargs', label: '{{ _('Arguments') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', mode: ['properties'], + disabled: true + },{ + id: 'proargtypenames', label: '{{ _('Signature arguments') }}', cell: + 'string', type: 'text', group: '{{ _('Definition') }}', mode: ['properties'], + disabled: true + },{ + id: 'prorettypename', label: '{{ _('Return type') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', disabled: true, + mode: ['properties'], visible: 'isVisible' + },{ + id: 'visibility', label: '{{ _('Visibility') }}', cell: 'string', + type: 'text', mode: ['properties'], + disabled: true + },{ + id: 'lanname', label: '{{ _('Language') }}', cell: 'string', + type: 'text', group: '{{ _('Definition') }}', disabled: true + },{ + id: 'prosrc', label: '{{ _('Code') }}', cell: 'string', + type: 'text', mode: ['properties'], + group: '{{ _('Definition') }}', + control: Backform.SqlFieldControl, + extraClasses:['custom_height_css_class'], + visible: function(m) { + if (m.get('lanname') == 'c') { + return false; + } + return true; + }, disabled: true + }], + validate: function() + { + return null; + }, + isVisible: function(m){ + if (this.name == 'sysproc') { return false; } + return true; + } + }) + }); + + } + + return pgBrowser.Nodes['edbfunc']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/get_body.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/get_body.sql new file mode 100644 index 000000000..ad47f2e12 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/get_body.sql @@ -0,0 +1,5 @@ +SELECT pg_catalog.edb_get_packagebodydef(nsp.oid) AS pkgbodysrc +FROM pg_namespace nsp +LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) +WHERE nspparent = {{scid}}::oid +AND nsp.oid = {{pkgid}}::oid; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/get_name.sql new file mode 100644 index 000000000..7410b7173 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/get_name.sql @@ -0,0 +1,3 @@ +SELECT proname AS name +FROM pg_proc +WHERE oid = {{edbfnid}}::oid diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/get_oid.sql new file mode 100644 index 000000000..2bc76a2e4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/get_oid.sql @@ -0,0 +1,17 @@ +SELECT + pr.oid, pr.proname || '(' || COALESCE(pg_catalog + .pg_get_function_identity_arguments(pr.oid), '') || ')' as name, + lanname, pg_get_userbyid(proowner) as funcowner +FROM + pg_proc pr +JOIN + pg_type typ ON typ.oid=prorettype +JOIN + pg_language lng ON lng.oid=prolang +JOIN + pg_namespace nsp ON nsp.oid=pr.pronamespace + AND nsp.nspname={{ nspname|qtLiteral }} +WHERE + proisagg = FALSE + AND typname NOT IN ('trigger', 'event_trigger') + AND pr.proname = {{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/get_schema.sql new file mode 100644 index 000000000..127d4b9db --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/get_schema.sql @@ -0,0 +1,6 @@ +SELECT + nspname +FROM + pg_namespace +WHERE + oid = {{ scid }}::oid; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/node.sql new file mode 100644 index 000000000..e55741ddb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/node.sql @@ -0,0 +1,6 @@ +SELECT pg_proc.oid, + pg_proc.proname || '(' || COALESCE(pg_catalog.pg_get_function_identity_arguments(pg_proc.oid), '') || ')' AS name +FROM pg_proc, pg_namespace +WHERE format_type(prorettype, NULL) != 'void' +AND pronamespace = {{pkgid}}::oid +AND pg_proc.pronamespace = pg_namespace.oid \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/properties.sql new file mode 100644 index 000000000..6576daf86 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/properties.sql @@ -0,0 +1,27 @@ +SELECT pg_proc.oid, + proname AS name, + pronargs, + proallargtypes, + proargnames AS argnames, + pronargdefaults, + oidvectortypes(proargtypes) AS proargtypenames, + proargmodes, + proargnames, + pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals, + pg_get_userbyid(proowner) AS funcowner, + pg_get_function_result(pg_proc.oid) AS prorettypename, + prosrc, + lanname, + CASE + WHEN proaccess = '+' THEN 'Public' + WHEN proaccess = '-' THEN 'Private' + ELSE 'Unknown' END AS visibility +FROM pg_proc, pg_namespace, pg_language lng +WHERE format_type(prorettype, NULL) != 'void' +AND pronamespace = {{pkgid}}::oid +AND pg_proc.pronamespace = pg_namespace.oid +AND lng.oid=prolang +{% if edbfnid %} +AND pg_proc.oid = {{edbfnid}}::oid +{% endif %} + ORDER BY name diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/stats.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/stats.sql new file mode 100644 index 000000000..2e276b65a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.1_plus/stats.sql @@ -0,0 +1,8 @@ +SELECT + calls AS {{ conn|qtIdent(_('Number of calls')) }}, + total_time AS {{ conn|qtIdent(_('Total time')) }}, + self_time AS {{ conn|qtIdent(_('Self time')) }} +FROM + pg_stat_user_functions +WHERE + funcid = {{fnid}}::OID diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/get_body.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/get_body.sql new file mode 100644 index 000000000..72675f4ab --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/get_body.sql @@ -0,0 +1,6 @@ +SELECT pg_catalog.edb_get_packagebodydef(nsp.oid) AS pkgbodysrc +FROM pg_namespace nsp +LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) +WHERE nspparent = {{scid}}::oid +AND nsp.oid = {{pkgid}}::oid +AND nspobjecttype = 0; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/get_name.sql new file mode 100644 index 000000000..7410b7173 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/get_name.sql @@ -0,0 +1,3 @@ +SELECT proname AS name +FROM pg_proc +WHERE oid = {{edbfnid}}::oid diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/get_oid.sql new file mode 100644 index 000000000..2bc76a2e4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/get_oid.sql @@ -0,0 +1,17 @@ +SELECT + pr.oid, pr.proname || '(' || COALESCE(pg_catalog + .pg_get_function_identity_arguments(pr.oid), '') || ')' as name, + lanname, pg_get_userbyid(proowner) as funcowner +FROM + pg_proc pr +JOIN + pg_type typ ON typ.oid=prorettype +JOIN + pg_language lng ON lng.oid=prolang +JOIN + pg_namespace nsp ON nsp.oid=pr.pronamespace + AND nsp.nspname={{ nspname|qtLiteral }} +WHERE + proisagg = FALSE + AND typname NOT IN ('trigger', 'event_trigger') + AND pr.proname = {{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/get_schema.sql new file mode 100644 index 000000000..127d4b9db --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/get_schema.sql @@ -0,0 +1,6 @@ +SELECT + nspname +FROM + pg_namespace +WHERE + oid = {{ scid }}::oid; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/node.sql new file mode 100644 index 000000000..e55741ddb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/node.sql @@ -0,0 +1,6 @@ +SELECT pg_proc.oid, + pg_proc.proname || '(' || COALESCE(pg_catalog.pg_get_function_identity_arguments(pg_proc.oid), '') || ')' AS name +FROM pg_proc, pg_namespace +WHERE format_type(prorettype, NULL) != 'void' +AND pronamespace = {{pkgid}}::oid +AND pg_proc.pronamespace = pg_namespace.oid \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/properties.sql new file mode 100644 index 000000000..6576daf86 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/properties.sql @@ -0,0 +1,27 @@ +SELECT pg_proc.oid, + proname AS name, + pronargs, + proallargtypes, + proargnames AS argnames, + pronargdefaults, + oidvectortypes(proargtypes) AS proargtypenames, + proargmodes, + proargnames, + pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals, + pg_get_userbyid(proowner) AS funcowner, + pg_get_function_result(pg_proc.oid) AS prorettypename, + prosrc, + lanname, + CASE + WHEN proaccess = '+' THEN 'Public' + WHEN proaccess = '-' THEN 'Private' + ELSE 'Unknown' END AS visibility +FROM pg_proc, pg_namespace, pg_language lng +WHERE format_type(prorettype, NULL) != 'void' +AND pronamespace = {{pkgid}}::oid +AND pg_proc.pronamespace = pg_namespace.oid +AND lng.oid=prolang +{% if edbfnid %} +AND pg_proc.oid = {{edbfnid}}::oid +{% endif %} + ORDER BY name diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/stats.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/stats.sql new file mode 100644 index 000000000..2e276b65a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbfunc/ppas/9.2_plus/stats.sql @@ -0,0 +1,8 @@ +SELECT + calls AS {{ conn|qtIdent(_('Number of calls')) }}, + total_time AS {{ conn|qtIdent(_('Total time')) }}, + self_time AS {{ conn|qtIdent(_('Self time')) }} +FROM + pg_stat_user_functions +WHERE + funcid = {{fnid}}::OID diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/js/edbproc.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/js/edbproc.js new file mode 100644 index 000000000..dc035b19f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/js/edbproc.js @@ -0,0 +1,65 @@ +/* Create and Register Procedure Collection and Node. */ +define( + ['jquery', 'underscore', 'underscore.string', + 'pgadmin', 'pgadmin.browser', 'alertify', + 'pgadmin.node.edbfunc', 'pgadmin.browser.collection', + 'pgadmin.browser.server.privilege'], +function($, _, S, pgAdmin, pgBrowser, alertify, EdbFunction) { + + if (!pgBrowser.Nodes['coll-edbproc']) { + pgAdmin.Browser.Nodes['coll-edbproc'] = + pgAdmin.Browser.Collection.extend({ + node: 'edbproc', + label: '{{ _('Procedures') }}', + type: 'coll-edbproc', + columns: ['name', 'funcowner', 'description'], + hasStatistics: true + }); + }; + + // Inherit Functions Node + if (!pgBrowser.Nodes['edbproc']) { + pgAdmin.Browser.Nodes['edbproc'] = pgBrowser.Node.extend({ + type: 'edbproc', + dialogHelp: '{{ url_for('help.static', filename='edbproc_dialog.html') }}', + label: '{{ _('Procedure') }}', + collection_type: 'coll-edbproc', + hasDepends: true, + canEdit: false, + hasSQL: true, + hasScriptTypes: [], + parent_type: ['package'], + Init: function() { + /* Avoid multiple registration of menus */ + if (this.proc_initialized) + return; + + this.proc_initialized = true; + + }, + canDrop: false, + canDropCascade: false, + model: EdbFunction.model.extend({ + defaults: _.extend({}, + EdbFunction.model.prototype.defaults, + { + lanname: 'edbspl' + } + ), + isVisible: function(m){ + if (this.name == 'sysfunc') { return false; } + else if (this.name == 'sysproc') { return true; } + return false; + }, + validate: function() + { + return null; + } + } + ) + }); + + } + + return pgBrowser.Nodes['edbproc']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/get_body.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/get_body.sql new file mode 100644 index 000000000..ad47f2e12 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/get_body.sql @@ -0,0 +1,5 @@ +SELECT pg_catalog.edb_get_packagebodydef(nsp.oid) AS pkgbodysrc +FROM pg_namespace nsp +LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) +WHERE nspparent = {{scid}}::oid +AND nsp.oid = {{pkgid}}::oid; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/get_name.sql new file mode 100644 index 000000000..7410b7173 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/get_name.sql @@ -0,0 +1,3 @@ +SELECT proname AS name +FROM pg_proc +WHERE oid = {{edbfnid}}::oid diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/get_oid.sql new file mode 100644 index 000000000..2bc76a2e4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/get_oid.sql @@ -0,0 +1,17 @@ +SELECT + pr.oid, pr.proname || '(' || COALESCE(pg_catalog + .pg_get_function_identity_arguments(pr.oid), '') || ')' as name, + lanname, pg_get_userbyid(proowner) as funcowner +FROM + pg_proc pr +JOIN + pg_type typ ON typ.oid=prorettype +JOIN + pg_language lng ON lng.oid=prolang +JOIN + pg_namespace nsp ON nsp.oid=pr.pronamespace + AND nsp.nspname={{ nspname|qtLiteral }} +WHERE + proisagg = FALSE + AND typname NOT IN ('trigger', 'event_trigger') + AND pr.proname = {{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/get_schema.sql new file mode 100644 index 000000000..127d4b9db --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/get_schema.sql @@ -0,0 +1,6 @@ +SELECT + nspname +FROM + pg_namespace +WHERE + oid = {{ scid }}::oid; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/node.sql new file mode 100644 index 000000000..66405985c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/node.sql @@ -0,0 +1,6 @@ +SELECT pg_proc.oid, + pg_proc.proname || '(' || COALESCE(pg_catalog.pg_get_function_identity_arguments(pg_proc.oid), '') || ')' AS name +FROM pg_proc, pg_namespace +WHERE format_type(prorettype, NULL) = 'void' +AND pronamespace = {{pkgid}}::oid +AND pg_proc.pronamespace = pg_namespace.oid \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/properties.sql new file mode 100644 index 000000000..067723361 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/properties.sql @@ -0,0 +1,27 @@ +SELECT pg_proc.oid, + proname AS name, + pronargs, + proallargtypes, + proargnames AS argnames, + pronargdefaults, + oidvectortypes(proargtypes) AS proargtypenames, + proargmodes, + proargnames, + pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals, + pg_get_userbyid(proowner) AS funcowner, + pg_get_function_result(pg_proc.oid) AS prorettypename, + prosrc, + lanname, + CASE + WHEN proaccess = '+' THEN 'Public' + WHEN proaccess = '-' THEN 'Private' + ELSE 'Unknown' END AS visibility +FROM pg_proc, pg_namespace, pg_language lng +WHERE format_type(prorettype, NULL) = 'void' +AND pronamespace = {{pkgid}}::oid +AND pg_proc.pronamespace = pg_namespace.oid +AND lng.oid=prolang +{% if edbfnid %} +AND pg_proc.oid = {{edbfnid}}::oid +{% endif %} + ORDER BY name diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/stats.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/stats.sql new file mode 100644 index 000000000..2e276b65a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.1_plus/stats.sql @@ -0,0 +1,8 @@ +SELECT + calls AS {{ conn|qtIdent(_('Number of calls')) }}, + total_time AS {{ conn|qtIdent(_('Total time')) }}, + self_time AS {{ conn|qtIdent(_('Self time')) }} +FROM + pg_stat_user_functions +WHERE + funcid = {{fnid}}::OID diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/get_body.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/get_body.sql new file mode 100644 index 000000000..72675f4ab --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/get_body.sql @@ -0,0 +1,6 @@ +SELECT pg_catalog.edb_get_packagebodydef(nsp.oid) AS pkgbodysrc +FROM pg_namespace nsp +LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) +WHERE nspparent = {{scid}}::oid +AND nsp.oid = {{pkgid}}::oid +AND nspobjecttype = 0; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/get_name.sql new file mode 100644 index 000000000..7410b7173 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/get_name.sql @@ -0,0 +1,3 @@ +SELECT proname AS name +FROM pg_proc +WHERE oid = {{edbfnid}}::oid diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/get_oid.sql new file mode 100644 index 000000000..2bc76a2e4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/get_oid.sql @@ -0,0 +1,17 @@ +SELECT + pr.oid, pr.proname || '(' || COALESCE(pg_catalog + .pg_get_function_identity_arguments(pr.oid), '') || ')' as name, + lanname, pg_get_userbyid(proowner) as funcowner +FROM + pg_proc pr +JOIN + pg_type typ ON typ.oid=prorettype +JOIN + pg_language lng ON lng.oid=prolang +JOIN + pg_namespace nsp ON nsp.oid=pr.pronamespace + AND nsp.nspname={{ nspname|qtLiteral }} +WHERE + proisagg = FALSE + AND typname NOT IN ('trigger', 'event_trigger') + AND pr.proname = {{ name|qtLiteral }}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/get_schema.sql new file mode 100644 index 000000000..127d4b9db --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/get_schema.sql @@ -0,0 +1,6 @@ +SELECT + nspname +FROM + pg_namespace +WHERE + oid = {{ scid }}::oid; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/node.sql new file mode 100644 index 000000000..66405985c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/node.sql @@ -0,0 +1,6 @@ +SELECT pg_proc.oid, + pg_proc.proname || '(' || COALESCE(pg_catalog.pg_get_function_identity_arguments(pg_proc.oid), '') || ')' AS name +FROM pg_proc, pg_namespace +WHERE format_type(prorettype, NULL) = 'void' +AND pronamespace = {{pkgid}}::oid +AND pg_proc.pronamespace = pg_namespace.oid \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/properties.sql new file mode 100644 index 000000000..067723361 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/properties.sql @@ -0,0 +1,27 @@ +SELECT pg_proc.oid, + proname AS name, + pronargs, + proallargtypes, + proargnames AS argnames, + pronargdefaults, + oidvectortypes(proargtypes) AS proargtypenames, + proargmodes, + proargnames, + pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals, + pg_get_userbyid(proowner) AS funcowner, + pg_get_function_result(pg_proc.oid) AS prorettypename, + prosrc, + lanname, + CASE + WHEN proaccess = '+' THEN 'Public' + WHEN proaccess = '-' THEN 'Private' + ELSE 'Unknown' END AS visibility +FROM pg_proc, pg_namespace, pg_language lng +WHERE format_type(prorettype, NULL) = 'void' +AND pronamespace = {{pkgid}}::oid +AND pg_proc.pronamespace = pg_namespace.oid +AND lng.oid=prolang +{% if edbfnid %} +AND pg_proc.oid = {{edbfnid}}::oid +{% endif %} + ORDER BY name diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/stats.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/stats.sql new file mode 100644 index 000000000..2e276b65a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbfuncs/templates/edbproc/ppas/9.2_plus/stats.sql @@ -0,0 +1,8 @@ +SELECT + calls AS {{ conn|qtIdent(_('Number of calls')) }}, + total_time AS {{ conn|qtIdent(_('Total time')) }}, + self_time AS {{ conn|qtIdent(_('Self time')) }} +FROM + pg_stat_user_functions +WHERE + funcid = {{fnid}}::OID diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/__init__.py new file mode 100644 index 000000000..79f25be70 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/__init__.py @@ -0,0 +1,326 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements Edb Functions/Edb Procedures Node.""" + +import copy +from functools import wraps + +import pgadmin.browser.server_groups.servers.databases.schemas.packages as packages +from flask import render_template, make_response +from flask_babel import gettext +from pgadmin.browser.collection import CollectionNodeModule +from pgadmin.browser.server_groups.servers.databases.schemas.utils import \ + DataTypeReader +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error, gone +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver + +from config import PG_DEFAULT_DRIVER + + +class EdbVarModule(CollectionNodeModule): + """ + class EdbvarModule(CollectionNodeModule): + + This class represents The Functions Module. + + Methods: + ------- + * __init__(*args, **kwargs) + - Initialize the Functions Module. + + * get_nodes(gid, sid, did, scid) + - Generate the Functions collection node. + + * node_inode(): + - Returns Functions node as leaf node. + + * script_load() + - Load the module script for Functions, when schema node is + initialized. + + * csssnippets() + - Returns a snippet of css. + """ + + NODE_TYPE = 'edbvar' + COLLECTION_LABEL = gettext("Variables") + + def __init__(self, *args, **kwargs): + """ + Initialize the Variable Module. + Args: + *args: + **kwargs: + """ + super(EdbVarModule, self).__init__(*args, **kwargs) + + self.min_ver = 90100 + self.max_ver = None + self.server_type = ['ppas'] + + def get_nodes(self, gid, sid, did, scid, pkgid): + """ + Generate Functions collection node. + """ + yield self.generate_browser_collection_node(pkgid) + + @property + def script_load(self): + """ + Load the module script for Functions, when the + package node is initialized. + """ + return packages.PackageModule.NODE_TYPE + + @property + def node_inode(self): + """ + Make the node as leaf node. + Returns: + False as this node doesn't have child nodes. + """ + return False + +blueprint = EdbVarModule(__name__) + + +class EdbVarView(PGChildNodeView, DataTypeReader): + """ + class EdbFuncView(PGChildNodeView, DataTypeReader) + + This class inherits PGChildNodeView and DataTypeReader to get the different routes for + the module. + + The class is responsible to Create, Read, Update and Delete operations for + the Functions. + + Methods: + ------- + * validate_request(f): + - Works as a decorator. + Validating request on the request of create, update and modified SQL. + + * module_js(): + - Overrides this property to define javascript for Functions node. + + * check_precondition(f): + - Works as a decorator. + - Checks database connection status. + - Attach connection object and template path. + + * list(gid, sid, did, scid, pkgid): + - List the Functions. + + * nodes(gid, sid, did, scid, pkgid): + - Returns all the Functions to generate Nodes in the browser. + + * properties(gid, sid, did, scid, pkgid, varid): + - Returns the Functions properties. + + * sql(gid, sid, did, scid, pkgid, varid): + - Returns the SQL for the Functions object. + + """ + + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'}, + {'type': 'int', 'id': 'pkgid'} + ] + ids = [ + {'type': 'int', 'id': 'varid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties'}, + {'get': 'list'} + ], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def module_js(self): + """ + Load JS file (edbvar.js) for this module. + """ + + return make_response( + render_template( + "edbvar/js/edbvar.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + Works as a decorator. + Checks the database connection status. + Attaches the connection object and template path to the class object. + """ + + @wraps(f) + def wrap(*args, **kwargs): + self = args[0] + driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = driver.connection_manager(kwargs['sid']) + + # Get database connection + self.conn = self.manager.connection(did=kwargs['did']) + + self.qtIdent = driver.qtIdent + self.qtLiteral = driver.qtLiteral + + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + self.sql_template_path = "/edbvar/ppas" + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid, pkgid): + """ + List all the Functions. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + pkgid: Package Id + """ + + SQL = render_template("/".join([self.sql_template_path, 'node.sql']), + pkgid=pkgid) + 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, pkgid): + """ + Returns all the Functions to generate the Nodes. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + pkgid: Package Id + """ + + res = [] + SQL = render_template("/".join([self.sql_template_path, + 'node.sql']), pkgid=pkgid) + 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'], + pkgid, + row['name'], + icon="icon-" + self.node_type + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, scid, pkgid, varid=None): + """ + Returns the Function properties. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + pkgid: Package Id + varid: Variable Id + """ + resp_data = {} + SQL = render_template("/".join([self.sql_template_path, + 'properties.sql']), + pkgid=pkgid, varid=varid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + if len(res['rows']) == 0: + return ajax_response( + response=resp_data, + status=200 + ) + + resp_data = res['rows'][0] + + return ajax_response( + response=resp_data, + status=200 + ) + + @check_precondition + def sql(self, gid, sid, did, scid, pkgid, varid=None): + """ + Returns the SQL for the Function object. + + Args: + gid: Server Group Id + sid: Server Id + did: Database Id + scid: Schema Id + pkgid: Package Id + varid: variable Id + """ + SQL = render_template("/".join([self.sql_template_path, 'properties.sql']), + varid=varid, + pkgid=pkgid) + + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + var = res['rows'][0] + + sql = "-- Package Variable: {}".format(var['name']) + sql += "\n\n" + sql += "{} {};".format(var['name'], var['datatype']) + if hasattr(str, 'decode'): + sql = sql.decode('utf-8') + + return ajax_response(response=sql) + +EdbVarView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/static/img/coll-edbvar.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/static/img/coll-edbvar.png new file mode 100644 index 000000000..236bad7dc Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/static/img/coll-edbvar.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/static/img/edbvar.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/static/img/edbvar.png new file mode 100644 index 000000000..046a08842 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/static/img/edbvar.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/templates/edbvar/js/edbvar.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/templates/edbvar/js/edbvar.js new file mode 100644 index 000000000..882c227a3 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/templates/edbvar/js/edbvar.js @@ -0,0 +1,70 @@ +/* Create and Register Function Collection and Node. */ +define( + ['jquery', 'underscore', 'underscore.string', + 'pgadmin', 'pgadmin.browser', 'alertify', + 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + if (!pgBrowser.Nodes['coll-edbvar']) { + pgBrowser.Nodes['coll-edbvar'] = + pgBrowser.Collection.extend({ + node: 'edbvar', + label: '{{ _('Variables') }}', + type: 'coll-edbvar', + columns: ['name', 'funcowner', 'description'] + }); + }; + + if (!pgBrowser.Nodes['edbvar']) { + pgBrowser.Nodes['edbvar'] = pgBrowser.Node.extend({ + type: 'edbvar', + dialogHelp: '{{ url_for('help.static', filename='edbvar_dialog.html') }}', + label: '{{ _('Function') }}', + collection_type: 'coll-edbvar', + canEdit: false, + hasSQL: true, + hasScriptTypes: [], + parent_type: ['package'], + Init: function(args) { + /* Avoid mulitple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + + }, + canDrop: false, + canDropCascade: false, + model: pgBrowser.Node.Model.extend({ + defaults: { + name: undefined, + oid: undefined, + datatype: undefined, + visibility: 'Unknown' + }, + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties'], + disabled: true + },{ + id: 'oid', label: '{{ _('OID') }}', cell: 'string', + type: 'text' , mode: ['properties'] + },{ + id: 'datatype', label: '{{ _('Data type') }}', cell: 'string', + type: 'text', disabled: true + },{ + id: 'visibility', label: '{{ _('Visibility') }}', cell: 'string', + type: 'text', mode: ['properties'], + disabled: true + }], + validate: function() + { + return null; + } + }) + }); + + } + + return pgBrowser.Nodes['edbvar']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/templates/edbvar/ppas/node.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/templates/edbvar/ppas/node.sql new file mode 100644 index 000000000..4ffd4fa24 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/templates/edbvar/ppas/node.sql @@ -0,0 +1,5 @@ +SELECT oid, + varname AS name +FROM edb_variable +WHERE varpackage = {{pkgid}}::oid +ORDER BY varname diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/templates/edbvar/ppas/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/templates/edbvar/ppas/properties.sql new file mode 100644 index 000000000..2c00e0375 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/edbvars/templates/edbvar/ppas/properties.sql @@ -0,0 +1,13 @@ +SELECT oid, + varname AS name, + format_type(vartype, NULL) as datatype, + CASE + WHEN varaccess = '+' THEN 'Public' + WHEN varaccess = '-' THEN 'Private' + ELSE 'Unknown' END AS visibility +FROM edb_variable +WHERE varpackage = {{pkgid}}::oid +{% if varid %} +AND oid = {{varid}} +{% endif %} +ORDER BY varname diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/static/img/coll-package.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/static/img/coll-package.png new file mode 100644 index 000000000..f1f91dbf5 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/static/img/coll-package.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/static/img/package.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/static/img/package.png new file mode 100644 index 000000000..323197762 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/static/img/package.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/js/package.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/js/package.js new file mode 100644 index 000000000..e9d325b91 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/js/package.js @@ -0,0 +1,169 @@ +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Extend the browser's collection class for package collection + if (!pgBrowser.Nodes['coll-package']) { + var databases = pgBrowser.Nodes['coll-package'] = + pgBrowser.Collection.extend({ + node: 'package', + label: '{{ _('Packages') }}', + type: 'coll-package', + columns: ['name', ,'owner', 'description'] + }); + }; + + // Extend the browser's node class for package node + if (!pgBrowser.Nodes['package']) { + pgBrowser.Nodes['package'] = pgBrowser.Node.extend({ + type: 'package', + dialogHelp: '{{ url_for('help.static', filename='package_dialog.html') }}', + label: '{{ _('Package') }}', + collection_type: 'coll-package', + hasSQL: true, + hasDepends: true, + parent_type: ['schema'], + Init: function() { + /* Avoid mulitple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_package_on_coll', node: 'coll-package', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Package...') }}', + icon: 'wcTabIcon icon-package', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_package', node: 'package', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Package...') }}', + icon: 'wcTabIcon icon-package', data: {action: 'create', check: true}, + enable: 'canCreate' + },{ + name: 'create_package', node: 'schema', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Package...') }}', + icon: 'wcTabIcon icon-package', data: {action: 'create', check: false}, + enable: 'canCreate' + } + ]); + + }, + canDrop: pgBrowser.Nodes['schema'].canChildDrop, + canDropCascade: pgBrowser.Nodes['schema'].canChildDrop, + 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 collation + if (_.indexOf(['schema'], d._type) > -1) + return true; + + if ('coll-package' == d._type) { + //Check if we are not child of catalog + prev_i = t.hasParent(i) ? t.parent(i) : null; + prev_d = prev_i ? t.itemData(prev_i) : null; + if( prev_d._type == 'catalog') { + return false; + } else { + return true; + } + } + i = t.hasParent(i) ? t.parent(i) : null; + d = i ? t.itemData(i) : null; + } + // by default we want to allow create menu + return true; + }, + // Define the model for package node. + model: pgBrowser.Node.Model.extend({ + defaults: { + name: undefined, + oid: undefined, + owner: undefined, + is_sys_object: undefined, + description: undefined, + pkgheadsrc: undefined, + pkgbodysrc: undefined, + acl: undefined, + pkgacl: [] + }, + // Define the schema for package node. + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'], + disabled: function(m) { + return !m.isNew(); + } + },{ + id: 'oid', label:'{{ _('OID') }}', cell: 'string', + type: 'text', mode: ['properties'] + },{ + id: 'owner', label:'{{ _('Owner') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'], + disabled: true, editable: false, visible: function(m) { + return !m.isNew(); + } + },{ + id: 'is_sys_object', label: '{{ _('System package?') }}', + cell:'boolean', type: 'switch',mode: ['properties'] + },{ + id: 'description', label:'{{ _('Comment') }}', type: 'multiline', + mode: ['properties', 'create', 'edit'] + },{ + id: 'pkgheadsrc', label: '{{ _('Header') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'], group: '{{ _('Code') }}', + control: Backform.SqlFieldControl + },{ + id: 'pkgbodysrc', label: '{{ _('Body') }}', cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'], group: '{{ _('Code') }}', + control: Backform.SqlFieldControl + },{ + id: 'acl', label: '{{ _('Privileges') }}', type: 'text', + group: '{{ _('Security') }}', mode: ['properties',] + },{ + id: 'pkgacl', label: '{{ _('Privileges') }}', type: 'collection', + model: pgBrowser.Node.PrivilegeRoleModel.extend({ + privileges: ['X'] + }), uniqueCol : ['grantee', 'grantor'], editable: false, + group: '{{ _('Security') }}', mode: ['edit', 'create'], + canAdd: true, canDelete: true, control: 'unique-col-collection', + }], + /* validate function is used to validate the input given by + * the user. In case of error, message will be displayed on + * the GUI for the respective control. + */ + validate: function() { + var msg = undefined; + // Clear any existing error msg. + this.errorModel.clear(); + + if (_.isUndefined(this.get('name')) + || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + msg = '{{ _('Name cannot be empty.') }}'; + this.errorModel.set('name', msg); + return msg; + } + + if (_.isUndefined(this.get('pkgheadsrc')) + || String(this.get('pkgheadsrc')).replace(/^\s+|\s+$/g, '') == '') { + msg = '{{ _('Header cannot be empty.') }}'; + this.errorModel.set('pkgheadsrc', msg); + return msg; + } + + return null; + } + }) + }); + } + + return pgBrowser.Nodes['package']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/acl.sql new file mode 100644 index 000000000..af1870cbe --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/acl.sql @@ -0,0 +1,33 @@ +SELECT 'pkgacl' 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 'CONNECT' THEN 'c' + WHEN 'CREATE' THEN 'C' + WHEN 'DELETE' THEN 'd' + WHEN 'EXECUTE' THEN 'X' + WHEN 'INSERT' THEN 'a' + WHEN 'REFERENCES' THEN 'x' + WHEN 'SELECT' THEN 'r' + WHEN 'TEMPORARY' THEN 'T' + WHEN 'TRIGGER' THEN 't' + WHEN 'TRUNCATE' THEN 'D' + WHEN 'UPDATE' THEN 'w' + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN' + END AS privilege_type + FROM + (SELECT nspacl + FROM pg_namespace + WHERE nspparent = {{scid}}::oid + AND oid = {{pkgid}}::oid + ) 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(nspacl) as d FROM pg_namespace + WHERE nspparent = {{scid}}::oid + AND oid = {{pkgid}}::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 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/create.sql new file mode 100644 index 000000000..0e3f0221e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/create.sql @@ -0,0 +1,24 @@ +{% import 'macros/schemas/privilege.macros' as PRIVILEGE %} + +{% if data %} +CREATE OR REPLACE PACKAGE {{ conn|qtIdent(data.schema,data.name) }} +IS +{{data.pkgheadsrc}} +END {{ conn|qtIdent(data.name) }}; +{% if data.pkgbodysrc %} + +CREATE OR REPLACE PACKAGE BODY {{ conn|qtIdent(data.schema,data.name) }} +IS +{{data.pkgbodysrc}} +END {{ conn|qtIdent(data.name) }}; +{% endif %} +{% if data.pkgacl %} + +{% for priv in data.pkgacl %} +{{ PRIVILEGE.SET(conn, 'PACKAGE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }} +{% endfor %} +{% endif %}{% if data.description %} +COMMENT ON PACKAGE {{ conn|qtIdent(data.schema,data.name) }} + IS {{ data.description|qtLiteral }}; +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/delete.sql new file mode 100644 index 000000000..c5bb596c8 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/delete.sql @@ -0,0 +1 @@ +DROP PACKAGE {{ conn|qtIdent(data.schema,data.name) }}{% if cascade%} CASCADE{% endif %}; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/get_oid.sql new file mode 100644 index 000000000..9931a115a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/get_oid.sql @@ -0,0 +1,4 @@ +SELECT nsp.oid +FROM pg_namespace nsp +WHERE nspparent = {{scid}}::oid +AND nspname = {{ name|qtLiteral }}; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/get_schema.sql new file mode 100644 index 000000000..127d4b9db --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/get_schema.sql @@ -0,0 +1,6 @@ +SELECT + nspname +FROM + pg_namespace +WHERE + oid = {{ scid }}::oid; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/grant.sql new file mode 100644 index 000000000..d4a382c66 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/grant.sql @@ -0,0 +1,26 @@ +{% import 'macros/schemas/security.macros' as SECLABEL %} +{% import 'macros/schemas/privilege.macros' as PRIVILEGE %} +{# Construct sequence name from name and schema #} +{% set seqname=conn|qtIdent(data.schema, data.name) %} +{% if data.seqowner %} + +ALTER SEQUENCE {{ seqname }} + OWNER TO {{ conn|qtIdent(data.seqowner) }}; +{% endif %} +{% if data.comment %} + +COMMENT ON SEQUENCE {{ seqname }} + IS {{ data.comment|qtLiteral }}; +{% endif %} +{% if data.securities %} + +{% for r in data.securities %} +{{ SECLABEL.SET(conn, 'SEQUENCE', data.name, r.provider, r.label, data.schema) }} +{% endfor %} +{% endif %} +{% if data.relacl %} + +{% for priv in data.relacl %} +{{ PRIVILEGE.SET(conn, 'SEQUENCE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }} +{% endfor %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/nodes.sql new file mode 100644 index 000000000..992f115b4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/nodes.sql @@ -0,0 +1,6 @@ +SELECT + nsp.oid, nspname AS name +FROM + pg_namespace nsp +WHERE nspparent = {{scid}}::oid +ORDER BY nspname; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/properties.sql new file mode 100644 index 000000000..85d5694c6 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/properties.sql @@ -0,0 +1,17 @@ +SELECT nsp.oid, nsp.xmin, nspname AS name, + pg_catalog.edb_get_packagebodydef(nsp.oid) AS pkgbodysrc, + pg_catalog.edb_get_packageheaddef(nsp.oid) AS pkgheadsrc, + pg_get_userbyid(nspowner) AS owner, + array_to_string(nsp.nspacl::text[], ', ') as acl, + description, + CASE + WHEN nspname LIKE E'pg\\_%' THEN true + ELSE false + END AS is_sys_object +FROM pg_namespace nsp +LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) +WHERE nspparent = {{scid}}::oid +{% if pkgid %} +AND nsp.oid = {{pkgid}}::oid +{% endif %} +ORDER BY nspname; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/update.sql new file mode 100644 index 000000000..2c1aed02a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.1_plus/update.sql @@ -0,0 +1,44 @@ +{% import 'macros/schemas/privilege.macros' as PRIVILEGE %} + +{% if data %} +{% if o_data.pkgbodysrc and data.pkgbodysrc == '' %} +DROP PACKAGE BODY {{ conn|qtIdent(data.schema,data.name) }}; +{% endif %} +{% if data.pkgheadsrc %} + +CREATE OR REPLACE PACKAGE {{ conn|qtIdent(data.schema,data.name) }} +IS +{{data.pkgheadsrc}} +END {{ conn|qtIdent(data.name) }}; +{% endif %} +{% if data.pkgbodysrc %} + +CREATE OR REPLACE PACKAGE BODY {{ conn|qtIdent(data.schema,data.name) }} +IS +{{data.pkgbodysrc}} +END {{ conn|qtIdent(data.name) }}; +{% endif %} +{% if data.pkgacl %} +{% if 'deleted' in data.pkgacl %} +{% for priv in data.pkgacl.deleted %} +{{ PRIVILEGE.UNSETALL(conn, 'PACKAGE', priv.grantee, data.name, data.schema) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.pkgacl %} +{% for priv in data.pkgacl.changed %} +{{ PRIVILEGE.UNSETALL(conn, 'PACKAGE', priv.grantee, data.name, data.schema) }} +{{ PRIVILEGE.SET(conn, 'PACKAGE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }} +{% endfor %} +{% endif %} +{% if 'added' in data.pkgacl %} +{% for priv in data.pkgacl.added %} +{{ PRIVILEGE.SET(conn, 'PACKAGE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }} +{% endfor %} +{% endif %} +{% endif %} +{% if data.description is defined %} + +COMMENT ON PACKAGE {{ conn|qtIdent(data.schema,data.name) }} + IS {{ data.description|qtLiteral }}; +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/acl.sql new file mode 100644 index 000000000..b67149b0f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/acl.sql @@ -0,0 +1,35 @@ +SELECT 'pkgacl' 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 'CONNECT' THEN 'c' + WHEN 'CREATE' THEN 'C' + WHEN 'DELETE' THEN 'd' + WHEN 'EXECUTE' THEN 'X' + WHEN 'INSERT' THEN 'a' + WHEN 'REFERENCES' THEN 'x' + WHEN 'SELECT' THEN 'r' + WHEN 'TEMPORARY' THEN 'T' + WHEN 'TRIGGER' THEN 't' + WHEN 'TRUNCATE' THEN 'D' + WHEN 'UPDATE' THEN 'w' + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN' + END AS privilege_type + FROM + (SELECT nspacl + FROM pg_namespace + WHERE nspparent = {{scid}}::oid + AND oid = {{pkgid}}::oid + AND nspobjecttype = 0 + ) 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(nspacl) as d FROM pg_namespace + WHERE nspparent = {{scid}}::oid + AND oid = {{pkgid}}::oid + AND nspobjecttype = 0) 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 diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/create.sql new file mode 100644 index 000000000..0e3f0221e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/create.sql @@ -0,0 +1,24 @@ +{% import 'macros/schemas/privilege.macros' as PRIVILEGE %} + +{% if data %} +CREATE OR REPLACE PACKAGE {{ conn|qtIdent(data.schema,data.name) }} +IS +{{data.pkgheadsrc}} +END {{ conn|qtIdent(data.name) }}; +{% if data.pkgbodysrc %} + +CREATE OR REPLACE PACKAGE BODY {{ conn|qtIdent(data.schema,data.name) }} +IS +{{data.pkgbodysrc}} +END {{ conn|qtIdent(data.name) }}; +{% endif %} +{% if data.pkgacl %} + +{% for priv in data.pkgacl %} +{{ PRIVILEGE.SET(conn, 'PACKAGE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }} +{% endfor %} +{% endif %}{% if data.description %} +COMMENT ON PACKAGE {{ conn|qtIdent(data.schema,data.name) }} + IS {{ data.description|qtLiteral }}; +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/delete.sql new file mode 100644 index 000000000..c5bb596c8 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/delete.sql @@ -0,0 +1 @@ +DROP PACKAGE {{ conn|qtIdent(data.schema,data.name) }}{% if cascade%} CASCADE{% endif %}; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/get_oid.sql new file mode 100644 index 000000000..5b4fd1dea --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/get_oid.sql @@ -0,0 +1,5 @@ +SELECT nsp.oid +FROM pg_namespace nsp +WHERE nspparent = {{scid}}::oid +AND nspname = {{ name|qtLiteral }} +AND nspobjecttype = 0; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/get_schema.sql new file mode 100644 index 000000000..127d4b9db --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/get_schema.sql @@ -0,0 +1,6 @@ +SELECT + nspname +FROM + pg_namespace +WHERE + oid = {{ scid }}::oid; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/grant.sql new file mode 100644 index 000000000..d4a382c66 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/grant.sql @@ -0,0 +1,26 @@ +{% import 'macros/schemas/security.macros' as SECLABEL %} +{% import 'macros/schemas/privilege.macros' as PRIVILEGE %} +{# Construct sequence name from name and schema #} +{% set seqname=conn|qtIdent(data.schema, data.name) %} +{% if data.seqowner %} + +ALTER SEQUENCE {{ seqname }} + OWNER TO {{ conn|qtIdent(data.seqowner) }}; +{% endif %} +{% if data.comment %} + +COMMENT ON SEQUENCE {{ seqname }} + IS {{ data.comment|qtLiteral }}; +{% endif %} +{% if data.securities %} + +{% for r in data.securities %} +{{ SECLABEL.SET(conn, 'SEQUENCE', data.name, r.provider, r.label, data.schema) }} +{% endfor %} +{% endif %} +{% if data.relacl %} + +{% for priv in data.relacl %} +{{ PRIVILEGE.SET(conn, 'SEQUENCE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }} +{% endfor %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/nodes.sql new file mode 100644 index 000000000..9361622f5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/nodes.sql @@ -0,0 +1,5 @@ +SELECT nsp.oid, nspname AS name +FROM pg_namespace nsp +WHERE nspparent = {{scid}}::oid +AND nspobjecttype = 0 +ORDER BY nspname; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/properties.sql new file mode 100644 index 000000000..38975b12a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/properties.sql @@ -0,0 +1,18 @@ +SELECT nsp.oid, nsp.xmin, nspname AS name, + pg_catalog.edb_get_packagebodydef(nsp.oid) AS pkgbodysrc, + pg_catalog.edb_get_packageheaddef(nsp.oid) AS pkgheadsrc, + pg_get_userbyid(nspowner) AS owner, + array_to_string(nsp.nspacl::text[], ', ') as acl, + description, + CASE + WHEN nspname LIKE E'pg\\_%' THEN true + ELSE false + END AS is_sys_object +FROM pg_namespace nsp +LEFT OUTER JOIN pg_description des ON (des.objoid=nsp.oid AND des.classoid='pg_namespace'::regclass) +WHERE nspparent = {{scid}}::oid +{% if pkgid %} +AND nsp.oid = {{pkgid}}::oid +{% endif %} +AND nspobjecttype = 0 +ORDER BY nspname; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/update.sql new file mode 100644 index 000000000..2c1aed02a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/package/ppas/9.2_plus/update.sql @@ -0,0 +1,44 @@ +{% import 'macros/schemas/privilege.macros' as PRIVILEGE %} + +{% if data %} +{% if o_data.pkgbodysrc and data.pkgbodysrc == '' %} +DROP PACKAGE BODY {{ conn|qtIdent(data.schema,data.name) }}; +{% endif %} +{% if data.pkgheadsrc %} + +CREATE OR REPLACE PACKAGE {{ conn|qtIdent(data.schema,data.name) }} +IS +{{data.pkgheadsrc}} +END {{ conn|qtIdent(data.name) }}; +{% endif %} +{% if data.pkgbodysrc %} + +CREATE OR REPLACE PACKAGE BODY {{ conn|qtIdent(data.schema,data.name) }} +IS +{{data.pkgbodysrc}} +END {{ conn|qtIdent(data.name) }}; +{% endif %} +{% if data.pkgacl %} +{% if 'deleted' in data.pkgacl %} +{% for priv in data.pkgacl.deleted %} +{{ PRIVILEGE.UNSETALL(conn, 'PACKAGE', priv.grantee, data.name, data.schema) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.pkgacl %} +{% for priv in data.pkgacl.changed %} +{{ PRIVILEGE.UNSETALL(conn, 'PACKAGE', priv.grantee, data.name, data.schema) }} +{{ PRIVILEGE.SET(conn, 'PACKAGE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }} +{% endfor %} +{% endif %} +{% if 'added' in data.pkgacl %} +{% for priv in data.pkgacl.added %} +{{ PRIVILEGE.SET(conn, 'PACKAGE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }} +{% endfor %} +{% endif %} +{% endif %} +{% if data.description is defined %} + +COMMENT ON PACKAGE {{ conn|qtIdent(data.schema,data.name) }} + IS {{ data.description|qtLiteral }}; +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/utils.py b/web/pgadmin/browser/server_groups/servers/utils.py index 4aa04ea32..630ef617d 100644 --- a/web/pgadmin/browser/server_groups/servers/utils.py +++ b/web/pgadmin/browser/server_groups/servers/utils.py @@ -98,9 +98,9 @@ def parse_priv_to_db(str_privileges, allowed_acls=[]): db_privileges[privilege['privilege_type']] ) # If we have all acl then just return all - if len(priv_with_grant) == allowed_acls_len: + if len(priv_with_grant) == allowed_acls_len > 1: priv_with_grant = ['ALL'] - if len(priv_without_grant) == allowed_acls_len: + if len(priv_without_grant) == allowed_acls_len > 1: priv_without_grant = ['ALL'] # Appending and returning all ACL privileges.append({ diff --git a/web/pgadmin/browser/templates/browser/js/node.js b/web/pgadmin/browser/templates/browser/js/node.js index 4cc8438a6..06152f6c6 100644 --- a/web/pgadmin/browser/templates/browser/js/node.js +++ b/web/pgadmin/browser/templates/browser/js/node.js @@ -73,17 +73,21 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) { self.node_initialized = true; pgAdmin.Browser.add_menus([{ - name: 'show_obj_properties', node: self.type, module: self, - applies: ['object', 'context'], callback: 'show_obj_properties', - priority: 999, label: '{{ _("Properties...") }}', - data: {'action': 'edit'}, icon: 'fa fa-pencil-square-o' - }, { name: 'refresh', node: self.type, module: self, applies: ['object', 'context'], callback: 'refresh', priority: 1, label: '{{ _("Refresh...") }}', icon: 'fa fa-refresh' }]); + if (self.canEdit) { + pgAdmin.Browser.add_menus([{ + name: 'show_obj_properties', node: self.type, module: self, + applies: ['object', 'context'], callback: 'show_obj_properties', + priority: 999, label: '{{ _("Properties...") }}', + data: {'action': 'edit'}, icon: 'fa fa-pencil-square-o' + }]); + } + if (self.canDrop) { pgAdmin.Browser.add_menus([{ name: 'delete_object', node: self.type, module: self, @@ -139,15 +143,6 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) { enable: this.check_user_permission }]); }); - // If node has hasSQL then provide CREATE Script by default - } else if(self.hasSQL) { - pgAdmin.Browser.add_menus([{ - name: 'show_script_create', node: self.type, module: self, - applies: ['object', 'context'], callback: 'show_script', - priority: 4, label: 'CREATE Script', category: 'Scripts', - data: {'script': 'create'}, icon: 'fa fa-pencil', - enable: this.check_user_permission - }]); } }, /////// @@ -320,6 +315,20 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) { }); p.load(pgBrowser.docker); }, + /* + * Default script type menu for node. + * + * Override this, to show more script type menus (e.g hasScriptTypes: ['create', 'select', 'insert', 'update', 'delete']) + * + * Or set it to empty array to disable script type menu on node (e.g hasScriptTypes: []) + */ + hasScriptTypes: ['create'], + /****************************************************************** + * This function determines the given item is editable or not. + * + * Override this, when a node is not editable. + */ + canEdit: true, /****************************************************************** * This function determines the given item is deletable or not. * @@ -884,18 +893,20 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) { // Create proper buttons var buttons = []; + buttons.push({ label: '', type: 'edit', tooltip: '{{ _("Edit") }}', extraClasses: ['btn-default'], icon: 'fa fa-lg fa-pencil-square-o', - disabled: false, + disabled: !that.canEdit, register: function(btn) { btn.click(function() { onEdit(); }); } }); + buttons.push({ label: '', type: 'help', tooltip: '{{ _("SQL help for this object type.") }}',