/*-------------------------------------------------------------------------
 *
 * plancache.c
 *	  Plan cache management.
 *
 * The plan cache manager has two principal responsibilities: deciding when
 * to use a generic plan versus a custom (parameter-value-specific) plan,
 * and tracking whether cached plans need to be invalidated because of schema
 * changes in the objects they depend on.
 *
 * The logic for choosing generic or custom plans is in choose_custom_plan,
 * which see for comments.
 *
 * Cache invalidation is driven off sinval events.  Any CachedPlanSource
 * that matches the event is marked invalid, as is its generic CachedPlan
 * if it has one.  When (and if) the next demand for a cached plan occurs,
 * parse analysis and/or rewrite is repeated to build a new valid query tree,
 * and then planning is performed as normal.  We also force re-analysis and
 * re-planning if the active search_path is different from the previous time
 * or, if RLS is involved, if the user changes or the RLS environment changes.
 *
 * Note that if the sinval was a result of user DDL actions, parse analysis
 * could throw an error, for example if a column referenced by the query is
 * no longer present.  Another possibility is for the query's output tupdesc
 * to change (for instance "SELECT *" might expand differently than before).
 * The creator of a cached plan can specify whether it is allowable for the
 * query to change output tupdesc on replan --- if so, it's up to the
 * caller to notice changes and cope with them.
 *
 * Currently, we track exactly the dependencies of plans on relations,
 * user-defined functions, and domains.  On relcache invalidation events or
 * pg_proc or pg_type syscache invalidation events, we invalidate just those
 * plans that depend on the particular object being modified.  (Note: this
 * scheme assumes that any table modification that requires replanning will
 * generate a relcache inval event.)  We also watch for inval events on
 * certain other system catalogs, such as pg_namespace; but for them, our
 * response is just to invalidate all plans.  We expect updates on those
 * catalogs to be infrequent enough that more-detailed tracking is not worth
 * the effort.
 *
 * In addition to full-fledged query plans, we provide a facility for
 * detecting invalidations of simple scalar expressions.  This is fairly
 * bare-bones; it's the caller's responsibility to build a new expression
 * if the old one gets invalidated.
 *
 *
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/utils/cache/plancache.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <limits.h>

#include "access/transam.h"
#include "catalog/namespace.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "parser/analyze.h"
#include "rewrite/rewriteHandler.h"
#include "storage/lmgr.h"
#include "tcop/pquery.h"
#include "tcop/utility.h"
#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/resowner.h"
#include "utils/rls.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"


/*
 * This is the head of the backend's list of "saved" CachedPlanSources (i.e.,
 * those that are in long-lived storage and are examined for sinval events).
 * We use a dlist instead of separate List cells so that we can guarantee
 * to save a CachedPlanSource without error.
 */
static dlist_head saved_plan_list = DLIST_STATIC_INIT(saved_plan_list);

/*
 * This is the head of the backend's list of CachedExpressions.
 */
static dlist_head cached_expression_list = DLIST_STATIC_INIT(cached_expression_list);

static void ReleaseGenericPlan(CachedPlanSource *plansource);
static bool StmtPlanRequiresRevalidation(CachedPlanSource *plansource);
static bool BuildingPlanRequiresSnapshot(CachedPlanSource *plansource);
static List *RevalidateCachedQuery(CachedPlanSource *plansource,
								   QueryEnvironment *queryEnv);
