Skip to content

Commit 8ee71ff

Browse files
Add support of DEPENDS/NO DEPENDS ON EXTENSION for ALTER FUNCTION.#6385
1 parent 8576a5e commit 8ee71ff

File tree

23 files changed

+702
-3
lines changed

23 files changed

+702
-3
lines changed

docs/en_US/function_dialog.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ Use the fields in the *Definition* tab to define the function:
4141
will change to an input text field.
4242
* Use the drop-down listbox next to *Language* to select the implementation
4343
language. The default is *sql*.
44+
* Use the drop-down listbox next to *Depends on extension* to select the extension that this function
45+
depends on (for example, plpgsql). If set, dropping the extension will automatically drop the
46+
function as well.
4447
* Use the fields in the *Arguments* to define an argument. Click the *Add*
4548
icon (+) to set parameters and values for the argument:
4649

171 Bytes
Loading

web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ def _create_wrap_data(req, key, data):
260260
list_params = []
261261
if request.method == 'GET':
262262
list_params = ['arguments', 'variables', 'proacl',
263-
'seclabels', 'acl', 'args']
263+
'seclabels', 'acl', 'args', 'dependsonextensions']
264264

265265
if key in list_params and req[key] != '' and req[key] is not None:
266266
# Coverts string into python list as expected.
@@ -1171,6 +1171,10 @@ def _get_sql_for_edit_mode(self, data, parallel_dict, all_ids_dict,
11711171
old_data['proparallel'] = \
11721172
parallel_dict[old_data['proparallel']]
11731173

1174+
if self.node_type == 'function' and \
1175+
old_data['dependsonextensions'] is None:
1176+
old_data['dependsonextensions'] = []
1177+
11741178
# If any of the below argument is changed,
11751179
# then CREATE OR REPLACE SQL statement should be called
11761180
fun_change_args = ['lanname', 'prosrc', 'probin', 'prosrc_c',

web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ define('pgadmin.node.function', [
7676

7777
},
7878
getSchema: function(treeNodeInfo, itemNodeData) {
79+
let nodeObj = pgBrowser.Nodes['extension'];
7980
return new FunctionSchema(
8081
(privileges)=>getNodePrivilegeRoleSchema(this, treeNodeInfo, itemNodeData, privileges),
8182
()=>getNodeVariableSchema(this, treeNodeInfo, itemNodeData, false, false),
@@ -85,6 +86,16 @@ define('pgadmin.node.function', [
8586
cacheLevel: 'database'
8687
}
8788
),
89+
extensionsList:()=>getNodeAjaxOptions('nodes', nodeObj, treeNodeInfo, itemNodeData, { cacheLevel: 'server'},
90+
(data)=>{
91+
let res = [];
92+
if (data && _.isArray(data)) {
93+
_.each(data, function(d) {
94+
res.push({label: d.label, value: d.label, data: d});
95+
});
96+
}
97+
return res;
98+
}),
8899
getTypes: ()=>getNodeAjaxOptions('get_types', this, treeNodeInfo, itemNodeData),
89100
getLanguage: ()=>getNodeAjaxOptions('get_languages', this, treeNodeInfo, itemNodeData),
90101
getSupportFunctions: ()=>getNodeAjaxOptions('get_support_functions', this, treeNodeInfo, itemNodeData, {

web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.ui.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ export default class FunctionSchema extends BaseUISchema {
137137
role: [],
138138
schema: [],
139139
getTypes: [],
140+
extensionsList: [],
140141
...fieldOptions,
141142
};
142143
}
@@ -274,8 +275,20 @@ export default class FunctionSchema extends BaseUISchema {
274275

275276
return this.node_info && 'catalog' in this.node_info;
276277
}
277-
},
278-
{
278+
},{
279+
id: 'dependsonextensions',
280+
label: gettext('Depends on extensions'),
281+
group: gettext('Definition'),
282+
type: 'select',
283+
options: this.fieldOptions.extensionsList,
284+
controlProps: {
285+
multiple: true,
286+
allowClear: true,
287+
allowSelectAll: true,
288+
placeholder: gettext('Select the Depends on extensions...'),
289+
},
290+
mode: ['create', 'edit', 'properties'],
291+
},{
279292
id: 'probin', label: gettext('Object file'), cell: 'string',
280293
type: 'text', group: gettext('Definition'), deps: ['lanname'], visible:
281294
function(state) {
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
{% import 'macros/functions/security.macros' as SECLABEL %}
2+
{% import 'macros/functions/privilege.macros' as PRIVILEGE %}
3+
{% import 'macros/functions/variable.macros' as VARIABLE %}
4+
{% set is_columns = [] %}
5+
{% set exclude_quoting = ['search_path'] %}
6+
{% if data %}
7+
{% if query_for == 'sql_panel' and func_def is defined %}
8+
CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{func_def}}
9+
{% else %}
10+
CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({% if data.arguments %}
11+
{% 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 %}
12+
{% if not loop.last %}, {% endif %}
13+
{% endfor %}
14+
{% endif -%}
15+
)
16+
{% endif %}
17+
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 %}
18+
19+
LANGUAGE {{ data.lanname|qtLiteral(conn) }}
20+
{% if data.procost %}
21+
COST {{data.procost}}
22+
{% endif %}
23+
{% if data.provolatile %}{% if data.provolatile == 'i' %}IMMUTABLE{% elif data.provolatile == 's' %}STABLE{% else %}VOLATILE{% endif %} {% endif %}{% if data.proleakproof %}LEAKPROOF {% endif %}
24+
{% if data.proisstrict %}STRICT {% endif %}
25+
{% if data.prosecdef %}SECURITY DEFINER {% endif %}
26+
{% if data.proiswindow %}WINDOW {% endif %}
27+
{% if data.proparallel and (data.proparallel == 'r' or data.proparallel == 's' or data.proparallel == 'u') %}
28+
{% if data.proparallel == 'r' %}PARALLEL RESTRICTED {% elif data.proparallel == 's' %}PARALLEL SAFE {% elif data.proparallel == 'u' %}PARALLEL UNSAFE{% endif %}{% endif %}
29+
{% if data.prorows and (data.prorows | int) > 0 %}
30+
31+
ROWS {{data.prorows}}
32+
{% endif %}
33+
{% if data.prosupportfunc %}
34+
SUPPORT {{ data.prosupportfunc }}
35+
{% endif -%}
36+
{% if data.variables %}{% for v in data.variables %}
37+
38+
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral(conn) }}{% endif %}{% endfor %}
39+
{% endif %}
40+
41+
{% if data.is_pure_sql %}{{ data.prosrc }}
42+
{% else %}
43+
AS {% if data.lanname == 'c' %}
44+
{{ data.probin|qtLiteral(conn) }}, {{ data.prosrc_c|qtLiteral(conn) }}
45+
{% else %}
46+
$BODY${{ data.prosrc }}$BODY${% endif -%};
47+
{% endif -%}
48+
{% if data.funcowner %}
49+
50+
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
51+
OWNER TO {{ conn|qtIdent(data.funcowner) }};
52+
{% endif -%}
53+
54+
{% if data.dependsonextensions %}
55+
{% for ext in data.dependsonextensions %}
56+
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
57+
DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
58+
{% endfor %}
59+
{% endif %}
60+
61+
{% if data.acl %}
62+
{% for p in data.acl %}
63+
64+
{{ PRIVILEGE.SET(conn, "FUNCTION", p.grantee, data.name, p.without_grant, p.with_grant, data.pronamespace, data.func_args_without)}}
65+
{% endfor %}{% endif %}
66+
{% if data.revoke_all %}
67+
68+
{{ PRIVILEGE.UNSETALL(conn, "FUNCTION", "PUBLIC", data.name, data.pronamespace, data.func_args_without)}}
69+
{% endif %}
70+
{% if data.description %}
71+
72+
COMMENT ON FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
73+
IS {{ data.description|qtLiteral(conn) }};
74+
{% endif -%}
75+
{% if data.seclabels %}
76+
{% for r in data.seclabels %}
77+
{% if r.label and r.provider %}
78+
79+
{{ SECLABEL.SET(conn, 'FUNCTION', data.name, r.provider, r.label, data.pronamespace, data.func_args_without) }}
80+
{% endif %}
81+
{% endfor %}
82+
{% endif -%}
83+
84+
{% endif %}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
SELECT
2+
pr.oid, pr.xmin,
3+
CASE WHEN pr.prokind = 'w' THEN true ELSE false END AS proiswindow,
4+
pr.prosrc, pr.prosrc AS prosrc_c, pr.pronamespace, pr.prolang, pr.procost, pr.prorows, pr.prokind,
5+
pr.prosecdef, pr.proleakproof, pr.proisstrict, pr.proretset, pr.provolatile, pr.proparallel,
6+
pr.pronargs, pr.prorettype, pr.proallargtypes, pr.proargmodes, pr.probin, pr.proacl,
7+
pr.proname, pr.proname AS name, pg_catalog.pg_get_function_result(pr.oid) AS prorettypename,
8+
typns.nspname AS typnsp, lanname, proargnames, pg_catalog.oidvectortypes(proargtypes) AS proargtypenames,
9+
pg_catalog.pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals,
10+
pr.pronargdefaults, proconfig, pg_catalog.pg_get_userbyid(proowner) AS funcowner, description,
11+
(
12+
SELECT array_agg(DISTINCT e.extname)
13+
FROM pg_depend d
14+
JOIN pg_extension e ON d.refobjid = e.oid
15+
WHERE d.objid = pr.oid
16+
) AS dependsonextensions,
17+
CASE WHEN prosupport = 0::oid THEN ''
18+
ELSE (
19+
SELECT pg_catalog.quote_ident(nspname) || '.' || pg_catalog.quote_ident(proname) AS tfunctions
20+
FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n
21+
WHERE p.pronamespace = n.oid
22+
AND p.oid = pr.prosupport::OID
23+
) END AS prosupportfunc,
24+
(SELECT
25+
pg_catalog.array_agg(provider || '=' || label)
26+
FROM
27+
pg_catalog.pg_seclabel sl1
28+
WHERE
29+
sl1.objoid=pr.oid) AS seclabels
30+
FROM
31+
pg_catalog.pg_proc pr
32+
JOIN
33+
pg_catalog.pg_type typ ON typ.oid=prorettype
34+
JOIN
35+
pg_catalog.pg_namespace typns ON typns.oid=typ.typnamespace
36+
JOIN
37+
pg_catalog.pg_language lng ON lng.oid=prolang
38+
LEFT OUTER JOIN
39+
pg_catalog.pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass and des.objsubid = 0)
40+
WHERE
41+
pr.prokind IN ('f', 'w')
42+
AND typname NOT IN ('trigger', 'event_trigger')
43+
{% if fnid %}
44+
AND pr.oid = {{fnid}}::oid
45+
{% else %}
46+
AND pronamespace = {{scid}}::oid
47+
{% endif %}
48+
ORDER BY
49+
proname;
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
{% import 'macros/functions/security.macros' as SECLABEL %}
2+
{% import 'macros/functions/privilege.macros' as PRIVILEGE %}
3+
{% import 'macros/functions/variable.macros' as VARIABLE %}{% if data %}
4+
{% set name = o_data.name %}
5+
{% set exclude_quoting = ['search_path'] %}
6+
{% set set_variables = [] %}
7+
{% if 'merged_variables' in data and data.merged_variables|length > 0 %}
8+
{% set set_variables = data.merged_variables %}
9+
{% elif 'variables' in o_data and o_data.variables|length > 0 %}
10+
{% set set_variables = o_data.variables %}
11+
{% endif %}
12+
{% if data.name %}
13+
{% if data.name != o_data.name %}
14+
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, o_data.name) }}({{
15+
o_data.proargtypenames }})
16+
RENAME TO {{ conn|qtIdent(data.name) }};
17+
{% set name = data.name %}
18+
{% endif %}
19+
{% endif -%}
20+
{% if data.change_func %}
21+
22+
CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if data.arguments %}
23+
{% 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 %}
24+
{% if not loop.last %},{% endif %}
25+
{% endfor %}
26+
{% endif -%}
27+
)
28+
RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
29+
30+
{% if 'lanname' in data %}
31+
LANGUAGE {{ data.lanname|qtLiteral(conn) }} {% else %}
32+
LANGUAGE {{ o_data.lanname|qtLiteral(conn) }}
33+
{% endif %}{% if 'provolatile' in data and data.provolatile %}{{ data.provolatile }} {% elif 'provolatile' not in data and o_data.provolatile %}{{ o_data.provolatile }}{% endif %}
34+
{% 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 %}
35+
{% if ('proisstrict' in data and data.proisstrict) or ('proisstrict' not in data and o_data.proisstrict) %} STRICT{% endif %}
36+
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
37+
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
38+
39+
{% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %}
40+
41+
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
42+
43+
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif %}
44+
45+
{% if data.prosupportfunc %}SUPPORT {{ data.prosupportfunc }}{% elif data.prosupportfunc is not defined and o_data.prosupportfunc %}SUPPORT {{ o_data.prosupportfunc }}{% endif -%}{% if set_variables and set_variables|length > 0 %}{% for v in set_variables %}
46+
47+
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral(conn) }}{% endif %}{% endfor -%}
48+
{% endif %}
49+
50+
{% if data.is_pure_sql %}{{ data.prosrc }}
51+
{% else %}
52+
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
53+
{% if 'probin' in data %}{{ data.probin|qtLiteral(conn) }}{% else %}{{ o_data.probin|qtLiteral(conn) }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral(conn) }}{% else %}{{ o_data.prosrc_c|qtLiteral(conn) }}{% endif %}{% elif 'prosrc' in data %}
54+
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
55+
{{ o_data.probin|qtLiteral(conn) }}, {{ o_data.prosrc_c|qtLiteral(conn) }}{% else %}
56+
$BODY${{ o_data.prosrc }}$BODY${% endif -%};
57+
{% endif -%}
58+
{% endif -%}
59+
{% if data.funcowner %}
60+
61+
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
62+
OWNER TO {{ conn|qtIdent(data.funcowner) }};
63+
{% endif -%}
64+
{# The SQL generated below will change priviledges #}
65+
{% if data.acl %}
66+
{% if 'deleted' in data.acl %}
67+
{% for priv in data.acl.deleted %}
68+
69+
{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
70+
{% endfor %}
71+
{% endif -%}
72+
{% if 'changed' in data.acl %}
73+
{% for priv in data.acl.changed %}
74+
75+
{% if priv.grantee != priv.old_grantee %}
76+
{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.old_grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
77+
{% else %}
78+
{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
79+
{% endif %}
80+
81+
{{ PRIVILEGE.SET(conn, 'FUNCTION', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }}
82+
{% endfor %}
83+
{% endif -%}
84+
{% if 'added' in data.acl %}
85+
{% for priv in data.acl.added %}
86+
87+
{{ PRIVILEGE.SET(conn, 'FUNCTION', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }}
88+
{% endfor %}{% endif -%}
89+
{% endif -%}
90+
{% if data.change_func == False %}
91+
{% if data.variables %}
92+
{% if 'deleted' in data.variables and data.variables.deleted|length > 0 %}
93+
94+
{{ VARIABLE.UNSET(conn, 'FUNCTION', name, data.variables.deleted, o_data.pronamespace, o_data.proargtypenames) }}
95+
{% endif -%}
96+
{% if 'merged_variables' in data and data.merged_variables|length > 0 %}
97+
98+
{{ VARIABLE.SET(conn, 'FUNCTION', name, data.merged_variables, o_data.pronamespace, o_data.proargtypenames) }}
99+
{% endif -%}
100+
{% endif -%}
101+
{% endif -%}
102+
{% set seclabels = data.seclabels %}
103+
{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
104+
{% for r in seclabels.deleted %}
105+
106+
{{ SECLABEL.UNSET(conn, 'FUNCTION', name, r.provider, o_data.pronamespace, o_data.proargtypenames) }}
107+
{% endfor %}
108+
{% endif -%}
109+
{% if 'added' in seclabels and seclabels.added|length > 0 %}
110+
{% for r in seclabels.added %}
111+
112+
{{ SECLABEL.SET(conn, 'FUNCTION', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }}
113+
{% endfor %}
114+
{% endif -%}
115+
{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
116+
{% for r in seclabels.changed %}
117+
118+
{{ SECLABEL.SET(conn, 'FUNCTION', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }}
119+
{% endfor %}
120+
{% endif -%}
121+
{% if data.description is defined and data.description != o_data.description%}
122+
123+
COMMENT ON FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
124+
IS {{ data.description|qtLiteral(conn) }};
125+
{% endif -%}
126+
127+
{% if data.pronamespace %}
128+
129+
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
130+
SET SCHEMA {{ conn|qtIdent(data.pronamespace) }};
131+
{% endif -%}
132+
133+
{% set old_exts = (o_data.dependsonextensions or []) | list %}
134+
{% set new_exts = data.dependsonextensions if 'dependsonextensions' in data else None %}
135+
136+
{% if new_exts is not none and old_exts != new_exts %}
137+
{% for ext in (old_exts + new_exts) | unique %}
138+
{% if ext in new_exts and ext not in old_exts %}
139+
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }})
140+
DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
141+
{% elif ext in old_exts and ext not in new_exts %}
142+
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }})
143+
NO DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
144+
{% endif %}
145+
{% endfor %}
146+
{% endif %}
147+
148+
{% endif %}

web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/pg/sql/14_plus/create.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ $BODY${{ data.prosrc }}$BODY${% endif -%};
5050
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
5151
OWNER TO {{ conn|qtIdent(data.funcowner) }};
5252
{% endif -%}
53+
54+
{% if data.dependsonextensions %}
55+
{% for ext in data.dependsonextensions %}
56+
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
57+
DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
58+
{% endfor %}
59+
{% endif %}
60+
5361
{% if data.acl %}
5462
{% for p in data.acl %}
5563

web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/pg/sql/14_plus/properties.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ SELECT
1010
pg_catalog.pg_get_function_sqlbody(pr.oid) AS prosrc_sql,
1111
CASE WHEN pr.prosqlbody IS NOT NULL THEN true ELSE false END as is_pure_sql,
1212
pr.pronargdefaults, proconfig, pg_catalog.pg_get_userbyid(proowner) AS funcowner, description,
13+
(
14+
SELECT array_agg(DISTINCT e.extname)
15+
FROM pg_depend d
16+
JOIN pg_extension e ON d.refobjid = e.oid
17+
WHERE d.objid = pr.oid
18+
) AS dependsonextensions,
1319
CASE WHEN prosupport = 0::oid THEN ''
1420
ELSE (
1521
SELECT pg_catalog.quote_ident(nspname) || '.' || pg_catalog.quote_ident(proname) AS tfunctions

0 commit comments

Comments
 (0)