summaryrefslogtreecommitdiff
path: root/src/include
diff options
context:
space:
mode:
Diffstat (limited to 'src/include')
-rw-r--r--src/include/catalog/namespace.h1
-rw-r--r--src/include/commands/prepare.h8
-rw-r--r--src/include/executor/spi.h1
-rw-r--r--src/include/executor/spi_priv.h41
-rw-r--r--src/include/nodes/parsenodes.h3
-rw-r--r--src/include/utils/memutils.h1
-rw-r--r--src/include/utils/plancache.h156
7 files changed, 126 insertions, 85 deletions
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 4bcbc20497f..904c6fd97d8 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -118,6 +118,7 @@ extern Oid GetTempToastNamespace(void);
extern void ResetTempTableNamespace(void);
extern OverrideSearchPath *GetOverrideSearchPath(MemoryContext context);
+extern OverrideSearchPath *CopyOverrideSearchPath(OverrideSearchPath *path);
extern void PushOverrideSearchPath(OverrideSearchPath *newpath);
extern void PopOverrideSearchPath(void);
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index 63c06ad8d69..52362fa933d 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -44,13 +44,7 @@ extern void ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainState *es,
/* Low-level access to stored prepared statements */
extern void StorePreparedStatement(const char *stmt_name,
- Node *raw_parse_tree,
- const char *query_string,
- const char *commandTag,
- Oid *param_types,
- int num_params,
- int cursor_options,
- List *stmt_list,
+ CachedPlanSource *plansource,
bool from_sql);
extern PreparedStatement *FetchPreparedStatement(const char *stmt_name,
bool throwError);
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index 7199debb27a..3b1b27ee49e 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -93,6 +93,7 @@ extern SPIPlanPtr SPI_prepare_params(const char *src,
ParserSetupHook parserSetup,
void *parserSetupArg,
int cursorOptions);
+extern int SPI_keepplan(SPIPlanPtr plan);
extern SPIPlanPtr SPI_saveplan(SPIPlanPtr plan);
extern int SPI_freeplan(SPIPlanPtr plan);
diff --git a/src/include/executor/spi_priv.h b/src/include/executor/spi_priv.h
index 5865f532802..3e7bf860948 100644
--- a/src/include/executor/spi_priv.h
+++ b/src/include/executor/spi_priv.h
@@ -32,27 +32,32 @@ typedef struct
} _SPI_connection;
/*
- * SPI plans have two states: saved or unsaved.
+ * SPI plans have three states: saved, unsaved, or temporary.
*
- * For an unsaved plan, the _SPI_plan struct and all its subsidiary data are in
- * a dedicated memory context identified by plancxt. An unsaved plan is good
- * at most for the current transaction, since the locks that protect it from
- * schema changes will be lost at end of transaction. Hence the plancxt is
- * always a transient one.
+ * Ordinarily, the _SPI_plan struct itself as well as the argtypes array
+ * are in a dedicated memory context identified by plancxt (which can be
+ * really small). All the other subsidiary state is in plancache entries
+ * identified by plancache_list (note: the list cells themselves are in
+ * plancxt).
*
- * For a saved plan, the _SPI_plan struct and the argument type array are in
- * the plancxt (which can be really small). All the other subsidiary state
- * is in plancache entries identified by plancache_list (note: the list cells
- * themselves are in plancxt). We rely on plancache.c to keep the cache
- * entries up-to-date as needed. The plancxt is a child of CacheMemoryContext
- * since it should persist until explicitly destroyed.
+ * In an unsaved plan, the plancxt as well as the plancache entries' contexts
+ * are children of the SPI procedure context, so they'll all disappear at
+ * function exit. plancache.c also knows that the plancache entries are
+ * "unsaved", so it doesn't link them into its global list; hence they do
+ * not respond to inval events. This is OK since we are presumably holding
+ * adequate locks to prevent other backends from messing with the tables.
*
- * To avoid redundant coding, the representation of unsaved plans matches
- * that of saved plans, ie, plancache_list is a list of CachedPlanSource
- * structs which in turn point to CachedPlan structs. However, in an unsaved
- * plan all these structs are just created by spi.c and are not known to
- * plancache.c. We don't try very hard to make all their fields valid,
- * only the ones spi.c actually uses.
+ * For a saved plan, the plancxt is made a child of CacheMemoryContext
+ * since it should persist until explicitly destroyed. Likewise, the
+ * plancache entries will be under CacheMemoryContext since we tell
+ * plancache.c to save them. We rely on plancache.c to keep the cache
+ * entries up-to-date as needed in the face of invalidation events.
+ *
+ * There are also "temporary" SPI plans, in which the _SPI_plan struct is
+ * not even palloc'd but just exists in some function's local variable.
+ * The plancache entries are unsaved and exist under the SPI executor context,
+ * while additional data such as argtypes and list cells is loose in the SPI
+ * executor context. Such plans can be identified by having plancxt == NULL.
*
* Note: if the original query string contained only whitespace and comments,
* the plancache_list will be NIL and so there is no place to store the
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index a4fb3b5f7f6..9998e2f24d6 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1996,7 +1996,10 @@ typedef struct SecLabelStmt
#define CURSOR_OPT_NO_SCROLL 0x0004 /* NO SCROLL explicitly given */
#define CURSOR_OPT_INSENSITIVE 0x0008 /* INSENSITIVE */
#define CURSOR_OPT_HOLD 0x0010 /* WITH HOLD */
+/* these planner-control flags do not correspond to any SQL grammar: */
#define CURSOR_OPT_FAST_PLAN 0x0020 /* prefer fast-start plan */
+#define CURSOR_OPT_GENERIC_PLAN 0x0040 /* force use of generic plan */
+#define CURSOR_OPT_CUSTOM_PLAN 0x0080 /* force use of custom plan */
typedef struct DeclareCursorStmt
{
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 94c78289b6e..4796f5c7057 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -94,6 +94,7 @@ extern void MemoryContextSetParent(MemoryContext context,
MemoryContext new_parent);
extern Size GetMemoryChunkSpace(void *pointer);
extern MemoryContext GetMemoryChunkContext(void *pointer);
+extern MemoryContext MemoryContextGetParent(MemoryContext context);
extern bool MemoryContextIsEmpty(MemoryContext context);
extern void MemoryContextStats(MemoryContext context);
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index b8639a59a0e..c8c27bbb207 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -18,26 +18,47 @@
#include "access/tupdesc.h"
#include "nodes/params.h"
+#define CACHEDPLANSOURCE_MAGIC 195726186
+#define CACHEDPLAN_MAGIC 953717834
+
/*
- * CachedPlanSource represents the portion of a cached plan that persists
- * across invalidation/replan cycles. It stores a raw parse tree (required),
- * the original source text (also required, as of 8.4), and adjunct data.
+ * CachedPlanSource (which might better have been called CachedQuery)
+ * represents a SQL query that we expect to use multiple times. It stores
+ * the query source text, the raw parse tree, and the analyzed-and-rewritten
+ * query tree, as well as adjunct data. Cache invalidation can happen as a
+ * result of DDL affecting objects used by the query. In that case we discard
+ * the analyzed-and-rewritten query tree, and rebuild it when next needed.
+ *
+ * An actual execution plan, represented by CachedPlan, is derived from the
+ * CachedPlanSource when we need to execute the query. The plan could be
+ * either generic (usable with any set of plan parameters) or custom (for a
+ * specific set of parameters). plancache.c contains the logic that decides
+ * which way to do it for any particular execution. If we are using a generic
+ * cached plan then it is meant to be re-used across multiple executions, so
+ * callers must always treat CachedPlans as read-only.
+ *
+ * Once successfully built and "saved", CachedPlanSources typically live
+ * for the life of the backend, although they can be dropped explicitly.
+ * CachedPlans are reference-counted and go away automatically when the last
+ * reference is dropped. A CachedPlan can outlive the CachedPlanSource it
+ * was created from.
*
- * Normally, both the struct itself and the subsidiary data live in the
- * context denoted by the context field, while the linked-to CachedPlan, if
- * any, has its own context. Thus an invalidated CachedPlan can be dropped
- * when no longer needed, and conversely a CachedPlanSource can be dropped
- * without worrying whether any portals depend on particular instances of
- * its plan.
+ * An "unsaved" CachedPlanSource can be used for generating plans, but it
+ * lives in transient storage and will not be updated in response to sinval
+ * events.
*
- * But for entries created by FastCreateCachedPlan, the CachedPlanSource
- * and the initial version of the CachedPlan share the same memory context.
- * In this case, we treat the memory context as belonging to the CachedPlan.
- * The CachedPlanSource has an extra reference-counted link (orig_plan)
- * to the CachedPlan, and the memory context goes away when the CachedPlan's
- * reference count goes to zero. This arrangement saves overhead for plans
- * that aren't expected to live long enough to need replanning, while not
- * losing any flexibility if a replan turns out to be necessary.
+ * CachedPlans made from saved CachedPlanSources are likewise in permanent
+ * storage, so to avoid memory leaks, the reference-counted references to them
+ * must be held in permanent data structures or ResourceOwners. CachedPlans
+ * made from unsaved CachedPlanSources are in children of the caller's
+ * memory context, so references to them should not be longer-lived than
+ * that context. (Reference counting is somewhat pro forma in that case,
+ * though it may be useful if the CachedPlan can be discarded early.)
+ *
+ * A CachedPlanSource has two associated memory contexts: one that holds the
+ * struct itself, the query source text and the raw parse tree, and another
+ * context that holds the rewritten query tree and associated data. This
+ * allows the query tree to be discarded easily when it is invalidated.
*
* Note: the string referenced by commandTag is not subsidiary storage;
* it is assumed to be a compile-time-constant string. As with portals,
@@ -46,78 +67,93 @@
*/
typedef struct CachedPlanSource
{
+ int magic; /* should equal CACHEDPLANSOURCE_MAGIC */
Node *raw_parse_tree; /* output of raw_parser() */
- char *query_string; /* text of query (as of 8.4, never NULL) */
+ char *query_string; /* source text of query */
const char *commandTag; /* command tag (a constant!), or NULL */
Oid *param_types; /* array of parameter type OIDs, or NULL */
int num_params; /* length of param_types array */
ParserSetupHook parserSetup; /* alternative parameter spec method */
void *parserSetupArg;
int cursor_options; /* cursor options used for planning */
- bool fully_planned; /* do we cache planner or rewriter output? */
bool fixed_result; /* disallow change in result tupdesc? */
- struct OverrideSearchPath *search_path; /* saved search_path */
- int generation; /* counter, starting at 1, for replans */
TupleDesc resultDesc; /* result type; NULL = doesn't return tuples */
- struct CachedPlan *plan; /* link to plan, or NULL if not valid */
- MemoryContext context; /* context containing this CachedPlanSource */
- struct CachedPlan *orig_plan; /* link to plan owning my context */
+ struct OverrideSearchPath *search_path; /* saved search_path */
+ MemoryContext context; /* memory context holding all above */
+ /* These fields describe the current analyzed-and-rewritten query tree: */
+ List *query_list; /* list of Query nodes, or NIL if not valid */
+ List *relationOids; /* OIDs of relations the queries depend on */
+ List *invalItems; /* other dependencies, as PlanInvalItems */
+ MemoryContext query_context; /* context holding the above, or NULL */
+ /* If we have a generic plan, this is a reference-counted link to it: */
+ struct CachedPlan *gplan; /* generic plan, or NULL if not valid */
+ /* Some state flags: */
+ bool is_complete; /* has CompleteCachedPlan been done? */
+ bool is_saved; /* has CachedPlanSource been "saved"? */
+ bool is_valid; /* is the query_list currently valid? */
+ int generation; /* increments each time we create a plan */
+ /* If CachedPlanSource has been saved, it is a member of a global list */
+ struct CachedPlanSource *next_saved; /* list link, if so */
+ /* State kept to help decide whether to use custom or generic plans: */
+ double generic_cost; /* cost of generic plan, or -1 if not known */
+ double total_custom_cost; /* total cost of custom plans so far */
+ int num_custom_plans; /* number of plans included in total */
} CachedPlanSource;
/*
- * CachedPlan represents the portion of a cached plan that is discarded when
- * invalidation occurs. The reference count includes both the link(s) from the
- * parent CachedPlanSource, and any active plan executions, so the plan can be
- * discarded exactly when refcount goes to zero. Both the struct itself and
- * the subsidiary data live in the context denoted by the context field.
+ * CachedPlan represents an execution plan derived from a CachedPlanSource.
+ * The reference count includes both the link from the parent CachedPlanSource
+ * (if any), and any active plan executions, so the plan can be discarded
+ * exactly when refcount goes to zero. Both the struct itself and the
+ * subsidiary data live in the context denoted by the context field.
* This makes it easy to free a no-longer-needed cached plan.
*/
typedef struct CachedPlan
{
- List *stmt_list; /* list of statement or Query nodes */
- bool fully_planned; /* do we cache planner or rewriter output? */
- bool dead; /* if true, do not use */
+ int magic; /* should equal CACHEDPLAN_MAGIC */
+ List *stmt_list; /* list of statement nodes (PlannedStmts
+ * and bare utility statements) */
+ bool is_saved; /* is CachedPlan in a long-lived context? */
+ bool is_valid; /* is the stmt_list currently valid? */
TransactionId saved_xmin; /* if valid, replan when TransactionXmin
* changes from this value */
+ int generation; /* parent's generation number for this plan */
int refcount; /* count of live references to this struct */
- int generation; /* counter, starting at 1, for replans */
MemoryContext context; /* context containing this CachedPlan */
- /* These fields are used only in the not-fully-planned case: */
- List *relationOids; /* OIDs of relations the stmts depend on */
- List *invalItems; /* other dependencies, as PlanInvalItems */
} CachedPlan;
extern void InitPlanCache(void);
+extern void ResetPlanCache(void);
+
extern CachedPlanSource *CreateCachedPlan(Node *raw_parse_tree,
const char *query_string,
- const char *commandTag,
- Oid *param_types,
- int num_params,
- int cursor_options,
- List *stmt_list,
- bool fully_planned,
- bool fixed_result);
-extern CachedPlanSource *FastCreateCachedPlan(Node *raw_parse_tree,
- char *query_string,
- const char *commandTag,
- Oid *param_types,
- int num_params,
- int cursor_options,
- List *stmt_list,
- bool fully_planned,
- bool fixed_result,
- MemoryContext context);
-extern void CachedPlanSetParserHook(CachedPlanSource *plansource,
+ const char *commandTag);
+extern void CompleteCachedPlan(CachedPlanSource *plansource,
+ List *querytree_list,
+ MemoryContext querytree_context,
+ Oid *param_types,
+ int num_params,
ParserSetupHook parserSetup,
- void *parserSetupArg);
+ void *parserSetupArg,
+ int cursor_options,
+ bool fixed_result);
+
+extern void SaveCachedPlan(CachedPlanSource *plansource);
extern void DropCachedPlan(CachedPlanSource *plansource);
-extern CachedPlan *RevalidateCachedPlan(CachedPlanSource *plansource,
- bool useResOwner);
-extern void ReleaseCachedPlan(CachedPlan *plan, bool useResOwner);
+
+extern void CachedPlanSetParentContext(CachedPlanSource *plansource,
+ MemoryContext newcontext);
+
+extern CachedPlanSource *CopyCachedPlan(CachedPlanSource *plansource);
+
extern bool CachedPlanIsValid(CachedPlanSource *plansource);
-extern TupleDesc PlanCacheComputeResultDesc(List *stmt_list);
-extern void ResetPlanCache(void);
+extern List *CachedPlanGetTargetList(CachedPlanSource *plansource);
+
+extern CachedPlan *GetCachedPlan(CachedPlanSource *plansource,
+ ParamListInfo boundParams,
+ bool useResOwner);
+extern void ReleaseCachedPlan(CachedPlan *plan, bool useResOwner);
#endif /* PLANCACHE_H */