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 @@
*
*
* 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 */
static bool tok_is_keyword(int token, union YYSTYPE *lval,
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,
int until2,
int until3,
......@@ -851,12 +853,12 @@ getdiag_target : T_DATUM
| T_WORD
{
/* just to give a better message than "syntax error" */
token_is_not_variable(T_WORD);
word_is_not_variable(&($1), @1);
}
| T_CWORD
{
/* 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
tok = yylex();
plpgsql_push_back_token(tok);
if (tok == ',')
{
/* 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)));
}
word_is_not_variable(&($1), @1);
}
| T_CWORD
{
/* 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 ';'
/*
* 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
* the core parser decide that.
* variable. (The composite case is probably a syntax error, but we'll let
* 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
{ $$ = make_execsql_stmt(K_INSERT, @1); }
{
$$ = make_execsql_stmt(K_INSERT, @1);
}
| 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
{ $$ = 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
......@@ -1793,12 +1811,12 @@ cursor_variable : T_DATUM
| T_WORD
{
/* just to give a better message than "syntax error" */
token_is_not_variable(T_WORD);
word_is_not_variable(&($1), @1);
}
| T_CWORD
{
/* 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,
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
* something else. "tok" must be the current token, since we also
* look at yylval and yylloc.
*/
static void
token_is_not_variable(int tok)
current_token_is_not_variable(int tok)
{
if (tok == T_WORD)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a known variable",
yylval.word.ident),
parser_errposition(yylloc)));
word_is_not_variable(&(yylval.word), yylloc);
else if (tok == T_CWORD)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a known variable",
NameListToString(yylval.cword.idents)),
parser_errposition(yylloc)));
cword_is_not_variable(&(yylval.cword), yylloc);
else
yyerror("syntax error");
}
......@@ -2848,7 +2883,7 @@ read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict)
default:
/* 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,
default:
/* 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