diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index f0cbbf2896cef9e02c486f1b29d0711a1bb5848b..60c7593362beb388cf1e7543607c6a5ae217b691 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.94 2006/05/30 13:40:55 momjian Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.95 2006/06/12 16:45:30 momjian Exp $ --> <chapter id="plpgsql"> <title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title> @@ -1975,7 +1975,7 @@ END LOOP; <synopsis> <optional> <<<replaceable>label</replaceable>>> </optional> -FOR <replaceable>name</replaceable> IN <optional> REVERSE </optional> <replaceable>expression</replaceable> .. <replaceable>expression</replaceable> LOOP +FOR <replaceable>name</replaceable> IN <optional> REVERSE </optional> <replaceable>expression</replaceable> .. <replaceable>expression</replaceable> <optional> BY <replaceable>expression</replaceable> </optional> LOOP <replaceable>statements</replaceable> END LOOP <optional> <replaceable>label</replaceable> </optional>; </synopsis> @@ -1988,8 +1988,10 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>; 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 <literal>REVERSE</> is - specified. + the loop. If the <literal>BY</> clause isn't specified the iteration + step is 1 otherwise it's the value specified in the <literal>BY</> + clause. If <literal>REVERSE</> is specified then the step value is + considered negative. </para> <para> @@ -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; </programlisting> </para> diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index 2461deaf328d7580342e54068b5b4473c81b37d8..5343dfb1978af529315c9b9c601266719898010d 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 b27849c8913e541717e1e83d30048a2bed85c7d1..3ac48bbcecb55e24871fffc2bf228ef30cf65a43 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 9420ab15cf37bffff52c3262a01c53611cb1d89e..a4e661a44af6cb2cebb468a81c409ff5a019cf23 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 86fea3ca465620ce16358ccdc6136f215feabf8e..16ffe7e93dfe0a4445f2f457ad4d18e2e812b93a 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 dfc2b942ecb92bc6c25fbb632d6b405cf005ab8e..daafe96b87499f02783cf2ec5eb94a831f6e3631 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; }