mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Add support for viewing/editing procedural languages.
This commit is contained in:
@@ -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 |
@@ -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'];
|
||||||
|
});
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -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 %}
|
||||||
@@ -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 %}
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -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 %}
|
||||||
@@ -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 %}
|
||||||
Reference in New Issue
Block a user