Add support for Packages on EPAS.
@ -19,6 +19,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
|||||||
type: 'catalog_object_column',
|
type: 'catalog_object_column',
|
||||||
label: '{{ _('catalog_object_column') }}',
|
label: '{{ _('catalog_object_column') }}',
|
||||||
hasSQL: false,
|
hasSQL: false,
|
||||||
|
hasScriptTypes: [],
|
||||||
hasDepends: true,
|
hasDepends: true,
|
||||||
Init: function() {
|
Init: function() {
|
||||||
/* Avoid mulitple registration of menus */
|
/* Avoid mulitple registration of menus */
|
||||||
|
@ -18,6 +18,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
|
|||||||
type: 'catalog_object',
|
type: 'catalog_object',
|
||||||
label: '{{ _('Catalog Object') }}',
|
label: '{{ _('Catalog Object') }}',
|
||||||
hasSQL: false,
|
hasSQL: false,
|
||||||
|
hasScriptTypes: [],
|
||||||
hasDepends: true,
|
hasDepends: true,
|
||||||
Init: function() {
|
Init: function() {
|
||||||
/* Avoid mulitple registration of menus */
|
/* 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']]
|
db_privileges[privilege['privilege_type']]
|
||||||
)
|
)
|
||||||
# If we have all acl then just return all
|
# 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']
|
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']
|
priv_without_grant = ['ALL']
|
||||||
# Appending and returning all ACL
|
# Appending and returning all ACL
|
||||||
privileges.append({
|
privileges.append({
|
||||||
|
@ -73,17 +73,21 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) {
|
|||||||
self.node_initialized = true;
|
self.node_initialized = true;
|
||||||
|
|
||||||
pgAdmin.Browser.add_menus([{
|
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,
|
name: 'refresh', node: self.type, module: self,
|
||||||
applies: ['object', 'context'], callback: 'refresh',
|
applies: ['object', 'context'], callback: 'refresh',
|
||||||
priority: 1, label: '{{ _("Refresh...") }}',
|
priority: 1, label: '{{ _("Refresh...") }}',
|
||||||
icon: 'fa fa-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) {
|
if (self.canDrop) {
|
||||||
pgAdmin.Browser.add_menus([{
|
pgAdmin.Browser.add_menus([{
|
||||||
name: 'delete_object', node: self.type, module: self,
|
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
|
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);
|
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.
|
* 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
|
// Create proper buttons
|
||||||
|
|
||||||
var buttons = [];
|
var buttons = [];
|
||||||
|
|
||||||
buttons.push({
|
buttons.push({
|
||||||
label: '', type: 'edit',
|
label: '', type: 'edit',
|
||||||
tooltip: '{{ _("Edit") }}',
|
tooltip: '{{ _("Edit") }}',
|
||||||
extraClasses: ['btn-default'],
|
extraClasses: ['btn-default'],
|
||||||
icon: 'fa fa-lg fa-pencil-square-o',
|
icon: 'fa fa-lg fa-pencil-square-o',
|
||||||
disabled: false,
|
disabled: !that.canEdit,
|
||||||
register: function(btn) {
|
register: function(btn) {
|
||||||
btn.click(function() {
|
btn.click(function() {
|
||||||
onEdit();
|
onEdit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
buttons.push({
|
buttons.push({
|
||||||
label: '', type: 'help',
|
label: '', type: 'help',
|
||||||
tooltip: '{{ _("SQL help for this object type.") }}',
|
tooltip: '{{ _("SQL help for this object type.") }}',
|
||||||
|