Separate snapshot management code from tuple visibility code, create a
[PostgreSQL.git] / src / backend / utils / cache / plancache.c
blobd1ff90e646a1e6bd7548baf7a03d6a7ba10bc679
1 /*-------------------------------------------------------------------------
3 * plancache.c
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
11 * in the second case.
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
35 * IDENTIFICATION
36 * $PostgreSQL$
38 *-------------------------------------------------------------------------
40 #include "postgres.h"
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"
57 typedef struct
59 void (*callback) ();
60 void *arg;
61 } ScanQueryWalkerContext;
63 typedef struct
65 Oid inval_relid;
66 CachedPlan *plan;
67 } InvalRelidContext;
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,
80 void (*callback) (),
81 void *arg);
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.
95 void
96 InitPlanCache(void)
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
118 CachedPlanSource *
119 CreateCachedPlan(Node *raw_parse_tree,
120 const char *query_string,
121 const char *commandTag,
122 Oid *param_types,
123 int num_params,
124 int cursor_options,
125 List *stmt_list,
126 bool fully_planned,
127 bool fixed_result)
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,
139 "CachedPlanSource",
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 */
158 if (num_params > 0)
160 plansource->param_types = (Oid *) palloc(num_params * sizeof(Oid));
161 memcpy(plansource->param_types, param_types, num_params * sizeof(Oid));
163 else
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);
191 return plansource;
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
209 * short-lived.)
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
214 * CreateCachedPlan.
216 CachedPlanSource *
217 FastCreateCachedPlan(Node *raw_parse_tree,
218 char *query_string,
219 const char *commandTag,
220 Oid *param_types,
221 int num_params,
222 int cursor_options,
223 List *stmt_list,
224 bool fully_planned,
225 bool fixed_result,
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);
279 return plansource;
283 * StoreCachedPlan: store a built or rebuilt plan into a plancache entry.
285 * Common subroutine for CreateCachedPlan and RevalidateCachedPlan.
287 static void
288 StoreCachedPlan(CachedPlanSource *plansource,
289 List *stmt_list,
290 MemoryContext plan_context)
292 CachedPlan *plan;
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,
303 "CachedPlan",
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);
315 else
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;
327 plan->dead = false;
328 if (plansource->fully_planned && plan_list_is_transient(stmt_list))
330 Assert(TransactionIdIsNormal(TransactionXmin));
331 plan->saved_xmin = TransactionXmin;
333 else
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
351 * still in use.
353 void
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);
375 else
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.
396 CachedPlan *
397 RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
399 CachedPlan *plan;
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);
420 else
421 AcquirePlannerLocks(plan->stmt_list, true);
424 * If plan was transient, check to see if TransactionXmin has
425 * advanced, and if so invalidate it.
427 if (!plan->dead &&
428 TransactionIdIsValid(plan->saved_xmin) &&
429 !TransactionIdEquals(plan->saved_xmin, TransactionXmin))
430 plan->dead = true;
433 * By now, if any invalidation has happened, PlanCacheCallback will
434 * have marked the plan dead.
436 if (plan->dead)
438 /* Ooops, the race case happened. Release useless locks. */
439 if (plan->fully_planned)
440 AcquireExecutorLocks(plan->stmt_list, false);
441 else
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);
453 plan = NULL;
457 * Build a new plan if needed.
459 if (!plan)
461 List *slist;
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)
504 ereport(ERROR,
505 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
506 errmsg("cached plan must not change result type")));
507 oldcxt = MemoryContextSwitchTo(plansource->context);
508 if (resultDesc)
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
521 * count.
523 StoreCachedPlan(plansource, slist, NULL);
525 plan = plansource->plan;
529 * Last step: flag the plan as in use by caller.
531 if (useResOwner)
532 ResourceOwnerEnlargePlanCacheRefs(CurrentResourceOwner);
533 plan->refcount++;
534 if (useResOwner)
535 ResourceOwnerRememberPlanCacheRef(CurrentResourceOwner, plan);
537 return 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.
545 static List *
546 do_planning(List *querytrees, int cursorOptions)
548 List *stmt_list;
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);
562 else
564 PG_TRY();
566 stmt_list = pg_plan_queries(querytrees, cursorOptions, NULL, true);
568 PG_CATCH();
570 /* Restore global vars and propagate error */
571 ActiveSnapshot = NULL;
572 PG_RE_THROW();
574 PG_END_TRY();
576 ActiveSnapshot = NULL;
579 return stmt_list;
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.
594 void
595 ReleaseCachedPlan(CachedPlan *plan, bool useResOwner)
597 if (useResOwner)
598 ResourceOwnerForgetPlanCacheRef(CurrentResourceOwner, plan);
599 Assert(plan->refcount > 0);
600 plan->refcount--;
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.
609 static void
610 AcquireExecutorLocks(List *stmt_list, bool acquire)
612 ListCell *lc1;
614 foreach(lc1, stmt_list)
616 PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc1);
617 int rt_index;
618 ListCell *lc2;
620 Assert(!IsA(plannedstmt, Query));
621 if (!IsA(plannedstmt, PlannedStmt))
622 continue; /* Ignore utility statements */
624 rt_index = 0;
625 foreach(lc2, plannedstmt->rtable)
627 RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
628 LOCKMODE lockmode;
630 rt_index++;
632 if (rte->rtekind != RTE_RELATION)
633 continue;
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;
645 else
646 lockmode = AccessShareLock;
648 if (acquire)
649 LockRelationOid(rte->relid, lockmode);
650 else
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.
664 static void
665 AcquirePlannerLocks(List *stmt_list, bool acquire)
667 ListCell *lc;
669 foreach(lc, stmt_list)
671 Query *query = (Query *) lfirst(lc);
673 Assert(IsA(query, Query));
674 if (acquire)
675 ScanQueryForRelids(query, LockRelid, NULL);
676 else
677 ScanQueryForRelids(query, UnlockRelid, NULL);
682 * ScanQueryForRelids callback functions for AcquirePlannerLocks
684 static void
685 LockRelid(Oid relid, LOCKMODE lockmode, void *arg)
687 LockRelationOid(relid, lockmode);
690 static void
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.
701 static void
702 ScanQueryForRelids(Query *parsetree,
703 void (*callback) (),
704 void *arg)
706 ListCell *lc;
707 int rt_index;
710 * First, process RTEs of the current query level.
712 rt_index = 0;
713 foreach(lc, parsetree->rtable)
715 RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
716 LOCKMODE lockmode;
718 rt_index++;
719 switch (rte->rtekind)
721 case RTE_RELATION:
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;
730 else
731 lockmode = AccessShareLock;
733 (*callback) (rte->relid, lockmode, arg);
734 break;
736 case RTE_SUBQUERY:
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);
743 break;
745 default:
746 /* ignore other types of RTEs */
747 break;
752 * Recurse into sublink subqueries, too. But we already did the ones in
753 * the rtable.
755 if (parsetree->hasSubLinks)
757 ScanQueryWalkerContext context;
759 context.callback = callback;
760 context.arg = arg;
761 query_tree_walker(parsetree, ScanQueryWalker,
762 (void *) &context,
763 QTW_IGNORE_RT_SUBQUERIES);
768 * Walker to find sublink subqueries for ScanQueryForRelids
770 static bool
771 ScanQueryWalker(Node *node, ScanQueryWalkerContext *context)
773 if (node == NULL)
774 return false;
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,
790 (void *) context);
794 * rowmark_member: check whether an RT index appears in a RowMarkClause list.
796 static bool
797 rowmark_member(List *rowMarks, int rt_index)
799 ListCell *l;
801 foreach(l, rowMarks)
803 RowMarkClause *rc = (RowMarkClause *) lfirst(l);
805 if (rc->rti == rt_index)
806 return true;
808 return false;
812 * plan_list_is_transient: check if any of the plans in the list are transient.
814 static bool
815 plan_list_is_transient(List *stmt_list)
817 ListCell *lc;
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)
827 return true;
830 return false;
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.
840 TupleDesc
841 PlanCacheComputeResultDesc(List *stmt_list)
843 Node *node;
844 Query *query;
845 PlannedStmt *pstmt;
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 */
862 break;
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 */
879 break;
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 */
894 break;
896 return NULL;
900 * PlanCacheCallback
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.
906 static void
907 PlanCacheCallback(Datum arg, Oid relid)
909 ListCell *lc1;
910 ListCell *lc2;
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)
919 continue;
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! */
933 plan->dead = true;
934 break; /* out of stmt_list scan */
938 else
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;
948 context.plan = plan;
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.
964 void
965 ResetPlanCache(void)
967 PlanCacheCallback((Datum) 0, InvalidOid);
971 * ScanQueryForRelids callback function for PlanCacheCallback
973 static void
974 InvalRelid(Oid relid, LOCKMODE lockmode, InvalRelidContext *context)
976 if (relid == context->inval_relid || context->inval_relid == InvalidOid)
977 context->plan->dead = true;