Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * plancache.c
4 : : * Plan cache management.
5 : : *
6 : : * The plan cache manager has two principal responsibilities: deciding when
7 : : * to use a generic plan versus a custom (parameter-value-specific) plan,
8 : : * and tracking whether cached plans need to be invalidated because of schema
9 : : * changes in the objects they depend on.
10 : : *
11 : : * The logic for choosing generic or custom plans is in choose_custom_plan,
12 : : * which see for comments.
13 : : *
14 : : * Cache invalidation is driven off sinval events. Any CachedPlanSource
15 : : * that matches the event is marked invalid, as is its generic CachedPlan
16 : : * if it has one. When (and if) the next demand for a cached plan occurs,
17 : : * parse analysis and/or rewrite is repeated to build a new valid query tree,
18 : : * and then planning is performed as normal. We also force re-analysis and
19 : : * re-planning if the active search_path is different from the previous time
20 : : * or, if RLS is involved, if the user changes or the RLS environment changes.
21 : : *
22 : : * Note that if the sinval was a result of user DDL actions, parse analysis
23 : : * could throw an error, for example if a column referenced by the query is
24 : : * no longer present. Another possibility is for the query's output tupdesc
25 : : * to change (for instance "SELECT *" might expand differently than before).
26 : : * The creator of a cached plan can specify whether it is allowable for the
27 : : * query to change output tupdesc on replan --- if so, it's up to the
28 : : * caller to notice changes and cope with them.
29 : : *
30 : : * Currently, we track exactly the dependencies of plans on relations,
31 : : * user-defined functions, and domains. On relcache invalidation events or
32 : : * pg_proc or pg_type syscache invalidation events, we invalidate just those
33 : : * plans that depend on the particular object being modified. (Note: this
34 : : * scheme assumes that any table modification that requires replanning will
35 : : * generate a relcache inval event.) We also watch for inval events on
36 : : * certain other system catalogs, such as pg_namespace; but for them, our
37 : : * response is just to invalidate all plans. We expect updates on those
38 : : * catalogs to be infrequent enough that more-detailed tracking is not worth
39 : : * the effort.
40 : : *
41 : : * In addition to full-fledged query plans, we provide a facility for
42 : : * detecting invalidations of simple scalar expressions. This is fairly
43 : : * bare-bones; it's the caller's responsibility to build a new expression
44 : : * if the old one gets invalidated.
45 : : *
46 : : *
47 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
48 : : * Portions Copyright (c) 1994, Regents of the University of California
49 : : *
50 : : * IDENTIFICATION
51 : : * src/backend/utils/cache/plancache.c
52 : : *
53 : : *-------------------------------------------------------------------------
54 : : */
55 : : #include "postgres.h"
56 : :
57 : : #include <limits.h>
58 : :
59 : : #include "access/transam.h"
60 : : #include "catalog/namespace.h"
61 : : #include "executor/executor.h"
62 : : #include "miscadmin.h"
63 : : #include "nodes/nodeFuncs.h"
64 : : #include "optimizer/optimizer.h"
65 : : #include "parser/analyze.h"
66 : : #include "rewrite/rewriteHandler.h"
67 : : #include "storage/lmgr.h"
68 : : #include "tcop/pquery.h"
69 : : #include "tcop/utility.h"
70 : : #include "utils/inval.h"
71 : : #include "utils/memutils.h"
72 : : #include "utils/resowner.h"
73 : : #include "utils/rls.h"
74 : : #include "utils/snapmgr.h"
75 : : #include "utils/syscache.h"
76 : :
77 : :
78 : : /*
79 : : * This is the head of the backend's list of "saved" CachedPlanSources (i.e.,
80 : : * those that are in long-lived storage and are examined for sinval events).
81 : : * We use a dlist instead of separate List cells so that we can guarantee
82 : : * to save a CachedPlanSource without error.
83 : : */
84 : : static dlist_head saved_plan_list = DLIST_STATIC_INIT(saved_plan_list);
85 : :
86 : : /*
87 : : * This is the head of the backend's list of CachedExpressions.
88 : : */
89 : : static dlist_head cached_expression_list = DLIST_STATIC_INIT(cached_expression_list);
90 : :
91 : : static void ReleaseGenericPlan(CachedPlanSource *plansource);
92 : : static bool StmtPlanRequiresRevalidation(CachedPlanSource *plansource);
93 : : static bool BuildingPlanRequiresSnapshot(CachedPlanSource *plansource);
94 : : static List *RevalidateCachedQuery(CachedPlanSource *plansource,
95 : : QueryEnvironment *queryEnv);
96 : : static bool CheckCachedPlan(CachedPlanSource *plansource);
97 : : static CachedPlan *BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
98 : : ParamListInfo boundParams, QueryEnvironment *queryEnv);
99 : : static bool choose_custom_plan(CachedPlanSource *plansource,
100 : : ParamListInfo boundParams);
101 : : static double cached_plan_cost(CachedPlan *plan, bool include_planner);
102 : : static Query *QueryListGetPrimaryStmt(List *stmts);
103 : : static void AcquireExecutorLocks(List *stmt_list, bool acquire);
104 : : static void AcquirePlannerLocks(List *stmt_list, bool acquire);
105 : : static void ScanQueryForLocks(Query *parsetree, bool acquire);
106 : : static bool ScanQueryWalker(Node *node, bool *acquire);
107 : : static TupleDesc PlanCacheComputeResultDesc(List *stmt_list);
108 : : static void PlanCacheRelCallback(Datum arg, Oid relid);
109 : : static void PlanCacheObjectCallback(Datum arg, int cacheid, uint32 hashvalue);
110 : : static void PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue);
111 : :
112 : : /* ResourceOwner callbacks to track plancache references */
113 : : static void ResOwnerReleaseCachedPlan(Datum res);
114 : :
115 : : static const ResourceOwnerDesc planref_resowner_desc =
116 : : {
117 : : .name = "plancache reference",
118 : : .release_phase = RESOURCE_RELEASE_AFTER_LOCKS,
119 : : .release_priority = RELEASE_PRIO_PLANCACHE_REFS,
120 : : .ReleaseResource = ResOwnerReleaseCachedPlan,
121 : : .DebugPrint = NULL /* the default message is fine */
122 : : };
123 : :
124 : : /* Convenience wrappers over ResourceOwnerRemember/Forget */
125 : : static inline void
770 heikki.linnakangas@i 126 :CBC 142342 : ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
127 : : {
128 : 142342 : ResourceOwnerRemember(owner, PointerGetDatum(plan), &planref_resowner_desc);
129 : 142342 : }
130 : : static inline void
131 : 95743 : ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
132 : : {
133 : 95743 : ResourceOwnerForget(owner, PointerGetDatum(plan), &planref_resowner_desc);
134 : 95743 : }
135 : :
136 : :
137 : : /* GUC parameter */
138 : : int plan_cache_mode = PLAN_CACHE_MODE_AUTO;
139 : :
140 : : /*
141 : : * InitPlanCache: initialize module during InitPostgres.
142 : : *
143 : : * All we need to do is hook into inval.c's callback lists.
144 : : */
145 : : void
6854 tgl@sss.pgh.pa.us 146 : 15511 : InitPlanCache(void)
147 : : {
6308 148 : 15511 : CacheRegisterRelcacheCallback(PlanCacheRelCallback, (Datum) 0);
2561 149 : 15511 : CacheRegisterSyscacheCallback(PROCOID, PlanCacheObjectCallback, (Datum) 0);
150 : 15511 : CacheRegisterSyscacheCallback(TYPEOID, PlanCacheObjectCallback, (Datum) 0);
6308 151 : 15511 : CacheRegisterSyscacheCallback(NAMESPACEOID, PlanCacheSysCallback, (Datum) 0);
152 : 15511 : CacheRegisterSyscacheCallback(OPEROID, PlanCacheSysCallback, (Datum) 0);
153 : 15511 : CacheRegisterSyscacheCallback(AMOPOPID, PlanCacheSysCallback, (Datum) 0);
3267 154 : 15511 : CacheRegisterSyscacheCallback(FOREIGNSERVEROID, PlanCacheSysCallback, (Datum) 0);
155 : 15511 : CacheRegisterSyscacheCallback(FOREIGNDATAWRAPPEROID, PlanCacheSysCallback, (Datum) 0);
6854 156 : 15511 : }
157 : :
158 : : /*
159 : : * CreateCachedPlan: initially create a plan cache entry for a raw parse tree.
160 : : *
161 : : * Creation of a cached plan is divided into two steps, CreateCachedPlan and
162 : : * CompleteCachedPlan. CreateCachedPlan should be called after running the
163 : : * query through raw_parser, but before doing parse analysis and rewrite;
164 : : * CompleteCachedPlan is called after that. The reason for this arrangement
165 : : * is that it can save one round of copying of the raw parse tree, since
166 : : * the parser will normally scribble on the raw parse tree. Callers would
167 : : * otherwise need to make an extra copy of the parse tree to ensure they
168 : : * still had a clean copy to present at plan cache creation time.
169 : : *
170 : : * All arguments presented to CreateCachedPlan are copied into a memory
171 : : * context created as a child of the call-time CurrentMemoryContext, which
172 : : * should be a reasonably short-lived working context that will go away in
173 : : * event of an error. This ensures that the cached plan data structure will
174 : : * likewise disappear if an error occurs before we have fully constructed it.
175 : : * Once constructed, the cached plan can be made longer-lived, if needed,
176 : : * by calling SaveCachedPlan.
177 : : *
178 : : * raw_parse_tree: output of raw_parser(), or NULL if empty query
179 : : * query_string: original query text
180 : : * commandTag: command tag for query, or UNKNOWN if empty query
181 : : */
182 : : CachedPlanSource *
3259 183 : 28579 : CreateCachedPlan(RawStmt *raw_parse_tree,
184 : : const char *query_string,
185 : : CommandTag commandTag)
186 : : {
187 : : CachedPlanSource *plansource;
188 : : MemoryContext source_context;
189 : : MemoryContext oldcxt;
190 : :
3101 191 [ - + ]: 28579 : Assert(query_string != NULL); /* required as of 8.4 */
192 : :
193 : : /*
194 : : * Make a dedicated memory context for the CachedPlanSource and its
195 : : * permanent subsidiary data. It's probably not going to be large, but
196 : : * just in case, allow it to grow large. Initially it's a child of the
197 : : * caller's context (which we assume to be transient), so that it will be
198 : : * cleaned up on error.
199 : : */
5206 200 : 28579 : source_context = AllocSetContextCreate(CurrentMemoryContext,
201 : : "CachedPlanSource",
202 : : ALLOCSET_START_SMALL_SIZES);
203 : :
204 : : /*
205 : : * Create and fill the CachedPlanSource struct within the new context.
206 : : * Most fields are just left empty for the moment.
207 : : */
6854 208 : 28579 : oldcxt = MemoryContextSwitchTo(source_context);
209 : :
7 michael@paquier.xyz 210 :GNC 28579 : plansource = palloc0_object(CachedPlanSource);
5206 tgl@sss.pgh.pa.us 211 :CBC 28579 : plansource->magic = CACHEDPLANSOURCE_MAGIC;
6854 212 : 28579 : plansource->raw_parse_tree = copyObject(raw_parse_tree);
259 213 : 28579 : plansource->analyzed_parse_tree = NULL;
6361 214 : 28579 : plansource->query_string = pstrdup(query_string);
2822 215 : 28579 : MemoryContextSetIdentifier(source_context, plansource->query_string);
5206 216 : 28579 : plansource->commandTag = commandTag;
217 : 28579 : plansource->param_types = NULL;
218 : 28579 : plansource->num_params = 0;
5887 219 : 28579 : plansource->parserSetup = NULL;
220 : 28579 : plansource->parserSetupArg = NULL;
259 221 : 28579 : plansource->postRewrite = NULL;
222 : 28579 : plansource->postRewriteArg = NULL;
5206 223 : 28579 : plansource->cursor_options = 0;
224 : 28579 : plansource->fixed_result = false;
225 : 28579 : plansource->resultDesc = NULL;
6854 226 : 28579 : plansource->context = source_context;
5206 227 : 28579 : plansource->query_list = NIL;
228 : 28579 : plansource->relationOids = NIL;
229 : 28579 : plansource->invalItems = NIL;
4709 230 : 28579 : plansource->search_path = NULL;
5206 231 : 28579 : plansource->query_context = NULL;
3442 232 : 28579 : plansource->rewriteRoleId = InvalidOid;
233 : 28579 : plansource->rewriteRowSecurity = false;
234 : 28579 : plansource->dependsOnRLS = false;
5206 235 : 28579 : plansource->gplan = NULL;
4730 236 : 28579 : plansource->is_oneshot = false;
5206 237 : 28579 : plansource->is_complete = false;
238 : 28579 : plansource->is_saved = false;
239 : 28579 : plansource->is_valid = false;
240 : 28579 : plansource->generation = 0;
241 : 28579 : plansource->generic_cost = -1;
242 : 28579 : plansource->total_custom_cost = 0;
1976 fujii@postgresql.org 243 : 28579 : plansource->num_generic_plans = 0;
5206 tgl@sss.pgh.pa.us 244 : 28579 : plansource->num_custom_plans = 0;
245 : :
6854 246 : 28579 : MemoryContextSwitchTo(oldcxt);
247 : :
248 : 28579 : return plansource;
249 : : }
250 : :
251 : : /*
252 : : * CreateCachedPlanForQuery: initially create a plan cache entry for a Query.
253 : : *
254 : : * This is used in the same way as CreateCachedPlan, except that the source
255 : : * query has already been through parse analysis, and the plancache will never
256 : : * try to re-do that step.
257 : : *
258 : : * Currently this is used only for new-style SQL functions, where we have a
259 : : * Query from the function's prosqlbody, but no source text. The query_string
260 : : * is typically empty, but is required anyway.
261 : : */
262 : : CachedPlanSource *
259 263 : 408 : CreateCachedPlanForQuery(Query *analyzed_parse_tree,
264 : : const char *query_string,
265 : : CommandTag commandTag)
266 : : {
267 : : CachedPlanSource *plansource;
268 : : MemoryContext oldcxt;
269 : :
270 : : /* Rather than duplicating CreateCachedPlan, just do this: */
271 : 408 : plansource = CreateCachedPlan(NULL, query_string, commandTag);
272 : 408 : oldcxt = MemoryContextSwitchTo(plansource->context);
273 : 408 : plansource->analyzed_parse_tree = copyObject(analyzed_parse_tree);
274 : 408 : MemoryContextSwitchTo(oldcxt);
275 : :
276 : 408 : return plansource;
277 : : }
278 : :
279 : : /*
280 : : * CreateOneShotCachedPlan: initially create a one-shot plan cache entry.
281 : : *
282 : : * This variant of CreateCachedPlan creates a plan cache entry that is meant
283 : : * to be used only once. No data copying occurs: all data structures remain
284 : : * in the caller's memory context (which typically should get cleared after
285 : : * completing execution). The CachedPlanSource struct itself is also created
286 : : * in that context.
287 : : *
288 : : * A one-shot plan cannot be saved or copied, since we make no effort to
289 : : * preserve the raw parse tree unmodified. There is also no support for
290 : : * invalidation, so plan use must be completed in the current transaction,
291 : : * and DDL that might invalidate the querytree_list must be avoided as well.
292 : : *
293 : : * raw_parse_tree: output of raw_parser(), or NULL if empty query
294 : : * query_string: original query text
295 : : * commandTag: command tag for query, or NULL if empty query
296 : : */
297 : : CachedPlanSource *
3259 298 : 8010 : CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
299 : : const char *query_string,
300 : : CommandTag commandTag)
301 : : {
302 : : CachedPlanSource *plansource;
303 : :
3101 304 [ - + ]: 8010 : Assert(query_string != NULL); /* required as of 8.4 */
305 : :
306 : : /*
307 : : * Create and fill the CachedPlanSource struct within the caller's memory
308 : : * context. Most fields are just left empty for the moment.
309 : : */
7 michael@paquier.xyz 310 :GNC 8010 : plansource = palloc0_object(CachedPlanSource);
4730 tgl@sss.pgh.pa.us 311 :CBC 8010 : plansource->magic = CACHEDPLANSOURCE_MAGIC;
312 : 8010 : plansource->raw_parse_tree = raw_parse_tree;
259 313 : 8010 : plansource->analyzed_parse_tree = NULL;
4730 314 : 8010 : plansource->query_string = query_string;
315 : 8010 : plansource->commandTag = commandTag;
316 : 8010 : plansource->param_types = NULL;
317 : 8010 : plansource->num_params = 0;
318 : 8010 : plansource->parserSetup = NULL;
319 : 8010 : plansource->parserSetupArg = NULL;
259 320 : 8010 : plansource->postRewrite = NULL;
321 : 8010 : plansource->postRewriteArg = NULL;
4730 322 : 8010 : plansource->cursor_options = 0;
323 : 8010 : plansource->fixed_result = false;
324 : 8010 : plansource->resultDesc = NULL;
325 : 8010 : plansource->context = CurrentMemoryContext;
326 : 8010 : plansource->query_list = NIL;
327 : 8010 : plansource->relationOids = NIL;
328 : 8010 : plansource->invalItems = NIL;
4709 329 : 8010 : plansource->search_path = NULL;
4730 330 : 8010 : plansource->query_context = NULL;
3442 331 : 8010 : plansource->rewriteRoleId = InvalidOid;
332 : 8010 : plansource->rewriteRowSecurity = false;
333 : 8010 : plansource->dependsOnRLS = false;
4730 334 : 8010 : plansource->gplan = NULL;
335 : 8010 : plansource->is_oneshot = true;
336 : 8010 : plansource->is_complete = false;
337 : 8010 : plansource->is_saved = false;
338 : 8010 : plansource->is_valid = false;
339 : 8010 : plansource->generation = 0;
340 : 8010 : plansource->generic_cost = -1;
341 : 8010 : plansource->total_custom_cost = 0;
1976 fujii@postgresql.org 342 : 8010 : plansource->num_generic_plans = 0;
4730 tgl@sss.pgh.pa.us 343 : 8010 : plansource->num_custom_plans = 0;
344 : :
345 : 8010 : return plansource;
346 : : }
347 : :
348 : : /*
349 : : * CompleteCachedPlan: second step of creating a plan cache entry.
350 : : *
351 : : * Pass in the analyzed-and-rewritten form of the query, as well as the
352 : : * required subsidiary data about parameters and such. All passed values will
353 : : * be copied into the CachedPlanSource's memory, except as specified below.
354 : : * After this is called, GetCachedPlan can be called to obtain a plan, and
355 : : * optionally the CachedPlanSource can be saved using SaveCachedPlan.
356 : : *
357 : : * If querytree_context is not NULL, the querytree_list must be stored in that
358 : : * context (but the other parameters need not be). The querytree_list is not
359 : : * copied, rather the given context is kept as the initial query_context of
360 : : * the CachedPlanSource. (It should have been created as a child of the
361 : : * caller's working memory context, but it will now be reparented to belong
362 : : * to the CachedPlanSource.) The querytree_context is normally the context in
363 : : * which the caller did raw parsing and parse analysis. This approach saves
364 : : * one tree copying step compared to passing NULL, but leaves lots of extra
365 : : * cruft in the query_context, namely whatever extraneous stuff parse analysis
366 : : * created, as well as whatever went unused from the raw parse tree. Using
367 : : * this option is a space-for-time tradeoff that is appropriate if the
368 : : * CachedPlanSource is not expected to survive long.
369 : : *
370 : : * plancache.c cannot know how to copy the data referenced by parserSetupArg,
371 : : * and it would often be inappropriate to do so anyway. When using that
372 : : * option, it is caller's responsibility that the referenced data remains
373 : : * valid for as long as the CachedPlanSource exists.
374 : : *
375 : : * If the CachedPlanSource is a "oneshot" plan, then no querytree copying
376 : : * occurs at all, and querytree_context is ignored; it is caller's
377 : : * responsibility that the passed querytree_list is sufficiently long-lived.
378 : : *
379 : : * plansource: structure returned by CreateCachedPlan
380 : : * querytree_list: analyzed-and-rewritten form of query (list of Query nodes)
381 : : * querytree_context: memory context containing querytree_list,
382 : : * or NULL to copy querytree_list into a fresh context
383 : : * param_types: array of fixed parameter type OIDs, or NULL if none
384 : : * num_params: number of fixed parameters
385 : : * parserSetup: alternate method for handling query parameters
386 : : * parserSetupArg: data to pass to parserSetup
387 : : * cursor_options: options bitmask to pass to planner
388 : : * fixed_result: true to disallow future changes in query's result tupdesc
389 : : */
390 : : void
5206 391 : 36512 : CompleteCachedPlan(CachedPlanSource *plansource,
392 : : List *querytree_list,
393 : : MemoryContext querytree_context,
394 : : Oid *param_types,
395 : : int num_params,
396 : : ParserSetupHook parserSetup,
397 : : void *parserSetupArg,
398 : : int cursor_options,
399 : : bool fixed_result)
400 : : {
401 : 36512 : MemoryContext source_context = plansource->context;
402 : 36512 : MemoryContext oldcxt = CurrentMemoryContext;
403 : :
404 : : /* Assert caller is doing things in a sane order */
405 [ - + ]: 36512 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
406 [ - + ]: 36512 : Assert(!plansource->is_complete);
407 : :
408 : : /*
409 : : * If caller supplied a querytree_context, reparent it underneath the
410 : : * CachedPlanSource's context; otherwise, create a suitable context and
411 : : * copy the querytree_list into it. But no data copying should be done
412 : : * for one-shot plans; for those, assume the passed querytree_list is
413 : : * sufficiently long-lived.
414 : : */
4730 415 [ + + ]: 36512 : if (plansource->is_oneshot)
416 : : {
417 : 8002 : querytree_context = CurrentMemoryContext;
418 : : }
419 [ + + ]: 28510 : else if (querytree_context != NULL)
420 : : {
5206 421 : 2896 : MemoryContextSetParent(querytree_context, source_context);
422 : 2896 : MemoryContextSwitchTo(querytree_context);
423 : : }
424 : : else
425 : : {
426 : : /* Again, it's a good bet the querytree_context can be small */
427 : 25614 : querytree_context = AllocSetContextCreate(source_context,
428 : : "CachedPlanQuery",
429 : : ALLOCSET_START_SMALL_SIZES);
430 : 25614 : MemoryContextSwitchTo(querytree_context);
3205 peter_e@gmx.net 431 : 25614 : querytree_list = copyObject(querytree_list);
432 : : }
433 : :
5206 tgl@sss.pgh.pa.us 434 : 36512 : plansource->query_context = querytree_context;
435 : 36512 : plansource->query_list = querytree_list;
436 : :
846 437 [ + + + + ]: 36512 : if (!plansource->is_oneshot && StmtPlanRequiresRevalidation(plansource))
438 : : {
439 : : /*
440 : : * Use the planner machinery to extract dependencies. Data is saved
441 : : * in query_context. (We assume that not a lot of extra cruft is
442 : : * created by this call.) We can skip this for one-shot plans, and
443 : : * plans not needing revalidation have no such dependencies anyway.
444 : : */
4730 445 : 27539 : extract_query_dependencies((Node *) querytree_list,
446 : : &plansource->relationOids,
447 : : &plansource->invalItems,
448 : : &plansource->dependsOnRLS);
449 : :
450 : : /* Update RLS info as well. */
3442 451 : 27539 : plansource->rewriteRoleId = GetUserId();
452 : 27539 : plansource->rewriteRowSecurity = row_security;
453 : :
454 : : /*
455 : : * Also save the current search_path in the query_context. (This
456 : : * should not generate much extra cruft either, since almost certainly
457 : : * the path is already valid.) Again, we don't really need this for
458 : : * one-shot plans; and we *must* skip this for transaction control
459 : : * commands, because this could result in catalog accesses.
460 : : */
870 noah@leadboat.com 461 : 27539 : plansource->search_path = GetSearchPathMatcher(querytree_context);
462 : : }
463 : :
464 : : /*
465 : : * Save the final parameter types (or other parameter specification data)
466 : : * into the source_context, as well as our other parameters.
467 : : */
5206 tgl@sss.pgh.pa.us 468 : 36512 : MemoryContextSwitchTo(source_context);
469 : :
470 [ + + ]: 36512 : if (num_params > 0)
471 : : {
7 michael@paquier.xyz 472 :GNC 5858 : plansource->param_types = palloc_array(Oid, num_params);
5206 tgl@sss.pgh.pa.us 473 :CBC 5858 : memcpy(plansource->param_types, param_types, num_params * sizeof(Oid));
474 : : }
475 : : else
476 : 30654 : plansource->param_types = NULL;
6854 477 : 36512 : plansource->num_params = num_params;
5206 478 : 36512 : plansource->parserSetup = parserSetup;
479 : 36512 : plansource->parserSetupArg = parserSetupArg;
6820 480 : 36512 : plansource->cursor_options = cursor_options;
6854 481 : 36512 : plansource->fixed_result = fixed_result;
482 : :
483 : : /*
484 : : * Also save the result tuple descriptor. PlanCacheComputeResultDesc may
485 : : * leak some cruft; normally we just accept that to save a copy step, but
486 : : * in USE_VALGRIND mode be tidy by running it in the caller's context.
487 : : */
488 : : #ifdef USE_VALGRIND
489 : : MemoryContextSwitchTo(oldcxt);
490 : : plansource->resultDesc = PlanCacheComputeResultDesc(querytree_list);
491 : : if (plansource->resultDesc)
492 : : {
493 : : MemoryContextSwitchTo(source_context);
494 : : plansource->resultDesc = CreateTupleDescCopy(plansource->resultDesc);
495 : : MemoryContextSwitchTo(oldcxt);
496 : : }
497 : : #else
137 tgl@sss.pgh.pa.us 498 :GNC 36512 : plansource->resultDesc = PlanCacheComputeResultDesc(querytree_list);
5206 tgl@sss.pgh.pa.us 499 :CBC 36512 : MemoryContextSwitchTo(oldcxt);
500 : : #endif
501 : :
502 : 36512 : plansource->is_complete = true;
503 : 36512 : plansource->is_valid = true;
504 : 36512 : }
505 : :
506 : : /*
507 : : * SetPostRewriteHook: set a hook to modify post-rewrite query trees
508 : : *
509 : : * Some callers have a need to modify the query trees between rewriting and
510 : : * planning. In the initial call to CompleteCachedPlan, it's assumed such
511 : : * work was already done on the querytree_list. However, if we're forced
512 : : * to replan, it will need to be done over. The caller can set this hook
513 : : * to provide code to make that happen.
514 : : *
515 : : * postRewriteArg is just passed verbatim to the hook. As with parserSetupArg,
516 : : * it is caller's responsibility that the referenced data remains
517 : : * valid for as long as the CachedPlanSource exists.
518 : : */
519 : : void
259 520 : 1068 : SetPostRewriteHook(CachedPlanSource *plansource,
521 : : PostRewriteHook postRewrite,
522 : : void *postRewriteArg)
523 : : {
524 [ - + ]: 1068 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
525 : 1068 : plansource->postRewrite = postRewrite;
526 : 1068 : plansource->postRewriteArg = postRewriteArg;
527 : 1068 : }
528 : :
529 : : /*
530 : : * SaveCachedPlan: save a cached plan permanently
531 : : *
532 : : * This function moves the cached plan underneath CacheMemoryContext (making
533 : : * it live for the life of the backend, unless explicitly dropped), and adds
534 : : * it to the list of cached plans that are checked for invalidation when an
535 : : * sinval event occurs.
536 : : *
537 : : * This is guaranteed not to throw error, except for the caller-error case
538 : : * of trying to save a one-shot plan. Callers typically depend on that
539 : : * since this is called just before or just after adding a pointer to the
540 : : * CachedPlanSource to some permanent data structure of their own. Up until
541 : : * this is done, a CachedPlanSource is just transient data that will go away
542 : : * automatically on transaction abort.
543 : : */
544 : : void
5206 545 : 23026 : SaveCachedPlan(CachedPlanSource *plansource)
546 : : {
547 : : /* Assert caller is doing things in a sane order */
548 [ - + ]: 23026 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
549 [ - + ]: 23026 : Assert(plansource->is_complete);
550 [ - + ]: 23026 : Assert(!plansource->is_saved);
551 : :
552 : : /* This seems worth a real test, though */
4730 553 [ - + ]: 23026 : if (plansource->is_oneshot)
4730 tgl@sss.pgh.pa.us 554 [ # # ]:UBC 0 : elog(ERROR, "cannot save one-shot cached plan");
555 : :
556 : : /*
557 : : * In typical use, this function would be called before generating any
558 : : * plans from the CachedPlanSource. If there is a generic plan, moving it
559 : : * into CacheMemoryContext would be pretty risky since it's unclear
560 : : * whether the caller has taken suitable care with making references
561 : : * long-lived. Best thing to do seems to be to discard the plan.
562 : : */
5206 tgl@sss.pgh.pa.us 563 :CBC 23026 : ReleaseGenericPlan(plansource);
564 : :
565 : : /*
566 : : * Reparent the source memory context under CacheMemoryContext so that it
567 : : * will live indefinitely. The query_context follows along since it's
568 : : * already a child of the other one.
569 : : */
570 : 23026 : MemoryContextSetParent(plansource->context, CacheMemoryContext);
571 : :
572 : : /*
573 : : * Add the entry to the global list of cached plans.
574 : : */
2561 575 : 23026 : dlist_push_tail(&saved_plan_list, &plansource->node);
576 : :
5206 577 : 23026 : plansource->is_saved = true;
6854 578 : 23026 : }
579 : :
580 : : /*
581 : : * DropCachedPlan: destroy a cached plan.
582 : : *
583 : : * Actually this only destroys the CachedPlanSource: any referenced CachedPlan
584 : : * is released, but not destroyed until its refcount goes to zero. That
585 : : * handles the situation where DropCachedPlan is called while the plan is
586 : : * still in use.
587 : : */
588 : : void
5206 589 : 7871 : DropCachedPlan(CachedPlanSource *plansource)
590 : : {
591 [ - + ]: 7871 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
592 : :
593 : : /* If it's been saved, remove it from the list */
594 [ + + ]: 7871 : if (plansource->is_saved)
595 : : {
2561 596 : 7776 : dlist_delete(&plansource->node);
5206 597 : 7776 : plansource->is_saved = false;
598 : : }
599 : :
600 : : /* Decrement generic CachedPlan's refcount and drop if no longer needed */
601 : 7871 : ReleaseGenericPlan(plansource);
602 : :
603 : : /* Mark it no longer valid */
4730 604 : 7871 : plansource->magic = 0;
605 : :
606 : : /*
607 : : * Remove the CachedPlanSource and all subsidiary data (including the
608 : : * query_context if any). But if it's a one-shot we can't free anything.
609 : : */
610 [ + - ]: 7871 : if (!plansource->is_oneshot)
611 : 7871 : MemoryContextDelete(plansource->context);
5887 612 : 7871 : }
613 : :
614 : : /*
615 : : * ReleaseGenericPlan: release a CachedPlanSource's generic plan, if any.
616 : : */
617 : : static void
5206 618 : 58961 : ReleaseGenericPlan(CachedPlanSource *plansource)
619 : : {
620 : : /* Be paranoid about the possibility that ReleaseCachedPlan fails */
621 [ + + ]: 58961 : if (plansource->gplan)
622 : : {
623 : 7629 : CachedPlan *plan = plansource->gplan;
624 : :
625 [ - + ]: 7629 : Assert(plan->magic == CACHEDPLAN_MAGIC);
626 : 7629 : plansource->gplan = NULL;
1787 627 : 7629 : ReleaseCachedPlan(plan, NULL);
628 : : }
5206 629 : 58961 : }
630 : :
631 : : /*
632 : : * We must skip "overhead" operations that involve database access when the
633 : : * cached plan's subject statement is a transaction control command or one
634 : : * that requires a snapshot not to be set yet (such as SET or LOCK). More
635 : : * generally, statements that do not require parse analysis/rewrite/plan
636 : : * activity never need to be revalidated, so we can treat them all like that.
637 : : * For the convenience of postgres.c, treat empty statements that way too.
638 : : */
639 : : static bool
259 640 : 15281887 : StmtPlanRequiresRevalidation(CachedPlanSource *plansource)
641 : : {
642 [ + + ]: 15281887 : if (plansource->raw_parse_tree != NULL)
643 : 15018746 : return stmt_requires_parse_analysis(plansource->raw_parse_tree);
644 [ + + ]: 263141 : else if (plansource->analyzed_parse_tree != NULL)
645 : 263138 : return query_requires_rewrite_plan(plansource->analyzed_parse_tree);
646 : : /* empty query never needs revalidation */
647 : 3 : return false;
648 : : }
649 : :
650 : : /*
651 : : * Determine if creating a plan for this CachedPlanSource requires a snapshot.
652 : : * In fact this function matches StmtPlanRequiresRevalidation(), but we want
653 : : * to preserve the distinction between stmt_requires_parse_analysis() and
654 : : * analyze_requires_snapshot().
655 : : */
656 : : static bool
657 : 491 : BuildingPlanRequiresSnapshot(CachedPlanSource *plansource)
658 : : {
659 [ + - ]: 491 : if (plansource->raw_parse_tree != NULL)
660 : 491 : return analyze_requires_snapshot(plansource->raw_parse_tree);
259 tgl@sss.pgh.pa.us 661 [ # # ]:UBC 0 : else if (plansource->analyzed_parse_tree != NULL)
662 : 0 : return query_requires_rewrite_plan(plansource->analyzed_parse_tree);
663 : : /* empty query never needs a snapshot */
664 : 0 : return false;
665 : : }
666 : :
667 : : /*
668 : : * RevalidateCachedQuery: ensure validity of analyzed-and-rewritten query tree.
669 : : *
670 : : * What we do here is re-acquire locks and redo parse analysis if necessary.
671 : : * On return, the query_list is valid and we have sufficient locks to begin
672 : : * planning.
673 : : *
674 : : * If any parse analysis activity is required, the caller's memory context is
675 : : * used for that work.
676 : : *
677 : : * The result value is the transient analyzed-and-rewritten query tree if we
678 : : * had to do re-analysis, and NIL otherwise. (This is returned just to save
679 : : * a tree copying step in a subsequent BuildCachedPlan call.)
680 : : */
681 : : static List *
3183 kgrittn@postgresql.o 682 :CBC 141749 : RevalidateCachedQuery(CachedPlanSource *plansource,
683 : : QueryEnvironment *queryEnv)
684 : : {
685 : : bool snapshot_set;
686 : : List *tlist; /* transient query-tree list */
687 : : List *qlist; /* permanent query-tree list */
688 : : TupleDesc resultDesc;
689 : : MemoryContext querytree_context;
690 : : MemoryContext oldcxt;
691 : :
692 : : /*
693 : : * For one-shot plans, we do not support revalidation checking; it's
694 : : * assumed the query is parsed, planned, and executed in one transaction,
695 : : * so that no lock re-acquisition is necessary. Also, if the statement
696 : : * type can't require revalidation, we needn't do anything (and we mustn't
697 : : * risk catalog accesses when handling, eg, transaction control commands).
698 : : */
846 tgl@sss.pgh.pa.us 699 [ + + + + ]: 141749 : if (plansource->is_oneshot || !StmtPlanRequiresRevalidation(plansource))
700 : : {
4730 701 [ - + ]: 19363 : Assert(plansource->is_valid);
702 : 19363 : return NIL;
703 : : }
704 : :
705 : : /*
706 : : * If the query is currently valid, we should have a saved search_path ---
707 : : * check to see if that matches the current environment. If not, we want
708 : : * to force replan. (We could almost ignore this consideration when
709 : : * working from an analyzed parse tree; but there are scenarios where
710 : : * planning can have search_path-dependent results, for example if it
711 : : * inlines an old-style SQL function.)
712 : : */
4709 713 [ + + ]: 122386 : if (plansource->is_valid)
714 : : {
715 [ - + ]: 119304 : Assert(plansource->search_path != NULL);
870 noah@leadboat.com 716 [ + + ]: 119304 : if (!SearchPathMatchesCurrentEnvironment(plansource->search_path))
717 : : {
718 : : /* Invalidate the querytree and generic plan */
4709 tgl@sss.pgh.pa.us 719 : 39 : plansource->is_valid = false;
720 [ + + ]: 39 : if (plansource->gplan)
721 : 25 : plansource->gplan->is_valid = false;
722 : : }
723 : : }
724 : :
725 : : /*
726 : : * If the query rewrite phase had a possible RLS dependency, we must redo
727 : : * it if either the role or the row_security setting has changed.
728 : : */
3442 729 [ + + + + : 122535 : if (plansource->is_valid && plansource->dependsOnRLS &&
+ + ]
730 : 149 : (plansource->rewriteRoleId != GetUserId() ||
731 [ + + ]: 104 : plansource->rewriteRowSecurity != row_security))
4107 sfrost@snowman.net 732 : 57 : plansource->is_valid = false;
733 : :
734 : : /*
735 : : * If the query is currently valid, acquire locks on the referenced
736 : : * objects; then check again. We need to do it this way to cover the race
737 : : * condition that an invalidation message arrives before we get the locks.
738 : : */
5206 tgl@sss.pgh.pa.us 739 [ + + ]: 122386 : if (plansource->is_valid)
740 : : {
741 : 119208 : AcquirePlannerLocks(plansource->query_list, true);
742 : :
743 : : /*
744 : : * By now, if any invalidation has happened, the inval callback
745 : : * functions will have marked the query invalid.
746 : : */
747 [ + + ]: 119208 : if (plansource->is_valid)
748 : : {
749 : : /* Successfully revalidated and locked the query. */
750 : 119201 : return NIL;
751 : : }
752 : :
753 : : /* Oops, the race case happened. Release useless locks. */
754 : 7 : AcquirePlannerLocks(plansource->query_list, false);
755 : : }
756 : :
757 : : /*
758 : : * Discard the no-longer-useful rewritten query tree. (Note: we don't
759 : : * want to do this any earlier, else we'd not have been able to release
760 : : * locks correctly in the race condition case.)
761 : : */
762 : 3185 : plansource->is_valid = false;
763 : 3185 : plansource->query_list = NIL;
764 : 3185 : plansource->relationOids = NIL;
765 : 3185 : plansource->invalItems = NIL;
4709 766 : 3185 : plansource->search_path = NULL;
767 : :
768 : : /*
769 : : * Free the query_context. We don't really expect MemoryContextDelete to
770 : : * fail, but just in case, make sure the CachedPlanSource is left in a
771 : : * reasonably sane state. (The generic plan won't get unlinked yet, but
772 : : * that's acceptable.)
773 : : */
5206 774 [ + + ]: 3185 : if (plansource->query_context)
775 : : {
4938 bruce@momjian.us 776 : 3170 : MemoryContext qcxt = plansource->query_context;
777 : :
5206 tgl@sss.pgh.pa.us 778 : 3170 : plansource->query_context = NULL;
779 : 3170 : MemoryContextDelete(qcxt);
780 : : }
781 : :
782 : : /* Drop the generic plan reference if any */
209 amitlan@postgresql.o 783 : 3185 : ReleaseGenericPlan(plansource);
784 : :
785 : : /*
786 : : * Now re-do parse analysis and rewrite. This not incidentally acquires
787 : : * the locks we need to do planning safely.
788 : : */
5206 tgl@sss.pgh.pa.us 789 [ - + ]: 3185 : Assert(plansource->is_complete);
790 : :
791 : : /*
792 : : * If a snapshot is already set (the normal case), we can just use that
793 : : * for parsing/planning. But if it isn't, install one. Note: no point in
794 : : * checking whether parse analysis requires a snapshot; utility commands
795 : : * don't have invalidatable plans, so we'd not get here for such a
796 : : * command.
797 : : */
798 : 3185 : snapshot_set = false;
799 [ + + ]: 3185 : if (!ActiveSnapshotSet())
800 : : {
801 : 10 : PushActiveSnapshot(GetTransactionSnapshot());
802 : 10 : snapshot_set = true;
803 : : }
804 : :
805 : : /*
806 : : * Run parse analysis (if needed) and rule rewriting.
807 : : */
259 808 [ + + ]: 3185 : if (plansource->raw_parse_tree != NULL)
809 : : {
810 : : /* Source is raw parse tree */
811 : : RawStmt *rawtree;
812 : :
813 : : /*
814 : : * The parser tends to scribble on its input, so we must copy the raw
815 : : * parse tree to prevent corruption of the cache.
816 : : */
817 : 3036 : rawtree = copyObject(plansource->raw_parse_tree);
818 [ + + ]: 3036 : if (plansource->parserSetup != NULL)
819 : 2817 : tlist = pg_analyze_and_rewrite_withcb(rawtree,
820 : : plansource->query_string,
821 : : plansource->parserSetup,
822 : : plansource->parserSetupArg,
823 : : queryEnv);
824 : : else
825 : 219 : tlist = pg_analyze_and_rewrite_fixedparams(rawtree,
826 : : plansource->query_string,
827 : 219 : plansource->param_types,
828 : : plansource->num_params,
829 : : queryEnv);
830 : : }
831 [ + - ]: 149 : else if (plansource->analyzed_parse_tree != NULL)
832 : : {
833 : : /* Source is pre-analyzed query, so we only need to rewrite */
834 : : Query *analyzed_tree;
835 : :
836 : : /* The rewriter scribbles on its input, too, so copy */
837 : 149 : analyzed_tree = copyObject(plansource->analyzed_parse_tree);
838 : : /* Acquire locks needed before rewriting ... */
839 : 149 : AcquireRewriteLocks(analyzed_tree, true, false);
840 : : /* ... and do it */
841 : 149 : tlist = pg_rewrite_query(analyzed_tree);
842 : : }
843 : : else
844 : : {
845 : : /* Empty query, nothing to do */
259 tgl@sss.pgh.pa.us 846 :UBC 0 : tlist = NIL;
847 : : }
848 : :
849 : : /* Apply post-rewrite callback if there is one */
259 tgl@sss.pgh.pa.us 850 [ + + ]:CBC 3165 : if (plansource->postRewrite != NULL)
851 : 192 : plansource->postRewrite(tlist, plansource->postRewriteArg);
852 : :
853 : : /* Release snapshot if we got one */
5206 854 [ + + ]: 3165 : if (snapshot_set)
855 : 10 : PopActiveSnapshot();
856 : :
857 : : /*
858 : : * Check or update the result tupdesc.
859 : : *
860 : : * We assume the parameter types didn't change from the first time, so no
861 : : * need to update that.
862 : : */
863 : 3165 : resultDesc = PlanCacheComputeResultDesc(tlist);
864 [ + + + - ]: 3165 : if (resultDesc == NULL && plansource->resultDesc == NULL)
865 : : {
866 : : /* OK, doesn't return tuples */
867 : : }
868 [ + - + - ]: 3082 : else if (resultDesc == NULL || plansource->resultDesc == NULL ||
640 peter@eisentraut.org 869 [ + + ]: 3082 : !equalRowTypes(resultDesc, plansource->resultDesc))
870 : : {
871 : : /* can we give a better error message? */
5206 tgl@sss.pgh.pa.us 872 [ + + ]: 29 : if (plansource->fixed_result)
873 [ + - ]: 6 : ereport(ERROR,
874 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
875 : : errmsg("cached plan must not change result type")));
876 : 23 : oldcxt = MemoryContextSwitchTo(plansource->context);
877 [ + - ]: 23 : if (resultDesc)
878 : 23 : resultDesc = CreateTupleDescCopy(resultDesc);
879 [ + - ]: 23 : if (plansource->resultDesc)
880 : 23 : FreeTupleDesc(plansource->resultDesc);
881 : 23 : plansource->resultDesc = resultDesc;
882 : 23 : MemoryContextSwitchTo(oldcxt);
883 : : }
884 : :
885 : : /*
886 : : * Allocate new query_context and copy the completed querytree into it.
887 : : * It's transient until we complete the copying and dependency extraction.
888 : : */
889 : 3159 : querytree_context = AllocSetContextCreate(CurrentMemoryContext,
890 : : "CachedPlanQuery",
891 : : ALLOCSET_START_SMALL_SIZES);
892 : 3159 : oldcxt = MemoryContextSwitchTo(querytree_context);
893 : :
3205 peter_e@gmx.net 894 : 3159 : qlist = copyObject(tlist);
895 : :
896 : : /*
897 : : * Use the planner machinery to extract dependencies. Data is saved in
898 : : * query_context. (We assume that not a lot of extra cruft is created by
899 : : * this call.)
900 : : */
5206 tgl@sss.pgh.pa.us 901 : 3159 : extract_query_dependencies((Node *) qlist,
902 : : &plansource->relationOids,
903 : : &plansource->invalItems,
904 : : &plansource->dependsOnRLS);
905 : :
906 : : /* Update RLS info as well. */
3442 907 : 3159 : plansource->rewriteRoleId = GetUserId();
908 : 3159 : plansource->rewriteRowSecurity = row_security;
909 : :
910 : : /*
911 : : * Also save the current search_path in the query_context. (This should
912 : : * not generate much extra cruft either, since almost certainly the path
913 : : * is already valid.)
914 : : */
870 noah@leadboat.com 915 : 3159 : plansource->search_path = GetSearchPathMatcher(querytree_context);
916 : :
5206 tgl@sss.pgh.pa.us 917 : 3159 : MemoryContextSwitchTo(oldcxt);
918 : :
919 : : /* Now reparent the finished query_context and save the links */
920 : 3159 : MemoryContextSetParent(querytree_context, plansource->context);
921 : :
922 : 3159 : plansource->query_context = querytree_context;
923 : 3159 : plansource->query_list = qlist;
924 : :
925 : : /*
926 : : * Note: we do not reset generic_cost or total_custom_cost, although we
927 : : * could choose to do so. If the DDL or statistics change that prompted
928 : : * the invalidation meant a significant change in the cost estimates, it
929 : : * would be better to reset those variables and start fresh; but often it
930 : : * doesn't, and we're better retaining our hard-won knowledge about the
931 : : * relative costs.
932 : : */
933 : :
934 : 3159 : plansource->is_valid = true;
935 : :
936 : : /* Return transient copy of querytrees for possible use in planning */
937 : 3159 : return tlist;
938 : : }
939 : :
940 : : /*
941 : : * CheckCachedPlan: see if the CachedPlanSource's generic plan is valid.
942 : : *
943 : : * Caller must have already called RevalidateCachedQuery to verify that the
944 : : * querytree is up to date.
945 : : *
946 : : * On a "true" return, we have acquired the locks needed to run the plan.
947 : : * (We must do this for the "true" result to be race-condition-free.)
948 : : */
949 : : static bool
950 : 111825 : CheckCachedPlan(CachedPlanSource *plansource)
951 : : {
952 : 111825 : CachedPlan *plan = plansource->gplan;
953 : :
954 : : /* Assert that caller checked the querytree */
955 [ - + ]: 111825 : Assert(plansource->is_valid);
956 : :
957 : : /* If there's no generic plan, just say "false" */
958 [ + + ]: 111825 : if (!plan)
959 : 24830 : return false;
960 : :
961 [ - + ]: 86995 : Assert(plan->magic == CACHEDPLAN_MAGIC);
962 : : /* Generic plans are never one-shot */
4730 963 [ - + ]: 86995 : Assert(!plan->is_oneshot);
964 : :
965 : : /*
966 : : * If plan isn't valid for current role, we can't use it.
967 : : */
3442 968 [ + + + + : 87016 : if (plan->is_valid && plan->dependsOnRole &&
+ + ]
969 : 21 : plan->planRoleId != GetUserId())
970 : 18 : plan->is_valid = false;
971 : :
972 : : /*
973 : : * If it appears valid, acquire locks and recheck; this is much the same
974 : : * logic as in RevalidateCachedQuery, but for a plan.
975 : : */
5206 976 [ + + ]: 86995 : if (plan->is_valid)
977 : : {
978 : : /*
979 : : * Plan must have positive refcount because it is referenced by
980 : : * plansource; so no need to fear it disappears under us here.
981 : : */
6854 982 [ - + ]: 86956 : Assert(plan->refcount > 0);
983 : :
5206 984 : 86956 : AcquireExecutorLocks(plan->stmt_list, true);
985 : :
986 : : /*
987 : : * If plan was transient, check to see if TransactionXmin has
988 : : * advanced, and if so invalidate it.
989 : : */
990 [ + - ]: 86956 : if (plan->is_valid &&
6663 991 [ - + ]: 86956 : TransactionIdIsValid(plan->saved_xmin) &&
6663 tgl@sss.pgh.pa.us 992 [ # # ]:UBC 0 : !TransactionIdEquals(plan->saved_xmin, TransactionXmin))
5206 993 : 0 : plan->is_valid = false;
994 : :
995 : : /*
996 : : * By now, if any invalidation has happened, the inval callback
997 : : * functions will have marked the plan invalid.
998 : : */
5206 tgl@sss.pgh.pa.us 999 [ + - ]:CBC 86956 : if (plan->is_valid)
1000 : : {
1001 : : /* Successfully revalidated and locked the query. */
1002 : 86956 : return true;
1003 : : }
1004 : :
1005 : : /* Oops, the race case happened. Release useless locks. */
5206 tgl@sss.pgh.pa.us 1006 :UBC 0 : AcquireExecutorLocks(plan->stmt_list, false);
1007 : : }
1008 : :
1009 : : /*
1010 : : * Plan has been invalidated, so unlink it from the parent and release it.
1011 : : */
5206 tgl@sss.pgh.pa.us 1012 :CBC 39 : ReleaseGenericPlan(plansource);
1013 : :
1014 : 39 : return false;
1015 : : }
1016 : :
1017 : : /*
1018 : : * BuildCachedPlan: construct a new CachedPlan from a CachedPlanSource.
1019 : : *
1020 : : * qlist should be the result value from a previous RevalidateCachedQuery,
1021 : : * or it can be set to NIL if we need to re-copy the plansource's query_list.
1022 : : *
1023 : : * To build a generic, parameter-value-independent plan, pass NULL for
1024 : : * boundParams. To build a custom plan, pass the actual parameter values via
1025 : : * boundParams. For best effect, the PARAM_FLAG_CONST flag should be set on
1026 : : * each parameter value; otherwise the planner will treat the value as a
1027 : : * hint rather than a hard constant.
1028 : : *
1029 : : * Planning work is done in the caller's memory context. The finished plan
1030 : : * is in a child memory context, which typically should get reparented
1031 : : * (unless this is a one-shot plan, in which case we don't copy the plan).
1032 : : */
1033 : : static CachedPlan *
1034 : 46933 : BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
1035 : : ParamListInfo boundParams, QueryEnvironment *queryEnv)
1036 : : {
1037 : : CachedPlan *plan;
1038 : : List *plist;
1039 : : bool snapshot_set;
1040 : : bool is_transient;
1041 : : MemoryContext plan_context;
4730 1042 : 46933 : MemoryContext oldcxt = CurrentMemoryContext;
1043 : : ListCell *lc;
1044 : :
1045 : : /*
1046 : : * Normally the querytree should be valid already, but if it's not,
1047 : : * rebuild it.
1048 : : *
1049 : : * NOTE: GetCachedPlan should have called RevalidateCachedQuery first, so
1050 : : * we ought to be holding sufficient locks to prevent any invalidation.
1051 : : * However, if we're building a custom plan after having built and
1052 : : * rejected a generic plan, it's possible to reach here with is_valid
1053 : : * false due to an invalidation while making the generic plan. In theory
1054 : : * the invalidation must be a false positive, perhaps a consequence of an
1055 : : * sinval reset event or the debug_discard_caches code. But for safety,
1056 : : * let's treat it as real and redo the RevalidateCachedQuery call.
1057 : : */
5205 1058 [ - + ]: 46933 : if (!plansource->is_valid)
209 amitlan@postgresql.o 1059 :UBC 0 : qlist = RevalidateCachedQuery(plansource, queryEnv);
1060 : :
1061 : : /*
1062 : : * If we don't already have a copy of the querytree list that can be
1063 : : * scribbled on by the planner, make one. For a one-shot plan, we assume
1064 : : * it's okay to scribble on the original query_list.
1065 : : */
5206 tgl@sss.pgh.pa.us 1066 [ + + ]:CBC 46933 : if (qlist == NIL)
1067 : : {
4730 1068 [ + + ]: 43775 : if (!plansource->is_oneshot)
3205 peter_e@gmx.net 1069 : 35776 : qlist = copyObject(plansource->query_list);
1070 : : else
4730 tgl@sss.pgh.pa.us 1071 : 7999 : qlist = plansource->query_list;
1072 : : }
1073 : :
1074 : : /*
1075 : : * If a snapshot is already set (the normal case), we can just use that
1076 : : * for planning. But if it isn't, and we need one, install one.
1077 : : */
5206 1078 : 46933 : snapshot_set = false;
5197 1079 [ + + + + ]: 47424 : if (!ActiveSnapshotSet() &&
259 1080 : 491 : BuildingPlanRequiresSnapshot(plansource))
1081 : : {
5206 1082 : 137 : PushActiveSnapshot(GetTransactionSnapshot());
1083 : 137 : snapshot_set = true;
1084 : : }
1085 : :
1086 : : /*
1087 : : * Generate the plan.
1088 : : */
2088 fujii@postgresql.org 1089 : 46933 : plist = pg_plan_queries(qlist, plansource->query_string,
1090 : : plansource->cursor_options, boundParams);
1091 : :
1092 : : /* Release snapshot if we got one */
5206 tgl@sss.pgh.pa.us 1093 [ + + ]: 46828 : if (snapshot_set)
1094 : 134 : PopActiveSnapshot();
1095 : :
1096 : : /*
1097 : : * Normally we make a dedicated memory context for the CachedPlan and its
1098 : : * subsidiary data. (It's probably not going to be large, but just in
1099 : : * case, allow it to grow large. It's transient for the moment.) But for
1100 : : * a one-shot plan, we just leave it in the caller's memory context.
1101 : : */
4730 1102 [ + + ]: 46828 : if (!plansource->is_oneshot)
1103 : : {
1104 : 38867 : plan_context = AllocSetContextCreate(CurrentMemoryContext,
1105 : : "CachedPlan",
1106 : : ALLOCSET_START_SMALL_SIZES);
2812 peter_e@gmx.net 1107 : 38867 : MemoryContextCopyAndSetIdentifier(plan_context, plansource->query_string);
1108 : :
1109 : : /*
1110 : : * Copy plan into the new context.
1111 : : */
209 amitlan@postgresql.o 1112 : 38867 : MemoryContextSwitchTo(plan_context);
1113 : :
3205 peter_e@gmx.net 1114 : 38867 : plist = copyObject(plist);
1115 : : }
1116 : : else
4730 tgl@sss.pgh.pa.us 1117 : 7961 : plan_context = CurrentMemoryContext;
1118 : :
1119 : : /*
1120 : : * Create and fill the CachedPlan struct within the new context.
1121 : : */
7 michael@paquier.xyz 1122 :GNC 46828 : plan = palloc_object(CachedPlan);
5206 tgl@sss.pgh.pa.us 1123 :CBC 46828 : plan->magic = CACHEDPLAN_MAGIC;
1124 : 46828 : plan->stmt_list = plist;
1125 : :
1126 : : /*
1127 : : * CachedPlan is dependent on role either if RLS affected the rewrite
1128 : : * phase or if a role dependency was injected during planning. And it's
1129 : : * transient if any plan is marked so.
1130 : : */
3442 1131 : 46828 : plan->planRoleId = GetUserId();
1132 : 46828 : plan->dependsOnRole = plansource->dependsOnRLS;
1133 : 46828 : is_transient = false;
1134 [ + - + + : 93659 : foreach(lc, plist)
+ + ]
1135 : : {
3173 1136 : 46831 : PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
1137 : :
3259 1138 [ + + ]: 46831 : if (plannedstmt->commandType == CMD_UTILITY)
3442 1139 : 8249 : continue; /* Ignore utility statements */
1140 : :
1141 [ + + ]: 38582 : if (plannedstmt->transientPlan)
1142 : 66 : is_transient = true;
1143 [ + + ]: 38582 : if (plannedstmt->dependsOnRole)
1144 : 36 : plan->dependsOnRole = true;
1145 : : }
1146 [ + + ]: 46828 : if (is_transient)
1147 : : {
5206 1148 [ - + ]: 66 : Assert(TransactionIdIsNormal(TransactionXmin));
1149 : 66 : plan->saved_xmin = TransactionXmin;
1150 : : }
1151 : : else
1152 : 46762 : plan->saved_xmin = InvalidTransactionId;
1153 : 46828 : plan->refcount = 0;
1154 : 46828 : plan->context = plan_context;
4730 1155 : 46828 : plan->is_oneshot = plansource->is_oneshot;
5206 1156 : 46828 : plan->is_saved = false;
1157 : 46828 : plan->is_valid = true;
1158 : :
1159 : : /* assign generation number to new plan */
1160 : 46828 : plan->generation = ++(plansource->generation);
1161 : :
1162 : 46828 : MemoryContextSwitchTo(oldcxt);
1163 : :
1164 : 46828 : return plan;
1165 : : }
1166 : :
1167 : : /*
1168 : : * choose_custom_plan: choose whether to use custom or generic plan
1169 : : *
1170 : : * This defines the policy followed by GetCachedPlan.
1171 : : */
1172 : : static bool
1173 : 158692 : choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams)
1174 : : {
1175 : : double avg_custom_cost;
1176 : :
1177 : : /* One-shot plans will always be considered custom */
4730 1178 [ + + ]: 158692 : if (plansource->is_oneshot)
1179 : 7999 : return true;
1180 : :
1181 : : /* Otherwise, never any point in a custom plan if there's no parameters */
5206 1182 [ + + ]: 150693 : if (boundParams == NULL)
1183 : 75443 : return false;
1184 : : /* ... nor when planning would be a no-op */
846 1185 [ - + ]: 75250 : if (!StmtPlanRequiresRevalidation(plansource))
4624 tgl@sss.pgh.pa.us 1186 :UBC 0 : return false;
1187 : :
1188 : : /* Let settings force the decision */
2711 peter_e@gmx.net 1189 [ + + ]:CBC 75250 : if (plan_cache_mode == PLAN_CACHE_MODE_FORCE_GENERIC_PLAN)
1190 : 1441 : return false;
1191 [ + + ]: 73809 : if (plan_cache_mode == PLAN_CACHE_MODE_FORCE_CUSTOM_PLAN)
1192 : 20 : return true;
1193 : :
1194 : : /* See if caller wants to force the decision */
5206 tgl@sss.pgh.pa.us 1195 [ - + ]: 73789 : if (plansource->cursor_options & CURSOR_OPT_GENERIC_PLAN)
5206 tgl@sss.pgh.pa.us 1196 :UBC 0 : return false;
5206 tgl@sss.pgh.pa.us 1197 [ - + ]:CBC 73789 : if (plansource->cursor_options & CURSOR_OPT_CUSTOM_PLAN)
5206 tgl@sss.pgh.pa.us 1198 :UBC 0 : return true;
1199 : :
1200 : : /* Generate custom plans until we have done at least 5 (arbitrary) */
5206 tgl@sss.pgh.pa.us 1201 [ + + ]:CBC 73789 : if (plansource->num_custom_plans < 5)
1202 : 13031 : return true;
1203 : :
1204 : 60758 : avg_custom_cost = plansource->total_custom_cost / plansource->num_custom_plans;
1205 : :
1206 : : /*
1207 : : * Prefer generic plan if it's less expensive than the average custom
1208 : : * plan. (Because we include a charge for cost of planning in the
1209 : : * custom-plan costs, this means the generic plan only has to be less
1210 : : * expensive than the execution cost plus replan cost of the custom
1211 : : * plans.)
1212 : : *
1213 : : * Note that if generic_cost is -1 (indicating we've not yet determined
1214 : : * the generic plan cost), we'll always prefer generic at this point.
1215 : : */
4498 1216 [ + + ]: 60758 : if (plansource->generic_cost < avg_custom_cost)
5206 1217 : 59744 : return false;
1218 : :
1219 : 1014 : return true;
1220 : : }
1221 : :
1222 : : /*
1223 : : * cached_plan_cost: calculate estimated cost of a plan
1224 : : *
1225 : : * If include_planner is true, also include the estimated cost of constructing
1226 : : * the plan. (We must factor that into the cost of using a custom plan, but
1227 : : * we don't count it for a generic plan.)
1228 : : */
1229 : : static double
4498 1230 : 46828 : cached_plan_cost(CachedPlan *plan, bool include_planner)
1231 : : {
5206 1232 : 46828 : double result = 0;
1233 : : ListCell *lc;
1234 : :
1235 [ + - + + : 93659 : foreach(lc, plan->stmt_list)
+ + ]
1236 : : {
3173 1237 : 46831 : PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
1238 : :
3259 1239 [ + + ]: 46831 : if (plannedstmt->commandType == CMD_UTILITY)
5206 1240 : 8249 : continue; /* Ignore utility statements */
1241 : :
1242 : 38582 : result += plannedstmt->planTree->total_cost;
1243 : :
4498 1244 [ + + ]: 38582 : if (include_planner)
1245 : : {
1246 : : /*
1247 : : * Currently we use a very crude estimate of planning effort based
1248 : : * on the number of relations in the finished plan's rangetable.
1249 : : * Join planning effort actually scales much worse than linearly
1250 : : * in the number of relations --- but only until the join collapse
1251 : : * limits kick in. Also, while inheritance child relations surely
1252 : : * add to planning effort, they don't make the join situation
1253 : : * worse. So the actual shape of the planning cost curve versus
1254 : : * number of relations isn't all that obvious. It will take
1255 : : * considerable work to arrive at a less crude estimate, and for
1256 : : * now it's not clear that's worth doing.
1257 : : *
1258 : : * The other big difficulty here is that we don't have any very
1259 : : * good model of how planning cost compares to execution costs.
1260 : : * The current multiplier of 1000 * cpu_operator_cost is probably
1261 : : * on the low side, but we'll try this for awhile before making a
1262 : : * more aggressive correction.
1263 : : *
1264 : : * If we ever do write a more complicated estimator, it should
1265 : : * probably live in src/backend/optimizer/ not here.
1266 : : */
1267 : 19302 : int nrelations = list_length(plannedstmt->rtable);
1268 : :
1269 : 19302 : result += 1000.0 * cpu_operator_cost * (nrelations + 1);
1270 : : }
1271 : : }
1272 : :
5206 1273 : 46828 : return result;
1274 : : }
1275 : :
1276 : : /*
1277 : : * GetCachedPlan: get a cached plan from a CachedPlanSource.
1278 : : *
1279 : : * This function hides the logic that decides whether to use a generic
1280 : : * plan or a custom plan for the given parameters: the caller does not know
1281 : : * which it will get.
1282 : : *
1283 : : * On return, the plan is valid and we have sufficient locks to begin
1284 : : * execution.
1285 : : *
1286 : : * On return, the refcount of the plan has been incremented; a later
1287 : : * ReleaseCachedPlan() call is expected. If "owner" is not NULL then
1288 : : * the refcount has been reported to that ResourceOwner (note that this
1289 : : * is only supported for "saved" CachedPlanSources).
1290 : : *
1291 : : * Note: if any replanning activity is required, the caller's memory context
1292 : : * is used for that work.
1293 : : */
1294 : : CachedPlan *
1295 : 133878 : GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
1296 : : ResourceOwner owner, QueryEnvironment *queryEnv)
1297 : : {
3298 sfrost@snowman.net 1298 : 133878 : CachedPlan *plan = NULL;
1299 : : List *qlist;
1300 : : bool customplan;
1301 : : ListCell *lc;
1302 : :
1303 : : /* Assert caller is doing things in a sane order */
5206 tgl@sss.pgh.pa.us 1304 [ - + ]: 133878 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1305 [ - + ]: 133878 : Assert(plansource->is_complete);
1306 : : /* This seems worth a real test, though */
1787 1307 [ + + - + ]: 133878 : if (owner && !plansource->is_saved)
5206 tgl@sss.pgh.pa.us 1308 [ # # ]:UBC 0 : elog(ERROR, "cannot apply ResourceOwner to non-saved cached plan");
1309 : :
1310 : : /* Make sure the querytree list is valid and we have parse-time locks */
209 amitlan@postgresql.o 1311 :CBC 133878 : qlist = RevalidateCachedQuery(plansource, queryEnv);
1312 : :
1313 : : /* Decide whether to use a custom plan */
5206 tgl@sss.pgh.pa.us 1314 : 133852 : customplan = choose_custom_plan(plansource, boundParams);
1315 : :
1316 [ + + ]: 133852 : if (!customplan)
1317 : : {
1318 [ + + ]: 111825 : if (CheckCachedPlan(plansource))
1319 : : {
1320 : : /* We want a generic plan, and we already have a valid one */
1321 : 86956 : plan = plansource->gplan;
1322 [ - + ]: 86956 : Assert(plan->magic == CACHEDPLAN_MAGIC);
1323 : : }
1324 : : else
1325 : : {
1326 : : /* Build a new generic plan */
3183 kgrittn@postgresql.o 1327 : 24869 : plan = BuildCachedPlan(plansource, qlist, NULL, queryEnv);
1328 : : /* Just make real sure plansource->gplan is clear */
5206 tgl@sss.pgh.pa.us 1329 : 24840 : ReleaseGenericPlan(plansource);
1330 : : /* Link the new generic plan into the plansource */
1331 : 24840 : plansource->gplan = plan;
1332 : 24840 : plan->refcount++;
1333 : : /* Immediately reparent into appropriate context */
1334 [ + + ]: 24840 : if (plansource->is_saved)
1335 : : {
1336 : : /* saved plans all live under CacheMemoryContext */
1337 : 19365 : MemoryContextSetParent(plan->context, CacheMemoryContext);
1338 : 19365 : plan->is_saved = true;
1339 : : }
1340 : : else
1341 : : {
1342 : : /* otherwise, it should be a sibling of the plansource */
1343 : 5475 : MemoryContextSetParent(plan->context,
1344 : : MemoryContextGetParent(plansource->context));
1345 : : }
1346 : : /* Update generic_cost whenever we make a new generic plan */
4498 1347 : 24840 : plansource->generic_cost = cached_plan_cost(plan, false);
1348 : :
1349 : : /*
1350 : : * If, based on the now-known value of generic_cost, we'd not have
1351 : : * chosen to use a generic plan, then forget it and make a custom
1352 : : * plan. This is a bit of a wart but is necessary to avoid a
1353 : : * glitch in behavior when the custom plans are consistently big
1354 : : * winners; at some point we'll experiment with a generic plan and
1355 : : * find it's a loser, but we don't want to actually execute that
1356 : : * plan.
1357 : : */
5206 1358 : 24840 : customplan = choose_custom_plan(plansource, boundParams);
1359 : :
1360 : : /*
1361 : : * If we choose to plan again, we need to re-copy the query_list,
1362 : : * since the planner probably scribbled on it. We can force
1363 : : * BuildCachedPlan to do that by passing NIL.
1364 : : */
5196 1365 : 24840 : qlist = NIL;
1366 : : }
1367 : : }
1368 : :
5206 1369 [ + + ]: 133823 : if (customplan)
1370 : : {
1371 : : /* Build a custom plan */
3183 kgrittn@postgresql.o 1372 : 22064 : plan = BuildCachedPlan(plansource, qlist, boundParams, queryEnv);
1373 : : /* Accumulate total costs of custom plans */
1976 fujii@postgresql.org 1374 : 21988 : plansource->total_custom_cost += cached_plan_cost(plan, true);
1375 : :
1376 : 21988 : plansource->num_custom_plans++;
1377 : : }
1378 : : else
1379 : : {
1380 : 111759 : plansource->num_generic_plans++;
1381 : : }
1382 : :
3298 sfrost@snowman.net 1383 [ - + ]: 133747 : Assert(plan != NULL);
1384 : :
1385 : : /* Flag the plan as in use by caller */
1787 tgl@sss.pgh.pa.us 1386 [ + + ]: 133747 : if (owner)
770 heikki.linnakangas@i 1387 : 101233 : ResourceOwnerEnlarge(owner);
6854 tgl@sss.pgh.pa.us 1388 : 133747 : plan->refcount++;
1787 1389 [ + + ]: 133747 : if (owner)
1390 : 101233 : ResourceOwnerRememberPlanCacheRef(owner, plan);
1391 : :
1392 : : /*
1393 : : * Saved plans should be under CacheMemoryContext so they will not go away
1394 : : * until their reference count goes to zero. In the generic-plan cases we
1395 : : * already took care of that, but for a custom plan, do it as soon as we
1396 : : * have created a reference-counted link.
1397 : : */
5206 1398 [ + + + + ]: 133747 : if (customplan && plansource->is_saved)
1399 : : {
1400 : 14021 : MemoryContextSetParent(plan->context, CacheMemoryContext);
1401 : 14021 : plan->is_saved = true;
1402 : : }
1403 : :
146 michael@paquier.xyz 1404 [ + - + + :GNC 267497 : foreach(lc, plan->stmt_list)
+ + ]
1405 : : {
1406 : 133750 : PlannedStmt *pstmt = (PlannedStmt *) lfirst(lc);
1407 : :
139 1408 [ + + ]: 133750 : pstmt->planOrigin = customplan ? PLAN_STMT_CACHE_CUSTOM : PLAN_STMT_CACHE_GENERIC;
1409 : : }
1410 : :
6854 tgl@sss.pgh.pa.us 1411 :CBC 133747 : return plan;
1412 : : }
1413 : :
1414 : : /*
1415 : : * ReleaseCachedPlan: release active use of a cached plan.
1416 : : *
1417 : : * This decrements the reference count, and frees the plan if the count
1418 : : * has thereby gone to zero. If "owner" is not NULL, it is assumed that
1419 : : * the reference count is managed by that ResourceOwner.
1420 : : *
1421 : : * Note: owner == NULL is used for releasing references that are in
1422 : : * persistent data structures, such as the parent CachedPlanSource or a
1423 : : * Portal. Transient references should be protected by a resource owner.
1424 : : */
1425 : : void
1787 1426 : 182323 : ReleaseCachedPlan(CachedPlan *plan, ResourceOwner owner)
1427 : : {
5206 1428 [ - + ]: 182323 : Assert(plan->magic == CACHEDPLAN_MAGIC);
1787 1429 [ + + ]: 182323 : if (owner)
1430 : : {
5206 1431 [ - + ]: 95743 : Assert(plan->is_saved);
1787 1432 : 95743 : ResourceOwnerForgetPlanCacheRef(owner, plan);
1433 : : }
6854 1434 [ - + ]: 182323 : Assert(plan->refcount > 0);
1435 : 182323 : plan->refcount--;
1436 [ + + ]: 182323 : if (plan->refcount == 0)
1437 : : {
1438 : : /* Mark it no longer valid */
4730 1439 : 29529 : plan->magic = 0;
1440 : :
1441 : : /* One-shot plans do not own their context, so we can't free them */
1442 [ + + ]: 29529 : if (!plan->is_oneshot)
1443 : 21656 : MemoryContextDelete(plan->context);
1444 : : }
6854 1445 : 182323 : }
1446 : :
1447 : : /*
1448 : : * CachedPlanAllowsSimpleValidityCheck: can we use CachedPlanIsSimplyValid?
1449 : : *
1450 : : * This function, together with CachedPlanIsSimplyValid, provides a fast path
1451 : : * for revalidating "simple" generic plans. The core requirement to be simple
1452 : : * is that the plan must not require taking any locks, which translates to
1453 : : * not touching any tables; this happens to match up well with an important
1454 : : * use-case in PL/pgSQL. This function tests whether that's true, along
1455 : : * with checking some other corner cases that we'd rather not bother with
1456 : : * handling in the fast path. (Note that it's still possible for such a plan
1457 : : * to be invalidated, for example due to a change in a function that was
1458 : : * inlined into the plan.)
1459 : : *
1460 : : * If the plan is simply valid, and "owner" is not NULL, record a refcount on
1461 : : * the plan in that resowner before returning. It is caller's responsibility
1462 : : * to be sure that a refcount is held on any plan that's being actively used.
1463 : : *
1464 : : * This must only be called on known-valid generic plans (eg, ones just
1465 : : * returned by GetCachedPlan). If it returns true, the caller may re-use
1466 : : * the cached plan as long as CachedPlanIsSimplyValid returns true; that
1467 : : * check is much cheaper than the full revalidation done by GetCachedPlan.
1468 : : * Nonetheless, no required checks are omitted.
1469 : : */
1470 : : bool
2092 1471 : 15061 : CachedPlanAllowsSimpleValidityCheck(CachedPlanSource *plansource,
1472 : : CachedPlan *plan, ResourceOwner owner)
1473 : : {
1474 : : ListCell *lc;
1475 : :
1476 : : /*
1477 : : * Sanity-check that the caller gave us a validated generic plan. Notice
1478 : : * that we *don't* assert plansource->is_valid as you might expect; that's
1479 : : * because it's possible that that's already false when GetCachedPlan
1480 : : * returns, e.g. because ResetPlanCache happened partway through. We
1481 : : * should accept the plan as long as plan->is_valid is true, and expect to
1482 : : * replan after the next CachedPlanIsSimplyValid call.
1483 : : */
1484 [ - + ]: 15061 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1485 [ - + ]: 15061 : Assert(plan->magic == CACHEDPLAN_MAGIC);
1486 [ - + ]: 15061 : Assert(plan->is_valid);
1487 [ - + ]: 15061 : Assert(plan == plansource->gplan);
2091 1488 [ - + ]: 15061 : Assert(plansource->search_path != NULL);
870 noah@leadboat.com 1489 [ - + ]: 15061 : Assert(SearchPathMatchesCurrentEnvironment(plansource->search_path));
1490 : :
1491 : : /* We don't support oneshot plans here. */
2092 tgl@sss.pgh.pa.us 1492 [ - + ]: 15061 : if (plansource->is_oneshot)
2092 tgl@sss.pgh.pa.us 1493 :UBC 0 : return false;
2092 tgl@sss.pgh.pa.us 1494 [ - + ]:CBC 15061 : Assert(!plan->is_oneshot);
1495 : :
1496 : : /*
1497 : : * If the plan is dependent on RLS considerations, or it's transient,
1498 : : * reject. These things probably can't ever happen for table-free
1499 : : * queries, but for safety's sake let's check.
1500 : : */
1501 [ - + ]: 15061 : if (plansource->dependsOnRLS)
2092 tgl@sss.pgh.pa.us 1502 :UBC 0 : return false;
2092 tgl@sss.pgh.pa.us 1503 [ - + ]:CBC 15061 : if (plan->dependsOnRole)
2092 tgl@sss.pgh.pa.us 1504 :UBC 0 : return false;
2092 tgl@sss.pgh.pa.us 1505 [ - + ]:CBC 15061 : if (TransactionIdIsValid(plan->saved_xmin))
2092 tgl@sss.pgh.pa.us 1506 :UBC 0 : return false;
1507 : :
1508 : : /*
1509 : : * Reject if AcquirePlannerLocks would have anything to do. This is
1510 : : * simplistic, but there's no need to inquire any more carefully; indeed,
1511 : : * for current callers it shouldn't even be possible to hit any of these
1512 : : * checks.
1513 : : */
2092 tgl@sss.pgh.pa.us 1514 [ + - + + :CBC 30122 : foreach(lc, plansource->query_list)
+ + ]
1515 : : {
1516 : 15061 : Query *query = lfirst_node(Query, lc);
1517 : :
1518 [ - + ]: 15061 : if (query->commandType == CMD_UTILITY)
2092 tgl@sss.pgh.pa.us 1519 :UBC 0 : return false;
2092 tgl@sss.pgh.pa.us 1520 [ + - + - :CBC 15061 : if (query->rtable || query->cteList || query->hasSubLinks)
- + ]
2092 tgl@sss.pgh.pa.us 1521 :UBC 0 : return false;
1522 : : }
1523 : :
1524 : : /*
1525 : : * Reject if AcquireExecutorLocks would have anything to do. This is
1526 : : * probably unnecessary given the previous check, but let's be safe.
1527 : : */
2092 tgl@sss.pgh.pa.us 1528 [ + - + + :CBC 30122 : foreach(lc, plan->stmt_list)
+ + ]
1529 : : {
1530 : 15061 : PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
1531 : : ListCell *lc2;
1532 : :
1533 [ - + ]: 15061 : if (plannedstmt->commandType == CMD_UTILITY)
2092 tgl@sss.pgh.pa.us 1534 :UBC 0 : return false;
1535 : :
1536 : : /*
1537 : : * We have to grovel through the rtable because it's likely to contain
1538 : : * an RTE_RESULT relation, rather than being totally empty.
1539 : : */
2092 tgl@sss.pgh.pa.us 1540 [ + - + + :CBC 30122 : foreach(lc2, plannedstmt->rtable)
+ + ]
1541 : : {
1542 : 15061 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
1543 : :
1544 [ - + ]: 15061 : if (rte->rtekind == RTE_RELATION)
2092 tgl@sss.pgh.pa.us 1545 :UBC 0 : return false;
1546 : : }
1547 : : }
1548 : :
1549 : : /*
1550 : : * Okay, it's simple. Note that what we've primarily established here is
1551 : : * that no locks need be taken before checking the plan's is_valid flag.
1552 : : */
1553 : :
1554 : : /* Bump refcount if requested. */
2091 tgl@sss.pgh.pa.us 1555 [ + - ]:CBC 15061 : if (owner)
1556 : : {
770 heikki.linnakangas@i 1557 : 15061 : ResourceOwnerEnlarge(owner);
2091 tgl@sss.pgh.pa.us 1558 : 15061 : plan->refcount++;
1559 : 15061 : ResourceOwnerRememberPlanCacheRef(owner, plan);
1560 : : }
1561 : :
2092 1562 : 15061 : return true;
1563 : : }
1564 : :
1565 : : /*
1566 : : * CachedPlanIsSimplyValid: quick check for plan still being valid
1567 : : *
1568 : : * This function must not be used unless CachedPlanAllowsSimpleValidityCheck
1569 : : * previously said it was OK.
1570 : : *
1571 : : * If the plan is valid, and "owner" is not NULL, record a refcount on
1572 : : * the plan in that resowner before returning. It is caller's responsibility
1573 : : * to be sure that a refcount is held on any plan that's being actively used.
1574 : : *
1575 : : * The code here is unconditionally safe as long as the only use of this
1576 : : * CachedPlanSource is in connection with the particular CachedPlan pointer
1577 : : * that's passed in. If the plansource were being used for other purposes,
1578 : : * it's possible that its generic plan could be invalidated and regenerated
1579 : : * while the current caller wasn't looking, and then there could be a chance
1580 : : * collision of address between this caller's now-stale plan pointer and the
1581 : : * actual address of the new generic plan. For current uses, that scenario
1582 : : * can't happen; but with a plansource shared across multiple uses, it'd be
1583 : : * advisable to also save plan->generation and verify that that still matches.
1584 : : */
1585 : : bool
1586 : 146046 : CachedPlanIsSimplyValid(CachedPlanSource *plansource, CachedPlan *plan,
1587 : : ResourceOwner owner)
1588 : : {
1589 : : /*
1590 : : * Careful here: since the caller doesn't necessarily hold a refcount on
1591 : : * the plan to start with, it's possible that "plan" is a dangling
1592 : : * pointer. Don't dereference it until we've verified that it still
1593 : : * matches the plansource's gplan (which is either valid or NULL).
1594 : : */
1595 [ - + ]: 146046 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1596 : :
1597 : : /*
1598 : : * Has cache invalidation fired on this plan? We can check this right
1599 : : * away since there are no locks that we'd need to acquire first. Note
1600 : : * that here we *do* check plansource->is_valid, so as to force plan
1601 : : * rebuild if that's become false.
1602 : : */
881 1603 [ + + + - ]: 146046 : if (!plansource->is_valid ||
1604 [ + - ]: 143654 : plan == NULL || plan != plansource->gplan ||
1605 [ + + ]: 143654 : !plan->is_valid)
2092 1606 : 2398 : return false;
1607 : :
1608 [ - + ]: 143648 : Assert(plan->magic == CACHEDPLAN_MAGIC);
1609 : :
1610 : : /* Is the search_path still the same as when we made it? */
1611 [ - + ]: 143648 : Assert(plansource->search_path != NULL);
870 noah@leadboat.com 1612 [ + + ]: 143648 : if (!SearchPathMatchesCurrentEnvironment(plansource->search_path))
2092 tgl@sss.pgh.pa.us 1613 : 14 : return false;
1614 : :
1615 : : /* It's still good. Bump refcount if requested. */
1616 [ + + ]: 143634 : if (owner)
1617 : : {
770 heikki.linnakangas@i 1618 : 26048 : ResourceOwnerEnlarge(owner);
2092 tgl@sss.pgh.pa.us 1619 : 26048 : plan->refcount++;
1620 : 26048 : ResourceOwnerRememberPlanCacheRef(owner, plan);
1621 : : }
1622 : :
1623 : 143634 : return true;
1624 : : }
1625 : :
1626 : : /*
1627 : : * CachedPlanSetParentContext: move a CachedPlanSource to a new memory context
1628 : : *
1629 : : * This can only be applied to unsaved plans; once saved, a plan always
1630 : : * lives underneath CacheMemoryContext.
1631 : : */
1632 : : void
5206 1633 : 17529 : CachedPlanSetParentContext(CachedPlanSource *plansource,
1634 : : MemoryContext newcontext)
1635 : : {
1636 : : /* Assert caller is doing things in a sane order */
1637 [ - + ]: 17529 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1638 [ - + ]: 17529 : Assert(plansource->is_complete);
1639 : :
1640 : : /* These seem worth real tests, though */
1641 [ - + ]: 17529 : if (plansource->is_saved)
5206 tgl@sss.pgh.pa.us 1642 [ # # ]:UBC 0 : elog(ERROR, "cannot move a saved cached plan to another context");
4730 tgl@sss.pgh.pa.us 1643 [ - + ]:CBC 17529 : if (plansource->is_oneshot)
4730 tgl@sss.pgh.pa.us 1644 [ # # ]:UBC 0 : elog(ERROR, "cannot move a one-shot cached plan to another context");
1645 : :
1646 : : /* OK, let the caller keep the plan where he wishes */
5206 tgl@sss.pgh.pa.us 1647 :CBC 17529 : MemoryContextSetParent(plansource->context, newcontext);
1648 : :
1649 : : /*
1650 : : * The query_context needs no special handling, since it's a child of
1651 : : * plansource->context. But if there's a generic plan, it should be
1652 : : * maintained as a sibling of plansource->context.
1653 : : */
1654 [ - + ]: 17529 : if (plansource->gplan)
1655 : : {
5206 tgl@sss.pgh.pa.us 1656 [ # # ]:UBC 0 : Assert(plansource->gplan->magic == CACHEDPLAN_MAGIC);
1657 : 0 : MemoryContextSetParent(plansource->gplan->context, newcontext);
1658 : : }
5206 tgl@sss.pgh.pa.us 1659 :CBC 17529 : }
1660 : :
1661 : : /*
1662 : : * CopyCachedPlan: make a copy of a CachedPlanSource
1663 : : *
1664 : : * This is a convenience routine that does the equivalent of
1665 : : * CreateCachedPlan + CompleteCachedPlan, using the data stored in the
1666 : : * input CachedPlanSource. The result is therefore "unsaved" (regardless
1667 : : * of the state of the source), and we don't copy any generic plan either.
1668 : : * The result will be currently valid, or not, the same as the source.
1669 : : */
1670 : : CachedPlanSource *
5206 tgl@sss.pgh.pa.us 1671 :UBC 0 : CopyCachedPlan(CachedPlanSource *plansource)
1672 : : {
1673 : : CachedPlanSource *newsource;
1674 : : MemoryContext source_context;
1675 : : MemoryContext querytree_context;
1676 : : MemoryContext oldcxt;
1677 : :
1678 [ # # ]: 0 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1679 [ # # ]: 0 : Assert(plansource->is_complete);
1680 : :
1681 : : /*
1682 : : * One-shot plans can't be copied, because we haven't taken care that
1683 : : * parsing/planning didn't scribble on the raw parse tree or querytrees.
1684 : : */
4730 1685 [ # # ]: 0 : if (plansource->is_oneshot)
1686 [ # # ]: 0 : elog(ERROR, "cannot copy a one-shot cached plan");
1687 : :
5206 1688 : 0 : source_context = AllocSetContextCreate(CurrentMemoryContext,
1689 : : "CachedPlanSource",
1690 : : ALLOCSET_START_SMALL_SIZES);
1691 : :
1692 : 0 : oldcxt = MemoryContextSwitchTo(source_context);
1693 : :
7 michael@paquier.xyz 1694 :UNC 0 : newsource = palloc0_object(CachedPlanSource);
5206 tgl@sss.pgh.pa.us 1695 :UBC 0 : newsource->magic = CACHEDPLANSOURCE_MAGIC;
1696 : 0 : newsource->raw_parse_tree = copyObject(plansource->raw_parse_tree);
259 1697 : 0 : newsource->analyzed_parse_tree = copyObject(plansource->analyzed_parse_tree);
5206 1698 : 0 : newsource->query_string = pstrdup(plansource->query_string);
2822 1699 : 0 : MemoryContextSetIdentifier(source_context, newsource->query_string);
5206 1700 : 0 : newsource->commandTag = plansource->commandTag;
1701 [ # # ]: 0 : if (plansource->num_params > 0)
1702 : : {
7 michael@paquier.xyz 1703 :UNC 0 : newsource->param_types = palloc_array(Oid, plansource->num_params);
5206 tgl@sss.pgh.pa.us 1704 :UBC 0 : memcpy(newsource->param_types, plansource->param_types,
1705 : 0 : plansource->num_params * sizeof(Oid));
1706 : : }
1707 : : else
1708 : 0 : newsource->param_types = NULL;
1709 : 0 : newsource->num_params = plansource->num_params;
1710 : 0 : newsource->parserSetup = plansource->parserSetup;
1711 : 0 : newsource->parserSetupArg = plansource->parserSetupArg;
259 1712 : 0 : newsource->postRewrite = plansource->postRewrite;
1713 : 0 : newsource->postRewriteArg = plansource->postRewriteArg;
5206 1714 : 0 : newsource->cursor_options = plansource->cursor_options;
1715 : 0 : newsource->fixed_result = plansource->fixed_result;
1716 [ # # ]: 0 : if (plansource->resultDesc)
1717 : 0 : newsource->resultDesc = CreateTupleDescCopy(plansource->resultDesc);
1718 : : else
1719 : 0 : newsource->resultDesc = NULL;
1720 : 0 : newsource->context = source_context;
1721 : :
1722 : 0 : querytree_context = AllocSetContextCreate(source_context,
1723 : : "CachedPlanQuery",
1724 : : ALLOCSET_START_SMALL_SIZES);
1725 : 0 : MemoryContextSwitchTo(querytree_context);
3205 peter_e@gmx.net 1726 : 0 : newsource->query_list = copyObject(plansource->query_list);
1727 : 0 : newsource->relationOids = copyObject(plansource->relationOids);
1728 : 0 : newsource->invalItems = copyObject(plansource->invalItems);
4709 tgl@sss.pgh.pa.us 1729 [ # # ]: 0 : if (plansource->search_path)
870 noah@leadboat.com 1730 : 0 : newsource->search_path = CopySearchPathMatcher(plansource->search_path);
5206 tgl@sss.pgh.pa.us 1731 : 0 : newsource->query_context = querytree_context;
3442 1732 : 0 : newsource->rewriteRoleId = plansource->rewriteRoleId;
1733 : 0 : newsource->rewriteRowSecurity = plansource->rewriteRowSecurity;
1734 : 0 : newsource->dependsOnRLS = plansource->dependsOnRLS;
1735 : :
5206 1736 : 0 : newsource->gplan = NULL;
1737 : :
4730 1738 : 0 : newsource->is_oneshot = false;
5206 1739 : 0 : newsource->is_complete = true;
1740 : 0 : newsource->is_saved = false;
1741 : 0 : newsource->is_valid = plansource->is_valid;
1742 : 0 : newsource->generation = plansource->generation;
1743 : :
1744 : : /* We may as well copy any acquired cost knowledge */
1745 : 0 : newsource->generic_cost = plansource->generic_cost;
1746 : 0 : newsource->total_custom_cost = plansource->total_custom_cost;
1976 fujii@postgresql.org 1747 : 0 : newsource->num_generic_plans = plansource->num_generic_plans;
5206 tgl@sss.pgh.pa.us 1748 : 0 : newsource->num_custom_plans = plansource->num_custom_plans;
1749 : :
1750 : 0 : MemoryContextSwitchTo(oldcxt);
1751 : :
1752 : 0 : return newsource;
1753 : : }
1754 : :
1755 : : /*
1756 : : * CachedPlanIsValid: test whether the rewritten querytree within a
1757 : : * CachedPlanSource is currently valid (that is, not marked as being in need
1758 : : * of revalidation).
1759 : : *
1760 : : * This result is only trustworthy (ie, free from race conditions) if
1761 : : * the caller has acquired locks on all the relations used in the plan.
1762 : : */
1763 : : bool
6302 tgl@sss.pgh.pa.us 1764 :CBC 1850 : CachedPlanIsValid(CachedPlanSource *plansource)
1765 : : {
5206 1766 [ - + ]: 1850 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1767 : 1850 : return plansource->is_valid;
1768 : : }
1769 : :
1770 : : /*
1771 : : * CachedPlanGetTargetList: return tlist, if any, describing plan's output
1772 : : *
1773 : : * The result is guaranteed up-to-date. However, it is local storage
1774 : : * within the cached plan, and may disappear next time the plan is updated.
1775 : : */
1776 : : List *
3183 kgrittn@postgresql.o 1777 : 7871 : CachedPlanGetTargetList(CachedPlanSource *plansource,
1778 : : QueryEnvironment *queryEnv)
1779 : : {
1780 : : Query *pstmt;
1781 : :
1782 : : /* Assert caller is doing things in a sane order */
5206 tgl@sss.pgh.pa.us 1783 [ - + ]: 7871 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1784 [ - + ]: 7871 : Assert(plansource->is_complete);
1785 : :
1786 : : /*
1787 : : * No work needed if statement doesn't return tuples (we assume this
1788 : : * feature cannot be changed by an invalidation)
1789 : : */
1790 [ - + ]: 7871 : if (plansource->resultDesc == NULL)
5206 tgl@sss.pgh.pa.us 1791 :UBC 0 : return NIL;
1792 : :
1793 : : /* Make sure the querytree list is valid and we have parse-time locks */
209 amitlan@postgresql.o 1794 :CBC 7871 : RevalidateCachedQuery(plansource, queryEnv);
1795 : :
1796 : : /* Get the primary statement and find out what it returns */
3259 tgl@sss.pgh.pa.us 1797 : 7871 : pstmt = QueryListGetPrimaryStmt(plansource->query_list);
1798 : :
1799 : 7871 : return FetchStatementTargetList((Node *) pstmt);
1800 : : }
1801 : :
1802 : : /*
1803 : : * GetCachedExpression: construct a CachedExpression for an expression.
1804 : : *
1805 : : * This performs the same transformations on the expression as
1806 : : * expression_planner(), ie, convert an expression as emitted by parse
1807 : : * analysis to be ready to pass to the executor.
1808 : : *
1809 : : * The result is stashed in a private, long-lived memory context.
1810 : : * (Note that this might leak a good deal of memory in the caller's
1811 : : * context before that.) The passed-in expr tree is not modified.
1812 : : */
1813 : : CachedExpression *
2561 1814 : 192 : GetCachedExpression(Node *expr)
1815 : : {
1816 : : CachedExpression *cexpr;
1817 : : List *relationOids;
1818 : : List *invalItems;
1819 : : MemoryContext cexpr_context;
1820 : : MemoryContext oldcxt;
1821 : :
1822 : : /*
1823 : : * Pass the expression through the planner, and collect dependencies.
1824 : : * Everything built here is leaked in the caller's context; that's
1825 : : * intentional to minimize the size of the permanent data structure.
1826 : : */
1827 : 192 : expr = (Node *) expression_planner_with_deps((Expr *) expr,
1828 : : &relationOids,
1829 : : &invalItems);
1830 : :
1831 : : /*
1832 : : * Make a private memory context, and copy what we need into that. To
1833 : : * avoid leaking a long-lived context if we fail while copying data, we
1834 : : * initially make the context under the caller's context.
1835 : : */
1836 : 192 : cexpr_context = AllocSetContextCreate(CurrentMemoryContext,
1837 : : "CachedExpression",
1838 : : ALLOCSET_SMALL_SIZES);
1839 : :
1840 : 192 : oldcxt = MemoryContextSwitchTo(cexpr_context);
1841 : :
7 michael@paquier.xyz 1842 :GNC 192 : cexpr = palloc_object(CachedExpression);
2561 tgl@sss.pgh.pa.us 1843 :CBC 192 : cexpr->magic = CACHEDEXPR_MAGIC;
1844 : 192 : cexpr->expr = copyObject(expr);
1845 : 192 : cexpr->is_valid = true;
1846 : 192 : cexpr->relationOids = copyObject(relationOids);
1847 : 192 : cexpr->invalItems = copyObject(invalItems);
1848 : 192 : cexpr->context = cexpr_context;
1849 : :
1850 : 192 : MemoryContextSwitchTo(oldcxt);
1851 : :
1852 : : /*
1853 : : * Reparent the expr's memory context under CacheMemoryContext so that it
1854 : : * will live indefinitely.
1855 : : */
1856 : 192 : MemoryContextSetParent(cexpr_context, CacheMemoryContext);
1857 : :
1858 : : /*
1859 : : * Add the entry to the global list of cached expressions.
1860 : : */
1861 : 192 : dlist_push_tail(&cached_expression_list, &cexpr->node);
1862 : :
1863 : 192 : return cexpr;
1864 : : }
1865 : :
1866 : : /*
1867 : : * FreeCachedExpression
1868 : : * Delete a CachedExpression.
1869 : : */
1870 : : void
1871 : 32 : FreeCachedExpression(CachedExpression *cexpr)
1872 : : {
1873 : : /* Sanity check */
1874 [ - + ]: 32 : Assert(cexpr->magic == CACHEDEXPR_MAGIC);
1875 : : /* Unlink from global list */
1876 : 32 : dlist_delete(&cexpr->node);
1877 : : /* Free all storage associated with CachedExpression */
1878 : 32 : MemoryContextDelete(cexpr->context);
1879 : 32 : }
1880 : :
1881 : : /*
1882 : : * QueryListGetPrimaryStmt
1883 : : * Get the "primary" stmt within a list, ie, the one marked canSetTag.
1884 : : *
1885 : : * Returns NULL if no such stmt. If multiple queries within the list are
1886 : : * marked canSetTag, returns the first one. Neither of these cases should
1887 : : * occur in present usages of this function.
1888 : : */
1889 : : static Query *
3259 1890 : 8022 : QueryListGetPrimaryStmt(List *stmts)
1891 : : {
1892 : : ListCell *lc;
1893 : :
1894 [ + - + - : 8022 : foreach(lc, stmts)
+ - ]
1895 : : {
3173 1896 : 8022 : Query *stmt = lfirst_node(Query, lc);
1897 : :
3259 1898 [ + - ]: 8022 : if (stmt->canSetTag)
1899 : 8022 : return stmt;
1900 : : }
3259 tgl@sss.pgh.pa.us 1901 :UBC 0 : return NULL;
1902 : : }
1903 : :
1904 : : /*
1905 : : * AcquireExecutorLocks: acquire locks needed for execution of a cached plan;
1906 : : * or release them if acquire is false.
1907 : : */
1908 : : static void
6854 tgl@sss.pgh.pa.us 1909 :CBC 86956 : AcquireExecutorLocks(List *stmt_list, bool acquire)
1910 : : {
1911 : : ListCell *lc1;
1912 : :
1913 [ + - + + : 173912 : foreach(lc1, stmt_list)
+ + ]
1914 : : {
3173 1915 : 86956 : PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc1);
1916 : : ListCell *lc2;
1917 : :
3259 1918 [ + + ]: 86956 : if (plannedstmt->commandType == CMD_UTILITY)
5815 1919 : 10414 : {
1920 : : /*
1921 : : * Ignore utility statements, except those (such as EXPLAIN) that
1922 : : * contain a parsed-but-not-planned query. Note: it's okay to use
1923 : : * ScanQueryForLocks, even though the query hasn't been through
1924 : : * rule rewriting, because rewriting doesn't change the query
1925 : : * representation.
1926 : : */
3259 1927 : 10414 : Query *query = UtilityContainsQuery(plannedstmt->utilityStmt);
1928 : :
5021 1929 [ + + ]: 10414 : if (query)
5815 1930 : 3 : ScanQueryForLocks(query, acquire);
1931 : 10414 : continue;
1932 : : }
1933 : :
209 amitlan@postgresql.o 1934 [ + - + + : 173769 : foreach(lc2, plannedstmt->rtable)
+ + ]
1935 : : {
1936 : 97227 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
1937 : :
1938 [ + + ]: 97227 : if (!(rte->rtekind == RTE_RELATION ||
1939 [ + + + + ]: 68083 : (rte->rtekind == RTE_SUBQUERY && OidIsValid(rte->relid))))
1940 : 66617 : continue;
1941 : :
1942 : : /*
1943 : : * Acquire the appropriate type of lock on each relation OID. Note
1944 : : * that we don't actually try to open the rel, and hence will not
1945 : : * fail if it's been dropped entirely --- we'll just transiently
1946 : : * acquire a non-conflicting lock.
1947 : : */
6854 tgl@sss.pgh.pa.us 1948 [ + - ]: 30610 : if (acquire)
2633 1949 : 30610 : LockRelationOid(rte->relid, rte->rellockmode);
1950 : : else
2633 tgl@sss.pgh.pa.us 1951 :UBC 0 : UnlockRelationOid(rte->relid, rte->rellockmode);
1952 : : }
1953 : : }
6854 tgl@sss.pgh.pa.us 1954 :CBC 86956 : }
1955 : :
1956 : : /*
1957 : : * AcquirePlannerLocks: acquire locks needed for planning of a querytree list;
1958 : : * or release them if acquire is false.
1959 : : *
1960 : : * Note that we don't actually try to open the relations, and hence will not
1961 : : * fail if one has been dropped entirely --- we'll just transiently acquire
1962 : : * a non-conflicting lock.
1963 : : */
1964 : : static void
1965 : 119215 : AcquirePlannerLocks(List *stmt_list, bool acquire)
1966 : : {
1967 : : ListCell *lc;
1968 : :
1969 [ + - + + : 238430 : foreach(lc, stmt_list)
+ + ]
1970 : : {
3173 1971 : 119215 : Query *query = lfirst_node(Query, lc);
1972 : :
5815 1973 [ + + ]: 119215 : if (query->commandType == CMD_UTILITY)
1974 : : {
1975 : : /* Ignore utility statements, unless they contain a Query */
5021 1976 : 5030 : query = UtilityContainsQuery(query->utilityStmt);
1977 [ + + ]: 5030 : if (query)
5815 1978 : 4900 : ScanQueryForLocks(query, acquire);
1979 : 5030 : continue;
1980 : : }
1981 : :
6308 1982 : 114185 : ScanQueryForLocks(query, acquire);
1983 : : }
6854 1984 : 119215 : }
1985 : :
1986 : : /*
1987 : : * ScanQueryForLocks: recursively scan one Query for AcquirePlannerLocks.
1988 : : */
1989 : : static void
6308 1990 : 130225 : ScanQueryForLocks(Query *parsetree, bool acquire)
1991 : : {
1992 : : ListCell *lc;
1993 : :
1994 : : /* Shouldn't get called on utility commands */
5815 1995 [ - + ]: 130225 : Assert(parsetree->commandType != CMD_UTILITY);
1996 : :
1997 : : /*
1998 : : * First, process RTEs of the current query level.
1999 : : */
6854 2000 [ + + + + : 215892 : foreach(lc, parsetree->rtable)
+ + ]
2001 : : {
2002 : 85667 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
2003 : :
2004 [ + + + ]: 85667 : switch (rte->rtekind)
2005 : : {
2006 : 61098 : case RTE_RELATION:
2007 : : /* Acquire or release the appropriate type of lock */
6308 2008 [ + + ]: 61098 : if (acquire)
2633 2009 : 61091 : LockRelationOid(rte->relid, rte->rellockmode);
2010 : : else
2011 : 7 : UnlockRelationOid(rte->relid, rte->rellockmode);
6854 2012 : 61098 : break;
2013 : :
2014 : 8461 : case RTE_SUBQUERY:
2015 : : /* If this was a view, must lock/unlock the view */
987 2016 [ + + ]: 8461 : if (OidIsValid(rte->relid))
2017 : : {
2018 [ + - ]: 1975 : if (acquire)
2019 : 1975 : LockRelationOid(rte->relid, rte->rellockmode);
2020 : : else
987 tgl@sss.pgh.pa.us 2021 :UBC 0 : UnlockRelationOid(rte->relid, rte->rellockmode);
2022 : : }
2023 : : /* Recurse into subquery-in-FROM */
6308 tgl@sss.pgh.pa.us 2024 :CBC 8461 : ScanQueryForLocks(rte->subquery, acquire);
6854 2025 : 8461 : break;
2026 : :
2027 : 16108 : default:
2028 : : /* ignore other types of RTEs */
2029 : 16108 : break;
2030 : : }
2031 : : }
2032 : :
2033 : : /* Recurse into subquery-in-WITH */
6283 2034 [ + + + + : 130273 : foreach(lc, parsetree->cteList)
+ + ]
2035 : : {
3173 2036 : 48 : CommonTableExpr *cte = lfirst_node(CommonTableExpr, lc);
2037 : :
3247 2038 : 48 : ScanQueryForLocks(castNode(Query, cte->ctequery), acquire);
2039 : : }
2040 : :
2041 : : /*
2042 : : * Recurse into sublink subqueries, too. But we already did the ones in
2043 : : * the rtable and cteList.
2044 : : */
6854 2045 [ + + ]: 130225 : if (parsetree->hasSubLinks)
2046 : : {
384 peter@eisentraut.org 2047 : 2565 : query_tree_walker(parsetree, ScanQueryWalker, &acquire,
2048 : : QTW_IGNORE_RC_SUBQUERIES);
2049 : : }
6854 tgl@sss.pgh.pa.us 2050 : 130225 : }
2051 : :
2052 : : /*
2053 : : * Walker to find sublink subqueries for ScanQueryForLocks
2054 : : */
2055 : : static bool
6308 2056 : 67070 : ScanQueryWalker(Node *node, bool *acquire)
2057 : : {
6854 2058 [ + + ]: 67070 : if (node == NULL)
2059 : 31102 : return false;
2060 [ + + ]: 35968 : if (IsA(node, SubLink))
2061 : : {
2062 : 2628 : SubLink *sub = (SubLink *) node;
2063 : :
2064 : : /* Do what we came for */
3247 2065 : 2628 : ScanQueryForLocks(castNode(Query, sub->subselect), *acquire);
2066 : : /* Fall through to process lefthand args of SubLink */
2067 : : }
2068 : :
2069 : : /*
2070 : : * Do NOT recurse into Query nodes, because ScanQueryForLocks already
2071 : : * processed subselects of subselects for us.
2072 : : */
384 peter@eisentraut.org 2073 : 35968 : return expression_tree_walker(node, ScanQueryWalker, acquire);
2074 : : }
2075 : :
2076 : : /*
2077 : : * PlanCacheComputeResultDesc: given a list of analyzed-and-rewritten Queries,
2078 : : * determine the result tupledesc it will produce. Returns NULL if the
2079 : : * execution will not return tuples.
2080 : : *
2081 : : * Note: the result is created or copied into current memory context.
2082 : : */
2083 : : static TupleDesc
6852 tgl@sss.pgh.pa.us 2084 : 39677 : PlanCacheComputeResultDesc(List *stmt_list)
2085 : : {
2086 : : Query *query;
2087 : :
6854 2088 [ + + + + : 39677 : switch (ChoosePortalStrategy(stmt_list))
- ]
2089 : : {
2090 : 28635 : case PORTAL_ONE_SELECT:
2091 : : case PORTAL_ONE_MOD_WITH:
3173 2092 : 28635 : query = linitial_node(Query, stmt_list);
2584 andres@anarazel.de 2093 : 28635 : return ExecCleanTypeFromTL(query->targetList);
2094 : :
6854 tgl@sss.pgh.pa.us 2095 : 151 : case PORTAL_ONE_RETURNING:
3259 2096 : 151 : query = QueryListGetPrimaryStmt(stmt_list);
5206 2097 [ - + ]: 151 : Assert(query->returningList);
2584 andres@anarazel.de 2098 : 151 : return ExecCleanTypeFromTL(query->returningList);
2099 : :
6854 tgl@sss.pgh.pa.us 2100 : 5038 : case PORTAL_UTIL_SELECT:
3173 2101 : 5038 : query = linitial_node(Query, stmt_list);
5206 2102 [ - + ]: 5038 : Assert(query->utilityStmt);
2103 : 5038 : return UtilityTupleDescriptor(query->utilityStmt);
2104 : :
6854 2105 : 5853 : case PORTAL_MULTI_QUERY:
2106 : : /* will not return tuples */
2107 : 5853 : break;
2108 : : }
2109 : 5853 : return NULL;
2110 : : }
2111 : :
2112 : : /*
2113 : : * PlanCacheRelCallback
2114 : : * Relcache inval callback function
2115 : : *
2116 : : * Invalidate all plans mentioning the given rel, or all plans mentioning
2117 : : * any rel at all if relid == InvalidOid.
2118 : : */
2119 : : static void
6308 2120 : 1643779 : PlanCacheRelCallback(Datum arg, Oid relid)
2121 : : {
2122 : : dlist_iter iter;
2123 : :
2561 2124 [ + - + + ]: 32360147 : dlist_foreach(iter, &saved_plan_list)
2125 : : {
2126 : 30716368 : CachedPlanSource *plansource = dlist_container(CachedPlanSource,
2127 : : node, iter.cur);
2128 : :
5206 2129 [ - + ]: 30716368 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
2130 : :
2131 : : /* No work if it's already invalidated */
2132 [ + + ]: 30716368 : if (!plansource->is_valid)
6854 2133 : 19919432 : continue;
2134 : :
2135 : : /* Never invalidate if parse/plan would be a no-op anyway */
846 2136 [ + + ]: 10796936 : if (!StmtPlanRequiresRevalidation(plansource))
4624 2137 : 194369 : continue;
2138 : :
2139 : : /*
2140 : : * Check the dependency list for the rewritten querytree.
2141 : : */
5206 2142 [ + + + + ]: 21205128 : if ((relid == InvalidOid) ? plansource->relationOids != NIL :
2143 : 10602561 : list_member_oid(plansource->relationOids, relid))
2144 : : {
2145 : : /* Invalidate the querytree and generic plan */
2146 : 1846 : plansource->is_valid = false;
2147 [ + + ]: 1846 : if (plansource->gplan)
2148 : 550 : plansource->gplan->is_valid = false;
2149 : : }
2150 : :
2151 : : /*
2152 : : * The generic plan, if any, could have more dependencies than the
2153 : : * querytree does, so we have to check it too.
2154 : : */
2155 [ + + + + ]: 10602567 : if (plansource->gplan && plansource->gplan->is_valid)
2156 : : {
2157 : : ListCell *lc;
2158 : :
2159 [ + - + + : 20080529 : foreach(lc, plansource->gplan->stmt_list)
+ + ]
2160 : : {
3173 2161 : 10040284 : PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
2162 : :
3259 2163 [ + + ]: 10040284 : if (plannedstmt->commandType == CMD_UTILITY)
6607 bruce@momjian.us 2164 : 2672 : continue; /* Ignore utility statements */
6642 tgl@sss.pgh.pa.us 2165 [ - + + + ]: 20075224 : if ((relid == InvalidOid) ? plannedstmt->relationOids != NIL :
1729 akapila@postgresql.o 2166 : 10037612 : list_member_oid(plannedstmt->relationOids, relid))
2167 : : {
2168 : : /* Invalidate the generic plan only */
5206 tgl@sss.pgh.pa.us 2169 : 39 : plansource->gplan->is_valid = false;
6607 bruce@momjian.us 2170 : 39 : break; /* out of stmt_list scan */
2171 : : }
2172 : : }
2173 : : }
2174 : : }
2175 : :
2176 : : /* Likewise check cached expressions */
2561 tgl@sss.pgh.pa.us 2177 [ + - + + ]: 1816588 : dlist_foreach(iter, &cached_expression_list)
2178 : : {
2179 : 172809 : CachedExpression *cexpr = dlist_container(CachedExpression,
2180 : : node, iter.cur);
2181 : :
2182 [ - + ]: 172809 : Assert(cexpr->magic == CACHEDEXPR_MAGIC);
2183 : :
2184 : : /* No work if it's already invalidated */
2185 [ + + ]: 172809 : if (!cexpr->is_valid)
2186 : 78460 : continue;
2187 : :
2188 [ - + - + ]: 188698 : if ((relid == InvalidOid) ? cexpr->relationOids != NIL :
2189 : 94349 : list_member_oid(cexpr->relationOids, relid))
2190 : : {
2561 tgl@sss.pgh.pa.us 2191 :UBC 0 : cexpr->is_valid = false;
2192 : : }
2193 : : }
6308 tgl@sss.pgh.pa.us 2194 :CBC 1643779 : }
2195 : :
2196 : : /*
2197 : : * PlanCacheObjectCallback
2198 : : * Syscache inval callback function for PROCOID and TYPEOID caches
2199 : : *
2200 : : * Invalidate all plans mentioning the object with the specified hash value,
2201 : : * or all plans mentioning any member of this cache if hashvalue == 0.
2202 : : */
2203 : : static void
2561 2204 : 607814 : PlanCacheObjectCallback(Datum arg, int cacheid, uint32 hashvalue)
2205 : : {
2206 : : dlist_iter iter;
2207 : :
2208 [ + - + + ]: 11794774 : dlist_foreach(iter, &saved_plan_list)
2209 : : {
2210 : 11186960 : CachedPlanSource *plansource = dlist_container(CachedPlanSource,
2211 : : node, iter.cur);
2212 : : ListCell *lc;
2213 : :
5206 2214 [ - + ]: 11186960 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
2215 : :
2216 : : /* No work if it's already invalidated */
2217 [ + + ]: 11186960 : if (!plansource->is_valid)
6308 2218 : 6950677 : continue;
2219 : :
2220 : : /* Never invalidate if parse/plan would be a no-op anyway */
846 2221 [ + + ]: 4236283 : if (!StmtPlanRequiresRevalidation(plansource))
4624 2222 : 50660 : continue;
2223 : :
2224 : : /*
2225 : : * Check the dependency list for the rewritten querytree.
2226 : : */
5206 2227 [ + + + + : 4250549 : foreach(lc, plansource->invalItems)
+ + ]
2228 : : {
2229 : 65004 : PlanInvalItem *item = (PlanInvalItem *) lfirst(lc);
2230 : :
5815 2231 [ + + ]: 65004 : if (item->cacheId != cacheid)
2232 : 44072 : continue;
5237 2233 [ + + ]: 20932 : if (hashvalue == 0 ||
2234 [ + + ]: 20930 : item->hashValue == hashvalue)
2235 : : {
2236 : : /* Invalidate the querytree and generic plan */
5206 2237 : 78 : plansource->is_valid = false;
2238 [ + + ]: 78 : if (plansource->gplan)
2239 : 74 : plansource->gplan->is_valid = false;
5815 2240 : 78 : break;
2241 : : }
2242 : : }
2243 : :
2244 : : /*
2245 : : * The generic plan, if any, could have more dependencies than the
2246 : : * querytree does, so we have to check it too.
2247 : : */
5206 2248 [ + + + + ]: 4185623 : if (plansource->gplan && plansource->gplan->is_valid)
2249 : : {
2250 [ + - + + : 7866490 : foreach(lc, plansource->gplan->stmt_list)
+ + ]
2251 : : {
3173 2252 : 3933251 : PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
2253 : : ListCell *lc3;
2254 : :
3259 2255 [ + + ]: 3933251 : if (plannedstmt->commandType == CMD_UTILITY)
6308 2256 : 1577 : continue; /* Ignore utility statements */
2257 [ + + + + : 3992764 : foreach(lc3, plannedstmt->invalItems)
+ + ]
2258 : : {
2259 : 61102 : PlanInvalItem *item = (PlanInvalItem *) lfirst(lc3);
2260 : :
2261 [ + + ]: 61102 : if (item->cacheId != cacheid)
2262 : 41196 : continue;
5237 2263 [ + - ]: 19906 : if (hashvalue == 0 ||
2264 [ + + ]: 19906 : item->hashValue == hashvalue)
2265 : : {
2266 : : /* Invalidate the generic plan only */
5206 2267 : 12 : plansource->gplan->is_valid = false;
6033 bruce@momjian.us 2268 : 12 : break; /* out of invalItems scan */
2269 : : }
2270 : : }
5206 tgl@sss.pgh.pa.us 2271 [ + + ]: 3931674 : if (!plansource->gplan->is_valid)
6308 2272 : 12 : break; /* out of stmt_list scan */
2273 : : }
2274 : : }
2275 : : }
2276 : :
2277 : : /* Likewise check cached expressions */
2561 2278 [ + - + + ]: 680043 : dlist_foreach(iter, &cached_expression_list)
2279 : : {
2280 : 72229 : CachedExpression *cexpr = dlist_container(CachedExpression,
2281 : : node, iter.cur);
2282 : : ListCell *lc;
2283 : :
2284 [ - + ]: 72229 : Assert(cexpr->magic == CACHEDEXPR_MAGIC);
2285 : :
2286 : : /* No work if it's already invalidated */
2287 [ + + ]: 72229 : if (!cexpr->is_valid)
2288 : 30837 : continue;
2289 : :
2290 [ + + + + : 41410 : foreach(lc, cexpr->invalItems)
+ + ]
2291 : : {
2292 : 21 : PlanInvalItem *item = (PlanInvalItem *) lfirst(lc);
2293 : :
2294 [ + + ]: 21 : if (item->cacheId != cacheid)
2295 : 12 : continue;
2296 [ + - ]: 9 : if (hashvalue == 0 ||
2297 [ + + ]: 9 : item->hashValue == hashvalue)
2298 : : {
2299 : 3 : cexpr->is_valid = false;
2300 : 3 : break;
2301 : : }
2302 : : }
2303 : : }
6854 2304 : 607814 : }
2305 : :
2306 : : /*
2307 : : * PlanCacheSysCallback
2308 : : * Syscache inval callback function for other caches
2309 : : *
2310 : : * Just invalidate everything...
2311 : : */
2312 : : static void
5237 2313 : 39832 : PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue)
2314 : : {
6308 2315 : 39832 : ResetPlanCache();
6824 neilc@samurai.com 2316 : 39832 : }
2317 : :
2318 : : /*
2319 : : * ResetPlanCache: invalidate all cached plans.
2320 : : */
2321 : : void
6308 tgl@sss.pgh.pa.us 2322 : 40401 : ResetPlanCache(void)
2323 : : {
2324 : : dlist_iter iter;
2325 : :
2561 2326 [ + - + + ]: 167311 : dlist_foreach(iter, &saved_plan_list)
2327 : : {
2328 : 126910 : CachedPlanSource *plansource = dlist_container(CachedPlanSource,
2329 : : node, iter.cur);
2330 : :
5206 2331 [ - + ]: 126910 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
2332 : :
2333 : : /* No work if it's already invalidated */
2334 [ + + ]: 126910 : if (!plansource->is_valid)
5817 2335 : 115752 : continue;
2336 : :
2337 : : /*
2338 : : * We *must not* mark transaction control statements as invalid,
2339 : : * particularly not ROLLBACK, because they may need to be executed in
2340 : : * aborted transactions when we can't revalidate them (cf bug #5269).
2341 : : * In general there's no point in invalidating statements for which a
2342 : : * new parse analysis/rewrite/plan cycle would certainly give the same
2343 : : * results.
2344 : : */
846 2345 [ + + ]: 11158 : if (!StmtPlanRequiresRevalidation(plansource))
4624 2346 : 2555 : continue;
2347 : :
846 2348 : 8603 : plansource->is_valid = false;
2349 [ + + ]: 8603 : if (plansource->gplan)
2350 : 7830 : plansource->gplan->is_valid = false;
2351 : : }
2352 : :
2353 : : /* Likewise invalidate cached expressions */
2561 2354 [ + - + + ]: 41268 : dlist_foreach(iter, &cached_expression_list)
2355 : : {
2356 : 867 : CachedExpression *cexpr = dlist_container(CachedExpression,
2357 : : node, iter.cur);
2358 : :
2359 [ - + ]: 867 : Assert(cexpr->magic == CACHEDEXPR_MAGIC);
2360 : :
2361 : 867 : cexpr->is_valid = false;
2362 : : }
6854 2363 : 40401 : }
2364 : :
2365 : : /*
2366 : : * Release all CachedPlans remembered by 'owner'
2367 : : */
2368 : : void
770 heikki.linnakangas@i 2369 : 8431 : ReleaseAllPlanCacheRefsInOwner(ResourceOwner owner)
2370 : : {
2371 : 8431 : ResourceOwnerReleaseAllOfKind(owner, &planref_resowner_desc);
2372 : 8431 : }
2373 : :
2374 : : /* ResourceOwner callbacks */
2375 : :
2376 : : static void
2377 : 46599 : ResOwnerReleaseCachedPlan(Datum res)
2378 : : {
2379 : 46599 : ReleaseCachedPlan((CachedPlan *) DatumGetPointer(res), NULL);
2380 : 46599 : }
|