PostgreSQL Source Code  git master
plancache.c File Reference
#include "postgres.h"
#include <limits.h>
#include "access/transam.h"
#include "catalog/namespace.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "parser/analyze.h"
#include "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.

Macros

#define StmtPlanRequiresRevalidation(plansource)
 

Functions

static void ReleaseGenericPlan (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)
 
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 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
 

Macro Definition Documentation

◆ StmtPlanRequiresRevalidation

#define StmtPlanRequiresRevalidation (   plansource)
Value:
((plansource)->raw_parse_tree != NULL && \
stmt_requires_parse_analysis((plansource)->raw_parse_tree))
bool stmt_requires_parse_analysis(RawStmt *parseTree)
Definition: analyze.c:441

Definition at line 85 of file plancache.c.

Function Documentation

◆ AcquireExecutorLocks()

static void AcquireExecutorLocks ( List stmt_list,
bool  acquire 
)
static

Definition at line 1772 of file plancache.c.

1773 {
1774  ListCell *lc1;
1775 
1776  foreach(lc1, stmt_list)
1777  {
1778  PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc1);
1779  ListCell *lc2;
1780 
1781  if (plannedstmt->commandType == CMD_UTILITY)
1782  {
1783  /*
1784  * Ignore utility statements, except those (such as EXPLAIN) that
1785  * contain a parsed-but-not-planned query. Note: it's okay to use
1786  * ScanQueryForLocks, even though the query hasn't been through
1787  * rule rewriting, because rewriting doesn't change the query
1788  * representation.
1789  */
1790  Query *query = UtilityContainsQuery(plannedstmt->utilityStmt);
1791 
1792  if (query)
1793  ScanQueryForLocks(query, acquire);
1794  continue;
1795  }
1796 
1797  foreach(lc2, plannedstmt->rtable)
1798  {
1799  RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
1800 
1801  if (!(rte->rtekind == RTE_RELATION ||
1802  (rte->rtekind == RTE_SUBQUERY && OidIsValid(rte->relid))))
1803  continue;
1804 
1805  /*
1806  * Acquire the appropriate type of lock on each relation OID. Note
1807  * that we don't actually try to open the rel, and hence will not
1808  * fail if it's been dropped entirely --- we'll just transiently
1809  * acquire a non-conflicting lock.
1810  */
1811  if (acquire)
1812  LockRelationOid(rte->relid, rte->rellockmode);
1813  else
1814  UnlockRelationOid(rte->relid, rte->rellockmode);
1815  }
1816  }
1817 }
#define OidIsValid(objectId)
Definition: c.h:775
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:227
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:108
@ CMD_UTILITY
Definition: nodes.h:270
@ RTE_SUBQUERY
Definition: parsenodes.h:1029
@ RTE_RELATION
Definition: parsenodes.h:1028
#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:1853
CmdType commandType
Definition: plannodes.h:52
Node * utilityStmt
Definition: plannodes.h:95
List * rtable
Definition: plannodes.h:72
RTEKind rtekind
Definition: parsenodes.h:1057
Query * UtilityContainsQuery(Node *parsetree)
Definition: utility.c:2176

References CMD_UTILITY, PlannedStmt::commandType, lfirst, lfirst_node, LockRelationOid(), OidIsValid, RangeTblEntry::relid, 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 1828 of file plancache.c.

1829 {
1830  ListCell *lc;
1831 
1832  foreach(lc, stmt_list)
1833  {
1834  Query *query = lfirst_node(Query, lc);
1835 
1836  if (query->commandType == CMD_UTILITY)
1837  {
1838  /* Ignore utility statements, unless they contain a Query */
1839  query = UtilityContainsQuery(query->utilityStmt);
1840  if (query)
1841  ScanQueryForLocks(query, acquire);
1842  continue;
1843  }
1844 
1845  ScanQueryForLocks(query, acquire);
1846  }
1847 }
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 906 of file plancache.c.

