PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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 1887 of file plancache.c.

1888{
1889 ListCell *lc1;
1890
1891 foreach(lc1, stmt_list)
1892 {
1893 PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc1);
1894 ListCell *lc2;
1895
1896 if (plannedstmt->commandType == CMD_UTILITY)
1897 {
1898 /*
1899 * Ignore utility statements, except those (such as EXPLAIN) that
1900 * contain a parsed-but-not-planned query. Note: it's okay to use
1901 * ScanQueryForLocks, even though the query hasn't been through
1902 * rule rewriting, because rewriting doesn't change the query
1903 * representation.
1904 */
1905 Query *query = UtilityContainsQuery(plannedstmt->utilityStmt);
1906
1907 if (query)
1908 ScanQueryForLocks(query, acquire);
1909 continue;
1910 }
1911
1912 foreach(lc2, plannedstmt->rtable)
1913 {
1914 RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
1915
1916 if (!(rte->rtekind == RTE_RELATION ||
1917 (rte->rtekind == RTE_SUBQUERY && OidIsValid(rte->relid))))
1918 continue;
1919
1920 /*
1921 * Acquire the appropriate type of lock on each relation OID. Note
1922 * that we don't actually try to open the rel, and hence will not
1923 * fail if it's been dropped entirely --- we'll just transiently
1924 * acquire a non-conflicting lock.
1925 */
1926 if (acquire)
1927 LockRelationOid(rte->relid, rte->rellockmode);
1928 else
1929 UnlockRelationOid(rte->relid, rte->rellockmode);
1930 }
1931 }
1932}
#define OidIsValid(objectId)
Definition: c.h:746
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:276
@ RTE_SUBQUERY
Definition: parsenodes.h:1027
@ RTE_RELATION
Definition: parsenodes.h:1026
#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:1968
CmdType commandType
Definition: plannodes.h:53
Node * utilityStmt
Definition: plannodes.h:132
List * rtable
Definition: plannodes.h:91
RTEKind rtekind
Definition: parsenodes.h:1061
Query * UtilityContainsQuery(Node *parsetree)
Definition: utility.c:2179

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

1944{
1945 ListCell *lc;
1946
1947 foreach(lc, stmt_list)
1948 {
1949 Query *query = lfirst_node(Query, lc);
1950
1951 if (query->commandType == CMD_UTILITY)
1952 {
1953 /* Ignore utility statements, unless they contain a Query */
1954 query = UtilityContainsQuery(query->utilityStmt);
1955 if (query)
1956 ScanQueryForLocks(query, acquire);
1957 continue;
1958 }
1959
1960 ScanQueryForLocks(query, acquire);
1961 }
1962}
CmdType commandType
Definition: parsenodes.h:121
Node * utilityStmt
Definition: parsenodes.h:136

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

