diff --git a/web/pgadmin/browser/server_groups/servers/databases/languages/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/languages/__init__.py new file mode 100644 index 0000000..8ae2c5a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/languages/__init__.py @@ -0,0 +1,521 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements Language Node""" + +import json +from functools import wraps + +from flask import render_template, make_response, request +from flask.ext.babel import gettext + +import pgadmin.browser.server_groups.servers.databases as databases +from config import PG_DEFAULT_DRIVER +from pgadmin.browser.collection import CollectionNodeModule +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 + + +class LanguageModule(CollectionNodeModule): + """ + class LanguageModule(CollectionNodeModule) + + A module class for Language node derived from CollectionNodeModule. + + Methods: + ------- + * __init__(*args, **kwargs) + - Method is used to initialize the LanguageModule and it's base module. + + * get_nodes(gid, sid, did) + - Method is used to generate the browser collection node. + + * node_inode() + - Method is overridden from its base class to make the node as leaf node. + + * script_load() + - Load the module script for language, when any of the database node is + initialized. + """ + + NODE_TYPE = 'language' + COLLECTION_LABEL = gettext("Languages") + + def __init__(self, *args, **kwargs): + """ + Method is used to initialize the LanguageModule and it's base module. + + Args: + *args: + **kwargs: + """ + self.min_ver = None + self.max_ver = None + + super(LanguageModule, self).__init__(*args, **kwargs) + + def get_nodes(self, gid, sid, did): + """ + Method is used to generate the browser collection node + + Args: + gid: Server Group ID + sid: Server ID + did: Database Id + """ + yield self.generate_browser_collection_node(did) + + @property + def node_inode(self): + """ + Override this property to make the node as leaf node. + + Returns: False as this is the leaf node + """ + return False + + @property + def script_load(self): + """ + Load the module script for language, when any of the database node is initialized. + + Returns: node type of the server module. + """ + return databases.DatabaseModule.NODE_TYPE + +blueprint = LanguageModule(__name__) + + +class LanguageView(PGChildNodeView): + """ + class LanguageView(PGChildNodeView) + + A view class for Language node derived from PGChildNodeView. This class is + responsible for all the stuff related to view like updating language + node, showing properties, showing sql in sql pane. + + Methods: + ------- + * __init__(**kwargs) + - Method is used to initialize the LanguageView and it's base view. + + * module_js() + - This property defines (if javascript) exists for this node. + Override this property for your own logic + + * check_precondition() + - 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 + + * list() + - This function is used to list all the language nodes within that collection. + + * nodes() + - This function will used to create all the child node within that collection. + Here it will create all the language node. + + * properties(gid, sid, did, lid) + - This function will show the properties of the selected language node + + * update(gid, sid, did, lid) + - This function will update the data for the selected language node + + * msql(gid, sid, did, lid) + - This function is used to return modified SQL for the selected language node + + * get_sql(data, lid) + - This function will generate sql from model data + + * get_functions(gid, sid, did) + - This function returns the handler and inline functions for the selected language node + + * sql(gid, sid, did, lid): + - This function will generate sql to show it in sql pane for the selected language node. + + * dependents(gid, sid, did, lid): + - This function get the dependents and return ajax response for the language node. + + * dependencies(self, gid, sid, did, lid): + - This function get the dependencies and return ajax response for the language node. + """ + + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'} + ] + ids = [ + {'type': 'int', 'id': 'lid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'put': 'update'}, + {'get': 'list'} + ], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_functions': [{}, {'get': 'get_functions'}] + }) + + def _init_(self, **kwargs): + """ + Method is used to initialize the LanguageView and it's base view. + Also initialize all the variables create/used dynamically like conn, template_path. + + Args: + **kwargs: + """ + self.conn = None + self.template_path = None + self.manager = None + + super(LanguageView, self).__init__(**kwargs) + + def module_js(self): + """ + This property defines (if javascript) exists for this node. + Override this property for your own logic. + """ + return make_response( + render_template( + "languages/js/languages.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + This function will behave as a decorator which will checks + database connection before running view, it will also attaches + manager,conn & template_path properties to self + """ + @wraps(f) + def wrap(*args, **kwargs): + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + self.driver = get_driver(PG_DEFAULT_DRIVER) + self.manager = self.driver.connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + ver = self.manager.version + # we will set template path for sql scripts + if ver >= 90300: + self.template_path = 'languages/sql/9.3_plus' + else: + self.template_path = 'languages/sql/9.1_plus' + + return f(*args, **kwargs) + return wrap + + @check_precondition + def list(self, gid, sid, did): + """ + This function is used to list all the language nodes within that collection. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + """ + sql = render_template("/".join([self.template_path, 'properties.sql'])) + 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): + """ + This function will used to create all the child node within that collection. + Here it will create all the language node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + """ + res = [] + sql = render_template("/".join([self.template_path, 'properties.sql'])) + status, result = self.conn.execute_2darray(sql) + if not status: + return internal_server_error(errormsg=result) + + for row in result['rows']: + res.append( + self.blueprint.generate_browser_node( + row['oid'], + did, + row['name'], + icon="icon-language" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, lid): + """ + This function will show the properties of the selected language node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + lid: Language ID + """ + sql = render_template("/".join([self.template_path, 'properties.sql']), lid=lid) + status, res = self.conn.execute_dict(sql) + + if not status: + return internal_server_error(errormsg=res) + + sql = render_template("/".join([self.template_path, 'acl.sql']), lid=lid) + status, result = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=result) + + # if no acl found then by default add public + if res['rows'][0]['acl'] is None: + res['rows'][0]['lanacl'] = dict() + res['rows'][0]['lanacl']['grantee'] = 'public' + res['rows'][0]['lanacl']['grantor'] = res['rows'][0]['lanowner'] + res['rows'][0]['lanacl']['privileges'] = [{'privilege_type': 'U', 'privilege': True, 'with_grant': False}] + else: + for row in result['rows']: + priv = parse_priv_from_db(row) + if row['deftype'] in res['rows'][0]: + res['rows'][0][row['deftype']].append(priv) + else: + res['rows'][0][row['deftype']] = [priv] + + return ajax_response( + response=res['rows'][0], + status=200 + ) + + @check_precondition + def update(self, gid, sid, did, lid): + """ + This function will update the data for the selected language node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + lid: Language ID + """ + data = request.form if request.form else json.loads(request.data.decode()) + sql = self.get_sql(data, lid) + + try: + if sql and sql.strip('\n') and sql.strip(' '): + status, res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Language updated", + data={ + 'id': lid, + 'did': did, + 'sid': sid, + 'gid': gid + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': lid, + 'did': did, + 'sid': sid, + 'gid': gid + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def msql(self, gid, sid, did, lid=None): + """ + This function is used to return modified SQL for the selected language node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + lid: Language ID + """ + data = {} + for k, v in request.args.items(): + try: + data[k] = json.loads(v) + except ValueError: + data[k] = v + + sql = self.get_sql(data, lid) + if sql and sql.strip('\n') and sql.strip(' '): + return make_json_response( + data=sql, + status=200 + ) + else: + return make_json_response( + data='-- Modified SQL --', + status=200 + ) + + def get_sql(self, data, lid=None): + """ + This function will generate sql from model data. + + Args: + data: Contains the data of the selected language node. + lid: Language ID + """ + required_args = [ + 'name', 'lanowner', 'description' + ] + try: + sql = '' + if lid is not None: + sql = render_template("/".join([self.template_path, 'properties.sql']), lid=lid) + status, res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res) + + for key in ['lanacl']: + 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']) + if 'changed' in data[key]: + data[key]['changed'] = parse_priv_to_db(data[key]['changed']) + if 'deleted' in data[key]: + data[key]['deleted'] = parse_priv_to_db(data[key]['deleted']) + + old_data = res['rows'][0] + 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) + return sql + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def get_functions(self, gid, sid, did): + """ + This function returns the handler and inline functions for the selected language node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + """ + sql = render_template("/".join([self.template_path, 'functions.sql'])) + status, result = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=result) + return make_json_response( + data=result['rows'], + status=200 + ) + + @check_precondition + def sql(self, gid, sid, did, lid): + """ + This function will generate sql to show it in sql pane for the selected language node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + lid: Language ID + """ + sql = render_template("/".join([self.template_path, 'properties.sql']), lid=lid) + status, res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res) + + # Making copy of output for future use + old_data = dict(res['rows'][0]) + sql = render_template("/".join([self.template_path, 'sqlpane.sql']), data=old_data, conn=self.conn) + + return ajax_response(response=sql) + + @check_precondition + def dependents(self, gid, sid, did, lid): + """ + This function get the dependents and return ajax response + for the language node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + lid: Language ID + """ + dependents_result = self.get_dependents(self.conn, lid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, lid): + """ + This function get the dependencies and return ajax response + for the language node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + lid: Language ID + """ + dependencies_result = self.get_dependencies(self.conn, lid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +LanguageView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/languages/static/img/coll-language.png b/web/pgadmin/browser/server_groups/servers/databases/languages/static/img/coll-language.png new file mode 100644 index 0000000000000000000000000000000000000000..e730cb2ec034b8ad0860a945fde442e8b698c83d GIT binary patch literal 477 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}SAb85E0BJ3YvzMPkvBGK-q7T!{zq~y1>v!(&-}&zz1poO{baS)9 zvvc+zpGN-rn)CNh$+fj|S5}Gr`&;(xY}x<66?UsN(t)mFED7=pW^j0RBMr#mEbxdd z2GSm2>~=ES4#?>8ba4!kxE$JlQ>@8=hxKsWg{al8YedCe4mSP&U)?4&{ap03GlzK@ z90c3~B@Rrt@Z7B9o8(p=r!%ca^+?p2?N4UEU0&!k)p~#6)qD9%>~3b?Sv8x#U^17aMMww)MOF^{=(Rt2quWmu2Jr-}CJ$w~6ur zX+A&R+4(bNJ_B8)TH+c}l9E`GYL#4+3Zxi}3=9o)4UKdS3`2|ztW1opOiZ;6OsxzI u=DpKfg`y!hKP5A*5~9Jt608BJ+QiDh45H!G^w2dx4Gf;HelF{r5}E*I-p~I4 literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/languages/static/img/language.png b/web/pgadmin/browser/server_groups/servers/databases/languages/static/img/language.png new file mode 100644 index 0000000000000000000000000000000000000000..f391a0836c11c4bc3eb9d25e37340adf3e25d003 GIT binary patch literal 465 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}dw@@fE0BJ4H1XC}ldG$x-rk;b zbBn^`(-vB3jHJ%0Ymyt&2b z>xaajzp}r6O#1aZ_swnZ-@o(j?brSKHsR0jg12|(Jv?Ik>3Q^@KSejUDm^`G_u*04 zkB=FDei#4!Q*vdM*uTG}&(4%Q`v(SXV-scH!NM%y4?8OPq~)XwXYQue#$XVWA*#P5bMk*{PSFAAJ8$X zC9V-ADTyViR>?)FK#IZ0z|cU~&`8(7FvQ5f%EZ{p#8lhB)XKnM-aEZjC>nC}Q!>*k iAsP%U!5V<7O{@&eAR10h4_yP)z~JfX=d#Wzp$Py#z|P|U literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/js/languages.js b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/js/languages.js new file mode 100644 index 0000000..d3caf50 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/js/languages.js @@ -0,0 +1,192 @@ +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', + 'alertify', 'pgadmin.browser.collection', 'pgadmin.browser.server.privilege'], + function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Extend the browser's node model class to create a security model + var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + provider: null, + security_label: null + }, + + // Defining schema for the Security Label + schema: [{ + id: 'provider', label: '{{ _('Provider') }}', + type: 'text', disabled: false + },{ + id: 'security_label', label: '{{ _('Security Label') }}', + type: 'text', disabled: false + }], + /* 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 errmsg = null; + if (_.isUndefined(this.get('security_label')) || + _.isNull(this.get('security_label')) || + String(this.get('security_label')).replace(/^\s+|\s+$/g, '') == '') { + errmsg = '{{ _('Please specify the value for all the security providers.')}}'; + this.errorModel.set('security_label', errmsg); + return errmsg; + } else { + this.errorModel.unset('security_label'); + } + return null; + } + }); + + // Extend the browser's collection class for languages collection + if (!pgBrowser.Nodes['coll-language']) { + var languages = pgAdmin.Browser.Nodes['coll-language'] = + pgAdmin.Browser.Collection.extend({ + node: 'language', + label: '{{ _('Languages') }}', + type: 'coll-language', + columns: ['name', 'lanowner', 'description'] + }); + }; + + // Extend the browser's node class for language node + if (!pgBrowser.Nodes['language']) { + pgAdmin.Browser.Nodes['language'] = pgAdmin.Browser.Node.extend({ + parent_type: 'database', + type: 'language', + label: '{{ _('language') }}', + hasSQL: true, + hasDepends: true, + Init: function() { + + // Avoid multiple registration of menus + if (this.initialized) + return; + + this.initialized = true; + }, + + // Defining model for language node + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + lanowner: undefined, + comment: undefined, + lanacl: [], + seclabels:[] + }, + + // Defining schema for the language node + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', + },{ + id: 'oid', label:'{{ _('Oid') }}', cell: 'string', + type: 'text', disabled: true + },{ + id: 'lanowner', label:'{{ _('Owner') }}', type: 'text', + control: Backform.NodeListByNameControl, node: 'role', + mode: ['edit', 'properties'], select2: { allowClear: false } + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'trusted', label:'{{ _('Trusted?') }}', type: 'switch', options: { onText: 'Yes', offText: 'No', + onColor: 'success', offColor: 'default', size: 'small'}, + group: 'Definition', mode: ['edit', 'properties'], + disabled: function(m) { + return !(m.isNew()); + } + },{ + id: 'lanproc', label:'{{ _('Handler Function') }}', type: 'text', control: 'node-ajax-options', + group: 'Definition', mode: ['edit', 'properties'], url:'get_functions', + /* This function is used to populate the handler function + * for the selected language node. It will check if the property + * type is 'handler' then push the data into the result array. + */ + transform: function(data) { + var res = []; + if (data && _.isArray(data)) { + _.each(data, function(d) { + if (d.prop_type == 'handler') { + res.push({label: d.label, value: d.label}); + } + }) + } + return res; + }, disabled: function(m) { + return !(m.isNew()); + } + },{ + id: 'laninl', label:'{{ _('Inline Function') }}', type: 'text', control: 'node-ajax-options', + group: 'Definition', mode: ['edit', 'properties'], url:'get_functions', + /* This function is used to populate the inline function + * for the selected language node. It will check if the property + * type is 'inline' then push the data into the result array. + */ + transform: function(data) { + var res = []; + if (data && _.isArray(data)) { + _.each(data, function(d) { + if (d.prop_type == 'inline') { + res.push({label: d.label, value: d.label}); + } + }) + } + return res; + }, disabled: function(m) { + return !(m.isNew()); + } + },{ + id: 'lanval', label:'{{ _('Validator Function') }}', type: 'text', control: 'node-ajax-options', + group: 'Definition', mode: ['edit', 'properties'], url:'get_functions', + /* This function is used to populate the validator function + * for the selected language node. It will check if the property + * type is 'validator' then push the data into the result array. + */ + transform: function(data) { + var res = []; + if (data && _.isArray(data)) { + _.each(data, function(d) { + if (d.prop_type == 'validator') { + res.push({label: d.label, value: d.label}); + } + }) + } + return res; + }, disabled: function(m) { + return !(m.isNew()); + } + },{ + id: 'lanacl', label: '{{ _('Privileges') }}', type: 'collection', group: '{{ _('Security') }}', + model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend({privileges: ['U']}), control: 'unique-col-collection', + mode: ['properties', 'edit'], canAdd: true, canDelete: true, uniqueCol : ['grantee'] + },{ + id: 'seclabels', label: '{{ _('Security Labels') }}', + model: SecurityModel, editable: false, type: 'collection', + group: '{{ _('Security') }}', mode: ['edit'], + min_version: 90200, canAdd: true, + canEdit: false, 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 name = this.get('name'); + + if (_.isUndefined(name) || _.isNull(name) || + String(name).replace(/^\s+|\s+$/g, '') == '') { + var msg = '{{ _('Name can not be empty!') }}'; + this.errorModel.set('name', msg); + return msg; + } else { + this.errorModel.unset('name'); + } + return null; + } + }) + }) + } + return pgBrowser.Nodes['coll-language']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.1_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.1_plus/acl.sql new file mode 100644 index 0000000..a88e24f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.1_plus/acl.sql @@ -0,0 +1,23 @@ +SELECT 'lanacl' 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 'USAGE' THEN 'U' + ELSE 'UNKNOWN' + END AS privilege_type + FROM + (SELECT lanacl FROM pg_language lan + LEFT OUTER JOIN pg_shdescription descr ON (lan.oid=descr.objoid AND descr.classoid='pg_language'::regclass) + WHERE lan.oid = {{ lid|qtLiteral }}::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(lanacl) as d FROM pg_language lan1 + WHERE lan1.oid = {{ lid|qtLiteral }}::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/languages/templates/languages/sql/9.1_plus/functions.sql b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.1_plus/functions.sql new file mode 100644 index 0000000..6545e84 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.1_plus/functions.sql @@ -0,0 +1,15 @@ +SELECT + CASE WHEN nspname != 'pg_catalog' THEN quote_ident(nspname) || '.' || quote_ident(proname) + ELSE quote_ident(proname) + END AS label, + CASE + WHEN prorettype = 2280 THEN 'handler' + WHEN proargtypes[0] = 2281 THEN 'inline' + ELSE 'validator' + END AS prop_type +FROM + pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace +WHERE + prorettype=2280 OR + (prorettype=2278 AND proargtypes[0]=26) OR + (prorettype=2278 AND proargtypes[0]=2281) \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.1_plus/properties.sql new file mode 100644 index 0000000..6f6879d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.1_plus/properties.sql @@ -0,0 +1,12 @@ +SELECT lan.oid as oid, lanname as name, lanpltrusted as trusted, lanacl as acl, hp.proname as lanproc, + vp.proname as lanval, description, pg_get_userbyid(lan.lanowner) as lanowner, ip.proname as laninl, + (SELECT array_agg(label) FROM pg_seclabels sl1 WHERE sl1.objoid=lan.oid) AS labels, + (SELECT array_agg(provider) FROM pg_seclabels sl2 WHERE sl2.objoid=lan.oid) AS providers +FROM pg_language lan JOIN pg_proc hp on hp.oid=lanplcallfoid LEFT OUTER JOIN pg_proc ip on ip.oid=laninline +LEFT OUTER JOIN pg_proc vp on vp.oid=lanvalidator +LEFT OUTER JOIN pg_description des ON (des.objoid=lan.oid AND des.objsubid=0 AND des.classoid='pg_language'::regclass) +WHERE lanispl IS TRUE +{% if lid %} +AND lan.oid={{lid}}::int +{% endif %} +ORDER BY lanname \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.1_plus/sqlpane.sql b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.1_plus/sqlpane.sql new file mode 100644 index 0000000..de942f5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.1_plus/sqlpane.sql @@ -0,0 +1,19 @@ +-- Language: {{data.name}} + +-- DROP LANGUAGE {{ conn|qtIdent(data.name) }} +{# ============= CREATE LANGUAGE Query ============= #} +CREATE {% if data.trusted %}TRUSTED{% endif %} PROCEDURAL LANGUAGE {{ conn|qtIdent(data.name) }} +{% if data.lanproc %} + HANDLER {{ conn|qtIdent(data.lanproc) }} +{% endif %} +{% if data.laninl %} + INLINE {{ conn|qtIdent(data.laninl) }} +{% endif %} +{% if data.lanval %} + VALIDATOR {{ conn|qtIdent(data.lanval) }} +{% endif %}; +{# ============= ALTER LANGUAGE Query ============= #} +{% if data.lanowner %} +ALTER LANGUAGE {{ conn|qtIdent(data.name) }} + OWNER TO {{ conn|qtIdent(data.lanowner) }}; +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.1_plus/update.sql new file mode 100644 index 0000000..5206284 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.1_plus/update.sql @@ -0,0 +1,38 @@ +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data %} +{# ============= Update language name ============= #} +{% if data.name != o_data.name %} +ALTER LANGUAGE {{ conn|qtIdent(o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% endif %} +{# ============= Update language user ============= #} +{% if data.lanowner and data.lanowner != o_data.lanowner %} +ALTER LANGUAGE {{ conn|qtIdent(data.name) }} + OWNER TO {{ conn|qtIdent(data.lanowner) }}; +{% endif %} +{# ============= Update language comments ============= #} +{% if data.description and data.description != o_data.description %} +COMMENT ON LANGUAGE {{ conn|qtIdent(data.name) }} + IS '{{ data.description }}'; +{% endif %} +{% endif %} + +{# Change the privileges #} +{% if data.lanacl %} +{% if 'deleted' in data.lanacl %} +{% for priv in data.lanacl.deleted %} +{{ PRIVILEGE.RESETALL(conn, 'LANGUAGE', priv.grantee, data.name) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.lanacl %} +{% for priv in data.lanacl.changed %} +{{ PRIVILEGE.RESETALL(conn, 'LANGUAGE', priv.grantee, data.name) }} +{{ PRIVILEGE.APPLY(conn, 'LANGUAGE', priv.grantee, data.name, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.lanacl %} +{% for priv in data.lanacl.added %} +{{ PRIVILEGE.APPLY(conn, 'LANGUAGE', priv.grantee, data.name, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.3_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.3_plus/acl.sql new file mode 100644 index 0000000..3d75620 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.3_plus/acl.sql @@ -0,0 +1,19 @@ +SELECT 'lanacl' 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 'USAGE' THEN 'U' + ELSE 'UNKNOWN' + END AS privilege_type + FROM + (SELECT lanacl FROM pg_language lan + LEFT OUTER JOIN pg_shdescription descr ON (lan.oid=descr.objoid AND descr.classoid='pg_language'::regclass) + WHERE lan.oid = {{ lid|qtLiteral }}::OID + ) acl, + aclexplode(lanacl) 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/languages/templates/languages/sql/9.3_plus/functions.sql b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.3_plus/functions.sql new file mode 100644 index 0000000..6545e84 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.3_plus/functions.sql @@ -0,0 +1,15 @@ +SELECT + CASE WHEN nspname != 'pg_catalog' THEN quote_ident(nspname) || '.' || quote_ident(proname) + ELSE quote_ident(proname) + END AS label, + CASE + WHEN prorettype = 2280 THEN 'handler' + WHEN proargtypes[0] = 2281 THEN 'inline' + ELSE 'validator' + END AS prop_type +FROM + pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace +WHERE + prorettype=2280 OR + (prorettype=2278 AND proargtypes[0]=26) OR + (prorettype=2278 AND proargtypes[0]=2281) \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.3_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.3_plus/properties.sql new file mode 100644 index 0000000..6f6879d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.3_plus/properties.sql @@ -0,0 +1,12 @@ +SELECT lan.oid as oid, lanname as name, lanpltrusted as trusted, lanacl as acl, hp.proname as lanproc, + vp.proname as lanval, description, pg_get_userbyid(lan.lanowner) as lanowner, ip.proname as laninl, + (SELECT array_agg(label) FROM pg_seclabels sl1 WHERE sl1.objoid=lan.oid) AS labels, + (SELECT array_agg(provider) FROM pg_seclabels sl2 WHERE sl2.objoid=lan.oid) AS providers +FROM pg_language lan JOIN pg_proc hp on hp.oid=lanplcallfoid LEFT OUTER JOIN pg_proc ip on ip.oid=laninline +LEFT OUTER JOIN pg_proc vp on vp.oid=lanvalidator +LEFT OUTER JOIN pg_description des ON (des.objoid=lan.oid AND des.objsubid=0 AND des.classoid='pg_language'::regclass) +WHERE lanispl IS TRUE +{% if lid %} +AND lan.oid={{lid}}::int +{% endif %} +ORDER BY lanname \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.3_plus/sqlpane.sql b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.3_plus/sqlpane.sql new file mode 100644 index 0000000..de942f5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.3_plus/sqlpane.sql @@ -0,0 +1,19 @@ +-- Language: {{data.name}} + +-- DROP LANGUAGE {{ conn|qtIdent(data.name) }} +{# ============= CREATE LANGUAGE Query ============= #} +CREATE {% if data.trusted %}TRUSTED{% endif %} PROCEDURAL LANGUAGE {{ conn|qtIdent(data.name) }} +{% if data.lanproc %} + HANDLER {{ conn|qtIdent(data.lanproc) }} +{% endif %} +{% if data.laninl %} + INLINE {{ conn|qtIdent(data.laninl) }} +{% endif %} +{% if data.lanval %} + VALIDATOR {{ conn|qtIdent(data.lanval) }} +{% endif %}; +{# ============= ALTER LANGUAGE Query ============= #} +{% if data.lanowner %} +ALTER LANGUAGE {{ conn|qtIdent(data.name) }} + OWNER TO {{ conn|qtIdent(data.lanowner) }}; +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.3_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.3_plus/update.sql new file mode 100644 index 0000000..5206284 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/languages/templates/languages/sql/9.3_plus/update.sql @@ -0,0 +1,38 @@ +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data %} +{# ============= Update language name ============= #} +{% if data.name != o_data.name %} +ALTER LANGUAGE {{ conn|qtIdent(o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% endif %} +{# ============= Update language user ============= #} +{% if data.lanowner and data.lanowner != o_data.lanowner %} +ALTER LANGUAGE {{ conn|qtIdent(data.name) }} + OWNER TO {{ conn|qtIdent(data.lanowner) }}; +{% endif %} +{# ============= Update language comments ============= #} +{% if data.description and data.description != o_data.description %} +COMMENT ON LANGUAGE {{ conn|qtIdent(data.name) }} + IS '{{ data.description }}'; +{% endif %} +{% endif %} + +{# Change the privileges #} +{% if data.lanacl %} +{% if 'deleted' in data.lanacl %} +{% for priv in data.lanacl.deleted %} +{{ PRIVILEGE.RESETALL(conn, 'LANGUAGE', priv.grantee, data.name) }} +{% endfor %} +{% endif %} +{% if 'changed' in data.lanacl %} +{% for priv in data.lanacl.changed %} +{{ PRIVILEGE.RESETALL(conn, 'LANGUAGE', priv.grantee, data.name) }} +{{ PRIVILEGE.APPLY(conn, 'LANGUAGE', priv.grantee, data.name, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% if 'added' in data.lanacl %} +{% for priv in data.lanacl.added %} +{{ PRIVILEGE.APPLY(conn, 'LANGUAGE', priv.grantee, data.name, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% endif %} \ No newline at end of file