908 {
909  CachedPlan *plan;
910  List *plist;
911  bool snapshot_set;
912  bool is_transient;
913  MemoryContext plan_context;
915  ListCell *lc;
916 
917  /*
918  * Normally the querytree should be valid already, but if it's not,
919  * rebuild it.
920  *
921  * NOTE: GetCachedPlan should have called RevalidateCachedQuery first, so
922  * we ought to be holding sufficient locks to prevent any invalidation.
923  * However, if we're building a custom plan after having built and
924  * rejected a generic plan, it's possible to reach here with is_valid
925  * false due to an invalidation while making the generic plan. In theory
926  * the invalidation must be a false positive, perhaps a consequence of an
927  * sinval reset event or the debug_discard_caches code. But for safety,
928  * let's treat it as real and redo the RevalidateCachedQuery call.
929  */
930  if (!plansource->is_valid)
931  qlist = RevalidateCachedQuery(plansource, queryEnv);
932 
933  /*
934  * If we don't already have a copy of the querytree list that can be
935  * scribbled on by the planner, make one. For a one-shot plan, we assume
936  * it's okay to scribble on the original query_list.
937  */
938  if (qlist == NIL)
939  {
940  if (!plansource->is_oneshot)
941  qlist = copyObject(plansource->query_list);
942  else
943  qlist = plansource->query_list;
944  }
945 
946  /*
947  * If a snapshot is already set (the normal case), we can just use that
948  * for planning. But if it isn't, and we need one, install one.
949  */
950  snapshot_set = false;
951  if (!ActiveSnapshotSet() &&
952  plansource->raw_parse_tree &&
954  {
956  snapshot_set = true;
957  }
958 
959  /*
960  * Generate the plan.
961  */
962  plist = pg_plan_queries(qlist, plansource->query_string,
963  plansource->cursor_options, boundParams);
964 
965  /* Release snapshot if we got one */
966  if (snapshot_set)
968 
969  /*
970  * Normally we make a dedicated memory context for the CachedPlan and its
971  * subsidiary data. (It's probably not going to be large, but just in
972  * case, allow it to grow large. It's transient for the moment.) But for
973  * a one-shot plan, we just leave it in the caller's memory context.
974  */
975  if (!plansource->is_oneshot)
976  {
978  "CachedPlan",
980  MemoryContextCopyAndSetIdentifier(plan_context, plansource->query_string);
981 
982  /*
983  * Copy plan into the new context.
984  */
985  MemoryContextSwitchTo(plan_context);
986 
987  plist = copyObject(plist);
988  }
989  else
990  plan_context = CurrentMemoryContext;
991 
992  /*
993  * Create and fill the CachedPlan struct within the new context.
994  */
995  plan = (CachedPlan *) palloc(sizeof(CachedPlan));
996  plan->magic = CACHEDPLAN_MAGIC;
997  plan->stmt_list = plist;
998 
999  /*
1000  * CachedPlan is dependent on role either if RLS affected the rewrite
1001  * phase or if a role dependency was injected during planning. And it's
1002  * transient if any plan is marked so.
1003  */
1004  plan->planRoleId = GetUserId();
1005  plan->dependsOnRole = plansource->dependsOnRLS;
1006  is_transient = false;
1007  foreach(lc, plist)
1008  {
1009  PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
1010 
1011  if (plannedstmt->commandType == CMD_UTILITY)
1012  continue; /* Ignore utility statements */
1013 
1014  if (plannedstmt->transientPlan)
1015  is_transient = true;
1016  if (plannedstmt->dependsOnRole)
1017  plan->dependsOnRole = true;
1018  }
1019  if (is_transient)
1020  {
1022  plan->saved_xmin = TransactionXmin;
1023  }
1024  else
1025  plan->saved_xmin = InvalidTransactionId;
1026  plan->refcount = 0;
1027  plan->context = plan_context;
1028  plan->is_oneshot = plansource->is_oneshot;
1029  plan->is_saved = false;
1030  plan->is_valid = true;
1031 
1032  /* assign generation number to new plan */
1033  plan->generation = ++(plansource->generation);
1034 
1035  MemoryContextSwitchTo(oldcxt);
1036 
1037  return plan;
1038 }
#define Assert(condition)
Definition: c.h:858
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
void * palloc(Size size)
Definition: mcxt.c:1316
#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:514
#define copyObject(obj)
Definition: nodes.h:224
bool analyze_requires_snapshot(RawStmt *parseTree)
Definition: analyze.c:485
#define NIL
Definition: pg_list.h:68
#define plan(x)
Definition: pg_regress.c:162
static List * RevalidateCachedQuery(CachedPlanSource *plansource, QueryEnvironment *queryEnv)
Definition: plancache.c:583
#define CACHEDPLAN_MAGIC
Definition: plancache.h:41
List * pg_plan_queries(List *querytrees, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition: postgres.c:972
MemoryContextSwitchTo(old_ctx)
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:216
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:648
TransactionId TransactionXmin
Definition: snapmgr.c:98
bool ActiveSnapshotSet(void)
Definition: snapmgr.c:782
void PopActiveSnapshot(void)
Definition: snapmgr.c:743
const char * query_string
Definition: plancache.h:100
struct RawStmt * raw_parse_tree
Definition: plancache.h:99
List * query_list
Definition: plancache.h:111
Definition: pg_list.h:54
bool transientPlan
Definition: plannodes.h:62
bool dependsOnRole
Definition: plannodes.h:64
#define InvalidTransactionId
Definition: transam.h:31
#define TransactionIdIsNormal(xid)
Definition: transam.h:42

References ActiveSnapshotSet(), ALLOCSET_START_SMALL_SIZES, AllocSetContextCreate, analyze_requires_snapshot(), Assert, 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, CachedPlanSource::raw_parse_tree, RevalidateCachedQuery(), TransactionIdIsNormal, TransactionXmin, and PlannedStmt::transientPlan.

Referenced by GetCachedPlan().

◆ cached_plan_cost()

static double cached_plan_cost ( CachedPlan plan,
bool  include_planner 
)
static

Definition at line 1103 of file plancache.c.

1104 {
1105  double result = 0;
1106  ListCell *lc;
1107 
1108  foreach(lc, plan->stmt_list)
1109  {
1110  PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
1111 
1112  if (plannedstmt->commandType == CMD_UTILITY)
1113  continue; /* Ignore utility statements */
1114 
1115  result += plannedstmt->planTree->total_cost;
1116 
1117  if (include_planner)
1118  {
1119  /*
1120  * Currently we use a very crude estimate of planning effort based
1121  * on the number of relations in the finished plan's rangetable.
1122  * Join planning effort actually scales much worse than linearly
1123  * in the number of relations --- but only until the join collapse
1124  * limits kick in. Also, while inheritance child relations surely
1125  * add to planning effort, they don't make the join situation
1126  * worse. So the actual shape of the planning cost curve versus
1127  * number of relations isn't all that obvious. It will take
1128  * considerable work to arrive at a less crude estimate, and for
1129  * now it's not clear that's worth doing.
1130  *
1131  * The other big difficulty here is that we don't have any very
1132  * good model of how planning cost compares to execution costs.
1133  * The current multiplier of 1000 * cpu_operator_cost is probably
1134  * on the low side, but we'll try this for awhile before making a
1135  * more aggressive correction.
1136  *
1137  * If we ever do write a more complicated estimator, it should
1138  * probably live in src/backend/optimizer/ not here.
1139  */
1140  int nrelations = list_length(plannedstmt->rtable);
1141 
1142  result += 1000.0 * cpu_operator_cost * (nrelations + 1);
1143  }
1144  }
1145 
1146  return result;
1147 }
double cpu_operator_cost
Definition: costsize.c:123
static int list_length(const List *l)
Definition: pg_list.h:152
Cost total_cost
Definition: plannodes.h:129
struct Plan * planTree
Definition: plannodes.h:70

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

1338 {
1339  ListCell *lc;
1340 
1341  /*
1342  * Sanity-check that the caller gave us a validated generic plan. Notice
1343  * that we *don't* assert plansource->is_valid as you might expect; that's
1344  * because it's possible that that's already false when GetCachedPlan
1345  * returns, e.g. because ResetPlanCache happened partway through. We
1346  * should accept the plan as long as plan->is_valid is true, and expect to
1347  * replan after the next CachedPlanIsSimplyValid call.
1348  */
1349  Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1350  Assert(plan->magic == CACHEDPLAN_MAGIC);
1351  Assert(plan->is_valid);
1352  Assert(plan == plansource->gplan);
1353  Assert(plansource->search_path != NULL);
1355 
1356  /* We don't support oneshot plans here. */
1357  if (plansource->is_oneshot)
1358  return false;
1359  Assert(!plan->is_oneshot);
1360 
1361  /*
1362  * If the plan is dependent on RLS considerations, or it's transient,
1363  * reject. These things probably can't ever happen for table-free
1364  * queries, but for safety's sake let's check.
1365  */
1366  if (plansource->dependsOnRLS)
1367  return false;
1368  if (plan->dependsOnRole)
1369  return false;
1370  if (TransactionIdIsValid(plan->saved_xmin))
1371  return false;
1372 
1373  /*
1374  * Reject if AcquirePlannerLocks would have anything to do. This is
1375  * simplistic, but there's no need to inquire any more carefully; indeed,
1376  * for current callers it shouldn't even be possible to hit any of these
1377  * checks.
1378  */
1379  foreach(lc, plansource->query_list)
1380  {
1381  Query *query = lfirst_node(Query, lc);
1382 
1383  if (query->commandType == CMD_UTILITY)
1384  return false;
1385  if (query->rtable || query->cteList || query->hasSubLinks)
1386  return false;
1387  }
1388 
1389  /*
1390  * Reject if AcquireExecutorLocks would have anything to do. This is
1391  * probably unnecessary given the previous check, but let's be safe.
1392  */
1393  foreach(lc, plan->stmt_list)
1394  {
1395  PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
1396  ListCell *lc2;
1397 
1398  if (plannedstmt->commandType == CMD_UTILITY)
1399  return false;
1400 
1401  /*
1402  * We have to grovel through the rtable because it's likely to contain
1403  * an RTE_RESULT relation, rather than being totally empty.
1404  */
1405  foreach(lc2, plannedstmt->rtable)
1406  {
1407  RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
1408 
1409  if (rte->rtekind == RTE_RELATION)
1410  return false;
1411  }
1412  }
1413 
1414  /*
1415  * Okay, it's simple. Note that what we've primarily established here is
1416  * that no locks need be taken before checking the plan's is_valid flag.
1417  */
1418 
1419  /* Bump refcount if requested. */
1420  if (owner)
1421  {
1422  ResourceOwnerEnlarge(owner);
1423  plan->refcount++;
1425  }
1426 
1427  return true;
1428 }
bool SearchPathMatchesCurrentEnvironment(SearchPathMatcher *path)
Definition: namespace.c:3896
static void ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
Definition: plancache.c:135
#define CACHEDPLANSOURCE_MAGIC
Definition: plancache.h:40
void ResourceOwnerEnlarge(ResourceOwner owner)
Definition: resowner.c:442
struct CachedPlan * gplan
Definition: plancache.h:121
struct SearchPathMatcher * search_path
Definition: plancache.h:114
List * cteList
Definition: parsenodes.h:166
List * rtable
Definition: parsenodes.h:168
#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 1640 of file plancache.c.

1642 {
1643  Query *pstmt;
1644 
1645  /* Assert caller is doing things in a sane order */
1646  Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1647  Assert(plansource->is_complete);
1648 
1649  /*
1650  * No work needed if statement doesn't return tuples (we assume this
1651  * feature cannot be changed by an invalidation)
1652  */
1653  if (plansource->resultDesc == NULL)
1654  return NIL;
1655 
1656  /* Make sure the querytree list is valid and we have parse-time locks */
1657  RevalidateCachedQuery(plansource, queryEnv);
1658 
1659  /* Get the primary statement and find out what it returns */
1660  pstmt = QueryListGetPrimaryStmt(plansource->query_list);
1661 
1662  return FetchStatementTargetList((Node *) pstmt);
1663 }
static Query * QueryListGetPrimaryStmt(List *stmts)
Definition: plancache.c:1753
List * FetchStatementTargetList(Node *stmt)
Definition: pquery.c:348
TupleDesc resultDesc
Definition: plancache.h:108
Definition: nodes.h:129

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

1453 {
1454  /*
1455  * Careful here: since the caller doesn't necessarily hold a refcount on
1456  * the plan to start with, it's possible that "plan" is a dangling
1457  * pointer. Don't dereference it until we've verified that it still
1458  * matches the plansource's gplan (which is either valid or NULL).
1459  */
1460  Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1461 
1462  /*
1463  * Has cache invalidation fired on this plan? We can check this right
1464  * away since there are no locks that we'd need to acquire first. Note
1465  * that here we *do* check plansource->is_valid, so as to force plan
1466  * rebuild if that's become false.
1467  */
1468  if (!plansource->is_valid ||
1469  plan == NULL || plan != plansource->gplan ||
1470  !plan->is_valid)
1471  return false;
1472 
1473  Assert(plan->magic == CACHEDPLAN_MAGIC);
1474 
1475  /* Is the search_path still the same as when we made it? */
1476  Assert(plansource->search_path != NULL);
1478  return false;
1479 
1480  /* It's still good. Bump refcount if requested. */
1481  if (owner)
1482  {
1483  ResourceOwnerEnlarge(owner);
1484  plan->refcount++;
1486  }
1487 
1488  return true;
1489 }

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

1628 {
1629  Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1630  return plansource->is_valid;
1631 }

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

1500 {
1501  /* Assert caller is doing things in a sane order */
1502  Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1503  Assert(plansource->is_complete);
1504 
1505  /* These seem worth real tests, though */
1506  if (plansource->is_saved)
1507  elog(ERROR, "cannot move a saved cached plan to another context");
1508  if (plansource->is_oneshot)
1509  elog(ERROR, "cannot move a one-shot cached plan to another context");
1510 
1511  /* OK, let the caller keep the plan where he wishes */
1512  MemoryContextSetParent(plansource->context, newcontext);
1513 
1514  /*
1515  * The query_context needs no special handling, since it's a child of
1516  * plansource->context. But if there's a generic plan, it should be
1517  * maintained as a sibling of plansource->context.
1518  */
1519  if (plansource->gplan)
1520  {
1521  Assert(plansource->gplan->magic == CACHEDPLAN_MAGIC);
1522  MemoryContextSetParent(plansource->gplan->context, newcontext);
1523  }
1524 }
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
void MemoryContextSetParent(MemoryContext context, MemoryContext new_parent)
Definition: mcxt.c:637
MemoryContext context
Definition: plancache.h:109
MemoryContext context
Definition: plancache.h:160

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

823 {
824  CachedPlan *plan = plansource->gplan;
825 
826  /* Assert that caller checked the querytree */
827  Assert(plansource->is_valid);
828 
829  /* If there's no generic plan, just say "false" */
830  if (!plan)
831  return false;
832 
833  Assert(plan->magic == CACHEDPLAN_MAGIC);
834  /* Generic plans are never one-shot */
835  Assert(!plan->is_oneshot);
836 
837  /*
838  * If plan isn't valid for current role, we can't use it.
839  */
840  if (plan->is_valid && plan->dependsOnRole &&
841  plan->planRoleId != GetUserId())
842  plan->is_valid = false;
843 
844  /*
845  * If it appears valid, acquire locks and recheck; this is much the same
846  * logic as in RevalidateCachedQuery, but for a plan.
847  */
848  if (plan->is_valid)
849  {
850  /*
851  * Plan must have positive refcount because it is referenced by
852  * plansource; so no need to fear it disappears under us here.
853  */
854  Assert(plan->refcount > 0);
855 
856  AcquireExecutorLocks(plan->stmt_list, true);
857 
858  /*
859  * If plan was transient, check to see if TransactionXmin has
860  * advanced, and if so invalidate it.
861  */
862  if (plan->is_valid &&
863  TransactionIdIsValid(plan->saved_xmin) &&
865  plan->is_valid = false;
866 
867  /*
868  * By now, if any invalidation has happened, the inval callback
869  * functions will have marked the plan invalid.
870  */
871  if (plan->is_valid)
872  {
873  /* Successfully revalidated and locked the query. */
874  return true;
875  }
876 
877  /* Oops, the race case happened. Release useless locks. */
878  AcquireExecutorLocks(plan->stmt_list, false);
879  }
880 
881  /*
882  * Plan has been invalidated, so unlink it from the parent and release it.
883  */
884  ReleaseGenericPlan(plansource);
885 
886  return false;
887 }
static void ReleaseGenericPlan(CachedPlanSource *plansource)
Definition: plancache.c:555
static void AcquireExecutorLocks(List *stmt_list, bool acquire)
Definition: plancache.c:1772
#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 1046 of file plancache.c.

1047 {
1048  double avg_custom_cost;
1049 
1050  /* One-shot plans will always be considered custom */
1051  if (plansource->is_oneshot)
1052  return true;
1053 
1054  /* Otherwise, never any point in a custom plan if there's no parameters */
1055  if (boundParams == NULL)
1056  return false;
1057  /* ... nor when planning would be a no-op */
1058  if (!StmtPlanRequiresRevalidation(plansource))
1059  return false;
1060 
1061  /* Let settings force the decision */
1063  return false;
1065  return true;
1066 
1067  /* See if caller wants to force the decision */
1068  if (plansource->cursor_options & CURSOR_OPT_GENERIC_PLAN)
1069  return false;
1070  if (plansource->cursor_options & CURSOR_OPT_CUSTOM_PLAN)
1071  return true;
1072 
1073  /* Generate custom plans until we have done at least 5 (arbitrary) */
1074  if (plansource->num_custom_plans < 5)
1075  return true;
1076 
1077  avg_custom_cost = plansource->total_custom_cost / plansource->num_custom_plans;
1078 
1079  /*
1080  * Prefer generic plan if it's less expensive than the average custom
1081  * plan. (Because we include a charge for cost of planning in the
1082  * custom-plan costs, this means the generic plan only has to be less
1083  * expensive than the execution cost plus replan cost of the custom
1084  * plans.)
1085  *
1086  * Note that if generic_cost is -1 (indicating we've not yet determined
1087  * the generic plan cost), we'll always prefer generic at this point.
1088  */
1089  if (plansource->generic_cost < avg_custom_cost)
1090  return false;
1091 
1092  return true;
1093 }
#define CURSOR_OPT_GENERIC_PLAN
Definition: parsenodes.h:3300
#define CURSOR_OPT_CUSTOM_PLAN
Definition: parsenodes.h:3301
int plan_cache_mode
Definition: plancache.c:147
#define StmtPlanRequiresRevalidation(plansource)
Definition: plancache.c:85
@ PLAN_CACHE_MODE_FORCE_CUSTOM_PLAN
Definition: plancache.h:34
@ PLAN_CACHE_MODE_FORCE_GENERIC_PLAN
Definition: plancache.h:33
double total_custom_cost
Definition: plancache.h:132
int64 num_custom_plans
Definition: plancache.h:133
double generic_cost
Definition: plancache.h:131

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

375 {
376  MemoryContext source_context = plansource->context;
378 
379  /* Assert caller is doing things in a sane order */
380  Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
381  Assert(!plansource->is_complete);
382 
383  /*
384  * If caller supplied a querytree_context, reparent it underneath the
385  * CachedPlanSource's context; otherwise, create a suitable context and
386  * copy the querytree_list into it. But no data copying should be done
387  * for one-shot plans; for those, assume the passed querytree_list is
388  * sufficiently long-lived.
389  */
390  if (plansource->is_oneshot)
391  {
392  querytree_context = CurrentMemoryContext;
393  }
394  else if (querytree_context != NULL)
395  {
396  MemoryContextSetParent(querytree_context, source_context);
397  MemoryContextSwitchTo(querytree_context);
398  }
399  else
400  {
401  /* Again, it's a good bet the querytree_context can be small */
402  querytree_context = AllocSetContextCreate(source_context,
403  "CachedPlanQuery",
405  MemoryContextSwitchTo(querytree_context);
406  querytree_list = copyObject(querytree_list);
407  }
408 
409  plansource->query_context = querytree_context;
410  plansource->query_list = querytree_list;
411 
412  if (!plansource->is_oneshot && StmtPlanRequiresRevalidation(plansource))
413  {
414  /*
415  * Use the planner machinery to extract dependencies. Data is saved
416  * in query_context. (We assume that not a lot of extra cruft is
417  * created by this call.) We can skip this for one-shot plans, and
418  * plans not needing revalidation have no such dependencies anyway.
419  */
420  extract_query_dependencies((Node *) querytree_list,
421  &plansource->relationOids,
422  &plansource->invalItems,
423  &plansource->dependsOnRLS);
424 
425  /* Update RLS info as well. */
426  plansource->rewriteRoleId = GetUserId();
427  plansource->rewriteRowSecurity = row_security;
428 
429  /*
430  * Also save the current search_path in the query_context. (This
431  * should not generate much extra cruft either, since almost certainly
432  * the path is already valid.) Again, we don't really need this for
433  * one-shot plans; and we *must* skip this for transaction control
434  * commands, because this could result in catalog accesses.
435  */
436  plansource->search_path = GetSearchPathMatcher(querytree_context);
437  }
438 
439  /*
440  * Save the final parameter types (or other parameter specification data)
441  * into the source_context, as well as our other parameters. Also save
442  * the result tuple descriptor.
443  */
444  MemoryContextSwitchTo(source_context);
445 
446  if (num_params > 0)
447  {
448  plansource->param_types = (Oid *) palloc(num_params * sizeof(Oid));
449  memcpy(plansource->param_types, param_types, num_params * sizeof(Oid));
450  }
451  else
452  plansource->param_types = NULL;
453  plansource->num_params = num_params;
454  plansource->parserSetup = parserSetup;
455  plansource->parserSetupArg = parserSetupArg;
456  plansource->cursor_options = cursor_options;
457  plansource->fixed_result = fixed_result;
458  plansource->resultDesc = PlanCacheComputeResultDesc(querytree_list);
459 
460  MemoryContextSwitchTo(oldcxt);
461 
462  plansource->is_complete = true;
463  plansource->is_valid = true;
464 }
bool row_security
Definition: guc_tables.c:513
SearchPathMatcher * GetSearchPathMatcher(MemoryContext context)
Definition: namespace.c:3837
static TupleDesc PlanCacheComputeResultDesc(List *stmt_list)
Definition: plancache.c:1949
unsigned int Oid
Definition: postgres_ext.h:31
void extract_query_dependencies(Node *query, List **relationOids, List **invalItems, bool *hasRowSecurity)
Definition: setrefs.c:3541
MemoryContext query_context
Definition: plancache.h:116
List * invalItems
Definition: plancache.h:113
ParserSetupHook parserSetup
Definition: plancache.h:104
bool rewriteRowSecurity
Definition: plancache.h:118
List * relationOids
Definition: plancache.h:112
void * parserSetupArg
Definition: plancache.h:105

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(), and PrepareQuery().

◆ CopyCachedPlan()

CachedPlanSource* CopyCachedPlan ( CachedPlanSource plansource)

Definition at line 1536 of file plancache.c.

1537 {
1538  CachedPlanSource *newsource;
1539  MemoryContext source_context;
1540  MemoryContext querytree_context;
1541  MemoryContext oldcxt;
1542 
1543  Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1544  Assert(plansource->is_complete);
1545 
1546  /*
1547  * One-shot plans can't be copied, because we haven't taken care that
1548  * parsing/planning didn't scribble on the raw parse tree or querytrees.
1549  */
1550  if (plansource->is_oneshot)
1551  elog(ERROR, "cannot copy a one-shot cached plan");
1552 
1554  "CachedPlanSource",
1556 
1557  oldcxt = MemoryContextSwitchTo(source_context);
1558 
1559  newsource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
1560  newsource->magic = CACHEDPLANSOURCE_MAGIC;
1561  newsource->raw_parse_tree = copyObject(plansource->raw_parse_tree);
1562  newsource->query_string = pstrdup(plansource->query_string);
1563  MemoryContextSetIdentifier(source_context, newsource->query_string);
1564  newsource->commandTag = plansource->commandTag;
1565  if (plansource->num_params > 0)
1566  {
1567  newsource->param_types = (Oid *)
1568  palloc(plansource->num_params * sizeof(Oid));
1569  memcpy(newsource->param_types, plansource->param_types,
1570  plansource->num_params * sizeof(Oid));
1571  }
1572  else
1573  newsource->param_types = NULL;
1574  newsource->num_params = plansource->num_params;
1575  newsource->parserSetup = plansource->parserSetup;
1576  newsource->parserSetupArg = plansource->parserSetupArg;
1577  newsource->cursor_options = plansource->cursor_options;
1578  newsource->fixed_result = plansource->fixed_result;
1579  if (plansource->resultDesc)
1580  newsource->resultDesc = CreateTupleDescCopy(plansource->resultDesc);
1581  else
1582  newsource->resultDesc = NULL;
1583  newsource->context = source_context;
1584 
1585  querytree_context = AllocSetContextCreate(source_context,
1586  "CachedPlanQuery",
1588  MemoryContextSwitchTo(querytree_context);
1589  newsource->query_list = copyObject(plansource->query_list);
1590  newsource->relationOids = copyObject(plansource->relationOids);
1591  newsource->invalItems = copyObject(plansource->invalItems);
1592  if (plansource->search_path)
1593  newsource->search_path = CopySearchPathMatcher(plansource->search_path);
1594  newsource->query_context = querytree_context;
1595  newsource->rewriteRoleId = plansource->rewriteRoleId;
1596  newsource->rewriteRowSecurity = plansource->rewriteRowSecurity;
1597  newsource->dependsOnRLS = plansource->dependsOnRLS;
1598 
1599  newsource->gplan = NULL;
1600 
1601  newsource->is_oneshot = false;
1602  newsource->is_complete = true;
1603  newsource->is_saved = false;
1604  newsource->is_valid = plansource->is_valid;
1605  newsource->generation = plansource->generation;
1606 
1607  /* We may as well copy any acquired cost knowledge */
1608  newsource->generic_cost = plansource->generic_cost;
1609  newsource->total_custom_cost = plansource->total_custom_cost;
1610  newsource->num_generic_plans = plansource->num_generic_plans;
1611  newsource->num_custom_plans = plansource->num_custom_plans;
1612 
1613  MemoryContextSwitchTo(oldcxt);
1614 
1615  return newsource;
1616 }
char * pstrdup(const char *in)
Definition: mcxt.c:1695
void * palloc0(Size size)
Definition: mcxt.c:1346
void MemoryContextSetIdentifier(MemoryContext context, const char *id)
Definition: mcxt.c:612
SearchPathMatcher * CopySearchPathMatcher(SearchPathMatcher *path)
Definition: namespace.c:3874
CommandTag commandTag
Definition: plancache.h:101
int64 num_generic_plans
Definition: plancache.h:134
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:133

References ALLOCSET_START_SMALL_SIZES, AllocSetContextCreate, 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, 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 192 of file plancache.c.

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

References ALLOCSET_START_SMALL_SIZES, AllocSetContextCreate, 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, 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(), exec_parse_message(), and PrepareQuery().

◆ CreateOneShotCachedPlan()

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

Definition at line 276 of file plancache.c.

279 {
280  CachedPlanSource *plansource;
281 
282  Assert(query_string != NULL); /* required as of 8.4 */
283 
284  /*
285  * Create and fill the CachedPlanSource struct within the caller's memory
286  * context. Most fields are just left empty for the moment.
287  */
288  plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
289  plansource->magic = CACHEDPLANSOURCE_MAGIC;
290  plansource->raw_parse_tree = raw_parse_tree;
291  plansource->query_string = query_string;
292  plansource->commandTag = commandTag;
293  plansource->param_types = NULL;
294  plansource->num_params = 0;
295  plansource->parserSetup = NULL;
296  plansource->parserSetupArg = NULL;
297  plansource->cursor_options = 0;
298  plansource->fixed_result = false;
299  plansource->resultDesc = NULL;
300  plansource->context = CurrentMemoryContext;
301  plansource->query_list = NIL;
302  plansource->relationOids = NIL;
303  plansource->invalItems = NIL;
304  plansource->search_path = NULL;
305  plansource->query_context = NULL;
306  plansource->rewriteRoleId = InvalidOid;
307  plansource->rewriteRowSecurity = false;
308  plansource->dependsOnRLS = false;
309  plansource->gplan = NULL;
310  plansource->is_oneshot = true;
311  plansource->is_complete = false;
312  plansource->is_saved = false;
313  plansource->is_valid = false;
314  plansource->generation = 0;
315  plansource->generic_cost = -1;
316  plansource->total_custom_cost = 0;
317  plansource->num_generic_plans = 0;
318  plansource->num_custom_plans = 0;
319 
320  return plansource;
321 }

References 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::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 526 of file plancache.c.

527 {
528  Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
529 
530  /* If it's been saved, remove it from the list */
531  if (plansource->is_saved)
532  {
533  dlist_delete(&plansource->node);
534  plansource->is_saved = false;
535  }
536 
537  /* Decrement generic CachedPlan's refcount and drop if no longer needed */
538  ReleaseGenericPlan(plansource);
539 
540  /* Mark it no longer valid */
541  plansource->magic = 0;
542 
543  /*
544  * Remove the CachedPlanSource and all subsidiary data (including the
545  * query_context if any). But if it's a one-shot we can't free anything.
546  */
547  if (!plansource->is_oneshot)
548  MemoryContextDelete(plansource->context);
549 }
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:129

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(), and SPI_freeplan().

◆ FreeCachedExpression()

void FreeCachedExpression ( CachedExpression cexpr)

Definition at line 1734 of file plancache.c.

1735 {
1736  /* Sanity check */
1737  Assert(cexpr->magic == CACHEDEXPR_MAGIC);
1738  /* Unlink from global list */
1739  dlist_delete(&cexpr->node);
1740  /* Free all storage associated with CachedExpression */
1741  MemoryContextDelete(cexpr->context);
1742 }
#define CACHEDEXPR_MAGIC
Definition: plancache.h:42
MemoryContext context
Definition: plancache.h:183
dlist_node node
Definition: plancache.h:184

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

1678 {
1679  CachedExpression *cexpr;
1680  List *relationOids;
1681  List *invalItems;
1682  MemoryContext cexpr_context;
1683  MemoryContext oldcxt;
1684 
1685  /*
1686  * Pass the expression through the planner, and collect dependencies.
1687  * Everything built here is leaked in the caller's context; that's
1688  * intentional to minimize the size of the permanent data structure.
1689  */
1690  expr = (Node *) expression_planner_with_deps((Expr *) expr,
1691  &relationOids,
1692  &invalItems);
1693 
1694  /*
1695  * Make a private memory context, and copy what we need into that. To
1696  * avoid leaking a long-lived context if we fail while copying data, we
1697  * initially make the context under the caller's context.
1698  */
1700  "CachedExpression",
1702 
1703  oldcxt = MemoryContextSwitchTo(cexpr_context);
1704 
1705  cexpr = (CachedExpression *) palloc(sizeof(CachedExpression));
1706  cexpr->magic = CACHEDEXPR_MAGIC;
1707  cexpr->expr = copyObject(expr);
1708  cexpr->is_valid = true;
1709  cexpr->relationOids = copyObject(relationOids);
1710  cexpr->invalItems = copyObject(invalItems);
1711  cexpr->context = cexpr_context;
1712 
1713  MemoryContextSwitchTo(oldcxt);
1714 
1715  /*
1716  * Reparent the expr's memory context under CacheMemoryContext so that it
1717  * will live indefinitely.
1718  */
1720 
1721  /*
1722  * Add the entry to the global list of cached expressions.
1723  */
1725 
1726  return cexpr;
1727 }
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:100
Expr * expression_planner_with_deps(Expr *expr, List **relationOids, List **invalItems)
Definition: planner.c:6484
List * relationOids
Definition: plancache.h:181
List * invalItems
Definition: plancache.h:182

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

1170 {
1171  CachedPlan *plan = NULL;
1172  List *qlist;
1173  bool customplan;
1174 
1175  /* Assert caller is doing things in a sane order */
1176  Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1177  Assert(plansource->is_complete);
1178  /* This seems worth a real test, though */
1179  if (owner && !plansource->is_saved)
1180  elog(ERROR, "cannot apply ResourceOwner to non-saved cached plan");
1181 
1182  /* Make sure the querytree list is valid and we have parse-time locks */
1183  qlist = RevalidateCachedQuery(plansource, queryEnv);
1184 
1185  /* Decide whether to use a custom plan */
1186  customplan = choose_custom_plan(plansource, boundParams);
1187 
1188  if (!customplan)
1189  {
1190  if (CheckCachedPlan(plansource))
1191  {
1192  /* We want a generic plan, and we already have a valid one */
1193  plan = plansource->gplan;
1194  Assert(plan->magic == CACHEDPLAN_MAGIC);
1195  }
1196  else
1197  {
1198  /* Build a new generic plan */
1199  plan = BuildCachedPlan(plansource, qlist, NULL, queryEnv);
1200  /* Just make real sure plansource->gplan is clear */
1201  ReleaseGenericPlan(plansource);
1202  /* Link the new generic plan into the plansource */
1203  plansource->gplan = plan;
1204  plan->refcount++;
1205  /* Immediately reparent into appropriate context */
1206  if (plansource->is_saved)
1207  {
1208  /* saved plans all live under CacheMemoryContext */
1210  plan->is_saved = true;
1211  }
1212  else
1213  {
1214  /* otherwise, it should be a sibling of the plansource */
1215  MemoryContextSetParent(plan->context,
1216  MemoryContextGetParent(plansource->context));
1217  }
1218  /* Update generic_cost whenever we make a new generic plan */
1219  plansource->generic_cost = cached_plan_cost(plan, false);
1220 
1221  /*
1222  * If, based on the now-known value of generic_cost, we'd not have
1223  * chosen to use a generic plan, then forget it and make a custom
1224  * plan. This is a bit of a wart but is necessary to avoid a
1225  * glitch in behavior when the custom plans are consistently big
1226  * winners; at some point we'll experiment with a generic plan and
1227  * find it's a loser, but we don't want to actually execute that
1228  * plan.
1229  */
1230  customplan = choose_custom_plan(plansource, boundParams);
1231 
1232  /*
1233  * If we choose to plan again, we need to re-copy the query_list,
1234  * since the planner probably scribbled on it. We can force
1235  * BuildCachedPlan to do that by passing NIL.
1236  */
1237  qlist = NIL;
1238  }
1239  }
1240 
1241  if (customplan)
1242  {
1243  /* Build a custom plan */
1244  plan = BuildCachedPlan(plansource, qlist, boundParams, queryEnv);
1245  /* Accumulate total costs of custom plans */
1246  plansource->total_custom_cost += cached_plan_cost(plan, true);
1247 
1248  plansource->num_custom_plans++;
1249  }
1250  else
1251  {
1252  plansource->num_generic_plans++;
1253  }
1254 
1255  Assert(plan != NULL);
1256 
1257  /* Flag the plan as in use by caller */
1258  if (owner)
1259  ResourceOwnerEnlarge(owner);
1260  plan->refcount++;
1261  if (owner)
1263 
1264  /*
1265  * Saved plans should be under CacheMemoryContext so they will not go away
1266  * until their reference count goes to zero. In the generic-plan cases we
1267  * already took care of that, but for a custom plan, do it as soon as we
1268  * have created a reference-counted link.
1269  */
1270  if (customplan && plansource->is_saved)
1271  {
1273  plan->is_saved = true;
1274  }
1275 
1276  return plan;
1277 }
MemoryContext MemoryContextGetParent(MemoryContext context)
Definition: mcxt.c:731
static bool choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams)
Definition: plancache.c:1046
static CachedPlan * BuildCachedPlan(CachedPlanSource *plansource, List *qlist, ParamListInfo boundParams, QueryEnvironment *queryEnv)
Definition: plancache.c:906
static bool CheckCachedPlan(CachedPlanSource *plansource)
Definition: plancache.c:822
static double cached_plan_cost(CachedPlan *plan, bool include_planner)
Definition: plancache.c:1103

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(), SPI_cursor_open_internal(), and SPI_plan_get_cached_plan().

◆ InitPlanCache()

void InitPlanCache ( void  )

Definition at line 155 of file plancache.c.

156 {
164  CacheRegisterSyscacheCallback(FOREIGNDATAWRAPPEROID, PlanCacheSysCallback, (Datum) 0);
165 }
void CacheRegisterRelcacheCallback(RelcacheCallbackFunction func, Datum arg)
Definition: inval.c:1558
void CacheRegisterSyscacheCallback(int cacheid, SyscacheCallbackFunction func, Datum arg)
Definition: inval.c:1516
static void PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue)
Definition: plancache.c:2178
static void PlanCacheObjectCallback(Datum arg, int cacheid, uint32 hashvalue)
Definition: plancache.c:2069
static void PlanCacheRelCallback(Datum arg, Oid relid)
Definition: plancache.c:1985
uintptr_t Datum
Definition: postgres.h:64

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

Referenced by InitPostgres().

◆ PlanCacheComputeResultDesc()

static TupleDesc PlanCacheComputeResultDesc ( List stmt_list)
static

Definition at line 1949 of file plancache.c.

1950 {
1951  Query *query;
1952 
1953  switch (ChoosePortalStrategy(stmt_list))
1954  {
1955  case PORTAL_ONE_SELECT:
1956  case PORTAL_ONE_MOD_WITH:
1957  query = linitial_node(Query, stmt_list);
1958  return ExecCleanTypeFromTL(query->targetList);
1959 
1960  case PORTAL_ONE_RETURNING:
1961  query = QueryListGetPrimaryStmt(stmt_list);
1962  Assert(query->returningList);
1963  return ExecCleanTypeFromTL(query->returningList);
1964 
1965  case PORTAL_UTIL_SELECT:
1966  query = linitial_node(Query, stmt_list);
1967  Assert(query->utilityStmt);
1968  return UtilityTupleDescriptor(query->utilityStmt);
1969 
1970  case PORTAL_MULTI_QUERY:
1971  /* will not return tuples */
1972  break;
1973  }
1974  return NULL;
1975 }
TupleDesc ExecCleanTypeFromTL(List *targetList)
Definition: execTuples.c:2037
#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:209
List * returningList
Definition: parsenodes.h:198
List * targetList
Definition: parsenodes.h:191
TupleDesc UtilityTupleDescriptor(Node *parsetree)
Definition: utility.c:2081

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

2070 {
2071  dlist_iter iter;
2072 
2074  {
2076  node, iter.cur);
2077  ListCell *lc;
2078 
2079  Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
2080 
2081  /* No work if it's already invalidated */
2082  if (!plansource->is_valid)
2083  continue;
2084 
2085  /* Never invalidate if parse/plan would be a no-op anyway */
2086  if (!StmtPlanRequiresRevalidation(plansource))
2087  continue;
2088 
2089  /*
2090  * Check the dependency list for the rewritten querytree.
2091  */
2092  foreach(lc, plansource->invalItems)
2093  {
2094  PlanInvalItem *item = (PlanInvalItem *) lfirst(lc);
2095 
2096  if (item->cacheId != cacheid)
2097  continue;
2098  if (hashvalue == 0 ||
2099  item->hashValue == hashvalue)
2100  {
2101  /* Invalidate the querytree and generic plan */
2102  plansource->is_valid = false;
2103  if (plansource->gplan)
2104  plansource->gplan->is_valid = false;
2105  break;
2106  }
2107  }
2108 
2109  /*
2110  * The generic plan, if any, could have more dependencies than the
2111  * querytree does, so we have to check it too.
2112  */
2113  if (plansource->gplan && plansource->gplan->is_valid)
2114  {
2115  foreach(lc, plansource->gplan->stmt_list)
2116  {
2117  PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
2118  ListCell *lc3;
2119 
2120  if (plannedstmt->commandType == CMD_UTILITY)
2121  continue; /* Ignore utility statements */
2122  foreach(lc3, plannedstmt->invalItems)
2123  {
2124  PlanInvalItem *item = (PlanInvalItem *) lfirst(lc3);
2125 
2126  if (item->cacheId != cacheid)
2127  continue;
2128  if (hashvalue == 0 ||
2129  item->hashValue == hashvalue)
2130  {
2131  /* Invalidate the generic plan only */
2132  plansource->gplan->is_valid = false;
2133  break; /* out of invalItems scan */
2134  }
2135  }
2136  if (!plansource->gplan->is_valid)
2137  break; /* out of stmt_list scan */
2138  }
2139  }
2140  }
2141 
2142  /* Likewise check cached expressions */
2144  {
2146  node, iter.cur);
2147  ListCell *lc;
2148 
2149  Assert(cexpr->magic == CACHEDEXPR_MAGIC);
2150 
2151  /* No work if it's already invalidated */
2152  if (!cexpr->is_valid)
2153  continue;
2154 
2155  foreach(lc, cexpr->invalItems)
2156  {
2157  PlanInvalItem *item = (PlanInvalItem *) lfirst(lc);
2158 
2159  if (item->cacheId != cacheid)
2160  continue;
2161  if (hashvalue == 0 ||
2162  item->hashValue == hashvalue)
2163  {
2164  cexpr->is_valid = false;
2165  break;
2166  }
2167  }
2168  }
2169 }
#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:95
bool is_valid
Definition: plancache.h:153
List * stmt_list
Definition: plancache.h:150
uint32 hashValue
Definition: plannodes.h:1573
List * invalItems
Definition: plannodes.h:91
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 1985 of file plancache.c.

1986 {
1987  dlist_iter iter;
1988 
1990  {
1992  node, iter.cur);
1993 
1994  Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
1995 
1996  /* No work if it's already invalidated */
1997  if (!plansource->is_valid)
1998  continue;
1999 
2000  /* Never invalidate if parse/plan would be a no-op anyway */
2001  if (!StmtPlanRequiresRevalidation(plansource))
2002  continue;
2003 
2004  /*
2005  * Check the dependency list for the rewritten querytree.
2006  */
2007  if ((relid == InvalidOid) ? plansource->relationOids != NIL :
2008  list_member_oid(plansource->relationOids, relid))
2009  {
2010  /* Invalidate the querytree and generic plan */
2011  plansource->is_valid = false;
2012  if (plansource->gplan)
2013  plansource->gplan->is_valid = false;
2014  }
2015 
2016  /*
2017  * The generic plan, if any, could have more dependencies than the
2018  * querytree does, so we have to check it too.
2019  */
2020  if (plansource->gplan && plansource->gplan->is_valid)
2021  {
2022  ListCell *lc;
2023 
2024  foreach(lc, plansource->gplan->stmt_list)
2025  {
2026  PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
2027 
2028  if (plannedstmt->commandType == CMD_UTILITY)
2029  continue; /* Ignore utility statements */
2030  if ((relid == InvalidOid) ? plannedstmt->relationOids != NIL :
2031  list_member_oid(plannedstmt->relationOids, relid))
2032  {
2033  /* Invalidate the generic plan only */
2034  plansource->gplan->is_valid = false;
2035  break; /* out of stmt_list scan */
2036  }
2037  }
2038  }
2039  }
2040 
2041  /* Likewise check cached expressions */
2043  {
2045  node, iter.cur);
2046 
2047  Assert(cexpr->magic == CACHEDEXPR_MAGIC);
2048 
2049  /* No work if it's already invalidated */
2050  if (!cexpr->is_valid)
2051  continue;
2052 
2053  if ((relid == InvalidOid) ? cexpr->relationOids != NIL :
2054  list_member_oid(cexpr->relationOids, relid))
2055  {
2056  cexpr->is_valid = false;
2057  }
2058  }
2059 }
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:722
List * relationOids
Definition: plannodes.h:89

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

