Re: pl/pgsql: END verbosity [patch] - Mailing list pgsql-hackers

From Neil Conway
Subject Re: pl/pgsql: END verbosity [patch]
Date
Msg-id 42C539BF.8040505@samurai.com
Whole thread Raw
In response to pl/pgsql: END verbosity [patch]  (Pavel Stehule <stehule@kix.fsv.cvut.cz>)
Responses Re: [PATCHES] pl/pgsql: END verbosity [patch]
List pgsql-hackers
Pavel Stehule wrote:
> this patch allows optional using label with END and END LOOP. Ending label
> has only informational value, but can enhance readability large block and
> enhance likeness with Oracle.
>
> <<main>>LOOP
>  ...
>  ...
> END LOOP<<main>>;

Attached is a revised version of this patch. Changes / comments:

- AFAICS Oracle's syntax is actually

<<label>> LOOP
...
END LOOP label;

i.e. the ending block label isn't enclosed in <<>>. I've adjusted the
patch accordingly.

- your patch broke EXIT and CONTINUE, as running the regression tests
would have made clear.

- yyerror() will set plpgsql_error_lineno, so you needn't do it
yourself. I changed it to use ereport(ERROR) anyway, as it seems a bit
more appropriate. I'm not quite happy with the error message text:

ERROR:  end label "outer_label" differs from block's label "inner_label"
CONTEXT:  compile of PL/pgSQL function "end_label3" near line 6

ERROR:  end label "outer_label" specified for unlabelled block
CONTEXT:  compile of PL/pgSQL function "end_label4" near line 5

suggestions for improvement are welcome.

BTW, I notice that some but not all the call sites of ereport(ERROR) in
PL/PgSQL's gram.y set plpgsql_error_lineno. Is there a reason for this?

Barring any objections, I'll apply the attached patch to CVS tomorrow.

-Neil
Index: doc/src/sgml/plpgsql.sgml
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/doc/src/sgml/plpgsql.sgml,v
retrieving revision 1.74
diff -c -r1.74 plpgsql.sgml
*** doc/src/sgml/plpgsql.sgml    22 Jun 2005 01:35:02 -0000    1.74
--- doc/src/sgml/plpgsql.sgml    1 Jul 2005 11:43:36 -0000
***************
*** 456,462 ****
      <replaceable>declarations</replaceable> </optional>
  BEGIN
      <replaceable>statements</replaceable>
! END;
  </synopsis>
      </para>

--- 456,462 ----
      <replaceable>declarations</replaceable> </optional>
  BEGIN
      <replaceable>statements</replaceable>
! END <optional> <replaceable>label</replaceable> </optional>;
  </synopsis>
      </para>

***************
*** 1789,1806 ****
       <title><literal>LOOP</></title>

  <synopsis>
! <optional><<<replaceable>label</replaceable>>></optional>
  LOOP
      <replaceable>statements</replaceable>
! END LOOP;
  </synopsis>

       <para>
!       <literal>LOOP</> defines an unconditional loop that is repeated indefinitely
!       until terminated by an <literal>EXIT</> or <command>RETURN</command>
!       statement.  The optional label can be used by <literal>EXIT</> statements in
!       nested loops to specify which level of nesting should be
!       terminated.
       </para>
      </sect3>

--- 1789,1807 ----
       <title><literal>LOOP</></title>

  <synopsis>
! <optional> <<<replaceable>label</replaceable>>> </optional>
  LOOP
      <replaceable>statements</replaceable>
! END LOOP <optional> <replaceable>label</replaceable> </optional>;
  </synopsis>

       <para>
!       <literal>LOOP</> defines an unconditional loop that is repeated
!       indefinitely until terminated by an <literal>EXIT</> or
!       <command>RETURN</command> statement.  The optional
!       <replaceable>label</replaceable> can be used by <literal>EXIT</>
!       and <literal>CONTINUE</literal> statements in nested loops to
!       specify which loop the statement should be applied to.
       </para>
      </sect3>

***************
*** 1920,1929 ****
       </indexterm>

  <synopsis>
! <optional><<<replaceable>label</replaceable>>></optional>
  WHILE <replaceable>expression</replaceable> LOOP
      <replaceable>statements</replaceable>
! END LOOP;
  </synopsis>

         <para>
--- 1921,1930 ----
       </indexterm>

  <synopsis>
