Fix bug in CONTINUE statement for PL/pgSQL: when we continue a loop,
authorNeil Conway
Wed, 22 Jun 2005 07:28:47 +0000 (07:28 +0000)
committerNeil Conway
Wed, 22 Jun 2005 07:28:47 +0000 (07:28 +0000)
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.

src/pl/plpgsql/src/pl_exec.c
src/test/regress/expected/plpgsql.out
src/test/regress/sql/plpgsql.sql

index d1ea2d843eeb2ed45eb236d193396e966dfd2aa0..946a5b1c82af28f48135f8e8768535f56cd7e57a 100644 (file)
@@ -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
             */
index 23ef2d3f2edc8b199f4dd143f046fff59a73eb95..db4aefe614977c77847335c42061bcb400a7c20d 100644 (file)
@@ -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 
 ----------------
  
index 393fdc52b0c2df3f4a82e10dd78deaa506cb7c61..22cc335e272d871ac4a700aad2777323587c81f2 100644 (file)
@@ -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();