static bool CheckCachedPlan(CachedPlanSource *plansource);
static CachedPlan *BuildCachedPlan(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 Query *QueryListGetPrimaryStmt(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);

/* ResourceOwner callbacks to track plancache references */
static void ResOwnerReleaseCachedPlan(Datum res);

static const ResourceOwnerDesc planref_resowner_desc =
{
	.name = "plancache reference",
	.release_phase = RESOURCE_RELEASE_AFTER_LOCKS,
	.release_priority = RELEASE_PRIO_PLANCACHE_REFS,
	.ReleaseResource = ResOwnerReleaseCachedPlan,
	.DebugPrint = NULL			/* the default message is fine */
};

/* Convenience wrappers over ResourceOwnerRemember/Forget */
static inline void
ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
{
	ResourceOwnerRemember(owner, PointerGetDatum(plan), &planref_resowner_desc);
}
static inline void
ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
{
	ResourceOwnerForget(owner, PointerGetDatum(plan), &planref_resowner_desc);
}


/* GUC parameter */
int			plan_cache_mode = PLAN_CACHE_MODE_AUTO;

/*
 * InitPlanCache: initialize module during InitPostgres.
 *
 * All we need to do is hook into inval.c's callback lists.
 */
void
InitPlanCache(void)
{
	CacheRegisterRelcacheCallback(PlanCacheRelCallback, (Datum) 0);
	CacheRegisterSyscacheCallback(PROCOID, PlanCacheObjectCallback, (Datum) 0);
	CacheRegisterSyscacheCallback(TYPEOID, PlanCacheObjectCallback, (Datum) 0);
	CacheRegisterSyscacheCallback(NAMESPACEOID, PlanCacheSysCallback, (Datum) 0);
	CacheRegisterSyscacheCallback(OPEROID, PlanCacheSysCallback, (Datum) 0);
	CacheRegisterSyscacheCallback(AMOPOPID, PlanCacheSysCallback, (Datum) 0);
	CacheRegisterSyscacheCallback(FOREIGNSERVEROID, PlanCacheSysCallback, (Datum) 0);
	CacheRegisterSyscacheCallback(FOREIGNDATAWRAPPEROID, PlanCacheSysCallback, (Datum) 0);
}

/*
 * CreateCachedPlan: initially create a plan cache entry for a raw parse tree.
 *
 * Creation of a cached plan is divided into two steps, CreateCachedPlan and
 * CompleteCachedPlan.  CreateCachedPlan should be called after running the
 * query through raw_parser, but before doing parse analysis and rewrite;
 * CompleteCachedPlan is called after that.  The reason for this arrangement
 * is that it can save one round of copying of the raw parse tree, since
 * the parser will normally scribble on the raw parse tree.  Callers would
 * otherwise need to make an extra copy of the parse tree to ensure they
 * still had a clean copy to present at plan cache creation time.
 *
 * All arguments presented to CreateCachedPlan are copied into a memory
 * context created as a child of the call-time CurrentMemoryContext, which
 * should be a reasonably short-lived working context that will go away in
 * event of an error.  This ensures that the cached plan data structure will
 * likewise disappear if an error occurs before we have fully constructed it.
 * Once constructed, the cached plan can be made longer-lived, if needed,
 * by calling SaveCachedPlan.
 *
 * raw_parse_tree: output of raw_parser(), or NULL if empty query
 * query_string: original query text
 * commandTag: command tag for query, or UNKNOWN if empty query
 */
CachedPlanSource *
CreateCachedPlan(RawStmt *raw_parse_tree,
				 const char *query_string,
				 CommandTag commandTag)
{
	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, allow it to grow large.  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_START_SMALL_SIZES);

	/*
	 * 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->analyzed_parse_tree = NULL;
	plansource->query_string = pstrdup(query_string);
	MemoryContextSetIdentifier(source_context, plansource->query_string);
	plansource->commandTag = commandTag;
	plansource->param_types = NULL;
	plansource->num_params = 0;
	plansource->parserSetup = NULL;
	plansource->parserSetupArg = NULL;
	plansource->postRewrite = NULL;
	plansource->postRewriteArg = 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->rewriteRoleId = InvalidOid;
	plansource->rewriteRowSecurity = false;
	plansource->dependsOnRLS = false;
	plansource->gplan = NULL;
	plansource->is_oneshot = false;
	plansource->is_complete = false;
	plansource->is_saved = false;
	plansource->is_valid = false;
	plansource->generation = 0;
	plansource->generic_cost = -1;
	plansource->total_custom_cost = 0;
	plansource->num_generic_plans = 0;
	plansource->num_custom_plans = 0;

	MemoryContextSwitchTo(oldcxt);

	return plansource;
}

/*
 * CreateCachedPlanForQuery: initially create a plan cache entry for a Query.
 *
 * This is used in the same way as CreateCachedPlan, except that the source
 * query has already been through parse analysis, and the plancache will never
 * try to re-do that step.
 *
 * Currently this is used only for new-style SQL functions, where we have a
 * Query from the function's prosqlbody, but no source text.  The query_string
 * is typically empty, but is required anyway.
 */
CachedPlanSource *
CreateCachedPlanForQuery(Query *analyzed_parse_tree,
						 const char *query_string,
						 CommandTag commandTag)
{
	CachedPlanSource *plansource;
	MemoryContext oldcxt;

	/* Rather than duplicating CreateCachedPlan, just do this: */
	plansource = CreateCachedPlan(NULL, query_string, commandTag);
	oldcxt = MemoryContextSwitchTo(plansource->context);
	plansource->analyzed_parse_tree = copyObject(analyzed_parse_tree);
	MemoryContextSwitchTo(oldcxt);

	return plansource;
}

/*
 * CreateOneShotCachedPlan: initially create a one-shot plan cache entry.
 *
 * This variant of CreateCachedPlan creates a plan cache entry that is meant
 * to be used only once.  No data copying occurs: all data structures remain
 * in the caller's memory context (which typically should get cleared after
 * completing execution).  The CachedPlanSource struct itself is also created
 * in that context.
 *
 * A one-shot plan cannot be saved or copied, since we make no effort to
 * preserve the raw parse tree unmodified.  There is also no support for
 * invalidation, so plan use must be completed in the current transaction,
 * and DDL that might invalidate the querytree_list must be avoided as well.
 *
 * raw_parse_tree: output of raw_parser(), or NULL if empty query
 * query_string: original query text
 * commandTag: command tag for query, or NULL if empty query
 */
CachedPlanSource *
CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
						const char *query_string,
						CommandTag commandTag)
{
	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->analyzed_parse_tree = NULL;
	plansource->query_string = query_string;
	plansource->commandTag = commandTag;
	plansource->param_types = NULL;
	plansource->num_params = 0;
	plansource->parserSetup = NULL;
	plansource->parserSetupArg = NULL;
	plansource->postRewrite = NULL;
	plansource->postRewriteArg = 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->rewriteRoleId = InvalidOid;
	plansource->rewriteRowSecurity = false;
	plansource->dependsOnRLS = false;
	plansource->gplan = NULL;
	plansource->is_oneshot = true;
	plansource->is_complete = false;
	plansource->is_saved = false;
	plansource->is_valid = false;
	plansource->generation = 0;
	plansource->generic_cost = -1;
	plansource->total_custom_cost = 0;
	plansource->num_generic_plans = 0;
	plansource->num_custom_plans = 0;

	return plansource;
}