! <optional> <<<replaceable>label</replaceable>>> </optional>
  WHILE <replaceable>expression</replaceable> LOOP
      <replaceable>statements</replaceable>
! END LOOP <optional> <replaceable>label</replaceable> </optional>;
  </synopsis>

         <para>
***************
*** 1951,1960 ****
        <title><literal>FOR</> (integer variant)</title>

  <synopsis>
! <optional><<<replaceable>label</replaceable>>></optional>
  FOR <replaceable>name</replaceable> IN <optional> REVERSE </optional> <replaceable>expression</replaceable> ..
<replaceable>expression</replaceable>LOOP 
      <replaceable>statements</replaceable>
! END LOOP;
  </synopsis>

         <para>
--- 1952,1961 ----
        <title><literal>FOR</> (integer variant)</title>

  <synopsis>
! <optional> <<<replaceable>label</replaceable>>> </optional>
  FOR <replaceable>name</replaceable> IN <optional> REVERSE </optional> <replaceable>expression</replaceable> ..
<replaceable>expression</replaceable>LOOP 
      <replaceable>statements</replaceable>
! END LOOP <optional> <replaceable>labal</replaceable> </optional>;
  </synopsis>

         <para>
***************
*** 1997,2006 ****
       the results of a query and manipulate that data
       accordingly. The syntax is:
  <synopsis>
! <optional><<<replaceable>label</replaceable>>></optional>
  FOR <replaceable>record_or_row</replaceable> IN <replaceable>query</replaceable> LOOP
      <replaceable>statements</replaceable>
! END LOOP;
  </synopsis>
       The record or row variable is successively assigned each row
       resulting from the <replaceable>query</replaceable> (which must be a
--- 1998,2007 ----
       the results of a query and manipulate that data
       accordingly. The syntax is:
  <synopsis>
! <optional> <<<replaceable>label</replaceable>>> </optional>
  FOR <replaceable>record_or_row</replaceable> IN <replaceable>query</replaceable> LOOP
      <replaceable>statements</replaceable>
! END LOOP <optional> <replaceable>label</replaceable> </optional>;
  </synopsis>
       The record or row variable is successively assigned each row
       resulting from the <replaceable>query</replaceable> (which must be a
***************
*** 2036,2045 ****
       The <literal>FOR-IN-EXECUTE</> statement is another way to iterate over
       rows:
  <synopsis>
! <optional><<<replaceable>label</replaceable>>></optional>
  FOR <replaceable>record_or_row</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP
      <replaceable>statements</replaceable>
! END LOOP;
  </synopsis>
       This is like the previous form, except that the source
       <command>SELECT</command> statement is specified as a string
--- 2037,2046 ----
       The <literal>FOR-IN-EXECUTE</> statement is another way to iterate over
       rows:
  <synopsis>
! <optional> <<<replaceable>label</replaceable>>> </optional>
  FOR <replaceable>record_or_row</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP
      <replaceable>statements</replaceable>
! END LOOP <optional> <replaceable>label</replaceable> </optional>;
  </synopsis>
       This is like the previous form, except that the source
       <command>SELECT</command> statement is specified as a string
Index: src/pl/plpgsql/src/gram.y
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/src/pl/plpgsql/src/gram.y,v
retrieving revision 1.77
diff -c -r1.77 gram.y
*** src/pl/plpgsql/src/gram.y    22 Jun 2005 01:35:02 -0000    1.77
--- src/pl/plpgsql/src/gram.y    1 Jul 2005 12:08:45 -0000
***************
*** 56,61 ****
--- 56,63 ----
                                                 PLpgSQL_datum *initial_datum);
  static    void             check_sql_expr(const char *stmt);
  static    void             plpgsql_sql_error_callback(void *arg);
+ static    void             check_labels(const char *start_label,
+                                       const char *end_label);

  %}

***************
*** 69,75 ****
              int  lineno;
          }                        varname;
          struct
!         {
              char *name;
              int  lineno;
              PLpgSQL_rec     *rec;
--- 71,77 ----
              int  lineno;
          }                        varname;
          struct
!         {
              char *name;
              int  lineno;
              PLpgSQL_rec     *rec;
***************
*** 81,86 ****
--- 83,93 ----
              int  n_initvars;
              int  *initvarnos;
          }                        declhdr;
