prepare.c 21.3 KB
Newer Older
1 2 3 4 5
/*-------------------------------------------------------------------------
 *
 * prepare.c
 *	  Prepareable SQL statements via PREPARE, EXECUTE and DEALLOCATE
 *
6 7 8 9
 * This module also implements storage of prepared statements that are
 * accessed via the extended FE/BE query protocol.
 *
 *
10
 * Copyright (c) 2002-2006, PostgreSQL Global Development Group
11 12
 *
 * IDENTIFICATION
13
 *	  $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.60 2006/08/12 02:52:04 tgl Exp $
14 15 16 17 18
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

19
#include "access/heapam.h"
20
#include "access/xact.h"
21
#include "catalog/pg_type.h"
22
#include "commands/explain.h"
23
#include "commands/prepare.h"
24
#include "funcapi.h"
25 26 27 28
#include "rewrite/rewriteHandler.h"
#include "tcop/pquery.h"
#include "tcop/tcopprot.h"
#include "tcop/utility.h"
29
#include "utils/builtins.h"
30 31
#include "utils/memutils.h"

32

33 34 35
/*
 * The hash table in which prepared queries are stored. This is
 * per-backend: query plans are not shared between backends.
36 37
 * The keys for this hash table are the arguments to PREPARE and EXECUTE
 * (statement names); the entries are PreparedStatement structs.
38 39 40 41
 */
static HTAB *prepared_queries = NULL;

static void InitQueryHashTable(void);
42
static ParamListInfo EvaluateParams(EState *estate,
B
Bruce Momjian 已提交
43
			   List *params, List *argtypes);
44
static Datum build_regtype_array(List *oid_list);
45 46 47 48 49 50 51

/*
 * Implements the 'PREPARE' utility statement.
 */
void
PrepareQuery(PrepareStmt *stmt)
{
52
	const char *commandTag;
53
	Query	   *query;
B
Bruce Momjian 已提交
54
	List	   *query_list,
55
			   *plan_list;
56

57 58 59 60 61
	/*
	 * Disallow empty-string statement name (conflicts with protocol-level
	 * unnamed statement).
	 */
	if (!stmt->name || stmt->name[0] == '\0')
62 63 64
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
				 errmsg("invalid statement name: must not be empty")));
65

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
	switch (stmt->query->commandType)
	{
		case CMD_SELECT:
			commandTag = "SELECT";
			break;
		case CMD_INSERT:
			commandTag = "INSERT";
			break;
		case CMD_UPDATE:
			commandTag = "UPDATE";
			break;
		case CMD_DELETE:
			commandTag = "DELETE";
			break;
		default:
81 82 83
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
					 errmsg("utility statements cannot be prepared")));
84 85 86
			commandTag = NULL;	/* keep compiler quiet */
			break;
	}
87

88
	/*
B
Bruce Momjian 已提交
89 90
	 * Parse analysis is already done, but we must still rewrite and plan the
	 * query.
91 92
	 */

93
	/*
B
Bruce Momjian 已提交
94 95
	 * Because the planner is not cool about not scribbling on its input, we
	 * make a preliminary copy of the source querytree.  This prevents
96 97
	 * problems in the case that the PREPARE is in a portal or plpgsql
	 * function and is executed repeatedly.  (See also the same hack in
B
Bruce Momjian 已提交
98 99
	 * DECLARE CURSOR and EXPLAIN.)  XXX the planner really shouldn't modify
	 * its input ... FIXME someday.
100 101 102
	 */
	query = copyObject(stmt->query);

103
	/* Rewrite the query. The result could be 0, 1, or many queries. */
104
	AcquireRewriteLocks(query);
105
	query_list = QueryRewrite(query);
106

B
Bruce Momjian 已提交
107
	/* Generate plans for queries.	Snapshot is already set. */
108
	plan_list = pg_plan_queries(query_list, NULL, false);
109

110
	/*
B
Bruce Momjian 已提交
111 112
	 * Save the results.  We don't have the query string for this PREPARE, but
	 * we do have the string we got from the client, so use that.
113
	 */
