Header And Logo

PostgreSQL
| The world's most advanced open source database.

prepare.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * prepare.c
00004  *    Prepareable SQL statements via PREPARE, EXECUTE and DEALLOCATE
00005  *
00006  * This module also implements storage of prepared statements that are
00007  * accessed via the extended FE/BE query protocol.
00008  *
00009  *
00010  * Copyright (c) 2002-2013, PostgreSQL Global Development Group
00011  *
00012  * IDENTIFICATION
00013  *    src/backend/commands/prepare.c
00014  *
00015  *-------------------------------------------------------------------------
00016  */
00017 #include "postgres.h"
00018 
00019 #include "access/xact.h"
00020 #include "catalog/pg_type.h"
00021 #include "commands/createas.h"
00022 #include "commands/prepare.h"
00023 #include "miscadmin.h"
00024 #include "nodes/nodeFuncs.h"
00025 #include "parser/analyze.h"
00026 #include "parser/parse_coerce.h"
00027 #include "parser/parse_collate.h"
00028 #include "parser/parse_expr.h"
00029 #include "parser/parse_type.h"
00030 #include "rewrite/rewriteHandler.h"
00031 #include "tcop/pquery.h"
00032 #include "tcop/utility.h"
00033 #include "utils/builtins.h"
00034 #include "utils/snapmgr.h"
00035 #include "utils/timestamp.h"
00036 
00037 
00038 /*
00039  * The hash table in which prepared queries are stored. This is
00040  * per-backend: query plans are not shared between backends.
00041  * The keys for this hash table are the arguments to PREPARE and EXECUTE
00042  * (statement names); the entries are PreparedStatement structs.
00043  */
00044 static HTAB *prepared_queries = NULL;
00045 
00046 static void InitQueryHashTable(void);
00047 static ParamListInfo EvaluateParams(PreparedStatement *pstmt, List *params,
00048                const char *queryString, EState *estate);
00049 static Datum build_regtype_array(Oid *param_types, int num_params);
00050 
00051 /*
00052  * Implements the 'PREPARE' utility statement.
00053  */
00054 void
00055 PrepareQuery(PrepareStmt *stmt, const char *queryString)
00056 {
00057     CachedPlanSource *plansource;
00058     Oid        *argtypes = NULL;
00059     int         nargs;
00060     Query      *query;
00061     List       *query_list;
00062     int         i;
00063 
00064     /*
00065      * Disallow empty-string statement name (conflicts with protocol-level
00066      * unnamed statement).
00067      */
00068     if (!stmt->name || stmt->name[0] == '\0')
00069         ereport(ERROR,
00070                 (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
00071                  errmsg("invalid statement name: must not be empty")));
00072 
00073     /*
00074      * Create the CachedPlanSource before we do parse analysis, since it needs
00075      * to see the unmodified raw parse tree.
00076      */
00077     plansource = CreateCachedPlan(stmt->query, queryString,
00078                                   CreateCommandTag(stmt->query));
00079 
00080     /* Transform list of TypeNames to array of type OIDs */
00081     nargs = list_length(stmt->argtypes);
00082 
00083     if (nargs)
00084     {
00085         ParseState *pstate;
00086         ListCell   *l;
00087 
00088         /*
00089          * typenameTypeId wants a ParseState to carry the source query string.
00090          * Is it worth refactoring its API to avoid this?
00091          */
00092         pstate = make_parsestate(NULL);
00093         pstate->p_sourcetext = queryString;
00094 
00095         argtypes = (Oid *) palloc(nargs * sizeof(Oid));
00096         i = 0;
00097 
00098         foreach(l, stmt->argtypes)
00099         {
00100             TypeName   *tn = lfirst(l);
00101             Oid         toid = typenameTypeId(pstate, tn);
00102 
00103             argtypes[i++] = toid;
00104         }
00105     }
00106 
00107     /*
00108      * Analyze the statement using these parameter types (any parameters
00109      * passed in from above us will not be visible to it), allowing
00110      * information about unknown parameters to be deduced from context.
00111      *
00112      * Because parse analysis scribbles on the raw querytree, we must make a
00113      * copy to ensure we don't modify the passed-in tree.  FIXME someday.
00114      */
00115     query = parse_analyze_varparams((Node *) copyObject(stmt->query),
00116                                     queryString,
00117                                     &argtypes, &nargs);
00118 
00119     /*
00120      * Check that all parameter types were determined.
00121      */
00122     for (i = 0; i < nargs; i++)
00123     {
00124         Oid         argtype = argtypes[i];
00125 
00126         if (argtype == InvalidOid || argtype == UNKNOWNOID)
00127             ereport(ERROR,
00128                     (errcode(ERRCODE_INDETERMINATE_DATATYPE),
00129                      errmsg("could not determine data type of parameter $%d",
00130                             i + 1)));
00131     }
00132 
00133     /*
00134      * grammar only allows OptimizableStmt, so this check should be redundant
00135      */
00136     switch (query->commandType)
00137     {
00138         case CMD_SELECT:
00139         case CMD_INSERT:
00140         case CMD_UPDATE:
00141         case CMD_DELETE:
00142             /* OK */
00143             break;
00144         default:
00145             ereport(ERROR,
00146                     (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
00147                      errmsg("utility statements cannot be prepared")));
00148             break;
00149     }
00150 
00151     /* Rewrite the query. The result could be 0, 1, or many queries. */
00152     query_list = QueryRewrite(query);
00153 
00154     /* Finish filling in the CachedPlanSource */
00155     CompleteCachedPlan(plansource,
00156                        query_list,
00157                        NULL,
00158                        argtypes,
00159                        nargs,
00160                        NULL,
00161                        NULL,
00162                        0,       /* default cursor options */
00163                        true);   /* fixed result */
00164 
00165     /*
00166      * Save the results.
00167      */
00168     StorePreparedStatement(stmt->name,
00169                            plansource,
00170                            true);
00171 }
00172 
00173 /*
00174  * ExecuteQuery --- implement the 'EXECUTE' utility statement.
00175  *
00176  * This code also supports CREATE TABLE ... AS EXECUTE.  That case is
00177  * indicated by passing a non-null intoClause.  The DestReceiver is already
00178  * set up correctly for CREATE TABLE AS, but we still have to make a few
00179  * other adjustments here.
00180  *
00181  * Note: this is one of very few places in the code that needs to deal with
00182  * two query strings at once.  The passed-in queryString is that of the
00183  * EXECUTE, which we might need for error reporting while processing the
00184  * parameter expressions.  The query_string that we copy from the plan
00185  * source is that of the original PREPARE.
00186  */
00187 void
00188 ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
00189              const char *queryString, ParamListInfo params,
00190              DestReceiver *dest, char *completionTag)
00191 {
00192     PreparedStatement *entry;
00193     CachedPlan *cplan;
00194     List       *plan_list;
00195     ParamListInfo paramLI = NULL;
00196     EState     *estate = NULL;
00197     Portal      portal;
00198     char       *query_string;
00199     int         eflags;
00200     long        count;
00201 
00202     /* Look it up in the hash table */
00203     entry = FetchPreparedStatement(stmt->name, true);
00204 
00205     /* Shouldn't find a non-fixed-result cached plan */
00206     if (!entry->plansource->fixed_result)
00207         elog(ERROR, "EXECUTE does not support variable-result cached plans");
00208 
00209     /* Evaluate parameters, if any */
00210     if (entry->plansource->num_params > 0)
00211     {
00212         /*
00213          * Need an EState to evaluate parameters; must not delete it till end
00214          * of query, in case parameters are pass-by-reference.  Note that the
00215          * passed-in "params" could possibly be referenced in the parameter
00216          * expressions.
00217          */
00218         estate = CreateExecutorState();
00219         estate->es_param_list_info = params;
00220         paramLI = EvaluateParams(entry, stmt->params,
00221                                  queryString, estate);
00222     }
00223 
00224     /* Create a new portal to run the query in */
00225     portal = CreateNewPortal();
00226     /* Don't display the portal in pg_cursors, it is for internal use only */
00227     portal->visible = false;
00228 
00229     /* Copy the plan's saved query string into the portal's memory */
00230     query_string = MemoryContextStrdup(PortalGetHeapMemory(portal),
00231                                        entry->plansource->query_string);
00232 
00233     /* Replan if needed, and increment plan refcount for portal */
00234     cplan = GetCachedPlan(entry->plansource, paramLI, false);
00235     plan_list = cplan->stmt_list;
00236 
00237     /*
00238      * For CREATE TABLE ... AS EXECUTE, we must verify that the prepared
00239      * statement is one that produces tuples.  Currently we insist that it be
00240      * a plain old SELECT.  In future we might consider supporting other
00241      * things such as INSERT ... RETURNING, but there are a couple of issues
00242      * to be settled first, notably how WITH NO DATA should be handled in such
00243      * a case (do we really want to suppress execution?) and how to pass down
00244      * the OID-determining eflags (PortalStart won't handle them in such a
00245      * case, and for that matter it's not clear the executor will either).
00246      *
00247      * For CREATE TABLE ... AS EXECUTE, we also have to ensure that the proper
00248      * eflags and fetch count are passed to PortalStart/PortalRun.
00249      */
00250     if (intoClause)
00251     {
00252         PlannedStmt *pstmt;
00253 
00254         if (list_length(plan_list) != 1)
00255             ereport(ERROR,
00256                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
00257                      errmsg("prepared statement is not a SELECT")));
00258         pstmt = (PlannedStmt *) linitial(plan_list);
00259         if (!IsA(pstmt, PlannedStmt) ||
00260             pstmt->commandType != CMD_SELECT ||
00261             pstmt->utilityStmt != NULL)
00262             ereport(ERROR,
00263                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
00264                      errmsg("prepared statement is not a SELECT")));
00265 
00266         /* Set appropriate eflags */
00267         eflags = GetIntoRelEFlags(intoClause);
00268 
00269         /* And tell PortalRun whether to run to completion or not */
00270         if (intoClause->skipData)
00271             count = 0;
00272         else
00273             count = FETCH_ALL;
00274     }
00275     else
00276     {
00277         /* Plain old EXECUTE */
00278         eflags = 0;
00279         count = FETCH_ALL;
00280     }
00281 
00282     PortalDefineQuery(portal,
00283                       NULL,
00284                       query_string,
00285                       entry->plansource->commandTag,
00286                       plan_list,
00287                       cplan);
00288 
00289     /*
00290      * Run the portal as appropriate.
00291      */
00292     PortalStart(portal, paramLI, eflags, GetActiveSnapshot());
00293 
00294     (void) PortalRun(portal, count, false, dest, dest, completionTag);
00295 
00296     PortalDrop(portal, false);
00297 
00298     if (estate)
00299         FreeExecutorState(estate);
00300 
00301     /* No need to pfree other memory, MemoryContext will be reset */
00302 }
00303 
00304 /*
00305  * EvaluateParams: evaluate a list of parameters.
00306  *
00307  * pstmt: statement we are getting parameters for.
00308  * params: list of given parameter expressions (raw parser output!)
00309  * queryString: source text for error messages.
00310  * estate: executor state to use.
00311  *
00312  * Returns a filled-in ParamListInfo -- this can later be passed to
00313  * CreateQueryDesc(), which allows the executor to make use of the parameters
00314  * during query execution.
00315  */
00316 static ParamListInfo
00317 EvaluateParams(PreparedStatement *pstmt, List *params,
00318                const char *queryString, EState *estate)
00319 {
00320     Oid        *param_types = pstmt->plansource->param_types;
00321     int         num_params = pstmt->plansource->num_params;
00322     int         nparams = list_length(params);
00323     ParseState *pstate;
00324     ParamListInfo paramLI;
00325     List       *exprstates;
00326     ListCell   *l;
00327     int         i;
00328 
00329     if (nparams != num_params)
00330         ereport(ERROR,
00331                 (errcode(ERRCODE_SYNTAX_ERROR),
00332            errmsg("wrong number of parameters for prepared statement \"%s\"",
00333                   pstmt->stmt_name),
00334                  errdetail("Expected %d parameters but got %d.",
00335                            num_params, nparams)));
00336 
00337     /* Quick exit if no parameters */
00338     if (num_params == 0)
00339         return NULL;
00340 
00341     /*
00342      * We have to run parse analysis for the expressions.  Since the parser is
00343      * not cool about scribbling on its input, copy first.
00344      */
00345     params = (List *) copyObject(params);
00346 
00347     pstate = make_parsestate(NULL);
00348     pstate->p_sourcetext = queryString;
00349 
00350     i = 0;
00351     foreach(l, params)
00352     {
00353         Node       *expr = lfirst(l);
00354         Oid         expected_type_id = param_types[i];
00355         Oid         given_type_id;
00356 
00357         expr = transformExpr(pstate, expr, EXPR_KIND_EXECUTE_PARAMETER);
00358 
00359         given_type_id = exprType(expr);
00360 
00361         expr = coerce_to_target_type(pstate, expr, given_type_id,
00362                                      expected_type_id, -1,
00363                                      COERCION_ASSIGNMENT,
00364                                      COERCE_IMPLICIT_CAST,
00365                                      -1);
00366 
00367         if (expr == NULL)
00368             ereport(ERROR,
00369                     (errcode(ERRCODE_DATATYPE_MISMATCH),
00370                      errmsg("parameter $%d of type %s cannot be coerced to the expected type %s",
00371                             i + 1,
00372                             format_type_be(given_type_id),
00373                             format_type_be(expected_type_id)),
00374                errhint("You will need to rewrite or cast the expression.")));
00375 
00376         /* Take care of collations in the finished expression. */
00377         assign_expr_collations(pstate, expr);
00378 
00379         lfirst(l) = expr;
00380         i++;
00381     }
00382 
00383     /* Prepare the expressions for execution */
00384     exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);
00385 
00386     /* sizeof(ParamListInfoData) includes the first array element */
00387     paramLI = (ParamListInfo)
00388         palloc(sizeof(ParamListInfoData) +
00389                (num_params - 1) * sizeof(ParamExternData));
00390     /* we have static list of params, so no hooks needed */
00391     paramLI->paramFetch = NULL;
00392     paramLI->paramFetchArg = NULL;
00393     paramLI->parserSetup = NULL;
00394     paramLI->parserSetupArg = NULL;
00395     paramLI->numParams = num_params;
00396 
00397     i = 0;
00398     foreach(l, exprstates)
00399     {
00400         ExprState  *n = lfirst(l);
00401         ParamExternData *prm = &paramLI->params[i];
00402 
00403         prm->ptype = param_types[i];
00404         prm->pflags = PARAM_FLAG_CONST;
00405         prm->value = ExecEvalExprSwitchContext(n,
00406                                                GetPerTupleExprContext(estate),
00407                                                &prm->isnull,
00408                                                NULL);
00409 
00410         i++;
00411     }
00412 
00413     return paramLI;
00414 }
00415 
00416 
00417 /*
00418  * Initialize query hash table upon first use.
00419  */
00420 static void
00421 InitQueryHashTable(void)
00422 {
00423     HASHCTL     hash_ctl;
00424 
00425     MemSet(&hash_ctl, 0, sizeof(hash_ctl));
00426 
00427     hash_ctl.keysize = NAMEDATALEN;
00428     hash_ctl.entrysize = sizeof(PreparedStatement);
00429 
00430     prepared_queries = hash_create("Prepared Queries",
00431                                    32,
00432                                    &hash_ctl,
00433                                    HASH_ELEM);
00434 }
00435 
00436 /*
00437  * Store all the data pertaining to a query in the hash table using
00438  * the specified key.  The passed CachedPlanSource should be "unsaved"
00439  * in case we get an error here; we'll save it once we've created the hash
00440  * table entry.
00441  */
00442 void
00443 StorePreparedStatement(const char *stmt_name,
00444                        CachedPlanSource *plansource,
00445                        bool from_sql)
00446 {
00447     PreparedStatement *entry;
00448     TimestampTz cur_ts = GetCurrentStatementStartTimestamp();
00449     bool        found;
00450 
00451     /* Initialize the hash table, if necessary */
00452     if (!prepared_queries)
00453         InitQueryHashTable();
00454 
00455     /* Add entry to hash table */
00456     entry = (PreparedStatement *) hash_search(prepared_queries,
00457                                               stmt_name,
00458                                               HASH_ENTER,
00459                                               &found);
00460 
00461     /* Shouldn't get a duplicate entry */
00462     if (found)
00463         ereport(ERROR,
00464                 (errcode(ERRCODE_DUPLICATE_PSTATEMENT),
00465                  errmsg("prepared statement \"%s\" already exists",
00466                         stmt_name)));
00467 
00468     /* Fill in the hash table entry */
00469     entry->plansource = plansource;
00470     entry->from_sql = from_sql;
00471     entry->prepare_time = cur_ts;
00472 
00473     /* Now it's safe to move the CachedPlanSource to permanent memory */
00474     SaveCachedPlan(plansource);
00475 }
00476 
00477 /*
00478  * Lookup an existing query in the hash table. If the query does not
00479  * actually exist, throw ereport(ERROR) or return NULL per second parameter.
00480  *
00481  * Note: this does not force the referenced plancache entry to be valid,
00482  * since not all callers care.
00483  */
00484 PreparedStatement *
00485 FetchPreparedStatement(const char *stmt_name, bool throwError)
00486 {
00487     PreparedStatement *entry;
00488 
00489     /*
00490      * If the hash table hasn't been initialized, it can't be storing
00491      * anything, therefore it couldn't possibly store our plan.
00492      */
00493     if (prepared_queries)
00494         entry = (PreparedStatement *) hash_search(prepared_queries,
00495                                                   stmt_name,
00496                                                   HASH_FIND,
00497                                                   NULL);
00498     else
00499         entry = NULL;
00500 
00501     if (!entry && throwError)
00502         ereport(ERROR,
00503                 (errcode(ERRCODE_UNDEFINED_PSTATEMENT),
00504                  errmsg("prepared statement \"%s\" does not exist",
00505                         stmt_name)));
00506 
00507     return entry;
00508 }
00509 
00510 /*
00511  * Given a prepared statement, determine the result tupledesc it will
00512  * produce.  Returns NULL if the execution will not return tuples.
00513  *
00514  * Note: the result is created or copied into current memory context.
00515  */
00516 TupleDesc
00517 FetchPreparedStatementResultDesc(PreparedStatement *stmt)
00518 {
00519     /*
00520      * Since we don't allow prepared statements' result tupdescs to change,
00521      * there's no need to worry about revalidating the cached plan here.
00522      */
00523     Assert(stmt->plansource->fixed_result);
00524     if (stmt->plansource->resultDesc)
00525         return CreateTupleDescCopy(stmt->plansource->resultDesc);
00526     else
00527         return NULL;
00528 }
00529 
00530 /*
00531  * Given a prepared statement that returns tuples, extract the query
00532  * targetlist.  Returns NIL if the statement doesn't have a determinable
00533  * targetlist.
00534  *
00535  * Note: this is pretty ugly, but since it's only used in corner cases like
00536  * Describe Statement on an EXECUTE command, we don't worry too much about
00537  * efficiency.
00538  */
00539 List *
00540 FetchPreparedStatementTargetList(PreparedStatement *stmt)
00541 {
00542     List       *tlist;
00543 
00544     /* Get the plan's primary targetlist */
00545     tlist = CachedPlanGetTargetList(stmt->plansource);
00546 
00547     /* Copy into caller's context in case plan gets invalidated */
00548     return (List *) copyObject(tlist);
00549 }
00550 
00551 /*
00552  * Implements the 'DEALLOCATE' utility statement: deletes the
00553  * specified plan from storage.
00554  */
00555 void
00556 DeallocateQuery(DeallocateStmt *stmt)
00557 {
00558     if (stmt->name)
00559         DropPreparedStatement(stmt->name, true);
00560     else
00561         DropAllPreparedStatements();
00562 }
00563 
00564 /*
00565  * Internal version of DEALLOCATE
00566  *
00567  * If showError is false, dropping a nonexistent statement is a no-op.
00568  */
00569 void
00570 DropPreparedStatement(const char *stmt_name, bool showError)
00571 {
00572     PreparedStatement *entry;
00573 
00574     /* Find the query's hash table entry; raise error if wanted */
00575     entry = FetchPreparedStatement(stmt_name, showError);
00576 
00577     if (entry)
00578     {
00579         /* Release the plancache entry */
00580         DropCachedPlan(entry->plansource);
00581 
00582         /* Now we can remove the hash table entry */
00583         hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
00584     }
00585 }
00586 
00587 /*
00588  * Drop all cached statements.
00589  */
00590 void
00591 DropAllPreparedStatements(void)
00592 {
00593     HASH_SEQ_STATUS seq;
00594     PreparedStatement *entry;
00595 
00596     /* nothing cached */
00597     if (!prepared_queries)
00598         return;
00599 
00600     /* walk over cache */
00601     hash_seq_init(&seq, prepared_queries);
00602     while ((entry = hash_seq_search(&seq)) != NULL)
00603     {
00604         /* Release the plancache entry */
00605         DropCachedPlan(entry->plansource);
00606 
00607         /* Now we can remove the hash table entry */
00608         hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
00609     }
00610 }
00611 
00612 /*
00613  * Implements the 'EXPLAIN EXECUTE' utility statement.
00614  *
00615  * "into" is NULL unless we are doing EXPLAIN CREATE TABLE AS EXECUTE,
00616  * in which case executing the query should result in creating that table.
00617  *
00618  * Note: the passed-in queryString is that of the EXPLAIN EXECUTE,
00619  * not the original PREPARE; we get the latter string from the plancache.
00620  */
00621 void
00622 ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
00623                     const char *queryString, ParamListInfo params)
00624 {
00625     PreparedStatement *entry;
00626     const char *query_string;
00627     CachedPlan *cplan;
00628     List       *plan_list;
00629     ListCell   *p;
00630     ParamListInfo paramLI = NULL;
00631     EState     *estate = NULL;
00632 
00633     /* Look it up in the hash table */
00634     entry = FetchPreparedStatement(execstmt->name, true);
00635 
00636     /* Shouldn't find a non-fixed-result cached plan */
00637     if (!entry->plansource->fixed_result)
00638         elog(ERROR, "EXPLAIN EXECUTE does not support variable-result cached plans");
00639 
00640     query_string = entry->plansource->query_string;
00641 
00642     /* Evaluate parameters, if any */
00643     if (entry->plansource->num_params)
00644     {
00645         /*
00646          * Need an EState to evaluate parameters; must not delete it till end
00647          * of query, in case parameters are pass-by-reference.  Note that the
00648          * passed-in "params" could possibly be referenced in the parameter
00649          * expressions.
00650          */
00651         estate = CreateExecutorState();
00652         estate->es_param_list_info = params;
00653         paramLI = EvaluateParams(entry, execstmt->params,
00654                                  queryString, estate);
00655     }
00656 
00657     /* Replan if needed, and acquire a transient refcount */
00658     cplan = GetCachedPlan(entry->plansource, paramLI, true);
00659 
00660     plan_list = cplan->stmt_list;
00661 
00662     /* Explain each query */
00663     foreach(p, plan_list)
00664     {
00665         PlannedStmt *pstmt = (PlannedStmt *) lfirst(p);
00666 
00667         if (IsA(pstmt, PlannedStmt))
00668             ExplainOnePlan(pstmt, into, es, query_string, paramLI);
00669         else
00670             ExplainOneUtility((Node *) pstmt, into, es, query_string, paramLI);
00671 
00672         /* No need for CommandCounterIncrement, as ExplainOnePlan did it */
00673 
00674         /* Separate plans with an appropriate separator */
00675         if (lnext(p) != NULL)
00676             ExplainSeparatePlans(es);
00677     }
00678 
00679     if (estate)
00680         FreeExecutorState(estate);
00681 
00682     ReleaseCachedPlan(cplan, true);
00683 }
00684 
00685 /*
00686  * This set returning function reads all the prepared statements and
00687  * returns a set of (name, statement, prepare_time, param_types, from_sql).
00688  */
00689 Datum
00690 pg_prepared_statement(PG_FUNCTION_ARGS)
00691 {
00692     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
00693     TupleDesc   tupdesc;
00694     Tuplestorestate *tupstore;
00695     MemoryContext per_query_ctx;
00696     MemoryContext oldcontext;
00697 
00698     /* check to see if caller supports us returning a tuplestore */
00699     if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
00700         ereport(ERROR,
00701                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00702                  errmsg("set-valued function called in context that cannot accept a set")));
00703     if (!(rsinfo->allowedModes & SFRM_Materialize))
00704         ereport(ERROR,
00705                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00706                  errmsg("materialize mode required, but it is not " \
00707                         "allowed in this context")));
00708 
00709     /* need to build tuplestore in query context */
00710     per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
00711     oldcontext = MemoryContextSwitchTo(per_query_ctx);
00712 
00713     /*
00714      * build tupdesc for result tuples. This must match the definition of the
00715      * pg_prepared_statements view in system_views.sql
00716      */
00717     tupdesc = CreateTemplateTupleDesc(5, false);
00718     TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
00719                        TEXTOID, -1, 0);
00720     TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
00721                        TEXTOID, -1, 0);
00722     TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepare_time",
00723                        TIMESTAMPTZOID, -1, 0);
00724     TupleDescInitEntry(tupdesc, (AttrNumber) 4, "parameter_types",
00725                        REGTYPEARRAYOID, -1, 0);
00726     TupleDescInitEntry(tupdesc, (AttrNumber) 5, "from_sql",
00727                        BOOLOID, -1, 0);
00728 
00729     /*
00730      * We put all the tuples into a tuplestore in one scan of the hashtable.
00731      * This avoids any issue of the hashtable possibly changing between calls.
00732      */
00733     tupstore =
00734         tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
00735                               false, work_mem);
00736 
00737     /* generate junk in short-term context */
00738     MemoryContextSwitchTo(oldcontext);
00739 
00740     /* hash table might be uninitialized */
00741     if (prepared_queries)
00742     {
00743         HASH_SEQ_STATUS hash_seq;
00744         PreparedStatement *prep_stmt;
00745 
00746         hash_seq_init(&hash_seq, prepared_queries);
00747         while ((prep_stmt = hash_seq_search(&hash_seq)) != NULL)
00748         {
00749             Datum       values[5];
00750             bool        nulls[5];
00751 
00752             MemSet(nulls, 0, sizeof(nulls));
00753 
00754             values[0] = CStringGetTextDatum(prep_stmt->stmt_name);
00755             values[1] = CStringGetTextDatum(prep_stmt->plansource->query_string);
00756             values[2] = TimestampTzGetDatum(prep_stmt->prepare_time);
00757             values[3] = build_regtype_array(prep_stmt->plansource->param_types,
00758                                           prep_stmt->plansource->num_params);
00759             values[4] = BoolGetDatum(prep_stmt->from_sql);
00760 
00761             tuplestore_putvalues(tupstore, tupdesc, values, nulls);
00762         }
00763     }
00764 
00765     /* clean up and return the tuplestore */
00766     tuplestore_donestoring(tupstore);
00767 
00768     rsinfo->returnMode = SFRM_Materialize;
00769     rsinfo->setResult = tupstore;
00770     rsinfo->setDesc = tupdesc;
00771 
00772     return (Datum) 0;
00773 }
00774 
00775 /*
00776  * This utility function takes a C array of Oids, and returns a Datum
00777  * pointing to a one-dimensional Postgres array of regtypes. An empty
00778  * array is returned as a zero-element array, not NULL.
00779  */
00780 static Datum
00781 build_regtype_array(Oid *param_types, int num_params)
00782 {
00783     Datum      *tmp_ary;
00784     ArrayType  *result;
00785     int         i;
00786 
00787     tmp_ary = (Datum *) palloc(num_params * sizeof(Datum));
00788 
00789     for (i = 0; i < num_params; i++)
00790         tmp_ary[i] = ObjectIdGetDatum(param_types[i]);
00791 
00792     /* XXX: this hardcodes assumptions about the regtype type */
00793     result = construct_array(tmp_ary, num_params, REGTYPEOID, 4, true, 'i');
00794     return PointerGetDatum(result);
00795 }