+         struct
+         {
+             char *end_label;
+             List *stmts;
+         }                        loop_body;
          List                    *list;
          PLpgSQL_type            *dtype;
          PLpgSQL_datum            *scalar;    /* a VAR, RECFIELD, or TRIGARG */
***************
*** 119,129 ****
  %type <forvariable>    for_variable
  %type <stmt>    for_control

! %type <str>        opt_lblname opt_label
! %type <str>        opt_exitlabel
  %type <str>        execsql_start

! %type <list>    proc_sect proc_stmts stmt_else loop_body
  %type <stmt>    proc_stmt pl_block
  %type <stmt>    stmt_assign stmt_if stmt_loop stmt_while stmt_exit
  %type <stmt>    stmt_return stmt_return_next stmt_raise stmt_execsql
--- 126,136 ----
  %type <forvariable>    for_variable
  %type <stmt>    for_control

! %type <str>        opt_lblname opt_block_label opt_label
  %type <str>        execsql_start

! %type <list>    proc_sect proc_stmts stmt_else
! %type <loop_body>    loop_body
  %type <stmt>    proc_stmt pl_block
  %type <stmt>    stmt_assign stmt_if stmt_loop stmt_while stmt_exit
  %type <stmt>    stmt_return stmt_return_next stmt_raise stmt_execsql
***************
*** 248,257 ****
                  | ';'
                  ;

