mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-02-17 19:30:00 +08:00
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.)
This commit is contained in:
parent
d3ff180163
commit
95f6d2d209
@ -19,7 +19,7 @@ typedef struct
|
||||
{
|
||||
char *ident;
|
||||
int nplans;
|
||||
void **splan;
|
||||
SPIPlanPtr *splan;
|
||||
} EPlan;
|
||||
|
||||
static EPlan *FPlans = NULL;
|
||||
@ -163,7 +163,7 @@ check_primary_key(PG_FUNCTION_ARGS)
|
||||
*/
|
||||
if (plan->nplans <= 0)
|
||||
{
|
||||
void *pplan;
|
||||
SPIPlanPtr pplan;
|
||||
char sql[8192];
|
||||
|
||||
/*
|
||||
@ -191,7 +191,7 @@ check_primary_key(PG_FUNCTION_ARGS)
|
||||
if (pplan == NULL)
|
||||
/* internal error */
|
||||
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->nplans = 1;
|
||||
}
|
||||
@ -413,11 +413,11 @@ check_foreign_key(PG_FUNCTION_ARGS)
|
||||
*/
|
||||
if (plan->nplans <= 0)
|
||||
{
|
||||
void *pplan;
|
||||
SPIPlanPtr pplan;
|
||||
char sql[8192];
|
||||
char **args2 = args;
|
||||
|
||||
plan->splan = (void **) malloc(nrefs * sizeof(void *));
|
||||
plan->splan = (SPIPlanPtr *) malloc(nrefs * sizeof(SPIPlanPtr));
|
||||
|
||||
for (r = 0; r < nrefs; r++)
|
||||
{
|
||||
|
@ -24,7 +24,7 @@ Datum get_timetravel(PG_FUNCTION_ARGS);
|
||||
typedef struct
|
||||
{
|
||||
char *ident;
|
||||
void *splan;
|
||||
SPIPlanPtr splan;
|
||||
} EPlan;
|
||||
|
||||
static EPlan *Plans = NULL; /* for UPDATE/DELETE */
|
||||
@ -308,7 +308,7 @@ timetravel(PG_FUNCTION_ARGS)
|
||||
/* if there is no plan ... */
|
||||
if (plan->splan == NULL)
|
||||
{
|
||||
void *pplan;
|
||||
SPIPlanPtr pplan;
|
||||
Oid *ctypes;
|
||||
char sql[8192];
|
||||
char separ = ' ';
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!-- $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">
|
||||
<title>Server Programming Interface</title>
|
||||
@ -14,7 +14,7 @@
|
||||
<acronym>SQL</acronym> commands inside their functions.
|
||||
<acronym>SPI</acronym> is a set of
|
||||
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.
|
||||
</para>
|
||||
|
||||
@ -322,7 +322,8 @@ SPI_execute("INSERT INTO foo SELECT * FROM bar", false, 5);
|
||||
|
||||
<para>
|
||||
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
|
||||
result for the command executed last. The <parameter>count</parameter>
|
||||
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<
|
||||
|
||||
<refsynopsisdiv>
|
||||
<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>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -790,6 +791,13 @@ void * SPI_prepare(const char * <parameter>command</parameter>, int <parameter>n
|
||||
<refsect1>
|
||||
<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>
|
||||
There is a disadvantage to using parameters: since the planner does
|
||||
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
|
||||
|
||||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
int SPI_getargcount(void * <parameter>plan</parameter>)
|
||||
int SPI_getargcount(SPIPlanPtr <parameter>plan</parameter>)
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -834,7 +842,7 @@ int SPI_getargcount(void * <parameter>plan</parameter>)
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><literal>void * <parameter>plan</parameter></literal></term>
|
||||
<term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
execution plan (returned by <function>SPI_prepare</function>)
|
||||
@ -847,9 +855,10 @@ int SPI_getargcount(void * <parameter>plan</parameter>)
|
||||
<refsect1>
|
||||
<title>Return Value</title>
|
||||
<para>
|
||||
The expected argument count for the <parameter>plan</parameter>, or
|
||||
<symbol>SPI_ERROR_ARGUMENT</symbol> if the <parameter>plan
|
||||
</parameter> is <symbol>NULL</symbol>
|
||||
The count of expected arguments for the <parameter>plan</parameter>.
|
||||
If the <parameter>plan</parameter> is <symbol>NULL</symbol> or invalid,
|
||||
<varname>SPI_result</varname> is set to <symbol>SPI_ERROR_ARGUMENT</symbol>
|
||||
and <literal>-1</literal> is returned.
|
||||
</para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
@ -871,7 +880,7 @@ int SPI_getargcount(void * <parameter>plan</parameter>)
|
||||
|
||||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
Oid SPI_getargtypeid(void * <parameter>plan</parameter>, int <parameter>argIndex</parameter>)
|
||||
Oid SPI_getargtypeid(SPIPlanPtr <parameter>plan</parameter>, int <parameter>argIndex</parameter>)
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -890,7 +899,7 @@ Oid SPI_getargtypeid(void * <parameter>plan</parameter>, int <parameter>argIndex
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><literal>void * <parameter>plan</parameter></literal></term>
|
||||
<term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
execution plan (returned by <function>SPI_prepare</function>)
|
||||
@ -912,11 +921,13 @@ Oid SPI_getargtypeid(void * <parameter>plan</parameter>, int <parameter>argIndex
|
||||
<refsect1>
|
||||
<title>Return Value</title>
|
||||
<para>
|
||||
The type id of the argument at the given index, or
|
||||
<symbol>SPI_ERROR_ARGUMENT</symbol> if the <parameter>plan</parameter> is
|
||||
<symbol>NULL</symbol> or <parameter>argIndex</parameter> is less than 0 or
|
||||
The type id of the argument at the given index.
|
||||
If the <parameter>plan</parameter> is <symbol>NULL</symbol> or invalid,
|
||||
or <parameter>argIndex</parameter> is less than 0 or
|
||||
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>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
@ -939,7 +950,7 @@ Oid SPI_getargtypeid(void * <parameter>plan</parameter>, int <parameter>argIndex
|
||||
|
||||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
bool SPI_is_cursor_plan(void * <parameter>plan</parameter>)
|
||||
bool SPI_is_cursor_plan(SPIPlanPtr <parameter>plan</parameter>)
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -964,7 +975,7 @@ bool SPI_is_cursor_plan(void * <parameter>plan</parameter>)
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><literal>void * <parameter>plan</parameter></literal></term>
|
||||
<term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
execution plan (returned by <function>SPI_prepare</function>)
|
||||
@ -978,9 +989,10 @@ bool SPI_is_cursor_plan(void * <parameter>plan</parameter>)
|
||||
<title>Return Value</title>
|
||||
<para>
|
||||
<symbol>true</symbol> or <symbol>false</symbol> to indicate if the
|
||||
<parameter>plan</parameter> can produce a cursor or not, or
|
||||
<symbol>SPI_ERROR_ARGUMENT</symbol> if the <parameter>plan</parameter>
|
||||
is <symbol>NULL</symbol>
|
||||
<parameter>plan</parameter> can produce a cursor or not.
|
||||
If the <parameter>plan</parameter> is <symbol>NULL</symbol> or invalid,
|
||||
<varname>SPI_result</varname> is set to <symbol>SPI_ERROR_ARGUMENT</symbol>
|
||||
and <symbol>false</symbol> is returned.
|
||||
</para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
@ -1001,7 +1013,7 @@ bool SPI_is_cursor_plan(void * <parameter>plan</parameter>)
|
||||
|
||||
<refsynopsisdiv>
|
||||
<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>)
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
@ -1022,7 +1034,7 @@ int SPI_execute_plan(void * <parameter>plan</parameter>, Datum * <parameter>valu
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><literal>void * <parameter>plan</parameter></literal></term>
|
||||
<term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
execution plan (returned by <function>SPI_prepare</function>)
|
||||
@ -1091,8 +1103,8 @@ int SPI_execute_plan(void * <parameter>plan</parameter>, Datum * <parameter>valu
|
||||
<term><symbol>SPI_ERROR_ARGUMENT</symbol></term>
|
||||
<listitem>
|
||||
<para>
|
||||
if <parameter>plan</parameter> is <symbol>NULL</symbol> or
|
||||
<parameter>count</parameter> is less than 0
|
||||
if <parameter>plan</parameter> is <symbol>NULL</symbol> or invalid,
|
||||
or <parameter>count</parameter> is less than 0
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -1143,7 +1155,7 @@ int SPI_execute_plan(void * <parameter>plan</parameter>, Datum * <parameter>valu
|
||||
|
||||
<refsynopsisdiv>
|
||||
<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>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -1163,7 +1175,7 @@ int SPI_execp(void * <parameter>plan</parameter>, Datum * <parameter>values</par
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><literal>void * <parameter>plan</parameter></literal></term>
|
||||
<term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
execution plan (returned by <function>SPI_prepare</function>)
|
||||
@ -1242,7 +1254,7 @@ int SPI_execp(void * <parameter>plan</parameter>, Datum * <parameter>values</par
|
||||
|
||||
<refsynopsisdiv>
|
||||
<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>,
|
||||
bool <parameter>read_only</parameter>)
|
||||
</synopsis>
|
||||
@ -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
|
||||
result.
|
||||
</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>
|
||||
@ -1285,7 +1302,7 @@ Portal SPI_cursor_open(const char * <parameter>name</parameter>, void * <paramet
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><literal>void * <parameter>plan</parameter></literal></term>
|
||||
<term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
execution plan (returned by <function>SPI_prepare</function>)
|
||||
@ -1602,7 +1619,7 @@ void SPI_cursor_close(Portal <parameter>portal</parameter>)
|
||||
|
||||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
void * SPI_saveplan(void * <parameter>plan</parameter>)
|
||||
SPIPlanPtr SPI_saveplan(SPIPlanPtr <parameter>plan</parameter>)
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -1611,8 +1628,8 @@ void * SPI_saveplan(void * <parameter>plan</parameter>)
|
||||
|
||||
<para>
|
||||
<function>SPI_saveplan</function> saves a passed plan (prepared by
|
||||
<function>SPI_prepare</function>) in memory protected from freeing
|
||||
by <function>SPI_finish</function> and by the transaction manager
|
||||
<function>SPI_prepare</function>) in memory that will not be freed
|
||||
by <function>SPI_finish</function> nor by the transaction manager,
|
||||
and returns a pointer to the saved plan. This gives you the
|
||||
ability to reuse prepared plans in the subsequent invocations of
|
||||
your procedure in the current session.
|
||||
@ -1624,7 +1641,7 @@ void * SPI_saveplan(void * <parameter>plan</parameter>)
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><literal>void * <parameter>plan</parameter></literal></term>
|
||||
<term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
the plan to be saved
|
||||
@ -1646,7 +1663,7 @@ void * SPI_saveplan(void * <parameter>plan</parameter>)
|
||||
<term><symbol>SPI_ERROR_ARGUMENT</symbol></term>
|
||||
<listitem>
|
||||
<para>
|
||||
if <parameter>plan</parameter> is <symbol>NULL</symbol>
|
||||
if <parameter>plan</parameter> is <symbol>NULL</symbol> or invalid
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -1666,10 +1683,17 @@ void * SPI_saveplan(void * <parameter>plan</parameter>)
|
||||
<refsect1>
|
||||
<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>
|
||||
If one of the objects (a table, function, etc.) referenced by the
|
||||
prepared plan is dropped during the session then the results of
|
||||
<function>SPI_execute_plan</function> for this plan will be unpredictable.
|
||||
prepared plan is dropped or redefined, then future executions of
|
||||
<function>SPI_execute_plan</function> may fail or return different
|
||||
results than the plan initially indicates.
|
||||
</para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
@ -2879,7 +2903,7 @@ void SPI_freetuptable(SPITupleTable * <parameter>tuptable</parameter>)
|
||||
|
||||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
int SPI_freeplan(void *<parameter>plan</parameter>)
|
||||
int SPI_freeplan(SPIPlanPtr <parameter>plan</parameter>)
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -2898,7 +2922,7 @@ int SPI_freeplan(void *<parameter>plan</parameter>)
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><literal>void * <parameter>plan</parameter></literal></term>
|
||||
<term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
pointer to plan to free
|
||||
@ -2913,7 +2937,7 @@ int SPI_freeplan(void *<parameter>plan</parameter>)
|
||||
|
||||
<para>
|
||||
<symbol>SPI_ERROR_ARGUMENT</symbol> if <parameter>plan</parameter>
|
||||
is <symbol>NULL</symbol>.
|
||||
is <symbol>NULL</symbol> or invalid
|
||||
</para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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 */
|
||||
static int _SPI_connected = -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,
|
||||
Snapshot snapshot, Snapshot crosscheck_snapshot,
|
||||
bool read_only, long tcount);
|
||||
@ -48,7 +48,8 @@ static void _SPI_error_callback(void *arg);
|
||||
static void _SPI_cursor_operation(Portal portal, bool forward, long count,
|
||||
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_end_call(bool procmem);
|
||||
@ -306,10 +307,8 @@ SPI_execute(const char *src, bool read_only, long tcount)
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
plan.plancxt = NULL; /* doesn't have own context */
|
||||
plan.query = src;
|
||||
plan.nargs = 0;
|
||||
plan.argtypes = NULL;
|
||||
memset(&plan, 0, sizeof(_SPI_plan));
|
||||
plan.magic = _SPI_PLAN_MAGIC;
|
||||
|
||||
_SPI_prepare_plan(src, &plan);
|
||||
|
||||
@ -330,22 +329,22 @@ SPI_exec(const char *src, long tcount)
|
||||
|
||||
/* Execute a previously prepared plan */
|
||||
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)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (plan == NULL || tcount < 0)
|
||||
if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
|
||||
return SPI_ERROR_ARGUMENT;
|
||||
|
||||
if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
|
||||
if (plan->nargs > 0 && Values == NULL)
|
||||
return SPI_ERROR_PARAM;
|
||||
|
||||
res = _SPI_begin_call(true);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
res = _SPI_execute_plan((_SPI_plan *) plan,
|
||||
res = _SPI_execute_plan(plan,
|
||||
Values, Nulls,
|
||||
InvalidSnapshot, InvalidSnapshot,
|
||||
read_only, tcount);
|
||||
@ -356,7 +355,7 @@ SPI_execute_plan(void *plan, Datum *Values, const char *Nulls,
|
||||
|
||||
/* Obsolete version of SPI_execute_plan */
|
||||
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);
|
||||
}
|
||||
@ -371,24 +370,24 @@ SPI_execp(void *plan, Datum *Values, const char *Nulls, long tcount)
|
||||
* fetching a new snapshot for each query.
|
||||
*/
|
||||
int
|
||||
SPI_execute_snapshot(void *plan,
|
||||
SPI_execute_snapshot(SPIPlanPtr plan,
|
||||
Datum *Values, const char *Nulls,
|
||||
Snapshot snapshot, Snapshot crosscheck_snapshot,
|
||||
bool read_only, long tcount)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (plan == NULL || tcount < 0)
|
||||
if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
|
||||
return SPI_ERROR_ARGUMENT;
|
||||
|
||||
if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
|
||||
if (plan->nargs > 0 && Values == NULL)
|
||||
return SPI_ERROR_PARAM;
|
||||
|
||||
res = _SPI_begin_call(true);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
res = _SPI_execute_plan((_SPI_plan *) plan,
|
||||
res = _SPI_execute_plan(plan,
|
||||
Values, Nulls,
|
||||
snapshot, crosscheck_snapshot,
|
||||
read_only, tcount);
|
||||
@ -397,11 +396,11 @@ SPI_execute_snapshot(void *plan,
|
||||
return res;
|
||||
}
|
||||
|
||||
void *
|
||||
SPIPlanPtr
|
||||
SPI_prepare(const char *src, int nargs, Oid *argtypes)
|
||||
{
|
||||
_SPI_plan plan;
|
||||
_SPI_plan *result;
|
||||
SPIPlanPtr result;
|
||||
|
||||
if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
|
||||
{
|
||||
@ -413,27 +412,28 @@ SPI_prepare(const char *src, int nargs, Oid *argtypes)
|
||||
if (SPI_result < 0)
|
||||
return NULL;
|
||||
|
||||
plan.plancxt = NULL; /* doesn't have own context */
|
||||
plan.query = src;
|
||||
memset(&plan, 0, sizeof(_SPI_plan));
|
||||
plan.magic = _SPI_PLAN_MAGIC;
|
||||
plan.nargs = nargs;
|
||||
plan.argtypes = argtypes;
|
||||
|
||||
_SPI_prepare_plan(src, &plan);
|
||||
|
||||
/* 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);
|
||||
|
||||
return (void *) result;
|
||||
return result;
|
||||
}
|
||||
|
||||
void *
|
||||
SPI_saveplan(void *plan)
|
||||
SPIPlanPtr
|
||||
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;
|
||||
return NULL;
|
||||
@ -443,23 +443,36 @@ SPI_saveplan(void *plan)
|
||||
if (SPI_result < 0)
|
||||
return NULL;
|
||||
|
||||
newplan = _SPI_copy_plan((_SPI_plan *) plan, _SPI_CPLAN_TOPCXT);
|
||||
newplan = _SPI_save_plan(plan);
|
||||
|
||||
_SPI_curid--;
|
||||
SPI_result = 0;
|
||||
|
||||
return (void *) newplan;
|
||||
return newplan;
|
||||
}
|
||||
|
||||
int
|
||||
SPI_freeplan(void *plan)
|
||||
SPI_freeplan(SPIPlanPtr plan)
|
||||
{
|
||||
_SPI_plan *spiplan = (_SPI_plan *) plan;
|
||||
|
||||
if (plan == NULL)
|
||||
if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -819,18 +832,18 @@ SPI_freetuptable(SPITupleTable *tuptable)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* SPI_cursor_open()
|
||||
*
|
||||
* Open a prepared SPI plan as a portal
|
||||
*/
|
||||
Portal
|
||||
SPI_cursor_open(const char *name, void *plan,
|
||||
SPI_cursor_open(const char *name, SPIPlanPtr plan,
|
||||
Datum *Values, const char *Nulls,
|
||||
bool read_only)
|
||||
{
|
||||
_SPI_plan *spiplan = (_SPI_plan *) plan;
|
||||
CachedPlanSource *plansource;
|
||||
CachedPlan *cplan;
|
||||
List *stmt_list;
|
||||
ParamListInfo paramLI;
|
||||
Snapshot snapshot;
|
||||
@ -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
|
||||
* returning one tupleset.
|
||||
*/
|
||||
if (!SPI_is_cursor_plan(spiplan))
|
||||
if (!SPI_is_cursor_plan(plan))
|
||||
{
|
||||
/* try to give a good error message */
|
||||
Node *stmt;
|
||||
|
||||
if (list_length(spiplan->stmt_list_list) != 1)
|
||||
if (list_length(plan->plancache_list) != 1)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
|
||||
errmsg("cannot open multi-query plan as cursor")));
|
||||
stmt = PortalListGetPrimaryStmt((List *) linitial(spiplan->stmt_list_list));
|
||||
plansource = (CachedPlanSource *) linitial(plan->plancache_list);
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
|
||||
/* translator: %s is name of a SQL command, eg INSERT */
|
||||
errmsg("cannot open %s query as cursor",
|
||||
CreateCommandTag(stmt))));
|
||||
plansource->commandTag)));
|
||||
}
|
||||
|
||||
Assert(list_length(spiplan->stmt_list_list) == 1);
|
||||
stmt_list = (List *) linitial(spiplan->stmt_list_list);
|
||||
Assert(list_length(plan->plancache_list) == 1);
|
||||
plansource = (CachedPlanSource *) linitial(plan->plancache_list);
|
||||
|
||||
/* Reset SPI result (note we deliberately don't touch lastoid) */
|
||||
SPI_processed = 0;
|
||||
@ -880,23 +891,20 @@ SPI_cursor_open(const char *name, void *plan,
|
||||
portal = CreatePortal(name, false, false);
|
||||
}
|
||||
|
||||
/* Switch to portal's memory and copy the plans to there */
|
||||
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||
stmt_list = copyObject(stmt_list);
|
||||
|
||||
/* If the plan has parameters, set them up */
|
||||
if (spiplan->nargs > 0)
|
||||
/* If the plan has parameters, copy them into the portal */
|
||||
if (plan->nargs > 0)
|
||||
{
|
||||
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||
/* sizeof(ParamListInfoData) includes the first array element */
|
||||
paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
|
||||
(spiplan->nargs - 1) *sizeof(ParamExternData));
|
||||
paramLI->numParams = spiplan->nargs;
|
||||
(plan->nargs - 1) *sizeof(ParamExternData));
|
||||
paramLI->numParams = plan->nargs;
|
||||
|
||||
for (k = 0; k < spiplan->nargs; k++)
|
||||
for (k = 0; k < plan->nargs; k++)
|
||||
{
|
||||
ParamExternData *prm = ¶mLI->params[k];
|
||||
|
||||
prm->ptype = spiplan->argtypes[k];
|
||||
prm->ptype = plan->argtypes[k];
|
||||
prm->pflags = 0;
|
||||
prm->isnull = (Nulls && Nulls[k] == 'n');
|
||||
if (prm->isnull)
|
||||
@ -915,21 +923,35 @@ SPI_cursor_open(const char *name, void *plan,
|
||||
paramTypByVal, paramTypLen);
|
||||
}
|
||||
}
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
else
|
||||
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.
|
||||
*/
|
||||
PortalDefineQuery(portal,
|
||||
NULL, /* no statement name */
|
||||
spiplan->query,
|
||||
CreateCommandTag(PortalListGetPrimaryStmt(stmt_list)),
|
||||
plansource->query_string,
|
||||
plansource->commandTag,
|
||||
stmt_list,
|
||||
NULL);
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
cplan);
|
||||
|
||||
/*
|
||||
* Set up options for portal.
|
||||
@ -1023,28 +1045,29 @@ SPI_cursor_close(Portal portal)
|
||||
* parameter is at index zero.
|
||||
*/
|
||||
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;
|
||||
return InvalidOid;
|
||||
}
|
||||
return ((_SPI_plan *) plan)->argtypes[argIndex];
|
||||
return plan->argtypes[argIndex];
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the number of arguments for the prepared plan.
|
||||
*/
|
||||
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;
|
||||
return -1;
|
||||
}
|
||||
return ((_SPI_plan *) plan)->nargs;
|
||||
return plan->nargs;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1057,31 +1080,32 @@ SPI_getargcount(void *plan)
|
||||
* plan: A plan previously prepared using SPI_prepare
|
||||
*/
|
||||
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;
|
||||
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 */
|
||||
plansource = (CachedPlanSource *) linitial(plan->plancache_list);
|
||||
|
||||
switch (ChoosePortalStrategy((List *) linitial(spiplan->stmt_list_list)))
|
||||
if (plan->saved)
|
||||
{
|
||||
case PORTAL_ONE_SELECT:
|
||||
case PORTAL_ONE_RETURNING:
|
||||
case PORTAL_UTIL_SELECT:
|
||||
/* OK */
|
||||
return true;
|
||||
|
||||
case PORTAL_MULTI_QUERY:
|
||||
/* will not return tuples */
|
||||
break;
|
||||
/* Make sure the plan is up to date */
|
||||
cplan = RevalidateCachedPlan(plansource, true);
|
||||
ReleaseCachedPlan(cplan, true);
|
||||
}
|
||||
|
||||
/* Does it return tuples? */
|
||||
if (plansource->resultDesc)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1248,13 +1272,15 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
|
||||
*
|
||||
* 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
|
||||
_SPI_prepare_plan(const char *src, _SPI_plan *plan)
|
||||
_SPI_prepare_plan(const char *src, SPIPlanPtr plan)
|
||||
{
|
||||
List *raw_parsetree_list;
|
||||
List *stmt_list_list;
|
||||
List *plancache_list;
|
||||
ListCell *list_item;
|
||||
ErrorContextCallback spierrcontext;
|
||||
Oid *argtypes = plan->argtypes;
|
||||
@ -1282,26 +1308,44 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan)
|
||||
raw_parsetree_list = pg_parse_query(src);
|
||||
|
||||
/*
|
||||
* Do parse analysis and rule rewrite for each raw parsetree.
|
||||
*
|
||||
* 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.
|
||||
* Do parse analysis and rule rewrite for each raw parsetree, then
|
||||
* cons up a phony plancache entry for each one.
|
||||
*/
|
||||
stmt_list_list = NIL;
|
||||
plancache_list = NIL;
|
||||
|
||||
foreach(list_item, raw_parsetree_list)
|
||||
{
|
||||
Node *parsetree = (Node *) lfirst(list_item);
|
||||
List *query_list;
|
||||
List *stmt_list;
|
||||
CachedPlanSource *plansource;
|
||||
CachedPlan *cplan;
|
||||
|
||||
query_list = pg_analyze_and_rewrite(parsetree, src, argtypes, nargs);
|
||||
/* Need a copyObject here to keep parser from modifying raw tree */
|
||||
stmt_list = pg_analyze_and_rewrite(copyObject(parsetree),
|
||||
src, argtypes, nargs);
|
||||
stmt_list = pg_plan_queries(stmt_list, NULL, false);
|
||||
|
||||
stmt_list_list = lappend(stmt_list_list,
|
||||
pg_plan_queries(query_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
|
||||
@ -1319,7 +1363,7 @@ _SPI_prepare_plan(const char *src, _SPI_plan *plan)
|
||||
* tcount: execution tuple-count limit, or 0 for none
|
||||
*/
|
||||
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,
|
||||
bool read_only, long tcount)
|
||||
{
|
||||
@ -1334,10 +1378,11 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
|
||||
saveActiveSnapshot = ActiveSnapshot;
|
||||
PG_TRY();
|
||||
{
|
||||
ListCell *stmt_list_list_item;
|
||||
ListCell *lc1;
|
||||
ErrorContextCallback spierrcontext;
|
||||
int nargs = plan->nargs;
|
||||
ParamListInfo paramLI;
|
||||
CachedPlan *cplan = NULL;
|
||||
|
||||
/* Convert parameters to form wanted by executor */
|
||||
if (nargs > 0)
|
||||
@ -1366,18 +1411,34 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
|
||||
* Setup error traceback support for ereport()
|
||||
*/
|
||||
spierrcontext.callback = _SPI_error_callback;
|
||||
spierrcontext.arg = (void *) plan->query;
|
||||
spierrcontext.arg = NULL;
|
||||
spierrcontext.previous = error_context_stack;
|
||||
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);
|
||||
ListCell *stmt_list_item;
|
||||
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
|
||||
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;
|
||||
QueryDesc *qdesc;
|
||||
DestReceiver *dest;
|
||||
@ -1510,10 +1571,19 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Done with this plan, so release refcount */
|
||||
if (cplan)
|
||||
ReleaseCachedPlan(cplan, true);
|
||||
cplan = NULL;
|
||||
}
|
||||
|
||||
fail:
|
||||
|
||||
/* We no longer need the cached plan refcount, if any */
|
||||
if (cplan)
|
||||
ReleaseCachedPlan(cplan, true);
|
||||
|
||||
/*
|
||||
* Pop the error context stack
|
||||
*/
|
||||
@ -1772,22 +1842,18 @@ _SPI_checktuples(void)
|
||||
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;
|
||||
MemoryContext oldcxt;
|
||||
SPIPlanPtr newplan;
|
||||
MemoryContext plancxt;
|
||||
MemoryContext parentcxt;
|
||||
MemoryContext oldcxt;
|
||||
ListCell *lc;
|
||||
|
||||
/* Determine correct parent for the plan's memory context */
|
||||
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;
|
||||
Assert(!plan->saved); /* not currently supported */
|
||||
|
||||
/*
|
||||
* 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)
|
||||
oldcxt = MemoryContextSwitchTo(plancxt);
|
||||
|
||||
/* 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->query = pstrdup(plan->query);
|
||||
newplan->stmt_list_list = (List *) copyObject(plan->stmt_list_list);
|
||||
newplan->nargs = plan->nargs;
|
||||
if (plan->nargs > 0)
|
||||
{
|
||||
@ -1814,6 +1881,101 @@ _SPI_copy_plan(_SPI_plan *plan, int location)
|
||||
else
|
||||
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);
|
||||
|
||||
return newplan;
|
||||
|
@ -8,16 +8,14 @@
|
||||
* across query and transaction boundaries, in fact they live as long as
|
||||
* the backend does. This works because the hashtable structures
|
||||
* themselves are allocated by dynahash.c in its permanent DynaHashCxt,
|
||||
* and the parse/plan node trees they point to are copied into
|
||||
* TopMemoryContext using SPI_saveplan(). This is pretty ugly, since there
|
||||
* is no way to free a no-longer-needed plan tree, but then again we don't
|
||||
* yet have any bookkeeping that would allow us to detect that a plan isn't
|
||||
* needed anymore. Improve it someday.
|
||||
* and the SPI plans they point to are saved using SPI_saveplan().
|
||||
* There is not currently any provision for throwing away a no-longer-needed
|
||||
* plan --- consider improving this someday.
|
||||
*
|
||||
*
|
||||
* 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 @@
|
||||
#include "catalog/pg_constraint.h"
|
||||
#include "catalog/pg_operator.h"
|
||||
#include "commands/trigger.h"
|
||||
#include "executor/spi_priv.h"
|
||||
#include "executor/spi.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_relation.h"
|
||||
#include "miscadmin.h"
|
||||
@ -135,7 +133,7 @@ typedef struct RI_QueryKey
|
||||
typedef struct RI_QueryHashEntry
|
||||
{
|
||||
RI_QueryKey key;
|
||||
void *plan;
|
||||
SPIPlanPtr plan;
|
||||
} RI_QueryHashEntry;
|
||||
|
||||
|
||||
@ -206,18 +204,18 @@ static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
|
||||
const RI_ConstraintInfo *riinfo);
|
||||
|
||||
static void ri_InitHashTables(void);
|
||||
static void *ri_FetchPreparedPlan(RI_QueryKey *key);
|
||||
static void ri_HashPreparedPlan(RI_QueryKey *key, void *plan);
|
||||
static SPIPlanPtr ri_FetchPreparedPlan(RI_QueryKey *key);
|
||||
static void ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan);
|
||||
static RI_CompareHashEntry *ri_HashCompareOp(Oid eq_opr, Oid typeid);
|
||||
|
||||
static void ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname,
|
||||
int tgkind);
|
||||
static void ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo,
|
||||
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,
|
||||
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,
|
||||
HeapTuple old_tuple, HeapTuple new_tuple,
|
||||
bool detectNewRows,
|
||||
@ -248,7 +246,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
|
||||
HeapTuple old_row;
|
||||
Buffer new_row_buf;
|
||||
RI_QueryKey qkey;
|
||||
void *qplan;
|
||||
SPIPlanPtr qplan;
|
||||
int i;
|
||||
|
||||
/*
|
||||
@ -542,7 +540,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
|
||||
HeapTuple old_row,
|
||||
const RI_ConstraintInfo *riinfo)
|
||||
{
|
||||
void *qplan;
|
||||
SPIPlanPtr qplan;
|
||||
RI_QueryKey qkey;
|
||||
int i;
|
||||
bool result;
|
||||
@ -678,7 +676,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
|
||||
Relation pk_rel;
|
||||
HeapTuple old_row;
|
||||
RI_QueryKey qkey;
|
||||
void *qplan;
|
||||
SPIPlanPtr qplan;
|
||||
int i;
|
||||
|
||||
/*
|
||||
@ -855,7 +853,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
|
||||
HeapTuple new_row;
|
||||
HeapTuple old_row;
|
||||
RI_QueryKey qkey;
|
||||
void *qplan;
|
||||
SPIPlanPtr qplan;
|
||||
int i;
|
||||
|
||||
/*
|
||||
@ -1040,7 +1038,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
|
||||
Relation pk_rel;
|
||||
HeapTuple old_row;
|
||||
RI_QueryKey qkey;
|
||||
void *qplan;
|
||||
SPIPlanPtr qplan;
|
||||
int i;
|
||||
|
||||
/*
|
||||
@ -1203,7 +1201,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
|
||||
HeapTuple new_row;
|
||||
HeapTuple old_row;
|
||||
RI_QueryKey qkey;
|
||||
void *qplan;
|
||||
SPIPlanPtr qplan;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
@ -1397,7 +1395,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
|
||||
Relation pk_rel;
|
||||
HeapTuple old_row;
|
||||
RI_QueryKey qkey;
|
||||
void *qplan;
|
||||
SPIPlanPtr qplan;
|
||||
int i;
|
||||
|
||||
/*
|
||||
@ -1569,7 +1567,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
|
||||
HeapTuple new_row;
|
||||
HeapTuple old_row;
|
||||
RI_QueryKey qkey;
|
||||
void *qplan;
|
||||
SPIPlanPtr qplan;
|
||||
int i;
|
||||
|
||||
/*
|
||||
@ -1744,7 +1742,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
|
||||
Relation pk_rel;
|
||||
HeapTuple old_row;
|
||||
RI_QueryKey qkey;
|
||||
void *qplan;
|
||||
SPIPlanPtr qplan;
|
||||
int i;
|
||||
|
||||
/*
|
||||
@ -1916,7 +1914,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
|
||||
HeapTuple new_row;
|
||||
HeapTuple old_row;
|
||||
RI_QueryKey qkey;
|
||||
void *qplan;
|
||||
SPIPlanPtr qplan;
|
||||
int i;
|
||||
bool use_cached_query;
|
||||
|
||||
@ -2130,7 +2128,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
|
||||
Relation pk_rel;
|
||||
HeapTuple old_row;
|
||||
RI_QueryKey qkey;
|
||||
void *qplan;
|
||||
SPIPlanPtr qplan;
|
||||
|
||||
/*
|
||||
* 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)
|
||||
HeapTuple new_row;
|
||||
HeapTuple old_row;
|
||||
RI_QueryKey qkey;
|
||||
void *qplan;
|
||||
SPIPlanPtr qplan;
|
||||
|
||||
/*
|
||||
* 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)
|
||||
int old_work_mem;
|
||||
char workmembuf[32];
|
||||
int spi_result;
|
||||
void *qplan;
|
||||
SPIPlanPtr qplan;
|
||||
|
||||
/*
|
||||
* Check to make sure current user has enough permissions to do the test
|
||||
@ -3165,12 +3163,12 @@ ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo,
|
||||
* If cache_plan is true, the plan is saved into our plan hashtable
|
||||
* so that we don't need to plan it again.
|
||||
*/
|
||||
static void *
|
||||
static SPIPlanPtr
|
||||
ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
|
||||
RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
|
||||
bool cache_plan)
|
||||
{
|
||||
void *qplan;
|
||||
SPIPlanPtr qplan;
|
||||
Relation query_rel;
|
||||
Oid save_uid;
|
||||
|
||||
@ -3212,7 +3210,7 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
|
||||
* Perform a query to enforce an RI restriction
|
||||
*/
|
||||
static bool
|
||||
ri_PerformCheck(RI_QueryKey *qkey, void *qplan,
|
||||
ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
|
||||
Relation fk_rel, Relation pk_rel,
|
||||
HeapTuple old_tuple, HeapTuple new_tuple,
|
||||
bool detectNewRows,
|
||||
@ -3576,7 +3574,7 @@ ri_InitHashTables(void)
|
||||
* and saved SPI execution plans. Return the plan if found or NULL.
|
||||
* ----------
|
||||
*/
|
||||
static void *
|
||||
static SPIPlanPtr
|
||||
ri_FetchPreparedPlan(RI_QueryKey *key)
|
||||
{
|
||||
RI_QueryHashEntry *entry;
|
||||
@ -3606,7 +3604,7 @@ ri_FetchPreparedPlan(RI_QueryKey *key)
|
||||
* ----------
|
||||
*/
|
||||
static void
|
||||
ri_HashPreparedPlan(RI_QueryKey *key, void *plan)
|
||||
ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
|
||||
{
|
||||
RI_QueryHashEntry *entry;
|
||||
bool found;
|
||||
|
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* 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
|
||||
* 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 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";
|
||||
|
||||
|
||||
@ -250,7 +250,7 @@ pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
|
||||
if (plan_getrulebyoid == NULL)
|
||||
{
|
||||
Oid argtypes[1];
|
||||
void *plan;
|
||||
SPIPlanPtr plan;
|
||||
|
||||
argtypes[0] = OIDOID;
|
||||
plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
|
||||
@ -380,7 +380,7 @@ pg_get_viewdef_worker(Oid viewoid, int prettyFlags)
|
||||
if (plan_getviewrule == NULL)
|
||||
{
|
||||
Oid argtypes[2];
|
||||
void *plan;
|
||||
SPIPlanPtr plan;
|
||||
|
||||
argtypes[0] = OIDOID;
|
||||
argtypes[1] = NAMEOID;
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
* 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)
|
||||
const char *targetns = _textout(PG_GETARG_TEXT_P(3));
|
||||
|
||||
const char *result;
|
||||
void *plan;
|
||||
SPIPlanPtr plan;
|
||||
Portal portal;
|
||||
|
||||
SPI_connect();
|
||||
@ -1871,7 +1871,7 @@ query_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
|
||||
const char *targetns = _textout(PG_GETARG_TEXT_P(3));
|
||||
|
||||
const char *xmlschema;
|
||||
void *plan;
|
||||
SPIPlanPtr plan;
|
||||
Portal portal;
|
||||
|
||||
SPI_connect();
|
||||
|
24
src/backend/utils/cache/plancache.c
vendored
24
src/backend/utils/cache/plancache.c
vendored
@ -33,7 +33,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* 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,
|
||||
void *arg);
|
||||
static bool ScanQueryWalker(Node *node, ScanQueryWalkerContext *context);
|
||||
static bool rowmark_member(List *rowMarks, int rt_index);
|
||||
static TupleDesc ComputeResultDesc(List *stmt_list);
|
||||
static void PlanCacheCallback(Datum arg, Oid relid);
|
||||
static void InvalRelid(Oid relid, LOCKMODE lockmode,
|
||||
InvalRelidContext *context);
|
||||
@ -153,7 +152,7 @@ CreateCachedPlan(Node *raw_parse_tree,
|
||||
plansource->fully_planned = fully_planned;
|
||||
plansource->fixed_result = fixed_result;
|
||||
plansource->generation = 0; /* StoreCachedPlan will increment */
|
||||
plansource->resultDesc = ComputeResultDesc(stmt_list);
|
||||
plansource->resultDesc = PlanCacheComputeResultDesc(stmt_list);
|
||||
plansource->plan = NULL;
|
||||
plansource->context = source_context;
|
||||
plansource->orig_plan = NULL;
|
||||
@ -225,7 +224,7 @@ FastCreateCachedPlan(Node *raw_parse_tree,
|
||||
plansource->fully_planned = fully_planned;
|
||||
plansource->fixed_result = fixed_result;
|
||||
plansource->generation = 0; /* StoreCachedPlan will increment */
|
||||
plansource->resultDesc = ComputeResultDesc(stmt_list);
|
||||
plansource->resultDesc = PlanCacheComputeResultDesc(stmt_list);
|
||||
plansource->plan = NULL;
|
||||
plansource->context = context;
|
||||
plansource->orig_plan = NULL;
|
||||
@ -271,12 +270,13 @@ StoreCachedPlan(CachedPlanSource *plansource,
|
||||
{
|
||||
/*
|
||||
* 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,
|
||||
"CachedPlan",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_SMALL_MINSIZE,
|
||||
ALLOCSET_SMALL_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
|
||||
/*
|
||||
@ -445,7 +445,7 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
|
||||
* Check or update the result tupdesc. XXX should we use a weaker
|
||||
* condition than equalTupleDescs() here?
|
||||
*/
|
||||
resultDesc = ComputeResultDesc(slist);
|
||||
resultDesc = PlanCacheComputeResultDesc(slist);
|
||||
if (resultDesc == NULL && plansource->resultDesc == NULL)
|
||||
{
|
||||
/* OK, doesn't return tuples */
|
||||
@ -718,14 +718,14 @@ rowmark_member(List *rowMarks, int rt_index)
|
||||
}
|
||||
|
||||
/*
|
||||
* ComputeResultDesc: given a list of either fully-planned statements or
|
||||
* Queries, determine the result tupledesc it will produce. Returns NULL
|
||||
* PlanCacheComputeResultDesc: given a list of either fully-planned statements
|
||||
* or Queries, determine the result tupledesc it will produce. Returns NULL
|
||||
* if the execution will not return tuples.
|
||||
*
|
||||
* Note: the result is created or copied into current memory context.
|
||||
*/
|
||||
static TupleDesc
|
||||
ComputeResultDesc(List *stmt_list)
|
||||
TupleDesc
|
||||
PlanCacheComputeResultDesc(List *stmt_list)
|
||||
{
|
||||
Node *node;
|
||||
Query *query;
|
||||
|
@ -1,8 +1,12 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* 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 @@
|
||||
#include "catalog/pg_language.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "executor/execdefs.h"
|
||||
#include "executor/executor.h"
|
||||
#include "nodes/execnodes.h"
|
||||
#include "nodes/params.h"
|
||||
@ -38,9 +43,9 @@
|
||||
#include "utils/datum.h"
|
||||
#include "utils/portal.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "executor/execdefs.h"
|
||||
|
||||
typedef struct
|
||||
|
||||
typedef struct SPITupleTable
|
||||
{
|
||||
MemoryContext tuptabcxt; /* memory context of result table */
|
||||
uint32 alloced; /* # of alloced vals */
|
||||
@ -49,6 +54,9 @@ typedef struct
|
||||
HeapTuple *vals; /* tuples */
|
||||
} SPITupleTable;
|
||||
|
||||
/* Plans are opaque structs for standard users of SPI */
|
||||
typedef struct _SPI_plan *SPIPlanPtr;
|
||||
|
||||
#define SPI_ERROR_CONNECT (-1)
|
||||
#define SPI_ERROR_COPY (-2)
|
||||
#define SPI_ERROR_OPUNKNOWN (-3)
|
||||
@ -86,23 +94,23 @@ extern void SPI_push(void);
|
||||
extern void SPI_pop(void);
|
||||
extern void SPI_restore_connection(void);
|
||||
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);
|
||||
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);
|
||||
extern int SPI_execute_snapshot(void *plan,
|
||||
extern int SPI_execute_snapshot(SPIPlanPtr plan,
|
||||
Datum *Values, const char *Nulls,
|
||||
Snapshot snapshot,
|
||||
Snapshot crosscheck_snapshot,
|
||||
bool read_only, long tcount);
|
||||
extern void *SPI_prepare(const char *src, int nargs, Oid *argtypes);
|
||||
extern void *SPI_saveplan(void *plan);
|
||||
extern int SPI_freeplan(void *plan);
|
||||
extern SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes);
|
||||
extern SPIPlanPtr SPI_saveplan(SPIPlanPtr plan);
|
||||
extern int SPI_freeplan(SPIPlanPtr plan);
|
||||
|
||||
extern Oid SPI_getargtypeid(void *plan, int argIndex);
|
||||
extern int SPI_getargcount(void *plan);
|
||||
extern bool SPI_is_cursor_plan(void *plan);
|
||||
extern Oid SPI_getargtypeid(SPIPlanPtr plan, int argIndex);
|
||||
extern int SPI_getargcount(SPIPlanPtr plan);
|
||||
extern bool SPI_is_cursor_plan(SPIPlanPtr plan);
|
||||
extern const char *SPI_result_code_string(int code);
|
||||
|
||||
extern HeapTuple SPI_copytuple(HeapTuple tuple);
|
||||
@ -123,7 +131,7 @@ extern void SPI_pfree(void *pointer);
|
||||
extern void SPI_freetuple(HeapTuple pointer);
|
||||
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);
|
||||
extern Portal SPI_cursor_find(const char *name);
|
||||
extern void SPI_cursor_fetch(Portal portal, bool forward, long count);
|
||||
|
@ -6,7 +6,7 @@
|
||||
* 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_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 @@
|
||||
#include "executor/spi.h"
|
||||
|
||||
|
||||
#define _SPI_PLAN_MAGIC 569278163
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* current results */
|
||||
@ -25,29 +27,46 @@ typedef struct
|
||||
|
||||
MemoryContext procCxt; /* procedure context */
|
||||
MemoryContext execCxt; /* executor context */
|
||||
MemoryContext savedcxt;
|
||||
MemoryContext savedcxt; /* context of SPI_connect's caller */
|
||||
SubTransactionId connectSubid; /* ID of connecting subtransaction */
|
||||
} _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 */
|
||||
MemoryContext plancxt;
|
||||
/* Original query string (used for error reporting) */
|
||||
const char *query;
|
||||
/*
|
||||
* List of List of PlannedStmts and utility stmts; one sublist per
|
||||
* original parsetree
|
||||
*/
|
||||
List *stmt_list_list;
|
||||
/* Argument types, if a prepared plan */
|
||||
int nargs;
|
||||
Oid *argtypes;
|
||||
int magic; /* should equal _SPI_PLAN_MAGIC */
|
||||
bool saved; /* saved or unsaved plan? */
|
||||
List *plancache_list; /* one CachedPlanSource per parsetree */
|
||||
MemoryContext plancxt; /* Context containing _SPI_plan and data */
|
||||
int nargs; /* number of plan arguments */
|
||||
Oid *argtypes; /* Argument types (NULL if nargs is 0) */
|
||||
} _SPI_plan;
|
||||
|
||||
|
||||
#define _SPI_CPLAN_CURCXT 0
|
||||
#define _SPI_CPLAN_PROCXT 1
|
||||
#define _SPI_CPLAN_TOPCXT 2
|
||||
|
||||
#endif /* SPI_PRIV_H */
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||
* 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);
|
||||
extern CachedPlan *RevalidateCachedPlan(CachedPlanSource *plansource,
|
||||
bool useResOwner);
|
||||
extern void ReleaseCachedPlan(CachedPlan *plan, bool useResOwner);
|
||||
extern TupleDesc PlanCacheComputeResultDesc(List *stmt_list);
|
||||
|
||||
#endif /* PLANCACHE_H */
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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,
|
||||
PLpgSQL_expr *expr);
|
||||
static bool exec_simple_check_node(Node *node);
|
||||
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,
|
||||
Datum *result,
|
||||
bool *isNull,
|
||||
Oid *rettype);
|
||||
|
||||
@ -409,7 +410,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
|
||||
void *tmp;
|
||||
|
||||
len = datumGetSize(estate.retval, false, func->fn_rettyplen);
|
||||
tmp = (void *) SPI_palloc(len);
|
||||
tmp = SPI_palloc(len);
|
||||
memcpy(tmp, DatumGetPointer(estate.retval), len);
|
||||
estate.retval = PointerGetDatum(tmp);
|
||||
}
|
||||
@ -2294,8 +2295,7 @@ exec_prepare_plan(PLpgSQL_execstate *estate,
|
||||
PLpgSQL_expr *expr)
|
||||
{
|
||||
int i;
|
||||
_SPI_plan *spi_plan;
|
||||
void *plan;
|
||||
SPIPlanPtr plan;
|
||||
Oid *argtypes;
|
||||
|
||||
/*
|
||||
@ -2343,12 +2343,11 @@ exec_prepare_plan(PLpgSQL_execstate *estate,
|
||||
}
|
||||
}
|
||||
expr->plan = SPI_saveplan(plan);
|
||||
spi_plan = (_SPI_plan *) expr->plan;
|
||||
expr->plan_argtypes = spi_plan->argtypes;
|
||||
expr->expr_simple_expr = NULL;
|
||||
SPI_freeplan(plan);
|
||||
plan = expr->plan;
|
||||
expr->plan_argtypes = plan->argtypes;
|
||||
exec_simple_check_plan(expr);
|
||||
|
||||
SPI_freeplan(plan);
|
||||
pfree(argtypes);
|
||||
}
|
||||
|
||||
@ -2374,17 +2373,16 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
|
||||
*/
|
||||
if (expr->plan == NULL)
|
||||
{
|
||||
_SPI_plan *spi_plan;
|
||||
ListCell *l;
|
||||
|
||||
exec_prepare_plan(estate, expr);
|
||||
stmt->mod_stmt = false;
|
||||
spi_plan = (_SPI_plan *) expr->plan;
|
||||
foreach(l, spi_plan->stmt_list_list)
|
||||
foreach(l, expr->plan->plancache_list)
|
||||
{
|
||||
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(l);
|
||||
ListCell *l2;
|
||||
|
||||
foreach(l2, (List *) lfirst(l))
|
||||
foreach(l2, plansource->plan->stmt_list)
|
||||
{
|
||||
PlannedStmt *p = (PlannedStmt *) lfirst(l2);
|
||||
|
||||
@ -2735,7 +2733,7 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt)
|
||||
PLpgSQL_row *row = NULL;
|
||||
SPITupleTable *tuptab;
|
||||
int n;
|
||||
void *plan;
|
||||
SPIPlanPtr plan;
|
||||
Portal portal;
|
||||
bool found = false;
|
||||
|
||||
@ -2959,7 +2957,7 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
|
||||
Datum queryD;
|
||||
Oid restype;
|
||||
char *querystr;
|
||||
void *curplan;
|
||||
SPIPlanPtr curplan;
|
||||
|
||||
/* ----------
|
||||
* We evaluate the string expression after the
|
||||
@ -3860,10 +3858,11 @@ exec_eval_expr(PLpgSQL_execstate *estate,
|
||||
bool *isNull,
|
||||
Oid *rettype)
|
||||
{
|
||||
Datum result;
|
||||
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)
|
||||
exec_prepare_plan(estate, expr);
|
||||
@ -3872,9 +3871,12 @@ exec_eval_expr(PLpgSQL_execstate *estate,
|
||||
* If this is a simple expression, bypass SPI and use the executor
|
||||
* directly
|
||||
*/
|
||||
if (expr->expr_simple_expr != NULL)
|
||||
return exec_eval_simple_expr(estate, expr, isNull, rettype);
|
||||
if (exec_eval_simple_expr(estate, expr, &result, isNull, rettype))
|
||||
return result;
|
||||
|
||||
/*
|
||||
* Else do it the hard way via exec_run_select
|
||||
*/
|
||||
rc = exec_run_select(estate, expr, 2, NULL);
|
||||
if (rc != SPI_OK_SELECT)
|
||||
ereport(ERROR,
|
||||
@ -3994,23 +3996,64 @@ exec_run_select(PLpgSQL_execstate *estate,
|
||||
* exec_eval_simple_expr - Evaluate a simple expression returning
|
||||
* 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
|
||||
* temporary memory context. It will be freed when exec_eval_cleanup
|
||||
* is done.
|
||||
* ----------
|
||||
*/
|
||||
static Datum
|
||||
static bool
|
||||
exec_eval_simple_expr(PLpgSQL_execstate *estate,
|
||||
PLpgSQL_expr *expr,
|
||||
Datum *result,
|
||||
bool *isNull,
|
||||
Oid *rettype)
|
||||
{
|
||||
Datum retval;
|
||||
ExprContext *econtext = estate->eval_econtext;
|
||||
CachedPlanSource *plansource;
|
||||
CachedPlan *cplan;
|
||||
ParamListInfo paramLI;
|
||||
int i;
|
||||
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.
|
||||
*/
|
||||
@ -4018,7 +4061,8 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
@ -4086,10 +4130,10 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
|
||||
/*
|
||||
* Finally we can call the executor to evaluate the expression
|
||||
*/
|
||||
retval = ExecEvalExpr(expr->expr_simple_state,
|
||||
econtext,
|
||||
isNull,
|
||||
NULL);
|
||||
*result = ExecEvalExpr(expr->expr_simple_state,
|
||||
econtext,
|
||||
isNull,
|
||||
NULL);
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
PG_CATCH();
|
||||
@ -4103,10 +4147,15 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
|
||||
ActiveSnapshot = saveActiveSnapshot;
|
||||
SPI_pop();
|
||||
|
||||
/*
|
||||
* Now we can release our refcount on the cached plan.
|
||||
*/
|
||||
ReleaseCachedPlan(cplan, true);
|
||||
|
||||
/*
|
||||
* That's it.
|
||||
*/
|
||||
return retval;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -4673,25 +4722,31 @@ exec_simple_check_node(Node *node)
|
||||
static void
|
||||
exec_simple_check_plan(PLpgSQL_expr *expr)
|
||||
{
|
||||
_SPI_plan *spi_plan = (_SPI_plan *) expr->plan;
|
||||
List *sublist;
|
||||
CachedPlanSource *plansource;
|
||||
PlannedStmt *stmt;
|
||||
Plan *plan;
|
||||
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_generation = 0;
|
||||
|
||||
/*
|
||||
* 1. We can only evaluate queries that resulted in one single execution
|
||||
* plan
|
||||
*/
|
||||
if (list_length(spi_plan->stmt_list_list) != 1)
|
||||
if (list_length(expr->plan->plancache_list) != 1)
|
||||
return;
|
||||
sublist = (List *) linitial(spi_plan->stmt_list_list);
|
||||
if (list_length(sublist) != 1)
|
||||
plansource = (CachedPlanSource *) linitial(expr->plan->plancache_list);
|
||||
expr->expr_simple_generation = plansource->generation;
|
||||
if (list_length(plansource->plan->stmt_list) != 1)
|
||||
return;
|
||||
|
||||
stmt = (PlannedStmt *) linitial(sublist);
|
||||
stmt = (PlannedStmt *) linitial(plansource->plan->stmt_list);
|
||||
|
||||
/*
|
||||
* 2. It must be a RESULT plan --> no scan's required
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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
|
||||
int dtype;
|
||||
int exprno;
|
||||
char *query;
|
||||
void *plan;
|
||||
SPIPlanPtr plan;
|
||||
Oid *plan_argtypes;
|
||||
/* fields for "simple expression" fast-path execution: */
|
||||
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,
|
||||
|
@ -100,3 +100,64 @@ EXECUTE vprep;
|
||||
4567890123456789 | 2283945061728394
|
||||
(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)
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $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"
|
||||
@ -451,7 +451,7 @@ extern Datum set_ttdummy(PG_FUNCTION_ARGS);
|
||||
|
||||
#define TTDUMMY_INFINITY 999999
|
||||
|
||||
static void *splan = NULL;
|
||||
static SPIPlanPtr splan = NULL;
|
||||
static bool ttoff = false;
|
||||
|
||||
PG_FUNCTION_INFO_V1(ttdummy);
|
||||
@ -599,7 +599,7 @@ ttdummy(PG_FUNCTION_ARGS)
|
||||
/* if there is no plan ... */
|
||||
if (splan == NULL)
|
||||
{
|
||||
void *pplan;
|
||||
SPIPlanPtr pplan;
|
||||
Oid *ctypes;
|
||||
char *query;
|
||||
|
||||
|
@ -51,3 +51,43 @@ EXECUTE vprep;
|
||||
CREATE OR REPLACE TEMP VIEW voo AS SELECT q1, q2/2 AS q2 FROM foo;
|
||||
|
||||
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();
|
||||
|
Loading…
Reference in New Issue
Block a user