Add support for planner support functions in PostgreSQL 12+ functions. Fixes #4333
parent
108af5c10b
commit
adb5cd34bf
|
@ -87,6 +87,8 @@ function:
|
|||
The default assumption is 1000 rows.
|
||||
* Move the *Leak proof?* switch to indicate whether the function has side
|
||||
effects. The default is *No*. This option can only be set by the superuser.
|
||||
* Use the *Support function* field to specify a planner support function to
|
||||
use for the function.
|
||||
|
||||
Click the *Arguments* tab to continue.
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 98 KiB |
|
@ -9,6 +9,7 @@ This release contains a number of bug fixes and new features since the release o
|
|||
New features
|
||||
************
|
||||
|
||||
| `Issue #4333 <https://redmine.postgresql.org/issues/4333>`_ - Add support for planner support functions in PostgreSQL 12+ functions.
|
||||
|
||||
Housekeeping
|
||||
************
|
||||
|
|
|
@ -208,7 +208,9 @@ class FunctionView(PGChildNodeView, DataTypeReader):
|
|||
'get_languages': [{'get': 'get_languages'}, {'get': 'get_languages'}],
|
||||
'vopts': [{}, {'get': 'variable_options'}],
|
||||
'select_sql': [{'get': 'select_sql'}],
|
||||
'exec_sql': [{'get': 'exec_sql'}]
|
||||
'exec_sql': [{'get': 'exec_sql'}],
|
||||
'get_support_functions': [{'get': 'get_support_functions'},
|
||||
{'get': 'get_support_functions'}]
|
||||
})
|
||||
|
||||
@property
|
||||
|
@ -1158,7 +1160,7 @@ class FunctionView(PGChildNodeView, DataTypeReader):
|
|||
fun_change_args = ['lanname', 'prosrc', 'probin', 'prosrc_c',
|
||||
'provolatile', 'proisstrict', 'prosecdef',
|
||||
'proparallel', 'procost', 'proleakproof',
|
||||
'arguments', 'prorows']
|
||||
'arguments', 'prorows', 'prosupportfunc']
|
||||
|
||||
data['change_func'] = False
|
||||
for arg in fun_change_args:
|
||||
|
@ -1390,6 +1392,36 @@ class FunctionView(PGChildNodeView, DataTypeReader):
|
|||
|
||||
return revoke_all
|
||||
|
||||
@check_precondition
|
||||
def get_support_functions(self, gid, sid, did, scid):
|
||||
"""
|
||||
This function will return list of available support functions.
|
||||
"""
|
||||
res = [{'label': '', 'value': ''}]
|
||||
|
||||
try:
|
||||
SQL = render_template(
|
||||
"/".join([self.sql_template_path,
|
||||
'get_support_functions.sql']),
|
||||
show_system_objects=self.blueprint.show_system_objects
|
||||
)
|
||||
status, rset = self.conn.execute_2darray(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
for row in rset['rows']:
|
||||
res.append(
|
||||
{'label': row['sfunctions'],
|
||||
'value': row['sfunctions']}
|
||||
)
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def dependents(self, gid, sid, did, scid, fnid):
|
||||
"""
|
||||
|
|
|
@ -178,6 +178,7 @@ define('pgadmin.node.function', [
|
|||
procost: undefined, /* Estimated execution Cost */
|
||||
prorows: undefined, /* Estimated number of rows */
|
||||
proleakproof: undefined,
|
||||
prosupportfunc: undefined, /* Support function */
|
||||
arguments: [],
|
||||
prosrc: undefined,
|
||||
prosrc_c: undefined,
|
||||
|
@ -312,6 +313,12 @@ define('pgadmin.node.function', [
|
|||
id: 'proleakproof', label: gettext('Leak proof?'),
|
||||
group: gettext('Options'), cell:'boolean', type: 'switch', min_version: 90200,
|
||||
disabled: 'isDisabled', deps: ['lanname'],
|
||||
},{
|
||||
id: 'prosupportfunc', label: gettext('Support function'),
|
||||
type: 'text', disabled: 'isDisabled',
|
||||
group: gettext('Options'), visible: 'isVisible',
|
||||
control: 'node-ajax-options', url: 'get_support_functions',
|
||||
cache_node: 'function', min_version: 120000,
|
||||
},{
|
||||
id: 'proacl', label: gettext('Privileges'), type: 'text',
|
||||
mode: ['properties'], group: gettext('Security'),
|
||||
|
@ -440,6 +447,11 @@ define('pgadmin.node.function', [
|
|||
return false;
|
||||
}
|
||||
return true;
|
||||
case 'prosupportfunc':
|
||||
var item = pgAdmin.Browser.tree.selected();
|
||||
if(pgAdmin.Browser.Nodes['function'].getTreeNodeHierarchy(item).server.user.is_superuser)
|
||||
return false;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
{% 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{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{func_def}}
|
||||
{% else %}
|
||||
CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ conn|qtIdent(data.pronamespace, data.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 %}{{ p.argtype }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %}
|
||||
{% if not loop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
{% endif -%}
|
||||
)
|
||||
{% endif %}
|
||||
RETURNS{% if data.proretset and (data.prorettypename.startswith('SETOF ') or data.prorettypename.startswith('TABLE')) %} {{ data.prorettypename }} {% elif data.proretset %} SETOF {{ data.prorettypename }}{% else %} {{ data.prorettypename }}{% endif %}
|
||||
|
||||
LANGUAGE {{ data.lanname|qtLiteral }}
|
||||
{% if data.procost %}
|
||||
|
||||
COST {{data.procost}}
|
||||
{% endif %}
|
||||
{% 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.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.prorows and (data.prorows | int) > 0 %}
|
||||
|
||||
ROWS {{data.prorows}}{% endif %}
|
||||
|
||||
{% if data.prosupportfunc %}SUPPORT {{ data.prosupportfunc }}{% endif -%}{% if data.variables %}{% for v in data.variables %}
|
||||
|
||||
SET {{ conn|qtIdent(v.name) }}={{ v.value|qtLiteral }}{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY${{ data.prosrc }}$BODY${% endif -%};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
|
||||
OWNER TO {{ conn|qtIdent(data.funcowner) }};
|
||||
{% endif -%}
|
||||
{% if data.acl %}
|
||||
{% for p in data.acl %}
|
||||
|
||||
{{ PRIVILEGE.SET(conn, "FUNCTION", 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, "FUNCTION", "PUBLIC", data.name, data.pronamespace, data.func_args_without)}}
|
||||
{% endif %}
|
||||
{% if data.description %}
|
||||
|
||||
COMMENT ON FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
|
||||
IS {{ data.description|qtLiteral }};
|
||||
{% endif -%}
|
||||
{% if data.seclabels %}
|
||||
{% for r in data.seclabels %}
|
||||
{% if r.label and r.provider %}
|
||||
|
||||
{{ SECLABEL.SET(conn, 'FUNCTION', data.name, r.provider, r.label, data.pronamespace, data.func_args_without) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif -%}
|
||||
|
||||
{% endif %}
|
|
@ -0,0 +1,12 @@
|
|||
SELECT quote_ident(nspname) || '.' || quote_ident(proname) AS sfunctions
|
||||
FROM pg_proc p, pg_namespace n, pg_language l
|
||||
WHERE p.pronamespace = n.oid
|
||||
AND p.prolang = l.oid
|
||||
AND p.prorettype = 'internal'::regtype::oid
|
||||
AND pg_catalog.array_to_string(p.proargtypes, ',') = 'internal'::regtype::oid::text
|
||||
AND (l.lanname = 'internal' or l.lanname = 'c' )
|
||||
-- -- If Show SystemObjects is not true
|
||||
{% if not show_system_objects %}
|
||||
AND (nspname NOT LIKE 'pg\_%' AND nspname NOT in ('information_schema'))
|
||||
{% endif %}
|
||||
ORDER BY nspname ASC, proname ASC
|
|
@ -0,0 +1,40 @@
|
|||
SELECT
|
||||
pr.oid, pr.xmin, pr.*, pr.prosrc AS prosrc_c,
|
||||
pr.proname AS name, pg_get_function_result(pr.oid) AS prorettypename,
|
||||
typns.nspname AS typnsp, lanname, proargnames, oidvectortypes(proargtypes) AS proargtypenames,
|
||||
pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals,
|
||||
pronargdefaults, proconfig, pg_get_userbyid(proowner) AS funcowner, description,
|
||||
CASE WHEN pr.prokind = 'w' THEN TRUE ELSE FALSE END AS proiswindow,
|
||||
CASE WHEN prosupport = 0::oid THEN ''
|
||||
ELSE (
|
||||
SELECT quote_ident(nspname) || '.' || quote_ident(proname) AS tfunctions
|
||||
FROM pg_proc p, pg_namespace n
|
||||
WHERE p.pronamespace = n.oid
|
||||
AND p.oid = pr.prosupport::OID
|
||||
) END AS prosupportfunc,
|
||||
(SELECT
|
||||
array_agg(provider || '=' || label)
|
||||
FROM
|
||||
pg_seclabel sl1
|
||||
WHERE
|
||||
sl1.objoid=pr.oid) AS seclabels
|
||||
FROM
|
||||
pg_proc pr
|
||||
JOIN
|
||||
pg_type typ ON typ.oid=prorettype
|
||||
JOIN
|
||||
pg_namespace typns ON typns.oid=typ.typnamespace
|
||||
JOIN
|
||||
pg_language lng ON lng.oid=prolang
|
||||
LEFT OUTER JOIN
|
||||
pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass)
|
||||
WHERE
|
||||
pr.prokind IN ('f', 'w')
|
||||
{% if fnid %}
|
||||
AND pr.oid = {{fnid}}::oid
|
||||
{% else %}
|
||||
AND pronamespace = {{scid}}::oid
|
||||
{% endif %}
|
||||
AND typname NOT IN ('trigger', 'event_trigger')
|
||||
ORDER BY
|
||||
proname;
|
|
@ -0,0 +1,118 @@
|
|||
{% 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 FUNCTION {{ conn|qtIdent(o_data.pronamespace, o_data.name) }}({{
|
||||
o_data.proargtypenames }})
|
||||
RENAME TO {{ conn|qtIdent(data.name) }};
|
||||
{% set name = data.name %}
|
||||
{% endif %}
|
||||
{% endif -%}
|
||||
{% if data.change_func %}
|
||||
|
||||
CREATE OR REPLACE FUNCTION {{ 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 %}{{ p.argtype }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %}
|
||||
{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
{% endif -%}
|
||||
)
|
||||
RETURNS {{ o_data.prorettypename }}
|
||||
{% if 'lanname' in data %}
|
||||
LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
|
||||
LANGUAGE {{ o_data.lanname|qtLiteral }}
|
||||
{% endif %}{% if 'provolatile' in data and data.provolatile %}{{ data.provolatile }} {% elif 'provolatile' not in data and o_data.provolatile %}{{ o_data.provolatile }}{% endif %}
|
||||
{% if ('proleakproof' in data and data.proleakproof) or ('proleakproof' not in data and o_data.proleakproof) %} LEAKPROOF{% elif 'proleakproof' in data and not data.proleakproof %} NOT LEAKPROOF{% endif %}
|
||||
{% if ('proisstrict' in data and data.proisstrict) or ('proisstrict' not in data and o_data.proisstrict) %} STRICT{% endif %}
|
||||
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
|
||||
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% 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.prosupportfunc %}SUPPORT {{ data.prosupportfunc }}{% elif data.prosupportfunc is not defined and o_data.prosupportfunc %}SUPPORT {{ o_data.prosupportfunc }}{% endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}
|
||||
|
||||
SET {{ conn|qtIdent(v.name) }}={{ v.value|qtLiteral }}{% endfor -%}
|
||||
{% 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 FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
|
||||
OWNER TO {{ conn|qtIdent(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, 'FUNCTION', 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, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
|
||||
|
||||
{{ PRIVILEGE.SET(conn, 'FUNCTION', 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, 'FUNCTION', 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, 'FUNCTION', 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) }}
|
||||
{% endif -%}
|
||||
{% endif -%}
|
||||
{% endif -%}
|
||||
{% set seclabels = data.seclabels %}
|
||||
{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
|
||||
{% for r in seclabels.deleted %}
|
||||
|
||||
{{ SECLABEL.UNSET(conn, 'FUNCTION', 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, 'FUNCTION', 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, 'FUNCTION', 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 FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
|
||||
IS {{ data.description|qtLiteral }};
|
||||
{% endif -%}
|
||||
|
||||
{% if data.pronamespace %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
|
||||
SET SCHEMA {{ conn|qtIdent(data.pronamespace) }};
|
||||
{% endif -%}
|
||||
|
||||
{% endif %}
|
|
@ -0,0 +1,68 @@
|
|||
{% 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{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{func_def}}
|
||||
{% else %}
|
||||
CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ conn|qtIdent(data.pronamespace, data.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 %}{{ p.argtype }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %}
|
||||
{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
{% endif -%}
|
||||
)
|
||||
{% endif -%}
|
||||
RETURNS{% if data.proretset and (data.prorettypename.startswith('SETOF ') or data.prorettypename.startswith('TABLE')) %} {{ data.prorettypename }} {% elif data.proretset %} SETOF {{ data.prorettypename }}{% else %} {{ data.prorettypename }}{% endif %}
|
||||
|
||||
LANGUAGE {{ data.lanname|qtLiteral }}
|
||||
{% 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.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.prosupportfunc %}SUPPORT {{ data.prosupportfunc }}{% endif -%}{% if data.variables %}{% for v in data.variables %}
|
||||
|
||||
SET {{ conn|qtIdent(v.name) }}={{ v.value|qtLiteral }}{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY${{ data.prosrc }}$BODY${% endif -%};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
|
||||
OWNER TO {{ conn|qtIdent(data.funcowner) }};
|
||||
{% endif -%}
|
||||
{% if data.acl %}
|
||||
{% for p in data.acl %}
|
||||
|
||||
{{ PRIVILEGE.SET(conn, "FUNCTION", 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, "FUNCTION", "PUBLIC", data.name, data.pronamespace, data.func_args_without)}}
|
||||
{% endif %}
|
||||
{% if data.description %}
|
||||
|
||||
COMMENT ON FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
|
||||
IS {{ data.description|qtLiteral }};
|
||||
{% endif -%}
|
||||
{% if data.seclabels %}
|
||||
{% for r in data.seclabels %}
|
||||
{% if r.label and r.provider %}
|
||||
|
||||
{{ SECLABEL.SET(conn, 'FUNCTION', data.name, r.provider, r.label, data.pronamespace, data.func_args_without) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif -%}
|
||||
|
||||
{% endif %}
|
|
@ -0,0 +1,33 @@
|
|||
SELECT
|
||||
pr.oid, pr.xmin, pr.*, pr.prosrc AS prosrc_c,
|
||||
pr.proname AS name, pg_get_function_result(pr.oid) AS prorettypename,
|
||||
typns.nspname AS typnsp, lanname, proargnames, oidvectortypes(proargtypes) AS proargtypenames,
|
||||
pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals,
|
||||
pronargdefaults, proconfig, pg_get_userbyid(proowner) AS funcowner, description,
|
||||
CASE WHEN prosupport = 0::oid THEN '' ELSE prosupport::text END AS prosupportfunc,
|
||||
(SELECT
|
||||
array_agg(provider || '=' || label)
|
||||
FROM
|
||||
pg_seclabel sl1
|
||||
WHERE
|
||||
sl1.objoid=pr.oid) AS seclabels
|
||||
FROM
|
||||
pg_proc pr
|
||||
JOIN
|
||||
pg_type typ ON typ.oid=prorettype
|
||||
JOIN
|
||||
pg_namespace typns ON typns.oid=typ.typnamespace
|
||||
JOIN
|
||||
pg_language lng ON lng.oid=prolang
|
||||
LEFT OUTER JOIN
|
||||
pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass)
|
||||
WHERE
|
||||
pr.prokind IN ('f', 'w')
|
||||
AND typname NOT IN ('trigger', 'event_trigger')
|
||||
{% if fnid %}
|
||||
AND pr.oid = {{fnid}}::oid
|
||||
{% else %}
|
||||
AND pronamespace = {{scid}}::oid
|
||||
{% endif %}
|
||||
ORDER BY
|
||||
proname;
|
|
@ -0,0 +1,118 @@
|
|||
{% 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 FUNCTION {{ conn|qtIdent(o_data.pronamespace, o_data.name) }}({{
|
||||
o_data.proargtypenames }})
|
||||
RENAME TO {{ conn|qtIdent(data.name) }};
|
||||
{% set name = data.name %}
|
||||
{% endif %}
|
||||
{% endif -%}
|
||||
{% if data.change_func %}
|
||||
|
||||
CREATE OR REPLACE FUNCTION {{ 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 %}{{ p.argtype }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %}
|
||||
{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
{% endif -%}
|
||||
)
|
||||
RETURNS {{ o_data.prorettypename }}
|
||||
{% if 'lanname' in data %}
|
||||
LANGUAGE {{ data.lanname|qtLiteral }} {% else %}
|
||||
LANGUAGE {{ o_data.lanname|qtLiteral }}
|
||||
{% endif %}{% if 'provolatile' in data and data.provolatile %}{{ data.provolatile }} {% elif 'provolatile' not in data and o_data.provolatile %}{{ o_data.provolatile }}{% endif %}
|
||||
{% if ('proleakproof' in data and data.proleakproof) or ('proleakproof' not in data and o_data.proleakproof) %} LEAKPROOF{% elif 'proleakproof' in data and not data.proleakproof %} NOT LEAKPROOF{% endif %}
|
||||
{% if ('proisstrict' in data and data.proisstrict) or ('proisstrict' not in data and o_data.proisstrict) %} STRICT{% endif %}
|
||||
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
|
||||
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% 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.prosupportfunc %}SUPPORT {{ data.prosupportfunc }}{% elif data.prosupportfunc is not defined and o_data.prosupportfunc %}SUPPORT {{ o_data.prosupportfunc }}{% endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %}
|
||||
|
||||
SET {{ conn|qtIdent(v.name) }}={{ v.value|qtLiteral }}{% endfor -%}
|
||||
{% 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 FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
|
||||
OWNER TO {{ conn|qtIdent(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, 'FUNCTION', 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, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
|
||||
|
||||
{{ PRIVILEGE.SET(conn, 'FUNCTION', 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, 'FUNCTION', 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, 'FUNCTION', 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) }}
|
||||
{% endif -%}
|
||||
{% endif -%}
|
||||
{% endif -%}
|
||||
{% set seclabels = data.seclabels %}
|
||||
{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
|
||||
{% for r in seclabels.deleted %}
|
||||
|
||||
{{ SECLABEL.UNSET(conn, 'FUNCTION', 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, 'FUNCTION', 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, 'FUNCTION', 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 FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
|
||||
IS {{ data.description|qtLiteral }};
|
||||
{% endif -%}
|
||||
|
||||
{% if data.pronamespace %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
|
||||
SET SCHEMA {{ conn|qtIdent(data.pronamespace) }};
|
||||
{% endif -%}
|
||||
|
||||
{% endif %}
|
|
@ -67,6 +67,18 @@ class FunctionAddTestCase(BaseTestGenerator):
|
|||
data['pronamespace'] = self.schema_id
|
||||
else:
|
||||
self.schema_id = data['pronamespace']
|
||||
|
||||
if self.server_version >= 120000:
|
||||
support_function_name = 'supportfunc_%s' % str(uuid.uuid4())[1:8]
|
||||
support_func = funcs_utils.create_support_internal_function(
|
||||
self.server,
|
||||
self.db_name,
|
||||
self.schema_name,
|
||||
support_function_name
|
||||
)
|
||||
|
||||
data['prosupportfuc'] = support_function_name
|
||||
|
||||
response = self.tester.post(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(self.server_id) + '/' + str(self.db_id) +
|
||||
|
|
|
@ -42,6 +42,17 @@ class FunctionPutTestCase(BaseTestGenerator):
|
|||
"id": func_id
|
||||
}
|
||||
|
||||
if self.server_version >= 120000:
|
||||
support_function_name = 'supportfunc_%s' % str(uuid.uuid4())[1:8]
|
||||
support_func = funcs_utils.create_support_internal_function(
|
||||
self.server,
|
||||
self.db_name,
|
||||
self.schema_name,
|
||||
support_function_name
|
||||
)
|
||||
|
||||
data['prosupportfuc'] = support_function_name
|
||||
|
||||
put_response = self.tester.put(
|
||||
self.url + str(utils.SERVER_GROUP) +
|
||||
'/' + str(self.server_id) + '/' + str(self.db_id) + '/' +
|
||||
|
|
|
@ -171,6 +171,34 @@ def create_function(server, db_name, schema_name, func_name):
|
|||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
|
||||
def create_support_internal_function(server, db_name, schema_name, func_name):
|
||||
"""Add the function to schema which will be used as support function for
|
||||
another function."""
|
||||
try:
|
||||
connection = utils.get_db_connection(db_name,
|
||||
server['username'],
|
||||
server['db_password'],
|
||||
server['host'],
|
||||
server['port'],
|
||||
server['sslmode'])
|
||||
pg_cursor = connection.cursor()
|
||||
query = "CREATE FUNCTION " + schema_name + "." + func_name + \
|
||||
"(internal)" \
|
||||
" RETURNS internal LANGUAGE 'internal'" \
|
||||
" AS $BODY$cidr_abbrev$BODY$;"
|
||||
pg_cursor.execute(query)
|
||||
connection.commit()
|
||||
# Get 'oid' from newly created function
|
||||
pg_cursor.execute("SELECT pro.oid, pro.proname FROM"
|
||||
" pg_proc pro WHERE pro.proname='%s'" %
|
||||
func_name)
|
||||
functions = pg_cursor.fetchone()
|
||||
connection.close()
|
||||
return functions
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
|
||||
def verify_procedure(server, db_name, proc_name):
|
||||
"""This function verifies the procedure in db"""
|
||||
connection = utils.get_db_connection(db_name,
|
||||
|
|
|
@ -873,7 +873,7 @@ class TriggerView(PGChildNodeView):
|
|||
|
||||
status, result = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
return internal_server_error(errormsg=result)
|
||||
|
||||
# Update the trigger function which we have fetched with schema
|
||||
# name
|
||||
|
|
Loading…
Reference in New Issue