/*
 * CompleteCachedPlan: second step of creating a plan cache entry.
 *
 * Pass in the analyzed-and-rewritten form of the query, as well as the
 * required subsidiary data about parameters and such.  All passed values will
 * be copied into the CachedPlanSource's memory, except as specified below.
 * After this is called, GetCachedPlan can be called to obtain a plan, and
 * optionally the CachedPlanSource can be saved using SaveCachedPlan.
 *
 * If querytree_context is not NULL, the querytree_list must be stored in that
 * context (but the other parameters need not be).  The querytree_list is not
 * copied, rather the given context is kept as the initial query_context of
 * the CachedPlanSource.  (It should have been created as a child of the
 * caller's working memory context, but it will now be reparented to belong
 * to the CachedPlanSource.)  The querytree_context is normally the context in
 * which the caller did raw parsing and parse analysis.  This approach saves
 * one tree copying step compared to passing NULL, but leaves lots of extra
 * cruft in the query_context, namely whatever extraneous stuff parse analysis
 * created, as well as whatever went unused from the raw parse tree.  Using
 * this option is a space-for-time tradeoff that is appropriate if the
 * CachedPlanSource is not expected to survive long.
 *
 * plancache.c cannot know how to copy the data referenced by parserSetupArg,
 * and it would often be inappropriate to do so anyway.  When using that
 * option, it is caller's responsibility that the referenced data remains
 * valid for as long as the CachedPlanSource exists.
 *
 * If the CachedPlanSource is a "oneshot" plan, then no querytree copying
 * occurs at all, and querytree_context is ignored; it is caller's
 * responsibility that the passed querytree_list is sufficiently long-lived.
 *
 * plansource: structure returned by CreateCachedPlan
 * querytree_list: analyzed-and-rewritten form of query (list of Query nodes)
 * querytree_context: memory context containing querytree_list,
 *					  or NULL to copy querytree_list into a fresh context
 * param_types: array of fixed parameter type OIDs, or NULL if none
 * num_params: number of fixed parameters
 * parserSetup: alternate method for handling query parameters
 * parserSetupArg: data to pass to parserSetup
 * cursor_options: options bitmask to pass to planner
 * fixed_result: true to disallow future changes in query's result tupdesc
 */
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)
{
	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_START_SMALL_SIZES);
		MemoryContextSwitchTo(querytree_context);
		querytree_list = copyObject(querytree_list);
	}

	plansource->query_context = querytree_context;
	plansource->query_list = querytree_list;

	if (!plansource->is_oneshot && StmtPlanRequiresRevalidation(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
		 * plans not needing revalidation have no such dependencies anyway.
		 */
		extract_query_dependencies((Node *) querytree_list,
								   &plansource->relationOids,
								   &plansource->invalItems,
								   &plansource->dependsOnRLS);

		/* Update RLS info as well. */
		plansource->rewriteRoleId = GetUserId();
		plansource->rewriteRowSecurity = row_security;

		/*
		 * 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 = GetSearchPathMatcher(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;
}

/*
 * SetPostRewriteHook: set a hook to modify post-rewrite query trees
 *
 * Some callers have a need to modify the query trees between rewriting and
 * planning.  In the initial call to CompleteCachedPlan, it's assumed such
 * work was already done on the querytree_list.  However, if we're forced
 * to replan, it will need to be done over.  The caller can set this hook
 * to provide code to make that happen.
 *
 * postRewriteArg is just passed verbatim to the hook.  As with parserSetupArg,
 * it is caller's responsibility that the referenced data remains
 * valid for as long as the CachedPlanSource exists.
 */
void
SetPostRewriteHook(CachedPlanSource *plansource,
				   PostRewriteHook postRewrite,
				   void *postRewriteArg)
{
	Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
	plansource->postRewrite = postRewrite;
	plansource->postRewriteArg = postRewriteArg;
}

/*
 * SaveCachedPlan: save a cached plan permanently
 *
 * This function moves the cached plan underneath CacheMemoryContext (making
 * it live for the life of the backend, unless explicitly dropped), and adds
 * it to the list of cached plans that are checked for invalidation when an
 * sinval event occurs.
 *
 * This is guaranteed not to throw error, except for the caller-error case
 * of trying to save a one-shot plan.  Callers typically depend on that
 * since this is called just before or just after adding a pointer to the
 * CachedPlanSource to some permanent data structure of their own.  Up until
 * this is done, a CachedPlanSource is just transient data that will go away
 * automatically on transaction abort.
 */
void
SaveCachedPlan(CachedPlanSource *plansource)
{
	/* 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.
	 */
	dlist_push_tail(&saved_plan_list, &plansource->node);

	plansource->is_saved = true;
}

/*
 * DropCachedPlan: destroy a cached plan.
 *
 * Actually this only destroys the CachedPlanSource: any referenced CachedPlan
 * is released, but not destroyed until its refcount goes to zero.  That
 * handles the situation where DropCachedPlan is called while the plan is
 * still in use.
 */
void
DropCachedPlan(CachedPlanSource *plansource)
{
	Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);

	/* If it's been saved, remove it from the list */
	if (plansource->is_saved)
	{
		dlist_delete(&plansource->node);
		plansource->is_saved = false;
	}

	/* Decrement generic CachedPlan'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);
}

/*
 * ReleaseGenericPlan: release a CachedPlanSource's generic plan, if any.
 */
static void
ReleaseGenericPlan(CachedPlanSource *plansource)
{
	/* 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, NULL);
	}
}

/*
 * We must skip "overhead" operations that involve database access when the
 * cached plan's subject statement is a transaction control command or one
 * that requires a snapshot not to be set yet (such as SET or LOCK).  More
 * generally, statements that do not require parse analysis/rewrite/plan
 * activity never need to be revalidated, so we can treat them all like that.
 * For the convenience of postgres.c, treat empty statements that way too.
 */
static bool
StmtPlanRequiresRevalidation(CachedPlanSource *plansource)
{
	if (plansource->raw_parse_tree != NULL)
		return stmt_requires_parse_analysis(plansource->raw_parse_tree);
	else if (plansource->analyzed_parse_tree != NULL)
		return query_requires_rewrite_plan(plansource->analyzed_parse_tree);
	/* empty query never needs revalidation */
	return false;
}

/*
 * Determine if creating a plan for this CachedPlanSource requires a snapshot.
 * In fact this function matches StmtPlanRequiresRevalidation(), but we want
 * to preserve the distinction between stmt_requires_parse_analysis() and
 * analyze_requires_snapshot().
 */
static bool
BuildingPlanRequiresSnapshot(CachedPlanSource *plansource)
{
	if (plansource->raw_parse_tree != NULL)
		return analyze_requires_snapshot(plansource->raw_parse_tree);
	else if (plansource->analyzed_parse_tree != NULL)
		return query_requires_rewrite_plan(plansource->analyzed_parse_tree);
	/* empty query never needs a snapshot */
	return false;
}

/*
 * RevalidateCachedQuery: ensure validity of analyzed-and-rewritten query tree.
 *
 * What we do here is re-acquire locks and redo parse analysis if necessary.
 * On return, the query_list is valid and we have sufficient locks to begin
 * planning.
 *
 * If any parse analysis activity is required, the caller's memory context is
 * used for that work.
 *
 * The result value is the transient analyzed-and-rewritten query tree if we
 * had to do re-analysis, and NIL otherwise.  (This is returned just to save
 * a tree copying step in a subsequent BuildCachedPlan call.)
 */
static List *
RevalidateCachedQuery(CachedPlanSource *plansource,
					  QueryEnvironment *queryEnv)
{
	bool		snapshot_set;
	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, if the statement
	 * type can't require revalidation, we needn't do anything (and we mustn't
	 * risk catalog accesses when handling, eg, transaction control commands).
	 */
	if (plansource->is_oneshot || !StmtPlanRequiresRevalidation(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.  (We could almost ignore this consideration when
	 * working from an analyzed parse tree; but there are scenarios where
	 * planning can have search_path-dependent results, for example if it
	 * inlines an old-style SQL function.)
	 */
	if (plansource->is_valid)
	{
		Assert(plansource->search_path != NULL);
		if (!SearchPathMatchesCurrentEnvironment(plansource->search_path))
		{
			/* Invalidate the querytree and generic plan */
			plansource->is_valid = false;
			if (plansource->gplan)
				plansource->gplan->is_valid = false;
		}
	}

	/*
	 * If the query rewrite phase had a possible RLS dependency, we must redo
	 * it if either the role or the row_security setting has changed.
	 */
	if (plansource->is_valid && plansource->dependsOnRLS &&
		(plansource->rewriteRoleId != GetUserId() ||
		 plansource->rewriteRowSecurity != row_security))
		plansource->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;
		}

		/* Oops, the race case happened.  Release useless locks. */
		AcquirePlannerLocks(plansource->query_list, false);
	}

	/*
	 * Discard the no-longer-useful rewritten 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 (if needed) and rule rewriting.
	 */
	if (plansource->raw_parse_tree != NULL)
	{
		/* Source is raw parse tree */
		RawStmt    *rawtree;

		/*
		 * 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_withcb(rawtree,
												  plansource->query_string,
												  plansource->parserSetup,
												  plansource->parserSetupArg,
												  queryEnv);
		else
			tlist = pg_analyze_and_rewrite_fixedparams(rawtree,
													   plansource->query_string,
													   plansource->param_types,
													   plansource->num_params,
													   queryEnv);
	}
	else if (plansource->analyzed_parse_tree != NULL)
	{
		/* Source is pre-analyzed query, so we only need to rewrite */
		Query	   *analyzed_tree;

		/* The rewriter scribbles on its input, too, so copy */
		analyzed_tree = copyObject(plansource->analyzed_parse_tree);
		/* Acquire locks needed before rewriting ... */
		AcquireRewriteLocks(analyzed_tree, true, false);
		/* ... and do it */
		tlist = pg_rewrite_query(analyzed_tree);
	}
	else
	{
		/* Empty query, nothing to do */
		tlist = NIL;
	}

	/* Apply post-rewrite callback if there is one */
	if (plansource->postRewrite != NULL)
		plansource->postRewrite(tlist, plansource->postRewriteArg);

	/* Release snapshot if we got one */
	if (snapshot_set)
		PopActiveSnapshot();

	/*
	 * Check or update the result tupdesc.
	 *
	 * 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 ||
			 !equalRowTypes(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_START_SMALL_SIZES);
	oldcxt = MemoryContextSwitchTo(querytree_context);

	qlist = 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,
							   &plansource->dependsOnRLS);

	/* Update RLS info as well. */
	plansource->rewriteRoleId = GetUserId();
	plansource->rewriteRowSecurity = row_security;

	/*
	 * 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 = GetSearchPathMatcher(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;
}

/*
 * CheckCachedPlan: see if the CachedPlanSource's generic plan is valid.
 *
 * Caller must have already called RevalidateCachedQuery to verify that the
 * querytree is up to date.
 *
 * On a "true" return, we have acquired the locks needed to run the plan.
 * (We must do this for the "true" result to be race-condition-free.)
 */
static bool
CheckCachedPlan(CachedPlanSource *plansource)
{
	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 plan isn't valid for current role, we can't use it.
	 */
	if (plan->is_valid && plan->dependsOnRole &&
		plan->planRoleId != GetUserId())
		plan->is_valid = false;

	/*
	 * 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;
		}

		/* Oops, 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;
}

/*
 * BuildCachedPlan: construct a new CachedPlan from a CachedPlanSource.
 *
 * qlist should be the result value from a previous RevalidateCachedQuery,
 * or it can be set to NIL if we need to re-copy the plansource's query_list.
 *
 * To build a generic, parameter-value-independent plan, pass NULL for
 * boundParams.  To build a custom plan, pass the actual parameter values via
 * boundParams.  For best effect, the PARAM_FLAG_CONST flag should be set on
 * each parameter value; otherwise the planner will treat the value as a
 * hint rather than a hard constant.
 *
 * Planning work is done in the caller's memory context.  The finished plan
 * is in a child memory context, which typically should get reparented
 * (unless this is a one-shot plan, in which case we don't copy the plan).
 */
static CachedPlan *
BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
				ParamListInfo boundParams, QueryEnvironment *queryEnv)
{
	CachedPlan *plan;
	List	   *plist;
	bool		snapshot_set;
	bool		is_transient;
	MemoryContext plan_context;
	MemoryContext oldcxt = CurrentMemoryContext;
	ListCell   *lc;

	/*
	 * 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 debug_discard_caches code.  But for safety,
	 * let's treat it as real and redo the RevalidateCachedQuery call.
	 */
	if (!plansource->is_valid)
		qlist = RevalidateCachedQuery(plansource, queryEnv);

	/*
	 * 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 = 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() &&
		BuildingPlanRequiresSnapshot(plansource))
	{
		PushActiveSnapshot(GetTransactionSnapshot());
		snapshot_set = true;
	}

	/*
	 * Generate the plan.
	 */
	plist = pg_plan_queries(qlist, plansource->query_string,
							plansource->cursor_options, boundParams);

	/* 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, allow it to grow large.  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_START_SMALL_SIZES);
		MemoryContextCopyAndSetIdentifier(plan_context, plansource->query_string);

		/*
		 * Copy plan into the new context.
		 */
		MemoryContextSwitchTo(plan_context);

		plist = 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;

	/*
	 * CachedPlan is dependent on role either if RLS affected the rewrite
	 * phase or if a role dependency was injected during planning.  And it's
	 * transient if any plan is marked so.
	 */
	plan->planRoleId = GetUserId();
	plan->dependsOnRole = plansource->dependsOnRLS;
	is_transient = false;
	foreach(lc, plist)
	{
		PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);

		if (plannedstmt->commandType == CMD_UTILITY)
			continue;			/* Ignore utility statements */

		if (plannedstmt->transientPlan)
			is_transient = true;
		if (plannedstmt->dependsOnRole)
			plan->dependsOnRole = true;
	}
	if (is_transient)
	{
		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;
}

/*
 * choose_custom_plan: choose whether to use custom or generic plan
 *
 * This defines the policy followed by GetCachedPlan.
 */
static bool
choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams)
{
	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 when planning would be a no-op */
	if (!StmtPlanRequiresRevalidation(plansource))
		return false;

	/* Let settings force the decision */
	if (plan_cache_mode == PLAN_CACHE_MODE_FORCE_GENERIC_PLAN)
		return false;
	if (plan_cache_mode == PLAN_CACHE_MODE_FORCE_CUSTOM_PLAN)
		return true;

	/* 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 expensive than the average custom
	 * plan.  (Because we include a charge for cost of planning in the
	 * custom-plan costs, this means the generic plan only has to be less
	 * expensive than the execution cost plus replan cost of the custom
	 * plans.)
	 *
	 * 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)
		return false;

	return true;
}

/*
 * cached_plan_cost: calculate estimated cost of a plan
 *
 * If include_planner is true, also include the estimated cost of constructing
 * the plan.  (We must factor that into the cost of using a custom plan, but
 * we don't count it for a generic plan.)
 */
static double
cached_plan_cost(CachedPlan *plan, bool include_planner)
{
	double		result = 0;
	ListCell   *lc;

	foreach(lc, plan->stmt_list)
	{
		PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);

		if (plannedstmt->commandType == CMD_UTILITY)
			continue;			/* Ignore utility statements */

		result += plannedstmt->planTree->total_cost;

		if (include_planner)
		{
			/*
			 * Currently we use a very crude estimate of planning effort based
			 * on the number of relations in the finished plan's rangetable.
			 * Join planning effort actually scales much worse than linearly
			 * in the number of relations --- but only until the join collapse
			 * limits kick in.  Also, while inheritance child relations surely
			 * add to planning effort, they don't make the join situation
			 * worse.  So the actual shape of the planning cost curve versus
			 * number of relations isn't all that obvious.  It will take
			 * considerable work to arrive at a less crude estimate, and for
			 * now it's not clear that's worth doing.
			 *
			 * The other big difficulty here is that we don't have any very
			 * good model of how planning cost compares to execution costs.
			 * The current multiplier of 1000 * cpu_operator_cost is probably
			 * on the low side, but we'll try this for awhile before making a
			 * more aggressive correction.
			 *
			 * If we ever do write a more complicated estimator, it should
			 * probably live in src/backend/optimizer/ not here.
			 */
			int			nrelations = list_length(plannedstmt->rtable);

			result += 1000.0 * cpu_operator_cost * (nrelations + 1);
		}
	}

	return result;
}

/*
 * GetCachedPlan: get a cached plan from a CachedPlanSource.
 *
 * This function hides the logic that decides whether to use a generic
 * plan or a custom plan for the given parameters: the caller does not know
 * which it will get.
 *
 * On return, the plan is valid and we have sufficient locks to begin
 * execution.
 *
 * On return, the refcount of the plan has been incremented; a later
 * ReleaseCachedPlan() call is expected.  If "owner" is not NULL then
 * the refcount has been reported to that ResourceOwner (note that this
 * is only supported for "saved" CachedPlanSources).
 *
 * Note: if any replanning activity is required, the caller's memory context
 * is used for that work.
 */
CachedPlan *
GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
			  ResourceOwner owner, QueryEnvironment *queryEnv)
{
	CachedPlan *plan = NULL;
	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 (owner && !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, queryEnv);

	/* 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, queryEnv);
			/* 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, false);

			/*
			 * 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, queryEnv);
		/* Accumulate total costs of custom plans */
		plansource->total_custom_cost += cached_plan_cost(plan, true);

		plansource->num_custom_plans++;
	}
	else
	{
		plansource->num_generic_plans++;
	}

	Assert(plan != NULL);

	/* Flag the plan as in use by caller */
	if (owner)
		ResourceOwnerEnlarge(owner);
	plan->refcount++;
	if (owner)
		ResourceOwnerRememberPlanCacheRef(owner, 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;
}

/*
 * ReleaseCachedPlan: release active use of a cached plan.
 *
 * This decrements the reference count, and frees the plan if the count
 * has thereby gone to zero.  If "owner" is not NULL, it is assumed that
 * the reference count is managed by that ResourceOwner.
 *
 * Note: owner == NULL is used for releasing references that are in
 * persistent data structures, such as the parent CachedPlanSource or a
 * Portal.  Transient references should be protected by a resource owner.
 */
void
ReleaseCachedPlan(CachedPlan *plan, ResourceOwner owner)
{
	Assert(plan->magic == CACHEDPLAN_MAGIC);
	if (owner)
	{
		Assert(plan->is_saved);
		ResourceOwnerForgetPlanCacheRef(owner, 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);
	}
}

/*
 * CachedPlanAllowsSimpleValidityCheck: can we use CachedPlanIsSimplyValid?
 *
 * This function, together with CachedPlanIsSimplyValid, provides a fast path
 * for revalidating "simple" generic plans.  The core requirement to be simple
 * is that the plan must not require taking any locks, which translates to
 * not touching any tables; this happens to match up well with an important
 * use-case in PL/pgSQL.  This function tests whether that's true, along
 * with checking some other corner cases that we'd rather not bother with
 * handling in the fast path.  (Note that it's still possible for such a plan
 * to be invalidated, for example due to a change in a function that was
 * inlined into the plan.)
 *
 * If the plan is simply valid, and "owner" is not NULL, record a refcount on
 * the plan in that resowner before returning.  It is caller's responsibility
 * to be sure that a refcount is held on any plan that's being actively used.
 *
 * This must only be called on known-valid generic plans (eg, ones just
 * returned by GetCachedPlan).  If it returns true, the caller may re-use
 * the cached plan as long as CachedPlanIsSimplyValid returns true; that
 * check is much cheaper than the full revalidation done by GetCachedPlan.
 * Nonetheless, no required checks are omitted.
 */
bool
CachedPlanAllowsSimpleValidityCheck(CachedPlanSource *plansource,
									CachedPlan *plan, ResourceOwner owner)
{
	ListCell   *lc;

	/*
	 * Sanity-check that the caller gave us a validated generic plan.  Notice
	 * that we *don't* assert plansource->is_valid as you might expect; that's
	 * because it's possible that that's already false when GetCachedPlan
	 * returns, e.g. because ResetPlanCache happened partway through.  We
	 * should accept the plan as long as plan->is_valid is true, and expect to
	 * replan after the next CachedPlanIsSimplyValid call.
	 */
	Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
	Assert(plan->magic == CACHEDPLAN_MAGIC);
	Assert(plan->is_valid);
	Assert(plan == plansource->gplan);
	Assert(plansource->search_path != NULL);
	Assert(SearchPathMatchesCurrentEnvironment(plansource->search_path));

	/* We don't support oneshot plans here. */
	if (plansource->is_oneshot)
		return false;
	Assert(!plan->is_oneshot);

	/*
	 * If the plan is dependent on RLS considerations, or it's transient,
	 * reject.  These things probably can't ever happen for table-free
	 * queries, but for safety's sake let's check.
	 */
	if (plansource->dependsOnRLS)
		return false;
	if (plan->dependsOnRole)
		return false;
	if (TransactionIdIsValid(plan->saved_xmin))
		return false;

	/*
	 * Reject if AcquirePlannerLocks would have anything to do.  This is
	 * simplistic, but there's no need to inquire any more carefully; indeed,
	 * for current callers it shouldn't even be possible to hit any of these
	 * checks.
	 */
	foreach(lc, plansource->query_list)
	{
		Query	   *query = lfirst_node(Query, lc);

		if (query->commandType == CMD_UTILITY)
			return false;
		if (query->rtable || query->cteList || query->hasSubLinks)
			return false;
	}

	/*
	 * Reject if AcquireExecutorLocks would have anything to do.  This is
	 * probably unnecessary given the previous check, but let's be safe.
	 */
	foreach(lc, plan->stmt_list)
	{
		PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc);
		ListCell   *lc2;

		if (plannedstmt->commandType == CMD_UTILITY)
			return false;

		/*
		 * We have to grovel through the rtable because it's likely to contain
		 * an RTE_RESULT relation, rather than being totally empty.
		 */
		foreach(lc2, plannedstmt->rtable)
		{
			RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);

			if (rte->rtekind == RTE_RELATION)
				return false;
		}
	}

	/*
	 * Okay, it's simple.  Note that what we've primarily established here is
	 * that no locks need be taken before checking the plan's is_valid flag.
	 */

	/* Bump refcount if requested. */
	if (owner)
	{
		ResourceOwnerEnlarge(owner);
		plan->refcount++;
		ResourceOwnerRememberPlanCacheRef(owner, plan);
	}

	return true;
}

