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