Add a CONTINUE statement to PL/PgSQL, which can be used to begin the
authorNeil Conway <neilc@samurai.com>
Wed, 22 Jun 2005 01:35:03 +0000 (01:35 +0000)
committerNeil Conway <neilc@samurai.com>
Wed, 22 Jun 2005 01:35:03 +0000 (01:35 +0000)
next iteration of a loop. Update documentation and add regression tests.
Patch from Pavel Stehule, reviewed by Neil Conway.

doc/src/sgml/plpgsql.sgml
src/pl/plpgsql/src/gram.y
src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/pl_funcs.c
src/pl/plpgsql/src/plpgsql.h
src/pl/plpgsql/src/scan.l
src/test/regress/expected/plpgsql.out
src/test/regress/sql/plpgsql.sql

index b202bba7b44d514c2aa8a10a2ad76e79eb0789c7..e8d687928f8183247d44e39827c24820fe9be506 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.73 2005/06/19 23:39:05 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.74 2005/06/22 01:35:02 neilc Exp $
 -->
 
 <chapter id="plpgsql"> 
@@ -1779,10 +1779,10 @@ END IF;
     </indexterm>
 
     <para>
-     With the <literal>LOOP</>, <literal>EXIT</>, <literal>WHILE</>,
-     and <literal>FOR</> statements, you can arrange for your
-     <application>PL/pgSQL</application> function to repeat a series
-     of commands.
+     With the <literal>LOOP</>, <literal>EXIT</>,
+     <literal>CONTINUE</>, <literal>WHILE</>, and <literal>FOR</>
+     statements, you can arrange for your <application>PL/pgSQL</>
+     function to repeat a series of commands.
     </para>
 
     <sect3>
@@ -1807,30 +1807,36 @@ END LOOP;
      <sect3>
       <title><literal>EXIT</></title>
 
+     <indexterm>
+      <primary>EXIT</primary>
+      <secondary>in PL/pgSQL</secondary>
+     </indexterm>
+
 <synopsis>
 EXIT <optional> <replaceable>label</replaceable> </optional> <optional> WHEN <replaceable>expression</replaceable> </optional>;
 </synopsis>
 
        <para>
-        If no <replaceable>label</replaceable> is given,
-        the innermost loop is terminated and the
-        statement following <literal>END LOOP</> is executed next.
-        If <replaceable>label</replaceable> is given, it
-        must be the label of the current or some outer level of nested loop
-        or block. Then the named loop or block is terminated and control
-        continues with the statement after the loop's/block's corresponding
-        <literal>END</>.
+        If no <replaceable>label</replaceable> is given, the innermost
+        loop is terminated and the statement following <literal>END
+        LOOP</> is executed next.  If <replaceable>label</replaceable>
+        is given, it must be the label of the current or some outer
+        level of nested loop or block. Then the named loop or block is
+        terminated and control continues with the statement after the
+        loop's/block's corresponding <literal>END</>.
        </para>
 
        <para>
-        If <literal>WHEN</> is present, loop exit occurs only if the specified
-        condition is true, otherwise control passes to the statement after
-        <literal>EXIT</>.
+        If <literal>WHEN</> is specified, the loop exit occurs only if
+        <replaceable>expression</> is true. Otherwise, control passes
+        to the statement after <literal>EXIT</>.
        </para>
 
        <para>
-        <literal>EXIT</> can be used to cause early exit from all types of
-        loops; it is not limited to use with unconditional loops.
+        <literal>EXIT</> can be used with all types of loops; it is
+        not limited to use with unconditional loops. When used with a
+        <literal>BEGIN</literal> block, <literal>EXIT</literal> passes
+        control to the next statement after the end of the block.
        </para>
 
        <para>
@@ -1858,9 +1864,61 @@ END;
        </para>
      </sect3>
 
