#include "postgres.h"
#include <limits.h>
#include "access/transam.h"
#include "catalog/namespace.h"
#include "executor/executor.h"
#include "executor/spi.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "parser/analyze.h"
#include "parser/parsetree.h"
#include "storage/lmgr.h"
#include "tcop/pquery.h"
#include "tcop/utility.h"
#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/resowner_private.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
Go to the source code of this file.
Defines | |
#define | IsTransactionStmtPlan(plansource) |
Functions | |
static void | ReleaseGenericPlan (CachedPlanSource *plansource) |
static List * | RevalidateCachedQuery (CachedPlanSource *plansource) |
static bool | CheckCachedPlan (CachedPlanSource *plansource) |
static CachedPlan * | BuildCachedPlan (CachedPlanSource *plansource, List *qlist, ParamListInfo boundParams) |
static bool | choose_custom_plan (CachedPlanSource *plansource, ParamListInfo boundParams) |
static double | cached_plan_cost (CachedPlan *plan) |
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 bool | plan_list_is_transient (List *stmt_list) |
static TupleDesc | PlanCacheComputeResultDesc (List *stmt_list) |
static void | PlanCacheRelCallback (Datum arg, Oid relid) |
static void | PlanCacheFuncCallback (Datum arg, int cacheid, uint32 hashvalue) |
static void | PlanCacheSysCallback (Datum arg, int cacheid, uint32 hashvalue) |
void | InitPlanCache (void) |
CachedPlanSource * | CreateCachedPlan (Node *raw_parse_tree, const char *query_string, const char *commandTag) |
CachedPlanSource * | CreateOneShotCachedPlan (Node *raw_parse_tree, const char *query_string, const char *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) |
CachedPlan * | GetCachedPlan (CachedPlanSource *plansource, ParamListInfo boundParams, bool useResOwner) |
void | ReleaseCachedPlan (CachedPlan *plan, bool useResOwner) |
void | CachedPlanSetParentContext (CachedPlanSource *plansource, MemoryContext newcontext) |
CachedPlanSource * | CopyCachedPlan (CachedPlanSource *plansource) |
bool | CachedPlanIsValid (CachedPlanSource *plansource) |
List * | CachedPlanGetTargetList (CachedPlanSource *plansource) |
void | ResetPlanCache (void) |
Variables | |
static CachedPlanSource * | first_saved_plan = NULL |
#define IsTransactionStmtPlan | ( | plansource | ) |
((plansource)->raw_parse_tree && \ IsA((plansource)->raw_parse_tree, TransactionStmt))
Definition at line 75 of file plancache.c.
Referenced by choose_custom_plan(), CompleteCachedPlan(), PlanCacheFuncCallback(), PlanCacheRelCallback(), ResetPlanCache(), and RevalidateCachedQuery().
Definition at line 1374 of file plancache.c.
References Assert, get_plan_rowmark(), IsA, lfirst, list_member_int(), LockRelationOid(), PlanRowMark::markType, NULL, RangeTblEntry::relid, PlannedStmt::resultRelations, RowExclusiveLock, RowMarkRequiresRowShareLock, PlannedStmt::rowMarks, RowShareLock, PlannedStmt::rtable, RTE_RELATION, RangeTblEntry::rtekind, ScanQueryForLocks(), UnlockRelationOid(), and UtilityContainsQuery().
Referenced by CheckCachedPlan().
{ ListCell *lc1; foreach(lc1, stmt_list) { PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc1); int rt_index; ListCell *lc2; Assert(!IsA(plannedstmt, Query)); if (!IsA(plannedstmt, PlannedStmt)) { /* * Ignore utility statements, except those (such as EXPLAIN) that * contain a parsed-but-not-planned query. Note: it's okay to use * ScanQueryForLocks, even though the query hasn't been through * rule rewriting, because rewriting doesn't change the query * representation. */ Query *query = UtilityContainsQuery((Node *) plannedstmt); if (query) ScanQueryForLocks(query, acquire); continue; } rt_index = 0; foreach(lc2, plannedstmt->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2); LOCKMODE lockmode; PlanRowMark *rc; rt_index++; if (rte->rtekind != RTE_RELATION) continue; /* * Acquire the appropriate type of lock on each relation OID. Note * that we don't actually try to open the rel, and hence will not * fail if it's been dropped entirely --- we'll just transiently * acquire a non-conflicting lock. */ if (list_member_int(plannedstmt->resultRelations, rt_index)) lockmode = RowExclusiveLock; else if ((rc = get_plan_rowmark(plannedstmt->rowMarks, rt_index)) != NULL && RowMarkRequiresRowShareLock(rc->markType)) lockmode = RowShareLock; else lockmode = AccessShareLock; if (acquire) LockRelationOid(rte->relid, lockmode); else UnlockRelationOid(rte->relid, lockmode); } } }
Definition at line 1444 of file plancache.c.
References Assert, CMD_UTILITY, Query::commandType, IsA, lfirst, ScanQueryForLocks(), UtilityContainsQuery(), and Query::utilityStmt.
Referenced by RevalidateCachedQuery().
{ ListCell *lc; foreach(lc, stmt_list) { Query *query = (Query *) lfirst(lc); Assert(IsA(query, Query)); if (query->commandType == CMD_UTILITY) { /* Ignore utility statements, unless they contain a Query */ query = UtilityContainsQuery(query->utilityStmt); if (query) ScanQueryForLocks(query, acquire); continue; } ScanQueryForLocks(query, acquire); } }
static CachedPlan * BuildCachedPlan | ( | CachedPlanSource * | plansource, | |
List * | qlist, | |||
ParamListInfo | boundParams | |||
) | [static] |
Definition at line 844 of file plancache.c.
References ActiveSnapshotSet(), ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MINSIZE, AllocSetContextCreate(), analyze_requires_snapshot(), Assert, CachedPlan::context, copyObject(), CurrentMemoryContext, CachedPlanSource::cursor_options, CachedPlanSource::generation, CachedPlan::generation, GetTransactionSnapshot(), CachedPlan::is_oneshot, CachedPlanSource::is_oneshot, CachedPlan::is_saved, CachedPlan::is_valid, CachedPlanSource::is_valid, CachedPlan::magic, MemoryContextSwitchTo(), NIL, palloc(), pg_plan_queries(), plan_list_is_transient(), PopActiveSnapshot(), PushActiveSnapshot(), CachedPlanSource::query_list, CachedPlanSource::raw_parse_tree, CachedPlan::refcount, RevalidateCachedQuery(), CachedPlan::saved_xmin, SPI_pop_conditional(), SPI_push_conditional(), CachedPlan::stmt_list, TransactionIdIsNormal, and TransactionXmin.
Referenced by GetCachedPlan().
{ CachedPlan *plan; List *plist; bool snapshot_set; bool spi_pushed; MemoryContext plan_context; MemoryContext oldcxt = CurrentMemoryContext; /* * Normally the querytree should be valid already, but if it's not, * rebuild it. * * NOTE: GetCachedPlan should have called RevalidateCachedQuery first, so * we ought to be holding sufficient locks to prevent any invalidation. * However, if we're building a custom plan after having built and * rejected a generic plan, it's possible to reach here with is_valid * false due to an invalidation while making the generic plan. In theory * the invalidation must be a false positive, perhaps a consequence of an * sinval reset event or the CLOBBER_CACHE_ALWAYS debug code. But for * safety, let's treat it as real and redo the RevalidateCachedQuery call. */ if (!plansource->is_valid) qlist = RevalidateCachedQuery(plansource); /* * If we don't already have a copy of the querytree list that can be * scribbled on by the planner, make one. For a one-shot plan, we assume * it's okay to scribble on the original query_list. */ if (qlist == NIL) { if (!plansource->is_oneshot) qlist = (List *) copyObject(plansource->query_list); else qlist = plansource->query_list; } /* * If a snapshot is already set (the normal case), we can just use that * for planning. But if it isn't, and we need one, install one. */ snapshot_set = false; if (!ActiveSnapshotSet() && analyze_requires_snapshot(plansource->raw_parse_tree)) { PushActiveSnapshot(GetTransactionSnapshot()); snapshot_set = true; } /* * The planner may try to call SPI-using functions, which causes a problem * if we're already inside one. Rather than expect all SPI-using code to * do SPI_push whenever a replan could happen, it seems best to take care * of the case here. */ spi_pushed = SPI_push_conditional(); /* * Generate the plan. */ plist = pg_plan_queries(qlist, plansource->cursor_options, boundParams); /* Clean up SPI state */ SPI_pop_conditional(spi_pushed); /* Release snapshot if we got one */ if (snapshot_set) PopActiveSnapshot(); /* * Normally we make a dedicated memory context for the CachedPlan and its * subsidiary data. (It's probably not going to be large, but just in * case, use the default maxsize parameter. It's transient for the * moment.) But for a one-shot plan, we just leave it in the caller's * memory context. */ if (!plansource->is_oneshot) { plan_context = AllocSetContextCreate(CurrentMemoryContext, "CachedPlan", ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); /* * Copy plan into the new context. */ MemoryContextSwitchTo(plan_context); plist = (List *) copyObject(plist); } else plan_context = CurrentMemoryContext; /* * Create and fill the CachedPlan struct within the new context. */ plan = (CachedPlan *) palloc(sizeof(CachedPlan)); plan->magic = CACHEDPLAN_MAGIC; plan->stmt_list = plist; if (plan_list_is_transient(plist)) { Assert(TransactionIdIsNormal(TransactionXmin)); plan->saved_xmin = TransactionXmin; } else plan->saved_xmin = InvalidTransactionId; plan->refcount = 0; plan->context = plan_context; plan->is_oneshot = plansource->is_oneshot; plan->is_saved = false; plan->is_valid = true; /* assign generation number to new plan */ plan->generation = ++(plansource->generation); MemoryContextSwitchTo(oldcxt); return plan; }
static double cached_plan_cost | ( | CachedPlan * | plan | ) | [static] |
Definition at line 1019 of file plancache.c.
References IsA, lfirst, PlannedStmt::planTree, CachedPlan::stmt_list, and Plan::total_cost.
Referenced by GetCachedPlan().
{ double result = 0; ListCell *lc; foreach(lc, plan->stmt_list) { PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc); if (!IsA(plannedstmt, PlannedStmt)) continue; /* Ignore utility statements */ result += plannedstmt->planTree->total_cost; } return result; }
List* CachedPlanGetTargetList | ( | CachedPlanSource * | plansource | ) |
Definition at line 1345 of file plancache.c.
References Assert, CACHEDPLANSOURCE_MAGIC, FetchStatementTargetList(), CachedPlanSource::is_complete, CachedPlanSource::magic, NULL, PortalListGetPrimaryStmt(), CachedPlanSource::query_list, CachedPlanSource::resultDesc, and RevalidateCachedQuery().
Referenced by exec_describe_statement_message(), and FetchPreparedStatementTargetList().
{ Node *pstmt; /* Assert caller is doing things in a sane order */ Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); Assert(plansource->is_complete); /* * No work needed if statement doesn't return tuples (we assume this * feature cannot be changed by an invalidation) */ if (plansource->resultDesc == NULL) return NIL; /* Make sure the querytree list is valid and we have parse-time locks */ RevalidateCachedQuery(plansource); /* Get the primary statement and find out what it returns */ pstmt = PortalListGetPrimaryStmt(plansource->query_list); return FetchStatementTargetList(pstmt); }
bool CachedPlanIsValid | ( | CachedPlanSource * | plansource | ) |
Definition at line 1332 of file plancache.c.
References Assert, CACHEDPLANSOURCE_MAGIC, CachedPlanSource::is_valid, and CachedPlanSource::magic.
Referenced by SPI_plan_is_valid().
{ Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); return plansource->is_valid; }
void CachedPlanSetParentContext | ( | CachedPlanSource * | plansource, | |
MemoryContext | newcontext | |||
) |
Definition at line 1203 of file plancache.c.
References Assert, CACHEDPLAN_MAGIC, CACHEDPLANSOURCE_MAGIC, CachedPlan::context, CachedPlanSource::context, elog, ERROR, CachedPlanSource::gplan, CachedPlanSource::is_complete, CachedPlanSource::is_oneshot, CachedPlanSource::is_saved, CachedPlan::magic, CachedPlanSource::magic, and MemoryContextSetParent().
Referenced by _SPI_make_plan_non_temp().
{ /* Assert caller is doing things in a sane order */ Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); Assert(plansource->is_complete); /* These seem worth real tests, though */ if (plansource->is_saved) elog(ERROR, "cannot move a saved cached plan to another context"); if (plansource->is_oneshot) elog(ERROR, "cannot move a one-shot cached plan to another context"); /* OK, let the caller keep the plan where he wishes */ MemoryContextSetParent(plansource->context, newcontext); /* * The query_context needs no special handling, since it's a child of * plansource->context. But if there's a generic plan, it should be * maintained as a sibling of plansource->context. */ if (plansource->gplan) { Assert(plansource->gplan->magic == CACHEDPLAN_MAGIC); MemoryContextSetParent(plansource->gplan->context, newcontext); } }
static bool CheckCachedPlan | ( | CachedPlanSource * | plansource | ) | [static] |
Definition at line 767 of file plancache.c.
References AcquireExecutorLocks(), Assert, CACHEDPLAN_MAGIC, CachedPlanSource::gplan, CachedPlan::is_oneshot, CachedPlan::is_valid, CachedPlanSource::is_valid, CachedPlan::magic, CachedPlan::refcount, ReleaseGenericPlan(), CachedPlan::saved_xmin, CachedPlan::stmt_list, TransactionIdEquals, TransactionIdIsValid, and TransactionXmin.
Referenced by GetCachedPlan().
{ CachedPlan *plan = plansource->gplan; /* Assert that caller checked the querytree */ Assert(plansource->is_valid); /* If there's no generic plan, just say "false" */ if (!plan) return false; Assert(plan->magic == CACHEDPLAN_MAGIC); /* Generic plans are never one-shot */ Assert(!plan->is_oneshot); /* * If it appears valid, acquire locks and recheck; this is much the same * logic as in RevalidateCachedQuery, but for a plan. */ if (plan->is_valid) { /* * Plan must have positive refcount because it is referenced by * plansource; so no need to fear it disappears under us here. */ Assert(plan->refcount > 0); AcquireExecutorLocks(plan->stmt_list, true); /* * If plan was transient, check to see if TransactionXmin has * advanced, and if so invalidate it. */ if (plan->is_valid && TransactionIdIsValid(plan->saved_xmin) && !TransactionIdEquals(plan->saved_xmin, TransactionXmin)) plan->is_valid = false; /* * By now, if any invalidation has happened, the inval callback * functions will have marked the plan invalid. */ if (plan->is_valid) { /* Successfully revalidated and locked the query. */ return true; } /* Ooops, the race case happened. Release useless locks. */ AcquireExecutorLocks(plan->stmt_list, false); } /* * Plan has been invalidated, so unlink it from the parent and release it. */ ReleaseGenericPlan(plansource); return false; }
static bool choose_custom_plan | ( | CachedPlanSource * | plansource, | |
ParamListInfo | boundParams | |||
) | [static] |
Definition at line 973 of file plancache.c.
References CURSOR_OPT_CUSTOM_PLAN, CURSOR_OPT_GENERIC_PLAN, CachedPlanSource::cursor_options, CachedPlanSource::generic_cost, CachedPlanSource::is_oneshot, IsTransactionStmtPlan, NULL, CachedPlanSource::num_custom_plans, and CachedPlanSource::total_custom_cost.
Referenced by GetCachedPlan().
{ double avg_custom_cost; /* One-shot plans will always be considered custom */ if (plansource->is_oneshot) return true; /* Otherwise, never any point in a custom plan if there's no parameters */ if (boundParams == NULL) return false; /* ... nor for transaction control statements */ if (IsTransactionStmtPlan(plansource)) return false; /* See if caller wants to force the decision */ if (plansource->cursor_options & CURSOR_OPT_GENERIC_PLAN) return false; if (plansource->cursor_options & CURSOR_OPT_CUSTOM_PLAN) return true; /* Generate custom plans until we have done at least 5 (arbitrary) */ if (plansource->num_custom_plans < 5) return true; avg_custom_cost = plansource->total_custom_cost / plansource->num_custom_plans; /* * Prefer generic plan if it's less than 10% more expensive than average * custom plan. This threshold is a bit arbitrary; it'd be better if we * had some means of comparing planning time to the estimated runtime cost * differential. * * Note that if generic_cost is -1 (indicating we've not yet determined * the generic plan cost), we'll always prefer generic at this point. */ if (plansource->generic_cost < avg_custom_cost * 1.1) return false; return true; }
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 315 of file plancache.c.
References ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MINSIZE, AllocSetContextCreate(), Assert, CACHEDPLANSOURCE_MAGIC, CachedPlanSource::context, copyObject(), CurrentMemoryContext, CachedPlanSource::cursor_options, extract_query_dependencies(), CachedPlanSource::fixed_result, GetOverrideSearchPath(), CachedPlanSource::invalItems, CachedPlanSource::is_complete, CachedPlanSource::is_oneshot, CachedPlanSource::is_valid, IsTransactionStmtPlan, CachedPlanSource::magic, MemoryContextSetParent(), MemoryContextSwitchTo(), NULL, CachedPlanSource::num_params, palloc(), CachedPlanSource::param_types, CachedPlanSource::parserSetup, CachedPlanSource::parserSetupArg, PlanCacheComputeResultDesc(), CachedPlanSource::query_context, CachedPlanSource::query_list, CachedPlanSource::relationOids, CachedPlanSource::resultDesc, and CachedPlanSource::search_path.
Referenced by _SPI_execute_plan(), _SPI_prepare_plan(), exec_parse_message(), and PrepareQuery().
{ MemoryContext source_context = plansource->context; MemoryContext oldcxt = CurrentMemoryContext; /* Assert caller is doing things in a sane order */ Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); Assert(!plansource->is_complete); /* * If caller supplied a querytree_context, reparent it underneath the * CachedPlanSource's context; otherwise, create a suitable context and * copy the querytree_list into it. But no data copying should be done * for one-shot plans; for those, assume the passed querytree_list is * sufficiently long-lived. */ if (plansource->is_oneshot) { querytree_context = CurrentMemoryContext; } else if (querytree_context != NULL) { MemoryContextSetParent(querytree_context, source_context); MemoryContextSwitchTo(querytree_context); } else { /* Again, it's a good bet the querytree_context can be small */ querytree_context = AllocSetContextCreate(source_context, "CachedPlanQuery", ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); MemoryContextSwitchTo(querytree_context); querytree_list = (List *) copyObject(querytree_list); } plansource->query_context = querytree_context; plansource->query_list = querytree_list; if (!plansource->is_oneshot && !IsTransactionStmtPlan(plansource)) { /* * Use the planner machinery to extract dependencies. Data is saved * in query_context. (We assume that not a lot of extra cruft is * created by this call.) We can skip this for one-shot plans, and * transaction control commands have no such dependencies anyway. */ extract_query_dependencies((Node *) querytree_list, &plansource->relationOids, &plansource->invalItems); /* * Also save the current search_path in the query_context. (This * should not generate much extra cruft either, since almost certainly * the path is already valid.) Again, we don't really need this for * one-shot plans; and we *must* skip this for transaction control * commands, because this could result in catalog accesses. */ plansource->search_path = GetOverrideSearchPath(querytree_context); } /* * Save the final parameter types (or other parameter specification data) * into the source_context, as well as our other parameters. Also save * the result tuple descriptor. */ MemoryContextSwitchTo(source_context); if (num_params > 0) { plansource->param_types = (Oid *) palloc(num_params * sizeof(Oid)); memcpy(plansource->param_types, param_types, num_params * sizeof(Oid)); } else plansource->param_types = NULL; plansource->num_params = num_params; plansource->parserSetup = parserSetup; plansource->parserSetupArg = parserSetupArg; plansource->cursor_options = cursor_options; plansource->fixed_result = fixed_result; plansource->resultDesc = PlanCacheComputeResultDesc(querytree_list); MemoryContextSwitchTo(oldcxt); plansource->is_complete = true; plansource->is_valid = true; }
CachedPlanSource* CopyCachedPlan | ( | CachedPlanSource * | plansource | ) |
Definition at line 1241 of file plancache.c.
References ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MINSIZE, AllocSetContextCreate(), Assert, CACHEDPLANSOURCE_MAGIC, CachedPlanSource::commandTag, CachedPlanSource::context, copyObject(), CopyOverrideSearchPath(), CreateTupleDescCopy(), CurrentMemoryContext, CachedPlanSource::cursor_options, 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, MemoryContextSwitchTo(), CachedPlanSource::next_saved, CachedPlanSource::num_custom_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::search_path, and CachedPlanSource::total_custom_cost.
Referenced by _SPI_save_plan().
{ CachedPlanSource *newsource; MemoryContext source_context; MemoryContext querytree_context; MemoryContext oldcxt; Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); Assert(plansource->is_complete); /* * One-shot plans can't be copied, because we haven't taken care that * parsing/planning didn't scribble on the raw parse tree or querytrees. */ if (plansource->is_oneshot) elog(ERROR, "cannot copy a one-shot cached plan"); source_context = AllocSetContextCreate(CurrentMemoryContext, "CachedPlanSource", ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); oldcxt = MemoryContextSwitchTo(source_context); newsource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource)); newsource->magic = CACHEDPLANSOURCE_MAGIC; newsource->raw_parse_tree = copyObject(plansource->raw_parse_tree); newsource->query_string = pstrdup(plansource->query_string); newsource->commandTag = plansource->commandTag; if (plansource->num_params > 0) { newsource->param_types = (Oid *) palloc(plansource->num_params * sizeof(Oid)); memcpy(newsource->param_types, plansource->param_types, plansource->num_params * sizeof(Oid)); } else newsource->param_types = NULL; newsource->num_params = plansource->num_params; newsource->parserSetup = plansource->parserSetup; newsource->parserSetupArg = plansource->parserSetupArg; newsource->cursor_options = plansource->cursor_options; newsource->fixed_result = plansource->fixed_result; if (plansource->resultDesc) newsource->resultDesc = CreateTupleDescCopy(plansource->resultDesc); else newsource->resultDesc = NULL; newsource->context = source_context; querytree_context = AllocSetContextCreate(source_context, "CachedPlanQuery", ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); MemoryContextSwitchTo(querytree_context); newsource->query_list = (List *) copyObject(plansource->query_list); newsource->relationOids = (List *) copyObject(plansource->relationOids); newsource->invalItems = (List *) copyObject(plansource->invalItems); if (plansource->search_path) newsource->search_path = CopyOverrideSearchPath(plansource->search_path); newsource->query_context = querytree_context; newsource->gplan = NULL; newsource->is_oneshot = false; newsource->is_complete = true; newsource->is_saved = false; newsource->is_valid = plansource->is_valid; newsource->generation = plansource->generation; newsource->next_saved = NULL; /* We may as well copy any acquired cost knowledge */ newsource->generic_cost = plansource->generic_cost; newsource->total_custom_cost = plansource->total_custom_cost; newsource->num_custom_plans = plansource->num_custom_plans; MemoryContextSwitchTo(oldcxt); return newsource; }
CachedPlanSource* CreateCachedPlan | ( | Node * | raw_parse_tree, | |
const char * | query_string, | |||
const char * | commandTag | |||
) |
Definition at line 146 of file plancache.c.
References ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MINSIZE, AllocSetContextCreate(), Assert, CachedPlanSource::commandTag, CachedPlanSource::context, copyObject(), CurrentMemoryContext, CachedPlanSource::cursor_options, 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, MemoryContextSwitchTo(), CachedPlanSource::next_saved, NULL, CachedPlanSource::num_custom_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::search_path, and CachedPlanSource::total_custom_cost.
Referenced by _SPI_prepare_plan(), exec_parse_message(), and PrepareQuery().
{ CachedPlanSource *plansource; MemoryContext source_context; MemoryContext oldcxt; Assert(query_string != NULL); /* required as of 8.4 */ /* * Make a dedicated memory context for the CachedPlanSource and its * permanent subsidiary data. It's probably not going to be large, but * just in case, use the default maxsize parameter. Initially it's a * child of the caller's context (which we assume to be transient), so * that it will be cleaned up on error. */ source_context = AllocSetContextCreate(CurrentMemoryContext, "CachedPlanSource", ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); /* * Create and fill the CachedPlanSource struct within the new context. * Most fields are just left empty for the moment. */ oldcxt = MemoryContextSwitchTo(source_context); plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource)); plansource->magic = CACHEDPLANSOURCE_MAGIC; plansource->raw_parse_tree = copyObject(raw_parse_tree); plansource->query_string = pstrdup(query_string); plansource->commandTag = commandTag; plansource->param_types = NULL; plansource->num_params = 0; plansource->parserSetup = NULL; plansource->parserSetupArg = NULL; plansource->cursor_options = 0; plansource->fixed_result = false; plansource->resultDesc = NULL; plansource->context = source_context; plansource->query_list = NIL; plansource->relationOids = NIL; plansource->invalItems = NIL; plansource->search_path = NULL; plansource->query_context = NULL; plansource->gplan = NULL; plansource->is_oneshot = false; plansource->is_complete = false; plansource->is_saved = false; plansource->is_valid = false; plansource->generation = 0; plansource->next_saved = NULL; plansource->generic_cost = -1; plansource->total_custom_cost = 0; plansource->num_custom_plans = 0; MemoryContextSwitchTo(oldcxt); return plansource; }
CachedPlanSource* CreateOneShotCachedPlan | ( | Node * | raw_parse_tree, | |
const char * | query_string, | |||
const char * | commandTag | |||
) |
Definition at line 228 of file plancache.c.
References Assert, CachedPlanSource::commandTag, CachedPlanSource::context, CurrentMemoryContext, CachedPlanSource::cursor_options, 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, CachedPlanSource::next_saved, NULL, CachedPlanSource::num_custom_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::search_path, and CachedPlanSource::total_custom_cost.
Referenced by _SPI_prepare_oneshot_plan().
{ CachedPlanSource *plansource; Assert(query_string != NULL); /* required as of 8.4 */ /* * Create and fill the CachedPlanSource struct within the caller's memory * context. Most fields are just left empty for the moment. */ plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource)); plansource->magic = CACHEDPLANSOURCE_MAGIC; plansource->raw_parse_tree = raw_parse_tree; plansource->query_string = query_string; plansource->commandTag = commandTag; plansource->param_types = NULL; plansource->num_params = 0; plansource->parserSetup = NULL; plansource->parserSetupArg = NULL; plansource->cursor_options = 0; plansource->fixed_result = false; plansource->resultDesc = NULL; plansource->context = CurrentMemoryContext; plansource->query_list = NIL; plansource->relationOids = NIL; plansource->invalItems = NIL; plansource->search_path = NULL; plansource->query_context = NULL; plansource->gplan = NULL; plansource->is_oneshot = true; plansource->is_complete = false; plansource->is_saved = false; plansource->is_valid = false; plansource->generation = 0; plansource->next_saved = NULL; plansource->generic_cost = -1; plansource->total_custom_cost = 0; plansource->num_custom_plans = 0; return plansource; }
void DropCachedPlan | ( | CachedPlanSource * | plansource | ) |
Definition at line 473 of file plancache.c.
References Assert, CACHEDPLANSOURCE_MAGIC, CachedPlanSource::context, CachedPlanSource::is_oneshot, CachedPlanSource::is_saved, CachedPlanSource::magic, MemoryContextDelete(), CachedPlanSource::next_saved, and ReleaseGenericPlan().
Referenced by drop_unnamed_stmt(), DropAllPreparedStatements(), DropPreparedStatement(), and SPI_freeplan().
{ Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); /* If it's been saved, remove it from the list */ if (plansource->is_saved) { if (first_saved_plan == plansource) first_saved_plan = plansource->next_saved; else { CachedPlanSource *psrc; for (psrc = first_saved_plan; psrc; psrc = psrc->next_saved) { if (psrc->next_saved == plansource) { psrc->next_saved = plansource->next_saved; break; } } } plansource->is_saved = false; } /* Decrement generic CachePlan's refcount and drop if no longer needed */ ReleaseGenericPlan(plansource); /* Mark it no longer valid */ plansource->magic = 0; /* * Remove the CachedPlanSource and all subsidiary data (including the * query_context if any). But if it's a one-shot we can't free anything. */ if (!plansource->is_oneshot) MemoryContextDelete(plansource->context); }
CachedPlan* GetCachedPlan | ( | CachedPlanSource * | plansource, | |
ParamListInfo | boundParams, | |||
bool | useResOwner | |||
) |
Definition at line 1056 of file plancache.c.
References Assert, BuildCachedPlan(), cached_plan_cost(), CACHEDPLAN_MAGIC, CACHEDPLANSOURCE_MAGIC, CacheMemoryContext, CheckCachedPlan(), choose_custom_plan(), CachedPlanSource::context, CachedPlan::context, CurrentResourceOwner, elog, ERROR, CachedPlanSource::generic_cost, CachedPlanSource::gplan, CachedPlanSource::is_complete, CachedPlan::is_saved, CachedPlanSource::is_saved, CachedPlan::magic, CachedPlanSource::magic, MemoryContextGetParent(), MemoryContextSetParent(), NULL, CachedPlanSource::num_custom_plans, CachedPlan::refcount, ReleaseGenericPlan(), ResourceOwnerEnlargePlanCacheRefs(), 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().
{ CachedPlan *plan; List *qlist; bool customplan; /* Assert caller is doing things in a sane order */ Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); Assert(plansource->is_complete); /* This seems worth a real test, though */ if (useResOwner && !plansource->is_saved) elog(ERROR, "cannot apply ResourceOwner to non-saved cached plan"); /* Make sure the querytree list is valid and we have parse-time locks */ qlist = RevalidateCachedQuery(plansource); /* Decide whether to use a custom plan */ customplan = choose_custom_plan(plansource, boundParams); if (!customplan) { if (CheckCachedPlan(plansource)) { /* We want a generic plan, and we already have a valid one */ plan = plansource->gplan; Assert(plan->magic == CACHEDPLAN_MAGIC); } else { /* Build a new generic plan */ plan = BuildCachedPlan(plansource, qlist, NULL); /* Just make real sure plansource->gplan is clear */ ReleaseGenericPlan(plansource); /* Link the new generic plan into the plansource */ plansource->gplan = plan; plan->refcount++; /* Immediately reparent into appropriate context */ if (plansource->is_saved) { /* saved plans all live under CacheMemoryContext */ MemoryContextSetParent(plan->context, CacheMemoryContext); plan->is_saved = true; } else { /* otherwise, it should be a sibling of the plansource */ MemoryContextSetParent(plan->context, MemoryContextGetParent(plansource->context)); } /* Update generic_cost whenever we make a new generic plan */ plansource->generic_cost = cached_plan_cost(plan); /* * If, based on the now-known value of generic_cost, we'd not have * chosen to use a generic plan, then forget it and make a custom * plan. This is a bit of a wart but is necessary to avoid a * glitch in behavior when the custom plans are consistently big * winners; at some point we'll experiment with a generic plan and * find it's a loser, but we don't want to actually execute that * plan. */ customplan = choose_custom_plan(plansource, boundParams); /* * If we choose to plan again, we need to re-copy the query_list, * since the planner probably scribbled on it. We can force * BuildCachedPlan to do that by passing NIL. */ qlist = NIL; } } if (customplan) { /* Build a custom plan */ plan = BuildCachedPlan(plansource, qlist, boundParams); /* Accumulate total costs of custom plans, but 'ware overflow */ if (plansource->num_custom_plans < INT_MAX) { plansource->total_custom_cost += cached_plan_cost(plan); plansource->num_custom_plans++; } } /* Flag the plan as in use by caller */ if (useResOwner) ResourceOwnerEnlargePlanCacheRefs(CurrentResourceOwner); plan->refcount++; if (useResOwner) ResourceOwnerRememberPlanCacheRef(CurrentResourceOwner, plan); /* * Saved plans should be under CacheMemoryContext so they will not go away * until their reference count goes to zero. In the generic-plan cases we * already took care of that, but for a custom plan, do it as soon as we * have created a reference-counted link. */ if (customplan && plansource->is_saved) { MemoryContextSetParent(plan->context, CacheMemoryContext); plan->is_saved = true; } return plan; }
void InitPlanCache | ( | void | ) |
Definition at line 112 of file plancache.c.
References AMOPOPID, CacheRegisterRelcacheCallback(), CacheRegisterSyscacheCallback(), NAMESPACEOID, OPEROID, PlanCacheFuncCallback(), PlanCacheRelCallback(), PlanCacheSysCallback(), and PROCOID.
Referenced by InitPostgres().
{ CacheRegisterRelcacheCallback(PlanCacheRelCallback, (Datum) 0); CacheRegisterSyscacheCallback(PROCOID, PlanCacheFuncCallback, (Datum) 0); CacheRegisterSyscacheCallback(NAMESPACEOID, PlanCacheSysCallback, (Datum) 0); CacheRegisterSyscacheCallback(OPEROID, PlanCacheSysCallback, (Datum) 0); CacheRegisterSyscacheCallback(AMOPOPID, PlanCacheSysCallback, (Datum) 0); }
Definition at line 1565 of file plancache.c.
References IsA, lfirst, and PlannedStmt::transientPlan.
Referenced by BuildCachedPlan().
{ ListCell *lc; foreach(lc, stmt_list) { PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc); if (!IsA(plannedstmt, PlannedStmt)) continue; /* Ignore utility statements */ if (plannedstmt->transientPlan) return true; } return false; }
Definition at line 1591 of file plancache.c.
References Assert, ChoosePortalStrategy(), ExecCleanTypeFromTL(), IsA, linitial, PORTAL_MULTI_QUERY, PORTAL_ONE_MOD_WITH, PORTAL_ONE_RETURNING, PORTAL_ONE_SELECT, PORTAL_UTIL_SELECT, PortalListGetPrimaryStmt(), Query::returningList, Query::targetList, Query::utilityStmt, and UtilityTupleDescriptor().
Referenced by CompleteCachedPlan(), and RevalidateCachedQuery().
{ Query *query; switch (ChoosePortalStrategy(stmt_list)) { case PORTAL_ONE_SELECT: case PORTAL_ONE_MOD_WITH: query = (Query *) linitial(stmt_list); Assert(IsA(query, Query)); return ExecCleanTypeFromTL(query->targetList, false); case PORTAL_ONE_RETURNING: query = (Query *) PortalListGetPrimaryStmt(stmt_list); Assert(IsA(query, Query)); Assert(query->returningList); return ExecCleanTypeFromTL(query->returningList, false); case PORTAL_UTIL_SELECT: query = (Query *) linitial(stmt_list); Assert(IsA(query, Query)); Assert(query->utilityStmt); return UtilityTupleDescriptor(query->utilityStmt); case PORTAL_MULTI_QUERY: /* will not return tuples */ break; } return NULL; }
Definition at line 1696 of file plancache.c.
References Assert, CACHEDPLANSOURCE_MAGIC, PlanInvalItem::cacheId, CachedPlanSource::gplan, PlanInvalItem::hashValue, PlannedStmt::invalItems, CachedPlanSource::invalItems, CachedPlan::is_valid, CachedPlanSource::is_valid, IsA, IsTransactionStmtPlan, lfirst, CachedPlanSource::magic, CachedPlanSource::next_saved, and CachedPlan::stmt_list.
Referenced by InitPlanCache().
{ CachedPlanSource *plansource; for (plansource = first_saved_plan; plansource; plansource = plansource->next_saved) { ListCell *lc; Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); /* No work if it's already invalidated */ if (!plansource->is_valid) continue; /* Never invalidate transaction control commands */ if (IsTransactionStmtPlan(plansource)) continue; /* * Check the dependency list for the rewritten querytree. */ foreach(lc, plansource->invalItems) { PlanInvalItem *item = (PlanInvalItem *) lfirst(lc); if (item->cacheId != cacheid) continue; if (hashvalue == 0 || item->hashValue == hashvalue) { /* Invalidate the querytree and generic plan */ plansource->is_valid = false; if (plansource->gplan) plansource->gplan->is_valid = false; break; } } /* * The generic plan, if any, could have more dependencies than the * querytree does, so we have to check it too. */ if (plansource->gplan && plansource->gplan->is_valid) { foreach(lc, plansource->gplan->stmt_list) { PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc); ListCell *lc3; Assert(!IsA(plannedstmt, Query)); if (!IsA(plannedstmt, PlannedStmt)) continue; /* Ignore utility statements */ foreach(lc3, plannedstmt->invalItems) { PlanInvalItem *item = (PlanInvalItem *) lfirst(lc3); if (item->cacheId != cacheid) continue; if (hashvalue == 0 || item->hashValue == hashvalue) { /* Invalidate the generic plan only */ plansource->gplan->is_valid = false; break; /* out of invalItems scan */ } } if (!plansource->gplan->is_valid) break; /* out of stmt_list scan */ } } } }
Definition at line 1630 of file plancache.c.
References Assert, CACHEDPLANSOURCE_MAGIC, CachedPlanSource::gplan, InvalidOid, CachedPlan::is_valid, CachedPlanSource::is_valid, IsA, IsTransactionStmtPlan, lfirst, list_member_oid(), CachedPlanSource::magic, CachedPlanSource::next_saved, NIL, PlannedStmt::relationOids, CachedPlanSource::relationOids, and CachedPlan::stmt_list.
Referenced by InitPlanCache().
{ CachedPlanSource *plansource; for (plansource = first_saved_plan; plansource; plansource = plansource->next_saved) { Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); /* No work if it's already invalidated */ if (!plansource->is_valid) continue; /* Never invalidate transaction control commands */ if (IsTransactionStmtPlan(plansource)) continue; /* * Check the dependency list for the rewritten querytree. */ if ((relid == InvalidOid) ? plansource->relationOids != NIL : list_member_oid(plansource->relationOids, relid)) { /* Invalidate the querytree and generic plan */ plansource->is_valid = false; if (plansource->gplan) plansource->gplan->is_valid = false; } /* * The generic plan, if any, could have more dependencies than the * querytree does, so we have to check it too. */ if (plansource->gplan && plansource->gplan->is_valid) { ListCell *lc; foreach(lc, plansource->gplan->stmt_list) { PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc); Assert(!IsA(plannedstmt, Query)); if (!IsA(plannedstmt, PlannedStmt)) continue; /* Ignore utility statements */ if ((relid == InvalidOid) ? plannedstmt->relationOids != NIL : list_member_oid(plannedstmt->relationOids, relid)) { /* Invalidate the generic plan only */ plansource->gplan->is_valid = false; break; /* out of stmt_list scan */ } } } } }
Definition at line 1776 of file plancache.c.
References ResetPlanCache().
Referenced by InitPlanCache().
{ ResetPlanCache(); }
void ReleaseCachedPlan | ( | CachedPlan * | plan, | |
bool | useResOwner | |||
) |
Definition at line 1175 of file plancache.c.
References Assert, CACHEDPLAN_MAGIC, CachedPlan::context, CurrentResourceOwner, CachedPlan::is_oneshot, CachedPlan::is_saved, CachedPlan::magic, MemoryContextDelete(), CachedPlan::refcount, and ResourceOwnerForgetPlanCacheRef().
Referenced by _SPI_execute_plan(), exec_eval_simple_expr(), exec_simple_check_plan(), ExplainExecuteQuery(), PortalReleaseCachedPlan(), ReleaseGenericPlan(), ResourceOwnerReleaseInternal(), and SPI_cursor_open_internal().
{ Assert(plan->magic == CACHEDPLAN_MAGIC); if (useResOwner) { Assert(plan->is_saved); ResourceOwnerForgetPlanCacheRef(CurrentResourceOwner, plan); } Assert(plan->refcount > 0); plan->refcount--; if (plan->refcount == 0) { /* Mark it no longer valid */ plan->magic = 0; /* One-shot plans do not own their context, so we can't free them */ if (!plan->is_oneshot) MemoryContextDelete(plan->context); } }
static void ReleaseGenericPlan | ( | CachedPlanSource * | plansource | ) | [static] |
Definition at line 516 of file plancache.c.
References Assert, CACHEDPLAN_MAGIC, CachedPlanSource::gplan, CachedPlan::magic, and ReleaseCachedPlan().
Referenced by CheckCachedPlan(), DropCachedPlan(), GetCachedPlan(), RevalidateCachedQuery(), and SaveCachedPlan().
{ /* Be paranoid about the possibility that ReleaseCachedPlan fails */ if (plansource->gplan) { CachedPlan *plan = plansource->gplan; Assert(plan->magic == CACHEDPLAN_MAGIC); plansource->gplan = NULL; ReleaseCachedPlan(plan, false); } }
void ResetPlanCache | ( | void | ) |
Definition at line 1785 of file plancache.c.
References Assert, CACHEDPLANSOURCE_MAGIC, CMD_UTILITY, Query::commandType, CachedPlanSource::gplan, CachedPlan::is_valid, CachedPlanSource::is_valid, IsA, IsTransactionStmtPlan, lfirst, CachedPlanSource::magic, CachedPlanSource::next_saved, CachedPlanSource::query_list, UtilityContainsQuery(), and Query::utilityStmt.
Referenced by assign_session_replication_role(), DiscardAll(), DiscardCommand(), and PlanCacheSysCallback().
{ CachedPlanSource *plansource; for (plansource = first_saved_plan; plansource; plansource = plansource->next_saved) { ListCell *lc; Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); /* No work if it's already invalidated */ if (!plansource->is_valid) continue; /* * We *must not* mark transaction control statements as invalid, * particularly not ROLLBACK, because they may need to be executed in * aborted transactions when we can't revalidate them (cf bug #5269). */ if (IsTransactionStmtPlan(plansource)) continue; /* * In general there is no point in invalidating utility statements * since they have no plans anyway. So invalidate it only if it * contains at least one non-utility statement, or contains a utility * statement that contains a pre-analyzed query (which could have * dependencies.) */ foreach(lc, plansource->query_list) { Query *query = (Query *) lfirst(lc); Assert(IsA(query, Query)); if (query->commandType != CMD_UTILITY || UtilityContainsQuery(query->utilityStmt)) { /* non-utility statement, so invalidate */ plansource->is_valid = false; if (plansource->gplan) plansource->gplan->is_valid = false; /* no need to look further */ break; } } } }
static List * RevalidateCachedQuery | ( | CachedPlanSource * | plansource | ) | [static] |
Definition at line 544 of file plancache.c.
References AcquirePlannerLocks(), ActiveSnapshotSet(), ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MINSIZE, AllocSetContextCreate(), Assert, CachedPlanSource::context, copyObject(), CreateTupleDescCopy(), CurrentMemoryContext, equalTupleDescs(), ereport, errcode(), errmsg(), ERROR, extract_query_dependencies(), CachedPlanSource::fixed_result, FreeTupleDesc(), GetOverrideSearchPath(), GetTransactionSnapshot(), CachedPlanSource::gplan, CachedPlanSource::invalItems, CachedPlanSource::is_complete, CachedPlanSource::is_oneshot, CachedPlan::is_valid, CachedPlanSource::is_valid, IsTransactionStmtPlan, MemoryContextDelete(), MemoryContextSetParent(), MemoryContextSwitchTo(), NULL, CachedPlanSource::num_params, OverrideSearchPathMatchesCurrent(), CachedPlanSource::param_types, CachedPlanSource::parserSetup, CachedPlanSource::parserSetupArg, pg_analyze_and_rewrite(), pg_analyze_and_rewrite_params(), PlanCacheComputeResultDesc(), PopActiveSnapshot(), PushActiveSnapshot(), CachedPlanSource::query_context, CachedPlanSource::query_list, CachedPlanSource::query_string, CachedPlanSource::raw_parse_tree, CachedPlanSource::relationOids, ReleaseGenericPlan(), CachedPlanSource::resultDesc, and CachedPlanSource::search_path.
Referenced by BuildCachedPlan(), CachedPlanGetTargetList(), and GetCachedPlan().
{ bool snapshot_set; Node *rawtree; List *tlist; /* transient query-tree list */ List *qlist; /* permanent query-tree list */ TupleDesc resultDesc; MemoryContext querytree_context; MemoryContext oldcxt; /* * For one-shot plans, we do not support revalidation checking; it's * assumed the query is parsed, planned, and executed in one transaction, * so that no lock re-acquisition is necessary. Also, there is never * any need to revalidate plans for transaction control commands (and * we mustn't risk any catalog accesses when handling those). */ if (plansource->is_oneshot || IsTransactionStmtPlan(plansource)) { Assert(plansource->is_valid); return NIL; } /* * If the query is currently valid, we should have a saved search_path --- * check to see if that matches the current environment. If not, we want * to force replan. */ if (plansource->is_valid) { Assert(plansource->search_path != NULL); if (!OverrideSearchPathMatchesCurrent(plansource->search_path)) { /* Invalidate the querytree and generic plan */ plansource->is_valid = false; if (plansource->gplan) plansource->gplan->is_valid = false; } } /* * If the query is currently valid, acquire locks on the referenced * objects; then check again. We need to do it this way to cover the race * condition that an invalidation message arrives before we get the locks. */ if (plansource->is_valid) { AcquirePlannerLocks(plansource->query_list, true); /* * By now, if any invalidation has happened, the inval callback * functions will have marked the query invalid. */ if (plansource->is_valid) { /* Successfully revalidated and locked the query. */ return NIL; } /* Ooops, the race case happened. Release useless locks. */ AcquirePlannerLocks(plansource->query_list, false); } /* * Discard the no-longer-useful query tree. (Note: we don't want to do * this any earlier, else we'd not have been able to release locks * correctly in the race condition case.) */ plansource->is_valid = false; plansource->query_list = NIL; plansource->relationOids = NIL; plansource->invalItems = NIL; plansource->search_path = NULL; /* * Free the query_context. We don't really expect MemoryContextDelete to * fail, but just in case, make sure the CachedPlanSource is left in a * reasonably sane state. (The generic plan won't get unlinked yet, but * that's acceptable.) */ if (plansource->query_context) { MemoryContext qcxt = plansource->query_context; plansource->query_context = NULL; MemoryContextDelete(qcxt); } /* Drop the generic plan reference if any */ ReleaseGenericPlan(plansource); /* * Now re-do parse analysis and rewrite. This not incidentally acquires * the locks we need to do planning safely. */ Assert(plansource->is_complete); /* * If a snapshot is already set (the normal case), we can just use that * for parsing/planning. But if it isn't, install one. Note: no point in * checking whether parse analysis requires a snapshot; utility commands * don't have invalidatable plans, so we'd not get here for such a * command. */ snapshot_set = false; if (!ActiveSnapshotSet()) { PushActiveSnapshot(GetTransactionSnapshot()); snapshot_set = true; } /* * Run parse analysis and rule rewriting. The parser tends to scribble on * its input, so we must copy the raw parse tree to prevent corruption of * the cache. */ rawtree = copyObject(plansource->raw_parse_tree); if (plansource->parserSetup != NULL) tlist = pg_analyze_and_rewrite_params(rawtree, plansource->query_string, plansource->parserSetup, plansource->parserSetupArg); else tlist = pg_analyze_and_rewrite(rawtree, plansource->query_string, plansource->param_types, plansource->num_params); /* Release snapshot if we got one */ if (snapshot_set) PopActiveSnapshot(); /* * Check or update the result tupdesc. XXX should we use a weaker * condition than equalTupleDescs() here? * * We assume the parameter types didn't change from the first time, so no * need to update that. */ resultDesc = PlanCacheComputeResultDesc(tlist); if (resultDesc == NULL && plansource->resultDesc == NULL) { /* OK, doesn't return tuples */ } else if (resultDesc == NULL || plansource->resultDesc == NULL || !equalTupleDescs(resultDesc, plansource->resultDesc)) { /* can we give a better error message? */ if (plansource->fixed_result) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cached plan must not change result type"))); oldcxt = MemoryContextSwitchTo(plansource->context); if (resultDesc) resultDesc = CreateTupleDescCopy(resultDesc); if (plansource->resultDesc) FreeTupleDesc(plansource->resultDesc); plansource->resultDesc = resultDesc; MemoryContextSwitchTo(oldcxt); } /* * Allocate new query_context and copy the completed querytree into it. * It's transient until we complete the copying and dependency extraction. */ querytree_context = AllocSetContextCreate(CurrentMemoryContext, "CachedPlanQuery", ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); oldcxt = MemoryContextSwitchTo(querytree_context); qlist = (List *) copyObject(tlist); /* * Use the planner machinery to extract dependencies. Data is saved in * query_context. (We assume that not a lot of extra cruft is created by * this call.) */ extract_query_dependencies((Node *) qlist, &plansource->relationOids, &plansource->invalItems); /* * Also save the current search_path in the query_context. (This should * not generate much extra cruft either, since almost certainly the path * is already valid.) */ plansource->search_path = GetOverrideSearchPath(querytree_context); MemoryContextSwitchTo(oldcxt); /* Now reparent the finished query_context and save the links */ MemoryContextSetParent(querytree_context, plansource->context); plansource->query_context = querytree_context; plansource->query_list = qlist; /* * Note: we do not reset generic_cost or total_custom_cost, although we * could choose to do so. If the DDL or statistics change that prompted * the invalidation meant a significant change in the cost estimates, it * would be better to reset those variables and start fresh; but often it * doesn't, and we're better retaining our hard-won knowledge about the * relative costs. */ plansource->is_valid = true; /* Return transient copy of querytrees for possible use in planning */ return tlist; }
void SaveCachedPlan | ( | CachedPlanSource * | plansource | ) |
Definition at line 428 of file plancache.c.
References Assert, CACHEDPLANSOURCE_MAGIC, CacheMemoryContext, CachedPlanSource::context, elog, ERROR, CachedPlanSource::is_complete, CachedPlanSource::is_oneshot, CachedPlanSource::is_saved, CachedPlanSource::magic, MemoryContextSetParent(), CachedPlanSource::next_saved, and ReleaseGenericPlan().
Referenced by _SPI_save_plan(), exec_parse_message(), SPI_keepplan(), and StorePreparedStatement().
{ /* Assert caller is doing things in a sane order */ Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC); Assert(plansource->is_complete); Assert(!plansource->is_saved); /* This seems worth a real test, though */ if (plansource->is_oneshot) elog(ERROR, "cannot save one-shot cached plan"); /* * In typical use, this function would be called before generating any * plans from the CachedPlanSource. If there is a generic plan, moving it * into CacheMemoryContext would be pretty risky since it's unclear * whether the caller has taken suitable care with making references * long-lived. Best thing to do seems to be to discard the plan. */ ReleaseGenericPlan(plansource); /* * Reparent the source memory context under CacheMemoryContext so that it * will live indefinitely. The query_context follows along since it's * already a child of the other one. */ MemoryContextSetParent(plansource->context, CacheMemoryContext); /* * Add the entry to the global list of cached plans. */ plansource->next_saved = first_saved_plan; first_saved_plan = plansource; plansource->is_saved = true; }
Definition at line 1471 of file plancache.c.
References Assert, CMD_UTILITY, Query::commandType, Query::cteList, CommonTableExpr::ctequery, get_parse_rowmark(), Query::hasSubLinks, lfirst, LockRelationOid(), QTW_IGNORE_RC_SUBQUERIES, query_tree_walker(), RangeTblEntry::relid, Query::resultRelation, RowShareLock, Query::rtable, RTE_RELATION, RTE_SUBQUERY, RangeTblEntry::rtekind, ScanQueryWalker(), RangeTblEntry::subquery, and UnlockRelationOid().
Referenced by AcquireExecutorLocks(), AcquirePlannerLocks(), and ScanQueryWalker().
{ ListCell *lc; int rt_index; /* Shouldn't get called on utility commands */ Assert(parsetree->commandType != CMD_UTILITY); /* * First, process RTEs of the current query level. */ rt_index = 0; foreach(lc, parsetree->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); LOCKMODE lockmode; rt_index++; switch (rte->rtekind) { case RTE_RELATION: /* Acquire or release the appropriate type of lock */ if (rt_index == parsetree->resultRelation) lockmode = RowExclusiveLock; else if (get_parse_rowmark(parsetree, rt_index) != NULL) lockmode = RowShareLock; else lockmode = AccessShareLock; if (acquire) LockRelationOid(rte->relid, lockmode); else UnlockRelationOid(rte->relid, lockmode); break; case RTE_SUBQUERY: /* Recurse into subquery-in-FROM */ ScanQueryForLocks(rte->subquery, acquire); break; default: /* ignore other types of RTEs */ break; } } /* Recurse into subquery-in-WITH */ foreach(lc, parsetree->cteList) { CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); ScanQueryForLocks((Query *) cte->ctequery, acquire); } /* * Recurse into sublink subqueries, too. But we already did the ones in * the rtable and cteList. */ if (parsetree->hasSubLinks) { query_tree_walker(parsetree, ScanQueryWalker, (void *) &acquire, QTW_IGNORE_RC_SUBQUERIES); } }
Definition at line 1540 of file plancache.c.
References expression_tree_walker(), IsA, NULL, ScanQueryForLocks(), and SubLink::subselect.
Referenced by ScanQueryForLocks().
{ if (node == NULL) return false; if (IsA(node, SubLink)) { SubLink *sub = (SubLink *) node; /* Do what we came for */ ScanQueryForLocks((Query *) sub->subselect, *acquire); /* Fall through to process lefthand args of SubLink */ } /* * Do NOT recurse into Query nodes, because ScanQueryForLocks already * processed subselects of subselects for us. */ return expression_tree_walker(node, ScanQueryWalker, (void *) acquire); }
CachedPlanSource* first_saved_plan = NULL [static] |
Definition at line 85 of file plancache.c.