1021{
1023 List *plist;
1024 bool snapshot_set;
1025 bool is_transient;
1026 MemoryContext plan_context;
1028 ListCell *lc;
1029
1030 /*
1031 * Normally the querytree should be valid already, but if it's not,
1032 * rebuild it.
1033 *
1034 * NOTE: GetCachedPlan should have called RevalidateCachedQuery first, so
1035 * we ought to be holding sufficient locks to prevent any invalidation.
1036 * However, if we're building a custom plan after having built and
1037 * rejected a generic plan, it's possible to reach here with is_valid
1038 * false due to an invalidation while making the generic plan. In theory
1039 * the invalidation must be a false positive, perhaps a consequence of an
1040 * sinval reset event or the debug_discard_caches code. But for safety,
1041 * let's treat it as real and redo the RevalidateCachedQuery call.
1042 */
1043 if (!plansource->is_valid)
1044 qlist = RevalidateCachedQuery(plansource, queryEnv);
1045
1046 /*
1047 * If we don't already have a copy of the querytree list that can be
1048 * scribbled on by the planner, make one. For a one-shot plan, we assume
1049 * it's okay to scribble on the original query_list.
1050 */
1051 if (qlist == NIL)
1052 {
1053 if (!plansource->is_oneshot)
1054 qlist = copyObject(plansource->query_list);
1055 else
1056 qlist = plansource->query_list;
1057 }
1058
1059 /*
1060 * If a snapshot is already set (the normal case), we can just use that
1061 * for planning. But if it isn't, and we need one, install one.
1062 */
1063 snapshot_set = false;
1064 if (!ActiveSnapshotSet() &&
1065 BuildingPlanRequiresSnapshot(plansource))
1066 {
1068 snapshot_set = true;
1069 }
1070
1071 /*
1072 * Generate the plan.
1073 */
1074 plist = pg_plan_queries(qlist, plansource->query_string,
1075 plansource->cursor_options, boundParams);
1076
1077 /* Release snapshot if we got one */
1078 if (snapshot_set)
1080
1081 /*
1082 * Normally we make a dedicated memory context for the CachedPlan and its
1083 * subsidiary data. (It's probably not going to be large, but just in
1084 * case, allow it to grow large. It's transient for the moment.) But for
1085 * a one-shot plan, we just leave it in the caller's memory context.
1086 */
1087 if (!plansource->is_oneshot)
1088 {
1090 "CachedPlan",
1092 MemoryContextCopyAndSetIdentifier(plan_context, plansource->query_string);
1093
1094 /*
1095 * Copy plan into the new context.
1096 */
1097 MemoryContextSwitchTo(plan_context);
1098
1099 plist = copyObject(plist);
1100 }
1101 else
1102 plan_context = CurrentMemoryContext;
1103
1104 /*
1105 * Create and fill the CachedPlan struct within the new context.
1106 */
1107 plan = (CachedPlan *) palloc(sizeof(CachedPlan));
1108 plan->magic = CACHEDPLAN_MAGIC;
1109 plan->stmt_list = plist;
1110
1111 /*
1112 * CachedPlan is dependent on role either if RLS affected the rewrite
1113 * phase or if a role dependency was injected during planning. And it's
1114 * transient if any plan is marked so.
1115 */
1116 plan->planRoleId = GetUserId();
1117 plan->dependsOnRole = plansource->dependsOnRLS;
1118 is_transient = false;
1119 foreach(lc, plist)
1120 {
1121 PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
1122
1123 if (plannedstmt->commandType == CMD_UTILITY)
1124 continue; /* Ignore utility statements */
1125
1126 if (plannedstmt->transientPlan)
1127 is_transient = true;
1128 if (plannedstmt->dependsOnRole)
1129 plan->dependsOnRole = true;
1130 }
1131 if (is_transient)
1132 {
1134 plan->saved_xmin = TransactionXmin;
1135 }
1136 else
1137 plan->saved_xmin = InvalidTransactionId;
1138 plan->refcount = 0;
1139 plan->context = plan_context;
1140 plan->is_oneshot = plansource->is_oneshot;
1141 plan->is_saved = false;
1142 plan->is_valid = true;
1143
1144 /* assign generation number to new plan */
1145 plan->generation = ++(plansource->generation);
1146
1147 MemoryContextSwitchTo(oldcxt);
1148
1149 return plan;
1150}
Assert(PointerIsAligned(start, uint64))
void * palloc(Size size)
Definition: mcxt.c:1321
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
#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:520
#define copyObject(obj)
Definition: nodes.h:230
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:667
static bool BuildingPlanRequiresSnapshot(CachedPlanSource *plansource)
Definition: plancache.c:642
#define CACHEDPLAN_MAGIC
Definition: plancache.h:45
List * pg_plan_queries(List *querytrees, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition: postgres.c:970
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:271
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:669
TransactionId TransactionXmin
Definition: snapmgr.c:158
bool ActiveSnapshotSet(void)
Definition: snapmgr.c:799
void PopActiveSnapshot(void)
Definition: snapmgr.c:762
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:71
bool dependsOnRole
Definition: plannodes.h:74
#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 642 of file plancache.c.

643{
644 if (plansource->raw_parse_tree != NULL)
645 return analyze_requires_snapshot(plansource->raw_parse_tree);
646 else if (plansource->analyzed_parse_tree != NULL)
648 /* empty query never needs a snapshot */
649 return false;
650}
bool analyze_requires_snapshot(RawStmt *parseTree)
Definition: analyze.c:571
bool query_requires_rewrite_plan(Query *query)
Definition: analyze.c:600
struct Query * analyzed_parse_tree
Definition: plancache.h:109
struct 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 1215 of file plancache.c.

1216{
1217 double result = 0;
1218 ListCell *lc;
1219
1220 foreach(lc, plan->stmt_list)
1221 {
1222 PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
1223
1224 if (plannedstmt->commandType == CMD_UTILITY)
1225 continue; /* Ignore utility statements */
1226
1227 result += plannedstmt->planTree->total_cost;
1228
1229 if (include_planner)
1230 {
1231 /*
1232 * Currently we use a very crude estimate of planning effort based
1233 * on the number of relations in the finished plan's rangetable.
1234 * Join planning effort actually scales much worse than linearly
1235 * in the number of relations --- but only until the join collapse
1236 * limits kick in. Also, while inheritance child relations surely
1237 * add to planning effort, they don't make the join situation
1238 * worse. So the actual shape of the planning cost curve versus
1239 * number of relations isn't all that obvious. It will take
1240 * considerable work to arrive at a less crude estimate, and for
1241 * now it's not clear that's worth doing.
1242 *
1243 * The other big difficulty here is that we don't have any very
1244 * good model of how planning cost compares to execution costs.
1245 * The current multiplier of 1000 * cpu_operator_cost is probably
1246 * on the low side, but we'll try this for awhile before making a
1247 * more aggressive correction.
1248 *
1249 * If we ever do write a more complicated estimator, it should
1250 * probably live in src/backend/optimizer/ not here.
1251 */
1252 int nrelations = list_length(plannedstmt->rtable);
1253
1254 result += 1000.0 * cpu_operator_cost * (nrelations + 1);
1255 }
1256 }
1257
1258 return result;
1259}
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:172
struct Plan * planTree
Definition: plannodes.h:83

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

1450{
1451 ListCell *lc;
1452
1453 /*
1454 * Sanity-check that the caller gave us a validated generic plan. Notice
1455 * that we *don't* assert plansource->is_valid as you might expect; that's
1456 * because it's possible that that's already false when GetCachedPlan
1457 * returns, e.g. because ResetPlanCache happened partway through. We
1458 * should accept the plan as long as plan->is_valid is true, and expect to
1459 * replan after the next CachedPlanIsSimplyValid call.
1460 */
1461 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1462 Assert(plan->magic == CACHEDPLAN_MAGIC);
1463 Assert(plan->is_valid);
1464 Assert(plan == plansource->gplan);
1465 Assert(plansource->search_path != NULL);
1467
1468 /* We don't support oneshot plans here. */
1469 if (plansource->is_oneshot)
1470 return false;
1471 Assert(!plan->is_oneshot);
1472
1473 /*
1474 * If the plan is dependent on RLS considerations, or it's transient,
1475 * reject. These things probably can't ever happen for table-free
1476 * queries, but for safety's sake let's check.
1477 */
1478 if (plansource->dependsOnRLS)
1479 return false;
1480 if (plan->dependsOnRole)
1481 return false;
1482 if (TransactionIdIsValid(plan->saved_xmin))
1483 return false;
1484
1485 /*
1486 * Reject if AcquirePlannerLocks would have anything to do. This is
1487 * simplistic, but there's no need to inquire any more carefully; indeed,
1488 * for current callers it shouldn't even be possible to hit any of these
1489 * checks.
1490 */
1491 foreach(lc, plansource->query_list)
1492 {
1493 Query *query = lfirst_node(Query, lc);
1494
1495 if (query->commandType == CMD_UTILITY)
1496 return false;
1497 if (query->rtable || query->cteList || query->hasSubLinks)
1498 return false;
1499 }
1500
1501 /*
1502 * Reject if AcquireExecutorLocks would have anything to do. This is
1503 * probably unnecessary given the previous check, but let's be safe.
1504 */
1505 foreach(lc, plan->stmt_list)
1506 {
1507 PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
1508 ListCell *lc2;
1509
1510 if (plannedstmt->commandType == CMD_UTILITY)
1511 return false;
1512
1513 /*
1514 * We have to grovel through the rtable because it's likely to contain
1515 * an RTE_RESULT relation, rather than being totally empty.
1516 */
1517 foreach(lc2, plannedstmt->rtable)
1518 {
1519 RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
1520
1521 if (rte->rtekind == RTE_RELATION)
1522 return false;
1523 }
1524 }
1525
1526 /*
1527 * Okay, it's simple. Note that what we've primarily established here is
1528 * that no locks need be taken before checking the plan's is_valid flag.
1529 */
1530
1531 /* Bump refcount if requested. */
1532 if (owner)
1533 {
1534 ResourceOwnerEnlarge(owner);
1535 plan->refcount++;
1537 }
1538
1539 return true;
1540}
bool SearchPathMatchesCurrentEnvironment(SearchPathMatcher *path)
Definition: namespace.c:3911
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:452
struct CachedPlan * gplan
Definition: plancache.h:133
struct SearchPathMatcher * search_path
Definition: plancache.h:126
List * cteList
Definition: parsenodes.h:168
List * rtable
Definition: parsenodes.h:170
#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 1755 of file plancache.c.

1757{
1758 Query *pstmt;
1759
1760 /* Assert caller is doing things in a sane order */
1761 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1762 Assert(plansource->is_complete);
1763
1764 /*
1765 * No work needed if statement doesn't return tuples (we assume this
1766 * feature cannot be changed by an invalidation)
1767 */
1768 if (plansource->resultDesc == NULL)
1769 return NIL;
1770
1771 /* Make sure the querytree list is valid and we have parse-time locks */
1772 RevalidateCachedQuery(plansource, queryEnv);
1773
1774 /* Get the primary statement and find out what it returns */
1775 pstmt = QueryListGetPrimaryStmt(plansource->query_list);
1776
1777 return FetchStatementTargetList((Node *) pstmt);
1778}
static Query * QueryListGetPrimaryStmt(List *stmts)
Definition: plancache.c:1868
List * FetchStatementTargetList(Node *stmt)
Definition: pquery.c:349
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 1563 of file plancache.c.

1565{
1566 /*
1567 * Careful here: since the caller doesn't necessarily hold a refcount on
1568 * the plan to start with, it's possible that "plan" is a dangling
1569 * pointer. Don't dereference it until we've verified that it still
1570 * matches the plansource's gplan (which is either valid or NULL).
1571 */
1572 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1573
1574 /*
1575 * Has cache invalidation fired on this plan? We can check this right
1576 * away since there are no locks that we'd need to acquire first. Note
1577 * that here we *do* check plansource->is_valid, so as to force plan
1578 * rebuild if that's become false.
1579 */
1580 if (!plansource->is_valid ||
1581 plan == NULL || plan != plansource->gplan ||
1582 !plan->is_valid)
1583 return false;
1584
1585 Assert(plan->magic == CACHEDPLAN_MAGIC);
1586
1587 /* Is the search_path still the same as when we made it? */
1588 Assert(plansource->search_path != NULL);
1590 return false;
1591
1592 /* It's still good. Bump refcount if requested. */
1593 if (owner)
1594 {
1595 ResourceOwnerEnlarge(owner);
1596 plan->refcount++;
1598 }
1599
1600 return true;
1601}

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

1743{
1744 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1745 return plansource->is_valid;
1746}

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

1612{
1613 /* Assert caller is doing things in a sane order */
1614 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1615 Assert(plansource->is_complete);
1616
1617 /* These seem worth real tests, though */
1618 if (plansource->is_saved)
1619 elog(ERROR, "cannot move a saved cached plan to another context");
1620 if (plansource->is_oneshot)
1621 elog(ERROR, "cannot move a one-shot cached plan to another context");
1622
1623 /* OK, let the caller keep the plan where he wishes */
1624 MemoryContextSetParent(plansource->context, newcontext);
1625
1626 /*
1627 * The query_context needs no special handling, since it's a child of
1628 * plansource->context. But if there's a generic plan, it should be
1629 * maintained as a sibling of plansource->context.
1630 */
1631 if (plansource->gplan)
1632 {
1633 Assert(plansource->gplan->magic == CACHEDPLAN_MAGIC);
1634 MemoryContextSetParent(plansource->gplan->context, newcontext);
1635 }
1636}
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
void MemoryContextSetParent(MemoryContext context, MemoryContext new_parent)
Definition: mcxt.c:637
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 935 of file plancache.c.

936{
937 CachedPlan *plan = plansource->gplan;
938
939 /* Assert that caller checked the querytree */
940 Assert(plansource->is_valid);
941
942 /* If there's no generic plan, just say "false" */
943 if (!plan)
944 return false;
945
946 Assert(plan->magic == CACHEDPLAN_MAGIC);
947 /* Generic plans are never one-shot */
948 Assert(!plan->is_oneshot);
949
950 /*
951 * If plan isn't valid for current role, we can't use it.
952 */
953 if (plan->is_valid && plan->dependsOnRole &&
954 plan->planRoleId != GetUserId())
955 plan->is_valid = false;
956
957 /*
958 * If it appears valid, acquire locks and recheck; this is much the same
959 * logic as in RevalidateCachedQuery, but for a plan.
960 */
961 if (plan->is_valid)
962 {
963 /*
964 * Plan must have positive refcount because it is referenced by
965 * plansource; so no need to fear it disappears under us here.
966 */
967 Assert(plan->refcount > 0);
968
969 AcquireExecutorLocks(plan->stmt_list, true);
970
971 /*
972 * If plan was transient, check to see if TransactionXmin has
973 * advanced, and if so invalidate it.
974 */
975 if (plan->is_valid &&
976 TransactionIdIsValid(plan->saved_xmin) &&
978 plan->is_valid = false;
979
980 /*
981 * By now, if any invalidation has happened, the inval callback
982 * functions will have marked the plan invalid.
983 */
984 if (plan->is_valid)
985 {
986 /* Successfully revalidated and locked the query. */
987 return true;
988 }
989
990 /* Oops, the race case happened. Release useless locks. */
991 AcquireExecutorLocks(plan->stmt_list, false);
992 }
993
994 /*
995 * Plan has been invalidated, so unlink it from the parent and release it.
996 */
997 ReleaseGenericPlan(plansource);
998
999 return false;
1000}
static void ReleaseGenericPlan(CachedPlanSource *plansource)
Definition: plancache.c:603
static void AcquireExecutorLocks(List *stmt_list, bool acquire)
Definition: plancache.c:1887
#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 1158 of file plancache.c.

1159{
1160 double avg_custom_cost;
1161
1162 /* One-shot plans will always be considered custom */
1163 if (plansource->is_oneshot)
1164 return true;
1165
1166 /* Otherwise, never any point in a custom plan if there's no parameters */
1167 if (boundParams == NULL)
1168 return false;
1169 /* ... nor when planning would be a no-op */
1170 if (!StmtPlanRequiresRevalidation(plansource))
1171 return false;
1172
1173 /* Let settings force the decision */
1175 return false;
1177 return true;
1178
1179 /* See if caller wants to force the decision */
1180 if (plansource->cursor_options & CURSOR_OPT_GENERIC_PLAN)
1181 return false;
1182 if (plansource->cursor_options & CURSOR_OPT_CUSTOM_PLAN)
1183 return true;
1184
1185 /* Generate custom plans until we have done at least 5 (arbitrary) */
1186 if (plansource->num_custom_plans < 5)
1187 return true;
1188
1189 avg_custom_cost = plansource->total_custom_cost / plansource->num_custom_plans;
1190
1191 /*
1192 * Prefer generic plan if it's less expensive than the average custom
1193 * plan. (Because we include a charge for cost of planning in the
1194 * custom-plan costs, this means the generic plan only has to be less
1195 * expensive than the execution cost plus replan cost of the custom
1196 * plans.)
1197 *
1198 * Note that if generic_cost is -1 (indicating we've not yet determined
1199 * the generic plan cost), we'll always prefer generic at this point.
1200 */
1201 if (plansource->generic_cost < avg_custom_cost)
1202 return false;
1203
1204 return true;
1205}
#define CURSOR_OPT_GENERIC_PLAN
Definition: parsenodes.h:3383
#define CURSOR_OPT_CUSTOM_PLAN
Definition: parsenodes.h:3384
int plan_cache_mode
Definition: plancache.c:138
static bool StmtPlanRequiresRevalidation(CachedPlanSource *plansource)
Definition: plancache.c:625
@ 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. Also save
467 * the result tuple descriptor.
468 */
469 MemoryContextSwitchTo(source_context);
470
471 if (num_params > 0)
472 {
473 plansource->param_types = (Oid *) palloc(num_params * sizeof(Oid));
474 memcpy(plansource->param_types, param_types, num_params * sizeof(Oid));
475 }
476 else
477 plansource->param_types = NULL;
478 plansource->num_params = num_params;
479 plansource->parserSetup = parserSetup;
480 plansource->parserSetupArg = parserSetupArg;
481 plansource->cursor_options = cursor_options;
482 plansource->fixed_result = fixed_result;
483 plansource->resultDesc = PlanCacheComputeResultDesc(querytree_list);
484
485 MemoryContextSwitchTo(oldcxt);
486
487 plansource->is_complete = true;
488 plansource->is_valid = true;
489}
bool row_security
Definition: guc_tables.c:527
SearchPathMatcher * GetSearchPathMatcher(MemoryContext context)
Definition: namespace.c:3852
static TupleDesc PlanCacheComputeResultDesc(List *stmt_list)
Definition: plancache.c:2062
unsigned int Oid
Definition: postgres_ext.h:30
void extract_query_dependencies(Node *query, List **relationOids, List **invalItems, bool *hasRowSecurity)
Definition: setrefs.c:3631
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

References ALLOCSET_START_SMALL_SIZES, AllocSetContextCreate, Assert(), CACHEDPLANSOURCE_MAGIC, CachedPlanSource::context, copyObject, 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 1648 of file plancache.c.

1649{
1650 CachedPlanSource *newsource;
1651 MemoryContext source_context;
1652 MemoryContext querytree_context;
1653 MemoryContext oldcxt;
1654
1655 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1656 Assert(plansource->is_complete);
1657
1658 /*
1659 * One-shot plans can't be copied, because we haven't taken care that
1660 * parsing/planning didn't scribble on the raw parse tree or querytrees.
1661 */
1662 if (plansource->is_oneshot)
1663 elog(ERROR, "cannot copy a one-shot cached plan");
1664
1666 "CachedPlanSource",
1668
1669 oldcxt = MemoryContextSwitchTo(source_context);
1670
1671 newsource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
1672 newsource->magic = CACHEDPLANSOURCE_MAGIC;
1673 newsource->raw_parse_tree = copyObject(plansource->raw_parse_tree);
1674 newsource->analyzed_parse_tree = copyObject(plansource->analyzed_parse_tree);
1675 newsource->query_string = pstrdup(plansource->query_string);
1676 MemoryContextSetIdentifier(source_context, newsource->query_string);
1677 newsource->commandTag = plansource->commandTag;
1678 if (plansource->num_params > 0)
1679 {
1680 newsource->param_types = (Oid *)
1681 palloc(plansource->num_params * sizeof(Oid));
1682 memcpy(newsource->param_types, plansource->param_types,
1683 plansource->num_params * sizeof(Oid));
1684 }
1685 else
1686 newsource->param_types = NULL;
1687 newsource->num_params = plansource->num_params;
1688 newsource->parserSetup = plansource->parserSetup;
1689 newsource->parserSetupArg = plansource->parserSetupArg;
1690 newsource->postRewrite = plansource->postRewrite;
1691 newsource->postRewriteArg = plansource->postRewriteArg;
1692 newsource->cursor_options = plansource->cursor_options;
1693 newsource->fixed_result = plansource->fixed_result;
1694 if (plansource->resultDesc)
1695 newsource->resultDesc = CreateTupleDescCopy(plansource->resultDesc);
1696 else
1697 newsource->resultDesc = NULL;
1698 newsource->context = source_context;
1699
1700 querytree_context = AllocSetContextCreate(source_context,
1701 "CachedPlanQuery",
1703 MemoryContextSwitchTo(querytree_context);
1704 newsource->query_list = copyObject(plansource->query_list);
1705 newsource->relationOids = copyObject(plansource->relationOids);
1706 newsource->invalItems = copyObject(plansource->invalItems);
1707 if (plansource->search_path)
1708 newsource->search_path = CopySearchPathMatcher(plansource->search_path);
1709 newsource->query_context = querytree_context;
1710 newsource->rewriteRoleId = plansource->rewriteRoleId;
1711 newsource->rewriteRowSecurity = plansource->rewriteRowSecurity;
1712 newsource->dependsOnRLS = plansource->dependsOnRLS;
1713
1714 newsource->gplan = NULL;
1715
1716 newsource->is_oneshot = false;
1717 newsource->is_complete = true;
1718 newsource->is_saved = false;
1719 newsource->is_valid = plansource->is_valid;
1720 newsource->generation = plansource->generation;
1721
1722 /* We may as well copy any acquired cost knowledge */
1723 newsource->generic_cost = plansource->generic_cost;
1724 newsource->total_custom_cost = plansource->total_custom_cost;
1725 newsource->num_generic_plans = plansource->num_generic_plans;
1726 newsource->num_custom_plans = plansource->num_custom_plans;
1727
1728 MemoryContextSwitchTo(oldcxt);
1729
1730 return newsource;
1731}
char * pstrdup(const char *in)
Definition: mcxt.c:1703
void * palloc0(Size size)
Definition: mcxt.c:1351
void MemoryContextSetIdentifier(MemoryContext context, const char *id)
Definition: mcxt.c:612
SearchPathMatcher * CopySearchPathMatcher(SearchPathMatcher *path)
Definition: namespace.c:3889
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
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:245

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:35

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

575{
576 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
577
578 /* If it's been saved, remove it from the list */
579 if (plansource->is_saved)
580 {
581 dlist_delete(&plansource->node);
582 plansource->is_saved = false;
583 }
584
585 /* Decrement generic CachedPlan's refcount and drop if no longer needed */
586 ReleaseGenericPlan(plansource);
587
588 /* Mark it no longer valid */
589 plansource->magic = 0;
590
591 /*
592 * Remove the CachedPlanSource and all subsidiary data (including the
593 * query_context if any). But if it's a one-shot we can't free anything.
594 */
595 if (!plansource->is_oneshot)
596 MemoryContextDelete(plansource->context);
597}
static void dlist_delete(dlist_node *node)
Definition: ilist.h:405
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
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 1849 of file plancache.c.

1850{
1851 /* Sanity check */
1852 Assert(cexpr->magic == CACHEDEXPR_MAGIC);
1853 /* Unlink from global list */
1854 dlist_delete(&cexpr->node);
1855 /* Free all storage associated with CachedExpression */
1857}
#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 1792 of file plancache.c.

1793{
1794 CachedExpression *cexpr;
1795 List *relationOids;
1796 List *invalItems;
1797 MemoryContext cexpr_context;
1798 MemoryContext oldcxt;
1799
1800 /*
1801 * Pass the expression through the planner, and collect dependencies.
1802 * Everything built here is leaked in the caller's context; that's
1803 * intentional to minimize the size of the permanent data structure.
1804 */
1805 expr = (Node *) expression_planner_with_deps((Expr *) expr,
1806 &relationOids,
1807 &invalItems);
1808
1809 /*
1810 * Make a private memory context, and copy what we need into that. To
1811 * avoid leaking a long-lived context if we fail while copying data, we
1812 * initially make the context under the caller's context.
1813 */
1815 "CachedExpression",
1817
1818 oldcxt = MemoryContextSwitchTo(cexpr_context);
1819
1820 cexpr = (CachedExpression *) palloc(sizeof(CachedExpression));
1821 cexpr->magic = CACHEDEXPR_MAGIC;
1822 cexpr->expr = copyObject(expr);
1823 cexpr->is_valid = true;
1824 cexpr->relationOids = copyObject(relationOids);
1825 cexpr->invalItems = copyObject(invalItems);
1826 cexpr->context = cexpr_context;
1827
1828 MemoryContextSwitchTo(oldcxt);
1829
1830 /*
1831 * Reparent the expr's memory context under CacheMemoryContext so that it
1832 * will live indefinitely.
1833 */
1835
1836 /*
1837 * Add the entry to the global list of cached expressions.
1838 */
1840
1841 return cexpr;
1842}
static void dlist_push_tail(dlist_head *head, dlist_node *node)
Definition: ilist.h:364
MemoryContext CacheMemoryContext
Definition: mcxt.c:152
#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:6718
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 1280 of file plancache.c.

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

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, CachedPlanSource::magic, MemoryContextGetParent(), MemoryContextSetParent(), NIL, CachedPlanSource::num_custom_plans, CachedPlanSource::num_generic_plans, plan, 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:2291
static void PlanCacheObjectCallback(Datum arg, int cacheid, uint32 hashvalue)
Definition: plancache.c:2182
static void PlanCacheRelCallback(Datum arg, Oid relid)
Definition: plancache.c:2098
uintptr_t Datum
Definition: postgres.h:69

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

Referenced by InitPostgres().

◆ PlanCacheComputeResultDesc()

static TupleDesc PlanCacheComputeResultDesc ( List stmt_list)
static

Definition at line 2062 of file plancache.c.

2063{
2064 Query *query;
2065
2066 switch (ChoosePortalStrategy(stmt_list))
2067 {
2068 case PORTAL_ONE_SELECT:
2070 query = linitial_node(Query, stmt_list);
2071 return ExecCleanTypeFromTL(query->targetList);
2072
2074 query = QueryListGetPrimaryStmt(stmt_list);
2075 Assert(query->returningList);
2076 return ExecCleanTypeFromTL(query->returningList);
2077
2078 case PORTAL_UTIL_SELECT:
2079 query = linitial_node(Query, stmt_list);
2080 Assert(query->utilityStmt);
2081 return UtilityTupleDescriptor(query->utilityStmt);
2082
2083 case PORTAL_MULTI_QUERY:
2084 /* will not return tuples */
2085 break;
2086 }
2087 return NULL;
2088}
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:210
List * returningList
Definition: parsenodes.h:209
List * targetList
Definition: parsenodes.h:193
TupleDesc UtilityTupleDescriptor(Node *parsetree)
Definition: utility.c:2084

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

2183{
2184 dlist_iter iter;
2185
2187 {
2189 node, iter.cur);
2190 ListCell *lc;
2191
2192 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
2193
2194 /* No work if it's already invalidated */
2195 if (!plansource->is_valid)
2196 continue;
2197
2198 /* Never invalidate if parse/plan would be a no-op anyway */
2199 if (!StmtPlanRequiresRevalidation(plansource))
2200 continue;
2201
2202 /*
2203 * Check the dependency list for the rewritten querytree.
2204 */
2205 foreach(lc, plansource->invalItems)
2206 {
2207 PlanInvalItem *item = (PlanInvalItem *) lfirst(lc);
2208
2209 if (item->cacheId != cacheid)
2210 continue;
2211 if (hashvalue == 0 ||
2212 item->hashValue == hashvalue)
2213 {
2214 /* Invalidate the querytree and generic plan */
2215 plansource->is_valid = false;
2216 if (plansource->gplan)
2217 plansource->gplan->is_valid = false;
2218 break;
2219 }
2220 }
2221
2222 /*
2223 * The generic plan, if any, could have more dependencies than the
2224 * querytree does, so we have to check it too.
2225 */
2226 if (plansource->gplan && plansource->gplan->is_valid)
2227 {
2228 foreach(lc, plansource->gplan->stmt_list)
2229 {
2230 PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
2231 ListCell *lc3;
2232
2233 if (plannedstmt->commandType == CMD_UTILITY)
2234 continue; /* Ignore utility statements */
2235 foreach(lc3, plannedstmt->invalItems)
2236 {
2237 PlanInvalItem *item = (PlanInvalItem *) lfirst(lc3);
2238
2239 if (item->cacheId != cacheid)
2240 continue;
2241 if (hashvalue == 0 ||
2242 item->hashValue == hashvalue)
2243 {
2244 /* Invalidate the generic plan only */
2245 plansource->gplan->is_valid = false;
2246 break; /* out of invalItems scan */
2247 }
2248 }
2249 if (!plansource->gplan->is_valid)
2250 break; /* out of stmt_list scan */
2251 }
2252 }
2253 }
2254
2255 /* Likewise check cached expressions */
2257 {
2259 node, iter.cur);
2260 ListCell *lc;
2261
2262 Assert(cexpr->magic == CACHEDEXPR_MAGIC);
2263
2264 /* No work if it's already invalidated */
2265 if (!cexpr->is_valid)
2266 continue;
2267
2268 foreach(lc, cexpr->invalItems)
2269 {
2270 PlanInvalItem *item = (PlanInvalItem *) lfirst(lc);
2271
2272 if (item->cacheId != cacheid)
2273 continue;
2274 if (hashvalue == 0 ||
2275 item->hashValue == hashvalue)
2276 {
2277 cexpr->is_valid = false;
2278 break;
2279 }
2280 }
2281 }
2282}
#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:1747
List * invalItems
Definition: plannodes.h:126
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 2098 of file plancache.c.

