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