oneshot_plans.v2.patch
application/octet-stream
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index a770daf..b77d023 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -282,6 +282,8 @@ ExplainOneQuery(Query *query, ExplainState *es,
return;
}
+ query->oneshot = true;
+
/* if an advisor plugin is present, let it manage things */
if (ExplainOneQuery_hook)
(*ExplainOneQuery_hook) (query, es, queryString, params);
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index d848926..7119ccd 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -701,6 +701,7 @@ execute_sql_string(const char *sql, const char *filename)
* Do parse analysis, rule rewrite, planning, and execution for each raw
* parsetree. We must fully execute each query before beginning parse
* analysis on the next one, since there may be interdependencies.
+ * Use one time plans so that we don't cache anything from this execution.
*/
foreach(lc1, raw_parsetree_list)
{
@@ -712,7 +713,7 @@ execute_sql_string(const char *sql, const char *filename)
sql,
NULL,
0);
- stmt_list = pg_plan_queries(stmt_list, 0, NULL);
+ stmt_list = pg_plan_queries(stmt_list, 0, NULL, true);
foreach(lc2, stmt_list)
{
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index dfa2ab0..6d8fd8c 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -145,7 +145,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
query_list = QueryRewrite(query);
/* Generate plans for queries. */
- plan_list = pg_plan_queries(query_list, 0, NULL);
+ plan_list = pg_plan_queries(query_list, 0, NULL, false);
/*
* Save the results.
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 6e723ca..cf87c6e 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1714,7 +1714,7 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams)
plan->argtypes,
plan->nargs);
}
- stmt_list = pg_plan_queries(stmt_list, cursor_options, boundParams);
+ stmt_list = pg_plan_queries(stmt_list, cursor_options, boundParams, false);
plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
cplan = (CachedPlan *) palloc0(sizeof(CachedPlan));
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 9aafc8a..2e322e6 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -170,6 +170,14 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob->lastRowMarkId = 0;
glob->transientPlan = false;
+ /*
+ * Record whether the plan will be used once and immediately by executor.
+ * If this is a oneshot plan we can evaluate pseudoconstants during
+ * planning, so we can take advantage of parameter values as constants,
+ * and new in 9.2, evaluate stable functions into constants.
+ */
+ glob->oneshot = parse->oneshot;
+
/* Determine what fraction of the plan is likely to be scanned */
if (cursorOptions & CURSOR_OPT_FAST_PLAN)
{
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 2914c39..a743920 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -61,6 +61,7 @@ typedef struct
List *active_fns;
Node *case_val;
bool estimate;
+ bool oneshot;
} eval_const_expressions_context;
typedef struct
@@ -2097,11 +2098,13 @@ eval_const_expressions(PlannerInfo *root, Node *node)
{
context.boundParams = root->glob->boundParams; /* bound Params */
context.glob = root->glob; /* for inlined-function dependencies */
+ context.oneshot = root->glob->oneshot; /* can we optimise for oneshot plans? */
}
else
{
context.boundParams = NULL;
context.glob = NULL;
+ context.oneshot = false;
}
context.active_fns = NIL; /* nothing being recursively simplified */
context.case_val = NULL; /* no CASE being examined */
@@ -2137,6 +2140,7 @@ estimate_expression_value(PlannerInfo *root, Node *node)
context.active_fns = NIL; /* nothing being recursively simplified */
context.case_val = NULL; /* no CASE being examined */
context.estimate = true; /* unsafe transformations OK */
+ context.oneshot = root->glob->oneshot; /* can we optimise for oneshot plans? */
return eval_const_expressions_mutator(node, &context);
}
@@ -3786,7 +3790,7 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
*/
if (funcform->provolatile == PROVOLATILE_IMMUTABLE)
/* okay */ ;
- else if (context->estimate && funcform->provolatile == PROVOLATILE_STABLE)
+ else if (funcform->provolatile == PROVOLATILE_STABLE && (context->estimate || context->oneshot))
/* okay */ ;
else
return NULL;
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index a07661f..ff9f3cd 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -762,7 +762,7 @@ pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams)
* list. Utility statements are simply represented by their statement nodes.
*/
List *
-pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams)
+pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams, bool oneshotplan)
{
List *stmt_list = NIL;
ListCell *query_list;
@@ -779,6 +779,7 @@ pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams)
}
else
{
+ query->oneshot = oneshotplan;
stmt = (Node *) pg_plan_query(query, cursorOptions, boundParams);
}
@@ -944,7 +945,7 @@ exec_simple_query(const char *query_string)
querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
NULL, 0);
- plantree_list = pg_plan_queries(querytree_list, 0, NULL);
+ plantree_list = pg_plan_queries(querytree_list, 0, NULL, true);
/* Done with the snapshot used for parsing/planning */
if (snapshot_set)
@@ -1284,7 +1285,7 @@ exec_parse_message(const char *query_string, /* string to execute */
}
else
{
- stmt_list = pg_plan_queries(querytree_list, 0, NULL);
+ stmt_list = pg_plan_queries(querytree_list, 0, NULL, false);
fully_planned = true;
}
@@ -1739,7 +1740,7 @@ exec_bind_message(StringInfo input_message)
*/
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
query_list = copyObject(cplan->stmt_list);
- plan_list = pg_plan_queries(query_list, 0, params);
+ plan_list = pg_plan_queries(query_list, 0, params, true);
MemoryContextSwitchTo(oldContext);
/* We no longer need the cached plan refcount ... */
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 08ddfa9..e65ba82 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -563,7 +563,7 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
pushed = SPI_push_conditional();
- slist = pg_plan_queries(slist, plansource->cursor_options, NULL);
+ slist = pg_plan_queries(slist, plansource->cursor_options, NULL, false);
SPI_pop_conditional(pushed);
}
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 14937d4..568262e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -103,6 +103,7 @@ typedef struct Query
QuerySource querySource; /* where did I come from? */
+ bool oneshot; /* can plan potentially be used more than once? */
bool canSetTag; /* do I set the command result tag? */
Node *utilityStmt; /* non-null if this is DECLARE CURSOR or a
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index f659269..e655542 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -76,6 +76,8 @@ typedef struct PlannerGlobal
ParamListInfo boundParams; /* Param values provided to planner() */
+ bool oneshot; /* Are we planning a non-reusable plan? */
+
List *paramlist; /* to keep track of cross-level Params */
List *subplans; /* Plans for SubPlan nodes */
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index d5192d9..3be6467 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -55,7 +55,7 @@ extern List *pg_analyze_and_rewrite_params(Node *parsetree,
extern PlannedStmt *pg_plan_query(Query *querytree, int cursorOptions,
ParamListInfo boundParams);
extern List *pg_plan_queries(List *querytrees, int cursorOptions,
- ParamListInfo boundParams);
+ ParamListInfo boundParams, bool oneshotplan);
extern bool check_max_stack_depth(int *newval, void **extra, GucSource source);
extern void assign_max_stack_depth(int newval, void *extra);