1/*-------------------------------------------------------------------------
4 * Prepareable SQL statements via PREPARE, EXECUTE and DEALLOCATE
6 * This module also implements storage of prepared statements that are
7 * accessed via the extended FE/BE query protocol.
10 * Copyright (c) 2002-2025, PostgreSQL Global Development Group
13 * src/backend/commands/prepare.c
15 *-------------------------------------------------------------------------
42 * The hash table in which prepared queries are stored. This is
43 * per-backend: query plans are not shared between backends.
44 * The keys for this hash table are the arguments to PREPARE and EXECUTE
45 * (statement names); the entries are PreparedStatement structs.
56 * Implements the 'PREPARE' utility statement.
60 int stmt_location,
int stmt_len)
69 * Disallow empty-string statement name (conflicts with protocol-level
72 if (!
stmt->name ||
stmt->name[0] ==
'0円')
74 (
errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
75 errmsg(
"invalid statement name: must not be empty")));
78 * Need to wrap the contained statement in a RawStmt node to pass it to
87 * Create the CachedPlanSource before we do parse analysis, since it needs
88 * to see the unmodified raw parse tree.
93 /* Transform list of TypeNames to array of type OIDs */
104 foreach(l,
stmt->argtypes)
109 argtypes[
i++] = toid;
114 * Analyze the statement using these parameter types (any parameters
115 * passed in from above us will not be visible to it), allowing
116 * information about unknown parameters to be deduced from context.
117 * Rewrite the query. The result could be 0, 1, or many queries.
120 &argtypes, &nargs, NULL);
122 /* Finish filling in the CachedPlanSource */
131 true);
/* fixed result */
142 * ExecuteQuery --- implement the 'EXECUTE' utility statement.
144 * This code also supports CREATE TABLE ... AS EXECUTE. That case is
145 * indicated by passing a non-null intoClause. The DestReceiver is already
146 * set up correctly for CREATE TABLE AS, but we still have to make a few
147 * other adjustments here.
165 /* Look it up in the hash table */
168 /* Shouldn't find a non-fixed-result cached plan */
170 elog(
ERROR,
"EXECUTE does not support variable-result cached plans");
172 /* Evaluate parameters, if any */
176 * Need an EState to evaluate parameters; must not delete it till end
177 * of query, in case parameters are pass-by-reference. Note that the
178 * passed-in "params" could possibly be referenced in the parameter
186 /* Create a new portal to run the query in */
188 /* Don't display the portal in pg_cursors, it is for internal use only */
191 /* Copy the plan's saved query string into the portal's memory */
195 /* Replan if needed, and increment plan refcount for portal */
200 * DO NOT add any logic that could possibly throw an error between
201 * GetCachedPlan and PortalDefineQuery, or you'll leak the plan refcount.
211 * For CREATE TABLE ... AS EXECUTE, we must verify that the prepared
212 * statement is one that produces tuples. Currently we insist that it be
213 * a plain old SELECT. In future we might consider supporting other
214 * things such as INSERT ... RETURNING, but there are a couple of issues
215 * to be settled first, notably how WITH NO DATA should be handled in such
216 * a case (do we really want to suppress execution?) and how to pass down
217 * the OID-determining eflags (PortalStart won't handle them in such a
218 * case, and for that matter it's not clear the executor will either).
220 * For CREATE TABLE ... AS EXECUTE, we also have to ensure that the proper
221 * eflags and fetch count are passed to PortalStart/PortalRun.
229 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
230 errmsg(
"prepared statement is not a SELECT")));
234 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
235 errmsg(
"prepared statement is not a SELECT")));
237 /* Set appropriate eflags */
240 /* And tell PortalRun whether to run to completion or not */
248 /* Plain old EXECUTE */
254 * Run the portal as appropriate.
265 /* No need to pfree other memory, MemoryContext will be reset */
269 * EvaluateParams: evaluate a list of parameters.
271 * pstate: parse state
272 * pstmt: statement we are getting parameters for.
273 * params: list of given parameter expressions (raw parser output!)
274 * estate: executor state to use.
276 * Returns a filled-in ParamListInfo -- this can later be passed to
277 * CreateQueryDesc(), which allows the executor to make use of the parameters
278 * during query execution.
292 if (nparams != num_params)
294 (
errcode(ERRCODE_SYNTAX_ERROR),
295 errmsg(
"wrong number of parameters for prepared statement \"%s\"",
297 errdetail(
"Expected %d parameters but got %d.",
298 num_params, nparams)));
300 /* Quick exit if no parameters */
305 * We have to run parse analysis for the expressions. Since the parser is
306 * not cool about scribbling on its input, copy first.
314 Oid expected_type_id = param_types[
i];
322 expected_type_id, -1,
329 (
errcode(ERRCODE_DATATYPE_MISMATCH),
330 errmsg(
"parameter $%d of type %s cannot be coerced to the expected type %s",
334 errhint(
"You will need to rewrite or cast the expression."),
337 /* Take care of collations in the finished expression. */
344 /* Prepare the expressions for execution */
350 foreach(l, exprstates)
355 prm->
ptype = param_types[
i];
369 * Initialize query hash table upon first use.
386 * Store all the data pertaining to a query in the hash table using
387 * the specified key. The passed CachedPlanSource should be "unsaved"
388 * in case we get an error here; we'll save it once we've created the hash
400 /* Initialize the hash table, if necessary */
404 /* Add entry to hash table */
410 /* Shouldn't get a duplicate entry */
413 (
errcode(ERRCODE_DUPLICATE_PSTATEMENT),
414 errmsg(
"prepared statement \"%s\" already exists",
417 /* Fill in the hash table entry */
422 /* Now it's safe to move the CachedPlanSource to permanent memory */
427 * Lookup an existing query in the hash table. If the query does not
428 * actually exist, throw ereport(ERROR) or return NULL per second parameter.
430 * Note: this does not force the referenced plancache entry to be valid,
431 * since not all callers care.
439 * If the hash table hasn't been initialized, it can't be storing
440 * anything, therefore it couldn't possibly store our plan.
450 if (!entry && throwError)
452 (
errcode(ERRCODE_UNDEFINED_PSTATEMENT),
453 errmsg(
"prepared statement \"%s\" does not exist",
460 * Given a prepared statement, determine the result tupledesc it will
461 * produce. Returns NULL if the execution will not return tuples.
463 * Note: the result is created or copied into current memory context.
469 * Since we don't allow prepared statements' result tupdescs to change,
470 * there's no need to worry about revalidating the cached plan here.
473 if (
stmt->plansource->resultDesc)
480 * Given a prepared statement that returns tuples, extract the query
481 * targetlist. Returns NIL if the statement doesn't have a determinable
484 * Note: this is pretty ugly, but since it's only used in corner cases like
485 * Describe Statement on an EXECUTE command, we don't worry too much about
493 /* Get the plan's primary targetlist */
496 /* Copy into caller's context in case plan gets invalidated */
501 * Implements the 'DEALLOCATE' utility statement: deletes the
502 * specified plan from storage.
514 * Internal version of DEALLOCATE
516 * If showError is false, dropping a nonexistent statement is a no-op.
523 /* Find the query's hash table entry; raise error if wanted */
528 /* Release the plancache entry */
531 /* Now we can remove the hash table entry */
537 * Drop all cached statements.
549 /* walk over cache */
553 /* Release the plancache entry */
556 /* Now we can remove the hash table entry */
562 * Implements the 'EXPLAIN EXECUTE' utility statement.
564 * "into" is NULL unless we are doing EXPLAIN CREATE TABLE AS EXECUTE,
565 * in which case executing the query should result in creating that table.
567 * Note: the passed-in pstate's queryString is that of the EXPLAIN EXECUTE,
568 * not the original PREPARE; we get the latter string from the plancache.
575 const char *query_string;
591 /* See ExplainOneQuery about this */
594 "explain analyze planner context",
603 /* Look it up in the hash table */
606 /* Shouldn't find a non-fixed-result cached plan */
608 elog(
ERROR,
"EXPLAIN EXECUTE does not support variable-result cached plans");
612 /* Evaluate parameters, if any */
621 * Need an EState to evaluate parameters; must not delete it till end
622 * of query, in case parameters are pass-by-reference. Note that the
623 * passed-in "params" could possibly be referenced in the parameter
632 /* Replan if needed, and acquire a transient refcount */
645 /* calc differences of buffer counters. */
654 /* Explain each query */
655 foreach(p, plan_list)
661 &planduration, (es->
buffers ? &bufusage : NULL),
662 es->
memory ? &mem_counters : NULL);
666 /* No need for CommandCounterIncrement, as ExplainOnePlan did it */
668 /* Separate plans with an appropriate separator */
669 if (
lnext(plan_list, p) != NULL)
680 * This set returning function reads all the prepared statements and
681 * returns a set of (name, statement, prepare_time, param_types, from_sql,
682 * generic_plans, custom_plans).
690 * We put all the tuples into a tuplestore in one scan of the hashtable.
691 * This avoids any issue of the hashtable possibly changing between calls.
695 /* hash table might be uninitialized */
720 for (
int i = 0;
i < result_desc->
natts;
i++)
726 /* no result descriptor (for example, DML statement) */
742 * This utility function takes a C array of Oids, and returns a Datum
743 * pointing to a one-dimensional Postgres array of regtypes. An empty
744 * array is returned as a zero-element array, not NULL.
755 for (
i = 0;
i < num_params;
i++)
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
static HTAB * prepared_queries
void DropPreparedStatement(const char *stmt_name, bool showError)
void PrepareQuery(ParseState *pstate, PrepareStmt *stmt, int stmt_location, int stmt_len)
static void InitQueryHashTable(void)
TupleDesc FetchPreparedStatementResultDesc(PreparedStatement *stmt)
Datum pg_prepared_statement(PG_FUNCTION_ARGS)
PreparedStatement * FetchPreparedStatement(const char *stmt_name, bool throwError)
void ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es, ParseState *pstate, ParamListInfo params)
static ParamListInfo EvaluateParams(ParseState *pstate, PreparedStatement *pstmt, List *params, EState *estate)
void StorePreparedStatement(const char *stmt_name, CachedPlanSource *plansource, bool from_sql)
static Datum build_regtype_array(Oid *param_types, int num_params)
void ExecuteQuery(ParseState *pstate, ExecuteStmt *stmt, IntoClause *intoClause, ParamListInfo params, DestReceiver *dest, QueryCompletion *qc)
void DropAllPreparedStatements(void)
List * FetchPreparedStatementTargetList(PreparedStatement *stmt)
void DeallocateQuery(DeallocateStmt *stmt)
static Datum values[MAXATTR]
#define CStringGetTextDatum(s)
int GetIntoRelEFlags(IntoClause *intoClause)
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
void * hash_seq_search(HASH_SEQ_STATUS *status)
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
int errdetail(const char *fmt,...)
int errhint(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
List * ExecPrepareExprList(List *nodes, EState *estate)
void FreeExecutorState(EState *estate)
EState * CreateExecutorState(void)
#define GetPerTupleExprContext(estate)
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
void ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv, const instr_time *planduration, const BufferUsage *bufusage, const MemoryContextCounters *mem_counters)
void ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es, ParseState *pstate, ParamListInfo params)
#define palloc_array(type, count)
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
Assert(PointerIsAligned(start, uint64))
#define INSTR_TIME_SET_CURRENT(t)
#define INSTR_TIME_SUBTRACT(x, y)
BufferUsage pgBufferUsage
void BufferUsageAccumDiff(BufferUsage *dst, const BufferUsage *add, const BufferUsage *sub)
char * MemoryContextStrdup(MemoryContext context, const char *string)
void MemoryContextMemConsumed(MemoryContext context, MemoryContextCounters *consumed)
MemoryContext CurrentMemoryContext
#define AllocSetContextCreate
#define ALLOCSET_DEFAULT_SIZES
Oid exprType(const Node *expr)
int exprLocation(const Node *expr)
#define IsA(nodeptr, _type_)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
ParamListInfo makeParamList(int numParams)
Node * coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location)
void assign_expr_collations(ParseState *pstate, Node *expr)
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
int parser_errposition(ParseState *pstate, int location)
ParseState * make_parsestate(ParseState *parentParseState)
@ EXPR_KIND_EXECUTE_PARAMETER
Oid typenameTypeId(ParseState *pstate, const TypeName *typeName)
#define CURSOR_OPT_PARALLEL_OK
#define lfirst_node(type, lc)
static int list_length(const List *l)
#define linitial_node(type, l)
static ListCell * lnext(const List *l, const ListCell *c)
void DropCachedPlan(CachedPlanSource *plansource)
void SaveCachedPlan(CachedPlanSource *plansource)
void CompleteCachedPlan(CachedPlanSource *plansource, List *querytree_list, MemoryContext querytree_context, Oid *param_types, int num_params, ParserSetupHook parserSetup, void *parserSetupArg, int cursor_options, bool fixed_result)
CachedPlan * GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams, ResourceOwner owner, QueryEnvironment *queryEnv)
CachedPlanSource * CreateCachedPlan(RawStmt *raw_parse_tree, const char *query_string, CommandTag commandTag)
List * CachedPlanGetTargetList(CachedPlanSource *plansource, QueryEnvironment *queryEnv)
void ReleaseCachedPlan(CachedPlan *plan, ResourceOwner owner)
Portal CreateNewPortal(void)
void PortalDrop(Portal portal, bool isTopCommit)
void PortalDefineQuery(Portal portal, const char *prepStmtName, const char *sourceText, CommandTag commandTag, List *stmts, CachedPlan *cplan)
List * pg_analyze_and_rewrite_varparams(RawStmt *parsetree, const char *query_string, Oid **paramTypes, int *numParams, QueryEnvironment *queryEnv)
#define Int64GetDatumFast(X)
static Datum PointerGetDatum(const void *X)
static Datum BoolGetDatum(bool X)
static Datum ObjectIdGetDatum(Oid X)
void PortalStart(Portal portal, ParamListInfo params, int eflags, Snapshot snapshot)
bool PortalRun(Portal portal, long count, bool isTopLevel, DestReceiver *dest, DestReceiver *altdest, QueryCompletion *qc)
ResourceOwner CurrentResourceOwner
Snapshot GetActiveSnapshot(void)
const char * query_string
ParamListInfo es_param_list_info
ParamExternData params[FLEXIBLE_ARRAY_MEMBER]
QueryEnvironment * p_queryEnv
const char * p_sourcetext
MemoryContext portalContext
char stmt_name[NAMEDATALEN]
CachedPlanSource * plansource
Tuplestorestate * setResult
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
CommandTag CreateCommandTag(Node *parsetree)
static Datum TimestampTzGetDatum(TimestampTz X)
TimestampTz GetCurrentStatementStartTimestamp(void)