aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2007-03-15 23:12:07 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2007-03-15 23:12:07 +0000
commit95f6d2d20921b7c2dbec29bf2706fd9448208aa6 (patch)
tree21dcb36f9df60546d82d547a7855605be73a771c /src/backend/executor
parentd3ff180163a0c88d7a05e0c865f649e5d8bcd6e1 (diff)
downloadpostgresql-95f6d2d20921b7c2dbec29bf2706fd9448208aa6.tar.gz
postgresql-95f6d2d20921b7c2dbec29bf2706fd9448208aa6.zip
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.)
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/spi.c402
1 files changed, 282 insertions, 120 deletions
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index f538f508ba3..7a34add7105 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -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 = &paramLI->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;
-
- query_list = pg_analyze_and_rewrite(parsetree, src, argtypes, nargs);
-
- stmt_list_list = lappend(stmt_list_list,
- pg_plan_queries(query_list, NULL, false));
+ List *stmt_list;
+ CachedPlanSource *plansource;
+ CachedPlan *cplan;
+
+ /* 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);
+
+ 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;