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