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 = &paramLI->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