]> granicus.if.org Git - postgresql/commitdiff
Improve plpgsql parsing to report "foo is not a known variable", rather than a
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 10 Jan 2010 17:56:50 +0000 (17:56 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 10 Jan 2010 17:56:50 +0000 (17:56 +0000)
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.

src/pl/plpgsql/src/gram.y

index 77afcbec8bb5ed4d82fef20eed3ff1525d9ea4e7..c4e3c128346ba05ee05ee91ad4caa562fe44ec68 100644 (file)
@@ -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);
                }
        }