2179 {
2180  ResetPlanCache();
2181 }
void ResetPlanCache(void)
Definition: plancache.c:2187

References ResetPlanCache().

Referenced by InitPlanCache().

◆ QueryListGetPrimaryStmt()

static Query * QueryListGetPrimaryStmt ( List stmts)
static

Definition at line 1753 of file plancache.c.

1754 {
1755  ListCell *lc;
1756 
1757  foreach(lc, stmts)
1758  {
1759  Query *stmt = lfirst_node(Query, lc);
1760 
1761  if (stmt->canSetTag)
1762  return stmt;
1763  }
1764  return NULL;
1765 }
#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 2234 of file plancache.c.

2235 {
2237 }
static const ResourceOwnerDesc planref_resowner_desc
Definition: plancache.c:124
void ResourceOwnerReleaseAllOfKind(ResourceOwner owner, const ResourceOwnerDesc *kind)
Definition: resowner.c:801

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

1292 {
1293  Assert(plan->magic == CACHEDPLAN_MAGIC);
1294  if (owner)
1295  {
1296  Assert(plan->is_saved);
1298  }
1299  Assert(plan->refcount > 0);
1300  plan->refcount--;
1301  if (plan->refcount == 0)
1302  {
1303  /* Mark it no longer valid */
1304  plan->magic = 0;
1305 
1306  /* One-shot plans do not own their context, so we can't free them */
1307  if (!plan->is_oneshot)
1308  MemoryContextDelete(plan->context);
1309  }
1310 }
static void ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
Definition: plancache.c:140

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

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

