Various procedure/function related fixes for EPAS/PG 11. Fixes #3446

- Fix the RM #3446 "plpgsql keyword should displayed in SQL pane for EPAS-11".
- Procedures for EPAS 11 always created as 'edbspl', even though language is sql or plpgsql.
- Not able to create Function/Procedure/Trigger function if the language is "C" for all supported database server.
- Function creation for EPAS 9.6 and above when set "Window?" flag to "Yes".
- Disable Volatility, Strict, Parallel, Estimated cost and Leak prof while creating procedures in EPAS 11 when language is not 'edbspl'.
- Rename "default" folder to "11_plus" inside "pg" directory for procedures, as procedures are supported from PG11 onwards.
- Creating/Deleting parameter in Procedure for PG 11.
- Drop procedures with arguments for PG/EPAS 11.
pg_proc table has added default value 'v' for Volatility, 100 for Cost and 'u' for Parallel for any language. All the three are not supported for Procedures in PG/EPAS 11 (excluding 'edbspl' language for EPAS). Fixed the issue where it is visible in properties panel if we create procedure using language 'plpgsql' or 'sql'.
pull/14/head
Akshay Joshi 2018-07-17 12:43:46 +01:00 committed by Dave Page
parent a49923dba2
commit 39b41d7b24
23 changed files with 255 additions and 20 deletions

View File

@ -22,4 +22,5 @@ Bug fixes
| `Bug #3319 <https://redmine.postgresql.org/issues/3319>`_ - Cleanup and fix handling of Query Tool Cancel button status.
| `Bug #3363 <https://redmine.postgresql.org/issues/3363>`_ - Fix restoring of restore options for sections.
| `Bug #3371 <https://redmine.postgresql.org/issues/3371>`_ - Don't create a session when the /misc/ping test endpoint is called.
| `Bug #3446 <https://redmine.postgresql.org/issues/3446>`_ - Various procedure/function related fixes for EPAS/PG 11.
| `Bug #3457 <https://redmine.postgresql.org/issues/3457>`_ - Fix debugging of procedures in EPAS packages.

View File

@ -257,8 +257,13 @@ class FunctionView(PGChildNodeView, DataTypeReader):
req = request.args or request.form
if 'fnid' not in kwargs:
req_args = self.required_args
# We need to remove 'prosrc' from the required arguments list
# if language is 'c'.
if req['lanname'] == 'c' and 'prosrc' in req_args:
req_args.remove('prosrc')
for arg in self.required_args:
for arg in req_args:
if (arg not in req or req[arg] == '') or \
(arg == 'probin' and req['lanname'] == 'c' and
(arg not in req or req[arg] == '')):
@ -1153,12 +1158,14 @@ class FunctionView(PGChildNodeView, DataTypeReader):
old_data['pronamespace']
)
if 'provolatile' in old_data:
if 'provolatile' in old_data and \
old_data['provolatile'] is not None:
old_data['provolatile'] = vol_dict[old_data['provolatile']]
if 'proparallel' in old_data:
old_data['proparallel'] = parallel_dict[
old_data['proparallel']]
if 'proparallel' in old_data and \
old_data['proparallel'] is not None:
old_data['proparallel'] = \
parallel_dict[old_data['proparallel']]
# If any of the below argument is changed,
# then CREATE OR REPLACE SQL statement should be called
@ -1354,6 +1361,17 @@ class FunctionView(PGChildNodeView, DataTypeReader):
resp_data.update(parse_variables_from_db([
{"setconfig": resp_data['proconfig']}]))
# Reset Volatile, Cost and Parallel parameter for Procedures
# if language is not 'edbspl' and database server version is
# greater than 10.
if self.manager.sversion >= 110000 \
and ('prokind' in resp_data and resp_data['prokind'] == 'p') \
and ('lanname' in resp_data and
resp_data['lanname'] != 'edbspl'):
resp_data['procost'] = None
resp_data['provolatile'] = None
resp_data['proparallel'] = None
return resp_data
def _get_schema(self, scid):

View File