114
	StorePreparedStatement(stmt->name,
115
						   debug_query_string,
116 117 118
						   commandTag,
						   query_list,
						   plan_list,
119 120
						   stmt->argtype_oids,
						   true);
121 122 123 124 125 126
}

/*
 * Implements the 'EXECUTE' utility statement.
 */
void
127 128
ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
			 DestReceiver *dest, char *completionTag)
129
{
130 131
	PreparedStatement *entry;
	char	   *query_string;
132
	List	   *query_list,
B
Bruce Momjian 已提交
133
			   *plan_list;
134
	MemoryContext qcontext;
135
	ParamListInfo paramLI = NULL;
136
	EState	   *estate = NULL;
137
	Portal		portal;
138 139

	/* Look it up in the hash table */
140
	entry = FetchPreparedStatement(stmt->name, true);
141

142
	query_string = entry->query_string;
143 144
	query_list = entry->query_list;
	plan_list = entry->plan_list;
145
	qcontext = entry->context;
146

147
	Assert(list_length(query_list) == list_length(plan_list));
148 149 150 151

	/* Evaluate parameters, if any */
	if (entry->argtype_list != NIL)
	{
152
		/*
B
Bruce Momjian 已提交
153 154
		 * Need an EState to evaluate parameters; must not delete it till end
		 * of query, in case parameters are pass-by-reference.
155 156
		 */
		estate = CreateExecutorState();
157
		estate->es_param_list_info = params;
158
		paramLI = EvaluateParams(estate, stmt->params, entry->argtype_list);
159 160
	}

161
	/* Create a new portal to run the query in */
162
	portal = CreateNewPortal();
163 164 165
	/* Don't display the portal in pg_cursors, it is for internal use only */
	portal->visible = false;
	
166
	/*
B
Bruce Momjian 已提交
167 168 169 170
	 * For CREATE TABLE / AS EXECUTE, make a copy of the stored query so that
	 * we can modify its destination (yech, but this has always been ugly).
	 * For regular EXECUTE we can just use the stored query where it sits,
	 * since the executor is read-only.
171 172 173 174
	 */
	if (stmt->into)
	{
		MemoryContext oldContext;
B
Bruce Momjian 已提交
175
		Query	   *query;
176

177
		oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
178

179 180
		if (query_string)
			query_string = pstrdup(query_string);
181 182 183
		query_list = copyObject(query_list);
		plan_list = copyObject(plan_list);
		qcontext = PortalGetHeapMemory(portal);
184

185
		if (list_length(query_list) != 1)
186 187 188
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("prepared statement is not a SELECT")));
189
		query = (Query *) linitial(query_list);
190
		if (query->commandType != CMD_SELECT)
191 192 193
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("prepared statement is not a SELECT")));
194
		query->into = copyObject(stmt->into);
B
Bruce Momjian 已提交
195
		query->intoOptions = copyObject(stmt->intoOptions);
196 197 198
		query->intoOnCommit = stmt->into_on_commit;
		if (stmt->into_tbl_space)
			query->intoTableSpaceName = pstrdup(stmt->into_tbl_space);
199

200 201
		MemoryContextSwitchTo(oldContext);
	}
202

203
	PortalDefineQuery(portal,
204
					  NULL,
205 206
					  query_string,
					  entry->commandTag,
207 208 209
					  query_list,
					  plan_list,
					  qcontext);
210

211 212 213
	/*
	 * Run the portal to completion.
	 */
214
	PortalStart(portal, paramLI, ActiveSnapshot);
215

216
	(void) PortalRun(portal, FETCH_ALL, dest, dest, completionTag);
217

218
	PortalDrop(portal, false);
219

220 221
	if (estate)
		FreeExecutorState(estate);
222 223

	/* No need to pfree other memory, MemoryContext will be reset */
224 225
}

226 227
/*
 * Evaluates a list of parameters, using the given executor state. It
228
 * requires a list of the parameter expressions themselves, and a list of
229 230 231 232 233 234 235
 * their types. It returns a filled-in ParamListInfo -- this can later
 * be passed to CreateQueryDesc(), which allows the executor to make use
 * of the parameters during query execution.
 */
