Support for MOVE in PL/PgSQL. Initial patch from Magnus, some improvements

by Pavel Stehule, and reviewed by Neil Conway.
This commit is contained in:
Neil Conway 2007-04-29 01:21:09 +00:00
parent f2321a3f37
commit 8690ebc26f
8 changed files with 227 additions and 48 deletions

View File

@ -1,4 +1,4 @@
<!-- $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>
@ -1522,6 +1522,13 @@ GET DIAGNOSTICS integer_var = ROW_COUNT;
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
@ -2562,6 +2569,53 @@ FETCH curs1 INTO rowvar;
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>

View File

@ -9,7 +9,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@ -125,7 +125,7 @@ static void check_labels(const char *start_label,
%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
@ -179,6 +179,7 @@ static void check_labels(const char *start_label,
%token K_IS
%token K_LOG
%token K_LOOP
%token K_MOVE
%token K_NEXT
%token K_NOSCROLL
%token K_NOT
@ -635,6 +636,8 @@ proc_stmt : pl_block ';'
{ $$ = $1; }
| stmt_fetch
{ $$ = $1; }
| stmt_move
{ $$ = $1; }
| stmt_close
{ $$ = $1; }
| stmt_null
@ -1478,6 +1481,19 @@ stmt_fetch : K_FETCH lno opt_fetch_direction cursor_variable K_INTO
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;
}

View File

@ -8,7 +8,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@ -3114,7 +3114,8 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
/* ----------
* 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
@ -3163,46 +3164,57 @@ exec_stmt_fetch(PLpgSQL_execstate *estate, PLpgSQL_stmt_fetch *stmt)
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");
/* ----------
* 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)
if (!stmt->is_move)
{
exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
exec_set_found(estate, false);
/* ----------
* 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);
}
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
* ----------

View File

@ -8,7 +8,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@ -493,6 +493,7 @@ static void dump_dynfors(PLpgSQL_stmt_dynfors *stmt);
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);
@ -761,21 +762,64 @@ static void
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
@ -1067,3 +1111,4 @@ plpgsql_dumptree(PLpgSQL_function *func)
printf("\nEnd of execution tree of function %s\n\n", func->fn_name);
fflush(stdout);
}

View File

@ -8,7 +8,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@ -446,7 +446,7 @@ typedef struct
typedef struct
{ /* FETCH statement */
{ /* FETCH or MOVE statement */
int cmd_type;
int lineno;
PLpgSQL_rec *rec; /* target, as record or row */
@ -455,6 +455,7 @@ typedef struct
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;

View File

@ -9,7 +9,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@ -142,6 +142,7 @@ into { return K_INTO; }
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; }

View File

@ -3023,4 +3023,31 @@ select * from sc_test();
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();

View File

@ -2511,4 +2511,27 @@ $$ language plpgsql;
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();