◆ ReleaseGenericPlan()

static void ReleaseGenericPlan ( CachedPlanSource plansource)
static

Definition at line 555 of file plancache.c.

556 {
557  /* Be paranoid about the possibility that ReleaseCachedPlan fails */
558  if (plansource->gplan)
559  {
560  CachedPlan *plan = plansource->gplan;
561 
562  Assert(plan->magic == CACHEDPLAN_MAGIC);
563  plansource->gplan = NULL;
564  ReleaseCachedPlan(plan, NULL);
565  }
566 }
void ReleaseCachedPlan(CachedPlan *plan, ResourceOwner owner)
Definition: plancache.c:1291

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

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

◆ ResetPlanCache()

void ResetPlanCache ( void  )

Definition at line 2187 of file plancache.c.

2188 {
2189  dlist_iter iter;
2190 
2192  {
2194  node, iter.cur);
2195 
2196  Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
2197 
2198  /* No work if it's already invalidated */
2199  if (!plansource->is_valid)
2200  continue;
2201 
2202  /*
2203  * We *must not* mark transaction control statements as invalid,
2204  * particularly not ROLLBACK, because they may need to be executed in
2205  * aborted transactions when we can't revalidate them (cf bug #5269).
2206  * In general there's no point in invalidating statements for which a
2207  * new parse analysis/rewrite/plan cycle would certainly give the same
2208  * results.
2209  */
2210  if (!StmtPlanRequiresRevalidation(plansource))
2211  continue;
2212 
2213  plansource->is_valid = false;
2214  if (plansource->gplan)
2215  plansource->gplan->is_valid = false;
2216  }
2217 
2218  /* Likewise invalidate cached expressions */
2220  {
2222  node, iter.cur);
2223 
2224  Assert(cexpr->magic == CACHEDEXPR_MAGIC);
2225 
2226  cexpr->is_valid = false;
2227  }
2228 }

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