/*
 * CachedPlanIsSimplyValid: quick check for plan still being valid
 *
 * This function must not be used unless CachedPlanAllowsSimpleValidityCheck
 * previously said it was OK.
 *
 * If the plan is valid, and "owner" is not NULL, record a refcount on
 * the plan in that resowner before returning.  It is caller's responsibility
 * to be sure that a refcount is held on any plan that's being actively used.
 *
 * The code here is unconditionally safe as long as the only use of this
 * CachedPlanSource is in connection with the particular CachedPlan pointer
 * that's passed in.  If the plansource were being used for other purposes,
 * it's possible that its generic plan could be invalidated and regenerated
 * while the current caller wasn't looking, and then there could be a chance
 * collision of address between this caller's now-stale plan pointer and the
 * actual address of the new generic plan.  For current uses, that scenario
 * can't happen; but with a plansource shared across multiple uses, it'd be
 * advisable to also save plan->generation and verify that that still matches.
 */
bool
CachedPlanIsSimplyValid(CachedPlanSource *plansource, CachedPlan *plan,
						ResourceOwner owner)
{
	/*
	 * Careful here: since the caller doesn't necessarily hold a refcount on
	 * the plan to start with, it's possible that "plan" is a dangling
	 * pointer.  Don't dereference it until we've verified that it still
	 * matches the plansource's gplan (which is either valid or NULL).
	 */
	Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);

	/*
	 * Has cache invalidation fired on this plan?  We can check this right
	 * away since there are no locks that we'd need to acquire first.  Note
	 * that here we *do* check plansource->is_valid, so as to force plan
	 * rebuild if that's become false.
	 */
	if (!plansource->is_valid ||
		plan == NULL || plan != plansource->gplan ||
		!plan->is_valid)
		return false;

	Assert(plan->magic == CACHEDPLAN_MAGIC);

	/* Is the search_path still the same as when we made it? */
	Assert(plansource->search_path != NULL);
	if (!SearchPathMatchesCurrentEnvironment(plansource->search_path))
		return false;

	/* It's still good.  Bump refcount if requested. */
	if (owner)
	{
		ResourceOwnerEnlarge(owner);
		plan->refcount++;
		ResourceOwnerRememberPlanCacheRef(owner, plan);
	}

	return true;
}

