PostgreSQL Source Code git master
plancache.c File Reference
#include "postgres.h"
#include <limits.h>
#include "access/transam.h"
#include "catalog/namespace.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "parser/analyze.h"
#include "rewrite/rewriteHandler.h"
#include "storage/lmgr.h"
#include "tcop/pquery.h"
#include "tcop/utility.h"
#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/resowner.h"
#include "utils/rls.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
Include dependency graph for plancache.c:

Go to the source code of this file.

Functions

static void ReleaseGenericPlan (CachedPlanSource *plansource)
 
static bool StmtPlanRequiresRevalidation (CachedPlanSource *plansource)
 
static bool BuildingPlanRequiresSnapshot (CachedPlanSource *plansource)
 
static ListRevalidateCachedQuery (CachedPlanSource *plansource, QueryEnvironment *queryEnv)
 
static bool CheckCachedPlan (CachedPlanSource *plansource)
 
static CachedPlanBuildCachedPlan (CachedPlanSource *plansource, List *qlist, ParamListInfo boundParams, QueryEnvironment *queryEnv)
 
static bool choose_custom_plan (CachedPlanSource *plansource, ParamListInfo boundParams)
 
static double cached_plan_cost (CachedPlan *plan, bool include_planner)
 
static QueryQueryListGetPrimaryStmt (List *stmts)
 
static void AcquireExecutorLocks (List *stmt_list, bool acquire)
 
static void AcquirePlannerLocks (List *stmt_list, bool acquire)
 
static void ScanQueryForLocks (Query *parsetree, bool acquire)
 
static bool ScanQueryWalker (Node *node, bool *acquire)
 
static TupleDesc PlanCacheComputeResultDesc (List *stmt_list)
 
static void PlanCacheRelCallback (Datum arg, Oid relid)
 
static void PlanCacheObjectCallback (Datum arg, int cacheid, uint32 hashvalue)
 
static void PlanCacheSysCallback (Datum arg, int cacheid, uint32 hashvalue)
 
static void ResOwnerReleaseCachedPlan (Datum res)
 
static void ResourceOwnerRememberPlanCacheRef (ResourceOwner owner, CachedPlan *plan)
 
static void ResourceOwnerForgetPlanCacheRef (ResourceOwner owner, CachedPlan *plan)
 
void InitPlanCache (void)
 
CachedPlanSourceCreateCachedPlan (RawStmt *raw_parse_tree, const char *query_string, CommandTag commandTag)
 
CachedPlanSourceCreateCachedPlanForQuery (Query *analyzed_parse_tree, const char *query_string, CommandTag commandTag)
 
CachedPlanSourceCreateOneShotCachedPlan (RawStmt *raw_parse_tree, const char *query_string, CommandTag commandTag)
 
void CompleteCachedPlan (CachedPlanSource *plansource, List *querytree_list, MemoryContext querytree_context, Oid *param_types, int num_params, ParserSetupHook parserSetup, void *parserSetupArg, int cursor_options, bool fixed_result)
 
void SetPostRewriteHook (CachedPlanSource *plansource, PostRewriteHook postRewrite, void *postRewriteArg)
 
void SaveCachedPlan (CachedPlanSource *plansource)
 
void DropCachedPlan (CachedPlanSource *plansource)
 
CachedPlanGetCachedPlan (CachedPlanSource *plansource, ParamListInfo boundParams, ResourceOwner owner, QueryEnvironment *queryEnv)
 
void ReleaseCachedPlan (CachedPlan *plan, ResourceOwner owner)
 
bool CachedPlanAllowsSimpleValidityCheck (CachedPlanSource *plansource, CachedPlan *plan, ResourceOwner owner)
 
bool CachedPlanIsSimplyValid (CachedPlanSource *plansource, CachedPlan *plan, ResourceOwner owner)
 
void CachedPlanSetParentContext (CachedPlanSource *plansource, MemoryContext newcontext)
 
CachedPlanSourceCopyCachedPlan (CachedPlanSource *plansource)
 
bool CachedPlanIsValid (CachedPlanSource *plansource)
 
ListCachedPlanGetTargetList (CachedPlanSource *plansource, QueryEnvironment *queryEnv)
 
CachedExpressionGetCachedExpression (Node *expr)
 
void FreeCachedExpression (CachedExpression *cexpr)
 
void ResetPlanCache (void)
 
void ReleaseAllPlanCacheRefsInOwner (ResourceOwner owner)
 

Variables

static dlist_head saved_plan_list = DLIST_STATIC_INIT(saved_plan_list)
 
static dlist_head cached_expression_list = DLIST_STATIC_INIT(cached_expression_list)
 
static const ResourceOwnerDesc planref_resowner_desc
 
int plan_cache_mode = PLAN_CACHE_MODE_AUTO
 

Function Documentation

◆ AcquireExecutorLocks()

static void AcquireExecutorLocks ( List stmt_list,
bool  acquire 
)
static

Definition at line 1909 of file plancache.c.

1910{
1911 ListCell *lc1;
1912
1913 foreach(lc1, stmt_list)
1914 {
1915 PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc1);
1916 ListCell *lc2;
1917
1918 if (plannedstmt->commandType == CMD_UTILITY)
1919 {
1920 /*
1921 * Ignore utility statements, except those (such as EXPLAIN) that
1922 * contain a parsed-but-not-planned query. Note: it's okay to use
1923 * ScanQueryForLocks, even though the query hasn't been through
1924 * rule rewriting, because rewriting doesn't change the query
1925 * representation.
1926 */
1927 Query *query = UtilityContainsQuery(plannedstmt->utilityStmt);
1928
1929 if (query)
1930 ScanQueryForLocks(query, acquire);
1931 continue;
1932 }
1933
1934 foreach(lc2, plannedstmt->rtable)
1935 {
1936 RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
1937
1938 if (!(rte->rtekind == RTE_RELATION ||
1939 (rte->rtekind == RTE_SUBQUERY && OidIsValid(rte->relid))))
1940 continue;
1941
1942 /*
1943 * Acquire the appropriate type of lock on each relation OID. Note
1944 * that we don't actually try to open the rel, and hence will not
1945 * fail if it's been dropped entirely --- we'll just transiently
1946 * acquire a non-conflicting lock.
1947 */
1948 if (acquire)
1949 LockRelationOid(rte->relid, rte->rellockmode);
1950 else
1951 UnlockRelationOid(rte->relid, rte->rellockmode);
1952 }
1953 }
1954}
#define OidIsValid(objectId)
Definition: c.h:788
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:229
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:107
@ CMD_UTILITY
Definition: nodes.h:280
@ RTE_SUBQUERY
Definition: parsenodes.h:1044
@ RTE_RELATION
Definition: parsenodes.h:1043
#define lfirst(lc)
Definition: pg_list.h:172
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static void ScanQueryForLocks(Query *parsetree, bool acquire)
Definition: plancache.c:1990
CmdType commandType
Definition: plannodes.h:68
Node * utilityStmt
Definition: plannodes.h:150
List * rtable
Definition: plannodes.h:109
RTEKind rtekind
Definition: parsenodes.h:1078
Query * UtilityContainsQuery(Node *parsetree)
Definition: utility.c:2185

References CMD_UTILITY, PlannedStmt::commandType, lfirst, lfirst_node, LockRelationOid(), OidIsValid, PlannedStmt::rtable, RTE_RELATION, RTE_SUBQUERY, RangeTblEntry::rtekind, ScanQueryForLocks(), UnlockRelationOid(), UtilityContainsQuery(), and PlannedStmt::utilityStmt.

Referenced by CheckCachedPlan().

◆ AcquirePlannerLocks()

static void AcquirePlannerLocks ( List stmt_list,
bool  acquire 
)
static

Definition at line 1965 of file plancache.c.

1966{
1967 ListCell *lc;
1968
1969 foreach(lc, stmt_list)
1970 {
1971 Query *query = lfirst_node(Query, lc);
1972
1973 if (query->commandType == CMD_UTILITY)
1974 {
1975 /* Ignore utility statements, unless they contain a Query */
1976 query = UtilityContainsQuery(query->utilityStmt);
1977 if (query)
1978 ScanQueryForLocks(query, acquire);
1979 continue;
1980 }
1981
1982 ScanQueryForLocks(query, acquire);
1983 }
1984}
CmdType commandType
Definition: parsenodes.h:121
Node * utilityStmt
Definition: parsenodes.h:141

References CMD_UTILITY, Query::commandType, lfirst_node, ScanQueryForLocks(), UtilityContainsQuery(), and Query::utilityStmt.

Referenced by RevalidateCachedQuery().

◆ BuildCachedPlan()

static CachedPlan * BuildCachedPlan ( CachedPlanSource plansource,
List qlist,
ParamListInfo  boundParams,
QueryEnvironment queryEnv 
)
static

Definition at line 1034 of file plancache.c.

