aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/cache/plancache.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/cache/plancache.c')
-rw-r--r--src/backend/utils/cache/plancache.c191
1 files changed, 153 insertions, 38 deletions
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 6c2979d5c82..3b681647060 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -14,7 +14,7 @@
* 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 rewrite is repeated to build a new valid query tree,
+ * 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.
@@ -63,6 +63,7 @@
#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"
@@ -75,18 +76,6 @@
/*
- * 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.
- */
-#define StmtPlanRequiresRevalidation(plansource) \
- ((plansource)->raw_parse_tree != NULL && \
- stmt_requires_parse_analysis((plansource)->raw_parse_tree))
-
-/*
* 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
@@ -100,6 +89,8 @@ static dlist_head saved_plan_list = DLIST_STATIC_INIT(saved_plan_list);
static dlist_head cached_expression_list = DLIST_STATIC_INIT(cached_expression_list);
static void ReleaseGenericPlan(CachedPlanSource *plansource);
+static bool StmtPlanRequiresRevalidation(CachedPlanSource *plansource);
+static bool BuildingPlanRequiresSnapshot(CachedPlanSource *plansource);
static List *RevalidateCachedQuery(CachedPlanSource *plansource,
QueryEnvironment *queryEnv,
bool release_generic);
@@ -166,7 +157,7 @@ InitPlanCache(void)
}
/*
- * CreateCachedPlan: initially create a plan cache entry.
+ * 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
@@ -220,6 +211,7 @@ CreateCachedPlan(RawStmt *raw_parse_tree,
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;
@@ -227,6 +219,8 @@ CreateCachedPlan(RawStmt *raw_parse_tree,
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;
@@ -256,6 +250,34 @@ CreateCachedPlan(RawStmt *raw_parse_tree,
}
/*
+ * 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
@@ -289,12 +311,15 @@ CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
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;
@@ -465,6 +490,29 @@ CompleteCachedPlan(CachedPlanSource *plansource,
}
/*
+ * 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
@@ -567,6 +615,42 @@ ReleaseGenericPlan(CachedPlanSource *plansource)
}
/*
+ * 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.
@@ -592,7 +676,6 @@ RevalidateCachedQuery(CachedPlanSource *plansource,
bool release_generic)
{
bool snapshot_set;
- RawStmt *rawtree;
List *tlist; /* transient query-tree list */
List *qlist; /* permanent query-tree list */
TupleDesc resultDesc;
@@ -615,7 +698,10 @@ RevalidateCachedQuery(CachedPlanSource *plansource,
/*
* 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.
+ * 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)
{
@@ -662,9 +748,9 @@ RevalidateCachedQuery(CachedPlanSource *plansource,
}
/*
- * Discard the no-longer-useful query tree. (Note: we don't want to do
- * this any earlier, else we'd not have been able to release locks
- * correctly in the race condition case.)
+ * 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;
@@ -711,25 +797,52 @@ RevalidateCachedQuery(CachedPlanSource *plansource,
}
/*
- * Run parse analysis and rule rewriting. The parser tends to scribble on
- * its input, so we must copy the raw parse tree to prevent corruption of
- * the cache.
+ * Run parse analysis (if needed) and rule rewriting.
*/
- rawtree = copyObject(plansource->raw_parse_tree);
- if (rawtree == NULL)
- tlist = NIL;
- else if (plansource->parserSetup != NULL)
- tlist = pg_analyze_and_rewrite_withcb(rawtree,
- plansource->query_string,
- plansource->parserSetup,
- plansource->parserSetupArg,
- queryEnv);
+ 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
- tlist = pg_analyze_and_rewrite_fixedparams(rawtree,
- plansource->query_string,
- plansource->param_types,
- plansource->num_params,
- queryEnv);
+ {
+ /* 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)
@@ -963,8 +1076,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
*/
snapshot_set = false;
if (!ActiveSnapshotSet() &&
- plansource->raw_parse_tree &&
- analyze_requires_snapshot(plansource->raw_parse_tree))
+ BuildingPlanRequiresSnapshot(plansource))
{
PushActiveSnapshot(GetTransactionSnapshot());
snapshot_set = true;
@@ -1703,6 +1815,7 @@ CopyCachedPlan(CachedPlanSource *plansource)
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;
@@ -1718,6 +1831,8 @@ CopyCachedPlan(CachedPlanSource *plansource)
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)