Add support for Packages on EPAS.

This commit is contained in:
Harshal Dhumal 2016-08-22 12:30:16 +01:00 committed by Dave Page
parent fe54a124da
commit cf1be2a320
68 changed files with 2749 additions and 17 deletions

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

View File

@ -0,0 +1,3 @@
.functions_code {
height: 130px !important;
}

View File

@ -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'];
});

View File

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

View File

@ -0,0 +1,3 @@
SELECT proname AS name
FROM pg_proc
WHERE oid = {{edbfnid}}::oid

View File

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

View File

@ -0,0 +1,6 @@
SELECT
nspname
FROM
pg_namespace
WHERE
oid = {{ scid }}::oid;

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,3 @@
SELECT proname AS name
FROM pg_proc
WHERE oid = {{edbfnid}}::oid

View File

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

View File

@ -0,0 +1,6 @@
SELECT
nspname
FROM
pg_namespace
WHERE
oid = {{ scid }}::oid;

View File

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

View File

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

View File

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

View File

@ -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'];
});

View File

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

View File

@ -0,0 +1,3 @@
SELECT proname AS name
FROM pg_proc
WHERE oid = {{edbfnid}}::oid

View File

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

View File

@ -0,0 +1,6 @@
SELECT
nspname
FROM
pg_namespace
WHERE
oid = {{ scid }}::oid;

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,3 @@
SELECT proname AS name
FROM pg_proc
WHERE oid = {{edbfnid}}::oid

View File

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

View File

@ -0,0 +1,6 @@
SELECT
nspname
FROM
pg_namespace
WHERE
oid = {{ scid }}::oid;

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

View File

@ -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'];
});

View File

@ -0,0 +1,5 @@
SELECT oid,
varname AS name
FROM edb_variable
WHERE varpackage = {{pkgid}}::oid
ORDER BY varname

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

View File

@ -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'];
});

View File

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

View File

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

View File

@ -0,0 +1 @@
DROP PACKAGE {{ conn|qtIdent(data.schema,data.name) }}{% if cascade%} CASCADE{% endif %};

View File

@ -0,0 +1,4 @@
SELECT nsp.oid
FROM pg_namespace nsp
WHERE nspparent = {{scid}}::oid
AND nspname = {{ name|qtLiteral }};

View File

@ -0,0 +1,6 @@
SELECT
nspname
FROM
pg_namespace
WHERE
oid = {{ scid }}::oid;

View File

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

View File

@ -0,0 +1,6 @@
SELECT
nsp.oid, nspname AS name
FROM
pg_namespace nsp
WHERE nspparent = {{scid}}::oid
ORDER BY nspname;

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
DROP PACKAGE {{ conn|qtIdent(data.schema,data.name) }}{% if cascade%} CASCADE{% endif %};

View File

@ -0,0 +1,5 @@
SELECT nsp.oid
FROM pg_namespace nsp
WHERE nspparent = {{scid}}::oid
AND nspname = {{ name|qtLiteral }}
AND nspobjecttype = 0;

View File

@ -0,0 +1,6 @@
SELECT
nspname
FROM
pg_namespace
WHERE
oid = {{ scid }}::oid;

View File

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

View File

@ -0,0 +1,5 @@
SELECT nsp.oid, nspname AS name
FROM pg_namespace nsp
WHERE nspparent = {{scid}}::oid
AND nspobjecttype = 0
ORDER BY nspname;

View File

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

View File

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

View File

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

View File

@ -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.") }}',