@ -255,6 +255,7 @@ define('pgadmin.node.function', [
},{
id: 'provolatile', label: gettext('Volatility'), cell: 'string',
control: 'node-ajax-options', type: 'text', group: gettext('Options'),
deps: ['lanname'],
options:[
{'label': 'VOLATILE', 'value': 'v'},
{'label': 'STABLE', 'value': 's'},
@ -267,6 +268,7 @@ define('pgadmin.node.function', [
},{
id: 'proisstrict', label: gettext('Strict?'), type: 'switch',
group: gettext('Options'), disabled: 'isDisabled',
deps: ['lanname'],
options: {
'onText': gettext('Yes'), 'offText': gettext('No'),
'onColor': 'success', 'offColor': 'primary',
@ -283,6 +285,7 @@ define('pgadmin.node.function', [
},{
id: 'proparallel', label: gettext('Parallel'), cell: 'string',
control: 'node-ajax-options', type: 'text', group: gettext('Options'),
deps: ['lanname'],
options:[
{'label': 'UNSAFE', 'value': 'u'},
{'label': 'RESTRICTED', 'value': 'r'},
@ -291,7 +294,7 @@ define('pgadmin.node.function', [
select2: {allowClear: false},
},{
id: 'procost', label: gettext('Estimated cost'), group: gettext('Options'),
cell:'string', type: 'text', disabled: 'isDisabled',
cell:'string', type: 'text', disabled: 'isDisabled', deps: ['lanname'],
},{
id: 'prorows', label: gettext('Estimated rows'), type: 'text',
deps: ['proretset'], visible: 'isVisible', disabled: 'isDisabled',
@ -299,7 +302,7 @@ define('pgadmin.node.function', [
},{
id: 'proleakproof', label: gettext('Leak proof?'),
group: gettext('Options'), cell:'boolean', type: 'switch', min_version: 90200,
disabled: 'isDisabled',
disabled: 'isDisabled', deps: ['lanname'],
},{
id: 'proacl', label: gettext('Privileges'), type: 'text',
mode: ['properties'], group: gettext('Security'),

View File

@ -105,7 +105,22 @@ define('pgadmin.node.procedure', [
case 'proisstrict':
case 'procost':
case 'proleakproof':
return (this.node_info.server.version < 90500 || this.node_info.server.server_type != 'ppas');
if(this.node_info.server.version < 90500 ||
this.node_info.server.server_type != 'ppas' ||
m.get('lanname') != 'edbspl') {
setTimeout(function() {
m.set('provolatile', undefined);
m.set('proisstrict', undefined);
m.set('procost', undefined);
m.set('proleakproof', undefined);
}, 10);
return true;
}
else{
return false;
}
case 'variables':
case 'prosecdef':
return this.node_info.server.version < 90500;
@ -116,7 +131,17 @@ define('pgadmin.node.procedure', [
case 'proargs':
return true;
case 'proparallel':
return (this.node_info.server.version < 90600 || this.node_info.server.server_type != 'ppas');
if (this.node_info.server.version < 90600 ||
this.node_info.server.server_type != 'ppas' ||
m.get('lanname') != 'edbspl') {
setTimeout(function() {
m.set('proparallel', undefined);
}, 10);
return true;
}
else{
return false;
}
case 'lanname':
return this.node_info.server.version < 110000;
default:
@ -144,11 +169,24 @@ define('pgadmin.node.procedure', [
errmsg = errmsg || err['lanname'];
}
if (_.isUndefined(this.get('prosrc')) || String(this.get('prosrc')).replace(/^\s+|\s+$/g, '') == '') {
err['prosrc'] = gettext('Code cannot be empty.');
errmsg = errmsg || err['prosrc'];
}
if (String(this.get('lanname')) == 'c') {
if (_.isUndefined(this.get('probin')) || String(this.get('probin'))
.replace(/^\s+|\s+$/g, '') == '') {
err['probin'] = gettext('Object File cannot be empty.');
errmsg = errmsg || err['probin'];
}
if (_.isUndefined(this.get('prosrc_c')) || String(this.get('prosrc_c')).replace(/^\s+|\s+$/g, '') == '') {
err['prosrc_c'] = gettext('Link Symbol cannot be empty.');
errmsg = errmsg || err['prosrc_c'];
}
}
else {
if (_.isUndefined(this.get('prosrc')) || String(this.get('prosrc')).replace(/^\s+|\s+$/g, '') == '') {
err['prosrc'] = gettext('Code cannot be empty.');
errmsg = errmsg || err['prosrc'];
}
}
if (seclabels) {
var secLabelsErr;

View File

@ -19,7 +19,7 @@ CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ con
{% if data.provolatile %}{% if data.provolatile == 'i' %}IMMUTABLE{% elif data.provolatile == 's' %}STABLE{% else %}VOLATILE{% endif %} {% endif %}{% if data.proleakproof %}LEAKPROOF {% endif %}
{% if data.proisstrict %}STRICT {% endif %}
{% if data.prosecdef %}SECURITY DEFINER {% endif %}
{% if data.proiswindow %}WINDOW{% endif %}
{% if data.proiswindow %}WINDOW {% endif %}
{% if data.proparallel and (data.proparallel == 'r' or data.proparallel == 's') %}
{% if data.proparallel == 'r' %}PARALLEL RESTRICTED{% elif data.proparallel == 's' %}PARALLEL SAFE{% endif %}{% endif %}
{% if data.procost %}

View File

@ -17,5 +17,5 @@ WHERE
{% endif %}
{% if name %}
DROP PROCEDURE {{ conn|qtIdent(nspname, name) }};
DROP PROCEDURE {{ conn|qtIdent(nspname, name) }}{{func_args}}{% if cascade %} CASCADE{% endif %};
{% endif %}

View File

@ -1,5 +1,6 @@
{% import 'macros/functions/security.macros' as SECLABEL %}
{% import 'macros/functions/privilege.macros' as PRIVILEGE %}{% if data %}
{% import 'macros/functions/privilege.macros' as PRIVILEGE %}
{% import 'macros/functions/variable.macros' as VARIABLE %}{% if data %}
{% set name = o_data.name %}
{% if data.name %}
{% if data.name != o_data.name %}
@ -24,7 +25,7 @@ CREATE OR REPLACE PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}({% if
{% if data.merged_variables %}{% for v in data.merged_variables %}
SET {{ conn|qtIdent(v.name) }}={{ v.value|qtLiteral }}{% endfor -%}
{% endif %}
{% endif %}
AS {% if 'probin' in data or 'prosrc_c' in data %}
{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
@ -65,11 +66,11 @@ ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}{% if o_data.proarg
{% if data.variables %}
{% if 'deleted' in data.variables and data.variables.deleted|length > 0 %}
{{ VARIABLE.UNSET(conn, 'FUNCTION', name, data.variables.deleted, o_data.pronamespace, o_data.proargtypenames) }}
{{ VARIABLE.UNSET(conn, 'PROCEDURE', name, data.variables.deleted, o_data.pronamespace, o_data.proargtypenames) }}
{% endif -%}
{% if 'merged_variables' in data and data.merged_variables|length > 0 %}
{{ VARIABLE.SET(conn, 'FUNCTION', name, data.merged_variables, o_data.pronamespace, o_data.proargtypenames) }}
{{ VARIABLE.SET(conn, 'PROCEDURE', name, data.merged_variables, o_data.pronamespace, o_data.proargtypenames) }}
{% endif -%}
{% endif -%}
{% endif -%}

View File

@ -0,0 +1,59 @@
{% import 'macros/functions/security.macros' as SECLABEL %}
{% import 'macros/functions/privilege.macros' as PRIVILEGE %}
{% import 'macros/functions/variable.macros' as VARIABLE %}
{% set is_columns = [] %}
{% if data %}
{% if query_for == 'sql_panel' and func_def is defined %}
CREATE OR REPLACE PROCEDURE {{func_def}}
{% else %}
CREATE OR REPLACE PROCEDURE {{ conn|qtIdent(data.pronamespace, data.name) }}{% if data.arguments is defined %}
({% for p in data.arguments %}{% if p.argmode %}{{p.argmode}} {% endif %}{% if p.argname %}{{ conn|qtIdent(p.argname)}} {% endif %}{% if p.argtype %}{{ conn|qtTypeIdent(p.argtype) }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %}
{% if not loop.last %}, {% endif %}
{% endfor -%}
{% endif %}
)
{% endif %}
LANGUAGE {{ data.lanname|qtLiteral }}
{% if data.prosecdef %}SECURITY DEFINER {% endif %}
{% if data.lanname == 'edbspl' %}
{{ data.provolatile }} {% if data.proleakproof %}LEAKPROOF {% endif %}
{% if data.proisstrict %}STRICT {% endif %}
{% if data.proparallel and (data.proparallel == 'r' or data.proparallel == 's') %}
{% if data.proparallel == 'r' %}PARALLEL RESTRICTED{% elif data.proparallel == 's' %}PARALLEL SAFE{% endif %}{% endif %}{% if data.procost %}
COST {{data.procost}}{% endif %}{% if data.prorows and (data.prorows | int) > 0 %}
ROWS {{data.prorows}}{% endif -%}{% if data.variables %}{% for v in data.variables %}
SET {{ conn|qtIdent(v.name) }}={{ v.value|qtLiteral }}{% endfor -%}
{% endif %}
{% endif %}
AS {% if data.lanname == 'c' %}
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
{% else %}
$BODY${{ data.prosrc }}$BODY${% endif -%};
{% if data.acl and not is_sql %}
{% for p in data.acl %}
{{ PRIVILEGE.SET(conn, "PROCEDURE", p.grantee, data.name, p.without_grant, p.with_grant, data.pronamespace, data.func_args_without)}}
{% endfor %}{% endif %}
{% if data.revoke_all %}
{{ PRIVILEGE.UNSETALL(conn, "PROCEDURE", "PUBLIC", data.name, data.pronamespace, data.func_args_without)}}
{% endif %}
{% if data.description %}
COMMENT ON PROCEDURE {{ conn|qtIdent(data.pronamespace, data.name) }}
IS {{ data.description|qtLiteral }};
{% endif -%}
{% if data.seclabels %}
{% for r in data.seclabels %}
{% if r.label and r.provider %}
{{ SECLABEL.SET(conn, 'PROCEDURE', data.name, r.provider, r.label, data.pronamespace, data.func_args_without) }}
{% endif %}
{% endfor %}
{% endif -%}
{% endif %}

View File

@ -17,5 +17,5 @@ WHERE
{% endif %}
{% if name %}
DROP PROCEDURE {{ conn|qtIdent(nspname, name) }};
DROP PROCEDURE {{ conn|qtIdent(nspname, name) }}{{func_args}}{% if cascade %} CASCADE{% endif %};
{% endif %}

View File

@ -0,0 +1,115 @@
{% import 'macros/functions/security.macros' as SECLABEL %}
{% import 'macros/functions/privilege.macros' as PRIVILEGE %}
{% import 'macros/functions/variable.macros' as VARIABLE %}{% if data %}
{% set name = o_data.name %}
{% if data.name %}
{% if data.name != o_data.name %}
ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, o_data.name) }}{% if o_data.proargtypenames %}({{ o_data.proargtypenames }}){% endif %}
RENAME TO {{ conn|qtIdent(data.name) }};
{% set name = data.name %}
{% endif %}
{% endif -%}
{% if data.change_func %}
CREATE OR REPLACE PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}({% if data.arguments %}{% for p in data.arguments %}{% if p.argmode %}{{p.argmode}} {% endif %}{% if p.argname %}{{ conn|qtIdent(p.argname) }} {% endif %}{% if p.argtype %}{{ conn|qtTypeIdent(p.argtype) }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %}
{% if not loop.last %}, {% endif %}
{% endfor %}
{% endif %}
)
{% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral }}
{% endif %}
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %}SECURITY DEFINER{% endif %}
{% if data.lanname == 'edbspl' %}
{% if ('proleakproof' in data and data.proleakproof) or ('proleakproof' not in data and o_data.proleakproof) %} LEAKPROOF{% else %} NOT LEAKPROOF{% endif %}
{% if ('proisstrict' in data and data.proisstrict) or ('proisstrict' not in data and o_data.proisstrict) %} STRICT{% endif %}
{% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}}{% endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}
SET {{ conn|qtIdent(v.name) }}={{ v.value|qtLiteral }}{% endfor -%}
{% endif %}
{% endif %}
AS {% if 'probin' in data or 'prosrc_c' in data %}
{% if 'probin' in data %}{{ data.probin|qtLiteral }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %}
$BODY${{ o_data.prosrc }}$BODY${% endif -%};
{% endif -%}
{% if data.funcowner %}
ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}{% if o_data.proargtypenames %}({{ o_data.proargtypenames }}){% endif %}
OWNER TO {{ data.funcowner }};
{% endif -%}
{# The SQL generated below will change priviledges #}
{% if data.acl %}
{% if 'deleted' in data.acl %}
{% for priv in data.acl.deleted %}
{{ PRIVILEGE.UNSETALL(conn, 'PROCEDURE', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif -%}
{% if 'changed' in data.datacl %}
{% for priv in data.acl.changed %}
{{ PRIVILEGE.UNSETALL(conn, 'PROCEDURE', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
{{ PRIVILEGE.SET(conn, 'PROCEDURE', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif -%}
{% if 'added' in data.acl %}
{% for priv in data.acl.added %}
{{ PRIVILEGE.SET(conn, 'PROCEDURE', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif %}
{% endif -%}
{% if data.change_func == False %}
{% if data.variables %}
{% if 'deleted' in data.variables and data.variables.deleted|length > 0 %}
{{ VARIABLE.UNSET(conn, 'PROCEDURE', name, data.variables.deleted, o_data.pronamespace, o_data.proargtypenames) }}
{% endif -%}
{% if 'merged_variables' in data and data.merged_variables|length > 0 %}
{{ VARIABLE.SET(conn, 'PROCEDURE', name, data.merged_variables, o_data.pronamespace, o_data.proargtypenames) }}
{% endif -%}
{% endif -%}
{% endif -%}
{% set seclabels = data.seclabels %}
{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
{% for r in seclabels.deleted %}
{{ SECLABEL.UNSET(conn, 'PROCEDURE', name, r.provider, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif -%}
{% if 'added' in seclabels and seclabels.added|length > 0 %}
{% for r in seclabels.added %}
{{ SECLABEL.SET(conn, 'PROCEDURE', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif -%}
{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
{% for r in seclabels.changed %}
{{ SECLABEL.SET(conn, 'PROCEDURE', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif -%}
{% if data.description is defined and data.description != o_data.description%}
COMMENT ON PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}
IS {{ data.description|qtLiteral }};
{% endif -%}
{% if data.pronamespace %}
ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}
SET SCHEMA {{ conn|qtIdent(data.pronamespace) }};
{% endif -%}
{% endif %}