Add BY clause to PL/PgSQL FOR loop, to control the iteration increment.
authorBruce Momjian <bruce@momjian.us>
Mon, 12 Jun 2006 16:45:30 +0000 (16:45 +0000)
committerBruce Momjian <bruce@momjian.us>
Mon, 12 Jun 2006 16:45:30 +0000 (16:45 +0000)
Jaime Casanova

doc/src/sgml/plpgsql.sgml
src/pl/plpgsql/src/gram.y
src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/pl_funcs.c
src/pl/plpgsql/src/plpgsql.h
src/pl/plpgsql/src/scan.l

index f0cbbf2896cef9e02c486f1b29d0711a1bb5848b..60c7593362beb388cf1e7543607c6a5ae217b691 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.94 2006/05/30 13:40:55 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.95 2006/06/12 16:45:30 momjian Exp $ -->
 
 <chapter id="plpgsql"> 
   <title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
@@ -1975,7 +1975,7 @@ END LOOP;
 
 <synopsis>
 <optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
-FOR <replaceable>name</replaceable> IN <optional> REVERSE </optional> <replaceable>expression</replaceable> .. <replaceable>expression</replaceable> LOOP
+FOR <replaceable>name</replaceable> IN <optional> REVERSE </optional> <replaceable>expression</replaceable> .. <replaceable>expression</replaceable> <optional> BY <replaceable>expression</replaceable> </optional> LOOP
     <replaceable>statements</replaceable>
 END LOOP <optional> <replaceable>label</replaceable> </optional>;
 </synopsis>
@@ -1988,8 +1988,10 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
         definition of the variable name is ignored within the loop).
         The two expressions giving
         the lower and upper bound of the range are evaluated once when entering
-        the loop. The iteration step is normally 1, but is -1 when <literal>REVERSE</> is
-        specified.
+        the loop. If the <literal>BY</> clause isn't specified the iteration 
+        step is 1 otherwise it's the value specified in the <literal>BY</> 
+        clause. If <literal>REVERSE</> is specified then the step value is 
+               considered negative.
        </para>
 
        <para>
@@ -2003,6 +2005,11 @@ END LOOP;
 FOR i IN REVERSE 10..1 LOOP
     -- some computations here
 END LOOP;
+
+FOR i IN REVERSE 10..1 BY 2 LOOP
+    -- some computations here
+    RAISE NOTICE 'i is %', i;
+END LOOP;
 </programlisting>
        </para>
 
index 2461deaf328d7580342e54068b5b4473c81b37d8..5343dfb1978af529315c9b9c601266719898010d 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.90 2006/05/27 19:45:52 tgl Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.91 2006/06/12 16:45:30 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -144,6 +144,7 @@ static      void                     check_labels(const char *start_label,
 %token K_ALIAS
 %token K_ASSIGN
 %token K_BEGIN
+%token K_BY
 %token K_CLOSE
 %token K_CONSTANT
 %token K_CONTINUE
@@ -935,6 +936,7 @@ for_control         :
                                                        {
                                                                /* Saw "..", so it must be an integer loop */
                                                                PLpgSQL_expr            *expr2;
+                                                               PLpgSQL_expr            *expr_by;
                                                                PLpgSQL_var                     *fvar;
                                                                PLpgSQL_stmt_fori       *new;
                                                                char                            *varname;
@@ -942,7 +944,34 @@ for_control                :
                                                                /* First expression is well-formed */
                                                                check_sql_expr(expr1->query);
 
-                                                               expr2 = plpgsql_read_expression(K_LOOP, "LOOP");
+
+                                                               expr2 = read_sql_construct(K_BY,
+                                                                                                                  K_LOOP,
+                                                                                                                  "LOOP",
+                                                                                                                  "SELECT ",
+                                                                                                                  true,
+                                                                                                                  false,
+                                                                                                                  &tok);
+
+                                                               if (tok == K_BY) 
+                                                                       expr_by = plpgsql_read_expression(K_LOOP, "LOOP");
+                                                               else
+                                                               {
+                                                                       /*
+                                                                        * If there is no BY clause we will assume 1
+                                                                        */
+                                                                       char buf[1024];
+                                                                       PLpgSQL_dstring         ds;
+
+                                                                       plpgsql_dstring_init(&ds);
+
+                                                                       expr_by = palloc0(sizeof(PLpgSQL_expr));
+                                                                       expr_by->dtype                  = PLPGSQL_DTYPE_EXPR;
+                                                                       strcpy(buf, "SELECT 1");
+                                                                       plpgsql_dstring_append(&ds, buf);
+                                                                       expr_by->query                      = pstrdup(plpgsql_dstring_get(&ds));
+                                                                       expr_by->plan                           = NULL;
+                                                               }
 
                                                                /* should have had a single variable name */
                                                                plpgsql_error_lineno = $2.lineno;
@@ -970,6 +999,7 @@ for_control         :
                                                                new->reverse  = reverse;
                                                                new->lower        = expr1;
                                                                new->upper        = expr2;
+                                                               new->by           = expr_by;
 
                                                                $$ = (PLpgSQL_stmt *) new;
                                                        }
index b27849c8913e541717e1e83d30048a2bed85c7d1..3ac48bbcecb55e24871fffc2bf228ef30cf65a43 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.169 2006/05/30 13:40:55 momjian Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.170 2006/06/12 16:45:30 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1361,7 +1361,8 @@ exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
 
 /* ----------
  * exec_stmt_fori                      Iterate an integer variable
- *                                     from a lower to an upper value.
+ *                                     from a lower to an upper value
+ *                                     incrementing or decrementing in BY value
  *                                     Loop can be left with exit.
  * ----------
  */
@@ -1370,6 +1371,7 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
 {
        PLpgSQL_var *var;
        Datum           value;
+       Datum           by_value;
        Oid                     valtype;
        bool            isnull;
        bool            found = false;
@@ -1407,6 +1409,21 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
                                 errmsg("upper bound of FOR loop cannot be NULL")));
        exec_eval_cleanup(estate);
 
+       /*
+        * Get the by value 
+        */
+       by_value = exec_eval_expr(estate, stmt->by, &isnull, &valtype);
+       by_value = exec_cast_value(by_value, valtype, var->datatype->typoid,
+                                                          &(var->datatype->typinput),
+                                                          var->datatype->typioparam,
+                                                          var->datatype->atttypmod, isnull);
+
+       if (isnull)
+               ereport(ERROR,
+                               (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                                errmsg("by value of FOR loop cannot be NULL")));
+       exec_eval_cleanup(estate);
+
        /*
         * Now do the loop
         */
@@ -1483,9 +1500,9 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
                 * Increase/decrease loop var
                 */
                if (stmt->reverse)
-                       var->value--;
+                       var->value -= by_value;
                else
-                       var->value++;
+                       var->value += by_value;
        }
 
        /*
index 9420ab15cf37bffff52c3262a01c53611cb1d89e..a4e661a44af6cb2cebb468a81c409ff5a019cf23 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.52 2006/05/30 13:40:55 momjian Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.53 2006/06/12 16:45:30 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -705,6 +705,10 @@ dump_fori(PLpgSQL_stmt_fori *stmt)
        printf("    upper = ");
        dump_expr(stmt->upper);
        printf("\n");
+       dump_ind();
+       printf("    by = ");
+       dump_expr(stmt->by);
+       printf("\n");
        dump_indent -= 2;
 
        dump_stmts(stmt->body);
index 86fea3ca465620ce16358ccdc6136f215feabf8e..16ffe7e93dfe0a4445f2f457ad4d18e2e812b93a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.74 2006/05/30 13:40:55 momjian Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.75 2006/06/12 16:45:30 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -398,6 +398,7 @@ typedef struct
        PLpgSQL_var *var;
        PLpgSQL_expr *lower;
        PLpgSQL_expr *upper;
+       PLpgSQL_expr *by;
        int                     reverse;
        List       *body;                       /* List of statements */
 } PLpgSQL_stmt_fori;
index dfc2b942ecb92bc6c25fbb632d6b405cf005ab8e..daafe96b87499f02783cf2ec5eb94a831f6e3631 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.49 2006/05/30 13:40:55 momjian Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.50 2006/06/12 16:45:30 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -116,6 +116,7 @@ dolqinside          [^$]+
 \.\.                   { return K_DOTDOT;                      }
 alias                  { return K_ALIAS;                       }
 begin                  { return K_BEGIN;                       }
+by                             { return K_BY;                          }
 close                  { return K_CLOSE;                       }
 constant               { return K_CONSTANT;            }
 continue               { return K_CONTINUE;            }