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
858 heikki.linnakangas@i 128 :CBC 552262 : ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
129 : : {
130 : 552262 : ResourceOwnerRemember(owner, PointerGetDatum(plan), &planref_resowner_desc);
131 : 552262 : }
132 : : static inline void
133 : 505148 : ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
134 : : {
135 : 505148 : ResourceOwnerForget(owner, PointerGetDatum(plan), &planref_resowner_desc);
136 : 505148 : }
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
6942 tgl@sss.pgh.pa.us 148 : 17034 : InitPlanCache(void)
149 : : {
6396 150 : 17034 : CacheRegisterRelcacheCallback(PlanCacheRelCallback, (Datum) 0);
2649 151 : 17034 : CacheRegisterSyscacheCallback(PROCOID, PlanCacheObjectCallback, (Datum) 0);
152 : 17034 : CacheRegisterSyscacheCallback(TYPEOID, PlanCacheObjectCallback, (Datum) 0);
6396 153 : 17034 : CacheRegisterSyscacheCallback(NAMESPACEOID, PlanCacheSysCallback, (Datum) 0);
154 : 17034 : CacheRegisterSyscacheCallback(OPEROID, PlanCacheSysCallback, (Datum) 0);
155 : 17034 : CacheRegisterSyscacheCallback(AMOPOPID, PlanCacheSysCallback, (Datum) 0);
3355 156 : 17034 : CacheRegisterSyscacheCallback(FOREIGNSERVEROID, PlanCacheSysCallback, (Datum) 0);
157 : 17034 : CacheRegisterSyscacheCallback(FOREIGNDATAWRAPPEROID, PlanCacheSysCallback, (Datum) 0);
6942 158 : 17034 : }
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 *
3347 185 : 29167 : CreateCachedPlan(RawStmt *raw_parse_tree,
186 : : const char *query_string,
187 : : CommandTag commandTag)
188 : : {
189 : : CachedPlanSource *plansource;
190 : : MemoryContext source_context;
191 : : MemoryContext oldcxt;
192 : :
3189 193 [ - + ]: 29167 : 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 : : */
5294 202 : 29167 : 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 : : */
6942 210 : 29167 : oldcxt = MemoryContextSwitchTo(source_context);
211 : :
95 michael@paquier.xyz 212 :GNC 29167 : plansource = palloc0_object(CachedPlanSource);
5294 tgl@sss.pgh.pa.us 213 :CBC 29167 : plansource->magic = CACHEDPLANSOURCE_MAGIC;
6942 214 : 29167 : plansource->raw_parse_tree = copyObject(raw_parse_tree);
347 215 : 29167 : plansource->analyzed_parse_tree = NULL;
6449 216 : 29167 : plansource->query_string = pstrdup(query_string);
2910 217 : 29167 : MemoryContextSetIdentifier(source_context, plansource->query_string);
5294 218 : 29167 : plansource->commandTag = commandTag;
219 : 29167 : plansource->param_types = NULL;
220 : 29167 : plansource->num_params = 0;
5975 221 : 29167 : plansource->parserSetup = NULL;
222 : 29167 : plansource->parserSetupArg = NULL;
347 223 : 29167 : plansource->postRewrite = NULL;
224 : 29167 : plansource->postRewriteArg = NULL;
5294 225 : 29167 : plansource->cursor_options = 0;
226 : 29167 : plansource->fixed_result = false;
227 : 29167 : plansource->resultDesc = NULL;
6942 228 : 29167 : plansource->context = source_context;
5294 229 : 29167 : plansource->query_list = NIL;
230 : 29167 : plansource->relationOids = NIL;
231 : 29167 : plansource->invalItems = NIL;
4797 232 : 29167 : plansource->search_path = NULL;
5294 233 : 29167 : plansource->query_context = NULL;
3530 234 : 29167 : plansource->rewriteRoleId = InvalidOid;
235 : 29167 : plansource->rewriteRowSecurity = false;
236 : 29167 : plansource->dependsOnRLS = false;
5294 237 : 29167 : plansource->gplan = NULL;
4818 238 : 29167 : plansource->is_oneshot = false;
5294 239 : 29167 : plansource->is_complete = false;
240 : 29167 : plansource->is_saved = false;
241 : 29167 : plansource->is_valid = false;
242 : 29167 : plansource->generation = 0;
243 : 29167 : plansource->generic_cost = -1;
244 : 29167 : plansource->total_custom_cost = 0;
2064 fujii@postgresql.org 245 : 29167 : plansource->num_generic_plans = 0;
5294 tgl@sss.pgh.pa.us 246 : 29167 : plansource->num_custom_plans = 0;
247 : :
6942 248 : 29167 : MemoryContextSwitchTo(oldcxt);
249 : :
250 : 29167 : 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 *
347 265 : 514 : 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 : 514 : plansource = CreateCachedPlan(NULL, query_string, commandTag);
274 : 514 : oldcxt = MemoryContextSwitchTo(plansource->context);
275 : 514 : plansource->analyzed_parse_tree = copyObject(analyzed_parse_tree);
276 : 514 : MemoryContextSwitchTo(oldcxt);
277 : :
278 : 514 : 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 *
3347 300 : 9915 : CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
301 : : const char *query_string,
302 : : CommandTag commandTag)
303 : : {
304 : : CachedPlanSource *plansource;
305 : :
3189 306 [ - + ]: 9915 : 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 : : */
95 michael@paquier.xyz 312 :GNC 9915 : plansource = palloc0_object(CachedPlanSource);
4818 tgl@sss.pgh.pa.us 313 :CBC 9915 : plansource->magic = CACHEDPLANSOURCE_MAGIC;
314 : 9915 : plansource->raw_parse_tree = raw_parse_tree;
347 315 : 9915 : plansource->analyzed_parse_tree = NULL;
4818 316 : 9915 : plansource->query_string = query_string;
317 : 9915 : plansource->commandTag = commandTag;
318 : 9915 : plansource->param_types = NULL;
319 : 9915 : plansource->num_params = 0;
320 : 9915 : plansource->parserSetup = NULL;
321 : 9915 : plansource->parserSetupArg = NULL;
347 322 : 9915 : plansource->postRewrite = NULL;
323 : 9915 : plansource->postRewriteArg = NULL;
4818 324 : 9915 : plansource->cursor_options = 0;
325 : 9915 : plansource->fixed_result = false;
326 : 9915 : plansource->resultDesc = NULL;
327 : 9915 : plansource->context = CurrentMemoryContext;
328 : 9915 : plansource->query_list = NIL;
329 : 9915 : plansource->relationOids = NIL;
330 : 9915 : plansource->invalItems = NIL;
4797 331 : 9915 : plansource->search_path = NULL;
4818 332 : 9915 : plansource->query_context = NULL;
3530 333 : 9915 : plansource->rewriteRoleId = InvalidOid;
334 : 9915 : plansource->rewriteRowSecurity = false;
335 : 9915 : plansource->dependsOnRLS = false;
4818 336 : 9915 : plansource->gplan = NULL;
337 : 9915 : plansource->is_oneshot = true;
338 : 9915 : plansource->is_complete = false;
339 : 9915 : plansource->is_saved = false;
340 : 9915 : plansource->is_valid = false;
341 : 9915 : plansource->generation = 0;
342 : 9915 : plansource->generic_cost = -1;
343 : 9915 : plansource->total_custom_cost = 0;
2064 fujii@postgresql.org 344 : 9915 : plansource->num_generic_plans = 0;
4818 tgl@sss.pgh.pa.us 345 : 9915 : plansource->num_custom_plans = 0;
346 : :
347 : 9915 : 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
5294 393 : 39005 : 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 : 39005 : MemoryContext source_context = plansource->context;
404 : 39005 : MemoryContext oldcxt = CurrentMemoryContext;
405 : :
406 : : /* Assert caller is doing things in a sane order */
407 [ - + ]: 39005 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
408 [ - + ]: 39005 : 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 : : */
4818 417 [ + + ]: 39005 : if (plansource->is_oneshot)
418 : : {
419 : 9907 : querytree_context = CurrentMemoryContext;
420 : : }
421 [ + + ]: 29098 : else if (querytree_context != NULL)
422 : : {
5294 423 : 2926 : MemoryContextSetParent(querytree_context, source_context);
424 : 2926 : MemoryContextSwitchTo(querytree_context);
425 : : }
426 : : else
427 : : {
428 : : /* Again, it's a good bet the querytree_context can be small */
429 : 26172 : querytree_context = AllocSetContextCreate(source_context,
430 : : "CachedPlanQuery",
431 : : ALLOCSET_START_SMALL_SIZES);
432 : 26172 : MemoryContextSwitchTo(querytree_context);
3293 peter_e@gmx.net 433 : 26172 : querytree_list = copyObject(querytree_list);
434 : : }
435 : :
5294 tgl@sss.pgh.pa.us 436 : 39005 : plansource->query_context = querytree_context;
437 : 39005 : plansource->query_list = querytree_list;
438 : :
934 439 [ + + + + ]: 39005 : 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 : : */
4818 447 : 28127 : extract_query_dependencies((Node *) querytree_list,
448 : : &plansource->relationOids,
449 : : &plansource->invalItems,
450 : : &plansource->dependsOnRLS);
451 : :
452 : : /* Update RLS info as well. */
3530 453 : 28127 : plansource->rewriteRoleId = GetUserId();
454 : 28127 : 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 : : */
958 noah@leadboat.com 463 : 28127 : 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 : : */
5294 tgl@sss.pgh.pa.us 470 : 39005 : MemoryContextSwitchTo(source_context);
471 : :
472 [ + + ]: 39005 : if (num_params > 0)
473 : : {
95 michael@paquier.xyz 474 :GNC 6012 : plansource->param_types = palloc_array(Oid, num_params);
5294 tgl@sss.pgh.pa.us 475 :CBC 6012 : memcpy(plansource->param_types, param_types, num_params * sizeof(Oid));
476 : : }
477 : : else
478 : 32993 : plansource->param_types = NULL;
6942 479 : 39005 : plansource->num_params = num_params;
5294 480 : 39005 : plansource->parserSetup = parserSetup;
481 : 39005 : plansource->parserSetupArg = parserSetupArg;
6908 482 : 39005 : plansource->cursor_options = cursor_options;
6942 483 : 39005 : 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
491 : : 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
225 tgl@sss.pgh.pa.us 500 :GNC 39005 : plansource->resultDesc = PlanCacheComputeResultDesc(querytree_list);
5294 tgl@sss.pgh.pa.us 501 :CBC 39005 : MemoryContextSwitchTo(oldcxt);
502 : : #endif
503 : :
504 : 39005 : plansource->is_complete = true;
505 : 39005 : plansource->is_valid = true;
506 : 39005 : }
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
347 522 : 1178 : SetPostRewriteHook(CachedPlanSource *plansource,
523 : : PostRewriteHook postRewrite,
524 : : void *postRewriteArg)
525 : : {
526 [ - + ]: 1178 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
527 : 1178 : plansource->postRewrite = postRewrite;
528 : 1178 : plansource->postRewriteArg = postRewriteArg;
529 : 1178 : }
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
5294 547 : 23593 : SaveCachedPlan(CachedPlanSource *plansource)
548 : : {
549 : : /* Assert caller is doing things in a sane order */
550 [ - + ]: 23593 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
551 [ - + ]: 23593 : Assert(plansource->is_complete);
552 [ - + ]: 23593 : Assert(!plansource->is_saved);
553 : :
554 : : /* This seems worth a real test, though */
4818 555 [ - + ]: 23593 : if (plansource->is_oneshot)
4818 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 : : */
5294 tgl@sss.pgh.pa.us 565 :CBC 23593 : 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 : 23593 : MemoryContextSetParent(plansource->context, CacheMemoryContext);
573 : :
574 : : /*
575 : : * Add the entry to the global list of cached plans.
576 : : */
2649 577 : 23593 : dlist_push_tail(&saved_plan_list, &plansource->node);
578 : :
5294 579 : 23593 : plansource->is_saved = true;
6942 580 : 23593 : }
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
5294 591 : 7927 : DropCachedPlan(CachedPlanSource *plansource)
592 : : {
593 [ - + ]: 7927 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
594 : :
595 : : /* If it's been saved, remove it from the list */
596 [ + + ]: 7927 : if (plansource->is_saved)
597 : : {
2649 598 : 7832 : dlist_delete(&plansource->node);
5294 599 : 7832 : plansource->is_saved = false;
600 : : }
601 : :
602 : : /* Decrement generic CachedPlan's refcount and drop if no longer needed */
603 : 7927 : ReleaseGenericPlan(plansource);
604 : :
605 : : /* Mark it no longer valid */
4818 606 : 7927 : 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 [ + - ]: 7927 : if (!plansource->is_oneshot)
613 : 7927 : MemoryContextDelete(plansource->context);
5975 614 : 7927 : }
615 : :
616 : : /*
617 : : * ReleaseGenericPlan: release a CachedPlanSource's generic plan, if any.
618 : : */
619 : : static void
5294 620 : 60451 : ReleaseGenericPlan(CachedPlanSource *plansource)
621 : : {
622 : : /* Be paranoid about the possibility that ReleaseCachedPlan fails */
623 [ + + ]: 60451 : if (plansource->gplan)
624 : : {
625 : 7890 : CachedPlan *plan = plansource->gplan;
626 : :
627 [ - + ]: 7890 : Assert(plan->magic == CACHEDPLAN_MAGIC);
628 : 7890 : plansource->gplan = NULL;
1875 629 : 7890 : ReleaseCachedPlan(plan, NULL);
630 : : }
5294 631 : 60451 : }
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
347 642 : 15243084 : StmtPlanRequiresRevalidation(CachedPlanSource *plansource)
643 : : {
644 [ + + ]: 15243084 : if (plansource->raw_parse_tree != NULL)
645 : 14948984 : return stmt_requires_parse_analysis(plansource->raw_parse_tree);
646 [ + + ]: 294100 : else if (plansource->analyzed_parse_tree != NULL)
647 : 294097 : return query_requires_rewrite_plan(plansource->analyzed_parse_tree);
648 : : /* empty query never needs revalidation */
649 : 3 : 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 : 498 : BuildingPlanRequiresSnapshot(CachedPlanSource *plansource)
660 : : {
661 [ + - ]: 498 : if (plansource->raw_parse_tree != NULL)
662 : 498 : return analyze_requires_snapshot(plansource->raw_parse_tree);
347 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 *
3271 kgrittn@postgresql.o 684 :CBC 553857 : 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 : : */
934 tgl@sss.pgh.pa.us 701 [ + + + + ]: 553857 : if (plansource->is_oneshot || !StmtPlanRequiresRevalidation(plansource))
702 : : {
4818 703 [ - + ]: 21282 : Assert(plansource->is_valid);
704 : 21282 : 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 : : */
4797 715 [ + + ]: 532575 : if (plansource->is_valid)
716 : : {
717 [ - + ]: 529184 : Assert(plansource->search_path != NULL);
958 noah@leadboat.com 718 [ + + ]: 529184 : if (!SearchPathMatchesCurrentEnvironment(plansource->search_path))
719 : : {
720 : : /* Invalidate the querytree and generic plan */
4797 tgl@sss.pgh.pa.us 721 : 35 : plansource->is_valid = false;
722 [ + + ]: 35 : if (plansource->gplan)
723 : 26 : 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 : : */
3530 731 [ + + + + : 532722 : if (plansource->is_valid && plansource->dependsOnRLS &&
+ + ]
732 : 147 : (plansource->rewriteRoleId != GetUserId() ||
733 [ + + ]: 104 : plansource->rewriteRowSecurity != row_security))
4195 sfrost@snowman.net 734 : 55 : 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 : : */
5294 tgl@sss.pgh.pa.us 741 [ + + ]: 532575 : if (plansource->is_valid)
742 : : {
743 : 529094 : 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 [ + + ]: 529094 : if (plansource->is_valid)
750 : : {
751 : : /* Successfully revalidated and locked the query. */
752 : 529091 : return NIL;
753 : : }
754 : :
755 : : /* Oops, the race case happened. Release useless locks. */
756 : 3 : 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 : 3484 : plansource->is_valid = false;
765 : 3484 : plansource->query_list = NIL;
766 : 3484 : plansource->relationOids = NIL;
767 : 3484 : plansource->invalItems = NIL;
4797 768 : 3484 : 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 : : */
5294 776 [ + + ]: 3484 : if (plansource->query_context)
777 : : {
5026 bruce@momjian.us 778 : 3469 : MemoryContext qcxt = plansource->query_context;
779 : :
5294 tgl@sss.pgh.pa.us 780 : 3469 : plansource->query_context = NULL;
781 : 3469 : MemoryContextDelete(qcxt);
782 : : }
783 : :
784 : : /* Drop the generic plan reference if any */
297 amitlan@postgresql.o 785 : 3484 : 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 : : */
5294 tgl@sss.pgh.pa.us 791 [ - + ]: 3484 : 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 : 3484 : snapshot_set = false;
801 [ + + ]: 3484 : if (!ActiveSnapshotSet())
802 : : {
803 : 10 : PushActiveSnapshot(GetTransactionSnapshot());
804 : 10 : snapshot_set = true;
805 : : }
806 : :
807 : : /*
808 : : * Run parse analysis (if needed) and rule rewriting.
809 : : */
347 810 [ + + ]: 3484 : 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 : 3241 : rawtree = copyObject(plansource->raw_parse_tree);
820 [ + + ]: 3241 : if (plansource->parserSetup != NULL)
821 : 3006 : tlist = pg_analyze_and_rewrite_withcb(rawtree,
822 : : plansource->query_string,
823 : : plansource->parserSetup,
824 : : plansource->parserSetupArg,
825 : : queryEnv);
826 : : else
827 : 235 : tlist = pg_analyze_and_rewrite_fixedparams(rawtree,
828 : : plansource->query_string,
829 : 235 : plansource->param_types,
830 : : plansource->num_params,
831 : : queryEnv);
832 : : }
833 [ + - ]: 243 : 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 : 243 : analyzed_tree = copyObject(plansource->analyzed_parse_tree);
840 : : /* Acquire locks needed before rewriting ... */
841 : 243 : AcquireRewriteLocks(analyzed_tree, true, false);
842 : : /* ... and do it */
843 : 243 : tlist = pg_rewrite_query(analyzed_tree);
844 : : }
845 : : else
846 : : {
847 : : /* Empty query, nothing to do */
347 tgl@sss.pgh.pa.us 848 :UBC 0 : tlist = NIL;
849 : : }
850 : :
851 : : /* Apply post-rewrite callback if there is one */
347 tgl@sss.pgh.pa.us 852 [ + + ]:CBC 3464 : if (plansource->postRewrite != NULL)
853 : 280 : plansource->postRewrite(tlist, plansource->postRewriteArg);
854 : :
855 : : /* Release snapshot if we got one */
5294 856 [ + + ]: 3464 : if (snapshot_set)
857 : 10 : 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 : 3464 : resultDesc = PlanCacheComputeResultDesc(tlist);
866 [ + + + - ]: 3464 : if (resultDesc == NULL && plansource->resultDesc == NULL)
867 : : {
868 : : /* OK, doesn't return tuples */
869 : : }
870 [ + - + - ]: 3385 : else if (resultDesc == NULL || plansource->resultDesc == NULL ||
728 peter@eisentraut.org 871 [ + + ]: 3385 : !equalRowTypes(resultDesc, plansource->resultDesc))
872 : : {
873 : : /* can we give a better error message? */
5294 tgl@sss.pgh.pa.us 874 [ + + ]: 29 : if (plansource->fixed_result)
875 [ + - ]: 6 : ereport(ERROR,
876 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
877 : : errmsg("cached plan must not change result type")));
878 : 23 : oldcxt = MemoryContextSwitchTo(plansource->context);
879 [ + - ]: 23 : if (resultDesc)
880 : 23 : resultDesc = CreateTupleDescCopy(resultDesc);
881 [ + - ]: 23 : if (plansource->resultDesc)
882 : 23 : FreeTupleDesc(plansource->resultDesc);
883 : 23 : plansource->resultDesc = resultDesc;
884 : 23 : 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 : 3458 : querytree_context = AllocSetContextCreate(CurrentMemoryContext,
892 : : "CachedPlanQuery",
893 : : ALLOCSET_START_SMALL_SIZES);
894 : 3458 : oldcxt = MemoryContextSwitchTo(querytree_context);
895 : :
3293 peter_e@gmx.net 896 : 3458 : 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 : : */
5294 tgl@sss.pgh.pa.us 903 : 3458 : extract_query_dependencies((Node *) qlist,
904 : : &plansource->relationOids,
905 : : &plansource->invalItems,
906 : : &plansource->dependsOnRLS);
907 : :
908 : : /* Update RLS info as well. */
3530 909 : 3458 : plansource->rewriteRoleId = GetUserId();
910 : 3458 : 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 : : */
958 noah@leadboat.com 917 : 3458 : plansource->search_path = GetSearchPathMatcher(querytree_context);
918 : :
5294 tgl@sss.pgh.pa.us 919 : 3458 : MemoryContextSwitchTo(oldcxt);
920 : :
921 : : /* Now reparent the finished query_context and save the links */
922 : 3458 : MemoryContextSetParent(querytree_context, plansource->context);
923 : :
924 : 3458 : plansource->query_context = querytree_context;
925 : 3458 : 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 : 3458 : plansource->is_valid = true;
937 : :
938 : : /* Return transient copy of querytrees for possible use in planning */
939 : 3458 : 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 : 520218 : CheckCachedPlan(CachedPlanSource *plansource)
953 : : {
954 : 520218 : CachedPlan *plan = plansource->gplan;
955 : :
956 : : /* Assert that caller checked the querytree */
957 [ - + ]: 520218 : Assert(plansource->is_valid);
958 : :
959 : : /* If there's no generic plan, just say "false" */
960 [ + + ]: 520218 : if (!plan)
961 : 25399 : return false;
962 : :
963 [ - + ]: 494819 : Assert(plan->magic == CACHEDPLAN_MAGIC);
964 : : /* Generic plans are never one-shot */
4818 965 [ - + ]: 494819 : Assert(!plan->is_oneshot);
966 : :
967 : : /*
968 : : * If plan isn't valid for current role, we can't use it.
969 : : */
3530 970 [ + + + + : 494838 : if (plan->is_valid && plan->dependsOnRole &&
+ + ]
971 : 19 : plan->planRoleId != GetUserId())
972 : 18 : 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 : : */
5294 978 [ + + ]: 494819 : 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 : : */
6942 984 [ - + ]: 494780 : Assert(plan->refcount > 0);
985 : :
5294 986 : 494780 : 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 [ + - ]: 494780 : if (plan->is_valid &&
6751 993 [ - + ]: 494780 : TransactionIdIsValid(plan->saved_xmin) &&
6751 tgl@sss.pgh.pa.us 994 [ # # ]:UBC 0 : !TransactionIdEquals(plan->saved_xmin, TransactionXmin))
5294 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 : : */
5294 tgl@sss.pgh.pa.us 1001 [ + - ]:CBC 494780 : if (plan->is_valid)
1002 : : {
1003 : : /* Successfully revalidated and locked the query. */
1004 : 494780 : return true;
1005 : : }
1006 : :
1007 : : /* Oops, the race case happened. Release useless locks. */
5294 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 : : */
5294 tgl@sss.pgh.pa.us 1014 :CBC 39 : ReleaseGenericPlan(plansource);
1015 : :
1016 : 39 : 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 : 50983 : 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;
4818 1044 : 50983 : 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 : : */
5293 1060 [ - + ]: 50983 : if (!plansource->is_valid)
297 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 : : */
5294 tgl@sss.pgh.pa.us 1068 [ + + ]:CBC 50983 : if (qlist == NIL)
1069 : : {
4818 1070 [ + + ]: 47526 : if (!plansource->is_oneshot)
3293 peter_e@gmx.net 1071 : 37622 : qlist = copyObject(plansource->query_list);
1072 : : else
4818 tgl@sss.pgh.pa.us 1073 : 9904 : 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 : : */
5294 1080 : 50983 : snapshot_set = false;
5285 1081 [ + + + + ]: 51481 : if (!ActiveSnapshotSet() &&
347 1082 : 498 : BuildingPlanRequiresSnapshot(plansource))
1083 : : {
5294 1084 : 144 : PushActiveSnapshot(GetTransactionSnapshot());
1085 : 144 : snapshot_set = true;
1086 : : }
1087 : :
1088 : : /*
1089 : : * Generate the plan.
1090 : : */
2176 fujii@postgresql.org 1091 : 50983 : plist = pg_plan_queries(qlist, plansource->query_string,
1092 : : plansource->cursor_options, boundParams);
1093 : :
1094 : : /* Release snapshot if we got one */
5294 tgl@sss.pgh.pa.us 1095 [ + + ]: 50877 : if (snapshot_set)
1096 : 141 : 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 : : */
4818 1104 [ + + ]: 50877 : if (!plansource->is_oneshot)
1105 : : {
1106 : 41011 : plan_context = AllocSetContextCreate(CurrentMemoryContext,
1107 : : "CachedPlan",
1108 : : ALLOCSET_START_SMALL_SIZES);
2900 peter_e@gmx.net 1109 : 41011 : MemoryContextCopyAndSetIdentifier(plan_context, plansource->query_string);
1110 : :
1111 : : /*
1112 : : * Copy plan into the new context.
1113 : : */
297 amitlan@postgresql.o 1114 : 41011 : MemoryContextSwitchTo(plan_context);
1115 : :
3293 peter_e@gmx.net 1116 : 41011 : plist = copyObject(plist);
1117 : : }
1118 : : else
4818 tgl@sss.pgh.pa.us 1119 : 9866 : plan_context = CurrentMemoryContext;
1120 : :
1121 : : /*
1122 : : * Create and fill the CachedPlan struct within the new context.
1123 : : */
95 michael@paquier.xyz 1124 :GNC 50877 : plan = palloc_object(CachedPlan);
5294 tgl@sss.pgh.pa.us 1125 :CBC 50877 : plan->magic = CACHEDPLAN_MAGIC;
1126 : 50877 : 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 : : */
3530 1133 : 50877 : plan->planRoleId = GetUserId();
1134 : 50877 : plan->dependsOnRole = plansource->dependsOnRLS;
1135 : 50877 : is_transient = false;
1136 [ + - + + : 101757 : foreach(lc, plist)
+ + ]
1137 : : {
3261 1138 : 50880 : PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
1139 : :
3347 1140 [ + + ]: 50880 : if (plannedstmt->commandType == CMD_UTILITY)
3530 1141 : 10146 : continue; /* Ignore utility statements */
1142 : :
1143 [ + + ]: 40734 : if (plannedstmt->transientPlan)
1144 : 66 : is_transient = true;
1145 [ + + ]: 40734 : if (plannedstmt->dependsOnRole)
1146 : 36 : plan->dependsOnRole = true;
1147 : : }
1148 [ + + ]: 50877 : if (is_transient)
1149 : : {
5294 1150 [ - + ]: 66 : Assert(TransactionIdIsNormal(TransactionXmin));
1151 : 66 : plan->saved_xmin = TransactionXmin;
1152 : : }
1153 : : else
1154 : 50811 : plan->saved_xmin = InvalidTransactionId;
1155 : 50877 : plan->refcount = 0;
1156 : 50877 : plan->context = plan_context;
4818 1157 : 50877 : plan->is_oneshot = plansource->is_oneshot;
5294 1158 : 50877 : plan->is_saved = false;
1159 : 50877 : plan->is_valid = true;
1160 : :
1161 : : /* assign generation number to new plan */
1162 : 50877 : plan->generation = ++(plansource->generation);
1163 : :
1164 : 50877 : MemoryContextSwitchTo(oldcxt);
1165 : :
1166 : 50877 : 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 : 571133 : choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams)
1176 : : {
1177 : : double avg_custom_cost;
1178 : :
1179 : : /* One-shot plans will always be considered custom */
4818 1180 [ + + ]: 571133 : if (plansource->is_oneshot)
1181 : 9904 : return true;
1182 : :
1183 : : /* Otherwise, never any point in a custom plan if there's no parameters */
5294 1184 [ + + ]: 561229 : if (boundParams == NULL)
1185 : 76393 : return false;
1186 : : /* ... nor when planning would be a no-op */
934 1187 [ - + ]: 484836 : if (!StmtPlanRequiresRevalidation(plansource))
4712 tgl@sss.pgh.pa.us 1188 :UBC 0 : return false;
1189 : :
1190 : : /* Let settings force the decision */
2799 peter_e@gmx.net 1191 [ + + ]:CBC 484836 : if (plan_cache_mode == PLAN_CACHE_MODE_FORCE_GENERIC_PLAN)
1192 : 1436 : return false;
1193 [ + + ]: 483400 : if (plan_cache_mode == PLAN_CACHE_MODE_FORCE_CUSTOM_PLAN)
1194 : 20 : return true;
1195 : :
1196 : : /* See if caller wants to force the decision */
5294 tgl@sss.pgh.pa.us 1197 [ - + ]: 483380 : if (plansource->cursor_options & CURSOR_OPT_GENERIC_PLAN)
5294 tgl@sss.pgh.pa.us 1198 :UBC 0 : return false;
5294 tgl@sss.pgh.pa.us 1199 [ - + ]:CBC 483380 : if (plansource->cursor_options & CURSOR_OPT_CUSTOM_PLAN)
5294 tgl@sss.pgh.pa.us 1200 :UBC 0 : return true;
1201 : :
1202 : : /* Generate custom plans until we have done at least 5 (arbitrary) */
5294 tgl@sss.pgh.pa.us 1203 [ + + ]:CBC 483380 : if (plansource->num_custom_plans < 5)
1204 : 13693 : return true;
1205 : :
1206 : 469687 : 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 : : */
4586 1218 [ + + ]: 469687 : if (plansource->generic_cost < avg_custom_cost)
5294 1219 : 467759 : return false;
1220 : :
1221 : 1928 : 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
4586 1232 : 50877 : cached_plan_cost(CachedPlan *plan, bool include_planner)
1233 : : {
5294 1234 : 50877 : double result = 0;
1235 : : ListCell *lc;
1236 : :
1237 [ + - + + : 101757 : foreach(lc, plan->stmt_list)
+ + ]
1238 : : {
3261 1239 : 50880 : PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
1240 : :
3347 1241 [ + + ]: 50880 : if (plannedstmt->commandType == CMD_UTILITY)
5294 1242 : 10146 : continue; /* Ignore utility statements */
1243 : :
1244 : 40734 : result += plannedstmt->planTree->total_cost;
1245 : :
4586 1246 [ + + ]: 40734 : 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 : 20888 : int nrelations = list_length(plannedstmt->rtable);
1270 : :
1271 : 20888 : result += 1000.0 * cpu_operator_cost * (nrelations + 1);
1272 : : }
1273 : : }
1274 : :
5294 1275 : 50877 : 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 : 545751 : GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
1298 : : ResourceOwner owner, QueryEnvironment *queryEnv)
1299 : : {
3386 sfrost@snowman.net 1300 : 545751 : CachedPlan *plan = NULL;
1301 : : List *qlist;
1302 : : bool customplan;
1303 : : ListCell *lc;
1304 : :
1305 : : /* Assert caller is doing things in a sane order */
5294 tgl@sss.pgh.pa.us 1306 [ - + ]: 545751 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1307 [ - + ]: 545751 : Assert(plansource->is_complete);
1308 : : /* This seems worth a real test, though */
1875 1309 [ + + - + ]: 545751 : if (owner && !plansource->is_saved)
5294 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 */
297 amitlan@postgresql.o 1313 :CBC 545751 : qlist = RevalidateCachedQuery(plansource, queryEnv);
1314 : :
1315 : : /* Decide whether to use a custom plan */
5294 tgl@sss.pgh.pa.us 1316 : 545725 : customplan = choose_custom_plan(plansource, boundParams);
1317 : :
1318 [ + + ]: 545725 : if (!customplan)
1319 : : {
1320 [ + + ]: 520218 : if (CheckCachedPlan(plansource))
1321 : : {
1322 : : /* We want a generic plan, and we already have a valid one */
1323 : 494780 : plan = plansource->gplan;
1324 [ - + ]: 494780 : Assert(plan->magic == CACHEDPLAN_MAGIC);
1325 : : }
1326 : : else
1327 : : {
1328 : : /* Build a new generic plan */
3271 kgrittn@postgresql.o 1329 : 25438 : plan = BuildCachedPlan(plansource, qlist, NULL, queryEnv);
1330 : : /* Just make real sure plansource->gplan is clear */
5294 tgl@sss.pgh.pa.us 1331 : 25408 : ReleaseGenericPlan(plansource);
1332 : : /* Link the new generic plan into the plansource */
1333 : 25408 : plansource->gplan = plan;
1334 : 25408 : plan->refcount++;
1335 : : /* Immediately reparent into appropriate context */
1336 [ + + ]: 25408 : if (plansource->is_saved)
1337 : : {
1338 : : /* saved plans all live under CacheMemoryContext */
1339 : 19912 : MemoryContextSetParent(plan->context, CacheMemoryContext);
1340 : 19912 : plan->is_saved = true;
1341 : : }
1342 : : else
1343 : : {
1344 : : /* otherwise, it should be a sibling of the plansource */
1345 : 5496 : MemoryContextSetParent(plan->context,
1346 : : MemoryContextGetParent(plansource->context));
1347 : : }
1348 : : /* Update generic_cost whenever we make a new generic plan */
4586 1349 : 25408 : 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 : : */
5294 1360 : 25408 : 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 : : */
5284 1367 : 25408 : qlist = NIL;
1368 : : }
1369 : : }
1370 : :
5294 1371 [ + + ]: 545695 : if (customplan)
1372 : : {
1373 : : /* Build a custom plan */
3271 kgrittn@postgresql.o 1374 : 25545 : plan = BuildCachedPlan(plansource, qlist, boundParams, queryEnv);
1375 : : /* Accumulate total costs of custom plans */
2064 fujii@postgresql.org 1376 : 25469 : plansource->total_custom_cost += cached_plan_cost(plan, true);
1377 : :
1378 : 25469 : plansource->num_custom_plans++;
1379 : : }
1380 : : else
1381 : : {
1382 : 520150 : plansource->num_generic_plans++;
1383 : : }
1384 : :
3386 sfrost@snowman.net 1385 [ - + ]: 545619 : Assert(plan != NULL);
1386 : :
1387 : : /* Flag the plan as in use by caller */
1875 tgl@sss.pgh.pa.us 1388 [ + + ]: 545619 : if (owner)
858 heikki.linnakangas@i 1389 : 510723 : ResourceOwnerEnlarge(owner);
6942 tgl@sss.pgh.pa.us 1390 : 545619 : plan->refcount++;
1875 1391 [ + + ]: 545619 : if (owner)
1392 : 510723 : 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 : : */
5294 1400 [ + + + + ]: 545619 : if (customplan && plansource->is_saved)
1401 : : {
1402 : 15597 : MemoryContextSetParent(plan->context, CacheMemoryContext);
1403 : 15597 : plan->is_saved = true;
1404 : : }
1405 : :
234 michael@paquier.xyz 1406 [ + - + + :GNC 1091241 : foreach(lc, plan->stmt_list)
+ + ]
1407 : : {
1408 : 545622 : PlannedStmt *pstmt = (PlannedStmt *) lfirst(lc);
1409 : :
227 1410 [ + + ]: 545622 : pstmt->planOrigin = customplan ? PLAN_STMT_CACHE_CUSTOM : PLAN_STMT_CACHE_GENERIC;
1411 : : }
1412 : :
6942 tgl@sss.pgh.pa.us 1413 :CBC 545619 : 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
1875 1428 : 594886 : ReleaseCachedPlan(CachedPlan *plan, ResourceOwner owner)
1429 : : {
5294 1430 [ - + ]: 594886 : Assert(plan->magic == CACHEDPLAN_MAGIC);
1875 1431 [ + + ]: 594886 : if (owner)
1432 : : {
5294 1433 [ - + ]: 505148 : Assert(plan->is_saved);
1875 1434 : 505148 : ResourceOwnerForgetPlanCacheRef(owner, plan);
1435 : : }
6942 1436 [ - + ]: 594886 : Assert(plan->refcount > 0);
1437 : 594886 : plan->refcount--;
1438 [ + + ]: 594886 : if (plan->refcount == 0)
1439 : : {
1440 : : /* Mark it no longer valid */
4818 1441 : 33271 : plan->magic = 0;
1442 : :
1443 : : /* One-shot plans do not own their context, so we can't free them */
1444 [ + + ]: 33271 : if (!plan->is_oneshot)
1445 : 23493 : MemoryContextDelete(plan->context);
1446 : : }
6942 1447 : 594886 : }
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
2180 1473 : 15465 : 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 [ - + ]: 15465 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1487 [ - + ]: 15465 : Assert(plan->magic == CACHEDPLAN_MAGIC);
1488 [ - + ]: 15465 : Assert(plan->is_valid);
1489 [ - + ]: 15465 : Assert(plan == plansource->gplan);
2179 1490 [ - + ]: 15465 : Assert(plansource->search_path != NULL);
958 noah@leadboat.com 1491 [ - + ]: 15465 : Assert(SearchPathMatchesCurrentEnvironment(plansource->search_path));
1492 : :
1493 : : /* We don't support oneshot plans here. */
2180 tgl@sss.pgh.pa.us 1494 [ - + ]: 15465 : if (plansource->is_oneshot)
2180 tgl@sss.pgh.pa.us 1495 :UBC 0 : return false;
2180 tgl@sss.pgh.pa.us 1496 [ - + ]:CBC 15465 : 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 [ - + ]: 15465 : if (plansource->dependsOnRLS)
2180 tgl@sss.pgh.pa.us 1504 :UBC 0 : return false;
2180 tgl@sss.pgh.pa.us 1505 [ - + ]:CBC 15465 : if (plan->dependsOnRole)
2180 tgl@sss.pgh.pa.us 1506 :UBC 0 : return false;
2180 tgl@sss.pgh.pa.us 1507 [ - + ]:CBC 15465 : if (TransactionIdIsValid(plan->saved_xmin))
2180 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 : : */
2180 tgl@sss.pgh.pa.us 1516 [ + - + + :CBC 30930 : foreach(lc, plansource->query_list)
+ + ]
1517 : : {
1518 : 15465 : Query *query = lfirst_node(Query, lc);
1519 : :
1520 [ - + ]: 15465 : if (query->commandType == CMD_UTILITY)
2180 tgl@sss.pgh.pa.us 1521 :UBC 0 : return false;
2180 tgl@sss.pgh.pa.us 1522 [ + - + - :CBC 15465 : if (query->rtable || query->cteList || query->hasSubLinks)
- + ]
2180 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 : : */
2180 tgl@sss.pgh.pa.us 1530 [ + - + + :CBC 30930 : foreach(lc, plan->stmt_list)
+ + ]
1531 : : {
1532 : 15465 : PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
1533 : : ListCell *lc2;
1534 : :
1535 [ - + ]: 15465 : if (plannedstmt->commandType == CMD_UTILITY)
2180 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 : : */
2180 tgl@sss.pgh.pa.us 1542 [ + - + + :CBC 30930 : foreach(lc2, plannedstmt->rtable)
+ + ]
1543 : : {
1544 : 15465 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
1545 : :
1546 [ - + ]: 15465 : if (rte->rtekind == RTE_RELATION)
2180 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. */
2179 tgl@sss.pgh.pa.us 1557 [ + - ]:CBC 15465 : if (owner)
1558 : : {
858 heikki.linnakangas@i 1559 : 15465 : ResourceOwnerEnlarge(owner);
2179 tgl@sss.pgh.pa.us 1560 : 15465 : plan->refcount++;
1561 : 15465 : ResourceOwnerRememberPlanCacheRef(owner, plan);
1562 : : }
1563 : :
2180 1564 : 15465 : 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 : 170791 : 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 [ - + ]: 170791 : 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 : : */
969 1605 [ + + + - ]: 170791 : if (!plansource->is_valid ||
1606 [ + - ]: 168261 : plan == NULL || plan != plansource->gplan ||
1607 [ + + ]: 168261 : !plan->is_valid)
2180 1608 : 2536 : return false;
1609 : :
1610 [ - + ]: 168255 : Assert(plan->magic == CACHEDPLAN_MAGIC);
1611 : :
1612 : : /* Is the search_path still the same as when we made it? */
1613 [ - + ]: 168255 : Assert(plansource->search_path != NULL);
958 noah@leadboat.com 1614 [ + + ]: 168255 : if (!SearchPathMatchesCurrentEnvironment(plansource->search_path))
2180 tgl@sss.pgh.pa.us 1615 : 14 : return false;
1616 : :
1617 : : /* It's still good. Bump refcount if requested. */
1618 [ + + ]: 168241 : if (owner)
1619 : : {
858 heikki.linnakangas@i 1620 : 26074 : ResourceOwnerEnlarge(owner);
2180 tgl@sss.pgh.pa.us 1621 : 26074 : plan->refcount++;
1622 : 26074 : ResourceOwnerRememberPlanCacheRef(owner, plan);
1623 : : }
1624 : :
1625 : 168241 : 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
5294 1635 : 17834 : CachedPlanSetParentContext(CachedPlanSource *plansource,
1636 : : MemoryContext newcontext)
1637 : : {
1638 : : /* Assert caller is doing things in a sane order */
1639 [ - + ]: 17834 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1640 [ - + ]: 17834 : Assert(plansource->is_complete);
1641 : :
1642 : : /* These seem worth real tests, though */
1643 [ - + ]: 17834 : if (plansource->is_saved)
5294 tgl@sss.pgh.pa.us 1644 [ # # ]:UBC 0 : elog(ERROR, "cannot move a saved cached plan to another context");
4818 tgl@sss.pgh.pa.us 1645 [ - + ]:CBC 17834 : if (plansource->is_oneshot)
4818 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 */
5294 tgl@sss.pgh.pa.us 1649 :CBC 17834 : 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 [ - + ]: 17834 : if (plansource->gplan)
1657 : : {
5294 tgl@sss.pgh.pa.us 1658 [ # # ]:UBC 0 : Assert(plansource->gplan->magic == CACHEDPLAN_MAGIC);
1659 : 0 : MemoryContextSetParent(plansource->gplan->context, newcontext);
1660 : : }
5294 tgl@sss.pgh.pa.us 1661 :CBC 17834 : }
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 *
5294 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 : : */
4818 1687 [ # # ]: 0 : if (plansource->is_oneshot)
1688 [ # # ]: 0 : elog(ERROR, "cannot copy a one-shot cached plan");
1689 : :
5294 1690 : 0 : source_context = AllocSetContextCreate(CurrentMemoryContext,
1691 : : "CachedPlanSource",
1692 : : ALLOCSET_START_SMALL_SIZES);
1693 : :
1694 : 0 : oldcxt = MemoryContextSwitchTo(source_context);
1695 : :
95 michael@paquier.xyz 1696 :UNC 0 : newsource = palloc0_object(CachedPlanSource);
5294 tgl@sss.pgh.pa.us 1697 :UBC 0 : newsource->magic = CACHEDPLANSOURCE_MAGIC;
1698 : 0 : newsource->raw_parse_tree = copyObject(plansource->raw_parse_tree);
347 1699 : 0 : newsource->analyzed_parse_tree = copyObject(plansource->analyzed_parse_tree);
5294 1700 : 0 : newsource->query_string = pstrdup(plansource->query_string);
2910 1701 : 0 : MemoryContextSetIdentifier(source_context, newsource->query_string);
5294 1702 : 0 : newsource->commandTag = plansource->commandTag;
1703 [ # # ]: 0 : if (plansource->num_params > 0)
1704 : : {
95 michael@paquier.xyz 1705 :UNC 0 : newsource->param_types = palloc_array(Oid, plansource->num_params);
5294 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;
347 1714 : 0 : newsource->postRewrite = plansource->postRewrite;
1715 : 0 : newsource->postRewriteArg = plansource->postRewriteArg;
5294 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);
3293 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);
4797 tgl@sss.pgh.pa.us 1731 [ # # ]: 0 : if (plansource->search_path)
958 noah@leadboat.com 1732 : 0 : newsource->search_path = CopySearchPathMatcher(plansource->search_path);
5294 tgl@sss.pgh.pa.us 1733 : 0 : newsource->query_context = querytree_context;
3530 1734 : 0 : newsource->rewriteRoleId = plansource->rewriteRoleId;
1735 : 0 : newsource->rewriteRowSecurity = plansource->rewriteRowSecurity;
1736 : 0 : newsource->dependsOnRLS = plansource->dependsOnRLS;
1737 : :
5294 1738 : 0 : newsource->gplan = NULL;
1739 : :
4818 1740 : 0 : newsource->is_oneshot = false;
5294 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;
2064 fujii@postgresql.org 1749 : 0 : newsource->num_generic_plans = plansource->num_generic_plans;
5294 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
6390 tgl@sss.pgh.pa.us 1766 :CBC 401849 : CachedPlanIsValid(CachedPlanSource *plansource)
1767 : : {
5294 1768 [ - + ]: 401849 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1769 : 401849 : 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 *
3271 kgrittn@postgresql.o 1779 : 8106 : CachedPlanGetTargetList(CachedPlanSource *plansource,
1780 : : QueryEnvironment *queryEnv)
1781 : : {
1782 : : Query *pstmt;
1783 : :
1784 : : /* Assert caller is doing things in a sane order */
5294 tgl@sss.pgh.pa.us 1785 [ - + ]: 8106 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1786 [ - + ]: 8106 : 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 [ - + ]: 8106 : if (plansource->resultDesc == NULL)
5294 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 */
297 amitlan@postgresql.o 1796 :CBC 8106 : RevalidateCachedQuery(plansource, queryEnv);
1797 : :
1798 : : /* Get the primary statement and find out what it returns */
3347 tgl@sss.pgh.pa.us 1799 : 8106 : pstmt = QueryListGetPrimaryStmt(plansource->query_list);
1800 : :
1801 : 8106 : 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 *
2649 1816 : 189 : 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 : 189 : 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 : 189 : cexpr_context = AllocSetContextCreate(CurrentMemoryContext,
1839 : : "CachedExpression",
1840 : : ALLOCSET_SMALL_SIZES);
1841 : :
1842 : 189 : oldcxt = MemoryContextSwitchTo(cexpr_context);
1843 : :
95 michael@paquier.xyz 1844 :GNC 189 : cexpr = palloc_object(CachedExpression);
2649 tgl@sss.pgh.pa.us 1845 :CBC 189 : cexpr->magic = CACHEDEXPR_MAGIC;
1846 : 189 : cexpr->expr = copyObject(expr);
1847 : 189 : cexpr->is_valid = true;
1848 : 189 : cexpr->relationOids = copyObject(relationOids);
1849 : 189 : cexpr->invalItems = copyObject(invalItems);
1850 : 189 : cexpr->context = cexpr_context;
1851 : :
1852 : 189 : MemoryContextSwitchTo(oldcxt);
1853 : :
1854 : : /*
1855 : : * Reparent the expr's memory context under CacheMemoryContext so that it
1856 : : * will live indefinitely.
1857 : : */
1858 : 189 : MemoryContextSetParent(cexpr_context, CacheMemoryContext);
1859 : :
1860 : : /*
1861 : : * Add the entry to the global list of cached expressions.
1862 : : */
1863 : 189 : dlist_push_tail(&cached_expression_list, &cexpr->node);
1864 : :
1865 : 189 : return cexpr;
1866 : : }
1867 : :
1868 : : /*
1869 : : * FreeCachedExpression
1870 : : * Delete a CachedExpression.
1871 : : */
1872 : : void
1873 : 28 : FreeCachedExpression(CachedExpression *cexpr)
1874 : : {
1875 : : /* Sanity check */
1876 [ - + ]: 28 : Assert(cexpr->magic == CACHEDEXPR_MAGIC);
1877 : : /* Unlink from global list */
1878 : 28 : dlist_delete(&cexpr->node);
1879 : : /* Free all storage associated with CachedExpression */
1880 : 28 : MemoryContextDelete(cexpr->context);
1881 : 28 : }
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 *
3347 1892 : 8264 : QueryListGetPrimaryStmt(List *stmts)
1893 : : {
1894 : : ListCell *lc;
1895 : :
1896 [ + - + - : 8264 : foreach(lc, stmts)
+ - ]
1897 : : {
3261 1898 : 8264 : Query *stmt = lfirst_node(Query, lc);
1899 : :
3347 1900 [ + - ]: 8264 : if (stmt->canSetTag)
1901 : 8264 : return stmt;
1902 : : }
3347 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
6942 tgl@sss.pgh.pa.us 1911 :CBC 494780 : AcquireExecutorLocks(List *stmt_list, bool acquire)
1912 : : {
1913 : : ListCell *lc1;
1914 : :
1915 [ + - + + : 989560 : foreach(lc1, stmt_list)
+ + ]
1916 : : {
3261 1917 : 494780 : PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc1);
1918 : : ListCell *lc2;
1919 : :
3347 1920 [ + + ]: 494780 : if (plannedstmt->commandType == CMD_UTILITY)
5903 1921 : 10427 : {
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 : : */
3347 1929 : 10427 : Query *query = UtilityContainsQuery(plannedstmt->utilityStmt);
1930 : :
5109 1931 [ + + ]: 10427 : if (query)
5903 1932 : 2 : ScanQueryForLocks(query, acquire);
1933 : 10427 : continue;
1934 : : }
1935 : :
297 amitlan@postgresql.o 1936 [ + - + + : 994349 : foreach(lc2, plannedstmt->rtable)
+ + ]
1937 : : {
1938 : 509996 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
1939 : :
1940 [ + + ]: 509996 : if (!(rte->rtekind == RTE_RELATION ||
1941 [ + + + + ]: 75442 : (rte->rtekind == RTE_SUBQUERY && OidIsValid(rte->relid))))
1942 : 73950 : 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 : : */
6942 tgl@sss.pgh.pa.us 1950 [ + - ]: 436046 : if (acquire)
2721 1951 : 436046 : LockRelationOid(rte->relid, rte->rellockmode);
1952 : : else
2721 tgl@sss.pgh.pa.us 1953 :UBC 0 : UnlockRelationOid(rte->relid, rte->rellockmode);
1954 : : }
1955 : : }
6942 tgl@sss.pgh.pa.us 1956 :CBC 494780 : }
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 : 529097 : AcquirePlannerLocks(List *stmt_list, bool acquire)
1968 : : {
1969 : : ListCell *lc;
1970 : :
1971 [ + - + + : 1058194 : foreach(lc, stmt_list)
+ + ]
1972 : : {
3261 1973 : 529097 : Query *query = lfirst_node(Query, lc);
1974 : :
5903 1975 [ + + ]: 529097 : if (query->commandType == CMD_UTILITY)
1976 : : {
1977 : : /* Ignore utility statements, unless they contain a Query */
5109 1978 : 5030 : query = UtilityContainsQuery(query->utilityStmt);
1979 [ + + ]: 5030 : if (query)
5903 1980 : 4900 : ScanQueryForLocks(query, acquire);
1981 : 5030 : continue;
1982 : : }
1983 : :
6396 1984 : 524067 : ScanQueryForLocks(query, acquire);
1985 : : }
6942 1986 : 529097 : }
1987 : :
1988 : : /*
1989 : : * ScanQueryForLocks: recursively scan one Query for AcquirePlannerLocks.
1990 : : */
1991 : : static void
6396 1992 : 544047 : ScanQueryForLocks(Query *parsetree, bool acquire)
1993 : : {
1994 : : ListCell *lc;
1995 : :
1996 : : /* Shouldn't get called on utility commands */
5903 1997 [ - + ]: 544047 : Assert(parsetree->commandType != CMD_UTILITY);
1998 : :
1999 : : /*
2000 : : * First, process RTEs of the current query level.
2001 : : */
6942 2002 [ + + + + : 1048900 : foreach(lc, parsetree->rtable)
+ + ]
2003 : : {
2004 : 504853 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
2005 : :
2006 [ + + + ]: 504853 : switch (rte->rtekind)
2007 : : {
2008 : 470405 : case RTE_RELATION:
2009 : : /* Acquire or release the appropriate type of lock */
6396 2010 [ + + ]: 470405 : if (acquire)
2721 2011 : 470402 : LockRelationOid(rte->relid, rte->rellockmode);
2012 : : else
2013 : 3 : UnlockRelationOid(rte->relid, rte->rellockmode);
6942 2014 : 470405 : break;
2015 : :
2016 : 11375 : case RTE_SUBQUERY:
2017 : : /* If this was a view, must lock/unlock the view */
1075 2018 [ + + ]: 11375 : if (OidIsValid(rte->relid))
2019 : : {
2020 [ + - ]: 2553 : if (acquire)
2021 : 2553 : LockRelationOid(rte->relid, rte->rellockmode);
2022 : : else
1075 tgl@sss.pgh.pa.us 2023 :UBC 0 : UnlockRelationOid(rte->relid, rte->rellockmode);
2024 : : }
2025 : : /* Recurse into subquery-in-FROM */
6396 tgl@sss.pgh.pa.us 2026 :CBC 11375 : ScanQueryForLocks(rte->subquery, acquire);
6942 2027 : 11375 : break;
2028 : :
2029 : 23073 : default:
2030 : : /* ignore other types of RTEs */
2031 : 23073 : break;
2032 : : }
2033 : : }
2034 : :
2035 : : /* Recurse into subquery-in-WITH */
6371 2036 [ + + + + : 544131 : foreach(lc, parsetree->cteList)
+ + ]
2037 : : {
3261 2038 : 84 : CommonTableExpr *cte = lfirst_node(CommonTableExpr, lc);
2039 : :
3335 2040 : 84 : ScanQueryForLocks(castNode(Query, cte->ctequery), acquire);
2041 : : }
2042 : :
2043 : : /*
2044 : : * Recurse into sublink subqueries, too. But we already did the ones in
2045 : : * the rtable and cteList.
2046 : : */
6942 2047 [ + + ]: 544047 : if (parsetree->hasSubLinks)
2048 : : {
472 peter@eisentraut.org 2049 : 3568 : query_tree_walker(parsetree, ScanQueryWalker, &acquire,
2050 : : QTW_IGNORE_RC_SUBQUERIES);
2051 : : }
6942 tgl@sss.pgh.pa.us 2052 : 544047 : }
2053 : :
2054 : : /*
2055 : : * Walker to find sublink subqueries for ScanQueryForLocks
2056 : : */
2057 : : static bool
6396 2058 : 188704 : ScanQueryWalker(Node *node, bool *acquire)
2059 : : {
6942 2060 [ + + ]: 188704 : if (node == NULL)
2061 : 46693 : return false;
2062 [ + + ]: 142011 : if (IsA(node, SubLink))
2063 : : {
2064 : 3619 : SubLink *sub = (SubLink *) node;
2065 : :
2066 : : /* Do what we came for */
3335 2067 : 3619 : ScanQueryForLocks(castNode(Query, sub->subselect), *acquire);
2068 : : /* Fall through to process lefthand args of SubLink */
2069 : : }
2070 : :
2071 : : /*
2072 : : * Do NOT recurse into Query nodes, because ScanQueryForLocks already
2073 : : * processed subselects of subselects for us.
2074 : : */
472 peter@eisentraut.org 2075 : 142011 : return expression_tree_walker(node, ScanQueryWalker, acquire);
2076 : : }
2077 : :
2078 : : /*
2079 : : * PlanCacheComputeResultDesc: given a list of analyzed-and-rewritten Queries,
2080 : : * determine the result tupledesc it will produce. Returns NULL if the
2081 : : * execution will not return tuples.
2082 : : *
2083 : : * Note: the result is created or copied into current memory context.
2084 : : */
2085 : : static TupleDesc
6940 tgl@sss.pgh.pa.us 2086 : 42469 : PlanCacheComputeResultDesc(List *stmt_list)
2087 : : {
2088 : : Query *query;
2089 : :
6942 2090 [ + + + + : 42469 : switch (ChoosePortalStrategy(stmt_list))
- ]
2091 : : {
2092 : 29522 : case PORTAL_ONE_SELECT:
2093 : : case PORTAL_ONE_MOD_WITH:
3261 2094 : 29522 : query = linitial_node(Query, stmt_list);
2672 andres@anarazel.de 2095 : 29522 : return ExecCleanTypeFromTL(query->targetList);
2096 : :
6942 tgl@sss.pgh.pa.us 2097 : 158 : case PORTAL_ONE_RETURNING:
3347 2098 : 158 : query = QueryListGetPrimaryStmt(stmt_list);
5294 2099 [ - + ]: 158 : Assert(query->returningList);
2672 andres@anarazel.de 2100 : 158 : return ExecCleanTypeFromTL(query->returningList);
2101 : :
6942 tgl@sss.pgh.pa.us 2102 : 5038 : case PORTAL_UTIL_SELECT:
3261 2103 : 5038 : query = linitial_node(Query, stmt_list);
5294 2104 [ - + ]: 5038 : Assert(query->utilityStmt);
2105 : 5038 : return UtilityTupleDescriptor(query->utilityStmt);
2106 : :
6942 2107 : 7751 : case PORTAL_MULTI_QUERY:
2108 : : /* will not return tuples */
2109 : 7751 : break;
2110 : : }
2111 : 7751 : return NULL;
2112 : : }
2113 : :
2114 : : /*
2115 : : * PlanCacheRelCallback
2116 : : * Relcache inval callback function
2117 : : *
2118 : : * Invalidate all plans mentioning the given rel, or all plans mentioning
2119 : : * any rel at all if relid == InvalidOid.
2120 : : */
2121 : : static void
6396 2122 : 1680174 : PlanCacheRelCallback(Datum arg, Oid relid)
2123 : : {
2124 : : dlist_iter iter;
2125 : :
2649 2126 [ + - + + ]: 33324618 : dlist_foreach(iter, &saved_plan_list)
2127 : : {
2128 : 31644444 : CachedPlanSource *plansource = dlist_container(CachedPlanSource,
2129 : : node, iter.cur);
2130 : :
5294 2131 [ - + ]: 31644444 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
2132 : :
2133 : : /* No work if it's already invalidated */
2134 [ + + ]: 31644444 : if (!plansource->is_valid)
6942 2135 : 21527351 : continue;
2136 : :
2137 : : /* Never invalidate if parse/plan would be a no-op anyway */
934 2138 [ + + ]: 10117093 : if (!StmtPlanRequiresRevalidation(plansource))
4712 2139 : 190603 : continue;
2140 : :
2141 : : /*
2142 : : * Check the dependency list for the rewritten querytree.
2143 : : */
5294 2144 [ + + + + ]: 19852928 : if ((relid == InvalidOid) ? plansource->relationOids != NIL :
2145 : 9926438 : list_member_oid(plansource->relationOids, relid))
2146 : : {
2147 : : /* Invalidate the querytree and generic plan */
2148 : 1907 : plansource->is_valid = false;
2149 [ + + ]: 1907 : if (plansource->gplan)
2150 : 615 : plansource->gplan->is_valid = false;
2151 : : }
2152 : :
2153 : : /*
2154 : : * The generic plan, if any, could have more dependencies than the
2155 : : * querytree does, so we have to check it too.
2156 : : */
2157 [ + + + + ]: 9926490 : if (plansource->gplan && plansource->gplan->is_valid)
2158 : : {
2159 : : ListCell *lc;
2160 : :
2161 [ + - + + : 18678887 : foreach(lc, plansource->gplan->stmt_list)
+ + ]
2162 : : {
3261 2163 : 9339463 : PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
2164 : :
3347 2165 [ + + ]: 9339463 : if (plannedstmt->commandType == CMD_UTILITY)
6695 bruce@momjian.us 2166 : 2348 : continue; /* Ignore utility statements */
6730 tgl@sss.pgh.pa.us 2167 [ - + + + ]: 18674230 : if ((relid == InvalidOid) ? plannedstmt->relationOids != NIL :
1817 akapila@postgresql.o 2168 : 9337115 : list_member_oid(plannedstmt->relationOids, relid))
2169 : : {
2170 : : /* Invalidate the generic plan only */
5294 tgl@sss.pgh.pa.us 2171 : 39 : plansource->gplan->is_valid = false;
6695 bruce@momjian.us 2172 : 39 : break; /* out of stmt_list scan */
2173 : : }
2174 : : }
2175 : : }
2176 : : }
2177 : :
2178 : : /* Likewise check cached expressions */
2649 tgl@sss.pgh.pa.us 2179 [ + - + + ]: 1856202 : dlist_foreach(iter, &cached_expression_list)
2180 : : {
2181 : 176028 : CachedExpression *cexpr = dlist_container(CachedExpression,
2182 : : node, iter.cur);
2183 : :
2184 [ - + ]: 176028 : Assert(cexpr->magic == CACHEDEXPR_MAGIC);
2185 : :
2186 : : /* No work if it's already invalidated */
2187 [ + + ]: 176028 : if (!cexpr->is_valid)
2188 : 79040 : continue;
2189 : :
2190 [ - + - + ]: 193976 : if ((relid == InvalidOid) ? cexpr->relationOids != NIL :
2191 : 96988 : list_member_oid(cexpr->relationOids, relid))
2192 : : {
2649 tgl@sss.pgh.pa.us 2193 :UBC 0 : cexpr->is_valid = false;
2194 : : }
2195 : : }
6396 tgl@sss.pgh.pa.us 2196 :CBC 1680174 : }
2197 : :
2198 : : /*
2199 : : * PlanCacheObjectCallback
2200 : : * Syscache inval callback function for PROCOID and TYPEOID caches
2201 : : *
2202 : : * Invalidate all plans mentioning the object with the specified hash value,
2203 : : * or all plans mentioning any member of this cache if hashvalue == 0.
2204 : : */
2205 : : static void
25 michael@paquier.xyz 2206 :GNC 643936 : PlanCacheObjectCallback(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
2207 : : {
2208 : : dlist_iter iter;
2209 : :
2649 tgl@sss.pgh.pa.us 2210 [ + - + + ]:CBC 12621420 : dlist_foreach(iter, &saved_plan_list)
2211 : : {
2212 : 11977484 : CachedPlanSource *plansource = dlist_container(CachedPlanSource,
2213 : : node, iter.cur);
2214 : : ListCell *lc;
2215 : :
5294 2216 [ - + ]: 11977484 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
2217 : :
2218 : : /* No work if it's already invalidated */
2219 [ + + ]: 11977484 : if (!plansource->is_valid)
6396 2220 : 7920308 : continue;
2221 : :
2222 : : /* Never invalidate if parse/plan would be a no-op anyway */
934 2223 [ + + ]: 4057176 : if (!StmtPlanRequiresRevalidation(plansource))
4712 2224 : 50366 : continue;
2225 : :
2226 : : /*
2227 : : * Check the dependency list for the rewritten querytree.
2228 : : */
5294 2229 [ + + + + : 4066070 : foreach(lc, plansource->invalItems)
+ + ]
2230 : : {
2231 : 59353 : PlanInvalItem *item = (PlanInvalItem *) lfirst(lc);
2232 : :
5903 2233 [ + + ]: 59353 : if (item->cacheId != cacheid)
2234 : 42778 : continue;
5325 2235 [ + - ]: 16575 : if (hashvalue == 0 ||
2236 [ + + ]: 16575 : item->hashValue == hashvalue)
2237 : : {
2238 : : /* Invalidate the querytree and generic plan */
5294 2239 : 93 : plansource->is_valid = false;
2240 [ + + ]: 93 : if (plansource->gplan)
2241 : 83 : plansource->gplan->is_valid = false;
5903 2242 : 93 : break;
2243 : : }
2244 : : }
2245 : :
2246 : : /*
2247 : : * The generic plan, if any, could have more dependencies than the
2248 : : * querytree does, so we have to check it too.
2249 : : */
5294 2250 [ + + + + ]: 4006810 : if (plansource->gplan && plansource->gplan->is_valid)
2251 : : {
2252 [ + - + + : 7510710 : foreach(lc, plansource->gplan->stmt_list)
+ + ]
2253 : : {
3261 2254 : 3755361 : PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
2255 : : ListCell *lc3;
2256 : :
3347 2257 [ + + ]: 3755361 : if (plannedstmt->commandType == CMD_UTILITY)
6396 2258 : 1335 : continue; /* Ignore utility statements */
2259 [ + + + + : 3809366 : foreach(lc3, plannedstmt->invalItems)
+ + ]
2260 : : {
2261 : 55352 : PlanInvalItem *item = (PlanInvalItem *) lfirst(lc3);
2262 : :
2263 [ + + ]: 55352 : if (item->cacheId != cacheid)
2264 : 39831 : continue;
5325 2265 [ + - ]: 15521 : if (hashvalue == 0 ||
2266 [ + + ]: 15521 : item->hashValue == hashvalue)
2267 : : {
2268 : : /* Invalidate the generic plan only */
5294 2269 : 12 : plansource->gplan->is_valid = false;
6121 bruce@momjian.us 2270 : 12 : break; /* out of invalItems scan */
2271 : : }
2272 : : }
5294 tgl@sss.pgh.pa.us 2273 [ + + ]: 3754026 : if (!plansource->gplan->is_valid)
6396 2274 : 12 : break; /* out of stmt_list scan */
2275 : : }
2276 : : }
2277 : : }
2278 : :
2279 : : /* Likewise check cached expressions */
2649 2280 [ + - + + ]: 717276 : dlist_foreach(iter, &cached_expression_list)
2281 : : {
2282 : 73340 : CachedExpression *cexpr = dlist_container(CachedExpression,
2283 : : node, iter.cur);
2284 : : ListCell *lc;
2285 : :
2286 [ - + ]: 73340 : Assert(cexpr->magic == CACHEDEXPR_MAGIC);
2287 : :
2288 : : /* No work if it's already invalidated */
2289 [ + + ]: 73340 : if (!cexpr->is_valid)
2290 : 30127 : continue;
2291 : :
2292 [ + + + + : 43225 : foreach(lc, cexpr->invalItems)
+ + ]
2293 : : {
2294 : 15 : PlanInvalItem *item = (PlanInvalItem *) lfirst(lc);
2295 : :
2296 [ + + ]: 15 : if (item->cacheId != cacheid)
2297 : 6 : continue;
2298 [ + - ]: 9 : if (hashvalue == 0 ||
2299 [ + + ]: 9 : item->hashValue == hashvalue)
2300 : : {
2301 : 3 : cexpr->is_valid = false;
2302 : 3 : break;
2303 : : }
2304 : : }
2305 : : }
6942 2306 : 643936 : }
2307 : :
2308 : : /*
2309 : : * PlanCacheSysCallback
2310 : : * Syscache inval callback function for other caches
2311 : : *
2312 : : * Just invalidate everything...
2313 : : */
2314 : : static void
25 michael@paquier.xyz 2315 :GNC 40779 : PlanCacheSysCallback(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
2316 : : {
6396 tgl@sss.pgh.pa.us 2317 :CBC 40779 : ResetPlanCache();
6912 neilc@samurai.com 2318 : 40779 : }
2319 : :
2320 : : /*
2321 : : * ResetPlanCache: invalidate all cached plans.
2322 : : */
2323 : : void
6396 tgl@sss.pgh.pa.us 2324 : 41382 : ResetPlanCache(void)
2325 : : {
2326 : : dlist_iter iter;
2327 : :
2649 2328 [ + - + + ]: 165787 : dlist_foreach(iter, &saved_plan_list)
2329 : : {
2330 : 124405 : CachedPlanSource *plansource = dlist_container(CachedPlanSource,
2331 : : node, iter.cur);
2332 : :
5294 2333 [ - + ]: 124405 : Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
2334 : :
2335 : : /* No work if it's already invalidated */
2336 [ + + ]: 124405 : if (!plansource->is_valid)
5905 2337 : 113477 : continue;
2338 : :
2339 : : /*
2340 : : * We *must not* mark transaction control statements as invalid,
2341 : : * particularly not ROLLBACK, because they may need to be executed in
2342 : : * aborted transactions when we can't revalidate them (cf bug #5269).
2343 : : * In general there's no point in invalidating statements for which a
2344 : : * new parse analysis/rewrite/plan cycle would certainly give the same
2345 : : * results.
2346 : : */
934 2347 [ + + ]: 10928 : if (!StmtPlanRequiresRevalidation(plansource))
4712 2348 : 2558 : continue;
2349 : :
934 2350 : 8370 : plansource->is_valid = false;
2351 [ + + ]: 8370 : if (plansource->gplan)
2352 : 7541 : plansource->gplan->is_valid = false;
2353 : : }
2354 : :
2355 : : /* Likewise invalidate cached expressions */
2649 2356 [ + - + + ]: 42034 : dlist_foreach(iter, &cached_expression_list)
2357 : : {
2358 : 652 : CachedExpression *cexpr = dlist_container(CachedExpression,
2359 : : node, iter.cur);
2360 : :
2361 [ - + ]: 652 : Assert(cexpr->magic == CACHEDEXPR_MAGIC);
2362 : :
2363 : 652 : cexpr->is_valid = false;
2364 : : }
6942 2365 : 41382 : }
2366 : :
2367 : : /*
2368 : : * Release all CachedPlans remembered by 'owner'
2369 : : */
2370 : : void
858 heikki.linnakangas@i 2371 : 8553 : ReleaseAllPlanCacheRefsInOwner(ResourceOwner owner)
2372 : : {
2373 : 8553 : ResourceOwnerReleaseAllOfKind(owner, &planref_resowner_desc);
2374 : 8553 : }
2375 : :
2376 : : /* ResourceOwner callbacks */
2377 : :
2378 : : static void
2379 : 47114 : ResOwnerReleaseCachedPlan(Datum res)
2380 : : {
2381 : 47114 : ReleaseCachedPlan((CachedPlan *) DatumGetPointer(res), NULL);
2382 : 47114 : }
|