diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index 8e7097c9619160931d9beeac19dbcf8635a077e9..72735873c4e5336f076b7e450308a453b93a8ab7 100644 --- a/src/pl/plpgsql/src/gram.y +++ b/src/pl/plpgsql/src/gram.y @@ -4,7 +4,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.82 2005/10/13 15:34:19 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.83 2006/02/12 04:59:32 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -881,9 +881,15 @@ for_control : new->cmd_type = PLPGSQL_STMT_DYNFORS; new->lineno = $1; if ($2.rec) + { new->rec = $2.rec; + check_assignable((PLpgSQL_datum *) new->rec); + } else if ($2.row) + { new->row = $2.row; + check_assignable((PLpgSQL_datum *) new->row); + } else { plpgsql_error_lineno = $1; @@ -942,6 +948,7 @@ for_control : expr2 = plpgsql_read_expression(K_LOOP, "LOOP"); + /* create loop's private variable */ fvar = (PLpgSQL_var *) plpgsql_build_variable($2.name, $2.lineno, @@ -986,9 +993,15 @@ for_control : new->cmd_type = PLPGSQL_STMT_FORS; new->lineno = $1; if ($2.rec) + { new->rec = $2.rec; + check_assignable((PLpgSQL_datum *) new->rec); + } else if ($2.row) + { new->row = $2.row; + check_assignable((PLpgSQL_datum *) new->row); + } else { plpgsql_error_lineno = $1; @@ -1002,6 +1015,17 @@ for_control : } ; +/* + * Processing the for_variable is tricky because we don't yet know if the + * FOR is an integer FOR loop or a loop over query results. In the former + * case, the variable is just a name that we must instantiate as a loop + * local variable, regardless of any other definition it might have. + * Therefore, we always save the actual identifier into $$.name where it + * can be used for that case. We also save the outer-variable definition, + * if any, because that's what we need for the loop-over-query case. Note + * that we must NOT apply check_assignable() or any other semantic check + * until we know what's what. + */ for_variable : T_SCALAR { char *name; @@ -1304,13 +1328,13 @@ stmt_dynexecute : K_EXECUTE lno switch (yylex()) { case T_ROW: - check_assignable((PLpgSQL_datum *) yylval.row); new->row = yylval.row; + check_assignable((PLpgSQL_datum *) new->row); break; case T_RECORD: - check_assignable((PLpgSQL_datum *) yylval.row); new->rec = yylval.rec; + check_assignable((PLpgSQL_datum *) new->rec); break; case T_SCALAR: @@ -1917,11 +1941,13 @@ make_select_stmt(void) { case T_ROW: row = yylval.row; + check_assignable((PLpgSQL_datum *) row); have_into = true; break; case T_RECORD: rec = yylval.rec; + check_assignable((PLpgSQL_datum *) rec); have_into = true; break; @@ -2028,10 +2054,12 @@ make_fetch_stmt(void) { case T_ROW: row = yylval.row; + check_assignable((PLpgSQL_datum *) row); break; case T_RECORD: rec = yylval.rec; + check_assignable((PLpgSQL_datum *) rec); break; case T_SCALAR: @@ -2039,7 +2067,12 @@ make_fetch_stmt(void) break; default: - yyerror("syntax error"); + plpgsql_error_lineno = plpgsql_scanner_lineno(); + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("syntax error at \"%s\"", yytext), + errdetail("Expected record variable, row variable, " + "or list of scalar variables."))); } tok = yylex(); @@ -2136,13 +2169,13 @@ read_into_scalar_list(const char *initial_name, plpgsql_error_lineno = plpgsql_scanner_lineno(); ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("\"%s\" is not a variable", + errmsg("\"%s\" is not a scalar variable", yytext))); } } /* - * We read an extra, non-comma character from yylex(), so push it + * We read an extra, non-comma token from yylex(), so push it * back onto the input stream */ plpgsql_push_back_token(tok);