*
*
* 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;
+ }
}
/*