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

Generated by: LCOV version 1.11