From 38ebb43bac5374e71c5299e9e04cd4033200eb2c Mon Sep 17 00:00:00 2001 From: Rohit Bhati Date: Fri, 31 Oct 2025 18:17:04 +0530 Subject: [PATCH] Add support of DEPENDS/NO DEPENDS ON EXTENSION for PROCEDURE. #6391 --- docs/en_US/images/procedure_definition.png | Bin 40030 -> 68740 bytes docs/en_US/procedure_dialog.rst | 3 + .../databases/schemas/functions/__init__.py | 5 +- .../functions/static/js/function.ui.js | 3 +- .../schemas/functions/static/js/procedure.js | 11 + .../procedures/pg/sql/13_plus/create.sql | 66 ++++++ .../procedures/pg/sql/13_plus/properties.sql | 53 +++++ .../procedures/pg/sql/13_plus/update.sql | 128 ++++++++++ .../procedures/pg/sql/14_plus/create.sql | 8 + .../procedures/pg/sql/14_plus/properties.sql | 6 + .../procedures/pg/sql/14_plus/update.sql | 16 ++ .../procedures/ppas/sql/13_plus/create.sql | 66 ++++++ .../ppas/sql/13_plus/properties.sql | 53 +++++ .../procedures/ppas/sql/13_plus/update.sql | 128 ++++++++++ .../procedures/ppas/sql/14_plus/create.sql | 8 + .../ppas/sql/14_plus/properties.sql | 6 + .../procedures/ppas/sql/14_plus/update.sql | 16 ++ .../alter_func_remove_partial_pri.msql | 1 + .../12_plus/alter_func_remove_partial_pri.sql | 23 ++ .../tests/pg/12_plus/alter_proc_add_priv.msql | 3 + .../tests/pg/12_plus/alter_proc_add_priv.sql | 23 ++ .../alter_proc_change_grantee_priv.msql | 12 +- .../pg/12_plus/alter_proc_delete_priv.msql | 1 + .../pg/12_plus/alter_proc_delete_priv.sql | 23 ++ .../tests/pg/12_plus/create_procedure.msql | 10 + .../tests/pg/12_plus/test_function.json | 4 +- .../tests/pg/12_plus/test_procedures.json | 23 +- .../pg/13_plus/alter_proc_no_depends.msql | 2 + .../pg/13_plus/alter_proc_no_depends.sql | 18 ++ .../13_plus/create_procedure_on_depends.msql | 16 ++ .../13_plus/create_procedure_on_depends.sql | 21 ++ .../tests/pg/13_plus/test_function.json | 16 +- .../tests/pg/13_plus/test_procedures.json | 224 ++++++++++++++++++ .../tests/pg/14_plus/alter_proc_add_priv.msql | 3 + .../tests/pg/14_plus/alter_proc_add_priv.sql | 22 ++ .../pg/14_plus/alter_proc_delete_priv.msql | 1 + .../pg/14_plus/alter_proc_delete_priv.sql | 22 ++ .../pg/14_plus/alter_proc_no_depends.msql | 2 + .../pg/14_plus/alter_proc_no_depends.sql | 17 ++ .../tests/pg/14_plus/create_atomic_func.sql | 2 +- .../tests/pg/14_plus/create_atomic_proc.msql | 7 +- .../tests/pg/14_plus/create_procedure.msql | 9 + .../14_plus/create_procedure_on_depends.msql | 15 ++ .../14_plus/create_procedure_on_depends.sql | 20 ++ .../tests/pg/14_plus/test_function.json | 16 +- .../tests/pg/14_plus/test_procedures.json | 87 ++++++- .../alter_func_remove_partial_pri.msql | 1 + .../12_plus/alter_func_remove_partial_pri.sql | 23 ++ .../12_plus/alter_function_add_parameter.msql | 5 +- .../ppas/12_plus/alter_proc_add_priv.msql | 3 + .../ppas/12_plus/alter_proc_add_priv.sql | 23 ++ .../alter_proc_change_grantee_priv.msql | 12 +- .../ppas/12_plus/alter_proc_delete_priv.msql | 1 + .../ppas/12_plus/alter_proc_delete_priv.sql | 23 ++ .../tests/ppas/12_plus/create_procedure.msql | 10 + .../tests/ppas/12_plus/test_function.json | 4 +- .../ppas/13_plus/alter_proc_no_depends.msql | 2 + .../ppas/13_plus/alter_proc_no_depends.sql | 18 ++ .../13_plus/create_procedure_on_depends.msql | 16 ++ .../13_plus/create_procedure_on_depends.sql | 21 ++ .../tests/ppas/13_plus/test_function.json | 19 +- .../tests/ppas/13_plus/test_procedures.json | 155 ++++++++++++ .../ppas/14_plus/alter_proc_add_priv.msql | 3 + .../ppas/14_plus/alter_proc_add_priv.sql | 22 ++ .../ppas/14_plus/alter_proc_delete_priv.msql | 1 + .../ppas/14_plus/alter_proc_delete_priv.sql | 22 ++ .../ppas/14_plus/alter_proc_no_depends.msql | 2 + .../ppas/14_plus/alter_proc_no_depends.sql | 17 ++ .../ppas/14_plus/create_atomic_proc.msql | 9 +- .../tests/ppas/14_plus/create_procedure.msql | 9 + .../14_plus/create_procedure_on_depends.msql | 15 ++ .../14_plus/create_procedure_on_depends.sql | 20 ++ .../tests/ppas/14_plus/test_function.json | 19 +- .../tests/ppas/14_plus/test_procedures.json | 93 +++++++- .../functions/tests/test_procedure_add.py | 1 + .../functions/tests/test_procedure_put.py | 3 +- 76 files changed, 1732 insertions(+), 59 deletions(-) create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/13_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/13_plus/properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/13_plus/update.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/13_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/13_plus/properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/13_plus/update.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_func_remove_partial_pri.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_func_remove_partial_pri.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_add_priv.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_add_priv.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_delete_priv.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_delete_priv.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/create_procedure.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/alter_proc_no_depends.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/alter_proc_no_depends.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/create_procedure_on_depends.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/create_procedure_on_depends.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/test_procedures.json create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_add_priv.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_add_priv.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_delete_priv.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_delete_priv.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_no_depends.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_no_depends.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_procedure.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_procedure_on_depends.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_procedure_on_depends.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_func_remove_partial_pri.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_func_remove_partial_pri.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_add_priv.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_add_priv.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_delete_priv.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_delete_priv.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/create_procedure.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/alter_proc_no_depends.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/alter_proc_no_depends.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/create_procedure_on_depends.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/create_procedure_on_depends.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/test_procedures.json create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_add_priv.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_add_priv.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_delete_priv.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_delete_priv.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_no_depends.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_no_depends.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_procedure.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_procedure_on_depends.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_procedure_on_depends.sql diff --git a/docs/en_US/images/procedure_definition.png b/docs/en_US/images/procedure_definition.png index 8d40d6b534e5de97e25ba268b2e4d5633b6ae842..581f34bea85d76460d10e19ea4e58c07a65c8eb8 100644 GIT binary patch literal 68740 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 z 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 SF!Mmb-v@E|_vNC7pZ`Depu~d! literal 40030 zcmX6^V_>9BvyIJ-ZQHgs=Ek;dZ=7swY$n{;wr$(C%{%XR`_IgD&&*R@-CbR$>QtnX zf)pYgE*uC52%_{)aTO2{uqY4^&@UJ;U{A7qO9BYUUkYh)5j9WHi}c;ta$=NFukSQl zj8KsZA)9NxLK||My2gok&dRJKUn8ys1w(8FfvB^$-bbp+==MY(WI&vf~dk$Y5bg0eM;yer$Gy}B)WJllPfZV-8=cTUnD6C zRYtA$UJ_<+p@(kBqghddJ(j|N(9#JsxHRZAkGzLtrOb3sJgxrp)0G-VGG~^6y+_>& z{V~~~ZFPM%-$Y$6E`ryq+8EywCF9u^3S0LiEt=|hXnW&^^lRgR%JVnXW#q9}5iZjT zp|~_FD&UMk90|X1&fWI^5+#!uaIwdxwK#5q|JOg@NiZzC8BbE}dLI2q5^@N?a*y<1 z|7egHOjw6~whUwTg_qEOJ@FT<)p#l?O{bTwH=XZSINV4>pdcW=%uYs`MX%rEm#c3w zI&Ky6b;p{?8yr}eY!>^s;oVBB9+bgSm>?i8*jq=$r;hKMcy1-pB&&eszen9S@3%%* z-`{XHLdOw`RcE8an1VRK2kk&Qtlw5KL_PVIOmguZgAlCNh3Wp_}JUSuOIEny+@FG@Q)qdbbtuMN+DJA;f#oiih_fIiHMF)ezk12%}Iyq=<5?5 zdd*4T=HN(4G1Kbr>oYbso|~JyQQzv*B8p=mgPx#L_}k)onwaWb(vk+0%D=iuj-W)) z@9iN@=<8+EW4qCA{b?u$Z^^%HlHzfOmX3~&ifUM;(K#a_!wMcCQqM?E+9t{`rYi_e zYMhGi0xzR5PRm)GC2vKkdTOB@wkjT@C^{FCJ-2Dd0cN{GH9Q;wxu%Y zT&>iSv9Z-(__j2NirD>tg^g@>*p7{hYo06TXTyyNM$v=ex3b3s=y$g&y*k`8ZP*LbX+qdqqHakz59=+1-0|gUxnV zXVroNfFD-yK;7#S4GoRUVas=?2ULkV#SxUUO1qJkjBHcaTKxtg(~V?zwiaVo>*8