! pl_block        : decl_sect K_BEGIN lno proc_sect exception_sect K_END
                      {
                          PLpgSQL_stmt_block *new;

                          new = palloc0(sizeof(PLpgSQL_stmt_block));

                          new->cmd_type    = PLPGSQL_STMT_BLOCK;
--- 255,266 ----
                  | ';'
                  ;

! pl_block        : decl_sect K_BEGIN lno proc_sect exception_sect K_END opt_label
                      {
                          PLpgSQL_stmt_block *new;

+                         check_labels($1.label, $7);
+
                          new = palloc0(sizeof(PLpgSQL_stmt_block));

                          new->cmd_type    = PLPGSQL_STMT_BLOCK;
***************
*** 269,275 ****
                  ;


! decl_sect        : opt_label
                      {
                          plpgsql_ns_setlocal(false);
                          $$.label      = $1;
--- 278,284 ----
                  ;


! decl_sect        : opt_block_label
                      {
                          plpgsql_ns_setlocal(false);
                          $$.label      = $1;
***************
*** 277,283 ****
                          $$.initvarnos = NULL;
                          plpgsql_add_initdatums(NULL);
                      }
!                 | opt_label decl_start
                      {
                          plpgsql_ns_setlocal(false);
                          $$.label      = $1;
--- 286,292 ----
                          $$.initvarnos = NULL;
                          plpgsql_add_initdatums(NULL);
                      }
!                 | opt_block_label decl_start
                      {
                          plpgsql_ns_setlocal(false);
                          $$.label      = $1;
***************
*** 285,291 ****
                          $$.initvarnos = NULL;
                          plpgsql_add_initdatums(NULL);
                      }
!                 | opt_label decl_start decl_stmts
                      {
                          plpgsql_ns_setlocal(false);
                          if ($3 != NULL)
--- 294,300 ----
                          $$.initvarnos = NULL;
                          plpgsql_add_initdatums(NULL);
                      }
!                 | opt_block_label decl_start decl_stmts
                      {
                          plpgsql_ns_setlocal(false);
                          if ($3 != NULL)
***************
*** 409,415 ****
                          plpgsql_ns_setlocal(false);
                          query = read_sql_stmt("");
                          plpgsql_ns_setlocal(true);
!
                          $$ = query;
                      }
                  ;
--- 418,424 ----
                          plpgsql_ns_setlocal(false);
                          query = read_sql_stmt("");
                          plpgsql_ns_setlocal(true);
!
                          $$ = query;
                      }
                  ;
***************
*** 757,763 ****
                           *     ...                               ...
                           * ELSE                               ELSE
                           *     ...                               ...
!                          * END IF                           END IF
                           *                               END IF
                           */
                          PLpgSQL_stmt_if *new_if;
--- 766,772 ----
                           *     ...                               ...
                           * ELSE                               ELSE
                           *     ...                               ...
!                          * END IF                           END IF
                           *                               END IF
                           */
                          PLpgSQL_stmt_if *new_if;
***************
*** 776,786 ****

                  | K_ELSE proc_sect
                      {
!                         $$ = $2;
                      }
                  ;

! stmt_loop        : opt_label K_LOOP lno loop_body
                      {
                          PLpgSQL_stmt_loop *new;

--- 785,795 ----

                  | K_ELSE proc_sect
                      {
!                         $$ = $2;
                      }
                  ;

! stmt_loop        : opt_block_label K_LOOP lno loop_body
                      {
                          PLpgSQL_stmt_loop *new;

***************
*** 788,802 ****
                          new->cmd_type = PLPGSQL_STMT_LOOP;
                          new->lineno   = $3;
                          new->label      = $1;
!                         new->body      = $4;

                          plpgsql_ns_pop();

                          $$ = (PLpgSQL_stmt *)new;
                      }
                  ;

! stmt_while        : opt_label K_WHILE lno expr_until_loop loop_body
                      {
                          PLpgSQL_stmt_while *new;

--- 797,812 ----
                          new->cmd_type = PLPGSQL_STMT_LOOP;
                          new->lineno   = $3;
                          new->label      = $1;
!                         new->body      = $4.stmts;

+                         check_labels($1, $4.end_label);
                          plpgsql_ns_pop();

                          $$ = (PLpgSQL_stmt *)new;
                      }
                  ;

! stmt_while        : opt_block_label K_WHILE lno expr_until_loop loop_body
                      {
                          PLpgSQL_stmt_while *new;

***************
*** 805,819 ****
                          new->lineno   = $3;
                          new->label      = $1;
                          new->cond      = $4;
!                         new->body      = $5;

                          plpgsql_ns_pop();

                          $$ = (PLpgSQL_stmt *)new;
                      }
                  ;

! stmt_for        : opt_label K_FOR for_control loop_body
                      {
                          /* This runs after we've scanned the loop body */
                          if ($3->cmd_type == PLPGSQL_STMT_FORI)
--- 815,830 ----
                          new->lineno   = $3;
                          new->label      = $1;
                          new->cond      = $4;
!                         new->body      = $5.stmts;

+                         check_labels($1, $5.end_label);
                          plpgsql_ns_pop();

                          $$ = (PLpgSQL_stmt *)new;
                      }
                  ;

! stmt_for        : opt_block_label K_FOR for_control loop_body
                      {
                          /* This runs after we've scanned the loop body */
                          if ($3->cmd_type == PLPGSQL_STMT_FORI)
***************
*** 822,828 ****

                              new = (PLpgSQL_stmt_fori *) $3;
                              new->label      = $1;
!                             new->body      = $4;
                              $$ = (PLpgSQL_stmt *) new;
                          }
                          else if ($3->cmd_type == PLPGSQL_STMT_FORS)
--- 833,839 ----

                              new = (PLpgSQL_stmt_fori *) $3;
                              new->label      = $1;
!                             new->body      = $4.stmts;
                              $$ = (PLpgSQL_stmt *) new;
                          }
                          else if ($3->cmd_type == PLPGSQL_STMT_FORS)
***************
*** 831,837 ****

                              new = (PLpgSQL_stmt_fors *) $3;
                              new->label      = $1;
!                             new->body      = $4;
                              $$ = (PLpgSQL_stmt *) new;
                          }
                          else
--- 842,848 ----

                              new = (PLpgSQL_stmt_fors *) $3;
                              new->label      = $1;
!                             new->body      = $4.stmts;
                              $$ = (PLpgSQL_stmt *) new;
                          }
                          else
***************
*** 841,850 ****
                              Assert($3->cmd_type == PLPGSQL_STMT_DYNFORS);
                              new = (PLpgSQL_stmt_dynfors *) $3;
                              new->label      = $1;
!                             new->body      = $4;
                              $$ = (PLpgSQL_stmt *) new;
                          }

                          /* close namespace started in opt_label */
                          plpgsql_ns_pop();
                      }
--- 852,862 ----
                              Assert($3->cmd_type == PLPGSQL_STMT_DYNFORS);
                              new = (PLpgSQL_stmt_dynfors *) $3;
                              new->label      = $1;
!                             new->body      = $4.stmts;
                              $$ = (PLpgSQL_stmt *) new;
                          }

