OSDN Git Service

Fix plpgsql to not treat INSERT INTO as an INTO-variables clause anywhere
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 2 Feb 2009 20:25:38 +0000 (20:25 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 2 Feb 2009 20:25:38 +0000 (20:25 +0000)
in the string, not just at the start.  Per bug #4629 from Martin Blazek.

Back-patch to 8.2; prior versions don't have the problem, at least not in
the reported case, because they don't try to recognize INTO in non-SELECT
statements.  (IOW, this is really fallout from the RETURNING patch.)

src/pl/plpgsql/src/gram.y

index 7ae3536..f9a2eab 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.119 2009/01/07 13:44:37 tgl Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.120 2009/02/02 20:25:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -149,7 +149,7 @@ static List                         *read_raise_options(void);
 %type <loop_body>      loop_body
 %type <stmt>   proc_stmt pl_block
 %type <stmt>   stmt_assign stmt_if stmt_loop stmt_while stmt_exit
-%type <stmt>   stmt_return stmt_raise stmt_execsql stmt_execsql_insert
+%type <stmt>   stmt_return stmt_raise stmt_execsql
 %type <stmt>   stmt_dynexecute stmt_for stmt_perform stmt_getdiag
 %type <stmt>   stmt_open stmt_fetch stmt_move stmt_close stmt_null
 %type <stmt>   stmt_case
@@ -646,8 +646,6 @@ proc_stmt           : pl_block ';'
                                                { $$ = $1; }
                                | stmt_execsql
                                                { $$ = $1; }
-                               | stmt_execsql_insert
-                                               { $$ = $1; }
                                | stmt_dynexecute
                                                { $$ = $1; }
                                | stmt_perform
@@ -1482,27 +1480,15 @@ stmt_execsql    : execsql_start lno
                                        }
                                ;
 
-/* this matches any otherwise-unrecognized starting keyword */
-execsql_start  : T_WORD
+/* T_WORD+T_ERROR match any otherwise-unrecognized starting keyword */
+execsql_start  : K_INSERT
+                                       { $$ = pstrdup(yytext); }
+                               | T_WORD
                                        { $$ = pstrdup(yytext); }
                                | T_ERROR
                                        { $$ = pstrdup(yytext); }
                                ;
 
-stmt_execsql_insert : K_INSERT lno K_INTO
-                                       {
-                                               /*
-                                                * We have to special-case INSERT so that its INTO
-                                                * won't be treated as an INTO-variables clause.
-                                                *
-                                                * Fortunately, this is the only valid use of INTO
-                                                * in a pl/pgsql SQL command, and INTO is already
-                                                * a fully reserved word in the main grammar.
-                                                */
-                                               $$ = make_execsql_stmt("INSERT INTO", $2);
-                                       }
-                               ;
-
 stmt_dynexecute : K_EXECUTE lno
                                        {
                                                PLpgSQL_stmt_dynexecute *new;
@@ -2156,20 +2142,36 @@ make_execsql_stmt(const char *sqlstart, int lineno)
        PLpgSQL_row                     *row = NULL;
        PLpgSQL_rec                     *rec = NULL;
        int                                     tok;
+       int                                     prev_tok;
        bool                            have_into = false;
        bool                            have_strict = false;
 
        plpgsql_dstring_init(&ds);
        plpgsql_dstring_append(&ds, sqlstart);
 
+       /*
+        * We have to special-case the sequence INSERT INTO, because we don't want
+        * that to be taken as an INTO-variables clause.  Fortunately, this is the
+        * only valid use of INTO in a pl/pgsql SQL command, and INTO is already a
+        * fully reserved word in the main grammar.  We have to treat it that way
+        * anywhere in the string, not only at the start; consider CREATE RULE
+        * containing an INSERT statement.
+        */
+       if (pg_strcasecmp(sqlstart, "insert") == 0)
+               tok = K_INSERT;
+       else
+               tok = 0;
+
        for (;;)
        {
+               prev_tok = tok;
                tok = yylex();
                if (tok == ';')
                        break;
                if (tok == 0)
                        yyerror("unexpected end of function definition");
-               if (tok == K_INTO)
+
+               if (tok == K_INTO && prev_tok != K_INSERT)
                {
                        if (have_into)
                                yyerror("INTO specified more than once");