Skip to content

Commit 91ca93a

Browse files
author
Commitfest Bot
committed
[CF 5722] v1 - Sanding down some edge cases for PL/pgSQL reserved words
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5722 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/[email protected] Author(s): Tom Lane
2 parents 92ee8a4 + adbe967 commit 91ca93a

File tree

7 files changed

+62
-14
lines changed

7 files changed

+62
-14
lines changed

src/pl/plpgsql/src/expected/plpgsql_misc.out

+22
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,25 @@ do $$ declare x public.foo%rowtype; begin end $$;
6565
ERROR: relation "public.foo" does not exist
6666
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1
6767
do $$ declare x public.misc_table%rowtype; begin end $$;
68+
-- Test handling of a reserved word as a record field name
69+
do $$ declare r record;
70+
begin
71+
select 1 as x, 2 as foreach into r;
72+
raise notice 'r.x = %', r.x;
73+
raise notice 'r.foreach = %', r.foreach;
74+
end $$;
75+
NOTICE: r.x = 1
76+
ERROR: field name "foreach" is a reserved key word
77+
LINE 1: r.foreach
78+
^
79+
HINT: Use double quotes to quote it.
80+
QUERY: r.foreach
81+
CONTEXT: PL/pgSQL function inline_code_block line 5 at RAISE
82+
do $$ declare r record;
83+
begin
84+
select 1 as x, 2 as foreach into r;
85+
raise notice 'r.x = %', r.x;
86+
raise notice 'r."foreach" = %', r."foreach";
87+
end $$;
88+
NOTICE: r.x = 1
89+
NOTICE: r."foreach" = 2

src/pl/plpgsql/src/pl_comp.c

+12-7
Original file line numberDiff line numberDiff line change
@@ -1201,17 +1201,22 @@ resolve_column_ref(ParseState *pstate, PLpgSQL_expr *expr,
12011201
}
12021202

