]> granicus.if.org Git - postgresql/commitdiff
Fix bug in CONTINUE statement for PL/pgSQL: when we continue a loop,
authorNeil Conway <neilc@samurai.com>
Wed, 22 Jun 2005 07:28:47 +0000 (07:28 +0000)
committerNeil Conway <neilc@samurai.com>
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();