aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/prepare.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/prepare.c')
-rw-r--r--src/backend/commands/prepare.c144
1 files changed, 57 insertions, 87 deletions
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index d929e14e0ed..a94921574fa 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -53,11 +53,11 @@ static Datum build_regtype_array(Oid *param_types, int num_params);
void
PrepareQuery(PrepareStmt *stmt, const char *queryString)
{
+ CachedPlanSource *plansource;
Oid *argtypes = NULL;
int nargs;
Query *query;
- List *query_list,
- *plan_list;
+ List *query_list;
int i;
/*
@@ -69,6 +69,13 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
(errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
errmsg("invalid statement name: must not be empty")));
+ /*
+ * Create the CachedPlanSource before we do parse analysis, since it needs
+ * to see the unmodified raw parse tree.
+ */
+ plansource = CreateCachedPlan(stmt->query, queryString,
+ CreateCommandTag(stmt->query));
+
/* Transform list of TypeNames to array of type OIDs */
nargs = list_length(stmt->argtypes);
@@ -102,7 +109,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
* information about unknown parameters to be deduced from context.
*
* Because parse analysis scribbles on the raw querytree, we must make a
- * copy to ensure we have a pristine raw tree to cache. FIXME someday.
+ * copy to ensure we don't modify the passed-in tree. FIXME someday.
*/
query = parse_analyze_varparams((Node *) copyObject(stmt->query),
queryString,
@@ -143,20 +150,22 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
/* Rewrite the query. The result could be 0, 1, or many queries. */
query_list = QueryRewrite(query);
- /* Generate plans for queries. */
- plan_list = pg_plan_queries(query_list, 0, NULL);
+ /* Finish filling in the CachedPlanSource */
+ CompleteCachedPlan(plansource,
+ query_list,
+ NULL,
+ argtypes,
+ nargs,
+ NULL,
+ NULL,
+ 0, /* default cursor options */
+ true); /* fixed result */
/*
* Save the results.
*/
StorePreparedStatement(stmt->name,
- stmt->query,
- queryString,
- CreateCommandTag((Node *) query),
- argtypes,
- nargs,
- 0, /* default cursor options */
- plan_list,
+ plansource,
true);
}
@@ -185,10 +194,7 @@ ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
/* Look it up in the hash table */
entry = FetchPreparedStatement(stmt->name, true);
- /* Shouldn't have a non-fully-planned plancache entry */
- if (!entry->plansource->fully_planned)
- elog(ERROR, "EXECUTE does not support unplanned prepared statements");
- /* Shouldn't get any non-fixed-result cached plan, either */
+ /* Shouldn't find a non-fixed-result cached plan */
if (!entry->plansource->fixed_result)
elog(ERROR, "EXECUTE does not support variable-result cached plans");
@@ -197,7 +203,9 @@ ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
{
/*
* Need an EState to evaluate parameters; must not delete it till end
- * of query, in case parameters are pass-by-reference.
+ * of query, in case parameters are pass-by-reference. Note that the
+ * passed-in "params" could possibly be referenced in the parameter
+ * expressions.
*/
estate = CreateExecutorState();
estate->es_param_list_info = params;
@@ -226,7 +234,7 @@ ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
PlannedStmt *pstmt;
/* Replan if needed, and increment plan refcount transiently */
- cplan = RevalidateCachedPlan(entry->plansource, true);
+ cplan = GetCachedPlan(entry->plansource, paramLI, true);
/* Copy plan into portal's context, and modify */
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
@@ -256,7 +264,7 @@ ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
else
{
/* Replan if needed, and increment plan refcount for portal */
- cplan = RevalidateCachedPlan(entry->plansource, false);
+ cplan = GetCachedPlan(entry->plansource, paramLI, false);
plan_list = cplan->stmt_list;
}
@@ -396,7 +404,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
ParamExternData *prm = &paramLI->params[i];
prm->ptype = param_types[i];
- prm->pflags = 0;
+ prm->pflags = PARAM_FLAG_CONST;
prm->value = ExecEvalExprSwitchContext(n,
GetPerTupleExprContext(estate),
&prm->isnull,
@@ -430,54 +438,24 @@ InitQueryHashTable(void)
/*
* Store all the data pertaining to a query in the hash table using
- * the specified key. All the given data is copied into either the hashtable
- * entry or the underlying plancache entry, so the caller can dispose of its
- * copy.
- *
- * Exception: commandTag is presumed to be a pointer to a constant string,
- * or possibly NULL, so it need not be copied. Note that commandTag should
- * be NULL only if the original query (before rewriting) was empty.
+ * the specified key. The passed CachedPlanSource should be "unsaved"
+ * in case we get an error here; we'll save it once we've created the hash
+ * table entry.
*/
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)
{
PreparedStatement *entry;
- CachedPlanSource *plansource;
+ TimestampTz cur_ts = GetCurrentStatementStartTimestamp();
bool found;
/* Initialize the hash table, if necessary */
if (!prepared_queries)
InitQueryHashTable();
- /* Check for pre-existing entry of same name */
- hash_search(prepared_queries, stmt_name, HASH_FIND, &found);
-
- if (found)
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_PSTATEMENT),
- errmsg("prepared statement \"%s\" already exists",
- stmt_name)));
-
- /* Create a plancache entry */
- plansource = CreateCachedPlan(raw_parse_tree,
- query_string,
- commandTag,
- param_types,
- num_params,
- cursor_options,
- stmt_list,
- true,
- true);
-
- /* Now we can add entry to hash table */
+ /* Add entry to hash table */
entry = (PreparedStatement *) hash_search(prepared_queries,
stmt_name,
HASH_ENTER,
@@ -485,13 +463,18 @@ StorePreparedStatement(const char *stmt_name,
/* Shouldn't get a duplicate entry */
if (found)
- elog(ERROR, "duplicate prepared statement \"%s\"",
- stmt_name);
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_PSTATEMENT),
+ errmsg("prepared statement \"%s\" already exists",
+ stmt_name)));
/* Fill in the hash table entry */
entry->plansource = plansource;
entry->from_sql = from_sql;
- entry->prepare_time = GetCurrentStatementStartTimestamp();
+ entry->prepare_time = cur_ts;
+
+ /* Now it's safe to move the CachedPlanSource to permanent memory */
+ SaveCachedPlan(plansource);
}
/*
@@ -538,7 +521,7 @@ FetchPreparedStatementResultDesc(PreparedStatement *stmt)
{
/*
* Since we don't allow prepared statements' result tupdescs to change,
- * there's no need for a revalidate call here.
+ * there's no need to worry about revalidating the cached plan here.
*/
Assert(stmt->plansource->fixed_result);
if (stmt->plansource->resultDesc)
@@ -560,24 +543,12 @@ List *
FetchPreparedStatementTargetList(PreparedStatement *stmt)
{
List *tlist;
- CachedPlan *cplan;
-
- /* No point in looking if it doesn't return tuples */
- if (stmt->plansource->resultDesc == NULL)
- return NIL;
- /* Make sure the plan is up to date */
- cplan = RevalidateCachedPlan(stmt->plansource, true);
-
- /* Get the primary statement and find out what it returns */
- tlist = FetchStatementTargetList(PortalListGetPrimaryStmt(cplan->stmt_list));
-
- /* Copy into caller's context so we can release the plancache entry */
- tlist = (List *) copyObject(tlist);
-
- ReleaseCachedPlan(cplan, true);
+ /* Get the plan's primary targetlist */
+ tlist = CachedPlanGetTargetList(stmt->plansource);
- return tlist;
+ /* Copy into caller's context in case plan gets invalidated */
+ return (List *) copyObject(tlist);
}
/*
@@ -662,26 +633,20 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainState *es,
/* Look it up in the hash table */
entry = FetchPreparedStatement(execstmt->name, true);
- /* Shouldn't have a non-fully-planned plancache entry */
- if (!entry->plansource->fully_planned)
- elog(ERROR, "EXPLAIN EXECUTE does not support unplanned prepared statements");
- /* Shouldn't get any non-fixed-result cached plan, either */
+ /* Shouldn't find a non-fixed-result cached plan */
if (!entry->plansource->fixed_result)
elog(ERROR, "EXPLAIN EXECUTE does not support variable-result cached plans");
query_string = entry->plansource->query_string;
- /* Replan if needed, and acquire a transient refcount */
- cplan = RevalidateCachedPlan(entry->plansource, true);
-
- plan_list = cplan->stmt_list;
-
/* Evaluate parameters, if any */
if (entry->plansource->num_params)
{
/*
* Need an EState to evaluate parameters; must not delete it till end
- * of query, in case parameters are pass-by-reference.
+ * of query, in case parameters are pass-by-reference. Note that the
+ * passed-in "params" could possibly be referenced in the parameter
+ * expressions.
*/
estate = CreateExecutorState();
estate->es_param_list_info = params;
@@ -689,6 +654,11 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainState *es,
queryString, estate);
}
+ /* Replan if needed, and acquire a transient refcount */
+ cplan = GetCachedPlan(entry->plansource, paramLI, true);
+
+ plan_list = cplan->stmt_list;
+
/* Explain each query */
foreach(p, plan_list)
{
@@ -714,7 +684,7 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainState *es,
}
else
{
- ExplainOneUtility((Node *) pstmt, es, query_string, params);
+ ExplainOneUtility((Node *) pstmt, es, query_string, paramLI);
}
/* No need for CommandCounterIncrement, as ExplainOnePlan did it */