+                         check_labels($1, $4.end_label);
                          /* close namespace started in opt_label */
                          plpgsql_ns_pop();
                      }
***************
*** 1037,1043 ****
                      }
                  ;

! stmt_exit        : exit_type lno opt_exitlabel opt_exitcond
                      {
                          PLpgSQL_stmt_exit *new;

--- 1049,1055 ----
                      }
                  ;

! stmt_exit        : exit_type lno opt_label opt_exitcond
                      {
                          PLpgSQL_stmt_exit *new;

***************
*** 1245,1252 ****
                      }
                  ;

! loop_body        : proc_sect K_END K_LOOP ';'
!                     { $$ = $1; }
                  ;

  stmt_execsql    : execsql_start lno
--- 1257,1267 ----
                      }
                  ;

! loop_body        : proc_sect K_END K_LOOP opt_label ';'
!                     {
!                         $$.stmts = $1;
!                         $$.end_label = $4;
!                     }
                  ;

  stmt_execsql    : execsql_start lno
***************
*** 1262,1268 ****
                      }
                  ;

! stmt_dynexecute : K_EXECUTE lno
                      {
                          PLpgSQL_stmt_dynexecute *new;
                          PLpgSQL_expr *expr;
--- 1277,1283 ----
                      }
                  ;

! stmt_dynexecute : K_EXECUTE lno
                      {
                          PLpgSQL_stmt_dynexecute *new;
                          PLpgSQL_expr *expr;
***************
*** 1418,1424 ****
                                               errmsg("cursor \"%s\" has no arguments",
                                                      $3->refname)));
                                  }
!
                                  if (tok != ';')
                                  {
                                      plpgsql_error_lineno = plpgsql_scanner_lineno();
--- 1433,1439 ----
                                               errmsg("cursor \"%s\" has no arguments",
                                                      $3->refname)));
                                  }
