OSDN Git Service

Move exprType(), exprTypmod(), expression_tree_walker(), and related routines
[pg-rex/syncrep.git] / src / backend / commands / prepare.c
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-2008, PostgreSQL Global Development Group
11  *
12  * IDENTIFICATION
13  *        $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.90 2008/08/25 22:42:32 tgl Exp $
14  *
15  *-------------------------------------------------------------------------
16  */
17 #include "postgres.h"
18
19 #include "access/xact.h"
20 #include "catalog/pg_type.h"
21 #include "commands/explain.h"
22 #include "commands/prepare.h"
23 #include "miscadmin.h"
24 #include "nodes/nodeFuncs.h"
25 #include "parser/analyze.h"
26 #include "parser/parse_coerce.h"
27 #include "parser/parse_expr.h"
28 #include "parser/parse_type.h"
29 #include "rewrite/rewriteHandler.h"
30 #include "tcop/pquery.h"
31 #include "tcop/tcopprot.h"
32 #include "tcop/utility.h"
33 #include "utils/builtins.h"
34 #include "utils/memutils.h"
35 #include "utils/snapmgr.h"
36
37
38 /*
39  * The hash table in which prepared queries are stored. This is
40  * per-backend: query plans are not shared between backends.
41  * The keys for this hash table are the arguments to PREPARE and EXECUTE
42  * (statement names); the entries are PreparedStatement structs.
43  */
44 static HTAB *prepared_queries = NULL;
45
46 static void InitQueryHashTable(void);
47 static ParamListInfo EvaluateParams(PreparedStatement *pstmt, List *params,
48                            const char *queryString, EState *estate);
49 static Datum build_regtype_array(Oid *param_types, int num_params);
50
51 /*
52  * Implements the 'PREPARE' utility statement.
53  */
54 void
55 PrepareQuery(PrepareStmt *stmt, const char *queryString)
56 {
57         Oid                *argtypes = NULL;
58         int                     nargs;
59         Query      *query;
60         List       *query_list,
61                            *plan_list;
62         int                     i;
63
64         /*
65          * Disallow empty-string statement name (conflicts with protocol-level
66          * unnamed statement).
67          */
68         if (!stmt->name || stmt->name[0] == '\0')
69                 ereport(ERROR,
70                                 (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
71                                  errmsg("invalid statement name: must not be empty")));
72
73         /* Transform list of TypeNames to array of type OIDs */
74         nargs = list_length(stmt->argtypes);
75
76         if (nargs)
77         {
78                 ParseState *pstate;
79                 ListCell   *l;
80
81                 /*
82                  * typenameTypeId wants a ParseState to carry the source query string.
83                  * Is it worth refactoring its API to avoid this?
84                  */
85                 pstate = make_parsestate(NULL);
86                 pstate->p_sourcetext = queryString;
87
88                 argtypes = (Oid *) palloc(nargs * sizeof(Oid));
89                 i = 0;
90
91                 foreach(l, stmt->argtypes)
92                 {
93                         TypeName   *tn = lfirst(l);
94                         Oid                     toid = typenameTypeId(pstate, tn, NULL);
95
96                         argtypes[i++] = toid;
97                 }
98         }
99
100         /*
101          * Analyze the statement using these parameter types (any parameters
102          * passed in from above us will not be visible to it), allowing
103          * information about unknown parameters to be deduced from context.
104          *
105          * Because parse analysis scribbles on the raw querytree, we must make a
106          * copy to ensure we have a pristine raw tree to cache.  FIXME someday.
107          */
108         query = parse_analyze_varparams((Node *) copyObject(stmt->query),
109                                                                         queryString,
110                                                                         &argtypes, &nargs);
111
112         /*
113          * Check that all parameter types were determined.
114          */
115         for (i = 0; i < nargs; i++)
116         {
117                 Oid                     argtype = argtypes[i];
118
119                 if (argtype == InvalidOid || argtype == UNKNOWNOID)
120                         ereport(ERROR,
121                                         (errcode(ERRCODE_INDETERMINATE_DATATYPE),
122                                          errmsg("could not determine data type of parameter $%d",
123                                                         i + 1)));
124         }
125
126         /*
127          * grammar only allows OptimizableStmt, so this check should be redundant
128          */
129         switch (query->commandType)
130         {
131                 case CMD_SELECT:
132                 case CMD_INSERT:
133                 case CMD_UPDATE:
134                 case CMD_DELETE:
135                         /* OK */
136                         break;
137                 default:
138                         ereport(ERROR,
139                                         (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
140                                          errmsg("utility statements cannot be prepared")));
141                         break;
142         }
143
144         /* Rewrite the query. The result could be 0, 1, or many queries. */
145         query_list = QueryRewrite(query);
146
147         /* Generate plans for queries.  Snapshot is already set. */
148         plan_list = pg_plan_queries(query_list, 0, NULL, false);
149
150         /*
151          * Save the results.
152          */
153         StorePreparedStatement(stmt->name,
154                                                    stmt->query,
155                                                    queryString,
156                                                    CreateCommandTag((Node *) query),
157                                                    argtypes,
158                                                    nargs,
159                                                    0,   /* default cursor options */
160                                                    plan_list,
161                                                    true);
162 }
163
164 /*
165  * Implements the 'EXECUTE' utility statement.
166  *
167  * Note: this is one of very few places in the code that needs to deal with
168  * two query strings at once.  The passed-in queryString is that of the
169  * EXECUTE, which we might need for error reporting while processing the
170  * parameter expressions.  The query_string that we copy from the plan
171  * source is that of the original PREPARE.
172  */
173 void
174 ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
175                          ParamListInfo params,
176                          DestReceiver *dest, char *completionTag)
177 {
178         PreparedStatement *entry;
179         CachedPlan *cplan;
180         List       *plan_list;
181         ParamListInfo paramLI = NULL;
182         EState     *estate = NULL;
183         Portal          portal;
184         char       *query_string;
185
186         /* Look it up in the hash table */
187         entry = FetchPreparedStatement(stmt->name, true);
188
189         /* Shouldn't have a non-fully-planned plancache entry */
190         if (!entry->plansource->fully_planned)
191                 elog(ERROR, "EXECUTE does not support unplanned prepared statements");
192         /* Shouldn't get any non-fixed-result cached plan, either */
193         if (!entry->plansource->fixed_result)
194                 elog(ERROR, "EXECUTE does not support variable-result cached plans");
195
196         /* Evaluate parameters, if any */
197         if (entry->plansource->num_params > 0)
198         {
199                 /*
200                  * Need an EState to evaluate parameters; must not delete it till end
201                  * of query, in case parameters are pass-by-reference.
202                  */
203                 estate = CreateExecutorState();
204                 estate->es_param_list_info = params;
205                 paramLI = EvaluateParams(entry, stmt->params,
206                                                                  queryString, estate);
207         }
208
209         /* Create a new portal to run the query in */
210         portal = CreateNewPortal();
211         /* Don't display the portal in pg_cursors, it is for internal use only */
212         portal->visible = false;
213
214         /* Copy the plan's saved query string into the portal's memory */
215         query_string = MemoryContextStrdup(PortalGetHeapMemory(portal),
216                                                                            entry->plansource->query_string);
217
218         /*
219          * For CREATE TABLE / AS EXECUTE, we must make a copy of the stored query
220          * so that we can modify its destination (yech, but this has always been
221          * ugly).  For regular EXECUTE we can just use the cached query, since the
222          * executor is read-only.
223          */
224         if (stmt->into)
225         {
226                 MemoryContext oldContext;
227                 PlannedStmt *pstmt;
228
229                 /* Replan if needed, and increment plan refcount transiently */
230                 cplan = RevalidateCachedPlan(entry->plansource, true);
231
232                 /* Copy plan into portal's context, and modify */
233                 oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
234
235                 plan_list = copyObject(cplan->stmt_list);
236
237                 if (list_length(plan_list) != 1)
238                         ereport(ERROR,
239                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
240                                          errmsg("prepared statement is not a SELECT")));
241                 pstmt = (PlannedStmt *) linitial(plan_list);
242                 if (!IsA(pstmt, PlannedStmt) ||
243                         pstmt->commandType != CMD_SELECT ||
244                         pstmt->utilityStmt != NULL)
245                         ereport(ERROR,
246                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
247                                          errmsg("prepared statement is not a SELECT")));
248                 pstmt->intoClause = copyObject(stmt->into);
249
250                 MemoryContextSwitchTo(oldContext);
251
252                 /* We no longer need the cached plan refcount ... */
253                 ReleaseCachedPlan(cplan, true);
254                 /* ... and we don't want the portal to depend on it, either */
255                 cplan = NULL;
256         }
257         else
258         {
259                 /* Replan if needed, and increment plan refcount for portal */
260                 cplan = RevalidateCachedPlan(entry->plansource, false);
261                 plan_list = cplan->stmt_list;
262         }
263
264         PortalDefineQuery(portal,
265                                           NULL,
266                                           query_string,
267                                           entry->plansource->commandTag,
268                                           plan_list,
269                                           cplan);
270
271         /*
272          * Run the portal to completion.
273          */
274         PortalStart(portal, paramLI, GetActiveSnapshot());
275
276         (void) PortalRun(portal, FETCH_ALL, false, dest, dest, completionTag);
277
278         PortalDrop(portal, false);
279
280         if (estate)
281                 FreeExecutorState(estate);
282
283         /* No need to pfree other memory, MemoryContext will be reset */
284 }
285
286 /*
287  * EvaluateParams: evaluate a list of parameters.
288  *
289  * pstmt: statement we are getting parameters for.
290  * params: list of given parameter expressions (raw parser output!)
291  * queryString: source text for error messages.
292  * estate: executor state to use.
293  *
294  * Returns a filled-in ParamListInfo -- this can later be passed to
295  * CreateQueryDesc(), which allows the executor to make use of the parameters
296  * during query execution.
297  */
298 static ParamListInfo
299 EvaluateParams(PreparedStatement *pstmt, List *params,
300                            const char *queryString, EState *estate)
301 {
302         Oid                *param_types = pstmt->plansource->param_types;
303         int                     num_params = pstmt->plansource->num_params;
304         int                     nparams = list_length(params);
305         ParseState *pstate;
306         ParamListInfo paramLI;
307         List       *exprstates;
308         ListCell   *l;
309         int                     i;
310
311         if (nparams != num_params)
312                 ereport(ERROR,
313                                 (errcode(ERRCODE_SYNTAX_ERROR),
314                    errmsg("wrong number of parameters for prepared statement \"%s\"",
315                                   pstmt->stmt_name),
316                                  errdetail("Expected %d parameters but got %d.",
317                                                    num_params, nparams)));
318
319         /* Quick exit if no parameters */
320         if (num_params == 0)
321                 return NULL;
322
323         /*
324          * We have to run parse analysis for the expressions.  Since the parser is
325          * not cool about scribbling on its input, copy first.
326          */
327         params = (List *) copyObject(params);
328
329         pstate = make_parsestate(NULL);
330         pstate->p_sourcetext = queryString;
331
332         i = 0;
333         foreach(l, params)
334         {
335                 Node       *expr = lfirst(l);
336                 Oid                     expected_type_id = param_types[i];
337                 Oid                     given_type_id;
338
339                 expr = transformExpr(pstate, expr);
340
341                 /* Cannot contain subselects or aggregates */
342                 if (pstate->p_hasSubLinks)
343                         ereport(ERROR,
344                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
345                                          errmsg("cannot use subquery in EXECUTE parameter")));
346                 if (pstate->p_hasAggs)
347                         ereport(ERROR,
348                                         (errcode(ERRCODE_GROUPING_ERROR),
349                           errmsg("cannot use aggregate function in EXECUTE parameter")));
350
351                 given_type_id = exprType(expr);
352
353                 expr = coerce_to_target_type(pstate, expr, given_type_id,
354                                                                          expected_type_id, -1,
355                                                                          COERCION_ASSIGNMENT,
356                                                                          COERCE_IMPLICIT_CAST);
357
358                 if (expr == NULL)
359                         ereport(ERROR,
360                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
361                                          errmsg("parameter $%d of type %s cannot be coerced to the expected type %s",
362                                                         i + 1,
363                                                         format_type_be(given_type_id),
364                                                         format_type_be(expected_type_id)),
365                            errhint("You will need to rewrite or cast the expression.")));
366
367                 lfirst(l) = expr;
368                 i++;
369         }
370
371         /* Prepare the expressions for execution */
372         exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);
373
374         /* sizeof(ParamListInfoData) includes the first array element */
375         paramLI = (ParamListInfo)
376                 palloc(sizeof(ParamListInfoData) +
377                            (num_params - 1) *sizeof(ParamExternData));
378         paramLI->numParams = num_params;
379
380         i = 0;
381         foreach(l, exprstates)
382         {
383                 ExprState  *n = lfirst(l);
384                 ParamExternData *prm = &paramLI->params[i];
385
386                 prm->ptype = param_types[i];
387                 prm->pflags = 0;
388                 prm->value = ExecEvalExprSwitchContext(n,
389                                                                                            GetPerTupleExprContext(estate),
390                                                                                            &prm->isnull,
391                                                                                            NULL);
392
393                 i++;
394         }
395
396         return paramLI;
397 }
398
399
400 /*
401  * Initialize query hash table upon first use.
402  */
403 static void
404 InitQueryHashTable(void)
405 {
406         HASHCTL         hash_ctl;
407
408         MemSet(&hash_ctl, 0, sizeof(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);
417 }
418
419 /*
420  * Store all the data pertaining to a query in the hash table using
421  * the specified key.  All the given data is copied into either the hashtable
422  * entry or the underlying plancache entry, so the caller can dispose of its
423  * copy.
424  *
425  * Exception: commandTag is presumed to be a pointer to a constant string,
426  * or possibly NULL, so it need not be copied.  Note that commandTag should
427  * be NULL only if the original query (before rewriting) was empty.
428  */
429 void
430 StorePreparedStatement(const char *stmt_name,
431                                            Node *raw_parse_tree,
432                                            const char *query_string,
433                                            const char *commandTag,
434                                            Oid *param_types,
435                                            int num_params,
436                                            int cursor_options,
437                                            List *stmt_list,
438                                            bool from_sql)
439 {
440         PreparedStatement *entry;
441         CachedPlanSource *plansource;
442         bool            found;
443
444         /* Initialize the hash table, if necessary */
445         if (!prepared_queries)
446                 InitQueryHashTable();
447
448         /* Check for pre-existing entry of same name */
449         hash_search(prepared_queries, stmt_name, HASH_FIND, &found);
450
451         if (found)
452                 ereport(ERROR,
453                                 (errcode(ERRCODE_DUPLICATE_PSTATEMENT),
454                                  errmsg("prepared statement \"%s\" already exists",
455                                                 stmt_name)));
456
457         /* Create a plancache entry */
458         plansource = CreateCachedPlan(raw_parse_tree,
459                                                                   query_string,
460                                                                   commandTag,
461                                                                   param_types,
462                                                                   num_params,
463                                                                   cursor_options,
464                                                                   stmt_list,
465                                                                   true,
466                                                                   true);
467
468         /* Now we can add entry to hash table */
469         entry = (PreparedStatement *) hash_search(prepared_queries,
470                                                                                           stmt_name,
471                                                                                           HASH_ENTER,
472                                                                                           &found);
473
474         /* Shouldn't get a duplicate entry */
475         if (found)
476                 elog(ERROR, "duplicate prepared statement \"%s\"",
477                          stmt_name);
478
479         /* Fill in the hash table entry */
480         entry->plansource = plansource;
481         entry->from_sql = from_sql;
482         entry->prepare_time = GetCurrentStatementStartTimestamp();
483 }
484
485 /*
486  * Lookup an existing query in the hash table. If the query does not
487  * actually exist, throw ereport(ERROR) or return NULL per second parameter.
488  *
489  * Note: this does not force the referenced plancache entry to be valid,
490  * since not all callers care.
491  */
492 PreparedStatement *
493 FetchPreparedStatement(const char *stmt_name, bool throwError)
494 {
495         PreparedStatement *entry;
496
497         /*
498          * If the hash table hasn't been initialized, it can't be storing
499          * anything, therefore it couldn't possibly store our plan.
500          */
501         if (prepared_queries)
502                 entry = (PreparedStatement *) hash_search(prepared_queries,
503                                                                                                   stmt_name,
504                                                                                                   HASH_FIND,
505                                                                                                   NULL);
506         else
507                 entry = NULL;
508
509         if (!entry && throwError)
510                 ereport(ERROR,
511                                 (errcode(ERRCODE_UNDEFINED_PSTATEMENT),
512                                  errmsg("prepared statement \"%s\" does not exist",
513                                                 stmt_name)));
514
515         return entry;
516 }
517
518 /*
519  * Given a prepared statement, determine the result tupledesc it will
520  * produce.  Returns NULL if the execution will not return tuples.
521  *
522  * Note: the result is created or copied into current memory context.
523  */
524 TupleDesc
525 FetchPreparedStatementResultDesc(PreparedStatement *stmt)
526 {
527         /*
528          * Since we don't allow prepared statements' result tupdescs to change,
529          * there's no need for a revalidate call here.
530          */
531         Assert(stmt->plansource->fixed_result);
532         if (stmt->plansource->resultDesc)
533                 return CreateTupleDescCopy(stmt->plansource->resultDesc);
534         else
535                 return NULL;
536 }
537
538 /*
539  * Given a prepared statement that returns tuples, extract the query
540  * targetlist.  Returns NIL if the statement doesn't have a determinable
541  * targetlist.
542  *
543  * Note: this is pretty ugly, but since it's only used in corner cases like
544  * Describe Statement on an EXECUTE command, we don't worry too much about
545  * efficiency.
546  */
547 List *
548 FetchPreparedStatementTargetList(PreparedStatement *stmt)
549 {
550         List       *tlist;
551         CachedPlan *cplan;
552
553         /* No point in looking if it doesn't return tuples */
554         if (stmt->plansource->resultDesc == NULL)
555                 return NIL;
556
557         /* Make sure the plan is up to date */
558         cplan = RevalidateCachedPlan(stmt->plansource, true);
559
560         /* Get the primary statement and find out what it returns */
561         tlist = FetchStatementTargetList(PortalListGetPrimaryStmt(cplan->stmt_list));
562
563         /* Copy into caller's context so we can release the plancache entry */
564         tlist = (List *) copyObject(tlist);
565
566         ReleaseCachedPlan(cplan, true);
567
568         return tlist;
569 }
570
571 /*
572  * Implements the 'DEALLOCATE' utility statement: deletes the
573  * specified plan from storage.
574  */
575 void
576 DeallocateQuery(DeallocateStmt *stmt)
577 {
578         if (stmt->name)
579                 DropPreparedStatement(stmt->name, true);
580         else
581                 DropAllPreparedStatements();
582 }
583
584 /*
585  * Internal version of DEALLOCATE
586  *
587  * If showError is false, dropping a nonexistent statement is a no-op.
588  */
589 void
590 DropPreparedStatement(const char *stmt_name, bool showError)
591 {
592         PreparedStatement *entry;
593
594         /* Find the query's hash table entry; raise error if wanted */
595         entry = FetchPreparedStatement(stmt_name, showError);
596
597         if (entry)
598         {
599                 /* Release the plancache entry */
600                 DropCachedPlan(entry->plansource);
601
602                 /* Now we can remove the hash table entry */
603                 hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
604         }
605 }
606
607 /*
608  * Drop all cached statements.
609  */
610 void
611 DropAllPreparedStatements(void)
612 {
613         HASH_SEQ_STATUS seq;
614         PreparedStatement *entry;
615
616         /* nothing cached */
617         if (!prepared_queries)
618                 return;
619
620         /* walk over cache */
621         hash_seq_init(&seq, prepared_queries);
622         while ((entry = hash_seq_search(&seq)) != NULL)
623         {
624                 /* Release the plancache entry */
625                 DropCachedPlan(entry->plansource);
626
627                 /* Now we can remove the hash table entry */
628                 hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
629         }
630 }
631
632 /*
633  * Implements the 'EXPLAIN EXECUTE' utility statement.
634  */
635 void
636 ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainStmt *stmt,
637                                         const char *queryString,
638                                         ParamListInfo params, TupOutputState *tstate)
639 {
640         PreparedStatement *entry;
641         CachedPlan *cplan;
642         List       *plan_list;
643         ListCell   *p;
644         ParamListInfo paramLI = NULL;
645         EState     *estate = NULL;
646
647         /* Look it up in the hash table */
648         entry = FetchPreparedStatement(execstmt->name, true);
649
650         /* Shouldn't have a non-fully-planned plancache entry */
651         if (!entry->plansource->fully_planned)
652                 elog(ERROR, "EXPLAIN EXECUTE does not support unplanned prepared statements");
653         /* Shouldn't get any non-fixed-result cached plan, either */
654         if (!entry->plansource->fixed_result)
655                 elog(ERROR, "EXPLAIN EXECUTE does not support variable-result cached plans");
656
657         /* Replan if needed, and acquire a transient refcount */
658         cplan = RevalidateCachedPlan(entry->plansource, true);
659
660         plan_list = cplan->stmt_list;
661
662         /* Evaluate parameters, if any */
663         if (entry->plansource->num_params)
664         {
665                 /*
666                  * Need an EState to evaluate parameters; must not delete it till end
667                  * of query, in case parameters are pass-by-reference.
668                  */
669                 estate = CreateExecutorState();
670                 estate->es_param_list_info = params;
671                 paramLI = EvaluateParams(entry, execstmt->params,
672                                                                  queryString, estate);
673         }
674
675         /* Explain each query */
676         foreach(p, plan_list)
677         {
678                 PlannedStmt *pstmt = (PlannedStmt *) lfirst(p);
679                 bool            is_last_query;
680
681                 is_last_query = (lnext(p) == NULL);
682
683                 if (IsA(pstmt, PlannedStmt))
684                 {
685                         if (execstmt->into)
686                         {
687                                 if (pstmt->commandType != CMD_SELECT ||
688                                         pstmt->utilityStmt != NULL)
689                                         ereport(ERROR,
690                                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
691                                                          errmsg("prepared statement is not a SELECT")));
692
693                                 /* Copy the stmt so we can modify it */
694                                 pstmt = copyObject(pstmt);
695
696                                 pstmt->intoClause = execstmt->into;
697                         }
698
699                         ExplainOnePlan(pstmt, paramLI, stmt, tstate);
700                 }
701                 else
702                 {
703                         ExplainOneUtility((Node *) pstmt, stmt, queryString,
704                                                           params, tstate);
705                 }
706
707                 /* No need for CommandCounterIncrement, as ExplainOnePlan did it */
708
709                 /* put a blank line between plans */
710                 if (!is_last_query)
711                         do_text_output_oneline(tstate, "");
712         }
713
714         if (estate)
715                 FreeExecutorState(estate);
716
717         ReleaseCachedPlan(cplan, true);
718 }
719
720 /*
721  * This set returning function reads all the prepared statements and
722  * returns a set of (name, statement, prepare_time, param_types, from_sql).
723  */
724 Datum
725 pg_prepared_statement(PG_FUNCTION_ARGS)
726 {
727         ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
728         TupleDesc       tupdesc;
729         Tuplestorestate *tupstore;
730         MemoryContext per_query_ctx;
731         MemoryContext oldcontext;
732
733         /* check to see if caller supports us returning a tuplestore */
734         if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
735                 ereport(ERROR,
736                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
737                                  errmsg("set-valued function called in context that cannot accept a set")));
738         if (!(rsinfo->allowedModes & SFRM_Materialize))
739                 ereport(ERROR,
740                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
741                                  errmsg("materialize mode required, but it is not " \
742                                                 "allowed in this context")));
743
744         /* need to build tuplestore in query context */
745         per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
746         oldcontext = MemoryContextSwitchTo(per_query_ctx);
747
748         /*
749          * build tupdesc for result tuples. This must match the definition of the
750          * pg_prepared_statements view in system_views.sql
751          */
752         tupdesc = CreateTemplateTupleDesc(5, false);
753         TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
754                                            TEXTOID, -1, 0);
755         TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
756                                            TEXTOID, -1, 0);
757         TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepare_time",
758                                            TIMESTAMPTZOID, -1, 0);
759         TupleDescInitEntry(tupdesc, (AttrNumber) 4, "parameter_types",
760                                            REGTYPEARRAYOID, -1, 0);
761         TupleDescInitEntry(tupdesc, (AttrNumber) 5, "from_sql",
762                                            BOOLOID, -1, 0);
763
764         /*
765          * We put all the tuples into a tuplestore in one scan of the hashtable.
766          * This avoids any issue of the hashtable possibly changing between calls.
767          */
768         tupstore = tuplestore_begin_heap(true, false, work_mem);
769
770         /* hash table might be uninitialized */
771         if (prepared_queries)
772         {
773                 HASH_SEQ_STATUS hash_seq;
774                 PreparedStatement *prep_stmt;
775
776                 hash_seq_init(&hash_seq, prepared_queries);
777                 while ((prep_stmt = hash_seq_search(&hash_seq)) != NULL)
778                 {
779                         Datum           values[5];
780                         bool            nulls[5];
781
782                         /* generate junk in short-term context */
783                         MemoryContextSwitchTo(oldcontext);
784
785                         MemSet(nulls, 0, sizeof(nulls));
786
787                         values[0] = CStringGetTextDatum(prep_stmt->stmt_name);
788                         values[1] = CStringGetTextDatum(prep_stmt->plansource->query_string);
789                         values[2] = TimestampTzGetDatum(prep_stmt->prepare_time);
790                         values[3] = build_regtype_array(prep_stmt->plansource->param_types,
791                                                                                   prep_stmt->plansource->num_params);
792                         values[4] = BoolGetDatum(prep_stmt->from_sql);
793
794                         /* switch to appropriate context while storing the tuple */
795                         MemoryContextSwitchTo(per_query_ctx);
796                         tuplestore_putvalues(tupstore, tupdesc, values, nulls);
797                 }
798         }
799
800         /* clean up and return the tuplestore */
801         tuplestore_donestoring(tupstore);
802
803         MemoryContextSwitchTo(oldcontext);
804
805         rsinfo->returnMode = SFRM_Materialize;
806         rsinfo->setResult = tupstore;
807         rsinfo->setDesc = tupdesc;
808
809         return (Datum) 0;
810 }
811
812 /*
813  * This utility function takes a C array of Oids, and returns a Datum
814  * pointing to a one-dimensional Postgres array of regtypes. An empty
815  * array is returned as a zero-element array, not NULL.
816  */
817 static Datum
818 build_regtype_array(Oid *param_types, int num_params)
819 {
820         Datum      *tmp_ary;
821         ArrayType  *result;
822         int                     i;
823
824         tmp_ary = (Datum *) palloc(num_params * sizeof(Datum));
825
826         for (i = 0; i < num_params; i++)
827                 tmp_ary[i] = ObjectIdGetDatum(param_types[i]);
828
829         /* XXX: this hardcodes assumptions about the regtype type */
830         result = construct_array(tmp_ary, num_params, REGTYPEOID, 4, true, 'i');
831         return PointerGetDatum(result);
832 }