Commit 2d7f136f authored by Tom Lane's avatar Tom Lane

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.
parent 01f7d299
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.138 2010/01/10 17:15:18 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.139 2010/01/10 17:56:50 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -56,7 +56,9 @@ union YYSTYPE; /* need forward reference for tok_is_keyword */ ...@@ -56,7 +56,9 @@ union YYSTYPE; /* need forward reference for tok_is_keyword */
static bool tok_is_keyword(int token, union YYSTYPE *lval, static bool tok_is_keyword(int token, union YYSTYPE *lval,
int kw_token, const char *kw_str); int kw_token, const char *kw_str);
static void token_is_not_variable(int tok); static void word_is_not_variable(PLword *word, int location);
static void cword_is_not_variable(PLcword *cword, int location);
static void current_token_is_not_variable(int tok);
static PLpgSQL_expr *read_sql_construct(int until, static PLpgSQL_expr *read_sql_construct(int until,
int until2, int until2,
int until3, int until3,
...@@ -851,12 +853,12 @@ getdiag_target : T_DATUM ...@@ -851,12 +853,12 @@ getdiag_target : T_DATUM
| T_WORD | T_WORD
{ {
/* just to give a better message than "syntax error" */ /* just to give a better message than "syntax error" */
token_is_not_variable(T_WORD); word_is_not_variable(&($1), @1);
} }
| T_CWORD | T_CWORD
{ {
/* just to give a better message than "syntax error" */ /* just to give a better message than "syntax error" */
token_is_not_variable(T_CWORD); cword_is_not_variable(&($1), @1);
} }
; ;
...@@ -1371,19 +1373,12 @@ for_variable : T_DATUM ...@@ -1371,19 +1373,12 @@ for_variable : T_DATUM
tok = yylex(); tok = yylex();
plpgsql_push_back_token(tok); plpgsql_push_back_token(tok);
if (tok == ',') if (tok == ',')
{ word_is_not_variable(&($1), @1);
/* can't use token_is_not_variable here */
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a known variable",
$1.ident),
parser_errposition(@1)));
}
} }
| T_CWORD | T_CWORD
{ {
/* just to give a better message than "syntax error" */ /* just to give a better message than "syntax error" */
token_is_not_variable(T_CWORD); cword_is_not_variable(&($1), @1);
} }
; ;
...@@ -1587,15 +1582,38 @@ loop_body : proc_sect K_END K_LOOP opt_label ';' ...@@ -1587,15 +1582,38 @@ loop_body : proc_sect K_END K_LOOP opt_label ';'
/* /*
* T_WORD+T_CWORD match any initial identifier that is not a known plpgsql * T_WORD+T_CWORD match any initial identifier that is not a known plpgsql
* variable. The composite case is probably a syntax error, but we'll let * variable. (The composite case is probably a syntax error, but we'll let
* the core parser decide that. * the core parser decide that.) Normally, we should assume that such a
* word is a SQL statement keyword that isn't also a plpgsql keyword.
* However, if the next token is assignment or '[', it can't be a valid
* SQL statement, and what we're probably looking at is an intended variable
* assignment. Give an appropriate complaint for that, instead of letting
* the core parser throw an unhelpful "syntax error".
*/ */
stmt_execsql : K_INSERT stmt_execsql : K_INSERT
{ $$ = make_execsql_stmt(K_INSERT, @1); } {
$$ = make_execsql_stmt(K_INSERT, @1);
}
| T_WORD | T_WORD
{ $$ = make_execsql_stmt(T_WORD, @1); } {
int tok;
tok = yylex();
plpgsql_push_back_token(tok);
if (tok == '=' || tok == COLON_EQUALS || tok == '[')
word_is_not_variable(&($1), @1);
$$ = make_execsql_stmt(T_WORD, @1);
}
| T_CWORD | T_CWORD
{ $$ = make_execsql_stmt(T_CWORD, @1); } {
int tok;
tok = yylex();
plpgsql_push_back_token(tok);
if (tok == '=' || tok == COLON_EQUALS || tok == '[')
cword_is_not_variable(&($1), @1);
$$ = make_execsql_stmt(T_CWORD, @1);
}
; ;
stmt_dynexecute : K_EXECUTE stmt_dynexecute : K_EXECUTE
...@@ -1793,12 +1811,12 @@ cursor_variable : T_DATUM ...@@ -1793,12 +1811,12 @@ cursor_variable : T_DATUM
| T_WORD | T_WORD
{ {
/* just to give a better message than "syntax error" */ /* just to give a better message than "syntax error" */
token_is_not_variable(T_WORD); word_is_not_variable(&($1), @1);
} }
| T_CWORD | T_CWORD
{ {
/* just to give a better message than "syntax error" */ /* just to give a better message than "syntax error" */
token_is_not_variable(T_CWORD); cword_is_not_variable(&($1), @1);
} }
; ;
...@@ -2045,26 +2063,43 @@ tok_is_keyword(int token, union YYSTYPE *lval, ...@@ -2045,26 +2063,43 @@ tok_is_keyword(int token, union YYSTYPE *lval,
return false; /* not the keyword */ return false; /* not the keyword */
} }
/*
* Convenience routine to complain when we expected T_DATUM and got T_WORD,
* ie, unrecognized variable.
*/
static void
word_is_not_variable(PLword *word, int location)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a known variable",
word->ident),
parser_errposition(location)));
}
/* Same, for a CWORD */
static void
cword_is_not_variable(PLcword *cword, int location)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a known variable",
NameListToString(cword->idents)),
parser_errposition(location)));
}
/* /*
* Convenience routine to complain when we expected T_DATUM and got * Convenience routine to complain when we expected T_DATUM and got
* something else. "tok" must be the current token, since we also * something else. "tok" must be the current token, since we also
* look at yylval and yylloc. * look at yylval and yylloc.
*/ */
static void static void
token_is_not_variable(int tok) current_token_is_not_variable(int tok)
{ {
if (tok == T_WORD) if (tok == T_WORD)
ereport(ERROR, word_is_not_variable(&(yylval.word), yylloc);
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a known variable",
yylval.word.ident),
parser_errposition(yylloc)));
else if (tok == T_CWORD) else if (tok == T_CWORD)
ereport(ERROR, cword_is_not_variable(&(yylval.cword), yylloc);
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a known variable",
NameListToString(yylval.cword.idents)),
parser_errposition(yylloc)));
else else
yyerror("syntax error"); yyerror("syntax error");
} }
...@@ -2848,7 +2883,7 @@ read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict) ...@@ -2848,7 +2883,7 @@ read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict)
default: default:
/* just to give a better message than "syntax error" */ /* just to give a better message than "syntax error" */
token_is_not_variable(tok); current_token_is_not_variable(tok);
} }
} }
...@@ -2901,7 +2936,7 @@ read_into_scalar_list(char *initial_name, ...@@ -2901,7 +2936,7 @@ read_into_scalar_list(char *initial_name,
default: default:
/* just to give a better message than "syntax error" */ /* just to give a better message than "syntax error" */
token_is_not_variable(tok); current_token_is_not_variable(tok);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment