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-2019, 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(portal->portalContext,
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 = makeParamList(num_params);
401 
402 	i = 0;
403 	foreach(l, exprstates)
404 	{
405 		ExprState  *n = (ExprState *) lfirst(l);
406 		ParamExternData *prm = &paramLI->params[i];
407 
408 		prm->ptype = param_types[i];
409 		prm->pflags = PARAM_FLAG_CONST;
410 		prm->value = ExecEvalExprSwitchContext(n,
411 											   GetPerTupleExprContext(estate),
412 											   &prm->isnull);
413 
414 		i++;
415 	}
416 
417 	return paramLI;
418 }
419 
420 
421 /*
422  * Initialize query hash table upon first use.
423  */
424 static void
InitQueryHashTable(void)425 InitQueryHashTable(void)
426 {
427 	HASHCTL		hash_ctl;
428 
429 	MemSet(&hash_ctl, 0, sizeof(hash_ctl));
430 
431 	hash_ctl.keysize = NAMEDATALEN;
432 	hash_ctl.entrysize = sizeof(PreparedStatement);
433 
434 	prepared_queries = hash_create("Prepared Queries",
435 								   32,
436 								   &hash_ctl,
437 								   HASH_ELEM);
438 }
439 
440 /*
441  * Store all the data pertaining to a query in the hash table using
442  * the specified key.  The passed CachedPlanSource should be "unsaved"
443  * in case we get an error here; we'll save it once we've created the hash
444  * table entry.
445  */
446 void
StorePreparedStatement(const char * stmt_name,CachedPlanSource * plansource,bool from_sql)447 StorePreparedStatement(const char *stmt_name,
448 					   CachedPlanSource *plansource,
449 					   bool from_sql)
450 {
451 	PreparedStatement *entry;
452 	TimestampTz cur_ts = GetCurrentStatementStartTimestamp();
453 	bool		found;
454 
455 	/* Initialize the hash table, if necessary */
456 	if (!prepared_queries)
457 		InitQueryHashTable();
458 
459 	/* Add entry to hash table */
460 	entry = (PreparedStatement *) hash_search(prepared_queries,
461 											  stmt_name,
462 											  HASH_ENTER,
463 											  &found);
464 
465 	/* Shouldn't get a duplicate entry */
466 	if (found)
467 		ereport(ERROR,
468 				(errcode(ERRCODE_DUPLICATE_PSTATEMENT),
469 				 errmsg("prepared statement \"%s\" already exists",
470 						stmt_name)));
471 
472 	/* Fill in the hash table entry */
473 	entry->plansource = plansource;
474 	entry->from_sql = from_sql;
475 	entry->prepare_time = cur_ts;
476 
477 	/* Now it's safe to move the CachedPlanSource to permanent memory */
478 	SaveCachedPlan(plansource);
479 }
480 
481 /*
482  * Lookup an existing query in the hash table. If the query does not
483  * actually exist, throw ereport(ERROR) or return NULL per second parameter.
484  *
485  * Note: this does not force the referenced plancache entry to be valid,
486  * since not all callers care.
487  */
488 PreparedStatement *
FetchPreparedStatement(const char * stmt_name,bool throwError)489 FetchPreparedStatement(const char *stmt_name, bool throwError)
490 {
491 	PreparedStatement *entry;
492 
493 	/*
494 	 * If the hash table hasn't been initialized, it can't be storing
495 	 * anything, therefore it couldn't possibly store our plan.
496 	 */
497 	if (prepared_queries)
498 		entry = (PreparedStatement *) hash_search(prepared_queries,
499 												  stmt_name,
500 												  HASH_FIND,
501 												  NULL);
502 	else
503 		entry = NULL;
504 
505 	if (!entry && throwError)
506 		ereport(ERROR,
507 				(errcode(ERRCODE_UNDEFINED_PSTATEMENT),
508 				 errmsg("prepared statement \"%s\" does not exist",
509 						stmt_name)));
510 
511 	return entry;
512 }
513 
514 /*
515  * Given a prepared statement, determine the result tupledesc it will
516  * produce.  Returns NULL if the execution will not return tuples.
517  *
518  * Note: the result is created or copied into current memory context.
519  */
520 TupleDesc
FetchPreparedStatementResultDesc(PreparedStatement * stmt)521 FetchPreparedStatementResultDesc(PreparedStatement *stmt)
522 {
523 	/*
524 	 * Since we don't allow prepared statements' result tupdescs to change,
525 	 * there's no need to worry about revalidating the cached plan here.
526 	 */
527 	Assert(stmt->plansource->fixed_result);
528 	if (stmt->plansource->resultDesc)
529 		return CreateTupleDescCopy(stmt->plansource->resultDesc);
530 	else
531 		return NULL;
532 }
533 
534 /*
535  * Given a prepared statement that returns tuples, extract the query
536  * targetlist.  Returns NIL if the statement doesn't have a determinable
537  * targetlist.
538  *
539  * Note: this is pretty ugly, but since it's only used in corner cases like
540  * Describe Statement on an EXECUTE command, we don't worry too much about
541  * efficiency.
542  */
543 List *
FetchPreparedStatementTargetList(PreparedStatement * stmt)544 FetchPreparedStatementTargetList(PreparedStatement *stmt)
545 {
546 	List	   *tlist;
547 
548 	/* Get the plan's primary targetlist */
549 	tlist = CachedPlanGetTargetList(stmt->plansource, NULL);
550 
551 	/* Copy into caller's context in case plan gets invalidated */
552 	return copyObject(tlist);
553 }
554 
555 /*
556  * Implements the 'DEALLOCATE' utility statement: deletes the
557  * specified plan from storage.
558  */
559 void
DeallocateQuery(DeallocateStmt * stmt)560 DeallocateQuery(DeallocateStmt *stmt)
561 {
562 	if (stmt->name)
563 		DropPreparedStatement(stmt->name, true);
564 	else
565 		DropAllPreparedStatements();
566 }
567 
568 /*
569  * Internal version of DEALLOCATE
570  *
571  * If showError is false, dropping a nonexistent statement is a no-op.
572  */
573 void
DropPreparedStatement(const char * stmt_name,bool showError)574 DropPreparedStatement(const char *stmt_name, bool showError)
575 {
576 	PreparedStatement *entry;
577 
578 	/* Find the query's hash table entry; raise error if wanted */
579 	entry = FetchPreparedStatement(stmt_name, showError);
580 
581 	if (entry)
582 	{
583 		/* Release the plancache entry */
584 		DropCachedPlan(entry->plansource);
585 
586 		/* Now we can remove the hash table entry */
587 		hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
588 	}
589 }
590 
591 /*
592  * Drop all cached statements.
593  */
594 void
DropAllPreparedStatements(void)595 DropAllPreparedStatements(void)
596 {
597 	HASH_SEQ_STATUS seq;
598 	PreparedStatement *entry;
599 
600 	/* nothing cached */
601 	if (!prepared_queries)
602 		return;
603 
604 	/* walk over cache */
605 	hash_seq_init(&seq, prepared_queries);
606 	while ((entry = hash_seq_search(&seq)) != NULL)
607 	{
608 		/* Release the plancache entry */
609 		DropCachedPlan(entry->plansource);
610 
611 		/* Now we can remove the hash table entry */
612 		hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
613 	}
614 }
615 
616 /*
617  * Implements the 'EXPLAIN EXECUTE' utility statement.
618  *
619  * "into" is NULL unless we are doing EXPLAIN CREATE TABLE AS EXECUTE,
620  * in which case executing the query should result in creating that table.
621  *
622  * Note: the passed-in queryString is that of the EXPLAIN EXECUTE,
623  * not the original PREPARE; we get the latter string from the plancache.
624  */
625 void
ExplainExecuteQuery(ExecuteStmt * execstmt,IntoClause * into,ExplainState * es,const char * queryString,ParamListInfo params,QueryEnvironment * queryEnv)626 ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
627 					const char *queryString, ParamListInfo params,
628 					QueryEnvironment *queryEnv)
629 {
630 	PreparedStatement *entry;
631 	const char *query_string;
632 	CachedPlan *cplan;
633 	List	   *plan_list;
634 	ListCell   *p;
635 	ParamListInfo paramLI = NULL;
636 	EState	   *estate = NULL;
637 	instr_time	planstart;
638 	instr_time	planduration;
639 
640 	INSTR_TIME_SET_CURRENT(planstart);
641 
642 	/* Look it up in the hash table */
643 	entry = FetchPreparedStatement(execstmt->name, true);
644 
645 	/* Shouldn't find a non-fixed-result cached plan */
646 	if (!entry->plansource->fixed_result)
647 		elog(ERROR, "EXPLAIN EXECUTE does not support variable-result cached plans");
648 
649 	query_string = entry->plansource->query_string;
650 
651 	/* Evaluate parameters, if any */
652 	if (entry->plansource->num_params)
653 	{
654 		/*
655 		 * Need an EState to evaluate parameters; must not delete it till end
656 		 * of query, in case parameters are pass-by-reference.  Note that the
657 		 * passed-in "params" could possibly be referenced in the parameter
658 		 * expressions.
659 		 */
660 		estate = CreateExecutorState();
661 		estate->es_param_list_info = params;
662 		paramLI = EvaluateParams(entry, execstmt->params,
663 								 queryString, estate);
664 	}
665 
666 	/* Replan if needed, and acquire a transient refcount */
667 	cplan = GetCachedPlan(entry->plansource, paramLI, true, queryEnv);
668 
669 	INSTR_TIME_SET_CURRENT(planduration);
670 	INSTR_TIME_SUBTRACT(planduration, planstart);
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);
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(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 " \
721 						"allowed in this context")));
722 
723 	/* need to build tuplestore in query context */
724 	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
725 	oldcontext = MemoryContextSwitchTo(per_query_ctx);
726 
727 	/*
728 	 * build tupdesc for result tuples. This must match the definition of the
729 	 * pg_prepared_statements view in system_views.sql
730 	 */
731 	tupdesc = CreateTemplateTupleDesc(5);
732 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
733 					   TEXTOID, -1, 0);
734 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
735 					   TEXTOID, -1, 0);
736 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepare_time",
737 					   TIMESTAMPTZOID, -1, 0);
738 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "parameter_types",
739 					   REGTYPEARRAYOID, -1, 0);
740 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "from_sql",
741 					   BOOLOID, -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[5];
764 			bool		nulls[5];
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 
775 			tuplestore_putvalues(tupstore, tupdesc, values, nulls);
776 		}
777 	}
778 
779 	/* clean up and return the tuplestore */
780 	tuplestore_donestoring(tupstore);
781 
782 	rsinfo->returnMode = SFRM_Materialize;
783 	rsinfo->setResult = tupstore;
784 	rsinfo->setDesc = tupdesc;
785 
786 	return (Datum) 0;
787 }
788 
789 /*
790  * This utility function takes a C array of Oids, and returns a Datum
791  * pointing to a one-dimensional Postgres array of regtypes. An empty
792  * array is returned as a zero-element array, not NULL.
793  */
794 static Datum
build_regtype_array(Oid * param_types,int num_params)795 build_regtype_array(Oid *param_types, int num_params)
796 {
797 	Datum	   *tmp_ary;
798 	ArrayType  *result;
799 	int			i;
800 
801 	tmp_ary = (Datum *) palloc(num_params * sizeof(Datum));
802 
803 	for (i = 0; i < num_params; i++)
804 		tmp_ary[i] = ObjectIdGetDatum(param_types[i]);
805 
806 	/* XXX: this hardcodes assumptions about the regtype type */
807 	result = construct_array(tmp_ary, num_params, REGTYPEOID, 4, true, 'i');
808 	return PointerGetDatum(result);
809 }
810