2099{
2100 dlist_iter iter;
2101
2103 {
2105 node, iter.cur);
2106
2107 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
2108
2109 /* No work if it's already invalidated */
2110 if (!plansource->is_valid)
2111 continue;
2112
2113 /* Never invalidate if parse/plan would be a no-op anyway */
2114 if (!StmtPlanRequiresRevalidation(plansource))
2115 continue;
2116
2117 /*
2118 * Check the dependency list for the rewritten querytree.
2119 */
2120 if ((relid == InvalidOid) ? plansource->relationOids != NIL :
2121 list_member_oid(plansource->relationOids, relid))
2122 {
2123 /* Invalidate the querytree and generic plan */
2124 plansource->is_valid = false;
2125 if (plansource->gplan)
2126 plansource->gplan->is_valid = false;
2127 }
2128
2129 /*
2130 * The generic plan, if any, could have more dependencies than the
2131 * querytree does, so we have to check it too.
2132 */
2133 if (plansource->gplan && plansource->gplan->is_valid)
2134 {
2135 ListCell *lc;
2136
2137 foreach(lc, plansource->gplan->stmt_list)
2138 {
2139 PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
2140
2141 if (plannedstmt->commandType == CMD_UTILITY)
2142 continue; /* Ignore utility statements */
2143 if ((relid == InvalidOid) ? plannedstmt->relationOids != NIL :
2144 list_member_oid(plannedstmt->relationOids, relid))
2145 {
2146 /* Invalidate the generic plan only */
2147 plansource->gplan->is_valid = false;
2148 break; /* out of stmt_list scan */
2149 }
2150 }
2151 }
2152 }
2153
2154 /* Likewise check cached expressions */
2156 {
2158 node, iter.cur);
2159
2160 Assert(cexpr->magic == CACHEDEXPR_MAGIC);
2161
2162 /* No work if it's already invalidated */
2163 if (!cexpr->is_valid)
2164 continue;
2165
2166 if ((relid == InvalidOid) ? cexpr->relationOids != NIL :
2167 list_member_oid(cexpr->relationOids, relid))
2168 {
2169 cexpr->is_valid = false;
2170 }
2171 }
2172}
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:722
List * relationOids
Definition: plannodes.h:123

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

