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 *-------------------------------------------------------------------------
21 #include "access/xact.h"
22 #include "catalog/pg_type.h"
23 #include "commands/createas.h"
24 #include "commands/prepare.h"
26 #include "nodes/nodeFuncs.h"
27 #include "parser/parse_coerce.h"
28 #include "parser/parse_collate.h"
29 #include "parser/parse_expr.h"
30 #include "parser/parse_type.h"
31 #include "tcop/pquery.h"
32 #include "tcop/utility.h"
33 #include "utils/builtins.h"
34 #include "utils/snapmgr.h"
35 #include "utils/timestamp.h"
39 * The hash table in which prepared queries are stored. This is
40 * per-backend: query plans are not shared between backends.
41 * The keys for this hash table are the arguments to PREPARE and EXECUTE
42 * (statement names); the entries are PreparedStatement structs.
44 static HTAB
*prepared_queries
= NULL
;
46 static void InitQueryHashTable(void);
47 static ParamListInfo
EvaluateParams(ParseState
*pstate
,
48 PreparedStatement
*pstmt
, List
*params
,
50 static Datum
build_regtype_array(Oid
*param_types
, int num_params
);
53 * Implements the 'PREPARE' utility statement.
56 PrepareQuery(ParseState
*pstate
, PrepareStmt
*stmt
,
57 int stmt_location
, int stmt_len
)
60 CachedPlanSource
*plansource
;
66 * Disallow empty-string statement name (conflicts with protocol-level
69 if (!stmt
->name
|| stmt
->name
[0] == '\0')
71 (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION
),
72 errmsg("invalid statement name: must not be empty")));
75 * Need to wrap the contained statement in a RawStmt node to pass it to
78 rawstmt
= makeNode(RawStmt
);
79 rawstmt
->stmt
= stmt
->query
;
80 rawstmt
->stmt_location
= stmt_location
;
81 rawstmt
->stmt_len
= stmt_len
;
84 * Create the CachedPlanSource before we do parse analysis, since it needs
85 * to see the unmodified raw parse tree.
87 plansource
= CreateCachedPlan(rawstmt
, pstate
->p_sourcetext
,
88 CreateCommandTag(stmt
->query
));
90 /* Transform list of TypeNames to array of type OIDs */
91 nargs
= list_length(stmt
->argtypes
);
98 argtypes
= palloc_array(Oid
, nargs
);
101 foreach(l
, stmt
->argtypes
)
103 TypeName
*tn
= lfirst(l
);
104 Oid toid
= typenameTypeId(pstate
, tn
);
106 argtypes
[i
++] = toid
;
111 * Analyze the statement using these parameter types (any parameters
112 * passed in from above us will not be visible to it), allowing
113 * information about unknown parameters to be deduced from context.
114 * Rewrite the query. The result could be 0, 1, or many queries.
116 query_list
= pg_analyze_and_rewrite_varparams(rawstmt
, pstate
->p_sourcetext
,
117 &argtypes
, &nargs
, NULL
);
119 /* Finish filling in the CachedPlanSource */
120 CompleteCachedPlan(plansource
,
127 CURSOR_OPT_PARALLEL_OK
, /* allow parallel mode */
128 true); /* fixed result */
133 StorePreparedStatement(stmt
->name
,
139 * ExecuteQuery --- implement the 'EXECUTE' utility statement.
141 * This code also supports CREATE TABLE ... AS EXECUTE. That case is
142 * indicated by passing a non-null intoClause. The DestReceiver is already
143 * set up correctly for CREATE TABLE AS, but we still have to make a few
144 * other adjustments here.
147 ExecuteQuery(ParseState
*pstate
,
148 ExecuteStmt
*stmt
, IntoClause
*intoClause
,
149 ParamListInfo params
,
150 DestReceiver
*dest
, QueryCompletion
*qc
)
152 PreparedStatement
*entry
;
155 ParamListInfo paramLI
= NULL
;
156 EState
*estate
= NULL
;
162 /* Look it up in the hash table */
163 entry
= FetchPreparedStatement(stmt
->name
, true);
165 /* Shouldn't find a non-fixed-result cached plan */
166 if (!entry
->plansource
->fixed_result
)
167 elog(ERROR
, "EXECUTE does not support variable-result cached plans");
169 /* Evaluate parameters, if any */
170 if (entry
->plansource
->num_params
> 0)
173 * Need an EState to evaluate parameters; must not delete it till end
174 * of query, in case parameters are pass-by-reference. Note that the
175 * passed-in "params" could possibly be referenced in the parameter
178 estate
= CreateExecutorState();
179 estate
->es_param_list_info
= params
;
180 paramLI
= EvaluateParams(pstate
, entry
, stmt
->params
, estate
);
183 /* Create a new portal to run the query in */
184 portal
= CreateNewPortal();
185 /* Don't display the portal in pg_cursors, it is for internal use only */
186 portal
->visible
= false;
188 /* Copy the plan's saved query string into the portal's memory */
189 query_string
= MemoryContextStrdup(portal
->portalContext
,
190 entry
->plansource
->query_string
);
192 /* Replan if needed, and increment plan refcount for portal */
193 cplan
= GetCachedPlan(entry
->plansource
, paramLI
, NULL
, NULL
);
194 plan_list
= cplan
->stmt_list
;
197 * DO NOT add any logic that could possibly throw an error between
198 * GetCachedPlan and PortalDefineQuery, or you'll leak the plan refcount.
200 PortalDefineQuery(portal
,
203 entry
->plansource
->commandTag
,
208 * For CREATE TABLE ... AS EXECUTE, we must verify that the prepared
209 * statement is one that produces tuples. Currently we insist that it be
210 * a plain old SELECT. In future we might consider supporting other
211 * things such as INSERT ... RETURNING, but there are a couple of issues
212 * to be settled first, notably how WITH NO DATA should be handled in such
213 * a case (do we really want to suppress execution?) and how to pass down
214 * the OID-determining eflags (PortalStart won't handle them in such a
215 * case, and for that matter it's not clear the executor will either).
217 * For CREATE TABLE ... AS EXECUTE, we also have to ensure that the proper
218 * eflags and fetch count are passed to PortalStart/PortalRun.
224 if (list_length(plan_list
) != 1)
226 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
227 errmsg("prepared statement is not a SELECT")));
228 pstmt
= linitial_node(PlannedStmt
, plan_list
);
229 if (pstmt
->commandType
!= CMD_SELECT
)
231 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
232 errmsg("prepared statement is not a SELECT")));
234 /* Set appropriate eflags */
235 eflags
= GetIntoRelEFlags(intoClause
);
237 /* And tell PortalRun whether to run to completion or not */
238 if (intoClause
->skipData
)
245 /* Plain old EXECUTE */
251 * Run the portal as appropriate.
253 PortalStart(portal
, paramLI
, eflags
, GetActiveSnapshot());
255 (void) PortalRun(portal
, count
, false, dest
, dest
, qc
);
257 PortalDrop(portal
, false);
260 FreeExecutorState(estate
);
262 /* No need to pfree other memory, MemoryContext will be reset */
266 * EvaluateParams: evaluate a list of parameters.
268 * pstate: parse state
269 * pstmt: statement we are getting parameters for.
270 * params: list of given parameter expressions (raw parser output!)
271 * estate: executor state to use.
273 * Returns a filled-in ParamListInfo -- this can later be passed to
274 * CreateQueryDesc(), which allows the executor to make use of the parameters
275 * during query execution.
278 EvaluateParams(ParseState
*pstate
, PreparedStatement
*pstmt
, List
*params
,
281 Oid
*param_types
= pstmt
->plansource
->param_types
;
282 int num_params
= pstmt
->plansource
->num_params
;
283 int nparams
= list_length(params
);
284 ParamListInfo paramLI
;
289 if (nparams
!= num_params
)
291 (errcode(ERRCODE_SYNTAX_ERROR
),
292 errmsg("wrong number of parameters for prepared statement \"%s\"",
294 errdetail("Expected %d parameters but got %d.",
295 num_params
, nparams
)));
297 /* Quick exit if no parameters */
302 * We have to run parse analysis for the expressions. Since the parser is
303 * not cool about scribbling on its input, copy first.
305 params
= copyObject(params
);
310 Node
*expr
= lfirst(l
);
311 Oid expected_type_id
= param_types
[i
];
314 expr
= transformExpr(pstate
, expr
, EXPR_KIND_EXECUTE_PARAMETER
);
316 given_type_id
= exprType(expr
);
318 expr
= coerce_to_target_type(pstate
, expr
, given_type_id
,
319 expected_type_id
, -1,
321 COERCE_IMPLICIT_CAST
,
326 (errcode(ERRCODE_DATATYPE_MISMATCH
),
327 errmsg("parameter $%d of type %s cannot be coerced to the expected type %s",
329 format_type_be(given_type_id
),
330 format_type_be(expected_type_id
)),
331 errhint("You will need to rewrite or cast the expression."),
332 parser_errposition(pstate
, exprLocation(lfirst(l
)))));
334 /* Take care of collations in the finished expression. */
335 assign_expr_collations(pstate
, expr
);
341 /* Prepare the expressions for execution */
342 exprstates
= ExecPrepareExprList(params
, estate
);
344 paramLI
= makeParamList(num_params
);
347 foreach(l
, exprstates
)
349 ExprState
*n
= (ExprState
*) lfirst(l
);
350 ParamExternData
*prm
= ¶mLI
->params
[i
];
352 prm
->ptype
= param_types
[i
];
353 prm
->pflags
= PARAM_FLAG_CONST
;
354 prm
->value
= ExecEvalExprSwitchContext(n
,
355 GetPerTupleExprContext(estate
),
366 * Initialize query hash table upon first use.
369 InitQueryHashTable(void)
373 hash_ctl
.keysize
= NAMEDATALEN
;
374 hash_ctl
.entrysize
= sizeof(PreparedStatement
);
376 prepared_queries
= hash_create("Prepared Queries",
379 HASH_ELEM
| HASH_STRINGS
);
383 * Store all the data pertaining to a query in the hash table using
384 * the specified key. The passed CachedPlanSource should be "unsaved"
385 * in case we get an error here; we'll save it once we've created the hash
389 StorePreparedStatement(const char *stmt_name
,
390 CachedPlanSource
*plansource
,
393 PreparedStatement
*entry
;
394 TimestampTz cur_ts
= GetCurrentStatementStartTimestamp();
397 /* Initialize the hash table, if necessary */
398 if (!prepared_queries
)
399 InitQueryHashTable();
401 /* Add entry to hash table */
402 entry
= (PreparedStatement
*) hash_search(prepared_queries
,
407 /* Shouldn't get a duplicate entry */
410 (errcode(ERRCODE_DUPLICATE_PSTATEMENT
),
411 errmsg("prepared statement \"%s\" already exists",
414 /* Fill in the hash table entry */
415 entry
->plansource
= plansource
;
416 entry
->from_sql
= from_sql
;
417 entry
->prepare_time
= cur_ts
;
419 /* Now it's safe to move the CachedPlanSource to permanent memory */
420 SaveCachedPlan(plansource
);
424 * Lookup an existing query in the hash table. If the query does not
425 * actually exist, throw ereport(ERROR) or return NULL per second parameter.
427 * Note: this does not force the referenced plancache entry to be valid,
428 * since not all callers care.
431 FetchPreparedStatement(const char *stmt_name
, bool throwError
)
433 PreparedStatement
*entry
;
436 * If the hash table hasn't been initialized, it can't be storing
437 * anything, therefore it couldn't possibly store our plan.
439 if (prepared_queries
)
440 entry
= (PreparedStatement
*) hash_search(prepared_queries
,
447 if (!entry
&& throwError
)
449 (errcode(ERRCODE_UNDEFINED_PSTATEMENT
),
450 errmsg("prepared statement \"%s\" does not exist",
457 * Given a prepared statement, determine the result tupledesc it will
458 * produce. Returns NULL if the execution will not return tuples.
460 * Note: the result is created or copied into current memory context.
463 FetchPreparedStatementResultDesc(PreparedStatement
*stmt
)
466 * Since we don't allow prepared statements' result tupdescs to change,
467 * there's no need to worry about revalidating the cached plan here.
469 Assert(stmt
->plansource
->fixed_result
);
470 if (stmt
->plansource
->resultDesc
)
471 return CreateTupleDescCopy(stmt
->plansource
->resultDesc
);
477 * Given a prepared statement that returns tuples, extract the query
478 * targetlist. Returns NIL if the statement doesn't have a determinable
481 * Note: this is pretty ugly, but since it's only used in corner cases like
482 * Describe Statement on an EXECUTE command, we don't worry too much about
486 FetchPreparedStatementTargetList(PreparedStatement
*stmt
)
490 /* Get the plan's primary targetlist */
491 tlist
= CachedPlanGetTargetList(stmt
->plansource
, NULL
);
493 /* Copy into caller's context in case plan gets invalidated */
494 return copyObject(tlist
);
498 * Implements the 'DEALLOCATE' utility statement: deletes the
499 * specified plan from storage.
502 DeallocateQuery(DeallocateStmt
*stmt
)
505 DropPreparedStatement(stmt
->name
, true);
507 DropAllPreparedStatements();
511 * Internal version of DEALLOCATE
513 * If showError is false, dropping a nonexistent statement is a no-op.
516 DropPreparedStatement(const char *stmt_name
, bool showError
)
518 PreparedStatement
*entry
;
520 /* Find the query's hash table entry; raise error if wanted */
521 entry
= FetchPreparedStatement(stmt_name
, showError
);
525 /* Release the plancache entry */
526 DropCachedPlan(entry
->plansource
);
528 /* Now we can remove the hash table entry */
529 hash_search(prepared_queries
, entry
->stmt_name
, HASH_REMOVE
, NULL
);
534 * Drop all cached statements.
537 DropAllPreparedStatements(void)
540 PreparedStatement
*entry
;
543 if (!prepared_queries
)
546 /* walk over cache */
547 hash_seq_init(&seq
, prepared_queries
);
548 while ((entry
= hash_seq_search(&seq
)) != NULL
)
550 /* Release the plancache entry */
551 DropCachedPlan(entry
->plansource
);
553 /* Now we can remove the hash table entry */
554 hash_search(prepared_queries
, entry
->stmt_name
, HASH_REMOVE
, NULL
);
559 * Implements the 'EXPLAIN EXECUTE' utility statement.
561 * "into" is NULL unless we are doing EXPLAIN CREATE TABLE AS EXECUTE,
562 * in which case executing the query should result in creating that table.
564 * Note: the passed-in pstate's queryString is that of the EXPLAIN EXECUTE,
565 * not the original PREPARE; we get the latter string from the plancache.
568 ExplainExecuteQuery(ExecuteStmt
*execstmt
, IntoClause
*into
, ExplainState
*es
,
569 ParseState
*pstate
, ParamListInfo params
)
571 PreparedStatement
*entry
;
572 const char *query_string
;
576 ParamListInfo paramLI
= NULL
;
577 EState
*estate
= NULL
;
578 instr_time planstart
;
579 instr_time planduration
;
580 BufferUsage bufusage_start
,
582 MemoryContextCounters mem_counters
;
583 MemoryContext planner_ctx
= NULL
;
584 MemoryContext saved_ctx
= NULL
;
588 /* See ExplainOneQuery about this */
589 Assert(IsA(CurrentMemoryContext
, AllocSetContext
));
590 planner_ctx
= AllocSetContextCreate(CurrentMemoryContext
,
591 "explain analyze planner context",
592 ALLOCSET_DEFAULT_SIZES
);
593 saved_ctx
= MemoryContextSwitchTo(planner_ctx
);
597 bufusage_start
= pgBufferUsage
;
598 INSTR_TIME_SET_CURRENT(planstart
);
600 /* Look it up in the hash table */
601 entry
= FetchPreparedStatement(execstmt
->name
, true);
603 /* Shouldn't find a non-fixed-result cached plan */
604 if (!entry
->plansource
->fixed_result
)
605 elog(ERROR
, "EXPLAIN EXECUTE does not support variable-result cached plans");
607 query_string
= entry
->plansource
->query_string
;
609 /* Evaluate parameters, if any */
610 if (entry
->plansource
->num_params
)
612 ParseState
*pstate_params
;
614 pstate_params
= make_parsestate(NULL
);
615 pstate_params
->p_sourcetext
= pstate
->p_sourcetext
;
618 * Need an EState to evaluate parameters; must not delete it till end
619 * of query, in case parameters are pass-by-reference. Note that the
620 * passed-in "params" could possibly be referenced in the parameter
623 estate
= CreateExecutorState();
624 estate
->es_param_list_info
= params
;
626 paramLI
= EvaluateParams(pstate_params
, entry
, execstmt
->params
, estate
);
629 /* Replan if needed, and acquire a transient refcount */
630 cplan
= GetCachedPlan(entry
->plansource
, paramLI
,
631 CurrentResourceOwner
, pstate
->p_queryEnv
);
633 INSTR_TIME_SET_CURRENT(planduration
);
634 INSTR_TIME_SUBTRACT(planduration
, planstart
);
638 MemoryContextSwitchTo(saved_ctx
);
639 MemoryContextMemConsumed(planner_ctx
, &mem_counters
);
642 /* calc differences of buffer counters. */
645 memset(&bufusage
, 0, sizeof(BufferUsage
));
646 BufferUsageAccumDiff(&bufusage
, &pgBufferUsage
, &bufusage_start
);
649 plan_list
= cplan
->stmt_list
;
651 /* Explain each query */
652 foreach(p
, plan_list
)
654 PlannedStmt
*pstmt
= lfirst_node(PlannedStmt
, p
);
656 if (pstmt
->commandType
!= CMD_UTILITY
)
657 ExplainOnePlan(pstmt
, into
, es
, query_string
, paramLI
, pstate
->p_queryEnv
,
658 &planduration
, (es
->buffers
? &bufusage
: NULL
),
659 es
->memory
? &mem_counters
: NULL
);
661 ExplainOneUtility(pstmt
->utilityStmt
, into
, es
, pstate
, paramLI
);
663 /* No need for CommandCounterIncrement, as ExplainOnePlan did it */
665 /* Separate plans with an appropriate separator */
666 if (lnext(plan_list
, p
) != NULL
)
667 ExplainSeparatePlans(es
);
671 FreeExecutorState(estate
);
673 ReleaseCachedPlan(cplan
, CurrentResourceOwner
);
677 * This set returning function reads all the prepared statements and
678 * returns a set of (name, statement, prepare_time, param_types, from_sql,
679 * generic_plans, custom_plans).
682 pg_prepared_statement(PG_FUNCTION_ARGS
)
684 ReturnSetInfo
*rsinfo
= (ReturnSetInfo
*) fcinfo
->resultinfo
;
687 * We put all the tuples into a tuplestore in one scan of the hashtable.
688 * This avoids any issue of the hashtable possibly changing between calls.
690 InitMaterializedSRF(fcinfo
, 0);
692 /* hash table might be uninitialized */
693 if (prepared_queries
)
695 HASH_SEQ_STATUS hash_seq
;
696 PreparedStatement
*prep_stmt
;
698 hash_seq_init(&hash_seq
, prepared_queries
);
699 while ((prep_stmt
= hash_seq_search(&hash_seq
)) != NULL
)
701 TupleDesc result_desc
;
705 result_desc
= prep_stmt
->plansource
->resultDesc
;
707 values
[0] = CStringGetTextDatum(prep_stmt
->stmt_name
);
708 values
[1] = CStringGetTextDatum(prep_stmt
->plansource
->query_string
);
709 values
[2] = TimestampTzGetDatum(prep_stmt
->prepare_time
);
710 values
[3] = build_regtype_array(prep_stmt
->plansource
->param_types
,
711 prep_stmt
->plansource
->num_params
);
716 result_types
= palloc_array(Oid
, result_desc
->natts
);
717 for (int i
= 0; i
< result_desc
->natts
; i
++)
718 result_types
[i
] = TupleDescAttr(result_desc
, i
)->atttypid
;
719 values
[4] = build_regtype_array(result_types
, result_desc
->natts
);
723 /* no result descriptor (for example, DML statement) */
726 values
[5] = BoolGetDatum(prep_stmt
->from_sql
);
727 values
[6] = Int64GetDatumFast(prep_stmt
->plansource
->num_generic_plans
);
728 values
[7] = Int64GetDatumFast(prep_stmt
->plansource
->num_custom_plans
);
730 tuplestore_putvalues(rsinfo
->setResult
, rsinfo
->setDesc
,
739 * This utility function takes a C array of Oids, and returns a Datum
740 * pointing to a one-dimensional Postgres array of regtypes. An empty
741 * array is returned as a zero-element array, not NULL.
744 build_regtype_array(Oid
*param_types
, int num_params
)
750 tmp_ary
= palloc_array(Datum
, num_params
);
752 for (i
= 0; i
< num_params
; i
++)
753 tmp_ary
[i
] = ObjectIdGetDatum(param_types
[i
]);
755 result
= construct_array_builtin(tmp_ary
, num_params
, REGTYPEOID
);
756 return PointerGetDatum(result
);