Re: Continue stmt for plpgsql - Mailing list pgsql-patches
From | Neil Conway |
---|---|
Subject | Re: Continue stmt for plpgsql |
Date | |
Msg-id | 42B7D507.8020600@samurai.com Whole thread Raw |
In response to | Continue stmt for plpgsql (Pavel Stehule <stehule@kix.fsv.cvut.cz>) |
Responses |
Re: Continue stmt for plpgsql
|
List | pgsql-patches |
Pavel Stehule wrote: > Attached patch provide continue stmt in plpgsql language. Continue stmt > start new step in any loop stmt. If stmt continue is in begin end block, > then find first outer loop (per recent discussion). Attached is a revised patch. I didn't see anything major that needed to be corrected, but there were some minor cleanups to be made. I also clarified the error message when a CONTINUE is specified outside a loop (although I wasn't quite sure what error message to use: SYNTAX_ERROR seems a bit weird, as the problem isn't detected until the end of the function's execution. I'm using PLPGSQL_ERROR at the moment, but suggestions for a better choice are welcome). Barring any objections, I'll apply this tomorrow. -Neil Index: doc/src/sgml/plpgsql.sgml =================================================================== RCS file: /var/lib/cvs/pgsql/doc/src/sgml/plpgsql.sgml,v retrieving revision 1.73 diff -c -r1.73 plpgsql.sgml *** doc/src/sgml/plpgsql.sgml 19 Jun 2005 23:39:05 -0000 1.73 --- doc/src/sgml/plpgsql.sgml 21 Jun 2005 08:46:27 -0000 *************** *** 1779,1788 **** </indexterm> <para> ! With the <literal>LOOP</>, <literal>EXIT</>, <literal>WHILE</>, ! and <literal>FOR</> statements, you can arrange for your ! <application>PL/pgSQL</application> function to repeat a series ! of commands. </para> <sect3> --- 1779,1788 ---- </indexterm> <para> ! With the <literal>LOOP</>, <literal>EXIT</>, ! <literal>CONTINUE</>, <literal>WHILE</>, and <literal>FOR</> ! statements, you can arrange for your <application>PL/pgSQL</> ! function to repeat a series of commands. </para> <sect3> *************** *** 1807,1836 **** <sect3> <title><literal>EXIT</></title> <synopsis> EXIT <optional> <replaceable>label</replaceable> </optional> <optional> WHEN <replaceable>expression</replaceable> </optional>; </synopsis> <para> ! If no <replaceable>label</replaceable> is given, ! the innermost loop is terminated and the ! statement following <literal>END LOOP</> is executed next. ! If <replaceable>label</replaceable> is given, it ! must be the label of the current or some outer level of nested loop ! or block. Then the named loop or block is terminated and control ! continues with the statement after the loop's/block's corresponding ! <literal>END</>. </para> <para> ! If <literal>WHEN</> is present, loop exit occurs only if the specified ! condition is true, otherwise control passes to the statement after ! <literal>EXIT</>. </para> <para> ! <literal>EXIT</> can be used to cause early exit from all types of ! loops; it is not limited to use with unconditional loops. </para> <para> --- 1807,1842 ---- <sect3> <title><literal>EXIT</></title> + <indexterm> + <primary>EXIT</primary> + <secondary>in PL/pgSQL</secondary> + </indexterm> + <synopsis> EXIT <optional> <replaceable>label</replaceable> </optional> <optional> WHEN <replaceable>expression</replaceable> </optional>; </synopsis> <para> ! If no <replaceable>label</replaceable> is given, the innermost ! loop is terminated and the statement following <literal>END ! LOOP</> is executed next. If <replaceable>label</replaceable> ! is given, it must be the label of the current or some outer ! level of nested loop or block. Then the named loop or block is ! terminated and control continues with the statement after the ! loop's/block's corresponding <literal>END</>. </para> <para> ! If <literal>WHEN</> is specified, the loop exit occurs only if ! <replaceable>expression</> is true. Otherwise, control passes ! to the statement after <literal>EXIT</>. </para> <para> ! <literal>EXIT</> can be used with all types of loops; it is ! not limited to use with unconditional loops. When used with a ! <literal>BEGIN</literal> block, <literal>EXIT</literal> passes ! control to the next statement after the end of the block. </para> <para> *************** *** 1859,1866 **** --- 1865,1924 ---- </sect3> <sect3> + <title><literal>CONTINUE</></title> + + <indexterm> + <primary>CONTINUE</primary> + <secondary>in PL/pgSQL</secondary> + </indexterm> + + <synopsis> + CONTINUE <optional> <replaceable>label</replaceable> </optional> <optional> WHEN <replaceable>expression</replaceable></optional>; + </synopsis> + + <para> + If no <replaceable>label</> is given, the next iteration of + the innermost loop is begun. That is, control is passed back + to the loop control expression (if any), and the body of the + loop is re-evaluated. If <replaceable>label</> is present, it + specifies the label of the loop whose execution will be + continued. + </para> + + <para> + If <literal>WHEN</> is specified, the next iteration of the + loop is begun only if <replaceable>expression</> is + true. Otherwise, control passes to the statement after + <literal>CONTINUE</>. + </para> + + <para> + <literal>CONTINUE</> can be used with all types of loops; it + is not limited to use with unconditional loops. + </para> + + <para> + Examples: + <programlisting> + LOOP + -- some computations + EXIT WHEN count > 100; + CONTINUE WHEN count < 50; + -- some computations for count IN [50 .. 100] + END LOOP; + </programlisting> + </para> + </sect3> + + + <sect3> <title><literal>WHILE</></title> + <indexterm> + <primary>WHILE</primary> + <secondary>in PL/pgSQL</secondary> + </indexterm> + <synopsis> <optional><<<replaceable>label</replaceable>>></optional> WHILE <replaceable>expression</replaceable> LOOP Index: src/pl/plpgsql/src/gram.y =================================================================== RCS file: /var/lib/cvs/pgsql/src/pl/plpgsql/src/gram.y,v retrieving revision 1.76 diff -c -r1.76 gram.y *** src/pl/plpgsql/src/gram.y 14 Jun 2005 06:43:14 -0000 1.76 --- src/pl/plpgsql/src/gram.y 21 Jun 2005 07:19:38 -0000 *************** *** 61,66 **** --- 61,67 ---- %union { int32 ival; + bool boolean; char *str; struct { *************** *** 100,106 **** %type <declhdr> decl_sect %type <varname> decl_varname %type <str> decl_renname ! %type <ival> decl_const decl_notnull %type <expr> decl_defval decl_cursor_query %type <dtype> decl_datatype %type <row> decl_cursor_args --- 101,107 ---- %type <declhdr> decl_sect %type <varname> decl_varname %type <str> decl_renname ! %type <boolean> decl_const decl_notnull exit_type %type <expr> decl_defval decl_cursor_query %type <dtype> decl_datatype %type <row> decl_cursor_args *************** *** 153,158 **** --- 154,160 ---- %token K_BEGIN %token K_CLOSE %token K_CONSTANT + %token K_CONTINUE %token K_CURSOR %token K_DEBUG %token K_DECLARE *************** *** 514,522 **** ; decl_const : ! { $$ = 0; } | K_CONSTANT ! { $$ = 1; } ; decl_datatype : --- 516,524 ---- ; decl_const : ! { $$ = false; } | K_CONSTANT ! { $$ = true; } ; decl_datatype : *************** *** 531,539 **** ; decl_notnull : ! { $$ = 0; } | K_NOT K_NULL ! { $$ = 1; } ; decl_defval : ';' --- 533,541 ---- ; decl_notnull : ! { $$ = false; } | K_NOT K_NULL ! { $$ = true; } ; decl_defval : ';' *************** *** 1035,1047 **** } ; ! stmt_exit : K_EXIT lno opt_exitlabel opt_exitcond { PLpgSQL_stmt_exit *new; new = palloc0(sizeof(PLpgSQL_stmt_exit)); new->cmd_type = PLPGSQL_STMT_EXIT; ! new->lineno = $2; new->label = $3; new->cond = $4; --- 1037,1050 ---- } ; ! stmt_exit : exit_type lno opt_exitlabel opt_exitcond { PLpgSQL_stmt_exit *new; new = palloc0(sizeof(PLpgSQL_stmt_exit)); new->cmd_type = PLPGSQL_STMT_EXIT; ! new->is_exit = $1; ! new->lineno = $2; new->label = $3; new->cond = $4; *************** *** 1049,1054 **** --- 1052,1067 ---- } ; + exit_type : K_EXIT + { + $$ = true; + } + | K_CONTINUE + { + $$ = false; + } + ; + stmt_return : K_RETURN lno { PLpgSQL_stmt_return *new; *************** *** 1056,1063 **** new = palloc0(sizeof(PLpgSQL_stmt_return)); new->cmd_type = PLPGSQL_STMT_RETURN; new->lineno = $2; ! new->expr = NULL; ! new->retvarno = -1; if (plpgsql_curr_compile->fn_retset) { --- 1069,1076 ---- new = palloc0(sizeof(PLpgSQL_stmt_return)); new->cmd_type = PLPGSQL_STMT_RETURN; new->lineno = $2; ! new->expr = NULL; ! new->retvarno = -1; if (plpgsql_curr_compile->fn_retset) { Index: src/pl/plpgsql/src/pl_exec.c =================================================================== RCS file: /var/lib/cvs/pgsql/src/pl/plpgsql/src/pl_exec.c,v retrieving revision 1.146 diff -c -r1.146 pl_exec.c *** src/pl/plpgsql/src/pl_exec.c 20 Jun 2005 22:51:29 -0000 1.146 --- src/pl/plpgsql/src/pl_exec.c 21 Jun 2005 08:01:39 -0000 *************** *** 194,199 **** --- 194,200 ---- PLpgSQL_execstate estate; ErrorContextCallback plerrcontext; int i; + int rc; /* * Setup the execution state *************** *** 282,294 **** */ estate.err_text = NULL; estate.err_stmt = (PLpgSQL_stmt *) (func->action); ! if (exec_stmt_block(&estate, func->action) != PLPGSQL_RC_RETURN) { estate.err_stmt = NULL; estate.err_text = NULL; ! ereport(ERROR, ! (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), ! errmsg("control reached end of function without RETURN"))); } /* --- 283,306 ---- */ estate.err_text = NULL; estate.err_stmt = (PLpgSQL_stmt *) (func->action); ! rc = exec_stmt_block(&estate, func->action); ! if (rc != PLPGSQL_RC_RETURN) { estate.err_stmt = NULL; estate.err_text = NULL; ! ! /* ! * Provide a more helpful message if a CONTINUE has been used ! * outside a loop. ! */ ! if (rc == PLPGSQL_RC_CONTINUE) ! ereport(ERROR, ! (errcode(ERRCODE_PLPGSQL_ERROR), /* XXX: sqlstate? */ ! errmsg("CONTINUE cannot be used outside a loop"))); ! else ! ereport(ERROR, ! (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), ! errmsg("control reached end of function without RETURN"))); } /* *************** *** 393,398 **** --- 405,411 ---- PLpgSQL_execstate estate; ErrorContextCallback plerrcontext; int i; + int rc; PLpgSQL_var *var; PLpgSQL_rec *rec_new, *rec_old; *************** *** 546,558 **** */ estate.err_text = NULL; estate.err_stmt = (PLpgSQL_stmt *) (func->action); ! if (exec_stmt_block(&estate, func->action) != PLPGSQL_RC_RETURN) { estate.err_stmt = NULL; estate.err_text = NULL; ! ereport(ERROR, ! (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), ! errmsg("control reached end of trigger procedure without RETURN"))); } if (estate.retisset) --- 559,582 ---- */ estate.err_text = NULL; estate.err_stmt = (PLpgSQL_stmt *) (func->action); ! rc = exec_stmt_block(&estate, func->action); ! if (rc != PLPGSQL_RC_RETURN) { estate.err_stmt = NULL; estate.err_text = NULL; ! ! /* ! * Provide a more helpful message if a CONTINUE has been used ! * outside a loop. ! */ ! if (rc == PLPGSQL_RC_CONTINUE) ! ereport(ERROR, ! (errcode(ERRCODE_PLPGSQL_ERROR), /* XXX: sqlstate? */ ! errmsg("CONTINUE cannot be used outside a loop"))); ! else ! ereport(ERROR, ! (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), ! errmsg("control reached end of trigger procedure without RETURN"))); } if (estate.retisset) *************** *** 919,925 **** switch (rc) { case PLPGSQL_RC_OK: ! return PLPGSQL_RC_OK; case PLPGSQL_RC_EXIT: if (estate->exitlabel == NULL) --- 943,951 ---- switch (rc) { case PLPGSQL_RC_OK: ! case PLPGSQL_RC_CONTINUE: ! case PLPGSQL_RC_RETURN: ! return rc; case PLPGSQL_RC_EXIT: if (estate->exitlabel == NULL) *************** *** 930,939 **** return PLPGSQL_RC_EXIT; estate->exitlabel = NULL; return PLPGSQL_RC_OK; ! ! case PLPGSQL_RC_RETURN: ! return PLPGSQL_RC_RETURN; ! default: elog(ERROR, "unrecognized rc: %d", rc); } --- 956,962 ---- return PLPGSQL_RC_EXIT; estate->exitlabel = NULL; return PLPGSQL_RC_OK; ! default: elog(ERROR, "unrecognized rc: %d", rc); } *************** *** 1120,1126 **** { PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc); PLpgSQL_datum *var; ! bool isnull = false; if (diag_item->target <= 0) continue; --- 1143,1149 ---- { PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc); PLpgSQL_datum *var; ! bool isnull; if (diag_item->target <= 0) continue; *************** *** 1165,1171 **** exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt) { bool value; ! bool isnull = false; value = exec_eval_boolean(estate, stmt->cond, &isnull); exec_eval_cleanup(estate); --- 1188,1194 ---- exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt) { bool value; ! bool isnull; value = exec_eval_boolean(estate, stmt->cond, &isnull); exec_eval_cleanup(estate); *************** *** 1209,1218 **** return PLPGSQL_RC_OK; if (stmt->label == NULL) return PLPGSQL_RC_EXIT; ! if (strcmp(stmt->label, estate->exitlabel)) return PLPGSQL_RC_EXIT; estate->exitlabel = NULL; return PLPGSQL_RC_OK; case PLPGSQL_RC_RETURN: return PLPGSQL_RC_RETURN; --- 1232,1254 ---- return PLPGSQL_RC_OK; if (stmt->label == NULL) return PLPGSQL_RC_EXIT; ! if (strcmp(stmt->label, estate->exitlabel) != 0) return PLPGSQL_RC_EXIT; estate->exitlabel = NULL; return PLPGSQL_RC_OK; + + case PLPGSQL_RC_CONTINUE: + if (estate->exitlabel == NULL) + /* anonymous continue, so re-run the loop */ + break; + else if (stmt->label != NULL && + strcmp(stmt->label, estate->exitlabel) == 0) + /* label matches named continue, so re-run loop */ + estate->exitlabel = NULL; + else + /* label doesn't match named continue, so propagate upward */ + return PLPGSQL_RC_CONTINUE; + break; case PLPGSQL_RC_RETURN: return PLPGSQL_RC_RETURN; *************** *** 1236,1242 **** exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt) { bool value; ! bool isnull = false; int rc; for (;;) --- 1272,1278 ---- exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt) { bool value; ! bool isnull; int rc; for (;;) *************** *** 1264,1269 **** --- 1300,1318 ---- estate->exitlabel = NULL; return PLPGSQL_RC_OK; + case PLPGSQL_RC_CONTINUE: + if (estate->exitlabel == NULL) + /* anonymous continue, so re-run loop */ + break; + else if (stmt->label != NULL && + strcmp(stmt->label, estate->exitlabel) == 0) + /* label matches named continue, so re-run loop */ + estate->exitlabel = NULL; + else + /* label doesn't match named continue, propagate upward */ + return PLPGSQL_RC_CONTINUE; + break; + case PLPGSQL_RC_RETURN: return PLPGSQL_RC_RETURN; *************** *** 1288,1294 **** PLpgSQL_var *var; Datum value; Oid valtype; ! bool isnull = false; bool found = false; int rc = PLPGSQL_RC_OK; --- 1337,1343 ---- PLpgSQL_var *var; Datum value; Oid valtype; ! bool isnull; bool found = false; int rc = PLPGSQL_RC_OK; *************** *** 1366,1378 **** } /* ! * otherwise, we processed a labelled exit that does not match ! * the current statement's label, if any: return RC_EXIT so ! * that the EXIT continues to recurse upward. */ break; } /* * Increase/decrease loop var --- 1415,1449 ---- } /* ! * otherwise, this is a labelled exit that does not match ! * the current statement's label, if any: return RC_EXIT ! * so that the EXIT continues to propagate up the stack. */ break; } + else if (rc == PLPGSQL_RC_CONTINUE) + { + if (estate->exitlabel == NULL) + /* anonymous continue, so continue the current loop */ + ; + else if (stmt->label != NULL && + strcmp(stmt->label, estate->exitlabel) == 0) + { + /* labelled continue, matches the current stmt's label */ + estate->exitlabel = NULL; + } + else + { + /* + * otherwise, this is a labelled continue that does + * not match the current statement's label, if any: + * return RC_CONTINUE so that the CONTINUE will + * propagate up the stack. + */ + break; + } + } /* * Increase/decrease loop var *************** *** 1460,1475 **** */ rc = exec_stmts(estate, stmt->body); if (rc != PLPGSQL_RC_OK) { - /* - * We're aborting the loop, so cleanup and set FOUND. - * (This code should match the code after the loop.) - */ - SPI_freetuptable(tuptab); - SPI_cursor_close(portal); - exec_set_found(estate, found); - if (rc == PLPGSQL_RC_EXIT) { if (estate->exitlabel == NULL) --- 1531,1539 ---- */ rc = exec_stmts(estate, stmt->body); + if (rc != PLPGSQL_RC_OK) { if (rc == PLPGSQL_RC_EXIT) { if (estate->exitlabel == NULL) *************** *** 1490,1495 **** --- 1554,1587 ---- * recurse upward. */ } + else if (rc == PLPGSQL_RC_CONTINUE) + { + if (estate->exitlabel == NULL) + /* unlabelled continue, continue the current loop */ + continue; + else if (stmt->label != NULL && + strcmp(stmt->label, estate->exitlabel) == 0) + { + /* labelled continue, matches the current stmt's label */ + estate->exitlabel = NULL; + continue; + } + + /* + * otherwise, we processed a labelled continue + * that does not match the current statement's + * label, if any: return RC_CONTINUE so that the + * CONTINUE will propagate up the stack. + */ + } + + /* + * We're aborting the loop, so cleanup and set FOUND. + * (This code should match the code after the loop.) + */ + SPI_freetuptable(tuptab); + SPI_cursor_close(portal); + exec_set_found(estate, found); return rc; } *************** *** 1563,1569 **** n = estate->eval_processed; /* ! * If the query didn't return any row, set the target to NULL and * return. */ if (n == 0) --- 1655,1661 ---- n = estate->eval_processed; /* ! * If the query didn't return any rows, set the target to NULL and * return. */ if (n == 0) *************** *** 1586,1613 **** /* ---------- ! * exec_stmt_exit Start exiting loop(s) or blocks * ---------- */ static int exec_stmt_exit(PLpgSQL_execstate *estate, PLpgSQL_stmt_exit *stmt) { /* ! * If the exit has a condition, check that it's true */ if (stmt->cond != NULL) { bool value; ! bool isnull = false; value = exec_eval_boolean(estate, stmt->cond, &isnull); exec_eval_cleanup(estate); ! if (isnull || !value) return PLPGSQL_RC_OK; } estate->exitlabel = stmt->label; ! return PLPGSQL_RC_EXIT; } --- 1678,1710 ---- /* ---------- ! * exec_stmt_exit Implements EXIT and CONTINUE ! * ! * This begins the process of exiting / restarting a loop. * ---------- */ static int exec_stmt_exit(PLpgSQL_execstate *estate, PLpgSQL_stmt_exit *stmt) { /* ! * If the exit / continue has a condition, evaluate it */ if (stmt->cond != NULL) { bool value; ! bool isnull; value = exec_eval_boolean(estate, stmt->cond, &isnull); exec_eval_cleanup(estate); ! if (isnull || value == false) return PLPGSQL_RC_OK; } estate->exitlabel = stmt->label; ! if (stmt->is_exit) ! return PLPGSQL_RC_EXIT; ! else ! return PLPGSQL_RC_CONTINUE; } *************** *** 2455,2468 **** if (rc != PLPGSQL_RC_OK) { - /* - * We're aborting the loop, so cleanup and set FOUND. - * (This code should match the code after the loop.) - */ - SPI_freetuptable(tuptab); - SPI_cursor_close(portal); - exec_set_found(estate, found); - if (rc == PLPGSQL_RC_EXIT) { if (estate->exitlabel == NULL) --- 2552,2557 ---- *************** *** 2483,2488 **** --- 2572,2604 ---- * recurse upward. */ } + else if (rc == PLPGSQL_RC_CONTINUE) + { + if (estate->exitlabel == NULL) + /* unlabelled continue, continue the current loop */ + continue; + else if (stmt->label != NULL && + strcmp(stmt->label, estate->exitlabel) == 0) + { + /* labelled continue, matches the current stmt's label */ + estate->exitlabel = NULL; + continue; + } + + /* + * otherwise, we process a labelled continue that + * does not match the current statement's label, + * so propagate RC_CONTINUE upward in the stack. + */ + } + + /* + * We're aborting the loop, so cleanup and set FOUND. + * (This code should match the code after the loop.) + */ + SPI_freetuptable(tuptab); + SPI_cursor_close(portal); + exec_set_found(estate, found); return rc; } Index: src/pl/plpgsql/src/pl_funcs.c =================================================================== RCS file: /var/lib/cvs/pgsql/src/pl/plpgsql/src/pl_funcs.c,v retrieving revision 1.44 diff -c -r1.44 pl_funcs.c *** src/pl/plpgsql/src/pl_funcs.c 19 Jun 2005 01:06:12 -0000 1.44 --- src/pl/plpgsql/src/pl_funcs.c 21 Jun 2005 07:19:38 -0000 *************** *** 845,851 **** dump_exit(PLpgSQL_stmt_exit *stmt) { dump_ind(); ! printf("EXIT lbl='%s'", stmt->label); if (stmt->cond != NULL) { printf(" WHEN "); --- 845,852 ---- dump_exit(PLpgSQL_stmt_exit *stmt) { dump_ind(); ! printf("%s label='%s'", ! stmt->is_exit ? "EXIT" : "CONTINUE", stmt->label); if (stmt->cond != NULL) { printf(" WHEN "); Index: src/pl/plpgsql/src/plpgsql.h =================================================================== RCS file: /var/lib/cvs/pgsql/src/pl/plpgsql/src/plpgsql.h,v retrieving revision 1.63 diff -c -r1.63 plpgsql.h *** src/pl/plpgsql/src/plpgsql.h 14 Jun 2005 06:43:14 -0000 1.63 --- src/pl/plpgsql/src/plpgsql.h 21 Jun 2005 07:19:38 -0000 *************** *** 125,131 **** { PLPGSQL_RC_OK, PLPGSQL_RC_EXIT, ! PLPGSQL_RC_RETURN }; /* ---------- --- 125,132 ---- { PLPGSQL_RC_OK, PLPGSQL_RC_EXIT, ! PLPGSQL_RC_RETURN, ! PLPGSQL_RC_CONTINUE }; /* ---------- *************** *** 485,493 **** typedef struct ! { /* EXIT statement */ int cmd_type; int lineno; char *label; PLpgSQL_expr *cond; } PLpgSQL_stmt_exit; --- 486,495 ---- typedef struct ! { /* EXIT or CONTINUE statement */ int cmd_type; int lineno; + bool is_exit; /* Is this an exit or a continue? */ char *label; PLpgSQL_expr *cond; } PLpgSQL_stmt_exit; *************** *** 610,616 **** bool readonly_func; TupleDesc rettupdesc; ! char *exitlabel; Tuplestorestate *tuple_store; /* SRFs accumulate results here */ MemoryContext tuple_store_cxt; --- 612,619 ---- bool readonly_func; TupleDesc rettupdesc; ! char *exitlabel; /* the "target" label of the current ! * EXIT or CONTINUE stmt, if any */ Tuplestorestate *tuple_store; /* SRFs accumulate results here */ MemoryContext tuple_store_cxt; Index: src/pl/plpgsql/src/scan.l =================================================================== RCS file: /var/lib/cvs/pgsql/src/pl/plpgsql/src/scan.l,v retrieving revision 1.40 diff -c -r1.40 scan.l *** src/pl/plpgsql/src/scan.l 11 Mar 2005 19:13:43 -0000 1.40 --- src/pl/plpgsql/src/scan.l 21 Jun 2005 07:19:38 -0000 *************** *** 139,144 **** --- 139,145 ---- begin { return K_BEGIN; } close { return K_CLOSE; } constant { return K_CONSTANT; } + continue { return K_CONTINUE; } cursor { return K_CURSOR; } debug { return K_DEBUG; } declare { return K_DECLARE; } Index: src/test/regress/expected/plpgsql.out =================================================================== RCS file: /var/lib/cvs/pgsql/src/test/regress/expected/plpgsql.out,v retrieving revision 1.34 diff -c -r1.34 plpgsql.out *** src/test/regress/expected/plpgsql.out 14 Jun 2005 06:43:14 -0000 1.34 --- src/test/regress/expected/plpgsql.out 21 Jun 2005 08:16:07 -0000 *************** *** 2491,2493 **** --- 2491,2608 ---- (1 row) drop function raise_exprs(); + -- continue statement + create table conttesttbl(idx serial, v integer); + NOTICE: CREATE TABLE will create implicit sequence "conttesttbl_idx_seq" for serial column "conttesttbl.idx" + insert into conttesttbl(v) values(10); + insert into conttesttbl(v) values(20); + insert into conttesttbl(v) values(30); + insert into conttesttbl(v) values(40); + create function continue_test1() returns void as $$ + declare _i integer = 0; _r record; + begin + raise notice '---1---'; + loop + _i := _i + 1; + raise notice '%', _i; + continue when _i < 10; + exit; + end loop; + raise notice '---2---'; + <<lbl>> + loop + _i := _i - 1; + loop + raise notice '%', _i; + continue lbl when _i > 0; + exit lbl; + end loop; + end loop; + raise notice '---3---'; + while _i < 10 loop + _i := _i + 1; + continue when _i % 2 = 0; + raise notice '%', _i; + end loop; + raise notice '---4---'; + for _i in 1..10 loop + begin + -- finfing outer loop + continue when _i < 5; + raise notice '%', _i; + end; + end loop; + raise notice '---5---'; + for _r in select * from conttesttbl loop + continue when _r.v <= 20; + raise notice '%', _r.v; + end loop; + raise notice '---6---'; + for _r in execute 'select * from conttesttbl' loop + continue when _r.v <= 20; + raise notice '%', _r.v; + end loop; + end; $$ language plpgsql; + select continue_test1(); + NOTICE: ---1--- + NOTICE: 1 + NOTICE: 2 + NOTICE: 3 + NOTICE: 4 + NOTICE: 5 + NOTICE: 6 + NOTICE: 7 + NOTICE: 8 + NOTICE: 9 + NOTICE: 10 + NOTICE: ---2--- + NOTICE: 9 + NOTICE: 8 + NOTICE: 7 + NOTICE: 6 + NOTICE: 5 + NOTICE: 4 + NOTICE: 3 + NOTICE: 2 + NOTICE: 1 + NOTICE: 0 + NOTICE: ---3--- + NOTICE: 1 + NOTICE: 3 + NOTICE: 5 + NOTICE: 7 + NOTICE: 9 + NOTICE: ---4--- + NOTICE: 5 + NOTICE: 6 + NOTICE: 7 + NOTICE: 8 + NOTICE: 9 + NOTICE: 10 + NOTICE: ---5--- + NOTICE: 30 + NOTICE: 40 + NOTICE: ---6--- + NOTICE: 30 + NOTICE: 40 + continue_test1 + ---------------- + + (1 row) + + -- CONTINUE is only legal inside a loop + create function continue_test2() returns void as $$ + begin + begin + continue; + end; + return; + end; + $$ language plpgsql; + -- should fail + select continue_test2(); + ERROR: CONTINUE cannot be used outside a loop + CONTEXT: PL/pgSQL function "continue_test2" + drop function continue_test1(); + drop function continue_test2(); + drop table conttesttbl; Index: src/test/regress/sql/plpgsql.sql =================================================================== RCS file: /var/lib/cvs/pgsql/src/test/regress/sql/plpgsql.sql,v retrieving revision 1.29 diff -c -r1.29 plpgsql.sql *** src/test/regress/sql/plpgsql.sql 14 Jun 2005 06:43:15 -0000 1.29 --- src/test/regress/sql/plpgsql.sql 21 Jun 2005 07:54:17 -0000 *************** *** 2112,2114 **** --- 2112,2186 ---- select raise_exprs(); drop function raise_exprs(); + + -- continue statement + create table conttesttbl(idx serial, v integer); + insert into conttesttbl(v) values(10); + insert into conttesttbl(v) values(20); + insert into conttesttbl(v) values(30); + insert into conttesttbl(v) values(40); + + create function continue_test1() returns void as $$ + declare _i integer = 0; _r record; + begin + raise notice '---1---'; + loop + _i := _i + 1; + raise notice '%', _i; + continue when _i < 10; + exit; + end loop; + raise notice '---2---'; + <<lbl>> + loop + _i := _i - 1; + loop + raise notice '%', _i; + continue lbl when _i > 0; + exit lbl; + end loop; + end loop; + raise notice '---3---'; + while _i < 10 loop + _i := _i + 1; + continue when _i % 2 = 0; + raise notice '%', _i; + end loop; + raise notice '---4---'; + for _i in 1..10 loop + begin + -- finfing outer loop + continue when _i < 5; + raise notice '%', _i; + end; + end loop; + raise notice '---5---'; + for _r in select * from conttesttbl loop + continue when _r.v <= 20; + raise notice '%', _r.v; + end loop; + raise notice '---6---'; + for _r in execute 'select * from conttesttbl' loop + continue when _r.v <= 20; + raise notice '%', _r.v; + end loop; + end; $$ language plpgsql; + + select continue_test1(); + + -- CONTINUE is only legal inside a loop + create function continue_test2() returns void as $$ + begin + begin + continue; + end; + return; + end; + $$ language plpgsql; + + -- should fail + select continue_test2(); + + drop function continue_test1(); + drop function continue_test2(); + drop table conttesttbl;
pgsql-patches by date: