diff -c -r --new-file pgsql/doc/src/sgml/plpgsql.sgml pgsql.00/doc/src/sgml/plpgsql.sgml *** pgsql/doc/src/sgml/plpgsql.sgml 2005-06-17 06:53:03.000000000 +0200 --- pgsql.00/doc/src/sgml/plpgsql.sgml 2005-06-17 10:00:15.000000000 +0200 *************** *** 1859,1864 **** --- 1859,1904 ---- + <literal>CONTINUE</> + + + CONTINUE label WHEN expression ; + + + + If no label is given, + the innermost loop is executed again (if all relevant + condition expression evaluates to true). If label + is given, it must be the label of the current or some outer level of nested loop + Then the named loop is evaluated again. + + + + If WHEN is present, loop continue occurs only if the specified + condition is true, otherwise control passes to the statement after + CONTINUE. + + + + CONTINUE can be used to cause early exit from all types of + loops; it is not limited to use with unconditional loops. + + + + Examples: + + LOOP + -- some computations + EXIT WHEN count > 100; + CONTINUE WHEN count < 50; + -- some computations for count IN [50 .. 100] + END LOOP; + + + + + + <literal>WHILE</> diff -c -r --new-file pgsql/src/pl/plpgsql/src/gram.y pgsql.00/src/pl/plpgsql/src/gram.y *** pgsql/src/pl/plpgsql/src/gram.y 2005-06-17 06:53:56.000000000 +0200 --- pgsql.00/src/pl/plpgsql/src/gram.y 2005-06-17 08:39:10.000000000 +0200 *************** *** 130,135 **** --- 130,137 ---- %type stmt_dynexecute stmt_getdiag %type stmt_open stmt_fetch stmt_close stmt_null + %type exit_type + %type proc_exceptions %type exception_sect %type proc_exception *************** *** 153,158 **** --- 155,161 ---- %token K_BEGIN %token K_CLOSE %token K_CONSTANT + %token K_CONTINUE %token K_CURSOR %token K_DEBUG %token K_DECLARE *************** *** 1035,1046 **** } ; ! 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; --- 1038,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->exit_type = $1; new->lineno = $2; new->label = $3; new->cond = $4; *************** *** 1049,1054 **** --- 1053,1068 ---- } ; + exit_type : K_EXIT + { + $$ = 0; + } + | K_CONTINUE + { + $$ = 1; + } + ; + stmt_return : K_RETURN lno { PLpgSQL_stmt_return *new; diff -c -r --new-file pgsql/src/pl/plpgsql/src/pl_exec.c pgsql.00/src/pl/plpgsql/src/pl_exec.c *** pgsql/src/pl/plpgsql/src/pl_exec.c 2005-06-17 06:53:56.000000000 +0200 --- pgsql.00/src/pl/plpgsql/src/pl_exec.c 2005-06-17 09:04:55.000000000 +0200 *************** *** 917,923 **** { case PLPGSQL_RC_OK: return PLPGSQL_RC_OK; ! case PLPGSQL_RC_EXIT: if (estate->exitlabel == NULL) return PLPGSQL_RC_OK; --- 917,923 ---- { case PLPGSQL_RC_OK: return PLPGSQL_RC_OK; ! case PLPGSQL_RC_EXIT: if (estate->exitlabel == NULL) return PLPGSQL_RC_OK; *************** *** 927,932 **** --- 927,935 ---- return PLPGSQL_RC_EXIT; estate->exitlabel = NULL; return PLPGSQL_RC_OK; + + case PLPGSQL_RC_CONTINUE: + return PLPGSQL_RC_CONTINUE; case PLPGSQL_RC_RETURN: return PLPGSQL_RC_RETURN; *************** *** 1210,1215 **** --- 1213,1228 ---- return PLPGSQL_RC_EXIT; estate->exitlabel = NULL; return PLPGSQL_RC_OK; + + case PLPGSQL_RC_CONTINUE: + if (estate->exitlabel == NULL) + break; + if (stmt->label == NULL) + return PLPGSQL_RC_CONTINUE; + if (strcmp(stmt->label, estate->exitlabel)) + return PLPGSQL_RC_CONTINUE; + estate->exitlabel = NULL; + break; case PLPGSQL_RC_RETURN: return PLPGSQL_RC_RETURN; *************** *** 1261,1266 **** --- 1274,1289 ---- estate->exitlabel = NULL; return PLPGSQL_RC_OK; + case PLPGSQL_RC_CONTINUE: + if (estate->exitlabel == NULL) + break; + if (stmt->label == NULL) + return PLPGSQL_RC_CONTINUE; + if (strcmp(stmt->label, estate->exitlabel)) + return PLPGSQL_RC_CONTINUE; + estate->exitlabel = NULL; + break; + case PLPGSQL_RC_RETURN: return PLPGSQL_RC_RETURN; *************** *** 1370,1376 **** --- 1393,1423 ---- break; } + else if (rc == PLPGSQL_RC_CONTINUE) + { + if (estate->exitlabel == NULL) + /* unlabelled continue, 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, we processed a labelled exit that does not match + * the current statement's label, if any: return RC_EXIT or RC_CONTINUE so + * that the EXIT|CONTINUE continues to recurse upward. + */ + rc = PLPGSQL_RC_CONTINUE; + break; + } + + } + /* * Increase/decrease loop var */ *************** *** 1457,1472 **** */ 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) --- 1504,1512 ---- */ rc = exec_stmts(estate, stmt->body); + if (rc != PLPGSQL_RC_OK) { if (rc == PLPGSQL_RC_EXIT) { if (estate->exitlabel == NULL) *************** *** 1487,1492 **** --- 1527,1554 ---- * 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; + } + rc = PLPGSQL_RC_CONTINUE; + } + + /* + * 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; } *************** *** 1604,1610 **** } estate->exitlabel = stmt->label; ! return PLPGSQL_RC_EXIT; } --- 1666,1672 ---- } estate->exitlabel = stmt->label; ! return stmt->exit_type == 0 ? PLPGSQL_RC_EXIT:PLPGSQL_RC_CONTINUE; } *************** *** 2433,2446 **** 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) --- 2495,2500 ---- *************** *** 2461,2466 **** --- 2515,2543 ---- * 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; + } + rc = PLPGSQL_RC_CONTINUE; + } + + /* + * 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; } diff -c -r --new-file pgsql/src/pl/plpgsql/src/pl_funcs.c pgsql.00/src/pl/plpgsql/src/pl_funcs.c *** pgsql/src/pl/plpgsql/src/pl_funcs.c 2005-06-17 06:53:56.000000000 +0200 --- pgsql.00/src/pl/plpgsql/src/pl_funcs.c 2005-06-17 09:38:43.000000000 +0200 *************** *** 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 lbl='%s'", stmt->exit_type == 0? "EXIT":"CONTINUE", ! stmt->label); if (stmt->cond != NULL) { printf(" WHEN "); diff -c -r --new-file pgsql/src/pl/plpgsql/src/plpgsql.h pgsql.00/src/pl/plpgsql/src/plpgsql.h *** pgsql/src/pl/plpgsql/src/plpgsql.h 2005-06-17 06:53:56.000000000 +0200 --- pgsql.00/src/pl/plpgsql/src/plpgsql.h 2005-06-17 07:33:34.000000000 +0200 *************** *** 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 }; /* ---------- *************** *** 488,493 **** --- 489,495 ---- { /* EXIT statement */ int cmd_type; int lineno; + int exit_type; /* exit or continue */ char *label; PLpgSQL_expr *cond; } PLpgSQL_stmt_exit; diff -c -r --new-file pgsql/src/pl/plpgsql/src/scan.l pgsql.00/src/pl/plpgsql/src/scan.l *** pgsql/src/pl/plpgsql/src/scan.l 2005-06-17 06:53:56.000000000 +0200 --- pgsql.00/src/pl/plpgsql/src/scan.l 2005-06-17 07:16:33.000000000 +0200 *************** *** 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; } diff -c -r --new-file pgsql/src/test/regress/expected/plpgsql.out pgsql.00/src/test/regress/expected/plpgsql.out *** pgsql/src/test/regress/expected/plpgsql.out 2005-06-17 06:54:08.000000000 +0200 --- pgsql.00/src/test/regress/expected/plpgsql.out 2005-06-17 09:46:06.000000000 +0200 *************** *** 2491,2493 **** --- 2491,2603 ---- (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" + CREATE TABLE + insert into conttesttbl(v) values(10); + INSERT 0 1 + insert into conttesttbl(v) values(20); + INSERT 0 1 + insert into conttesttbl(v) values(30); + INSERT 0 1 + insert into conttesttbl(v) values(40); + INSERT 0 1 + create or replace function conttest() 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---'; + <> + 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; + CREATE FUNCTION + select conttest(); + 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 + conttest + ---------- + + (1 row) + + drop table conttesttbl; + DROP TABLE + drop function conttest(); + DROP FUNCTION diff -c -r --new-file pgsql/src/test/regress/sql/plpgsql.sql pgsql.00/src/test/regress/sql/plpgsql.sql *** pgsql/src/test/regress/sql/plpgsql.sql 2005-06-17 06:54:06.000000000 +0200 --- pgsql.00/src/test/regress/sql/plpgsql.sql 2005-06-17 09:44:48.000000000 +0200 *************** *** 2112,2114 **** --- 2112,2172 ---- 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 or replace function conttest() 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---'; + <> + 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 conttest(); + + drop table conttesttbl; + drop function conttest();