Skip to content

Commit 4879a51

Browse files
committed
Support plpgsql variable names that conflict with unreserved SQL keywords.
A variable name matching a statement-introducing keyword, such as "comment" or "update", caused parse failures if one tried to write a statement using that keyword. Commit bb1b8f6 already addressed this scenario for the case of variable names matching unreserved plpgsql keywords, but we didn't think about unreserved core-grammar keywords. The same heuristic (viz, it can't be a variable name unless the next token is assignment or '[') should work fine for that case too, and as a bonus the code gets shorter and less duplicative. Per bug #15555 from Feike Steenbergen. Since this hasn't been complained of before, and is easily worked around anyway, I won't risk a back-patch. Discussion: https://postgr.es/m/15555-149bbd70ddc7b4b6@postgresql.org
1 parent cb719fa commit 4879a51

File tree

5 files changed

+75
-49
lines changed

5 files changed

+75
-49
lines changed

src/pl/plpgsql/src/pl_comp.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,24 +1353,27 @@ make_datum_param(PLpgSQL_expr *expr, int dno, int location)
13531353
* yytxt is the original token text; we need this to check for quoting,
13541354
* so that later checks for unreserved keywords work properly.
13551355
*
1356+
* We attempt to recognize the token as a variable only if lookup is true
1357+
* and the plpgsql_IdentifierLookup context permits it.
1358+
*
13561359
* If recognized as a variable, fill in *wdatum and return true;
13571360
* if not recognized, fill in *word and return false.
13581361
* (Note: those two pointers actually point to members of the same union,
13591362
* but for notational reasons we pass them separately.)
13601363
* ----------
13611364
*/
13621365
bool
1363-
plpgsql_parse_word(char *word1, const char *yytxt,
1366+
plpgsql_parse_word(char *word1, const char *yytxt, bool lookup,
13641367
PLwdatum *wdatum, PLword *word)
13651368
{
13661369
PLpgSQL_nsitem *ns;
13671370

13681371
/*
1369-
* We should do nothing in DECLARE sections. In SQL expressions, there's
1370-
* no need to do anything either --- lookup will happen when the
1371-
* expression is compiled.
1372+
* We should not lookup variables in DECLARE sections. In SQL
1373+
* expressions, there's no need to do so either --- lookup will happen
1374+
* when the expression is compiled.
13721375
*/
1373-
if (plpgsql_IdentifierLookup == IDENTIFIER_LOOKUP_NORMAL)
1376+
if (lookup && plpgsql_IdentifierLookup == IDENTIFIER_LOOKUP_NORMAL)
13741377
{
13751378
/*
13761379
* Do a lookup in the current namespace stack

src/pl/plpgsql/src/pl_scanner.c

Lines changed: 31 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ plpgsql_yylex(void)
328328
push_back_token(tok2, &aux2);
329329
if (plpgsql_parse_word(aux1.lval.str,
330330
core_yy.scanbuf + aux1.lloc,
331+
true,
331332
&aux1.lval.wdatum,
332333
&aux1.lval.word))
333334
tok1 = T_DATUM;
@@ -349,53 +350,40 @@ plpgsql_yylex(void)
349350
push_back_token(tok2, &aux2);
350351

351352
/*
352-
* If we are at start of statement, prefer unreserved keywords
353-
* over variable names, unless the next token is assignment or
354-
* '[', in which case prefer variable names. (Note we need not
355-
* consider '.' as the next token; that case was handled above,
356-
* and we always prefer variable names in that case.) If we are
357-
* not at start of statement, always prefer variable names over
358-
* unreserved keywords.
353+
* See if it matches a variable name, except in the context where
354+
* we are at start of statement and the next token isn't
355+
* assignment or '['. In that case, it couldn't validly be a
356+
* variable name, and skipping the lookup allows variable names to
357+
* be used that would conflict with plpgsql or core keywords that
358+
* introduce statements (e.g., "comment"). Without this special
359+
* logic, every statement-introducing keyword would effectively be
360+
* reserved in PL/pgSQL, which would be unpleasant.
361+
*
362+
* If it isn't a variable name, try to match against unreserved
363+
* plpgsql keywords. If not one of those either, it's T_WORD.
364+
*
365+
* Note: we must call plpgsql_parse_word even if we don't want to
366+
* do variable lookup, because it sets up aux1.lval.word for the
367+
* non-variable cases.
359368
*/
360-
if (AT_STMT_START(plpgsql_yytoken) &&
361-
!(tok2 == '=' || tok2 == COLON_EQUALS || tok2 == '['))
369+
if (plpgsql_parse_word(aux1.lval.str,
370+
core_yy.scanbuf + aux1.lloc,
371+
(!AT_STMT_START(plpgsql_yytoken) ||
372+
(tok2 == '=' || tok2 == COLON_EQUALS ||
373+
tok2 == '[')),
374+
&aux1.lval.wdatum,
375+
&aux1.lval.word))
376+
tok1 = T_DATUM;
377+
else if (!aux1.lval.word.quoted &&
378+
(kw = ScanKeywordLookup(aux1.lval.word.ident,
379+
unreserved_keywords,
380+
num_unreserved_keywords)))
362381
{
363-
/* try for unreserved keyword, then for variable name */
364-
if (core_yy.scanbuf[aux1.lloc] != '"' &&
365-
(kw = ScanKeywordLookup(aux1.lval.str,
366-
unreserved_keywords,
367-
num_unreserved_keywords)))
368-
{
369-
aux1.lval.keyword = kw->name;
370-
tok1 = kw->value;
371-
}
372-
else if (plpgsql_parse_word(aux1.lval.str,
373-
core_yy.scanbuf + aux1.lloc,
374-
&aux1.lval.wdatum,
375-
&aux1.lval.word))
376-
tok1 = T_DATUM;
377-
else
378-
tok1 = T_WORD;
382+
aux1.lval.keyword = kw->name;
383+
tok1 = kw->value;
379384
}
380385
else
381-
{
382-
/* try for variable name, then for unreserved keyword */
383-
if (plpgsql_parse_word(aux1.lval.str,
384-
core_yy.scanbuf + aux1.lloc,
385-
&aux1.lval.wdatum,
386-
&aux1.lval.word))
387-
tok1 = T_DATUM;
388-
else if (!aux1.lval.word.quoted &&
389-
(kw = ScanKeywordLookup(aux1.lval.word.ident,
390-
unreserved_keywords,
391-
num_unreserved_keywords)))
392-
{
393-
aux1.lval.keyword = kw->name;
394-
tok1 = kw->value;
395-
}
396-
else
397-
tok1 = T_WORD;
398-
}
386+
tok1 = T_WORD;
399387
}
400388
}
401389
else

src/pl/plpgsql/src/plpgsql.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1175,7 +1175,7 @@ extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo,
11751175
extern PLpgSQL_function *plpgsql_compile_inline(char *proc_source);
11761176
extern void plpgsql_parser_setup(struct ParseState *pstate,
11771177
PLpgSQL_expr *expr);
1178-
extern bool plpgsql_parse_word(char *word1, const char *yytxt,
1178+
extern bool plpgsql_parse_word(char *word1, const char *yytxt, bool lookup,
11791179
PLwdatum *wdatum, PLword *word);
11801180
extern bool plpgsql_parse_dblword(char *word1, char *word2,
11811181
PLwdatum *wdatum, PLcword *cword);

src/test/regress/expected/plpgsql.out

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4781,6 +4781,27 @@ select unreserved_test();
47814781
43
47824782
(1 row)
47834783

4784+
create or replace function unreserved_test() returns int as $$
4785+
declare
4786+
comment int := 21;
4787+
begin
4788+
comment := comment * 2;
4789+
comment on function unreserved_test() is 'this is a test';
4790+
return comment;
4791+
end
4792+
$$ language plpgsql;
4793+
select unreserved_test();
4794+
unreserved_test
4795+
-----------------
4796+
42
4797+
(1 row)
4798+
4799+
select obj_description('unreserved_test()'::regprocedure, 'pg_proc');
4800+
obj_description
4801+
-----------------
4802+
this is a test
4803+
(1 row)
4804+
47844805
drop function unreserved_test();
47854806
--
47864807
-- Test FOREACH over arrays

src/test/regress/sql/plpgsql.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3892,6 +3892,20 @@ $$ language plpgsql;
38923892
38933893
select unreserved_test();
38943894
3895+
create or replace function unreserved_test() returns int as $$
3896+
declare
3897+
comment int := 21;
3898+
begin
3899+
comment := comment * 2;
3900+
comment on function unreserved_test() is 'this is a test';
3901+
return comment;
3902+
end
3903+
$$ language plpgsql;
3904+
3905+
select unreserved_test();
3906+
3907+
select obj_description('unreserved_test()'::regprocedure, 'pg_proc');
3908+
38953909
drop function unreserved_test();
38963910
38973911
--

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy