Add BY clause to PL/PgSQL FOR loop, to control the iteration increment.
authorBruce Momjian
Mon, 12 Jun 2006 16:45:30 +0000 (16:45 +0000)
committerBruce Momjian
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 @@
-
+
 
  
   <application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language
@@ -1975,7 +1975,7 @@ END LOOP;
 
 
  <<label>> 
-FOR name IN  REVERSE  expression .. expression LOOP
+FOR name IN  REVERSE  expression .. expression  BY expression  LOOP
     statements
 END LOOP  label ;
 
@@ -1988,8 +1988,10 @@ END LOOP  label ;
         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 REVERSE is
-        specified.
+        the loop. If the BY clause isn't specified the iteration 
+        step is 1 otherwise it's the value specified in the BY 
+        clause. If REVERSE is specified then the step value is 
+       considered negative.
        
 
        
@@ -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;
 
        
 
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;        }