LCOV - code coverage report
Current view: top level - src/backend/commands - prepare.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 94.9 % 296 281
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 14 14
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 66.7 % 144 96

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

Generated by: LCOV version 2.3.2-1