141 {
143 }
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
void ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition: resowner.c:554

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

Referenced by ReleaseCachedPlan().

◆ ResourceOwnerRememberPlanCacheRef()

static void ResourceOwnerRememberPlanCacheRef ( ResourceOwner  owner,
CachedPlan plan 
)
inlinestatic

Definition at line 135 of file plancache.c.

136 {
138 }
void ResourceOwnerRemember(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition: resowner.c:514

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

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

◆ ResOwnerReleaseCachedPlan()

static void ResOwnerReleaseCachedPlan ( Datum  res)
static

Definition at line 2242 of file plancache.c.

2243 {
2245 }
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312

References DatumGetPointer(), ReleaseCachedPlan(), and res.

◆ RevalidateCachedQuery()

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

Definition at line 583 of file plancache.c.

585 {
586  bool snapshot_set;
587  RawStmt *rawtree;
588  List *tlist; /* transient query-tree list */
589  List *qlist; /* permanent query-tree list */
590  TupleDesc resultDesc;
591  MemoryContext querytree_context;
592  MemoryContext oldcxt;
593 
594  /*
595  * For one-shot plans, we do not support revalidation checking; it's
596  * assumed the query is parsed, planned, and executed in one transaction,
597  * so that no lock re-acquisition is necessary. Also, if the statement
598  * type can't require revalidation, we needn't do anything (and we mustn't
599  * risk catalog accesses when handling, eg, transaction control commands).
600  */
601  if (plansource->is_oneshot || !StmtPlanRequiresRevalidation(plansource))
602  {
603  Assert(plansource->is_valid);
604  return NIL;
605  }
606 
607  /*
608  * If the query is currently valid, we should have a saved search_path ---
609  * check to see if that matches the current environment. If not, we want
610  * to force replan.
611  */
612  if (plansource->is_valid)
613  {
614  Assert(plansource->search_path != NULL);
616  {
617  /* Invalidate the querytree and generic plan */
618  plansource->is_valid = false;
619  if (plansource->gplan)
620  plansource->gplan->is_valid = false;
621  }
622  }
623 
624  /*
625  * If the query rewrite phase had a possible RLS dependency, we must redo
626  * it if either the role or the row_security setting has changed.
627  */
628  if (plansource->is_valid && plansource->dependsOnRLS &&
629  (plansource->rewriteRoleId != GetUserId() ||
630  plansource->rewriteRowSecurity != row_security))
631  plansource->is_valid = false;
632 
633  /*
634  * If the query is currently valid, acquire locks on the referenced
635  * objects; then check again. We need to do it this way to cover the race
636  * condition that an invalidation message arrives before we get the locks.
637  */
638  if (plansource->is_valid)
639  {
640  AcquirePlannerLocks(plansource->query_list, true);
641 
642  /*
643  * By now, if any invalidation has happened, the inval callback
644  * functions will have marked the query invalid.
645  */
646  if (plansource->is_valid)
647  {
648  /* Successfully revalidated and locked the query. */
649  return NIL;
650  }
651 
652  /* Oops, the race case happened. Release useless locks. */
653  AcquirePlannerLocks(plansource->query_list, false);
654  }
655 
656  /*
657  * Discard the no-longer-useful query tree. (Note: we don't want to do
658  * this any earlier, else we'd not have been able to release locks
659  * correctly in the race condition case.)
660  */
661  plansource->is_valid = false;
662  plansource->query_list = NIL;
663  plansource->relationOids = NIL;
664  plansource->invalItems = NIL;
665  plansource->search_path = NULL;
666 
667  /*
668  * Free the query_context. We don't really expect MemoryContextDelete to
669  * fail, but just in case, make sure the CachedPlanSource is left in a
670  * reasonably sane state. (The generic plan won't get unlinked yet, but
671  * that's acceptable.)
672  */
673  if (plansource->query_context)
674  {
675  MemoryContext qcxt = plansource->query_context;
676 
677  plansource->query_context = NULL;
678  MemoryContextDelete(qcxt);
679  }
680 
681  /* Drop the generic plan reference if any */
682  ReleaseGenericPlan(plansource);
683 
684  /*
685  * Now re-do parse analysis and rewrite. This not incidentally acquires
686  * the locks we need to do planning safely.
687  */
688  Assert(plansource->is_complete);
689 
690  /*
691  * If a snapshot is already set (the normal case), we can just use that
692  * for parsing/planning. But if it isn't, install one. Note: no point in
693  * checking whether parse analysis requires a snapshot; utility commands
694  * don't have invalidatable plans, so we'd not get here for such a
695  * command.
696  */
697  snapshot_set = false;
698  if (!ActiveSnapshotSet())
699  {
701  snapshot_set = true;
702  }
703 
704  /*
705  * Run parse analysis and rule rewriting. The parser tends to scribble on
706  * its input, so we must copy the raw parse tree to prevent corruption of
707  * the cache.
708  */
709  rawtree = copyObject(plansource->raw_parse_tree);
710  if (rawtree == NULL)
711  tlist = NIL;
712  else if (plansource->parserSetup != NULL)
713  tlist = pg_analyze_and_rewrite_withcb(rawtree,
714  plansource->query_string,
715  plansource->parserSetup,
716  plansource->parserSetupArg,
717  queryEnv);
718  else
719  tlist = pg_analyze_and_rewrite_fixedparams(rawtree,
720  plansource->query_string,
721  plansource->param_types,
722  plansource->num_params,
723  queryEnv);
724 
725  /* Release snapshot if we got one */
726  if (snapshot_set)
728 
729  /*
730  * Check or update the result tupdesc.
731  *
732  * We assume the parameter types didn't change from the first time, so no
733  * need to update that.
734  */
735  resultDesc = PlanCacheComputeResultDesc(tlist);
736  if (resultDesc == NULL && plansource->resultDesc == NULL)
737  {
738  /* OK, doesn't return tuples */
739  }
740  else if (resultDesc == NULL || plansource->resultDesc == NULL ||
741  !equalRowTypes(resultDesc, plansource->resultDesc))
742  {
743  /* can we give a better error message? */
744  if (plansource->fixed_result)
745  ereport(ERROR,
746  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
747  errmsg("cached plan must not change result type")));
748  oldcxt = MemoryContextSwitchTo(plansource->context);
749  if (resultDesc)
750  resultDesc = CreateTupleDescCopy(resultDesc);
751  if (plansource->resultDesc)
752  FreeTupleDesc(plansource->resultDesc);
753  plansource->resultDesc = resultDesc;
754  MemoryContextSwitchTo(oldcxt);
755  }
756 
757  /*
758  * Allocate new query_context and copy the completed querytree into it.
759  * It's transient until we complete the copying and dependency extraction.
760  */
761  querytree_context = AllocSetContextCreate(CurrentMemoryContext,
762  "CachedPlanQuery",
764  oldcxt = MemoryContextSwitchTo(querytree_context);
765 
766  qlist = copyObject(tlist);
767 
768  /*
769  * Use the planner machinery to extract dependencies. Data is saved in
770  * query_context. (We assume that not a lot of extra cruft is created by
771  * this call.)
772  */
774  &plansource->relationOids,
775  &plansource->invalItems,
776  &plansource->dependsOnRLS);
777 
778  /* Update RLS info as well. */
779  plansource->rewriteRoleId = GetUserId();
780  plansource->rewriteRowSecurity = row_security;
781 
782  /*
783  * Also save the current search_path in the query_context. (This should
784  * not generate much extra cruft either, since almost certainly the path
785  * is already valid.)
786  */
787  plansource->search_path = GetSearchPathMatcher(querytree_context);
788 
789  MemoryContextSwitchTo(oldcxt);
790 
791  /* Now reparent the finished query_context and save the links */
792  MemoryContextSetParent(querytree_context, plansource->context);
793 
794  plansource->query_context = querytree_context;
795  plansource->query_list = qlist;
796 
797  /*
798  * Note: we do not reset generic_cost or total_custom_cost, although we
799  * could choose to do so. If the DDL or statistics change that prompted
800  * the invalidation meant a significant change in the cost estimates, it
801  * would be better to reset those variables and start fresh; but often it
802  * doesn't, and we're better retaining our hard-won knowledge about the
803  * relative costs.
804  */
805 
806  plansource->is_valid = true;
807 
808  /* Return transient copy of querytrees for possible use in planning */
809  return tlist;
810 }
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ereport(elevel,...)
Definition: elog.h:149
static void AcquirePlannerLocks(List *stmt_list, bool acquire)
Definition: plancache.c:1828
List * pg_analyze_and_rewrite_withcb(RawStmt *parsetree, const char *query_string, ParserSetupHook parserSetup, void *parserSetupArg, QueryEnvironment *queryEnv)
Definition: postgres.c:764
List * pg_analyze_and_rewrite_fixedparams(RawStmt *parsetree, const char *query_string, const Oid *paramTypes, int numParams, QueryEnvironment *queryEnv)
Definition: postgres.c:671
void FreeTupleDesc(TupleDesc tupdesc)
Definition: tupdesc.c:331
bool equalRowTypes(TupleDesc tupdesc1, TupleDesc tupdesc2)
Definition: tupdesc.c:586

