9
9
*
10
10
*
11
11
* IDENTIFICATION
12
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.109 2008/04/01 03:51:09 tgl Exp $
12
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.110 2008/04/06 23:43:29 tgl Exp $
13
13
*
14
14
*-------------------------------------------------------------------------
15
15
*/
@@ -50,6 +50,8 @@ static void plpgsql_sql_error_callback(void *arg);
50
50
static char *check_label (const char *yytxt);
51
51
static void check_labels (const char *start_label,
52
52
const char *end_label);
53
+ static PLpgSQL_expr *read_cursor_args (PLpgSQL_var *cursor,
54
+ int until, const char *expected);
53
55
54
56
%}
55
57
@@ -861,21 +863,15 @@ stmt_for : opt_block_label K_FOR for_control loop_body
861
863
new ->body = $4 .stmts;
862
864
$$ = (PLpgSQL_stmt *) new ;
863
865
}
864
- else if ($3 ->cmd_type == PLPGSQL_STMT_FORS)
865
- {
866
- PLpgSQL_stmt_fors *new ;
867
-
868
- new = (PLpgSQL_stmt_fors *) $3 ;
869
- new ->label = $1 ;
870
- new ->body = $4 .stmts;
871
- $$ = (PLpgSQL_stmt *) new ;
872
- }
873
866
else
874
867
{
875
- PLpgSQL_stmt_dynfors *new ;
868
+ PLpgSQL_stmt_forq *new ;
876
869
877
- Assert ($3 ->cmd_type == PLPGSQL_STMT_DYNFORS);
878
- new = (PLpgSQL_stmt_dynfors *) $3 ;
870
+ Assert ($3 ->cmd_type == PLPGSQL_STMT_FORS ||
871
+ $3 ->cmd_type == PLPGSQL_STMT_FORC ||
872
+ $3 ->cmd_type == PLPGSQL_STMT_DYNFORS);
873
+ /* forq is the common supertype of all three */
874
+ new = (PLpgSQL_stmt_forq *) $3 ;
879
875
new ->label = $1 ;
880
876
new ->body = $4 .stmts;
881
877
$$ = (PLpgSQL_stmt *) new ;
@@ -892,9 +888,9 @@ for_control :
892
888
{
893
889
int tok = yylex ();
894
890
895
- /* Simple case: EXECUTE is a dynamic FOR loop */
896
891
if (tok == K_EXECUTE)
897
892
{
893
+ /* EXECUTE means it's a dynamic FOR loop */
898
894
PLpgSQL_stmt_dynfors *new ;
899
895
PLpgSQL_expr *expr;
900
896
int term;
@@ -942,6 +938,47 @@ for_control :
942
938
943
939
$$ = (PLpgSQL_stmt *) new ;
944
940
}
941
+ else if (tok == T_SCALAR &&
942
+ yylval.scalar->dtype == PLPGSQL_DTYPE_VAR &&
943
+ ((PLpgSQL_var *) yylval.scalar)->datatype->typoid == REFCURSOROID)
944
+ {
945
+ /* It's FOR var IN cursor */
946
+ PLpgSQL_stmt_forc *new ;
947
+ PLpgSQL_var *cursor = (PLpgSQL_var *) yylval.scalar;
948
+ char *varname;
949
+
950
+ new = (PLpgSQL_stmt_forc *) palloc0(sizeof (PLpgSQL_stmt_forc));
951
+ new ->cmd_type = PLPGSQL_STMT_FORC;
952
+ new ->lineno = $1 ;
953
+
954
+ new ->curvar = cursor->varno;
955
+
956
+ /* Should have had a single variable name */
957
+ plpgsql_error_lineno = $2 .lineno;
958
+ if ($2 .scalar && $2 .row)
959
+ ereport (ERROR,
960
+ (errcode(ERRCODE_SYNTAX_ERROR),
961
+ errmsg(" cursor FOR loop must have just one target variable" )));
962
+
963
+ /* create loop's private RECORD variable */
964
+ plpgsql_convert_ident ($2 .name, &varname, 1 );
965
+ new ->rec = plpgsql_build_record(varname,
966
+ $2 .lineno,
967
+ true );
968
+
969
+ /* can't use an unbound cursor this way */
970
+ if (cursor->cursor_explicit_expr == NULL )
971
+ ereport (ERROR,
972
+ (errcode(ERRCODE_SYNTAX_ERROR),
973
+ errmsg(" cursor FOR loop must use a bound cursor variable" )));
974
+
975
+ /* collect cursor's parameters if any */
976
+ new ->argquery = read_cursor_args(cursor,
977
+ K_LOOP,
978
+ " LOOP" );
979
+
980
+ $$ = (PLpgSQL_stmt *) new ;
981
+ }
945
982
else
946
983
{
947
984
PLpgSQL_expr *expr1;
@@ -1412,81 +1449,8 @@ stmt_open : K_OPEN lno cursor_variable
1412
1449
}
1413
1450
else
1414
1451
{
1415
- if ($3 ->cursor_explicit_argrow >= 0 )
1416
- {
1417
- char *cp;
1418
-
1419
- tok = yylex ();
1420
- if (tok != ' (' )
1421
- {
1422
- plpgsql_error_lineno = plpgsql_scanner_lineno();
1423
- ereport (ERROR,
1424
- (errcode(ERRCODE_SYNTAX_ERROR),
1425
- errmsg(" cursor \" %s\" has arguments" ,
1426
- $3 ->refname)));
1427
- }
1428
-
1429
- /*
1430
- * Push back the '(', else read_sql_stmt
1431
- * will complain about unbalanced parens.
1432
- */
1433
- plpgsql_push_back_token (tok);
1434
-
1435
- new ->argquery = read_sql_stmt(" SELECT " );
1436
-
1437
- /*
1438
- * Now remove the leading and trailing parens,
1439
- * because we want "select 1, 2", not
1440
- * "select (1, 2)".
1441
- */
1442
- cp = new ->argquery->query;
1443
-
1444
- if (strncmp(cp, " SELECT" , 6 ) != 0 )
1445
- {
1446
- plpgsql_error_lineno = plpgsql_scanner_lineno();
1447
- /* internal error */
1448
- elog (ERROR, " expected \" SELECT (\" , got \" %s\" " ,
1449
- new ->argquery->query);
1450
- }
1451
- cp += 6 ;
1452
- while (*cp == ' ' ) /* could be more than 1 space here */
1453
- cp++;
1454
- if (*cp != ' (' )
1455
- {
1456
- plpgsql_error_lineno = plpgsql_scanner_lineno();
1457
- /* internal error */
1458
- elog (ERROR, " expected \" SELECT (\" , got \" %s\" " ,
1459
- new ->argquery->query);
1460
- }
1461
- *cp = ' ' ;
1462
-
1463
- cp += strlen(cp) - 1 ;
1464
-
1465
- if (*cp != ' )' )
1466
- yyerror (" expected \" )\" " );
1467
- *cp = ' \0 ' ;
1468
- }
1469
- else
1470
- {
1471
- tok = yylex ();
1472
- if (tok == ' (' )
1473
- {
1474
- plpgsql_error_lineno = plpgsql_scanner_lineno();
1475
- ereport (ERROR,
1476
- (errcode(ERRCODE_SYNTAX_ERROR),
1477
- errmsg(" cursor \" %s\" has no arguments" ,
1478
- $3 ->refname)));
1479
- }
1480
-
1481
- if (tok != ' ;' )
1482
- {
1483
- plpgsql_error_lineno = plpgsql_scanner_lineno();
1484
- ereport (ERROR,
1485
- (errcode(ERRCODE_SYNTAX_ERROR),
1486
- errmsg(" syntax error at \" %s\" " ,
1487
- yytext)));
1488
- }
1489
- }
1452
+ /* predefined cursor query, so read args */
1453
+ new ->argquery = read_cursor_args($3 , ' ;' , " ;" );
1490
1454
}
1491
1455
1492
1456
$$ = (PLpgSQL_stmt *)new ;
@@ -2578,6 +2542,97 @@ check_labels(const char *start_label, const char *end_label)
2578
2542
}
2579
2543
}
2580
2544
2545
+ /*
2546
+ * Read the arguments (if any) for a cursor, followed by the until token
2547
+ *
2548
+ * If cursor has no args, just swallow the until token and return NULL.
2549
+ * If it does have args, we expect to see "( expr [, expr ...] )" followed
2550
+ * by the until token. Consume all that and return a SELECT query that
2551
+ * evaluates the expression(s) (without the outer parens).
2552
+ */
2553
+ static PLpgSQL_expr *
2554
+ read_cursor_args (PLpgSQL_var *cursor, int until, const char *expected)
2555
+ {
2556
+ PLpgSQL_expr *expr;
2557
+ int tok;
2558
+ char *cp;
2559
+
2560
+ tok = yylex ();
2561
+ if (cursor->cursor_explicit_argrow < 0 )
2562
+ {
2563
+ /* No arguments expected */
2564
+ if (tok == ' (' )
2565
+ {
2566
+ plpgsql_error_lineno = plpgsql_scanner_lineno ();
2567
+ ereport (ERROR,
2568
+ (errcode (ERRCODE_SYNTAX_ERROR),
2569
+ errmsg (" cursor \" %s\" has no arguments" ,
2570
+ cursor->refname )));
2571
+ }
2572
+
2573
+ if (tok != until)
2574
+ {
2575
+ plpgsql_error_lineno = plpgsql_scanner_lineno ();
2576
+ ereport (ERROR,
2577
+ (errcode (ERRCODE_SYNTAX_ERROR),
2578
+ errmsg (" syntax error at \" %s\" " ,
2579
+ yytext)));
2580
+ }
2581
+
2582
+ return NULL ;
2583
+ }
2584
+
2585
+ /* Else better provide arguments */
2586
+ if (tok != ' (' )
2587
+ {
2588
+ plpgsql_error_lineno = plpgsql_scanner_lineno ();
2589
+ ereport (ERROR,
2590
+ (errcode (ERRCODE_SYNTAX_ERROR),
2591
+ errmsg (" cursor \" %s\" has arguments" ,
2592
+ cursor->refname )));
2593
+ }
2594
+
2595
+ /*
2596
+ * Push back the '(', else plpgsql_read_expression
2597
+ * will complain about unbalanced parens.
2598
+ */
2599
+ plpgsql_push_back_token (tok);
2600
+
2601
+ expr = plpgsql_read_expression (until, expected);
2602
+
2603
+ /*
2604
+ * Now remove the leading and trailing parens,
2605
+ * because we want "SELECT 1, 2", not "SELECT (1, 2)".
2606
+ */
2607
+ cp = expr->query ;
2608
+
2609
+ if (strncmp (cp, " SELECT" , 6 ) != 0 )
2610
+ {
2611
+ plpgsql_error_lineno = plpgsql_scanner_lineno ();
2612
+ /* internal error */
2613
+ elog (ERROR, " expected \" SELECT (\" , got \" %s\" " , expr->query );
2614
+ }
2615
+ cp += 6 ;
2616
+ while (*cp == ' ' ) /* could be more than 1 space here */
2617
+ cp++;
2618
+ if (*cp != ' (' )
2619
+ {
2620
+ plpgsql_error_lineno = plpgsql_scanner_lineno ();
2621
+ /* internal error */
2622
+ elog (ERROR, " expected \" SELECT (\" , got \" %s\" " , expr->query );
2623
+ }
2624
+ *cp = ' ' ;
2625
+
2626
+ cp += strlen (cp) - 1 ;
2627
+
2628
+ if (*cp != ' )' )
2629
+ yyerror (" expected \" )\" " );
2630
+ *cp = ' \0 ' ;
2631
+
2632
+ return expr;
2633
+ }
2634
+
2635
+
2581
2636
/* Needed to avoid conflict between different prefix settings: */
2582
2637
#undef yylex
2583
2638
0 commit comments