1036{
1038 List *plist;
1039 bool snapshot_set;
1040 bool is_transient;
1041 MemoryContext plan_context;
1043 ListCell *lc;
1044
1045 /*
1046 * Normally the querytree should be valid already, but if it's not,
1047 * rebuild it.
1048 *
1049 * NOTE: GetCachedPlan should have called RevalidateCachedQuery first, so
1050 * we ought to be holding sufficient locks to prevent any invalidation.
1051 * However, if we're building a custom plan after having built and
1052 * rejected a generic plan, it's possible to reach here with is_valid
1053 * false due to an invalidation while making the generic plan. In theory
1054 * the invalidation must be a false positive, perhaps a consequence of an
1055 * sinval reset event or the debug_discard_caches code. But for safety,
1056 * let's treat it as real and redo the RevalidateCachedQuery call.
1057 */
1058 if (!plansource->is_valid)
1059 qlist = RevalidateCachedQuery(plansource, queryEnv);
1060
1061 /*
1062 * If we don't already have a copy of the querytree list that can be
1063 * scribbled on by the planner, make one. For a one-shot plan, we assume
1064 * it's okay to scribble on the original query_list.
1065 */
1066 if (qlist == NIL)
1067 {
1068 if (!plansource->is_oneshot)
1069 qlist = copyObject(plansource->query_list);
1070 else
1071 qlist = plansource->query_list;
1072 }
1073
1074 /*
1075 * If a snapshot is already set (the normal case), we can just use that
1076 * for planning. But if it isn't, and we need one, install one.
1077 */
1078 snapshot_set = false;
1079 if (!ActiveSnapshotSet() &&
1080 BuildingPlanRequiresSnapshot(plansource))
1081 {
1083 snapshot_set = true;
1084 }
1085
1086 /*
1087 * Generate the plan.
1088 */
1089 plist = pg_plan_queries(qlist, plansource->query_string,
1090 plansource->cursor_options, boundParams);
1091
1092 /* Release snapshot if we got one */
1093 if (snapshot_set)
1095
1096 /*
1097 * Normally we make a dedicated memory context for the CachedPlan and its
1098 * subsidiary data. (It's probably not going to be large, but just in
1099 * case, allow it to grow large. It's transient for the moment.) But for
1100 * a one-shot plan, we just leave it in the caller's memory context.
1101 */
1102 if (!plansource->is_oneshot)
1103 {
1105 "CachedPlan",
1107 MemoryContextCopyAndSetIdentifier(plan_context, plansource->query_string);
1108
1109 /*
1110 * Copy plan into the new context.
1111 */
1112 MemoryContextSwitchTo(plan_context);
1113
1114 plist = copyObject(plist);
1115 }
1116 else
1117 plan_context = CurrentMemoryContext;
1118
1119 /*
1120 * Create and fill the CachedPlan struct within the new context.
1121 */
1123 plan->magic = CACHEDPLAN_MAGIC;
1124 plan->stmt_list = plist;
1125
1126 /*
1127 * CachedPlan is dependent on role either if RLS affected the rewrite
1128 * phase or if a role dependency was injected during planning. And it's
1129 * transient if any plan is marked so.
1130 */
1131 plan->planRoleId = GetUserId();
1132 plan->dependsOnRole = plansource->dependsOnRLS;
1133 is_transient = false;
1134 foreach(lc, plist)
1135 {
1136 PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
1137
1138 if (plannedstmt->commandType == CMD_UTILITY)
1139 continue; /* Ignore utility statements */
1140
1141 if (plannedstmt->transientPlan)
1142 is_transient = true;
1143 if (plannedstmt->dependsOnRole)
1144 plan->dependsOnRole = true;
1145 }
1146 if (is_transient)
1147 {
1149 plan->saved_xmin = TransactionXmin;
1150 }
1151 else
1152 plan->saved_xmin = InvalidTransactionId;
1153 plan->refcount = 0;
1154 plan->context = plan_context;
1155 plan->is_oneshot = plansource->is_oneshot;
1156 plan->is_saved = false;
1157 plan->is_valid = true;
1158
1159 /* assign generation number to new plan */
1160 plan->generation = ++(plansource->generation);
1161
1162 MemoryContextSwitchTo(oldcxt);
1163
1164 return plan;
1165}
#define palloc_object(type)
Definition: fe_memutils.h:74
Assert(PointerIsAligned(start, uint64))
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_START_SMALL_SIZES
Definition: memutils.h:177
#define MemoryContextCopyAndSetIdentifier(cxt, id)
Definition: memutils.h:101
Oid GetUserId(void)
Definition: miscinit.c:469
#define copyObject(obj)
Definition: nodes.h:232
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
#define NIL
Definition: pg_list.h:68
#define plan(x)
Definition: pg_regress.c:161
static List * RevalidateCachedQuery(CachedPlanSource *plansource, QueryEnvironment *queryEnv)
Definition: plancache.c:682
static bool BuildingPlanRequiresSnapshot(CachedPlanSource *plansource)
Definition: plancache.c:657
#define CACHEDPLAN_MAGIC
Definition: plancache.h:45
List * pg_plan_queries(List *querytrees, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition: postgres.c:975
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:272
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:682
TransactionId TransactionXmin
Definition: snapmgr.c:159
bool ActiveSnapshotSet(void)
Definition: snapmgr.c:812
void PopActiveSnapshot(void)
Definition: snapmgr.c:775
const char * query_string
Definition: plancache.h:110
List * query_list
Definition: plancache.h:123
Definition: pg_list.h:54
bool transientPlan
Definition: plannodes.h:89
bool dependsOnRole
Definition: plannodes.h:92
#define InvalidTransactionId
Definition: transam.h:31
#define TransactionIdIsNormal(xid)
Definition: transam.h:42

References ActiveSnapshotSet(), ALLOCSET_START_SMALL_SIZES, AllocSetContextCreate, Assert(), BuildingPlanRequiresSnapshot(), CACHEDPLAN_MAGIC, CMD_UTILITY, PlannedStmt::commandType, copyObject, CurrentMemoryContext, CachedPlanSource::cursor_options, CachedPlanSource::dependsOnRLS, PlannedStmt::dependsOnRole, CachedPlanSource::generation, GetTransactionSnapshot(), GetUserId(), InvalidTransactionId, CachedPlanSource::is_oneshot, CachedPlanSource::is_valid, lfirst_node, MemoryContextCopyAndSetIdentifier, MemoryContextSwitchTo(), NIL, palloc_object, pg_plan_queries(), plan, PopActiveSnapshot(), PushActiveSnapshot(), CachedPlanSource::query_list, CachedPlanSource::query_string, RevalidateCachedQuery(), TransactionIdIsNormal, TransactionXmin, and PlannedStmt::transientPlan.

Referenced by GetCachedPlan().

◆ BuildingPlanRequiresSnapshot()

static bool BuildingPlanRequiresSnapshot ( CachedPlanSource plansource)
static

Definition at line 657 of file plancache.c.

658{
659 if (plansource->raw_parse_tree != NULL)
660 return analyze_requires_snapshot(plansource->raw_parse_tree);
661 else if (plansource->analyzed_parse_tree != NULL)
663 /* empty query never needs a snapshot */
664 return false;
665}
bool analyze_requires_snapshot(RawStmt *parseTree)
Definition: analyze.c:503
bool query_requires_rewrite_plan(Query *query)
Definition: analyze.c:532
Query * analyzed_parse_tree
Definition: plancache.h:109
RawStmt * raw_parse_tree
Definition: plancache.h:108

References analyze_requires_snapshot(), CachedPlanSource::analyzed_parse_tree, query_requires_rewrite_plan(), and CachedPlanSource::raw_parse_tree.

Referenced by BuildCachedPlan().

◆ cached_plan_cost()

static double cached_plan_cost ( CachedPlan plan,
bool  include_planner 
)
static

Definition at line 1230 of file plancache.c.

1231{
1232 double result = 0;
1233 ListCell *lc;
1234
1235 foreach(lc, plan->stmt_list)
1236 {
1237 PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
1238
1239 if (plannedstmt->commandType == CMD_UTILITY)
1240 continue; /* Ignore utility statements */
1241
1242 result += plannedstmt->planTree->total_cost;
1243
1244 if (include_planner)
1245 {
1246 /*
1247 * Currently we use a very crude estimate of planning effort based
1248 * on the number of relations in the finished plan's rangetable.
1249 * Join planning effort actually scales much worse than linearly
1250 * in the number of relations --- but only until the join collapse
1251 * limits kick in. Also, while inheritance child relations surely
1252 * add to planning effort, they don't make the join situation
1253 * worse. So the actual shape of the planning cost curve versus
1254 * number of relations isn't all that obvious. It will take
1255 * considerable work to arrive at a less crude estimate, and for
1256 * now it's not clear that's worth doing.
1257 *
1258 * The other big difficulty here is that we don't have any very
1259 * good model of how planning cost compares to execution costs.
1260 * The current multiplier of 1000 * cpu_operator_cost is probably
1261 * on the low side, but we'll try this for awhile before making a
1262 * more aggressive correction.
1263 *
1264 * If we ever do write a more complicated estimator, it should
1265 * probably live in src/backend/optimizer/ not here.
1266 */
1267 int nrelations = list_length(plannedstmt->rtable);
1268
1269 result += 1000.0 * cpu_operator_cost * (nrelations + 1);
1270 }
1271 }
1272
1273 return result;
1274}
double cpu_operator_cost
Definition: costsize.c:134
static int list_length(const List *l)
Definition: pg_list.h:152
Cost total_cost
Definition: plannodes.h:199
struct Plan * planTree
Definition: plannodes.h:101

References CMD_UTILITY, PlannedStmt::commandType, cpu_operator_cost, lfirst_node, list_length(), plan, PlannedStmt::planTree, PlannedStmt::rtable, and Plan::total_cost.

Referenced by GetCachedPlan().

◆ CachedPlanAllowsSimpleValidityCheck()

bool CachedPlanAllowsSimpleValidityCheck ( CachedPlanSource plansource,
CachedPlan plan,
ResourceOwner  owner 
)

Definition at line 1471 of file plancache.c.

1473{
1474 ListCell *lc;
1475
1476 /*
1477 * Sanity-check that the caller gave us a validated generic plan. Notice
1478 * that we *don't* assert plansource->is_valid as you might expect; that's
1479 * because it's possible that that's already false when GetCachedPlan
1480 * returns, e.g. because ResetPlanCache happened partway through. We
1481 * should accept the plan as long as plan->is_valid is true, and expect to
1482 * replan after the next CachedPlanIsSimplyValid call.
1483 */
1484 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1485 Assert(plan->magic == CACHEDPLAN_MAGIC);
1486 Assert(plan->is_valid);
1487 Assert(plan == plansource->gplan);
1488 Assert(plansource->search_path != NULL);
1490
1491 /* We don't support oneshot plans here. */
1492 if (plansource->is_oneshot)
1493 return false;
1494 Assert(!plan->is_oneshot);
1495
1496 /*
1497 * If the plan is dependent on RLS considerations, or it's transient,
1498 * reject. These things probably can't ever happen for table-free
1499 * queries, but for safety's sake let's check.
1500 */
1501 if (plansource->dependsOnRLS)
1502 return false;
1503 if (plan->dependsOnRole)
1504 return false;
1505 if (TransactionIdIsValid(plan->saved_xmin))
1506 return false;
1507
1508 /*
1509 * Reject if AcquirePlannerLocks would have anything to do. This is
1510 * simplistic, but there's no need to inquire any more carefully; indeed,
1511 * for current callers it shouldn't even be possible to hit any of these
1512 * checks.
1513 */
1514 foreach(lc, plansource->query_list)
1515 {
1516 Query *query = lfirst_node(Query, lc);
1517
1518 if (query->commandType == CMD_UTILITY)
1519 return false;
1520 if (query->rtable || query->cteList || query->hasSubLinks)
1521 return false;
1522 }
1523
1524 /*
1525 * Reject if AcquireExecutorLocks would have anything to do. This is
1526 * probably unnecessary given the previous check, but let's be safe.
1527 */
1528 foreach(lc, plan->stmt_list)
1529 {
1530 PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
1531 ListCell *lc2;
1532
1533 if (plannedstmt->commandType == CMD_UTILITY)
1534 return false;
1535
1536 /*
1537 * We have to grovel through the rtable because it's likely to contain
1538 * an RTE_RESULT relation, rather than being totally empty.
1539 */
1540 foreach(lc2, plannedstmt->rtable)
1541 {
1542 RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
1543
1544 if (rte->rtekind == RTE_RELATION)
1545 return false;
1546 }
1547 }
1548
1549 /*
1550 * Okay, it's simple. Note that what we've primarily established here is
1551 * that no locks need be taken before checking the plan's is_valid flag.
1552 */
1553
1554 /* Bump refcount if requested. */
1555 if (owner)
1556 {
1557 ResourceOwnerEnlarge(owner);
1558 plan->refcount++;
1560 }
1561
1562 return true;
1563}
bool SearchPathMatchesCurrentEnvironment(SearchPathMatcher *path)
Definition: namespace.c:3981
static void ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
Definition: plancache.c:126
#define CACHEDPLANSOURCE_MAGIC
Definition: plancache.h:44
void ResourceOwnerEnlarge(ResourceOwner owner)
Definition: resowner.c:449
struct CachedPlan * gplan
Definition: plancache.h:133
struct SearchPathMatcher * search_path
Definition: plancache.h:126
List * cteList
Definition: parsenodes.h:173
List * rtable
Definition: parsenodes.h:175
#define TransactionIdIsValid(xid)
Definition: transam.h:41

References Assert(), CACHEDPLAN_MAGIC, CACHEDPLANSOURCE_MAGIC, CMD_UTILITY, Query::commandType, PlannedStmt::commandType, Query::cteList, CachedPlanSource::dependsOnRLS, CachedPlanSource::gplan, CachedPlanSource::is_oneshot, lfirst, lfirst_node, CachedPlanSource::magic, plan, CachedPlanSource::query_list, ResourceOwnerEnlarge(), ResourceOwnerRememberPlanCacheRef(), Query::rtable, PlannedStmt::rtable, RTE_RELATION, RangeTblEntry::rtekind, CachedPlanSource::search_path, SearchPathMatchesCurrentEnvironment(), and TransactionIdIsValid.

Referenced by exec_eval_simple_expr(), and exec_simple_check_plan().

◆ CachedPlanGetTargetList()

List * CachedPlanGetTargetList ( CachedPlanSource plansource,
QueryEnvironment queryEnv 
)

Definition at line 1777 of file plancache.c.

1779{
1780 Query *pstmt;
1781
1782 /* Assert caller is doing things in a sane order */
1783 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1784 Assert(plansource->is_complete);
1785
1786 /*
1787 * No work needed if statement doesn't return tuples (we assume this
1788 * feature cannot be changed by an invalidation)
1789 */
1790 if (plansource->resultDesc == NULL)
1791 return NIL;
1792
1793 /* Make sure the querytree list is valid and we have parse-time locks */
1794 RevalidateCachedQuery(plansource, queryEnv);
1795
1796 /* Get the primary statement and find out what it returns */
1797 pstmt = QueryListGetPrimaryStmt(plansource->query_list);
1798
1799 return FetchStatementTargetList((Node *) pstmt);
1800}
static Query * QueryListGetPrimaryStmt(List *stmts)
Definition: plancache.c:1890
List * FetchStatementTargetList(Node *stmt)
Definition: pquery.c:344
TupleDesc resultDesc
Definition: plancache.h:120
Definition: nodes.h:135

References Assert(), CACHEDPLANSOURCE_MAGIC, FetchStatementTargetList(), CachedPlanSource::is_complete, CachedPlanSource::magic, NIL, CachedPlanSource::query_list, QueryListGetPrimaryStmt(), CachedPlanSource::resultDesc, and RevalidateCachedQuery().

Referenced by exec_describe_statement_message(), and FetchPreparedStatementTargetList().

◆ CachedPlanIsSimplyValid()

bool CachedPlanIsSimplyValid ( CachedPlanSource plansource,
CachedPlan plan,
ResourceOwner  owner 
)

Definition at line 1586 of file plancache.c.

1588{
1589 /*
1590 * Careful here: since the caller doesn't necessarily hold a refcount on
1591 * the plan to start with, it's possible that "plan" is a dangling
1592 * pointer. Don't dereference it until we've verified that it still
1593 * matches the plansource's gplan (which is either valid or NULL).
1594 */
1595 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1596
1597 /*
1598 * Has cache invalidation fired on this plan? We can check this right
1599 * away since there are no locks that we'd need to acquire first. Note
1600 * that here we *do* check plansource->is_valid, so as to force plan
1601 * rebuild if that's become false.
1602 */
1603 if (!plansource->is_valid ||
1604 plan == NULL || plan != plansource->gplan ||
1605 !plan->is_valid)
1606 return false;
1607
1608 Assert(plan->magic == CACHEDPLAN_MAGIC);
1609
1610 /* Is the search_path still the same as when we made it? */
1611 Assert(plansource->search_path != NULL);
1613 return false;
1614
1615 /* It's still good. Bump refcount if requested. */
1616 if (owner)
1617 {
1618 ResourceOwnerEnlarge(owner);
1619 plan->refcount++;
1621 }
1622
1623 return true;
1624}

References Assert(), CACHEDPLAN_MAGIC, CACHEDPLANSOURCE_MAGIC, CachedPlanSource::gplan, CachedPlanSource::is_valid, CachedPlanSource::magic, plan, ResourceOwnerEnlarge(), ResourceOwnerRememberPlanCacheRef(), CachedPlanSource::search_path, and SearchPathMatchesCurrentEnvironment().

Referenced by exec_eval_simple_expr().

◆ CachedPlanIsValid()

bool CachedPlanIsValid ( CachedPlanSource plansource)

Definition at line 1764 of file plancache.c.

1765{
1766 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1767 return plansource->is_valid;
1768}

References Assert(), CACHEDPLANSOURCE_MAGIC, CachedPlanSource::is_valid, and CachedPlanSource::magic.

Referenced by SPI_plan_is_valid().

◆ CachedPlanSetParentContext()

void CachedPlanSetParentContext ( CachedPlanSource plansource,
MemoryContext  newcontext 
)

Definition at line 1633 of file plancache.c.

1635{
1636 /* Assert caller is doing things in a sane order */
1637 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1638 Assert(plansource->is_complete);
1639
1640 /* These seem worth real tests, though */
1641 if (plansource->is_saved)
1642 elog(ERROR, "cannot move a saved cached plan to another context");
1643 if (plansource->is_oneshot)
1644 elog(ERROR, "cannot move a one-shot cached plan to another context");
1645
1646 /* OK, let the caller keep the plan where he wishes */
1647 MemoryContextSetParent(plansource->context, newcontext);
1648
1649 /*
1650 * The query_context needs no special handling, since it's a child of
1651 * plansource->context. But if there's a generic plan, it should be
1652 * maintained as a sibling of plansource->context.
1653 */
1654 if (plansource->gplan)
1655 {
1656 Assert(plansource->gplan->magic == CACHEDPLAN_MAGIC);
1657 MemoryContextSetParent(plansource->gplan->context, newcontext);
1658 }
1659}
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
void MemoryContextSetParent(MemoryContext context, MemoryContext new_parent)
Definition: mcxt.c:683
MemoryContext context
Definition: plancache.h:121
MemoryContext context
Definition: plancache.h:172

References Assert(), CACHEDPLAN_MAGIC, CACHEDPLANSOURCE_MAGIC, CachedPlanSource::context, CachedPlan::context, elog, ERROR, CachedPlanSource::gplan, CachedPlanSource::is_complete, CachedPlanSource::is_oneshot, CachedPlanSource::is_saved, CachedPlanSource::magic, CachedPlan::magic, and MemoryContextSetParent().

Referenced by _SPI_make_plan_non_temp().

◆ CheckCachedPlan()

static bool CheckCachedPlan ( CachedPlanSource plansource)
static

Definition at line 950 of file plancache.c.

951{
952 CachedPlan *plan = plansource->gplan;
953
954 /* Assert that caller checked the querytree */
955 Assert(plansource->is_valid);
956
957 /* If there's no generic plan, just say "false" */
958 if (!plan)
959 return false;
960
961 Assert(plan->magic == CACHEDPLAN_MAGIC);
962 /* Generic plans are never one-shot */
963 Assert(!plan->is_oneshot);
964
965 /*
966 * If plan isn't valid for current role, we can't use it.
967 */
968 if (plan->is_valid && plan->dependsOnRole &&
969 plan->planRoleId != GetUserId())
970 plan->is_valid = false;
971
972 /*
973 * If it appears valid, acquire locks and recheck; this is much the same
974 * logic as in RevalidateCachedQuery, but for a plan.
975 */
976 if (plan->is_valid)
977 {
978 /*
979 * Plan must have positive refcount because it is referenced by
980 * plansource; so no need to fear it disappears under us here.
981 */
982 Assert(plan->refcount > 0);
983
984 AcquireExecutorLocks(plan->stmt_list, true);
985
986 /*
987 * If plan was transient, check to see if TransactionXmin has
988 * advanced, and if so invalidate it.
989 */
990 if (plan->is_valid &&
991 TransactionIdIsValid(plan->saved_xmin) &&
993 plan->is_valid = false;
994
995 /*
996 * By now, if any invalidation has happened, the inval callback
997 * functions will have marked the plan invalid.
998 */
999 if (plan->is_valid)
1000 {
1001 /* Successfully revalidated and locked the query. */
1002 return true;
1003 }
1004
1005 /* Oops, the race case happened. Release useless locks. */
1006 AcquireExecutorLocks(plan->stmt_list, false);
1007 }
1008
1009 /*
1010 * Plan has been invalidated, so unlink it from the parent and release it.
1011 */
1012 ReleaseGenericPlan(plansource);
1013
1014 return false;
1015}
static void ReleaseGenericPlan(CachedPlanSource *plansource)
Definition: plancache.c:618
static void AcquireExecutorLocks(List *stmt_list, bool acquire)
Definition: plancache.c:1909
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43

References AcquireExecutorLocks(), Assert(), CACHEDPLAN_MAGIC, GetUserId(), CachedPlanSource::gplan, CachedPlanSource::is_valid, plan, ReleaseGenericPlan(), TransactionIdEquals, TransactionIdIsValid, and TransactionXmin.

Referenced by GetCachedPlan().

◆ choose_custom_plan()

static bool choose_custom_plan ( CachedPlanSource plansource,
ParamListInfo  boundParams 
)
static

Definition at line 1173 of file plancache.c.

1174{
1175 double avg_custom_cost;
1176
1177 /* One-shot plans will always be considered custom */
1178 if (plansource->is_oneshot)
1179 return true;
1180
1181 /* Otherwise, never any point in a custom plan if there's no parameters */
1182 if (boundParams == NULL)
1183 return false;
1184 /* ... nor when planning would be a no-op */
1185 if (!StmtPlanRequiresRevalidation(plansource))
1186 return false;
1187
1188 /* Let settings force the decision */
1190 return false;
1192 return true;
1193
1194 /* See if caller wants to force the decision */
1195 if (plansource->cursor_options & CURSOR_OPT_GENERIC_PLAN)
1196 return false;
1197 if (plansource->cursor_options & CURSOR_OPT_CUSTOM_PLAN)
1198 return true;
1199
1200 /* Generate custom plans until we have done at least 5 (arbitrary) */
1201 if (plansource->num_custom_plans < 5)
1202 return true;
1203
1204 avg_custom_cost = plansource->total_custom_cost / plansource->num_custom_plans;
1205
1206 /*
1207 * Prefer generic plan if it's less expensive than the average custom
1208 * plan. (Because we include a charge for cost of planning in the
1209 * custom-plan costs, this means the generic plan only has to be less
1210 * expensive than the execution cost plus replan cost of the custom
1211 * plans.)
1212 *
1213 * Note that if generic_cost is -1 (indicating we've not yet determined
1214 * the generic plan cost), we'll always prefer generic at this point.
1215 */
1216 if (plansource->generic_cost < avg_custom_cost)
1217 return false;
1218
1219 return true;
1220}
#define CURSOR_OPT_GENERIC_PLAN
Definition: parsenodes.h:3394
#define CURSOR_OPT_CUSTOM_PLAN
Definition: parsenodes.h:3395
int plan_cache_mode
Definition: plancache.c:138
static bool StmtPlanRequiresRevalidation(CachedPlanSource *plansource)
Definition: plancache.c:640
@ PLAN_CACHE_MODE_FORCE_CUSTOM_PLAN
Definition: plancache.h:35
@ PLAN_CACHE_MODE_FORCE_GENERIC_PLAN
Definition: plancache.h:34
double total_custom_cost
Definition: plancache.h:144
int64 num_custom_plans
Definition: plancache.h:145
double generic_cost
Definition: plancache.h:143

References CURSOR_OPT_CUSTOM_PLAN, CURSOR_OPT_GENERIC_PLAN, CachedPlanSource::cursor_options, CachedPlanSource::generic_cost, CachedPlanSource::is_oneshot, CachedPlanSource::num_custom_plans, plan_cache_mode, PLAN_CACHE_MODE_FORCE_CUSTOM_PLAN, PLAN_CACHE_MODE_FORCE_GENERIC_PLAN, StmtPlanRequiresRevalidation(), and CachedPlanSource::total_custom_cost.

Referenced by GetCachedPlan().

◆ CompleteCachedPlan()

void CompleteCachedPlan ( CachedPlanSource plansource,
List querytree_list,
MemoryContext  querytree_context,
Oid param_types,
int  num_params,
ParserSetupHook  parserSetup,
void *  parserSetupArg,
int  cursor_options,
bool  fixed_result 
)

Definition at line 391 of file plancache.c.

400{
401 MemoryContext source_context = plansource->context;
403
404 /* Assert caller is doing things in a sane order */
405 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
406 Assert(!plansource->is_complete);
407
408 /*
409 * If caller supplied a querytree_context, reparent it underneath the
410 * CachedPlanSource's context; otherwise, create a suitable context and
411 * copy the querytree_list into it. But no data copying should be done
412 * for one-shot plans; for those, assume the passed querytree_list is
413 * sufficiently long-lived.
414 */
415 if (plansource->is_oneshot)
416 {
417 querytree_context = CurrentMemoryContext;
418 }
419 else if (querytree_context != NULL)
420 {
421 MemoryContextSetParent(querytree_context, source_context);
422 MemoryContextSwitchTo(querytree_context);
423 }
424 else
425 {
426 /* Again, it's a good bet the querytree_context can be small */
427 querytree_context = AllocSetContextCreate(source_context,
428 "CachedPlanQuery",
430 MemoryContextSwitchTo(querytree_context);
431 querytree_list = copyObject(querytree_list);
432 }
433
434 plansource->query_context = querytree_context;
435 plansource->query_list = querytree_list;
436
437 if (!plansource->is_oneshot && StmtPlanRequiresRevalidation(plansource))
438 {
439 /*
440 * Use the planner machinery to extract dependencies. Data is saved
441 * in query_context. (We assume that not a lot of extra cruft is
442 * created by this call.) We can skip this for one-shot plans, and
443 * plans not needing revalidation have no such dependencies anyway.
444 */
445 extract_query_dependencies((Node *) querytree_list,
446 &plansource->relationOids,
447 &plansource->invalItems,
448 &plansource->dependsOnRLS);
449
450 /* Update RLS info as well. */
451 plansource->rewriteRoleId = GetUserId();
452 plansource->rewriteRowSecurity = row_security;
453
454 /*
455 * Also save the current search_path in the query_context. (This
456 * should not generate much extra cruft either, since almost certainly
457 * the path is already valid.) Again, we don't really need this for
458 * one-shot plans; and we *must* skip this for transaction control
459 * commands, because this could result in catalog accesses.
460 */
461 plansource->search_path = GetSearchPathMatcher(querytree_context);
462 }
463
464 /*
465 * Save the final parameter types (or other parameter specification data)
466 * into the source_context, as well as our other parameters.
467 */
468 MemoryContextSwitchTo(source_context);
469
470 if (num_params > 0)
471 {
472 plansource->param_types = palloc_array(Oid, num_params);
473 memcpy(plansource->param_types, param_types, num_params * sizeof(Oid));
474 }
475 else
476 plansource->param_types = NULL;
477 plansource->num_params = num_params;
478 plansource->parserSetup = parserSetup;
479 plansource->parserSetupArg = parserSetupArg;
480 plansource->cursor_options = cursor_options;
481 plansource->fixed_result = fixed_result;
482
483 /*
484 * Also save the result tuple descriptor. PlanCacheComputeResultDesc may
485 * leak some cruft; normally we just accept that to save a copy step, but
486 * in USE_VALGRIND mode be tidy by running it in the caller's context.
487 */
488#ifdef USE_VALGRIND
489 MemoryContextSwitchTo(oldcxt);
490 plansource->resultDesc = PlanCacheComputeResultDesc(querytree_list);
491 if (plansource->resultDesc)
492 {
493 MemoryContextSwitchTo(source_context);
494 plansource->resultDesc = CreateTupleDescCopy(plansource->resultDesc);
495 MemoryContextSwitchTo(oldcxt);
496 }
497#else
498 plansource->resultDesc = PlanCacheComputeResultDesc(querytree_list);
499 MemoryContextSwitchTo(oldcxt);
500#endif
501
502 plansource->is_complete = true;
503 plansource->is_valid = true;
504}
#define palloc_array(type, count)
Definition: fe_memutils.h:76
bool row_security
Definition: guc_tables.c:528
SearchPathMatcher * GetSearchPathMatcher(MemoryContext context)
Definition: namespace.c:3922
static TupleDesc PlanCacheComputeResultDesc(List *stmt_list)
Definition: plancache.c:2084
unsigned int Oid
Definition: postgres_ext.h:32
void extract_query_dependencies(Node *query, List **relationOids, List **invalItems, bool *hasRowSecurity)
Definition: setrefs.c:3656
MemoryContext query_context
Definition: plancache.h:128
List * invalItems
Definition: plancache.h:125
ParserSetupHook parserSetup
Definition: plancache.h:114
bool rewriteRowSecurity
Definition: plancache.h:130
List * relationOids
Definition: plancache.h:124
void * parserSetupArg
Definition: plancache.h:115
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:252

References ALLOCSET_START_SMALL_SIZES, AllocSetContextCreate, Assert(), CACHEDPLANSOURCE_MAGIC, CachedPlanSource::context, copyObject, CreateTupleDescCopy(), CurrentMemoryContext, CachedPlanSource::cursor_options, CachedPlanSource::dependsOnRLS, extract_query_dependencies(), CachedPlanSource::fixed_result, GetSearchPathMatcher(), GetUserId(), CachedPlanSource::invalItems, CachedPlanSource::is_complete, CachedPlanSource::is_oneshot, CachedPlanSource::is_valid, CachedPlanSource::magic, MemoryContextSetParent(), MemoryContextSwitchTo(), CachedPlanSource::num_params, palloc_array, CachedPlanSource::param_types, CachedPlanSource::parserSetup, CachedPlanSource::parserSetupArg, PlanCacheComputeResultDesc(), CachedPlanSource::query_context, CachedPlanSource::query_list, CachedPlanSource::relationOids, CachedPlanSource::resultDesc, CachedPlanSource::rewriteRoleId, CachedPlanSource::rewriteRowSecurity, row_security, CachedPlanSource::search_path, and StmtPlanRequiresRevalidation().

Referenced by _SPI_execute_plan(), _SPI_prepare_plan(), exec_parse_message(), prepare_next_query(), and PrepareQuery().

◆ CopyCachedPlan()

CachedPlanSource * CopyCachedPlan ( CachedPlanSource plansource)

Definition at line 1671 of file plancache.c.

1672{
1673 CachedPlanSource *newsource;
1674 MemoryContext source_context;
1675 MemoryContext querytree_context;
1676 MemoryContext oldcxt;
1677
1678 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1679 Assert(plansource->is_complete);
1680
1681 /*
1682 * One-shot plans can't be copied, because we haven't taken care that
1683 * parsing/planning didn't scribble on the raw parse tree or querytrees.
1684 */
1685 if (plansource->is_oneshot)
1686 elog(ERROR, "cannot copy a one-shot cached plan");
1687
1689 "CachedPlanSource",
1691
1692 oldcxt = MemoryContextSwitchTo(source_context);
1693
1694 newsource = palloc0_object(CachedPlanSource);
1695 newsource->magic = CACHEDPLANSOURCE_MAGIC;
1696 newsource->raw_parse_tree = copyObject(plansource->raw_parse_tree);
1697 newsource->analyzed_parse_tree = copyObject(plansource->analyzed_parse_tree);
1698 newsource->query_string = pstrdup(plansource->query_string);
1699 MemoryContextSetIdentifier(source_context, newsource->query_string);
1700 newsource->commandTag = plansource->commandTag;
1701 if (plansource->num_params > 0)
1702 {
1703 newsource->param_types = palloc_array(Oid, plansource->num_params);
1704 memcpy(newsource->param_types, plansource->param_types,
1705 plansource->num_params * sizeof(Oid));
1706 }
1707 else
1708 newsource->param_types = NULL;
1709 newsource->num_params = plansource->num_params;
1710 newsource->parserSetup = plansource->parserSetup;
1711 newsource->parserSetupArg = plansource->parserSetupArg;
1712 newsource->postRewrite = plansource->postRewrite;
1713 newsource->postRewriteArg = plansource->postRewriteArg;
1714 newsource->cursor_options = plansource->cursor_options;
1715 newsource->fixed_result = plansource->fixed_result;
1716 if (plansource->resultDesc)
1717 newsource->resultDesc = CreateTupleDescCopy(plansource->resultDesc);
1718 else
1719 newsource->resultDesc = NULL;
1720 newsource->context = source_context;
1721
1722 querytree_context = AllocSetContextCreate(source_context,
1723 "CachedPlanQuery",
1725 MemoryContextSwitchTo(querytree_context);
1726 newsource->query_list = copyObject(plansource->query_list);
1727 newsource->relationOids = copyObject(plansource->relationOids);
1728 newsource->invalItems = copyObject(plansource->invalItems);
1729 if (plansource->search_path)
1730 newsource->search_path = CopySearchPathMatcher(plansource->search_path);
1731 newsource->query_context = querytree_context;
1732 newsource->rewriteRoleId = plansource->rewriteRoleId;
1733 newsource->rewriteRowSecurity = plansource->rewriteRowSecurity;
1734 newsource->dependsOnRLS = plansource->dependsOnRLS;
1735
1736 newsource->gplan = NULL;
1737
1738 newsource->is_oneshot = false;
1739 newsource->is_complete = true;
1740 newsource->is_saved = false;
1741 newsource->is_valid = plansource->is_valid;
1742 newsource->generation = plansource->generation;
1743
1744 /* We may as well copy any acquired cost knowledge */
1745 newsource->generic_cost = plansource->generic_cost;
1746 newsource->total_custom_cost = plansource->total_custom_cost;
1747 newsource->num_generic_plans = plansource->num_generic_plans;
1748 newsource->num_custom_plans = plansource->num_custom_plans;
1749
1750 MemoryContextSwitchTo(oldcxt);
1751
1752 return newsource;
1753}
#define palloc0_object(type)
Definition: fe_memutils.h:75
char * pstrdup(const char *in)
Definition: mcxt.c:1759
void MemoryContextSetIdentifier(MemoryContext context, const char *id)
Definition: mcxt.c:658
SearchPathMatcher * CopySearchPathMatcher(SearchPathMatcher *path)
Definition: namespace.c:3959
PostRewriteHook postRewrite
Definition: plancache.h:116
CommandTag commandTag
Definition: plancache.h:111
int64 num_generic_plans
Definition: plancache.h:146
void * postRewriteArg
Definition: plancache.h:117

References ALLOCSET_START_SMALL_SIZES, AllocSetContextCreate, CachedPlanSource::analyzed_parse_tree, Assert(), CACHEDPLANSOURCE_MAGIC, CachedPlanSource::commandTag, CachedPlanSource::context, copyObject, CopySearchPathMatcher(), CreateTupleDescCopy(), CurrentMemoryContext, CachedPlanSource::cursor_options, CachedPlanSource::dependsOnRLS, elog, ERROR, CachedPlanSource::fixed_result, CachedPlanSource::generation, CachedPlanSource::generic_cost, CachedPlanSource::gplan, CachedPlanSource::invalItems, CachedPlanSource::is_complete, CachedPlanSource::is_oneshot, CachedPlanSource::is_saved, CachedPlanSource::is_valid, CachedPlanSource::magic, MemoryContextSetIdentifier(), MemoryContextSwitchTo(), CachedPlanSource::num_custom_plans, CachedPlanSource::num_generic_plans, CachedPlanSource::num_params, palloc0_object, palloc_array, CachedPlanSource::param_types, CachedPlanSource::parserSetup, CachedPlanSource::parserSetupArg, CachedPlanSource::postRewrite, CachedPlanSource::postRewriteArg, pstrdup(), CachedPlanSource::query_context, CachedPlanSource::query_list, CachedPlanSource::query_string, CachedPlanSource::raw_parse_tree, CachedPlanSource::relationOids, CachedPlanSource::resultDesc, CachedPlanSource::rewriteRoleId, CachedPlanSource::rewriteRowSecurity, CachedPlanSource::search_path, and CachedPlanSource::total_custom_cost.

Referenced by _SPI_save_plan().

◆ CreateCachedPlan()

CachedPlanSource * CreateCachedPlan ( RawStmt raw_parse_tree,
const char *  query_string,
CommandTag  commandTag 
)

Definition at line 183 of file plancache.c.

186{
187 CachedPlanSource *plansource;
188 MemoryContext source_context;
189 MemoryContext oldcxt;
190
191 Assert(query_string != NULL); /* required as of 8.4 */
192
193 /*
194 * Make a dedicated memory context for the CachedPlanSource and its
195 * permanent subsidiary data. It's probably not going to be large, but
196 * just in case, allow it to grow large. Initially it's a child of the
197 * caller's context (which we assume to be transient), so that it will be
198 * cleaned up on error.
199 */
201 "CachedPlanSource",
203
204 /*
205 * Create and fill the CachedPlanSource struct within the new context.
206 * Most fields are just left empty for the moment.
207 */
208 oldcxt = MemoryContextSwitchTo(source_context);
209
210 plansource = palloc0_object(CachedPlanSource);
211 plansource->magic = CACHEDPLANSOURCE_MAGIC;
212 plansource->raw_parse_tree = copyObject(raw_parse_tree);
213 plansource->analyzed_parse_tree = NULL;
214 plansource->query_string = pstrdup(query_string);
215 MemoryContextSetIdentifier(source_context, plansource->query_string);
216 plansource->commandTag = commandTag;
217 plansource->param_types = NULL;
218 plansource->num_params = 0;
219 plansource->parserSetup = NULL;
220 plansource->parserSetupArg = NULL;
221 plansource->postRewrite = NULL;
222 plansource->postRewriteArg = NULL;
223 plansource->cursor_options = 0;
224 plansource->fixed_result = false;
225 plansource->resultDesc = NULL;
226 plansource->context = source_context;
227 plansource->query_list = NIL;
228 plansource->relationOids = NIL;
229 plansource->invalItems = NIL;
230 plansource->search_path = NULL;
231 plansource->query_context = NULL;
232 plansource->rewriteRoleId = InvalidOid;
233 plansource->rewriteRowSecurity = false;
234 plansource->dependsOnRLS = false;
235 plansource->gplan = NULL;
236 plansource->is_oneshot = false;
237 plansource->is_complete = false;
238 plansource->is_saved = false;
239 plansource->is_valid = false;
240 plansource->generation = 0;
241 plansource->generic_cost = -1;
242 plansource->total_custom_cost = 0;
243 plansource->num_generic_plans = 0;
244 plansource->num_custom_plans = 0;
245
246 MemoryContextSwitchTo(oldcxt);
247
248 return plansource;
249}
#define InvalidOid
Definition: postgres_ext.h:37

References ALLOCSET_START_SMALL_SIZES, AllocSetContextCreate, CachedPlanSource::analyzed_parse_tree, Assert(), CACHEDPLANSOURCE_MAGIC, CachedPlanSource::commandTag, CachedPlanSource::context, copyObject, CurrentMemoryContext, CachedPlanSource::cursor_options, CachedPlanSource::dependsOnRLS, CachedPlanSource::fixed_result, CachedPlanSource::generation, CachedPlanSource::generic_cost, CachedPlanSource::gplan, InvalidOid, CachedPlanSource::invalItems, CachedPlanSource::is_complete, CachedPlanSource::is_oneshot, CachedPlanSource::is_saved, CachedPlanSource::is_valid, CachedPlanSource::magic, MemoryContextSetIdentifier(), MemoryContextSwitchTo(), NIL, CachedPlanSource::num_custom_plans, CachedPlanSource::num_generic_plans, CachedPlanSource::num_params, palloc0_object, CachedPlanSource::param_types, CachedPlanSource::parserSetup, CachedPlanSource::parserSetupArg, CachedPlanSource::postRewrite, CachedPlanSource::postRewriteArg, pstrdup(), CachedPlanSource::query_context, CachedPlanSource::query_list, CachedPlanSource::query_string, CachedPlanSource::raw_parse_tree, CachedPlanSource::relationOids, CachedPlanSource::resultDesc, CachedPlanSource::rewriteRoleId, CachedPlanSource::rewriteRowSecurity, CachedPlanSource::search_path, and CachedPlanSource::total_custom_cost.

Referenced by _SPI_prepare_plan(), CreateCachedPlanForQuery(), exec_parse_message(), prepare_next_query(), and PrepareQuery().

◆ CreateCachedPlanForQuery()

CachedPlanSource * CreateCachedPlanForQuery ( Query analyzed_parse_tree,
const char *  query_string,
CommandTag  commandTag 
)

Definition at line 263 of file plancache.c.

266{
267 CachedPlanSource *plansource;
268 MemoryContext oldcxt;
269
270 /* Rather than duplicating CreateCachedPlan, just do this: */
271 plansource = CreateCachedPlan(NULL, query_string, commandTag);
272 oldcxt = MemoryContextSwitchTo(plansource->context);
273 plansource->analyzed_parse_tree = copyObject(analyzed_parse_tree);
274 MemoryContextSwitchTo(oldcxt);
275
276 return plansource;
277}
CachedPlanSource * CreateCachedPlan(RawStmt *raw_parse_tree, const char *query_string, CommandTag commandTag)
Definition: plancache.c:183

References CachedPlanSource::analyzed_parse_tree, CachedPlanSource::context, copyObject, CreateCachedPlan(), and MemoryContextSwitchTo().

Referenced by prepare_next_query().

◆ CreateOneShotCachedPlan()

CachedPlanSource * CreateOneShotCachedPlan ( RawStmt raw_parse_tree,
const char *  query_string,
CommandTag  commandTag 
)

Definition at line 298 of file plancache.c.

301{
302 CachedPlanSource *plansource;
303
304 Assert(query_string != NULL); /* required as of 8.4 */
305
306 /*
307 * Create and fill the CachedPlanSource struct within the caller's memory
308 * context. Most fields are just left empty for the moment.
309 */
310 plansource = palloc0_object(CachedPlanSource);
311 plansource->magic = CACHEDPLANSOURCE_MAGIC;
312 plansource->raw_parse_tree = raw_parse_tree;
313 plansource->analyzed_parse_tree = NULL;
314 plansource->query_string = query_string;
315 plansource->commandTag = commandTag;
316 plansource->param_types = NULL;
317 plansource->num_params = 0;
318 plansource->parserSetup = NULL;
319 plansource->parserSetupArg = NULL;
320 plansource->postRewrite = NULL;
321 plansource->postRewriteArg = NULL;
322 plansource->cursor_options = 0;
323 plansource->fixed_result = false;
324 plansource->resultDesc = NULL;
325 plansource->context = CurrentMemoryContext;
326 plansource->query_list = NIL;
327 plansource->relationOids = NIL;
328 plansource->invalItems = NIL;
329 plansource->search_path = NULL;
330 plansource->query_context = NULL;
331 plansource->rewriteRoleId = InvalidOid;
332 plansource->rewriteRowSecurity = false;
333 plansource->dependsOnRLS = false;
334 plansource->gplan = NULL;
335 plansource->is_oneshot = true;
336 plansource->is_complete = false;
337 plansource->is_saved = false;
338 plansource->is_valid = false;
339 plansource->generation = 0;
340 plansource->generic_cost = -1;
341 plansource->total_custom_cost = 0;
342 plansource->num_generic_plans = 0;
343 plansource->num_custom_plans = 0;
344
345 return plansource;
346}

References CachedPlanSource::analyzed_parse_tree, Assert(), CACHEDPLANSOURCE_MAGIC, CachedPlanSource::commandTag, CachedPlanSource::context, CurrentMemoryContext, CachedPlanSource::cursor_options, CachedPlanSource::dependsOnRLS, CachedPlanSource::fixed_result, CachedPlanSource::generation, CachedPlanSource::generic_cost, CachedPlanSource::gplan, InvalidOid, CachedPlanSource::invalItems, CachedPlanSource::is_complete, CachedPlanSource::is_oneshot, CachedPlanSource::is_saved, CachedPlanSource::is_valid, CachedPlanSource::magic, NIL, CachedPlanSource::num_custom_plans, CachedPlanSource::num_generic_plans, CachedPlanSource::num_params, palloc0_object, CachedPlanSource::param_types, CachedPlanSource::parserSetup, CachedPlanSource::parserSetupArg, CachedPlanSource::postRewrite, CachedPlanSource::postRewriteArg, CachedPlanSource::query_context, CachedPlanSource::query_list, CachedPlanSource::query_string, CachedPlanSource::raw_parse_tree, CachedPlanSource::relationOids, CachedPlanSource::resultDesc, CachedPlanSource::rewriteRoleId, CachedPlanSource::rewriteRowSecurity, CachedPlanSource::search_path, and CachedPlanSource::total_custom_cost.

Referenced by _SPI_prepare_oneshot_plan().

◆ DropCachedPlan()

void DropCachedPlan ( CachedPlanSource plansource)

Definition at line 589 of file plancache.c.

590{
591 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
592
593 /* If it's been saved, remove it from the list */
594 if (plansource->is_saved)
595 {
596 dlist_delete(&plansource->node);
597 plansource->is_saved = false;
598 }
599
600 /* Decrement generic CachedPlan's refcount and drop if no longer needed */
601 ReleaseGenericPlan(plansource);
602
603 /* Mark it no longer valid */
604 plansource->magic = 0;
605
606 /*
607 * Remove the CachedPlanSource and all subsidiary data (including the
608 * query_context if any). But if it's a one-shot we can't free anything.
609 */
610 if (!plansource->is_oneshot)
611 MemoryContextDelete(plansource->context);
612}
static void dlist_delete(dlist_node *node)
Definition: ilist.h:405
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:469
dlist_node node
Definition: plancache.h:141

References Assert(), CACHEDPLANSOURCE_MAGIC, CachedPlanSource::context, dlist_delete(), CachedPlanSource::is_oneshot, CachedPlanSource::is_saved, CachedPlanSource::magic, MemoryContextDelete(), CachedPlanSource::node, and ReleaseGenericPlan().

Referenced by drop_unnamed_stmt(), DropAllPreparedStatements(), DropPreparedStatement(), SPI_freeplan(), and sql_delete_callback().

◆ FreeCachedExpression()

void FreeCachedExpression ( CachedExpression cexpr)

Definition at line 1871 of file plancache.c.

1872{
1873 /* Sanity check */
1874 Assert(cexpr->magic == CACHEDEXPR_MAGIC);
1875 /* Unlink from global list */
1876 dlist_delete(&cexpr->node);
1877 /* Free all storage associated with CachedExpression */
1879}
#define CACHEDEXPR_MAGIC
Definition: plancache.h:46
MemoryContext context
Definition: plancache.h:195
dlist_node node
Definition: plancache.h:196

References Assert(), CACHEDEXPR_MAGIC, CachedExpression::context, dlist_delete(), CachedExpression::magic, MemoryContextDelete(), and CachedExpression::node.

Referenced by get_cast_hashentry().

◆ GetCachedExpression()

CachedExpression * GetCachedExpression ( Node expr)

Definition at line 1814 of file plancache.c.

1815{
1816 CachedExpression *cexpr;
1817 List *relationOids;
1818 List *invalItems;
1819 MemoryContext cexpr_context;
1820 MemoryContext oldcxt;
1821
1822 /*
1823 * Pass the expression through the planner, and collect dependencies.
1824 * Everything built here is leaked in the caller's context; that's
1825 * intentional to minimize the size of the permanent data structure.
1826 */
1827 expr = (Node *) expression_planner_with_deps((Expr *) expr,
1828 &relationOids,
1829 &invalItems);
1830
1831 /*
1832 * Make a private memory context, and copy what we need into that. To
1833 * avoid leaking a long-lived context if we fail while copying data, we
1834 * initially make the context under the caller's context.
1835 */
1837 "CachedExpression",
1839
1840 oldcxt = MemoryContextSwitchTo(cexpr_context);
1841
1843 cexpr->magic = CACHEDEXPR_MAGIC;
1844 cexpr->expr = copyObject(expr);
1845 cexpr->is_valid = true;
1846 cexpr->relationOids = copyObject(relationOids);
1847 cexpr->invalItems = copyObject(invalItems);
1848 cexpr->context = cexpr_context;
1849
1850 MemoryContextSwitchTo(oldcxt);
1851
1852 /*
1853 * Reparent the expr's memory context under CacheMemoryContext so that it
1854 * will live indefinitely.
1855 */
1857
1858 /*
1859 * Add the entry to the global list of cached expressions.
1860 */
1862
1863 return cexpr;
1864}
static void dlist_push_tail(dlist_head *head, dlist_node *node)
Definition: ilist.h:364
MemoryContext CacheMemoryContext
Definition: mcxt.c:169
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:170
static dlist_head cached_expression_list
Definition: plancache.c:89
Expr * expression_planner_with_deps(Expr *expr, List **relationOids, List **invalItems)
Definition: planner.c:6790
List * relationOids
Definition: plancache.h:193
List * invalItems
Definition: plancache.h:194

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, cached_expression_list, CACHEDEXPR_MAGIC, CacheMemoryContext, CachedExpression::context, copyObject, CurrentMemoryContext, dlist_push_tail(), CachedExpression::expr, expression_planner_with_deps(), CachedExpression::invalItems, CachedExpression::is_valid, CachedExpression::magic, MemoryContextSetParent(), MemoryContextSwitchTo(), CachedExpression::node, palloc_object, and CachedExpression::relationOids.

Referenced by get_cast_hashentry().

◆ GetCachedPlan()

CachedPlan * GetCachedPlan ( CachedPlanSource plansource,
ParamListInfo  boundParams,
ResourceOwner  owner,
QueryEnvironment queryEnv 
)

Definition at line 1295 of file plancache.c.

1297{
1298 CachedPlan *plan = NULL;
1299 List *qlist;
1300 bool customplan;
1301 ListCell *lc;
1302
1303 /* Assert caller is doing things in a sane order */
1304 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1305 Assert(plansource->is_complete);
1306 /* This seems worth a real test, though */
1307 if (owner && !plansource->is_saved)
1308 elog(ERROR, "cannot apply ResourceOwner to non-saved cached plan");
1309
1310 /* Make sure the querytree list is valid and we have parse-time locks */
1311 qlist = RevalidateCachedQuery(plansource, queryEnv);
1312
1313 /* Decide whether to use a custom plan */
1314 customplan = choose_custom_plan(plansource, boundParams);
1315
1316 if (!customplan)
1317 {
1318 if (CheckCachedPlan(plansource))
1319 {
1320 /* We want a generic plan, and we already have a valid one */
1321 plan = plansource->gplan;
1322 Assert(plan->magic == CACHEDPLAN_MAGIC);
1323 }
1324 else
1325 {
1326 /* Build a new generic plan */
1327 plan = BuildCachedPlan(plansource, qlist, NULL, queryEnv);
1328 /* Just make real sure plansource->gplan is clear */
1329 ReleaseGenericPlan(plansource);
1330 /* Link the new generic plan into the plansource */
1331 plansource->gplan = plan;
1332 plan->refcount++;
1333 /* Immediately reparent into appropriate context */
1334 if (plansource->is_saved)
1335 {
1336 /* saved plans all live under CacheMemoryContext */
1338 plan->is_saved = true;
1339 }
1340 else
1341 {
1342 /* otherwise, it should be a sibling of the plansource */
1344 MemoryContextGetParent(plansource->context));
1345 }
1346 /* Update generic_cost whenever we make a new generic plan */
1347 plansource->generic_cost = cached_plan_cost(plan, false);
1348
1349 /*
1350 * If, based on the now-known value of generic_cost, we'd not have
1351 * chosen to use a generic plan, then forget it and make a custom
1352 * plan. This is a bit of a wart but is necessary to avoid a
1353 * glitch in behavior when the custom plans are consistently big
1354 * winners; at some point we'll experiment with a generic plan and
1355 * find it's a loser, but we don't want to actually execute that
1356 * plan.
1357 */
1358 customplan = choose_custom_plan(plansource, boundParams);
1359
1360 /*
1361 * If we choose to plan again, we need to re-copy the query_list,
1362 * since the planner probably scribbled on it. We can force
1363 * BuildCachedPlan to do that by passing NIL.
1364 */
1365 qlist = NIL;
1366 }
1367 }
1368
1369 if (customplan)
1370 {
1371 /* Build a custom plan */
1372 plan = BuildCachedPlan(plansource, qlist, boundParams, queryEnv);
1373 /* Accumulate total costs of custom plans */
1374 plansource->total_custom_cost += cached_plan_cost(plan, true);
1375
1376 plansource->num_custom_plans++;
1377 }
1378 else
1379 {
1380 plansource->num_generic_plans++;
1381 }
1382
1383 Assert(plan != NULL);
1384
1385 /* Flag the plan as in use by caller */
1386 if (owner)
1387 ResourceOwnerEnlarge(owner);
1388 plan->refcount++;
1389 if (owner)
1391
1392 /*
1393 * Saved plans should be under CacheMemoryContext so they will not go away
1394 * until their reference count goes to zero. In the generic-plan cases we
1395 * already took care of that, but for a custom plan, do it as soon as we
1396 * have created a reference-counted link.
1397 */
1398 if (customplan && plansource->is_saved)
1399 {
1401 plan->is_saved = true;
1402 }
1403
1404 foreach(lc, plan->stmt_list)
1405 {
1406 PlannedStmt *pstmt = (PlannedStmt *) lfirst(lc);
1407
1409 }
1410
1411 return plan;
1412}
MemoryContext MemoryContextGetParent(MemoryContext context)
Definition: mcxt.c:777
static bool choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams)
Definition: plancache.c:1173
static CachedPlan * BuildCachedPlan(CachedPlanSource *plansource, List *qlist, ParamListInfo boundParams, QueryEnvironment *queryEnv)
Definition: plancache.c:1034
static bool CheckCachedPlan(CachedPlanSource *plansource)
Definition: plancache.c:950
static double cached_plan_cost(CachedPlan *plan, bool include_planner)
Definition: plancache.c:1230
@ PLAN_STMT_CACHE_CUSTOM
Definition: plannodes.h:43
@ PLAN_STMT_CACHE_GENERIC
Definition: plannodes.h:42
PlannedStmtOrigin planOrigin
Definition: plannodes.h:77

References Assert(), BuildCachedPlan(), cached_plan_cost(), CACHEDPLAN_MAGIC, CACHEDPLANSOURCE_MAGIC, CacheMemoryContext, CheckCachedPlan(), choose_custom_plan(), CachedPlanSource::context, elog, ERROR, CachedPlanSource::generic_cost, CachedPlanSource::gplan, CachedPlanSource::is_complete, CachedPlanSource::is_saved, lfirst, CachedPlanSource::magic, MemoryContextGetParent(), MemoryContextSetParent(), NIL, CachedPlanSource::num_custom_plans, CachedPlanSource::num_generic_plans, plan, PLAN_STMT_CACHE_CUSTOM, PLAN_STMT_CACHE_GENERIC, PlannedStmt::planOrigin, ReleaseGenericPlan(), ResourceOwnerEnlarge(), ResourceOwnerRememberPlanCacheRef(), RevalidateCachedQuery(), and CachedPlanSource::total_custom_cost.

Referenced by _SPI_execute_plan(), exec_bind_message(), ExecuteQuery(), ExplainExecuteQuery(), init_execution_state(), SPI_cursor_open_internal(), and SPI_plan_get_cached_plan().

◆ InitPlanCache()

void InitPlanCache ( void  )

Definition at line 146 of file plancache.c.

147{
155 CacheRegisterSyscacheCallback(FOREIGNDATAWRAPPEROID, PlanCacheSysCallback, (Datum) 0);
156}
void CacheRegisterRelcacheCallback(RelcacheCallbackFunction func, Datum arg)
Definition: inval.c:1854
void CacheRegisterSyscacheCallback(int cacheid, SyscacheCallbackFunction func, Datum arg)
Definition: inval.c:1812
static void PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue)
Definition: plancache.c:2313
static void PlanCacheObjectCallback(Datum arg, int cacheid, uint32 hashvalue)
Definition: plancache.c:2204
static void PlanCacheRelCallback(Datum arg, Oid relid)
Definition: plancache.c:2120
uint64_t Datum
Definition: postgres.h:70

References CacheRegisterRelcacheCallback(), CacheRegisterSyscacheCallback(), PlanCacheObjectCallback(), PlanCacheRelCallback(), and PlanCacheSysCallback().

Referenced by InitPostgres().

◆ PlanCacheComputeResultDesc()

static TupleDesc PlanCacheComputeResultDesc ( List stmt_list)
static

Definition at line 2084 of file plancache.c.

2085{
2086 Query *query;
2087
2088 switch (ChoosePortalStrategy(stmt_list))
2089 {
2090 case PORTAL_ONE_SELECT:
2092 query = linitial_node(Query, stmt_list);
2093 return ExecCleanTypeFromTL(query->targetList);
2094
2096 query = QueryListGetPrimaryStmt(stmt_list);
2097 Assert(query->returningList);
2098 return ExecCleanTypeFromTL(query->returningList);
2099
2100 case PORTAL_UTIL_SELECT:
2101 query = linitial_node(Query, stmt_list);
2102 Assert(query->utilityStmt);
2103 return UtilityTupleDescriptor(query->utilityStmt);
2104
2105 case PORTAL_MULTI_QUERY:
2106 /* will not return tuples */
2107 break;
2108 }
2109 return NULL;
2110}
TupleDesc ExecCleanTypeFromTL(List *targetList)
Definition: execTuples.c:2139
#define linitial_node(type, l)
Definition: pg_list.h:181
@ PORTAL_ONE_RETURNING
Definition: portal.h:92
@ PORTAL_MULTI_QUERY
Definition: portal.h:95
@ PORTAL_ONE_SELECT
Definition: portal.h:91
@ PORTAL_ONE_MOD_WITH
Definition: portal.h:93
@ PORTAL_UTIL_SELECT
Definition: portal.h:94
PortalStrategy ChoosePortalStrategy(List *stmts)
Definition: pquery.c:205
List * returningList
Definition: parsenodes.h:214
List * targetList
Definition: parsenodes.h:198
TupleDesc UtilityTupleDescriptor(Node *parsetree)
Definition: utility.c:2087

References Assert(), ChoosePortalStrategy(), ExecCleanTypeFromTL(), linitial_node, PORTAL_MULTI_QUERY, PORTAL_ONE_MOD_WITH, PORTAL_ONE_RETURNING, PORTAL_ONE_SELECT, PORTAL_UTIL_SELECT, QueryListGetPrimaryStmt(), Query::returningList, Query::targetList, Query::utilityStmt, and UtilityTupleDescriptor().

Referenced by CompleteCachedPlan(), and RevalidateCachedQuery().

◆ PlanCacheObjectCallback()

static void PlanCacheObjectCallback ( Datum  arg,
int  cacheid,
uint32  hashvalue 
)
static

Definition at line 2204 of file plancache.c.

2205{
2206 dlist_iter iter;
2207
2209 {
2211 node, iter.cur);
2212 ListCell *lc;
2213
2214 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
2215
2216 /* No work if it's already invalidated */
2217 if (!plansource->is_valid)
2218 continue;
2219
2220 /* Never invalidate if parse/plan would be a no-op anyway */
2221 if (!StmtPlanRequiresRevalidation(plansource))
2222 continue;
2223
2224 /*
2225 * Check the dependency list for the rewritten querytree.
2226 */
2227 foreach(lc, plansource->invalItems)
2228 {
2229 PlanInvalItem *item = (PlanInvalItem *) lfirst(lc);
2230
2231 if (item->cacheId != cacheid)
2232 continue;
2233 if (hashvalue == 0 ||
2234 item->hashValue == hashvalue)
2235 {
2236 /* Invalidate the querytree and generic plan */
2237 plansource->is_valid = false;
2238 if (plansource->gplan)
2239 plansource->gplan->is_valid = false;
2240 break;
2241 }
2242 }
2243
2244 /*
2245 * The generic plan, if any, could have more dependencies than the
2246 * querytree does, so we have to check it too.
2247 */
2248 if (plansource->gplan && plansource->gplan->is_valid)
2249 {
2250 foreach(lc, plansource->gplan->stmt_list)
2251 {
2252 PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
2253 ListCell *lc3;
2254
2255 if (plannedstmt->commandType == CMD_UTILITY)
2256 continue; /* Ignore utility statements */
2257 foreach(lc3, plannedstmt->invalItems)
2258 {
2259 PlanInvalItem *item = (PlanInvalItem *) lfirst(lc3);
2260
2261 if (item->cacheId != cacheid)
2262 continue;
2263 if (hashvalue == 0 ||
2264 item->hashValue == hashvalue)
2265 {
2266 /* Invalidate the generic plan only */
2267 plansource->gplan->is_valid = false;
2268 break; /* out of invalItems scan */
2269 }
2270 }
2271 if (!plansource->gplan->is_valid)
2272 break; /* out of stmt_list scan */
2273 }
2274 }
2275 }
2276
2277 /* Likewise check cached expressions */
2279 {
2281 node, iter.cur);
2282 ListCell *lc;
2283
2284 Assert(cexpr->magic == CACHEDEXPR_MAGIC);
2285
2286 /* No work if it's already invalidated */
2287 if (!cexpr->is_valid)
2288 continue;
2289
2290 foreach(lc, cexpr->invalItems)
2291 {
2292 PlanInvalItem *item = (PlanInvalItem *) lfirst(lc);
2293
2294 if (item->cacheId != cacheid)
2295 continue;
2296 if (hashvalue == 0 ||
2297 item->hashValue == hashvalue)
2298 {
2299 cexpr->is_valid = false;
2300 break;
2301 }
2302 }
2303 }
2304}
#define dlist_foreach(iter, lhead)
Definition: ilist.h:623
#define dlist_container(type, membername, ptr)
Definition: ilist.h:593
static dlist_head saved_plan_list
Definition: plancache.c:84
bool is_valid
Definition: plancache.h:165
List * stmt_list
Definition: plancache.h:162
uint32 hashValue
Definition: plannodes.h:1804
List * invalItems
Definition: plannodes.h:144
dlist_node * cur
Definition: ilist.h:179