12031203
/*
1204-
* We should not get here, because a RECFIELD datum should
1205-
* have been built at parse time for every possible qualified
1206-
* reference to fields of this record. But if we do, handle
1207-
* it like field-not-found: throw error or return NULL.
1204+
* Ideally we'd never get here, because a RECFIELD datum
1205+
* should have been built at parse time for every qualified
1206+
* reference to a field of this record that appears in the
1207+
* source text. However, plpgsql_yylex will not build such a
1208+
* datum unless the field name lexes as token type IDENT.
1209+
* Hence, if the would-be field name is a PL/pgSQL reserved
1210+
* word, we lose. Assume that that's what happened and tell
1211+
* the user to quote it, unless the caller prefers we just
1212+
* return NULL.
12081213
*/
12091214
if (error_if_no_field)
12101215
ereport(ERROR,
1211-
(errcode(ERRCODE_UNDEFINED_COLUMN),
1212-
errmsg("record \"%s\" has no field \"%s\"",
1213-
(nnames_field == 1) ? name1 : name2,
1216+
(errcode(ERRCODE_SYNTAX_ERROR),
1217+
errmsg("field name \"%s\" is a reserved key word",
12141218
colname),
1219+
errhint("Use double quotes to quote it."),
12151220
parser_errposition(pstate, cref->location)));
12161221
}
12171222
break;

src/pl/plpgsql/src/pl_gram.y

+9-4
Original file line numberDiff line numberDiff line change
@@ -1368,7 +1368,8 @@ for_control : for_variable K_IN
13681368
int tok = yylex(&yylval, &yylloc, yyscanner);
13691369
int tokloc = yylloc;
13701370

1371-
if (tok == K_EXECUTE)
1371+
if (tok_is_keyword(tok, &yylval,
1372+
K_EXECUTE, "execute"))
13721373
{
13731374
/* EXECUTE means it's a dynamic FOR loop */
13741375
PLpgSQL_stmt_dynfors *new;
@@ -2135,7 +2136,8 @@ stmt_open : K_OPEN cursor_variable
21352136
yyerror(&yylloc, NULL, yyscanner, "syntax error, expected \"FOR\"");
21362137

21372138
tok = yylex(&yylval, &yylloc, yyscanner);
2138-
if (tok == K_EXECUTE)
2139+
if (tok_is_keyword(tok, &yylval,
2140+
K_EXECUTE, "execute"))
21392141
{
21402142
int endtoken;
21412143

@@ -2536,6 +2538,7 @@ unreserved_keyword :
25362538
| K_ERRCODE
25372539
| K_ERROR
25382540
| K_EXCEPTION
2541+
| K_EXECUTE
25392542
| K_EXIT
25402543
| K_FETCH
25412544
| K_FIRST
@@ -2581,6 +2584,7 @@ unreserved_keyword :
25812584
| K_SLICE
25822585
| K_SQLSTATE
25832586
| K_STACKED
2587+
| K_STRICT
25842588
| K_TABLE
25852589
| K_TABLE_NAME
25862590
| K_TYPE
@@ -3514,7 +3518,8 @@ make_return_query_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_
35143518
new->stmtid = ++plpgsql_curr_compile->nstatements;
35153519

35163520
/* check for RETURN QUERY EXECUTE */
3517-
if ((tok = yylex(yylvalp, yyllocp, yyscanner)) != K_EXECUTE)
3521+
tok = yylex(yylvalp, yyllocp, yyscanner);
3522+
if (!tok_is_keyword(tok, yylvalp, K_EXECUTE, "execute"))
35183523
{
35193524
/* ordinary static query */
35203525
plpgsql_push_back_token(tok, yylvalp, yyllocp, yyscanner);
@@ -3597,7 +3602,7 @@ read_into_target(PLpgSQL_variable **target, bool *strict, YYSTYPE *yylvalp, YYLT
35973602
*strict = false;
35983603

35993604
tok = yylex(yylvalp, yyllocp, yyscanner);
3600-
if (strict && tok == K_STRICT)
3605+
if (strict && tok_is_keyword(tok, yylvalp, K_STRICT, "strict"))
36013606
{
36023607
*strict = true;
36033608
tok = yylex(yylvalp, yyllocp, yyscanner);

src/pl/plpgsql/src/pl_reserved_kwlist.h

-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ PG_KEYWORD("case", K_CASE)
3333
PG_KEYWORD("declare", K_DECLARE)
3434
PG_KEYWORD("else", K_ELSE)
3535
PG_KEYWORD("end", K_END)
36-
PG_KEYWORD("execute", K_EXECUTE)
3736
PG_KEYWORD("for", K_FOR)
3837
PG_KEYWORD("foreach", K_FOREACH)
3938
PG_KEYWORD("from", K_FROM)
@@ -44,7 +43,6 @@ PG_KEYWORD("loop", K_LOOP)
4443
PG_KEYWORD("not", K_NOT)
4544
PG_KEYWORD("null", K_NULL)
4645
PG_KEYWORD("or", K_OR)
47-
PG_KEYWORD("strict", K_STRICT)
4846
PG_KEYWORD("then", K_THEN)
4947
PG_KEYWORD("to", K_TO)
5048
PG_KEYWORD("using", K_USING)

src/pl/plpgsql/src/pl_scanner.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ IdentifierLookup plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
5353
* We try to avoid reserving more keywords than we have to; but there's
5454
* little point in not reserving a word if it's reserved in the core grammar.
5555
* Currently, the following words are reserved here but not in the core:
56-
* BEGIN BY DECLARE EXECUTE FOREACH IF LOOP STRICT WHILE
56+
* BEGIN BY DECLARE FOREACH IF LOOP WHILE
5757
*/
5858

5959
/* ScanKeywordList lookup data for PL/pgSQL keywords */

src/pl/plpgsql/src/pl_unreserved_kwlist.h

+2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ PG_KEYWORD("elsif", K_ELSIF)
5858
PG_KEYWORD("errcode", K_ERRCODE)
5959
PG_KEYWORD("error", K_ERROR)
6060
PG_KEYWORD("exception", K_EXCEPTION)
61+
PG_KEYWORD("execute", K_EXECUTE)
6162
PG_KEYWORD("exit", K_EXIT)
6263
PG_KEYWORD("fetch", K_FETCH)
6364
PG_KEYWORD("first", K_FIRST)
@@ -103,6 +104,7 @@ PG_KEYWORD("scroll", K_SCROLL)
103104
PG_KEYWORD("slice", K_SLICE)
104105
PG_KEYWORD("sqlstate", K_SQLSTATE)
105106
PG_KEYWORD("stacked", K_STACKED)
107+
PG_KEYWORD("strict", K_STRICT)
106108
PG_KEYWORD("table", K_TABLE)
107109
PG_KEYWORD("table_name", K_TABLE_NAME)
108110
PG_KEYWORD("type", K_TYPE)

src/pl/plpgsql/src/sql/plpgsql_misc.sql

+16
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,19 @@ do $$ declare x foo.bar%rowtype; begin end $$;
3737
do $$ declare x foo.bar.baz%rowtype; begin end $$;
3838
do $$ declare x public.foo%rowtype; begin end $$;
3939
do $$ declare x public.misc_table%rowtype; begin end $$;
40+
41+
-- Test handling of a reserved word as a record field name
42+
43+
do $$ declare r record;
44+
begin
45+
select 1 as x, 2 as foreach into r;
46+
raise notice 'r.x = %', r.x;
47+
raise notice 'r.foreach = %', r.foreach;
48+
end $$;
49+
50+
do $$ declare r record;
51+
begin
52+
select 1 as x, 2 as foreach into r;
53+
raise notice 'r.x = %', r.x;
54+
raise notice 'r."foreach" = %', r."foreach";
55+
end $$;

0 commit comments

Comments
 (0)