1 /*-------------------------------------------------------------------------
4 * Plan cache management.
6 * We can store a cached plan in either fully-planned format, or just
7 * parsed-and-rewritten if the caller wishes to postpone planning until
8 * actual parameter values are available. CachedPlanSource has the same
9 * contents either way, but CachedPlan contains a list of PlannedStmts
10 * and bare utility statements in the first case, or a list of Query nodes
13 * The plan cache manager itself is principally responsible for tracking
14 * whether cached plans should be invalidated because of schema changes in
15 * the tables they depend on. When (and if) the next demand for a cached
16 * plan occurs, the query will be replanned. Note that this could result
17 * in an error, for example if a column referenced by the query is no
18 * longer present. The creator of a cached plan can specify whether it
19 * is allowable for the query to change output tupdesc on replan (this
20 * could happen with "SELECT *" for example) --- if so, it's up to the
21 * caller to notice changes and cope with them.
23 * Currently, we use only relcache invalidation events to invalidate plans.
24 * This means that changes such as modification of a function definition do
25 * not invalidate plans using the function. This is not 100% OK --- for
26 * example, changing a SQL function that's been inlined really ought to
27 * cause invalidation of the plan that it's been inlined into --- but the
28 * cost of tracking additional types of object seems much higher than the
29 * gain, so we're just ignoring them for now.
32 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
33 * Portions Copyright (c) 1994, Regents of the University of California
38 *-------------------------------------------------------------------------
42 #include "utils/plancache.h"
43 #include "access/transam.h"
44 #include "catalog/namespace.h"
45 #include "executor/executor.h"
46 #include "optimizer/clauses.h"
47 #include "storage/lmgr.h"
48 #include "tcop/pquery.h"
49 #include "tcop/tcopprot.h"
50 #include "tcop/utility.h"
51 #include "utils/inval.h"
52 #include "utils/memutils.h"
53 #include "utils/resowner.h"
54 #include "utils/snapmgmt.h"
61 } ScanQueryWalkerContext
;
70 static List
*cached_plans_list
= NIL
;
72 static void StoreCachedPlan(CachedPlanSource
*plansource
, List
*stmt_list
,
73 MemoryContext plan_context
);
74 static List
*do_planning(List
*querytrees
, int cursorOptions
);
75 static void AcquireExecutorLocks(List
*stmt_list
, bool acquire
);
76 static void AcquirePlannerLocks(List
*stmt_list
, bool acquire
);
77 static void LockRelid(Oid relid
, LOCKMODE lockmode
, void *arg
);
78 static void UnlockRelid(Oid relid
, LOCKMODE lockmode
, void *arg
);
79 static void ScanQueryForRelids(Query
*parsetree
,
82 static bool ScanQueryWalker(Node
*node
, ScanQueryWalkerContext
*context
);
83 static bool rowmark_member(List
*rowMarks
, int rt_index
);
84 static bool plan_list_is_transient(List
*stmt_list
);
85 static void PlanCacheCallback(Datum arg
, Oid relid
);
86 static void InvalRelid(Oid relid
, LOCKMODE lockmode
,
87 InvalRelidContext
*context
);
91 * InitPlanCache: initialize module during InitPostgres.
93 * All we need to do is hook into inval.c's callback list.
98 CacheRegisterRelcacheCallback(PlanCacheCallback
, (Datum
) 0);
102 * CreateCachedPlan: initially create a plan cache entry.
104 * The caller must already have successfully parsed/planned the query;
105 * about all that we do here is copy it into permanent storage.
107 * raw_parse_tree: output of raw_parser()
108 * query_string: original query text (can be NULL if not available, but
109 * that is discouraged because it degrades error message quality)
110 * commandTag: compile-time-constant tag for query, or NULL if empty query
111 * param_types: array of parameter type OIDs, or NULL if none
112 * num_params: number of parameters
113 * cursor_options: options bitmask that was/will be passed to planner
114 * stmt_list: list of PlannedStmts/utility stmts, or list of Query trees
115 * fully_planned: are we caching planner or rewriter output?
116 * fixed_result: TRUE to disallow changes in result tupdesc
119 CreateCachedPlan(Node
*raw_parse_tree
,
120 const char *query_string
,
121 const char *commandTag
,
129 CachedPlanSource
*plansource
;
130 OverrideSearchPath
*search_path
;
131 MemoryContext source_context
;
132 MemoryContext oldcxt
;
135 * Make a dedicated memory context for the CachedPlanSource and its
136 * subsidiary data. We expect it can be pretty small.
138 source_context
= AllocSetContextCreate(CacheMemoryContext
,
140 ALLOCSET_SMALL_MINSIZE
,
141 ALLOCSET_SMALL_INITSIZE
,
142 ALLOCSET_SMALL_MAXSIZE
);
145 * Fetch current search_path into new context, but do any recalculation
146 * work required in caller's context.
148 search_path
= GetOverrideSearchPath(source_context
);
151 * Create and fill the CachedPlanSource struct within the new context.
153 oldcxt
= MemoryContextSwitchTo(source_context
);
154 plansource
= (CachedPlanSource
*) palloc(sizeof(CachedPlanSource
));
155 plansource
->raw_parse_tree
= copyObject(raw_parse_tree
);
156 plansource
->query_string
= query_string
? pstrdup(query_string
) : NULL
;
157 plansource
->commandTag
= commandTag
; /* no copying needed */
160 plansource
->param_types
= (Oid
*) palloc(num_params
* sizeof(Oid
));
161 memcpy(plansource
->param_types
, param_types
, num_params
* sizeof(Oid
));
164 plansource
->param_types
= NULL
;
165 plansource
->num_params
= num_params
;
166 plansource
->cursor_options
= cursor_options
;
167 plansource
->fully_planned
= fully_planned
;
168 plansource
->fixed_result
= fixed_result
;
169 plansource
->search_path
= search_path
;
170 plansource
->generation
= 0; /* StoreCachedPlan will increment */
171 plansource
->resultDesc
= PlanCacheComputeResultDesc(stmt_list
);
172 plansource
->plan
= NULL
;
173 plansource
->context
= source_context
;
174 plansource
->orig_plan
= NULL
;
177 * Copy the current output plans into the plancache entry.
179 StoreCachedPlan(plansource
, stmt_list
, NULL
);
182 * Now we can add the entry to the list of cached plans. The List nodes
183 * live in CacheMemoryContext.
185 MemoryContextSwitchTo(CacheMemoryContext
);
187 cached_plans_list
= lappend(cached_plans_list
, plansource
);
189 MemoryContextSwitchTo(oldcxt
);
195 * FastCreateCachedPlan: create a plan cache entry with minimal data copying.
197 * For plans that aren't expected to live very long, the copying overhead of
198 * CreateCachedPlan is annoying. We provide this variant entry point in which
199 * the caller has already placed all the data in a suitable memory context.
200 * The source data and completed plan are in the same context, since this
201 * avoids extra copy steps during plan construction. If the query ever does
202 * need replanning, we'll generate a separate new CachedPlan at that time, but
203 * the CachedPlanSource and the initial CachedPlan share the caller-provided
204 * context and go away together when neither is needed any longer. (Because
205 * the parser and planner generate extra cruft in addition to their real
206 * output, this approach means that the context probably contains a bunch of
207 * useless junk as well as the useful trees. Hence, this method is a
208 * space-for-time tradeoff, which is worth making for plans expected to be
211 * raw_parse_tree, query_string, param_types, and stmt_list must reside in the
212 * given context, which must have adequate lifespan (recommendation: make it a
213 * child of CacheMemoryContext). Otherwise the API is the same as
217 FastCreateCachedPlan(Node
*raw_parse_tree
,
219 const char *commandTag
,
226 MemoryContext context
)
228 CachedPlanSource
*plansource
;
229 OverrideSearchPath
*search_path
;
230 MemoryContext oldcxt
;
233 * Fetch current search_path into given context, but do any recalculation
234 * work required in caller's context.
236 search_path
= GetOverrideSearchPath(context
);
239 * Create and fill the CachedPlanSource struct within the given context.
241 oldcxt
= MemoryContextSwitchTo(context
);
242 plansource
= (CachedPlanSource
*) palloc(sizeof(CachedPlanSource
));
243 plansource
->raw_parse_tree
= raw_parse_tree
;
244 plansource
->query_string
= query_string
;
245 plansource
->commandTag
= commandTag
; /* no copying needed */
246 plansource
->param_types
= param_types
;
247 plansource
->num_params
= num_params
;
248 plansource
->cursor_options
= cursor_options
;
249 plansource
->fully_planned
= fully_planned
;
250 plansource
->fixed_result
= fixed_result
;
251 plansource
->search_path
= search_path
;
252 plansource
->generation
= 0; /* StoreCachedPlan will increment */
253 plansource
->resultDesc
= PlanCacheComputeResultDesc(stmt_list
);
254 plansource
->plan
= NULL
;
255 plansource
->context
= context
;
256 plansource
->orig_plan
= NULL
;
259 * Store the current output plans into the plancache entry.
261 StoreCachedPlan(plansource
, stmt_list
, context
);
264 * Since the context is owned by the CachedPlan, advance its refcount.
266 plansource
->orig_plan
= plansource
->plan
;
267 plansource
->orig_plan
->refcount
++;
270 * Now we can add the entry to the list of cached plans. The List nodes
271 * live in CacheMemoryContext.
273 MemoryContextSwitchTo(CacheMemoryContext
);
275 cached_plans_list
= lappend(cached_plans_list
, plansource
);
277 MemoryContextSwitchTo(oldcxt
);
283 * StoreCachedPlan: store a built or rebuilt plan into a plancache entry.
285 * Common subroutine for CreateCachedPlan and RevalidateCachedPlan.
288 StoreCachedPlan(CachedPlanSource
*plansource
,
290 MemoryContext plan_context
)
293 MemoryContext oldcxt
;
295 if (plan_context
== NULL
)
298 * Make a dedicated memory context for the CachedPlan and its
299 * subsidiary data. It's probably not going to be large, but just in
300 * case, use the default maxsize parameter.
302 plan_context
= AllocSetContextCreate(CacheMemoryContext
,
304 ALLOCSET_SMALL_MINSIZE
,
305 ALLOCSET_SMALL_INITSIZE
,
306 ALLOCSET_DEFAULT_MAXSIZE
);
309 * Copy supplied data into the new context.
311 oldcxt
= MemoryContextSwitchTo(plan_context
);
313 stmt_list
= (List
*) copyObject(stmt_list
);
317 /* Assume subsidiary data is in the given context */
318 oldcxt
= MemoryContextSwitchTo(plan_context
);
322 * Create and fill the CachedPlan struct within the new context.
324 plan
= (CachedPlan
*) palloc(sizeof(CachedPlan
));
325 plan
->stmt_list
= stmt_list
;
326 plan
->fully_planned
= plansource
->fully_planned
;
328 if (plansource
->fully_planned
&& plan_list_is_transient(stmt_list
))
330 Assert(TransactionIdIsNormal(TransactionXmin
));
331 plan
->saved_xmin
= TransactionXmin
;
334 plan
->saved_xmin
= InvalidTransactionId
;
335 plan
->refcount
= 1; /* for the parent's link */
336 plan
->generation
= ++(plansource
->generation
);
337 plan
->context
= plan_context
;
339 Assert(plansource
->plan
== NULL
);
340 plansource
->plan
= plan
;
342 MemoryContextSwitchTo(oldcxt
);
346 * DropCachedPlan: destroy a cached plan.
348 * Actually this only destroys the CachedPlanSource: the referenced CachedPlan
349 * is released, but not destroyed until its refcount goes to zero. That
350 * handles the situation where DropCachedPlan is called while the plan is
354 DropCachedPlan(CachedPlanSource
*plansource
)
356 /* Validity check that we were given a CachedPlanSource */
357 Assert(list_member_ptr(cached_plans_list
, plansource
));
359 /* Remove it from the list */
360 cached_plans_list
= list_delete_ptr(cached_plans_list
, plansource
);
362 /* Decrement child CachePlan's refcount and drop if no longer needed */
363 if (plansource
->plan
)
364 ReleaseCachedPlan(plansource
->plan
, false);
367 * If CachedPlanSource has independent storage, just drop it. Otherwise
368 * decrement the refcount on the CachePlan that owns the storage.
370 if (plansource
->orig_plan
== NULL
)
372 /* Remove the CachedPlanSource and all subsidiary data */
373 MemoryContextDelete(plansource
->context
);
377 Assert(plansource
->context
== plansource
->orig_plan
->context
);
378 ReleaseCachedPlan(plansource
->orig_plan
, false);
383 * RevalidateCachedPlan: prepare for re-use of a previously cached plan.
385 * What we do here is re-acquire locks and rebuild the plan if necessary.
386 * On return, the plan is valid and we have sufficient locks to begin
387 * execution (or planning, if not fully_planned).
389 * On return, the refcount of the plan has been incremented; a later
390 * ReleaseCachedPlan() call is expected. The refcount has been reported
391 * to the CurrentResourceOwner if useResOwner is true.
393 * Note: if any replanning activity is required, the caller's memory context
394 * is used for that work.
397 RevalidateCachedPlan(CachedPlanSource
*plansource
, bool useResOwner
)
401 /* Validity check that we were given a CachedPlanSource */
402 Assert(list_member_ptr(cached_plans_list
, plansource
));
405 * If the plan currently appears valid, acquire locks on the referenced
406 * objects; then check again. We need to do it this way to cover the race
407 * condition that an invalidation message arrives before we get the lock.
409 plan
= plansource
->plan
;
410 if (plan
&& !plan
->dead
)
413 * Plan must have positive refcount because it is referenced by
414 * plansource; so no need to fear it disappears under us here.
416 Assert(plan
->refcount
> 0);
418 if (plan
->fully_planned
)
419 AcquireExecutorLocks(plan
->stmt_list
, true);
421 AcquirePlannerLocks(plan
->stmt_list
, true);
424 * If plan was transient, check to see if TransactionXmin has
425 * advanced, and if so invalidate it.
428 TransactionIdIsValid(plan
->saved_xmin
) &&
429 !TransactionIdEquals(plan
->saved_xmin
, TransactionXmin
))
433 * By now, if any invalidation has happened, PlanCacheCallback will
434 * have marked the plan dead.
438 /* Ooops, the race case happened. Release useless locks. */
439 if (plan
->fully_planned
)
440 AcquireExecutorLocks(plan
->stmt_list
, false);
442 AcquirePlannerLocks(plan
->stmt_list
, false);
447 * If plan has been invalidated, unlink it from the parent and release it.
449 if (plan
&& plan
->dead
)
451 plansource
->plan
= NULL
;
452 ReleaseCachedPlan(plan
, false);
457 * Build a new plan if needed.
462 TupleDesc resultDesc
;
465 * Restore the search_path that was in use when the plan was made.
466 * (XXX is there anything else we really need to restore?)
468 PushOverrideSearchPath(plansource
->search_path
);
471 * Run parse analysis and rule rewriting. The parser tends to
472 * scribble on its input, so we must copy the raw parse tree to
473 * prevent corruption of the cache. Note that we do not use
474 * parse_analyze_varparams(), assuming that the caller never wants the
475 * parameter types to change from the original values.
477 slist
= pg_analyze_and_rewrite(copyObject(plansource
->raw_parse_tree
),
478 plansource
->query_string
,
479 plansource
->param_types
,
480 plansource
->num_params
);
482 if (plansource
->fully_planned
)
484 /* Generate plans for queries */
485 slist
= do_planning(slist
, plansource
->cursor_options
);
489 * Check or update the result tupdesc. XXX should we use a weaker
490 * condition than equalTupleDescs() here?
492 resultDesc
= PlanCacheComputeResultDesc(slist
);
493 if (resultDesc
== NULL
&& plansource
->resultDesc
== NULL
)
495 /* OK, doesn't return tuples */
497 else if (resultDesc
== NULL
|| plansource
->resultDesc
== NULL
||
498 !equalTupleDescs(resultDesc
, plansource
->resultDesc
))
500 MemoryContext oldcxt
;
502 /* can we give a better error message? */
503 if (plansource
->fixed_result
)
505 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
506 errmsg("cached plan must not change result type")));
507 oldcxt
= MemoryContextSwitchTo(plansource
->context
);
509 resultDesc
= CreateTupleDescCopy(resultDesc
);
510 if (plansource
->resultDesc
)
511 FreeTupleDesc(plansource
->resultDesc
);
512 plansource
->resultDesc
= resultDesc
;
513 MemoryContextSwitchTo(oldcxt
);
516 /* Now we can restore current search path */
517 PopOverrideSearchPath();
520 * Store the plans into the plancache entry, advancing the generation
523 StoreCachedPlan(plansource
, slist
, NULL
);
525 plan
= plansource
->plan
;
529 * Last step: flag the plan as in use by caller.
532 ResourceOwnerEnlargePlanCacheRefs(CurrentResourceOwner
);
535 ResourceOwnerRememberPlanCacheRef(CurrentResourceOwner
, plan
);
541 * Invoke the planner on some rewritten queries. This is broken out of
542 * RevalidateCachedPlan just to avoid plastering "volatile" all over that
543 * function's variables.
546 do_planning(List
*querytrees
, int cursorOptions
)
551 * If a snapshot is already set (the normal case), we can just use that
552 * for planning. But if it isn't, we have to tell pg_plan_queries to make
553 * a snap if it needs one. In that case we should arrange to reset
554 * ActiveSnapshot afterward, to ensure that RevalidateCachedPlan has no
555 * caller-visible effects on the snapshot. Having to replan is an unusual
556 * case, and it seems a really bad idea for RevalidateCachedPlan to affect
557 * the snapshot only in unusual cases. (Besides, the snap might have been
558 * created in a short-lived context.)
560 if (ActiveSnapshot
!= NULL
)
561 stmt_list
= pg_plan_queries(querytrees
, cursorOptions
, NULL
, false);
566 stmt_list
= pg_plan_queries(querytrees
, cursorOptions
, NULL
, true);
570 /* Restore global vars and propagate error */
571 ActiveSnapshot
= NULL
;
576 ActiveSnapshot
= NULL
;
584 * ReleaseCachedPlan: release active use of a cached plan.
586 * This decrements the reference count, and frees the plan if the count
587 * has thereby gone to zero. If useResOwner is true, it is assumed that
588 * the reference count is managed by the CurrentResourceOwner.
590 * Note: useResOwner = false is used for releasing references that are in
591 * persistent data structures, such as the parent CachedPlanSource or a
592 * Portal. Transient references should be protected by a resource owner.
595 ReleaseCachedPlan(CachedPlan
*plan
, bool useResOwner
)
598 ResourceOwnerForgetPlanCacheRef(CurrentResourceOwner
, plan
);
599 Assert(plan
->refcount
> 0);
601 if (plan
->refcount
== 0)
602 MemoryContextDelete(plan
->context
);
606 * AcquireExecutorLocks: acquire locks needed for execution of a fully-planned
607 * cached plan; or release them if acquire is false.
610 AcquireExecutorLocks(List
*stmt_list
, bool acquire
)
614 foreach(lc1
, stmt_list
)
616 PlannedStmt
*plannedstmt
= (PlannedStmt
*) lfirst(lc1
);
620 Assert(!IsA(plannedstmt
, Query
));
621 if (!IsA(plannedstmt
, PlannedStmt
))
622 continue; /* Ignore utility statements */
625 foreach(lc2
, plannedstmt
->rtable
)
627 RangeTblEntry
*rte
= (RangeTblEntry
*) lfirst(lc2
);
632 if (rte
->rtekind
!= RTE_RELATION
)
636 * Acquire the appropriate type of lock on each relation OID. Note
637 * that we don't actually try to open the rel, and hence will not
638 * fail if it's been dropped entirely --- we'll just transiently
639 * acquire a non-conflicting lock.
641 if (list_member_int(plannedstmt
->resultRelations
, rt_index
))
642 lockmode
= RowExclusiveLock
;
643 else if (rowmark_member(plannedstmt
->rowMarks
, rt_index
))
644 lockmode
= RowShareLock
;
646 lockmode
= AccessShareLock
;
649 LockRelationOid(rte
->relid
, lockmode
);
651 UnlockRelationOid(rte
->relid
, lockmode
);
657 * AcquirePlannerLocks: acquire locks needed for planning and execution of a
658 * not-fully-planned cached plan; or release them if acquire is false.
660 * Note that we don't actually try to open the relations, and hence will not
661 * fail if one has been dropped entirely --- we'll just transiently acquire
662 * a non-conflicting lock.
665 AcquirePlannerLocks(List
*stmt_list
, bool acquire
)
669 foreach(lc
, stmt_list
)
671 Query
*query
= (Query
*) lfirst(lc
);
673 Assert(IsA(query
, Query
));
675 ScanQueryForRelids(query
, LockRelid
, NULL
);
677 ScanQueryForRelids(query
, UnlockRelid
, NULL
);
682 * ScanQueryForRelids callback functions for AcquirePlannerLocks
685 LockRelid(Oid relid
, LOCKMODE lockmode
, void *arg
)
687 LockRelationOid(relid
, lockmode
);
691 UnlockRelid(Oid relid
, LOCKMODE lockmode
, void *arg
)
693 UnlockRelationOid(relid
, lockmode
);
697 * ScanQueryForRelids: recursively scan one Query and apply the callback
698 * function to each relation OID found therein. The callback function
699 * takes the arguments relation OID, lockmode, pointer arg.
702 ScanQueryForRelids(Query
*parsetree
,
710 * First, process RTEs of the current query level.
713 foreach(lc
, parsetree
->rtable
)
715 RangeTblEntry
*rte
= (RangeTblEntry
*) lfirst(lc
);
719 switch (rte
->rtekind
)
724 * Determine the lock type required for this RTE.
726 if (rt_index
== parsetree
->resultRelation
)
727 lockmode
= RowExclusiveLock
;
728 else if (rowmark_member(parsetree
->rowMarks
, rt_index
))
729 lockmode
= RowShareLock
;
731 lockmode
= AccessShareLock
;
733 (*callback
) (rte
->relid
, lockmode
, arg
);
739 * The subquery RTE itself is all right, but we have to
740 * recurse to process the represented subquery.
742 ScanQueryForRelids(rte
->subquery
, callback
, arg
);
746 /* ignore other types of RTEs */
752 * Recurse into sublink subqueries, too. But we already did the ones in
755 if (parsetree
->hasSubLinks
)
757 ScanQueryWalkerContext context
;
759 context
.callback
= callback
;
761 query_tree_walker(parsetree
, ScanQueryWalker
,
763 QTW_IGNORE_RT_SUBQUERIES
);
768 * Walker to find sublink subqueries for ScanQueryForRelids
771 ScanQueryWalker(Node
*node
, ScanQueryWalkerContext
*context
)
775 if (IsA(node
, SubLink
))
777 SubLink
*sub
= (SubLink
*) node
;
779 /* Do what we came for */
780 ScanQueryForRelids((Query
*) sub
->subselect
,
781 context
->callback
, context
->arg
);
782 /* Fall through to process lefthand args of SubLink */
786 * Do NOT recurse into Query nodes, because ScanQueryForRelids already
787 * processed subselects of subselects for us.
789 return expression_tree_walker(node
, ScanQueryWalker
,
794 * rowmark_member: check whether an RT index appears in a RowMarkClause list.
797 rowmark_member(List
*rowMarks
, int rt_index
)
803 RowMarkClause
*rc
= (RowMarkClause
*) lfirst(l
);
805 if (rc
->rti
== rt_index
)
812 * plan_list_is_transient: check if any of the plans in the list are transient.
815 plan_list_is_transient(List
*stmt_list
)
819 foreach(lc
, stmt_list
)
821 PlannedStmt
*plannedstmt
= (PlannedStmt
*) lfirst(lc
);
823 if (!IsA(plannedstmt
, PlannedStmt
))
824 continue; /* Ignore utility statements */
826 if (plannedstmt
->transientPlan
)
834 * PlanCacheComputeResultDesc: given a list of either fully-planned statements
835 * or Queries, determine the result tupledesc it will produce. Returns NULL
836 * if the execution will not return tuples.
838 * Note: the result is created or copied into current memory context.
841 PlanCacheComputeResultDesc(List
*stmt_list
)
847 switch (ChoosePortalStrategy(stmt_list
))
849 case PORTAL_ONE_SELECT
:
850 node
= (Node
*) linitial(stmt_list
);
851 if (IsA(node
, Query
))
853 query
= (Query
*) node
;
854 return ExecCleanTypeFromTL(query
->targetList
, false);
856 if (IsA(node
, PlannedStmt
))
858 pstmt
= (PlannedStmt
*) node
;
859 return ExecCleanTypeFromTL(pstmt
->planTree
->targetlist
, false);
861 /* other cases shouldn't happen, but return NULL */
864 case PORTAL_ONE_RETURNING
:
865 node
= PortalListGetPrimaryStmt(stmt_list
);
866 if (IsA(node
, Query
))
868 query
= (Query
*) node
;
869 Assert(query
->returningList
);
870 return ExecCleanTypeFromTL(query
->returningList
, false);
872 if (IsA(node
, PlannedStmt
))
874 pstmt
= (PlannedStmt
*) node
;
875 Assert(pstmt
->returningLists
);
876 return ExecCleanTypeFromTL((List
*) linitial(pstmt
->returningLists
), false);
878 /* other cases shouldn't happen, but return NULL */
881 case PORTAL_UTIL_SELECT
:
882 node
= (Node
*) linitial(stmt_list
);
883 if (IsA(node
, Query
))
885 query
= (Query
*) node
;
886 Assert(query
->utilityStmt
);
887 return UtilityTupleDescriptor(query
->utilityStmt
);
889 /* else it's a bare utility statement */
890 return UtilityTupleDescriptor(node
);
892 case PORTAL_MULTI_QUERY
:
893 /* will not return tuples */
901 * Relcache inval callback function
903 * Invalidate all plans mentioning the given rel, or all plans mentioning
904 * any rel at all if relid == InvalidOid.
907 PlanCacheCallback(Datum arg
, Oid relid
)
912 foreach(lc1
, cached_plans_list
)
914 CachedPlanSource
*plansource
= (CachedPlanSource
*) lfirst(lc1
);
915 CachedPlan
*plan
= plansource
->plan
;
917 /* No work if it's already invalidated */
918 if (!plan
|| plan
->dead
)
920 if (plan
->fully_planned
)
922 foreach(lc2
, plan
->stmt_list
)
924 PlannedStmt
*plannedstmt
= (PlannedStmt
*) lfirst(lc2
);
926 Assert(!IsA(plannedstmt
, Query
));
927 if (!IsA(plannedstmt
, PlannedStmt
))
928 continue; /* Ignore utility statements */
929 if ((relid
== InvalidOid
) ? plannedstmt
->relationOids
!= NIL
:
930 list_member_oid(plannedstmt
->relationOids
, relid
))
932 /* Invalidate the plan! */
934 break; /* out of stmt_list scan */
941 * For not-fully-planned entries we use ScanQueryForRelids, since
942 * a recursive traversal is needed. The callback API is a bit
943 * tedious but avoids duplication of coding.
945 InvalRelidContext context
;
947 context
.inval_relid
= relid
;
950 foreach(lc2
, plan
->stmt_list
)
952 Query
*query
= (Query
*) lfirst(lc2
);
954 Assert(IsA(query
, Query
));
955 ScanQueryForRelids(query
, InvalRelid
, (void *) &context
);
962 * ResetPlanCache: drop all cached plans.
967 PlanCacheCallback((Datum
) 0, InvalidOid
);
971 * ScanQueryForRelids callback function for PlanCacheCallback
974 InvalRelid(Oid relid
, LOCKMODE lockmode
, InvalRelidContext
*context
)
976 if (relid
== context
->inval_relid
|| context
->inval_relid
== InvalidOid
)
977 context
->plan
->dead
= true;