提交 95f6d2d2 编写于 作者: T 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.)
上级 d3ff1801
......@@ -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 = ' ';
......
<!-- $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,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();
......
......@@ -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;
......
/*-------------------------------------------------------------------------
*
* 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)
/*
* $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();
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册