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