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 @@ staticPLpgSQL_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
16221625if (yylex () !=' ;' )
16231626yyerror (" 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+
16251637fetch->lineno =$2 ;
16261638fetch->rec= rec;
16271639fetch->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)
22692284fetch->direction = FETCH_FORWARD;
22702285fetch->how_many =1 ;
22712286fetch->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)
23112327NULL );
23122328check_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+ }
23142335else if (pg_strcasecmp (yytext," forward" ) ==0 )
23152336{
2316- /* use defaults */
2337+ complete_direction (fetch, &check_FROM);
23172338}
23182339else if (pg_strcasecmp (yytext," backward" ) ==0 )
23192340{
23202341fetch->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*/
23242352plpgsql_push_back_token (tok);
2325- fetch->expr =read_sql_expression2 (K_FROM, K_IN,
2326- " FROM or IN" ,
2327- NULL );
23282353check_FROM =false ;
23292354}
23302355else
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+ */
23332365plpgsql_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 ;
23342370check_FROM =false ;
23352371}
23362372
@@ -2345,6 +2381,43 @@ read_fetch_direction(void)
23452381return 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)*/
30453118Assert (strncmp (expr->query ," SELECT" ,7 ) ==0 );
3046-
3119+
30473120/* And do the string hacking*/
30483121initStringInfo (&ds);
30493122
3050- appendStringInfo (&ds," SELECT $%d IN(%s)" ,
3123+ appendStringInfo (&ds," SELECT $%d IN(%s)" ,
30513124nparams +1 ,
30523125expr->query +7 );
30533126