2292{
2294}
void ResetPlanCache(void)
Definition: plancache.c:2300

References ResetPlanCache().

Referenced by InitPlanCache().

◆ QueryListGetPrimaryStmt()

static Query * QueryListGetPrimaryStmt ( List stmts)
static

Definition at line 1868 of file plancache.c.

1869{
1870 ListCell *lc;
1871
1872 foreach(lc, stmts)
1873 {
1874 Query *stmt = lfirst_node(Query, lc);
1875
1876 if (stmt->canSetTag)
1877 return stmt;
1878 }
1879 return NULL;
1880}
#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 2347 of file plancache.c.

2348{
2350}
static const ResourceOwnerDesc planref_resowner_desc
Definition: plancache.c:115
void ResourceOwnerReleaseAllOfKind(ResourceOwner owner, const ResourceOwnerDesc *kind)
Definition: resowner.c:818

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

1404{
1405 Assert(plan->magic == CACHEDPLAN_MAGIC);
1406 if (owner)
1407 {
1408 Assert(plan->is_saved);
1410 }
1411 Assert(plan->refcount > 0);
1412 plan->refcount--;
1413 if (plan->refcount == 0)
1414 {
1415 /* Mark it no longer valid */
1416 plan->magic = 0;
1417
1418 /* One-shot plans do not own their context, so we can't free them */
1419 if (!plan->is_oneshot)
1420 MemoryContextDelete(plan->context);
1421 }
1422}
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 603 of file plancache.c.

