Commit cfd27281 authored by Bruce Momjian's avatar Bruce Momjian

This patch makes a minor cleanup to the implementation of PERFORM in

PL/PgSQL. Previously, it had been bundled together with the assign
statement implementation, for some reason that wasn't clear to me
(they certainly don't share any code with one another). So I separated
them and made PERFORM a statement like any other. No changes in
functionality.

Along the way, I added some regression tests for PERFORM, added a
bunch more SGML tags to the PL/PgSQL docs, and removed an obsolete
comment relating to the implementation of RETURN NEXT.

Neil Conway
parent ceb4f5ea
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/plpgsql.sgml,v 1.8 2002/09/21 18:32:53 petere Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/plpgsql.sgml,v 1.9 2002/11/10 00:35:58 momjian Exp $
--> -->
<chapter id="plpgsql"> <chapter id="plpgsql">
...@@ -102,7 +102,7 @@ END; ...@@ -102,7 +102,7 @@ END;
</programlisting> </programlisting>
If you execute the above function, it will reference the OID for If you execute the above function, it will reference the OID for
<function>my_function()</function> in the query plan produced for <function>my_function()</function> in the query plan produced for
the PERFORM statement. Later, if you the <command>PERFORM</command> statement. Later, if you
drop and re-create <function>my_function()</function>, then drop and re-create <function>my_function()</function>, then
<function>populate()</function> will not be able to find <function>populate()</function> will not be able to find
<function>my_function()</function> anymore. You would then have to <function>my_function()</function> anymore. You would then have to
...@@ -117,17 +117,19 @@ END; ...@@ -117,17 +117,19 @@ END;
same tables and fields on every execution; that is, you cannot use same tables and fields on every execution; that is, you cannot use
a parameter as the name of a table or field in a query. To get a parameter as the name of a table or field in a query. To get
around this restriction, you can construct dynamic queries using around this restriction, you can construct dynamic queries using
the <application>PL/pgSQL</application> EXECUTE statement --- at the <application>PL/pgSQL</application> <command>EXECUTE</command>
the price of constructing a new query plan on every execution. statement --- at the price of constructing a new query plan on
every execution.
</para> </para>
<note> <note>
<para> <para>
The <application>PL/pgSQL</application> EXECUTE statement is not The <application>PL/pgSQL</application>
related to the EXECUTE statement supported by the <command>EXECUTE</command> statement is not related to the
<command>EXECUTE</command> statement supported by the
<productname>PostgreSQL</productname> backend. The backend <productname>PostgreSQL</productname> backend. The backend
EXECUTE statement cannot be used within <application>PL/pgSQL</> functions (and <command>EXECUTE</command> statement cannot be used within
is not needed). <application>PL/pgSQL</> functions (and is not needed).
</para> </para>
</note> </note>
...@@ -173,13 +175,12 @@ END; ...@@ -173,13 +175,12 @@ END;
</para> </para>
<para> <para>
That means that your client application must send each That means that your client application must send each query to
query to the database server, wait for it to process it, the database server, wait for it to process it, receive the
receive the results, do some computation, then send results, do some computation, then send other queries to the
other queries to the server. All this incurs inter-process communication server. All this incurs inter-process communication and may also
and may also incur network incur network overhead if your client is on a different machine
overhead if your client is on a different machine than than the database server.
the database server.
</para> </para>
<para> <para>
...@@ -753,14 +754,14 @@ CREATE FUNCTION logfunc2 (TEXT) RETURNS TIMESTAMP AS ' ...@@ -753,14 +754,14 @@ CREATE FUNCTION logfunc2 (TEXT) RETURNS TIMESTAMP AS '
<para> <para>
The mutable nature of record variables presents a problem in this The mutable nature of record variables presents a problem in this
connection. When fields of a record variable are used in expressions or connection. When fields of a record variable are used in
statements, the data types of the expressions or statements, the data types of the fields must not
fields must not change between calls of one and the same expression, change between calls of one and the same expression, since the
since the expression will be planned using the data type that is present expression will be planned using the data type that is present
when the expression is first reached. when the expression is first reached. Keep this in mind when
Keep this in mind when writing trigger procedures that handle events writing trigger procedures that handle events for more than one
for more than one table. (EXECUTE can be used to get around this table. (<command>EXECUTE</command> can be used to get around
problem when necessary.) this problem when necessary.)
</para> </para>
</sect1> </sect1>
...@@ -904,10 +905,11 @@ END; ...@@ -904,10 +905,11 @@ END;
<title>Executing an expression or query with no result</title> <title>Executing an expression or query with no result</title>
<para> <para>
Sometimes one wishes to evaluate an expression or query but discard Sometimes one wishes to evaluate an expression or query but
the result (typically because one is calling a function that has discard the result (typically because one is calling a function
useful side-effects but no useful result value). To do this in that has useful side-effects but no useful result value). To do
<application>PL/pgSQL</application>, use the PERFORM statement: this in <application>PL/pgSQL</application>, use the
<command>PERFORM</command> statement:
<synopsis> <synopsis>
PERFORM <replaceable>query</replaceable>; PERFORM <replaceable>query</replaceable>;
...@@ -923,8 +925,9 @@ PERFORM <replaceable>query</replaceable>; ...@@ -923,8 +925,9 @@ PERFORM <replaceable>query</replaceable>;
<note> <note>
<para> <para>
One might expect that SELECT with no INTO clause would accomplish One might expect that <command>SELECT</command> with no INTO
this result, but at present the only accepted way to do it is PERFORM. clause would accomplish this result, but at present the only
accepted way to do it is <command>PERFORM</command>.
</para> </para>
</note> </note>
...@@ -940,13 +943,13 @@ PERFORM create_mv(''cs_session_page_requests_mv'', my_query); ...@@ -940,13 +943,13 @@ PERFORM create_mv(''cs_session_page_requests_mv'', my_query);
<title>Executing dynamic queries</title> <title>Executing dynamic queries</title>
<para> <para>
Oftentimes you will want to generate dynamic queries inside Oftentimes you will want to generate dynamic queries inside your
your <application>PL/pgSQL</application> functions, that is, <application>PL/pgSQL</application> functions, that is, queries
queries that will involve different tables or different data types that will involve different tables or different data types each
each time they are executed. <application>PL/pgSQL</application>'s time they are executed. <application>PL/pgSQL</application>'s
normal attempts to cache plans for queries will not work in such normal attempts to cache plans for queries will not work in such
scenarios. To handle this sort of problem, the EXECUTE statement scenarios. To handle this sort of problem, the
is provided: <command>EXECUTE</command> statement is provided:
<synopsis> <synopsis>
EXECUTE <replaceable class="command">query-string</replaceable>; EXECUTE <replaceable class="command">query-string</replaceable>;
...@@ -973,20 +976,22 @@ EXECUTE <replaceable class="command">query-string</replaceable>; ...@@ -973,20 +976,22 @@ EXECUTE <replaceable class="command">query-string</replaceable>;
<para> <para>
Unlike all other queries in <application>PL/pgSQL</>, a Unlike all other queries in <application>PL/pgSQL</>, a
<replaceable>query</replaceable> run by an EXECUTE statement is <replaceable>query</replaceable> run by an
not prepared and saved just once during the life of the server. <command>EXECUTE</command> statement is not prepared and saved
Instead, the <replaceable>query</replaceable> is prepared each just once during the life of the server. Instead, the
time the statement is run. The <replaceable>query</replaceable> is prepared each time the
<replaceable>query-string</replaceable> can be dynamically statement is run. The <replaceable>query-string</replaceable> can
created within the procedure to perform actions on variable be dynamically created within the procedure to perform actions on
tables and fields. variable tables and fields.
</para> </para>
<para> <para>
The results from SELECT queries are discarded by EXECUTE, and The results from <command>SELECT</command> queries are discarded
SELECT INTO is not currently supported within EXECUTE. So, the by <command>EXECUTE</command>, and <command>SELECT INTO</command>
only way to extract a result from a dynamically-created SELECT is is not currently supported within <command>EXECUTE</command>.
to use the FOR-IN-EXECUTE form described later. So, the only way to extract a result from a dynamically-created
<command>SELECT</command> is to use the FOR-IN-EXECUTE form
described later.
</para> </para>
<para> <para>
...@@ -1017,7 +1022,8 @@ EXECUTE ''UPDATE tbl SET '' ...@@ -1017,7 +1022,8 @@ EXECUTE ''UPDATE tbl SET ''
</para> </para>
<para> <para>
Here is a much larger example of a dynamic query and EXECUTE: Here is a much larger example of a dynamic query and
<command>EXECUTE</command>:
<programlisting> <programlisting>
CREATE FUNCTION cs_update_referrer_type_proc() RETURNS INTEGER AS ' CREATE FUNCTION cs_update_referrer_type_proc() RETURNS INTEGER AS '
DECLARE DECLARE
...@@ -1159,9 +1165,9 @@ GET DIAGNOSTICS <replaceable>variable</replaceable> = <replaceable>item</replace ...@@ -1159,9 +1165,9 @@ GET DIAGNOSTICS <replaceable>variable</replaceable> = <replaceable>item</replace
RETURN <replaceable>expression</replaceable>; RETURN <replaceable>expression</replaceable>;
</synopsis> </synopsis>
RETURN with an expression is used to return from a <command>RETURN</command> with an expression is used to return
<application>PL/pgSQL</> function that does not return a set. from a <application>PL/pgSQL</> function that does not return a
The function terminates and the value of set. The function terminates and the value of
<replaceable>expression</replaceable> is returned to the caller. <replaceable>expression</replaceable> is returned to the caller.
</para> </para>
...@@ -1176,22 +1182,24 @@ RETURN <replaceable>expression</replaceable>; ...@@ -1176,22 +1182,24 @@ RETURN <replaceable>expression</replaceable>;
</para> </para>
<para> <para>
The return value of a function cannot be left undefined. If control The return value of a function cannot be left undefined. If
reaches the end of the top-level block of control reaches the end of the top-level block of the function
the function without hitting a RETURN statement, a run-time error without hitting a <command>RETURN</command> statement, a run-time
will occur. error will occur.
</para> </para>
<para> <para>
When a <application>PL/pgSQL</> function is declared to return When a <application>PL/pgSQL</> function is declared to return
<literal>SETOF</literal> <replaceable>sometype</>, the procedure <literal>SETOF</literal> <replaceable>sometype</>, the procedure
to follow is slightly different. In that case, the individual to follow is slightly different. In that case, the individual
items to return are specified in RETURN NEXT commands, and then a items to return are specified in <command>RETURN NEXT</command>
final RETURN command with no arguments is used to indicate that commands, and then a final <command>RETURN</command> command with
the function has finished executing. RETURN NEXT can be used with no arguments is used to indicate that the function has finished
both scalar and composite data types; in the later case, an executing. <command>RETURN NEXT</command> can be used with both
entire "table" of results will be returned. Functions that use scalar and composite data types; in the later case, an entire
RETURN NEXT should be called in the following fashion: "table" of results will be returned. Functions that use
<command>RETURN NEXT</command> should be called in the following
fashion:
<programlisting> <programlisting>
SELECT * FROM some_func(); SELECT * FROM some_func();
...@@ -1203,19 +1211,19 @@ SELECT * FROM some_func(); ...@@ -1203,19 +1211,19 @@ SELECT * FROM some_func();
RETURN NEXT <replaceable>expression</replaceable>; RETURN NEXT <replaceable>expression</replaceable>;
</synopsis> </synopsis>
RETURN NEXT does not actually return from the function; it simply <command>RETURN NEXT</command> does not actually return from the
saves away the value of the expression (or record or row variable, function; it simply saves away the value of the expression (or
as appropriate for the data type being returned). record or row variable, as appropriate for the data type being
Execution then continues with the next statement in the returned). Execution then continues with the next statement in
<application>PL/pgSQL</> function. As successive RETURN NEXT the <application>PL/pgSQL</> function. As successive
commands are executed, the result set is built up. A final <command>RETURN NEXT</command> commands are executed, the result
RETURN, which need have no argument, causes control to exit set is built up. A final <command>RETURN</commmand>, which need
the function. have no argument, causes control to exit the function.
</para> </para>
<note> <note>
<para> <para>
The current implementation of RETURN NEXT for The current implementation of <command>RETURN NEXT</command> for
<application>PL/pgSQL</> stores the entire result set before <application>PL/pgSQL</> stores the entire result set before
returning from the function, as discussed above. That means that returning from the function, as discussed above. That means that
if a <application>PL/pgSQL</> function produces a very large result set, if a <application>PL/pgSQL</> function produces a very large result set,
...@@ -1586,12 +1594,12 @@ FOR <replaceable>record | row</replaceable> IN EXECUTE <replaceable>text_express ...@@ -1586,12 +1594,12 @@ FOR <replaceable>record | row</replaceable> IN EXECUTE <replaceable>text_express
<replaceable>statements</replaceable> <replaceable>statements</replaceable>
END LOOP; END LOOP;
</synopsis> </synopsis>
This is like the previous form, except that the source SELECT This is like the previous form, except that the source
statement is specified as a string expression, which is evaluated <command>SELECT</command> statement is specified as a string
and re-planned on each entry to the FOR loop. This allows the expression, which is evaluated and re-planned on each entry to
programmer to choose the speed of a pre-planned query or the the FOR loop. This allows the programmer to choose the speed of
flexibility of a dynamic query, just as with a plain EXECUTE a pre-planned query or the flexibility of a dynamic query, just
statement. as with a plain <command>EXECUTE</command> statement.
</para> </para>
<note> <note>
...@@ -1705,12 +1713,12 @@ OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey; ...@@ -1705,12 +1713,12 @@ OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;
OPEN <replaceable>unbound-cursor</replaceable> FOR EXECUTE <replaceable class="command">query-string</replaceable>; OPEN <replaceable>unbound-cursor</replaceable> FOR EXECUTE <replaceable class="command">query-string</replaceable>;
</synopsis> </synopsis>
The cursor variable is opened and given the specified query The cursor variable is opened and given the specified query to
to execute. The cursor cannot be open already, and it must execute. The cursor cannot be open already, and it must have been
have been declared as an unbound cursor (that is, as a simple declared as an unbound cursor (that is, as a simple
<type>refcursor</> variable). The query is specified as a <type>refcursor</> variable). The query is specified as a string
string expression in the same way as in the EXECUTE command. expression in the same way as in the <command>EXECUTE</command>
As usual, this gives flexibility so the query can vary command. As usual, this gives flexibility so the query can vary
from one run to the next. from one run to the next.
<programlisting> <programlisting>
...@@ -1727,14 +1735,13 @@ OPEN curs1 FOR EXECUTE ''SELECT * FROM '' || quote_ident($1); ...@@ -1727,14 +1735,13 @@ OPEN curs1 FOR EXECUTE ''SELECT * FROM '' || quote_ident($1);
OPEN <replaceable>bound-cursor</replaceable> <optional> ( <replaceable>argument_values</replaceable> ) </optional>; OPEN <replaceable>bound-cursor</replaceable> <optional> ( <replaceable>argument_values</replaceable> ) </optional>;
</synopsis> </synopsis>
This form of OPEN is used to open a cursor variable whose query This form of <command>OPEN</command> is used to open a cursor
was bound to it when it was declared. variable whose query was bound to it when it was declared. The
The cursor cannot be open already. A list of actual argument cursor cannot be open already. A list of actual argument value
value expressions must appear if and only if the cursor was expressions must appear if and only if the cursor was declared to
declared to take arguments. These values will be substituted take arguments. These values will be substituted in the query.
in the query. The query plan for a bound cursor is always considered cacheable
The query plan for a bound cursor is always considered --- there is no equivalent of <command>EXECUTE</command> in this case.
cacheable --- there is no equivalent of EXECUTE in this case.
<programlisting> <programlisting>
OPEN curs2; OPEN curs2;
...@@ -1776,11 +1783,12 @@ OPEN curs3(42); ...@@ -1776,11 +1783,12 @@ OPEN curs3(42);
FETCH <replaceable>cursor</replaceable> INTO <replaceable>target</replaceable>; FETCH <replaceable>cursor</replaceable> INTO <replaceable>target</replaceable>;
</synopsis> </synopsis>
FETCH retrieves the next row from the cursor into a target, <command>FETCH</command> retrieves the next row from the
which may be a row variable, a record variable, or a comma-separated cursor into a target, which may be a row variable, a record
list of simple variables, just like SELECT INTO. As with variable, or a comma-separated list of simple variables, just like
SELECT INTO, the special variable <literal>FOUND</literal> may be <command>SELECT INTO</command>. As with <command>SELECT
checked to see whether a row was obtained or not. INTO</command>, the special variable <literal>FOUND</literal> may
be checked to see whether a row was obtained or not.
<programlisting> <programlisting>
FETCH curs1 INTO rowvar; FETCH curs1 INTO rowvar;
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.39 2002/11/01 22:52:34 tgl Exp $ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.40 2002/11/10 00:35:58 momjian Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -728,14 +728,13 @@ proc_stmt : pl_block ';' ...@@ -728,14 +728,13 @@ proc_stmt : pl_block ';'
stmt_perform : K_PERFORM lno expr_until_semi stmt_perform : K_PERFORM lno expr_until_semi
{ {
PLpgSQL_stmt_assign *new; PLpgSQL_stmt_perform *new;
new = malloc(sizeof(PLpgSQL_stmt_assign)); new = malloc(sizeof(PLpgSQL_stmt_perform));
memset(new, 0, sizeof(PLpgSQL_stmt_assign)); memset(new, 0, sizeof(PLpgSQL_stmt_perform));
new->cmd_type = PLPGSQL_STMT_ASSIGN; new->cmd_type = PLPGSQL_STMT_PERFORM;
new->lineno = $2; new->lineno = $2;
new->varno = -1;
new->expr = $3; new->expr = $3;
$$ = (PLpgSQL_stmt *)new; $$ = (PLpgSQL_stmt *)new;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.65 2002/10/19 22:10:58 tgl Exp $ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.66 2002/11/10 00:35:58 momjian Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -73,6 +73,8 @@ static int exec_stmt(PLpgSQL_execstate * estate, ...@@ -73,6 +73,8 @@ static int exec_stmt(PLpgSQL_execstate * estate,
PLpgSQL_stmt * stmt); PLpgSQL_stmt * stmt);
static int exec_stmt_assign(PLpgSQL_execstate * estate, static int exec_stmt_assign(PLpgSQL_execstate * estate,
PLpgSQL_stmt_assign * stmt); PLpgSQL_stmt_assign * stmt);
static int exec_stmt_perform(PLpgSQL_execstate * estate,
PLpgSQL_stmt_perform * stmt);
static int exec_stmt_getdiag(PLpgSQL_execstate * estate, static int exec_stmt_getdiag(PLpgSQL_execstate * estate,
PLpgSQL_stmt_getdiag * stmt); PLpgSQL_stmt_getdiag * stmt);
static int exec_stmt_if(PLpgSQL_execstate * estate, static int exec_stmt_if(PLpgSQL_execstate * estate,
...@@ -890,6 +892,10 @@ exec_stmt(PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt) ...@@ -890,6 +892,10 @@ exec_stmt(PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt)
rc = exec_stmt_assign(estate, (PLpgSQL_stmt_assign *) stmt); rc = exec_stmt_assign(estate, (PLpgSQL_stmt_assign *) stmt);
break; break;
case PLPGSQL_STMT_PERFORM:
rc = exec_stmt_perform(estate, (PLpgSQL_stmt_perform *) stmt);
break;
case PLPGSQL_STMT_GETDIAG: case PLPGSQL_STMT_GETDIAG:
rc = exec_stmt_getdiag(estate, (PLpgSQL_stmt_getdiag *) stmt); rc = exec_stmt_getdiag(estate, (PLpgSQL_stmt_getdiag *) stmt);
break; break;
...@@ -973,27 +979,28 @@ exec_stmt(PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt) ...@@ -973,27 +979,28 @@ exec_stmt(PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt)
/* ---------- /* ----------
* exec_stmt_assign Evaluate an expression and * exec_stmt_assign Evaluate an expression and
* put the result into a variable. * put the result into a variable.
*
* For no very good reason, this is also used for PERFORM statements.
* ---------- * ----------
*/ */
static int static int
exec_stmt_assign(PLpgSQL_execstate * estate, PLpgSQL_stmt_assign * stmt) exec_stmt_assign(PLpgSQL_execstate * estate, PLpgSQL_stmt_assign * stmt)
{ {
PLpgSQL_expr *expr = stmt->expr; Assert(stmt->varno >= 0);
if (stmt->varno >= 0) exec_assign_expr(estate, estate->datums[stmt->varno], stmt->expr);
exec_assign_expr(estate, estate->datums[stmt->varno], expr);
else return PLPGSQL_RC_OK;
{ }
/*
* PERFORM: evaluate query and discard result (but set FOUND /* ----------
* depending on whether at least one row was returned). * exec_stmt_perform Evaluate query and discard result (but set
* * FOUND depending on whether at least one row
* This cannot share code with the assignment case since we do not * was returned).
* wish to constrain the discarded result to be only one * ----------
* row/column.
*/ */
static int
exec_stmt_perform(PLpgSQL_execstate * estate, PLpgSQL_stmt_perform * stmt)
{
PLpgSQL_expr *expr = stmt->expr;
int rc; int rc;
/* /*
...@@ -1009,7 +1016,6 @@ exec_stmt_assign(PLpgSQL_execstate * estate, PLpgSQL_stmt_assign * stmt) ...@@ -1009,7 +1016,6 @@ exec_stmt_assign(PLpgSQL_execstate * estate, PLpgSQL_stmt_assign * stmt)
exec_set_found(estate, (estate->eval_processed != 0)); exec_set_found(estate, (estate->eval_processed != 0));
exec_eval_cleanup(estate); exec_eval_cleanup(estate);
}
return PLPGSQL_RC_OK; return PLPGSQL_RC_OK;
} }
...@@ -1579,12 +1585,11 @@ exec_stmt_return(PLpgSQL_execstate * estate, PLpgSQL_stmt_return * stmt) ...@@ -1579,12 +1585,11 @@ exec_stmt_return(PLpgSQL_execstate * estate, PLpgSQL_stmt_return * stmt)
return PLPGSQL_RC_RETURN; return PLPGSQL_RC_RETURN;
} }
/* /* ----------
* Notes: * exec_stmt_return_next Evaluate an expression and add it to the
* - the tuple store must be created in a sufficiently long-lived * list of tuples returned by the current
* memory context, as the same store must be used within the executor * SRF.
* after the PL/PgSQL call returns. At present, the code uses * ----------
* TopTransactionContext.
*/ */
static int static int
exec_stmt_return_next(PLpgSQL_execstate * estate, exec_stmt_return_next(PLpgSQL_execstate * estate,
...@@ -1732,7 +1737,6 @@ exec_init_tuple_store(PLpgSQL_execstate * estate) ...@@ -1732,7 +1737,6 @@ exec_init_tuple_store(PLpgSQL_execstate * estate)
estate->rettupdesc = rsi->expectedDesc; estate->rettupdesc = rsi->expectedDesc;
} }
/* ---------- /* ----------
* exec_stmt_raise Build a message and throw it with * exec_stmt_raise Build a message and throw it with
* elog() * elog()
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.28 2002/09/12 00:24:09 momjian Exp $ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.29 2002/11/10 00:35:58 momjian Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -100,7 +100,8 @@ enum ...@@ -100,7 +100,8 @@ enum
PLPGSQL_STMT_GETDIAG, PLPGSQL_STMT_GETDIAG,
PLPGSQL_STMT_OPEN, PLPGSQL_STMT_OPEN,
PLPGSQL_STMT_FETCH, PLPGSQL_STMT_FETCH,
PLPGSQL_STMT_CLOSE PLPGSQL_STMT_CLOSE,
PLPGSQL_STMT_PERFORM
}; };
...@@ -288,6 +289,12 @@ typedef struct ...@@ -288,6 +289,12 @@ typedef struct
PLpgSQL_expr *expr; PLpgSQL_expr *expr;
} PLpgSQL_stmt_assign; } PLpgSQL_stmt_assign;
typedef struct
{ /* PERFORM statement */
int cmd_type;
int lineno;
PLpgSQL_expr *expr;
} PLpgSQL_stmt_perform;
typedef struct typedef struct
{ /* Get Diagnostics item */ { /* Get Diagnostics item */
......
...@@ -1733,3 +1733,54 @@ SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text); ...@@ -1733,3 +1733,54 @@ SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text);
50 | 5 | xxx 50 | 5 | xxx
(1 row) (1 row)
--
-- test PERFORM
--
create table perform_test (
a INT,
b INT
);
create function simple_func(int) returns boolean as '
BEGIN
IF $1 < 20 THEN
INSERT INTO perform_test VALUES ($1, $1 + 10);
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
END;' language 'plpgsql';
create function perform_test_func() returns void as '
BEGIN
IF FOUND then
INSERT INTO perform_test VALUES (100, 100);
END IF;
PERFORM simple_func(5);
IF FOUND then
INSERT INTO perform_test VALUES (100, 100);
END IF;
PERFORM simple_func(50);
IF FOUND then
INSERT INTO perform_test VALUES (100, 100);
END IF;
RETURN;
END;' language 'plpgsql';
SELECT perform_test_func();
perform_test_func
-------------------
(1 row)
SELECT * FROM perform_test;
a | b
-----+-----
5 | 15
100 | 100
100 | 100
(3 rows)
drop table perform_test;
...@@ -1559,3 +1559,48 @@ END;' language 'plpgsql'; ...@@ -1559,3 +1559,48 @@ END;' language 'plpgsql';
SELECT * FROM test_ret_rec_dyn(1500) AS (a int, b int, c int); SELECT * FROM test_ret_rec_dyn(1500) AS (a int, b int, c int);
SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text); SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text);
--
-- test PERFORM
--
create table perform_test (
a INT,
b INT
);
create function simple_func(int) returns boolean as '
BEGIN
IF $1 < 20 THEN
INSERT INTO perform_test VALUES ($1, $1 + 10);
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
END;' language 'plpgsql';
create function perform_test_func() returns void as '
BEGIN
IF FOUND then
INSERT INTO perform_test VALUES (100, 100);
END IF;
PERFORM simple_func(5);
IF FOUND then
INSERT INTO perform_test VALUES (100, 100);
END IF;
PERFORM simple_func(50);
IF FOUND then
INSERT INTO perform_test VALUES (100, 100);
END IF;
RETURN;
END;' language 'plpgsql';
SELECT perform_test_func();
SELECT * FROM perform_test;
drop table perform_test;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment