diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index 164a2179a31e78365958016a7f3e9a21bbc0e1f7..0f41c999724b5726fad8710064ecd7df0d74cf56 100644
--- a/src/pl/plpgsql/src/gram.y
+++ b/src/pl/plpgsql/src/gram.y
@@ -9,7 +9,7 @@
  *
  *
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -876,7 +876,7 @@ stmt_for		: opt_block_label K_FOR for_control loop_body
 						}
 
 						check_labels($1, $4.end_label);
-						/* close namespace started in opt_label */
+						/* close namespace started in opt_block_label */
 						plpgsql_ns_pop();
 					}
 				;
@@ -968,39 +968,25 @@ for_control		:
 								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,
@@ -1023,7 +1009,7 @@ for_control		:
 								new->reverse  = reverse;
 								new->lower	  = expr1;
 								new->upper	  = expr2;
-								new->by		  = expr_by;
+								new->step	  = expr_by;
 
 								$$ = (PLpgSQL_stmt *) new;
 							}
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 2582935452d0150c76ff429476c414f8fa00b4d8..9527fdc61d5aa9c3324acf1ebdf004717c596209 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -8,7 +8,7 @@
  *
  *
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -1517,8 +1517,7 @@ exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
 /* ----------
  * 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
@@ -1526,16 +1525,18 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
 {
 	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,
@@ -1546,8 +1547,7 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
 		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);
 
 	/*
@@ -1562,22 +1562,32 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
 		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
@@ -1585,21 +1595,27 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
 	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
 		 */
@@ -1625,13 +1641,12 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
 			 * 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)
@@ -1652,12 +1667,21 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
 		}
 
 		/*
-		 * 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;
+		}
 	}
 
 	/*
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index c344c9e4ea7ee32f7bb9acb4af842f854ba760cf..4d5eba73e3b43e7c3c96f74e6844cff37d7b1f3c 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.59 2007/04/29 01:21:09 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.60 2007/07/15 02:15:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -701,8 +701,8 @@ dump_fori(PLpgSQL_stmt_fori *stmt)
 	dump_expr(stmt->upper);
 	printf("\n");
 	dump_ind();
-	printf("    by = ");
-	dump_expr(stmt->by);
+	printf("    step = ");
+	dump_expr(stmt->step);
 	printf("\n");
 	dump_indent -= 2;
 
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 20ee074564a101d23eba4fcbfc269bb8eb1e168e..dce7be2b1bd29fe3f09c71d45812ed6b08e13dca 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.88 2007/04/29 01:21:09 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.89 2007/07/15 02:15:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -402,7 +402,7 @@ typedef struct
 	PLpgSQL_var *var;
 	PLpgSQL_expr *lower;
 	PLpgSQL_expr *upper;
-	PLpgSQL_expr *by;
+	PLpgSQL_expr *step;			/* NULL means default (ie, BY 1) */
 	int			reverse;
 	List	   *body;			/* List of statements */
 } PLpgSQL_stmt_fori;