References Assert(), cached_expression_list, CACHEDEXPR_MAGIC, CACHEDPLANSOURCE_MAGIC, PlanInvalItem::cacheId, CMD_UTILITY, PlannedStmt::commandType, dlist_iter::cur, dlist_container, dlist_foreach, CachedPlanSource::gplan, PlanInvalItem::hashValue, PlannedStmt::invalItems, CachedPlanSource::invalItems, CachedExpression::invalItems, CachedPlanSource::is_valid, CachedPlan::is_valid, CachedExpression::is_valid, lfirst, lfirst_node, CachedPlanSource::magic, CachedExpression::magic, saved_plan_list, CachedPlan::stmt_list, and StmtPlanRequiresRevalidation().

Referenced by InitPlanCache().

◆ PlanCacheRelCallback()

static void PlanCacheRelCallback ( Datum  arg,
Oid  relid 
)
static

Definition at line 2120 of file plancache.c.

2121{
2122 dlist_iter iter;
2123
2125 {
2127 node, iter.cur);
2128
2129 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
2130
2131 /* No work if it's already invalidated */
2132 if (!plansource->is_valid)
2133 continue;
2134
2135 /* Never invalidate if parse/plan would be a no-op anyway */
2136 if (!StmtPlanRequiresRevalidation(plansource))
2137 continue;
2138
2139 /*
2140 * Check the dependency list for the rewritten querytree.
2141 */
2142 if ((relid == InvalidOid) ? plansource->relationOids != NIL :
2143 list_member_oid(plansource->relationOids, relid))
2144 {
2145 /* Invalidate the querytree and generic plan */
2146 plansource->is_valid = false;
2147 if (plansource->gplan)
2148 plansource->gplan->is_valid = false;
2149 }
2150
2151 /*
2152 * The generic plan, if any, could have more dependencies than the
2153 * querytree does, so we have to check it too.
2154 */
2155 if (plansource->gplan && plansource->gplan->is_valid)
2156 {
2157 ListCell *lc;
2158
2159 foreach(lc, plansource->gplan->stmt_list)
2160 {
2161 PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
2162
2163 if (plannedstmt->commandType == CMD_UTILITY)
2164 continue; /* Ignore utility statements */
2165 if ((relid == InvalidOid) ? plannedstmt->relationOids != NIL :
2166 list_member_oid(plannedstmt->relationOids, relid))
2167 {
2168 /* Invalidate the generic plan only */
2169 plansource->gplan->is_valid = false;
2170 break; /* out of stmt_list scan */
2171 }
2172 }
2173 }
2174 }
2175
2176 /* Likewise check cached expressions */
2178 {
2180 node, iter.cur);
2181
2182 Assert(cexpr->magic == CACHEDEXPR_MAGIC);
2183
2184 /* No work if it's already invalidated */
2185 if (!cexpr->is_valid)
2186 continue;
2187
2188 if ((relid == InvalidOid) ? cexpr->relationOids != NIL :
2189 list_member_oid(cexpr->relationOids, relid))
2190 {
2191 cexpr->is_valid = false;
2192 }
2193 }
2194}
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:722
List * relationOids
Definition: plannodes.h:141

