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