static ParamListInfo
EvaluateParams(EState *estate, List *params, List *argtypes)
{
236
	int			nargs = list_length(argtypes);
B
Bruce Momjian 已提交
237 238
	ParamListInfo paramLI;
	List	   *exprstates;
B
Bruce Momjian 已提交
239 240
	ListCell   *le,
			   *la;
B
Bruce Momjian 已提交
241
	int			i = 0;
242

243
	/* Parser should have caught this error, but check for safety */
244
	if (list_length(params) != nargs)
245
		elog(ERROR, "wrong number of arguments");
246

247 248 249
	if (nargs == 0)
		return NULL;

250 251
	exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);

252 253 254 255
	/* sizeof(ParamListInfoData) includes the first array element */
	paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
									 (nargs - 1) * sizeof(ParamExternData));
	paramLI->numParams = nargs;
256

257
	forboth(le, exprstates, la, argtypes)
258
	{
259
		ExprState  *n = lfirst(le);
260
		ParamExternData *prm = &paramLI->params[i];
261

262 263 264 265 266
		prm->ptype = lfirst_oid(la);
		prm->value = ExecEvalExprSwitchContext(n,
											   GetPerTupleExprContext(estate),
											   &prm->isnull,
											   NULL);
267 268 269 270 271 272 273 274

		i++;
	}

	return paramLI;
}


275 276 277 278 279 280
/*
 * Initialize query hash table upon first use.
 */
static void
InitQueryHashTable(void)
{
B
Bruce Momjian 已提交
281
	HASHCTL		hash_ctl;
282 283 284

	MemSet(&hash_ctl, 0, sizeof(hash_ctl));

285 286
	hash_ctl.keysize = NAMEDATALEN;
	hash_ctl.entrysize = sizeof(PreparedStatement);
287 288 289 290 291 292 293 294 295 296 297

	prepared_queries = hash_create("Prepared Queries",
								   32,
								   &hash_ctl,
								   HASH_ELEM);
}

/*
 * Store all the data pertaining to a query in the hash table using
 * the specified key. A copy of the data is made in a memory context belonging
 * to the hash entry, so the caller can dispose of their copy.
298 299
 *
 * Exception: commandTag is presumed to be a pointer to a constant string,
B
Bruce Momjian 已提交
300
 * or possibly NULL, so it need not be copied.	Note that commandTag should
301
 * be NULL only if the original query (before rewriting) was empty.
302
 */
303 304 305 306 307 308
void
StorePreparedStatement(const char *stmt_name,
					   const char *query_string,
					   const char *commandTag,
					   List *query_list,
					   List *plan_list,
309 310
					   List *argtype_list,
					   bool from_sql)
311
{
312
	PreparedStatement *entry;
313
	MemoryContext oldcxt,
B
Bruce Momjian 已提交
314
				entrycxt;
315 316
	char	   *qstring;
	char		key[NAMEDATALEN];
B
Bruce Momjian 已提交
317
	bool		found;
318 319 320 321 322 323

	/* Initialize the hash table, if necessary */
	if (!prepared_queries)
		InitQueryHashTable();

	/* Check for pre-existing entry of same name */
324
	/* See notes in FetchPreparedStatement */
325
	StrNCpy(key, stmt_name, sizeof(key));
326 327 328 329

	hash_search(prepared_queries, key, HASH_FIND, &found);

	if (found)
330 331 332 333
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_PSTATEMENT),
				 errmsg("prepared statement \"%s\" already exists",
						stmt_name)));
334

T
Tom Lane 已提交
335
	/* Make a permanent memory context for the hashtable entry */
336 337
	entrycxt = AllocSetContextCreate(TopMemoryContext,
									 stmt_name,
338 339 340
									 ALLOCSET_SMALL_MINSIZE,
									 ALLOCSET_SMALL_INITSIZE,
									 ALLOCSET_SMALL_MAXSIZE);
