From 91a288c2bbb429da0f0db0768df04d507d1a2d04 Mon Sep 17 00:00:00 2001 From: Rahul Shirsat Date: Wed, 8 Dec 2021 12:31:59 +0530 Subject: [PATCH] Correct the SQL definition for function/procedure with the Atomic keyword in PG14. Fixes #7029 --- docs/en_US/release_notes_6_3.rst | 1 + .../databases/schemas/functions/__init__.py | 78 +++++++++++ .../functions/pg/sql/14_plus/create.sql | 76 +++++++++++ .../functions/pg/sql/14_plus/properties.sql | 45 +++++++ .../functions/pg/sql/14_plus/update.sql | 127 ++++++++++++++++++ .../functions/ppas/sql/14_plus/create.sql | 76 +++++++++++ .../functions/ppas/sql/14_plus/properties.sql | 39 ++++++ .../functions/ppas/sql/14_plus/update.sql | 127 ++++++++++++++++++ .../procedures/pg/sql/14_plus/create.sql | 55 ++++++++ .../procedures/pg/sql/14_plus/properties.sql | 49 +++++++ .../procedures/pg/sql/14_plus/update.sql | 114 ++++++++++++++++ .../procedures/ppas/sql/14_plus/create.sql | 63 +++++++++ .../ppas/sql/14_plus/properties.sql | 49 +++++++ .../procedures/ppas/sql/14_plus/update.sql | 124 +++++++++++++++++ .../tests/pg/14_plus/create_atomic_func.msql | 12 ++ .../tests/pg/14_plus/create_atomic_func.sql | 17 +++ .../tests/pg/14_plus/create_atomic_proc.msql | 10 ++ .../tests/pg/14_plus/create_atomic_proc.sql | 15 +++ .../tests/pg/14_plus/test_function.json | 40 ++++++ .../tests/pg/14_plus/test_procedures.json | 36 +++++ .../ppas/14_plus/create_atomic_func.msql | 12 ++ .../tests/ppas/14_plus/create_atomic_func.sql | 17 +++ .../ppas/14_plus/create_atomic_proc.msql | 10 ++ .../tests/ppas/14_plus/create_atomic_proc.sql | 15 +++ .../tests/ppas/14_plus/test_function.json | 40 ++++++ .../tests/ppas/14_plus/test_procedures.json | 36 +++++ .../tools/sqleditor/static/js/sqleditor.js | 2 +- 27 files changed, 1284 insertions(+), 1 deletion(-) create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/pg/sql/14_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/pg/sql/14_plus/properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/pg/sql/14_plus/update.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/ppas/sql/14_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/ppas/sql/14_plus/properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/ppas/sql/14_plus/update.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/update.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/update.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_func.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_func.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_proc.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_proc.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_func.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_func.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_proc.msql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_proc.sql diff --git a/docs/en_US/release_notes_6_3.rst b/docs/en_US/release_notes_6_3.rst index aabd24cef..a6439310d 100644 --- a/docs/en_US/release_notes_6_3.rst +++ b/docs/en_US/release_notes_6_3.rst @@ -36,6 +36,7 @@ Bug fixes | `Issue #7013 `_ - Fix an RPM build issue that could lead to a conflict with python3 at installation. | `Issue #7015 `_ - Fixed an issue where the error is thrown while creating a new server using Add New Server from the dashboard while tree item is not selected. | `Issue #7024 `_ - Fixed an issue where reverse engineering SQL is wrong for Aggregate. +| `Issue #7029 `_ - Correct the SQL definition for function/procedure with the Atomic keyword in PG14. | `Issue #7031 `_ - Fixed an issue where SQLite database definition is wrong because the USER_ID FK references the table user_old which is not available. | `Issue #7040 `_ - Add "section" to the Debian package control files. | `Issue #7044 `_ - Update the dropzone version to 5.9.3 and Flask-SQLAlchemy to 2.5.*. 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 37758ad3d..4493f81db 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 @@ -1455,6 +1455,8 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): else: FunctionView._merge_variables(data) + self._format_prosrc_for_pure_sql(data, False) + if allow_code_formatting: self.reformat_prosrc_code(data) @@ -1464,6 +1466,28 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): ) return True, '', sql + def _format_prosrc_for_pure_sql(self, data, view_only=True): + + if self.manager.sversion < 140000: + return + + # no need to test whether function/procedure definition is pure sql + # or not, the parameter from 'is_pure_sql' is sufficient. + if view_only: + if 'is_pure_sql' in data and data['is_pure_sql'] is True: + data['prosrc'] = data['prosrc_sql'] + if data['prosrc'].endswith(';') is False: + data['prosrc'] = ''.join((data['prosrc'], ';')) + else: + data['is_pure_sql'] = False + else: + # when function/procedure definition is changed, we need to find + # whether definition is of pure or have std sql definition. + if self._is_function_def_sql_standard(data): + data['is_pure_sql'] = True + if data['prosrc'].endswith(';') is False: + data['prosrc'] = ''.join((data['prosrc'], ';')) + def _get_sql(self, **kwargs): """ Generates the SQL statements to create/update the Function. @@ -1529,6 +1553,8 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): data['func_args_without'] = ', '.join(args_without_name) + self._format_prosrc_for_pure_sql(data, False) + if allow_code_formatting: self.reformat_prosrc_code(data) @@ -1610,6 +1636,8 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): resp_data['provolatile'] = None resp_data['proparallel'] = None + self._format_prosrc_for_pure_sql(resp_data) + return resp_data def _get_schema(self, scid): @@ -1946,6 +1974,56 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare): return res + @staticmethod + def _is_function_def_sql_standard(resp_data): + + """ + This function is responsible for checking the sql to determine + whether it is as per SQL-standard or not. In fact, the function + is mainly utilised for the sql language with the newly added + ATOMIC in the version v14 of Postgres for functions & procedures + respectively. + + :param resp_data: + :return: boolean + """ + # if language is other than 'sql', return False + if 'lanname' in resp_data and resp_data['lanname'] != 'sql': + return False + + # invalid regex, these combination should not be present in the sql + invalid_match = [r"^.*(?:\'|\")?.*(?=.*?atomic).*(?:\'|\").*$", + r"^.*(?:\"|\')(?=.*(atomic)).*$"] + + # valid regex, these combination a must in definition to detect a + # standard sql or pure sql + valid_match = [ + r"(?=.*begin)(.+?(\n)+)(?=.*atomic)|(?=.*begin)(?=.*atomic)", + r"(?=return)" + ] + + is_func_def_sql_std = False + + if 'prosrc' in resp_data and resp_data['prosrc'] is not None \ + and resp_data['prosrc'] != '': + + prosrc = str(resp_data['prosrc']).lower().strip('\n').strip('\t') + + for invalid in invalid_match: + for match in enumerate( + re.finditer(invalid, prosrc, re.MULTILINE), start=1): + if match: + return is_func_def_sql_std + + for valid in valid_match: + for match in enumerate( + re.finditer(valid, prosrc, re.MULTILINE), start=1): + if match: + is_func_def_sql_std = True + return is_func_def_sql_std + + return is_func_def_sql_std + SchemaDiffRegistry(blueprint.node_type, FunctionView) FunctionView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/pg/sql/14_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/pg/sql/14_plus/create.sql new file mode 100644 index 000000000..4f4dbc76b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/pg/sql/14_plus/create.sql @@ -0,0 +1,76 @@ +{% 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{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{func_def}} +{% else %} +CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({% if data.arguments %} +{% for p in data.arguments %}{% if p.argmode %}{{p.argmode}} {% endif %}{% if p.argname %}{{ conn|qtIdent(p.argname) }} {% endif %}{% if p.argtype %}{{ p.argtype }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %} +{% if not loop.last %}, {% endif %} +{% endfor %} +{% endif -%} +) +{% endif %} + RETURNS{% if data.proretset and (data.prorettypename.startswith('SETOF ') or data.prorettypename.startswith('TABLE')) %} {{ data.prorettypename }} {% elif data.proretset %} SETOF {{ data.prorettypename }}{% else %} {{ data.prorettypename }}{% endif %} + + LANGUAGE {{ data.lanname|qtLiteral }} +{% if data.procost %} + COST {{data.procost}} +{% endif %} + {% if data.provolatile %}{% if data.provolatile == 'i' %}IMMUTABLE{% elif data.provolatile == 's' %}STABLE{% else %}VOLATILE{% endif %} {% endif %}{% if data.proleakproof %}LEAKPROOF {% endif %} +{% if data.proisstrict %}STRICT {% endif %} +{% if data.prosecdef %}SECURITY DEFINER {% endif %} +{% if data.proiswindow %}WINDOW {% endif %} +{% if data.proparallel and (data.proparallel == 'r' or data.proparallel == 's' or data.proparallel == 'u') %} +{% if data.proparallel == 'r' %}PARALLEL RESTRICTED {% elif data.proparallel == 's' %}PARALLEL SAFE {% elif data.proparallel == 'u' %}PARALLEL UNSAFE{% endif %}{% endif %} +{% if data.prorows and (data.prorows | int) > 0 %} + + ROWS {{data.prorows}} +{% endif %} +{% if data.prosupportfunc %} + SUPPORT {{ data.prosupportfunc }} +{% endif -%} +{% if data.variables %}{% for v in data.variables %} + + SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor %} +{% endif %} + +{% if data.is_pure_sql %}{{ data.prosrc }} +{% else %} +AS {% if data.lanname == 'c' %} +{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }} +{% else %} +$BODY${{ data.prosrc }}$BODY${% endif -%}; +{% endif -%} +{% if data.funcowner %} + +ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}}) + OWNER TO {{ conn|qtIdent(data.funcowner) }}; +{% endif -%} +{% if data.acl %} +{% for p in data.acl %} + +{{ PRIVILEGE.SET(conn, "FUNCTION", p.grantee, data.name, p.without_grant, p.with_grant, data.pronamespace, data.func_args_without)}} +{% endfor %}{% endif %} +{% if data.revoke_all %} + +{{ PRIVILEGE.UNSETALL(conn, "FUNCTION", "PUBLIC", data.name, data.pronamespace, data.func_args_without)}} +{% endif %} +{% if data.description %} + +COMMENT ON FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}}) + IS {{ data.description|qtLiteral }}; +{% endif -%} +{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.label and r.provider %} + +{{ SECLABEL.SET(conn, 'FUNCTION', data.name, r.provider, r.label, data.pronamespace, data.func_args_without) }} +{% endif %} +{% endfor %} +{% endif -%} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/pg/sql/14_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/pg/sql/14_plus/properties.sql new file mode 100644 index 000000000..782b94385 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/pg/sql/14_plus/properties.sql @@ -0,0 +1,45 @@ +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, + 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, + CASE WHEN prosupport = 0::oid THEN '' + ELSE ( + SELECT pg_catalog.quote_ident(nspname) || '.' || pg_catalog.quote_ident(proname) AS tfunctions + FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n + WHERE p.pronamespace = n.oid + AND p.oid = pr.prosupport::OID + ) END AS prosupportfunc, + (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 IN ('f', 'w') + AND typname NOT IN ('trigger', 'event_trigger') +{% if fnid %} + AND pr.oid = {{fnid}}::oid +{% else %} + AND pronamespace = {{scid}}::oid +{% endif %} +ORDER BY + proname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/pg/sql/14_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/pg/sql/14_plus/update.sql new file mode 100644 index 000000000..433fc6577 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/pg/sql/14_plus/update.sql @@ -0,0 +1,127 @@ +{% 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 FUNCTION {{ conn|qtIdent(o_data.pronamespace, o_data.name) }}({{ +o_data.proargtypenames }}) + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} +{% endif -%} +{% if data.change_func %} + +CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if data.arguments %} +{% for p in data.arguments %}{% if p.argmode %}{{p.argmode}} {% endif %}{% if p.argname %}{{ conn|qtIdent(p.argname) }} {% endif %}{% if p.argtype %}{{ p.argtype }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %} +{% if not loop.last %},{% endif %} +{% endfor %} +{% endif -%} +) + RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %} + +{% if 'lanname' in data %} + LANGUAGE {{ data.lanname|qtLiteral }} {% else %} + LANGUAGE {{ o_data.lanname|qtLiteral }} + {% endif %}{% if 'provolatile' in data and data.provolatile %}{{ data.provolatile }} {% elif 'provolatile' not in data and o_data.provolatile %}{{ o_data.provolatile }}{% endif %} +{% if ('proleakproof' in data and data.proleakproof) or ('proleakproof' not in data and o_data.proleakproof) %} LEAKPROOF{% elif 'proleakproof' in data and not data.proleakproof %} NOT LEAKPROOF{% endif %} +{% if ('proisstrict' in data and data.proisstrict) or ('proisstrict' not in data and o_data.proisstrict) %} STRICT{% endif %} +{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %} +{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %} + + {% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %} + + {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %} + + ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif %} + + {% if data.prosupportfunc %}SUPPORT {{ data.prosupportfunc }}{% elif data.prosupportfunc is not defined and o_data.prosupportfunc %}SUPPORT {{ o_data.prosupportfunc }}{% endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %} + + SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor -%} + {% endif %} + +{% if data.is_pure_sql %}{{ data.prosrc }} +{% else %} +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 }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %} +$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %} +{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %} +$BODY${{ o_data.prosrc }}$BODY${% endif -%}; +{% endif -%} +{% endif -%} +{% if data.funcowner %} + +ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }}) + OWNER TO {{ conn|qtIdent(data.funcowner) }}; +{% endif -%} +{# The SQL generated below will change priviledges #} +{% if data.acl %} +{% if 'deleted' in data.acl %} +{% for priv in data.acl.deleted %} + +{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }} +{% endfor %} +{% endif -%} +{% if 'changed' in data.acl %} +{% for priv in data.acl.changed %} + +{% if priv.grantee != priv.old_grantee %} +{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.old_grantee, name, o_data.pronamespace, o_data.proargtypenames) }} +{% else %} +{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }} +{% endif %} + +{{ PRIVILEGE.SET(conn, 'FUNCTION', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }} +{% endfor %} +{% endif -%} +{% if 'added' in data.acl %} +{% for priv in data.acl.added %} + +{{ PRIVILEGE.SET(conn, 'FUNCTION', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }} +{% endfor %}{% endif -%} +{% endif -%} +{% if data.change_func == False %} +{% if data.variables %} +{% if 'deleted' in data.variables and data.variables.deleted|length > 0 %} + +{{ VARIABLE.UNSET(conn, 'FUNCTION', name, data.variables.deleted, o_data.pronamespace, o_data.proargtypenames) }} +{% endif -%} +{% if 'merged_variables' in data and data.merged_variables|length > 0 %} + +{{ VARIABLE.SET(conn, 'FUNCTION', name, data.merged_variables, o_data.pronamespace, o_data.proargtypenames) }} +{% endif -%} +{% endif -%} +{% endif -%} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} + +{{ SECLABEL.UNSET(conn, 'FUNCTION', name, r.provider, o_data.pronamespace, o_data.proargtypenames) }} +{% endfor %} +{% endif -%} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} + +{{ SECLABEL.SET(conn, 'FUNCTION', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }} +{% endfor %} +{% endif -%} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} + +{{ SECLABEL.SET(conn, 'FUNCTION', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }} +{% endfor %} +{% endif -%} +{% if data.description is defined and data.description != o_data.description%} + +COMMENT ON FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }}) + IS {{ data.description|qtLiteral }}; +{% endif -%} + +{% if data.pronamespace %} + +ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }}) + SET SCHEMA {{ conn|qtIdent(data.pronamespace) }}; +{% endif -%} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/ppas/sql/14_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/ppas/sql/14_plus/create.sql new file mode 100644 index 000000000..4f4dbc76b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/ppas/sql/14_plus/create.sql @@ -0,0 +1,76 @@ +{% 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{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{func_def}} +{% else %} +CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({% if data.arguments %} +{% for p in data.arguments %}{% if p.argmode %}{{p.argmode}} {% endif %}{% if p.argname %}{{ conn|qtIdent(p.argname) }} {% endif %}{% if p.argtype %}{{ p.argtype }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %} +{% if not loop.last %}, {% endif %} +{% endfor %} +{% endif -%} +) +{% endif %} + RETURNS{% if data.proretset and (data.prorettypename.startswith('SETOF ') or data.prorettypename.startswith('TABLE')) %} {{ data.prorettypename }} {% elif data.proretset %} SETOF {{ data.prorettypename }}{% else %} {{ data.prorettypename }}{% endif %} + + LANGUAGE {{ data.lanname|qtLiteral }} +{% if data.procost %} + COST {{data.procost}} +{% endif %} + {% if data.provolatile %}{% if data.provolatile == 'i' %}IMMUTABLE{% elif data.provolatile == 's' %}STABLE{% else %}VOLATILE{% endif %} {% endif %}{% if data.proleakproof %}LEAKPROOF {% endif %} +{% if data.proisstrict %}STRICT {% endif %} +{% if data.prosecdef %}SECURITY DEFINER {% endif %} +{% if data.proiswindow %}WINDOW {% endif %} +{% if data.proparallel and (data.proparallel == 'r' or data.proparallel == 's' or data.proparallel == 'u') %} +{% if data.proparallel == 'r' %}PARALLEL RESTRICTED {% elif data.proparallel == 's' %}PARALLEL SAFE {% elif data.proparallel == 'u' %}PARALLEL UNSAFE{% endif %}{% endif %} +{% if data.prorows and (data.prorows | int) > 0 %} + + ROWS {{data.prorows}} +{% endif %} +{% if data.prosupportfunc %} + SUPPORT {{ data.prosupportfunc }} +{% endif -%} +{% if data.variables %}{% for v in data.variables %} + + SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor %} +{% endif %} + +{% if data.is_pure_sql %}{{ data.prosrc }} +{% else %} +AS {% if data.lanname == 'c' %} +{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }} +{% else %} +$BODY${{ data.prosrc }}$BODY${% endif -%}; +{% endif -%} +{% if data.funcowner %} + +ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}}) + OWNER TO {{ conn|qtIdent(data.funcowner) }}; +{% endif -%} +{% if data.acl %} +{% for p in data.acl %} + +{{ PRIVILEGE.SET(conn, "FUNCTION", p.grantee, data.name, p.without_grant, p.with_grant, data.pronamespace, data.func_args_without)}} +{% endfor %}{% endif %} +{% if data.revoke_all %} + +{{ PRIVILEGE.UNSETALL(conn, "FUNCTION", "PUBLIC", data.name, data.pronamespace, data.func_args_without)}} +{% endif %} +{% if data.description %} + +COMMENT ON FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}}) + IS {{ data.description|qtLiteral }}; +{% endif -%} +{% if data.seclabels %} +{% for r in data.seclabels %} +{% if r.label and r.provider %} + +{{ SECLABEL.SET(conn, 'FUNCTION', data.name, r.provider, r.label, data.pronamespace, data.func_args_without) }} +{% endif %} +{% endfor %} +{% endif -%} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/ppas/sql/14_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/ppas/sql/14_plus/properties.sql new file mode 100644 index 000000000..4576d25f8 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/ppas/sql/14_plus/properties.sql @@ -0,0 +1,39 @@ +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, + 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, + CASE WHEN prosupport = 0::oid THEN '' ELSE prosupport::text END AS prosupportfunc, + (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 IN ('f', 'w') + AND typname NOT IN ('trigger', 'event_trigger') +{% if fnid %} + AND pr.oid = {{fnid}}::oid +{% else %} + AND pronamespace = {{scid}}::oid +{% endif %} +ORDER BY + proname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/ppas/sql/14_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/ppas/sql/14_plus/update.sql new file mode 100644 index 000000000..433fc6577 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/functions/ppas/sql/14_plus/update.sql @@ -0,0 +1,127 @@ +{% 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 FUNCTION {{ conn|qtIdent(o_data.pronamespace, o_data.name) }}({{ +o_data.proargtypenames }}) + RENAME TO {{ conn|qtIdent(data.name) }}; +{% set name = data.name %} +{% endif %} +{% endif -%} +{% if data.change_func %} + +CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if data.arguments %} +{% for p in data.arguments %}{% if p.argmode %}{{p.argmode}} {% endif %}{% if p.argname %}{{ conn|qtIdent(p.argname) }} {% endif %}{% if p.argtype %}{{ p.argtype }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %} +{% if not loop.last %},{% endif %} +{% endfor %} +{% endif -%} +) + RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %} + +{% if 'lanname' in data %} + LANGUAGE {{ data.lanname|qtLiteral }} {% else %} + LANGUAGE {{ o_data.lanname|qtLiteral }} + {% endif %}{% if 'provolatile' in data and data.provolatile %}{{ data.provolatile }} {% elif 'provolatile' not in data and o_data.provolatile %}{{ o_data.provolatile }}{% endif %} +{% if ('proleakproof' in data and data.proleakproof) or ('proleakproof' not in data and o_data.proleakproof) %} LEAKPROOF{% elif 'proleakproof' in data and not data.proleakproof %} NOT LEAKPROOF{% endif %} +{% if ('proisstrict' in data and data.proisstrict) or ('proisstrict' not in data and o_data.proisstrict) %} STRICT{% endif %} +{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %} +{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %} + + {% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %} + + {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %} + + ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif %} + + {% if data.prosupportfunc %}SUPPORT {{ data.prosupportfunc }}{% elif data.prosupportfunc is not defined and o_data.prosupportfunc %}SUPPORT {{ o_data.prosupportfunc }}{% endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %} + + SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor -%} + {% endif %} + +{% if data.is_pure_sql %}{{ data.prosrc }} +{% else %} +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 }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %} +$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %} +{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %} +$BODY${{ o_data.prosrc }}$BODY${% endif -%}; +{% endif -%} +{% endif -%} +{% if data.funcowner %} + +ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }}) + OWNER TO {{ conn|qtIdent(data.funcowner) }}; +{% endif -%} +{# The SQL generated below will change priviledges #} +{% if data.acl %} +{% if 'deleted' in data.acl %} +{% for priv in data.acl.deleted %} + +{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }} +{% endfor %} +{% endif -%} +{% if 'changed' in data.acl %} +{% for priv in data.acl.changed %} + +{% if priv.grantee != priv.old_grantee %} +{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.old_grantee, name, o_data.pronamespace, o_data.proargtypenames) }} +{% else %} +{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }} +{% endif %} + +{{ PRIVILEGE.SET(conn, 'FUNCTION', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }} +{% endfor %} +{% endif -%} +{% if 'added' in data.acl %} +{% for priv in data.acl.added %} + +{{ PRIVILEGE.SET(conn, 'FUNCTION', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }} +{% endfor %}{% endif -%} +{% endif -%} +{% if data.change_func == False %} +{% if data.variables %} +{% if 'deleted' in data.variables and data.variables.deleted|length > 0 %} + +{{ VARIABLE.UNSET(conn, 'FUNCTION', name, data.variables.deleted, o_data.pronamespace, o_data.proargtypenames) }} +{% endif -%} +{% if 'merged_variables' in data and data.merged_variables|length > 0 %} + +{{ VARIABLE.SET(conn, 'FUNCTION', name, data.merged_variables, o_data.pronamespace, o_data.proargtypenames) }} +{% endif -%} +{% endif -%} +{% endif -%} +{% set seclabels = data.seclabels %} +{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %} +{% for r in seclabels.deleted %} + +{{ SECLABEL.UNSET(conn, 'FUNCTION', name, r.provider, o_data.pronamespace, o_data.proargtypenames) }} +{% endfor %} +{% endif -%} +{% if 'added' in seclabels and seclabels.added|length > 0 %} +{% for r in seclabels.added %} + +{{ SECLABEL.SET(conn, 'FUNCTION', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }} +{% endfor %} +{% endif -%} +{% if 'changed' in seclabels and seclabels.changed|length > 0 %} +{% for r in seclabels.changed %} + +{{ SECLABEL.SET(conn, 'FUNCTION', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }} +{% endfor %} +{% endif -%} +{% if data.description is defined and data.description != o_data.description%} + +COMMENT ON FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }}) + IS {{ data.description|qtLiteral }}; +{% endif -%} + +{% if data.pronamespace %} + +ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }}) + SET SCHEMA {{ conn|qtIdent(data.pronamespace) }}; +{% endif -%} + +{% endif %} 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 new file mode 100644 index 000000000..4441ef54c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/create.sql @@ -0,0 +1,55 @@ +{% 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 }}{% 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 }}{% endif %}{% endfor -%} +{% endif %} + +{% if data.is_pure_sql %}{{ data.prosrc }} +{% else %} +AS {% if data.lanname == 'c' %} +{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }} +{% else %} +$BODY${{ data.prosrc }}$BODY${% endif -%}; +{% 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 }}; +{% 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/14_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/properties.sql new file mode 100644 index 000000000..4dad586d5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/properties.sql @@ -0,0 +1,49 @@ +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, + 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, + ( + 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/14_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/update.sql new file mode 100644 index 000000000..a5f907dc8 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/pg/sql/14_plus/update.sql @@ -0,0 +1,114 @@ +{% 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 }} {% else %} + LANGUAGE {{ o_data.lanname|qtLiteral }} + {% endif %} +{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %}SECURITY DEFINER{% endif %} +{% if data.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 }}{% endif %}{% endfor -%} +{% endif %} + +{% if data.is_pure_sql %}{{ data.prosrc }}{% else %} +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 }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %} +$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %} +{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %} +$BODY${{ o_data.prosrc }}$BODY${% endif -%}; +{% endif -%} +{% 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 }}; +{% endif -%} +{% if data.pronamespace %} + +ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }} + SET SCHEMA {{ conn|qtIdent(data.pronamespace) }}; +{% 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 new file mode 100644 index 000000000..8aef2b230 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/create.sql @@ -0,0 +1,63 @@ +{% 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 }}{% if data.prosecdef %} + + SECURITY DEFINER {% endif %} +{% if data.lanname == 'edbspl' %} +{% if data.provolatile %}{% if data.provolatile == 'i' %}IMMUTABLE{% elif data.provolatile == 's' %}STABLE{% else %}VOLATILE{% endif %} {% endif %}{% if data.proleakproof %}LEAKPROOF {% endif %} +{% if data.proisstrict %}STRICT {% endif %} +{% if data.proparallel and (data.proparallel == 'r' or data.proparallel == 's' or data.proparallel == 'u') %} +{% if data.proparallel == 'r' %}PARALLEL RESTRICTED{% elif data.proparallel == 's' %}PARALLEL SAFE{% elif data.proparallel == 'u' %}PARALLEL UNSAFE{% endif %} {% endif %}{% if data.procost %} + + COST {{data.procost}}{% endif %}{% if data.prorows and (data.prorows | int) > 0 %} + + ROWS {{data.prorows}}{% endif -%}{% 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 }}{% endif %}{% endfor -%} +{% endif %} + +{% if data.is_pure_sql %}{{ data.prosrc }} +{% else %} +AS {% if data.lanname == 'c' %} +{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }} +{% else %} +$BODY${{ data.prosrc }}$BODY${% endif -%}; +{% 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 }}; +{% 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/14_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/properties.sql new file mode 100644 index 000000000..a1a81dcf5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/properties.sql @@ -0,0 +1,49 @@ +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, + 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, + ( + 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'::char + 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/14_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/update.sql new file mode 100644 index 000000000..6b2f589a6 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/templates/procedures/ppas/sql/14_plus/update.sql @@ -0,0 +1,124 @@ +{% 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 }} {% else %} + LANGUAGE {{ o_data.lanname|qtLiteral }} + {% endif %} +{% if 'provolatile' in data and data.provolatile %}{{ data.provolatile }} {% elif 'provolatile' not in data and o_data.provolatile %}{{ o_data.provolatile }} {% endif %} +{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %}SECURITY DEFINER{% endif %} +{% if data.lanname == 'edbspl' or (o_data.lanname == 'edbspl' and not 'lanname' in data ) %} +{% if ('proleakproof' in data and data.proleakproof) or ('proleakproof' not in data and o_data.proleakproof) %} LEAKPROOF{% else %} NOT LEAKPROOF{% endif %} + {% if ('proisstrict' in data and data.proisstrict) or ('proisstrict' not in data and o_data.proisstrict) %} STRICT{% endif %} + + {% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %} + + {% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows %} + + ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}}{% endif -%}{% if data.merged_variables %}{% for v in data.merged_variables %} + + SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral }}{% endif %}{% endfor -%} +{% endif %} +{% endif %} + +{% if data.is_pure_sql %}{{ data.prosrc }} +{% else %} +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 }}{% else %}{{ o_data.probin|qtLiteral }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral }}{% else %}{{ o_data.prosrc_c|qtLiteral }}{% endif %}{% elif 'prosrc' in data %} +$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %} +{{ o_data.probin|qtLiteral }}, {{ o_data.prosrc_c|qtLiteral }}{% else %} +$BODY${{ o_data.prosrc }}$BODY${% endif -%}; +{% endif -%} +{% 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 }}; +{% endif -%} +{% if data.pronamespace %} + +ALTER PROCEDURE {{ conn|qtIdent(o_data.pronamespace, name) }} + SET SCHEMA {{ conn|qtIdent(data.pronamespace) }}; +{% endif -%} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_func.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_func.msql new file mode 100644 index 000000000..13a004949 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_func.msql @@ -0,0 +1,12 @@ +CREATE FUNCTION public."Function4_$%{}[]()&*^!@""'`\/#"() + RETURNS numeric + LANGUAGE 'sql' + COST 100 + VOLATILE LEAKPROOF STRICT SECURITY DEFINER WINDOW PARALLEL UNSAFE + +begin atomic + select 1; +end; + +ALTER FUNCTION public."Function4_$%{}[]()&*^!@""'`\/#"() + OWNER TO postgres; 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 new file mode 100644 index 000000000..175b4b6aa --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_func.sql @@ -0,0 +1,17 @@ +-- FUNCTION: public.Function4_$%{}[]()&*^!@"'`\/#() + +-- DROP FUNCTION IF EXISTS public."Function4_$%{}[]()&*^!@""'`\/#"(); + +CREATE OR REPLACE FUNCTION public."Function4_$%{}[]()&*^!@""'`\/#"( + ) + RETURNS numeric + LANGUAGE 'sql' + COST 100 + VOLATILE LEAKPROOF STRICT SECURITY DEFINER WINDOW PARALLEL UNSAFE + +BEGIN ATOMIC + SELECT 1; +END; + +ALTER FUNCTION public."Function4_$%{}[]()&*^!@""'`\/#"() + OWNER TO postgres; 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 new file mode 100644 index 000000000..6d6399cb5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_proc.msql @@ -0,0 +1,10 @@ +CREATE PROCEDURE public."Proc3_$%{}[]()&*^!@"'`\/#"() +LANGUAGE 'sql' + SET application_name='demo' + +begin atomic +select 1; +end; + +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_atomic_proc.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_proc.sql new file mode 100644 index 000000000..2d2c176c4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/pg/14_plus/create_atomic_proc.sql @@ -0,0 +1,15 @@ +-- PROCEDURE: public.Proc3_$%{}[]()&*^!@"'`\/#() + +-- DROP PROCEDURE IF EXISTS public."Proc3_$%{}[]()&*^!@""'`\/#"(); + +CREATE OR REPLACE PROCEDURE public."Proc3_$%{}[]()&*^!@""'`\/#"( + ) +LANGUAGE 'sql' + SET application_name='demo' + +BEGIN ATOMIC + SELECT 1; +END; + +COMMENT ON PROCEDURE public."Proc3_$%{}[]()&*^!@""'`\/#"() + IS 'demo comments'; 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 9ff8bb6b6..4d1f216b5 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 @@ -300,6 +300,46 @@ "expected_sql_file": "alter_function_delete_acl.sql", "expected_msql_file": "alter_function_delete_acl.msql" }, + { + "type": "delete", + "name": "Drop function", + "endpoint": "NODE-function.delete_id", + "data": { + } + }, + { + "type": "create", + "endpoint": "NODE-function.obj", + "msql_endpoint": "NODE-function.msql", + "sql_endpoint": "NODE-function.sql_id", + "name": "Create function (atomic).", + "store_object_id": "True", + "data": { + "name": "Function4_$%{}[]()&*^!@\"'`\\/#", + "funcowner": "postgres", + "pronamespace": 2200, + "prorettypename": "numeric", + "lanname": "sql", + "prorows": 0, + "arguments": [], + "prosrc": "begin atomic\n select 1;\nend", + "probin": "$libdir/", + "options": [], + "variables": [], + "seclabels": [], + "prosecdef":true, + "acl": [], + "provolatile":"v", + "proisstrict":true, + "proiswindow":true, + "proparallel":"u", + "procost":"100", + "proleakproof":true, + "schema": "public" + }, + "expected_sql_file": "create_atomic_func.sql", + "expected_msql_file": "create_atomic_func.msql" + }, { "type": "delete", "name": "Drop function", 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 cf0996aab..00c37d4e1 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 @@ -131,6 +131,42 @@ "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": { + } + }, + { + "type": "create", + "name": "Create procedure (atomic)", + "endpoint": "NODE-procedure.obj", + "sql_endpoint": "NODE-procedure.sql_id", + "data": { + "name":"Proc3_$%{}[]()&*^!@\"'`\\/#", + "funcowner":"postgres", + "pronamespace":2200, + "description":"demo comments", + "lanname":"sql", + "provolatile":null, + "proisstrict":false, + "proparallel":null, + "procost":null, + "prorows":0, + "proleakproof":false, + "arguments":[], + "prosrc":"begin atomic\nselect 1;\nend", + "probin":"$libdir/", + "options":[], + "seclabels":[], + "acl":[], + "schema": "public", + "variables":[{"name":"application_name","value":"demo"}] + }, + "expected_sql_file": "create_atomic_proc.sql", + "expected_msql_file": "create_atomic_proc.msql" + }, { "type": "delete", "name": "Drop procedure", diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_func.msql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_func.msql new file mode 100644 index 000000000..9adb935cb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_func.msql @@ -0,0 +1,12 @@ +CREATE FUNCTION public."Function4_$%{}[]()&*^!@""'`\/#"() + RETURNS numeric + LANGUAGE 'sql' + COST 100 + VOLATILE LEAKPROOF STRICT SECURITY DEFINER WINDOW PARALLEL UNSAFE + +begin atomic + select 1; +end; + +ALTER FUNCTION public."Function4_$%{}[]()&*^!@""'`\/#"() + OWNER TO enterprisedb; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_func.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_func.sql new file mode 100644 index 000000000..bf017e28e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_func.sql @@ -0,0 +1,17 @@ +-- FUNCTION: public.Function4_$%{}[]()&*^!@"'`\/#() + +-- DROP FUNCTION IF EXISTS public."Function4_$%{}[]()&*^!@""'`\/#"(); + +CREATE OR REPLACE FUNCTION public."Function4_$%{}[]()&*^!@""'`\/#"( + ) + RETURNS numeric + LANGUAGE 'sql' + COST 100 + VOLATILE LEAKPROOF STRICT SECURITY DEFINER WINDOW PARALLEL UNSAFE + +BEGIN ATOMIC + SELECT 1; +END; + +ALTER FUNCTION public."Function4_$%{}[]()&*^!@""'`\/#"() + OWNER TO enterprisedb; 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 new file mode 100644 index 000000000..6d6399cb5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_proc.msql @@ -0,0 +1,10 @@ +CREATE PROCEDURE public."Proc3_$%{}[]()&*^!@"'`\/#"() +LANGUAGE 'sql' + SET application_name='demo' + +begin atomic +select 1; +end; + +COMMENT ON PROCEDURE public."Proc3_$%{}[]()&*^!@"'`\/#"() + IS 'demo comments'; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_proc.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_proc.sql new file mode 100644 index 000000000..2d2c176c4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/tests/ppas/14_plus/create_atomic_proc.sql @@ -0,0 +1,15 @@ +-- PROCEDURE: public.Proc3_$%{}[]()&*^!@"'`\/#() + +-- DROP PROCEDURE IF EXISTS public."Proc3_$%{}[]()&*^!@""'`\/#"(); + +CREATE OR REPLACE PROCEDURE public."Proc3_$%{}[]()&*^!@""'`\/#"( + ) +LANGUAGE 'sql' + SET application_name='demo' + +BEGIN ATOMIC + SELECT 1; +END; + +COMMENT ON PROCEDURE public."Proc3_$%{}[]()&*^!@""'`\/#"() + IS 'demo comments'; 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 22bf13586..1798b0431 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 @@ -312,6 +312,46 @@ "expected_sql_file": "alter_function_delete_acl.sql", "expected_msql_file": "alter_function_delete_acl.msql" }, + { + "type": "delete", + "name": "Drop function", + "endpoint": "NODE-function.delete_id", + "data": { + } + }, + { + "type": "create", + "endpoint": "NODE-function.obj", + "msql_endpoint": "NODE-function.msql", + "sql_endpoint": "NODE-function.sql_id", + "name": "Create function (atomic).", + "store_object_id": "True", + "data": { + "name": "Function4_$%{}[]()&*^!@\"'`\\/#", + "funcowner": "enterprisedb", + "pronamespace": 2200, + "prorettypename": "numeric", + "lanname": "sql", + "prorows": 0, + "arguments": [], + "prosrc": "begin atomic\n select 1;\nend", + "probin": "$libdir/", + "options": [], + "variables": [], + "seclabels": [], + "prosecdef":true, + "acl": [], + "provolatile":"v", + "proisstrict":true, + "proiswindow":true, + "proparallel":"u", + "procost":"100", + "proleakproof":true, + "schema": "public" + }, + "expected_sql_file": "create_atomic_func.sql", + "expected_msql_file": "create_atomic_func.msql" + }, { "type": "delete", "name": "Drop function", 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 ed8de0427..708d1d80e 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 @@ -131,6 +131,42 @@ "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": { + } + }, + { + "type": "create", + "name": "Create procedure (atomic)", + "endpoint": "NODE-procedure.obj", + "sql_endpoint": "NODE-procedure.sql_id", + "data": { + "name":"Proc3_$%{}[]()&*^!@\"'`\\/#", + "funcowner":"enterprisedb", + "pronamespace":2200, + "description":"demo comments", + "lanname":"sql", + "provolatile":null, + "proisstrict":false, + "proparallel":null, + "procost":null, + "prorows":0, + "proleakproof":false, + "arguments":[], + "prosrc":"begin atomic\nselect 1;\nend", + "probin":"$libdir/", + "options":[], + "seclabels":[], + "acl":[], + "schema": "public", + "variables":[{"name":"application_name","value":"demo"}] + }, + "expected_sql_file": "create_atomic_proc.sql", + "expected_msql_file": "create_atomic_proc.msql" + }, { "type": "delete", "name": "Drop procedure", diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js index 0c6b4f014..a0f12d53c 100644 --- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js +++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js @@ -2560,7 +2560,7 @@ define('tools.querytool', [ Notify.confirm( gettext('Connection Warning'), - '

'+ + '

'+ ''+ '

'+