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

Generated by: LCOV version 1.14