341 342 343 344

	oldcxt = MemoryContextSwitchTo(entrycxt);

	/*
B
Bruce Momjian 已提交
345 346
	 * We need to copy the data so that it is stored in the correct memory
	 * context.  Do this before making hashtable entry, so that an
B
Bruce Momjian 已提交
347 348
	 * out-of-memory failure only wastes memory and doesn't leave us with an
	 * incomplete (ie corrupt) hashtable entry.
349
	 */
350
	qstring = query_string ? pstrdup(query_string) : NULL;
351 352
	query_list = (List *) copyObject(query_list);
	plan_list = (List *) copyObject(plan_list);
353
	argtype_list = list_copy(argtype_list);
354 355

	/* Now we can add entry to hash table */
356 357 358 359
	entry = (PreparedStatement *) hash_search(prepared_queries,
											  key,
											  HASH_ENTER,
											  &found);
360

361 362 363
	/* Shouldn't get a duplicate entry */
	if (found)
		elog(ERROR, "duplicate prepared statement \"%s\"",
364 365 366
			 stmt_name);

	/* Fill in the hash table entry with copied data */
367 368
	entry->query_string = qstring;
	entry->commandTag = commandTag;
369 370 371 372
	entry->query_list = query_list;
	entry->plan_list = plan_list;
	entry->argtype_list = argtype_list;
	entry->context = entrycxt;
373
	entry->prepare_time = GetCurrentStatementStartTimestamp();
374
	entry->from_sql = from_sql;
375 376 377 378 379

	MemoryContextSwitchTo(oldcxt);
}

/*
380
 * Lookup an existing query in the hash table. If the query does not
381
 * actually exist, throw ereport(ERROR) or return NULL per second parameter.
382
 */
383 384
PreparedStatement *
FetchPreparedStatement(const char *stmt_name, bool throwError)
385
{
386 387
	char		key[NAMEDATALEN];
	PreparedStatement *entry;
388 389 390 391 392

	/*
	 * If the hash table hasn't been initialized, it can't be storing
	 * anything, therefore it couldn't possibly store our plan.
	 */
393 394 395
	if (prepared_queries)
	{
		/*
B
Bruce Momjian 已提交
396
		 * We can't just use the statement name as supplied by the user: the
397
		 * hash package is picky enough that it needs to be NUL-padded out to
B
Bruce Momjian 已提交
398
		 * the appropriate length to work correctly.
399
		 */
400
		StrNCpy(key, stmt_name, sizeof(key));
401

402 403 404 405 406 407 408
		entry = (PreparedStatement *) hash_search(prepared_queries,
												  key,
												  HASH_FIND,
												  NULL);
	}
	else
		entry = NULL;
409

410
	if (!entry && throwError)
411 412 413 414
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_PSTATEMENT),
				 errmsg("prepared statement \"%s\" does not exist",
						stmt_name)));
415 416 417 418 419

	return entry;
}

/*
420
 * Look up a prepared statement given the name (giving error if not found).
421 422 423
 * If found, return the list of argument type OIDs.
 */
List *
424
FetchPreparedStatementParams(const char *stmt_name)
425
{
426
	PreparedStatement *entry;
427

428
	entry = FetchPreparedStatement(stmt_name, true);
429 430 431 432

	return entry->argtype_list;
}

433 434 435 436 437 438 439
/*
 * Given a prepared statement, determine the result tupledesc it will
 * produce.  Returns NULL if the execution will not return tuples.
 *
 * Note: the result is created or copied into current memory context.
 */
TupleDesc
440
FetchPreparedStatementResultDesc(PreparedStatement *stmt)
441
{
B
Bruce Momjian 已提交
442
	Query	   *query;
443 444 445 446

	switch (ChoosePortalStrategy(stmt->query_list))
	{
		case PORTAL_ONE_SELECT:
447
			query = (Query *) linitial(stmt->query_list);
448 449
			return ExecCleanTypeFromTL(query->targetList, false);

450 451 452 453
		case PORTAL_ONE_RETURNING:
			query = (Query *) linitial(stmt->query_list);
			return ExecCleanTypeFromTL(query->returningList, false);

454
		case PORTAL_UTIL_SELECT:
455
			query = (Query *) linitial(stmt->query_list);
456 457 458 459 460 461 462 463 464
			return UtilityTupleDescriptor(query->utilityStmt);

		case PORTAL_MULTI_QUERY:
			/* will not return tuples */
			break;
	}
	return NULL;
}