eiALoq2T2Tt$y~S>70P43oR_Hv|@mFlo2dF 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�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 diff --git a/docs/en_US/procedure_dialog.rst b/docs/en_US/procedure_dialog.rst index 161c53edb..47540b766 100644 --- a/docs/en_US/procedure_dialog.rst +++ b/docs/en_US/procedure_dialog.rst @@ -36,6 +36,9 @@ Use the fields in the *Definition* tab to define the procedure: * Use the drop-down listbox next to *Language* to select a language. The default is *edbspl*. +* Use the drop-down listbox next to *Depends on extensions* to select the extension that this procedure + depends on (for example, edbspl). If set, dropping the extension will automatically drop the + procedure as well. * Use the fields in the *Arguments* section to define an argument. Click *Add* to set parameters and values for the argument: * Use the drop-down listbox next to *Data type* to select a data type. diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py index dc233ba6e..a0f8a10e1 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/__init__.py @@ -1171,7 +1171,10 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): old_data['proparallel'] = \ parallel_dict[old_data['proparallel']] - if self.node_type == 'function': + if self.node_type in ('function', 'procedure') and ( + old_data.get('dependsonextensions') is None or + data.get('dependsonextensions') is None + ): old_data['dependsonextensions'] = \ old_data.get('dependsonextensions') or [] data['dependsonextensions'] = \ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.ui.js index c1e35ba90..1fcf533e4 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.ui.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/function.ui.js @@ -288,8 +288,7 @@ export default class FunctionSchema extends BaseUISchema { placeholder: gettext('Select the Depends on extensions...'), }, min_version: 130000, - mode: ['create', 'edit', 'properties'], - visible: obj.isVisible + mode: ['create', 'edit', 'properties'] },{ id: 'probin', label: gettext('Object file'), cell: 'string', type: 'text', group: gettext('Definition'), deps: ['lanname'], visible: diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/procedure.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/procedure.js index 7eb34183b..7661c86c0 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/procedure.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/js/procedure.js @@ -92,6 +92,7 @@ define('pgadmin.node.procedure', [ ); }, getSchema: function(treeNodeInfo, itemNodeData) { + let nodeObj = pgBrowser.Nodes['extension']; return new FunctionSchema( (privileges)=>getNodePrivilegeRoleSchema(this, treeNodeInfo, itemNodeData, privileges), ()=>getNodeVariableSchema(this, treeNodeInfo, itemNodeData, false, false), @@ -101,6 +102,16 @@ define('pgadmin.node.procedure', [ cacheLevel: 'database' } ), + extensionsList:()=>getNodeAjaxOptions('nodes', nodeObj, treeNodeInfo, itemNodeData, { cacheLevel: 'server'}, + (data)=>{ + let res = []; + if (data && _.isArray(data)) { + _.each(data, function(d) { + res.push({label: d.label, value: d.label, data: d}); + }); + } + return res; + }), getTypes: ()=>getNodeAjaxOptions('get_types', this, treeNodeInfo, itemNodeData), getLanguage: ()=>getNodeAjaxOptions('get_languages', this, treeNodeInfo, itemNodeData), getSupportFunctions: ()=>getNodeAjaxOptions('get_support_functions', this, treeNodeInfo, itemNodeData, { diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/13_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/13_plus/create.sql new file mode 100644 index 000000000..04f20fe2d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/13_plus/create.sql @@ -0,0 +1,66 @@ +{% import 'macros/functions/security.macros' as SECLABEL %} +{% import 'macros/functions/privilege.macros' as PRIVILEGE %} +{% import 'macros/functions/variable.macros' as VARIABLE %} +{% set is_columns = [] %} +{% set exclude_quoting = ['search_path'] %} +{% if data %} +{% if query_for == 'sql_panel' and func_def is defined %} +CREATE OR REPLACE PROCEDURE {{func_def}} +{% else %} +CREATE{% if add_replace_clause %} OR REPLACE{% endif %} 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 %}{{ p.argtype }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %} +{% if not loop.last %}, {% endif %} +{% endfor -%} +{% endif %} +) +{% endif %} +LANGUAGE {{ data.lanname|qtLiteral(conn) }}{% if data.prosecdef %} + + SECURITY DEFINER {% endif %} +{% if data.variables %}{% for v in data.variables %} + + SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral(conn) }}{% endif %}{% endfor -%} +{% endif %} + +AS {% if data.lanname == 'c' %} +{{ data.probin|qtLiteral(conn) }}, {{ data.prosrc_c|qtLiteral(conn) }} +{% else %} +$BODY${{ data.prosrc }}$BODY${% endif -%}; + +{% if data.funcowner %} +ALTER PROCEDURE {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}}) + OWNER TO {{ conn|qtIdent(data.funcowner) }}; +{% endif -%} + +{% if data.dependsonextensions %} +{% for ext in data.dependsonextensions %} + +ALTER PROCEDURE {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}}) + DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }}; +{% endfor %} +{% 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) }}({{data.func_args_without}}) + IS {{ data.description|qtLiteral(conn) }}; +{% 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 %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/13_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/13_plus/properties.sql new file mode 100644 index 000000000..4f1ba4665 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/13_plus/properties.sql @@ -0,0 +1,53 @@ +SELECT + pr.oid, pr.xmin, + CASE WHEN pr.prokind = 'w' THEN true ELSE false END AS proiswindow, + pr.prosrc, pr.prosrc AS prosrc_c, pr.pronamespace, pr.prolang, pr.procost, pr.prorows, pr.prokind, + pr.prosecdef, pr.proleakproof, pr.proisstrict, pr.proretset, pr.provolatile, pr.proparallel, + pr.pronargs, pr.prorettype, pr.proallargtypes, pr.proargmodes, pr.probin, pr.proacl, + pr.proname, pr.proname AS name, pg_catalog.pg_get_function_result(pr.oid) AS prorettypename, + typns.nspname AS typnsp, lanname, proargnames, pg_catalog.oidvectortypes(proargtypes) AS proargtypenames, + pg_catalog.pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals, + pr.pronargdefaults, proconfig, pg_catalog.pg_get_userbyid(proowner) AS funcowner, description, + ( + SELECT array_agg(DISTINCT e.extname) + FROM pg_depend d + JOIN pg_extension e ON d.refobjid = e.oid + WHERE d.objid = pr.oid + ) AS dependsonextensions, + ( + WITH name_with_args_tab AS (SELECT pg_catalog.pg_get_function_identity_arguments(pr.oid) AS val) + SELECT CASE WHEN + val <> '' + THEN + pr.proname || '(' || val || ')' + ELSE + pr.proname::text + END + FROM name_with_args_tab + ) AS name_with_args, + (SELECT + pg_catalog.array_agg(provider || '=' || label) + FROM + pg_catalog.pg_seclabel sl1 + WHERE + sl1.objoid=pr.oid) AS seclabels +FROM + pg_catalog.pg_proc pr +JOIN + pg_catalog.pg_type typ ON typ.oid=prorettype +JOIN + pg_catalog.pg_namespace typns ON typns.oid=typ.typnamespace +JOIN + pg_catalog.pg_language lng ON lng.oid=prolang +LEFT OUTER JOIN + pg_catalog.pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass and des.objsubid = 0) +WHERE + pr.prokind = 'p' + AND typname NOT IN ('trigger', 'event_trigger') +{% if fnid %} + AND pr.oid = {{fnid}}::oid +{% else %} + AND pronamespace = {{scid}}::oid +{% endif %} +ORDER BY + proname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/13_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/13_plus/update.sql new file mode 100644 index 000000000..c2d004b82 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/13_plus/update.sql @@ -0,0 +1,128 @@ +{% 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 %} +{% set exclude_quoting = ['search_path'] %} +{% 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 %}{{ 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(conn) }} {% else %} + LANGUAGE {{ o_data.lanname|qtLiteral(conn) }} + {% endif %} +{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %}SECURITY DEFINER{% endif %} +{% if data.merged_variables %}{% for v in data.merged_variables %} + + SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral(conn) }}{% endif %}{% endfor -%} +{% endif %} + +AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %} +{% 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 %} +$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %} +{{ o_data.probin|qtLiteral(conn) }}, {{ o_data.prosrc_c|qtLiteral(conn) }}{% 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 {{ 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, 'PROCEDURE', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }} +{% endfor %} +{% endif -%} +{% if 'changed' in data.acl %} +{% for priv in data.acl.changed %} + +{% if priv.grantee != priv.old_grantee %} +{{ PRIVILEGE.UNSETALL(conn, 'PROCEDURE', priv.old_grantee, name, o_data.pronamespace, o_data.proargtypenames) }} +{% else %} +{{ PRIVILEGE.UNSETALL(conn, 'PROCEDURE', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }} +{% endif %} + +{{ 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) }}({{o_data.proargtypenames }}) + IS {{ data.description|qtLiteral(conn) }}; +{% endif -%} +{% if data.pronamespace %} + +ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }} + SET SCHEMA {{ conn|qtIdent(data.pronamespace) }}; +{% endif -%} + +{% set old_exts = (o_data.dependsonextensions or []) | list %} +{% set new_exts = data.dependsonextensions if 'dependsonextensions' in data else None %} + +{% if new_exts is not none and old_exts != new_exts %} +{% for ext in (old_exts + new_exts) | unique %} + +{% if ext in new_exts and ext not in old_exts %} +ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }}) + DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }}; +{% elif ext in old_exts and ext not in new_exts %} +ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }}) + NO DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }}; +{% endif %} +{% endfor %} +{% endif %} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/create.sql index 2be6c82b1..e38b9a58f 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/create.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/create.sql @@ -35,6 +35,14 @@ ALTER PROCEDURE {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_arg OWNER TO {{ conn|qtIdent(data.funcowner) }}; {% endif -%} +{% if data.dependsonextensions %} +{% for ext in data.dependsonextensions %} + +ALTER PROCEDURE {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}}) + DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }}; +{% endfor %} +{% endif %} + {% if data.acl and not is_sql %} {% for p in data.acl %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/properties.sql index 4dad586d5..6cbdaf6d9 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/properties.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/properties.sql @@ -10,6 +10,12 @@ SELECT pg_catalog.pg_get_function_sqlbody(pr.oid) AS prosrc_sql, CASE WHEN pr.prosqlbody IS NOT NULL THEN true ELSE false END as is_pure_sql, pr.pronargdefaults, proconfig, pg_catalog.pg_get_userbyid(proowner) AS funcowner, description, + ( + SELECT array_agg(DISTINCT e.extname) + FROM pg_depend d + JOIN pg_extension e ON d.refobjid = e.oid + WHERE d.objid = pr.oid + ) AS dependsonextensions, ( WITH name_with_args_tab AS (SELECT pg_catalog.pg_get_function_identity_arguments(pr.oid) AS val) SELECT CASE WHEN diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/update.sql index d5701a023..95accc3e3 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/update.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/update.sql @@ -111,4 +111,20 @@ ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }} SET SCHEMA {{ conn|qtIdent(data.pronamespace) }}; {% endif -%} +{% set old_exts = (o_data.dependsonextensions or []) | list %} +{% set new_exts = data.dependsonextensions if 'dependsonextensions' in data else None %} + +{% if new_exts is not none and old_exts != new_exts %} +{% for ext in (old_exts + new_exts) | unique %} + +{% if ext in new_exts and ext not in old_exts %} +ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }}) + DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }}; +{% elif ext in old_exts and ext not in new_exts %} +ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }}) + NO DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }}; +{% endif %} +{% endfor %} +{% endif %} + {% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/13_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/13_plus/create.sql new file mode 100644 index 000000000..04f20fe2d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/13_plus/create.sql @@ -0,0 +1,66 @@ +{% import 'macros/functions/security.macros' as SECLABEL %} +{% import 'macros/functions/privilege.macros' as PRIVILEGE %} +{% import 'macros/functions/variable.macros' as VARIABLE %} +{% set is_columns = [] %} +{% set exclude_quoting = ['search_path'] %} +{% if data %} +{% if query_for == 'sql_panel' and func_def is defined %} +CREATE OR REPLACE PROCEDURE {{func_def}} +{% else %} +CREATE{% if add_replace_clause %} OR REPLACE{% endif %} 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 %}{{ p.argtype }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %} +{% if not loop.last %}, {% endif %} +{% endfor -%} +{% endif %} +) +{% endif %} +LANGUAGE {{ data.lanname|qtLiteral(conn) }}{% if data.prosecdef %} + + SECURITY DEFINER {% endif %} +{% if data.variables %}{% for v in data.variables %} + + SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral(conn) }}{% endif %}{% endfor -%} +{% endif %} + +AS {% if data.lanname == 'c' %} +{{ data.probin|qtLiteral(conn) }}, {{ data.prosrc_c|qtLiteral(conn) }} +{% else %} +$BODY${{ data.prosrc }}$BODY${% endif -%}; + +{% if data.funcowner %} +ALTER PROCEDURE {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}}) + OWNER TO {{ conn|qtIdent(data.funcowner) }}; +{% endif -%} + +{% if data.dependsonextensions %} +{% for ext in data.dependsonextensions %} + +ALTER PROCEDURE {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}}) + DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }}; +{% endfor %} +{% 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) }}({{data.func_args_without}}) + IS {{ data.description|qtLiteral(conn) }}; +{% 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 %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/13_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/13_plus/properties.sql new file mode 100644 index 000000000..4f1ba4665 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/13_plus/properties.sql @@ -0,0 +1,53 @@ +SELECT + pr.oid, pr.xmin, + CASE WHEN pr.prokind = 'w' THEN true ELSE false END AS proiswindow, + pr.prosrc, pr.prosrc AS prosrc_c, pr.pronamespace, pr.prolang, pr.procost, pr.prorows, pr.prokind, + pr.prosecdef, pr.proleakproof, pr.proisstrict, pr.proretset, pr.provolatile, pr.proparallel, + pr.pronargs, pr.prorettype, pr.proallargtypes, pr.proargmodes, pr.probin, pr.proacl, + pr.proname, pr.proname AS name, pg_catalog.pg_get_function_result(pr.oid) AS prorettypename, + typns.nspname AS typnsp, lanname, proargnames, pg_catalog.oidvectortypes(proargtypes) AS proargtypenames, + pg_catalog.pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals, + pr.pronargdefaults, proconfig, pg_catalog.pg_get_userbyid(proowner) AS funcowner, description, + ( + SELECT array_agg(DISTINCT e.extname) + FROM pg_depend d + JOIN pg_extension e ON d.refobjid = e.oid + WHERE d.objid = pr.oid + ) AS dependsonextensions, + ( + WITH name_with_args_tab AS (SELECT pg_catalog.pg_get_function_identity_arguments(pr.oid) AS val) + SELECT CASE WHEN + val <> '' + THEN + pr.proname || '(' || val || ')' + ELSE + pr.proname::text + END + FROM name_with_args_tab + ) AS name_with_args, + (SELECT + pg_catalog.array_agg(provider || '=' || label) + FROM + pg_catalog.pg_seclabel sl1 + WHERE + sl1.objoid=pr.oid) AS seclabels +FROM + pg_catalog.pg_proc pr +JOIN + pg_catalog.pg_type typ ON typ.oid=prorettype +JOIN + pg_catalog.pg_namespace typns ON typns.oid=typ.typnamespace +JOIN + pg_catalog.pg_language lng ON lng.oid=prolang +LEFT OUTER JOIN + pg_catalog.pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass and des.objsubid = 0) +WHERE + pr.prokind = 'p' + AND typname NOT IN ('trigger', 'event_trigger') +{% if fnid %} + AND pr.oid = {{fnid}}::oid +{% else %} + AND pronamespace = {{scid}}::oid +{% endif %} +ORDER BY + proname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/13_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/13_plus/update.sql new file mode 100644 index 000000000..c2d004b82 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/13_plus/update.sql @@ -0,0 +1,128 @@ +{% 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 %} +{% set exclude_quoting = ['search_path'] %} +{% 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 %}{{ 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(conn) }} {% else %} + LANGUAGE {{ o_data.lanname|qtLiteral(conn) }} + {% endif %} +{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %}SECURITY DEFINER{% endif %} +{% if data.merged_variables %}{% for v in data.merged_variables %} + + SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral(conn) }}{% endif %}{% endfor -%} +{% endif %} + +AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %} +{% 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 %} +$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %} +{{ o_data.probin|qtLiteral(conn) }}, {{ o_data.prosrc_c|qtLiteral(conn) }}{% 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 {{ 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, 'PROCEDURE', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }} +{% endfor %} +{% endif -%} +{% if 'changed' in data.acl %} +{% for priv in data.acl.changed %} + +{% if priv.grantee != priv.old_grantee %} +{{ PRIVILEGE.UNSETALL(conn, 'PROCEDURE', priv.old_grantee, name, o_data.pronamespace, o_data.proargtypenames) }} +{% else %} +{{ PRIVILEGE.UNSETALL(conn, 'PROCEDURE', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }} +{% endif %} + +{{ 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) }}({{o_data.proargtypenames }}) + IS {{ data.description|qtLiteral(conn) }}; +{% endif -%} +{% if data.pronamespace %} + +ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }} + SET SCHEMA {{ conn|qtIdent(data.pronamespace) }}; +{% endif -%} + +{% set old_exts = (o_data.dependsonextensions or []) | list %} +{% set new_exts = data.dependsonextensions if 'dependsonextensions' in data else None %} + +{% if new_exts is not none and old_exts != new_exts %} +{% for ext in (old_exts + new_exts) | unique %} + +{% if ext in new_exts and ext not in old_exts %} +ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }}) + DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }}; +{% elif ext in old_exts and ext not in new_exts %} +ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }}) + NO DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }}; +{% endif %} +{% endfor %} +{% endif %} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/create.sql index f0f23945b..64b9ff86f 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/create.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/create.sql @@ -43,6 +43,14 @@ ALTER PROCEDURE {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_arg OWNER TO {{ conn|qtIdent(data.funcowner) }}; {% endif -%} +{% if data.dependsonextensions %} +{% for ext in data.dependsonextensions %} + +ALTER PROCEDURE {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}}) + DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }}; +{% endfor %} +{% endif %} + {% if data.acl and not is_sql %} {% for p in data.acl %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/properties.sql index a1a81dcf5..594e4769d 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/properties.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/properties.sql @@ -8,6 +8,12 @@ SELECT typns.nspname AS typnsp, lanname, proargnames, pg_catalog.oidvectortypes(proargtypes) AS proargtypenames, pg_catalog.pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals, pr.pronargdefaults, proconfig, pg_catalog.pg_get_userbyid(proowner) AS funcowner, description, + ( + SELECT array_agg(DISTINCT e.extname) + FROM pg_depend d + JOIN pg_extension e ON d.refobjid = e.oid + WHERE d.objid = pr.oid + ) AS dependsonextensions, pg_catalog.pg_get_function_sqlbody(pr.oid) AS prosrc_sql, CASE WHEN pr.prosqlbody IS NOT NULL THEN true ELSE false END as is_pure_sql, ( diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/update.sql index 8de573c55..307fa18ed 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/update.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/update.sql @@ -121,4 +121,20 @@ ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }} SET SCHEMA {{ conn|qtIdent(data.pronamespace) }}; {% endif -%} +{% set old_exts = (o_data.dependsonextensions or []) | list %} +{% set new_exts = data.dependsonextensions if 'dependsonextensions' in data else None %} + +{% if new_exts is not none and old_exts != new_exts %} +{% for ext in (old_exts + new_exts) | unique %} + +{% if ext in new_exts and ext not in old_exts %} +ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }}) + DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }}; +{% elif ext in old_exts and ext not in new_exts %} +ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }}) + NO DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }}; +{% endif %} +{% endfor %} +{% endif %} + {% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_func_remove_partial_pri.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_func_remove_partial_pri.msql new file mode 100644 index 000000000..4fda43733 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_func_remove_partial_pri.msql @@ -0,0 +1 @@ +REVOKE ALL ON FUNCTION public."Function2_$%{}[]()&*^!@""'`\/#"() FROM PUBLIC; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_func_remove_partial_pri.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_func_remove_partial_pri.sql new file mode 100644 index 000000000..d31d790cd --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_func_remove_partial_pri.sql @@ -0,0 +1,23 @@ +-- FUNCTION: public.Function2_$%{}[]()&*^!@"'`\/#() + +-- DROP FUNCTION IF EXISTS public."Function2_$%{}[]()&*^!@""'`\/#"(); + +CREATE OR REPLACE FUNCTION public."Function2_$%{}[]()&*^!@""'`\/#"( + ) + RETURNS character varying + LANGUAGE 'plpgsql' + COST 100 + VOLATILE PARALLEL UNSAFE + SET enable_sort='true' +AS $BODY$ +begin +select '1'; +end +$BODY$; + +ALTER FUNCTION public."Function2_$%{}[]()&*^!@""'`\/#"() + OWNER TO postgres; + +GRANT EXECUTE ON FUNCTION public."Function2_$%{}[]()&*^!@""'`\/#"() TO postgres; + +REVOKE ALL ON FUNCTION public."Function2_$%{}[]()&*^!@""'`\/#"() FROM PUBLIC; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_add_priv.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_add_priv.msql new file mode 100644 index 000000000..9f6362a8a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_add_priv.msql @@ -0,0 +1,3 @@ +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO PUBLIC; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO postgres; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_add_priv.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_add_priv.sql new file mode 100644 index 000000000..8472d26f0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_add_priv.sql @@ -0,0 +1,23 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + i1 integer) +LANGUAGE 'plpgsql' + SET application_name='pgadmin' +AS $BODY$ +begin +select 1; +end; +$BODY$; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO postgres; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO PUBLIC; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO postgres; + +COMMENT ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + IS 'some comment'; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_change_grantee_priv.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_change_grantee_priv.msql index 67a549ead..c5055f4c5 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_change_grantee_priv.msql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_change_grantee_priv.msql @@ -1,13 +1,3 @@ -CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(IN i1 integer) - LANGUAGE 'plpgsql' - - SET application_name='pgadmin' -AS $BODY$ -begin -select 1; -end; -$BODY$; - REVOKE ALL ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) FROM postgres; -GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO PUBLIC; +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO PUBLIC; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_delete_priv.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_delete_priv.msql new file mode 100644 index 000000000..b1662bf7f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_delete_priv.msql @@ -0,0 +1 @@ +REVOKE ALL ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) FROM PUBLIC; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_delete_priv.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_delete_priv.sql new file mode 100644 index 000000000..0c429dac9 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/alter_proc_delete_priv.sql @@ -0,0 +1,23 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + i1 integer) +LANGUAGE 'plpgsql' + SET application_name='pgadmin' +AS $BODY$ +begin +select 1; +end; +$BODY$; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO postgres; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO postgres; + +REVOKE ALL ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) FROM PUBLIC; + +COMMENT ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + IS 'some comment'; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/create_procedure.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/create_procedure.msql new file mode 100644 index 000000000..390e7b822 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/create_procedure.msql @@ -0,0 +1,10 @@ +CREATE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(IN i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO postgres; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/test_function.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/test_function.json index 217330ad9..61f394474 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/test_function.json +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/test_function.json @@ -234,7 +234,9 @@ } ] } - } + }, + "expected_sql_file": "alter_func_remove_partial_pri.sql", + "expected_msql_file": "alter_func_remove_partial_pri.msql" }, { "type": "alter", diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/test_procedures.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/test_procedures.json index cf0996aab..7a3d54d64 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/test_procedures.json +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/12_plus/test_procedures.json @@ -5,6 +5,7 @@ "name": "Create procedure", "endpoint": "NODE-procedure.obj", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql", "data": { "name": "Proc1_$%{}[]()&*^!@\"'`\\/#", "acl": [], @@ -24,22 +25,27 @@ "proleakproof": false, "probin": "$libdir/" }, - "expected_sql_file": "create_procedure.sql" - }, { + "expected_sql_file": "create_procedure.sql", + "expected_msql_file": "create_procedure.msql" + }, + { "type": "alter", "name": "Alter procedure comment", "endpoint": "NODE-procedure.obj_id", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", "data": { "description": "some comment" }, "expected_sql_file": "alter_proc_comment.sql", "expected_msql_file": "alter_proc_comment_msql.sql" - }, { + }, + { "type": "alter", "name": "Alter procedure param", "endpoint": "NODE-procedure.obj_id", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", "data": { "variables": { "added": [{"name": "application_name", "value": "pgadmin"}] @@ -53,6 +59,7 @@ "name": "Alter procedure add privileges", "endpoint": "NODE-procedure.obj_id", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", "data": { "acl": { "added": [ @@ -80,13 +87,16 @@ } ] } - } + }, + "expected_sql_file": "alter_proc_add_priv.sql", + "expected_msql_file": "alter_proc_add_priv.msql" }, { "type": "alter", "name": "Alter procedure delete privileges", "endpoint": "NODE-procedure.obj_id", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", "data": { "acl": { "deleted": [ @@ -103,13 +113,16 @@ } ] } - } + }, + "expected_sql_file": "alter_proc_delete_priv.sql", + "expected_msql_file": "alter_proc_delete_priv.msql" }, { "type": "alter", "name": "Alter procedure change privileges", "endpoint": "NODE-procedure.obj_id", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", "data": { "acl": { "changed": [ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/alter_proc_no_depends.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/alter_proc_no_depends.msql new file mode 100644 index 000000000..b7e724941 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/alter_proc_no_depends.msql @@ -0,0 +1,2 @@ +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + NO DEPENDS ON EXTENSION postgres_fdw; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/alter_proc_no_depends.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/alter_proc_no_depends.sql new file mode 100644 index 000000000..632073e31 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/alter_proc_no_depends.sql @@ -0,0 +1,18 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO postgres; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION plpgsql; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/create_procedure_on_depends.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/create_procedure_on_depends.msql new file mode 100644 index 000000000..d6bb2b075 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/create_procedure_on_depends.msql @@ -0,0 +1,16 @@ +CREATE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(IN i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO postgres; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION plpgsql; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION postgres_fdw; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/create_procedure_on_depends.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/create_procedure_on_depends.sql new file mode 100644 index 000000000..ddb2b7eb6 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/create_procedure_on_depends.sql @@ -0,0 +1,21 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO postgres; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION plpgsql; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION postgres_fdw; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/test_function.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/test_function.json index 857f7d6ce..944d1fbcf 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/test_function.json +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/test_function.json @@ -9,7 +9,8 @@ "name": "postgres_fdw", "version": "", "relocatable": true - } + }, + "store_object_id": true }, { "type": "create", @@ -76,6 +77,15 @@ "data": { } }, + { + "type": "delete", + "name": "Drop Extension", + "endpoint": "NODE-extension.delete", + "data": { + "ids": [""] + }, + "preprocess_data": true + }, { "type": "create", "name": "Create function with all options.", @@ -310,7 +320,9 @@ } ] } - } + }, + "expected_sql_file": "alter_func_remove_partial_pri.sql", + "expected_msql_file": "alter_func_remove_partial_pri.msql" }, { "type": "alter", diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/test_procedures.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/test_procedures.json new file mode 100644 index 000000000..b95698ac8 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/13_plus/test_procedures.json @@ -0,0 +1,224 @@ +{ + "scenarios": [ + { + "type": "create", + "name": "Create Extension", + "endpoint": "NODE-extension.obj", + "sql_endpoint": "NODE-extension.sql_id", + "data": { + "name": "postgres_fdw", + "version": "", + "relocatable": true + }, + "store_object_id": true + }, + { + "type": "create", + "name": "Create procedure with extensions.", + "endpoint": "NODE-procedure.obj", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql", + "data": { + "name": "Proc1_$%{}[]()&*^!@\"'`\\/#", + "acl": [], + "arguments": [{"argtype": "integer", "argmode": "IN", "argname": "i1", "argdefval": ""}], + "funcowner": "postgres", + "lanname": "plpgsql", + "options": [], + "pronamespace": 2200, + "prosrc": "begin\nselect 1;\nend;", + "seclabels": [], + "variables": [], + "schema": "public", + "provolatile": null, + "proisstrict": false, + "proparallel": null, + "procost": null, + "proleakproof": false, + "probin": "$libdir/", + "dependsonextensions": ["plpgsql", "postgres_fdw"] + }, + "expected_sql_file": "create_procedure_on_depends.sql", + "expected_msql_file": "create_procedure_on_depends.msql" + }, + { + "type": "alter", + "name": "Alter procedure with NO DEPENDS ON", + "endpoint": "NODE-procedure.obj_id", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", + "data": { + "dependsonextensions": ["plpgsql"] + }, + "expected_sql_file": "alter_proc_no_depends.sql", + "expected_msql_file": "alter_proc_no_depends.msql" + }, + { + "type": "delete", + "name": "Drop procedure", + "endpoint": "NODE-procedure.delete_id", + "data": { + } + }, + { + "type": "delete", + "name": "Drop Extension", + "endpoint": "NODE-extension.delete", + "data": { + "ids": [""] + }, + "preprocess_data": true + }, + { + "type": "create", + "name": "Create procedure", + "endpoint": "NODE-procedure.obj", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql", + "data": { + "name": "Proc1_$%{}[]()&*^!@\"'`\\/#", + "acl": [], + "arguments": [{"argtype": "integer", "argmode": "IN", "argname": "i1", "argdefval": ""}], + "funcowner": "postgres", + "lanname": "plpgsql", + "options": [], + "pronamespace": 2200, + "prosrc": "begin\nselect 1;\nend;", + "seclabels": [], + "variables": [], + "schema": "public", + "provolatile": null, + "proisstrict": false, + "proparallel": null, + "procost": null, + "proleakproof": false, + "probin": "$libdir/" + }, + "expected_sql_file": "create_procedure.sql", + "expected_msql_file": "create_procedure.msql" + }, + { + "type": "alter", + "name": "Alter procedure comment", + "endpoint": "NODE-procedure.obj_id", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", + "data": { + "description": "some comment" + }, + "expected_sql_file": "alter_proc_comment.sql", + "expected_msql_file": "alter_proc_comment_msql.sql" + }, + { + "type": "alter", + "name": "Alter procedure param", + "endpoint": "NODE-procedure.obj_id", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", + "data": { + "variables": { + "added": [{"name": "application_name", "value": "pgadmin"}] + } + }, + "expected_sql_file": "alter_proc_param.sql", + "expected_msql_file": "alter_proc_param_msql.sql" + }, + { + "type": "alter", + "name": "Alter procedure add privileges", + "endpoint": "NODE-procedure.obj_id", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", + "data": { + "acl": { + "added": [ + { + "grantee": "PUBLIC", + "grantor": "postgres", + "privileges": [ + { + "privilege": true, + "privilege_type": "X", + "with_grant": false + } + ] + }, + { + "grantee": "postgres", + "grantor": "postgres", + "privileges": [ + { + "privilege": true, + "privilege_type": "X", + "with_grant": false + } + ] + } + ] + } + }, + "expected_sql_file": "alter_proc_add_priv.sql", + "expected_msql_file": "alter_proc_add_priv.msql" + }, + { + "type": "alter", + "name": "Alter procedure delete privileges", + "endpoint": "NODE-procedure.obj_id", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", + "data": { + "acl": { + "deleted": [ + { + "grantee": "PUBLIC", + "grantor": "postgres", + "privileges": [ + { + "privilege": true, + "privilege_type": "X", + "with_grant": false + } + ] + } + ] + } + }, + "expected_sql_file": "alter_proc_delete_priv.sql", + "expected_msql_file": "alter_proc_delete_priv.msql" + }, + { + "type": "alter", + "name": "Alter procedure change privileges", + "endpoint": "NODE-procedure.obj_id", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", + "data": { + "acl": { + "changed": [ + { + "grantee": "PUBLIC", + "grantor": "postgres", + "old_grantee": "postgres", + "privileges": [ + { + "privilege": true, + "privilege_type": "X", + "with_grant": false + } + ] + } + ] + } + }, + "expected_sql_file": "alter_proc_change_grantee_priv.sql", + "expected_msql_file": "alter_proc_change_grantee_priv.msql" + }, + { + "type": "delete", + "name": "Drop procedure", + "endpoint": "NODE-procedure.delete_id", + "data": { + } + } + ] +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_add_priv.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_add_priv.msql new file mode 100644 index 000000000..9f6362a8a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_add_priv.msql @@ -0,0 +1,3 @@ +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO PUBLIC; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO postgres; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_add_priv.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_add_priv.sql new file mode 100644 index 000000000..6d2d05f8f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_add_priv.sql @@ -0,0 +1,22 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + IN i1 integer) +LANGUAGE 'plpgsql' + SET application_name='pgadmin' +AS $BODY$ +begin +select 1; +end; +$BODY$; +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO postgres; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO PUBLIC; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO postgres; + +COMMENT ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + IS 'some comment'; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_delete_priv.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_delete_priv.msql new file mode 100644 index 000000000..b1662bf7f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_delete_priv.msql @@ -0,0 +1 @@ +REVOKE ALL ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) FROM PUBLIC; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_delete_priv.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_delete_priv.sql new file mode 100644 index 000000000..54ed1a4ce --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_delete_priv.sql @@ -0,0 +1,22 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + IN i1 integer) +LANGUAGE 'plpgsql' + SET application_name='pgadmin' +AS $BODY$ +begin +select 1; +end; +$BODY$; +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO postgres; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO postgres; + +REVOKE ALL ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) FROM PUBLIC; + +COMMENT ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + IS 'some comment'; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_no_depends.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_no_depends.msql new file mode 100644 index 000000000..b7e724941 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_no_depends.msql @@ -0,0 +1,2 @@ +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + NO DEPENDS ON EXTENSION postgres_fdw; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_no_depends.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_no_depends.sql new file mode 100644 index 000000000..c5ecff1e2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/alter_proc_no_depends.sql @@ -0,0 +1,17 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + IN i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO postgres; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION plpgsql; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_func.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_func.sql index 175b4b6aa..3218799f0 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_func.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_func.sql @@ -14,4 +14,4 @@ BEGIN ATOMIC END; ALTER FUNCTION public."Function4_$%{}[]()&*^!@""'`\/#"() - OWNER TO postgres; + OWNER TO postgres; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_proc.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_proc.msql index 6d6399cb5..15e277828 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_proc.msql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_proc.msql @@ -1,4 +1,4 @@ -CREATE PROCEDURE public."Proc3_$%{}[]()&*^!@"'`\/#"() +CREATE PROCEDURE public."Proc3_$%{}[]()&*^!@""'`\/#"() LANGUAGE 'sql' SET application_name='demo' @@ -6,5 +6,8 @@ begin atomic select 1; end; -COMMENT ON PROCEDURE public."Proc3_$%{}[]()&*^!@"'`\/#"() +ALTER PROCEDURE public."Proc3_$%{}[]()&*^!@""'`\/#"() + OWNER TO postgres; + +COMMENT ON PROCEDURE public."Proc3_$%{}[]()&*^!@""'`\/#"() IS 'demo comments'; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_procedure.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_procedure.msql new file mode 100644 index 000000000..10f1d3687 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_procedure.msql @@ -0,0 +1,9 @@ +CREATE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(IN i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO postgres; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_procedure_on_depends.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_procedure_on_depends.msql new file mode 100644 index 000000000..fc7d54c28 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_procedure_on_depends.msql @@ -0,0 +1,15 @@ +CREATE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(IN i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO postgres; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION plpgsql; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION postgres_fdw; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_procedure_on_depends.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_procedure_on_depends.sql new file mode 100644 index 000000000..82cf5aa34 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_procedure_on_depends.sql @@ -0,0 +1,20 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + IN i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO postgres; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION plpgsql; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION postgres_fdw; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/test_function.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/test_function.json index cfa30eb68..39c720ecd 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/test_function.json +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/test_function.json @@ -9,7 +9,8 @@ "name": "postgres_fdw", "version": "", "relocatable": true - } + }, + "store_object_id": true }, { "type": "create", @@ -76,6 +77,15 @@ "data": { } }, + { + "type": "delete", + "name": "Drop Extension", + "endpoint": "NODE-extension.delete", + "data": { + "ids": [""] + }, + "preprocess_data": true + }, { "type": "create", "name": "Create function with all options.", @@ -310,7 +320,9 @@ } ] } - } + }, + "expected_sql_file": "alter_func_remove_partial_pri.sql", + "expected_msql_file": "alter_func_remove_partial_pri.msql" }, { "type": "alter", diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/test_procedures.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/test_procedures.json index 00c37d4e1..67c1976d5 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/test_procedures.json +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/test_procedures.json @@ -1,10 +1,80 @@ { "scenarios": [ + { + "type": "create", + "name": "Create Extension", + "endpoint": "NODE-extension.obj", + "sql_endpoint": "NODE-extension.sql_id", + "data": { + "name": "postgres_fdw", + "version": "", + "relocatable": true + }, + "store_object_id": true + }, + { + "type": "create", + "name": "Create procedure with extensions.", + "endpoint": "NODE-procedure.obj", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql", + "data": { + "name": "Proc1_$%{}[]()&*^!@\"'`\\/#", + "acl": [], + "arguments": [{"argtype": "integer", "argmode": "IN", "argname": "i1", "argdefval": ""}], + "funcowner": "postgres", + "lanname": "plpgsql", + "options": [], + "pronamespace": 2200, + "prosrc": "begin\nselect 1;\nend;", + "seclabels": [], + "variables": [], + "schema": "public", + "provolatile": null, + "proisstrict": false, + "proparallel": null, + "procost": null, + "proleakproof": false, + "probin": "$libdir/", + "dependsonextensions": ["plpgsql", "postgres_fdw"] + }, + "expected_sql_file": "create_procedure_on_depends.sql", + "expected_msql_file": "create_procedure_on_depends.msql" + }, + { + "type": "alter", + "name": "Alter procedure with NO DEPENDS ON", + "endpoint": "NODE-procedure.obj_id", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", + "data": { + "dependsonextensions": ["plpgsql"] + }, + "expected_sql_file": "alter_proc_no_depends.sql", + "expected_msql_file": "alter_proc_no_depends.msql" + }, + { + "type": "delete", + "name": "Drop procedure", + "endpoint": "NODE-procedure.delete_id", + "data": { + } + }, + { + "type": "delete", + "name": "Drop Extension", + "endpoint": "NODE-extension.delete", + "data": { + "ids": [""] + }, + "preprocess_data": true + }, { "type": "create", "name": "Create procedure", "endpoint": "NODE-procedure.obj", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql", "data": { "name": "Proc1_$%{}[]()&*^!@\"'`\\/#", "acl": [], @@ -24,12 +94,14 @@ "proleakproof": false, "probin": "$libdir/" }, - "expected_sql_file": "create_procedure.sql" + "expected_sql_file": "create_procedure.sql", + "expected_msql_file": "create_procedure.msql" }, { "type": "alter", "name": "Alter procedure comment", "endpoint": "NODE-procedure.obj_id", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", "data": { "description": "some comment" }, @@ -40,6 +112,7 @@ "name": "Alter procedure param", "endpoint": "NODE-procedure.obj_id", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", "data": { "variables": { "added": [{"name": "application_name", "value": "pgadmin"}] @@ -53,6 +126,7 @@ "name": "Alter procedure add privileges", "endpoint": "NODE-procedure.obj_id", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", "data": { "acl": { "added": [ @@ -80,13 +154,16 @@ } ] } - } + }, + "expected_sql_file": "alter_proc_add_priv.sql", + "expected_msql_file": "alter_proc_add_priv.msql" }, { "type": "alter", "name": "Alter procedure delete privileges", "endpoint": "NODE-procedure.obj_id", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", "data": { "acl": { "deleted": [ @@ -103,13 +180,16 @@ } ] } - } + }, + "expected_sql_file": "alter_proc_delete_priv.sql", + "expected_msql_file": "alter_proc_delete_priv.msql" }, { "type": "alter", "name": "Alter procedure change privileges", "endpoint": "NODE-procedure.obj_id", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", "data": { "acl": { "changed": [ @@ -143,6 +223,7 @@ "name": "Create procedure (atomic)", "endpoint": "NODE-procedure.obj", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql", "data": { "name":"Proc3_$%{}[]()&*^!@\"'`\\/#", "funcowner":"postgres", diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_func_remove_partial_pri.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_func_remove_partial_pri.msql new file mode 100644 index 000000000..4fda43733 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_func_remove_partial_pri.msql @@ -0,0 +1 @@ +REVOKE ALL ON FUNCTION public."Function2_$%{}[]()&*^!@""'`\/#"() FROM PUBLIC; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_func_remove_partial_pri.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_func_remove_partial_pri.sql new file mode 100644 index 000000000..a4ce502c1 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_func_remove_partial_pri.sql @@ -0,0 +1,23 @@ +-- FUNCTION: public.Function2_$%{}[]()&*^!@"'`\/#() + +-- DROP FUNCTION IF EXISTS public."Function2_$%{}[]()&*^!@""'`\/#"(); + +CREATE OR REPLACE FUNCTION public."Function2_$%{}[]()&*^!@""'`\/#"( + ) + RETURNS character varying + LANGUAGE 'plpgsql' + COST 100 + VOLATILE SECURITY DEFINER PARALLEL UNSAFE + SET enable_sort='true' +AS $BODY$ +begin +select '1'; +end +$BODY$; + +ALTER FUNCTION public."Function2_$%{}[]()&*^!@""'`\/#"() + OWNER TO enterprisedb; + +GRANT EXECUTE ON FUNCTION public."Function2_$%{}[]()&*^!@""'`\/#"() TO enterprisedb; + +REVOKE ALL ON FUNCTION public."Function2_$%{}[]()&*^!@""'`\/#"() FROM PUBLIC; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_function_add_parameter.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_function_add_parameter.msql index 900e3a9cd..0b48852ba 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_function_add_parameter.msql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_function_add_parameter.msql @@ -2,12 +2,13 @@ CREATE OR REPLACE FUNCTION public."Function3_$%{}[]()&*^!@""'`\/#"() RETURNS character varying LANGUAGE 'plpgsql' VOLATILE SECURITY DEFINER - + PARALLEL UNSAFE COST 100 + SET application_name='appname' SET enable_sort='true' AS $BODY$ begin select '2'; end -$BODY$; +$BODY$; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_add_priv.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_add_priv.msql new file mode 100644 index 000000000..23bc0f422 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_add_priv.msql @@ -0,0 +1,3 @@ +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO PUBLIC; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO enterprisedb; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_add_priv.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_add_priv.sql new file mode 100644 index 000000000..0deb3c0f0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_add_priv.sql @@ -0,0 +1,23 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + i1 integer) +LANGUAGE 'plpgsql' + SET application_name='pgadmin' +AS $BODY$ +begin +select 1; +end; +$BODY$; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO enterprisedb; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO PUBLIC; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO enterprisedb; + +COMMENT ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + IS 'some comment'; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_change_grantee_priv.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_change_grantee_priv.msql index 5577b14cc..be3dbd0a6 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_change_grantee_priv.msql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_change_grantee_priv.msql @@ -1,13 +1,3 @@ -CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(IN i1 integer) - LANGUAGE 'plpgsql' - - SET application_name='pgadmin' -AS $BODY$ -begin -select 1; -end; -$BODY$; - REVOKE ALL ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) FROM enterprisedb; -GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO PUBLIC; +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO PUBLIC; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_delete_priv.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_delete_priv.msql new file mode 100644 index 000000000..b1662bf7f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_delete_priv.msql @@ -0,0 +1 @@ +REVOKE ALL ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) FROM PUBLIC; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_delete_priv.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_delete_priv.sql new file mode 100644 index 000000000..6595b00c8 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/alter_proc_delete_priv.sql @@ -0,0 +1,23 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + i1 integer) +LANGUAGE 'plpgsql' + SET application_name='pgadmin' +AS $BODY$ +begin +select 1; +end; +$BODY$; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO enterprisedb; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO enterprisedb; + +REVOKE ALL ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) FROM PUBLIC; + +COMMENT ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + IS 'some comment'; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/create_procedure.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/create_procedure.msql new file mode 100644 index 000000000..96271e0e2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/create_procedure.msql @@ -0,0 +1,10 @@ +CREATE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(IN i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO enterprisedb; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/test_function.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/test_function.json index 25f346a04..98649f228 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/test_function.json +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/12_plus/test_function.json @@ -246,7 +246,9 @@ } ] } - } + }, + "expected_sql_file": "alter_func_remove_partial_pri.sql", + "expected_msql_file": "alter_func_remove_partial_pri.msql" }, { "type": "alter", diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/alter_proc_no_depends.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/alter_proc_no_depends.msql new file mode 100644 index 000000000..b7e724941 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/alter_proc_no_depends.msql @@ -0,0 +1,2 @@ +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + NO DEPENDS ON EXTENSION postgres_fdw; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/alter_proc_no_depends.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/alter_proc_no_depends.sql new file mode 100644 index 000000000..3290c13f1 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/alter_proc_no_depends.sql @@ -0,0 +1,18 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO enterprisedb; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION plpgsql; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/create_procedure_on_depends.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/create_procedure_on_depends.msql new file mode 100644 index 000000000..b826da50a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/create_procedure_on_depends.msql @@ -0,0 +1,16 @@ +CREATE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(IN i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO enterprisedb; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION plpgsql; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION postgres_fdw; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/create_procedure_on_depends.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/create_procedure_on_depends.sql new file mode 100644 index 000000000..b715b914e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/create_procedure_on_depends.sql @@ -0,0 +1,21 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO enterprisedb; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION plpgsql; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION postgres_fdw; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/test_function.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/test_function.json index 2ab43d30b..5f5c3b59a 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/test_function.json +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/test_function.json @@ -9,7 +9,8 @@ "name": "postgres_fdw", "version": "", "relocatable": true - } + }, + "store_object_id": true }, { "type": "create", @@ -76,6 +77,15 @@ "data": { } }, + { + "type": "delete", + "name": "Drop Extension", + "endpoint": "NODE-extension.delete", + "data": { + "ids": [""] + }, + "preprocess_data": true + }, { "type": "create", "name": "Create function with all options.", @@ -202,7 +212,8 @@ ] } }, - "expected_sql_file": "alter_function_add_parameter.sql" + "expected_sql_file": "alter_function_add_parameter.sql", + "expected_msql_file": "alter_function_add_parameter.msql" }, { "type": "alter", @@ -322,7 +333,9 @@ } ] } - } + }, + "expected_sql_file": "alter_func_remove_partial_pri.sql", + "expected_msql_file": "alter_func_remove_partial_pri.msql" }, { "type": "alter", diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/test_procedures.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/test_procedures.json new file mode 100644 index 000000000..a5449a5bc --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/13_plus/test_procedures.json @@ -0,0 +1,155 @@ +{ + "scenarios": [ + { + "type": "create", + "name": "Create procedure", + "endpoint": "NODE-procedure.obj", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql", + "data": { + "name": "Proc1_$%{}[]()&*^!@\"'`\\/#", + "acl": [], + "arguments": [{"argtype": "integer", "argmode": "IN", "argname": "i1", "argdefval": ""}], + "funcowner": "enterprisedb", + "lanname": "plpgsql", + "options": [], + "pronamespace": 2200, + "prosrc": "begin\nselect 1;\nend;", + "seclabels": [], + "variables": [], + "schema": "public", + "provolatile": null, + "proisstrict": false, + "proparallel": null, + "procost": null, + "proleakproof": false, + "probin": "$libdir/" + }, + "expected_sql_file": "create_procedure.sql", + "expected_msql_file": "create_procedure.msql" + }, + { + "type": "alter", + "name": "Alter procedure comment", + "endpoint": "NODE-procedure.obj_id", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", + "data": { + "description": "some comment" + }, + "expected_sql_file": "alter_proc_comment.sql", + "expected_msql_file": "alter_proc_comment_msql.sql" + }, + { + "type": "alter", + "name": "Alter procedure param", + "endpoint": "NODE-procedure.obj_id", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", + "data": { + "variables": { + "added": [{"name": "application_name", "value": "pgadmin"}] + } + }, + "expected_sql_file": "alter_proc_param.sql", + "expected_msql_file": "alter_proc_param_msql.sql" + }, + { + "type": "alter", + "name": "Alter procedure add privileges", + "endpoint": "NODE-procedure.obj_id", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", + "data": { + "acl": { + "added": [ + { + "grantee": "PUBLIC", + "grantor": "enterprisedb", + "privileges": [ + { + "privilege": true, + "privilege_type": "X", + "with_grant": false + } + ] + }, + { + "grantee": "enterprisedb", + "grantor": "enterprisedb", + "privileges": [ + { + "privilege": true, + "privilege_type": "X", + "with_grant": false + } + ] + } + ] + } + }, + "expected_sql_file": "alter_proc_add_priv.sql", + "expected_msql_file": "alter_proc_add_priv.msql" + }, + { + "type": "alter", + "name": "Alter procedure delete privileges", + "endpoint": "NODE-procedure.obj_id", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", + "data": { + "acl": { + "deleted": [ + { + "grantee": "PUBLIC", + "grantor": "enterprisedb", + "privileges": [ + { + "privilege": true, + "privilege_type": "X", + "with_grant": false + } + ] + } + ] + } + }, + "expected_sql_file": "alter_proc_delete_priv.sql", + "expected_msql_file": "alter_proc_delete_priv.msql" + }, + { + "type": "alter", + "name": "Alter procedure change privileges", + "endpoint": "NODE-procedure.obj_id", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", + "data": { + "acl": { + "changed": [ + { + "grantee": "PUBLIC", + "grantor": "enterprisedb", + "old_grantee": "enterprisedb", + "privileges": [ + { + "privilege": true, + "privilege_type": "X", + "with_grant": false + } + ] + } + ] + } + }, + "expected_sql_file": "alter_proc_change_grantee_priv.sql", + "expected_msql_file": "alter_proc_change_grantee_priv.msql" + }, + { + "type": "delete", + "name": "Drop procedure", + "endpoint": "NODE-procedure.delete_id", + "data": { + } + } + ] +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_add_priv.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_add_priv.msql new file mode 100644 index 000000000..23bc0f422 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_add_priv.msql @@ -0,0 +1,3 @@ +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO PUBLIC; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO enterprisedb; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_add_priv.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_add_priv.sql new file mode 100644 index 000000000..4d0cb5e0f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_add_priv.sql @@ -0,0 +1,22 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + IN i1 integer) +LANGUAGE 'plpgsql' + SET application_name='pgadmin' +AS $BODY$ +begin +select 1; +end; +$BODY$; +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO enterprisedb; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO PUBLIC; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO enterprisedb; + +COMMENT ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + IS 'some comment'; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_delete_priv.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_delete_priv.msql new file mode 100644 index 000000000..b1662bf7f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_delete_priv.msql @@ -0,0 +1 @@ +REVOKE ALL ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) FROM PUBLIC; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_delete_priv.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_delete_priv.sql new file mode 100644 index 000000000..c7bf30cb3 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_delete_priv.sql @@ -0,0 +1,22 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + IN i1 integer) +LANGUAGE 'plpgsql' + SET application_name='pgadmin' +AS $BODY$ +begin +select 1; +end; +$BODY$; +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO enterprisedb; + +GRANT EXECUTE ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) TO enterprisedb; + +REVOKE ALL ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) FROM PUBLIC; + +COMMENT ON PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + IS 'some comment'; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_no_depends.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_no_depends.msql new file mode 100644 index 000000000..b7e724941 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_no_depends.msql @@ -0,0 +1,2 @@ +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + NO DEPENDS ON EXTENSION postgres_fdw; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_no_depends.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_no_depends.sql new file mode 100644 index 000000000..d755deb7c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/alter_proc_no_depends.sql @@ -0,0 +1,17 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + IN i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO enterprisedb; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION plpgsql; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_proc.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_proc.msql index 6d6399cb5..c11fd60d0 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_proc.msql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_proc.msql @@ -1,4 +1,4 @@ -CREATE PROCEDURE public."Proc3_$%{}[]()&*^!@"'`\/#"() +CREATE PROCEDURE public."Proc3_$%{}[]()&*^!@""'`\/#"() LANGUAGE 'sql' SET application_name='demo' @@ -6,5 +6,8 @@ begin atomic select 1; end; -COMMENT ON PROCEDURE public."Proc3_$%{}[]()&*^!@"'`\/#"() - IS 'demo comments'; +ALTER PROCEDURE public."Proc3_$%{}[]()&*^!@""'`\/#"() + OWNER TO enterprisedb; + +COMMENT ON PROCEDURE public."Proc3_$%{}[]()&*^!@""'`\/#"() + IS 'demo comments'; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_procedure.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_procedure.msql new file mode 100644 index 000000000..c71cd0996 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_procedure.msql @@ -0,0 +1,9 @@ +CREATE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(IN i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO enterprisedb; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_procedure_on_depends.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_procedure_on_depends.msql new file mode 100644 index 000000000..745784656 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_procedure_on_depends.msql @@ -0,0 +1,15 @@ +CREATE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(IN i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO enterprisedb; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION plpgsql; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION postgres_fdw; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_procedure_on_depends.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_procedure_on_depends.sql new file mode 100644 index 000000000..8571364c2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_procedure_on_depends.sql @@ -0,0 +1,20 @@ +-- PROCEDURE: public.Proc1_$%{}[]()&*^!@"'`\/#(integer) + +-- DROP PROCEDURE IF EXISTS public."Proc1_$%{}[]()&*^!@""'`\/#"(integer); + +CREATE OR REPLACE PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"( + IN i1 integer) +LANGUAGE 'plpgsql' +AS $BODY$ +begin +select 1; +end; +$BODY$; +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + OWNER TO enterprisedb; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION plpgsql; + +ALTER PROCEDURE public."Proc1_$%{}[]()&*^!@""'`\/#"(integer) + DEPENDS ON EXTENSION postgres_fdw; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/test_function.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/test_function.json index 135b9cece..03ac40223 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/test_function.json +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/test_function.json @@ -9,7 +9,8 @@ "name": "postgres_fdw", "version": "", "relocatable": true - } + }, + "store_object_id": true }, { "type": "create", @@ -76,6 +77,15 @@ "data": { } }, + { + "type": "delete", + "name": "Drop Extension", + "endpoint": "NODE-extension.delete", + "data": { + "ids": [""] + }, + "preprocess_data": true + }, { "type": "create", "name": "Create function with all options.", @@ -202,7 +212,8 @@ ] } }, - "expected_sql_file": "alter_function_add_parameter.sql" + "expected_sql_file": "alter_function_add_parameter.sql", + "expected_msql_file": "alter_function_add_parameter.msql" }, { "type": "alter", @@ -322,7 +333,9 @@ } ] } - } + }, + "expected_sql_file": "alter_func_remove_partial_pri.sql", + "expected_msql_file": "alter_func_remove_partial_pri.msql" }, { "type": "alter", diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/test_procedures.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/test_procedures.json index 708d1d80e..e65f8d6f5 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/test_procedures.json +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/test_procedures.json @@ -1,10 +1,80 @@ { "scenarios": [ + { + "type": "create", + "name": "Create Extension", + "endpoint": "NODE-extension.obj", + "sql_endpoint": "NODE-extension.sql_id", + "data": { + "name": "postgres_fdw", + "version": "", + "relocatable": true + }, + "store_object_id": true + }, + { + "type": "create", + "name": "Create procedure with extensions.", + "endpoint": "NODE-procedure.obj", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql", + "data": { + "name": "Proc1_$%{}[]()&*^!@\"'`\\/#", + "acl": [], + "arguments": [{"argtype": "integer", "argmode": "IN", "argname": "i1", "argdefval": ""}], + "funcowner": "enterprisedb", + "lanname": "plpgsql", + "options": [], + "pronamespace": 2200, + "prosrc": "begin\nselect 1;\nend;", + "seclabels": [], + "variables": [], + "schema": "public", + "provolatile": null, + "proisstrict": false, + "proparallel": null, + "procost": null, + "proleakproof": false, + "probin": "$libdir/", + "dependsonextensions": ["plpgsql", "postgres_fdw"] + }, + "expected_sql_file": "create_procedure_on_depends.sql", + "expected_msql_file": "create_procedure_on_depends.msql" + }, + { + "type": "alter", + "name": "Alter procedure with NO DEPENDS ON", + "endpoint": "NODE-procedure.obj_id", + "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", + "data": { + "dependsonextensions": ["plpgsql"] + }, + "expected_sql_file": "alter_proc_no_depends.sql", + "expected_msql_file": "alter_proc_no_depends.msql" + }, + { + "type": "delete", + "name": "Drop procedure", + "endpoint": "NODE-procedure.delete_id", + "data": { + } + }, + { + "type": "delete", + "name": "Drop Extension", + "endpoint": "NODE-extension.delete", + "data": { + "ids": [""] + }, + "preprocess_data": true + }, { "type": "create", "name": "Create procedure", "endpoint": "NODE-procedure.obj", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql", "data": { "name": "Proc1_$%{}[]()&*^!@\"'`\\/#", "acl": [], @@ -24,22 +94,27 @@ "proleakproof": false, "probin": "$libdir/" }, - "expected_sql_file": "create_procedure.sql" - }, { + "expected_sql_file": "create_procedure.sql", + "expected_msql_file": "create_procedure.msql" + }, + { "type": "alter", "name": "Alter procedure comment", "endpoint": "NODE-procedure.obj_id", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", "data": { "description": "some comment" }, "expected_sql_file": "alter_proc_comment.sql", "expected_msql_file": "alter_proc_comment_msql.sql" - }, { + }, + { "type": "alter", "name": "Alter procedure param", "endpoint": "NODE-procedure.obj_id", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", "data": { "variables": { "added": [{"name": "application_name", "value": "pgadmin"}] @@ -53,6 +128,7 @@ "name": "Alter procedure add privileges", "endpoint": "NODE-procedure.obj_id", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", "data": { "acl": { "added": [ @@ -80,13 +156,16 @@ } ] } - } + }, + "expected_sql_file": "alter_proc_add_priv.sql", + "expected_msql_file": "alter_proc_add_priv.msql" }, { "type": "alter", "name": "Alter procedure delete privileges", "endpoint": "NODE-procedure.obj_id", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", "data": { "acl": { "deleted": [ @@ -103,13 +182,16 @@ } ] } - } + }, + "expected_sql_file": "alter_proc_delete_priv.sql", + "expected_msql_file": "alter_proc_delete_priv.msql" }, { "type": "alter", "name": "Alter procedure change privileges", "endpoint": "NODE-procedure.obj_id", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql_id", "data": { "acl": { "changed": [ @@ -143,6 +225,7 @@ "name": "Create procedure (atomic)", "endpoint": "NODE-procedure.obj", "sql_endpoint": "NODE-procedure.sql_id", + "msql_endpoint": "NODE-procedure.msql", "data": { "name":"Proc3_$%{}[]()&*^!@\"'`\\/#", "funcowner":"enterprisedb", diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_add.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_add.py index 6bd46c5e7..184a71636 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_add.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_add.py @@ -62,6 +62,7 @@ class ProcedureAddTestCase(BaseTestGenerator): "prosrc": "BEGIN RAISE EXCEPTION 'command % is disabled'," " tg_tag; END;", "seclabels": [], + "dependsonextensions": ["plpgsql"], "variables": [ { "name": "enable_sort", diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_put.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_put.py index 455677739..e0565afc2 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_put.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/test_procedure_put.py @@ -43,7 +43,8 @@ class ProcedurePutTestCase(BaseTestGenerator): proc_id = proc_info[0] data = { "description": "This is procedure update comment", - "id": proc_id + "id": proc_id, + "dependsonextensions": ["plpgsql"] } put_response = self.tester.put(