*** ./doc/src/sgml/plpgsql.sgml.orig 2007-04-20 09:01:50.000000000 +0200 --- ./doc/src/sgml/plpgsql.sgml 2007-04-20 09:09:04.000000000 +0200 *************** *** 1524,1529 **** --- 1524,1536 ---- + A MOVE statement sets FOUND + true if is success, false if is out of table. + + + + + A FOR statement sets FOUND true if it iterates one or more times, else false. This applies to all three variants of the FOR statement (integer *************** *** 2567,2572 **** --- 2574,2624 ---- + <literal>MOVE</> + + + MOVE direction FROM cursor; + + + + MOVE repositions a cursor without retrieving any data. MOVE works + exactly like the FETCH command, except it only positions the + cursor and does not return rows. As with SELECT + INTO, the special variable FOUND can + be checked to see whether a cursor was repositioned or not. + + + + The direction clause can be any of the + variants allowed in the SQL command except the ones that can fetch + more than one row; namely, it can be + NEXT, + PRIOR, + FIRST, + LAST, + ABSOLUTE count, + RELATIVE count, + FORWARD, or + BACKWARD. + Omitting direction is the same + as specifying NEXT. + direction values that require moving + backward are likely to fail unless the cursor was declared or opened + with the SCROLL option. + + + + Examples: + + MOVE curs1; + MOVE LAST FROM curs3; + MOVE RELATIVE -2 FROM curs4; + + + + + <literal>CLOSE</> *** ./src/pl/plpgsql/src/gram.y.orig 2007-04-19 19:15:28.000000000 +0200 --- ./src/pl/plpgsql/src/gram.y 2007-04-19 19:39:36.000000000 +0200 *************** *** 125,131 **** %type stmt_assign stmt_if stmt_loop stmt_while stmt_exit %type stmt_return stmt_raise stmt_execsql stmt_execsql_insert %type stmt_dynexecute stmt_for stmt_perform stmt_getdiag ! %type stmt_open stmt_fetch stmt_close stmt_null %type proc_exceptions %type exception_sect --- 125,131 ---- %type stmt_assign stmt_if stmt_loop stmt_while stmt_exit %type stmt_return stmt_raise stmt_execsql stmt_execsql_insert %type stmt_dynexecute stmt_for stmt_perform stmt_getdiag ! %type stmt_open stmt_fetch stmt_move stmt_close stmt_null %type proc_exceptions %type exception_sect *************** *** 179,184 **** --- 179,185 ---- %token K_IS %token K_LOG %token K_LOOP + %token K_MOVE %token K_NEXT %token K_NOSCROLL %token K_NOT *************** *** 635,640 **** --- 636,643 ---- { $$ = $1; } | stmt_fetch { $$ = $1; } + | stmt_move + { $$ = $1; } | stmt_close { $$ = $1; } | stmt_null *************** *** 1478,1483 **** --- 1481,1499 ---- fetch->rec = rec; fetch->row = row; fetch->curvar = $4->varno; + fetch->is_move = false; + + $$ = (PLpgSQL_stmt *)fetch; + } + ; + + stmt_move : K_MOVE lno opt_fetch_direction cursor_variable ';' + { + PLpgSQL_stmt_fetch *fetch = $3; + + fetch->lineno = $2; + fetch->curvar = $4->varno; + fetch->is_move = true; $$ = (PLpgSQL_stmt *)fetch; } *** ./src/pl/plpgsql/src/pl_exec.c.orig 2007-04-20 09:24:27.000000000 +0200 --- ./src/pl/plpgsql/src/pl_exec.c 2007-04-20 09:25:14.000000000 +0200 *************** *** 3112,3118 **** return PLPGSQL_RC_OK; } - /* ---------- * exec_stmt_fetch Fetch from a cursor into a target * ---------- --- 3112,3117 ---- *************** *** 3164,3208 **** } /* ---------- ! * Determine if we fetch into a record or a row ! * ---------- ! */ ! if (stmt->rec != NULL) ! rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]); ! else if (stmt->row != NULL) ! row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]); ! else ! elog(ERROR, "unsupported target"); ! ! /* ---------- ! * Fetch 1 tuple from the cursor * ---------- */ ! SPI_scroll_cursor_fetch(portal, stmt->direction, how_many); ! tuptab = SPI_tuptable; ! n = SPI_processed; ! ! /* ---------- ! * Set the target and the global FOUND variable appropriately. ! * ---------- ! */ ! if (n == 0) { ! exec_move_row(estate, rec, row, NULL, tuptab->tupdesc); ! exec_set_found(estate, false); } else { ! exec_move_row(estate, rec, row, tuptab->vals[0], tuptab->tupdesc); ! exec_set_found(estate, true); } - SPI_freetuptable(tuptab); - return PLPGSQL_RC_OK; } - /* ---------- * exec_stmt_close Close a cursor * ---------- --- 3163,3224 ---- } /* ---------- ! * Fetch 1 tuple from the cursor or move * ---------- */ ! if (!stmt->is_move) { ! /* ---------- ! * Determine if we fetch into a record or a row ! * ---------- ! */ ! if (stmt->rec != NULL) ! rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]); ! else if (stmt->row != NULL) ! row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]); ! else ! elog(ERROR, "unsupported target"); ! ! SPI_scroll_cursor_fetch(portal, stmt->direction, how_many); ! tuptab = SPI_tuptable; ! n = SPI_processed; ! ! /* ---------- ! * Set the target and the global FOUND variable appropriately. ! * ---------- ! */ ! if (n == 0) ! { ! exec_move_row(estate, rec, row, NULL, tuptab->tupdesc); ! exec_set_found(estate, false); ! } ! else ! { ! exec_move_row(estate, rec, row, tuptab->vals[0], tuptab->tupdesc); ! exec_set_found(estate, true); ! } ! ! SPI_freetuptable(tuptab); } else { ! /* ---------- ! * Move a cursor ! * ---------- ! */ ! SPI_scroll_cursor_move(portal, stmt->direction, how_many); ! n = SPI_processed; ! ! /* ---------- ! * Set global FOUND variable appropriately. ! * ---------- ! */ ! exec_set_found(estate, n != 0); } return PLPGSQL_RC_OK; } /* ---------- * exec_stmt_close Close a cursor * ---------- *** ./src/pl/plpgsql/src/pl_funcs.c.orig 2007-04-20 08:46:12.000000000 +0200 --- ./src/pl/plpgsql/src/pl_funcs.c 2007-04-20 08:43:01.000000000 +0200 *************** *** 496,501 **** --- 496,502 ---- static void dump_close(PLpgSQL_stmt_close *stmt); static void dump_perform(PLpgSQL_stmt_perform *stmt); static void dump_expr(PLpgSQL_expr *expr); + static void dump_cursor_direction(PLpgSQL_stmt_fetch *stmt); static void *************** *** 761,781 **** dump_fetch(PLpgSQL_stmt_fetch *stmt) { dump_ind(); ! printf("FETCH curvar=%d\n", stmt->curvar); ! ! dump_indent += 2; ! if (stmt->rec != NULL) { ! dump_ind(); ! printf(" target = %d %s\n", stmt->rec->recno, stmt->rec->refname); } ! if (stmt->row != NULL) { ! dump_ind(); ! printf(" target = %d %s\n", stmt->row->rowno, stmt->row->refname); } - dump_indent -= 2; - } static void --- 762,791 ---- dump_fetch(PLpgSQL_stmt_fetch *stmt) { dump_ind(); ! ! if (!stmt->is_move) { ! printf("FETCH curvar=%d\n", stmt->curvar); ! dump_cursor_direction(stmt); ! ! dump_indent += 2; ! if (stmt->rec != NULL) ! { ! dump_ind(); ! printf(" target = %d %s\n", stmt->rec->recno, stmt->rec->refname); ! } ! if (stmt->row != NULL) ! { ! dump_ind(); ! printf(" target = %d %s\n", stmt->row->rowno, stmt->row->refname); ! } ! dump_indent -= 2; } ! else { ! printf("MOVE curvar=%d\n", stmt->curvar); ! dump_cursor_direction(stmt); } } static void *************** *** 1067,1069 **** --- 1077,1113 ---- printf("\nEnd of execution tree of function %s\n\n", func->fn_name); fflush(stdout); } + + static void + dump_cursor_direction(PLpgSQL_stmt_fetch *stmt) + { + dump_indent += 2; + dump_ind(); + switch (stmt->direction) + { + case FETCH_FORWARD: + printf(" FORWARD "); + break; + case FETCH_BACKWARD: + printf(" BACKWARD "); + break; + case FETCH_ABSOLUTE: + printf(" ABSOLUTE "); + break; + case FETCH_RELATIVE: + printf(" RELATIVE "); + break; + default: + printf("??? unknown cursor direction %d\n", stmt->direction); + } + + if (stmt->expr) + { + dump_expr(stmt->expr); + printf("\n"); + } + else + printf("%d\n", stmt->how_many); + + dump_indent -= 2; + } *** ./src/pl/plpgsql/src/plpgsql.h.orig 2007-04-19 19:19:55.000000000 +0200 --- ./src/pl/plpgsql/src/plpgsql.h 2007-04-19 19:21:39.000000000 +0200 *************** *** 446,452 **** typedef struct ! { /* FETCH statement */ int cmd_type; int lineno; PLpgSQL_rec *rec; /* target, as record or row */ --- 446,452 ---- typedef struct ! { /* FETCH or MOVE statement */ int cmd_type; int lineno; PLpgSQL_rec *rec; /* target, as record or row */ *************** *** 455,460 **** --- 455,461 ---- FetchDirection direction; /* fetch direction */ int how_many; /* count, if constant (expr is NULL) */ PLpgSQL_expr *expr; /* count, if expression */ + bool is_move; /* Is this an fetch or move? */ } PLpgSQL_stmt_fetch; *** ./src/pl/plpgsql/src/scan.l.orig 2007-04-19 19:15:34.000000000 +0200 --- ./src/pl/plpgsql/src/scan.l 2007-04-19 19:16:19.000000000 +0200 *************** *** 142,147 **** --- 142,148 ---- is { return K_IS; } log { return K_LOG; } loop { return K_LOOP; } + move { return K_MOVE; } next { return K_NEXT; } no{space}+scroll { return K_NOSCROLL; } not { return K_NOT; } *** ./src/test/regress/expected/plpgsql.out.orig 2007-04-20 08:57:51.000000000 +0200 --- ./src/test/regress/expected/plpgsql.out 2007-04-20 08:56:57.000000000 +0200 *************** *** 3024,3026 **** --- 3024,3051 ---- (3 rows) drop function sc_test(); + create or replace function sc_test() returns setof integer as $$ + declare + c cursor for select * from generate_series(1,10); + x integer; + begin + open c; + fetch c into x; + while found loop + return next x; + move relative 2 from c; + fetch c into x; + end loop; + close c; + end; + $$ language plpgsql; + select * from sc_test(); + sc_test + --------- + 1 + 4 + 7 + 10 + (4 rows) + + drop function sc_test(); *** ./src/test/regress/sql/plpgsql.sql.orig 2007-04-20 08:50:20.000000000 +0200 --- ./src/test/regress/sql/plpgsql.sql 2007-04-20 08:55:43.000000000 +0200 *************** *** 2512,2514 **** --- 2512,2535 ---- select * from sc_test(); drop function sc_test(); + + create or replace function sc_test() returns setof integer as $$ + declare + c cursor for select * from generate_series(1,10); + x integer; + begin + open c; + fetch c into x; + while found loop + return next x; + move relative 2 from c; + fetch c into x; + end loop; + close c; + end; + $$ language plpgsql; + + select * from sc_test(); + + drop function sc_test(); +