References AcquirePlannerLocks(), ActiveSnapshotSet(), ALLOCSET_START_SMALL_SIZES, AllocSetContextCreate, 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(), PlanCacheComputeResultDesc(), PopActiveSnapshot(), 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 482 of file plancache.c.

483 {
484  /* Assert caller is doing things in a sane order */
485  Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
486  Assert(plansource->is_complete);
487  Assert(!plansource->is_saved);
488 
489  /* This seems worth a real test, though */
490  if (plansource->is_oneshot)
491  elog(ERROR, "cannot save one-shot cached plan");
492 
493  /*
494  * In typical use, this function would be called before generating any
495  * plans from the CachedPlanSource. If there is a generic plan, moving it
496  * into CacheMemoryContext would be pretty risky since it's unclear
497  * whether the caller has taken suitable care with making references
498  * long-lived. Best thing to do seems to be to discard the plan.
499  */
500  ReleaseGenericPlan(plansource);
501 
502  /*
503  * Reparent the source memory context under CacheMemoryContext so that it
504  * will live indefinitely. The query_context follows along since it's
505  * already a child of the other one.
506  */
508 
509  /*
510  * Add the entry to the global list of cached plans.
511  */
512  dlist_push_tail(&saved_plan_list, &plansource->node);
513 
514  plansource->is_saved = true;
515 }

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(), SPI_keepplan(), and StorePreparedStatement().

