Skip to content

Commit 2d7f136

Browse files
committed
Improve plpgsql parsing to report "foo is not a known variable", rather than a
generic syntax error, when seeing "foo := something" and foo isn't recognized. This buys back most of the helpfulness discarded in my previous patch by not throwing errors when a qualified name appears to match a row variable but the last component doesn't match any field of the row. It covers other cases where our error messages left something to be desired, too.
1 parent 01f7d29 commit 2d7f136

File tree

1 file changed

+68
-33
lines changed

1 file changed

+68
-33
lines changed

src/pl/plpgsql/src/gram.y

Lines changed: 68 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.138 2010/01/10 17:15:18 tgl Exp $
11+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.139 2010/01/10 17:56:50 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -56,7 +56,9 @@ union YYSTYPE; /* need forward reference for tok_is_keyword */
5656

5757
static bool tok_is_keyword(int token, union YYSTYPE *lval,
5858
int kw_token, const char *kw_str);
59-
static void token_is_not_variable(int tok);
59+
static void word_is_not_variable(PLword *word, int location);
60+
static void cword_is_not_variable(PLcword *cword, int location);
61+
static void current_token_is_not_variable(int tok);
6062
static PLpgSQL_expr *read_sql_construct(int until,
6163
int until2,
6264
int until3,
@@ -851,12 +853,12 @@ getdiag_target : T_DATUM
851853
| T_WORD
852854
{
853855
/* just to give a better message than "syntax error" */
854-
token_is_not_variable(T_WORD);
856+
word_is_not_variable(&($1), @1);
855857
}
856858
| T_CWORD
857859
{
858860
/* just to give a better message than "syntax error" */
859-
token_is_not_variable(T_CWORD);
861+
cword_is_not_variable(&($1), @1);
860862
}
861863
;
862864

@@ -1371,19 +1373,12 @@ for_variable : T_DATUM
13711373
tok = yylex();
13721374
plpgsql_push_back_token(tok);
13731375
if (tok == ',')
1374-
{
1375-
/* can't use token_is_not_variable here */
1376-
ereport(ERROR,
1377-
(errcode(ERRCODE_SYNTAX_ERROR),
1378-
errmsg("\"%s\" is not a known variable",
1379-
$1.ident),
1380-
parser_errposition(@1)));
1381-
}
1376+
word_is_not_variable(&($1), @1);
13821377
}
13831378
| T_CWORD
13841379
{
13851380
/* just to give a better message than "syntax error" */
1386-
token_is_not_variable(T_CWORD);
1381+
cword_is_not_variable(&($1), @1);
13871382
}
13881383
;
13891384

@@ -1587,15 +1582,38 @@ loop_body : proc_sect K_END K_LOOP opt_label ';'
15871582

15881583
/*
15891584
* T_WORD+T_CWORD match any initial identifier that is not a known plpgsql
1590-
* variable. The composite case is probably a syntax error, but we'll let
1591-
* the core parser decide that.
1585+
* variable. (The composite case is probably a syntax error, but we'll let
1586+
* the core parser decide that.) Normally, we should assume that such a
1587+
* word is a SQL statement keyword that isn't also a plpgsql keyword.
1588+
* However, if the next token is assignment or '[', it can't be a valid
1589+
* SQL statement, and what we're probably looking at is an intended variable
1590+
* assignment. Give an appropriate complaint for that, instead of letting
1591+
* the core parser throw an unhelpful "syntax error".
15921592
*/
15931593
stmt_execsql : K_INSERT
1594-
{ $$ = make_execsql_stmt(K_INSERT, @1); }
1594+
{
1595+
$$ = make_execsql_stmt(K_INSERT, @1);
1596+
}
15951597
| T_WORD
1596-
{ $$ = make_execsql_stmt(T_WORD, @1); }
1598+
{
1599+
int tok;
1600+
1601+
tok = yylex();
1602+
plpgsql_push_back_token(tok);
1603+
if (tok == '=' || tok == COLON_EQUALS || tok == '[')
1604+
word_is_not_variable(&($1), @1);
1605+
$$ = make_execsql_stmt(T_WORD, @1);
1606+
}
15971607
| T_CWORD
1598-
{ $$ = make_execsql_stmt(T_CWORD, @1); }
1608+
{
1609+
int tok;
1610+
1611+
tok = yylex();
1612+
plpgsql_push_back_token(tok);
1613+
if (tok == '=' || tok == COLON_EQUALS || tok == '[')
1614+
cword_is_not_variable(&($1), @1);
1615+
$$ = make_execsql_stmt(T_CWORD, @1);
1616+
}
15991617
;
16001618

