diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index f0cbbf2896..60c7593362 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -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; diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index 2461deaf32..5343dfb197 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.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; } diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index b27849c891..3ac48bbcec 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.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; } /* diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c index 9420ab15cf..a4e661a44a 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.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); diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 86fea3ca46..16ffe7e93d 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.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; diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l index dfc2b942ec..daafe96b87 100644 --- a/src/pl/plpgsql/src/scan.l +++ b/src/pl/plpgsql/src/scan.l @@ -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; }