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

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

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

Along the way, clean up SPI's API a little bit by declaring SPI plan
pointers as "SPIPlanPtr" instead of "void *".  This is cosmetic but
helps to forestall simple programming mistakes.  (I have changed some
but not all of the callers to match; there are still some "void *"'s
in contrib and the PL's.  This is intentional so that we can see if
anyone's compiler complains about it.)
parent d3ff1801
...@@ -19,7 +19,7 @@ typedef struct ...@@ -19,7 +19,7 @@ typedef struct
{ {
char *ident; char *ident;
int nplans; int nplans;
void **splan; SPIPlanPtr *splan;
} EPlan; } EPlan;
static EPlan *FPlans = NULL; static EPlan *FPlans = NULL;
...@@ -163,7 +163,7 @@ check_primary_key(PG_FUNCTION_ARGS) ...@@ -163,7 +163,7 @@ check_primary_key(PG_FUNCTION_ARGS)
*/ */
if (plan->nplans <= 0) if (plan->nplans <= 0)
{ {
void *pplan; SPIPlanPtr pplan;
char sql[8192]; char sql[8192];
/* /*
...@@ -191,7 +191,7 @@ check_primary_key(PG_FUNCTION_ARGS) ...@@ -191,7 +191,7 @@ check_primary_key(PG_FUNCTION_ARGS)
if (pplan == NULL) if (pplan == NULL)
/* internal error */ /* internal error */
elog(ERROR, "check_primary_key: SPI_saveplan returned %d", SPI_result); elog(ERROR, "check_primary_key: SPI_saveplan returned %d", SPI_result);
plan->splan = (void **) malloc(sizeof(void *)); plan->splan = (SPIPlanPtr *) malloc(sizeof(SPIPlanPtr));
*(plan->splan) = pplan; *(plan->splan) = pplan;
plan->nplans = 1; plan->nplans = 1;
} }
...@@ -413,11 +413,11 @@ check_foreign_key(PG_FUNCTION_ARGS) ...@@ -413,11 +413,11 @@ check_foreign_key(PG_FUNCTION_ARGS)
*/ */
if (plan->nplans <= 0) if (plan->nplans <= 0)
{ {
void *pplan; SPIPlanPtr pplan;
char sql[8192]; char sql[8192];
char **args2 = args; char **args2 = args;
plan->splan = (void **) malloc(nrefs * sizeof(void *)); plan->splan = (SPIPlanPtr *) malloc(nrefs * sizeof(SPIPlanPtr));
for (r = 0; r < nrefs; r++) for (r = 0; r < nrefs; r++)
{ {
......
...@@ -24,7 +24,7 @@ Datum get_timetravel(PG_FUNCTION_ARGS); ...@@ -24,7 +24,7 @@ Datum get_timetravel(PG_FUNCTION_ARGS);
typedef struct typedef struct
{ {
char *ident; char *ident;
void *splan; SPIPlanPtr splan;
} EPlan; } EPlan;
static EPlan *Plans = NULL; /* for UPDATE/DELETE */ static EPlan *Plans = NULL; /* for UPDATE/DELETE */
...@@ -308,7 +308,7 @@ timetravel(PG_FUNCTION_ARGS) ...@@ -308,7 +308,7 @@ timetravel(PG_FUNCTION_ARGS)
/* if there is no plan ... */ /* if there is no plan ... */
if (plan->splan == NULL) if (plan->splan == NULL)
{ {
void *pplan; SPIPlanPtr pplan;
Oid *ctypes; Oid *ctypes;
char sql[8192]; char sql[8192];
char separ = ' '; char separ = ' ';
......
This diff is collapsed.
This diff is collapsed.
...@@ -8,16 +8,14 @@ ...@@ -8,16 +8,14 @@
* across query and transaction boundaries, in fact they live as long as * across query and transaction boundaries, in fact they live as long as
* the backend does. This works because the hashtable structures * the backend does. This works because the hashtable structures
* themselves are allocated by dynahash.c in its permanent DynaHashCxt, * themselves are allocated by dynahash.c in its permanent DynaHashCxt,
* and the parse/plan node trees they point to are copied into * and the SPI plans they point to are saved using SPI_saveplan().
* TopMemoryContext using SPI_saveplan(). This is pretty ugly, since there * There is not currently any provision for throwing away a no-longer-needed
* is no way to free a no-longer-needed plan tree, but then again we don't * plan --- consider improving this someday.
* yet have any bookkeeping that would allow us to detect that a plan isn't
* needed anymore. Improve it someday.
* *
* *
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.91 2007/02/14 01:58:57 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.92 2007/03/15 23:12:06 tgl Exp $
* *
* ---------- * ----------
*/ */
...@@ -35,7 +33,7 @@ ...@@ -35,7 +33,7 @@
#include "catalog/pg_constraint.h" #include "catalog/pg_constraint.h"
#include "catalog/pg_operator.h" #include "catalog/pg_operator.h"
#include "commands/trigger.h" #include "commands/trigger.h"
#include "executor/spi_priv.h" #include "executor/spi.h"
#include "parser/parse_coerce.h" #include "parser/parse_coerce.h"
#include "parser/parse_relation.h" #include "parser/parse_relation.h"
#include "miscadmin.h" #include "miscadmin.h"
...@@ -135,7 +133,7 @@ typedef struct RI_QueryKey ...@@ -135,7 +133,7 @@ typedef struct RI_QueryKey
typedef struct RI_QueryHashEntry typedef struct RI_QueryHashEntry
{ {
RI_QueryKey key; RI_QueryKey key;
void *plan; SPIPlanPtr plan;
} RI_QueryHashEntry; } RI_QueryHashEntry;
...@@ -206,18 +204,18 @@ static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel, ...@@ -206,18 +204,18 @@ static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
const RI_ConstraintInfo *riinfo); const RI_ConstraintInfo *riinfo);
static void ri_InitHashTables(void); static void ri_InitHashTables(void);
static void *ri_FetchPreparedPlan(RI_QueryKey *key); static SPIPlanPtr ri_FetchPreparedPlan(RI_QueryKey *key);
static void ri_HashPreparedPlan(RI_QueryKey *key, void *plan); static void ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan);
static RI_CompareHashEntry *ri_HashCompareOp(Oid eq_opr, Oid typeid); static RI_CompareHashEntry *ri_HashCompareOp(Oid eq_opr, Oid typeid);
static void ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname, static void ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname,
int tgkind); int tgkind);
static void ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo, static void ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo,
Trigger *trigger, Relation trig_rel, bool rel_is_pk); Trigger *trigger, Relation trig_rel, bool rel_is_pk);
static void *ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel, RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
bool cache_plan); bool cache_plan);
static bool ri_PerformCheck(RI_QueryKey *qkey, void *qplan, static bool ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
Relation fk_rel, Relation pk_rel, Relation fk_rel, Relation pk_rel,
HeapTuple old_tuple, HeapTuple new_tuple, HeapTuple old_tuple, HeapTuple new_tuple,
bool detectNewRows, bool detectNewRows,
...@@ -248,7 +246,7 @@ RI_FKey_check(PG_FUNCTION_ARGS) ...@@ -248,7 +246,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
HeapTuple old_row; HeapTuple old_row;
Buffer new_row_buf; Buffer new_row_buf;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
/* /*
...@@ -542,7 +540,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel, ...@@ -542,7 +540,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
HeapTuple old_row, HeapTuple old_row,
const RI_ConstraintInfo *riinfo) const RI_ConstraintInfo *riinfo)
{ {
void *qplan; SPIPlanPtr qplan;
RI_QueryKey qkey; RI_QueryKey qkey;
int i; int i;
bool result; bool result;
...@@ -678,7 +676,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS) ...@@ -678,7 +676,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
Relation pk_rel; Relation pk_rel;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
/* /*
...@@ -855,7 +853,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS) ...@@ -855,7 +853,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
HeapTuple new_row; HeapTuple new_row;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
/* /*
...@@ -1040,7 +1038,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS) ...@@ -1040,7 +1038,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
Relation pk_rel; Relation pk_rel;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
/* /*
...@@ -1203,7 +1201,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS) ...@@ -1203,7 +1201,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
HeapTuple new_row; HeapTuple new_row;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
int j; int j;
...@@ -1397,7 +1395,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS) ...@@ -1397,7 +1395,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
Relation pk_rel; Relation pk_rel;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
/* /*
...@@ -1569,7 +1567,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS) ...@@ -1569,7 +1567,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
HeapTuple new_row; HeapTuple new_row;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
/* /*
...@@ -1744,7 +1742,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS) ...@@ -1744,7 +1742,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
Relation pk_rel; Relation pk_rel;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
/* /*
...@@ -1916,7 +1914,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS) ...@@ -1916,7 +1914,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
HeapTuple new_row; HeapTuple new_row;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
int i; int i;
bool use_cached_query; bool use_cached_query;
...@@ -2130,7 +2128,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS) ...@@ -2130,7 +2128,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
Relation pk_rel; Relation pk_rel;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
/* /*
* Check that this is a valid trigger call on the right time and event. * Check that this is a valid trigger call on the right time and event.
...@@ -2313,7 +2311,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS) ...@@ -2313,7 +2311,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
HeapTuple new_row; HeapTuple new_row;
HeapTuple old_row; HeapTuple old_row;
RI_QueryKey qkey; RI_QueryKey qkey;
void *qplan; SPIPlanPtr qplan;
/* /*
* Check that this is a valid trigger call on the right time and event. * Check that this is a valid trigger call on the right time and event.
...@@ -2637,7 +2635,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) ...@@ -2637,7 +2635,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
int old_work_mem; int old_work_mem;
char workmembuf[32]; char workmembuf[32];
int spi_result; int spi_result;
void *qplan; SPIPlanPtr qplan;
/* /*
* Check to make sure current user has enough permissions to do the test * Check to make sure current user has enough permissions to do the test
...@@ -3165,12 +3163,12 @@ ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo, ...@@ -3165,12 +3163,12 @@ ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo,
* If cache_plan is true, the plan is saved into our plan hashtable * If cache_plan is true, the plan is saved into our plan hashtable
* so that we don't need to plan it again. * so that we don't need to plan it again.
*/ */
static void * static SPIPlanPtr
ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel, RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
bool cache_plan) bool cache_plan)
{ {
void *qplan; SPIPlanPtr qplan;
Relation query_rel; Relation query_rel;
Oid save_uid; Oid save_uid;
...@@ -3212,7 +3210,7 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, ...@@ -3212,7 +3210,7 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
* Perform a query to enforce an RI restriction * Perform a query to enforce an RI restriction
*/ */
static bool static bool
ri_PerformCheck(RI_QueryKey *qkey, void *qplan, ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
Relation fk_rel, Relation pk_rel, Relation fk_rel, Relation pk_rel,
HeapTuple old_tuple, HeapTuple new_tuple, HeapTuple old_tuple, HeapTuple new_tuple,
bool detectNewRows, bool detectNewRows,
...@@ -3576,7 +3574,7 @@ ri_InitHashTables(void) ...@@ -3576,7 +3574,7 @@ ri_InitHashTables(void)
* and saved SPI execution plans. Return the plan if found or NULL. * and saved SPI execution plans. Return the plan if found or NULL.
* ---------- * ----------
*/ */
static void * static SPIPlanPtr
ri_FetchPreparedPlan(RI_QueryKey *key) ri_FetchPreparedPlan(RI_QueryKey *key)
{ {
RI_QueryHashEntry *entry; RI_QueryHashEntry *entry;
...@@ -3606,7 +3604,7 @@ ri_FetchPreparedPlan(RI_QueryKey *key) ...@@ -3606,7 +3604,7 @@ ri_FetchPreparedPlan(RI_QueryKey *key)
* ---------- * ----------
*/ */
static void static void
ri_HashPreparedPlan(RI_QueryKey *key, void *plan) ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
{ {
RI_QueryHashEntry *entry; RI_QueryHashEntry *entry;
bool found; bool found;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.252 2007/02/27 23:48:08 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.253 2007/03/15 23:12:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -105,9 +105,9 @@ typedef struct ...@@ -105,9 +105,9 @@ typedef struct
* Global data * Global data
* ---------- * ----------
*/ */
static void *plan_getrulebyoid = NULL; static SPIPlanPtr plan_getrulebyoid = NULL;
static char *query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1"; static char *query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1";
static void *plan_getviewrule = NULL; static SPIPlanPtr plan_getviewrule = NULL;
static char *query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2"; static char *query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2";
...@@ -250,7 +250,7 @@ pg_get_ruledef_worker(Oid ruleoid, int prettyFlags) ...@@ -250,7 +250,7 @@ pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
if (plan_getrulebyoid == NULL) if (plan_getrulebyoid == NULL)
{ {
Oid argtypes[1]; Oid argtypes[1];
void *plan; SPIPlanPtr plan;
argtypes[0] = OIDOID; argtypes[0] = OIDOID;
plan = SPI_prepare(query_getrulebyoid, 1, argtypes); plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
...@@ -380,7 +380,7 @@ pg_get_viewdef_worker(Oid viewoid, int prettyFlags) ...@@ -380,7 +380,7 @@ pg_get_viewdef_worker(Oid viewoid, int prettyFlags)
if (plan_getviewrule == NULL) if (plan_getviewrule == NULL)
{ {
Oid argtypes[2]; Oid argtypes[2];
void *plan; SPIPlanPtr plan;
argtypes[0] = OIDOID; argtypes[0] = OIDOID;
argtypes[1] = NAMEOID; argtypes[1] = NAMEOID;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.34 2007/03/03 19:32:55 neilc Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.35 2007/03/15 23:12:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1800,7 +1800,7 @@ query_to_xmlschema(PG_FUNCTION_ARGS) ...@@ -1800,7 +1800,7 @@ query_to_xmlschema(PG_FUNCTION_ARGS)
const char *targetns = _textout(PG_GETARG_TEXT_P(3)); const char *targetns = _textout(PG_GETARG_TEXT_P(3));
const char *result; const char *result;
void *plan; SPIPlanPtr plan;
Portal portal; Portal portal;
SPI_connect(); SPI_connect();
...@@ -1871,7 +1871,7 @@ query_to_xml_and_xmlschema(PG_FUNCTION_ARGS) ...@@ -1871,7 +1871,7 @@ query_to_xml_and_xmlschema(PG_FUNCTION_ARGS)
const char *targetns = _textout(PG_GETARG_TEXT_P(3)); const char *targetns = _textout(PG_GETARG_TEXT_P(3));
const char *xmlschema; const char *xmlschema;
void *plan; SPIPlanPtr plan;
Portal portal; Portal portal;
SPI_connect(); SPI_connect();
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.1 2007/03/13 00:33:42 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.2 2007/03/15 23:12:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -77,7 +77,6 @@ static void ScanQueryForRelids(Query *parsetree, ...@@ -77,7 +77,6 @@ static void ScanQueryForRelids(Query *parsetree,
void *arg); void *arg);
static bool ScanQueryWalker(Node *node, ScanQueryWalkerContext *context); static bool ScanQueryWalker(Node *node, ScanQueryWalkerContext *context);
static bool rowmark_member(List *rowMarks, int rt_index); static bool rowmark_member(List *rowMarks, int rt_index);
static TupleDesc ComputeResultDesc(List *stmt_list);
static void PlanCacheCallback(Datum arg, Oid relid); static void PlanCacheCallback(Datum arg, Oid relid);
static void InvalRelid(Oid relid, LOCKMODE lockmode, static void InvalRelid(Oid relid, LOCKMODE lockmode,
InvalRelidContext *context); InvalRelidContext *context);
...@@ -153,7 +152,7 @@ CreateCachedPlan(Node *raw_parse_tree, ...@@ -153,7 +152,7 @@ CreateCachedPlan(Node *raw_parse_tree,
plansource->fully_planned = fully_planned; plansource->fully_planned = fully_planned;
plansource->fixed_result = fixed_result; plansource->fixed_result = fixed_result;
plansource->generation = 0; /* StoreCachedPlan will increment */ plansource->generation = 0; /* StoreCachedPlan will increment */
plansource->resultDesc = ComputeResultDesc(stmt_list); plansource->resultDesc = PlanCacheComputeResultDesc(stmt_list);
plansource->plan = NULL; plansource->plan = NULL;
plansource->context = source_context; plansource->context = source_context;
plansource->orig_plan = NULL; plansource->orig_plan = NULL;
...@@ -225,7 +224,7 @@ FastCreateCachedPlan(Node *raw_parse_tree, ...@@ -225,7 +224,7 @@ FastCreateCachedPlan(Node *raw_parse_tree,
plansource->fully_planned = fully_planned; plansource->fully_planned = fully_planned;
plansource->fixed_result = fixed_result; plansource->fixed_result = fixed_result;
plansource->generation = 0; /* StoreCachedPlan will increment */ plansource->generation = 0; /* StoreCachedPlan will increment */
plansource->resultDesc = ComputeResultDesc(stmt_list); plansource->resultDesc = PlanCacheComputeResultDesc(stmt_list);
plansource->plan = NULL; plansource->plan = NULL;
plansource->context = context; plansource->context = context;
plansource->orig_plan = NULL; plansource->orig_plan = NULL;
...@@ -271,12 +270,13 @@ StoreCachedPlan(CachedPlanSource *plansource, ...@@ -271,12 +270,13 @@ StoreCachedPlan(CachedPlanSource *plansource,
{ {
/* /*
* Make a dedicated memory context for the CachedPlan and its * Make a dedicated memory context for the CachedPlan and its
* subsidiary data. * subsidiary data. It's probably not going to be large, but
* just in case, use the default maxsize parameter.
*/ */
plan_context = AllocSetContextCreate(CacheMemoryContext, plan_context = AllocSetContextCreate(CacheMemoryContext,
"CachedPlan", "CachedPlan",
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_SMALL_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_SMALL_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE); ALLOCSET_DEFAULT_MAXSIZE);
/* /*
...@@ -445,7 +445,7 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner) ...@@ -445,7 +445,7 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
* Check or update the result tupdesc. XXX should we use a weaker * Check or update the result tupdesc. XXX should we use a weaker
* condition than equalTupleDescs() here? * condition than equalTupleDescs() here?
*/ */
resultDesc = ComputeResultDesc(slist); resultDesc = PlanCacheComputeResultDesc(slist);
if (resultDesc == NULL && plansource->resultDesc == NULL) if (resultDesc == NULL && plansource->resultDesc == NULL)
{ {
/* OK, doesn't return tuples */ /* OK, doesn't return tuples */
...@@ -718,14 +718,14 @@ rowmark_member(List *rowMarks, int rt_index) ...@@ -718,14 +718,14 @@ rowmark_member(List *rowMarks, int rt_index)
} }
/* /*
* ComputeResultDesc: given a list of either fully-planned statements or * PlanCacheComputeResultDesc: given a list of either fully-planned statements
* Queries, determine the result tupledesc it will produce. Returns NULL * or Queries, determine the result tupledesc it will produce. Returns NULL
* if the execution will not return tuples. * if the execution will not return tuples.
* *
* Note: the result is created or copied into current memory context. * Note: the result is created or copied into current memory context.
*/ */
static TupleDesc TupleDesc
ComputeResultDesc(List *stmt_list) PlanCacheComputeResultDesc(List *stmt_list)
{ {
Node *node; Node *node;
Query *query; Query *query;
......
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* spi.h * spi.h
* Server Programming Interface public declarations
* *
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.58 2006/10/04 00:30:08 momjian Exp $ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.59 2007/03/15 23:12:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -24,6 +28,7 @@ ...@@ -24,6 +28,7 @@
#include "catalog/pg_language.h" #include "catalog/pg_language.h"
#include "catalog/pg_proc.h" #include "catalog/pg_proc.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "executor/execdefs.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "nodes/execnodes.h" #include "nodes/execnodes.h"
#include "nodes/params.h" #include "nodes/params.h"
...@@ -38,9 +43,9 @@ ...@@ -38,9 +43,9 @@
#include "utils/datum.h" #include "utils/datum.h"
#include "utils/portal.h" #include "utils/portal.h"
#include "utils/syscache.h" #include "utils/syscache.h"
#include "executor/execdefs.h"
typedef struct
typedef struct SPITupleTable
{ {
MemoryContext tuptabcxt; /* memory context of result table */ MemoryContext tuptabcxt; /* memory context of result table */
uint32 alloced; /* # of alloced vals */ uint32 alloced; /* # of alloced vals */
...@@ -49,6 +54,9 @@ typedef struct ...@@ -49,6 +54,9 @@ typedef struct
HeapTuple *vals; /* tuples */ HeapTuple *vals; /* tuples */
} SPITupleTable; } SPITupleTable;
/* Plans are opaque structs for standard users of SPI */
typedef struct _SPI_plan *SPIPlanPtr;
#define SPI_ERROR_CONNECT (-1) #define SPI_ERROR_CONNECT (-1)
#define SPI_ERROR_COPY (-2) #define SPI_ERROR_COPY (-2)
#define SPI_ERROR_OPUNKNOWN (-3) #define SPI_ERROR_OPUNKNOWN (-3)
...@@ -86,23 +94,23 @@ extern void SPI_push(void); ...@@ -86,23 +94,23 @@ extern void SPI_push(void);
extern void SPI_pop(void); extern void SPI_pop(void);
extern void SPI_restore_connection(void); extern void SPI_restore_connection(void);
extern int SPI_execute(const char *src, bool read_only, long tcount); extern int SPI_execute(const char *src, bool read_only, long tcount);
extern int SPI_execute_plan(void *plan, Datum *Values, const char *Nulls, extern int SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
bool read_only, long tcount); bool read_only, long tcount);
extern int SPI_exec(const char *src, long tcount); extern int SPI_exec(const char *src, long tcount);
extern int SPI_execp(void *plan, Datum *Values, const char *Nulls, extern int SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls,
long tcount); long tcount);
extern int SPI_execute_snapshot(void *plan, extern int SPI_execute_snapshot(SPIPlanPtr plan,
Datum *Values, const char *Nulls, Datum *Values, const char *Nulls,
Snapshot snapshot, Snapshot snapshot,
Snapshot crosscheck_snapshot, Snapshot crosscheck_snapshot,
bool read_only, long tcount); bool read_only, long tcount);
extern void *SPI_prepare(const char *src, int nargs, Oid *argtypes); extern SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes);
extern void *SPI_saveplan(void *plan); extern SPIPlanPtr SPI_saveplan(SPIPlanPtr plan);
extern int SPI_freeplan(void *plan); extern int SPI_freeplan(SPIPlanPtr plan);
extern Oid SPI_getargtypeid(void *plan, int argIndex); extern Oid SPI_getargtypeid(SPIPlanPtr plan, int argIndex);
extern int SPI_getargcount(void *plan); extern int SPI_getargcount(SPIPlanPtr plan);
extern bool SPI_is_cursor_plan(void *plan); extern bool SPI_is_cursor_plan(SPIPlanPtr plan);
extern const char *SPI_result_code_string(int code); extern const char *SPI_result_code_string(int code);
extern HeapTuple SPI_copytuple(HeapTuple tuple); extern HeapTuple SPI_copytuple(HeapTuple tuple);
...@@ -123,7 +131,7 @@ extern void SPI_pfree(void *pointer); ...@@ -123,7 +131,7 @@ extern void SPI_pfree(void *pointer);
extern void SPI_freetuple(HeapTuple pointer); extern void SPI_freetuple(HeapTuple pointer);
extern void SPI_freetuptable(SPITupleTable *tuptable); extern void SPI_freetuptable(SPITupleTable *tuptable);
extern Portal SPI_cursor_open(const char *name, void *plan, extern Portal SPI_cursor_open(const char *name, SPIPlanPtr plan,
Datum *Values, const char *Nulls, bool read_only); Datum *Values, const char *Nulls, bool read_only);
extern Portal SPI_cursor_find(const char *name); extern Portal SPI_cursor_find(const char *name);
extern void SPI_cursor_fetch(Portal portal, bool forward, long count); extern void SPI_cursor_fetch(Portal portal, bool forward, long count);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.27 2007/02/20 17:32:17 tgl Exp $ * $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.28 2007/03/15 23:12:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include "executor/spi.h" #include "executor/spi.h"
#define _SPI_PLAN_MAGIC 569278163
typedef struct typedef struct
{ {
/* current results */ /* current results */
...@@ -25,29 +27,46 @@ typedef struct ...@@ -25,29 +27,46 @@ typedef struct
MemoryContext procCxt; /* procedure context */ MemoryContext procCxt; /* procedure context */
MemoryContext execCxt; /* executor context */ MemoryContext execCxt; /* executor context */
MemoryContext savedcxt; MemoryContext savedcxt; /* context of SPI_connect's caller */
SubTransactionId connectSubid; /* ID of connecting subtransaction */ SubTransactionId connectSubid; /* ID of connecting subtransaction */
} _SPI_connection; } _SPI_connection;
typedef struct /*
* SPI plans have two states: saved or unsaved.
*
* For an unsaved plan, the _SPI_plan struct and all its subsidiary data are in
* a dedicated memory context identified by plancxt. An unsaved plan is good
* at most for the current transaction, since the locks that protect it from
* schema changes will be lost at end of transaction. Hence the plancxt is
* always a transient one.
*
* For a saved plan, the _SPI_plan struct and the argument type array are in
* the plancxt (which can be really small). All the other subsidiary state
* is in plancache entries identified by plancache_list (note: the list cells
* themselves are in plancxt). We rely on plancache.c to keep the cache
* entries up-to-date as needed. The plancxt is a child of CacheMemoryContext
* since it should persist until explicitly destroyed.
*
* To avoid redundant coding, the representation of unsaved plans matches
* that of saved plans, ie, plancache_list is a list of CachedPlanSource
* structs which in turn point to CachedPlan structs. However, in an unsaved
* plan all these structs are just created by spi.c and are not known to
* plancache.c. We don't try very hard to make all their fields valid,
* only the ones spi.c actually uses.
*
* Note: if the original query string contained only whitespace and comments,
* the plancache_list will be NIL and so there is no place to store the
* query string. We don't care about that, but we do care about the
* argument type array, which is why it's seemingly-redundantly stored.
*/
typedef struct _SPI_plan
{ {
/* Context containing _SPI_plan itself as well as subsidiary data */ int magic; /* should equal _SPI_PLAN_MAGIC */
MemoryContext plancxt; bool saved; /* saved or unsaved plan? */
/* Original query string (used for error reporting) */ List *plancache_list; /* one CachedPlanSource per parsetree */
const char *query; MemoryContext plancxt; /* Context containing _SPI_plan and data */
/* int nargs; /* number of plan arguments */
* List of List of PlannedStmts and utility stmts; one sublist per Oid *argtypes; /* Argument types (NULL if nargs is 0) */
* original parsetree
*/
List *stmt_list_list;
/* Argument types, if a prepared plan */
int nargs;
Oid *argtypes;
} _SPI_plan; } _SPI_plan;
#define _SPI_CPLAN_CURCXT 0
#define _SPI_CPLAN_PROCXT 1
#define _SPI_CPLAN_TOPCXT 2
#endif /* SPI_PRIV_H */ #endif /* SPI_PRIV_H */
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.1 2007/03/13 00:33:43 tgl Exp $ * $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.2 2007/03/15 23:12:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -101,5 +101,6 @@ extern void DropCachedPlan(CachedPlanSource *plansource); ...@@ -101,5 +101,6 @@ extern void DropCachedPlan(CachedPlanSource *plansource);
extern CachedPlan *RevalidateCachedPlan(CachedPlanSource *plansource, extern CachedPlan *RevalidateCachedPlan(CachedPlanSource *plansource,
bool useResOwner); bool useResOwner);
extern void ReleaseCachedPlan(CachedPlan *plan, bool useResOwner); extern void ReleaseCachedPlan(CachedPlan *plan, bool useResOwner);
extern TupleDesc PlanCacheComputeResultDesc(List *stmt_list);
#endif /* PLANCACHE_H */ #endif /* PLANCACHE_H */
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.189 2007/02/20 17:32:18 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.190 2007/03/15 23:12:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -123,8 +123,9 @@ static void exec_prepare_plan(PLpgSQL_execstate *estate, ...@@ -123,8 +123,9 @@ static void exec_prepare_plan(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr); PLpgSQL_expr *expr);
static bool exec_simple_check_node(Node *node); static bool exec_simple_check_node(Node *node);
static void exec_simple_check_plan(PLpgSQL_expr *expr); static void exec_simple_check_plan(PLpgSQL_expr *expr);
static Datum exec_eval_simple_expr(PLpgSQL_execstate *estate, static bool exec_eval_simple_expr(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr, PLpgSQL_expr *expr,
Datum *result,
bool *isNull, bool *isNull,
Oid *rettype); Oid *rettype);
...@@ -409,7 +410,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo) ...@@ -409,7 +410,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
void *tmp; void *tmp;
len = datumGetSize(estate.retval, false, func->fn_rettyplen); len = datumGetSize(estate.retval, false, func->fn_rettyplen);
tmp = (void *) SPI_palloc(len); tmp = SPI_palloc(len);
memcpy(tmp, DatumGetPointer(estate.retval), len); memcpy(tmp, DatumGetPointer(estate.retval), len);
estate.retval = PointerGetDatum(tmp); estate.retval = PointerGetDatum(tmp);
} }
...@@ -2294,8 +2295,7 @@ exec_prepare_plan(PLpgSQL_execstate *estate, ...@@ -2294,8 +2295,7 @@ exec_prepare_plan(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr) PLpgSQL_expr *expr)
{ {
int i; int i;
_SPI_plan *spi_plan; SPIPlanPtr plan;
void *plan;
Oid *argtypes; Oid *argtypes;
/* /*
...@@ -2343,12 +2343,11 @@ exec_prepare_plan(PLpgSQL_execstate *estate, ...@@ -2343,12 +2343,11 @@ exec_prepare_plan(PLpgSQL_execstate *estate,
} }
} }
expr->plan = SPI_saveplan(plan); expr->plan = SPI_saveplan(plan);
spi_plan = (_SPI_plan *) expr->plan; SPI_freeplan(plan);
expr->plan_argtypes = spi_plan->argtypes; plan = expr->plan;
expr->expr_simple_expr = NULL; expr->plan_argtypes = plan->argtypes;
exec_simple_check_plan(expr); exec_simple_check_plan(expr);
SPI_freeplan(plan);
pfree(argtypes); pfree(argtypes);
} }
...@@ -2374,17 +2373,16 @@ exec_stmt_execsql(PLpgSQL_execstate *estate, ...@@ -2374,17 +2373,16 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
*/ */
if (expr->plan == NULL) if (expr->plan == NULL)
{ {
_SPI_plan *spi_plan;
ListCell *l; ListCell *l;
exec_prepare_plan(estate, expr); exec_prepare_plan(estate, expr);
stmt->mod_stmt = false; stmt->mod_stmt = false;
spi_plan = (_SPI_plan *) expr->plan; foreach(l, expr->plan->plancache_list)
foreach(l, spi_plan->stmt_list_list)
{ {
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(l);
ListCell *l2; ListCell *l2;
foreach(l2, (List *) lfirst(l)) foreach(l2, plansource->plan->stmt_list)
{ {
PlannedStmt *p = (PlannedStmt *) lfirst(l2); PlannedStmt *p = (PlannedStmt *) lfirst(l2);
...@@ -2735,7 +2733,7 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt) ...@@ -2735,7 +2733,7 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt)
PLpgSQL_row *row = NULL; PLpgSQL_row *row = NULL;
SPITupleTable *tuptab; SPITupleTable *tuptab;
int n; int n;
void *plan; SPIPlanPtr plan;
Portal portal; Portal portal;
bool found = false; bool found = false;
...@@ -2959,7 +2957,7 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt) ...@@ -2959,7 +2957,7 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
Datum queryD; Datum queryD;
Oid restype; Oid restype;
char *querystr; char *querystr;
void *curplan; SPIPlanPtr curplan;
/* ---------- /* ----------
* We evaluate the string expression after the * We evaluate the string expression after the
...@@ -3860,10 +3858,11 @@ exec_eval_expr(PLpgSQL_execstate *estate, ...@@ -3860,10 +3858,11 @@ exec_eval_expr(PLpgSQL_execstate *estate,
bool *isNull, bool *isNull,
Oid *rettype) Oid *rettype)
{ {
Datum result;
int rc; int rc;
/* /*
* If not already done create a plan for this expression * If first time through, create a plan for this expression.
*/ */
if (expr->plan == NULL) if (expr->plan == NULL)
exec_prepare_plan(estate, expr); exec_prepare_plan(estate, expr);
...@@ -3872,9 +3871,12 @@ exec_eval_expr(PLpgSQL_execstate *estate, ...@@ -3872,9 +3871,12 @@ exec_eval_expr(PLpgSQL_execstate *estate,
* If this is a simple expression, bypass SPI and use the executor * If this is a simple expression, bypass SPI and use the executor
* directly * directly
*/ */
if (expr->expr_simple_expr != NULL) if (exec_eval_simple_expr(estate, expr, &result, isNull, rettype))
return exec_eval_simple_expr(estate, expr, isNull, rettype); return result;
/*
* Else do it the hard way via exec_run_select
*/
rc = exec_run_select(estate, expr, 2, NULL); rc = exec_run_select(estate, expr, 2, NULL);
if (rc != SPI_OK_SELECT) if (rc != SPI_OK_SELECT)
ereport(ERROR, ereport(ERROR,
...@@ -3994,23 +3996,64 @@ exec_run_select(PLpgSQL_execstate *estate, ...@@ -3994,23 +3996,64 @@ exec_run_select(PLpgSQL_execstate *estate,
* exec_eval_simple_expr - Evaluate a simple expression returning * exec_eval_simple_expr - Evaluate a simple expression returning
* a Datum by directly calling ExecEvalExpr(). * a Datum by directly calling ExecEvalExpr().
* *
* If successful, store results into *result, *isNull, *rettype and return
* TRUE. If the expression is not simple (any more), return FALSE.
*
* It is possible though unlikely for a simple expression to become non-simple
* (consider for example redefining a trivial view). We must handle that for
* correctness; fortunately it's normally inexpensive to do
* RevalidateCachedPlan on a simple expression. We do not consider the other
* direction (non-simple expression becoming simple) because we'll still give
* correct results if that happens, and it's unlikely to be worth the cycles
* to check.
*
* Note: if pass-by-reference, the result is in the eval_econtext's * Note: if pass-by-reference, the result is in the eval_econtext's
* temporary memory context. It will be freed when exec_eval_cleanup * temporary memory context. It will be freed when exec_eval_cleanup
* is done. * is done.
* ---------- * ----------
*/ */
static Datum static bool
exec_eval_simple_expr(PLpgSQL_execstate *estate, exec_eval_simple_expr(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr, PLpgSQL_expr *expr,
Datum *result,
bool *isNull, bool *isNull,
Oid *rettype) Oid *rettype)
{ {
Datum retval;
ExprContext *econtext = estate->eval_econtext; ExprContext *econtext = estate->eval_econtext;
CachedPlanSource *plansource;
CachedPlan *cplan;
ParamListInfo paramLI; ParamListInfo paramLI;
int i; int i;
Snapshot saveActiveSnapshot; Snapshot saveActiveSnapshot;
/*
* Forget it if expression wasn't simple before.
*/
if (expr->expr_simple_expr == NULL)
return false;
/*
* Revalidate cached plan, so that we will notice if it became stale.
* (We also need to hold a refcount while using the plan.) Note that
* even if replanning occurs, the length of plancache_list can't change,
* since it is a property of the raw parsetree generated from the query
* text.
*/
Assert(list_length(expr->plan->plancache_list) == 1);
plansource = (CachedPlanSource *) linitial(expr->plan->plancache_list);
cplan = RevalidateCachedPlan(plansource, true);
if (cplan->generation != expr->expr_simple_generation)
{
/* It got replanned ... is it still simple? */
exec_simple_check_plan(expr);
if (expr->expr_simple_expr == NULL)
{
/* Ooops, release refcount and fail */
ReleaseCachedPlan(cplan, true);
return false;
}
}
/* /*
* Pass back previously-determined result type. * Pass back previously-determined result type.
*/ */
...@@ -4018,7 +4061,8 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate, ...@@ -4018,7 +4061,8 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
/* /*
* Prepare the expression for execution, if it's not been done already in * Prepare the expression for execution, if it's not been done already in
* the current eval_estate. * the current eval_estate. (This will be forced to happen if we called
* exec_simple_check_plan above.)
*/ */
if (expr->expr_simple_id != estate->eval_estate_simple_id) if (expr->expr_simple_id != estate->eval_estate_simple_id)
{ {
...@@ -4086,10 +4130,10 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate, ...@@ -4086,10 +4130,10 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
/* /*
* Finally we can call the executor to evaluate the expression * Finally we can call the executor to evaluate the expression
*/ */
retval = ExecEvalExpr(expr->expr_simple_state, *result = ExecEvalExpr(expr->expr_simple_state,
econtext, econtext,
isNull, isNull,
NULL); NULL);
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
} }
PG_CATCH(); PG_CATCH();
...@@ -4103,10 +4147,15 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate, ...@@ -4103,10 +4147,15 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
ActiveSnapshot = saveActiveSnapshot; ActiveSnapshot = saveActiveSnapshot;
SPI_pop(); SPI_pop();
/*
* Now we can release our refcount on the cached plan.
*/
ReleaseCachedPlan(cplan, true);
/* /*
* That's it. * That's it.
*/ */
return retval; return true;
} }
...@@ -4673,25 +4722,31 @@ exec_simple_check_node(Node *node) ...@@ -4673,25 +4722,31 @@ exec_simple_check_node(Node *node)
static void static void
exec_simple_check_plan(PLpgSQL_expr *expr) exec_simple_check_plan(PLpgSQL_expr *expr)
{ {
_SPI_plan *spi_plan = (_SPI_plan *) expr->plan; CachedPlanSource *plansource;
List *sublist;
PlannedStmt *stmt; PlannedStmt *stmt;
Plan *plan; Plan *plan;
TargetEntry *tle; TargetEntry *tle;
/*
* Initialize to "not simple", and remember the plan generation number
* we last checked. (If the query produces more or less than one parsetree
* we just leave expr_simple_generation set to 0.)
*/
expr->expr_simple_expr = NULL; expr->expr_simple_expr = NULL;
expr->expr_simple_generation = 0;
/* /*
* 1. We can only evaluate queries that resulted in one single execution * 1. We can only evaluate queries that resulted in one single execution
* plan * plan
*/ */
if (list_length(spi_plan->stmt_list_list) != 1) if (list_length(expr->plan->plancache_list) != 1)
return; return;
sublist = (List *) linitial(spi_plan->stmt_list_list); plansource = (CachedPlanSource *) linitial(expr->plan->plancache_list);
if (list_length(sublist) != 1) expr->expr_simple_generation = plansource->generation;
if (list_length(plansource->plan->stmt_list) != 1)
return; return;
stmt = (PlannedStmt *) linitial(sublist); stmt = (PlannedStmt *) linitial(plansource->plan->stmt_list);
/* /*
* 2. It must be a RESULT plan --> no scan's required * 2. It must be a RESULT plan --> no scan's required
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.85 2007/02/09 03:35:34 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.86 2007/03/15 23:12:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -173,11 +173,12 @@ typedef struct PLpgSQL_expr ...@@ -173,11 +173,12 @@ typedef struct PLpgSQL_expr
int dtype; int dtype;
int exprno; int exprno;
char *query; char *query;
void *plan; SPIPlanPtr plan;
Oid *plan_argtypes; Oid *plan_argtypes;
/* fields for "simple expression" fast-path execution: */ /* fields for "simple expression" fast-path execution: */
Expr *expr_simple_expr; /* NULL means not a simple expr */ Expr *expr_simple_expr; /* NULL means not a simple expr */
Oid expr_simple_type; int expr_simple_generation; /* plancache generation we checked */
Oid expr_simple_type; /* result type Oid, if simple */
/* /*
* if expr is simple AND prepared in current eval_estate, * if expr is simple AND prepared in current eval_estate,
......
...@@ -100,3 +100,64 @@ EXECUTE vprep; ...@@ -100,3 +100,64 @@ EXECUTE vprep;
4567890123456789 | 2283945061728394 4567890123456789 | 2283945061728394
(5 rows) (5 rows)
-- Check basic SPI plan invalidation
create function cache_test(int) returns int as $$
declare total int;
begin
create temp table t1(f1 int);
insert into t1 values($1);
insert into t1 values(11);
insert into t1 values(12);
insert into t1 values(13);
select sum(f1) into total from t1;
drop table t1;
return total;
end
$$ language plpgsql;
select cache_test(1);
cache_test
------------
37
(1 row)
select cache_test(2);
cache_test
------------
38
(1 row)
select cache_test(3);
cache_test
------------
39
(1 row)
-- Check invalidation of plpgsql "simple expression"
create temp view v1 as
select 2+2 as f1;
create function cache_test_2() returns int as $$
begin
return f1 from v1;
end$$ language plpgsql;
select cache_test_2();
cache_test_2
--------------
4
(1 row)
create or replace temp view v1 as
select 2+2+4 as f1;
select cache_test_2();
cache_test_2
--------------
8
(1 row)
create or replace temp view v1 as
select 2+2+4+(select max(unique1) from tenk1) as f1;
select cache_test_2();
cache_test_2
--------------
10007
(1 row)
/* /*
* $PostgreSQL: pgsql/src/test/regress/regress.c,v 1.69 2007/02/01 19:10:30 momjian Exp $ * $PostgreSQL: pgsql/src/test/regress/regress.c,v 1.70 2007/03/15 23:12:07 tgl Exp $
*/ */
#include "postgres.h" #include "postgres.h"
...@@ -451,7 +451,7 @@ extern Datum set_ttdummy(PG_FUNCTION_ARGS); ...@@ -451,7 +451,7 @@ extern Datum set_ttdummy(PG_FUNCTION_ARGS);
#define TTDUMMY_INFINITY 999999 #define TTDUMMY_INFINITY 999999
static void *splan = NULL; static SPIPlanPtr splan = NULL;
static bool ttoff = false; static bool ttoff = false;
PG_FUNCTION_INFO_V1(ttdummy); PG_FUNCTION_INFO_V1(ttdummy);
...@@ -599,7 +599,7 @@ ttdummy(PG_FUNCTION_ARGS) ...@@ -599,7 +599,7 @@ ttdummy(PG_FUNCTION_ARGS)
/* if there is no plan ... */ /* if there is no plan ... */
if (splan == NULL) if (splan == NULL)
{ {
void *pplan; SPIPlanPtr pplan;
Oid *ctypes; Oid *ctypes;
char *query; char *query;
......
...@@ -51,3 +51,43 @@ EXECUTE vprep; ...@@ -51,3 +51,43 @@ EXECUTE vprep;
CREATE OR REPLACE TEMP VIEW voo AS SELECT q1, q2/2 AS q2 FROM foo; CREATE OR REPLACE TEMP VIEW voo AS SELECT q1, q2/2 AS q2 FROM foo;
EXECUTE vprep; EXECUTE vprep;
-- Check basic SPI plan invalidation
create function cache_test(int) returns int as $$
declare total int;
begin
create temp table t1(f1 int);
insert into t1 values($1);
insert into t1 values(11);
insert into t1 values(12);
insert into t1 values(13);
select sum(f1) into total from t1;
drop table t1;
return total;
end
$$ language plpgsql;
select cache_test(1);
select cache_test(2);
select cache_test(3);
-- Check invalidation of plpgsql "simple expression"
create temp view v1 as
select 2+2 as f1;
create function cache_test_2() returns int as $$
begin
return f1 from v1;
end$$ language plpgsql;
select cache_test_2();
create or replace temp view v1 as
select 2+2+4 as f1;
select cache_test_2();
create or replace temp view v1 as
select 2+2+4+(select max(unique1) from tenk1) as f1;
select cache_test_2();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment