99 *
1010 *
1111 * IDENTIFICATION
12- * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.127 2009/07/22 02:31:38 joe Exp $
12+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.128 2009/09/29 20:05:29 tgl Exp $
1313 *
1414 *-------------------------------------------------------------------------
1515 */
@@ -48,6 +48,8 @@ static PLpgSQL_expr *read_sql_stmt(const char *sqlstart);
4848static PLpgSQL_type *read_datatype (int tok);
4949static PLpgSQL_stmt *make_execsql_stmt (const char *sqlstart, int lineno);
5050static PLpgSQL_stmt_fetch *read_fetch_direction (void );
51+ static void complete_direction (PLpgSQL_stmt_fetch *fetch,
52+ bool *check_FROM);
5153static PLpgSQL_stmt *make_return_stmt (int lineno);
5254static PLpgSQL_stmt *make_return_next_stmt (int lineno);
5355static PLpgSQL_stmt *make_return_query_stmt (int lineno);
@@ -178,6 +180,7 @@ static List *read_raise_options(void);
178180 * Keyword tokens
179181 */
180182%token K_ALIAS
183+ %token K_ALL
181184%token K_ASSIGN
182185%token K_BEGIN
183186%token K_BY
@@ -1622,6 +1625,15 @@ stmt_fetch : K_FETCH lno opt_fetch_direction cursor_variable K_INTO
16221625 if (yylex () != ' ;' )
16231626 yyerror (" syntax error" );
16241627
1628+ /*
1629+ * We don't allow multiple rows in PL/pgSQL's FETCH
1630+ * statement, only in MOVE.
1631+ */
1632+ if (fetch->returns_multiple_rows)
1633+ ereport (ERROR,
1634+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1635+ errmsg(" FETCH statement cannot return multiple rows" )));
1636+
16251637 fetch->lineno = $2 ;
16261638 fetch->rec = rec;
16271639 fetch->row = row;
@@ -2252,6 +2264,9 @@ make_execsql_stmt(const char *sqlstart, int lineno)
22522264}
22532265
22542266
2267+ /*
2268+ * Read FETCH or MOVE direction clause (everything through FROM/IN).
2269+ */
22552270static PLpgSQL_stmt_fetch *
22562271read_fetch_direction (void )
22572272{
@@ -2269,6 +2284,7 @@ read_fetch_direction(void)
22692284 fetch->direction = FETCH_FORWARD;
22702285 fetch->how_many = 1 ;
22712286 fetch->expr = NULL ;
2287+ fetch->returns_multiple_rows = false ;
22722288
22732289 /*
22742290 * Most of the direction keywords are not plpgsql keywords, so we
@@ -2311,26 +2327,46 @@ read_fetch_direction(void)
23112327 NULL );
23122328 check_FROM = false ;
23132329 }
2330+ else if (pg_strcasecmp (yytext, " all" ) == 0 )
2331+ {
2332+ fetch->how_many = FETCH_ALL;
2333+ fetch->returns_multiple_rows = true ;
2334+ }
23142335 else if (pg_strcasecmp (yytext, " forward" ) == 0 )
23152336 {
2316- /* use defaults */
2337+ complete_direction (fetch, &check_FROM);
23172338 }
23182339 else if (pg_strcasecmp (yytext, " backward" ) == 0 )
23192340 {
23202341 fetch->direction = FETCH_BACKWARD;
2342+ complete_direction (fetch, &check_FROM);
23212343 }
2322- else if (tok != T_SCALAR )
2344+ else if (tok == K_FROM || tok == K_IN )
23232345 {
2346+ /* empty direction */
2347+ check_FROM = false ;
2348+ }
2349+ else if (tok == T_SCALAR)
2350+ {
2351+ /* Assume there's no direction clause and tok is a cursor name */
23242352 plpgsql_push_back_token (tok);
2325- fetch->expr = read_sql_expression2 (K_FROM, K_IN,
2326- " FROM or IN" ,
2327- NULL );
23282353 check_FROM = false ;
23292354 }
23302355 else
23312356 {
2332- /* Assume there's no direction clause */
2357+ /*
2358+ * Assume it's a count expression with no preceding keyword.
2359+ * Note: we allow this syntax because core SQL does, but we don't
2360+ * document it because of the ambiguity with the omitted-direction
2361+ * case. For instance, "MOVE n IN c" will fail if n is a scalar.
2362+ * Perhaps this can be improved someday, but it's hardly worth a
2363+ * lot of work.
2364+ */
23332365 plpgsql_push_back_token (tok);
2366+ fetch->expr = read_sql_expression2 (K_FROM, K_IN,
2367+ " FROM or IN" ,
2368+ NULL );
2369+ fetch->returns_multiple_rows = true ;
23342370 check_FROM = false ;
23352371 }
23362372
@@ -2345,6 +2381,43 @@ read_fetch_direction(void)
23452381 return fetch;
23462382}
23472383
2384+ /*
2385+ * Process remainder of FETCH/MOVE direction after FORWARD or BACKWARD.
2386+ * Allows these cases:
2387+ * FORWARD expr, FORWARD ALL, FORWARD
2388+ * BACKWARD expr, BACKWARD ALL, BACKWARD
2389+ */
2390+ static void
2391+ complete_direction (PLpgSQL_stmt_fetch *fetch, bool *check_FROM)
2392+ {
2393+ int tok;
2394+
2395+ tok = yylex ();
2396+ if (tok == 0 )
2397+ yyerror (" unexpected end of function definition" );
2398+
2399+ if (tok == K_FROM || tok == K_IN)
2400+ {
2401+ *check_FROM = false ;
2402+ return ;
2403+ }
2404+
2405+ if (tok == K_ALL)
2406+ {
2407+ fetch->how_many = FETCH_ALL;
2408+ fetch->returns_multiple_rows = true ;
2409+ *check_FROM = true ;
2410+ return ;
2411+ }
2412+
2413+ plpgsql_push_back_token (tok);
2414+ fetch->expr = read_sql_expression2 (K_FROM, K_IN,
2415+ " FROM or IN" ,
2416+ NULL );
2417+ fetch->returns_multiple_rows = true ;
2418+ *check_FROM = false ;
2419+ }
2420+
23482421
23492422static PLpgSQL_stmt *
23502423make_return_stmt (int lineno)
@@ -3043,11 +3116,11 @@ make_case(int lineno, PLpgSQL_expr *t_expr,
30433116
30443117 /* copy expression query without SELECT keyword (expr->query + 7) */
30453118 Assert (strncmp (expr->query , " SELECT " , 7 ) == 0 );
3046-
3119+
30473120 /* And do the string hacking */
30483121 initStringInfo (&ds);
30493122
3050- appendStringInfo (&ds, " SELECT $%d IN(%s)" ,
3123+ appendStringInfo (&ds, " SELECT $%d IN(%s)" ,
30513124 nparams + 1 ,
30523125 expr->query + 7 );
30533126
0 commit comments