LCOV - differential code coverage report
Current view: top level - src/backend/commands - prepare.c (source / functions) Coverage Total Hit UBC GIC GNC CBC ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 93.7 % 205 192 13 7 14 171 17 4
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 14 14 1 2 11 1
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  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-2023, 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 "miscadmin.h"
      27                 : #include "nodes/nodeFuncs.h"
      28                 : #include "parser/analyze.h"
      29                 : #include "parser/parse_coerce.h"
      30                 : #include "parser/parse_collate.h"
      31                 : #include "parser/parse_expr.h"
      32                 : #include "parser/parse_type.h"
      33                 : #include "rewrite/rewriteHandler.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 CBC         813 : PrepareQuery(ParseState *pstate, PrepareStmt *stmt,
      60                 :              int stmt_location, int stmt_len)
      61                 : {
      62                 :     RawStmt    *rawstmt;
      63                 :     CachedPlanSource *plansource;
      64             813 :     Oid        *argtypes = NULL;
      65                 :     int         nargs;
      66                 :     List       *query_list;
      67                 : 
      68                 :     /*
      69                 :      * Disallow empty-string statement name (conflicts with protocol-level
      70                 :      * unnamed statement).
      71                 :      */
      72             813 :     if (!stmt->name || stmt->name[0] == '\0')
      73 UBC           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 CBC         813 :     rawstmt = makeNode(RawStmt);
      82             813 :     rawstmt->stmt = stmt->query;
      83             813 :     rawstmt->stmt_location = stmt_location;
      84             813 :     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             813 :     plansource = CreateCachedPlan(rawstmt, pstate->p_sourcetext,
      91                 :                                   CreateCommandTag(stmt->query));
      92                 : 
      93                 :     /* Transform list of TypeNames to array of type OIDs */
      94             813 :     nargs = list_length(stmt->argtypes);
      95                 : 
      96             813 :     if (nargs)
      97                 :     {
      98                 :         int         i;
      99                 :         ListCell   *l;
     100                 : 
     101 GNC         686 :         argtypes = palloc_array(Oid, nargs);
     102 CBC         686 :         i = 0;
     103                 : 
     104            1446 :         foreach(l, stmt->argtypes)
     105                 :         {
     106             763 :             TypeName   *tn = lfirst(l);
     107             763 :             Oid         toid = typenameTypeId(pstate, tn);
     108                 : 
     109             760 :             argtypes[i++] = toid;
     110                 :         }
     111                 :     }
     112                 : 
     113                 :     /*
     114                 :      * Analyze the statement using these parameter types (any parameters
     115                 :      * passed in from above us will not be visible to it), allowing
     116                 :      * information about unknown parameters to be deduced from context.
     117                 :      * Rewrite the query. The result could be 0, 1, or many queries.
     118                 :      */
     119             810 :     query_list = pg_analyze_and_rewrite_varparams(rawstmt, pstate->p_sourcetext,
     120                 :                                                   &argtypes, &nargs, NULL);
     121                 : 
     122                 :     /* Finish filling in the CachedPlanSource */
     123             810 :     CompleteCachedPlan(plansource,
     124                 :                        query_list,
     125                 :                        NULL,
     126                 :                        argtypes,
     127                 :                        nargs,
     128                 :                        NULL,
     129                 :                        NULL,
     130                 :                        CURSOR_OPT_PARALLEL_OK,  /* allow parallel mode */
     131                 :                        true);   /* fixed result */
     132                 : 
     133                 :     /*
     134                 :      * Save the results.
     135                 :      */
     136             810 :     StorePreparedStatement(stmt->name,
     137                 :                            plansource,
     138                 :                            true);
     139             807 : }
     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            4619 : ExecuteQuery(ParseState *pstate,
     151                 :              ExecuteStmt *stmt, IntoClause *intoClause,
     152                 :              ParamListInfo params,
     153                 :              DestReceiver *dest, QueryCompletion *qc)
     154                 : {
     155                 :     PreparedStatement *entry;
     156                 :     CachedPlan *cplan;
     157                 :     List       *plan_list;
     158            4619 :     ParamListInfo paramLI = NULL;
     159            4619 :     EState     *estate = NULL;
     160                 :     Portal      portal;
     161                 :     char       *query_string;
     162                 :     int         eflags;
     163                 :     long        count;
     164                 : 
     165                 :     /* Look it up in the hash table */
     166            4619 :     entry = FetchPreparedStatement(stmt->name, true);
     167                 : 
     168                 :     /* Shouldn't find a non-fixed-result cached plan */
     169            4619 :     if (!entry->plansource->fixed_result)
     170 UBC           0 :         elog(ERROR, "EXECUTE does not support variable-result cached plans");
     171                 : 
     172                 :     /* Evaluate parameters, if any */
     173 CBC        4619 :     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            4169 :         estate = CreateExecutorState();
     182            4169 :         estate->es_param_list_info = params;
     183            4169 :         paramLI = EvaluateParams(pstate, entry, stmt->params, estate);
     184                 :     }
     185                 : 
     186                 :     /* Create a new portal to run the query in */
     187            4601 :     portal = CreateNewPortal();
     188                 :     /* Don't display the portal in pg_cursors, it is for internal use only */
     189            4601 :     portal->visible = false;
     190                 : 
     191                 :     /* Copy the plan's saved query string into the portal's memory */
     192            4601 :     query_string = MemoryContextStrdup(portal->portalContext,
     193            4601 :                                        entry->plansource->query_string);
     194                 : 
     195                 :     /* Replan if needed, and increment plan refcount for portal */
     196            4601 :     cplan = GetCachedPlan(entry->plansource, paramLI, NULL, NULL);
     197            4586 :     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            4586 :     PortalDefineQuery(portal,
     204                 :                       NULL,
     205                 :                       query_string,
     206            4586 :                       entry->plansource->commandTag,
     207                 :                       plan_list,
     208                 :                       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            4586 :     if (intoClause)
     224                 :     {
     225                 :         PlannedStmt *pstmt;
     226                 : 
     227              21 :         if (list_length(plan_list) != 1)
     228 UBC           0 :             ereport(ERROR,
     229                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     230                 :                      errmsg("prepared statement is not a SELECT")));
     231 CBC          21 :         pstmt = linitial_node(PlannedStmt, plan_list);
     232              21 :         if (pstmt->commandType != CMD_SELECT)
     233 UBC           0 :             ereport(ERROR,
     234                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     235                 :                      errmsg("prepared statement is not a SELECT")));
     236                 : 
     237                 :         /* Set appropriate eflags */
     238 CBC          21 :         eflags = GetIntoRelEFlags(intoClause);
     239                 : 
     240                 :         /* And tell PortalRun whether to run to completion or not */
     241              21 :         if (intoClause->skipData)
     242               6 :             count = 0;
     243                 :         else
     244              15 :             count = FETCH_ALL;
     245                 :     }
     246                 :     else
     247                 :     {
     248                 :         /* Plain old EXECUTE */
     249            4565 :         eflags = 0;
     250            4565 :         count = FETCH_ALL;
     251                 :     }
     252                 : 
     253                 :     /*
     254                 :      * Run the portal as appropriate.
     255                 :      */
     256            4586 :     PortalStart(portal, paramLI, eflags, GetActiveSnapshot());
     257                 : 
     258            4586 :     (void) PortalRun(portal, count, false, true, dest, dest, qc);
     259                 : 
     260            4570 :     PortalDrop(portal, false);
     261                 : 
     262            4570 :     if (estate)
     263            4133 :         FreeExecutorState(estate);
     264                 : 
     265                 :     /* No need to pfree other memory, MemoryContext will be reset */
     266            4570 : }
     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            4288 : EvaluateParams(ParseState *pstate, PreparedStatement *pstmt, List *params,
     282                 :                EState *estate)
     283                 : {
     284            4288 :     Oid        *param_types = pstmt->plansource->param_types;
     285            4288 :     int         num_params = pstmt->plansource->num_params;
     286            4288 :     int         nparams = list_length(params);
     287                 :     ParamListInfo paramLI;
     288                 :     List       *exprstates;
     289                 :     ListCell   *l;
     290                 :     int         i;
     291                 : 
     292            4288 :     if (nparams != num_params)
     293               6 :         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            4282 :     if (num_params == 0)
     302 UBC           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 CBC        4282 :     params = copyObject(params);
     309                 : 
     310            4282 :     i = 0;
     311            8710 :     foreach(l, params)
     312                 :     {
     313            4434 :         Node       *expr = lfirst(l);
     314            4434 :         Oid         expected_type_id = param_types[i];
     315                 :         Oid         given_type_id;
     316                 : 
     317            4434 :         expr = transformExpr(pstate, expr, EXPR_KIND_EXECUTE_PARAMETER);
     318                 : 
     319            4434 :         given_type_id = exprType(expr);
     320                 : 
     321            4434 :         expr = coerce_to_target_type(pstate, expr, given_type_id,
     322                 :                                      expected_type_id, -1,
     323                 :                                      COERCION_ASSIGNMENT,
     324                 :                                      COERCE_IMPLICIT_CAST,
     325                 :                                      -1);
     326                 : 
     327            4431 :         if (expr == NULL)
     328               3 :             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            4428 :         assign_expr_collations(pstate, expr);
     339                 : 
     340            4428 :         lfirst(l) = expr;
     341            4428 :         i++;
     342                 :     }
     343                 : 
     344                 :     /* Prepare the expressions for execution */
     345            4276 :     exprstates = ExecPrepareExprList(params, estate);
     346                 : 
     347            4276 :     paramLI = makeParamList(num_params);
     348                 : 
     349            4276 :     i = 0;
     350            8692 :     foreach(l, exprstates)
     351                 :     {
     352            4422 :         ExprState  *n = (ExprState *) lfirst(l);
     353            4422 :         ParamExternData *prm = &paramLI->params[i];
     354                 : 
     355            4422 :         prm->ptype = param_types[i];
     356            4422 :         prm->pflags = PARAM_FLAG_CONST;
     357            4422 :         prm->value = ExecEvalExprSwitchContext(n,
     358            4422 :                                                GetPerTupleExprContext(estate),
     359                 :                                                &prm->isnull);
     360                 : 
     361            4416 :         i++;
     362                 :     }
     363                 : 
     364            4270 :     return paramLI;
     365                 : }
     366                 : 
     367                 : 
     368                 : /*
     369                 :  * Initialize query hash table upon first use.
     370                 :  */
     371                 : static void
     372             407 : InitQueryHashTable(void)
     373                 : {
     374                 :     HASHCTL     hash_ctl;
     375                 : 
     376             407 :     hash_ctl.keysize = NAMEDATALEN;
     377             407 :     hash_ctl.entrysize = sizeof(PreparedStatement);
     378                 : 
     379             407 :     prepared_queries = hash_create("Prepared Queries",
     380                 :                                    32,
     381                 :                                    &hash_ctl,
     382                 :                                    HASH_ELEM | HASH_STRINGS);
     383             407 : }
     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            2077 : StorePreparedStatement(const char *stmt_name,
     393                 :                        CachedPlanSource *plansource,
     394                 :                        bool from_sql)
     395                 : {
     396                 :     PreparedStatement *entry;
     397            2077 :     TimestampTz cur_ts = GetCurrentStatementStartTimestamp();
     398                 :     bool        found;
     399                 : 
     400                 :     /* Initialize the hash table, if necessary */
     401            2077 :     if (!prepared_queries)
     402             407 :         InitQueryHashTable();
     403                 : 
     404                 :     /* Add entry to hash table */
     405            2077 :     entry = (PreparedStatement *) hash_search(prepared_queries,
     406                 :                                               stmt_name,
     407                 :                                               HASH_ENTER,
     408                 :                                               &found);
     409                 : 
     410                 :     /* Shouldn't get a duplicate entry */
     411            2077 :     if (found)
     412               3 :         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            2074 :     entry->plansource = plansource;
     419            2074 :     entry->from_sql = from_sql;
     420            2074 :     entry->prepare_time = cur_ts;
     421                 : 
     422                 :     /* Now it's safe to move the CachedPlanSource to permanent memory */
     423            2074 :     SaveCachedPlan(plansource);
     424            2074 : }
     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           31857 : FetchPreparedStatement(const char *stmt_name, bool throwError)
     435                 : {
     436                 :     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           31857 :     if (prepared_queries)
     443           31856 :         entry = (PreparedStatement *) hash_search(prepared_queries,
     444                 :                                                   stmt_name,
     445                 :                                                   HASH_FIND,
     446                 :                                                   NULL);
     447                 :     else
     448               1 :         entry = NULL;
     449                 : 
     450           31857 :     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           31856 :     return entry;
     457                 : }
     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            4550 : 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            4550 :     Assert(stmt->plansource->fixed_result);
     473            4550 :     if (stmt->plansource->resultDesc)
     474            4550 :         return CreateTupleDescCopy(stmt->plansource->resultDesc);
     475                 :     else
     476 UBC           0 :         return NULL;
     477                 : }
     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 CBC        4516 : FetchPreparedStatementTargetList(PreparedStatement *stmt)
     490                 : {
     491                 :     List       *tlist;
     492                 : 
     493                 :     /* Get the plan's primary targetlist */
     494            4516 :     tlist = CachedPlanGetTargetList(stmt->plansource, NULL);
     495                 : 
     496                 :     /* Copy into caller's context in case plan gets invalidated */
     497            4516 :     return copyObject(tlist);
     498                 : }
     499                 : 
     500                 : /*
     501                 :  * Implements the 'DEALLOCATE' utility statement: deletes the
     502                 :  * specified plan from storage.
     503                 :  */
     504                 : void
     505            1151 : DeallocateQuery(DeallocateStmt *stmt)
     506                 : {
     507            1151 :     if (stmt->name)
     508            1128 :         DropPreparedStatement(stmt->name, true);
     509                 :     else
     510              23 :         DropAllPreparedStatements();
     511            1151 : }
     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            1128 : DropPreparedStatement(const char *stmt_name, bool showError)
     520                 : {
     521                 :     PreparedStatement *entry;
     522                 : 
     523                 :     /* Find the query's hash table entry; raise error if wanted */
     524            1128 :     entry = FetchPreparedStatement(stmt_name, showError);
     525                 : 
     526            1128 :     if (entry)
     527                 :     {
     528                 :         /* Release the plancache entry */
     529            1128 :         DropCachedPlan(entry->plansource);
     530                 : 
     531                 :         /* Now we can remove the hash table entry */
     532            1128 :         hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
     533                 :     }
     534            1128 : }
     535                 : 
     536                 : /*
     537                 :  * Drop all cached statements.
     538                 :  */
     539                 : void
     540              26 : DropAllPreparedStatements(void)
     541                 : {
     542                 :     HASH_SEQ_STATUS seq;
     543                 :     PreparedStatement *entry;
     544                 : 
     545                 :     /* nothing cached */
     546              26 :     if (!prepared_queries)
     547 UBC           0 :         return;
     548                 : 
     549                 :     /* walk over cache */
     550 CBC          26 :     hash_seq_init(&seq, prepared_queries);
     551              59 :     while ((entry = hash_seq_search(&seq)) != NULL)
     552                 :     {
     553                 :         /* Release the plancache entry */
     554              33 :         DropCachedPlan(entry->plansource);
     555                 : 
     556                 :         /* Now we can remove the hash table entry */
     557              33 :         hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
     558                 :     }
     559                 : }
     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 queryString is that of the EXPLAIN EXECUTE,
     568                 :  * not the original PREPARE; we get the latter string from the plancache.
     569                 :  */
     570                 : void
     571             181 : ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
     572                 :                     const char *queryString, ParamListInfo params,
     573                 :                     QueryEnvironment *queryEnv)
     574                 : {
     575                 :     PreparedStatement *entry;
     576                 :     const char *query_string;
     577                 :     CachedPlan *cplan;
     578                 :     List       *plan_list;
     579                 :     ListCell   *p;
     580             181 :     ParamListInfo paramLI = NULL;
     581             181 :     EState     *estate = NULL;
     582                 :     instr_time  planstart;
     583                 :     instr_time  planduration;
     584                 :     BufferUsage bufusage_start,
     585                 :                 bufusage;
     586                 : 
     587             181 :     if (es->buffers)
     588 UBC           0 :         bufusage_start = pgBufferUsage;
     589 CBC         181 :     INSTR_TIME_SET_CURRENT(planstart);
     590                 : 
     591                 :     /* Look it up in the hash table */
     592             181 :     entry = FetchPreparedStatement(execstmt->name, true);
     593                 : 
     594                 :     /* Shouldn't find a non-fixed-result cached plan */
     595             181 :     if (!entry->plansource->fixed_result)
     596 UBC           0 :         elog(ERROR, "EXPLAIN EXECUTE does not support variable-result cached plans");
     597                 : 
     598 CBC         181 :     query_string = entry->plansource->query_string;
     599                 : 
     600                 :     /* Evaluate parameters, if any */
     601             181 :     if (entry->plansource->num_params)
     602                 :     {
     603                 :         ParseState *pstate;
     604                 : 
     605             119 :         pstate = make_parsestate(NULL);
     606             119 :         pstate->p_sourcetext = queryString;
     607                 : 
     608                 :         /*
     609                 :          * Need an EState to evaluate parameters; must not delete it till end
     610                 :          * of query, in case parameters are pass-by-reference.  Note that the
     611                 :          * passed-in "params" could possibly be referenced in the parameter
     612                 :          * expressions.
     613                 :          */
     614             119 :         estate = CreateExecutorState();
     615             119 :         estate->es_param_list_info = params;
     616                 : 
     617             119 :         paramLI = EvaluateParams(pstate, entry, execstmt->params, estate);
     618                 :     }
     619                 : 
     620                 :     /* Replan if needed, and acquire a transient refcount */
     621             181 :     cplan = GetCachedPlan(entry->plansource, paramLI,
     622                 :                           CurrentResourceOwner, queryEnv);
     623                 : 
     624             181 :     INSTR_TIME_SET_CURRENT(planduration);
     625             181 :     INSTR_TIME_SUBTRACT(planduration, planstart);
     626                 : 
     627                 :     /* calc differences of buffer counters. */
     628             181 :     if (es->buffers)
     629                 :     {
     630 UBC           0 :         memset(&bufusage, 0, sizeof(BufferUsage));
     631               0 :         BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
     632                 :     }
     633                 : 
     634 CBC         181 :     plan_list = cplan->stmt_list;
     635                 : 
     636                 :     /* Explain each query */
     637             362 :     foreach(p, plan_list)
     638                 :     {
     639             181 :         PlannedStmt *pstmt = lfirst_node(PlannedStmt, p);
     640                 : 
     641             181 :         if (pstmt->commandType != CMD_UTILITY)
     642             181 :             ExplainOnePlan(pstmt, into, es, query_string, paramLI, queryEnv,
     643             181 :                            &planduration, (es->buffers ? &bufusage : NULL));
     644                 :         else
     645 UBC           0 :             ExplainOneUtility(pstmt->utilityStmt, into, es, query_string,
     646                 :                               paramLI, queryEnv);
     647                 : 
     648                 :         /* No need for CommandCounterIncrement, as ExplainOnePlan did it */
     649                 : 
     650                 :         /* Separate plans with an appropriate separator */
     651 CBC         181 :         if (lnext(plan_list, p) != NULL)
     652 UBC           0 :             ExplainSeparatePlans(es);
     653                 :     }
     654                 : 
     655 CBC         181 :     if (estate)
     656             119 :         FreeExecutorState(estate);
     657                 : 
     658             181 :     ReleaseCachedPlan(cplan, CurrentResourceOwner);
     659             181 : }
     660                 : 
     661                 : /*
     662                 :  * This set returning function reads all the prepared statements and
     663                 :  * returns a set of (name, statement, prepare_time, param_types, from_sql,
     664                 :  * generic_plans, custom_plans).
     665                 :  */
     666                 : Datum
     667              48 : pg_prepared_statement(PG_FUNCTION_ARGS)
     668                 : {
     669              48 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     670                 : 
     671                 :     /*
     672                 :      * We put all the tuples into a tuplestore in one scan of the hashtable.
     673                 :      * This avoids any issue of the hashtable possibly changing between calls.
     674                 :      */
     675              48 :     InitMaterializedSRF(fcinfo, 0);
     676                 : 
     677                 :     /* hash table might be uninitialized */
     678              48 :     if (prepared_queries)
     679                 :     {
     680                 :         HASH_SEQ_STATUS hash_seq;
     681                 :         PreparedStatement *prep_stmt;
     682                 : 
     683              42 :         hash_seq_init(&hash_seq, prepared_queries);
     684             183 :         while ((prep_stmt = hash_seq_search(&hash_seq)) != NULL)
     685                 :         {
     686                 :             TupleDesc   result_desc;
     687                 :             Datum       values[8];
     688 GNC         141 :             bool        nulls[8] = {0};
     689 ECB             : 
     690 GNC         141 :             result_desc = prep_stmt->plansource->resultDesc;
     691 ECB             : 
     692 GIC         141 :             values[0] = CStringGetTextDatum(prep_stmt->stmt_name);
     693 CBC         141 :             values[1] = CStringGetTextDatum(prep_stmt->plansource->query_string);
     694             141 :             values[2] = TimestampTzGetDatum(prep_stmt->prepare_time);
     695             282 :             values[3] = build_regtype_array(prep_stmt->plansource->param_types,
     696             141 :                                             prep_stmt->plansource->num_params);
     697 GNC         141 :             if (result_desc)
     698                 :             {
     699                 :                 Oid        *result_types;
     700                 : 
     701             138 :                 result_types = palloc_array(Oid, result_desc->natts);
     702             474 :                 for (int i = 0; i < result_desc->natts; i++)
     703             336 :                     result_types[i] = result_desc->attrs[i].atttypid;
     704             138 :                 values[4] = build_regtype_array(result_types, result_desc->natts);
     705                 :             }
     706                 :             else
     707                 :             {
     708                 :                 /* no result descriptor (for example, DML statement) */
     709               3 :                 nulls[4] = true;
     710                 :             }
     711             141 :             values[5] = BoolGetDatum(prep_stmt->from_sql);
     712             141 :             values[6] = Int64GetDatumFast(prep_stmt->plansource->num_generic_plans);
     713             141 :             values[7] = Int64GetDatumFast(prep_stmt->plansource->num_custom_plans);
     714                 : 
     715 GIC         141 :             tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
     716 ECB             :                                  values, nulls);
     717                 :         }
     718                 :     }
     719                 : 
     720 GIC          48 :     return (Datum) 0;
     721                 : }
     722                 : 
     723                 : /*
     724 ECB             :  * This utility function takes a C array of Oids, and returns a Datum
     725                 :  * pointing to a one-dimensional Postgres array of regtypes. An empty
     726                 :  * array is returned as a zero-element array, not NULL.
     727                 :  */
     728                 : static Datum
     729 GIC         279 : build_regtype_array(Oid *param_types, int num_params)
     730 ECB             : {
     731                 :     Datum      *tmp_ary;
     732                 :     ArrayType  *result;
     733                 :     int         i;
     734                 : 
     735 GNC         279 :     tmp_ary = palloc_array(Datum, num_params);
     736                 : 
     737 GIC         690 :     for (i = 0; i < num_params; i++)
     738             411 :         tmp_ary[i] = ObjectIdGetDatum(param_types[i]);
     739                 : 
     740 GNC         279 :     result = construct_array_builtin(tmp_ary, num_params, REGTYPEOID);
     741 GIC         279 :     return PointerGetDatum(result);
     742 ECB             : }
        

Generated by: LCOV version v1.16-55-g56c0a2a