604{
605 /* Be paranoid about the possibility that ReleaseCachedPlan fails */
606 if (plansource->gplan)
607 {
608 CachedPlan *plan = plansource->gplan;
609
610 Assert(plan->magic == CACHEDPLAN_MAGIC);
611 plansource->gplan = NULL;
612 ReleaseCachedPlan(plan, NULL);
613 }
614}
void ReleaseCachedPlan(CachedPlan *plan, ResourceOwner owner)
Definition: plancache.c:1403

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

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

◆ ResetPlanCache()

void ResetPlanCache ( void  )

Definition at line 2300 of file plancache.c.

2301{
2302 dlist_iter iter;
2303
2305 {
2307 node, iter.cur);
2308
2309 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
2310
2311 /* No work if it's already invalidated */
2312 if (!plansource->is_valid)
2313 continue;
2314
2315 /*
2316 * We *must not* mark transaction control statements as invalid,
2317 * particularly not ROLLBACK, because they may need to be executed in
2318 * aborted transactions when we can't revalidate them (cf bug #5269).
2319 * In general there's no point in invalidating statements for which a
2320 * new parse analysis/rewrite/plan cycle would certainly give the same
2321 * results.
2322 */
2323 if (!StmtPlanRequiresRevalidation(plansource))
2324 continue;
2325
2326 plansource->is_valid = false;
2327 if (plansource->gplan)
2328 plansource->gplan->is_valid = false;
2329 }
2330
2331 /* Likewise invalidate cached expressions */
2333 {
2335 node, iter.cur);
2336
2337 Assert(cexpr->magic == CACHEDEXPR_MAGIC);
2338
2339 cexpr->is_valid = false;
2340 }
2341}

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:327
void ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition: resowner.c:564

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:524

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

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