16011619
stmt_dynexecute : K_EXECUTE
@@ -1793,12 +1811,12 @@ cursor_variable : T_DATUM
17931811
| T_WORD
17941812
{
17951813
/* just to give a better message than "syntax error" */
1796-
token_is_not_variable(T_WORD);
1814+
word_is_not_variable(&($1), @1);
17971815
}
17981816
| T_CWORD
17991817
{
18001818
/* just to give a better message than "syntax error" */
1801-
token_is_not_variable(T_CWORD);
1819+
cword_is_not_variable(&($1), @1);
18021820
}
18031821
;
18041822

@@ -2045,26 +2063,43 @@ tok_is_keyword(int token, union YYSTYPE *lval,
20452063
return false; /* not the keyword */
20462064
}
20472065

2066+
/*
2067+
* Convenience routine to complain when we expected T_DATUM and got T_WORD,
2068+
* ie, unrecognized variable.
2069+
*/
2070+
static void
2071+
word_is_not_variable(PLword *word, int location)
2072+
{
2073+
ereport(ERROR,
2074+
(errcode(ERRCODE_SYNTAX_ERROR),
2075+
errmsg("\"%s\" is not a known variable",
2076+
word->ident),
2077+
parser_errposition(location)));
2078+
}
2079+
2080+
/* Same, for a CWORD */
2081+
static void
2082+
cword_is_not_variable(PLcword *cword, int location)
2083+
{
2084+
ereport(ERROR,
2085+
(errcode(ERRCODE_SYNTAX_ERROR),
2086+
errmsg("\"%s\" is not a known variable",
2087+
NameListToString(cword->idents)),
2088+
parser_errposition(location)));
2089+
}
2090+
20482091
/*
20492092
* Convenience routine to complain when we expected T_DATUM and got
20502093
* something else. "tok" must be the current token, since we also
20512094
* look at yylval and yylloc.
20522095
*/
20532096
static void
2054-
token_is_not_variable(int tok)
2097+
current_token_is_not_variable(int tok)
20552098
{
20562099
if (tok == T_WORD)
2057-
ereport(ERROR,
2058-
(errcode(ERRCODE_SYNTAX_ERROR),
2059-
errmsg("\"%s\" is not a known variable",
2060-
yylval.word.ident),
2061-
parser_errposition(yylloc)));
2100+
word_is_not_variable(&(yylval.word), yylloc);
20622101
else if (tok == T_CWORD)
2063-
ereport(ERROR,
2064-
(errcode(ERRCODE_SYNTAX_ERROR),
2065-
errmsg("\"%s\" is not a known variable",
2066-
NameListToString(yylval.cword.idents)),
2067-
parser_errposition(yylloc)));
2102+
cword_is_not_variable(&(yylval.cword), yylloc);
20682103
else
20692104
yyerror("syntax error");
20702105
}
@@ -2848,7 +2883,7 @@ read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict)
28482883

28492884
default:
28502885
/* just to give a better message than "syntax error" */
2851-
token_is_not_variable(tok);
2886+
current_token_is_not_variable(tok);
28522887
}
28532888
}
28542889

@@ -2901,7 +2936,7 @@ read_into_scalar_list(char *initial_name,
29012936

29022937
default:
29032938
/* just to give a better message than "syntax error" */
2904-
token_is_not_variable(tok);
2939+
current_token_is_not_variable(tok);
29052940
}
29062941
}
29072942

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