Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * prepare.c
4 : * Prepareable SQL statements via PREPARE, EXECUTE and DEALLOCATE
5 : *
6 : * This module also implements storage of prepared statements that are
7 : * accessed via the extended FE/BE query protocol.
8 : *
9 : *
10 : * Copyright (c) 2002-2023, PostgreSQL Global Development Group
11 : *
12 : * IDENTIFICATION
13 : * src/backend/commands/prepare.c
14 : *
15 : *-------------------------------------------------------------------------
16 : */
17 : #include "postgres.h"
18 :
19 : #include <limits.h>
20 :
21 : #include "access/xact.h"
22 : #include "catalog/pg_type.h"
23 : #include "commands/createas.h"
24 : #include "commands/prepare.h"
25 : #include "funcapi.h"
26 : #include "miscadmin.h"
27 : #include "nodes/nodeFuncs.h"
28 : #include "parser/analyze.h"
29 : #include "parser/parse_coerce.h"
30 : #include "parser/parse_collate.h"
31 : #include "parser/parse_expr.h"
32 : #include "parser/parse_type.h"
33 : #include "rewrite/rewriteHandler.h"
34 : #include "tcop/pquery.h"
35 : #include "tcop/utility.h"
36 : #include "utils/builtins.h"
37 : #include "utils/snapmgr.h"
38 : #include "utils/timestamp.h"
39 :
40 :
41 : /*
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.
46 : */
47 : static HTAB *prepared_queries = NULL;
48 :
49 : static void InitQueryHashTable(void);
50 : static ParamListInfo EvaluateParams(ParseState *pstate,
51 : PreparedStatement *pstmt, List *params,
52 : EState *estate);
53 : static Datum build_regtype_array(Oid *param_types, int num_params);
54 :
55 : /*
56 : * Implements the 'PREPARE' utility statement.
57 : */
58 : void
1191 peter 59 CBC 813 : PrepareQuery(ParseState *pstate, PrepareStmt *stmt,
60 : int stmt_location, int stmt_len)
61 : {
62 : RawStmt *rawstmt;
63 : CachedPlanSource *plansource;
5871 tgl 64 813 : Oid *argtypes = NULL;
65 : int nargs;
66 : List *query_list;
67 :
68 : /*
69 : * Disallow empty-string statement name (conflicts with protocol-level
70 : * unnamed statement).
71 : */
7279 72 813 : if (!stmt->name || stmt->name[0] == '\0')
7203 tgl 73 UBC 0 : ereport(ERROR,
74 : (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
75 : errmsg("invalid statement name: must not be empty")));
76 :
77 : /*
78 : * Need to wrap the contained statement in a RawStmt node to pass it to
79 : * parse analysis.
80 : */
2276 tgl 81 CBC 813 : rawstmt = makeNode(RawStmt);
660 82 813 : rawstmt->stmt = stmt->query;
2276 83 813 : rawstmt->stmt_location = stmt_location;
84 813 : rawstmt->stmt_len = stmt_len;
85 :
86 : /*
87 : * Create the CachedPlanSource before we do parse analysis, since it needs
88 : * to see the unmodified raw parse tree.
89 : */
1191 peter 90 813 : plansource = CreateCachedPlan(rawstmt, pstate->p_sourcetext,
91 : CreateCommandTag(stmt->query));
92 :
93 : /* Transform list of TypeNames to array of type OIDs */
5871 tgl 94 813 : nargs = list_length(stmt->argtypes);
95 :
96 813 : if (nargs)
97 : {
98 : int i;
99 : ListCell *l;
100 :
209 peter 101 GNC 686 : argtypes = palloc_array(Oid, nargs);
5871 tgl 102 CBC 686 : i = 0;
103 :
104 1446 : foreach(l, stmt->argtypes)
105 : {
106 763 : TypeName *tn = lfirst(l);
4549 peter_e 107 763 : Oid toid = typenameTypeId(pstate, tn);
108 :
5871 tgl 109 760 : argtypes[i++] = toid;
110 : }
111 : }
112 :
113 : /*
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.
118 : */
401 peter 119 810 : query_list = pg_analyze_and_rewrite_varparams(rawstmt, pstate->p_sourcetext,
120 : &argtypes, &nargs, NULL);
121 :
122 : /* Finish filling in the CachedPlanSource */
4223 tgl 123 810 : CompleteCachedPlan(plansource,
124 : query_list,
125 : NULL,
126 : argtypes,
127 : nargs,
128 : NULL,
129 : NULL,
130 : CURSOR_OPT_PARALLEL_OK, /* allow parallel mode */
131 : true); /* fixed result */
132 :
133 : /*
134 : * Save the results.
135 : */
7279 136 810 : StorePreparedStatement(stmt->name,
137 : plansource,
138 : true);
7530 139 807 : }
140 :
141 : /*
142 : * ExecuteQuery --- implement the 'EXECUTE' utility statement.
143 : *
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.
148 : */
149 : void
1191 peter 150 4619 : ExecuteQuery(ParseState *pstate,
151 : ExecuteStmt *stmt, IntoClause *intoClause,
152 : ParamListInfo params,
153 : DestReceiver *dest, QueryCompletion *qc)
154 : {
155 : PreparedStatement *entry;
156 : CachedPlan *cplan;
157 : List *plan_list;
7530 tgl 158 4619 : ParamListInfo paramLI = NULL;
7371 159 4619 : EState *estate = NULL;
160 : Portal portal;
161 : char *query_string;
162 : int eflags;
163 : long count;
164 :
165 : /* Look it up in the hash table */
7279 166 4619 : entry = FetchPreparedStatement(stmt->name, true);
167 :
168 : /* Shouldn't find a non-fixed-result cached plan */
5871 169 4619 : if (!entry->plansource->fixed_result)
5871 tgl 170 UBC 0 : elog(ERROR, "EXECUTE does not support variable-result cached plans");
171 :
172 : /* Evaluate parameters, if any */
5871 tgl 173 CBC 4619 : if (entry->plansource->num_params > 0)
174 : {
175 : /*
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
179 : * expressions.
180 : */
7371 181 4169 : estate = CreateExecutorState();
6340 182 4169 : estate->es_param_list_info = params;
1191 peter 183 4169 : paramLI = EvaluateParams(pstate, entry, stmt->params, estate);
184 : }
185 :
186 : /* Create a new portal to run the query in */
7282 tgl 187 4601 : portal = CreateNewPortal();
188 : /* Don't display the portal in pg_cursors, it is for internal use only */
6290 neilc 189 4601 : portal->visible = false;
190 :
191 : /* Copy the plan's saved query string into the portal's memory */
1940 peter_e 192 4601 : query_string = MemoryContextStrdup(portal->portalContext,
5378 tgl 193 4601 : entry->plansource->query_string);
194 :
195 : /* Replan if needed, and increment plan refcount for portal */
804 196 4601 : cplan = GetCachedPlan(entry->plansource, paramLI, NULL, NULL);
4038 197 4586 : plan_list = cplan->stmt_list;
198 :
199 : /*
200 : * DO NOT add any logic that could possibly throw an error between
201 : * GetCachedPlan and PortalDefineQuery, or you'll leak the plan refcount.
202 : */
662 203 4586 : PortalDefineQuery(portal,
204 : NULL,
205 : query_string,
206 4586 : entry->plansource->commandTag,
207 : plan_list,
208 : cplan);
209 :
210 : /*
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).
219 : *
220 : * For CREATE TABLE ... AS EXECUTE, we also have to ensure that the proper
221 : * eflags and fetch count are passed to PortalStart/PortalRun.
222 : */
4038 223 4586 : if (intoClause)
224 : {
225 : PlannedStmt *pstmt;
226 :
5892 227 21 : if (list_length(plan_list) != 1)
7203 tgl 228 UBC 0 : ereport(ERROR,
229 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
230 : errmsg("prepared statement is not a SELECT")));
2190 tgl 231 CBC 21 : pstmt = linitial_node(PlannedStmt, plan_list);
2276 232 21 : if (pstmt->commandType != CMD_SELECT)
7203 tgl 233 UBC 0 : ereport(ERROR,
234 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
235 : errmsg("prepared statement is not a SELECT")));
236 :
237 : /* Set appropriate eflags */
4038 tgl 238 CBC 21 : eflags = GetIntoRelEFlags(intoClause);
239 :
240 : /* And tell PortalRun whether to run to completion or not */
241 21 : if (intoClause->skipData)
242 6 : count = 0;
243 : else
244 15 : count = FETCH_ALL;
245 : }
246 : else
247 : {
248 : /* Plain old EXECUTE */
249 4565 : eflags = 0;
250 4565 : count = FETCH_ALL;
251 : }
252 :
253 : /*
254 : * Run the portal as appropriate.
255 : */
3786 256 4586 : PortalStart(portal, paramLI, eflags, GetActiveSnapshot());
257 :
1133 alvherre 258 4586 : (void) PortalRun(portal, count, false, true, dest, dest, qc);
259 :
7282 tgl 260 4570 : PortalDrop(portal, false);
261 :
7371 262 4570 : if (estate)
263 4133 : FreeExecutorState(estate);
264 :
265 : /* No need to pfree other memory, MemoryContext will be reset */
7530 266 4570 : }
267 :
268 : /*
269 : * EvaluateParams: evaluate a list of parameters.
270 : *
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.
275 : *
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.
279 : */
280 : static ParamListInfo
1191 peter 281 4288 : EvaluateParams(ParseState *pstate, PreparedStatement *pstmt, List *params,
282 : EState *estate)
283 : {
5871 tgl 284 4288 : Oid *param_types = pstmt->plansource->param_types;
285 4288 : int num_params = pstmt->plansource->num_params;
286 4288 : int nparams = list_length(params);
287 : ParamListInfo paramLI;
288 : List *exprstates;
289 : ListCell *l;
290 : int i;
291 :
292 4288 : if (nparams != num_params)
293 6 : ereport(ERROR,
294 : (errcode(ERRCODE_SYNTAX_ERROR),
295 : errmsg("wrong number of parameters for prepared statement \"%s\"",
296 : pstmt->stmt_name),
297 : errdetail("Expected %d parameters but got %d.",
298 : num_params, nparams)));
299 :
300 : /* Quick exit if no parameters */
301 4282 : if (num_params == 0)
6196 tgl 302 UBC 0 : return NULL;
303 :
304 : /*
305 : * We have to run parse analysis for the expressions. Since the parser is
306 : * not cool about scribbling on its input, copy first.
307 : */
2222 peter_e 308 CBC 4282 : params = copyObject(params);
309 :
5871 tgl 310 4282 : i = 0;
311 8710 : foreach(l, params)
312 : {
313 4434 : Node *expr = lfirst(l);
314 4434 : Oid expected_type_id = param_types[i];
315 : Oid given_type_id;
316 :
3894 317 4434 : expr = transformExpr(pstate, expr, EXPR_KIND_EXECUTE_PARAMETER);
318 :
5871 319 4434 : given_type_id = exprType(expr);
320 :
321 4434 : expr = coerce_to_target_type(pstate, expr, given_type_id,
322 : expected_type_id, -1,
323 : COERCION_ASSIGNMENT,
324 : COERCE_IMPLICIT_CAST,
325 : -1);
326 :
327 4431 : if (expr == NULL)
328 3 : ereport(ERROR,
329 : (errcode(ERRCODE_DATATYPE_MISMATCH),
330 : errmsg("parameter $%d of type %s cannot be coerced to the expected type %s",
331 : i + 1,
332 : format_type_be(given_type_id),
333 : format_type_be(expected_type_id)),
334 : errhint("You will need to rewrite or cast the expression."),
335 : parser_errposition(pstate, exprLocation(lfirst(l)))));
336 :
337 : /* Take care of collations in the finished expression. */
4404 338 4428 : assign_expr_collations(pstate, expr);
339 :
5871 340 4428 : lfirst(l) = expr;
341 4428 : i++;
342 : }
343 :
344 : /* Prepare the expressions for execution */
2217 andres 345 4276 : exprstates = ExecPrepareExprList(params, estate);
346 :
1487 peter 347 4276 : paramLI = makeParamList(num_params);
348 :
5871 tgl 349 4276 : i = 0;
350 8692 : foreach(l, exprstates)
351 : {
2217 andres 352 4422 : ExprState *n = (ExprState *) lfirst(l);
6196 tgl 353 4422 : ParamExternData *prm = ¶mLI->params[i];
354 :
5871 355 4422 : prm->ptype = param_types[i];
4223 356 4422 : prm->pflags = PARAM_FLAG_CONST;
6196 357 4422 : prm->value = ExecEvalExprSwitchContext(n,
358 4422 : GetPerTupleExprContext(estate),
359 : &prm->isnull);
360 :
7371 361 4416 : i++;
362 : }
363 :
364 4270 : return paramLI;
365 : }
366 :
367 :
368 : /*
369 : * Initialize query hash table upon first use.
370 : */
371 : static void
7530 372 407 : InitQueryHashTable(void)
373 : {
374 : HASHCTL hash_ctl;
375 :
7279 376 407 : hash_ctl.keysize = NAMEDATALEN;
377 407 : hash_ctl.entrysize = sizeof(PreparedStatement);
378 :
7530 379 407 : prepared_queries = hash_create("Prepared Queries",
380 : 32,
381 : &hash_ctl,
382 : HASH_ELEM | HASH_STRINGS);
383 407 : }
384 :
385 : /*
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
389 : * table entry.
390 : */
391 : void
7279 392 2077 : StorePreparedStatement(const char *stmt_name,
393 : CachedPlanSource *plansource,
394 : bool from_sql)
395 : {
396 : PreparedStatement *entry;
4223 397 2077 : TimestampTz cur_ts = GetCurrentStatementStartTimestamp();
398 : bool found;
399 :
400 : /* Initialize the hash table, if necessary */
7530 401 2077 : if (!prepared_queries)
402 407 : InitQueryHashTable();
403 :
404 : /* Add entry to hash table */
7279 405 2077 : entry = (PreparedStatement *) hash_search(prepared_queries,
406 : stmt_name,
407 : HASH_ENTER,
408 : &found);
409 :
410 : /* Shouldn't get a duplicate entry */
6524 411 2077 : if (found)
4223 412 3 : ereport(ERROR,
413 : (errcode(ERRCODE_DUPLICATE_PSTATEMENT),
414 : errmsg("prepared statement \"%s\" already exists",
415 : stmt_name)));
416 :
417 : /* Fill in the hash table entry */
5871 418 2074 : entry->plansource = plansource;
5892 419 2074 : entry->from_sql = from_sql;
4223 420 2074 : entry->prepare_time = cur_ts;
421 :
422 : /* Now it's safe to move the CachedPlanSource to permanent memory */
423 2074 : SaveCachedPlan(plansource);
7530 424 2074 : }
425 :
426 : /*
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.
429 : *
430 : * Note: this does not force the referenced plancache entry to be valid,
431 : * since not all callers care.
432 : */
433 : PreparedStatement *
7279 434 31857 : FetchPreparedStatement(const char *stmt_name, bool throwError)
435 : {
436 : PreparedStatement *entry;
437 :
438 : /*
439 : * If the hash table hasn't been initialized, it can't be storing
440 : * anything, therefore it couldn't possibly store our plan.
441 : */
442 31857 : if (prepared_queries)
443 31856 : entry = (PreparedStatement *) hash_search(prepared_queries,
444 : stmt_name,
445 : HASH_FIND,
446 : NULL);
447 : else
448 1 : entry = NULL;
449 :
450 31857 : if (!entry && throwError)
7203 451 1 : ereport(ERROR,
452 : (errcode(ERRCODE_UNDEFINED_PSTATEMENT),
453 : errmsg("prepared statement \"%s\" does not exist",
454 : stmt_name)));
455 :
7530 456 31856 : return entry;
457 : }
458 :
459 : /*
460 : * Given a prepared statement, determine the result tupledesc it will
461 : * produce. Returns NULL if the execution will not return tuples.
462 : *
463 : * Note: the result is created or copied into current memory context.
464 : */
465 : TupleDesc
7184 bruce 466 4550 : FetchPreparedStatementResultDesc(PreparedStatement *stmt)
467 : {
468 : /*
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.
471 : */
5871 tgl 472 4550 : Assert(stmt->plansource->fixed_result);
473 4550 : if (stmt->plansource->resultDesc)
474 4550 : return CreateTupleDescCopy(stmt->plansource->resultDesc);
475 : else
5871 tgl 476 UBC 0 : return NULL;
477 : }
478 :
479 : /*
480 : * Given a prepared statement that returns tuples, extract the query
481 : * targetlist. Returns NIL if the statement doesn't have a determinable
482 : * targetlist.
483 : *
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
486 : * efficiency.
487 : */
488 : List *
6500 tgl 489 CBC 4516 : FetchPreparedStatementTargetList(PreparedStatement *stmt)
490 : {
491 : List *tlist;
492 :
493 : /* Get the plan's primary targetlist */
2200 kgrittn 494 4516 : tlist = CachedPlanGetTargetList(stmt->plansource, NULL);
495 :
496 : /* Copy into caller's context in case plan gets invalidated */
2222 peter_e 497 4516 : return copyObject(tlist);
498 : }
499 :
500 : /*
501 : * Implements the 'DEALLOCATE' utility statement: deletes the
502 : * specified plan from storage.
503 : */
504 : void
7530 tgl 505 1151 : DeallocateQuery(DeallocateStmt *stmt)
506 : {
5841 neilc 507 1151 : if (stmt->name)
508 1128 : DropPreparedStatement(stmt->name, true);
509 : else
510 23 : DropAllPreparedStatements();
7279 tgl 511 1151 : }
512 :
513 : /*
514 : * Internal version of DEALLOCATE
515 : *
516 : * If showError is false, dropping a nonexistent statement is a no-op.
517 : */
518 : void
519 1128 : DropPreparedStatement(const char *stmt_name, bool showError)
520 : {
521 : PreparedStatement *entry;
522 :
523 : /* Find the query's hash table entry; raise error if wanted */
524 1128 : entry = FetchPreparedStatement(stmt_name, showError);
525 :
526 1128 : if (entry)
527 : {
528 : /* Release the plancache entry */
5871 529 1128 : DropCachedPlan(entry->plansource);
530 :
531 : /* Now we can remove the hash table entry */
7279 532 1128 : hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
533 : }
7371 534 1128 : }
535 :
536 : /*
537 : * Drop all cached statements.
538 : */
539 : void
5841 neilc 540 26 : DropAllPreparedStatements(void)
541 : {
542 : HASH_SEQ_STATUS seq;
543 : PreparedStatement *entry;
544 :
545 : /* nothing cached */
546 26 : if (!prepared_queries)
5841 neilc 547 UBC 0 : return;
548 :
549 : /* walk over cache */
5841 neilc 550 CBC 26 : hash_seq_init(&seq, prepared_queries);
551 59 : while ((entry = hash_seq_search(&seq)) != NULL)
552 : {
553 : /* Release the plancache entry */
554 33 : DropCachedPlan(entry->plansource);
555 :
556 : /* Now we can remove the hash table entry */
557 33 : hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
558 : }
559 : }
560 :
561 : /*
562 : * Implements the 'EXPLAIN EXECUTE' utility statement.
563 : *
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.
566 : *
567 : * Note: the passed-in queryString is that of the EXPLAIN EXECUTE,
568 : * not the original PREPARE; we get the latter string from the plancache.
569 : */
570 : void
4038 tgl 571 181 : ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
572 : const char *queryString, ParamListInfo params,
573 : QueryEnvironment *queryEnv)
574 : {
575 : PreparedStatement *entry;
576 : const char *query_string;
577 : CachedPlan *cplan;
578 : List *plan_list;
579 : ListCell *p;
7371 580 181 : ParamListInfo paramLI = NULL;
581 181 : EState *estate = NULL;
582 : instr_time planstart;
583 : instr_time planduration;
584 : BufferUsage bufusage_start,
585 : bufusage;
586 :
1100 fujii 587 181 : if (es->buffers)
1100 fujii 588 UBC 0 : bufusage_start = pgBufferUsage;
2223 sfrost 589 CBC 181 : INSTR_TIME_SET_CURRENT(planstart);
590 :
591 : /* Look it up in the hash table */
7279 tgl 592 181 : entry = FetchPreparedStatement(execstmt->name, true);
593 :
594 : /* Shouldn't find a non-fixed-result cached plan */
5871 595 181 : if (!entry->plansource->fixed_result)
5871 tgl 596 UBC 0 : elog(ERROR, "EXPLAIN EXECUTE does not support variable-result cached plans");
597 :
5210 tgl 598 CBC 181 : query_string = entry->plansource->query_string;
599 :
600 : /* Evaluate parameters, if any */
5871 601 181 : if (entry->plansource->num_params)
602 : {
603 : ParseState *pstate;
604 :
1191 peter 605 119 : pstate = make_parsestate(NULL);
606 119 : pstate->p_sourcetext = queryString;
607 :
608 : /*
609 : * Need an EState to evaluate parameters; must not delete it till end
610 : * of query, in case parameters are pass-by-reference. Note that the
611 : * passed-in "params" could possibly be referenced in the parameter
612 : * expressions.
613 : */
7371 tgl 614 119 : estate = CreateExecutorState();
6340 615 119 : estate->es_param_list_info = params;
616 :
1191 peter 617 119 : paramLI = EvaluateParams(pstate, entry, execstmt->params, estate);
618 : }
619 :
620 : /* Replan if needed, and acquire a transient refcount */
804 tgl 621 181 : cplan = GetCachedPlan(entry->plansource, paramLI,
622 : CurrentResourceOwner, queryEnv);
623 :
2223 sfrost 624 181 : INSTR_TIME_SET_CURRENT(planduration);
625 181 : INSTR_TIME_SUBTRACT(planduration, planstart);
626 :
627 : /* calc differences of buffer counters. */
1100 fujii 628 181 : if (es->buffers)
629 : {
1100 fujii 630 UBC 0 : memset(&bufusage, 0, sizeof(BufferUsage));
631 0 : BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
632 : }
633 :
4223 tgl 634 CBC 181 : plan_list = cplan->stmt_list;
635 :
636 : /* Explain each query */
5892 637 362 : foreach(p, plan_list)
638 : {
2190 639 181 : PlannedStmt *pstmt = lfirst_node(PlannedStmt, p);
640 :
2276 641 181 : if (pstmt->commandType != CMD_UTILITY)
2200 kgrittn 642 181 : ExplainOnePlan(pstmt, into, es, query_string, paramLI, queryEnv,
1100 fujii 643 181 : &planduration, (es->buffers ? &bufusage : NULL));
644 : else
2200 kgrittn 645 UBC 0 : ExplainOneUtility(pstmt->utilityStmt, into, es, query_string,
646 : paramLI, queryEnv);
647 :
648 : /* No need for CommandCounterIncrement, as ExplainOnePlan did it */
649 :
650 : /* Separate plans with an appropriate separator */
1364 tgl 651 CBC 181 : if (lnext(plan_list, p) != NULL)
4990 tgl 652 UBC 0 : ExplainSeparatePlans(es);
653 : }
654 :
7371 tgl 655 CBC 181 : if (estate)
656 119 : FreeExecutorState(estate);
657 :
804 658 181 : ReleaseCachedPlan(cplan, CurrentResourceOwner);
7530 659 181 : }
660 :
661 : /*
662 : * This set returning function reads all the prepared statements and
663 : * returns a set of (name, statement, prepare_time, param_types, from_sql,
664 : * generic_plans, custom_plans).
665 : */
666 : Datum
6300 neilc 667 48 : pg_prepared_statement(PG_FUNCTION_ARGS)
668 : {
5827 tgl 669 48 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
670 :
671 : /*
672 : * We put all the tuples into a tuplestore in one scan of the hashtable.
673 : * This avoids any issue of the hashtable possibly changing between calls.
674 : */
173 michael 675 48 : InitMaterializedSRF(fcinfo, 0);
676 :
677 : /* hash table might be uninitialized */
5827 tgl 678 48 : if (prepared_queries)
679 : {
680 : HASH_SEQ_STATUS hash_seq;
681 : PreparedStatement *prep_stmt;
682 :
683 42 : hash_seq_init(&hash_seq, prepared_queries);
684 183 : while ((prep_stmt = hash_seq_search(&hash_seq)) != NULL)
685 : {
686 : TupleDesc result_desc;
687 : Datum values[8];
267 peter 688 GNC 141 : bool nulls[8] = {0};
278 peter 689 ECB :
278 peter 690 GNC 141 : result_desc = prep_stmt->plansource->resultDesc;
6300 neilc 691 ECB :
5493 tgl 692 GIC 141 : values[0] = CStringGetTextDatum(prep_stmt->stmt_name);
5378 tgl 693 CBC 141 : values[1] = CStringGetTextDatum(prep_stmt->plansource->query_string);
5827 694 141 : values[2] = TimestampTzGetDatum(prep_stmt->prepare_time);
695 282 : values[3] = build_regtype_array(prep_stmt->plansource->param_types,
2118 696 141 : prep_stmt->plansource->num_params);
278 peter 697 GNC 141 : if (result_desc)
698 : {
699 : Oid *result_types;
700 :
209 701 138 : result_types = palloc_array(Oid, result_desc->natts);
278 702 474 : for (int i = 0; i < result_desc->natts; i++)
703 336 : result_types[i] = result_desc->attrs[i].atttypid;
704 138 : values[4] = build_regtype_array(result_types, result_desc->natts);
705 : }
706 : else
707 : {
708 : /* no result descriptor (for example, DML statement) */
709 3 : nulls[4] = true;
710 : }
711 141 : values[5] = BoolGetDatum(prep_stmt->from_sql);
712 141 : values[6] = Int64GetDatumFast(prep_stmt->plansource->num_generic_plans);
713 141 : values[7] = Int64GetDatumFast(prep_stmt->plansource->num_custom_plans);
714 :
398 michael 715 GIC 141 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
398 michael 716 ECB : values, nulls);
5827 tgl 717 : }
6300 neilc 718 : }
719 :
5827 tgl 720 GIC 48 : return (Datum) 0;
721 : }
722 :
723 : /*
5871 tgl 724 ECB : * This utility function takes a C array of Oids, and returns a Datum
725 : * pointing to a one-dimensional Postgres array of regtypes. An empty
726 : * array is returned as a zero-element array, not NULL.
6300 neilc 727 : */
728 : static Datum
5871 tgl 729 GIC 279 : build_regtype_array(Oid *param_types, int num_params)
6300 neilc 730 ECB : {
731 : Datum *tmp_ary;
732 : ArrayType *result;
733 : int i;
734 :
209 peter 735 GNC 279 : tmp_ary = palloc_array(Datum, num_params);
736 :
5871 tgl 737 GIC 690 : for (i = 0; i < num_params; i++)
738 411 : tmp_ary[i] = ObjectIdGetDatum(param_types[i]);
739 :
282 peter 740 GNC 279 : result = construct_array_builtin(tmp_ary, num_params, REGTYPEOID);
6292 neilc 741 GIC 279 : return PointerGetDatum(result);
6300 neilc 742 ECB : }
|