by Pavel Stehule, and reviewed by Neil Conway.
-<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.108 2007/04/28 23:54:58 neilc Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.109 2007/04/29 01:21:08 neilc Exp $ -->
<chapter id="plpgsql">
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
true if it returns a row, false if no row is returned.
</para>
</listitem>
+ <listitem>
+ <para>
+ A <command>MOVE</> statement sets <literal>FOUND</literal>
+ true if it successfully repositions the cursor, false otherwise.
+ </para>
+ </listitem>
+
<listitem>
<para>
A <command>FOR</> statement sets <literal>FOUND</literal> true
FETCH curs2 INTO foo, bar, baz;
FETCH LAST FROM curs3 INTO x, y;
FETCH RELATIVE -2 FROM curs4 INTO x;
+</programlisting>
+ </para>
+ </sect3>
+
+ <sect3>
+ <title><literal>MOVE</></title>
+
+<synopsis>
+MOVE <optional> <replaceable>direction</replaceable> { FROM | IN } </optional> <replaceable>cursor</replaceable>;
+</synopsis>
+
+ <para>
+ <command>MOVE</command> repositions a cursor without retrieving
+ any data. <command>MOVE</command> works exactly like the
+ <command>FETCH</command> command, except it only positions the
+ cursor and does not return rows. As with <command>SELECT
+ INTO</command>, the special variable <literal>FOUND</literal> can
+ be checked to see whether the cursor was successfully
+ repositioned or not.
+ </para>
+
+ <para>
+ The <replaceable>direction</replaceable> clause can be any of the
+ variants allowed in the SQL <xref linkend="sql-move"
+ endterm="sql-move-title"> command except the ones that can move by
+ more than one row; namely, it can be
+ <literal>NEXT</>,
+ <literal>PRIOR</>,
+ <literal>FIRST</>,
+ <literal>LAST</>,
+ <literal>ABSOLUTE</> <replaceable>count</replaceable>,
+ <literal>RELATIVE</> <replaceable>count</replaceable>,
+ <literal>FORWARD</>, or
+ <literal>BACKWARD</>.
+ Omitting <replaceable>direction</replaceable> is the same
+ as specifying <literal>NEXT</>.
+ <replaceable>direction</replaceable> values that require moving
+ backward are likely to fail unless the cursor was declared or opened
+ with the <literal>SCROLL</> option.
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+MOVE curs1;
+MOVE LAST FROM curs3;
+MOVE RELATIVE -2 FROM curs4;
</programlisting>
</para>
</sect3>
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.101 2007/04/28 23:54:59 neilc Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.102 2007/04/29 01:21:09 neilc Exp $
*
*-------------------------------------------------------------------------
*/
%type <stmt> stmt_assign stmt_if stmt_loop stmt_while stmt_exit
%type <stmt> stmt_return stmt_raise stmt_execsql stmt_execsql_insert
%type <stmt> stmt_dynexecute stmt_for stmt_perform stmt_getdiag
-%type <stmt> stmt_open stmt_fetch stmt_close stmt_null
+%type <stmt> stmt_open stmt_fetch stmt_move stmt_close stmt_null
%type <list> proc_exceptions
%type <exception_block> exception_sect
%token K_IS
%token K_LOG
%token K_LOOP
+%token K_MOVE
%token K_NEXT
%token K_NOSCROLL
%token K_NOT
{ $$ = $1; }
| stmt_fetch
{ $$ = $1; }
+ | stmt_move
+ { $$ = $1; }
| stmt_close
{ $$ = $1; }
| stmt_null
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;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.195 2007/04/19 16:33:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.196 2007/04/29 01:21:09 neilc Exp $
*
*-------------------------------------------------------------------------
*/
/* ----------
- * exec_stmt_fetch Fetch from a cursor into a target
+ * exec_stmt_fetch Fetch from a cursor into a target, or just
+ * move the current position of the cursor
* ----------
*/
static int
exec_eval_cleanup(estate);
}
- /* ----------
- * 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");
+ 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");
- /* ----------
- * Fetch 1 tuple from the cursor
- * ----------
- */
- SPI_scroll_cursor_fetch(portal, stmt->direction, how_many);
- tuptab = SPI_tuptable;
- n = SPI_processed;
+ /* ----------
+ * 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);
+ /* ----------
+ * 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
{
- exec_move_row(estate, rec, row, tuptab->vals[0], tuptab->tupdesc);
- exec_set_found(estate, true);
- }
+ /* Move the cursor */
+ SPI_scroll_cursor_move(portal, stmt->direction, how_many);
+ n = SPI_processed;
- SPI_freetuptable(tuptab);
+ /* Set the global FOUND variable appropriately. */
+ exec_set_found(estate, n != 0);
+ }
return PLPGSQL_RC_OK;
}
-
/* ----------
* exec_stmt_close Close a cursor
* ----------
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.58 2007/03/18 05:36:49 neilc Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.59 2007/04/29 01:21:09 neilc Exp $
*
*-------------------------------------------------------------------------
*/
static void dump_getdiag(PLpgSQL_stmt_getdiag *stmt);
static void dump_open(PLpgSQL_stmt_open *stmt);
static void dump_fetch(PLpgSQL_stmt_fetch *stmt);
+static void dump_cursor_direction(PLpgSQL_stmt_fetch *stmt);
static void dump_close(PLpgSQL_stmt_close *stmt);
static void dump_perform(PLpgSQL_stmt_perform *stmt);
static void dump_expr(PLpgSQL_expr *expr);
dump_fetch(PLpgSQL_stmt_fetch *stmt)
{
dump_ind();
- printf("FETCH curvar=%d\n", stmt->curvar);
+
+ 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
+dump_cursor_direction(PLpgSQL_stmt_fetch *stmt)
+{
dump_indent += 2;
- if (stmt->rec != NULL)
+ dump_ind();
+ switch (stmt->direction)
{
- dump_ind();
- printf(" target = %d %s\n", stmt->rec->recno, stmt->rec->refname);
+ 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", stmt->direction);
}
- if (stmt->row != NULL)
+
+ if (stmt->expr)
{
- dump_ind();
- printf(" target = %d %s\n", stmt->row->rowno, stmt->row->refname);
+ dump_expr(stmt->expr);
+ printf("\n");
}
+ else
+ printf("%d\n", stmt->how_many);
+
dump_indent -= 2;
-
}
static void
printf("\nEnd of execution tree of function %s\n\n", func->fn_name);
fflush(stdout);
}
+
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.87 2007/04/16 17:21:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.88 2007/04/29 01:21:09 neilc Exp $
*
*-------------------------------------------------------------------------
*/
typedef struct
-{ /* FETCH statement */
+{ /* FETCH or MOVE statement */
int cmd_type;
int lineno;
PLpgSQL_rec *rec; /* target, as record or row */
FetchDirection direction; /* fetch direction */
int how_many; /* count, if constant (expr is NULL) */
PLpgSQL_expr *expr; /* count, if expression */
+ bool is_move; /* is this a fetch or move? */
} PLpgSQL_stmt_fetch;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.56 2007/04/16 17:21:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.57 2007/04/29 01:21:09 neilc Exp $
*
*-------------------------------------------------------------------------
*/
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; }
0
(3 rows)
+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;
+ loop
+ move relative 2 in c;
+ if not found then
+ exit;
+ end if;
+ fetch next from c into x;
+ if found then
+ return next x;
+ end if;
+ end loop;
+ close c;
+end;
+$$ language plpgsql;
+select * from sc_test();
+ sc_test
+---------
+ 3
+ 6
+ 9
+(3 rows)
+
drop function sc_test();
select * from 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;
+ loop
+ move relative 2 in c;
+ if not found then
+ exit;
+ end if;
+ fetch next from c into x;
+ if found then
+ return next x;
+ end if;
+ end loop;
+ close c;
+end;
+$$ language plpgsql;
+
+select * from sc_test();
+
drop function sc_test();
+