!
                                  if (tok != ';')
                                  {
                                      plpgsql_error_lineno = plpgsql_scanner_lineno();
***************
*** 1596,1602 ****
                      { $$ = plpgsql_read_expression(K_LOOP, "LOOP"); }
                  ;

! opt_label        :
                      {
                          plpgsql_ns_push(NULL);
                          $$ = NULL;
--- 1611,1617 ----
                      { $$ = plpgsql_read_expression(K_LOOP, "LOOP"); }
                  ;

! opt_block_label    :
                      {
                          plpgsql_ns_push(NULL);
                          $$ = NULL;
***************
*** 1608,1621 ****
                      }
                  ;

! opt_exitlabel    :
!                     { $$ = NULL; }
                  | T_LABEL
                      {
!                         char    *name;
!
!                         plpgsql_convert_ident(yytext, &name, 1);
!                         $$ = name;
                      }
                  | T_WORD
                      {
--- 1623,1637 ----
                      }
                  ;

! opt_label    :
!                     {
!                         $$ = NULL;
!                     }
                  | T_LABEL
                      {
!                         char *label_name;
!                         plpgsql_convert_ident(yytext, &label_name, 1);
!                         $$ = label_name;
                      }
                  | T_WORD
                      {
***************
*** 2210,2213 ****
--- 2226,2254 ----
      errposition(0);
  }

+ static void
+ check_labels(const char *start_label, const char *end_label)
+ {
+     if (end_label)
+     {
+         if (!start_label)
+         {
+             plpgsql_error_lineno = plpgsql_scanner_lineno();
+             ereport(ERROR,
+                     (errcode(ERRCODE_SYNTAX_ERROR),
+                      errmsg("end label \"%s\" specified for unlabelled block",
+                             end_label)));
+         }
+
+         if (strcmp(start_label, end_label) != 0)
+         {
+             plpgsql_error_lineno = plpgsql_scanner_lineno();
+             ereport(ERROR,
+                     (errcode(ERRCODE_SYNTAX_ERROR),
+                      errmsg("end label \"%s\" differs from block's label \"%s\"",
+                             end_label, start_label)));
+         }
+     }
+ }
+
  #include "pl_scan.c"
Index: src/test/regress/expected/plpgsql.out
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/src/test/regress/expected/plpgsql.out,v
retrieving revision 1.36
diff -c -r1.36 plpgsql.out
*** src/test/regress/expected/plpgsql.out    22 Jun 2005 07:28:47 -0000    1.36
--- src/test/regress/expected/plpgsql.out    1 Jul 2005 11:53:55 -0000
***************
*** 2491,2497 ****
  (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);
--- 2491,2497 ----
  (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);
***************
*** 2532,2538 ****
    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;
--- 2532,2538 ----
    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;
***************
*** 2666,2668 ****
--- 2666,2723 ----
  drop function continue_test2();
  drop function continue_test3();
  drop table conttesttbl;
+ -- verbose end block and end loop
+ create function end_label1() returns void as $$
+ <<blbl>>
+ begin
+   <<flbl1>>
+   for _i in 1 .. 10 loop
+     exit flbl1;
+   end loop flbl1;
+   <<flbl2>>
+   for _i in 1 .. 10 loop
+     exit flbl2;
+   end loop;
+ end blbl;
+ $$ language plpgsql;
+ select end_label1();
+  end_label1
+ ------------
+
+ (1 row)
+
+ drop function end_label1();
+ -- should fail: undefined end label
+ create function end_label2() returns void as $$
+ begin
+   for _i in 1 .. 10 loop
+     exit;
+   end loop flbl1;
+ end;
+ $$ language plpgsql;
+ ERROR:  no such label at or near "flbl1" at character 101
+ LINE 5:   end loop flbl1;
+                    ^
+ -- should fail: end label does not match start label
+ create function end_label3() returns void as $$
+ <<outer_label>>
+ begin
+   <<inner_label>>
+   for _i in 1 .. 10 loop
+     exit;
+   end loop outer_label;
+ end;
+ $$ language plpgsql;
+ ERROR:  end label "outer_label" differs from block's label "inner_label"
+ CONTEXT:  compile of PL/pgSQL function "end_label3" near line 6
+ -- should fail: end label on a block without a start label
+ create function end_label4() returns void as $$
+ <<outer_label>>
+ begin
+   for _i in 1 .. 10 loop
+     exit;
+   end loop outer_label;
+ end;
+ $$ language plpgsql;
+ ERROR:  end label "outer_label" specified for unlabelled block
+ CONTEXT:  compile of PL/pgSQL function "end_label4" near line 5
Index: src/test/regress/sql/plpgsql.sql
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/src/test/regress/sql/plpgsql.sql,v
retrieving revision 1.31
diff -c -r1.31 plpgsql.sql
*** src/test/regress/sql/plpgsql.sql    22 Jun 2005 07:28:47 -0000    1.31
--- src/test/regress/sql/plpgsql.sql    1 Jul 2005 11:43:36 -0000
***************
*** 2113,2119 ****
  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);
--- 2113,2119 ----
  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);
***************
*** 2154,2160 ****
    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;
--- 2154,2160 ----
    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;
***************
*** 2232,2234 ****
--- 2232,2282 ----
  drop function continue_test2();
  drop function continue_test3();
  drop table conttesttbl;
+
+ -- verbose end block and end loop
+ create function end_label1() returns void as $$
+ <<blbl>>
+ begin
+   <<flbl1>>
+   for _i in 1 .. 10 loop
+     exit flbl1;
+   end loop flbl1;
+   <<flbl2>>
+   for _i in 1 .. 10 loop
+     exit flbl2;
+   end loop;
+ end blbl;
+ $$ language plpgsql;
+
+ select end_label1();
+ drop function end_label1();
+
+ -- should fail: undefined end label
+ create function end_label2() returns void as $$
+ begin
+   for _i in 1 .. 10 loop
+     exit;
+   end loop flbl1;
+ end;
+ $$ language plpgsql;
+
+ -- should fail: end label does not match start label
+ create function end_label3() returns void as $$
+ <<outer_label>>
+ begin
+   <<inner_label>>
+   for _i in 1 .. 10 loop
+     exit;
+   end loop outer_label;
+ end;
+ $$ language plpgsql;
+
+ -- should fail: end label on a block without a start label
+ create function end_label4() returns void as $$
+ <<outer_label>>
+ begin
+   for _i in 1 .. 10 loop
+     exit;
+   end loop outer_label;
+ end;
+ $$ language plpgsql;

pgsql-hackers by date:

Previous
From: Oliver Jowett
Date:
Subject: Re: 2PC transaction id
Next
From: Stephen Frost
Date:
Subject: Re: [PATCHES] Users/Groups -> Roles