465 466 467 468 469 470 471 472 473 474 475 476 477 478
/*
 * Given a prepared statement, determine whether it will return tuples.
 *
 * Note: this is used rather than just testing the result of
 * FetchPreparedStatementResultDesc() because that routine can fail if
 * invoked in an aborted transaction.  This one is safe to use in any
 * context.  Be sure to keep the two routines in sync!
 */
bool
PreparedStatementReturnsTuples(PreparedStatement *stmt)
{
	switch (ChoosePortalStrategy(stmt->query_list))
	{
		case PORTAL_ONE_SELECT:
479
		case PORTAL_ONE_RETURNING:
480 481 482 483 484 485 486 487 488 489
		case PORTAL_UTIL_SELECT:
			return true;

		case PORTAL_MULTI_QUERY:
			/* will not return tuples */
			break;
	}
	return false;
}

490 491
/*
 * Given a prepared statement that returns tuples, extract the query
B
Bruce Momjian 已提交
492
 * targetlist.	Returns NIL if the statement doesn't have a determinable
493 494 495 496 497 498 499 500 501 502 503 504 505 506
 * targetlist.
 *
 * Note: do not modify the result.
 *
 * XXX be careful to keep this in sync with FetchPortalTargetList,
 * and with UtilityReturnsTuples.
 */
List *
FetchPreparedStatementTargetList(PreparedStatement *stmt)
{
	PortalStrategy strategy = ChoosePortalStrategy(stmt->query_list);

	if (strategy == PORTAL_ONE_SELECT)
		return ((Query *) linitial(stmt->query_list))->targetList;
507 508
	if (strategy == PORTAL_ONE_RETURNING)
		return ((Query *) linitial(stmt->query_list))->returningList;
509 510
	if (strategy == PORTAL_UTIL_SELECT)
	{
B
Bruce Momjian 已提交
511
		Node	   *utilityStmt;
512 513 514 515 516

		utilityStmt = ((Query *) linitial(stmt->query_list))->utilityStmt;
		switch (nodeTag(utilityStmt))
		{
			case T_FetchStmt:
B
Bruce Momjian 已提交
517 518 519
				{
					FetchStmt  *substmt = (FetchStmt *) utilityStmt;
					Portal		subportal;
520

B
Bruce Momjian 已提交
521 522 523 524 525
					Assert(!substmt->ismove);
					subportal = GetPortalByName(substmt->portalname);
					Assert(PortalIsValid(subportal));
					return FetchPortalTargetList(subportal);
				}
526 527

			case T_ExecuteStmt:
B
Bruce Momjian 已提交
528 529 530
				{
					ExecuteStmt *substmt = (ExecuteStmt *) utilityStmt;
					PreparedStatement *entry;
531

B
Bruce Momjian 已提交
532 533 534 535
					Assert(!substmt->into);
					entry = FetchPreparedStatement(substmt->name, true);
					return FetchPreparedStatementTargetList(entry);
				}
536 537 538 539 540 541 542 543

			default:
				break;
		}
	}
	return NIL;
}

544 545 546 547 548 549 550
/*
 * Implements the 'DEALLOCATE' utility statement: deletes the
 * specified plan from storage.
 */
void
DeallocateQuery(DeallocateStmt *stmt)
{
551 552 553 554 555 556 557 558 559 560 561 562
	DropPreparedStatement(stmt->name, true);
}

/*
 * Internal version of DEALLOCATE
 *
 * If showError is false, dropping a nonexistent statement is a no-op.
 */
void
DropPreparedStatement(const char *stmt_name, bool showError)
{
	PreparedStatement *entry;
563

564 565
	/* Find the query's hash table entry; raise error if wanted */
	entry = FetchPreparedStatement(stmt_name, showError);
566

567 568 569 570 571
	if (entry)
	{
		/* Drop any open portals that depend on this prepared statement */
		Assert(MemoryContextIsValid(entry->context));
		DropDependentPortals(entry->context);
572

573 574
		/* Flush the context holding the subsidiary data */
		MemoryContextDelete(entry->context);
575

576 577 578
		/* Now we can remove the hash table entry */
		hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
	}
579 580 581 582 583 584
}