/*
 * CachedPlanSetParentContext: move a CachedPlanSource to a new memory context
 *
 * This can only be applied to unsaved plans; once saved, a plan always
 * lives underneath CacheMemoryContext.
 */
void
CachedPlanSetParentContext(CachedPlanSource *plansource,
						   MemoryContext newcontext)
{
	/* 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);
	}
}

/*
 * CopyCachedPlan: make a copy of a CachedPlanSource
 *
 * This is a convenience routine that does the equivalent of
 * CreateCachedPlan + CompleteCachedPlan, using the data stored in the
 * input CachedPlanSource.  The result is therefore "unsaved" (regardless
 * of the state of the source), and we don't copy any generic plan either.
 * The result will be currently valid, or not, the same as the source.
 */
CachedPlanSource *
CopyCachedPlan(CachedPlanSource *plansource)
{
	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_START_SMALL_SIZES);

	oldcxt = MemoryContextSwitchTo(source_context);

	newsource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
	newsource->magic = CACHEDPLANSOURCE_MAGIC;
	newsource->raw_parse_tree = copyObject(plansource->raw_parse_tree);
	newsource->analyzed_parse_tree = copyObject(plansource->analyzed_parse_tree);
	newsource->query_string = pstrdup(plansource->query_string);
	MemoryContextSetIdentifier(source_context, newsource->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->postRewrite = plansource->postRewrite;
	newsource->postRewriteArg = plansource->postRewriteArg;
	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_START_SMALL_SIZES);
	MemoryContextSwitchTo(querytree_context);
	newsource->query_list = copyObject(plansource->query_list);
	newsource->relationOids = copyObject(plansource->relationOids);
	newsource->invalItems = copyObject(plansource->invalItems);
	if (plansource->search_path)
		newsource->search_path = CopySearchPathMatcher(plansource->search_path);
	newsource->query_context = querytree_context;
	newsource->rewriteRoleId = plansource->rewriteRoleId;
	newsource->rewriteRowSecurity = plansource->rewriteRowSecurity;
	newsource->dependsOnRLS = plansource->dependsOnRLS;

	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;

	/* 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_generic_plans = plansource->num_generic_plans;
	newsource->num_custom_plans = plansource->num_custom_plans;

	MemoryContextSwitchTo(oldcxt);

	return newsource;
}

/*
 * CachedPlanIsValid: test whether the rewritten querytree within a
 * CachedPlanSource is currently valid (that is, not marked as being in need
 * of revalidation).
 *
 * This result is only trustworthy (ie, free from race conditions) if
 * the caller has acquired locks on all the relations used in the plan.
 */
bool
CachedPlanIsValid(CachedPlanSource *plansource)
{
	Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
	return plansource->is_valid;
}

/*
 * CachedPlanGetTargetList: return tlist, if any, describing plan's output
 *
 * The result is guaranteed up-to-date.  However, it is local storage
 * within the cached plan, and may disappear next time the plan is updated.
 */
List *
CachedPlanGetTargetList(CachedPlanSource *plansource,
						QueryEnvironment *queryEnv)
{
	Query	   *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, queryEnv);

	/* Get the primary statement and find out what it returns */
	pstmt = QueryListGetPrimaryStmt(plansource->query_list);

	return FetchStatementTargetList((Node *) pstmt);
}

