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

Generated by: LCOV version 1.14