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 1910 of file plancache.c.

1911{
1912 ListCell *lc1;
1913
1914 foreach(lc1, stmt_list)
1915 {
1916 PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc1);
1917 ListCell *lc2;
1918
1919 if (plannedstmt->commandType == CMD_UTILITY)
1920 {
1921 /*
1922 * Ignore utility statements, except those (such as EXPLAIN) that
1923 * contain a parsed-but-not-planned query. Note: it's okay to use
1924 * ScanQueryForLocks, even though the query hasn't been through
1925 * rule rewriting, because rewriting doesn't change the query
1926 * representation.
1927 */
1928 Query *query = UtilityContainsQuery(plannedstmt->utilityStmt);
1929
1930 if (query)
1931 ScanQueryForLocks(query, acquire);
1932 continue;
1933 }
1934
1935 foreach(lc2, plannedstmt->rtable)
1936 {
1937 RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
1938
1939 if (!(rte->rtekind == RTE_RELATION ||
1940 (rte->rtekind == RTE_SUBQUERY && OidIsValid(rte->relid))))
1941 continue;
1942
1943 /*
1944 * Acquire the appropriate type of lock on each relation OID. Note
1945 * that we don't actually try to open the rel, and hence will not
1946 * fail if it's been dropped entirely --- we'll just transiently
1947 * acquire a non-conflicting lock.
1948 */
1949 if (acquire)
1950 LockRelationOid(rte->relid, rte->rellockmode);
1951 else
1952 UnlockRelationOid(rte->relid, rte->rellockmode);
1953 }
1954 }
1955}
#define OidIsValid(objectId)
Definition: c.h:777
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:1991
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 1966 of file plancache.c.