◆ ScanQueryForLocks()

static void ScanQueryForLocks ( Query parsetree,
bool  acquire 
)
static

Definition at line 1853 of file plancache.c.

1854 {
1855  ListCell *lc;
1856 
1857  /* Shouldn't get called on utility commands */
1858  Assert(parsetree->commandType != CMD_UTILITY);
1859 
1860  /*
1861  * First, process RTEs of the current query level.
1862  */
1863  foreach(lc, parsetree->rtable)
1864  {
1865  RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
1866 
1867  switch (rte->rtekind)
1868  {
1869  case RTE_RELATION:
1870  /* Acquire or release the appropriate type of lock */
1871  if (acquire)
1872  LockRelationOid(rte->relid, rte->rellockmode);
1873  else
1874  UnlockRelationOid(rte->relid, rte->rellockmode);
1875  break;
1876 
1877  case RTE_SUBQUERY:
1878  /* If this was a view, must lock/unlock the view */
1879  if (OidIsValid(rte->relid))
1880  {
1881  if (acquire)
1882  LockRelationOid(rte->relid, rte->rellockmode);
1883  else
1884  UnlockRelationOid(rte->relid, rte->rellockmode);
1885  }
1886  /* Recurse into subquery-in-FROM */
1887  ScanQueryForLocks(rte->subquery, acquire);
1888  break;
1889 
1890  default:
1891  /* ignore other types of RTEs */
1892  break;
1893  }
1894  }
1895 
1896  /* Recurse into subquery-in-WITH */
1897  foreach(lc, parsetree->cteList)
1898  {
1900 
1901  ScanQueryForLocks(castNode(Query, cte->ctequery), acquire);
1902  }
1903 
1904  /*
1905  * Recurse into sublink subqueries, too. But we already did the ones in
1906  * the rtable and cteList.
1907  */
1908  if (parsetree->hasSubLinks)
1909  {
1911  (void *) &acquire,
1913  }
1914 }
#define query_tree_walker(q, w, c, f)
Definition: nodeFuncs.h:156
#define QTW_IGNORE_RC_SUBQUERIES
Definition: nodeFuncs.h:24
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
static bool ScanQueryWalker(Node *node, bool *acquire)
Definition: plancache.c:1920
Query * subquery
Definition: parsenodes.h:1114

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

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

◆ ScanQueryWalker()

static bool ScanQueryWalker ( Node node,
bool acquire 
)
static

Definition at line 1920 of file plancache.c.

1921 {
1922  if (node == NULL)
1923  return false;
1924  if (IsA(node, SubLink))
1925  {
1926  SubLink *sub = (SubLink *) node;
1927 
1928  /* Do what we came for */
1929  ScanQueryForLocks(castNode(Query, sub->subselect), *acquire);
1930  /* Fall through to process lefthand args of SubLink */
1931  }
1932 
1933  /*
1934  * Do NOT recurse into Query nodes, because ScanQueryForLocks already
1935  * processed subselects of subselects for us.
1936  */
1938  (void *) acquire);
1939 }
#define expression_tree_walker(n, w, c)
Definition: nodeFuncs.h:151
#define IsA(nodeptr, _type_)
Definition: nodes.h:158

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

Referenced by ScanQueryForLocks().

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 147 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:2242
#define RELEASE_PRIO_PLANCACHE_REFS
Definition: resowner.h:73
@ RESOURCE_RELEASE_AFTER_LOCKS
Definition: resowner.h:56

Definition at line 124 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