References Assert(), cached_expression_list, CACHEDEXPR_MAGIC, CACHEDPLANSOURCE_MAGIC, CMD_UTILITY, PlannedStmt::commandType, dlist_iter::cur, dlist_container, dlist_foreach, CachedPlanSource::gplan, InvalidOid, CachedPlanSource::is_valid, CachedPlan::is_valid, CachedExpression::is_valid, lfirst_node, list_member_oid(), CachedPlanSource::magic, CachedExpression::magic, NIL, PlannedStmt::relationOids, CachedPlanSource::relationOids, CachedExpression::relationOids, saved_plan_list, CachedPlan::stmt_list, and StmtPlanRequiresRevalidation().

Referenced by InitPlanCache().

◆ PlanCacheSysCallback()

static void PlanCacheSysCallback ( Datum  arg,
int  cacheid,
uint32  hashvalue 
)
static

Definition at line 2313 of file plancache.c.

2314{
2316}
void ResetPlanCache(void)
Definition: plancache.c:2322

References ResetPlanCache().

Referenced by InitPlanCache().

◆ QueryListGetPrimaryStmt()

static Query * QueryListGetPrimaryStmt ( List stmts)
static

Definition at line 1890 of file plancache.c.

1891{
1892 ListCell *lc;
1893
1894 foreach(lc, stmts)
1895 {
1896 Query *stmt = lfirst_node(Query, lc);
1897
1898 if (stmt->canSetTag)
1899 return stmt;
1900 }
1901 return NULL;
1902}
#define stmt
Definition: indent_codes.h:59

