diff options
Diffstat (limited to 'src/backend/commands/prepare.c')
-rw-r--r-- | src/backend/commands/prepare.c | 144 |
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 = ¶mLI->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 */ |