/*
 * GetCachedExpression: construct a CachedExpression for an expression.
 *
 * This performs the same transformations on the expression as
 * expression_planner(), ie, convert an expression as emitted by parse
 * analysis to be ready to pass to the executor.
 *
 * The result is stashed in a private, long-lived memory context.
 * (Note that this might leak a good deal of memory in the caller's
 * context before that.)  The passed-in expr tree is not modified.
 */
CachedExpression *
GetCachedExpression(Node *expr)
{
	CachedExpression *cexpr;
	List	   *relationOids;
	List	   *invalItems;
	MemoryContext cexpr_context;
	MemoryContext oldcxt;

	/*
	 * Pass the expression through the planner, and collect dependencies.
	 * Everything built here is leaked in the caller's context; that's
	 * intentional to minimize the size of the permanent data structure.
	 */
	expr = (Node *) expression_planner_with_deps((Expr *) expr,
												 &relationOids,
												 &invalItems);

	/*
	 * Make a private memory context, and copy what we need into that.  To
	 * avoid leaking a long-lived context if we fail while copying data, we
	 * initially make the context under the caller's context.
	 */
	cexpr_context = AllocSetContextCreate(CurrentMemoryContext,
										  "CachedExpression",
										  ALLOCSET_SMALL_SIZES);

	oldcxt = MemoryContextSwitchTo(cexpr_context);

	cexpr = (CachedExpression *) palloc(sizeof(CachedExpression));
	cexpr->magic = CACHEDEXPR_MAGIC;
	cexpr->expr = copyObject(expr);
	cexpr->is_valid = true;
	cexpr->relationOids = copyObject(relationOids);
	cexpr->invalItems = copyObject(invalItems);
	cexpr->context = cexpr_context;

	MemoryContextSwitchTo(oldcxt);

	/*
	 * Reparent the expr's memory context under CacheMemoryContext so that it
	 * will live indefinitely.
	 */
	MemoryContextSetParent(cexpr_context, CacheMemoryContext);

	/*
	 * Add the entry to the global list of cached expressions.
	 */
	dlist_push_tail(&cached_expression_list, &cexpr->node);

	return cexpr;
}

