From: Neil Conway Date: Wed, 22 Jun 2005 07:28:47 +0000 (+0000) Subject: Fix bug in CONTINUE statement for PL/pgSQL: when we continue a loop, X-Git-Tag: REL8_1_0BETA1~494 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=738df437b277b0e5ed95e5a3bd3da9420839b9ec;p=postgresql Fix bug in CONTINUE statement for PL/pgSQL: when we continue a loop, we need to be careful to reset rc to PLPGSQL_RC_OK, depending on how the loop's logic is structured. If we continue a loop but it then exits without executing the loop's body again, we want to return PLPGSQL_RC_OK to our caller. Enhance the regression tests to catch this problem. Per report from Michael Fuhr. --- diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index d1ea2d843e..946a5b1c82 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.147 2005/06/22 01:35:02 neilc Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.148 2005/06/22 07:28:47 neilc Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -1216,11 +1216,9 @@ exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt) static int exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt) { - int rc; - for (;;) { - rc = exec_stmts(estate, stmt->body); + int rc = exec_stmts(estate, stmt->body); switch (rc) { @@ -1271,12 +1269,12 @@ exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt) static int exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt) { - bool value; - bool isnull; - int rc; - for (;;) { + int rc; + bool value; + bool isnull; + value = exec_eval_boolean(estate, stmt->cond, &isnull); exec_eval_cleanup(estate); @@ -1425,21 +1423,22 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt) else if (rc == PLPGSQL_RC_CONTINUE) { if (estate->exitlabel == NULL) - /* anonymous continue, so continue the current loop */ - ; + /* anonymous continue, so re-run the current loop */ + rc = PLPGSQL_RC_OK; else if (stmt->label != NULL && strcmp(stmt->label, estate->exitlabel) == 0) { - /* labelled continue, matches the current stmt's label */ + /* label matches named continue, so re-run loop */ estate->exitlabel = NULL; + rc = PLPGSQL_RC_OK; } 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. + * otherwise, this is a named 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; } @@ -1555,18 +1554,22 @@ exec_stmt_fors(PLpgSQL_execstate *estate, PLpgSQL_stmt_fors *stmt) else if (rc == PLPGSQL_RC_CONTINUE) { if (estate->exitlabel == NULL) - /* unlabelled continue, continue the current loop */ + { + /* anonymous continue, so re-run the current loop */ + rc = PLPGSQL_RC_OK; continue; + } else if (stmt->label != NULL && strcmp(stmt->label, estate->exitlabel) == 0) { - /* labelled continue, matches the current stmt's label */ + /* label matches named continue, so re-run loop */ + rc = PLPGSQL_RC_OK; estate->exitlabel = NULL; continue; } /* - * otherwise, we processed a labelled continue + * otherwise, we processed a named continue * that does not match the current statement's * label, if any: return RC_CONTINUE so that the * CONTINUE will propagate up the stack. @@ -2462,14 +2465,12 @@ static int exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt) { Datum query; - bool isnull = false; + bool isnull; Oid restype; char *querystr; PLpgSQL_rec *rec = NULL; PLpgSQL_row *row = NULL; SPITupleTable *tuptab; - int rc = PLPGSQL_RC_OK; - int i; int n; void *plan; Portal portal; @@ -2536,8 +2537,12 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt) */ while (n > 0) { + int i; + for (i = 0; i < n; i++) { + int rc; + /* * Assign the tuple to the target */ diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index 23ef2d3f2e..db4aefe614 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -2547,7 +2547,33 @@ begin for _r in execute 'select * from conttesttbl' loop continue when _r.v <= 20; raise notice '%', _r.v; - end loop; + end loop; + + raise notice '---7---'; + for _i in 1..3 loop + raise notice '%', _i; + continue when _i = 3; + end loop; + + raise notice '---8---'; + _i := 1; + while _i <= 3 loop + raise notice '%', _i; + _i := _i + 1; + continue when _i = 3; + end loop; + + raise notice '---9---'; + for _r in select * from conttesttbl order by v limit 1 loop + raise notice '%', _r.v; + continue; + end loop; + + raise notice '---10---'; + for _r in execute 'select * from conttesttbl order by v limit 1' loop + raise notice '%', _r.v; + continue; + end loop; end; $$ language plpgsql; select continue_test1(); NOTICE: ---1--- @@ -2591,6 +2617,18 @@ NOTICE: 40 NOTICE: ---6--- NOTICE: 30 NOTICE: 40 +NOTICE: ---7--- +NOTICE: 1 +NOTICE: 2 +NOTICE: 3 +NOTICE: ---8--- +NOTICE: 1 +NOTICE: 2 +NOTICE: 3 +NOTICE: ---9--- +NOTICE: 10 +NOTICE: ---10--- +NOTICE: 10 continue_test1 ---------------- diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index 393fdc52b0..22cc335e27 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -2169,7 +2169,33 @@ begin for _r in execute 'select * from conttesttbl' loop continue when _r.v <= 20; raise notice '%', _r.v; - end loop; + end loop; + + raise notice '---7---'; + for _i in 1..3 loop + raise notice '%', _i; + continue when _i = 3; + end loop; + + raise notice '---8---'; + _i := 1; + while _i <= 3 loop + raise notice '%', _i; + _i := _i + 1; + continue when _i = 3; + end loop; + + raise notice '---9---'; + for _r in select * from conttesttbl order by v limit 1 loop + raise notice '%', _r.v; + continue; + end loop; + + raise notice '---10---'; + for _r in execute 'select * from conttesttbl order by v limit 1' loop + raise notice '%', _r.v; + continue; + end loop; end; $$ language plpgsql; select continue_test1();