+     <sect3>
+      <title><literal>CONTINUE</></title>
+
+     <indexterm>
+      <primary>CONTINUE</primary>
+      <secondary>in PL/pgSQL</secondary>
+     </indexterm>
+
+<synopsis>
+CONTINUE <optional> <replaceable>label</replaceable> </optional> <optional> WHEN <replaceable>expression</replaceable> </optional>;
+</synopsis>
+
+       <para>
+        If no <replaceable>label</> is given, the next iteration of
+        the innermost loop is begun. That is, control is passed back
+        to the loop control expression (if any), and the body of the
+        loop is re-evaluated. If <replaceable>label</> is present, it
+        specifies the label of the loop whose execution will be
+        continued.
+       </para>
+
+       <para>
+        If <literal>WHEN</> is specified, the next iteration of the
+        loop is begun only if <replaceable>expression</> is
+        true. Otherwise, control passes to the statement after
+        <literal>CONTINUE</>.
+       </para>
+
+       <para>
+        <literal>CONTINUE</> can be used with all types of loops; it
+        is not limited to use with unconditional loops.
+       </para>
+
+       <para>
+        Examples:
+<programlisting>
+LOOP
+    -- some computations
+    EXIT WHEN count &gt; 100;
+    CONTINUE WHEN count &lt; 50;
+    -- some computations for count IN [50 .. 100] 
+END LOOP;
+</programlisting>
+       </para>
+     </sect3>
+
+
      <sect3>
       <title><literal>WHILE</></title>
 
+     <indexterm>
+      <primary>WHILE</primary>
+      <secondary>in PL/pgSQL</secondary>
+     </indexterm>
+
 <synopsis>
 <optional>&lt;&lt;<replaceable>label</replaceable>&gt;&gt;</optional>
 WHILE <replaceable>expression</replaceable> LOOP
index e8350686eee3f2cb7897e0b00429e2d8d9b5dbec..f33d373883e6aa7fd7164df1022fbf5b5be7e60b 100644 (file)
@@ -4,7 +4,7 @@
  *                       procedural language
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.76 2005/06/14 06:43:14 neilc Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.77 2005/06/22 01:35:02 neilc Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -61,6 +61,7 @@ static    void             plpgsql_sql_error_callback(void *arg);
 
 %union {
        int32                   ival;
+       bool                    boolean;
        char                    *str;
        struct
        {
@@ -100,7 +101,7 @@ static  void             plpgsql_sql_error_callback(void *arg);
 %type <declhdr> decl_sect
 %type <varname> decl_varname
 %type <str>        decl_renname
-%type <ival>   decl_const decl_notnull
+%type <boolean>    decl_const decl_notnull exit_type
 %type <expr>   decl_defval decl_cursor_query
 %type <dtype>  decl_datatype
 %type <row>        decl_cursor_args
@@ -153,6 +154,7 @@ static  void             plpgsql_sql_error_callback(void *arg);
 %token K_BEGIN
 %token K_CLOSE
 %token K_CONSTANT
+%token K_CONTINUE
 %token K_CURSOR
 %token K_DEBUG
 %token K_DECLARE
@@ -514,9 +516,9 @@ decl_renname    : T_WORD
                ;
 
 decl_const     :
-                   { $$ = 0; }
+                   { $$ = false; }
                | K_CONSTANT
-                   { $$ = 1; }
+                   { $$ = true; }
                ;
 
 decl_datatype  :
@@ -531,9 +533,9 @@ decl_datatype   :
                ;
 
 decl_notnull   :
-                   { $$ = 0; }
+                   { $$ = false; }
                | K_NOT K_NULL
-                   { $$ = 1; }
+                   { $$ = true; }
                ;
 
 decl_defval        : ';'
@@ -1035,13 +1037,14 @@ stmt_select     : K_SELECT lno
                    }
                ;
 
-stmt_exit      : K_EXIT lno opt_exitlabel opt_exitcond
+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->lineno   = $2;
+                       new->is_exit  = $1;
+                       new->lineno   = $2;
                        new->label    = $3;
                        new->cond     = $4;
 
@@ -1049,6 +1052,16 @@ stmt_exit        : K_EXIT lno opt_exitlabel opt_exitcond
                    }
                ;
 
