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 objects 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 track exactly the dependencies of plans on relations and
24 * user-defined functions. On relcache invalidation events or pg_proc
25 * syscache invalidation events, we invalidate just those plans that depend
26 * on the particular object being modified. (Note: this scheme assumes
27 * that any table modification that requires replanning will generate a
28 * relcache inval event.) We also watch for inval events on certain other
29 * system catalogs, such as pg_namespace; but for them, our response is
30 * just to invalidate all plans. We expect updates on those catalogs to
31 * be infrequent enough that more-detailed tracking is not worth the effort.
34 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
35 * Portions Copyright (c) 1994, Regents of the University of California
40 *-------------------------------------------------------------------------
44 #include "utils/plancache.h"
45 #include "access/transam.h"
46 #include "catalog/namespace.h"
47 #include "executor/executor.h"
48 #include "nodes/nodeFuncs.h"
49 #include "optimizer/planmain.h"
50 #include "storage/lmgr.h"
51 #include "tcop/pquery.h"
52 #include "tcop/tcopprot.h"
53 #include "tcop/utility.h"
54 #include "utils/inval.h"
55 #include "utils/memutils.h"
56 #include "utils/resowner.h"
57 #include "utils/snapmgr.h"
58 #include "utils/syscache.h"
61 static List
*cached_plans_list
= NIL
;
63 static void StoreCachedPlan(CachedPlanSource
*plansource
, List
*stmt_list
,
64 MemoryContext plan_context
);
65 static void AcquireExecutorLocks(List
*stmt_list
, bool acquire
);
66 static void AcquirePlannerLocks(List
*stmt_list
, bool acquire
);
67 static void ScanQueryForLocks(Query
*parsetree
, bool acquire
);
68 static bool ScanQueryWalker(Node
*node
, bool *acquire
);
69 static bool rowmark_member(List
*rowMarks
, int rt_index
);
70 static bool plan_list_is_transient(List
*stmt_list
);
71 static void PlanCacheRelCallback(Datum arg
, Oid relid
);
72 static void PlanCacheFuncCallback(Datum arg
, int cacheid
, ItemPointer tuplePtr
);
73 static void PlanCacheSysCallback(Datum arg
, int cacheid
, ItemPointer tuplePtr
);
77 * InitPlanCache: initialize module during InitPostgres.
79 * All we need to do is hook into inval.c's callback lists.
84 CacheRegisterRelcacheCallback(PlanCacheRelCallback
, (Datum
) 0);
85 CacheRegisterSyscacheCallback(PROCOID
, PlanCacheFuncCallback
, (Datum
) 0);
86 CacheRegisterSyscacheCallback(NAMESPACEOID
, PlanCacheSysCallback
, (Datum
) 0);
87 CacheRegisterSyscacheCallback(OPEROID
, PlanCacheSysCallback
, (Datum
) 0);
88 CacheRegisterSyscacheCallback(AMOPOPID
, PlanCacheSysCallback
, (Datum
) 0);
92 * CreateCachedPlan: initially create a plan cache entry.
94 * The caller must already have successfully parsed/planned the query;
95 * about all that we do here is copy it into permanent storage.
97 * raw_parse_tree: output of raw_parser()
98 * query_string: original query text (as of PG 8.4, must not be NULL)
99 * commandTag: compile-time-constant tag for query, or NULL if empty query
100 * param_types: array of parameter type OIDs, or NULL if none
101 * num_params: number of parameters
102 * cursor_options: options bitmask that was/will be passed to planner
103 * stmt_list: list of PlannedStmts/utility stmts, or list of Query trees
104 * fully_planned: are we caching planner or rewriter output?
105 * fixed_result: TRUE to disallow changes in result tupdesc
108 CreateCachedPlan(Node
*raw_parse_tree
,
109 const char *query_string
,
110 const char *commandTag
,
118 CachedPlanSource
*plansource
;
119 OverrideSearchPath
*search_path
;
120 MemoryContext source_context
;
121 MemoryContext oldcxt
;
123 Assert(query_string
!= NULL
); /* required as of 8.4 */
126 * Make a dedicated memory context for the CachedPlanSource and its
127 * subsidiary data. We expect it can be pretty small.
129 source_context
= AllocSetContextCreate(CacheMemoryContext
,
131 ALLOCSET_SMALL_MINSIZE
,
132 ALLOCSET_SMALL_INITSIZE
,
133 ALLOCSET_SMALL_MAXSIZE
);
136 * Fetch current search_path into new context, but do any recalculation
137 * work required in caller's context.
139 search_path
= GetOverrideSearchPath(source_context
);
142 * Create and fill the CachedPlanSource struct within the new context.
144 oldcxt
= MemoryContextSwitchTo(source_context
);
145 plansource
= (CachedPlanSource
*) palloc(sizeof(CachedPlanSource
));
146 plansource
->raw_parse_tree
= copyObject(raw_parse_tree
);
147 plansource
->query_string
= pstrdup(query_string
);
148 plansource
->commandTag
= commandTag
; /* no copying needed */
151 plansource
->param_types
= (Oid
*) palloc(num_params
* sizeof(Oid
));
152 memcpy(plansource
->param_types
, param_types
, num_params
* sizeof(Oid
));
155 plansource
->param_types
= NULL
;
156 plansource
->num_params
= num_params
;
157 plansource
->cursor_options
= cursor_options
;
158 plansource
->fully_planned
= fully_planned
;
159 plansource
->fixed_result
= fixed_result
;
160 plansource
->search_path
= search_path
;
161 plansource
->generation
= 0; /* StoreCachedPlan will increment */
162 plansource
->resultDesc
= PlanCacheComputeResultDesc(stmt_list
);
163 plansource
->plan
= NULL
;
164 plansource
->context
= source_context
;
165 plansource
->orig_plan
= NULL
;
168 * Copy the current output plans into the plancache entry.
170 StoreCachedPlan(plansource
, stmt_list
, NULL
);
173 * Now we can add the entry to the list of cached plans. The List nodes
174 * live in CacheMemoryContext.
176 MemoryContextSwitchTo(CacheMemoryContext
);
178 cached_plans_list
= lappend(cached_plans_list
, plansource
);
180 MemoryContextSwitchTo(oldcxt
);
186 * FastCreateCachedPlan: create a plan cache entry with minimal data copying.
188 * For plans that aren't expected to live very long, the copying overhead of
189 * CreateCachedPlan is annoying. We provide this variant entry point in which
190 * the caller has already placed all the data in a suitable memory context.
191 * The source data and completed plan are in the same context, since this
192 * avoids extra copy steps during plan construction. If the query ever does
193 * need replanning, we'll generate a separate new CachedPlan at that time, but
194 * the CachedPlanSource and the initial CachedPlan share the caller-provided
195 * context and go away together when neither is needed any longer. (Because
196 * the parser and planner generate extra cruft in addition to their real
197 * output, this approach means that the context probably contains a bunch of
198 * useless junk as well as the useful trees. Hence, this method is a
199 * space-for-time tradeoff, which is worth making for plans expected to be
202 * raw_parse_tree, query_string, param_types, and stmt_list must reside in the
203 * given context, which must have adequate lifespan (recommendation: make it a
204 * child of CacheMemoryContext). Otherwise the API is the same as
208 FastCreateCachedPlan(Node
*raw_parse_tree
,
210 const char *commandTag
,
217 MemoryContext context
)
219 CachedPlanSource
*plansource
;
220 OverrideSearchPath
*search_path
;
221 MemoryContext oldcxt
;
223 Assert(query_string
!= NULL
); /* required as of 8.4 */
226 * Fetch current search_path into given context, but do any recalculation
227 * work required in caller's context.
229 search_path
= GetOverrideSearchPath(context
);
232 * Create and fill the CachedPlanSource struct within the given context.
234 oldcxt
= MemoryContextSwitchTo(context
);
235 plansource
= (CachedPlanSource
*) palloc(sizeof(CachedPlanSource
));
236 plansource
->raw_parse_tree
= raw_parse_tree
;
237 plansource
->query_string
= query_string
;
238 plansource
->commandTag
= commandTag
; /* no copying needed */
239 plansource
->param_types
= param_types
;
240 plansource
->num_params
= num_params
;
241 plansource
->cursor_options
= cursor_options
;
242 plansource
->fully_planned
= fully_planned
;
243 plansource
->fixed_result
= fixed_result
;
244 plansource
->search_path
= search_path
;
245 plansource
->generation
= 0; /* StoreCachedPlan will increment */
246 plansource
->resultDesc
= PlanCacheComputeResultDesc(stmt_list
);
247 plansource
->plan
= NULL
;
248 plansource
->context
= context
;
249 plansource
->orig_plan
= NULL
;
252 * Store the current output plans into the plancache entry.
254 StoreCachedPlan(plansource
, stmt_list
, context
);
257 * Since the context is owned by the CachedPlan, advance its refcount.
259 plansource
->orig_plan
= plansource
->plan
;
260 plansource
->orig_plan
->refcount
++;
263 * Now we can add the entry to the list of cached plans. The List nodes
264 * live in CacheMemoryContext.
266 MemoryContextSwitchTo(CacheMemoryContext
);
268 cached_plans_list
= lappend(cached_plans_list
, plansource
);
270 MemoryContextSwitchTo(oldcxt
);
276 * StoreCachedPlan: store a built or rebuilt plan into a plancache entry.
278 * Common subroutine for CreateCachedPlan and RevalidateCachedPlan.
281 StoreCachedPlan(CachedPlanSource
*plansource
,
283 MemoryContext plan_context
)
286 MemoryContext oldcxt
;
288 if (plan_context
== NULL
)
291 * Make a dedicated memory context for the CachedPlan and its
292 * subsidiary data. It's probably not going to be large, but just in
293 * case, use the default maxsize parameter.
295 plan_context
= AllocSetContextCreate(CacheMemoryContext
,
297 ALLOCSET_SMALL_MINSIZE
,
298 ALLOCSET_SMALL_INITSIZE
,
299 ALLOCSET_DEFAULT_MAXSIZE
);
302 * Copy supplied data into the new context.
304 oldcxt
= MemoryContextSwitchTo(plan_context
);
306 stmt_list
= (List
*) copyObject(stmt_list
);
310 /* Assume subsidiary data is in the given context */
311 oldcxt
= MemoryContextSwitchTo(plan_context
);
315 * Create and fill the CachedPlan struct within the new context.
317 plan
= (CachedPlan
*) palloc(sizeof(CachedPlan
));
318 plan
->stmt_list
= stmt_list
;
319 plan
->fully_planned
= plansource
->fully_planned
;
321 if (plansource
->fully_planned
&& plan_list_is_transient(stmt_list
))
323 Assert(TransactionIdIsNormal(TransactionXmin
));
324 plan
->saved_xmin
= TransactionXmin
;
327 plan
->saved_xmin
= InvalidTransactionId
;
328 plan
->refcount
= 1; /* for the parent's link */
329 plan
->generation
= ++(plansource
->generation
);
330 plan
->context
= plan_context
;
331 if (plansource
->fully_planned
)
333 /* Planner already extracted dependencies, we don't have to */
334 plan
->relationOids
= plan
->invalItems
= NIL
;
338 /* Use the planner machinery to extract dependencies */
339 extract_query_dependencies(stmt_list
,
344 Assert(plansource
->plan
== NULL
);
345 plansource
->plan
= plan
;
347 MemoryContextSwitchTo(oldcxt
);
351 * DropCachedPlan: destroy a cached plan.
353 * Actually this only destroys the CachedPlanSource: the referenced CachedPlan
354 * is released, but not destroyed until its refcount goes to zero. That
355 * handles the situation where DropCachedPlan is called while the plan is
359 DropCachedPlan(CachedPlanSource
*plansource
)
361 /* Validity check that we were given a CachedPlanSource */
362 Assert(list_member_ptr(cached_plans_list
, plansource
));
364 /* Remove it from the list */
365 cached_plans_list
= list_delete_ptr(cached_plans_list
, plansource
);
367 /* Decrement child CachePlan's refcount and drop if no longer needed */
368 if (plansource
->plan
)
369 ReleaseCachedPlan(plansource
->plan
, false);
372 * If CachedPlanSource has independent storage, just drop it. Otherwise
373 * decrement the refcount on the CachePlan that owns the storage.
375 if (plansource
->orig_plan
== NULL
)
377 /* Remove the CachedPlanSource and all subsidiary data */
378 MemoryContextDelete(plansource
->context
);
382 Assert(plansource
->context
== plansource
->orig_plan
->context
);
383 ReleaseCachedPlan(plansource
->orig_plan
, false);
388 * RevalidateCachedPlan: prepare for re-use of a previously cached plan.
390 * What we do here is re-acquire locks and rebuild the plan if necessary.
391 * On return, the plan is valid and we have sufficient locks to begin
392 * execution (or planning, if not fully_planned).
394 * On return, the refcount of the plan has been incremented; a later
395 * ReleaseCachedPlan() call is expected. The refcount has been reported
396 * to the CurrentResourceOwner if useResOwner is true.
398 * Note: if any replanning activity is required, the caller's memory context
399 * is used for that work.
402 RevalidateCachedPlan(CachedPlanSource
*plansource
, bool useResOwner
)
406 /* Validity check that we were given a CachedPlanSource */
407 Assert(list_member_ptr(cached_plans_list
, plansource
));
410 * If the plan currently appears valid, acquire locks on the referenced
411 * objects; then check again. We need to do it this way to cover the race
412 * condition that an invalidation message arrives before we get the lock.
414 plan
= plansource
->plan
;
415 if (plan
&& !plan
->dead
)
418 * Plan must have positive refcount because it is referenced by
419 * plansource; so no need to fear it disappears under us here.
421 Assert(plan
->refcount
> 0);
423 if (plan
->fully_planned
)
424 AcquireExecutorLocks(plan
->stmt_list
, true);
426 AcquirePlannerLocks(plan
->stmt_list
, true);
429 * If plan was transient, check to see if TransactionXmin has
430 * advanced, and if so invalidate it.
433 TransactionIdIsValid(plan
->saved_xmin
) &&
434 !TransactionIdEquals(plan
->saved_xmin
, TransactionXmin
))
438 * By now, if any invalidation has happened, the inval callback
439 * functions will have marked the plan dead.
443 /* Ooops, the race case happened. Release useless locks. */
444 if (plan
->fully_planned
)
445 AcquireExecutorLocks(plan
->stmt_list
, false);
447 AcquirePlannerLocks(plan
->stmt_list
, false);
452 * If plan has been invalidated, unlink it from the parent and release it.
454 if (plan
&& plan
->dead
)
456 plansource
->plan
= NULL
;
457 ReleaseCachedPlan(plan
, false);
462 * Build a new plan if needed.
466 bool snapshot_set
= false;
468 TupleDesc resultDesc
;
471 * Restore the search_path that was in use when the plan was made.
472 * (XXX is there anything else we really need to restore?)
474 PushOverrideSearchPath(plansource
->search_path
);
477 * If a snapshot is already set (the normal case), we can just use
478 * that for parsing/planning. But if it isn't, install one. Note: no
479 * point in checking whether parse analysis requires a snapshot;
480 * utility commands don't have invalidatable plans, so we'd not get
481 * here for such a command.
483 if (!ActiveSnapshotSet())
485 PushActiveSnapshot(GetTransactionSnapshot());
490 * Run parse analysis and rule rewriting. The parser tends to
491 * scribble on its input, so we must copy the raw parse tree to
492 * prevent corruption of the cache. Note that we do not use
493 * parse_analyze_varparams(), assuming that the caller never wants the
494 * parameter types to change from the original values.
496 slist
= pg_analyze_and_rewrite(copyObject(plansource
->raw_parse_tree
),
497 plansource
->query_string
,
498 plansource
->param_types
,
499 plansource
->num_params
);
501 if (plansource
->fully_planned
)
504 * Generate plans for queries.
506 slist
= pg_plan_queries(slist
, plansource
->cursor_options
, NULL
);
510 * Check or update the result tupdesc. XXX should we use a weaker
511 * condition than equalTupleDescs() here?
513 resultDesc
= PlanCacheComputeResultDesc(slist
);
514 if (resultDesc
== NULL
&& plansource
->resultDesc
== NULL
)
516 /* OK, doesn't return tuples */
518 else if (resultDesc
== NULL
|| plansource
->resultDesc
== NULL
||
519 !equalTupleDescs(resultDesc
, plansource
->resultDesc
))
521 MemoryContext oldcxt
;
523 /* can we give a better error message? */
524 if (plansource
->fixed_result
)
526 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
527 errmsg("cached plan must not change result type")));
528 oldcxt
= MemoryContextSwitchTo(plansource
->context
);
530 resultDesc
= CreateTupleDescCopy(resultDesc
);
531 if (plansource
->resultDesc
)
532 FreeTupleDesc(plansource
->resultDesc
);
533 plansource
->resultDesc
= resultDesc
;
534 MemoryContextSwitchTo(oldcxt
);
537 /* Release snapshot if we got one */
541 /* Now we can restore current search path */
542 PopOverrideSearchPath();
545 * Store the plans into the plancache entry, advancing the generation
548 StoreCachedPlan(plansource
, slist
, NULL
);
550 plan
= plansource
->plan
;
554 * Last step: flag the plan as in use by caller.
557 ResourceOwnerEnlargePlanCacheRefs(CurrentResourceOwner
);
560 ResourceOwnerRememberPlanCacheRef(CurrentResourceOwner
, plan
);
566 * ReleaseCachedPlan: release active use of a cached plan.
568 * This decrements the reference count, and frees the plan if the count
569 * has thereby gone to zero. If useResOwner is true, it is assumed that
570 * the reference count is managed by the CurrentResourceOwner.
572 * Note: useResOwner = false is used for releasing references that are in
573 * persistent data structures, such as the parent CachedPlanSource or a
574 * Portal. Transient references should be protected by a resource owner.
577 ReleaseCachedPlan(CachedPlan
*plan
, bool useResOwner
)
580 ResourceOwnerForgetPlanCacheRef(CurrentResourceOwner
, plan
);
581 Assert(plan
->refcount
> 0);
583 if (plan
->refcount
== 0)
584 MemoryContextDelete(plan
->context
);
588 * CachedPlanIsValid: test whether the plan within a CachedPlanSource is
589 * currently valid (that is, not marked as being in need of revalidation).
591 * This result is only trustworthy (ie, free from race conditions) if
592 * the caller has acquired locks on all the relations used in the plan.
595 CachedPlanIsValid(CachedPlanSource
*plansource
)
599 /* Validity check that we were given a CachedPlanSource */
600 Assert(list_member_ptr(cached_plans_list
, plansource
));
602 plan
= plansource
->plan
;
603 if (plan
&& !plan
->dead
)
606 * Plan must have positive refcount because it is referenced by
607 * plansource; so no need to fear it disappears under us here.
609 Assert(plan
->refcount
> 0);
612 * Although we don't want to acquire locks here, it still seems useful
613 * to check for expiration of a transient plan.
615 if (TransactionIdIsValid(plan
->saved_xmin
) &&
616 !TransactionIdEquals(plan
->saved_xmin
, TransactionXmin
))
626 * AcquireExecutorLocks: acquire locks needed for execution of a fully-planned
627 * cached plan; or release them if acquire is false.
630 AcquireExecutorLocks(List
*stmt_list
, bool acquire
)
634 foreach(lc1
, stmt_list
)
636 PlannedStmt
*plannedstmt
= (PlannedStmt
*) lfirst(lc1
);
640 Assert(!IsA(plannedstmt
, Query
));
641 if (!IsA(plannedstmt
, PlannedStmt
))
642 continue; /* Ignore utility statements */
645 foreach(lc2
, plannedstmt
->rtable
)
647 RangeTblEntry
*rte
= (RangeTblEntry
*) lfirst(lc2
);
652 if (rte
->rtekind
!= RTE_RELATION
)
656 * Acquire the appropriate type of lock on each relation OID. Note
657 * that we don't actually try to open the rel, and hence will not
658 * fail if it's been dropped entirely --- we'll just transiently
659 * acquire a non-conflicting lock.
661 if (list_member_int(plannedstmt
->resultRelations
, rt_index
))
662 lockmode
= RowExclusiveLock
;
663 else if (rowmark_member(plannedstmt
->rowMarks
, rt_index
))
664 lockmode
= RowShareLock
;
666 lockmode
= AccessShareLock
;
669 LockRelationOid(rte
->relid
, lockmode
);
671 UnlockRelationOid(rte
->relid
, lockmode
);
677 * AcquirePlannerLocks: acquire locks needed for planning and execution of a
678 * not-fully-planned cached plan; or release them if acquire is false.
680 * Note that we don't actually try to open the relations, and hence will not
681 * fail if one has been dropped entirely --- we'll just transiently acquire
682 * a non-conflicting lock.
685 AcquirePlannerLocks(List
*stmt_list
, bool acquire
)
689 foreach(lc
, stmt_list
)
691 Query
*query
= (Query
*) lfirst(lc
);
693 Assert(IsA(query
, Query
));
694 ScanQueryForLocks(query
, acquire
);
699 * ScanQueryForLocks: recursively scan one Query for AcquirePlannerLocks.
702 ScanQueryForLocks(Query
*parsetree
, bool acquire
)
708 * First, process RTEs of the current query level.
711 foreach(lc
, parsetree
->rtable
)
713 RangeTblEntry
*rte
= (RangeTblEntry
*) lfirst(lc
);
717 switch (rte
->rtekind
)
720 /* Acquire or release the appropriate type of lock */
721 if (rt_index
== parsetree
->resultRelation
)
722 lockmode
= RowExclusiveLock
;
723 else if (rowmark_member(parsetree
->rowMarks
, rt_index
))
724 lockmode
= RowShareLock
;
726 lockmode
= AccessShareLock
;
728 LockRelationOid(rte
->relid
, lockmode
);
730 UnlockRelationOid(rte
->relid
, lockmode
);
734 /* Recurse into subquery-in-FROM */
735 ScanQueryForLocks(rte
->subquery
, acquire
);
739 /* ignore other types of RTEs */
744 /* Recurse into subquery-in-WITH */
745 foreach(lc
, parsetree
->cteList
)
747 CommonTableExpr
*cte
= (CommonTableExpr
*) lfirst(lc
);
749 ScanQueryForLocks((Query
*) cte
->ctequery
, acquire
);
753 * Recurse into sublink subqueries, too. But we already did the ones in
754 * the rtable and cteList.
756 if (parsetree
->hasSubLinks
)
758 query_tree_walker(parsetree
, ScanQueryWalker
,
760 QTW_IGNORE_RC_SUBQUERIES
);
765 * Walker to find sublink subqueries for ScanQueryForLocks
768 ScanQueryWalker(Node
*node
, bool *acquire
)
772 if (IsA(node
, SubLink
))
774 SubLink
*sub
= (SubLink
*) node
;
776 /* Do what we came for */
777 ScanQueryForLocks((Query
*) sub
->subselect
, *acquire
);
778 /* Fall through to process lefthand args of SubLink */
782 * Do NOT recurse into Query nodes, because ScanQueryForLocks already
783 * processed subselects of subselects for us.
785 return expression_tree_walker(node
, ScanQueryWalker
,
790 * rowmark_member: check whether an RT index appears in a RowMarkClause list.
793 rowmark_member(List
*rowMarks
, int rt_index
)
799 RowMarkClause
*rc
= (RowMarkClause
*) lfirst(l
);
801 if (rc
->rti
== rt_index
)
808 * plan_list_is_transient: check if any of the plans in the list are transient.
811 plan_list_is_transient(List
*stmt_list
)
815 foreach(lc
, stmt_list
)
817 PlannedStmt
*plannedstmt
= (PlannedStmt
*) lfirst(lc
);
819 if (!IsA(plannedstmt
, PlannedStmt
))
820 continue; /* Ignore utility statements */
822 if (plannedstmt
->transientPlan
)
830 * PlanCacheComputeResultDesc: given a list of either fully-planned statements
831 * or Queries, determine the result tupledesc it will produce. Returns NULL
832 * if the execution will not return tuples.
834 * Note: the result is created or copied into current memory context.
837 PlanCacheComputeResultDesc(List
*stmt_list
)
843 switch (ChoosePortalStrategy(stmt_list
))
845 case PORTAL_ONE_SELECT
:
846 node
= (Node
*) linitial(stmt_list
);
847 if (IsA(node
, Query
))
849 query
= (Query
*) node
;
850 return ExecCleanTypeFromTL(query
->targetList
, false);
852 if (IsA(node
, PlannedStmt
))
854 pstmt
= (PlannedStmt
*) node
;
855 return ExecCleanTypeFromTL(pstmt
->planTree
->targetlist
, false);
857 /* other cases shouldn't happen, but return NULL */
860 case PORTAL_ONE_RETURNING
:
861 node
= PortalListGetPrimaryStmt(stmt_list
);
862 if (IsA(node
, Query
))
864 query
= (Query
*) node
;
865 Assert(query
->returningList
);
866 return ExecCleanTypeFromTL(query
->returningList
, false);
868 if (IsA(node
, PlannedStmt
))
870 pstmt
= (PlannedStmt
*) node
;
871 Assert(pstmt
->returningLists
);
872 return ExecCleanTypeFromTL((List
*) linitial(pstmt
->returningLists
), false);
874 /* other cases shouldn't happen, but return NULL */
877 case PORTAL_UTIL_SELECT
:
878 node
= (Node
*) linitial(stmt_list
);
879 if (IsA(node
, Query
))
881 query
= (Query
*) node
;
882 Assert(query
->utilityStmt
);
883 return UtilityTupleDescriptor(query
->utilityStmt
);
885 /* else it's a bare utility statement */
886 return UtilityTupleDescriptor(node
);
888 case PORTAL_MULTI_QUERY
:
889 /* will not return tuples */
896 * PlanCacheRelCallback
897 * Relcache inval callback function
899 * Invalidate all plans mentioning the given rel, or all plans mentioning
900 * any rel at all if relid == InvalidOid.
903 PlanCacheRelCallback(Datum arg
, Oid relid
)
907 foreach(lc1
, cached_plans_list
)
909 CachedPlanSource
*plansource
= (CachedPlanSource
*) lfirst(lc1
);
910 CachedPlan
*plan
= plansource
->plan
;
912 /* No work if it's already invalidated */
913 if (!plan
|| plan
->dead
)
915 if (plan
->fully_planned
)
917 /* Have to check the per-PlannedStmt relid lists */
920 foreach(lc2
, plan
->stmt_list
)
922 PlannedStmt
*plannedstmt
= (PlannedStmt
*) lfirst(lc2
);
924 Assert(!IsA(plannedstmt
, Query
));
925 if (!IsA(plannedstmt
, PlannedStmt
))
926 continue; /* Ignore utility statements */
927 if ((relid
== InvalidOid
) ? plannedstmt
->relationOids
!= NIL
:
928 list_member_oid(plannedstmt
->relationOids
, relid
))
930 /* Invalidate the plan! */
932 break; /* out of stmt_list scan */
938 /* Otherwise check the single list we built ourselves */
939 if ((relid
== InvalidOid
) ? plan
->relationOids
!= NIL
:
940 list_member_oid(plan
->relationOids
, relid
))
947 * PlanCacheFuncCallback
948 * Syscache inval callback function for PROCOID cache
950 * Invalidate all plans mentioning the given catalog entry, or all plans
951 * mentioning any member of this cache if tuplePtr == NULL.
953 * Note that the coding would support use for multiple caches, but right
954 * now only user-defined functions are tracked this way.
957 PlanCacheFuncCallback(Datum arg
, int cacheid
, ItemPointer tuplePtr
)
961 foreach(lc1
, cached_plans_list
)
963 CachedPlanSource
*plansource
= (CachedPlanSource
*) lfirst(lc1
);
964 CachedPlan
*plan
= plansource
->plan
;
966 /* No work if it's already invalidated */
967 if (!plan
|| plan
->dead
)
969 if (plan
->fully_planned
)
971 /* Have to check the per-PlannedStmt inval-item lists */
974 foreach(lc2
, plan
->stmt_list
)
976 PlannedStmt
*plannedstmt
= (PlannedStmt
*) lfirst(lc2
);
979 Assert(!IsA(plannedstmt
, Query
));
980 if (!IsA(plannedstmt
, PlannedStmt
))
981 continue; /* Ignore utility statements */
982 foreach(lc3
, plannedstmt
->invalItems
)
984 PlanInvalItem
*item
= (PlanInvalItem
*) lfirst(lc3
);
986 if (item
->cacheId
!= cacheid
)
988 if (tuplePtr
== NULL
||
989 ItemPointerEquals(tuplePtr
, &item
->tupleId
))
991 /* Invalidate the plan! */
993 break; /* out of invalItems scan */
997 break; /* out of stmt_list scan */
1002 /* Otherwise check the single list we built ourselves */
1005 foreach(lc2
, plan
->invalItems
)
1007 PlanInvalItem
*item
= (PlanInvalItem
*) lfirst(lc2
);
1009 if (item
->cacheId
!= cacheid
)
1011 if (tuplePtr
== NULL
||
1012 ItemPointerEquals(tuplePtr
, &item
->tupleId
))
1014 /* Invalidate the plan! */
1024 * PlanCacheSysCallback
1025 * Syscache inval callback function for other caches
1027 * Just invalidate everything...
1030 PlanCacheSysCallback(Datum arg
, int cacheid
, ItemPointer tuplePtr
)
1036 * ResetPlanCache: drop all cached plans.
1039 ResetPlanCache(void)
1043 foreach(lc
, cached_plans_list
)
1045 CachedPlanSource
*plansource
= (CachedPlanSource
*) lfirst(lc
);
1046 CachedPlan
*plan
= plansource
->plan
;