*
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.102 2007/04/29 01:21:09 neilc Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.103 2007/07/15 02:15:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
                        }
 
                        check_labels($1, $4.end_label);
-                       /* close namespace started in opt_label */
+                       /* close namespace started in opt_block_label */
                        plpgsql_ns_pop();
                    }
                ;
                                PLpgSQL_stmt_fori   *new;
                                char                *varname;
 
-                               /* First expression is well-formed */
+                               /* Check first expression is well-formed */
                                check_sql_expr(expr1->query);
 
-
-                               expr2 = read_sql_construct(K_BY,
-                                                          K_LOOP,
+                               /* Read and check the second one */
+                               expr2 = read_sql_construct(K_LOOP,
+                                                          K_BY,
                                                           "LOOP",
                                                           "SELECT ",
                                                           true,
-                                                          false,
+                                                          true,
                                                           &tok);
 
+                               /* Get the BY clause if any */
                                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;
-                               }
+                                   expr_by = NULL;
 
-                               /* should have had a single variable name */
+                               /* Should have had a single variable name */
                                plpgsql_error_lineno = $2.lineno;
                                if ($2.scalar && $2.row)
                                    ereport(ERROR,
                                new->reverse  = reverse;
                                new->lower    = expr1;
                                new->upper    = expr2;
-                               new->by       = expr_by;
+                               new->step     = expr_by;
 
                                $$ = (PLpgSQL_stmt *) new;
                            }
 
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.197 2007/06/05 21:31:08 tgl Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.198 2007/07/15 02:15:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 /* ----------
  * exec_stmt_fori          Iterate an integer variable
  *                 from a lower to an upper value
- *                 incrementing or decrementing in BY value
- *                 Loop can be left with exit.
+ *                 incrementing or decrementing by the BY value
  * ----------
  */
 static int
 {
    PLpgSQL_var *var;
    Datum       value;
-   Datum       by_value;
-   Oid         valtype;
    bool        isnull;
+   Oid         valtype;
+   int32       loop_value;
+   int32       end_value;
+   int32       step_value;
    bool        found = false;
    int         rc = PLPGSQL_RC_OK;
 
    var = (PLpgSQL_var *) (estate->datums[stmt->var->varno]);
 
    /*
-    * Get the value of the lower bound into the loop var
+    * Get the value of the lower bound
     */
    value = exec_eval_expr(estate, stmt->lower, &isnull, &valtype);
    value = exec_cast_value(value, valtype, var->datatype->typoid,
        ereport(ERROR,
                (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                 errmsg("lower bound of FOR loop cannot be NULL")));
-   var->value = value;
-   var->isnull = false;
+   loop_value = DatumGetInt32(value);
    exec_eval_cleanup(estate);
 
    /*
        ereport(ERROR,
                (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                 errmsg("upper bound of FOR loop cannot be NULL")));
+   end_value = DatumGetInt32(value);
    exec_eval_cleanup(estate);
 
    /*
-    * Get the by value
+    * Get the step 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);
+   if (stmt->step)
+   {
+       value = exec_eval_expr(estate, stmt->step, &isnull, &valtype);
+       value = exec_cast_value(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")));
+       step_value = DatumGetInt32(value);
+       exec_eval_cleanup(estate);
+       if (step_value <= 0)
+           ereport(ERROR,
+                   (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                    errmsg("BY value of FOR loop must be greater than zero")));
+   }
+   else
+       step_value = 1;
 
    /*
     * Now do the loop
    for (;;)
    {
        /*
-        * Check bounds
+        * Check against upper bound
         */
        if (stmt->reverse)
        {
-           if ((int4) (var->value) < (int4) value)
+           if (loop_value < end_value)
                break;
        }
        else
        {
-           if ((int4) (var->value) > (int4) value)
+           if (loop_value > end_value)
                break;
        }
 
        found = true;           /* looped at least once */
 
+       /*
+        * Assign current value to loop var
+        */
+       var->value = Int32GetDatum(loop_value);
+       var->isnull = false;
+
        /*
         * Execute the statements
         */
             * 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 re-run the current loop */
+               /* unlabelled continue, so re-run the current loop */
                rc = PLPGSQL_RC_OK;
            else if (stmt->label != NULL &&
                     strcmp(stmt->label, estate->exitlabel) == 0)
        }
 
        /*
-        * Increase/decrease loop var
+        * Increase/decrease loop value, unless it would overflow, in which
+        * case exit the loop.
         */
        if (stmt->reverse)
-           var->value -= by_value;
+       {
+           if ((int32) (loop_value - step_value) > loop_value)
+               break;
+           loop_value -= step_value;
+       }
        else
-           var->value += by_value;
+       {
+           if ((int32) (loop_value + step_value) < loop_value)
+               break;
+           loop_value += step_value;
+       }
    }
 
    /*