1967{
1968 ListCell *lc;
1969
1970 foreach(lc, stmt_list)
1971 {
1972 Query *query = lfirst_node(Query, lc);
1973
1974 if (query->commandType == CMD_UTILITY)
1975 {
1976 /* Ignore utility statements, unless they contain a Query */
1977 query = UtilityContainsQuery(query->utilityStmt);
1978 if (query)
1979 ScanQueryForLocks(query, acquire);
1980 continue;
1981 }
1982
1983 ScanQueryForLocks(query, acquire);
1984 }
1985}
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 */
1122 plan = (CachedPlan *) palloc(sizeof(CachedPlan));
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}
Assert(PointerIsAligned(start, uint64))
void * palloc(Size size)
Definition: mcxt.c:1365
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(), 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 1778 of file plancache.c.

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

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

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:1910
#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 = (Oid *) palloc(num_params * sizeof(Oid));
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}
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:2085
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(), 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 = (CachedPlanSource *) palloc0(sizeof(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 = (Oid *)
1704 palloc(plansource->num_params * sizeof(Oid));
1705 memcpy(newsource->param_types, plansource->param_types,
1706 plansource->num_params * sizeof(Oid));
1707 }
1708 else
1709 newsource->param_types = NULL;
1710 newsource->num_params = plansource->num_params;
1711 newsource->parserSetup = plansource->parserSetup;
1712 newsource->parserSetupArg = plansource->parserSetupArg;
1713 newsource->postRewrite = plansource->postRewrite;
1714 newsource->postRewriteArg = plansource->postRewriteArg;
1715 newsource->cursor_options = plansource->cursor_options;
1716 newsource->fixed_result = plansource->fixed_result;
1717 if (plansource->resultDesc)
1718 newsource->resultDesc = CreateTupleDescCopy(plansource->resultDesc);
1719 else
1720 newsource->resultDesc = NULL;
1721 newsource->context = source_context;
1722
1723 querytree_context = AllocSetContextCreate(source_context,
1724 "CachedPlanQuery",
1726 MemoryContextSwitchTo(querytree_context);
1727 newsource->query_list = copyObject(plansource->query_list);
1728 newsource->relationOids = copyObject(plansource->relationOids);
1729 newsource->invalItems = copyObject(plansource->invalItems);
1730 if (plansource->search_path)
1731 newsource->search_path = CopySearchPathMatcher(plansource->search_path);
1732 newsource->query_context = querytree_context;
1733 newsource->rewriteRoleId = plansource->rewriteRoleId;
1734 newsource->rewriteRowSecurity = plansource->rewriteRowSecurity;
1735 newsource->dependsOnRLS = plansource->dependsOnRLS;
1736
1737 newsource->gplan = NULL;
1738
1739 newsource->is_oneshot = false;
1740 newsource->is_complete = true;
1741 newsource->is_saved = false;
1742 newsource->is_valid = plansource->is_valid;
1743 newsource->generation = plansource->generation;
1744
1745 /* We may as well copy any acquired cost knowledge */
1746 newsource->generic_cost = plansource->generic_cost;
1747 newsource->total_custom_cost = plansource->total_custom_cost;
1748 newsource->num_generic_plans = plansource->num_generic_plans;
1749 newsource->num_custom_plans = plansource->num_custom_plans;
1750
1751 MemoryContextSwitchTo(oldcxt);
1752
1753 return newsource;
1754}
char * pstrdup(const char *in)
Definition: mcxt.c:1759
void * palloc0(Size size)
Definition: mcxt.c:1395
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, palloc(), palloc0(), 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 = (CachedPlanSource *) palloc0(sizeof(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(), 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 = (CachedPlanSource *) palloc0(sizeof(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(), 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 1872 of file plancache.c.

1873{
1874 /* Sanity check */
1875 Assert(cexpr->magic == CACHEDEXPR_MAGIC);
1876 /* Unlink from global list */
1877 dlist_delete(&cexpr->node);
1878 /* Free all storage associated with CachedExpression */
1880}
#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 1815 of file plancache.c.

1816{
1817 CachedExpression *cexpr;
1818 List *relationOids;
1819 List *invalItems;
1820 MemoryContext cexpr_context;
1821 MemoryContext oldcxt;
1822
1823 /*
1824 * Pass the expression through the planner, and collect dependencies.
1825 * Everything built here is leaked in the caller's context; that's
1826 * intentional to minimize the size of the permanent data structure.
1827 */
1828 expr = (Node *) expression_planner_with_deps((Expr *) expr,
1829 &relationOids,
1830 &invalItems);
1831
1832 /*
1833 * Make a private memory context, and copy what we need into that. To
1834 * avoid leaking a long-lived context if we fail while copying data, we
1835 * initially make the context under the caller's context.
1836 */
1838 "CachedExpression",
1840
1841 oldcxt = MemoryContextSwitchTo(cexpr_context);
1842
1843 cexpr = (CachedExpression *) palloc(sizeof(CachedExpression));
1844 cexpr->magic = CACHEDEXPR_MAGIC;
1845 cexpr->expr = copyObject(expr);
1846 cexpr->is_valid = true;
1847 cexpr->relationOids = copyObject(relationOids);
1848 cexpr->invalItems = copyObject(invalItems);
1849 cexpr->context = cexpr_context;
1850
1851 MemoryContextSwitchTo(oldcxt);
1852
1853 /*
1854 * Reparent the expr's memory context under CacheMemoryContext so that it
1855 * will live indefinitely.
1856 */
1858
1859 /*
1860 * Add the entry to the global list of cached expressions.
1861 */
1863
1864 return cexpr;
1865}
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(), 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:2314
static void PlanCacheObjectCallback(Datum arg, int cacheid, uint32 hashvalue)
Definition: plancache.c:2205
static void PlanCacheRelCallback(Datum arg, Oid relid)
Definition: plancache.c:2121
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 2085 of file plancache.c.

2086{
2087 Query *query;
2088
2089 switch (ChoosePortalStrategy(stmt_list))
2090 {
2091 case PORTAL_ONE_SELECT:
2093 query = linitial_node(Query, stmt_list);
2094 return ExecCleanTypeFromTL(query->targetList);
2095
2097 query = QueryListGetPrimaryStmt(stmt_list);
2098 Assert(query->returningList);
2099 return ExecCleanTypeFromTL(query->returningList);
2100
2101 case PORTAL_UTIL_SELECT:
2102 query = linitial_node(Query, stmt_list);
2103 Assert(query->utilityStmt);
2104 return UtilityTupleDescriptor(query->utilityStmt);
2105
2106 case PORTAL_MULTI_QUERY:
2107 /* will not return tuples */
2108 break;
2109 }
2110 return NULL;
2111}
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 2205 of file plancache.c.

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

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

2315{
2317}
void ResetPlanCache(void)
Definition: plancache.c:2323

References ResetPlanCache().

Referenced by InitPlanCache().

◆ QueryListGetPrimaryStmt()

static Query * QueryListGetPrimaryStmt ( List stmts)
static

Definition at line 1891 of file plancache.c.

1892{
1893 ListCell *lc;
1894
1895 foreach(lc, stmts)
1896 {
1897 Query *stmt = lfirst_node(Query, lc);
1898
1899 if (stmt->canSetTag)
1900 return stmt;
1901 }
1902 return NULL;
1903}
#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 2370 of file plancache.c.

2371{
2373}
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 2323 of file plancache.c.

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

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 2378 of file plancache.c.

2379{
2381}
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:1966
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 1991 of file plancache.c.

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

2058{
2059 if (node == NULL)
2060 return false;
2061 if (IsA(node, SubLink))
2062 {
2063 SubLink *sub = (SubLink *) node;
2064
2065 /* Do what we came for */
2066 ScanQueryForLocks(castNode(Query, sub->subselect), *acquire);
2067 /* Fall through to process lefthand args of SubLink */
2068 }
2069
2070 /*
2071 * Do NOT recurse into Query nodes, because ScanQueryForLocks already
2072 * processed subselects of subselects for us.
2073 */
2074 return expression_tree_walker(node, ScanQueryWalker, acquire);
2075}
#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:2378
#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