mirror of
https://github.com/postgres/postgres.git
synced 2025-06-13 07:41:39 +03:00
This patch improves the behavior of FOUND in PL/PgSQL. In Oracle,
FOUND is set whenever a SELECT INTO returns > 0 rows, *or* when an INSERT, UPDATE, or DELETE affects > 0 rows. We implemented the first part of this behavior, but not the second. I also improved the documentation on the various situations in which FOUND can be set (excluding inside FOR loops, which I still need to think about), and added some regression tests for this behavior. Neil Conway
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$Header: /cvsroot/pgsql/doc/src/sgml/plpgsql.sgml,v 1.1 2002/07/30 19:36:10 momjian Exp $
|
$Header: /cvsroot/pgsql/doc/src/sgml/plpgsql.sgml,v 1.2 2002/08/20 05:28:23 momjian Exp $
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<chapter id="plpgsql">
|
<chapter id="plpgsql">
|
||||||
@ -126,7 +126,7 @@ END;
|
|||||||
them to define operators or use them in functional indexes.
|
them to define operators or use them in functional indexes.
|
||||||
</para>
|
</para>
|
||||||
<sect2 id="plpgsql-advantages">
|
<sect2 id="plpgsql-advantages">
|
||||||
<title>Advantages of Using PL/pgSQL</title>
|
<title>Advantages of Using <application>PL/pgSQL</application></title>
|
||||||
|
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
<listitem>
|
<listitem>
|
||||||
@ -852,10 +852,58 @@ SELECT INTO <replaceable>target</replaceable> <replaceable>expressions</replacea
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
There is a special variable named FOUND of type
|
There is a special variable named <literal>FOUND</literal> of
|
||||||
<type>boolean</type> that can be used immediately after a SELECT
|
type <type>boolean</type>. The initial value of
|
||||||
INTO to check if an assignment had success (that is, at least one
|
<literal>FOUND</literal> is false; it is set to true when one of
|
||||||
row was returned by the SELECT). For example,
|
the following events occurs:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
A SELECT INTO statement is executed, and it returns one or
|
||||||
|
more rows.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
A UPDATE, INSERT, or DELETE statement is executed, and it
|
||||||
|
affects one or more rows.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
A PERFORM statement is executed, and it discards one or more
|
||||||
|
rows.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
A FETCH statement is executed, and it returns an additional
|
||||||
|
row.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
A FOR statement is executed, and it iterates one or more
|
||||||
|
times. This applies to all three variants of the FOR statement
|
||||||
|
(integer FOR loops, record-set FOR loops, and dynamic
|
||||||
|
record-set FOR loops). <literal>FOUND</literal> is only set
|
||||||
|
when the FOR loop exits: inside the execution of the loop,
|
||||||
|
<literal>FOUND</literal> is not modified, although it may be
|
||||||
|
set by the execution of other statements.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
If none of these events occur, <literal>FOUND</literal> is set to
|
||||||
|
false. <literal>FOUND</literal> is a local variable; any changes
|
||||||
|
to it effect only the current <application>PL/pgSQL</application>
|
||||||
|
function.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
You can use <literal>FOUND</literal> immediately after a SELECT
|
||||||
|
INTO statement to determine whether the assignment was successful
|
||||||
|
(that is, at least one row was was returned by the SELECT
|
||||||
|
statement). For example:
|
||||||
|
|
||||||
<programlisting>
|
<programlisting>
|
||||||
SELECT INTO myrec * FROM EMP WHERE empname = myname;
|
SELECT INTO myrec * FROM EMP WHERE empname = myname;
|
||||||
@ -902,10 +950,10 @@ PERFORM <replaceable>query</replaceable>;
|
|||||||
|
|
||||||
This executes a <literal>SELECT</literal>
|
This executes a <literal>SELECT</literal>
|
||||||
<replaceable>query</replaceable> and discards the
|
<replaceable>query</replaceable> and discards the
|
||||||
result. <application>PL/pgSQL</application> variables are substituted
|
result. <application>PL/pgSQL</application> variables are
|
||||||
in the query as usual. Also, the special variable FOUND is set to
|
substituted in the query as usual. Also, the special variable
|
||||||
true if the query produced at least one row, or false if it produced
|
<literal>FOUND</literal> is set to true if the query produced at
|
||||||
no rows.
|
least one row, or false if it produced no rows.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<note>
|
<note>
|
||||||
@ -1638,8 +1686,8 @@ FETCH <replaceable>cursor</replaceable> INTO <replaceable>target</replaceable>;
|
|||||||
FETCH retrieves the next row from the cursor into a target,
|
FETCH retrieves the next row from the cursor into a target,
|
||||||
which may be a row variable, a record variable, or a comma-separated
|
which may be a row variable, a record variable, or a comma-separated
|
||||||
list of simple variables, just like SELECT INTO. As with
|
list of simple variables, just like SELECT INTO. As with
|
||||||
SELECT INTO, the special variable FOUND may be checked to see
|
SELECT INTO, the special variable <literal>FOUND</literal> may be
|
||||||
whether a row was obtained or not.
|
checked to see whether a row was obtained or not.
|
||||||
|
|
||||||
<programlisting>
|
<programlisting>
|
||||||
FETCH curs1 INTO rowvar;
|
FETCH curs1 INTO rowvar;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.56 2002/06/24 23:12:06 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.57 2002/08/20 05:28:23 momjian Exp $
|
||||||
*
|
*
|
||||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -1180,7 +1180,8 @@ exec_stmt_fori(PLpgSQL_execstate * estate, PLpgSQL_stmt_fori * stmt)
|
|||||||
Datum value;
|
Datum value;
|
||||||
Oid valtype;
|
Oid valtype;
|
||||||
bool isnull = false;
|
bool isnull = false;
|
||||||
int rc;
|
bool found = false;
|
||||||
|
int rc = PLPGSQL_RC_OK;
|
||||||
|
|
||||||
var = (PLpgSQL_var *) (estate->datums[stmt->var->varno]);
|
var = (PLpgSQL_var *) (estate->datums[stmt->var->varno]);
|
||||||
|
|
||||||
@ -1213,7 +1214,6 @@ exec_stmt_fori(PLpgSQL_execstate * estate, PLpgSQL_stmt_fori * stmt)
|
|||||||
/*
|
/*
|
||||||
* Now do the loop
|
* Now do the loop
|
||||||
*/
|
*/
|
||||||
exec_set_found(estate, false);
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -1229,36 +1229,36 @@ exec_stmt_fori(PLpgSQL_execstate * estate, PLpgSQL_stmt_fori * stmt)
|
|||||||
if ((int4) (var->value) > (int4) value)
|
if ((int4) (var->value) > (int4) value)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
exec_set_found(estate, true);
|
|
||||||
|
found = true; /* looped at least once */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execute the statements
|
* Execute the statements
|
||||||
*/
|
*/
|
||||||
rc = exec_stmts(estate, stmt->body);
|
rc = exec_stmts(estate, stmt->body);
|
||||||
|
|
||||||
/*
|
if (rc == PLPGSQL_RC_RETURN)
|
||||||
* Check returncode
|
break; /* return from function */
|
||||||
*/
|
else if (rc == PLPGSQL_RC_EXIT)
|
||||||
switch (rc)
|
|
||||||
{
|
{
|
||||||
case PLPGSQL_RC_OK:
|
if (estate->exitlabel == NULL)
|
||||||
break;
|
/* unlabelled exit, finish the current loop */
|
||||||
|
rc = PLPGSQL_RC_OK;
|
||||||
case PLPGSQL_RC_EXIT:
|
else if (stmt->label != NULL &&
|
||||||
if (estate->exitlabel == NULL)
|
strcmp(stmt->label, estate->exitlabel) == 0)
|
||||||
return PLPGSQL_RC_OK;
|
{
|
||||||
if (stmt->label == NULL)
|
/* labelled exit, matches the current stmt's label */
|
||||||
return PLPGSQL_RC_EXIT;
|
|
||||||
if (strcmp(stmt->label, estate->exitlabel))
|
|
||||||
return PLPGSQL_RC_EXIT;
|
|
||||||
estate->exitlabel = NULL;
|
estate->exitlabel = NULL;
|
||||||
return PLPGSQL_RC_OK;
|
rc = PLPGSQL_RC_OK;
|
||||||
|
}
|
||||||
|
|
||||||
case PLPGSQL_RC_RETURN:
|
/*
|
||||||
return PLPGSQL_RC_RETURN;
|
* otherwise, we processed a labelled exit that does not
|
||||||
|
* match the current statement's label, if any: return
|
||||||
|
* RC_EXIT so that the EXIT continues to recurse upward.
|
||||||
|
*/
|
||||||
|
|
||||||
default:
|
break;
|
||||||
elog(ERROR, "unknown rc %d from exec_stmts()", rc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1270,7 +1270,15 @@ exec_stmt_fori(PLpgSQL_execstate * estate, PLpgSQL_stmt_fori * stmt)
|
|||||||
var->value++;
|
var->value++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PLPGSQL_RC_OK;
|
/*
|
||||||
|
* Set the FOUND variable to indicate the result of executing the
|
||||||
|
* loop (namely, whether we looped one or more times). This must be
|
||||||
|
* set here so that it does not interfere with the value of the
|
||||||
|
* FOUND variable inside the loop processing itself.
|
||||||
|
*/
|
||||||
|
exec_set_found(estate, found);
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1288,15 +1296,11 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
|
|||||||
PLpgSQL_row *row = NULL;
|
PLpgSQL_row *row = NULL;
|
||||||
SPITupleTable *tuptab;
|
SPITupleTable *tuptab;
|
||||||
Portal portal;
|
Portal portal;
|
||||||
int rc;
|
bool found = false;
|
||||||
|
int rc = PLPGSQL_RC_OK;
|
||||||
int i;
|
int i;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize the global found variable to false
|
|
||||||
*/
|
|
||||||
exec_set_found(estate, false);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine if we assign to a record or a row
|
* Determine if we assign to a record or a row
|
||||||
*/
|
*/
|
||||||
@ -1321,25 +1325,18 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
|
|||||||
tuptab = SPI_tuptable;
|
tuptab = SPI_tuptable;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the query didn't return any row, set the target to NULL and
|
* If the query didn't return any rows, set the target to NULL and
|
||||||
* return.
|
* return with FOUND = false.
|
||||||
*/
|
*/
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
{
|
|
||||||
exec_move_row(estate, rec, row, NULL, NULL);
|
exec_move_row(estate, rec, row, NULL, NULL);
|
||||||
SPI_cursor_close(portal);
|
else
|
||||||
return PLPGSQL_RC_OK;
|
found = true; /* processed at least one tuple */
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* There are tuples, so set found to true
|
|
||||||
*/
|
|
||||||
exec_set_found(estate, true);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now do the loop
|
* Now do the loop
|
||||||
*/
|
*/
|
||||||
for (;;)
|
while (n > 0)
|
||||||
{
|
{
|
||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
{
|
{
|
||||||
@ -1353,35 +1350,36 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
|
|||||||
*/
|
*/
|
||||||
rc = exec_stmts(estate, stmt->body);
|
rc = exec_stmts(estate, stmt->body);
|
||||||
|
|
||||||
/*
|
if (rc != PLPGSQL_RC_OK)
|
||||||
* Check returncode
|
|
||||||
*/
|
|
||||||
switch (rc)
|
|
||||||
{
|
{
|
||||||
case PLPGSQL_RC_OK:
|
/*
|
||||||
break;
|
* We're aborting the loop, so cleanup and set FOUND
|
||||||
|
*/
|
||||||
case PLPGSQL_RC_EXIT:
|
exec_set_found(estate, found);
|
||||||
SPI_freetuptable(tuptab);
|
SPI_freetuptable(tuptab);
|
||||||
SPI_cursor_close(portal);
|
SPI_cursor_close(portal);
|
||||||
|
|
||||||
|
if (rc == PLPGSQL_RC_EXIT)
|
||||||
|
{
|
||||||
if (estate->exitlabel == NULL)
|
if (estate->exitlabel == NULL)
|
||||||
return PLPGSQL_RC_OK;
|
/* unlabelled exit, finish the current loop */
|
||||||
if (stmt->label == NULL)
|
rc = PLPGSQL_RC_OK;
|
||||||
return PLPGSQL_RC_EXIT;
|
else if (stmt->label != NULL &&
|
||||||
if (strcmp(stmt->label, estate->exitlabel))
|
strcmp(stmt->label, estate->exitlabel) == 0)
|
||||||
return PLPGSQL_RC_EXIT;
|
{
|
||||||
estate->exitlabel = NULL;
|
/* labelled exit, matches the current stmt's label */
|
||||||
return PLPGSQL_RC_OK;
|
estate->exitlabel = NULL;
|
||||||
|
rc = PLPGSQL_RC_OK;
|
||||||
|
}
|
||||||
|
|
||||||
case PLPGSQL_RC_RETURN:
|
/*
|
||||||
SPI_freetuptable(tuptab);
|
* otherwise, we processed a labelled exit that does not
|
||||||
SPI_cursor_close(portal);
|
* match the current statement's label, if any: return
|
||||||
|
* RC_EXIT so that the EXIT continues to recurse upward.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
return PLPGSQL_RC_RETURN;
|
return rc;
|
||||||
|
|
||||||
default:
|
|
||||||
elog(ERROR, "unknown rc %d from exec_stmts()", rc);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1393,9 +1391,6 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
|
|||||||
SPI_cursor_fetch(portal, true, 50);
|
SPI_cursor_fetch(portal, true, 50);
|
||||||
n = SPI_processed;
|
n = SPI_processed;
|
||||||
tuptab = SPI_tuptable;
|
tuptab = SPI_tuptable;
|
||||||
|
|
||||||
if (n == 0)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1403,14 +1398,22 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
|
|||||||
*/
|
*/
|
||||||
SPI_cursor_close(portal);
|
SPI_cursor_close(portal);
|
||||||
|
|
||||||
return PLPGSQL_RC_OK;
|
/*
|
||||||
|
* Set the FOUND variable to indicate the result of executing the
|
||||||
|
* loop (namely, whether we looped one or more times). This must be
|
||||||
|
* set here so that it does not interfere with the value of the
|
||||||
|
* FOUND variable inside the loop processing itself.
|
||||||
|
*/
|
||||||
|
exec_set_found(estate, found);
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* exec_stmt_select Run a query and assign the first
|
* exec_stmt_select Run a query and assign the first
|
||||||
* row to a record or rowtype.
|
* row to a record or rowtype.
|
||||||
* ----------
|
* ----------
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt)
|
exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt)
|
||||||
@ -1845,6 +1848,11 @@ exec_stmt_execsql(PLpgSQL_execstate * estate,
|
|||||||
PLpgSQL_expr *expr = stmt->sqlstmt;
|
PLpgSQL_expr *expr = stmt->sqlstmt;
|
||||||
bool isnull;
|
bool isnull;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set magic FOUND variable to false
|
||||||
|
*/
|
||||||
|
exec_set_found(estate, false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* On the first call for this expression generate the plan
|
* On the first call for this expression generate the plan
|
||||||
*/
|
*/
|
||||||
@ -1921,9 +1929,18 @@ exec_stmt_execsql(PLpgSQL_execstate * estate,
|
|||||||
{
|
{
|
||||||
case SPI_OK_UTILITY:
|
case SPI_OK_UTILITY:
|
||||||
case SPI_OK_SELINTO:
|
case SPI_OK_SELINTO:
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the INSERT, DELETE, or UPDATE query affected at least
|
||||||
|
* one tuple, set the magic 'FOUND' variable to true. This
|
||||||
|
* conforms with the behavior of PL/SQL.
|
||||||
|
*/
|
||||||
case SPI_OK_INSERT:
|
case SPI_OK_INSERT:
|
||||||
case SPI_OK_DELETE:
|
case SPI_OK_DELETE:
|
||||||
case SPI_OK_UPDATE:
|
case SPI_OK_UPDATE:
|
||||||
|
if (SPI_processed > 0)
|
||||||
|
exec_set_found(estate, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SPI_OK_SELECT:
|
case SPI_OK_SELECT:
|
||||||
@ -1931,8 +1948,7 @@ exec_stmt_execsql(PLpgSQL_execstate * estate,
|
|||||||
"\n\tIf you want to discard the results, use PERFORM instead.");
|
"\n\tIf you want to discard the results, use PERFORM instead.");
|
||||||
|
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "error executing query \"%s\"",
|
elog(ERROR, "error executing query \"%s\"", expr->query);
|
||||||
expr->query);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2078,7 +2094,7 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
|
|||||||
PLpgSQL_rec *rec = NULL;
|
PLpgSQL_rec *rec = NULL;
|
||||||
PLpgSQL_row *row = NULL;
|
PLpgSQL_row *row = NULL;
|
||||||
SPITupleTable *tuptab;
|
SPITupleTable *tuptab;
|
||||||
int rc;
|
int rc = PLPGSQL_RC_OK;
|
||||||
int i;
|
int i;
|
||||||
int n;
|
int n;
|
||||||
HeapTuple typetup;
|
HeapTuple typetup;
|
||||||
@ -2086,11 +2102,7 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
|
|||||||
FmgrInfo finfo_output;
|
FmgrInfo finfo_output;
|
||||||
void *plan;
|
void *plan;
|
||||||
Portal portal;
|
Portal portal;
|
||||||
|
bool found = false;
|
||||||
/*
|
|
||||||
* Initialize the global found variable to false
|
|
||||||
*/
|
|
||||||
exec_set_found(estate, false);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine if we assign to a record or a row
|
* Determine if we assign to a record or a row
|
||||||
@ -2153,25 +2165,18 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
|
|||||||
tuptab = SPI_tuptable;
|
tuptab = SPI_tuptable;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the query didn't return any row, set the target to NULL and
|
* If the query didn't return any rows, set the target to NULL and
|
||||||
* return.
|
* return with FOUND = false.
|
||||||
*/
|
*/
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
{
|
|
||||||
exec_move_row(estate, rec, row, NULL, NULL);
|
exec_move_row(estate, rec, row, NULL, NULL);
|
||||||
SPI_cursor_close(portal);
|
else
|
||||||
return PLPGSQL_RC_OK;
|
found = true;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* There are tuples, so set found to true
|
|
||||||
*/
|
|
||||||
exec_set_found(estate, true);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now do the loop
|
* Now do the loop
|
||||||
*/
|
*/
|
||||||
for (;;)
|
while (n > 0)
|
||||||
{
|
{
|
||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
{
|
{
|
||||||
@ -2186,34 +2191,35 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
|
|||||||
rc = exec_stmts(estate, stmt->body);
|
rc = exec_stmts(estate, stmt->body);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check returncode
|
* We're aborting the loop, so cleanup and set FOUND
|
||||||
*/
|
*/
|
||||||
switch (rc)
|
if (rc != PLPGSQL_RC_OK)
|
||||||
{
|
{
|
||||||
case PLPGSQL_RC_OK:
|
exec_set_found(estate, found);
|
||||||
break;
|
SPI_freetuptable(tuptab);
|
||||||
|
SPI_cursor_close(portal);
|
||||||
case PLPGSQL_RC_EXIT:
|
|
||||||
SPI_freetuptable(tuptab);
|
|
||||||
SPI_cursor_close(portal);
|
|
||||||
|
|
||||||
|
if (rc == PLPGSQL_RC_EXIT)
|
||||||
|
{
|
||||||
if (estate->exitlabel == NULL)
|
if (estate->exitlabel == NULL)
|
||||||
return PLPGSQL_RC_OK;
|
/* unlabelled exit, finish the current loop */
|
||||||
if (stmt->label == NULL)
|
rc = PLPGSQL_RC_OK;
|
||||||
return PLPGSQL_RC_EXIT;
|
else if (stmt->label != NULL &&
|
||||||
if (strcmp(stmt->label, estate->exitlabel))
|
strcmp(stmt->label, estate->exitlabel) == 0)
|
||||||
return PLPGSQL_RC_EXIT;
|
{
|
||||||
estate->exitlabel = NULL;
|
/* labelled exit, matches the current stmt's label */
|
||||||
return PLPGSQL_RC_OK;
|
estate->exitlabel = NULL;
|
||||||
|
rc = PLPGSQL_RC_OK;
|
||||||
|
}
|
||||||
|
|
||||||
case PLPGSQL_RC_RETURN:
|
/*
|
||||||
SPI_freetuptable(tuptab);
|
* otherwise, we processed a labelled exit that does not
|
||||||
SPI_cursor_close(portal);
|
* match the current statement's label, if any: return
|
||||||
|
* RC_EXIT so that the EXIT continues to recurse upward.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
return PLPGSQL_RC_RETURN;
|
return rc;
|
||||||
|
|
||||||
default:
|
|
||||||
elog(ERROR, "unknown rc %d from exec_stmts()", rc);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2225,9 +2231,6 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
|
|||||||
SPI_cursor_fetch(portal, true, 50);
|
SPI_cursor_fetch(portal, true, 50);
|
||||||
n = SPI_processed;
|
n = SPI_processed;
|
||||||
tuptab = SPI_tuptable;
|
tuptab = SPI_tuptable;
|
||||||
|
|
||||||
if (n == 0)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2235,6 +2238,14 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
|
|||||||
*/
|
*/
|
||||||
SPI_cursor_close(portal);
|
SPI_cursor_close(portal);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the FOUND variable to indicate the result of executing the
|
||||||
|
* loop (namely, whether we looped one or more times). This must be
|
||||||
|
* set here so that it does not interfere with the value of the
|
||||||
|
* FOUND variable inside the loop processing itself.
|
||||||
|
*/
|
||||||
|
exec_set_found(estate, found);
|
||||||
|
|
||||||
return PLPGSQL_RC_OK;
|
return PLPGSQL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2615,7 +2626,7 @@ exec_stmt_close(PLpgSQL_execstate * estate, PLpgSQL_stmt_close * stmt)
|
|||||||
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* exec_assign_expr Put an expressions result into
|
* exec_assign_expr Put an expression's result into
|
||||||
* a variable.
|
* a variable.
|
||||||
* ----------
|
* ----------
|
||||||
*/
|
*/
|
||||||
|
@ -1534,3 +1534,59 @@ SELECT recursion_test(4,3);
|
|||||||
4,3,2,1,3
|
4,3,2,1,3
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Test the FOUND magic variable
|
||||||
|
--
|
||||||
|
CREATE TABLE found_test_tbl (a int);
|
||||||
|
create function test_found ()
|
||||||
|
returns boolean as '
|
||||||
|
declare
|
||||||
|
begin
|
||||||
|
insert into found_test_tbl values (1);
|
||||||
|
if FOUND then
|
||||||
|
insert into found_test_tbl values (2);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
update found_test_tbl set a = 100 where a = 1;
|
||||||
|
if FOUND then
|
||||||
|
insert into found_test_tbl values (3);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
delete from found_test_tbl where a = 9999; -- matches no rows
|
||||||
|
if not FOUND then
|
||||||
|
insert into found_test_tbl values (4);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
for i in 1 .. 10 loop
|
||||||
|
-- no need to do anything
|
||||||
|
end loop;
|
||||||
|
if FOUND then
|
||||||
|
insert into found_test_tbl values (5);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- never executes the loop
|
||||||
|
for i in 2 .. 1 loop
|
||||||
|
-- no need to do anything
|
||||||
|
end loop;
|
||||||
|
if not FOUND then
|
||||||
|
insert into found_test_tbl values (6);
|
||||||
|
end if;
|
||||||
|
return true;
|
||||||
|
end;' language 'plpgsql';
|
||||||
|
select test_found();
|
||||||
|
test_found
|
||||||
|
------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select * from found_test_tbl;
|
||||||
|
a
|
||||||
|
-----
|
||||||
|
2
|
||||||
|
100
|
||||||
|
3
|
||||||
|
4
|
||||||
|
5
|
||||||
|
6
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
@ -1414,3 +1414,47 @@ BEGIN
|
|||||||
END;' LANGUAGE 'plpgsql';
|
END;' LANGUAGE 'plpgsql';
|
||||||
|
|
||||||
SELECT recursion_test(4,3);
|
SELECT recursion_test(4,3);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Test the FOUND magic variable
|
||||||
|
--
|
||||||
|
CREATE TABLE found_test_tbl (a int);
|
||||||
|
|
||||||
|
create function test_found ()
|
||||||
|
returns boolean as '
|
||||||
|
declare
|
||||||
|
begin
|
||||||
|
insert into found_test_tbl values (1);
|
||||||
|
if FOUND then
|
||||||
|
insert into found_test_tbl values (2);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
update found_test_tbl set a = 100 where a = 1;
|
||||||
|
if FOUND then
|
||||||
|
insert into found_test_tbl values (3);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
delete from found_test_tbl where a = 9999; -- matches no rows
|
||||||
|
if not FOUND then
|
||||||
|
insert into found_test_tbl values (4);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
for i in 1 .. 10 loop
|
||||||
|
-- no need to do anything
|
||||||
|
end loop;
|
||||||
|
if FOUND then
|
||||||
|
insert into found_test_tbl values (5);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- never executes the loop
|
||||||
|
for i in 2 .. 1 loop
|
||||||
|
-- no need to do anything
|
||||||
|
end loop;
|
||||||
|
if not FOUND then
|
||||||
|
insert into found_test_tbl values (6);
|
||||||
|
end if;
|
||||||
|
return true;
|
||||||
|
end;' language 'plpgsql';
|
||||||
|
|
||||||
|
select test_found();
|
||||||
|
select * from found_test_tbl;
|
||||||
|
Reference in New Issue
Block a user