<literal><function>spi_query_prepared(<replaceable>plan</replaceable>, <replaceable>arguments</replaceable>)</function></literal>
<indexterm>
<primary>spi_query_prepared</primary>
<secondary>in PL/Perl</secondary>
</indexterm>
</term>
<term>
<literal><function>spi_exec_prepared(<replaceable>plan</replaceable> [, <replaceable>attributes</replaceable>], <replaceable>arguments</replaceable>)</function></literal>
<indexterm>
<primary>spi_exec_prepared</primary>
<secondary>in PL/Perl</secondary>
</indexterm>
</term>
<term>
<literal><function>spi_freeplan(<replaceable>plan</replaceable>)</function></literal>
<indexterm>
<primary>spi_freeplan</primary>
<secondary>in PL/Perl</secondary>
</indexterm>
</term>
<listitem>
<para>
<literal>spi_prepare</literal>, <literal>spi_query_prepared</literal>, <literal>spi_exec_prepared</literal>,
and <literal>spi_freeplan</literal> implement the same functionality but for prepared queries.
<literal>spi_prepare</literal> accepts a query string with numbered argument placeholders ($1, $2, etc.)
and a string list of argument types:
<programlisting>
$plan = spi_prepare('SELECT * FROM test WHERE id > $1 AND name = $2',
'INTEGER', 'TEXT');
</programlisting>
Once a query plan is prepared by a call to <literal>spi_prepare</literal>, the plan can be used instead
of the string query, either in <literal>spi_exec_prepared</literal>, where the result is the same as returned
by <literal>spi_exec_query</literal>, or in <literal>spi_query_prepared</literal> which returns a cursor
exactly as <literal>spi_query</literal> does, which can be later passed to <literal>spi_fetchrow</literal>.
The optional second parameter to <literal>spi_exec_prepared</literal> is a hash reference of attributes;
the only attribute currently supported is <literal>limit</literal>, which
sets the maximum number of rows returned from the query.
Omitting <literal>limit</literal> or specifying it as zero results in no
row limit.
</para>
<para>
The advantage of prepared queries is that is it possible to use one prepared plan for more
than one query execution. After the plan is not needed anymore, it can be freed with
<literal>spi_freeplan</literal>:
<programlisting>
CREATE OR REPLACE FUNCTION init() RETURNS VOID AS $$
$_SHARED{my_plan} = spi_prepare('SELECT (now() + $1)::date AS now',
'INTERVAL');
$$ LANGUAGE plperl;
CREATE OR REPLACE FUNCTION add_time( INTERVAL ) RETURNS TEXT AS $$
return spi_exec_prepared(
$_SHARED{my_plan},
$_[0]
)->{rows}->[0]->{now};
$$ LANGUAGE plperl;
CREATE OR REPLACE FUNCTION done() RETURNS VOID AS $$
spi_freeplan( $_SHARED{my_plan});
undef $_SHARED{my_plan};
$$ LANGUAGE plperl;
SELECT init();
SELECT add_time('1 day'), add_time('2 days'), add_time('3 days');
SELECT done();
add_time | add_time | add_time
------------+------------+------------
2005-12-10 | 2005-12-11 | 2005-12-12
</programlisting>
Note that the parameter subscript in <literal>spi_prepare</literal> is defined via
$1, $2, $3, etc., so avoid declaring query strings in double quotes that might easily
lead to hard-to-catch bugs.
</para>
<para>
Another example illustrates usage of an optional parameter in <literal>spi_exec_prepared</literal>:
<programlisting>
CREATE TABLE hosts AS SELECT id, ('192.168.1.'||id)::inet AS address
FROM generate_series(1,3) AS id;
CREATE OR REPLACE FUNCTION init_hosts_query() RETURNS VOID AS $$
$_SHARED{plan} = spi_prepare('SELECT * FROM hosts
WHERE address << $1', 'inet');
$$ LANGUAGE plperl;
CREATE OR REPLACE FUNCTION query_hosts(inet) RETURNS SETOF hosts AS $$