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