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.
This commit is contained in:
Neil Conway 2005-06-22 07:28:47 +00:00
parent 05db8b501b
commit 738df437b2
3 changed files with 92 additions and 23 deletions

View File

@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * 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. * This software is copyrighted by Jan Wieck - Hamburg.
* *
@ -1216,11 +1216,9 @@ exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt)
static int static int
exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt) exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt)
{ {
int rc;
for (;;) for (;;)
{ {
rc = exec_stmts(estate, stmt->body); int rc = exec_stmts(estate, stmt->body);
switch (rc) switch (rc)
{ {
@ -1271,12 +1269,12 @@ exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt)
static int static int
exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt) exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
{ {
bool value;
bool isnull;
int rc;
for (;;) for (;;)
{ {
int rc;
bool value;
bool isnull;
value = exec_eval_boolean(estate, stmt->cond, &isnull); value = exec_eval_boolean(estate, stmt->cond, &isnull);
exec_eval_cleanup(estate); exec_eval_cleanup(estate);
@ -1425,21 +1423,22 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
else if (rc == PLPGSQL_RC_CONTINUE) else if (rc == PLPGSQL_RC_CONTINUE)
{ {
if (estate->exitlabel == NULL) 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 && else if (stmt->label != NULL &&
strcmp(stmt->label, estate->exitlabel) == 0) 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; estate->exitlabel = NULL;
rc = PLPGSQL_RC_OK;
} }
else else
{ {
/* /*
* otherwise, this is a labelled continue that does * otherwise, this is a named continue that does not
* not match the current statement's label, if any: * match the current statement's label, if any: return
* return RC_CONTINUE so that the CONTINUE will * RC_CONTINUE so that the CONTINUE will propagate up
* propagate up the stack. * the stack.
*/ */
break; break;
} }
@ -1555,18 +1554,22 @@ exec_stmt_fors(PLpgSQL_execstate *estate, PLpgSQL_stmt_fors *stmt)
else if (rc == PLPGSQL_RC_CONTINUE) else if (rc == PLPGSQL_RC_CONTINUE)
{ {
if (estate->exitlabel == NULL) if (estate->exitlabel == NULL)
/* unlabelled continue, continue the current loop */ {
/* anonymous continue, so re-run the current loop */
rc = PLPGSQL_RC_OK;
continue; continue;
}
else if (stmt->label != NULL && else if (stmt->label != NULL &&
strcmp(stmt->label, estate->exitlabel) == 0) 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; estate->exitlabel = NULL;
continue; continue;
} }
/* /*
* otherwise, we processed a labelled continue * otherwise, we processed a named continue
* that does not match the current statement's * that does not match the current statement's
* label, if any: return RC_CONTINUE so that the * label, if any: return RC_CONTINUE so that the
* CONTINUE will propagate up the stack. * CONTINUE will propagate up the stack.
@ -2462,14 +2465,12 @@ static int
exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt) exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt)
{ {
Datum query; Datum query;
bool isnull = false; bool isnull;
Oid restype; Oid restype;
char *querystr; char *querystr;
PLpgSQL_rec *rec = NULL; PLpgSQL_rec *rec = NULL;
PLpgSQL_row *row = NULL; PLpgSQL_row *row = NULL;
SPITupleTable *tuptab; SPITupleTable *tuptab;
int rc = PLPGSQL_RC_OK;
int i;
int n; int n;
void *plan; void *plan;
Portal portal; Portal portal;
@ -2536,8 +2537,12 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt)
*/ */
while (n > 0) while (n > 0)
{ {
int i;
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
{ {
int rc;
/* /*
* Assign the tuple to the target * Assign the tuple to the target
*/ */

View File

@ -2548,6 +2548,32 @@ begin
continue when _r.v <= 20; continue when _r.v <= 20;
raise notice '%', _r.v; 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; end; $$ language plpgsql;
select continue_test1(); select continue_test1();
NOTICE: ---1--- NOTICE: ---1---
@ -2591,6 +2617,18 @@ NOTICE: 40
NOTICE: ---6--- NOTICE: ---6---
NOTICE: 30 NOTICE: 30
NOTICE: 40 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 continue_test1
---------------- ----------------

View File

@ -2170,6 +2170,32 @@ begin
continue when _r.v <= 20; continue when _r.v <= 20;
raise notice '%', _r.v; 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; end; $$ language plpgsql;
select continue_test1(); select continue_test1();