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