LCOV - code coverage report
Current view: top level - src/backend/commands - prepare.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 204 222 91.9 %
Date: 2020-06-01 10:07:15 Functions: 14 14 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13