Thread: Continue stmt for plpgsql

Continue stmt for plpgsql

From
Pavel Stehule
Date:
Hello

  Attached patch provide continue stmt in plpgsql language. Continue stmt
start new step in any loop stmt. If stmt continue is in begin end block,
then find first outer loop (per recent discussion).

Regards
Pavel Stehule

Attachment

Re: Continue stmt for plpgsql

From
Neil Conway
Date:
Pavel Stehule wrote:
>   Attached patch provide continue stmt in plpgsql language. Continue stmt
> start new step in any loop stmt. If stmt continue is in begin end block,
> then find first outer loop (per recent discussion).

Attached is a revised patch. I didn't see anything major that needed to
be corrected, but there were some minor cleanups to be made. I also
clarified the error message when a CONTINUE is specified outside a loop
(although I wasn't quite sure what error message to use: SYNTAX_ERROR
seems a bit weird, as the problem isn't detected until the end of the
function's execution. I'm using PLPGSQL_ERROR at the moment, but
suggestions for a better choice are welcome).

Barring any objections, I'll apply this tomorrow.

-Neil
Index: doc/src/sgml/plpgsql.sgml
===================================================================
RCS file: /var/lib/cvs/pgsql/doc/src/sgml/plpgsql.sgml,v
retrieving revision 1.73
diff -c -r1.73 plpgsql.sgml
*** doc/src/sgml/plpgsql.sgml    19 Jun 2005 23:39:05 -0000    1.73
--- doc/src/sgml/plpgsql.sgml    21 Jun 2005 08:46:27 -0000
***************
*** 1779,1788 ****
      </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.
      </para>

      <sect3>
--- 1779,1788 ----
      </indexterm>

      <para>
!      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,1836 ****
       <sect3>
        <title><literal>EXIT</></title>

  <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</>.
         </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</>.
         </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.
         </para>

         <para>
--- 1807,1842 ----
       <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</>.
         </para>

         <para>
!         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 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>
***************
*** 1859,1866 ****
--- 1865,1924 ----
       </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 > 100;
+     CONTINUE WHEN count < 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><<<replaceable>label</replaceable>>></optional>
  WHILE <replaceable>expression</replaceable> LOOP
Index: src/pl/plpgsql/src/gram.y
===================================================================
RCS file: /var/lib/cvs/pgsql/src/pl/plpgsql/src/gram.y,v
retrieving revision 1.76
diff -c -r1.76 gram.y
*** src/pl/plpgsql/src/gram.y    14 Jun 2005 06:43:14 -0000    1.76
--- src/pl/plpgsql/src/gram.y    21 Jun 2005 07:19:38 -0000
***************
*** 61,66 ****
--- 61,67 ----

  %union {
          int32                    ival;
+         bool                    boolean;
          char                    *str;
          struct
          {
***************
*** 100,106 ****
  %type <declhdr> decl_sect
  %type <varname> decl_varname
  %type <str>        decl_renname
! %type <ival>    decl_const decl_notnull
  %type <expr>    decl_defval decl_cursor_query
  %type <dtype>    decl_datatype
  %type <row>        decl_cursor_args
--- 101,107 ----
  %type <declhdr> decl_sect
  %type <varname> decl_varname
  %type <str>        decl_renname
! %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,158 ****
--- 154,160 ----
  %token    K_BEGIN
  %token    K_CLOSE
  %token    K_CONSTANT
+ %token    K_CONTINUE
  %token    K_CURSOR
  %token    K_DEBUG
  %token    K_DECLARE
***************
*** 514,522 ****
                  ;

  decl_const        :
!                     { $$ = 0; }
                  | K_CONSTANT
!                     { $$ = 1; }
                  ;

  decl_datatype    :
--- 516,524 ----
                  ;

  decl_const        :
!                     { $$ = false; }
                  | K_CONSTANT
!                     { $$ = true; }
                  ;

  decl_datatype    :
***************
*** 531,539 ****
                  ;

  decl_notnull    :
!                     { $$ = 0; }
                  | K_NOT K_NULL
!                     { $$ = 1; }
                  ;

  decl_defval        : ';'
--- 533,541 ----
                  ;

  decl_notnull    :
!                     { $$ = false; }
                  | K_NOT K_NULL
!                     { $$ = true; }
                  ;

  decl_defval        : ';'
***************
*** 1035,1047 ****
                      }
                  ;

! stmt_exit        : K_EXIT 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->label      = $3;
                          new->cond      = $4;

--- 1037,1050 ----
                      }
                  ;