+exit_type      : K_EXIT
+                   {
+                       $$ = true;
+                   }
+               | K_CONTINUE
+                   {
+                       $$ = false;
+                   }
+               ;
+
 stmt_return        : K_RETURN lno
                    {
                        PLpgSQL_stmt_return *new;
@@ -1056,8 +1069,8 @@ stmt_return       : K_RETURN lno
                        new = palloc0(sizeof(PLpgSQL_stmt_return));
                        new->cmd_type = PLPGSQL_STMT_RETURN;
                        new->lineno   = $2;
-                       new->expr = NULL;
-                       new->retvarno   = -1;
+                       new->expr     = NULL;
+                       new->retvarno = -1;
 
                        if (plpgsql_curr_compile->fn_retset)
                        {
index 04403fa5640b01d4d98aa52d0b4a14c059094b6d..d1ea2d843eeb2ed45eb236d193396e966dfd2aa0 100644 (file)
@@ -3,7 +3,7 @@
  *           procedural language
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.146 2005/06/20 22:51:29 tgl Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.147 2005/06/22 01:35:02 neilc Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -194,6 +194,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
    PLpgSQL_execstate estate;
    ErrorContextCallback plerrcontext;
    int         i;
+   int         rc;
 
    /*
     * Setup the execution state
@@ -282,13 +283,24 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
     */
    estate.err_text = NULL;
    estate.err_stmt = (PLpgSQL_stmt *) (func->action);
-   if (exec_stmt_block(&estate, func->action) != PLPGSQL_RC_RETURN)
+   rc = exec_stmt_block(&estate, func->action);
+   if (rc != PLPGSQL_RC_RETURN)
    {
        estate.err_stmt = NULL;
        estate.err_text = NULL;
-       ereport(ERROR,
-          (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
-           errmsg("control reached end of function without RETURN")));
+
+       /*
+        * Provide a more helpful message if a CONTINUE has been used
+        * outside a loop.
+        */
+       if (rc == PLPGSQL_RC_CONTINUE)
+           ereport(ERROR,
+                   (errcode(ERRCODE_SYNTAX_ERROR),
+                    errmsg("CONTINUE cannot be used outside a loop")));
+       else
+           ereport(ERROR,
+                   (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
+                    errmsg("control reached end of function without RETURN")));
    }
 
    /*
@@ -393,6 +405,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
    PLpgSQL_execstate estate;
    ErrorContextCallback plerrcontext;
    int         i;
+   int         rc;
    PLpgSQL_var *var;
    PLpgSQL_rec *rec_new,
               *rec_old;
@@ -546,13 +559,24 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
     */
    estate.err_text = NULL;
    estate.err_stmt = (PLpgSQL_stmt *) (func->action);
-   if (exec_stmt_block(&estate, func->action) != PLPGSQL_RC_RETURN)
+   rc = exec_stmt_block(&estate, func->action);
+   if (rc != PLPGSQL_RC_RETURN)
    {
        estate.err_stmt = NULL;
        estate.err_text = NULL;
-       ereport(ERROR,
-          (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
-           errmsg("control reached end of trigger procedure without RETURN")));
+
+       /*
+        * Provide a more helpful message if a CONTINUE has been used
+        * outside a loop.
+        */
+       if (rc == PLPGSQL_RC_CONTINUE)
+           ereport(ERROR,
+                   (errcode(ERRCODE_SYNTAX_ERROR),
+                    errmsg("CONTINUE cannot be used outside a loop")));
+       else
+           ereport(ERROR,
+                   (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
+                    errmsg("control reached end of trigger procedure without RETURN")));
    }
 
    if (estate.retisset)
@@ -919,7 +943,9 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
    switch (rc)
    {
        case PLPGSQL_RC_OK:
-           return PLPGSQL_RC_OK;
+       case PLPGSQL_RC_CONTINUE:
+       case PLPGSQL_RC_RETURN:
+           return rc;
 
        case PLPGSQL_RC_EXIT:
            if (estate->exitlabel == NULL)
@@ -930,10 +956,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
                return PLPGSQL_RC_EXIT;
            estate->exitlabel = NULL;
            return PLPGSQL_RC_OK;
-
-       case PLPGSQL_RC_RETURN:
-           return PLPGSQL_RC_RETURN;
-
+       
        default:
            elog(ERROR, "unrecognized rc: %d", rc);
    }
@@ -1120,7 +1143,7 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
    {
        PLpgSQL_diag_item   *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
        PLpgSQL_datum       *var;
-       bool                 isnull = false;
+       bool                 isnull;
 
        if (diag_item->target <= 0)
            continue;
@@ -1165,7 +1188,7 @@ static int
 exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt)
 {
    bool        value;
-   bool        isnull = false;
+   bool        isnull;
 
    value = exec_eval_boolean(estate, stmt->cond, &isnull);
    exec_eval_cleanup(estate);
@@ -1209,10 +1232,23 @@ exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt)
                    return PLPGSQL_RC_OK;
                if (stmt->label == NULL)
                    return PLPGSQL_RC_EXIT;
-               if (strcmp(stmt->label, estate->exitlabel))
+               if (strcmp(stmt->label, estate->exitlabel) != 0)
                    return PLPGSQL_RC_EXIT;
                estate->exitlabel = NULL;
                return PLPGSQL_RC_OK;
+               
+           case PLPGSQL_RC_CONTINUE:
+               if (estate->exitlabel == NULL)
+                   /* anonymous continue, so re-run the loop */
+                   break;
+               else if (stmt->label != NULL &&
+                        strcmp(stmt->label, estate->exitlabel) == 0)
+                   /* label matches named continue, so re-run loop */
+                   estate->exitlabel = NULL;
+               else
+                   /* label doesn't match named continue, so propagate upward */
+                   return PLPGSQL_RC_CONTINUE;
+               break;
 
            case PLPGSQL_RC_RETURN:
                return PLPGSQL_RC_RETURN;
@@ -1236,7 +1272,7 @@ static int
 exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
 {
    bool        value;
-   bool        isnull = false;
+   bool        isnull;
    int         rc;
 
    for (;;)
@@ -1264,6 +1300,19 @@ exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
                estate->exitlabel = NULL;
                return PLPGSQL_RC_OK;
 
+           case PLPGSQL_RC_CONTINUE:
+               if (estate->exitlabel == NULL)
+                   /* anonymous continue, so re-run loop */
+                   break;
+               else if (stmt->label != NULL &&
+                        strcmp(stmt->label, estate->exitlabel) == 0)
+                   /* label matches named continue, so re-run loop */
+                   estate->exitlabel = NULL;
+               else
+                   /* label doesn't match named continue, propagate upward */
+                   return PLPGSQL_RC_CONTINUE;
+               break;
+
            case PLPGSQL_RC_RETURN:
                return PLPGSQL_RC_RETURN;
 
@@ -1288,7 +1337,7 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
    PLpgSQL_var *var;
    Datum       value;
    Oid         valtype;
-   bool        isnull = false;
+   bool        isnull;
    bool        found = false;
    int         rc = PLPGSQL_RC_OK;
 
@@ -1366,13 +1415,35 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
            }
 
            /*
-            * otherwise, we processed a labelled exit that does not match
-            * the current statement's label, if any: return RC_EXIT so
-            * that the EXIT continues to recurse upward.
+            * otherwise, this is a labelled exit that does not match
+            * the current statement's label, if any: return RC_EXIT
+            * so that the EXIT continues to propagate up the stack.
             */
 
            break;
        }
+       else if (rc == PLPGSQL_RC_CONTINUE)
+       {
+           if (estate->exitlabel == NULL)
+               /* anonymous continue, so 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, 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.
+                */
+               break;
+           }
+       }
 
        /*
         * Increase/decrease loop var
@@ -1459,17 +1530,8 @@ exec_stmt_fors(PLpgSQL_execstate *estate, PLpgSQL_stmt_fors *stmt)
             * Execute the statements
             */
            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)
