Commit 95f6d2d2 authored by Tom Lane's avatar Tom Lane

Make use of plancache module for SPI plans. In particular, since plpgsql

uses SPI plans, this finally fixes the ancient gotcha that you can't
drop and recreate a temp table used by a plpgsql function.

Along the way, clean up SPI's API a little bit by declaring SPI plan
pointers as "SPIPlanPtr" instead of "void *".  This is cosmetic but
helps to forestall simple programming mistakes.  (I have changed some
but not all of the callers to match; there are still some "void *"'s
in contrib and the PL's.  This is intentional so that we can see if
anyone's compiler complains about it.)
parent d3ff1801
...@@ -19,7 +19,7 @@ typedef struct ...@@ -19,7 +19,7 @@ typedef struct
{ {
char *ident; char *ident;
int nplans; int nplans;
void **splan; SPIPlanPtr *splan;
} EPlan; } EPlan;
static EPlan *FPlans = NULL; static EPlan *FPlans = NULL;
...@@ -163,7 +163,7 @@ check_primary_key(PG_FUNCTION_ARGS) ...@@ -163,7 +163,7 @@ check_primary_key(PG_FUNCTION_ARGS)
*/ */
if (plan->nplans <= 0) if (plan->nplans <= 0)
{ {
void *pplan; SPIPlanPtr pplan;
char sql[8192]; char sql[8192];
/* /*
...@@ -191,7 +191,7 @@ check_primary_key(PG_FUNCTION_ARGS) ...@@ -191,7 +191,7 @@ check_primary_key(PG_FUNCTION_ARGS)
if (pplan == NULL) if (pplan == NULL)
/* internal error */ /* internal error */
elog(ERROR, "check_primary_key: SPI_saveplan returned %d", SPI_result); elog(ERROR, "check_primary_key: SPI_saveplan returned %d", SPI_result);
plan->splan = (void **) malloc(sizeof(void *)); plan->splan = (SPIPlanPtr *) malloc(sizeof(SPIPlanPtr));
*(plan->splan) = pplan; *(plan->splan) = pplan;
plan->nplans = 1; plan->nplans = 1;
} }
...@@ -413,11 +413,11 @@ check_foreign_key(PG_FUNCTION_ARGS) ...@@ -413,11 +413,11 @@ check_foreign_key(PG_FUNCTION_ARGS)
*/ */
if (plan->nplans <= 0) if (plan->nplans <= 0)
{ {
void *pplan; SPIPlanPtr pplan;
char sql[8192]; char sql[8192];
char **args2 = args; char **args2 = args;
plan->splan = (void **) malloc(nrefs * sizeof(void *)); plan->splan = (SPIPlanPtr *) malloc(nrefs * sizeof(SPIPlanPtr));
for (r = 0; r < nrefs; r++) for (r = 0; r < nrefs; r++)
{ {
......
...@@ -24,7 +24,7 @@ Datum get_timetravel(PG_FUNCTION_ARGS); ...@@ -24,7 +24,7 @@ Datum get_timetravel(PG_FUNCTION_ARGS);
typedef struct typedef struct
{ {
char *ident; char *ident;
void *splan; SPIPlanPtr splan;
} EPlan; } EPlan;
static EPlan *Plans = NULL; /* for UPDATE/DELETE */ static EPlan *Plans = NULL; /* for UPDATE/DELETE */
...@@ -308,7 +308,7 @@ timetravel(PG_FUNCTION_ARGS) ...@@ -308,7 +308,7 @@ timetravel(PG_FUNCTION_ARGS)
/* if there is no plan ... */ /* if there is no plan ... */
if (plan->splan == NULL) if (plan->splan == NULL)
{ {
void *pplan; SPIPlanPtr pplan;
Oid *ctypes; Oid *ctypes;
char sql[8192]; char sql[8192];
char separ = ' '; char separ = ' ';
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.53 2007/02/18 01:47:40 momjian Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.54 2007/03/15 23:12:06 tgl Exp $ -->
<chapter id="spi"> <chapter id="spi">
<title>Server Programming Interface</title> <title>Server Programming Interface</title>
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<acronym>SQL</acronym> commands inside their functions. <acronym>SQL</acronym> commands inside their functions.
<acronym>SPI</acronym> is a set of <acronym>SPI</acronym> is a set of
interface functions to simplify access to the parser, planner, interface functions to simplify access to the parser, planner,
optimizer, and executor. <acronym>SPI</acronym> also does some and executor. <acronym>SPI</acronym> also does some
memory management. memory management.
</para> </para>
...@@ -322,7 +322,8 @@ SPI_execute("INSERT INTO foo SELECT * FROM bar", false, 5); ...@@ -322,7 +322,8 @@ SPI_execute("INSERT INTO foo SELECT * FROM bar", false, 5);
<para> <para>
You can pass multiple commands in one string, but later commands cannot You can pass multiple commands in one string, but later commands cannot
depend on the creation of objects earlier in the string. depend on the creation of objects earlier in the string, because the
whole string will be parsed and planned before execution begins.
<function>SPI_execute</function> returns the <function>SPI_execute</function> returns the
result for the command executed last. The <parameter>count</parameter> result for the command executed last. The <parameter>count</parameter>
limit applies to each command separately, but it is not applied to limit applies to each command separately, but it is not applied to
...@@ -699,7 +700,7 @@ int SPI_exec(const char * <parameter>command</parameter>, long <parameter>count< ...@@ -699,7 +700,7 @@ int SPI_exec(const char * <parameter>command</parameter>, long <parameter>count<
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
void * SPI_prepare(const char * <parameter>command</parameter>, int <parameter>nargs</parameter>, Oid * <parameter>argtypes</parameter>) SPIPlanPtr SPI_prepare(const char * <parameter>command</parameter>, int <parameter>nargs</parameter>, Oid * <parameter>argtypes</parameter>)
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -790,6 +791,13 @@ void * SPI_prepare(const char * <parameter>command</parameter>, int <parameter>n ...@@ -790,6 +791,13 @@ void * SPI_prepare(const char * <parameter>command</parameter>, int <parameter>n
<refsect1> <refsect1>
<title>Notes</title> <title>Notes</title>
<para>
<type>SPIPlanPtr</> is declared as a pointer to an opaque struct type in
<filename>spi.h</>. It is unwise to try to access its contents
directly, as that makes your code much more likely to break in
future revisions of <productname>PostgreSQL</productname>.
</para>
<para> <para>
There is a disadvantage to using parameters: since the planner does There is a disadvantage to using parameters: since the planner does
not know the values that will be supplied for the parameters, it not know the values that will be supplied for the parameters, it
...@@ -816,7 +824,7 @@ void * SPI_prepare(const char * <parameter>command</parameter>, int <parameter>n ...@@ -816,7 +824,7 @@ void * SPI_prepare(const char * <parameter>command</parameter>, int <parameter>n
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
int SPI_getargcount(void * <parameter>plan</parameter>) int SPI_getargcount(SPIPlanPtr <parameter>plan</parameter>)
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -834,7 +842,7 @@ int SPI_getargcount(void * <parameter>plan</parameter>) ...@@ -834,7 +842,7 @@ int SPI_getargcount(void * <parameter>plan</parameter>)
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><literal>void * <parameter>plan</parameter></literal></term> <term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
<listitem> <listitem>
<para> <para>
execution plan (returned by <function>SPI_prepare</function>) execution plan (returned by <function>SPI_prepare</function>)
...@@ -847,9 +855,10 @@ int SPI_getargcount(void * <parameter>plan</parameter>) ...@@ -847,9 +855,10 @@ int SPI_getargcount(void * <parameter>plan</parameter>)
<refsect1> <refsect1>
<title>Return Value</title> <title>Return Value</title>
<para> <para>
The expected argument count for the <parameter>plan</parameter>, or The count of expected arguments for the <parameter>plan</parameter>.
<symbol>SPI_ERROR_ARGUMENT</symbol> if the <parameter>plan If the <parameter>plan</parameter> is <symbol>NULL</symbol> or invalid,
</parameter> is <symbol>NULL</symbol> <varname>SPI_result</varname> is set to <symbol>SPI_ERROR_ARGUMENT</symbol>
and <literal>-1</literal> is returned.
</para> </para>
</refsect1> </refsect1>
</refentry> </refentry>
...@@ -871,7 +880,7 @@ int SPI_getargcount(void * <parameter>plan</parameter>) ...@@ -871,7 +880,7 @@ int SPI_getargcount(void * <parameter>plan</parameter>)
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
Oid SPI_getargtypeid(void * <parameter>plan</parameter>, int <parameter>argIndex</parameter>) Oid SPI_getargtypeid(SPIPlanPtr <parameter>plan</parameter>, int <parameter>argIndex</parameter>)
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -890,7 +899,7 @@ Oid SPI_getargtypeid(void * <parameter>plan</parameter>, int <parameter>argIndex ...@@ -890,7 +899,7 @@ Oid SPI_getargtypeid(void * <parameter>plan</parameter>, int <parameter>argIndex
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><literal>void * <parameter>plan</parameter></literal></term> <term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
<listitem> <listitem>
<para> <para>
execution plan (returned by <function>SPI_prepare</function>) execution plan (returned by <function>SPI_prepare</function>)
...@@ -912,11 +921,13 @@ Oid SPI_getargtypeid(void * <parameter>plan</parameter>, int <parameter>argIndex ...@@ -912,11 +921,13 @@ Oid SPI_getargtypeid(void * <parameter>plan</parameter>, int <parameter>argIndex
<refsect1> <refsect1>
<title>Return Value</title> <title>Return Value</title>
<para> <para>
The type id of the argument at the given index, or The type id of the argument at the given index.
<symbol>SPI_ERROR_ARGUMENT</symbol> if the <parameter>plan</parameter> is If the <parameter>plan</parameter> is <symbol>NULL</symbol> or invalid,
<symbol>NULL</symbol> or <parameter>argIndex</parameter> is less than 0 or or <parameter>argIndex</parameter> is less than 0 or
not less than the number of arguments declared for the not less than the number of arguments declared for the
<parameter>plan</parameter> <parameter>plan</parameter>,
<varname>SPI_result</varname> is set to <symbol>SPI_ERROR_ARGUMENT</symbol>
and <symbol>InvalidOid</symbol> is returned.
</para> </para>
</refsect1> </refsect1>
</refentry> </refentry>
...@@ -939,7 +950,7 @@ Oid SPI_getargtypeid(void * <parameter>plan</parameter>, int <parameter>argIndex ...@@ -939,7 +950,7 @@ Oid SPI_getargtypeid(void * <parameter>plan</parameter>, int <parameter>argIndex
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
bool SPI_is_cursor_plan(void * <parameter>plan</parameter>) bool SPI_is_cursor_plan(SPIPlanPtr <parameter>plan</parameter>)
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -964,7 +975,7 @@ bool SPI_is_cursor_plan(void * <parameter>plan</parameter>) ...@@ -964,7 +975,7 @@ bool SPI_is_cursor_plan(void * <parameter>plan</parameter>)
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><literal>void * <parameter>plan</parameter></literal></term> <term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
<listitem> <listitem>
<para> <para>
execution plan (returned by <function>SPI_prepare</function>) execution plan (returned by <function>SPI_prepare</function>)
...@@ -978,9 +989,10 @@ bool SPI_is_cursor_plan(void * <parameter>plan</parameter>) ...@@ -978,9 +989,10 @@ bool SPI_is_cursor_plan(void * <parameter>plan</parameter>)
<title>Return Value</title> <title>Return Value</title>
<para> <para>
<symbol>true</symbol> or <symbol>false</symbol> to indicate if the <symbol>true</symbol> or <symbol>false</symbol> to indicate if the
<parameter>plan</parameter> can produce a cursor or not, or <parameter>plan</parameter> can produce a cursor or not.
<symbol>SPI_ERROR_ARGUMENT</symbol> if the <parameter>plan</parameter> If the <parameter>plan</parameter> is <symbol>NULL</symbol> or invalid,
is <symbol>NULL</symbol> <varname>SPI_result</varname> is set to <symbol>SPI_ERROR_ARGUMENT</symbol>
and <symbol>false</symbol> is returned.
</para> </para>
</refsect1> </refsect1>
</refentry> </refentry>
...@@ -1001,7 +1013,7 @@ bool SPI_is_cursor_plan(void * <parameter>plan</parameter>) ...@@ -1001,7 +1013,7 @@ bool SPI_is_cursor_plan(void * <parameter>plan</parameter>)
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
int SPI_execute_plan(void * <parameter>plan</parameter>, Datum * <parameter>values</parameter>, const char * <parameter>nulls</parameter>, int SPI_execute_plan(SPIPlanPtr <parameter>plan</parameter>, Datum * <parameter>values</parameter>, const char * <parameter>nulls</parameter>,
bool <parameter>read_only</parameter>, long <parameter>count</parameter>) bool <parameter>read_only</parameter>, long <parameter>count</parameter>)
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -1022,7 +1034,7 @@ int SPI_execute_plan(void * <parameter>plan</parameter>, Datum * <parameter>valu ...@@ -1022,7 +1034,7 @@ int SPI_execute_plan(void * <parameter>plan</parameter>, Datum * <parameter>valu
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><literal>void * <parameter>plan</parameter></literal></term> <term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
<listitem> <listitem>
<para> <para>
execution plan (returned by <function>SPI_prepare</function>) execution plan (returned by <function>SPI_prepare</function>)
...@@ -1091,8 +1103,8 @@ int SPI_execute_plan(void * <parameter>plan</parameter>, Datum * <parameter>valu ...@@ -1091,8 +1103,8 @@ int SPI_execute_plan(void * <parameter>plan</parameter>, Datum * <parameter>valu
<term><symbol>SPI_ERROR_ARGUMENT</symbol></term> <term><symbol>SPI_ERROR_ARGUMENT</symbol></term>
<listitem> <listitem>
<para> <para>
if <parameter>plan</parameter> is <symbol>NULL</symbol> or if <parameter>plan</parameter> is <symbol>NULL</symbol> or invalid,
<parameter>count</parameter> is less than 0 or <parameter>count</parameter> is less than 0
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -1143,7 +1155,7 @@ int SPI_execute_plan(void * <parameter>plan</parameter>, Datum * <parameter>valu ...@@ -1143,7 +1155,7 @@ int SPI_execute_plan(void * <parameter>plan</parameter>, Datum * <parameter>valu
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
int SPI_execp(void * <parameter>plan</parameter>, Datum * <parameter>values</parameter>, const char * <parameter>nulls</parameter>, long <parameter>count</parameter>) int SPI_execp(SPIPlanPtr <parameter>plan</parameter>, Datum * <parameter>values</parameter>, const char * <parameter>nulls</parameter>, long <parameter>count</parameter>)
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -1163,7 +1175,7 @@ int SPI_execp(void * <parameter>plan</parameter>, Datum * <parameter>values</par ...@@ -1163,7 +1175,7 @@ int SPI_execp(void * <parameter>plan</parameter>, Datum * <parameter>values</par
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><literal>void * <parameter>plan</parameter></literal></term> <term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
<listitem> <listitem>
<para> <para>
execution plan (returned by <function>SPI_prepare</function>) execution plan (returned by <function>SPI_prepare</function>)
...@@ -1242,7 +1254,7 @@ int SPI_execp(void * <parameter>plan</parameter>, Datum * <parameter>values</par ...@@ -1242,7 +1254,7 @@ int SPI_execp(void * <parameter>plan</parameter>, Datum * <parameter>values</par
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
Portal SPI_cursor_open(const char * <parameter>name</parameter>, void * <parameter>plan</parameter>, Portal SPI_cursor_open(const char * <parameter>name</parameter>, SPIPlanPtr <parameter>plan</parameter>,
Datum * <parameter>values</parameter>, const char * <parameter>nulls</parameter>, Datum * <parameter>values</parameter>, const char * <parameter>nulls</parameter>,
bool <parameter>read_only</parameter>) bool <parameter>read_only</parameter>)
</synopsis> </synopsis>
...@@ -1268,6 +1280,11 @@ Portal SPI_cursor_open(const char * <parameter>name</parameter>, void * <paramet ...@@ -1268,6 +1280,11 @@ Portal SPI_cursor_open(const char * <parameter>name</parameter>, void * <paramet
to the procedure's caller provides a way of returning a row set as to the procedure's caller provides a way of returning a row set as
result. result.
</para> </para>
<para>
The passed-in data will be copied into the cursor's portal, so it
can be freed while the cursor still exists.
</para>
</refsect1> </refsect1>
<refsect1> <refsect1>
...@@ -1285,7 +1302,7 @@ Portal SPI_cursor_open(const char * <parameter>name</parameter>, void * <paramet ...@@ -1285,7 +1302,7 @@ Portal SPI_cursor_open(const char * <parameter>name</parameter>, void * <paramet
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><literal>void * <parameter>plan</parameter></literal></term> <term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
<listitem> <listitem>
<para> <para>
execution plan (returned by <function>SPI_prepare</function>) execution plan (returned by <function>SPI_prepare</function>)
...@@ -1602,7 +1619,7 @@ void SPI_cursor_close(Portal <parameter>portal</parameter>) ...@@ -1602,7 +1619,7 @@ void SPI_cursor_close(Portal <parameter>portal</parameter>)
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
void * SPI_saveplan(void * <parameter>plan</parameter>) SPIPlanPtr SPI_saveplan(SPIPlanPtr <parameter>plan</parameter>)
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -1611,8 +1628,8 @@ void * SPI_saveplan(void * <parameter>plan</parameter>) ...@@ -1611,8 +1628,8 @@ void * SPI_saveplan(void * <parameter>plan</parameter>)
<para> <para>
<function>SPI_saveplan</function> saves a passed plan (prepared by <function>SPI_saveplan</function> saves a passed plan (prepared by
<function>SPI_prepare</function>) in memory protected from freeing <function>SPI_prepare</function>) in memory that will not be freed
by <function>SPI_finish</function> and by the transaction manager by <function>SPI_finish</function> nor by the transaction manager,
and returns a pointer to the saved plan. This gives you the and returns a pointer to the saved plan. This gives you the
ability to reuse prepared plans in the subsequent invocations of ability to reuse prepared plans in the subsequent invocations of
your procedure in the current session. your procedure in the current session.
...@@ -1624,7 +1641,7 @@ void * SPI_saveplan(void * <parameter>plan</parameter>) ...@@ -1624,7 +1641,7 @@ void * SPI_saveplan(void * <parameter>plan</parameter>)
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><literal>void * <parameter>plan</parameter></literal></term> <term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
<listitem> <listitem>
<para> <para>
the plan to be saved the plan to be saved
...@@ -1646,7 +1663,7 @@ void * SPI_saveplan(void * <parameter>plan</parameter>) ...@@ -1646,7 +1663,7 @@ void * SPI_saveplan(void * <parameter>plan</parameter>)
<term><symbol>SPI_ERROR_ARGUMENT</symbol></term> <term><symbol>SPI_ERROR_ARGUMENT</symbol></term>
<listitem> <listitem>
<para> <para>
if <parameter>plan</parameter> is <symbol>NULL</symbol> if <parameter>plan</parameter> is <symbol>NULL</symbol> or invalid
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -1666,10 +1683,17 @@ void * SPI_saveplan(void * <parameter>plan</parameter>) ...@@ -1666,10 +1683,17 @@ void * SPI_saveplan(void * <parameter>plan</parameter>)
<refsect1> <refsect1>
<title>Notes</title> <title>Notes</title>
<para>
The passed-in plan is not freed, so you might wish to do
<function>SPI_freeplan</function> on it to avoid leaking memory
until <function>SPI_finish</>.
</para>
<para> <para>
If one of the objects (a table, function, etc.) referenced by the If one of the objects (a table, function, etc.) referenced by the
prepared plan is dropped during the session then the results of prepared plan is dropped or redefined, then future executions of
<function>SPI_execute_plan</function> for this plan will be unpredictable. <function>SPI_execute_plan</function> may fail or return different
results than the plan initially indicates.
</para> </para>
</refsect1> </refsect1>
</refentry> </refentry>
...@@ -2879,7 +2903,7 @@ void SPI_freetuptable(SPITupleTable * <parameter>tuptable</parameter>) ...@@ -2879,7 +2903,7 @@ void SPI_freetuptable(SPITupleTable * <parameter>tuptable</parameter>)
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
int SPI_freeplan(void *<parameter>plan</parameter>) int SPI_freeplan(SPIPlanPtr <parameter>plan</parameter>)
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -2898,7 +2922,7 @@ int SPI_freeplan(void *<parameter>plan</parameter>) ...@@ -2898,7 +2922,7 @@ int SPI_freeplan(void *<parameter>plan</parameter>)
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><literal>void * <parameter>plan</parameter></literal></term> <term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
<listitem> <listitem>
<para> <para>
pointer to plan to free pointer to plan to free
...@@ -2913,7 +2937,7 @@ int SPI_freeplan(void *<parameter>plan</parameter>) ...@@ -2913,7 +2937,7 @@ int SPI_freeplan(void *<parameter>plan</parameter>)
<para> <para>
<symbol>SPI_ERROR_ARGUMENT</symbol> if <parameter>plan</parameter> <symbol>SPI_ERROR_ARGUMENT</symbol> if <parameter>plan</parameter>
is <symbol>NULL</symbol>. is <symbol>NULL</symbol> or invalid
</para> </para>
</refsect1> </refsect1>
</refentry> </refentry>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.171 2007/03/13 00:33:40 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.172 2007/03/15 23:12:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -34,9 +34,9 @@ static int _SPI_stack_depth = 0; /* allocated size of _SPI_stack */ ...@@ -34,9 +34,9 @@ static int _SPI_stack_depth = 0; /* allocated size of _SPI_stack */
static int _SPI_connected = -1; static int _SPI_connected = -1;
static int _SPI_curid = -1; static int _SPI_curid = -1;
static void _SPI_prepare_plan(const char *src, _SPI_plan *plan); static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan);
static int _SPI_execute_plan(_SPI_plan *plan, static int _SPI_execute_plan(SPIPlanPtr plan,
Datum *Values, const char *Nulls, Datum *Values, const char *Nulls,
Snapshot snapshot, Snapshot crosscheck_snapshot, Snapshot snapshot, Snapshot crosscheck_snapshot,
bool read_only, long tcount); bool read_only, long tcount);
...@@ -48,7 +48,8 @@ static void _SPI_error_callback(void *arg); ...@@ -48,7 +48,8 @@ static void _SPI_error_callback(void *arg);
static void _SPI_cursor_operation(Portal portal, bool forward, long count, static void _SPI_cursor_operation(Portal portal, bool forward, long count,
DestReceiver *dest); DestReceiver *dest);
static _SPI_plan *_SPI_copy_plan(_SPI_plan *plan, int location); static SPIPlanPtr _SPI_copy_plan(SPIPlanPtr plan, MemoryContext parentcxt);
static SPIPlanPtr _SPI_save_plan(SPIPlanPtr plan);
static int _SPI_begin_call(bool execmem); static int _SPI_begin_call(bool execmem);
static int _SPI_end_call(bool procmem); static int _SPI_end_call(bool procmem);
...@@ -306,10 +307,8 @@ SPI_execute(const char *src, bool read_only, long tcount) ...@@ -306,10 +307,8 @@ SPI_execute(const char *src, bool read_only, long tcount)
if (res < 0) if (res < 0)
return res; return res;
plan.plancxt = NULL; /* doesn't have own context */ memset(&plan, 0, sizeof(_SPI_plan));
plan.query = src; plan.magic = _SPI_PLAN_MAGIC;
plan.nargs = 0;
plan.argtypes = NULL;
_SPI_prepare_plan(src, &plan); _SPI_prepare_plan(src, &plan);
...@@ -330,22 +329,22 @@ SPI_exec(const char *src, long tcount) ...@@ -330,22 +329,22 @@ SPI_exec(const char *src, long tcount)
/* Execute a previously prepared plan */ /* Execute a previously prepared plan */
int int
SPI_execute_plan(void *plan, Datum *Values, const char *Nulls, SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
bool read_only, long tcount) bool read_only, long tcount)
{ {
int res; int res;
if (plan == NULL || tcount < 0) if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
return SPI_ERROR_ARGUMENT; return SPI_ERROR_ARGUMENT;
if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL) if (plan->nargs > 0 && Values == NULL)
return SPI_ERROR_PARAM; return SPI_ERROR_PARAM;
res = _SPI_begin_call(true); res = _SPI_begin_call(true);
if (res < 0) if (res < 0)
return res; return res;
res = _SPI_execute_plan((_SPI_plan *) plan, res = _SPI_execute_plan(plan,
Values, Nulls, Values, Nulls,
InvalidSnapshot, InvalidSnapshot, InvalidSnapshot, InvalidSnapshot,
read_only, tcount); read_only, tcount);
...@@ -356,7 +355,7 @@ SPI_execute_plan(void *plan, Datum *Values, const char *Nulls, ...@@ -356,7 +355,7 @@ SPI_execute_plan(void *plan, Datum *Values, const char *Nulls,
/* Obsolete version of SPI_execute_plan */ /* Obsolete version of SPI_execute_plan */
int int
SPI_execp(void *plan, Datum *Values, const char *Nulls, long tcount) SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, long tcount)
{ {
return SPI_execute_plan(plan, Values, Nulls, false, tcount); return SPI_execute_plan(plan, Values, Nulls, false, tcount);
} }
...@@ -371,24 +370,24 @@ SPI_execp(void *plan, Datum *Values, const char *Nulls, long tcount) ...@@ -371,24 +370,24 @@ SPI_execp(void *plan, Datum *Values, const char *Nulls, long tcount)
* fetching a new snapshot for each query. * fetching a new snapshot for each query.
*/ */
int int
SPI_execute_snapshot(void *plan, SPI_execute_snapshot(SPIPlanPtr plan,
Datum *Values, const char *Nulls, Datum *Values, const char *Nulls,
Snapshot snapshot, Snapshot crosscheck_snapshot, Snapshot snapshot, Snapshot crosscheck_snapshot,
bool read_only, long tcount) bool read_only, long tcount)
{ {
int res; int res;
if (plan == NULL || tcount < 0) if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
return SPI_ERROR_ARGUMENT; return SPI_ERROR_ARGUMENT;
if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL) if (plan->nargs > 0 && Values == NULL)
return SPI_ERROR_PARAM; return SPI_ERROR_PARAM;
res = _SPI_begin_call(true); res = _SPI_begin_call(true);
if (res < 0) if (res < 0)
return res; return res;
res = _SPI_execute_plan((_SPI_plan *) plan, res = _SPI_execute_plan(plan,
Values, Nulls, Values, Nulls,
snapshot, crosscheck_snapshot, snapshot, crosscheck_snapshot,
read_only, tcount); read_only, tcount);
...@@ -397,11 +396,11 @@ SPI_execute_snapshot(void *plan, ...@@ -397,11 +396,11 @@ SPI_execute_snapshot(void *plan,
return res; return res;
} }
void * SPIPlanPtr
SPI_prepare(const char *src, int nargs, Oid *argtypes) SPI_prepare(const char *src, int nargs, Oid *argtypes)
{ {
_SPI_plan plan; _SPI_plan plan;
_SPI_plan *result; SPIPlanPtr result;
if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL)) if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
{ {
...@@ -413,27 +412,28 @@ SPI_prepare(const char *src, int nargs, Oid *argtypes) ...@@ -413,27 +412,28 @@ SPI_prepare(const char *src, int nargs, Oid *argtypes)
if (SPI_result < 0) if (SPI_result < 0)
return NULL; return NULL;
plan.plancxt = NULL; /* doesn't have own context */ memset(&plan, 0, sizeof(_SPI_plan));
plan.query = src; plan.magic = _SPI_PLAN_MAGIC;
plan.nargs = nargs; plan.nargs = nargs;
plan.argtypes = argtypes; plan.argtypes = argtypes;
_SPI_prepare_plan(src, &plan); _SPI_prepare_plan(src, &plan);
/* copy plan to procedure context */ /* copy plan to procedure context */
result = _SPI_copy_plan(&plan, _SPI_CPLAN_PROCXT); result = _SPI_copy_plan(&plan, _SPI_current->procCxt);
_SPI_end_call(true); _SPI_end_call(true);
return (void *) result; return result;
} }
void * SPIPlanPtr
SPI_saveplan(void *plan) SPI_saveplan(SPIPlanPtr plan)
{ {
_SPI_plan *newplan; SPIPlanPtr newplan;
if (plan == NULL) /* We don't currently support copying an already-saved plan */
if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || plan->saved)
{ {
SPI_result = SPI_ERROR_ARGUMENT; SPI_result = SPI_ERROR_ARGUMENT;
return NULL; return NULL;
...@@ -443,23 +443,36 @@ SPI_saveplan(void *plan) ...@@ -443,23 +443,36 @@ SPI_saveplan(void *plan)
if (SPI_result < 0) if (SPI_result < 0)
return NULL; return NULL;
newplan = _SPI_copy_plan((_SPI_plan *) plan, _SPI_CPLAN_TOPCXT); newplan = _SPI_save_plan(plan);
_SPI_curid--; _SPI_curid--;
SPI_result = 0; SPI_result = 0;
return (void *) newplan; return newplan;
} }
int int
SPI_freeplan(void *plan) SPI_freeplan(SPIPlanPtr plan)
{ {
_SPI_plan *spiplan = (_SPI_plan *) plan; if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
if (plan == NULL)
return SPI_ERROR_ARGUMENT; return SPI_ERROR_ARGUMENT;
MemoryContextDelete(spiplan->plancxt); /* If plancache.c owns the plancache entries, we must release them */
if (plan->saved)
{
ListCell *lc;
foreach(lc, plan->plancache_list)
{
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
DropCachedPlan(plansource);
}
}
/* Now get rid of the _SPI_plan and subsidiary data in its plancxt */
MemoryContextDelete(plan->plancxt);
return 0; return 0;
} }
...@@ -819,18 +832,18 @@ SPI_freetuptable(SPITupleTable *tuptable) ...@@ -819,18 +832,18 @@ SPI_freetuptable(SPITupleTable *tuptable)
} }
/* /*
* SPI_cursor_open() * SPI_cursor_open()
* *
* Open a prepared SPI plan as a portal * Open a prepared SPI plan as a portal
*/ */
Portal Portal
SPI_cursor_open(const char *name, void *plan, SPI_cursor_open(const char *name, SPIPlanPtr plan,
Datum *Values, const char *Nulls, Datum *Values, const char *Nulls,
bool read_only) bool read_only)
{ {
_SPI_plan *spiplan = (_SPI_plan *) plan; CachedPlanSource *plansource;
CachedPlan *cplan;
List *stmt_list; List *stmt_list;
ParamListInfo paramLI; ParamListInfo paramLI;
Snapshot snapshot; Snapshot snapshot;
...@@ -842,25 +855,23 @@ SPI_cursor_open(const char *name, void *plan, ...@@ -842,25 +855,23 @@ SPI_cursor_open(const char *name, void *plan,
* Check that the plan is something the Portal code will special-case as * Check that the plan is something the Portal code will special-case as
* returning one tupleset. * returning one tupleset.
*/ */
if (!SPI_is_cursor_plan(spiplan)) if (!SPI_is_cursor_plan(plan))
{ {
/* try to give a good error message */ /* try to give a good error message */
Node *stmt; if (list_length(plan->plancache_list) != 1)
if (list_length(spiplan->stmt_list_list) != 1)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION), (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
errmsg("cannot open multi-query plan as cursor"))); errmsg("cannot open multi-query plan as cursor")));
stmt = PortalListGetPrimaryStmt((List *) linitial(spiplan->stmt_list_list)); plansource = (CachedPlanSource *) linitial(plan->plancache_list);
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION), (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
/* translator: %s is name of a SQL command, eg INSERT */ /* translator: %s is name of a SQL command, eg INSERT */
errmsg("cannot open %s query as cursor", errmsg("cannot open %s query as cursor",
CreateCommandTag(stmt)))); plansource->commandTag)));
} }
Assert(list_length(spiplan->stmt_list_list) == 1); Assert(list_length(plan->plancache_list) == 1);
stmt_list = (List *) linitial(spiplan->stmt_list_list); plansource = (CachedPlanSource *) linitial(plan->plancache_list);
/* Reset SPI result (note we deliberately don't touch lastoid) */ /* Reset SPI result (note we deliberately don't touch lastoid) */
SPI_processed = 0; SPI_processed = 0;
...@@ -880,23 +891,20 @@ SPI_cursor_open(const char *name, void *plan, ...@@ -880,23 +891,20 @@ SPI_cursor_open(const char *name, void *plan,
portal = CreatePortal(name, false, false); portal = CreatePortal(name, false, false);
} }
/* Switch to portal's memory and copy the plans to there */ /* If the plan has parameters, copy them into the portal */
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); if (plan->nargs > 0)
stmt_list = copyObject(stmt_list);
/* If the plan has parameters, set them up */
if (spiplan->nargs > 0)
{ {
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
/* sizeof(ParamListInfoData) includes the first array element */ /* sizeof(ParamListInfoData) includes the first array element */
paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) + paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
(spiplan->nargs - 1) *sizeof(ParamExternData)); (plan->nargs - 1) *sizeof(ParamExternData));
paramLI->numParams = spiplan->nargs; paramLI->numParams = plan->nargs;
for (k = 0; k < spiplan->nargs; k++) for (k = 0; k < plan->nargs; k++)
{ {
ParamExternData *prm = &paramLI->params[k]; ParamExternData *prm = &paramLI->params[k];
prm->ptype = spiplan->argtypes[k]; prm->ptype = plan->argtypes[k];
prm->pflags = 0; prm->pflags = 0;
prm->isnull = (Nulls && Nulls[k] == 'n'); prm->isnull = (Nulls && Nulls[k] == 'n');
if (prm->isnull) if (prm->isnull)
...@@ -915,21 +923,35 @@ SPI_cursor_open(const char *name, void *plan, ...@@ -915,21 +923,35 @@ SPI_cursor_open(const char *name, void *plan,
paramTypByVal, paramTypLen); paramTypByVal, paramTypLen);
} }
} }
MemoryContextSwitchTo(oldcontext);
} }
else else
paramLI = NULL; paramLI = NULL;
if (plan->saved)
{
/* Replan if needed, and increment plan refcount for portal */
cplan = RevalidateCachedPlan(plansource, false);
stmt_list = cplan->stmt_list;
}
else
{
/* No replan, but copy the plan into the portal's context */
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
stmt_list = copyObject(plansource->plan->stmt_list);
MemoryContextSwitchTo(oldcontext);
cplan = NULL; /* portal shouldn't depend on cplan */
}
/* /*
* Set up the portal. * Set up the portal.
*/ */
PortalDefineQuery(portal, PortalDefineQuery(portal,
NULL, /* no statement name */ NULL, /* no statement name */
spiplan->query, plansource->query_string,
CreateCommandTag(PortalListGetPrimaryStmt(stmt_list)), plansource->commandTag,
stmt_list, stmt_list,
NULL); cplan);
MemoryContextSwitchTo(oldcontext);
/* /*
* Set up options for portal. * Set up options for portal.
...@@ -1023,28 +1045,29 @@ SPI_cursor_close(Portal portal) ...@@ -1023,28 +1045,29 @@ SPI_cursor_close(Portal portal)
* parameter is at index zero. * parameter is at index zero.
*/ */
Oid Oid
SPI_getargtypeid(void *plan, int argIndex) SPI_getargtypeid(SPIPlanPtr plan, int argIndex)
{ {
if (plan == NULL || argIndex < 0 || argIndex >= ((_SPI_plan *) plan)->nargs) if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC ||
argIndex < 0 || argIndex >= plan->nargs)
{ {
SPI_result = SPI_ERROR_ARGUMENT; SPI_result = SPI_ERROR_ARGUMENT;
return InvalidOid; return InvalidOid;
} }
return ((_SPI_plan *) plan)->argtypes[argIndex]; return plan->argtypes[argIndex];
} }
/* /*
* Returns the number of arguments for the prepared plan. * Returns the number of arguments for the prepared plan.
*/ */
int int
SPI_getargcount(void *plan) SPI_getargcount(SPIPlanPtr plan)
{ {
if (plan == NULL) if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
{ {
SPI_result = SPI_ERROR_ARGUMENT; SPI_result = SPI_ERROR_ARGUMENT;
return -1; return -1;
} }
return ((_SPI_plan *) plan)->nargs; return plan->nargs;
} }
/* /*
...@@ -1057,31 +1080,32 @@ SPI_getargcount(void *plan) ...@@ -1057,31 +1080,32 @@ SPI_getargcount(void *plan)
* plan: A plan previously prepared using SPI_prepare * plan: A plan previously prepared using SPI_prepare
*/ */
bool bool
SPI_is_cursor_plan(void *plan) SPI_is_cursor_plan(SPIPlanPtr plan)
{ {
_SPI_plan *spiplan = (_SPI_plan *) plan; CachedPlanSource *plansource;
CachedPlan *cplan;
if (spiplan == NULL) if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
{ {
SPI_result = SPI_ERROR_ARGUMENT; SPI_result = SPI_ERROR_ARGUMENT;
return false; return false;
} }
if (list_length(spiplan->stmt_list_list) != 1) if (list_length(plan->plancache_list) != 1)
return false; /* not exactly 1 pre-rewrite command */ return false; /* not exactly 1 pre-rewrite command */
plansource = (CachedPlanSource *) linitial(plan->plancache_list);
switch (ChoosePortalStrategy((List *) linitial(spiplan->stmt_list_list))) if (plan->saved)
{ {
case PORTAL_ONE_SELECT: /* Make sure the plan is up to date */
case PORTAL_ONE_RETURNING: cplan = RevalidateCachedPlan(plansource, true);
case PORTAL_UTIL_SELECT: ReleaseCachedPlan(cplan, true);
/* OK */
return true;
case PORTAL_MULTI_QUERY:
/* will not return tuples */
break;
} }
/* Does it return tuples? */
if (plansource->resultDesc)
return true;
return false; return false;
} }
...@@ -1248,13 +1272,15 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self) ...@@ -1248,13 +1272,15 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
* *
* At entry, plan->argtypes and plan->nargs must be valid. * At entry, plan->argtypes and plan->nargs must be valid.
* *
* Result lists are stored into *plan. * Results are stored into *plan (specifically, plan->plancache_list).
* Note however that the result trees are all in CurrentMemoryContext
* and need to be copied somewhere to survive.
*/ */
static void static void
_SPI_prepare_plan(const char *src, _SPI_plan *plan) _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
{ {
List *raw_parsetree_list; List *raw_parsetree_list;
List *stmt_list_list; List *plancache_list;
ListCell *list_item; ListCell *list_item;
ErrorContextCallback spierrcontext; ErrorContextCallback spierrcontext;
Oid *argtypes = plan->argtypes; Oid *argtypes = plan->argtypes;
...@@ -1282,26 +1308,44 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan) ...@@ -1282,26 +1308,44 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan)
raw_parsetree_list = pg_parse_query(src); raw_parsetree_list = pg_parse_query(src);
/* /*
* Do parse analysis and rule rewrite for each raw parsetree. * Do parse analysis and rule rewrite for each raw parsetree, then
* * cons up a phony plancache entry for each one.
* We save the results from each raw parsetree as a separate sublist.
* This allows _SPI_execute_plan() to know where the boundaries between
* original queries fall.
*/ */
stmt_list_list = NIL; plancache_list = NIL;
foreach(list_item, raw_parsetree_list) foreach(list_item, raw_parsetree_list)
{ {
Node *parsetree = (Node *) lfirst(list_item); Node *parsetree = (Node *) lfirst(list_item);
List *query_list; List *stmt_list;
CachedPlanSource *plansource;
query_list = pg_analyze_and_rewrite(parsetree, src, argtypes, nargs); CachedPlan *cplan;
stmt_list_list = lappend(stmt_list_list, /* Need a copyObject here to keep parser from modifying raw tree */
pg_plan_queries(query_list, NULL, false)); stmt_list = pg_analyze_and_rewrite(copyObject(parsetree),
src, argtypes, nargs);
stmt_list = pg_plan_queries(stmt_list, NULL, false);
plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
cplan = (CachedPlan *) palloc0(sizeof(CachedPlan));
plansource->raw_parse_tree = parsetree;
/* cast-away-const here is a bit ugly, but there's no reason to copy */
plansource->query_string = (char *) src;
plansource->commandTag = CreateCommandTag(parsetree);
plansource->param_types = argtypes;
plansource->num_params = nargs;
plansource->fully_planned = true;
plansource->fixed_result = false;
plansource->resultDesc = PlanCacheComputeResultDesc(stmt_list);
plansource->plan = cplan;
cplan->stmt_list = stmt_list;
cplan->fully_planned = true;
plancache_list = lappend(plancache_list, plansource);
} }
plan->stmt_list_list = stmt_list_list; plan->plancache_list = plancache_list;
/* /*
* Pop the error context stack * Pop the error context stack
...@@ -1319,7 +1363,7 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan) ...@@ -1319,7 +1363,7 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan)
* tcount: execution tuple-count limit, or 0 for none * tcount: execution tuple-count limit, or 0 for none
*/ */
static int static int
_SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, _SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
Snapshot snapshot, Snapshot crosscheck_snapshot, Snapshot snapshot, Snapshot crosscheck_snapshot,
bool read_only, long tcount) bool read_only, long tcount)
{ {
...@@ -1334,10 +1378,11 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, ...@@ -1334,10 +1378,11 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
saveActiveSnapshot = ActiveSnapshot; saveActiveSnapshot = ActiveSnapshot;
PG_TRY(); PG_TRY();
{ {
ListCell *stmt_list_list_item; ListCell *lc1;
ErrorContextCallback spierrcontext; ErrorContextCallback spierrcontext;
int nargs = plan->nargs; int nargs = plan->nargs;
ParamListInfo paramLI; ParamListInfo paramLI;
CachedPlan *cplan = NULL;
/* Convert parameters to form wanted by executor */ /* Convert parameters to form wanted by executor */
if (nargs > 0) if (nargs > 0)
...@@ -1366,18 +1411,34 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, ...@@ -1366,18 +1411,34 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
* Setup error traceback support for ereport() * Setup error traceback support for ereport()
*/ */
spierrcontext.callback = _SPI_error_callback; spierrcontext.callback = _SPI_error_callback;
spierrcontext.arg = (void *) plan->query; spierrcontext.arg = NULL;
spierrcontext.previous = error_context_stack; spierrcontext.previous = error_context_stack;
error_context_stack = &spierrcontext; error_context_stack = &spierrcontext;
foreach(stmt_list_list_item, plan->stmt_list_list) foreach(lc1, plan->plancache_list)
{ {
List *stmt_list = (List *) lfirst(stmt_list_list_item); CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
ListCell *stmt_list_item; List *stmt_list;
ListCell *lc2;
foreach(stmt_list_item, stmt_list) spierrcontext.arg = (void *) plansource->query_string;
if (plan->saved)
{ {
Node *stmt = (Node *) lfirst(stmt_list_item); /* Replan if needed, and increment plan refcount locally */
cplan = RevalidateCachedPlan(plansource, true);
stmt_list = cplan->stmt_list;
}
else
{
/* No replan here */
cplan = NULL;
stmt_list = plansource->plan->stmt_list;
}
foreach(lc2, stmt_list)
{
Node *stmt = (Node *) lfirst(lc2);
bool canSetTag; bool canSetTag;
QueryDesc *qdesc; QueryDesc *qdesc;
DestReceiver *dest; DestReceiver *dest;
...@@ -1510,10 +1571,19 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, ...@@ -1510,10 +1571,19 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
goto fail; goto fail;
} }
} }
/* Done with this plan, so release refcount */
if (cplan)
ReleaseCachedPlan(cplan, true);
cplan = NULL;
} }
fail: fail:
/* We no longer need the cached plan refcount, if any */
if (cplan)
ReleaseCachedPlan(cplan, true);
/* /*
* Pop the error context stack * Pop the error context stack
*/ */
...@@ -1772,22 +1842,18 @@ _SPI_checktuples(void) ...@@ -1772,22 +1842,18 @@ _SPI_checktuples(void)
return failed; return failed;
} }
static _SPI_plan * /*
_SPI_copy_plan(_SPI_plan *plan, int location) * Make an "unsaved" copy of the given plan, in a child context of parentcxt.
*/
static SPIPlanPtr
_SPI_copy_plan(SPIPlanPtr plan, MemoryContext parentcxt)
{ {
_SPI_plan *newplan; SPIPlanPtr newplan;
MemoryContext oldcxt;
MemoryContext plancxt; MemoryContext plancxt;
MemoryContext parentcxt; MemoryContext oldcxt;
ListCell *lc;
/* Determine correct parent for the plan's memory context */ Assert(!plan->saved); /* not currently supported */
if (location == _SPI_CPLAN_PROCXT)
parentcxt = _SPI_current->procCxt;
else if (location == _SPI_CPLAN_TOPCXT)
parentcxt = TopMemoryContext;
else
/* (this case not currently used) */
parentcxt = CurrentMemoryContext;
/* /*
* Create a memory context for the plan. We don't expect the plan to be * Create a memory context for the plan. We don't expect the plan to be
...@@ -1801,10 +1867,11 @@ _SPI_copy_plan(_SPI_plan *plan, int location) ...@@ -1801,10 +1867,11 @@ _SPI_copy_plan(_SPI_plan *plan, int location)
oldcxt = MemoryContextSwitchTo(plancxt); oldcxt = MemoryContextSwitchTo(plancxt);
/* Copy the SPI plan into its own context */ /* Copy the SPI plan into its own context */
newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan)); newplan = (SPIPlanPtr) palloc(sizeof(_SPI_plan));
newplan->magic = _SPI_PLAN_MAGIC;
newplan->saved = false;
newplan->plancache_list = NIL;
newplan->plancxt = plancxt; newplan->plancxt = plancxt;
newplan->query = pstrdup(plan->query);
newplan->stmt_list_list = (List *) copyObject(plan->stmt_list_list);
newplan->nargs = plan->nargs; newplan->nargs = plan->nargs;
if (plan->nargs > 0) if (plan->nargs > 0)
{ {
...@@ -1814,6 +1881,101 @@ _SPI_copy_plan(_SPI_plan *plan, int location) ...@@ -1814,6 +1881,101 @@ _SPI_copy_plan(_SPI_plan *plan, int location)
else else
newplan->argtypes = NULL; newplan->argtypes = NULL;
foreach(lc, plan->plancache_list)
{
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
CachedPlanSource *newsource;
CachedPlan *cplan;
CachedPlan *newcplan;
/* Note: we assume we don't need to revalidate the plan */
cplan = plansource->plan;
newsource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
newcplan = (CachedPlan *) palloc0(sizeof(CachedPlan));
newsource->raw_parse_tree = copyObject(plansource->raw_parse_tree);
newsource->query_string = pstrdup(plansource->query_string);
newsource->commandTag = plansource->commandTag;
newsource->param_types = newplan->argtypes;
newsource->num_params = newplan->nargs;
newsource->fully_planned = plansource->fully_planned;
newsource->fixed_result = plansource->fixed_result;
if (plansource->resultDesc)
newsource->resultDesc = CreateTupleDescCopy(plansource->resultDesc);
newsource->plan = newcplan;
newcplan->stmt_list = copyObject(cplan->stmt_list);
newcplan->fully_planned = cplan->fully_planned;
newplan->plancache_list = lappend(newplan->plancache_list, newsource);
}
MemoryContextSwitchTo(oldcxt);
return newplan;
}
/*
* Make a "saved" copy of the given plan, entrusting everything to plancache.c
*/
static SPIPlanPtr
_SPI_save_plan(SPIPlanPtr plan)
{
SPIPlanPtr newplan;
MemoryContext plancxt;
MemoryContext oldcxt;
ListCell *lc;
Assert(!plan->saved); /* not currently supported */
/*
* Create a memory context for the plan. We don't expect the plan to be
* very large, so use smaller-than-default alloc parameters.
*/
plancxt = AllocSetContextCreate(CacheMemoryContext,
"SPI Plan",
ALLOCSET_SMALL_MINSIZE,
ALLOCSET_SMALL_INITSIZE,
ALLOCSET_SMALL_MAXSIZE);
oldcxt = MemoryContextSwitchTo(plancxt);
/* Copy the SPI plan into its own context */
newplan = (SPIPlanPtr) palloc(sizeof(_SPI_plan));
newplan->magic = _SPI_PLAN_MAGIC;
newplan->saved = true;
newplan->plancache_list = NIL;
newplan->plancxt = plancxt;
newplan->nargs = plan->nargs;
if (plan->nargs > 0)
{
newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
}
else
newplan->argtypes = NULL;
foreach(lc, plan->plancache_list)
{
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
CachedPlanSource *newsource;
CachedPlan *cplan;
/* Note: we assume we don't need to revalidate the plan */
cplan = plansource->plan;
newsource = CreateCachedPlan(plansource->raw_parse_tree,
plansource->query_string,
plansource->commandTag,
newplan->argtypes,
newplan->nargs,
cplan->stmt_list,
true,
false);
newplan->plancache_list = lappend(newplan->plancache_list, newsource);
}
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
return newplan; return newplan;
......
...@@ -8,16 +8,14 @@ ...@@ -8,16 +8,14 @@
* across query and transaction boundaries, in fact they live as long as * across query and transaction boundaries, in fact they live as long as
* the backend does. This works because the hashtable structures * the backend does. This works because the hashtable structures
* themselves are allocated by dynahash.c in its permanent DynaHashCxt, * themselves are allocated by dynahash.c in its permanent DynaHashCxt,
* and the parse/plan node trees they point to are copied into * and the SPI plans they point to are saved using SPI_saveplan().
* TopMemoryContext using SPI_saveplan(). This is pretty ugly, since there * There is not currently any provision for throwing away a no-longer-needed
* is no way to free a no-longer-needed plan tree, but then again we don't * plan --- consider improving this someday.
* yet have any bookkeeping that would allow us to detect that a plan isn't
* needed anymore. Improve it someday.
* *
* *
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.91 2007/02/14 01:58:57 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.92 2007/03/15 23:12:06 tgl Exp $
* *
* ---------- * ----------
*/ */
...@@ -35,7 +33,7 @@ ...@@ -35,7 +33,7 @@
#include "catalog/pg_constraint.h" #include "catalog/pg_constraint.h"
#include "catalog/pg_operator.h" #include "catalog/pg_operator.h"
#include "commands/trigger.h" #include "commands/trigger.h"
#include "executor/spi_priv.h" #include "executor/spi.h"
#include "parser/parse_coerce.h" #include "parser/parse_coerce.h"
#include "parser/parse_relation.h" #include "parser/parse_relation.h"
#include "miscadmin.h" #include "miscadmin.h"
...@@ -135,7 +133,7 @@ typedef struct RI_QueryKey ...@@ -135,7 +133,7 @@ typedef struct RI_QueryKey
typedef struct RI_QueryHashEntry typedef struct RI_QueryHashEntry
{ {
RI_QueryKey key; RI_QueryKey key;
void *plan; SPIPlanPtr plan;
} RI_QueryHashEntry; } RI_QueryHashEntry;
...@@ -206,18 +204,18 @@ static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel, ...@@ -206,18 +204,18 @@ static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
const RI_ConstraintInfo *riinfo); const RI_ConstraintInfo *riinfo);
static void ri_InitHashTables(void); static void ri_InitHashTables(void);
static void *ri_FetchPreparedPlan(RI_QueryKey *key); static SPIPlanPtr ri_FetchPreparedPlan(RI_QueryKey *key);
static void ri_HashPreparedPlan(RI_QueryKey *key, void *plan); static void ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan);
static RI_CompareHashEntry *ri_HashCompareOp(Oid eq_opr, Oid typeid); static RI_CompareHashEntry *ri_HashCompareOp(Oid eq_opr, Oid typeid);
static void ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname, static void ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname,
int tgkind); int tgkind);
static void ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo, static void ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo,
Trigger *trigger, Relation trig_rel, bool rel_is_pk); Trigger *trigger, Relation trig_rel, bool rel_is_pk);
static void *ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel, RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
bool cache_plan); bool cache_plan);
static bool ri_PerformCheck(RI_QueryKey *qkey, void *qplan, static bool ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
Relation fk_rel, Relation pk_rel, Relation fk_rel, Relation pk_rel,
HeapTuple old_tuple, HeapTuple new_tuple, HeapTuple old_tuple, HeapTuple new_tuple,
bool detectNewRows, bool detectNewRows,
...@@ -248,7 +246,7 @@ RI_FKey_check(PG_FUNCTION_ARGS) ...@@ -248,7 +246,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
HeapTuple old_row; HeapTuple old_row;
Buffer new_row_buf; Buffer new_row_buf;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
/* /*
...@@ -542,7 +540,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel, ...@@ -542,7 +540,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
HeapTuple old_row, HeapTuple old_row,
const RI_ConstraintInfo *riinfo) const RI_ConstraintInfo *riinfo)
{ {
void *qplan; SPIPlanPtr qplan;
RI_QueryKey qkey; RI_QueryKey qkey;
int i; int i;
bool result; bool result;
...@@ -678,7 +676,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS) ...@@ -678,7 +676,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
Relation pk_rel; Relation pk_rel;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
/* /*
...@@ -855,7 +853,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS) ...@@ -855,7 +853,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
HeapTuple new_row; HeapTuple new_row;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
/* /*
...@@ -1040,7 +1038,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS) ...@@ -1040,7 +1038,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
Relation pk_rel; Relation pk_rel;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
/* /*
...@@ -1203,7 +1201,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS) ...@@ -1203,7 +1201,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
HeapTuple new_row; HeapTuple new_row;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
int j; int j;
...@@ -1397,7 +1395,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS) ...@@ -1397,7 +1395,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
Relation pk_rel; Relation pk_rel;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
/* /*
...@@ -1569,7 +1567,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS) ...@@ -1569,7 +1567,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
HeapTuple new_row; HeapTuple new_row;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
/* /*
...@@ -1744,7 +1742,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS) ...@@ -1744,7 +1742,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
Relation pk_rel; Relation pk_rel;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
/* /*
...@@ -1916,7 +1914,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS) ...@@ -1916,7 +1914,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
HeapTuple new_row; HeapTuple new_row;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
bool use_cached_query; bool use_cached_query;
...@@ -2130,7 +2128,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS) ...@@ -2130,7 +2128,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
Relation pk_rel; Relation pk_rel;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
/* /*
* Check that this is a valid trigger call on the right time and event. * Check that this is a valid trigger call on the right time and event.
...@@ -2313,7 +2311,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS) ...@@ -2313,7 +2311,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
HeapTuple new_row; HeapTuple new_row;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
/* /*
* Check that this is a valid trigger call on the right time and event. * Check that this is a valid trigger call on the right time and event.
...@@ -2637,7 +2635,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) ...@@ -2637,7 +2635,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
int old_work_mem; int old_work_mem;
char workmembuf[32]; char workmembuf[32];
int spi_result; int spi_result;
void *qplan; SPIPlanPtr qplan;
/* /*
* Check to make sure current user has enough permissions to do the test * Check to make sure current user has enough permissions to do the test
...@@ -3165,12 +3163,12 @@ ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo, ...@@ -3165,12 +3163,12 @@ ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo,
* If cache_plan is true, the plan is saved into our plan hashtable * If cache_plan is true, the plan is saved into our plan hashtable
* so that we don't need to plan it again. * so that we don't need to plan it again.
*/ */
static void * static SPIPlanPtr
ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel, RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
bool cache_plan) bool cache_plan)
{ {
void *qplan; SPIPlanPtr qplan;
Relation query_rel; Relation query_rel;
Oid save_uid; Oid save_uid;
...@@ -3212,7 +3210,7 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, ...@@ -3212,7 +3210,7 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
* Perform a query to enforce an RI restriction * Perform a query to enforce an RI restriction
*/ */
static bool static bool
ri_PerformCheck(RI_QueryKey *qkey, void *qplan, ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
Relation fk_rel, Relation pk_rel, Relation fk_rel, Relation pk_rel,
HeapTuple old_tuple, HeapTuple new_tuple, HeapTuple old_tuple, HeapTuple new_tuple,
bool detectNewRows, bool detectNewRows,
...@@ -3576,7 +3574,7 @@ ri_InitHashTables(void) ...@@ -3576,7 +3574,7 @@ ri_InitHashTables(void)
* and saved SPI execution plans. Return the plan if found or NULL. * and saved SPI execution plans. Return the plan if found or NULL.
* ---------- * ----------
*/ */
static void * static SPIPlanPtr
ri_FetchPreparedPlan(RI_QueryKey *key) ri_FetchPreparedPlan(RI_QueryKey *key)
{ {
RI_QueryHashEntry *entry; RI_QueryHashEntry *entry;
...@@ -3606,7 +3604,7 @@ ri_FetchPreparedPlan(RI_QueryKey *key) ...@@ -3606,7 +3604,7 @@ ri_FetchPreparedPlan(RI_QueryKey *key)
* ---------- * ----------
*/ */
static void static void
ri_HashPreparedPlan(RI_QueryKey *key, void *plan) ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
{ {
RI_QueryHashEntry *entry; RI_QueryHashEntry *entry;
bool found; bool found;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.252 2007/02/27 23:48:08 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.253 2007/03/15 23:12:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -105,9 +105,9 @@ typedef struct ...@@ -105,9 +105,9 @@ typedef struct
* Global data * Global data
* ---------- * ----------
*/ */
static void *plan_getrulebyoid = NULL; static SPIPlanPtr plan_getrulebyoid = NULL;
static char *query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1"; static char *query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1";
static void *plan_getviewrule = NULL; static SPIPlanPtr plan_getviewrule = NULL;
static char *query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2"; static char *query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2";
...@@ -250,7 +250,7 @@ pg_get_ruledef_worker(Oid ruleoid, int prettyFlags) ...@@ -250,7 +250,7 @@ pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
if (plan_getrulebyoid == NULL) if (plan_getrulebyoid == NULL)
{ {
Oid argtypes[1]; Oid argtypes[1];
void *plan; SPIPlanPtr plan;
argtypes[0] = OIDOID; argtypes[0] = OIDOID;
plan = SPI_prepare(query_getrulebyoid, 1, argtypes); plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
...@@ -380,7 +380,7 @@ pg_get_viewdef_worker(Oid viewoid, int prettyFlags) ...@@ -380,7 +380,7 @@ pg_get_viewdef_worker(Oid viewoid, int prettyFlags)
if (plan_getviewrule == NULL) if (plan_getviewrule == NULL)
{ {
Oid argtypes[2]; Oid argtypes[2];
void *plan; SPIPlanPtr plan;
argtypes[0] = OIDOID; argtypes[0] = OIDOID;
argtypes[1] = NAMEOID; argtypes[1] = NAMEOID;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.34 2007/03/03 19:32:55 neilc Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.35 2007/03/15 23:12:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1800,7 +1800,7 @@ query_to_xmlschema(PG_FUNCTION_ARGS) ...@@ -1800,7 +1800,7 @@ query_to_xmlschema(PG_FUNCTION_ARGS)
const char *targetns = _textout(PG_GETARG_TEXT_P(3)); const char *targetns = _textout(PG_GETARG_TEXT_P(3));
const char *result; const char *result;
void *plan; SPIPlanPtr plan;
Portal portal; Portal portal;
SPI_connect(); SPI_connect();
...@@ -1871,7 +1871,7 @@ query_to_xml_and_xmlschema(PG_FUNCTION_ARGS) ...@@ -1871,7 +1871,7 @@ query_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
const char *targetns = _textout(PG_GETARG_TEXT_P(3)); const char *targetns = _textout(PG_GETARG_TEXT_P(3));
const char *xmlschema; const char *xmlschema;
void *plan; SPIPlanPtr plan;
Portal portal; Portal portal;
SPI_connect(); SPI_connect();
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.1 2007/03/13 00:33:42 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.2 2007/03/15 23:12:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -77,7 +77,6 @@ static void ScanQueryForRelids(Query *parsetree, ...@@ -77,7 +77,6 @@ static void ScanQueryForRelids(Query *parsetree,
void *arg); void *arg);
static bool ScanQueryWalker(Node *node, ScanQueryWalkerContext *context); static bool ScanQueryWalker(Node *node, ScanQueryWalkerContext *context);
static bool rowmark_member(List *rowMarks, int rt_index); static bool rowmark_member(List *rowMarks, int rt_index);
static TupleDesc ComputeResultDesc(List *stmt_list);
static void PlanCacheCallback(Datum arg, Oid relid); static void PlanCacheCallback(Datum arg, Oid relid);
static void InvalRelid(Oid relid, LOCKMODE lockmode, static void InvalRelid(Oid relid, LOCKMODE lockmode,
InvalRelidContext *context); InvalRelidContext *context);
...@@ -153,7 +152,7 @@ CreateCachedPlan(Node *raw_parse_tree, ...@@ -153,7 +152,7 @@ CreateCachedPlan(Node *raw_parse_tree,
plansource->fully_planned = fully_planned; plansource->fully_planned = fully_planned;
plansource->fixed_result = fixed_result; plansource->fixed_result = fixed_result;
plansource->generation = 0; /* StoreCachedPlan will increment */ plansource->generation = 0; /* StoreCachedPlan will increment */
plansource->resultDesc = ComputeResultDesc(stmt_list); plansource->resultDesc = PlanCacheComputeResultDesc(stmt_list);
plansource->plan = NULL; plansource->plan = NULL;
plansource->context = source_context; plansource->context = source_context;
plansource->orig_plan = NULL; plansource->orig_plan = NULL;
...@@ -225,7 +224,7 @@ FastCreateCachedPlan(Node *raw_parse_tree, ...@@ -225,7 +224,7 @@ FastCreateCachedPlan(Node *raw_parse_tree,
plansource->fully_planned = fully_planned; plansource->fully_planned = fully_planned;
plansource->fixed_result = fixed_result; plansource->fixed_result = fixed_result;
plansource->generation = 0; /* StoreCachedPlan will increment */ plansource->generation = 0; /* StoreCachedPlan will increment */
plansource->resultDesc = ComputeResultDesc(stmt_list); plansource->resultDesc = PlanCacheComputeResultDesc(stmt_list);
plansource->plan = NULL; plansource->plan = NULL;
plansource->context = context; plansource->context = context;
plansource->orig_plan = NULL; plansource->orig_plan = NULL;
...@@ -271,12 +270,13 @@ StoreCachedPlan(CachedPlanSource *plansource, ...@@ -271,12 +270,13 @@ StoreCachedPlan(CachedPlanSource *plansource,
{ {
/* /*
* Make a dedicated memory context for the CachedPlan and its * Make a dedicated memory context for the CachedPlan and its
* subsidiary data. * subsidiary data. It's probably not going to be large, but
* just in case, use the default maxsize parameter.
*/ */
plan_context = AllocSetContextCreate(CacheMemoryContext, plan_context = AllocSetContextCreate(CacheMemoryContext,
"CachedPlan", "CachedPlan",
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_SMALL_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_SMALL_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE); ALLOCSET_DEFAULT_MAXSIZE);
/* /*
...@@ -445,7 +445,7 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner) ...@@ -445,7 +445,7 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
* Check or update the result tupdesc. XXX should we use a weaker * Check or update the result tupdesc. XXX should we use a weaker
* condition than equalTupleDescs() here? * condition than equalTupleDescs() here?
*/ */
resultDesc = ComputeResultDesc(slist); resultDesc = PlanCacheComputeResultDesc(slist);
if (resultDesc == NULL && plansource->resultDesc == NULL) if (resultDesc == NULL && plansource->resultDesc == NULL)
{ {
/* OK, doesn't return tuples */ /* OK, doesn't return tuples */
...@@ -718,14 +718,14 @@ rowmark_member(List *rowMarks, int rt_index) ...@@ -718,14 +718,14 @@ rowmark_member(List *rowMarks, int rt_index)
} }
/* /*
* ComputeResultDesc: given a list of either fully-planned statements or * PlanCacheComputeResultDesc: given a list of either fully-planned statements
* Queries, determine the result tupledesc it will produce. Returns NULL * or Queries, determine the result tupledesc it will produce. Returns NULL
* if the execution will not return tuples. * if the execution will not return tuples.
* *
* Note: the result is created or copied into current memory context. * Note: the result is created or copied into current memory context.
*/ */
static TupleDesc TupleDesc
ComputeResultDesc(List *stmt_list) PlanCacheComputeResultDesc(List *stmt_list)
{ {
Node *node; Node *node;
Query *query; Query *query;
......
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* spi.h * spi.h
* Server Programming Interface public declarations
* *
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.58 2006/10/04 00:30:08 momjian Exp $ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.59 2007/03/15 23:12:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -24,6 +28,7 @@ ...@@ -24,6 +28,7 @@
#include "catalog/pg_language.h" #include "catalog/pg_language.h"
#include "catalog/pg_proc.h" #include "catalog/pg_proc.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "executor/execdefs.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "nodes/execnodes.h" #include "nodes/execnodes.h"
#include "nodes/params.h" #include "nodes/params.h"
...@@ -38,9 +43,9 @@ ...@@ -38,9 +43,9 @@
#include "utils/datum.h" #include "utils/datum.h"
#include "utils/portal.h" #include "utils/portal.h"
#include "utils/syscache.h" #include "utils/syscache.h"
#include "executor/execdefs.h"
typedef struct
typedef struct SPITupleTable
{ {
MemoryContext tuptabcxt; /* memory context of result table */ MemoryContext tuptabcxt; /* memory context of result table */
uint32 alloced; /* # of alloced vals */ uint32 alloced; /* # of alloced vals */
...@@ -49,6 +54,9 @@ typedef struct ...@@ -49,6 +54,9 @@ typedef struct
HeapTuple *vals; /* tuples */ HeapTuple *vals; /* tuples */
} SPITupleTable; } SPITupleTable;
/* Plans are opaque structs for standard users of SPI */
typedef struct _SPI_plan *SPIPlanPtr;
#define SPI_ERROR_CONNECT (-1) #define SPI_ERROR_CONNECT (-1)
#define SPI_ERROR_COPY (-2) #define SPI_ERROR_COPY (-2)
#define SPI_ERROR_OPUNKNOWN (-3) #define SPI_ERROR_OPUNKNOWN (-3)
...@@ -86,23 +94,23 @@ extern void SPI_push(void); ...@@ -86,23 +94,23 @@ extern void SPI_push(void);
extern void SPI_pop(void); extern void SPI_pop(void);
extern void SPI_restore_connection(void); extern void SPI_restore_connection(void);
extern int SPI_execute(const char *src, bool read_only, long tcount); extern int SPI_execute(const char *src, bool read_only, long tcount);
extern int SPI_execute_plan(void *plan, Datum *Values, const char *Nulls, extern int SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
bool read_only, long tcount); bool read_only, long tcount);
extern int SPI_exec(const char *src, long tcount); extern int SPI_exec(const char *src, long tcount);
extern int SPI_execp(void *plan, Datum *Values, const char *Nulls, extern int SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls,
long tcount); long tcount);
extern int SPI_execute_snapshot(void *plan, extern int SPI_execute_snapshot(SPIPlanPtr plan,
Datum *Values, const char *Nulls, Datum *Values, const char *Nulls,
Snapshot snapshot, Snapshot snapshot,
Snapshot crosscheck_snapshot, Snapshot crosscheck_snapshot,
bool read_only, long tcount); bool read_only, long tcount);
extern void *SPI_prepare(const char *src, int nargs, Oid *argtypes); extern SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes);
extern void *SPI_saveplan(void *plan); extern SPIPlanPtr SPI_saveplan(SPIPlanPtr plan);
extern int SPI_freeplan(void *plan); extern int SPI_freeplan(SPIPlanPtr plan);
extern Oid SPI_getargtypeid(void *plan, int argIndex); extern Oid SPI_getargtypeid(SPIPlanPtr plan, int argIndex);
extern int SPI_getargcount(void *plan); extern int SPI_getargcount(SPIPlanPtr plan);
extern bool SPI_is_cursor_plan(void *plan); extern bool SPI_is_cursor_plan(SPIPlanPtr plan);
extern const char *SPI_result_code_string(int code); extern const char *SPI_result_code_string(int code);
extern HeapTuple SPI_copytuple(HeapTuple tuple); extern HeapTuple SPI_copytuple(HeapTuple tuple);
...@@ -123,7 +131,7 @@ extern void SPI_pfree(void *pointer); ...@@ -123,7 +131,7 @@ extern void SPI_pfree(void *pointer);
extern void SPI_freetuple(HeapTuple pointer); extern void SPI_freetuple(HeapTuple pointer);
extern void SPI_freetuptable(SPITupleTable *tuptable); extern void SPI_freetuptable(SPITupleTable *tuptable);
extern Portal SPI_cursor_open(const char *name, void *plan, extern Portal SPI_cursor_open(const char *name, SPIPlanPtr plan,
Datum *Values, const char *Nulls, bool read_only); Datum *Values, const char *Nulls, bool read_only);
extern Portal SPI_cursor_find(const char *name); extern Portal SPI_cursor_find(const char *name);
extern void SPI_cursor_fetch(Portal portal, bool forward, long count); extern void SPI_cursor_fetch(Portal portal, bool forward, long count);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.27 2007/02/20 17:32:17 tgl Exp $ * $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.28 2007/03/15 23:12:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include "executor/spi.h" #include "executor/spi.h"
#define _SPI_PLAN_MAGIC 569278163
typedef struct typedef struct
{ {
/* current results */ /* current results */
...@@ -25,29 +27,46 @@ typedef struct ...@@ -25,29 +27,46 @@ typedef struct
MemoryContext procCxt; /* procedure context */ MemoryContext procCxt; /* procedure context */
MemoryContext execCxt; /* executor context */ MemoryContext execCxt; /* executor context */
MemoryContext savedcxt; MemoryContext savedcxt; /* context of SPI_connect's caller */
SubTransactionId connectSubid; /* ID of connecting subtransaction */ SubTransactionId connectSubid; /* ID of connecting subtransaction */
} _SPI_connection; } _SPI_connection;
typedef struct /*
* SPI plans have two states: saved or unsaved.
*
* For an unsaved plan, the _SPI_plan struct and all its subsidiary data are in
* a dedicated memory context identified by plancxt. An unsaved plan is good
* at most for the current transaction, since the locks that protect it from
* schema changes will be lost at end of transaction. Hence the plancxt is
* always a transient one.
*
* For a saved plan, the _SPI_plan struct and the argument type array are in
* the plancxt (which can be really small). All the other subsidiary state
* is in plancache entries identified by plancache_list (note: the list cells
* themselves are in plancxt). We rely on plancache.c to keep the cache
* entries up-to-date as needed. The plancxt is a child of CacheMemoryContext
* since it should persist until explicitly destroyed.
*
* To avoid redundant coding, the representation of unsaved plans matches
* that of saved plans, ie, plancache_list is a list of CachedPlanSource
* structs which in turn point to CachedPlan structs. However, in an unsaved
* plan all these structs are just created by spi.c and are not known to
* plancache.c. We don't try very hard to make all their fields valid,
* only the ones spi.c actually uses.
*
* Note: if the original query string contained only whitespace and comments,
* the plancache_list will be NIL and so there is no place to store the
* query string. We don't care about that, but we do care about the
* argument type array, which is why it's seemingly-redundantly stored.
*/
typedef struct _SPI_plan
{ {
/* Context containing _SPI_plan itself as well as subsidiary data */ int magic; /* should equal _SPI_PLAN_MAGIC */
MemoryContext plancxt; bool saved; /* saved or unsaved plan? */
/* Original query string (used for error reporting) */ List *plancache_list; /* one CachedPlanSource per parsetree */
const char *query; MemoryContext plancxt; /* Context containing _SPI_plan and data */
/* int nargs; /* number of plan arguments */
* List of List of PlannedStmts and utility stmts; one sublist per Oid *argtypes; /* Argument types (NULL if nargs is 0) */
* original parsetree
*/
List *stmt_list_list;
/* Argument types, if a prepared plan */
int nargs;
Oid *argtypes;
} _SPI_plan; } _SPI_plan;
#define _SPI_CPLAN_CURCXT 0
#define _SPI_CPLAN_PROCXT 1
#define _SPI_CPLAN_TOPCXT 2
#endif /* SPI_PRIV_H */ #endif /* SPI_PRIV_H */
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.1 2007/03/13 00:33:43 tgl Exp $ * $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.2 2007/03/15 23:12:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -101,5 +101,6 @@ extern void DropCachedPlan(CachedPlanSource *plansource); ...@@ -101,5 +101,6 @@ extern void DropCachedPlan(CachedPlanSource *plansource);
extern CachedPlan *RevalidateCachedPlan(CachedPlanSource *plansource, extern CachedPlan *RevalidateCachedPlan(CachedPlanSource *plansource,
bool useResOwner); bool useResOwner);
extern void ReleaseCachedPlan(CachedPlan *plan, bool useResOwner); extern void ReleaseCachedPlan(CachedPlan *plan, bool useResOwner);
extern TupleDesc PlanCacheComputeResultDesc(List *stmt_list);
#endif /* PLANCACHE_H */ #endif /* PLANCACHE_H */
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.189 2007/02/20 17:32:18 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.190 2007/03/15 23:12:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -123,8 +123,9 @@ static void exec_prepare_plan(PLpgSQL_execstate *estate, ...@@ -123,8 +123,9 @@ static void exec_prepare_plan(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr); PLpgSQL_expr *expr);
static bool exec_simple_check_node(Node *node); static bool exec_simple_check_node(Node *node);
static void exec_simple_check_plan(PLpgSQL_expr *expr); static void exec_simple_check_plan(PLpgSQL_expr *expr);
static Datum exec_eval_simple_expr(PLpgSQL_execstate *estate, static bool exec_eval_simple_expr(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr, PLpgSQL_expr *expr,
Datum *result,
bool *isNull, bool *isNull,
Oid *rettype); Oid *rettype);
...@@ -409,7 +410,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo) ...@@ -409,7 +410,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
void *tmp; void *tmp;
len = datumGetSize(estate.retval, false, func->fn_rettyplen); len = datumGetSize(estate.retval, false, func->fn_rettyplen);
tmp = (void *) SPI_palloc(len); tmp = SPI_palloc(len);
memcpy(tmp, DatumGetPointer(estate.retval), len); memcpy(tmp, DatumGetPointer(estate.retval), len);
estate.retval = PointerGetDatum(tmp); estate.retval = PointerGetDatum(tmp);
} }
...@@ -2294,8 +2295,7 @@ exec_prepare_plan(PLpgSQL_execstate *estate, ...@@ -2294,8 +2295,7 @@ exec_prepare_plan(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr) PLpgSQL_expr *expr)
{ {
int i; int i;
_SPI_plan *spi_plan; SPIPlanPtr plan;
void *plan;
Oid *argtypes; Oid *argtypes;
/* /*
...@@ -2343,12 +2343,11 @@ exec_prepare_plan(PLpgSQL_execstate *estate, ...@@ -2343,12 +2343,11 @@ exec_prepare_plan(PLpgSQL_execstate *estate,
} }
} }
expr->plan = SPI_saveplan(plan); expr->plan = SPI_saveplan(plan);
spi_plan = (_SPI_plan *) expr->plan; SPI_freeplan(plan);
expr->plan_argtypes = spi_plan->argtypes; plan = expr->plan;
expr->expr_simple_expr = NULL; expr->plan_argtypes = plan->argtypes;
exec_simple_check_plan(expr); exec_simple_check_plan(expr);
SPI_freeplan(plan);
pfree(argtypes); pfree(argtypes);
} }
...@@ -2374,17 +2373,16 @@ exec_stmt_execsql(PLpgSQL_execstate *estate, ...@@ -2374,17 +2373,16 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
*/ */
if (expr->plan == NULL) if (expr->plan == NULL)
{ {
_SPI_plan *spi_plan;
ListCell *l; ListCell *l;
exec_prepare_plan(estate, expr); exec_prepare_plan(estate, expr);
stmt->mod_stmt = false; stmt->mod_stmt = false;
spi_plan = (_SPI_plan *) expr->plan; foreach(l, expr->plan->plancache_list)
foreach(l, spi_plan->stmt_list_list)
{ {
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(l);
ListCell *l2; ListCell *l2;
foreach(l2, (List *) lfirst(l)) foreach(l2, plansource->plan->stmt_list)
{ {
PlannedStmt *p = (PlannedStmt *) lfirst(l2); PlannedStmt *p = (PlannedStmt *) lfirst(l2);
...@@ -2735,7 +2733,7 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt) ...@@ -2735,7 +2733,7 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt)
PLpgSQL_row *row = NULL; PLpgSQL_row *row = NULL;
SPITupleTable *tuptab; SPITupleTable *tuptab;
int n; int n;
void *plan; SPIPlanPtr plan;
Portal portal; Portal portal;
bool found = false; bool found = false;
...@@ -2959,7 +2957,7 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt) ...@@ -2959,7 +2957,7 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
Datum queryD; Datum queryD;
Oid restype; Oid restype;
char *querystr; char *querystr;
void *curplan; SPIPlanPtr curplan;
/* ---------- /* ----------
* We evaluate the string expression after the * We evaluate the string expression after the
...@@ -3860,10 +3858,11 @@ exec_eval_expr(PLpgSQL_execstate *estate, ...@@ -3860,10 +3858,11 @@ exec_eval_expr(PLpgSQL_execstate *estate,
bool *isNull, bool *isNull,
Oid *rettype) Oid *rettype)
{ {
Datum result;
int rc; int rc;
/* /*
* If not already done create a plan for this expression * If first time through, create a plan for this expression.
*/ */
if (expr->plan == NULL) if (expr->plan == NULL)
exec_prepare_plan(estate, expr); exec_prepare_plan(estate, expr);
...@@ -3872,9 +3871,12 @@ exec_eval_expr(PLpgSQL_execstate *estate, ...@@ -3872,9 +3871,12 @@ exec_eval_expr(PLpgSQL_execstate *estate,
* If this is a simple expression, bypass SPI and use the executor * If this is a simple expression, bypass SPI and use the executor
* directly * directly
*/ */
if (expr->expr_simple_expr != NULL) if (exec_eval_simple_expr(estate, expr, &result, isNull, rettype))
return exec_eval_simple_expr(estate, expr, isNull, rettype); return result;
/*
* Else do it the hard way via exec_run_select
*/
rc = exec_run_select(estate, expr, 2, NULL); rc = exec_run_select(estate, expr, 2, NULL);
if (rc != SPI_OK_SELECT) if (rc != SPI_OK_SELECT)
ereport(ERROR, ereport(ERROR,
...@@ -3994,23 +3996,64 @@ exec_run_select(PLpgSQL_execstate *estate, ...@@ -3994,23 +3996,64 @@ exec_run_select(PLpgSQL_execstate *estate,
* exec_eval_simple_expr - Evaluate a simple expression returning * exec_eval_simple_expr - Evaluate a simple expression returning
* a Datum by directly calling ExecEvalExpr(). * a Datum by directly calling ExecEvalExpr().
* *
* If successful, store results into *result, *isNull, *rettype and return
* TRUE. If the expression is not simple (any more), return FALSE.
*
* It is possible though unlikely for a simple expression to become non-simple
* (consider for example redefining a trivial view). We must handle that for
* correctness; fortunately it's normally inexpensive to do
* RevalidateCachedPlan on a simple expression. We do not consider the other
* direction (non-simple expression becoming simple) because we'll still give
* correct results if that happens, and it's unlikely to be worth the cycles
* to check.
*
* Note: if pass-by-reference, the result is in the eval_econtext's * Note: if pass-by-reference, the result is in the eval_econtext's
* temporary memory context. It will be freed when exec_eval_cleanup * temporary memory context. It will be freed when exec_eval_cleanup
* is done. * is done.
* ---------- * ----------
*/ */
static Datum static bool
exec_eval_simple_expr(PLpgSQL_execstate *estate, exec_eval_simple_expr(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr, PLpgSQL_expr *expr,
Datum *result,
bool *isNull, bool *isNull,
Oid *rettype) Oid *rettype)
{ {
Datum retval;
ExprContext *econtext = estate->eval_econtext; ExprContext *econtext = estate->eval_econtext;
CachedPlanSource *plansource;
CachedPlan *cplan;
ParamListInfo paramLI; ParamListInfo paramLI;
int i; int i;
Snapshot saveActiveSnapshot; Snapshot saveActiveSnapshot;
/*
* Forget it if expression wasn't simple before.
*/
if (expr->expr_simple_expr == NULL)
return false;
/*
* Revalidate cached plan, so that we will notice if it became stale.
* (We also need to hold a refcount while using the plan.) Note that
* even if replanning occurs, the length of plancache_list can't change,
* since it is a property of the raw parsetree generated from the query
* text.
*/
Assert(list_length(expr->plan->plancache_list) == 1);
plansource = (CachedPlanSource *) linitial(expr->plan->plancache_list);
cplan = RevalidateCachedPlan(plansource, true);
if (cplan->generation != expr->expr_simple_generation)
{
/* It got replanned ... is it still simple? */
exec_simple_check_plan(expr);
if (expr->expr_simple_expr == NULL)
{
/* Ooops, release refcount and fail */
ReleaseCachedPlan(cplan, true);
return false;
}
}
/* /*
* Pass back previously-determined result type. * Pass back previously-determined result type.
*/ */
...@@ -4018,7 +4061,8 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate, ...@@ -4018,7 +4061,8 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
/* /*
* Prepare the expression for execution, if it's not been done already in * Prepare the expression for execution, if it's not been done already in
* the current eval_estate. * the current eval_estate. (This will be forced to happen if we called
* exec_simple_check_plan above.)
*/ */
if (expr->expr_simple_id != estate->eval_estate_simple_id) if (expr->expr_simple_id != estate->eval_estate_simple_id)
{ {
...@@ -4086,10 +4130,10 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate, ...@@ -4086,10 +4130,10 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
/* /*
* Finally we can call the executor to evaluate the expression * Finally we can call the executor to evaluate the expression
*/ */
retval = ExecEvalExpr(expr->expr_simple_state, *result = ExecEvalExpr(expr->expr_simple_state,
econtext, econtext,
isNull, isNull,
NULL); NULL);
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
} }
PG_CATCH(); PG_CATCH();
...@@ -4103,10 +4147,15 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate, ...@@ -4103,10 +4147,15 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
ActiveSnapshot = saveActiveSnapshot; ActiveSnapshot = saveActiveSnapshot;
SPI_pop(); SPI_pop();
/*
* Now we can release our refcount on the cached plan.
*/
ReleaseCachedPlan(cplan, true);
/* /*
* That's it. * That's it.
*/ */
return retval; return true;
} }
...@@ -4673,25 +4722,31 @@ exec_simple_check_node(Node *node) ...@@ -4673,25 +4722,31 @@ exec_simple_check_node(Node *node)
static void static void
exec_simple_check_plan(PLpgSQL_expr *expr) exec_simple_check_plan(PLpgSQL_expr *expr)
{ {
_SPI_plan *spi_plan = (_SPI_plan *) expr->plan; CachedPlanSource *plansource;
List *sublist;
PlannedStmt *stmt; PlannedStmt *stmt;
Plan *plan; Plan *plan;
TargetEntry *tle; TargetEntry *tle;
/*
* Initialize to "not simple", and remember the plan generation number
* we last checked. (If the query produces more or less than one parsetree
* we just leave expr_simple_generation set to 0.)
*/
expr->expr_simple_expr = NULL; expr->expr_simple_expr = NULL;
expr->expr_simple_generation = 0;
/* /*
* 1. We can only evaluate queries that resulted in one single execution * 1. We can only evaluate queries that resulted in one single execution
* plan * plan
*/ */
if (list_length(spi_plan->stmt_list_list) != 1) if (list_length(expr->plan->plancache_list) != 1)
return; return;
sublist = (List *) linitial(spi_plan->stmt_list_list); plansource = (CachedPlanSource *) linitial(expr->plan->plancache_list);
if (list_length(sublist) != 1) expr->expr_simple_generation = plansource->generation;
if (list_length(plansource->plan->stmt_list) != 1)
return; return;
stmt = (PlannedStmt *) linitial(sublist); stmt = (PlannedStmt *) linitial(plansource->plan->stmt_list);
/* /*
* 2. It must be a RESULT plan --> no scan's required * 2. It must be a RESULT plan --> no scan's required
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.85 2007/02/09 03:35:34 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.86 2007/03/15 23:12:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -173,11 +173,12 @@ typedef struct PLpgSQL_expr ...@@ -173,11 +173,12 @@ typedef struct PLpgSQL_expr
int dtype; int dtype;
int exprno; int exprno;
char *query; char *query;
void *plan; SPIPlanPtr plan;
Oid *plan_argtypes; Oid *plan_argtypes;
/* fields for "simple expression" fast-path execution: */ /* fields for "simple expression" fast-path execution: */
Expr *expr_simple_expr; /* NULL means not a simple expr */ Expr *expr_simple_expr; /* NULL means not a simple expr */
Oid expr_simple_type; int expr_simple_generation; /* plancache generation we checked */
Oid expr_simple_type; /* result type Oid, if simple */
/* /*
* if expr is simple AND prepared in current eval_estate, * if expr is simple AND prepared in current eval_estate,
......
...@@ -100,3 +100,64 @@ EXECUTE vprep; ...@@ -100,3 +100,64 @@ EXECUTE vprep;
4567890123456789 | 2283945061728394 4567890123456789 | 2283945061728394
(5 rows) (5 rows)
-- Check basic SPI plan invalidation
create function cache_test(int) returns int as $$
declare total int;
begin
create temp table t1(f1 int);
insert into t1 values($1);
insert into t1 values(11);
insert into t1 values(12);
insert into t1 values(13);
select sum(f1) into total from t1;
drop table t1;
return total;
end
$$ language plpgsql;
select cache_test(1);
cache_test
------------
37
(1 row)
select cache_test(2);
cache_test
------------
38
(1 row)
select cache_test(3);
cache_test
------------
39
(1 row)
-- Check invalidation of plpgsql "simple expression"
create temp view v1 as
select 2+2 as f1;
create function cache_test_2() returns int as $$
begin
return f1 from v1;
end$$ language plpgsql;
select cache_test_2();
cache_test_2
--------------
4
(1 row)
create or replace temp view v1 as
select 2+2+4 as f1;
select cache_test_2();
cache_test_2
--------------
8
(1 row)
create or replace temp view v1 as
select 2+2+4+(select max(unique1) from tenk1) as f1;
select cache_test_2();
cache_test_2
--------------
10007
(1 row)
/* /*
* $PostgreSQL: pgsql/src/test/regress/regress.c,v 1.69 2007/02/01 19:10:30 momjian Exp $ * $PostgreSQL: pgsql/src/test/regress/regress.c,v 1.70 2007/03/15 23:12:07 tgl Exp $
*/ */
#include "postgres.h" #include "postgres.h"
...@@ -451,7 +451,7 @@ extern Datum set_ttdummy(PG_FUNCTION_ARGS); ...@@ -451,7 +451,7 @@ extern Datum set_ttdummy(PG_FUNCTION_ARGS);
#define TTDUMMY_INFINITY 999999 #define TTDUMMY_INFINITY 999999
static void *splan = NULL; static SPIPlanPtr splan = NULL;
static bool ttoff = false; static bool ttoff = false;
PG_FUNCTION_INFO_V1(ttdummy); PG_FUNCTION_INFO_V1(ttdummy);
...@@ -599,7 +599,7 @@ ttdummy(PG_FUNCTION_ARGS) ...@@ -599,7 +599,7 @@ ttdummy(PG_FUNCTION_ARGS)
/* if there is no plan ... */ /* if there is no plan ... */
if (splan == NULL) if (splan == NULL)
{ {
void *pplan; SPIPlanPtr pplan;
Oid *ctypes; Oid *ctypes;
char *query; char *query;
......
...@@ -51,3 +51,43 @@ EXECUTE vprep; ...@@ -51,3 +51,43 @@ EXECUTE vprep;
CREATE OR REPLACE TEMP VIEW voo AS SELECT q1, q2/2 AS q2 FROM foo; CREATE OR REPLACE TEMP VIEW voo AS SELECT q1, q2/2 AS q2 FROM foo;
EXECUTE vprep; EXECUTE vprep;
-- Check basic SPI plan invalidation
create function cache_test(int) returns int as $$
declare total int;
begin
create temp table t1(f1 int);
insert into t1 values($1);
insert into t1 values(11);
insert into t1 values(12);
insert into t1 values(13);
select sum(f1) into total from t1;
drop table t1;
return total;
end
$$ language plpgsql;
select cache_test(1);
select cache_test(2);
select cache_test(3);
-- Check invalidation of plpgsql "simple expression"
create temp view v1 as
select 2+2 as f1;
create function cache_test_2() returns int as $$
begin
return f1 from v1;
end$$ language plpgsql;
select cache_test_2();
create or replace temp view v1 as
select 2+2+4 as f1;
select cache_test_2();
create or replace temp view v1 as
select 2+2+4+(select max(unique1) from tenk1) as f1;
select cache_test_2();
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