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