]> granicus.if.org Git - postgresql/commitdiff
Allow PL/pgSQL FOR statement to return values to scalars as well as
authorBruce Momjian <bruce@momjian.us>
Sun, 12 Feb 2006 06:03:38 +0000 (06:03 +0000)
committerBruce Momjian <bruce@momjian.us>
Sun, 12 Feb 2006 06:03:38 +0000 (06:03 +0000)
records and row types.

Pavel Stehule

doc/src/sgml/plpgsql.sgml
src/pl/plpgsql/src/gram.y
src/test/regress/expected/plpgsql.out
src/test/regress/sql/plpgsql.sql

index 517adc9d51de26bcedea0a6c28ccbf9e1c1a2bb6..0c3839cd4dc0efb531c8bfa0a91c9cd9e693d135 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.84 2006/02/05 02:47:53 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.85 2006/02/12 06:03:38 momjian Exp $
 -->
 
 <chapter id="plpgsql"> 
@@ -2008,11 +2008,13 @@ END LOOP;
      accordingly. The syntax is:
 <synopsis>
 <optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
-FOR <replaceable>record_or_row</replaceable> IN <replaceable>query</replaceable> LOOP
+FOR <replaceable>target</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
+     <replaceable>Target</replaceable> is a record variable, row variable, 
+     or a comma-separated list of  simple variables and record/row fields
+     which is successively assigned each row
      resulting from the <replaceable>query</replaceable> (which must be a
      <command>SELECT</command> command) and the loop body is executed for each
      row. Here is an example:
@@ -2047,7 +2049,7 @@ $$ LANGUAGE plpgsql;
      rows:
 <synopsis>
 <optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
-FOR <replaceable>record_or_row</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP 
+FOR <replaceable>target</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP 
     <replaceable>statements</replaceable>
 END LOOP <optional> <replaceable>label</replaceable> </optional>;
 </synopsis>
@@ -2067,7 +2069,7 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
      <literal>IN</> and <literal>LOOP</>.  If <literal>..</> is not seen then
      the loop is presumed to be a loop over rows.  Mistyping the <literal>..</>
      is thus likely to lead to a complaint along the lines of
-     <quote>loop variable of loop over rows must be a record or row variable</>,
+     <quote>loop variable of loop over rows must be a record or row or scalar variable</>,
      rather than the simple syntax error one might expect to get.
     </para>
     </note>
index 72735873c4e5336f076b7e450308a453b93a8ab7..a23e57a158ba6bb35913cb1a767165fc6d909bbd 100644 (file)
@@ -4,7 +4,7 @@
  *                                               procedural language
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.83 2006/02/12 04:59:32 tgl Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.84 2006/02/12 06:03:38 momjian Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -58,7 +58,9 @@ 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);
-
+static PLpgSQL_row       *make_scalar_list1(const char *name,
+                                           PLpgSQL_datum *variable);
 %}
 
 %union {
@@ -76,6 +78,7 @@ static        void                     check_labels(const char *start_label,
                        int  lineno;
                        PLpgSQL_rec     *rec;
                        PLpgSQL_row     *row;
+                       PLpgSQL_datum   *scalar;
                }                                               forvariable;
                struct
                {
@@ -890,10 +893,15 @@ for_control               :
                                                                new->row = $2.row;
                                                                check_assignable((PLpgSQL_datum *) new->row);
                                                        }
+                                                       else if ($2.scalar)
+                                                       {
+                                                       new->row = make_scalar_list1($2.name, $2.scalar);
+                                                               check_assignable((PLpgSQL_datum *) new->row);
+                                                       }
                                                        else
                                                        {
                                                                plpgsql_error_lineno = $1;
-                                                               yyerror("loop variable of loop over rows must be a record or row variable");
+                                                               yyerror("loop variable of loop over rows must be a record, row, or scalar variable");
                                                        }
                                                        new->query = expr;
 
@@ -948,6 +956,15 @@ for_control                :
 
                                                                expr2 = plpgsql_read_expression(K_LOOP, "LOOP");
 
+                                                               /* T_SCALAR identifier waits for converting */
+                                                               if ($2.scalar)
+                                                               {
+                                                                   char *name;
+                                                                   plpgsql_convert_ident($2.name, &name, 1);
+                                                                   pfree($2.name);
+                                                                   $2.name = name;
+                                                               }
+
                                                                /* create loop's private variable */
                                                                fvar = (PLpgSQL_var *)
                                                                        plpgsql_build_variable($2.name,
@@ -1002,10 +1019,15 @@ for_control             :
                                                                        new->row = $2.row;
                                                                        check_assignable((PLpgSQL_datum *) new->row);
                                                                }
+                                                               else if ($2.scalar)
+                                                               {
+                                                               new->row = make_scalar_list1($2.name, $2.scalar);
+                                                                       check_assignable((PLpgSQL_datum *) new->row);
+                                                               }
                                                                else
                                                                {
                                                                        plpgsql_error_lineno = $1;
-                                                                       yyerror("loop variable of loop over rows must be record or row variable");
+                                                                       yyerror("loop variable of loop over rows must be record, row, or scalar variable");
                                                                }
 
                                                                new->query = expr1;
@@ -1027,14 +1049,31 @@ for_control             :
  * until we know what's what.
  */
 for_variable   : T_SCALAR
-                                       {
+                                       {
+                                               int tok;
                                                char            *name;
+                                               
+                                               name = pstrdup(yytext);
+                                               $$.scalar = yylval.scalar;
+                                               $$.lineno = plpgsql_scanner_lineno();
 
-                                               plpgsql_convert_ident(yytext, &name, 1);
-                                               $$.name = name;
-                                               $$.lineno  = plpgsql_scanner_lineno();
-                                               $$.rec = NULL;
-                                               $$.row = NULL;
+                                               if((tok = yylex()) == ',')
+                                               {
+                                                   plpgsql_push_back_token(tok);
+                                                   $$.name = NULL;
+                                                   $$.row = read_into_scalar_list(name, $$.scalar);
+                                                   $$.rec = NULL;
+                                                   $$.scalar = NULL;
+                                                   
+                                                   pfree(name);
+                                               }
+                                               else
+                                               {
+                                                   plpgsql_push_back_token(tok);
+                                                   $$.name = name;
+                                                   $$.row = NULL;
+                                                   $$.rec = NULL;
+                                               }
                                        }
                                | T_WORD
                                        {
@@ -1048,20 +1087,14 @@ for_variable    : T_SCALAR
                                        }
                                | T_RECORD
                                        {
-                                               char            *name;
-
-                                               plpgsql_convert_ident(yytext, &name, 1);
-                                               $$.name = name;
+                                               $$.name = NULL;
                                                $$.lineno  = plpgsql_scanner_lineno();
                                                $$.rec = yylval.rec;
                                                $$.row = NULL;
                                        }
                                | T_ROW
                                        {
-                                               char            *name;
-
-                                               plpgsql_convert_ident(yytext, &name, 1);
-                                               $$.name = name;
+                                               $$.name = NULL;
                                                $$.lineno  = plpgsql_scanner_lineno();
                                                $$.row = yylval.row;
                                                $$.rec = NULL;
@@ -2088,6 +2121,30 @@ make_fetch_stmt(void)
 }
 
 
+static PLpgSQL_row *
+make_scalar_list1(const char *name,
+                                 PLpgSQL_datum *variable)
+{
+       PLpgSQL_row             *row;
+       check_assignable(variable);
+       
+       row = palloc(sizeof(PLpgSQL_row));
+       row->dtype = PLPGSQL_DTYPE_ROW;
+       row->refname = pstrdup("*internal*");
+       row->lineno = plpgsql_scanner_lineno();
+       row->rowtupdesc = NULL;
+       row->nfields = 1;
+       row->fieldnames = palloc(sizeof(char *) * 1);
+       row->varnos = palloc(sizeof(int) * 1);
+       row->fieldnames[0] = pstrdup(name);
+       row->varnos[0] = variable->dno;
+       plpgsql_adddatum((PLpgSQL_datum *)row);
+       return row;
+} 
+
+
 static void
 check_assignable(PLpgSQL_datum *datum)
 {
index aef73e1eee79611cb0f43d08a26e5510e36bdc3a..3bcafcfb4d0918bd672dc64299e848fa1d747b79 100644 (file)
@@ -2721,3 +2721,23 @@ end;
 $$ language plpgsql;
 ERROR:  end label "outer_label" specified for unlabelled block
 CONTEXT:  compile of PL/pgSQL function "end_label4" near line 5
+-- using list of scalars in fori and fore stmts
+create function for_vect() returns void as $$
+<<lbl>>declare a integer; b varchar; c varchar; r record;
+begin
+  -- old fori
+  for i in 1 .. 10 loop
+    raise notice '%', i;
+  end loop;
+  for a in select 1 from generate_series(1,4) loop
+    raise notice '%', a;
+  end loop;
+  for a,b,c in select generate_series, 'BB','CC' from generate_series(1,4) loop
+    raise notice '% % %', a, b, c;
+  end loop;
+  -- using qualified names in fors, fore is enabled, disabled only for fori
+  for lbl.a, lbl.b, lbl.c in execute E'select generate_series, \'bb\',\'cc\' from generate_series(1,4)' loop
+    raise notice '% % %', a, b, c;
+  end loop;
+end;
+$$ language plpgsql;
index fdb2f46ff89d720bf24ff3989f64ea8c8ff85475..63b8bef14eb6f337136cb7a92f9df899d8e59541 100644 (file)
@@ -2280,3 +2280,25 @@ begin
   end loop outer_label;
 end;
 $$ language plpgsql;
+
+
+-- using list of scalars in fori and fore stmts
+create function for_vect() returns void as $$
+<<lbl>>declare a integer; b varchar; c varchar; r record;
+begin
+  -- old fori
+  for i in 1 .. 10 loop
+    raise notice '%', i;
+  end loop;
+  for a in select 1 from generate_series(1,4) loop
+    raise notice '%', a;
+  end loop;
+  for a,b,c in select generate_series, 'BB','CC' from generate_series(1,4) loop
+    raise notice '% % %', a, b, c;
+  end loop;
+  -- using qualified names in fors, fore is enabled, disabled only for fori
+  for lbl.a, lbl.b, lbl.c in execute E'select generate_series, \'bb\',\'cc\' from generate_series(1,4)' loop
+    raise notice '% % %', a, b, c;
+  end loop;
+end;
+$$ language plpgsql;