Add support for viewing/editing procedural languages.

pull/3/head
Akshay Joshi 2016-02-23 10:07:14 +00:00 committed by Dave Page
parent 3b7886e61e
commit 7ffbb8c039
14 changed files with 922 additions and 0 deletions

View File

@ -0,0 +1,520 @@
##########################################################################
#
# 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 a 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 nodes are 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 its base view.
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 whether javascript exists for this node.
"""
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 check the
database connection before running the view. It 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 is used to create all the child nodes within the collection.
Here it will create all the language nodes.
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 in the 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 gets the dependents and returns an 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 gets the dependencies and returns an 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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

View File

@ -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
},
// Define the 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;
},
// Define the model for language node
model: pgAdmin.Browser.Node.Model.extend({
defaults: {
name: undefined,
lanowner: undefined,
comment: undefined,
lanacl: [],
seclabels:[]
},
// Define the 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'];
});

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 %}

View File

@ -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 %}