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 <v...@postgresfriends.org> 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);