/*
 * Implements the 'EXPLAIN EXECUTE' utility statement.
 */
void
585 586
ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
					TupOutputState *tstate)
587
{
B
Bruce Momjian 已提交
588
	ExecuteStmt *execstmt = (ExecuteStmt *) stmt->query->utilityStmt;
589
	PreparedStatement *entry;
590 591 592
	ListCell   *q,
			   *p;
	List	   *query_list,
593 594 595 596 597 598 599 600
			   *plan_list;
	ParamListInfo paramLI = NULL;
	EState	   *estate = NULL;

	/* explain.c should only call me for EXECUTE stmt */
	Assert(execstmt && IsA(execstmt, ExecuteStmt));

	/* Look it up in the hash table */
601
	entry = FetchPreparedStatement(execstmt->name, true);
602 603 604 605

	query_list = entry->query_list;
	plan_list = entry->plan_list;

606
	Assert(list_length(query_list) == list_length(plan_list));
607 608 609 610 611

	/* Evaluate parameters, if any */
	if (entry->argtype_list != NIL)
	{
		/*
B
Bruce Momjian 已提交
612 613
		 * Need an EState to evaluate parameters; must not delete it till end
		 * of query, in case parameters are pass-by-reference.
614 615
		 */
		estate = CreateExecutorState();
616
		estate->es_param_list_info = params;
617 618 619 620 621
		paramLI = EvaluateParams(estate, execstmt->params,
								 entry->argtype_list);
	}

	/* Explain each query */
B
Bruce Momjian 已提交
622
	forboth(q, query_list, p, plan_list)
623
	{
624 625
		Query	   *query = (Query *) lfirst(q);
		Plan	   *plan = (Plan *) lfirst(p);
626 627
		bool		is_last_query;

628
		is_last_query = (lnext(p) == NULL);
629 630 631 632 633 634 635 636 637 638 639 640 641 642

		if (query->commandType == CMD_UTILITY)
		{
			if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
				do_text_output_oneline(tstate, "NOTIFY");
			else
				do_text_output_oneline(tstate, "UTILITY");
		}
		else
		{
			QueryDesc  *qdesc;

			if (execstmt->into)
			{
643
				if (query->commandType != CMD_SELECT)
644 645
					ereport(ERROR,
							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
B
Bruce Momjian 已提交
646
							 errmsg("prepared statement is not a SELECT")));
647

648 649 650
				/* Copy the query so we can modify it */
				query = copyObject(query);

651 652 653
				query->into = execstmt->into;
			}

654 655 656 657 658 659 660 661 662
			/*
			 * Update snapshot command ID to ensure this query sees results of
			 * any previously executed queries.  (It's a bit cheesy to modify
			 * ActiveSnapshot without making a copy, but for the limited ways
			 * in which EXPLAIN can be invoked, I think it's OK, because the
			 * active snapshot shouldn't be shared with anything else anyway.)
			 */
			ActiveSnapshot->curcid = GetCurrentCommandId();

663
			/* Create a QueryDesc requesting no output */
664 665 666
			qdesc = CreateQueryDesc(query, plan,
									ActiveSnapshot, InvalidSnapshot,
									None_Receiver,
667 668
									paramLI, stmt->analyze);

669 670 671 672 673 674 675 676 677 678 679 680
			ExplainOnePlan(qdesc, stmt, tstate);
		}

		/* No need for CommandCounterIncrement, as ExplainOnePlan did it */

		/* put a blank line between plans */
		if (!is_last_query)
			do_text_output_oneline(tstate, "");
	}

	if (estate)
		FreeExecutorState(estate);
681
}
682 683 684

/*
 * This set returning function reads all the prepared statements and
685
 * returns a set of (name, statement, prepare_time, param_types, from_sql).
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731
 */
