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