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