LCOV - code coverage report
Current view: top level - src/backend/commands - prepare.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 93.9 % 213 200
Test Date: 2026-03-20 21:16:03 Functions: 100.0 % 14 14
Legend: Lines:     hit not hit

            Line data    Source code
       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-2026, PostgreSQL Global Development Group
      11              :  *
      12              :  * IDENTIFICATION
      13              :  *    src/backend/commands/prepare.c
      14              :  *
      15              :  *-------------------------------------------------------------------------
      16              :  */
      17              : #include "postgres.h"
      18              : 
      19              : #include <limits.h>
      20              : 
      21              : #include "access/xact.h"
      22              : #include "catalog/pg_type.h"
      23              : #include "commands/createas.h"
      24              : #include "commands/explain.h"
      25              : #include "commands/explain_format.h"
      26              : #include "commands/explain_state.h"
      27              : #include "commands/prepare.h"
      28              : #include "funcapi.h"
      29              : #include "nodes/nodeFuncs.h"
      30              : #include "parser/parse_coerce.h"
      31              : #include "parser/parse_collate.h"
      32              : #include "parser/parse_expr.h"
      33              : #include "parser/parse_type.h"
      34              : #include "tcop/pquery.h"
      35              : #include "tcop/utility.h"
      36              : #include "utils/builtins.h"
      37              : #include "utils/hsearch.h"
      38              : #include "utils/snapmgr.h"
      39              : #include "utils/timestamp.h"
      40              : #include "utils/tuplestore.h"
      41              : 
      42              : 
      43              : /*
      44              :  * The hash table in which prepared queries are stored. This is
      45              :  * per-backend: query plans are not shared between backends.
      46              :  * The keys for this hash table are the arguments to PREPARE and EXECUTE
      47              :  * (statement names); the entries are PreparedStatement structs.
      48              :  */
      49              : static HTAB *prepared_queries = NULL;
      50              : 
      51              : static void InitQueryHashTable(void);
      52              : static ParamListInfo EvaluateParams(ParseState *pstate,
      53              :                                     PreparedStatement *pstmt, List *params,
      54              :                                     EState *estate);
      55              : static Datum build_regtype_array(Oid *param_types, int num_params);
      56              : 
      57              : /*
      58              :  * Implements the 'PREPARE' utility statement.
      59              :  */
      60              : void
      61         1185 : PrepareQuery(ParseState *pstate, PrepareStmt *stmt,
      62              :              int stmt_location, int stmt_len)
      63              : {
      64              :     RawStmt    *rawstmt;
      65              :     CachedPlanSource *plansource;
      66         1185 :     Oid        *argtypes = NULL;
      67              :     int         nargs;
      68              :     List       *query_list;
      69              : 
      70              :     /*
      71              :      * Disallow empty-string statement name (conflicts with protocol-level
      72              :      * unnamed statement).
      73              :      */
      74         1185 :     if (!stmt->name || stmt->name[0] == '\0')
      75            0 :         ereport(ERROR,
      76              :                 (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
      77              :                  errmsg("invalid statement name: must not be empty")));
      78              : 
      79              :     /*
      80              :      * Need to wrap the contained statement in a RawStmt node to pass it to
      81              :      * parse analysis.
      82              :      */
      83         1185 :     rawstmt = makeNode(RawStmt);
      84         1185 :     rawstmt->stmt = stmt->query;
      85         1185 :     rawstmt->stmt_location = stmt_location;
      86         1185 :     rawstmt->stmt_len = stmt_len;
      87              : 
      88              :     /*
      89              :      * Create the CachedPlanSource before we do parse analysis, since it needs
      90              :      * to see the unmodified raw parse tree.
      91              :      */
      92         1185 :     plansource = CreateCachedPlan(rawstmt, pstate->p_sourcetext,
      93              :                                   CreateCommandTag(stmt->query));
      94              : 
      95              :     /* Transform list of TypeNames to array of type OIDs */
      96         1185 :     nargs = list_length(stmt->argtypes);
      97              : 
      98         1185 :     if (nargs)
      99              :     {
     100              :         int         i;
     101              :         ListCell   *l;
     102              : 
     103          960 :         argtypes = palloc_array(Oid, nargs);
     104          960 :         i = 0;
     105              : 
     106         2070 :         foreach(l, stmt->argtypes)
     107              :         {
     108         1114 :             TypeName   *tn = lfirst(l);
     109         1114 :             Oid         toid = typenameTypeId(pstate, tn);
     110              : 
     111         1110 :             argtypes[i++] = toid;
     112              :         }
     113              :     }
     114              : 
     115              :     /*
     116              :      * Analyze the statement using these parameter types (any parameters
     117              :      * passed in from above us will not be visible to it), allowing
     118              :      * information about unknown parameters to be deduced from context.
     119              :      * Rewrite the query. The result could be 0, 1, or many queries.
     120              :      */
     121         1181 :     query_list = pg_analyze_and_rewrite_varparams(rawstmt, pstate->p_sourcetext,
     122              :                                                   &argtypes, &nargs, NULL);
     123              : 
     124              :     /* Finish filling in the CachedPlanSource */
     125         1181 :     CompleteCachedPlan(plansource,
     126              :                        query_list,
     127              :                        NULL,
     128              :                        argtypes,
     129              :                        nargs,
     130              :                        NULL,
     131              :                        NULL,
     132              :                        CURSOR_OPT_PARALLEL_OK,  /* allow parallel mode */
     133              :                        true);   /* fixed result */
     134              : 
     135              :     /*
     136              :      * Save the results.
     137              :      */
     138         1181 :     StorePreparedStatement(stmt->name,
     139              :                            plansource,
     140              :                            true);
     141         1177 : }
     142              : 
     143              : /*
     144              :  * ExecuteQuery --- implement the 'EXECUTE' utility statement.
     145              :  *
     146              :  * This code also supports CREATE TABLE ... AS EXECUTE.  That case is
     147              :  * indicated by passing a non-null intoClause.  The DestReceiver is already
     148              :  * set up correctly for CREATE TABLE AS, but we still have to make a few
     149              :  * other adjustments here.
     150              :  */
     151              : void
     152         8618 : ExecuteQuery(ParseState *pstate,
     153              :              ExecuteStmt *stmt, IntoClause *intoClause,
     154              :              ParamListInfo params,
     155              :              DestReceiver *dest, QueryCompletion *qc)
     156              : {
     157              :     PreparedStatement *entry;
     158              :     CachedPlan *cplan;
     159              :     List       *plan_list;
     160         8618 :     ParamListInfo paramLI = NULL;
     161         8618 :     EState     *estate = NULL;
     162              :     Portal      portal;
     163              :     char       *query_string;
     164              :     int         eflags;
     165              :     long        count;
     166              : 
     167              :     /* Look it up in the hash table */
     168         8618 :     entry = FetchPreparedStatement(stmt->name, true);
     169              : 
     170              :     /* Shouldn't find a non-fixed-result cached plan */
     171         8618 :     if (!entry->plansource->fixed_result)
     172            0 :         elog(ERROR, "EXECUTE does not support variable-result cached plans");
     173              : 
     174              :     /* Evaluate parameters, if any */
     175         8618 :     if (entry->plansource->num_params > 0)
     176              :     {
     177              :         /*
     178              :          * Need an EState to evaluate parameters; must not delete it till end
     179              :          * of query, in case parameters are pass-by-reference.  Note that the
     180              :          * passed-in "params" could possibly be referenced in the parameter
     181              :          * expressions.
     182              :          */
     183         7798 :         estate = CreateExecutorState();
     184         7798 :         estate->es_param_list_info = params;
     185         7798 :         paramLI = EvaluateParams(pstate, entry, stmt->params, estate);
     186              :     }
     187              : 
     188              :     /* Create a new portal to run the query in */
     189         8594 :     portal = CreateNewPortal();
     190              :     /* Don't display the portal in pg_cursors, it is for internal use only */
     191         8594 :     portal->visible = false;
     192              : 
     193              :     /* Copy the plan's saved query string into the portal's memory */
     194         8594 :     query_string = MemoryContextStrdup(portal->portalContext,
     195         8594 :                                        entry->plansource->query_string);
     196              : 
     197              :     /* Replan if needed, and increment plan refcount for portal */
     198         8594 :     cplan = GetCachedPlan(entry->plansource, paramLI, NULL, NULL);
     199         8550 :     plan_list = cplan->stmt_list;
     200              : 
     201              :     /*
     202              :      * DO NOT add any logic that could possibly throw an error between
     203              :      * GetCachedPlan and PortalDefineQuery, or you'll leak the plan refcount.
     204              :      */
     205         8550 :     PortalDefineQuery(portal,
     206              :                       NULL,
     207              :                       query_string,
     208         8550 :                       entry->plansource->commandTag,
     209              :                       plan_list,
     210              :                       cplan);
     211              : 
     212              :     /*
     213              :      * For CREATE TABLE ... AS EXECUTE, we must verify that the prepared
     214              :      * statement is one that produces tuples.  Currently we insist that it be
     215              :      * a plain old SELECT.  In future we might consider supporting other
     216              :      * things such as INSERT ... RETURNING, but there are a couple of issues
     217              :      * to be settled first, notably how WITH NO DATA should be handled in such
     218              :      * a case (do we really want to suppress execution?) and how to pass down
     219              :      * the OID-determining eflags (PortalStart won't handle them in such a
     220              :      * case, and for that matter it's not clear the executor will either).
     221              :      *
     222              :      * For CREATE TABLE ... AS EXECUTE, we also have to ensure that the proper
     223              :      * eflags and fetch count are passed to PortalStart/PortalRun.
     224              :      */
     225         8550 :     if (intoClause)
     226              :     {
     227              :         PlannedStmt *pstmt;
     228              : 
     229           30 :         if (list_length(plan_list) != 1)
     230            0 :             ereport(ERROR,
     231              :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     232              :                      errmsg("prepared statement is not a SELECT")));
     233           30 :         pstmt = linitial_node(PlannedStmt, plan_list);
     234           30 :         if (pstmt->commandType != CMD_SELECT)
     235            0 :             ereport(ERROR,
     236              :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     237              :                      errmsg("prepared statement is not a SELECT")));
     238              : 
     239              :         /* Set appropriate eflags */
     240           30 :         eflags = GetIntoRelEFlags(intoClause);
     241              : 
     242              :         /* And tell PortalRun whether to run to completion or not */
     243           30 :         if (intoClause->skipData)
     244            8 :             count = 0;
     245              :         else
     246           22 :             count = FETCH_ALL;
     247              :     }
     248              :     else
     249              :     {
     250              :         /* Plain old EXECUTE */
     251         8520 :         eflags = 0;
     252         8520 :         count = FETCH_ALL;
     253              :     }
     254              : 
     255              :     /*
     256              :      * Run the portal as appropriate.
     257              :      */
     258         8550 :     PortalStart(portal, paramLI, eflags, GetActiveSnapshot());
     259              : 
     260         8550 :     (void) PortalRun(portal, count, false, dest, dest, qc);
     261              : 
     262         8525 :     PortalDrop(portal, false);
     263              : 
     264         8525 :     if (estate)
     265         7746 :         FreeExecutorState(estate);
     266              : 
     267              :     /* No need to pfree other memory, MemoryContext will be reset */
     268         8525 : }
     269              : 
     270              : /*
     271              :  * EvaluateParams: evaluate a list of parameters.
     272              :  *
     273              :  * pstate: parse state
     274              :  * pstmt: statement we are getting parameters for.
     275              :  * params: list of given parameter expressions (raw parser output!)
     276              :  * estate: executor state to use.
     277              :  *
     278              :  * Returns a filled-in ParamListInfo -- this can later be passed to
     279              :  * CreateQueryDesc(), which allows the executor to make use of the parameters
     280              :  * during query execution.
     281              :  */
     282              : static ParamListInfo
     283         7988 : EvaluateParams(ParseState *pstate, PreparedStatement *pstmt, List *params,
     284              :                EState *estate)
     285              : {
     286         7988 :     Oid        *param_types = pstmt->plansource->param_types;
     287         7988 :     int         num_params = pstmt->plansource->num_params;
     288         7988 :     int         nparams = list_length(params);
     289              :     ParamListInfo paramLI;
     290              :     List       *exprstates;
     291              :     ListCell   *l;
     292              :     int         i;
     293              : 
     294         7988 :     if (nparams != num_params)
     295            8 :         ereport(ERROR,
     296              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     297              :                  errmsg("wrong number of parameters for prepared statement \"%s\"",
     298              :                         pstmt->stmt_name),
     299              :                  errdetail("Expected %d parameters but got %d.",
     300              :                            num_params, nparams)));
     301              : 
     302              :     /* Quick exit if no parameters */
     303         7980 :     if (num_params == 0)
     304            0 :         return NULL;
     305              : 
     306              :     /*
     307              :      * We have to run parse analysis for the expressions.  Since the parser is
     308              :      * not cool about scribbling on its input, copy first.
     309              :      */
     310         7980 :     params = copyObject(params);
     311              : 
     312         7980 :     i = 0;
     313        16349 :     foreach(l, params)
     314              :     {
     315         8377 :         Node       *expr = lfirst(l);
     316         8377 :         Oid         expected_type_id = param_types[i];
     317              :         Oid         given_type_id;
     318              : 
     319         8377 :         expr = transformExpr(pstate, expr, EXPR_KIND_EXECUTE_PARAMETER);
     320              : 
     321         8377 :         given_type_id = exprType(expr);
     322              : 
     323         8377 :         expr = coerce_to_target_type(pstate, expr, given_type_id,
     324              :                                      expected_type_id, -1,
     325              :                                      COERCION_ASSIGNMENT,
     326              :                                      COERCE_IMPLICIT_CAST,
     327              :                                      -1);
     328              : 
     329         8373 :         if (expr == NULL)
     330            4 :             ereport(ERROR,
     331              :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
     332              :                      errmsg("parameter $%d of type %s cannot be coerced to the expected type %s",
     333              :                             i + 1,
     334              :                             format_type_be(given_type_id),
     335              :                             format_type_be(expected_type_id)),
     336              :                      errhint("You will need to rewrite or cast the expression."),
     337              :                      parser_errposition(pstate, exprLocation(lfirst(l)))));
     338              : 
     339              :         /* Take care of collations in the finished expression. */
     340         8369 :         assign_expr_collations(pstate, expr);
     341              : 
     342         8369 :         lfirst(l) = expr;
     343         8369 :         i++;
     344              :     }
     345              : 
     346              :     /* Prepare the expressions for execution */
     347         7972 :     exprstates = ExecPrepareExprList(params, estate);
     348              : 
     349         7972 :     paramLI = makeParamList(num_params);
     350              : 
     351         7972 :     i = 0;
     352        16325 :     foreach(l, exprstates)
     353              :     {
     354         8361 :         ExprState  *n = (ExprState *) lfirst(l);
     355         8361 :         ParamExternData *prm = &paramLI->params[i];
     356              : 
     357         8361 :         prm->ptype = param_types[i];
     358         8361 :         prm->pflags = PARAM_FLAG_CONST;
     359         8361 :         prm->value = ExecEvalExprSwitchContext(n,
     360         8361 :                                                GetPerTupleExprContext(estate),
     361              :                                                &prm->isnull);
     362              : 
     363         8353 :         i++;
     364              :     }
     365              : 
     366         7964 :     return paramLI;
     367              : }
     368              : 
     369              : 
     370              : /*
     371              :  * Initialize query hash table upon first use.
     372              :  */
     373              : static void
     374          665 : InitQueryHashTable(void)
     375              : {
     376              :     HASHCTL     hash_ctl;
     377              : 
     378          665 :     hash_ctl.keysize = NAMEDATALEN;
     379          665 :     hash_ctl.entrysize = sizeof(PreparedStatement);
     380              : 
     381          665 :     prepared_queries = hash_create("Prepared Queries",
     382              :                                    32,
     383              :                                    &hash_ctl,
     384              :                                    HASH_ELEM | HASH_STRINGS);
     385          665 : }
     386              : 
     387              : /*
     388              :  * Store all the data pertaining to a query in the hash table using
     389              :  * the specified key.  The passed CachedPlanSource should be "unsaved"
     390              :  * in case we get an error here; we'll save it once we've created the hash
     391              :  * table entry.
     392              :  */
     393              : void
     394         3380 : StorePreparedStatement(const char *stmt_name,
     395              :                        CachedPlanSource *plansource,
     396              :                        bool from_sql)
     397              : {
     398              :     PreparedStatement *entry;
     399         3380 :     TimestampTz cur_ts = GetCurrentStatementStartTimestamp();
     400              :     bool        found;
     401              : 
     402              :     /* Initialize the hash table, if necessary */
     403         3380 :     if (!prepared_queries)
     404          665 :         InitQueryHashTable();
     405              : 
     406              :     /* Add entry to hash table */
     407         3380 :     entry = (PreparedStatement *) hash_search(prepared_queries,
     408              :                                               stmt_name,
     409              :                                               HASH_ENTER,
     410              :                                               &found);
     411              : 
     412              :     /* Shouldn't get a duplicate entry */
     413         3380 :     if (found)
     414            4 :         ereport(ERROR,
     415              :                 (errcode(ERRCODE_DUPLICATE_PSTATEMENT),
     416              :                  errmsg("prepared statement \"%s\" already exists",
     417              :                         stmt_name)));
     418              : 
     419              :     /* Fill in the hash table entry */
     420         3376 :     entry->plansource = plansource;
     421         3376 :     entry->from_sql = from_sql;
     422         3376 :     entry->prepare_time = cur_ts;
     423              : 
     424              :     /* Now it's safe to move the CachedPlanSource to permanent memory */
     425         3376 :     SaveCachedPlan(plansource);
     426         3376 : }
     427              : 
     428              : /*
     429              :  * Lookup an existing query in the hash table. If the query does not
     430              :  * actually exist, throw ereport(ERROR) or return NULL per second parameter.
     431              :  *
     432              :  * Note: this does not force the referenced plancache entry to be valid,
     433              :  * since not all callers care.
     434              :  */
     435              : PreparedStatement *
     436        50147 : FetchPreparedStatement(const char *stmt_name, bool throwError)
     437              : {
     438              :     PreparedStatement *entry;
     439              : 
     440              :     /*
     441              :      * If the hash table hasn't been initialized, it can't be storing
     442              :      * anything, therefore it couldn't possibly store our plan.
     443              :      */
     444        50147 :     if (prepared_queries)
     445        50146 :         entry = (PreparedStatement *) hash_search(prepared_queries,
     446              :                                                   stmt_name,
     447              :                                                   HASH_FIND,
     448              :                                                   NULL);
     449              :     else
     450            1 :         entry = NULL;
     451              : 
     452        50147 :     if (!entry && throwError)
     453            6 :         ereport(ERROR,
     454              :                 (errcode(ERRCODE_UNDEFINED_PSTATEMENT),
     455              :                  errmsg("prepared statement \"%s\" does not exist",
     456              :                         stmt_name)));
     457              : 
     458        50141 :     return entry;
     459              : }
     460              : 
     461              : /*
     462              :  * Given a prepared statement, determine the result tupledesc it will
     463              :  * produce.  Returns NULL if the execution will not return tuples.
     464              :  *
     465              :  * Note: the result is created or copied into current memory context.
     466              :  */
     467              : TupleDesc
     468         8521 : FetchPreparedStatementResultDesc(PreparedStatement *stmt)
     469              : {
     470              :     /*
     471              :      * Since we don't allow prepared statements' result tupdescs to change,
     472              :      * there's no need to worry about revalidating the cached plan here.
     473              :      */
     474              :     Assert(stmt->plansource->fixed_result);
     475         8521 :     if (stmt->plansource->resultDesc)
     476         8521 :         return CreateTupleDescCopy(stmt->plansource->resultDesc);
     477              :     else
     478            0 :         return NULL;
     479              : }
     480              : 
     481              : /*
     482              :  * Given a prepared statement that returns tuples, extract the query
     483              :  * targetlist.  Returns NIL if the statement doesn't have a determinable
     484              :  * targetlist.
     485              :  *
     486              :  * Note: this is pretty ugly, but since it's only used in corner cases like
     487              :  * Describe Statement on an EXECUTE command, we don't worry too much about
     488              :  * efficiency.
     489              :  */
     490              : List *
     491         8448 : FetchPreparedStatementTargetList(PreparedStatement *stmt)
     492              : {
     493              :     List       *tlist;
     494              : 
     495              :     /* Get the plan's primary targetlist */
     496         8448 :     tlist = CachedPlanGetTargetList(stmt->plansource, NULL);
     497              : 
     498              :     /* Copy into caller's context in case plan gets invalidated */
     499         8448 :     return copyObject(tlist);
     500              : }
     501              : 
     502              : /*
     503              :  * Implements the 'DEALLOCATE' utility statement: deletes the
     504              :  * specified plan from storage.
     505              :  */
     506              : void
     507         2093 : DeallocateQuery(DeallocateStmt *stmt)
     508              : {
     509         2093 :     if (stmt->name)
     510         2056 :         DropPreparedStatement(stmt->name, true);
     511              :     else
     512           37 :         DropAllPreparedStatements();
     513         2093 : }
     514              : 
     515              : /*
     516              :  * Internal version of DEALLOCATE
     517              :  *
     518              :  * If showError is false, dropping a nonexistent statement is a no-op.
     519              :  */
     520              : void
     521         2071 : DropPreparedStatement(const char *stmt_name, bool showError)
     522              : {
     523              :     PreparedStatement *entry;
     524              : 
     525              :     /* Find the query's hash table entry; raise error if wanted */
     526         2071 :     entry = FetchPreparedStatement(stmt_name, showError);
     527              : 
     528         2071 :     if (entry)
     529              :     {
     530              :         /* Release the plancache entry */
     531         2066 :         DropCachedPlan(entry->plansource);
     532              : 
     533              :         /* Now we can remove the hash table entry */
     534         2066 :         hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
     535              :     }
     536         2071 : }
     537              : 
     538              : /*
     539              :  * Drop all cached statements.
     540              :  */
     541              : void
     542           41 : DropAllPreparedStatements(void)
     543              : {
     544              :     HASH_SEQ_STATUS seq;
     545              :     PreparedStatement *entry;
     546              : 
     547              :     /* nothing cached */
     548           41 :     if (!prepared_queries)
     549            0 :         return;
     550              : 
     551              :     /* walk over cache */
     552           41 :     hash_seq_init(&seq, prepared_queries);
     553           89 :     while ((entry = hash_seq_search(&seq)) != NULL)
     554              :     {
     555              :         /* Release the plancache entry */
     556           48 :         DropCachedPlan(entry->plansource);
     557              : 
     558              :         /* Now we can remove the hash table entry */
     559           48 :         hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
     560              :     }
     561              : }
     562              : 
     563              : /*
     564              :  * Implements the 'EXPLAIN EXECUTE' utility statement.
     565              :  *
     566              :  * "into" is NULL unless we are doing EXPLAIN CREATE TABLE AS EXECUTE,
     567              :  * in which case executing the query should result in creating that table.
     568              :  *
     569              :  * Note: the passed-in pstate's queryString is that of the EXPLAIN EXECUTE,
     570              :  * not the original PREPARE; we get the latter string from the plancache.
     571              :  */
     572              : void
     573          278 : ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
     574              :                     ParseState *pstate, ParamListInfo params)
     575              : {
     576              :     PreparedStatement *entry;
     577              :     const char *query_string;
     578              :     CachedPlan *cplan;
     579              :     List       *plan_list;
     580              :     ListCell   *p;
     581          278 :     ParamListInfo paramLI = NULL;
     582          278 :     EState     *estate = NULL;
     583              :     instr_time  planstart;
     584              :     instr_time  planduration;
     585              :     BufferUsage bufusage_start,
     586              :                 bufusage;
     587              :     MemoryContextCounters mem_counters;
     588          278 :     MemoryContext planner_ctx = NULL;
     589          278 :     MemoryContext saved_ctx = NULL;
     590              : 
     591          278 :     if (es->memory)
     592              :     {
     593              :         /* See ExplainOneQuery about this */
     594              :         Assert(IsA(CurrentMemoryContext, AllocSetContext));
     595            4 :         planner_ctx = AllocSetContextCreate(CurrentMemoryContext,
     596              :                                             "explain analyze planner context",
     597              :                                             ALLOCSET_DEFAULT_SIZES);
     598            4 :         saved_ctx = MemoryContextSwitchTo(planner_ctx);
     599              :     }
     600              : 
     601          278 :     if (es->buffers)
     602            0 :         bufusage_start = pgBufferUsage;
     603          278 :     INSTR_TIME_SET_CURRENT(planstart);
     604              : 
     605              :     /* Look it up in the hash table */
     606          278 :     entry = FetchPreparedStatement(execstmt->name, true);
     607              : 
     608              :     /* Shouldn't find a non-fixed-result cached plan */
     609          278 :     if (!entry->plansource->fixed_result)
     610            0 :         elog(ERROR, "EXPLAIN EXECUTE does not support variable-result cached plans");
     611              : 
     612          278 :     query_string = entry->plansource->query_string;
     613              : 
     614              :     /* Evaluate parameters, if any */
     615          278 :     if (entry->plansource->num_params)
     616              :     {
     617              :         ParseState *pstate_params;
     618              : 
     619          190 :         pstate_params = make_parsestate(NULL);
     620          190 :         pstate_params->p_sourcetext = pstate->p_sourcetext;
     621              : 
     622              :         /*
     623              :          * Need an EState to evaluate parameters; must not delete it till end
     624              :          * of query, in case parameters are pass-by-reference.  Note that the
     625              :          * passed-in "params" could possibly be referenced in the parameter
     626              :          * expressions.
     627              :          */
     628          190 :         estate = CreateExecutorState();
     629          190 :         estate->es_param_list_info = params;
     630              : 
     631          190 :         paramLI = EvaluateParams(pstate_params, entry, execstmt->params, estate);
     632              :     }
     633              : 
     634              :     /* Replan if needed, and acquire a transient refcount */
     635          278 :     cplan = GetCachedPlan(entry->plansource, paramLI,
     636              :                           CurrentResourceOwner, pstate->p_queryEnv);
     637              : 
     638          278 :     INSTR_TIME_SET_CURRENT(planduration);
     639          278 :     INSTR_TIME_SUBTRACT(planduration, planstart);
     640              : 
     641          278 :     if (es->memory)
     642              :     {
     643            4 :         MemoryContextSwitchTo(saved_ctx);
     644            4 :         MemoryContextMemConsumed(planner_ctx, &mem_counters);
     645              :     }
     646              : 
     647              :     /* calc differences of buffer counters. */
     648          278 :     if (es->buffers)
     649              :     {
     650            0 :         memset(&bufusage, 0, sizeof(BufferUsage));
     651            0 :         BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
     652              :     }
     653              : 
     654          278 :     plan_list = cplan->stmt_list;
     655              : 
     656              :     /* Explain each query */
     657          556 :     foreach(p, plan_list)
     658              :     {
     659          278 :         PlannedStmt *pstmt = lfirst_node(PlannedStmt, p);
     660              : 
     661          278 :         if (pstmt->commandType != CMD_UTILITY)
     662          556 :             ExplainOnePlan(pstmt, into, es, query_string, paramLI, pstate->p_queryEnv,
     663          278 :                            &planduration, (es->buffers ? &bufusage : NULL),
     664          278 :                            es->memory ? &mem_counters : NULL);
     665              :         else
     666            0 :             ExplainOneUtility(pstmt->utilityStmt, into, es, pstate, paramLI);
     667              : 
     668              :         /* No need for CommandCounterIncrement, as ExplainOnePlan did it */
     669              : 
     670              :         /* Separate plans with an appropriate separator */
     671          278 :         if (lnext(plan_list, p) != NULL)
     672            0 :             ExplainSeparatePlans(es);
     673              :     }
     674              : 
     675          278 :     if (estate)
     676          190 :         FreeExecutorState(estate);
     677              : 
     678          278 :     ReleaseCachedPlan(cplan, CurrentResourceOwner);
     679          278 : }
     680              : 
     681              : /*
     682              :  * This set returning function reads all the prepared statements and
     683              :  * returns a set of (name, statement, prepare_time, param_types, from_sql,
     684              :  * generic_plans, custom_plans).
     685              :  */
     686              : Datum
     687           68 : pg_prepared_statement(PG_FUNCTION_ARGS)
     688              : {
     689           68 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     690              : 
     691              :     /*
     692              :      * We put all the tuples into a tuplestore in one scan of the hashtable.
     693              :      * This avoids any issue of the hashtable possibly changing between calls.
     694              :      */
     695           68 :     InitMaterializedSRF(fcinfo, 0);
     696              : 
     697              :     /* hash table might be uninitialized */
     698           68 :     if (prepared_queries)
     699              :     {
     700              :         HASH_SEQ_STATUS hash_seq;
     701              :         PreparedStatement *prep_stmt;
     702              : 
     703           60 :         hash_seq_init(&hash_seq, prepared_queries);
     704          256 :         while ((prep_stmt = hash_seq_search(&hash_seq)) != NULL)
     705              :         {
     706              :             TupleDesc   result_desc;
     707              :             Datum       values[8];
     708          196 :             bool        nulls[8] = {0};
     709              : 
     710          196 :             result_desc = prep_stmt->plansource->resultDesc;
     711              : 
     712          196 :             values[0] = CStringGetTextDatum(prep_stmt->stmt_name);
     713          196 :             values[1] = CStringGetTextDatum(prep_stmt->plansource->query_string);
     714          196 :             values[2] = TimestampTzGetDatum(prep_stmt->prepare_time);
     715          392 :             values[3] = build_regtype_array(prep_stmt->plansource->param_types,
     716          196 :                                             prep_stmt->plansource->num_params);
     717          196 :             if (result_desc)
     718              :             {
     719              :                 Oid        *result_types;
     720              : 
     721          192 :                 result_types = palloc_array(Oid, result_desc->natts);
     722          652 :                 for (int i = 0; i < result_desc->natts; i++)
     723          460 :                     result_types[i] = TupleDescAttr(result_desc, i)->atttypid;
     724          192 :                 values[4] = build_regtype_array(result_types, result_desc->natts);
     725              :             }
     726              :             else
     727              :             {
     728              :                 /* no result descriptor (for example, DML statement) */
     729            4 :                 nulls[4] = true;
     730              :             }
     731          196 :             values[5] = BoolGetDatum(prep_stmt->from_sql);
     732          196 :             values[6] = Int64GetDatumFast(prep_stmt->plansource->num_generic_plans);
     733          196 :             values[7] = Int64GetDatumFast(prep_stmt->plansource->num_custom_plans);
     734              : 
     735          196 :             tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
     736              :                                  values, nulls);
     737              :         }
     738              :     }
     739              : 
     740           68 :     return (Datum) 0;
     741              : }
     742              : 
     743              : /*
     744              :  * This utility function takes a C array of Oids, and returns a Datum
     745              :  * pointing to a one-dimensional Postgres array of regtypes. An empty
     746              :  * array is returned as a zero-element array, not NULL.
     747              :  */
     748              : static Datum
     749          388 : build_regtype_array(Oid *param_types, int num_params)
     750              : {
     751              :     Datum      *tmp_ary;
     752              :     ArrayType  *result;
     753              :     int         i;
     754              : 
     755          388 :     tmp_ary = palloc_array(Datum, num_params);
     756              : 
     757          956 :     for (i = 0; i < num_params; i++)
     758          568 :         tmp_ary[i] = ObjectIdGetDatum(param_types[i]);
     759              : 
     760          388 :     result = construct_array_builtin(tmp_ary, num_params, REGTYPEOID);
     761          388 :     return PointerGetDatum(result);
     762              : }
        

Generated by: LCOV version 2.0-1