]> granicus.if.org Git - postgresql/blob - src/backend/utils/cache/plancache.c
Fix the plan-invalidation mechanism to treat regclass constants that refer to
[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-2007, 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.12 2007/10/11 18:05:27 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 "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
55
56 typedef struct
57 {
58         void (*callback) ();
59         void       *arg;
60 } ScanQueryWalkerContext;
61
62 typedef struct
63 {
64         Oid                     inval_relid;
65         CachedPlan *plan;
66 } InvalRelidContext;
67
68
69 static List *cached_plans_list = NIL;
70
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,
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 (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
116  */
117 CachedPlanSource *
118 CreateCachedPlan(Node *raw_parse_tree,
119                                  const char *query_string,
120                                  const char *commandTag,
121                                  Oid *param_types,
122                                  int num_params,
123                                  int cursor_options,
124                                  List *stmt_list,
125                                  bool fully_planned,
126                                  bool fixed_result)
127 {
128         CachedPlanSource *plansource;
129         OverrideSearchPath *search_path;
130         MemoryContext source_context;
131         MemoryContext oldcxt;
132
133         /*
134          * Make a dedicated memory context for the CachedPlanSource and its
135          * subsidiary data.  We expect it can be pretty small.
136          */
137         source_context = AllocSetContextCreate(CacheMemoryContext,
138                                                                                    "CachedPlanSource",
139                                                                                    ALLOCSET_SMALL_MINSIZE,
140                                                                                    ALLOCSET_SMALL_INITSIZE,
141                                                                                    ALLOCSET_SMALL_MAXSIZE);
142
143         /*
144          * Fetch current search_path into new context, but do any recalculation
145          * work required in caller's context.
146          */
147         search_path = GetOverrideSearchPath(source_context);
148
149         /*
150          * Create and fill the CachedPlanSource struct within the new context.
151          */
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 */
157         if (num_params > 0)
158         {
159                 plansource->param_types = (Oid *) palloc(num_params * sizeof(Oid));
160                 memcpy(plansource->param_types, param_types, num_params * sizeof(Oid));
161         }
162         else
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;
174
175         /*
176          * Copy the current output plans into the plancache entry.
177          */
178         StoreCachedPlan(plansource, stmt_list, NULL);
179
180         /*
181          * Now we can add the entry to the list of cached plans.  The List nodes
182          * live in CacheMemoryContext.
183          */
184         MemoryContextSwitchTo(CacheMemoryContext);
185
186         cached_plans_list = lappend(cached_plans_list, plansource);
187
188         MemoryContextSwitchTo(oldcxt);
189
190         return plansource;
191 }
192
193 /*
194  * FastCreateCachedPlan: create a plan cache entry with minimal data copying.
195  *
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
208  * short-lived.)
209  *
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
213  * CreateCachedPlan.
214  */
215 CachedPlanSource *
216 FastCreateCachedPlan(Node *raw_parse_tree,
217                                          char *query_string,
218                                          const char *commandTag,
219                                          Oid *param_types,
220                                          int num_params,
221                                          int cursor_options,
222                                          List *stmt_list,
223                                          bool fully_planned,
224                                          bool fixed_result,
225                                          MemoryContext context)
226 {
227         CachedPlanSource *plansource;
228         OverrideSearchPath *search_path;
229         MemoryContext oldcxt;
230
231         /*
232          * Fetch current search_path into given context, but do any recalculation
233          * work required in caller's context.
234          */
235         search_path = GetOverrideSearchPath(context);
236
237         /*
238          * Create and fill the CachedPlanSource struct within the given context.
239          */
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;
256
257         /*
258          * Store the current output plans into the plancache entry.
259          */
260         StoreCachedPlan(plansource, stmt_list, context);
261
262         /*
263          * Since the context is owned by the CachedPlan, advance its refcount.
264          */
265         plansource->orig_plan = plansource->plan;
266         plansource->orig_plan->refcount++;
267
268         /*
269          * Now we can add the entry to the list of cached plans.  The List nodes
270          * live in CacheMemoryContext.
271          */
272         MemoryContextSwitchTo(CacheMemoryContext);
273
274         cached_plans_list = lappend(cached_plans_list, plansource);
275
276         MemoryContextSwitchTo(oldcxt);
277
278         return plansource;
279 }
280
281 /*
282  * StoreCachedPlan: store a built or rebuilt plan into a plancache entry.
283  *
284  * Common subroutine for CreateCachedPlan and RevalidateCachedPlan.
285  */
286 static void
287 StoreCachedPlan(CachedPlanSource *plansource,
288                                 List *stmt_list,
289                                 MemoryContext plan_context)
290 {
291         CachedPlan *plan;
292         MemoryContext oldcxt;
293
294         if (plan_context == NULL)
295         {
296                 /*
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.
300                  */
301                 plan_context = AllocSetContextCreate(CacheMemoryContext,
302                                                                                          "CachedPlan",
303                                                                                          ALLOCSET_SMALL_MINSIZE,
304                                                                                          ALLOCSET_SMALL_INITSIZE,
305                                                                                          ALLOCSET_DEFAULT_MAXSIZE);
306
307                 /*
308                  * Copy supplied data into the new context.
309                  */
310                 oldcxt = MemoryContextSwitchTo(plan_context);
311
312                 stmt_list = (List *) copyObject(stmt_list);
313         }
314         else
315         {
316                 /* Assume subsidiary data is in the given context */
317                 oldcxt = MemoryContextSwitchTo(plan_context);
318         }
319
320         /*
321          * Create and fill the CachedPlan struct within the new context.
322          */
323         plan = (CachedPlan *) palloc(sizeof(CachedPlan));
324         plan->stmt_list = stmt_list;
325         plan->fully_planned = plansource->fully_planned;
326         plan->dead = false;
327         if (plansource->fully_planned && plan_list_is_transient(stmt_list))
328         {
329                 Assert(TransactionIdIsNormal(TransactionXmin));
330                 plan->saved_xmin = TransactionXmin;
331         }
332         else
333                 plan->saved_xmin = InvalidTransactionId;
334         plan->refcount = 1;                     /* for the parent's link */
335         plan->generation = ++(plansource->generation);
336         plan->context = plan_context;
337
338         Assert(plansource->plan == NULL);
339         plansource->plan = plan;
340
341         MemoryContextSwitchTo(oldcxt);
342 }
343
344 /*
345  * DropCachedPlan: destroy a cached plan.
346  *
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
350  * still in use.
351  */
352 void
353 DropCachedPlan(CachedPlanSource *plansource)
354 {
355         /* Validity check that we were given a CachedPlanSource */
356         Assert(list_member_ptr(cached_plans_list, plansource));
357
358         /* Remove it from the list */
359         cached_plans_list = list_delete_ptr(cached_plans_list, plansource);
360
361         /* Decrement child CachePlan's refcount and drop if no longer needed */
362         if (plansource->plan)
363                 ReleaseCachedPlan(plansource->plan, false);
364
365         /*
366          * If CachedPlanSource has independent storage, just drop it.  Otherwise
367          * decrement the refcount on the CachePlan that owns the storage.
368          */
369         if (plansource->orig_plan == NULL)
370         {
371                 /* Remove the CachedPlanSource and all subsidiary data */
372                 MemoryContextDelete(plansource->context);
373         }
374         else
375         {
376                 Assert(plansource->context == plansource->orig_plan->context);
377                 ReleaseCachedPlan(plansource->orig_plan, false);
378         }
379 }
380
381 /*
382  * RevalidateCachedPlan: prepare for re-use of a previously cached plan.
383  *
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).
387  *
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.
391  *
392  * Note: if any replanning activity is required, the caller's memory context
393  * is used for that work.
394  */
395 CachedPlan *
396 RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
397 {
398         CachedPlan *plan;
399
400         /* Validity check that we were given a CachedPlanSource */
401         Assert(list_member_ptr(cached_plans_list, plansource));
402
403         /*
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
407          * the lock.
408          */
409         plan = plansource->plan;
410         if (plan && !plan->dead)
411         {
412                 /*
413                  * Plan must have positive refcount because it is referenced by
414                  * plansource; so no need to fear it disappears under us here.
415                  */
416                 Assert(plan->refcount > 0);
417
418                 if (plan->fully_planned)
419                         AcquireExecutorLocks(plan->stmt_list, true);
420                 else
421                         AcquirePlannerLocks(plan->stmt_list, true);
422
423                 /*
424                  * If plan was transient, check to see if TransactionXmin has
425                  * advanced, and if so invalidate it.
426                  */
427                 if (!plan->dead &&
428                         TransactionIdIsValid(plan->saved_xmin) &&
429                         !TransactionIdEquals(plan->saved_xmin, TransactionXmin))
430                         plan->dead = true;
431
432                 /*
433                  * By now, if any invalidation has happened, PlanCacheCallback
434                  * will have marked the plan dead.
435                  */
436                 if (plan->dead)
437                 {
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);
443                 }
444         }
445
446         /*
447          * If plan has been invalidated, unlink it from the parent and release it.
448          */
449         if (plan && plan->dead)
450         {
451                 plansource->plan = NULL;
452                 ReleaseCachedPlan(plan, false);
453                 plan = NULL;
454         }
455
456         /*
457          * Build a new plan if needed.
458          */
459         if (!plan)
460         {
461                 List   *slist;
462                 TupleDesc resultDesc;
463
464                 /*
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?)
467                  */
468                 PushOverrideSearchPath(plansource->search_path);
469
470                 /*
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.
476                  */
477                 slist = pg_analyze_and_rewrite(copyObject(plansource->raw_parse_tree),
478                                                                            plansource->query_string,
479                                                                            plansource->param_types,
480                                                                            plansource->num_params);
481
482                 if (plansource->fully_planned)
483                 {
484                         /* Generate plans for queries */
485                         slist = do_planning(slist, plansource->cursor_options);
486                 }
487
488                 /*
489                  * Check or update the result tupdesc.  XXX should we use a weaker
490                  * condition than equalTupleDescs() here?
491                  */
492                 resultDesc = PlanCacheComputeResultDesc(slist);
493                 if (resultDesc == NULL && plansource->resultDesc == NULL)
494                 {
495                         /* OK, doesn't return tuples */
496                 }
497                 else if (resultDesc == NULL || plansource->resultDesc == NULL ||
498                                  !equalTupleDescs(resultDesc, plansource->resultDesc))
499                 {
500                         MemoryContext oldcxt;
501
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);
514                 }
515
516                 /* Now we can restore current search path */
517                 PopOverrideSearchPath();
518
519                 /*
520                  * Store the plans into the plancache entry, advancing the generation
521                  * count.
522                  */
523                 StoreCachedPlan(plansource, slist, NULL);
524
525                 plan = plansource->plan;
526         }
527
528         /*
529          * Last step: flag the plan as in use by caller.
530          */
531         if (useResOwner)
532                 ResourceOwnerEnlargePlanCacheRefs(CurrentResourceOwner);
533         plan->refcount++;
534         if (useResOwner)
535                 ResourceOwnerRememberPlanCacheRef(CurrentResourceOwner, plan);
536
537         return plan;
538 }
539
540 /*
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.
544  */
545 static List *
546 do_planning(List *querytrees, int cursorOptions)
547 {
548         List       *stmt_list;
549
550         /*
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.)
559          */
560         if (ActiveSnapshot != NULL)
561                 stmt_list = pg_plan_queries(querytrees, cursorOptions, NULL, false);
562         else
563         {
564                 PG_TRY();
565                 {
566                         stmt_list = pg_plan_queries(querytrees, cursorOptions, NULL, true);
567                 }
568                 PG_CATCH();
569                 {
570                         /* Restore global vars and propagate error */
571                         ActiveSnapshot = NULL;
572                         PG_RE_THROW();
573                 }
574                 PG_END_TRY();
575
576                 ActiveSnapshot = NULL;
577         }
578
579         return stmt_list;
580 }
581
582
583 /*
584  * ReleaseCachedPlan: release active use of a cached plan.
585  *
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.
589  *
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.
593  */
594 void
595 ReleaseCachedPlan(CachedPlan *plan, bool useResOwner)
596 {
597         if (useResOwner)
598                 ResourceOwnerForgetPlanCacheRef(CurrentResourceOwner, plan);
599         Assert(plan->refcount > 0);
600         plan->refcount--;
601         if (plan->refcount == 0)
602                 MemoryContextDelete(plan->context);
603 }
604
605 /*
606  * AcquireExecutorLocks: acquire locks needed for execution of a fully-planned
607  * cached plan; or release them if acquire is false.
608  */
609 static void
610 AcquireExecutorLocks(List *stmt_list, bool acquire)
611 {
612         ListCell   *lc1;
613
614         foreach(lc1, stmt_list)
615         {
616                 PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc1);
617                 int                     rt_index;
618                 ListCell   *lc2;
619
620                 Assert(!IsA(plannedstmt, Query));
621                 if (!IsA(plannedstmt, PlannedStmt))
622                         continue;                       /* Ignore utility statements */
623
624                 rt_index = 0;
625                 foreach(lc2, plannedstmt->rtable)
626                 {
627                         RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
628                         LOCKMODE        lockmode;
629
630                         rt_index++;
631
632                         if (rte->rtekind != RTE_RELATION)
633                                 continue;
634
635                         /*
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.
640                          */
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;
647
648                         if (acquire)
649                                 LockRelationOid(rte->relid, lockmode);
650                         else
651                                 UnlockRelationOid(rte->relid, lockmode);
652                 }
653         }
654 }
655
656 /*
657  * AcquirePlannerLocks: acquire locks needed for planning and execution of a
658  * not-fully-planned cached plan; or release them if acquire is false.
659  *
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.
663  */
664 static void
665 AcquirePlannerLocks(List *stmt_list, bool acquire)
666 {
667         ListCell   *lc;
668
669         foreach(lc, stmt_list)
670         {
671                 Query      *query = (Query *) lfirst(lc);
672
673                 Assert(IsA(query, Query));
674                 if (acquire)
675                         ScanQueryForRelids(query, LockRelid, NULL);
676                 else
677                         ScanQueryForRelids(query, UnlockRelid, NULL);
678         }
679 }
680
681 /*
682  * ScanQueryForRelids callback functions for AcquirePlannerLocks
683  */
684 static void
685 LockRelid(Oid relid, LOCKMODE lockmode, void *arg)
686 {
687         LockRelationOid(relid, lockmode);
688 }
689
690 static void
691 UnlockRelid(Oid relid, LOCKMODE lockmode, void *arg)
692 {
693         UnlockRelationOid(relid, lockmode);
694 }
695
696 /*
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.
700  */
701 static void
702 ScanQueryForRelids(Query *parsetree,
703                                    void (*callback) (),
704                                    void *arg)
705 {
706         ListCell   *lc;
707         int                     rt_index;
708
709         /*
710          * First, process RTEs of the current query level.
711          */
712         rt_index = 0;
713         foreach(lc, parsetree->rtable)
714         {
715                 RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
716                 LOCKMODE        lockmode;
717
718                 rt_index++;
719                 switch (rte->rtekind)
720                 {
721                         case RTE_RELATION:
722                                 /*
723                                  * Determine the lock type required for this RTE.
724                                  */
725                                 if (rt_index == parsetree->resultRelation)
726                                         lockmode = RowExclusiveLock;
727                                 else if (rowmark_member(parsetree->rowMarks, rt_index))
728                                         lockmode = RowShareLock;
729                                 else
730                                         lockmode = AccessShareLock;
731
732                                 (*callback) (rte->relid, lockmode, arg);
733                                 break;
734
735                         case RTE_SUBQUERY:
736
737                                 /*
738                                  * The subquery RTE itself is all right, but we have to
739                                  * recurse to process the represented subquery.
740                                  */
741                                 ScanQueryForRelids(rte->subquery, callback, arg);
742                                 break;
743
744                         default:
745                                 /* ignore other types of RTEs */
746                                 break;
747                 }
748         }
749
750         /*
751          * Recurse into sublink subqueries, too.  But we already did the ones in
752          * the rtable.
753          */
754         if (parsetree->hasSubLinks)
755         {
756                 ScanQueryWalkerContext context;
757
758                 context.callback = callback;
759                 context.arg = arg;
760                 query_tree_walker(parsetree, ScanQueryWalker,
761                                                   (void *) &context,
762                                                   QTW_IGNORE_RT_SUBQUERIES);
763         }
764 }
765
766 /*
767  * Walker to find sublink subqueries for ScanQueryForRelids
768  */
769 static bool
770 ScanQueryWalker(Node *node, ScanQueryWalkerContext *context)
771 {
772         if (node == NULL)
773                 return false;
774         if (IsA(node, SubLink))
775         {
776                 SubLink    *sub = (SubLink *) node;
777
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 */
782         }
783
784         /*
785          * Do NOT recurse into Query nodes, because ScanQueryForRelids
786          * already processed subselects of subselects for us.
787          */
788         return expression_tree_walker(node, ScanQueryWalker,
789                                                                   (void *) context);
790 }
791
792 /*
793  * rowmark_member: check whether an RT index appears in a RowMarkClause list.
794  */
795 static bool
796 rowmark_member(List *rowMarks, int rt_index)
797 {
798         ListCell   *l;
799
800         foreach(l, rowMarks)
801         {
802                 RowMarkClause *rc = (RowMarkClause *) lfirst(l);
803
804                 if (rc->rti == rt_index)
805                         return true;
806         }
807         return false;
808 }
809
810 /*
811  * plan_list_is_transient: check if any of the plans in the list are transient.
812  */
813 static bool
814 plan_list_is_transient(List *stmt_list)
815 {
816         ListCell   *lc;
817
818         foreach(lc, stmt_list)
819         {
820                 PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
821                 
822                 if (!IsA(plannedstmt, PlannedStmt))
823                         continue;                       /* Ignore utility statements */
824
825                 if (plannedstmt->transientPlan)
826                         return true;
827         }       
828
829         return false;
830 }
831
832 /*
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.
836  *
837  * Note: the result is created or copied into current memory context.
838  */
839 TupleDesc
840 PlanCacheComputeResultDesc(List *stmt_list)
841 {
842         Node       *node;
843         Query      *query;
844         PlannedStmt *pstmt;
845
846         switch (ChoosePortalStrategy(stmt_list))
847         {
848                 case PORTAL_ONE_SELECT:
849                         node = (Node *) linitial(stmt_list);
850                         if (IsA(node, Query))
851                         {
852                                 query = (Query *) node;
853                                 return ExecCleanTypeFromTL(query->targetList, false);
854                         }
855                         if (IsA(node, PlannedStmt))
856                         {
857                                 pstmt = (PlannedStmt *) node;
858                                 return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false);
859                         }
860                         /* other cases shouldn't happen, but return NULL */
861                         break;
862
863                 case PORTAL_ONE_RETURNING:
864                         node = PortalListGetPrimaryStmt(stmt_list);
865                         if (IsA(node, Query))
866                         {
867                                 query = (Query *) node;
868                                 Assert(query->returningList);
869                                 return ExecCleanTypeFromTL(query->returningList, false);
870                         }
871                         if (IsA(node, PlannedStmt))
872                         {
873                                 pstmt = (PlannedStmt *) node;
874                                 Assert(pstmt->returningLists);
875                                 return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false);
876                         }
877                         /* other cases shouldn't happen, but return NULL */
878                         break;
879
880                 case PORTAL_UTIL_SELECT:
881                         node = (Node *) linitial(stmt_list);
882                         if (IsA(node, Query))
883                         {
884                                 query = (Query *) node;
885                                 Assert(query->utilityStmt);
886                                 return UtilityTupleDescriptor(query->utilityStmt);
887                         }
888                         /* else it's a bare utility statement */
889                         return UtilityTupleDescriptor(node);
890
891                 case PORTAL_MULTI_QUERY:
892                         /* will not return tuples */
893                         break;
894         }
895         return NULL;
896 }
897
898 /*
899  * PlanCacheCallback
900  *              Relcache inval callback function
901  *
902  * Invalidate all plans mentioning the given rel, or all plans mentioning
903  * any rel at all if relid == InvalidOid.
904  */
905 static void
906 PlanCacheCallback(Datum arg, Oid relid)
907 {
908         ListCell   *lc1;
909         ListCell   *lc2;
910
911         foreach(lc1, cached_plans_list)
912         {
913                 CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
914                 CachedPlan *plan = plansource->plan;
915
916                 /* No work if it's already invalidated */
917                 if (!plan || plan->dead)
918                         continue;
919                 if (plan->fully_planned)
920                 {
921                         foreach(lc2, plan->stmt_list)
922                         {
923                                 PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2);
924
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))
930                                 {
931                                         /* Invalidate the plan! */
932                                         plan->dead = true;
933                                         break;                  /* out of stmt_list scan */
934                                 }
935                         }
936                 }
937                 else
938                 {
939                         /*
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.
943                          */
944                         InvalRelidContext context;
945
946                         context.inval_relid = relid;
947                         context.plan = plan;
948
949                         foreach(lc2, plan->stmt_list)
950                         {
951                                 Query      *query = (Query *) lfirst(lc2);
952
953                                 Assert(IsA(query, Query));
954                                 ScanQueryForRelids(query, InvalRelid, (void *) &context);
955                         }
956                 }
957         }
958 }
959
960 /*
961  * ResetPlanCache: drop all cached plans.
962  */
963 void
964 ResetPlanCache(void)
965 {
966         PlanCacheCallback((Datum) 0, InvalidOid);
967 }
968
969 /*
970  * ScanQueryForRelids callback function for PlanCacheCallback
971  */
972 static void
973 InvalRelid(Oid relid, LOCKMODE lockmode, InvalRelidContext *context)
974 {
975         if (relid == context->inval_relid || context->inval_relid == InvalidOid)
976                 context->plan->dead = true;
977 }