References lfirst_node, and stmt.

Referenced by CachedPlanGetTargetList(), and PlanCacheComputeResultDesc().

◆ ReleaseAllPlanCacheRefsInOwner()

void ReleaseAllPlanCacheRefsInOwner ( ResourceOwner  owner)

Definition at line 2369 of file plancache.c.

2370{
2372}
static const ResourceOwnerDesc planref_resowner_desc
Definition: plancache.c:115
void ResourceOwnerReleaseAllOfKind(ResourceOwner owner, const ResourceOwnerDesc *kind)
Definition: resowner.c:815

References planref_resowner_desc, and ResourceOwnerReleaseAllOfKind().

Referenced by plpgsql_call_handler(), plpgsql_inline_handler(), and plpgsql_xact_cb().

◆ ReleaseCachedPlan()

void ReleaseCachedPlan ( CachedPlan plan,
ResourceOwner  owner 
)

Definition at line 1426 of file plancache.c.

1427{
1428 Assert(plan->magic == CACHEDPLAN_MAGIC);
1429 if (owner)
1430 {
1431 Assert(plan->is_saved);
1433 }
1434 Assert(plan->refcount > 0);
1435 plan->refcount--;
1436 if (plan->refcount == 0)
1437 {
1438 /* Mark it no longer valid */
1439 plan->magic = 0;
1440
1441 /* One-shot plans do not own their context, so we can't free them */
1442 if (!plan->is_oneshot)
1443 MemoryContextDelete(plan->context);
1444 }
1445}
static void ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
Definition: plancache.c:131

References Assert(), CACHEDPLAN_MAGIC, MemoryContextDelete(), plan, and ResourceOwnerForgetPlanCacheRef().

Referenced by _SPI_execute_plan(), exec_eval_simple_expr(), exec_simple_check_plan(), ExplainExecuteQuery(), init_execution_state(), make_callstmt_target(), PortalReleaseCachedPlan(), ReleaseGenericPlan(), ResOwnerReleaseCachedPlan(), ShutdownSQLFunction(), and SPI_cursor_open_internal().

◆ ReleaseGenericPlan()

static void ReleaseGenericPlan ( CachedPlanSource plansource)
static

Definition at line 618 of file plancache.c.

619{
620 /* Be paranoid about the possibility that ReleaseCachedPlan fails */
621 if (plansource->gplan)
622 {
623 CachedPlan *plan = plansource->gplan;
624
625 Assert(plan->magic == CACHEDPLAN_MAGIC);
626 plansource->gplan = NULL;
627 ReleaseCachedPlan(plan, NULL);
628 }
629}
void ReleaseCachedPlan(CachedPlan *plan, ResourceOwner owner)
Definition: plancache.c:1426

References Assert(), CACHEDPLAN_MAGIC, CachedPlanSource::gplan, plan, and ReleaseCachedPlan().

Referenced by CheckCachedPlan(), DropCachedPlan(), GetCachedPlan(), RevalidateCachedQuery(), and SaveCachedPlan().

◆ ResetPlanCache()

void ResetPlanCache ( void  )

Definition at line 2322 of file plancache.c.

2323{
2324 dlist_iter iter;
2325
2327 {
2329 node, iter.cur);
2330
2331 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
2332
2333 /* No work if it's already invalidated */
2334 if (!plansource->is_valid)
2335 continue;
2336
2337 /*
2338 * We *must not* mark transaction control statements as invalid,
2339 * particularly not ROLLBACK, because they may need to be executed in
2340 * aborted transactions when we can't revalidate them (cf bug #5269).
2341 * In general there's no point in invalidating statements for which a
2342 * new parse analysis/rewrite/plan cycle would certainly give the same
2343 * results.
2344 */
2345 if (!StmtPlanRequiresRevalidation(plansource))
2346 continue;
2347
2348 plansource->is_valid = false;
2349 if (plansource->gplan)
2350 plansource->gplan->is_valid = false;
2351 }
2352
2353 /* Likewise invalidate cached expressions */
2355 {
2357 node, iter.cur);
2358
2359 Assert(cexpr->magic == CACHEDEXPR_MAGIC);
2360
2361 cexpr->is_valid = false;
2362 }
2363}

References Assert(), cached_expression_list, CACHEDEXPR_MAGIC, CACHEDPLANSOURCE_MAGIC, dlist_iter::cur, dlist_container, dlist_foreach, CachedPlanSource::gplan, CachedPlanSource::is_valid, CachedPlan::is_valid, CachedExpression::is_valid, CachedPlanSource::magic, CachedExpression::magic, saved_plan_list, and StmtPlanRequiresRevalidation().

Referenced by assign_session_replication_role(), DiscardAll(), DiscardCommand(), and PlanCacheSysCallback().

◆ ResourceOwnerForgetPlanCacheRef()

static void ResourceOwnerForgetPlanCacheRef ( ResourceOwner  owner,
CachedPlan plan 
)
inlinestatic

Definition at line 131 of file plancache.c.

132{
134}
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:332
void ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition: resowner.c:561

References plan, planref_resowner_desc, PointerGetDatum(), and ResourceOwnerForget().

Referenced by ReleaseCachedPlan().

◆ ResourceOwnerRememberPlanCacheRef()

static void ResourceOwnerRememberPlanCacheRef ( ResourceOwner  owner,
CachedPlan plan 
)
inlinestatic

Definition at line 126 of file plancache.c.

127{
129}
void ResourceOwnerRemember(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition: resowner.c:521

References plan, planref_resowner_desc, PointerGetDatum(), and ResourceOwnerRemember().

Referenced by CachedPlanAllowsSimpleValidityCheck(), CachedPlanIsSimplyValid(), and GetCachedPlan().

◆ ResOwnerReleaseCachedPlan()

static void ResOwnerReleaseCachedPlan ( Datum  res)
static

Definition at line 2377 of file plancache.c.

2378{
2380}
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:322

References DatumGetPointer(), and ReleaseCachedPlan().

◆ RevalidateCachedQuery()

static List * RevalidateCachedQuery ( CachedPlanSource plansource,
QueryEnvironment queryEnv 
)
static

Definition at line 682 of file plancache.c.

684{
685 bool snapshot_set;
686 List *tlist; /* transient query-tree list */
687 List *qlist; /* permanent query-tree list */
688 TupleDesc resultDesc;
689 MemoryContext querytree_context;
690 MemoryContext oldcxt;
691
692 /*
693 * For one-shot plans, we do not support revalidation checking; it's
694 * assumed the query is parsed, planned, and executed in one transaction,
695 * so that no lock re-acquisition is necessary. Also, if the statement
696 * type can't require revalidation, we needn't do anything (and we mustn't
697 * risk catalog accesses when handling, eg, transaction control commands).
698 */
699 if (plansource->is_oneshot || !StmtPlanRequiresRevalidation(plansource))
700 {
701 Assert(plansource->is_valid);
702 return NIL;
703 }
704
705 /*
706 * If the query is currently valid, we should have a saved search_path ---
707 * check to see if that matches the current environment. If not, we want
708 * to force replan. (We could almost ignore this consideration when
709 * working from an analyzed parse tree; but there are scenarios where
710 * planning can have search_path-dependent results, for example if it
711 * inlines an old-style SQL function.)
712 */
713 if (plansource->is_valid)
714 {
715 Assert(plansource->search_path != NULL);
717 {
718 /* Invalidate the querytree and generic plan */
719 plansource->is_valid = false;
720 if (plansource->gplan)
721 plansource->gplan->is_valid = false;
722 }
723 }
724
725 /*
726 * If the query rewrite phase had a possible RLS dependency, we must redo
727 * it if either the role or the row_security setting has changed.
728 */
729 if (plansource->is_valid && plansource->dependsOnRLS &&
730 (plansource->rewriteRoleId != GetUserId() ||
731 plansource->rewriteRowSecurity != row_security))
732 plansource->is_valid = false;
733
734 /*
735 * If the query is currently valid, acquire locks on the referenced
736 * objects; then check again. We need to do it this way to cover the race
737 * condition that an invalidation message arrives before we get the locks.
738 */
739 if (plansource->is_valid)
740 {
741 AcquirePlannerLocks(plansource->query_list, true);
742
743 /*
744 * By now, if any invalidation has happened, the inval callback
745 * functions will have marked the query invalid.
746 */
747 if (plansource->is_valid)
748 {
749 /* Successfully revalidated and locked the query. */
750 return NIL;
751 }
752
753 /* Oops, the race case happened. Release useless locks. */
754 AcquirePlannerLocks(plansource->query_list, false);
755 }
756
757 /*
758 * Discard the no-longer-useful rewritten query tree. (Note: we don't
759 * want to do this any earlier, else we'd not have been able to release
760 * locks correctly in the race condition case.)
761 */
762 plansource->is_valid = false;
763 plansource->query_list = NIL;
764 plansource->relationOids = NIL;
765 plansource->invalItems = NIL;
766 plansource->search_path = NULL;
767
768 /*
769 * Free the query_context. We don't really expect MemoryContextDelete to
770 * fail, but just in case, make sure the CachedPlanSource is left in a
771 * reasonably sane state. (The generic plan won't get unlinked yet, but
772 * that's acceptable.)
773 */
774 if (plansource->query_context)
775 {
776 MemoryContext qcxt = plansource->query_context;
777
778 plansource->query_context = NULL;
780 }
781
782 /* Drop the generic plan reference if any */
783 ReleaseGenericPlan(plansource);
784
785 /*
786 * Now re-do parse analysis and rewrite. This not incidentally acquires
787 * the locks we need to do planning safely.
788 */
789 Assert(plansource->is_complete);
790
791 /*
792 * If a snapshot is already set (the normal case), we can just use that
793 * for parsing/planning. But if it isn't, install one. Note: no point in
794 * checking whether parse analysis requires a snapshot; utility commands
795 * don't have invalidatable plans, so we'd not get here for such a
796 * command.
797 */
798 snapshot_set = false;
799 if (!ActiveSnapshotSet())
800 {
802 snapshot_set = true;
803 }
804
805 /*
806 * Run parse analysis (if needed) and rule rewriting.
807 */
808 if (plansource->raw_parse_tree != NULL)
809 {
810 /* Source is raw parse tree */
811 RawStmt *rawtree;
812
813 /*
814 * The parser tends to scribble on its input, so we must copy the raw
815 * parse tree to prevent corruption of the cache.
816 */
817 rawtree = copyObject(plansource->raw_parse_tree);
818 if (plansource->parserSetup != NULL)
819 tlist = pg_analyze_and_rewrite_withcb(rawtree,
820 plansource->query_string,
821 plansource->parserSetup,
822 plansource->parserSetupArg,
823 queryEnv);
824 else
826 plansource->query_string,
827 plansource->param_types,
828 plansource->num_params,
829 queryEnv);
830 }
831 else if (plansource->analyzed_parse_tree != NULL)
832 {
833 /* Source is pre-analyzed query, so we only need to rewrite */
834 Query *analyzed_tree;
835
836 /* The rewriter scribbles on its input, too, so copy */
837 analyzed_tree = copyObject(plansource->analyzed_parse_tree);
838 /* Acquire locks needed before rewriting ... */
839 AcquireRewriteLocks(analyzed_tree, true, false);
840 /* ... and do it */
841 tlist = pg_rewrite_query(analyzed_tree);
842 }
843 else
844 {
845 /* Empty query, nothing to do */
846 tlist = NIL;
847 }
848
849 /* Apply post-rewrite callback if there is one */
850 if (plansource->postRewrite != NULL)
851 plansource->postRewrite(tlist, plansource->postRewriteArg);
852
853 /* Release snapshot if we got one */
854 if (snapshot_set)
856
857 /*
858 * Check or update the result tupdesc.
859 *
860 * We assume the parameter types didn't change from the first time, so no
861 * need to update that.
862 */
863 resultDesc = PlanCacheComputeResultDesc(tlist);
864 if (resultDesc == NULL && plansource->resultDesc == NULL)
865 {
866 /* OK, doesn't return tuples */
867 }
868 else if (resultDesc == NULL || plansource->resultDesc == NULL ||
869 !equalRowTypes(resultDesc, plansource->resultDesc))
870 {
871 /* can we give a better error message? */
872 if (plansource->fixed_result)
874 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
875 errmsg("cached plan must not change result type")));
876 oldcxt = MemoryContextSwitchTo(plansource->context);
877 if (resultDesc)
878 resultDesc = CreateTupleDescCopy(resultDesc);
879 if (plansource->resultDesc)
880 FreeTupleDesc(plansource->resultDesc);
881 plansource->resultDesc = resultDesc;
882 MemoryContextSwitchTo(oldcxt);
883 }
884
885 /*
886 * Allocate new query_context and copy the completed querytree into it.
887 * It's transient until we complete the copying and dependency extraction.
888 */
890 "CachedPlanQuery",
892 oldcxt = MemoryContextSwitchTo(querytree_context);
893
894 qlist = copyObject(tlist);
895
896 /*
897 * Use the planner machinery to extract dependencies. Data is saved in
898 * query_context. (We assume that not a lot of extra cruft is created by
899 * this call.)
900 */
902 &plansource->relationOids,
903 &plansource->invalItems,
904 &plansource->dependsOnRLS);
905
906 /* Update RLS info as well. */
907 plansource->rewriteRoleId = GetUserId();
908 plansource->rewriteRowSecurity = row_security;
909
910 /*
911 * Also save the current search_path in the query_context. (This should
912 * not generate much extra cruft either, since almost certainly the path
913 * is already valid.)
914 */
915 plansource->search_path = GetSearchPathMatcher(querytree_context);
916
917 MemoryContextSwitchTo(oldcxt);
918
919 /* Now reparent the finished query_context and save the links */
920 MemoryContextSetParent(querytree_context, plansource->context);
921
922 plansource->query_context = querytree_context;
923 plansource->query_list = qlist;
924
925 /*
926 * Note: we do not reset generic_cost or total_custom_cost, although we
927 * could choose to do so. If the DDL or statistics change that prompted
928 * the invalidation meant a significant change in the cost estimates, it
929 * would be better to reset those variables and start fresh; but often it
930 * doesn't, and we're better retaining our hard-won knowledge about the
931 * relative costs.
932 */
933
934 plansource->is_valid = true;
935
936 /* Return transient copy of querytrees for possible use in planning */
937 return tlist;
938}
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#define ereport(elevel,...)
Definition: elog.h:150
static void AcquirePlannerLocks(List *stmt_list, bool acquire)
Definition: plancache.c:1965
List * pg_analyze_and_rewrite_withcb(RawStmt *parsetree, const char *query_string, ParserSetupHook parserSetup, void *parserSetupArg, QueryEnvironment *queryEnv)
Definition: postgres.c:763
List * pg_analyze_and_rewrite_fixedparams(RawStmt *parsetree, const char *query_string, const Oid *paramTypes, int numParams, QueryEnvironment *queryEnv)
Definition: postgres.c:670
List * pg_rewrite_query(Query *query)
Definition: postgres.c:803
void AcquireRewriteLocks(Query *parsetree, bool forExecute, bool forUpdatePushedDown)
void FreeTupleDesc(TupleDesc tupdesc)
Definition: tupdesc.c:502
bool equalRowTypes(TupleDesc tupdesc1, TupleDesc tupdesc2)
Definition: tupdesc.c:777