@@ -1490,6 +1552,34 @@ exec_stmt_fors(PLpgSQL_execstate *estate, PLpgSQL_stmt_fors *stmt)
                     * 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;
+                   }
+
+                   /*
+                    * otherwise, we processed 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.
+                    */
+               }
+
+               /*
+                * 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;
            }
@@ -1563,7 +1653,7 @@ exec_stmt_select(PLpgSQL_execstate *estate, PLpgSQL_stmt_select *stmt)
    n = estate->eval_processed;
 
    /*
-    * If the query didn't return any row, set the target to NULL and
+    * If the query didn't return any rows, set the target to NULL and
     * return.
     */
    if (n == 0)
@@ -1586,28 +1676,33 @@ exec_stmt_select(PLpgSQL_execstate *estate, PLpgSQL_stmt_select *stmt)
 
 
 /* ----------
- * exec_stmt_exit          Start exiting loop(s) or blocks
+ * exec_stmt_exit          Implements EXIT and CONTINUE
+ *
+ * This begins the process of exiting / restarting a loop.
  * ----------
  */
 static int
 exec_stmt_exit(PLpgSQL_execstate *estate, PLpgSQL_stmt_exit *stmt)
 {
    /*
-    * If the exit has a condition, check that it's true
+    * If the exit / continue has a condition, evaluate it
     */
    if (stmt->cond != NULL)
    {
        bool        value;
-       bool        isnull = false;
+       bool        isnull;
 
        value = exec_eval_boolean(estate, stmt->cond, &isnull);
        exec_eval_cleanup(estate);
-       if (isnull || !value)
+       if (isnull || value == false)
            return PLPGSQL_RC_OK;
    }
 
    estate->exitlabel = stmt->label;
-   return PLPGSQL_RC_EXIT;
+   if (stmt->is_exit)
+       return PLPGSQL_RC_EXIT;
+   else
+       return PLPGSQL_RC_CONTINUE;
 }
 
 
