Re: Continue stmt for plpgsql - Mailing list pgsql-patches

From Neil Conway
Subject Re: Continue stmt for plpgsql
Date
Msg-id 42B7D507.8020600@samurai.com
Whole thread Raw
In response to Continue stmt for plpgsql  (Pavel Stehule <stehule@kix.fsv.cvut.cz>)
Responses Re: Continue stmt for plpgsql  (Neil Conway <neilc@samurai.com>)
List pgsql-patches
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;

pgsql-patches by date:

Previous
From: Eugen Nedelcu
Date:
Subject: thousands comma numeric formatting in psql
Next
From: Andreas Pflug
Date:
Subject: Re: Server instrumentation