]> granicus.if.org Git - postgresql/blob - src/backend/utils/cache/plancache.c
c0dc3649ac0e308730b000059c636cb3fc9ecc1a
[postgresql] / src / backend / utils / cache / plancache.c
1 /*-------------------------------------------------------------------------
2  *
3  * plancache.c
4  *        Plan cache management.
5  *
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.
12  *
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.
22  *
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.
30  *
31  *
32  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
33  * Portions Copyright (c) 1994, Regents of the University of California
34  *
35  * IDENTIFICATION
36  *        $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.20 2008/08/25 22:42:34 tgl Exp $
37  *
38  *-------------------------------------------------------------------------
39  */
40 #include "postgres.h"
41
42 #include "utils/plancache.h"
43 #include "access/transam.h"
44 #include "catalog/namespace.h"
45 #include "executor/executor.h"
46 #include "nodes/nodeFuncs.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/snapmgr.h"
55
56
57 typedef struct
58 {
59         void            (*callback) ();
60         void       *arg;
61 } ScanQueryWalkerContext;
62
63 typedef struct
64 {
65         Oid                     inval_relid;
66         CachedPlan *plan;
67 } InvalRelidContext;
68
69
70 static List *cached_plans_list = NIL;
71
72 static void StoreCachedPlan(CachedPlanSource *plansource, List *stmt_list,
73                                 MemoryContext plan_context);
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,
79                                    void (*callback) (),
80                                    void *arg);
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);
87
88
89 /*
90  * InitPlanCache: initialize module during InitPostgres.
91  *
92  * All we need to do is hook into inval.c's callback list.
93  */
94 void
95 InitPlanCache(void)
96 {
97         CacheRegisterRelcacheCallback(PlanCacheCallback, (Datum) 0);
98 }
99
100 /*
101  * CreateCachedPlan: initially create a plan cache entry.
102  *
103  * The caller must already have successfully parsed/planned the query;
104  * about all that we do here is copy it into permanent storage.
105  *
106  * raw_parse_tree: output of raw_parser()
107  * query_string: original query text (as of PG 8.4, must not be NULL)
108  * commandTag: compile-time-constant tag for query, or NULL if empty query
109  * param_types: array of parameter type OIDs, or NULL if none
110  * num_params: number of parameters
111  * cursor_options: options bitmask that was/will be passed to planner
112  * stmt_list: list of PlannedStmts/utility stmts, or list of Query trees
113  * fully_planned: are we caching planner or rewriter output?
114  * fixed_result: TRUE to disallow changes in result tupdesc
115  */
116 CachedPlanSource *
117 CreateCachedPlan(Node *raw_parse_tree,
118                                  const char *query_string,
119                                  const char *commandTag,
120                                  Oid *param_types,
121                                  int num_params,
122                                  int cursor_options,
123                                  List *stmt_list,
124                                  bool fully_planned,
125                                  bool fixed_result)
126 {
127         CachedPlanSource *plansource;
128         OverrideSearchPath *search_path;
129         MemoryContext source_context;
130         MemoryContext oldcxt;
131
132         Assert(query_string != NULL);                           /* required as of 8.4 */
133
134         /*
135          * Make a dedicated memory context for the CachedPlanSource and its
136          * subsidiary data.  We expect it can be pretty small.
137          */
138         source_context = AllocSetContextCreate(CacheMemoryContext,
139                                                                                    "CachedPlanSource",
140                                                                                    ALLOCSET_SMALL_MINSIZE,
141                                                                                    ALLOCSET_SMALL_INITSIZE,
142                                                                                    ALLOCSET_SMALL_MAXSIZE);
143
144         /*
145          * Fetch current search_path into new context, but do any recalculation
146          * work required in caller's context.
147          */
148         search_path = GetOverrideSearchPath(source_context);
149
150         /*
151          * Create and fill the CachedPlanSource struct within the new context.
152          */
153         oldcxt = MemoryContextSwitchTo(source_context);
154         plansource = (CachedPlanSource *) palloc(sizeof(CachedPlanSource));
155         plansource->raw_parse_tree = copyObject(raw_parse_tree);
156         plansource->query_string = pstrdup(query_string);
157         plansource->commandTag = commandTag;            /* no copying needed */
158         if (num_params > 0)
159         {
160                 plansource->param_types = (Oid *) palloc(num_params * sizeof(Oid));
161                 memcpy(plansource->param_types, param_types, num_params * sizeof(Oid));
162         }
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;
175
176         /*
177          * Copy the current output plans into the plancache entry.
178          */
179         StoreCachedPlan(plansource, stmt_list, NULL);
180
181         /*
182          * Now we can add the entry to the list of cached plans.  The List nodes
183          * live in CacheMemoryContext.
184          */
185         MemoryContextSwitchTo(CacheMemoryContext);
186
187         cached_plans_list = lappend(cached_plans_list, plansource);
188
189         MemoryContextSwitchTo(oldcxt);
190
191         return plansource;
192 }
193
194 /*
195  * FastCreateCachedPlan: create a plan cache entry with minimal data copying.
196  *
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.)
210  *
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.
215  */
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)
227 {
228         CachedPlanSource *plansource;
229         OverrideSearchPath *search_path;
230         MemoryContext oldcxt;
231
232         Assert(query_string != NULL);                           /* required as of 8.4 */
233
234         /*
235          * Fetch current search_path into given context, but do any recalculation
236          * work required in caller's context.
237          */
238         search_path = GetOverrideSearchPath(context);
239
240         /*
241          * Create and fill the CachedPlanSource struct within the given context.
242          */
243         oldcxt = MemoryContextSwitchTo(context);
244         plansource = (CachedPlanSource *) palloc(sizeof(CachedPlanSource));
245         plansource->raw_parse_tree = raw_parse_tree;
246         plansource->query_string = query_string;
247         plansource->commandTag = commandTag;            /* no copying needed */
248         plansource->param_types = param_types;
249         plansource->num_params = num_params;
250         plansource->cursor_options = cursor_options;
251         plansource->fully_planned = fully_planned;
252         plansource->fixed_result = fixed_result;
253         plansource->search_path = search_path;
254         plansource->generation = 0; /* StoreCachedPlan will increment */
255         plansource->resultDesc = PlanCacheComputeResultDesc(stmt_list);
256         plansource->plan = NULL;
257         plansource->context = context;
258         plansource->orig_plan = NULL;
259
260         /*
261          * Store the current output plans into the plancache entry.
262          */
263         StoreCachedPlan(plansource, stmt_list, context);
264
265         /*
266          * Since the context is owned by the CachedPlan, advance its refcount.
267          */
268         plansource->orig_plan = plansource->plan;
269         plansource->orig_plan->refcount++;
270
271         /*
272          * Now we can add the entry to the list of cached plans.  The List nodes
273          * live in CacheMemoryContext.
274          */
275         MemoryContextSwitchTo(CacheMemoryContext);
276
277         cached_plans_list = lappend(cached_plans_list, plansource);
278
279         MemoryContextSwitchTo(oldcxt);
280
281         return plansource;
282 }
283
284 /*
285  * StoreCachedPlan: store a built or rebuilt plan into a plancache entry.
286  *
287  * Common subroutine for CreateCachedPlan and RevalidateCachedPlan.
288  */
289 static void
290 StoreCachedPlan(CachedPlanSource *plansource,
291                                 List *stmt_list,
292                                 MemoryContext plan_context)
293 {
294         CachedPlan *plan;
295         MemoryContext oldcxt;
296
297         if (plan_context == NULL)
298         {
299                 /*
300                  * Make a dedicated memory context for the CachedPlan and its
301                  * subsidiary data.  It's probably not going to be large, but just in
302                  * case, use the default maxsize parameter.
303                  */
304                 plan_context = AllocSetContextCreate(CacheMemoryContext,
305                                                                                          "CachedPlan",
306                                                                                          ALLOCSET_SMALL_MINSIZE,
307                                                                                          ALLOCSET_SMALL_INITSIZE,
308                                                                                          ALLOCSET_DEFAULT_MAXSIZE);
309
310                 /*
311                  * Copy supplied data into the new context.
312                  */
313                 oldcxt = MemoryContextSwitchTo(plan_context);
314
315                 stmt_list = (List *) copyObject(stmt_list);
316         }
317         else
318         {
319                 /* Assume subsidiary data is in the given context */
320                 oldcxt = MemoryContextSwitchTo(plan_context);
321         }
322
323         /*
324          * Create and fill the CachedPlan struct within the new context.
325          */
326         plan = (CachedPlan *) palloc(sizeof(CachedPlan));
327         plan->stmt_list = stmt_list;
328         plan->fully_planned = plansource->fully_planned;
329         plan->dead = false;
330         if (plansource->fully_planned && plan_list_is_transient(stmt_list))
331         {
332                 Assert(TransactionIdIsNormal(TransactionXmin));
333                 plan->saved_xmin = TransactionXmin;
334         }
335         else
336                 plan->saved_xmin = InvalidTransactionId;
337         plan->refcount = 1;                     /* for the parent's link */
338         plan->generation = ++(plansource->generation);
339         plan->context = plan_context;
340
341         Assert(plansource->plan == NULL);
342         plansource->plan = plan;
343
344         MemoryContextSwitchTo(oldcxt);
345 }
346
347 /*
348  * DropCachedPlan: destroy a cached plan.
349  *
350  * Actually this only destroys the CachedPlanSource: the referenced CachedPlan
351  * is released, but not destroyed until its refcount goes to zero.      That
352  * handles the situation where DropCachedPlan is called while the plan is
353  * still in use.
354  */
355 void
356 DropCachedPlan(CachedPlanSource *plansource)
357 {
358         /* Validity check that we were given a CachedPlanSource */
359         Assert(list_member_ptr(cached_plans_list, plansource));
360
361         /* Remove it from the list */
362         cached_plans_list = list_delete_ptr(cached_plans_list, plansource);
363
364         /* Decrement child CachePlan's refcount and drop if no longer needed */
365         if (plansource->plan)
366                 ReleaseCachedPlan(plansource->plan, false);
367
368         /*
369          * If CachedPlanSource has independent storage, just drop it.  Otherwise
370          * decrement the refcount on the CachePlan that owns the storage.
371          */
372         if (plansource->orig_plan == NULL)
373         {
374                 /* Remove the CachedPlanSource and all subsidiary data */
375                 MemoryContextDelete(plansource->context);
376         }
377         else
378         {
379                 Assert(plansource->context == plansource->orig_plan->context);
380                 ReleaseCachedPlan(plansource->orig_plan, false);
381         }
382 }
383
384 /*
385  * RevalidateCachedPlan: prepare for re-use of a previously cached plan.
386  *
387  * What we do here is re-acquire locks and rebuild the plan if necessary.
388  * On return, the plan is valid and we have sufficient locks to begin
389  * execution (or planning, if not fully_planned).
390  *
391  * On return, the refcount of the plan has been incremented; a later
392  * ReleaseCachedPlan() call is expected.  The refcount has been reported
393  * to the CurrentResourceOwner if useResOwner is true.
394  *
395  * Note: if any replanning activity is required, the caller's memory context
396  * is used for that work.
397  */
398 CachedPlan *
399 RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
400 {
401         CachedPlan *plan;
402
403         /* Validity check that we were given a CachedPlanSource */
404         Assert(list_member_ptr(cached_plans_list, plansource));
405
406         /*
407          * If the plan currently appears valid, acquire locks on the referenced
408          * objects; then check again.  We need to do it this way to cover the race
409          * condition that an invalidation message arrives before we get the lock.
410          */
411         plan = plansource->plan;
412         if (plan && !plan->dead)
413         {
414                 /*
415                  * Plan must have positive refcount because it is referenced by
416                  * plansource; so no need to fear it disappears under us here.
417                  */
418                 Assert(plan->refcount > 0);
419
420                 if (plan->fully_planned)
421                         AcquireExecutorLocks(plan->stmt_list, true);
422                 else
423                         AcquirePlannerLocks(plan->stmt_list, true);
424
425                 /*
426                  * If plan was transient, check to see if TransactionXmin has
427                  * advanced, and if so invalidate it.
428                  */
429                 if (!plan->dead &&
430                         TransactionIdIsValid(plan->saved_xmin) &&
431                         !TransactionIdEquals(plan->saved_xmin, TransactionXmin))
432                         plan->dead = true;
433
434                 /*
435                  * By now, if any invalidation has happened, PlanCacheCallback will
436                  * have marked the plan dead.
437                  */
438                 if (plan->dead)
439                 {
440                         /* Ooops, the race case happened.  Release useless locks. */
441                         if (plan->fully_planned)
442                                 AcquireExecutorLocks(plan->stmt_list, false);
443                         else
444                                 AcquirePlannerLocks(plan->stmt_list, false);
445                 }
446         }
447
448         /*
449          * If plan has been invalidated, unlink it from the parent and release it.
450          */
451         if (plan && plan->dead)
452         {
453                 plansource->plan = NULL;
454                 ReleaseCachedPlan(plan, false);
455                 plan = NULL;
456         }
457
458         /*
459          * Build a new plan if needed.
460          */
461         if (!plan)
462         {
463                 List       *slist;
464                 TupleDesc       resultDesc;
465
466                 /*
467                  * Restore the search_path that was in use when the plan was made.
468                  * (XXX is there anything else we really need to restore?)
469                  */
470                 PushOverrideSearchPath(plansource->search_path);
471
472                 /*
473                  * Run parse analysis and rule rewriting.  The parser tends to
474                  * scribble on its input, so we must copy the raw parse tree to
475                  * prevent corruption of the cache.  Note that we do not use
476                  * parse_analyze_varparams(), assuming that the caller never wants the
477                  * parameter types to change from the original values.
478                  */
479                 slist = pg_analyze_and_rewrite(copyObject(plansource->raw_parse_tree),
480                                                                            plansource->query_string,
481                                                                            plansource->param_types,
482                                                                            plansource->num_params);
483
484                 if (plansource->fully_planned)
485                 {
486                         /*
487                          * Generate plans for queries.
488                          *
489                          * If a snapshot is already set (the normal case), we can just use
490                          * that for planning.  But if it isn't, we have to tell
491                          * pg_plan_queries to make a snap if it needs one.
492                          */
493                         slist = pg_plan_queries(slist, plansource->cursor_options,
494                                                                         NULL, !ActiveSnapshotSet());
495                 }
496
497                 /*
498                  * Check or update the result tupdesc.  XXX should we use a weaker
499                  * condition than equalTupleDescs() here?
500                  */
501                 resultDesc = PlanCacheComputeResultDesc(slist);
502                 if (resultDesc == NULL && plansource->resultDesc == NULL)
503                 {
504                         /* OK, doesn't return tuples */
505                 }
506                 else if (resultDesc == NULL || plansource->resultDesc == NULL ||
507                                  !equalTupleDescs(resultDesc, plansource->resultDesc))
508                 {
509                         MemoryContext oldcxt;
510
511                         /* can we give a better error message? */
512                         if (plansource->fixed_result)
513                                 ereport(ERROR,
514                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
515                                                  errmsg("cached plan must not change result type")));
516                         oldcxt = MemoryContextSwitchTo(plansource->context);
517                         if (resultDesc)
518                                 resultDesc = CreateTupleDescCopy(resultDesc);
519                         if (plansource->resultDesc)
520                                 FreeTupleDesc(plansource->resultDesc);
521                         plansource->resultDesc = resultDesc;
522                         MemoryContextSwitchTo(oldcxt);
523                 }
524
525                 /* Now we can restore current search path */
526                 PopOverrideSearchPath();
527
528                 /*
529                  * Store the plans into the plancache entry, advancing the generation
530                  * count.
531                  */
532                 StoreCachedPlan(plansource, slist, NULL);
533
534                 plan = plansource->plan;
535         }
536
537         /*
538          * Last step: flag the plan as in use by caller.
539          */
540         if (useResOwner)
541                 ResourceOwnerEnlargePlanCacheRefs(CurrentResourceOwner);
542         plan->refcount++;
543         if (useResOwner)
544                 ResourceOwnerRememberPlanCacheRef(CurrentResourceOwner, plan);
545
546         return plan;
547 }
548
549 /*
550  * ReleaseCachedPlan: release active use of a cached plan.
551  *
552  * This decrements the reference count, and frees the plan if the count
553  * has thereby gone to zero.  If useResOwner is true, it is assumed that
554  * the reference count is managed by the CurrentResourceOwner.
555  *
556  * Note: useResOwner = false is used for releasing references that are in
557  * persistent data structures, such as the parent CachedPlanSource or a
558  * Portal.      Transient references should be protected by a resource owner.
559  */
560 void
561 ReleaseCachedPlan(CachedPlan *plan, bool useResOwner)
562 {
563         if (useResOwner)
564                 ResourceOwnerForgetPlanCacheRef(CurrentResourceOwner, plan);
565         Assert(plan->refcount > 0);
566         plan->refcount--;
567         if (plan->refcount == 0)
568                 MemoryContextDelete(plan->context);
569 }
570
571 /*
572  * AcquireExecutorLocks: acquire locks needed for execution of a fully-planned
573  * cached plan; or release them if acquire is false.
574  */
575 static void
576 AcquireExecutorLocks(List *stmt_list, bool acquire)
577 {
578         ListCell   *lc1;
579
580         foreach(lc1, stmt_list)
581         {
582                 PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc1);
583                 int                     rt_index;
584                 ListCell   *lc2;
585
586                 Assert(!IsA(plannedstmt, Query));
587                 if (!IsA(plannedstmt, PlannedStmt))
588                         continue;                       /* Ignore utility statements */
589
590                 rt_index = 0;
591                 foreach(lc2, plannedstmt->rtable)
592                 {
593                         RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
594                         LOCKMODE        lockmode;
595
596                         rt_index++;
597
598                         if (rte->rtekind != RTE_RELATION)
599                                 continue;
600
601                         /*
602                          * Acquire the appropriate type of lock on each relation OID. Note
603                          * that we don't actually try to open the rel, and hence will not
604                          * fail if it's been dropped entirely --- we'll just transiently
605                          * acquire a non-conflicting lock.
606                          */
607                         if (list_member_int(plannedstmt->resultRelations, rt_index))
608                                 lockmode = RowExclusiveLock;
609                         else if (rowmark_member(plannedstmt->rowMarks, rt_index))
610                                 lockmode = RowShareLock;
611                         else
612                                 lockmode = AccessShareLock;
613
614                         if (acquire)
615                                 LockRelationOid(rte->relid, lockmode);
616                         else
617                                 UnlockRelationOid(rte->relid, lockmode);
618                 }
619         }
620 }
621
622 /*
623  * AcquirePlannerLocks: acquire locks needed for planning and execution of a
624  * not-fully-planned cached plan; or release them if acquire is false.
625  *
626  * Note that we don't actually try to open the relations, and hence will not
627  * fail if one has been dropped entirely --- we'll just transiently acquire
628  * a non-conflicting lock.
629  */
630 static void
631 AcquirePlannerLocks(List *stmt_list, bool acquire)
632 {
633         ListCell   *lc;
634
635         foreach(lc, stmt_list)
636         {
637                 Query      *query = (Query *) lfirst(lc);
638
639                 Assert(IsA(query, Query));
640                 if (acquire)
641                         ScanQueryForRelids(query, LockRelid, NULL);
642                 else
643                         ScanQueryForRelids(query, UnlockRelid, NULL);
644         }
645 }
646
647 /*
648  * ScanQueryForRelids callback functions for AcquirePlannerLocks
649  */
650 static void
651 LockRelid(Oid relid, LOCKMODE lockmode, void *arg)
652 {
653         LockRelationOid(relid, lockmode);
654 }
655
656 static void
657 UnlockRelid(Oid relid, LOCKMODE lockmode, void *arg)
658 {
659         UnlockRelationOid(relid, lockmode);
660 }
661
662 /*
663  * ScanQueryForRelids: recursively scan one Query and apply the callback
664  * function to each relation OID found therein.  The callback function
665  * takes the arguments relation OID, lockmode, pointer arg.
666  */
667 static void
668 ScanQueryForRelids(Query *parsetree,
669                                    void (*callback) (),
670                                    void *arg)
671 {
672         ListCell   *lc;
673         int                     rt_index;
674
675         /*
676          * First, process RTEs of the current query level.
677          */
678         rt_index = 0;
679         foreach(lc, parsetree->rtable)
680         {
681                 RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
682                 LOCKMODE        lockmode;
683
684                 rt_index++;
685                 switch (rte->rtekind)
686                 {
687                         case RTE_RELATION:
688
689                                 /*
690                                  * Determine the lock type required for this RTE.
691                                  */
692                                 if (rt_index == parsetree->resultRelation)
693                                         lockmode = RowExclusiveLock;
694                                 else if (rowmark_member(parsetree->rowMarks, rt_index))
695                                         lockmode = RowShareLock;
696                                 else
697                                         lockmode = AccessShareLock;
698
699                                 (*callback) (rte->relid, lockmode, arg);
700                                 break;
701
702                         case RTE_SUBQUERY:
703
704                                 /*
705                                  * The subquery RTE itself is all right, but we have to
706                                  * recurse to process the represented subquery.
707                                  */
708                                 ScanQueryForRelids(rte->subquery, callback, arg);
709                                 break;
710
711                         default:
712                                 /* ignore other types of RTEs */
713                                 break;
714                 }
715         }
716
717         /*
718          * Recurse into sublink subqueries, too.  But we already did the ones in
719          * the rtable.
720          */
721         if (parsetree->hasSubLinks)
722         {
723                 ScanQueryWalkerContext context;
724
725                 context.callback = callback;
726                 context.arg = arg;
727                 query_tree_walker(parsetree, ScanQueryWalker,
728                                                   (void *) &context,
729                                                   QTW_IGNORE_RT_SUBQUERIES);
730         }
731 }
732
733 /*
734  * Walker to find sublink subqueries for ScanQueryForRelids
735  */
736 static bool
737 ScanQueryWalker(Node *node, ScanQueryWalkerContext *context)
738 {
739         if (node == NULL)
740                 return false;
741         if (IsA(node, SubLink))
742         {
743                 SubLink    *sub = (SubLink *) node;
744
745                 /* Do what we came for */
746                 ScanQueryForRelids((Query *) sub->subselect,
747                                                    context->callback, context->arg);
748                 /* Fall through to process lefthand args of SubLink */
749         }
750
751         /*
752          * Do NOT recurse into Query nodes, because ScanQueryForRelids already
753          * processed subselects of subselects for us.
754          */
755         return expression_tree_walker(node, ScanQueryWalker,
756                                                                   (void *) context);
757 }
758
759 /*
760  * rowmark_member: check whether an RT index appears in a RowMarkClause list.
761  */
762 static bool
763 rowmark_member(List *rowMarks, int rt_index)
764 {
765         ListCell   *l;
766
767         foreach(l, rowMarks)
768         {
769                 RowMarkClause *rc = (RowMarkClause *) lfirst(l);
770
771                 if (rc->rti == rt_index)
772                         return true;
773         }
774         return false;
775 }
776
777 /*
778  * plan_list_is_transient: check if any of the plans in the list are transient.
779  */
780 static bool
781 plan_list_is_transient(List *stmt_list)
782 {
783         ListCell   *lc;
784
785         foreach(lc, stmt_list)
786         {
787                 PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
788
789                 if (!IsA(plannedstmt, PlannedStmt))
790                         continue;                       /* Ignore utility statements */
791
792                 if (plannedstmt->transientPlan)
793                         return true;
794         }
795
796         return false;
797 }
798
799 /*
800  * PlanCacheComputeResultDesc: given a list of either fully-planned statements
801  * or Queries, determine the result tupledesc it will produce.  Returns NULL
802  * if the execution will not return tuples.
803  *
804  * Note: the result is created or copied into current memory context.
805  */
806 TupleDesc
807 PlanCacheComputeResultDesc(List *stmt_list)
808 {
809         Node       *node;
810         Query      *query;
811         PlannedStmt *pstmt;
812
813         switch (ChoosePortalStrategy(stmt_list))
814         {
815                 case PORTAL_ONE_SELECT:
816                         node = (Node *) linitial(stmt_list);
817                         if (IsA(node, Query))
818                         {
819                                 query = (Query *) node;
820                                 return ExecCleanTypeFromTL(query->targetList, false);
821                         }
822                         if (IsA(node, PlannedStmt))
823                         {
824                                 pstmt = (PlannedStmt *) node;
825                                 return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false);
826                         }
827                         /* other cases shouldn't happen, but return NULL */
828                         break;
829
830                 case PORTAL_ONE_RETURNING:
831                         node = PortalListGetPrimaryStmt(stmt_list);
832                         if (IsA(node, Query))
833                         {
834                                 query = (Query *) node;
835                                 Assert(query->returningList);
836                                 return ExecCleanTypeFromTL(query->returningList, false);
837                         }
838                         if (IsA(node, PlannedStmt))
839                         {
840                                 pstmt = (PlannedStmt *) node;
841                                 Assert(pstmt->returningLists);
842                                 return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false);
843                         }
844                         /* other cases shouldn't happen, but return NULL */
845                         break;
846
847                 case PORTAL_UTIL_SELECT:
848                         node = (Node *) linitial(stmt_list);
849                         if (IsA(node, Query))
850                         {
851                                 query = (Query *) node;
852                                 Assert(query->utilityStmt);
853                                 return UtilityTupleDescriptor(query->utilityStmt);
854                         }
855                         /* else it's a bare utility statement */
856                         return UtilityTupleDescriptor(node);
857
858                 case PORTAL_MULTI_QUERY:
859                         /* will not return tuples */
860                         break;
861         }
862         return NULL;
863 }
864
865 /*
866  * PlanCacheCallback
867  *              Relcache inval callback function
868  *
869  * Invalidate all plans mentioning the given rel, or all plans mentioning
870  * any rel at all if relid == InvalidOid.
871  */
872 static void
873 PlanCacheCallback(Datum arg, Oid relid)
874 {
875         ListCell   *lc1;
876         ListCell   *lc2;
877
878         foreach(lc1, cached_plans_list)
879         {
880                 CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
881                 CachedPlan *plan = plansource->plan;
882
883                 /* No work if it's already invalidated */
884                 if (!plan || plan->dead)
885                         continue;
886                 if (plan->fully_planned)
887                 {
888                         foreach(lc2, plan->stmt_list)
889                         {
890                                 PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2);
891
892                                 Assert(!IsA(plannedstmt, Query));
893                                 if (!IsA(plannedstmt, PlannedStmt))
894                                         continue;       /* Ignore utility statements */
895                                 if ((relid == InvalidOid) ? plannedstmt->relationOids != NIL :
896                                         list_member_oid(plannedstmt->relationOids, relid))
897                                 {
898                                         /* Invalidate the plan! */
899                                         plan->dead = true;
900                                         break;          /* out of stmt_list scan */
901                                 }
902                         }
903                 }
904                 else
905                 {
906                         /*
907                          * For not-fully-planned entries we use ScanQueryForRelids, since
908                          * a recursive traversal is needed.  The callback API is a bit
909                          * tedious but avoids duplication of coding.
910                          */
911                         InvalRelidContext context;
912
913                         context.inval_relid = relid;
914                         context.plan = plan;
915
916                         foreach(lc2, plan->stmt_list)
917                         {
918                                 Query      *query = (Query *) lfirst(lc2);
919
920                                 Assert(IsA(query, Query));
921                                 ScanQueryForRelids(query, InvalRelid, (void *) &context);
922                         }
923                 }
924         }
925 }
926
927 /*
928  * ResetPlanCache: drop all cached plans.
929  */
930 void
931 ResetPlanCache(void)
932 {
933         PlanCacheCallback((Datum) 0, InvalidOid);
934 }
935
936 /*
937  * ScanQueryForRelids callback function for PlanCacheCallback
938  */
939 static void
940 InvalRelid(Oid relid, LOCKMODE lockmode, InvalRelidContext *context)
941 {
942         if (relid == context->inval_relid || context->inval_relid == InvalidOid)
943                 context->plan->dead = true;
944 }