◆ ResOwnerReleaseCachedPlan()

static void ResOwnerReleaseCachedPlan ( Datum  res)
static

Definition at line 2355 of file plancache.c.

2356{
2358}
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317

References DatumGetPointer(), and ReleaseCachedPlan().

◆ RevalidateCachedQuery()

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

Definition at line 667 of file plancache.c.

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

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

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

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

1969{
1970 ListCell *lc;
1971
1972 /* Shouldn't get called on utility commands */
1973 Assert(parsetree->commandType != CMD_UTILITY);
1974
1975 /*
1976 * First, process RTEs of the current query level.
1977 */
1978 foreach(lc, parsetree->rtable)
1979 {
1980 RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
1981
1982 switch (rte->rtekind)
1983 {
1984 case RTE_RELATION:
1985 /* Acquire or release the appropriate type of lock */
1986 if (acquire)
1987 LockRelationOid(rte->relid, rte->rellockmode);
1988 else
1989 UnlockRelationOid(rte->relid, rte->rellockmode);
1990 break;
1991
1992 case RTE_SUBQUERY:
1993 /* If this was a view, must lock/unlock the view */
1994 if (OidIsValid(rte->relid))
1995 {
1996 if (acquire)
1997 LockRelationOid(rte->relid, rte->rellockmode);
1998 else
1999 UnlockRelationOid(rte->relid, rte->rellockmode);
2000 }
2001 /* Recurse into subquery-in-FROM */
2002 ScanQueryForLocks(rte->subquery, acquire);
2003 break;
2004
2005 default:
2006 /* ignore other types of RTEs */
2007 break;
2008 }
2009 }
2010
2011 /* Recurse into subquery-in-WITH */
2012 foreach(lc, parsetree->cteList)
2013 {
2015
2016 ScanQueryForLocks(castNode(Query, cte->ctequery), acquire);
2017 }
2018
2019 /*
2020 * Recurse into sublink subqueries, too. But we already did the ones in
2021 * the rtable and cteList.
2022 */
2023 if (parsetree->hasSubLinks)
2024 {
2025 query_tree_walker(parsetree, ScanQueryWalker, &acquire,
2027 }
2028}
#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:2034
Query * subquery
Definition: parsenodes.h:1118

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

2035{
2036 if (node == NULL)
2037 return false;
2038 if (IsA(node, SubLink))
2039 {
2040 SubLink *sub = (SubLink *) node;
2041
2042 /* Do what we came for */
2043 ScanQueryForLocks(castNode(Query, sub->subselect), *acquire);
2044 /* Fall through to process lefthand args of SubLink */
2045 }
2046
2047 /*
2048 * Do NOT recurse into Query nodes, because ScanQueryForLocks already
2049 * processed subselects of subselects for us.
2050 */
2051 return expression_tree_walker(node, ScanQueryWalker, acquire);
2052}
#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 505 of file plancache.c.

508{
509 Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
510 plansource->postRewrite = postRewrite;
511 plansource->postRewriteArg = postRewriteArg;
512}

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

626{
627 if (plansource->raw_parse_tree != NULL)
629 else if (plansource->analyzed_parse_tree != NULL)
631 /* empty query never needs revalidation */
632 return false;
633}
bool stmt_requires_parse_analysis(RawStmt *parseTree)
Definition: analyze.c:527

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:2355
#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