@@ -2455,14 +2550,6 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt)
 
            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)
@@ -2483,6 +2570,33 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt)
                     * 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;
+                   }
+
+                   /*
+                    * otherwise, we process a labelled continue that
+                    * does not match the current statement's label,
+                    * so propagate RC_CONTINUE upward in the stack.
+                    */
+               }
+
+               /*
+                * 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;
            }
index 23b2b3e71a1d21511a4e531c04dfb107bbffa603..03280c94b122408ef25ec6d35b078ffa7b081a43 100644 (file)
@@ -3,7 +3,7 @@
  *           procedural language
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.44 2005/06/19 01:06:12 momjian Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.45 2005/06/22 01:35:02 neilc Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -845,7 +845,8 @@ static void
 dump_exit(PLpgSQL_stmt_exit *stmt)
 {
    dump_ind();
-   printf("EXIT lbl='%s'", stmt->label);
+   printf("%s label='%s'",
+          stmt->is_exit ? "EXIT" : "CONTINUE", stmt->label);
    if (stmt->cond != NULL)
    {
        printf(" WHEN ");
index a724df67963b45808622af67c13032379f7623ab..3615b3bf066957886d651924e6fef635e77359d4 100644 (file)
@@ -3,7 +3,7 @@
  *           procedural language
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.63 2005/06/14 06:43:14 neilc Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.64 2005/06/22 01:35:02 neilc Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -125,7 +125,8 @@ enum
 {
    PLPGSQL_RC_OK,
    PLPGSQL_RC_EXIT,
-   PLPGSQL_RC_RETURN
+   PLPGSQL_RC_RETURN,
+   PLPGSQL_RC_CONTINUE
 };
 
 /* ----------
@@ -485,9 +486,10 @@ typedef struct
 
 
 typedef struct
-{                              /* EXIT statement           */
+{                              /* EXIT or CONTINUE statement           */
    int         cmd_type;
    int         lineno;
+   bool        is_exit;        /* Is this an exit or a continue? */
    char       *label;
    PLpgSQL_expr *cond;
 } PLpgSQL_stmt_exit;
@@ -610,7 +612,8 @@ typedef struct
    bool        readonly_func;
 
    TupleDesc   rettupdesc;
-   char       *exitlabel;
+   char       *exitlabel;      /* the "target" label of the current
+                                * EXIT or CONTINUE stmt, if any */
 
    Tuplestorestate *tuple_store;       /* SRFs accumulate results here */
    MemoryContext tuple_store_cxt;
