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-2007, PostgreSQL Global Development Group
33 * Portions Copyright (c) 1994, Regents of the University of California
36 * $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.12 2007/10/11 18:05:27 tgl Exp $
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"
60 } ScanQueryWalkerContext;
69 static List *cached_plans_list = NIL;
71 static void StoreCachedPlan(CachedPlanSource *plansource, List *stmt_list,
72 MemoryContext plan_context);
73 static List *do_planning(List *querytrees, int cursorOptions);
74 static void AcquireExecutorLocks(List *stmt_list, bool acquire);
75 static void AcquirePlannerLocks(List *stmt_list, bool acquire);
76 static void LockRelid(Oid relid, LOCKMODE lockmode, void *arg);
77 static void UnlockRelid(Oid relid, LOCKMODE lockmode, void *arg);
78 static void ScanQueryForRelids(Query *parsetree,
81 static bool ScanQueryWalker(Node *node, ScanQueryWalkerContext *context);
82 static bool rowmark_member(List *rowMarks, int rt_index);
83 static bool plan_list_is_transient(List *stmt_list);
84 static void PlanCacheCallback(Datum arg, Oid relid);
85 static void InvalRelid(Oid relid, LOCKMODE lockmode,
86 InvalRelidContext *context);
90 * InitPlanCache: initialize module during InitPostgres.
92 * All we need to do is hook into inval.c's callback list.
97 CacheRegisterRelcacheCallback(PlanCacheCallback, (Datum) 0);
101 * CreateCachedPlan: initially create a plan cache entry.
103 * The caller must already have successfully parsed/planned the query;
104 * about all that we do here is copy it into permanent storage.
106 * raw_parse_tree: output of raw_parser()
107 * query_string: original query text (can be NULL if not available, but
108 * that is discouraged because it degrades error message quality)
109 * commandTag: compile-time-constant tag for query, or NULL if empty query
110 * param_types: array of parameter type OIDs, or NULL if none
111 * num_params: number of parameters
112 * cursor_options: options bitmask that was/will be passed to planner
113 * stmt_list: list of PlannedStmts/utility stmts, or list of Query trees
114 * fully_planned: are we caching planner or rewriter output?
115 * fixed_result: TRUE to disallow changes in result tupdesc
118 CreateCachedPlan(Node *raw_parse_tree,
119 const char *query_string,
120 const char *commandTag,
128 CachedPlanSource *plansource;
129 OverrideSearchPath *search_path;
130 MemoryContext source_context;
131 MemoryContext oldcxt;
134 * Make a dedicated memory context for the CachedPlanSource and its
135 * subsidiary data. We expect it can be pretty small.
137 source_context = AllocSetContextCreate(CacheMemoryContext,
139 ALLOCSET_SMALL_MINSIZE,
140 ALLOCSET_SMALL_INITSIZE,
141 ALLOCSET_SMALL_MAXSIZE);
144 * Fetch current search_path into new context, but do any recalculation
145 * work required in caller's context.
147 search_path = GetOverrideSearchPath(source_context);
150 * Create and fill the CachedPlanSource struct within the new context.
152 oldcxt = MemoryContextSwitchTo(source_context);
153 plansource = (CachedPlanSource *) palloc(sizeof(CachedPlanSource));
154 plansource->raw_parse_tree = copyObject(raw_parse_tree);
155 plansource->query_string = query_string ? pstrdup(query_string) : NULL;
156 plansource->commandTag = commandTag; /* no copying needed */
159 plansource->param_types = (Oid *) palloc(num_params * sizeof(Oid));
160 memcpy(plansource->param_types, param_types, num_params * sizeof(Oid));
163 plansource->param_types = NULL;
164 plansource->num_params = num_params;
165 plansource->cursor_options = cursor_options;
166 plansource->fully_planned = fully_planned;
167 plansource->fixed_result = fixed_result;
168 plansource->search_path = search_path;
169 plansource->generation = 0; /* StoreCachedPlan will increment */
170 plansource->resultDesc = PlanCacheComputeResultDesc(stmt_list);
171 plansource->plan = NULL;
172 plansource->context = source_context;
173 plansource->orig_plan = NULL;
176 * Copy the current output plans into the plancache entry.
178 StoreCachedPlan(plansource, stmt_list, NULL);
181 * Now we can add the entry to the list of cached plans. The List nodes
182 * live in CacheMemoryContext.
184 MemoryContextSwitchTo(CacheMemoryContext);
186 cached_plans_list = lappend(cached_plans_list, plansource);
188 MemoryContextSwitchTo(oldcxt);
194 * FastCreateCachedPlan: create a plan cache entry with minimal data copying.
196 * For plans that aren't expected to live very long, the copying overhead of
197 * CreateCachedPlan is annoying. We provide this variant entry point in which
198 * the caller has already placed all the data in a suitable memory context.
199 * The source data and completed plan are in the same context, since this
200 * avoids extra copy steps during plan construction. If the query ever does
201 * need replanning, we'll generate a separate new CachedPlan at that time, but
202 * the CachedPlanSource and the initial CachedPlan share the caller-provided
203 * context and go away together when neither is needed any longer. (Because
204 * the parser and planner generate extra cruft in addition to their real
205 * output, this approach means that the context probably contains a bunch of
206 * useless junk as well as the useful trees. Hence, this method is a
207 * space-for-time tradeoff, which is worth making for plans expected to be
210 * raw_parse_tree, query_string, param_types, and stmt_list must reside in the
211 * given context, which must have adequate lifespan (recommendation: make it a
212 * child of CacheMemoryContext). Otherwise the API is the same as
216 FastCreateCachedPlan(Node *raw_parse_tree,
218 const char *commandTag,
225 MemoryContext context)
227 CachedPlanSource *plansource;
228 OverrideSearchPath *search_path;
229 MemoryContext oldcxt;
232 * Fetch current search_path into given context, but do any recalculation
233 * work required in caller's context.
235 search_path = GetOverrideSearchPath(context);
238 * Create and fill the CachedPlanSource struct within the given context.
240 oldcxt = MemoryContextSwitchTo(context);
241 plansource = (CachedPlanSource *) palloc(sizeof(CachedPlanSource));
242 plansource->raw_parse_tree = raw_parse_tree;
243 plansource->query_string = query_string;
244 plansource->commandTag = commandTag; /* no copying needed */
245 plansource->param_types = param_types;
246 plansource->num_params = num_params;
247 plansource->cursor_options = cursor_options;
248 plansource->fully_planned = fully_planned;
249 plansource->fixed_result = fixed_result;
250 plansource->search_path = search_path;
251 plansource->generation = 0; /* StoreCachedPlan will increment */
252 plansource->resultDesc = PlanCacheComputeResultDesc(stmt_list);
253 plansource->plan = NULL;
254 plansource->context = context;
255 plansource->orig_plan = NULL;
258 * Store the current output plans into the plancache entry.
260 StoreCachedPlan(plansource, stmt_list, context);
263 * Since the context is owned by the CachedPlan, advance its refcount.
265 plansource->orig_plan = plansource->plan;
266 plansource->orig_plan->refcount++;
269 * Now we can add the entry to the list of cached plans. The List nodes
270 * live in CacheMemoryContext.
272 MemoryContextSwitchTo(CacheMemoryContext);
274 cached_plans_list = lappend(cached_plans_list, plansource);
276 MemoryContextSwitchTo(oldcxt);
282 * StoreCachedPlan: store a built or rebuilt plan into a plancache entry.
284 * Common subroutine for CreateCachedPlan and RevalidateCachedPlan.
287 StoreCachedPlan(CachedPlanSource *plansource,
289 MemoryContext plan_context)
292 MemoryContext oldcxt;
294 if (plan_context == NULL)
297 * Make a dedicated memory context for the CachedPlan and its
298 * subsidiary data. It's probably not going to be large, but
299 * just in case, use the default maxsize parameter.
301 plan_context = AllocSetContextCreate(CacheMemoryContext,
303 ALLOCSET_SMALL_MINSIZE,
304 ALLOCSET_SMALL_INITSIZE,
305 ALLOCSET_DEFAULT_MAXSIZE);
308 * Copy supplied data into the new context.
310 oldcxt = MemoryContextSwitchTo(plan_context);
312 stmt_list = (List *) copyObject(stmt_list);
316 /* Assume subsidiary data is in the given context */
317 oldcxt = MemoryContextSwitchTo(plan_context);
321 * Create and fill the CachedPlan struct within the new context.
323 plan = (CachedPlan *) palloc(sizeof(CachedPlan));
324 plan->stmt_list = stmt_list;
325 plan->fully_planned = plansource->fully_planned;
327 if (plansource->fully_planned && plan_list_is_transient(stmt_list))
329 Assert(TransactionIdIsNormal(TransactionXmin));
330 plan->saved_xmin = TransactionXmin;
333 plan->saved_xmin = InvalidTransactionId;
334 plan->refcount = 1; /* for the parent's link */
335 plan->generation = ++(plansource->generation);
336 plan->context = plan_context;
338 Assert(plansource->plan == NULL);
339 plansource->plan = plan;
341 MemoryContextSwitchTo(oldcxt);
345 * DropCachedPlan: destroy a cached plan.
347 * Actually this only destroys the CachedPlanSource: the referenced CachedPlan
348 * is released, but not destroyed until its refcount goes to zero. That
349 * handles the situation where DropCachedPlan is called while the plan is
353 DropCachedPlan(CachedPlanSource *plansource)
355 /* Validity check that we were given a CachedPlanSource */
356 Assert(list_member_ptr(cached_plans_list, plansource));
358 /* Remove it from the list */
359 cached_plans_list = list_delete_ptr(cached_plans_list, plansource);
361 /* Decrement child CachePlan's refcount and drop if no longer needed */
362 if (plansource->plan)
363 ReleaseCachedPlan(plansource->plan, false);
366 * If CachedPlanSource has independent storage, just drop it. Otherwise
367 * decrement the refcount on the CachePlan that owns the storage.
369 if (plansource->orig_plan == NULL)
371 /* Remove the CachedPlanSource and all subsidiary data */
372 MemoryContextDelete(plansource->context);
376 Assert(plansource->context == plansource->orig_plan->context);
377 ReleaseCachedPlan(plansource->orig_plan, false);
382 * RevalidateCachedPlan: prepare for re-use of a previously cached plan.
384 * What we do here is re-acquire locks and rebuild the plan if necessary.
385 * On return, the plan is valid and we have sufficient locks to begin
386 * execution (or planning, if not fully_planned).
388 * On return, the refcount of the plan has been incremented; a later
389 * ReleaseCachedPlan() call is expected. The refcount has been reported
390 * to the CurrentResourceOwner if useResOwner is true.
392 * Note: if any replanning activity is required, the caller's memory context
393 * is used for that work.
396 RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
400 /* Validity check that we were given a CachedPlanSource */
401 Assert(list_member_ptr(cached_plans_list, plansource));
404 * If the plan currently appears valid, acquire locks on the referenced
405 * objects; then check again. We need to do it this way to cover the
406 * race condition that an invalidation message arrives before we get
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
434 * will 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
558 * been 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.
637 * Note that we don't actually try to open the rel, and hence
638 * will not fail if it's been dropped entirely --- we'll just
639 * transiently 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)
723 * Determine the lock type required for this RTE.
725 if (rt_index == parsetree->resultRelation)
726 lockmode = RowExclusiveLock;
727 else if (rowmark_member(parsetree->rowMarks, rt_index))
728 lockmode = RowShareLock;
730 lockmode = AccessShareLock;
732 (*callback) (rte->relid, lockmode, arg);
738 * The subquery RTE itself is all right, but we have to
739 * recurse to process the represented subquery.
741 ScanQueryForRelids(rte->subquery, callback, arg);
745 /* ignore other types of RTEs */
751 * Recurse into sublink subqueries, too. But we already did the ones in
754 if (parsetree->hasSubLinks)
756 ScanQueryWalkerContext context;
758 context.callback = callback;
760 query_tree_walker(parsetree, ScanQueryWalker,
762 QTW_IGNORE_RT_SUBQUERIES);
767 * Walker to find sublink subqueries for ScanQueryForRelids
770 ScanQueryWalker(Node *node, ScanQueryWalkerContext *context)
774 if (IsA(node, SubLink))
776 SubLink *sub = (SubLink *) node;
778 /* Do what we came for */
779 ScanQueryForRelids((Query *) sub->subselect,
780 context->callback, context->arg);
781 /* Fall through to process lefthand args of SubLink */
785 * Do NOT recurse into Query nodes, because ScanQueryForRelids
786 * already processed subselects of subselects for us.
788 return expression_tree_walker(node, ScanQueryWalker,
793 * rowmark_member: check whether an RT index appears in a RowMarkClause list.
796 rowmark_member(List *rowMarks, int rt_index)
802 RowMarkClause *rc = (RowMarkClause *) lfirst(l);
804 if (rc->rti == rt_index)
811 * plan_list_is_transient: check if any of the plans in the list are transient.
814 plan_list_is_transient(List *stmt_list)
818 foreach(lc, stmt_list)
820 PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
822 if (!IsA(plannedstmt, PlannedStmt))
823 continue; /* Ignore utility statements */
825 if (plannedstmt->transientPlan)
833 * PlanCacheComputeResultDesc: given a list of either fully-planned statements
834 * or Queries, determine the result tupledesc it will produce. Returns NULL
835 * if the execution will not return tuples.
837 * Note: the result is created or copied into current memory context.
840 PlanCacheComputeResultDesc(List *stmt_list)
846 switch (ChoosePortalStrategy(stmt_list))
848 case PORTAL_ONE_SELECT:
849 node = (Node *) linitial(stmt_list);
850 if (IsA(node, Query))
852 query = (Query *) node;
853 return ExecCleanTypeFromTL(query->targetList, false);
855 if (IsA(node, PlannedStmt))
857 pstmt = (PlannedStmt *) node;
858 return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false);
860 /* other cases shouldn't happen, but return NULL */
863 case PORTAL_ONE_RETURNING:
864 node = PortalListGetPrimaryStmt(stmt_list);
865 if (IsA(node, Query))
867 query = (Query *) node;
868 Assert(query->returningList);
869 return ExecCleanTypeFromTL(query->returningList, false);
871 if (IsA(node, PlannedStmt))
873 pstmt = (PlannedStmt *) node;
874 Assert(pstmt->returningLists);
875 return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false);
877 /* other cases shouldn't happen, but return NULL */
880 case PORTAL_UTIL_SELECT:
881 node = (Node *) linitial(stmt_list);
882 if (IsA(node, Query))
884 query = (Query *) node;
885 Assert(query->utilityStmt);
886 return UtilityTupleDescriptor(query->utilityStmt);
888 /* else it's a bare utility statement */
889 return UtilityTupleDescriptor(node);
891 case PORTAL_MULTI_QUERY:
892 /* will not return tuples */
900 * Relcache inval callback function
902 * Invalidate all plans mentioning the given rel, or all plans mentioning
903 * any rel at all if relid == InvalidOid.
906 PlanCacheCallback(Datum arg, Oid relid)
911 foreach(lc1, cached_plans_list)
913 CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
914 CachedPlan *plan = plansource->plan;
916 /* No work if it's already invalidated */
917 if (!plan || plan->dead)
919 if (plan->fully_planned)
921 foreach(lc2, plan->stmt_list)
923 PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2);
925 Assert(!IsA(plannedstmt, Query));
926 if (!IsA(plannedstmt, PlannedStmt))
927 continue; /* Ignore utility statements */
928 if ((relid == InvalidOid) ? plannedstmt->relationOids != NIL :
929 list_member_oid(plannedstmt->relationOids, relid))
931 /* Invalidate the plan! */
933 break; /* out of stmt_list scan */
940 * For not-fully-planned entries we use ScanQueryForRelids,
941 * since a recursive traversal is needed. The callback API
942 * is a bit tedious but avoids duplication of coding.
944 InvalRelidContext context;
946 context.inval_relid = relid;
949 foreach(lc2, plan->stmt_list)
951 Query *query = (Query *) lfirst(lc2);
953 Assert(IsA(query, Query));
954 ScanQueryForRelids(query, InvalRelid, (void *) &context);
961 * ResetPlanCache: drop all cached plans.
966 PlanCacheCallback((Datum) 0, InvalidOid);
970 * ScanQueryForRelids callback function for PlanCacheCallback
973 InvalRelid(Oid relid, LOCKMODE lockmode, InvalRelidContext *context)
975 if (relid == context->inval_relid || context->inval_relid == InvalidOid)
976 context->plan->dead = true;