9
9
*
10
10
*
11
11
* 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 $
13
13
*
14
14
*-------------------------------------------------------------------------
15
15
*/
@@ -48,6 +48,8 @@ static PLpgSQL_expr *read_sql_stmt(const char *sqlstart);
48
48
static PLpgSQL_type *read_datatype (int tok);
49
49
static PLpgSQL_stmt *make_execsql_stmt (const char *sqlstart, int lineno);
50
50
static PLpgSQL_stmt_fetch *read_fetch_direction (void );
51
+ static void complete_direction (PLpgSQL_stmt_fetch *fetch,
52
+ bool *check_FROM);
51
53
static PLpgSQL_stmt *make_return_stmt (int lineno);
52
54
static PLpgSQL_stmt *make_return_next_stmt (int lineno);
53
55
static PLpgSQL_stmt *make_return_query_stmt (int lineno);
@@ -178,6 +180,7 @@ static List *read_raise_options(void);
178
180
* Keyword tokens
179
181
*/
180
182
%token K_ALIAS
183
+ %token K_ALL
181
184
%token K_ASSIGN
182
185
%token K_BEGIN
183
186
%token K_BY
@@ -1622,6 +1625,15 @@ stmt_fetch : K_FETCH lno opt_fetch_direction cursor_variable K_INTO
1622
1625
if (yylex () != ' ;' )
1623
1626
yyerror (" syntax error" );
1624
1627
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
+
1625
1637
fetch->lineno = $2 ;
1626
1638
fetch->rec = rec;
1627
1639
fetch->row = row;
@@ -2252,6 +2264,9 @@ make_execsql_stmt(const char *sqlstart, int lineno)
2252
2264
}
2253
2265
2254
2266
2267
+ /*
2268
+ * Read FETCH or MOVE direction clause (everything through FROM/IN).
2269
+ */
2255
2270
static PLpgSQL_stmt_fetch *
2256
2271
read_fetch_direction (void )
2257
2272
{
@@ -2269,6 +2284,7 @@ read_fetch_direction(void)
2269
2284
fetch->direction = FETCH_FORWARD;
2270
2285
fetch->how_many = 1 ;
2271
2286
fetch->expr = NULL ;
2287
+ fetch->returns_multiple_rows = false ;
2272
2288
2273
2289
/*
2274
2290
* Most of the direction keywords are not plpgsql keywords, so we
@@ -2311,26 +2327,46 @@ read_fetch_direction(void)
2311
2327
NULL );
2312
2328
check_FROM = false ;
2313
2329
}
2330
+ else if (pg_strcasecmp (yytext, " all" ) == 0 )
2331
+ {
2332
+ fetch->how_many = FETCH_ALL;
2333
+ fetch->returns_multiple_rows = true ;
2334
+ }
2314
2335
else if (pg_strcasecmp (yytext, " forward" ) == 0 )
2315
2336
{
2316
- /* use defaults */
2337
+ complete_direction (fetch, &check_FROM);
2317
2338
}
2318
2339
else if (pg_strcasecmp (yytext, " backward" ) == 0 )
2319
2340
{
2320
2341
fetch->direction = FETCH_BACKWARD;
2342
+ complete_direction (fetch, &check_FROM);
2321
2343
}
2322
- else if (tok != T_SCALAR )
2344
+ else if (tok == K_FROM || tok == K_IN )
2323
2345
{
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 */
2324
2352
plpgsql_push_back_token (tok);
2325
- fetch->expr = read_sql_expression2 (K_FROM, K_IN,
2326
- " FROM or IN" ,
2327
- NULL );
2328
2353
check_FROM = false ;
2329
2354
}
2330
2355
else
2331
2356
{
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
+ */
2333
2365
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 ;
2334
2370
check_FROM = false ;
2335
2371
}
2336
2372
@@ -2345,6 +2381,43 @@ read_fetch_direction(void)
2345
2381
return fetch;
2346
2382
}
2347
2383
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
+
2348
2421
2349
2422
static PLpgSQL_stmt *
2350
2423
make_return_stmt (int lineno)
@@ -3043,11 +3116,11 @@ make_case(int lineno, PLpgSQL_expr *t_expr,
3043
3116
3044
3117
/* copy expression query without SELECT keyword (expr->query + 7) */
3045
3118
Assert (strncmp (expr->query , " SELECT " , 7 ) == 0 );
3046
-
3119
+
3047
3120
/* And do the string hacking */
3048
3121
initStringInfo (&ds);
3049
3122
3050
- appendStringInfo (&ds, " SELECT $%d IN(%s)" ,
3123
+ appendStringInfo (&ds, " SELECT $%d IN(%s)" ,
3051
3124
nparams + 1 ,
3052
3125
expr->query + 7 );
3053
3126
0 commit comments