/*
 * FreeCachedExpression
 *		Delete a CachedExpression.
 */
void
FreeCachedExpression(CachedExpression *cexpr)
{
	/* Sanity check */
	Assert(cexpr->magic == CACHEDEXPR_MAGIC);
	/* Unlink from global list */
	dlist_delete(&cexpr->node);
	/* Free all storage associated with CachedExpression */
	MemoryContextDelete(cexpr->context);
}

/*
 * QueryListGetPrimaryStmt
 *		Get the "primary" stmt within a list, ie, the one marked canSetTag.
 *
 * Returns NULL if no such stmt.  If multiple queries within the list are
 * marked canSetTag, returns the first one.  Neither of these cases should
 * occur in present usages of this function.
 */
static Query *
QueryListGetPrimaryStmt(List *stmts)
{
	ListCell   *lc;

	foreach(lc, stmts)
	{
		Query	   *stmt = lfirst_node(Query, lc);

		if (stmt->canSetTag)
			return stmt;
	}
	return NULL;
}

/*
 * AcquireExecutorLocks: acquire locks needed for execution of a cached plan;
 * or release them if acquire is false.
 */
static void
AcquireExecutorLocks(List *stmt_list, bool acquire)
{
	ListCell   *lc1;

	foreach(lc1, stmt_list)
	{
		PlannedStmt *plannedstmt = lfirst_node(PlannedStmt, lc1);
		ListCell   *lc2;

		if (plannedstmt->commandType == CMD_UTILITY)
		{
			/*
			 * 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(plannedstmt->utilityStmt);

			if (query)
				ScanQueryForLocks(query, acquire);
			continue;
		}

		foreach(lc2, plannedstmt->rtable)
		{
			RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);

			if (!(rte->rtekind == RTE_RELATION ||
				  (rte->rtekind == RTE_SUBQUERY && OidIsValid(rte->relid))))
				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 (acquire)
				LockRelationOid(rte->relid, rte->rellockmode);
			else
				UnlockRelationOid(rte->relid, rte->rellockmode);
		}
	}
}

/*
 * AcquirePlannerLocks: acquire locks needed for planning of a querytree list;
 * or release them if acquire is false.
 *
 * Note that we don't actually try to open the relations, and hence will not
 * fail if one has been dropped entirely --- we'll just transiently acquire
 * a non-conflicting lock.
 */
static void
AcquirePlannerLocks(List *stmt_list, bool acquire)
{
	ListCell   *lc;

	foreach(lc, stmt_list)
	{
		Query	   *query = lfirst_node(Query, lc);

		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);
	}
}

/*
 * ScanQueryForLocks: recursively scan one Query for AcquirePlannerLocks.
 */
static void
ScanQueryForLocks(Query *parsetree, bool acquire)
{
	ListCell   *lc;

	/* Shouldn't get called on utility commands */
	Assert(parsetree->commandType != CMD_UTILITY);

	/*
	 * First, process RTEs of the current query level.
	 */
	foreach(lc, parsetree->rtable)
	{
		RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);

		switch (rte->rtekind)
		{
			case RTE_RELATION:
				/* Acquire or release the appropriate type of lock */
				if (acquire)
					LockRelationOid(rte->relid, rte->rellockmode);
				else
					UnlockRelationOid(rte->relid, rte->rellockmode);
				break;

			case RTE_SUBQUERY:
				/* If this was a view, must lock/unlock the view */
				if (OidIsValid(rte->relid))
				{
					if (acquire)
						LockRelationOid(rte->relid, rte->rellockmode);
					else
						UnlockRelationOid(rte->relid, rte->rellockmode);
				}
				/* 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 = lfirst_node(CommonTableExpr, lc);

		ScanQueryForLocks(castNode(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, &acquire,
						  QTW_IGNORE_RC_SUBQUERIES);
	}
}

/*
 * Walker to find sublink subqueries for ScanQueryForLocks
 */
static bool
ScanQueryWalker(Node *node, bool *acquire)
{
	if (node == NULL)
		return false;
	if (IsA(node, SubLink))
	{
		SubLink    *sub = (SubLink *) node;

		/* Do what we came for */
		ScanQueryForLocks(castNode(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, acquire);
}

/*
 * PlanCacheComputeResultDesc: given a list of analyzed-and-rewritten Queries,
 * determine the result tupledesc it will produce.  Returns NULL if the
 * execution will not return tuples.
 *
 * Note: the result is created or copied into current memory context.
 */
static TupleDesc
PlanCacheComputeResultDesc(List *stmt_list)
{
	Query	   *query;

	switch (ChoosePortalStrategy(stmt_list))
	{
		case PORTAL_ONE_SELECT:
		case PORTAL_ONE_MOD_WITH:
			query = linitial_node(Query, stmt_list);
			return ExecCleanTypeFromTL(query->targetList);

		case PORTAL_ONE_RETURNING:
			query = QueryListGetPrimaryStmt(stmt_list);
			Assert(query->returningList);
			return ExecCleanTypeFromTL(query->returningList);

		case PORTAL_UTIL_SELECT:
			query = linitial_node(Query, stmt_list);
			Assert(query->utilityStmt);
			return UtilityTupleDescriptor(query->utilityStmt);

		case PORTAL_MULTI_QUERY:
			/* will not return tuples */
			break;
	}
	return NULL;
}

/*
 * PlanCacheRelCallback
 *		Relcache inval callback function
 *
 * Invalidate all plans mentioning the given rel, or all plans mentioning
 * any rel at all if relid == InvalidOid.
 */
static void
PlanCacheRelCallback(Datum arg, Oid relid)
{
	dlist_iter	iter;

	dlist_foreach(iter, &saved_plan_list)
	{
		CachedPlanSource *plansource = dlist_container(CachedPlanSource,
													   node, iter.cur);

		Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);

		/* No work if it's already invalidated */
		if (!plansource->is_valid)
			continue;

		/* Never invalidate if parse/plan would be a no-op anyway */
		if (!StmtPlanRequiresRevalidation(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 = lfirst_node(PlannedStmt, lc);

				if (plannedstmt->commandType == CMD_UTILITY)
					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 */
				}
			}
		}
	}

	/* Likewise check cached expressions */
	dlist_foreach(iter, &cached_expression_list)
	{
		CachedExpression *cexpr = dlist_container(CachedExpression,
												  node, iter.cur);

		Assert(cexpr->magic == CACHEDEXPR_MAGIC);

		/* No work if it's already invalidated */
		if (!cexpr->is_valid)
			continue;

		if ((relid == InvalidOid) ? cexpr->relationOids != NIL :
			list_member_oid(cexpr->relationOids, relid))
		{
			cexpr->is_valid = false;
		}
	}
}

/*
 * PlanCacheObjectCallback
 *		Syscache inval callback function for PROCOID and TYPEOID caches
 *
 * Invalidate all plans mentioning the object with the specified hash value,
 * or all plans mentioning any member of this cache if hashvalue == 0.
 */
static void
PlanCacheObjectCallback(Datum arg, int cacheid, uint32 hashvalue)
{
	dlist_iter	iter;

	dlist_foreach(iter, &saved_plan_list)
	{
		CachedPlanSource *plansource = dlist_container(CachedPlanSource,
													   node, iter.cur);
		ListCell   *lc;

		Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);

		/* No work if it's already invalidated */
		if (!plansource->is_valid)
			continue;

		/* Never invalidate if parse/plan would be a no-op anyway */
		if (!StmtPlanRequiresRevalidation(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 = lfirst_node(PlannedStmt, lc);
				ListCell   *lc3;

				if (plannedstmt->commandType == CMD_UTILITY)
					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 */
			}
		}
	}

	/* Likewise check cached expressions */
	dlist_foreach(iter, &cached_expression_list)
	{
		CachedExpression *cexpr = dlist_container(CachedExpression,
												  node, iter.cur);
		ListCell   *lc;

		Assert(cexpr->magic == CACHEDEXPR_MAGIC);

		/* No work if it's already invalidated */
		if (!cexpr->is_valid)
			continue;

		foreach(lc, cexpr->invalItems)
		{
			PlanInvalItem *item = (PlanInvalItem *) lfirst(lc);

			if (item->cacheId != cacheid)
				continue;
			if (hashvalue == 0 ||
				item->hashValue == hashvalue)
			{
				cexpr->is_valid = false;
				break;
			}
		}
	}
}

/*
 * PlanCacheSysCallback
 *		Syscache inval callback function for other caches
 *
 * Just invalidate everything...
 */
static void
PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue)
{
	ResetPlanCache();
}

/*
 * ResetPlanCache: invalidate all cached plans.
 */
void
ResetPlanCache(void)
{
	dlist_iter	iter;

	dlist_foreach(iter, &saved_plan_list)
	{
		CachedPlanSource *plansource = dlist_container(CachedPlanSource,
													   node, iter.cur);

		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).
		 * In general there's no point in invalidating statements for which a
		 * new parse analysis/rewrite/plan cycle would certainly give the same
		 * results.
		 */
		if (!StmtPlanRequiresRevalidation(plansource))
			continue;

		plansource->is_valid = false;
		if (plansource->gplan)
			plansource->gplan->is_valid = false;
	}

	/* Likewise invalidate cached expressions */
	dlist_foreach(iter, &cached_expression_list)
	{
		CachedExpression *cexpr = dlist_container(CachedExpression,
												  node, iter.cur);

		Assert(cexpr->magic == CACHEDEXPR_MAGIC);

		cexpr->is_valid = false;
	}
}

/*
 * Release all CachedPlans remembered by 'owner'
 */
void
ReleaseAllPlanCacheRefsInOwner(ResourceOwner owner)
{
	ResourceOwnerReleaseAllOfKind(owner, &planref_resowner_desc);
}

/* ResourceOwner callbacks */

static void
ResOwnerReleaseCachedPlan(Datum res)
{
	ReleaseCachedPlan((CachedPlan *) DatumGetPointer(res), NULL);
}