References AcquirePlannerLocks(), AcquireRewriteLocks(), ActiveSnapshotSet(), ALLOCSET_START_SMALL_SIZES, AllocSetContextCreate, CachedPlanSource::analyzed_parse_tree, Assert(), CachedPlanSource::context, copyObject, CreateTupleDescCopy(), CurrentMemoryContext, CachedPlanSource::dependsOnRLS, equalRowTypes(), ereport, errcode(), errmsg(), ERROR, extract_query_dependencies(), CachedPlanSource::fixed_result, FreeTupleDesc(), GetSearchPathMatcher(), GetTransactionSnapshot(), GetUserId(), CachedPlanSource::gplan, CachedPlanSource::invalItems, CachedPlanSource::is_complete, CachedPlanSource::is_oneshot, CachedPlanSource::is_valid, CachedPlan::is_valid, MemoryContextDelete(), MemoryContextSetParent(), MemoryContextSwitchTo(), NIL, CachedPlanSource::num_params, CachedPlanSource::param_types, CachedPlanSource::parserSetup, CachedPlanSource::parserSetupArg, pg_analyze_and_rewrite_fixedparams(), pg_analyze_and_rewrite_withcb(), pg_rewrite_query(), PlanCacheComputeResultDesc(), PopActiveSnapshot(), CachedPlanSource::postRewrite, CachedPlanSource::postRewriteArg, PushActiveSnapshot(), CachedPlanSource::query_context, CachedPlanSource::query_list, CachedPlanSource::query_string, CachedPlanSource::raw_parse_tree, CachedPlanSource::relationOids, ReleaseGenericPlan(), CachedPlanSource::resultDesc, CachedPlanSource::rewriteRoleId, CachedPlanSource::rewriteRowSecurity, row_security, CachedPlanSource::search_path, SearchPathMatchesCurrentEnvironment(), and StmtPlanRequiresRevalidation().

Referenced by BuildCachedPlan(), CachedPlanGetTargetList(), and GetCachedPlan().

◆ SaveCachedPlan()

void SaveCachedPlan ( CachedPlanSource plansource)

Definition at line 545 of file plancache.c.

546{
547 /* Assert caller is doing things in a sane order */
548 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
549 Assert(plansource->is_complete);
550 Assert(!plansource->is_saved);
551
552 /* This seems worth a real test, though */
553 if (plansource->is_oneshot)
554 elog(ERROR, "cannot save one-shot cached plan");
555
556 /*
557 * In typical use, this function would be called before generating any
558 * plans from the CachedPlanSource. If there is a generic plan, moving it
559 * into CacheMemoryContext would be pretty risky since it's unclear
560 * whether the caller has taken suitable care with making references
561 * long-lived. Best thing to do seems to be to discard the plan.
562 */
563 ReleaseGenericPlan(plansource);
564
565 /*
566 * Reparent the source memory context under CacheMemoryContext so that it
567 * will live indefinitely. The query_context follows along since it's
568 * already a child of the other one.
569 */
571
572 /*
573 * Add the entry to the global list of cached plans.
574 */
575 dlist_push_tail(&saved_plan_list, &plansource->node);
576
577 plansource->is_saved = true;
578}

References Assert(), CACHEDPLANSOURCE_MAGIC, CacheMemoryContext, CachedPlanSource::context, dlist_push_tail(), elog, ERROR, CachedPlanSource::is_complete, CachedPlanSource::is_oneshot, CachedPlanSource::is_saved, CachedPlanSource::magic, MemoryContextSetParent(), CachedPlanSource::node, ReleaseGenericPlan(), and saved_plan_list.

Referenced by _SPI_save_plan(), exec_parse_message(), prepare_next_query(), SPI_keepplan(), and StorePreparedStatement().

◆ ScanQueryForLocks()

static void ScanQueryForLocks ( Query parsetree,
bool  acquire 
)
static

Definition at line 1990 of file plancache.c.

1991{
1992 ListCell *lc;
1993
1994 /* Shouldn't get called on utility commands */
1995 Assert(parsetree->commandType != CMD_UTILITY);
1996
1997 /*
1998 * First, process RTEs of the current query level.
1999 */
2000 foreach(lc, parsetree->rtable)
2001 {
2002 RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
2003
2004 switch (rte->rtekind)
2005 {
2006 case RTE_RELATION:
2007 /* Acquire or release the appropriate type of lock */
2008 if (acquire)
2009 LockRelationOid(rte->relid, rte->rellockmode);
2010 else
2011 UnlockRelationOid(rte->relid, rte->rellockmode);
2012 break;
2013
2014 case RTE_SUBQUERY:
2015 /* If this was a view, must lock/unlock the view */
2016 if (OidIsValid(rte->relid))
2017 {
2018 if (acquire)
2019 LockRelationOid(rte->relid, rte->rellockmode);
2020 else
2021 UnlockRelationOid(rte->relid, rte->rellockmode);
2022 }
2023 /* Recurse into subquery-in-FROM */
2024 ScanQueryForLocks(rte->subquery, acquire);
2025 break;
2026
2027 default:
2028 /* ignore other types of RTEs */
2029 break;
2030 }
2031 }
2032
2033 /* Recurse into subquery-in-WITH */
2034 foreach(lc, parsetree->cteList)
2035 {
2037
2038 ScanQueryForLocks(castNode(Query, cte->ctequery), acquire);
2039 }
2040
2041 /*
2042 * Recurse into sublink subqueries, too. But we already did the ones in
2043 * the rtable and cteList.
2044 */
2045 if (parsetree->hasSubLinks)
2046 {
2047 query_tree_walker(parsetree, ScanQueryWalker, &acquire,
2049 }
2050}
#define query_tree_walker(q, w, c, f)
Definition: nodeFuncs.h:158
#define QTW_IGNORE_RC_SUBQUERIES
Definition: nodeFuncs.h:24
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
static bool ScanQueryWalker(Node *node, bool *acquire)
Definition: plancache.c:2056
Query * subquery
Definition: parsenodes.h:1135

References Assert(), castNode, CMD_UTILITY, Query::commandType, Query::cteList, CommonTableExpr::ctequery, lfirst, lfirst_node, LockRelationOid(), OidIsValid, QTW_IGNORE_RC_SUBQUERIES, query_tree_walker, Query::rtable, RTE_RELATION, RTE_SUBQUERY, RangeTblEntry::rtekind, ScanQueryForLocks(), ScanQueryWalker(), RangeTblEntry::subquery, and UnlockRelationOid().

Referenced by AcquireExecutorLocks(), AcquirePlannerLocks(), ScanQueryForLocks(), and ScanQueryWalker().

◆ ScanQueryWalker()

static bool ScanQueryWalker ( Node node,
bool *  acquire 
)
static

Definition at line 2056 of file plancache.c.

2057{
2058 if (node == NULL)
2059 return false;
2060 if (IsA(node, SubLink))
2061 {
2062 SubLink *sub = (SubLink *) node;
2063
2064 /* Do what we came for */
2065 ScanQueryForLocks(castNode(Query, sub->subselect), *acquire);
2066 /* Fall through to process lefthand args of SubLink */
2067 }
2068
2069 /*
2070 * Do NOT recurse into Query nodes, because ScanQueryForLocks already
2071 * processed subselects of subselects for us.
2072 */
2073 return expression_tree_walker(node, ScanQueryWalker, acquire);
2074}
#define expression_tree_walker(n, w, c)
Definition: nodeFuncs.h:153
#define IsA(nodeptr, _type_)
Definition: nodes.h:164

References castNode, expression_tree_walker, IsA, ScanQueryForLocks(), ScanQueryWalker(), and SubLink::subselect.

Referenced by ScanQueryForLocks(), and ScanQueryWalker().

◆ SetPostRewriteHook()

void SetPostRewriteHook ( CachedPlanSource plansource,
PostRewriteHook  postRewrite,
void *  postRewriteArg 
)

Definition at line 520 of file plancache.c.

523{
524 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
525 plansource->postRewrite = postRewrite;
526 plansource->postRewriteArg = postRewriteArg;
527}

References Assert(), CACHEDPLANSOURCE_MAGIC, CachedPlanSource::magic, CachedPlanSource::postRewrite, and CachedPlanSource::postRewriteArg.

Referenced by prepare_next_query().

◆ StmtPlanRequiresRevalidation()

static bool StmtPlanRequiresRevalidation ( CachedPlanSource plansource)
static

Definition at line 640 of file plancache.c.

641{
642 if (plansource->raw_parse_tree != NULL)
644 else if (plansource->analyzed_parse_tree != NULL)
646 /* empty query never needs revalidation */
647 return false;
648}
bool stmt_requires_parse_analysis(RawStmt *parseTree)
Definition: analyze.c:459

References CachedPlanSource::analyzed_parse_tree, query_requires_rewrite_plan(), CachedPlanSource::raw_parse_tree, and stmt_requires_parse_analysis().

Referenced by choose_custom_plan(), CompleteCachedPlan(), PlanCacheObjectCallback(), PlanCacheRelCallback(), ResetPlanCache(), and RevalidateCachedQuery().

Variable Documentation

◆ cached_expression_list

dlist_head cached_expression_list = DLIST_STATIC_INIT(cached_expression_list)
static

◆ plan_cache_mode

int plan_cache_mode = PLAN_CACHE_MODE_AUTO

Definition at line 138 of file plancache.c.

Referenced by choose_custom_plan().

◆ planref_resowner_desc

const ResourceOwnerDesc planref_resowner_desc
static
Initial value:
=
{
.name = "plancache reference",
.release_phase = RESOURCE_RELEASE_AFTER_LOCKS,
.release_priority = RELEASE_PRIO_PLANCACHE_REFS,
.ReleaseResource = ResOwnerReleaseCachedPlan,
.DebugPrint = NULL
}
static void ResOwnerReleaseCachedPlan(Datum res)
Definition: plancache.c:2377
#define RELEASE_PRIO_PLANCACHE_REFS
Definition: resowner.h:73
@ RESOURCE_RELEASE_AFTER_LOCKS
Definition: resowner.h:56

Definition at line 115 of file plancache.c.

Referenced by ReleaseAllPlanCacheRefsInOwner(), ResourceOwnerForgetPlanCacheRef(), and ResourceOwnerRememberPlanCacheRef().

◆ saved_plan_list

dlist_head saved_plan_list = DLIST_STATIC_INIT(saved_plan_list)
static