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-2017, 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 "miscadmin.h"
26 : #include "nodes/nodeFuncs.h"
27 : #include "parser/analyze.h"
28 : #include "parser/parse_coerce.h"
29 : #include "parser/parse_collate.h"
30 : #include "parser/parse_expr.h"
31 : #include "parser/parse_type.h"
32 : #include "rewrite/rewriteHandler.h"
33 : #include "tcop/pquery.h"
34 : #include "tcop/utility.h"
35 : #include "utils/builtins.h"
36 : #include "utils/snapmgr.h"
37 : #include "utils/timestamp.h"
38 :
39 :
40 : /*
41 : * The hash table in which prepared queries are stored. This is
42 : * per-backend: query plans are not shared between backends.
43 : * The keys for this hash table are the arguments to PREPARE and EXECUTE
44 : * (statement names); the entries are PreparedStatement structs.
45 : */
46 : static HTAB *prepared_queries = NULL;
47 :
48 : static void InitQueryHashTable(void);
49 : static ParamListInfo EvaluateParams(PreparedStatement *pstmt, List *params,
50 : const char *queryString, EState *estate);
51 : static Datum build_regtype_array(Oid *param_types, int num_params);
52 :
53 : /*
54 : * Implements the 'PREPARE' utility statement.
55 : */
56 : void
57 37 : PrepareQuery(PrepareStmt *stmt, const char *queryString,
58 : int stmt_location, int stmt_len)
59 : {
60 : RawStmt *rawstmt;
61 : CachedPlanSource *plansource;
62 37 : Oid *argtypes = NULL;
63 : int nargs;
64 : Query *query;
65 : List *query_list;
66 : int i;
67 :
68 : /*
69 : * Disallow empty-string statement name (conflicts with protocol-level
70 : * unnamed statement).
71 : */
72 37 : if (!stmt->name || stmt->name[0] == '\0')
73 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 : *
81 : * Because parse analysis scribbles on the raw querytree, we must make a
82 : * copy to ensure we don't modify the passed-in tree. FIXME someday.
83 : */
84 37 : rawstmt = makeNode(RawStmt);
85 37 : rawstmt->stmt = (Node *) copyObject(stmt->query);
86 37 : rawstmt->stmt_location = stmt_location;
87 37 : rawstmt->stmt_len = stmt_len;
88 :
89 : /*
90 : * Create the CachedPlanSource before we do parse analysis, since it needs
91 : * to see the unmodified raw parse tree.
92 : */
93 37 : plansource = CreateCachedPlan(rawstmt, queryString,
94 : CreateCommandTag(stmt->query));
95 :
96 : /* Transform list of TypeNames to array of type OIDs */
97 37 : nargs = list_length(stmt->argtypes);
98 :
99 37 : if (nargs)
100 : {
101 : ParseState *pstate;
102 : ListCell *l;
103 :
104 : /*
105 : * typenameTypeId wants a ParseState to carry the source query string.
106 : * Is it worth refactoring its API to avoid this?
107 : */
108 13 : pstate = make_parsestate(NULL);
109 13 : pstate->p_sourcetext = queryString;
110 :
111 13 : argtypes = (Oid *) palloc(nargs * sizeof(Oid));
112 13 : i = 0;
113 :
114 32 : foreach(l, stmt->argtypes)
115 : {
116 20 : TypeName *tn = lfirst(l);
117 20 : Oid toid = typenameTypeId(pstate, tn);
118 :
119 19 : argtypes[i++] = toid;
120 : }
121 : }
122 :
123 : /*
124 : * Analyze the statement using these parameter types (any parameters
125 : * passed in from above us will not be visible to it), allowing
126 : * information about unknown parameters to be deduced from context.
127 : */
128 36 : query = parse_analyze_varparams(rawstmt, queryString,
129 : &argtypes, &nargs);
130 :
131 : /*
132 : * Check that all parameter types were determined.
133 : */
134 56 : for (i = 0; i < nargs; i++)
135 : {
136 21 : Oid argtype = argtypes[i];
137 :
138 21 : if (argtype == InvalidOid || argtype == UNKNOWNOID)
139 0 : ereport(ERROR,
140 : (errcode(ERRCODE_INDETERMINATE_DATATYPE),
141 : errmsg("could not determine data type of parameter $%d",
142 : i + 1)));
143 : }
144 :
145 : /*
146 : * grammar only allows OptimizableStmt, so this check should be redundant
147 : */
148 35 : switch (query->commandType)
149 : {
150 : case CMD_SELECT:
151 : case CMD_INSERT:
152 : case CMD_UPDATE:
153 : case CMD_DELETE:
154 : /* OK */
155 35 : break;
156 : default:
157 0 : ereport(ERROR,
158 : (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
159 : errmsg("utility statements cannot be prepared")));
160 : break;
161 : }
162 :
163 : /* Rewrite the query. The result could be 0, 1, or many queries. */
164 35 : query_list = QueryRewrite(query);
165 :
166 : /* Finish filling in the CachedPlanSource */
167 35 : CompleteCachedPlan(plansource,
168 : query_list,
169 : NULL,
170 : argtypes,
171 : nargs,
172 : NULL,
173 : NULL,
174 : CURSOR_OPT_PARALLEL_OK, /* allow parallel mode */
175 : true); /* fixed result */
176 :
177 : /*
178 : * Save the results.
179 : */
180 35 : StorePreparedStatement(stmt->name,
181 : plansource,
182 : true);
183 34 : }
184 :
185 : /*
186 : * ExecuteQuery --- implement the 'EXECUTE' utility statement.
187 : *
188 : * This code also supports CREATE TABLE ... AS EXECUTE. That case is
189 : * indicated by passing a non-null intoClause. The DestReceiver is already
190 : * set up correctly for CREATE TABLE AS, but we still have to make a few
191 : * other adjustments here.
192 : *
193 : * Note: this is one of very few places in the code that needs to deal with
194 : * two query strings at once. The passed-in queryString is that of the
195 : * EXECUTE, which we might need for error reporting while processing the
196 : * parameter expressions. The query_string that we copy from the plan
197 : * source is that of the original PREPARE.
198 : */
199 : void
200 139 : ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
201 : const char *queryString, ParamListInfo params,
202 : DestReceiver *dest, char *completionTag)
203 : {
204 : PreparedStatement *entry;
205 : CachedPlan *cplan;
206 : List *plan_list;
207 139 : ParamListInfo paramLI = NULL;
208 139 : EState *estate = NULL;
209 : Portal portal;
210 : char *query_string;
211 : int eflags;
212 : long count;
213 :
214 : /* Look it up in the hash table */
215 139 : entry = FetchPreparedStatement(stmt->name, true);
216 :
217 : /* Shouldn't find a non-fixed-result cached plan */
218 135 : if (!entry->plansource->fixed_result)
219 0 : elog(ERROR, "EXECUTE does not support variable-result cached plans");
220 :
221 : /* Evaluate parameters, if any */
222 135 : if (entry->plansource->num_params > 0)
223 : {
224 : /*
225 : * Need an EState to evaluate parameters; must not delete it till end
226 : * of query, in case parameters are pass-by-reference. Note that the
227 : * passed-in "params" could possibly be referenced in the parameter
228 : * expressions.
229 : */
230 25 : estate = CreateExecutorState();
231 25 : estate->es_param_list_info = params;
232 25 : paramLI = EvaluateParams(entry, stmt->params,
233 : queryString, estate);
234 : }
235 :
236 : /* Create a new portal to run the query in */
237 130 : portal = CreateNewPortal();
238 : /* Don't display the portal in pg_cursors, it is for internal use only */
239 130 : portal->visible = false;
240 :
241 : /* Copy the plan's saved query string into the portal's memory */
242 130 : query_string = MemoryContextStrdup(PortalGetHeapMemory(portal),
243 130 : entry->plansource->query_string);
244 :
245 : /* Replan if needed, and increment plan refcount for portal */
246 130 : cplan = GetCachedPlan(entry->plansource, paramLI, false, NULL);
247 125 : plan_list = cplan->stmt_list;
248 :
249 : /*
250 : * For CREATE TABLE ... AS EXECUTE, we must verify that the prepared
251 : * statement is one that produces tuples. Currently we insist that it be
252 : * a plain old SELECT. In future we might consider supporting other
253 : * things such as INSERT ... RETURNING, but there are a couple of issues
254 : * to be settled first, notably how WITH NO DATA should be handled in such
255 : * a case (do we really want to suppress execution?) and how to pass down
256 : * the OID-determining eflags (PortalStart won't handle them in such a
257 : * case, and for that matter it's not clear the executor will either).
258 : *
259 : * For CREATE TABLE ... AS EXECUTE, we also have to ensure that the proper
260 : * eflags and fetch count are passed to PortalStart/PortalRun.
261 : */
262 125 : if (intoClause)
263 : {
264 : PlannedStmt *pstmt;
265 :
266 4 : if (list_length(plan_list) != 1)
267 0 : ereport(ERROR,
268 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
269 : errmsg("prepared statement is not a SELECT")));
270 4 : pstmt = linitial_node(PlannedStmt, plan_list);
271 4 : if (pstmt->commandType != CMD_SELECT)
272 0 : ereport(ERROR,
273 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
274 : errmsg("prepared statement is not a SELECT")));
275 :
276 : /* Set appropriate eflags */
277 4 : eflags = GetIntoRelEFlags(intoClause);
278 :
279 : /* And tell PortalRun whether to run to completion or not */
280 4 : if (intoClause->skipData)
281 0 : count = 0;
282 : else
283 4 : count = FETCH_ALL;
284 : }
285 : else
286 : {
287 : /* Plain old EXECUTE */
288 121 : eflags = 0;
289 121 : count = FETCH_ALL;
290 : }
291 :
292 125 : PortalDefineQuery(portal,
293 : NULL,
294 : query_string,
295 125 : entry->plansource->commandTag,
296 : plan_list,
297 : cplan);
298 :
299 : /*
300 : * Run the portal as appropriate.
301 : */
302 125 : PortalStart(portal, paramLI, eflags, GetActiveSnapshot());
303 :
304 125 : (void) PortalRun(portal, count, false, true, dest, dest, completionTag);
305 :
306 123 : PortalDrop(portal, false);
307 :
308 123 : if (estate)
309 17 : FreeExecutorState(estate);
310 :
311 : /* No need to pfree other memory, MemoryContext will be reset */
312 123 : }
313 :
314 : /*
315 : * EvaluateParams: evaluate a list of parameters.
316 : *
317 : * pstmt: statement we are getting parameters for.
318 : * params: list of given parameter expressions (raw parser output!)
319 : * queryString: source text for error messages.
320 : * estate: executor state to use.
321 : *
322 : * Returns a filled-in ParamListInfo -- this can later be passed to
323 : * CreateQueryDesc(), which allows the executor to make use of the parameters
324 : * during query execution.
325 : */
326 : static ParamListInfo
327 29 : EvaluateParams(PreparedStatement *pstmt, List *params,
328 : const char *queryString, EState *estate)
329 : {
330 29 : Oid *param_types = pstmt->plansource->param_types;
331 29 : int num_params = pstmt->plansource->num_params;
332 29 : int nparams = list_length(params);
333 : ParseState *pstate;
334 : ParamListInfo paramLI;
335 : List *exprstates;
336 : ListCell *l;
337 : int i;
338 :
339 29 : if (nparams != num_params)
340 2 : ereport(ERROR,
341 : (errcode(ERRCODE_SYNTAX_ERROR),
342 : errmsg("wrong number of parameters for prepared statement \"%s\"",
343 : pstmt->stmt_name),
344 : errdetail("Expected %d parameters but got %d.",
345 : num_params, nparams)));
346 :
347 : /* Quick exit if no parameters */
348 27 : if (num_params == 0)
349 0 : return NULL;
350 :
351 : /*
352 : * We have to run parse analysis for the expressions. Since the parser is
353 : * not cool about scribbling on its input, copy first.
354 : */
355 27 : params = copyObject(params);
356 :
357 27 : pstate = make_parsestate(NULL);
358 27 : pstate->p_sourcetext = queryString;
359 :
360 27 : i = 0;
361 63 : foreach(l, params)
362 : {
363 37 : Node *expr = lfirst(l);
364 37 : Oid expected_type_id = param_types[i];
365 : Oid given_type_id;
366 :
367 37 : expr = transformExpr(pstate, expr, EXPR_KIND_EXECUTE_PARAMETER);
368 :
369 37 : given_type_id = exprType(expr);
370 :
371 37 : expr = coerce_to_target_type(pstate, expr, given_type_id,
372 : expected_type_id, -1,
373 : COERCION_ASSIGNMENT,
374 : COERCE_IMPLICIT_CAST,
375 : -1);
376 :
377 37 : if (expr == NULL)
378 1 : ereport(ERROR,
379 : (errcode(ERRCODE_DATATYPE_MISMATCH),
380 : errmsg("parameter $%d of type %s cannot be coerced to the expected type %s",
381 : i + 1,
382 : format_type_be(given_type_id),
383 : format_type_be(expected_type_id)),
384 : errhint("You will need to rewrite or cast the expression.")));
385 :
386 : /* Take care of collations in the finished expression. */
387 36 : assign_expr_collations(pstate, expr);
388 :
389 36 : lfirst(l) = expr;
390 36 : i++;
391 : }
392 :
393 : /* Prepare the expressions for execution */
394 26 : exprstates = ExecPrepareExprList(params, estate);
395 :
396 26 : paramLI = (ParamListInfo)
397 26 : palloc(offsetof(ParamListInfoData, params) +
398 26 : num_params * sizeof(ParamExternData));
399 : /* we have static list of params, so no hooks needed */
400 26 : paramLI->paramFetch = NULL;
401 26 : paramLI->paramFetchArg = NULL;
402 26 : paramLI->parserSetup = NULL;
403 26 : paramLI->parserSetupArg = NULL;
404 26 : paramLI->numParams = num_params;
405 26 : paramLI->paramMask = NULL;
406 :
407 26 : i = 0;
408 58 : foreach(l, exprstates)
409 : {
410 34 : ExprState *n = (ExprState *) lfirst(l);
411 34 : ParamExternData *prm = ¶mLI->params[i];
412 :
413 34 : prm->ptype = param_types[i];
414 34 : prm->pflags = PARAM_FLAG_CONST;
415 68 : prm->value = ExecEvalExprSwitchContext(n,
416 34 : GetPerTupleExprContext(estate),
417 : &prm->isnull);
418 :
419 32 : i++;
420 : }
421 :
422 24 : return paramLI;
423 : }
424 :
425 :
426 : /*
427 : * Initialize query hash table upon first use.
428 : */
429 : static void
430 15 : InitQueryHashTable(void)
431 : {
432 : HASHCTL hash_ctl;
433 :
434 15 : MemSet(&hash_ctl, 0, sizeof(hash_ctl));
435 :
436 15 : hash_ctl.keysize = NAMEDATALEN;
437 15 : hash_ctl.entrysize = sizeof(PreparedStatement);
438 :
439 15 : prepared_queries = hash_create("Prepared Queries",
440 : 32,
441 : &hash_ctl,
442 : HASH_ELEM);
443 15 : }
444 :
445 : /*
446 : * Store all the data pertaining to a query in the hash table using
447 : * the specified key. The passed CachedPlanSource should be "unsaved"
448 : * in case we get an error here; we'll save it once we've created the hash
449 : * table entry.
450 : */
451 : void
452 35 : StorePreparedStatement(const char *stmt_name,
453 : CachedPlanSource *plansource,
454 : bool from_sql)
455 : {
456 : PreparedStatement *entry;
457 35 : TimestampTz cur_ts = GetCurrentStatementStartTimestamp();
458 : bool found;
459 :
460 : /* Initialize the hash table, if necessary */
461 35 : if (!prepared_queries)
462 15 : InitQueryHashTable();
463 :
464 : /* Add entry to hash table */
465 35 : entry = (PreparedStatement *) hash_search(prepared_queries,
466 : stmt_name,
467 : HASH_ENTER,
468 : &found);
469 :
470 : /* Shouldn't get a duplicate entry */
471 35 : if (found)
472 1 : ereport(ERROR,
473 : (errcode(ERRCODE_DUPLICATE_PSTATEMENT),
474 : errmsg("prepared statement \"%s\" already exists",
475 : stmt_name)));
476 :
477 : /* Fill in the hash table entry */
478 34 : entry->plansource = plansource;
479 34 : entry->from_sql = from_sql;
480 34 : entry->prepare_time = cur_ts;
481 :
482 : /* Now it's safe to move the CachedPlanSource to permanent memory */
483 34 : SaveCachedPlan(plansource);
484 34 : }
485 :
486 : /*
487 : * Lookup an existing query in the hash table. If the query does not
488 : * actually exist, throw ereport(ERROR) or return NULL per second parameter.
489 : *
490 : * Note: this does not force the referenced plancache entry to be valid,
491 : * since not all callers care.
492 : */
493 : PreparedStatement *
494 546 : FetchPreparedStatement(const char *stmt_name, bool throwError)
495 : {
496 : PreparedStatement *entry;
497 :
498 : /*
499 : * If the hash table hasn't been initialized, it can't be storing
500 : * anything, therefore it couldn't possibly store our plan.
501 : */
502 546 : if (prepared_queries)
503 538 : entry = (PreparedStatement *) hash_search(prepared_queries,
504 : stmt_name,
505 : HASH_FIND,
506 : NULL);
507 : else
508 8 : entry = NULL;
509 :
510 546 : if (!entry && throwError)
511 4 : ereport(ERROR,
512 : (errcode(ERRCODE_UNDEFINED_PSTATEMENT),
513 : errmsg("prepared statement \"%s\" does not exist",
514 : stmt_name)));
515 :
516 542 : return entry;
517 : }
518 :
519 : /*
520 : * Given a prepared statement, determine the result tupledesc it will
521 : * produce. Returns NULL if the execution will not return tuples.
522 : *
523 : * Note: the result is created or copied into current memory context.
524 : */
525 : TupleDesc
526 128 : FetchPreparedStatementResultDesc(PreparedStatement *stmt)
527 : {
528 : /*
529 : * Since we don't allow prepared statements' result tupdescs to change,
530 : * there's no need to worry about revalidating the cached plan here.
531 : */
532 128 : Assert(stmt->plansource->fixed_result);
533 128 : if (stmt->plansource->resultDesc)
534 128 : return CreateTupleDescCopy(stmt->plansource->resultDesc);
535 : else
536 0 : return NULL;
537 : }
538 :
539 : /*
540 : * Given a prepared statement that returns tuples, extract the query
541 : * targetlist. Returns NIL if the statement doesn't have a determinable
542 : * targetlist.
543 : *
544 : * Note: this is pretty ugly, but since it's only used in corner cases like
545 : * Describe Statement on an EXECUTE command, we don't worry too much about
546 : * efficiency.
547 : */
548 : List *
549 118 : FetchPreparedStatementTargetList(PreparedStatement *stmt)
550 : {
551 : List *tlist;
552 :
553 : /* Get the plan's primary targetlist */
554 118 : tlist = CachedPlanGetTargetList(stmt->plansource, NULL);
555 :
556 : /* Copy into caller's context in case plan gets invalidated */
557 118 : return copyObject(tlist);
558 : }
559 :
560 : /*
561 : * Implements the 'DEALLOCATE' utility statement: deletes the
562 : * specified plan from storage.
563 : */
564 : void
565 8 : DeallocateQuery(DeallocateStmt *stmt)
566 : {
567 8 : if (stmt->name)
568 7 : DropPreparedStatement(stmt->name, true);
569 : else
570 1 : DropAllPreparedStatements();
571 8 : }
572 :
573 : /*
574 : * Internal version of DEALLOCATE
575 : *
576 : * If showError is false, dropping a nonexistent statement is a no-op.
577 : */
578 : void
579 7 : DropPreparedStatement(const char *stmt_name, bool showError)
580 : {
581 : PreparedStatement *entry;
582 :
583 : /* Find the query's hash table entry; raise error if wanted */
584 7 : entry = FetchPreparedStatement(stmt_name, showError);
585 :
586 7 : if (entry)
587 : {
588 : /* Release the plancache entry */
589 7 : DropCachedPlan(entry->plansource);
590 :
591 : /* Now we can remove the hash table entry */
592 7 : hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
593 : }
594 7 : }
595 :
596 : /*
597 : * Drop all cached statements.
598 : */
599 : void
600 2 : DropAllPreparedStatements(void)
601 : {
602 : HASH_SEQ_STATUS seq;
603 : PreparedStatement *entry;
604 :
605 : /* nothing cached */
606 2 : if (!prepared_queries)
607 2 : return;
608 :
609 : /* walk over cache */
610 2 : hash_seq_init(&seq, prepared_queries);
611 10 : while ((entry = hash_seq_search(&seq)) != NULL)
612 : {
613 : /* Release the plancache entry */
614 6 : DropCachedPlan(entry->plansource);
615 :
616 : /* Now we can remove the hash table entry */
617 6 : hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
618 : }
619 : }
620 :
621 : /*
622 : * Implements the 'EXPLAIN EXECUTE' utility statement.
623 : *
624 : * "into" is NULL unless we are doing EXPLAIN CREATE TABLE AS EXECUTE,
625 : * in which case executing the query should result in creating that table.
626 : *
627 : * Note: the passed-in queryString is that of the EXPLAIN EXECUTE,
628 : * not the original PREPARE; we get the latter string from the plancache.
629 : */
630 : void
631 19 : ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
632 : const char *queryString, ParamListInfo params,
633 : QueryEnvironment *queryEnv)
634 : {
635 : PreparedStatement *entry;
636 : const char *query_string;
637 : CachedPlan *cplan;
638 : List *plan_list;
639 : ListCell *p;
640 19 : ParamListInfo paramLI = NULL;
641 19 : EState *estate = NULL;
642 : instr_time planstart;
643 : instr_time planduration;
644 :
645 19 : INSTR_TIME_SET_CURRENT(planstart);
646 :
647 : /* Look it up in the hash table */
648 19 : entry = FetchPreparedStatement(execstmt->name, true);
649 :
650 : /* Shouldn't find a non-fixed-result cached plan */
651 19 : if (!entry->plansource->fixed_result)
652 0 : elog(ERROR, "EXPLAIN EXECUTE does not support variable-result cached plans");
653 :
654 19 : query_string = entry->plansource->query_string;
655 :
656 : /* Evaluate parameters, if any */
657 19 : if (entry->plansource->num_params)
658 : {
659 : /*
660 : * Need an EState to evaluate parameters; must not delete it till end
661 : * of query, in case parameters are pass-by-reference. Note that the
662 : * passed-in "params" could possibly be referenced in the parameter
663 : * expressions.
664 : */
665 4 : estate = CreateExecutorState();
666 4 : estate->es_param_list_info = params;
667 4 : paramLI = EvaluateParams(entry, execstmt->params,
668 : queryString, estate);
669 : }
670 :
671 : /* Replan if needed, and acquire a transient refcount */
672 19 : cplan = GetCachedPlan(entry->plansource, paramLI, true, queryEnv);
673 :
674 19 : INSTR_TIME_SET_CURRENT(planduration);
675 19 : INSTR_TIME_SUBTRACT(planduration, planstart);
676 :
677 19 : plan_list = cplan->stmt_list;
678 :
679 : /* Explain each query */
680 38 : foreach(p, plan_list)
681 : {
682 19 : PlannedStmt *pstmt = lfirst_node(PlannedStmt, p);
683 :
684 19 : if (pstmt->commandType != CMD_UTILITY)
685 19 : ExplainOnePlan(pstmt, into, es, query_string, paramLI, queryEnv,
686 : &planduration);
687 : else
688 0 : ExplainOneUtility(pstmt->utilityStmt, into, es, query_string,
689 : paramLI, queryEnv);
690 :
691 : /* No need for CommandCounterIncrement, as ExplainOnePlan did it */
692 :
693 : /* Separate plans with an appropriate separator */
694 19 : if (lnext(p) != NULL)
695 0 : ExplainSeparatePlans(es);
696 : }
697 :
698 19 : if (estate)
699 4 : FreeExecutorState(estate);
700 :
701 19 : ReleaseCachedPlan(cplan, true);
702 19 : }
703 :
704 : /*
705 : * This set returning function reads all the prepared statements and
706 : * returns a set of (name, statement, prepare_time, param_types, from_sql).
707 : */
708 : Datum
709 10 : pg_prepared_statement(PG_FUNCTION_ARGS)
710 : {
711 10 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
712 : TupleDesc tupdesc;
713 : Tuplestorestate *tupstore;
714 : MemoryContext per_query_ctx;
715 : MemoryContext oldcontext;
716 :
717 : /* check to see if caller supports us returning a tuplestore */
718 10 : if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
719 0 : ereport(ERROR,
720 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
721 : errmsg("set-valued function called in context that cannot accept a set")));
722 10 : if (!(rsinfo->allowedModes & SFRM_Materialize))
723 0 : ereport(ERROR,
724 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
725 : errmsg("materialize mode required, but it is not " \
726 : "allowed in this context")));
727 :
728 : /* need to build tuplestore in query context */
729 10 : per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
730 10 : oldcontext = MemoryContextSwitchTo(per_query_ctx);
731 :
732 : /*
733 : * build tupdesc for result tuples. This must match the definition of the
734 : * pg_prepared_statements view in system_views.sql
735 : */
736 10 : tupdesc = CreateTemplateTupleDesc(5, false);
737 10 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
738 : TEXTOID, -1, 0);
739 10 : TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
740 : TEXTOID, -1, 0);
741 10 : TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepare_time",
742 : TIMESTAMPTZOID, -1, 0);
743 10 : TupleDescInitEntry(tupdesc, (AttrNumber) 4, "parameter_types",
744 : REGTYPEARRAYOID, -1, 0);
745 10 : TupleDescInitEntry(tupdesc, (AttrNumber) 5, "from_sql",
746 : BOOLOID, -1, 0);
747 :
748 : /*
749 : * We put all the tuples into a tuplestore in one scan of the hashtable.
750 : * This avoids any issue of the hashtable possibly changing between calls.
751 : */
752 10 : tupstore =
753 10 : tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
754 : false, work_mem);
755 :
756 : /* generate junk in short-term context */
757 10 : MemoryContextSwitchTo(oldcontext);
758 :
759 : /* hash table might be uninitialized */
760 10 : if (prepared_queries)
761 : {
762 : HASH_SEQ_STATUS hash_seq;
763 : PreparedStatement *prep_stmt;
764 :
765 8 : hash_seq_init(&hash_seq, prepared_queries);
766 26 : while ((prep_stmt = hash_seq_search(&hash_seq)) != NULL)
767 : {
768 : Datum values[5];
769 : bool nulls[5];
770 :
771 10 : MemSet(nulls, 0, sizeof(nulls));
772 :
773 10 : values[0] = CStringGetTextDatum(prep_stmt->stmt_name);
774 10 : values[1] = CStringGetTextDatum(prep_stmt->plansource->query_string);
775 10 : values[2] = TimestampTzGetDatum(prep_stmt->prepare_time);
776 10 : values[3] = build_regtype_array(prep_stmt->plansource->param_types,
777 10 : prep_stmt->plansource->num_params);
778 10 : values[4] = BoolGetDatum(prep_stmt->from_sql);
779 :
780 10 : tuplestore_putvalues(tupstore, tupdesc, values, nulls);
781 : }
782 : }
783 :
784 : /* clean up and return the tuplestore */
785 : tuplestore_donestoring(tupstore);
786 :
787 10 : rsinfo->returnMode = SFRM_Materialize;
788 10 : rsinfo->setResult = tupstore;
789 10 : rsinfo->setDesc = tupdesc;
790 :
791 10 : return (Datum) 0;
792 : }
793 :
794 : /*
795 : * This utility function takes a C array of Oids, and returns a Datum
796 : * pointing to a one-dimensional Postgres array of regtypes. An empty
797 : * array is returned as a zero-element array, not NULL.
798 : */
799 : static Datum
800 10 : build_regtype_array(Oid *param_types, int num_params)
801 : {
802 : Datum *tmp_ary;
803 : ArrayType *result;
804 : int i;
805 :
806 10 : tmp_ary = (Datum *) palloc(num_params * sizeof(Datum));
807 :
808 22 : for (i = 0; i < num_params; i++)
809 12 : tmp_ary[i] = ObjectIdGetDatum(param_types[i]);
810 :
811 : /* XXX: this hardcodes assumptions about the regtype type */
812 10 : result = construct_array(tmp_ary, num_params, REGTYPEOID, 4, true, 'i');
813 10 : return PointerGetDatum(result);
814 : }
|