]> granicus.if.org Git - postgresql/blob - src/backend/commands/prepare.c
9a4f88d1a0b1f1e3c966e3ce0217d595c00d743f
[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-2007, PostgreSQL Global Development Group
11  *
12  * IDENTIFICATION
13  *        $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.72 2007/04/16 01:14:55 tgl Exp $
14  *
15  *-------------------------------------------------------------------------
16  */
17 #include "postgres.h"
18
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 #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
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         List       *queries;
59         Query      *query;
60         const char *commandTag;
61         List       *query_list,
62                            *plan_list;
63         int                     i;
64
65         /*
66          * Disallow empty-string statement name (conflicts with protocol-level
67          * unnamed statement).
68          */
69         if (!stmt->name || stmt->name[0] == '\0')
70                 ereport(ERROR,
71                                 (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
72                                  errmsg("invalid statement name: must not be empty")));
73
74         /* Transform list of TypeNames to array of type OIDs */
75         nargs = list_length(stmt->argtypes);
76
77         if (nargs)
78         {
79                 ParseState *pstate;
80                 ListCell   *l;
81
82                 /*
83                  * typenameTypeId wants a ParseState to carry the source query string.
84                  * Is it worth refactoring its API to avoid this?
85                  */
86                 pstate = make_parsestate(NULL);
87                 pstate->p_sourcetext = queryString;
88
89                 argtypes = (Oid *) palloc(nargs * sizeof(Oid));
90                 i = 0;
91
92                 foreach(l, stmt->argtypes)
93                 {
94                         TypeName   *tn = lfirst(l);
95                         Oid                     toid = typenameTypeId(pstate, tn);
96
97                         argtypes[i++] = toid;
98                 }
99         }
100
101         /*
102          * Analyze the statement using these parameter types (any parameters
103          * passed in from above us will not be visible to it), allowing
104          * information about unknown parameters to be deduced from context.
105          *
106          * Because parse analysis scribbles on the raw querytree, we must make
107          * a copy to ensure we have a pristine raw tree to cache.  FIXME someday.
108          */
109         queries = parse_analyze_varparams((Node *) copyObject(stmt->query),
110                                                                           queryString,
111                                                                           &argtypes, &nargs);
112
113         /*
114          * Check that all parameter types were determined.
115          */
116         for (i = 0; i < nargs; i++)
117         {
118                 Oid                     argtype = argtypes[i];
119
120                 if (argtype == InvalidOid || argtype == UNKNOWNOID)
121                         ereport(ERROR,
122                                         (errcode(ERRCODE_INDETERMINATE_DATATYPE),
123                                          errmsg("could not determine data type of parameter $%d",
124                                                         i + 1)));
125         }
126
127         /*
128          * Shouldn't get any extra statements, since grammar only allows
129          * OptimizableStmt
130          */
131         if (list_length(queries) != 1)
132                 elog(ERROR, "unexpected extra stuff in prepared statement");
133
134         query = (Query *) linitial(queries);
135         Assert(IsA(query, Query));
136
137         switch (query->commandType)
138         {
139                 case CMD_SELECT:
140                         commandTag = "SELECT";
141                         break;
142                 case CMD_INSERT:
143                         commandTag = "INSERT";
144                         break;
145                 case CMD_UPDATE:
146                         commandTag = "UPDATE";
147                         break;
148                 case CMD_DELETE:
149                         commandTag = "DELETE";
150                         break;
151                 default:
152                         ereport(ERROR,
153                                         (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
154                                          errmsg("utility statements cannot be prepared")));
155                         commandTag = NULL;      /* keep compiler quiet */
156                         break;
157         }
158
159         /* Rewrite the query. The result could be 0, 1, or many queries. */
160         query_list = QueryRewrite(query);
161
162         /* Generate plans for queries.  Snapshot is already set. */
163         plan_list = pg_plan_queries(query_list, 0, NULL, false);
164
165         /*
166          * Save the results.
167          */
168         StorePreparedStatement(stmt->name,
169                                                    stmt->query,
170                                                    queryString,
171                                                    commandTag,
172                                                    argtypes,
173                                                    nargs,
174                                                    plan_list,
175                                                    true);
176 }
177
178 /*
179  * Implements the 'EXECUTE' utility statement.
180  */
181 void
182 ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
183                          ParamListInfo params,
184                          DestReceiver *dest, char *completionTag)
185 {
186         PreparedStatement *entry;
187         CachedPlan *cplan;
188         List       *plan_list;
189         ParamListInfo paramLI = NULL;
190         EState     *estate = NULL;
191         Portal          portal;
192
193         /* Look it up in the hash table */
194         entry = FetchPreparedStatement(stmt->name, true);
195
196         /* Shouldn't have a non-fully-planned plancache entry */
197         if (!entry->plansource->fully_planned)
198                 elog(ERROR, "EXECUTE does not support unplanned prepared statements");
199         /* Shouldn't get any non-fixed-result cached plan, either */
200         if (!entry->plansource->fixed_result)
201                 elog(ERROR, "EXECUTE does not support variable-result cached plans");
202
203         /* Evaluate parameters, if any */
204         if (entry->plansource->num_params > 0)
205         {
206                 /*
207                  * Need an EState to evaluate parameters; must not delete it till end
208                  * of query, in case parameters are pass-by-reference.
209                  */
210                 estate = CreateExecutorState();
211                 estate->es_param_list_info = params;
212                 paramLI = EvaluateParams(entry, stmt->params,
213                                                                  queryString, estate);
214         }
215
216         /* Create a new portal to run the query in */
217         portal = CreateNewPortal();
218         /* Don't display the portal in pg_cursors, it is for internal use only */
219         portal->visible = false;
220
221         /*
222          * For CREATE TABLE / AS EXECUTE, we must make a copy of the stored query
223          * so that we can modify its destination (yech, but this has always been
224          * ugly).  For regular EXECUTE we can just use the cached query, since the
225          * executor is read-only.
226          */
227         if (stmt->into)
228         {
229                 MemoryContext oldContext;
230                 PlannedStmt *pstmt;
231
232                 /* Replan if needed, and increment plan refcount transiently */
233                 cplan = RevalidateCachedPlan(entry->plansource, true);
234
235                 /* Copy plan into portal's context, and modify */
236                 oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
237
238                 plan_list = copyObject(cplan->stmt_list);
239
240                 if (list_length(plan_list) != 1)
241                         ereport(ERROR,
242                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
243                                          errmsg("prepared statement is not a SELECT")));
244                 pstmt = (PlannedStmt *) linitial(plan_list);
245                 if (!IsA(pstmt, PlannedStmt) ||
246                         pstmt->commandType != CMD_SELECT)
247                         ereport(ERROR,
248                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
249                                          errmsg("prepared statement is not a SELECT")));
250                 pstmt->into = copyObject(stmt->into);
251
252                 MemoryContextSwitchTo(oldContext);
253
254                 /* We no longer need the cached plan refcount ... */
255                 ReleaseCachedPlan(cplan, true);
256                 /* ... and we don't want the portal to depend on it, either */
257                 cplan = NULL;
258         }
259         else
260         {
261                 /* Replan if needed, and increment plan refcount for portal */
262                 cplan = RevalidateCachedPlan(entry->plansource, false);
263                 plan_list = cplan->stmt_list;
264         }
265
266         PortalDefineQuery(portal,
267                                           NULL,
268                                           entry->plansource->query_string,
269                                           entry->plansource->commandTag,
270                                           plan_list,
271                                           cplan);
272
273         /*
274          * Run the portal to completion.
275          */
276         PortalStart(portal, paramLI, ActiveSnapshot);
277
278         (void) PortalRun(portal, FETCH_ALL, false, dest, dest, completionTag);
279
280         PortalDrop(portal, false);
281
282         if (estate)
283                 FreeExecutorState(estate);
284
285         /* No need to pfree other memory, MemoryContext will be reset */
286 }
287
288 /*
289  * EvaluateParams: evaluate a list of parameters.
290  *
291  * pstmt: statement we are getting parameters for.
292  * params: list of given parameter expressions (raw parser output!)
293  * queryString: source text for error messages.
294  * estate: executor state to use.
295  *
296  * Returns a filled-in ParamListInfo -- this can later be passed to
297  * CreateQueryDesc(), which allows the executor to make use of the parameters
298  * during query execution.
299  */
300 static ParamListInfo
301 EvaluateParams(PreparedStatement *pstmt, List *params,
302                            const char *queryString, EState *estate)
303 {
304         Oid                *param_types = pstmt->plansource->param_types;
305         int                     num_params = pstmt->plansource->num_params;
306         int                     nparams = list_length(params);
307         ParseState *pstate;
308         ParamListInfo paramLI;
309         List       *exprstates;
310         ListCell   *l;
311         int                     i;
312
313         if (nparams != num_params)
314                 ereport(ERROR,
315                                 (errcode(ERRCODE_SYNTAX_ERROR),
316                                  errmsg("wrong number of parameters for prepared statement \"%s\"",
317                                                 pstmt->stmt_name),
318                                  errdetail("Expected %d parameters but got %d.",
319                                                    num_params, nparams)));
320
321         /* Quick exit if no parameters */
322         if (num_params == 0)
323                 return NULL;
324
325         /*
326          * We have to run parse analysis for the expressions.  Since the
327          * parser is not cool about scribbling on its input, copy first.
328          */
329         params = (List *) copyObject(params);
330
331         pstate = make_parsestate(NULL);
332         pstate->p_sourcetext = queryString;
333
334         i = 0;
335         foreach(l, params)
336         {
337                 Node       *expr = lfirst(l);
338                 Oid                     expected_type_id = param_types[i];
339                 Oid                     given_type_id;
340
341                 expr = transformExpr(pstate, expr);
342
343                 /* Cannot contain subselects or aggregates */
344                 if (pstate->p_hasSubLinks)
345                         ereport(ERROR,
346                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
347                                          errmsg("cannot use subquery in EXECUTE parameter")));
348                 if (pstate->p_hasAggs)
349                         ereport(ERROR,
350                                         (errcode(ERRCODE_GROUPING_ERROR),
351                                          errmsg("cannot use aggregate function in EXECUTE parameter")));
352
353                 given_type_id = exprType(expr);
354
355                 expr = coerce_to_target_type(pstate, expr, given_type_id,
356                                                                          expected_type_id, -1,
357                                                                          COERCION_ASSIGNMENT,
358                                                                          COERCE_IMPLICIT_CAST);
359
360                 if (expr == NULL)
361                         ereport(ERROR,
362                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
363                                          errmsg("parameter $%d of type %s cannot be coerced to the expected type %s",
364                                                         i + 1,
365                                                         format_type_be(given_type_id),
366                                                         format_type_be(expected_type_id)),
367                                          errhint("You will need to rewrite or cast the expression.")));
368
369                 lfirst(l) = expr;
370                 i++;
371         }
372
373         /* Prepare the expressions for execution */
374         exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);
375
376         /* sizeof(ParamListInfoData) includes the first array element */
377         paramLI = (ParamListInfo)
378                 palloc(sizeof(ParamListInfoData) +
379                            (num_params - 1) *sizeof(ParamExternData));
380         paramLI->numParams = num_params;
381
382         i = 0;
383         foreach(l, exprstates)
384         {
385                 ExprState  *n = lfirst(l);
386                 ParamExternData *prm = &paramLI->params[i];
387
388                 prm->ptype = param_types[i];
389                 prm->pflags = 0;
390                 prm->value = ExecEvalExprSwitchContext(n,
391                                                                                            GetPerTupleExprContext(estate),
392                                                                                            &prm->isnull,
393                                                                                            NULL);
394
395                 i++;
396         }
397
398         return paramLI;
399 }
400
401
402 /*
403  * Initialize query hash table upon first use.
404  */
405 static void
406 InitQueryHashTable(void)
407 {
408         HASHCTL         hash_ctl;
409
410         MemSet(&hash_ctl, 0, sizeof(hash_ctl));
411
412         hash_ctl.keysize = NAMEDATALEN;
413         hash_ctl.entrysize = sizeof(PreparedStatement);
414
415         prepared_queries = hash_create("Prepared Queries",
416                                                                    32,
417                                                                    &hash_ctl,
418                                                                    HASH_ELEM);
419 }
420
421 /*
422  * Store all the data pertaining to a query in the hash table using
423  * the specified key.  All the given data is copied into either the hashtable
424  * entry or the underlying plancache entry, so the caller can dispose of its
425  * copy.
426  *
427  * Exception: commandTag is presumed to be a pointer to a constant string,
428  * or possibly NULL, so it need not be copied.  Note that commandTag should
429  * be NULL only if the original query (before rewriting) was empty.
430  */
431 void
432 StorePreparedStatement(const char *stmt_name,
433                                            Node *raw_parse_tree,
434                                            const char *query_string,
435                                            const char *commandTag,
436                                            Oid *param_types,
437                                            int num_params,
438                                            List *stmt_list,
439                                            bool from_sql)
440 {
441         PreparedStatement *entry;
442         CachedPlanSource *plansource;
443         bool            found;
444
445         /* Initialize the hash table, if necessary */
446         if (!prepared_queries)
447                 InitQueryHashTable();
448
449         /* Check for pre-existing entry of same name */
450         hash_search(prepared_queries, stmt_name, HASH_FIND, &found);
451
452         if (found)
453                 ereport(ERROR,
454                                 (errcode(ERRCODE_DUPLICATE_PSTATEMENT),
455                                  errmsg("prepared statement \"%s\" already exists",
456                                                 stmt_name)));
457
458         /* Create a plancache entry */
459         plansource = CreateCachedPlan(raw_parse_tree,
460                                                                   query_string,
461                                                                   commandTag,
462                                                                   param_types,
463                                                                   num_params,
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                         QueryDesc  *qdesc;
686
687                         if (execstmt->into)
688                         {
689                                 if (pstmt->commandType != CMD_SELECT)
690                                         ereport(ERROR,
691                                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
692                                                          errmsg("prepared statement is not a SELECT")));
693
694                                 /* Copy the stmt so we can modify it */
695                                 pstmt = copyObject(pstmt);
696
697                                 pstmt->into = execstmt->into;
698                         }
699
700                         /*
701                          * Update snapshot command ID to ensure this query sees results of
702                          * any previously executed queries.  (It's a bit cheesy to modify
703                          * ActiveSnapshot without making a copy, but for the limited ways
704                          * in which EXPLAIN can be invoked, I think it's OK, because the
705                          * active snapshot shouldn't be shared with anything else anyway.)
706                          */
707                         ActiveSnapshot->curcid = GetCurrentCommandId();
708
709                         /* Create a QueryDesc requesting no output */
710                         qdesc = CreateQueryDesc(pstmt,
711                                                                         ActiveSnapshot, InvalidSnapshot,
712                                                                         None_Receiver,
713                                                                         paramLI, stmt->analyze);
714
715                         ExplainOnePlan(qdesc, stmt, tstate);
716                 }
717                 else
718                 {
719                         ExplainOneUtility((Node *) pstmt, stmt, queryString,
720                                                           params, tstate);
721                 }
722
723                 /* No need for CommandCounterIncrement, as ExplainOnePlan did it */
724
725                 /* put a blank line between plans */
726                 if (!is_last_query)
727                         do_text_output_oneline(tstate, "");
728         }
729
730         if (estate)
731                 FreeExecutorState(estate);
732
733         ReleaseCachedPlan(cplan, true);
734 }
735
736 /*
737  * This set returning function reads all the prepared statements and
738  * returns a set of (name, statement, prepare_time, param_types, from_sql).
739  */
740 Datum
741 pg_prepared_statement(PG_FUNCTION_ARGS)
742 {
743         FuncCallContext *funcctx;
744         HASH_SEQ_STATUS *hash_seq;
745         PreparedStatement *prep_stmt;
746
747         /* stuff done only on the first call of the function */
748         if (SRF_IS_FIRSTCALL())
749         {
750                 TupleDesc       tupdesc;
751                 MemoryContext oldcontext;
752
753                 /* create a function context for cross-call persistence */
754                 funcctx = SRF_FIRSTCALL_INIT();
755
756                 /*
757                  * switch to memory context appropriate for multiple function calls
758                  */
759                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
760
761                 /* allocate memory for user context */
762                 if (prepared_queries)
763                 {
764                         hash_seq = (HASH_SEQ_STATUS *) palloc(sizeof(HASH_SEQ_STATUS));
765                         hash_seq_init(hash_seq, prepared_queries);
766                         funcctx->user_fctx = (void *) hash_seq;
767                 }
768                 else
769                         funcctx->user_fctx = NULL;
770
771                 /*
772                  * build tupdesc for result tuples. This must match the definition of
773                  * the pg_prepared_statements view in system_views.sql
774                  */
775                 tupdesc = CreateTemplateTupleDesc(5, false);
776                 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
777                                                    TEXTOID, -1, 0);
778                 TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
779                                                    TEXTOID, -1, 0);
780                 TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepare_time",
781                                                    TIMESTAMPTZOID, -1, 0);
782                 TupleDescInitEntry(tupdesc, (AttrNumber) 4, "parameter_types",
783                                                    REGTYPEARRAYOID, -1, 0);
784                 TupleDescInitEntry(tupdesc, (AttrNumber) 5, "from_sql",
785                                                    BOOLOID, -1, 0);
786
787                 funcctx->tuple_desc = BlessTupleDesc(tupdesc);
788                 MemoryContextSwitchTo(oldcontext);
789         }
790
791         /* stuff done on every call of the function */
792         funcctx = SRF_PERCALL_SETUP();
793         hash_seq = (HASH_SEQ_STATUS *) funcctx->user_fctx;
794
795         /* if the hash table is uninitialized, we're done */
796         if (hash_seq == NULL)
797                 SRF_RETURN_DONE(funcctx);
798
799         prep_stmt = hash_seq_search(hash_seq);
800         if (prep_stmt)
801         {
802                 Datum           result;
803                 HeapTuple       tuple;
804                 Datum           values[5];
805                 bool            nulls[5];
806
807                 MemSet(nulls, 0, sizeof(nulls));
808
809                 values[0] = DirectFunctionCall1(textin,
810                                                                           CStringGetDatum(prep_stmt->stmt_name));
811
812                 if (prep_stmt->plansource->query_string == NULL)
813                         nulls[1] = true;
814                 else
815                         values[1] = DirectFunctionCall1(textin,
816                                                 CStringGetDatum(prep_stmt->plansource->query_string));
817
818                 values[2] = TimestampTzGetDatum(prep_stmt->prepare_time);
819                 values[3] = build_regtype_array(prep_stmt->plansource->param_types,
820                                                                                 prep_stmt->plansource->num_params);
821                 values[4] = BoolGetDatum(prep_stmt->from_sql);
822
823                 tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
824                 result = HeapTupleGetDatum(tuple);
825                 SRF_RETURN_NEXT(funcctx, result);
826         }
827
828         SRF_RETURN_DONE(funcctx);
829 }
830
831 /*
832  * This utility function takes a C array of Oids, and returns a Datum
833  * pointing to a one-dimensional Postgres array of regtypes. An empty
834  * array is returned as a zero-element array, not NULL.
835  */
836 static Datum
837 build_regtype_array(Oid *param_types, int num_params)
838 {
839         Datum      *tmp_ary;
840         ArrayType  *result;
841         int                     i;
842
843         tmp_ary = (Datum *) palloc(num_params * sizeof(Datum));
844
845         for (i = 0; i < num_params; i++)
846                 tmp_ary[i] = ObjectIdGetDatum(param_types[i]);
847
848         /* XXX: this hardcodes assumptions about the regtype type */
849         result = construct_array(tmp_ary, num_params, REGTYPEOID, 4, true, 'i');
850         return PointerGetDatum(result);
851 }