index f9295196439d766644856d8f2ac33212f7e16f87..680a58fc018bd33d1bb37759639fb661e620ba76 100644 (file)
@@ -4,7 +4,7 @@
  *           procedural language
  *
  * IDENTIFICATION
- *    $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.40 2005/03/11 19:13:43 momjian Exp $
+ *    $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.41 2005/06/22 01:35:02 neilc Exp $
  *
  *    This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -139,6 +139,7 @@ alias           { return K_ALIAS;           }
 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;         }
index 21101958ab4366c865e0d7353af5e8a08677857c..23ef2d3f2edc8b199f4dd143f046fff59a73eb95 100644 (file)
@@ -2491,3 +2491,140 @@ NOTICE:  {10,20,30}; 20; xyz; xyzabc; (10,aaa,,30); <NULL>
 (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"
+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 function continue_test1() 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---';
+  <<lbl>>
+  loop
+    _i := _i - 1;
+    loop
+      raise notice '%', _i;
+      continue lbl when _i > 0;
+      exit lbl;
+    end loop;
+  end loop;
+
+  raise notice '---3---';
+  <<the_loop>>
+  while _i < 10 loop
+    _i := _i + 1;
+    continue the_loop when _i % 2 = 0;
+    raise notice '%', _i;
+  end loop;
+
+  raise notice '---4---';
+  for _i in 1..10 loop
+    begin
+      -- applies to outer loop, not the nested begin block
+      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 continue_test1();
+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
+ continue_test1 
+----------------
+(1 row)
+
+-- CONTINUE is only legal inside a loop
+create function continue_test2() returns void as $$
+begin
+    begin
+        continue;
+    end;
+    return;
+end;
+$$ language plpgsql;
+-- should fail
+select continue_test2();
+ERROR:  CONTINUE cannot be used outside a loop
+CONTEXT:  PL/pgSQL function "continue_test2"
+-- CONTINUE can't reference the label of a named block
+create function continue_test3() returns void as $$
+begin
+    <<begin_block1>>
+    begin
+        loop
+            continue begin_block1;
+        end loop;
+    end;
+end;
+$$ language plpgsql;
+-- should fail
+select continue_test3();
+ERROR:  CONTINUE cannot be used outside a loop
+CONTEXT:  PL/pgSQL function "continue_test3"
+drop function continue_test1();
+drop function continue_test2();
+drop function continue_test3();
+drop table conttesttbl;
index 375eef8959cdc88006acb92822d45b90aeaeed55..393fdc52b0c2df3f4a82e10dd78deaa506cb7c61 100644 (file)
@@ -2112,3 +2112,97 @@ end;$$ language plpgsql;
 
 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 function continue_test1() 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---';
+  <<lbl>>
+  loop
+    _i := _i - 1;
+    loop
+      raise notice '%', _i;
+      continue lbl when _i > 0;
+      exit lbl;
+    end loop;
+  end loop;
+
+  raise notice '---3---';
+  <<the_loop>>
+  while _i < 10 loop
+    _i := _i + 1;
+    continue the_loop when _i % 2 = 0;
+    raise notice '%', _i;
+  end loop;
+
+  raise notice '---4---';
+  for _i in 1..10 loop
+    begin
+      -- applies to outer loop, not the nested begin block
+      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 continue_test1();
+
+-- CONTINUE is only legal inside a loop
+create function continue_test2() returns void as $$
+begin
+    begin
+        continue;
+    end;
+    return;
+end;
+$$ language plpgsql;
+
+-- should fail
+select continue_test2();
+
+-- CONTINUE can't reference the label of a named block
+create function continue_test3() returns void as $$
+begin
+    <<begin_block1>>
+    begin
+        loop
+            continue begin_block1;
+        end loop;
+    end;
+end;
+$$ language plpgsql;
+
+-- should fail
+select continue_test3();
+
+drop function continue_test1();
+drop function continue_test2();
+drop function continue_test3();
+drop table conttesttbl;