Datum
pg_prepared_statement(PG_FUNCTION_ARGS)
{
	FuncCallContext	   *funcctx;
	HASH_SEQ_STATUS    *hash_seq;
	PreparedStatement  *prep_stmt;

	/* stuff done only on the first call of the function */
	if (SRF_IS_FIRSTCALL())
	{
		TupleDesc		tupdesc;
		MemoryContext	oldcontext;

		/* create a function context for cross-call persistence */
		funcctx = SRF_FIRSTCALL_INIT();

		/*
		 * switch to memory context appropriate for multiple function
		 * calls
		 */
		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);

		/* allocate memory for user context */
		if (prepared_queries)
		{
			hash_seq = (HASH_SEQ_STATUS *) palloc(sizeof(HASH_SEQ_STATUS));
			hash_seq_init(hash_seq, prepared_queries);
			funcctx->user_fctx = (void *) hash_seq;
		}
		else
			funcctx->user_fctx = NULL;

		/*
		 * build tupdesc for result tuples. This must match the
		 * definition of the pg_prepared_statements view in
		 * system_views.sql
		 */
		tupdesc = CreateTemplateTupleDesc(5, false);
		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
						   TEXTOID, -1, 0);
		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepare_time",
						   TIMESTAMPTZOID, -1, 0);
		TupleDescInitEntry(tupdesc, (AttrNumber) 4, "parameter_types",
732
						   REGTYPEARRAYOID, -1, 0);
733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "from_sql",
						   BOOLOID, -1, 0);

		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
		MemoryContextSwitchTo(oldcontext);
	}

	/* stuff done on every call of the function */
	funcctx = SRF_PERCALL_SETUP();
	hash_seq = (HASH_SEQ_STATUS *) funcctx->user_fctx;

	/* if the hash table is uninitialized, we're done */
	if (hash_seq == NULL)
		SRF_RETURN_DONE(funcctx);

	prep_stmt = hash_seq_search(hash_seq);
	if (prep_stmt)
	{
		Datum			result;
		HeapTuple		tuple;
		Datum			values[5];
		bool			nulls[5];

		MemSet(nulls, 0, sizeof(nulls));

		values[0] = DirectFunctionCall1(textin,
										CStringGetDatum(prep_stmt->stmt_name));

		if (prep_stmt->query_string == NULL)
			nulls[1] = true;
		else
			values[1] = DirectFunctionCall1(textin,
									CStringGetDatum(prep_stmt->query_string));

		values[2] = TimestampTzGetDatum(prep_stmt->prepare_time);
768
		values[3] = build_regtype_array(prep_stmt->argtype_list);
769 770 771 772 773 774 775 776 777 778 779 780
		values[4] = BoolGetDatum(prep_stmt->from_sql);

		tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
		result = HeapTupleGetDatum(tuple);
		SRF_RETURN_NEXT(funcctx, result);
	}

	SRF_RETURN_DONE(funcctx);
}

/*
 * This utility function takes a List of Oids, and returns a Datum
781 782
 * pointing to a one-dimensional Postgres array of regtypes. The empty
 * list is returned as a zero-element array, not NULL.
783 784
 */
static Datum
785
build_regtype_array(List *oid_list)
786
{
787 788 789 790 791
	ListCell   *lc;
	int			len;
	int			i;
	Datum	   *tmp_ary;
	ArrayType  *result;
792 793 794 795 796 797

	len = list_length(oid_list);
	tmp_ary = (Datum *) palloc(len * sizeof(Datum));

	i = 0;
	foreach(lc, oid_list)
798 799 800 801 802 803 804 805
	{
		Oid		oid;
		Datum	oid_str;

		oid = lfirst_oid(lc);
		oid_str = DirectFunctionCall1(oidout, ObjectIdGetDatum(oid));
		tmp_ary[i++] = DirectFunctionCall1(regtypein, oid_str);
	}
806

807 808 809
	/* XXX: this hardcodes assumptions about the regtype type */
	result = construct_array(tmp_ary, len, REGTYPEOID, 4, true, 'i');
	return PointerGetDatum(result);
810
}