! 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->is_exit  = $1;
!                         new->lineno      = $2;
                          new->label      = $3;
                          new->cond      = $4;

***************
*** 1049,1054 ****
--- 1052,1067 ----
                      }
                  ;

+ exit_type        : K_EXIT
+                     {
+                         $$ = true;
+                     }
+                 | K_CONTINUE
+                     {
+                         $$ = false;
+                     }
+                 ;
+
  stmt_return        : K_RETURN lno
                      {
                          PLpgSQL_stmt_return *new;
***************
*** 1056,1063 ****
                          new = palloc0(sizeof(PLpgSQL_stmt_return));
                          new->cmd_type = PLPGSQL_STMT_RETURN;
                          new->lineno   = $2;
!                         new->expr = NULL;
!                         new->retvarno    = -1;

                          if (plpgsql_curr_compile->fn_retset)
                          {
--- 1069,1076 ----
                          new = palloc0(sizeof(PLpgSQL_stmt_return));
                          new->cmd_type = PLPGSQL_STMT_RETURN;
                          new->lineno   = $2;
!                         new->expr      = NULL;
!                         new->retvarno = -1;

                          if (plpgsql_curr_compile->fn_retset)
                          {
Index: src/pl/plpgsql/src/pl_exec.c
===================================================================
RCS file: /var/lib/cvs/pgsql/src/pl/plpgsql/src/pl_exec.c,v
retrieving revision 1.146
diff -c -r1.146 pl_exec.c
*** src/pl/plpgsql/src/pl_exec.c    20 Jun 2005 22:51:29 -0000    1.146
--- src/pl/plpgsql/src/pl_exec.c    21 Jun 2005 08:01:39 -0000
***************
*** 194,199 ****
--- 194,200 ----
      PLpgSQL_execstate estate;
      ErrorContextCallback plerrcontext;
      int            i;
+     int            rc;

      /*
       * Setup the execution state
***************
*** 282,294 ****
       */
      estate.err_text = NULL;
      estate.err_stmt = (PLpgSQL_stmt *) (func->action);
!     if (exec_stmt_block(&estate, func->action) != 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")));
      }

      /*
--- 283,306 ----
       */
      estate.err_text = NULL;
      estate.err_stmt = (PLpgSQL_stmt *) (func->action);
!     rc = exec_stmt_block(&estate, func->action);
!     if (rc != PLPGSQL_RC_RETURN)
      {
          estate.err_stmt = NULL;
          estate.err_text = NULL;
!
!         /*
!          * Provide a more helpful message if a CONTINUE has been used
!          * outside a loop.
!          */
!         if (rc == PLPGSQL_RC_CONTINUE)
!             ereport(ERROR,
!                     (errcode(ERRCODE_PLPGSQL_ERROR), /* XXX: sqlstate? */
!                      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,398 ****
--- 405,411 ----
      PLpgSQL_execstate estate;
      ErrorContextCallback plerrcontext;
      int            i;
+     int            rc;
      PLpgSQL_var *var;
      PLpgSQL_rec *rec_new,
                 *rec_old;
***************
*** 546,558 ****
       */
      estate.err_text = NULL;
      estate.err_stmt = (PLpgSQL_stmt *) (func->action);
!     if (exec_stmt_block(&estate, func->action) != 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")));
      }

      if (estate.retisset)
--- 559,582 ----
       */
      estate.err_text = NULL;
      estate.err_stmt = (PLpgSQL_stmt *) (func->action);
!     rc = exec_stmt_block(&estate, func->action);
!     if (rc != PLPGSQL_RC_RETURN)
      {
          estate.err_stmt = NULL;
          estate.err_text = NULL;
!
!         /*
!          * Provide a more helpful message if a CONTINUE has been used
!          * outside a loop.
!          */
!         if (rc == PLPGSQL_RC_CONTINUE)
!             ereport(ERROR,
!                     (errcode(ERRCODE_PLPGSQL_ERROR), /* XXX: sqlstate? */
!                      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,925 ****
      switch (rc)
      {
          case PLPGSQL_RC_OK:
!             return PLPGSQL_RC_OK;

          case PLPGSQL_RC_EXIT:
              if (estate->exitlabel == NULL)
--- 943,951 ----
      switch (rc)
      {
          case PLPGSQL_RC_OK:
!         case PLPGSQL_RC_CONTINUE:
!         case PLPGSQL_RC_RETURN:
!             return rc;

          case PLPGSQL_RC_EXIT:
              if (estate->exitlabel == NULL)
***************
*** 930,939 ****
                  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);
      }
--- 956,962 ----
                  return PLPGSQL_RC_EXIT;
              estate->exitlabel = NULL;
              return PLPGSQL_RC_OK;
!
          default:
              elog(ERROR, "unrecognized rc: %d", rc);
      }
***************
*** 1120,1126 ****
      {
          PLpgSQL_diag_item    *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
          PLpgSQL_datum        *var;
!         bool                 isnull = false;

          if (diag_item->target <= 0)
              continue;
--- 1143,1149 ----
      {
          PLpgSQL_diag_item    *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
          PLpgSQL_datum        *var;
!         bool                 isnull;

          if (diag_item->target <= 0)
              continue;
***************
*** 1165,1171 ****
  exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt)
  {
      bool        value;
!     bool        isnull = false;

      value = exec_eval_boolean(estate, stmt->cond, &isnull);
      exec_eval_cleanup(estate);
--- 1188,1194 ----
  exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt)
  {
      bool        value;
!     bool        isnull;

      value = exec_eval_boolean(estate, stmt->cond, &isnull);
      exec_eval_cleanup(estate);
***************
*** 1209,1218 ****
                      return PLPGSQL_RC_OK;
                  if (stmt->label == NULL)
                      return PLPGSQL_RC_EXIT;
!                 if (strcmp(stmt->label, estate->exitlabel))
                      return PLPGSQL_RC_EXIT;
                  estate->exitlabel = NULL;
                  return PLPGSQL_RC_OK;

              case PLPGSQL_RC_RETURN:
                  return PLPGSQL_RC_RETURN;
--- 1232,1254 ----
                      return PLPGSQL_RC_OK;
                  if (stmt->label == NULL)
                      return PLPGSQL_RC_EXIT;
!                 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,1242 ****
  exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
  {
      bool        value;
!     bool        isnull = false;
      int            rc;

      for (;;)
--- 1272,1278 ----
  exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
  {
      bool        value;
!     bool        isnull;
      int            rc;

      for (;;)
***************
*** 1264,1269 ****
--- 1300,1318 ----
                  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,1294 ****
      PLpgSQL_var *var;
      Datum        value;
      Oid            valtype;
!     bool        isnull = false;
      bool        found = false;
      int            rc = PLPGSQL_RC_OK;

--- 1337,1343 ----
      PLpgSQL_var *var;
      Datum        value;
      Oid            valtype;
!     bool        isnull;
      bool        found = false;
      int            rc = PLPGSQL_RC_OK;

***************
*** 1366,1378 ****
              }

              /*
!              * 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.
               */

              break;
          }

          /*
           * Increase/decrease loop var
--- 1415,1449 ----
              }

              /*
!              * 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
***************
*** 1460,1475 ****
               */
              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)
--- 1531,1539 ----
               */
              rc = exec_stmts(estate, stmt->body);

+
              if (rc != PLPGSQL_RC_OK)
              {
                  if (rc == PLPGSQL_RC_EXIT)
                  {
                      if (estate->exitlabel == NULL)
***************
*** 1490,1495 ****
--- 1554,1587 ----
                       * 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,1569 ****
      n = estate->eval_processed;

      /*
!      * If the query didn't return any row, set the target to NULL and
       * return.
       */
      if (n == 0)
--- 1655,1661 ----
      n = estate->eval_processed;

      /*
!      * If the query didn't return any rows, set the target to NULL and
       * return.
       */
      if (n == 0)
***************
*** 1586,1613 ****


  /* ----------
!  * exec_stmt_exit            Start exiting loop(s) or blocks
   * ----------
   */
  static int
  exec_stmt_exit(PLpgSQL_execstate *estate, PLpgSQL_stmt_exit *stmt)
  {
      /*
!      * If the exit has a condition, check that it's true
       */
      if (stmt->cond != NULL)
      {
          bool        value;
!         bool        isnull = false;

          value = exec_eval_boolean(estate, stmt->cond, &isnull);
          exec_eval_cleanup(estate);
!         if (isnull || !value)
              return PLPGSQL_RC_OK;
      }

      estate->exitlabel = stmt->label;
!     return PLPGSQL_RC_EXIT;
  }


--- 1678,1710 ----


  /* ----------
!  * 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 / continue has a condition, evaluate it
       */
      if (stmt->cond != NULL)
      {
          bool        value;
!         bool        isnull;

          value = exec_eval_boolean(estate, stmt->cond, &isnull);
          exec_eval_cleanup(estate);
!         if (isnull || value == false)
              return PLPGSQL_RC_OK;
      }

      estate->exitlabel = stmt->label;
!     if (stmt->is_exit)
!         return PLPGSQL_RC_EXIT;
!     else
!         return PLPGSQL_RC_CONTINUE;
  }


***************
*** 2455,2468 ****

              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)
--- 2552,2557 ----
***************
*** 2483,2488 ****
--- 2572,2604 ----
                       * 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: src/pl/plpgsql/src/pl_funcs.c
===================================================================
RCS file: /var/lib/cvs/pgsql/src/pl/plpgsql/src/pl_funcs.c,v
retrieving revision 1.44
diff -c -r1.44 pl_funcs.c
*** src/pl/plpgsql/src/pl_funcs.c    19 Jun 2005 01:06:12 -0000    1.44
--- src/pl/plpgsql/src/pl_funcs.c    21 Jun 2005 07:19:38 -0000
***************
*** 845,851 ****
  dump_exit(PLpgSQL_stmt_exit *stmt)
  {
      dump_ind();
!     printf("EXIT lbl='%s'", stmt->label);
      if (stmt->cond != NULL)
      {
          printf(" WHEN ");
--- 845,852 ----
  dump_exit(PLpgSQL_stmt_exit *stmt)
  {
      dump_ind();
!     printf("%s label='%s'",
!            stmt->is_exit ? "EXIT" : "CONTINUE", stmt->label);
      if (stmt->cond != NULL)
      {
          printf(" WHEN ");
Index: src/pl/plpgsql/src/plpgsql.h
===================================================================
RCS file: /var/lib/cvs/pgsql/src/pl/plpgsql/src/plpgsql.h,v
retrieving revision 1.63
diff -c -r1.63 plpgsql.h
*** src/pl/plpgsql/src/plpgsql.h    14 Jun 2005 06:43:14 -0000    1.63
--- src/pl/plpgsql/src/plpgsql.h    21 Jun 2005 07:19:38 -0000
***************
*** 125,131 ****
  {
      PLPGSQL_RC_OK,
      PLPGSQL_RC_EXIT,
!     PLPGSQL_RC_RETURN
  };

  /* ----------
--- 125,132 ----
  {
      PLPGSQL_RC_OK,
      PLPGSQL_RC_EXIT,
!     PLPGSQL_RC_RETURN,
!     PLPGSQL_RC_CONTINUE
  };

  /* ----------
***************
*** 485,493 ****


  typedef struct
! {                                /* EXIT statement            */
      int            cmd_type;
      int            lineno;
      char       *label;
      PLpgSQL_expr *cond;
  } PLpgSQL_stmt_exit;
--- 486,495 ----


  typedef struct
! {                                /* 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,616 ****
      bool        readonly_func;

      TupleDesc    rettupdesc;
!     char       *exitlabel;

      Tuplestorestate *tuple_store;        /* SRFs accumulate results here */
      MemoryContext tuple_store_cxt;
--- 612,619 ----
      bool        readonly_func;

      TupleDesc    rettupdesc;
!     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: src/pl/plpgsql/src/scan.l
===================================================================
RCS file: /var/lib/cvs/pgsql/src/pl/plpgsql/src/scan.l,v
retrieving revision 1.40
diff -c -r1.40 scan.l
*** src/pl/plpgsql/src/scan.l    11 Mar 2005 19:13:43 -0000    1.40
--- src/pl/plpgsql/src/scan.l    21 Jun 2005 07:19:38 -0000
***************
*** 139,144 ****
--- 139,145 ----
  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: src/test/regress/expected/plpgsql.out
===================================================================
RCS file: /var/lib/cvs/pgsql/src/test/regress/expected/plpgsql.out,v
retrieving revision 1.34
diff -c -r1.34 plpgsql.out
*** src/test/regress/expected/plpgsql.out    14 Jun 2005 06:43:14 -0000    1.34
--- src/test/regress/expected/plpgsql.out    21 Jun 2005 08:16:07 -0000
***************
*** 2491,2493 ****
--- 2491,2608 ----
  (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---';
+   while _i < 10 loop
+     _i := _i + 1;
+     continue when _i % 2 = 0;
+     raise notice '%', _i;
+   end loop;
+   raise notice '---4---';
+   for _i in 1..10 loop
+     begin
+       -- finfing outer loop
+       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"
+ drop function continue_test1();
+ drop function continue_test2();
+ drop table conttesttbl;
Index: src/test/regress/sql/plpgsql.sql
===================================================================
RCS file: /var/lib/cvs/pgsql/src/test/regress/sql/plpgsql.sql,v
retrieving revision 1.29
diff -c -r1.29 plpgsql.sql
*** src/test/regress/sql/plpgsql.sql    14 Jun 2005 06:43:15 -0000    1.29
--- src/test/regress/sql/plpgsql.sql    21 Jun 2005 07:54:17 -0000
***************
*** 2112,2114 ****
--- 2112,2186 ----

  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---';
+   while _i < 10 loop
+     _i := _i + 1;
+     continue when _i % 2 = 0;
+     raise notice '%', _i;
+   end loop;
+   raise notice '---4---';
+   for _i in 1..10 loop
+     begin
+       -- finfing outer loop
+       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();
+
+ drop function continue_test1();
+ drop function continue_test2();
+ drop table conttesttbl;

Re: Continue stmt for plpgsql

From
Neil Conway
Date:
Neil Conway wrote:
> Attached is a revised patch.

Patch applied -- thanks, Pavel. I ended up using ERRCODE_SYNTAX_ERROR
when a CONTINUE is specified outside a loop. BTW, one minor annoyance is:

<<lbl>>
BEGIN
     LOOP
         CONTINUE lbl;
     END LOOP;
END;

The current implementation will raise an error, as it should, but it
does so at runtime, and claims that "CONTINUE cannot be used outside a
loop". We could fix this by dividing labels into "block labels" and
"loop labels" at compile-time and only accepting a loop label for
CONTINUE, but I haven't implemented this yet.

-Neil