Hi
this is less invasive, and probably more correct work with ns items patch.
čt 19. 11. 2020 v 1:54 odesílatel Vik Fearing <[email protected]>
napsal:
> On 11/18/20 9:21 PM, Pavel Stehule wrote:
> > postgres=# create or replace function bubu(a int, b int)
> > returns void as $$
> > #routine_label b
> > begin
> > raise notice '% %', b.a, b.b;
> > end;
> > $$ language plpgsql;
>
> Why not use the block labeling syntax we already have?
>
> create or replace function bubu(a int, b int)
> returns void as $$
> << b >>
> begin
> raise notice '% %', b.a, b.b;
> end;
> $$ language plpgsql;
>
> That doesn't currently work, but it could be made to.
>
I don't think it is correct - in your example you are trying to merge two
different namespaces - so minimally it should allow duplicate names in one
namespace, or it breaks compatibility.
And I don't think so we want to lose information and separate access to
function's arguments.
Regards
Pavel
> --
> Vik Fearing
>
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 6df8e14629..b346753b76 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -376,6 +376,10 @@ do_compile(FunctionCallInfo fcinfo,
*/
plpgsql_ns_init();
plpgsql_ns_push(NameStr(procStruct->proname), PLPGSQL_LABEL_BLOCK);
+
+ /* save top ns for possibility to alter top label */
+ function->root_ns = plpgsql_ns_top();
+
plpgsql_DumpExecTree = false;
plpgsql_start_datums();
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index ee60ced583..60ff1e2eca 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -105,6 +105,29 @@ plpgsql_ns_additem(PLpgSQL_nsitem_type itemtype, int itemno, const char *name)
ns_top = nse;
}
+/*
+ * Replace ns item of label type by creating new entry and redirect
+ * old entry to new one.
+ */
+void
+plpgsql_ns_replace_root_label(PLpgSQL_nsitem *nse, const char *name)
+{
+ PLpgSQL_nsitem *new_nse;
+
+ Assert(name != NULL);
+ Assert(nse->itemtype == PLPGSQL_NSTYPE_LABEL &&
+ nse->itemno == (int) PLPGSQL_LABEL_BLOCK &&
+ nse->prev == NULL);
+
+ new_nse = palloc(offsetof(PLpgSQL_nsitem, name) + strlen(name) + 1);
+ new_nse->itemtype = nse->itemtype;
+ new_nse->itemno = nse->itemno;
+ new_nse->prev = NULL;
+ strcpy(new_nse->name, name);
+
+ nse->prev = new_nse;
+ nse->itemno = (int) PLPGSQL_LABEL_REPLACED;
+}
/* ----------
* plpgsql_ns_lookup Lookup an identifier in the given namespace chain
@@ -153,6 +176,14 @@ plpgsql_ns_lookup(PLpgSQL_nsitem *ns_cur, bool localmode,
}
}
+ if (nsitem->itemtype == PLPGSQL_NSTYPE_LABEL &&
+ nsitem->itemno == (int) PLPGSQL_LABEL_REPLACED)
+ {
+ Assert(nsitem->prev &&
+ nsitem->prev->itemtype == PLPGSQL_NSTYPE_LABEL);
+ nsitem = nsitem->prev;
+ }
+
/* Check this level for qualified match to variable name */
if (name2 != NULL &&
strcmp(nsitem->name, name1) == 0)
@@ -197,6 +228,7 @@ plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur, const char *name)
while (ns_cur != NULL)
{
if (ns_cur->itemtype == PLPGSQL_NSTYPE_LABEL &&
+ ns_cur->itemno != PLPGSQL_LABEL_REPLACED &&
strcmp(ns_cur->name, name) == 0)
return ns_cur;
ns_cur = ns_cur->prev;
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 8227bf0449..6eaf1dfee4 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -335,6 +335,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%token <keyword> K_RETURNED_SQLSTATE
%token <keyword> K_REVERSE
%token <keyword> K_ROLLBACK
+%token <keyword> K_ROUTINE_LABEL
%token <keyword> K_ROW_COUNT
%token <keyword> K_ROWTYPE
%token <keyword> K_SCHEMA
@@ -395,6 +396,11 @@ comp_option : '#' K_OPTION K_DUMP
{
plpgsql_curr_compile->resolve_option = PLPGSQL_RESOLVE_COLUMN;
}
+ | '#' K_ROUTINE_LABEL any_identifier
+ {
+ plpgsql_ns_replace_root_label(plpgsql_curr_compile->root_ns,
+ pstrdup($3));
+ }
;
option_value : T_WORD
diff --git a/src/pl/plpgsql/src/pl_unreserved_kwlist.h b/src/pl/plpgsql/src/pl_unreserved_kwlist.h
index 99b3cf7d8a..d6ed65b0a1 100644
--- a/src/pl/plpgsql/src/pl_unreserved_kwlist.h
+++ b/src/pl/plpgsql/src/pl_unreserved_kwlist.h
@@ -94,6 +94,7 @@ PG_KEYWORD("return", K_RETURN)
PG_KEYWORD("returned_sqlstate", K_RETURNED_SQLSTATE)
PG_KEYWORD("reverse", K_REVERSE)
PG_KEYWORD("rollback", K_ROLLBACK)
+PG_KEYWORD("routine_label", K_ROUTINE_LABEL)
PG_KEYWORD("row_count", K_ROW_COUNT)
PG_KEYWORD("rowtype", K_ROWTYPE)
PG_KEYWORD("schema", K_SCHEMA)
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 0c3d30fb13..6cba95a201 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -52,7 +52,8 @@ typedef enum PLpgSQL_label_type
{
PLPGSQL_LABEL_BLOCK, /* DECLARE/BEGIN block */
PLPGSQL_LABEL_LOOP, /* looping construct */
- PLPGSQL_LABEL_OTHER /* anything else */
+ PLPGSQL_LABEL_OTHER, /* anything else */
+ PLPGSQL_LABEL_REPLACED, /* replaced label */
} PLpgSQL_label_type;
/*
@@ -1037,6 +1038,9 @@ typedef struct PLpgSQL_function
/* these fields change when the function is used */
struct PLpgSQL_execstate *cur_estate;
unsigned long use_count;
+
+ /* routine level namespace entry */
+ struct PLpgSQL_nsitem *root_ns;
} PLpgSQL_function;
/*
@@ -1303,6 +1307,7 @@ extern void plpgsql_ns_push(const char *label,
extern void plpgsql_ns_pop(void);
extern PLpgSQL_nsitem *plpgsql_ns_top(void);
extern void plpgsql_ns_additem(PLpgSQL_nsitem_type itemtype, int itemno, const char *name);
+extern void plpgsql_ns_replace_root_label(PLpgSQL_nsitem *nse, const char *name);
extern PLpgSQL_nsitem *plpgsql_ns_lookup(PLpgSQL_nsitem *ns_cur, bool localmode,
const char *name1, const char *name2,
const char *name3, int *names_used);
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index d0a6b630b8..c049b568d3 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -5703,3 +5703,34 @@ END; $$ LANGUAGE plpgsql;
ERROR: "x" is not a scalar variable
LINE 3: GET DIAGNOSTICS x = ROW_COUNT;
^
+--
+-- Check root namespace renaming (routine_label option)
+--
+CREATE OR REPLACE FUNCTION test_root_namespace_rename(arg1 int)
+RETURNS void AS $$
+#ROUTINE_LABEL argsns
+BEGIN
+ RAISE NOTICE '% %', arg1, argsns.arg1;
+END;
+$$ LANGUAGE plpgsql;
+SELECT test_root_namespace_rename(10);
+NOTICE: 10 10
+ test_root_namespace_rename
+----------------------------
+
+(1 row)
+
+CREATE OR REPLACE FUNCTION test_root_namespace_rename(arg1 int)
+RETURNS void AS $$
+#ROUTINE_LABEL argsns
+BEGIN
+ -- should to fail, original name is overwritten
+ RAISE NOTICE '% %', arg1, test_root_namespace_rename.arg1;
+END;
+$$ LANGUAGE plpgsql;
+SELECT test_root_namespace_rename(10);
+ERROR: missing FROM-clause entry for table "test_root_namespace_rename"
+LINE 1: SELECT test_root_namespace_rename.arg1
+ ^
+QUERY: SELECT test_root_namespace_rename.arg1
+CONTEXT: PL/pgSQL function test_root_namespace_rename(integer) line 5 at RAISE
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 07c60c80e4..1ca34f8e50 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -4647,3 +4647,27 @@ BEGIN
GET DIAGNOSTICS x = ROW_COUNT;
RETURN;
END; $$ LANGUAGE plpgsql;
+
+--
+-- Check root namespace renaming (routine_label option)
+--
+CREATE OR REPLACE FUNCTION test_root_namespace_rename(arg1 int)
+RETURNS void AS $$
+#ROUTINE_LABEL argsns
+BEGIN
+ RAISE NOTICE '% %', arg1, argsns.arg1;
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT test_root_namespace_rename(10);
+
+CREATE OR REPLACE FUNCTION test_root_namespace_rename(arg1 int)
+RETURNS void AS $$
+#ROUTINE_LABEL argsns
+BEGIN
+ -- should to fail, original name is overwritten
+ RAISE NOTICE '% %', arg1, test_root_namespace_rename.arg1;
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT test_root_namespace_rename(10);