Header And Logo

PostgreSQL
| The world's most advanced open source database.

Functions | Variables

prepare.c File Reference

#include "postgres.h"
#include "access/xact.h"
#include "catalog/pg_type.h"
#include "commands/createas.h"
#include "commands/prepare.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "parser/analyze.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_type.h"
#include "rewrite/rewriteHandler.h"
#include "tcop/pquery.h"
#include "tcop/utility.h"
#include "utils/builtins.h"
#include "utils/snapmgr.h"
#include "utils/timestamp.h"
Include dependency graph for prepare.c:

Go to the source code of this file.

Functions

static void InitQueryHashTable (void)
static ParamListInfo EvaluateParams (PreparedStatement *pstmt, List *params, const char *queryString, EState *estate)
static Datum build_regtype_array (Oid *param_types, int num_params)
void PrepareQuery (PrepareStmt *stmt, const char *queryString)
void ExecuteQuery (ExecuteStmt *stmt, IntoClause *intoClause, const char *queryString, ParamListInfo params, DestReceiver *dest, char *completionTag)
void StorePreparedStatement (const char *stmt_name, CachedPlanSource *plansource, bool from_sql)
PreparedStatementFetchPreparedStatement (const char *stmt_name, bool throwError)
TupleDesc FetchPreparedStatementResultDesc (PreparedStatement *stmt)
ListFetchPreparedStatementTargetList (PreparedStatement *stmt)
void DeallocateQuery (DeallocateStmt *stmt)
void DropPreparedStatement (const char *stmt_name, bool showError)
void DropAllPreparedStatements (void)
void ExplainExecuteQuery (ExecuteStmt *execstmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params)
Datum pg_prepared_statement (PG_FUNCTION_ARGS)

Variables

static HTABprepared_queries = NULL

Function Documentation

static Datum build_regtype_array ( Oid param_types,
int  num_params 
) [static]

Definition at line 781 of file prepare.c.

References construct_array(), i, ObjectIdGetDatum, palloc(), PointerGetDatum, and REGTYPEOID.

Referenced by pg_prepared_statement().

{
    Datum      *tmp_ary;
    ArrayType  *result;
    int         i;

    tmp_ary = (Datum *) palloc(num_params * sizeof(Datum));

    for (i = 0; i < num_params; i++)
        tmp_ary[i] = ObjectIdGetDatum(param_types[i]);

    /* XXX: this hardcodes assumptions about the regtype type */
    result = construct_array(tmp_ary, num_params, REGTYPEOID, 4, true, 'i');
    return PointerGetDatum(result);
}

void DeallocateQuery ( DeallocateStmt stmt  ) 

Definition at line 556 of file prepare.c.

References DropAllPreparedStatements(), DropPreparedStatement(), and DeallocateStmt::name.

Referenced by standard_ProcessUtility().

{
    if (stmt->name)
        DropPreparedStatement(stmt->name, true);
    else
        DropAllPreparedStatements();
}

void DropAllPreparedStatements ( void   ) 

Definition at line 591 of file prepare.c.

References DropCachedPlan(), HASH_REMOVE, hash_search(), hash_seq_init(), hash_seq_search(), NULL, PreparedStatement::plansource, and PreparedStatement::stmt_name.

Referenced by DeallocateQuery(), and DiscardAll().

{
    HASH_SEQ_STATUS seq;
    PreparedStatement *entry;

    /* nothing cached */
    if (!prepared_queries)
        return;

    /* walk over cache */
    hash_seq_init(&seq, prepared_queries);
    while ((entry = hash_seq_search(&seq)) != NULL)
    {
        /* Release the plancache entry */
        DropCachedPlan(entry->plansource);

        /* Now we can remove the hash table entry */
        hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
    }
}

void DropPreparedStatement ( const char *  stmt_name,
bool  showError 
)

Definition at line 570 of file prepare.c.

References DropCachedPlan(), FetchPreparedStatement(), HASH_REMOVE, hash_search(), NULL, PreparedStatement::plansource, and PreparedStatement::stmt_name.

Referenced by DeallocateQuery(), and PostgresMain().

{
    PreparedStatement *entry;

    /* Find the query's hash table entry; raise error if wanted */
    entry = FetchPreparedStatement(stmt_name, showError);

    if (entry)
    {
        /* Release the plancache entry */
        DropCachedPlan(entry->plansource);

        /* Now we can remove the hash table entry */
        hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
    }
}

static ParamListInfo EvaluateParams ( PreparedStatement pstmt,
List params,
const char *  queryString,
EState estate 
) [static]

Definition at line 317 of file prepare.c.

References assign_expr_collations(), COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, copyObject(), ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, ExecEvalExprSwitchContext(), ExecPrepareExpr(), EXPR_KIND_EXECUTE_PARAMETER, exprType(), format_type_be(), GetPerTupleExprContext, i, ParamExternData::isnull, lfirst, list_length(), make_parsestate(), NULL, CachedPlanSource::num_params, ParamListInfoData::numParams, ParseState::p_sourcetext, palloc(), CachedPlanSource::param_types, ParamListInfoData::paramFetch, ParamListInfoData::paramFetchArg, ParamListInfoData::params, ParamListInfoData::parserSetup, ParamListInfoData::parserSetupArg, ParamExternData::pflags, PreparedStatement::plansource, ParamExternData::ptype, PreparedStatement::stmt_name, transformExpr(), and ParamExternData::value.

Referenced by ExecuteQuery(), and ExplainExecuteQuery().

{
    Oid        *param_types = pstmt->plansource->param_types;
    int         num_params = pstmt->plansource->num_params;
    int         nparams = list_length(params);
    ParseState *pstate;
    ParamListInfo paramLI;
    List       *exprstates;
    ListCell   *l;
    int         i;

    if (nparams != num_params)
        ereport(ERROR,
                (errcode(ERRCODE_SYNTAX_ERROR),
           errmsg("wrong number of parameters for prepared statement \"%s\"",
                  pstmt->stmt_name),
                 errdetail("Expected %d parameters but got %d.",
                           num_params, nparams)));

    /* Quick exit if no parameters */
    if (num_params == 0)
        return NULL;

    /*
     * We have to run parse analysis for the expressions.  Since the parser is
     * not cool about scribbling on its input, copy first.
     */
    params = (List *) copyObject(params);

    pstate = make_parsestate(NULL);
    pstate->p_sourcetext = queryString;

    i = 0;
    foreach(l, params)
    {
        Node       *expr = lfirst(l);
        Oid         expected_type_id = param_types[i];
        Oid         given_type_id;

        expr = transformExpr(pstate, expr, EXPR_KIND_EXECUTE_PARAMETER);

        given_type_id = exprType(expr);

        expr = coerce_to_target_type(pstate, expr, given_type_id,
                                     expected_type_id, -1,
                                     COERCION_ASSIGNMENT,
                                     COERCE_IMPLICIT_CAST,
                                     -1);

        if (expr == NULL)
            ereport(ERROR,
                    (errcode(ERRCODE_DATATYPE_MISMATCH),
                     errmsg("parameter $%d of type %s cannot be coerced to the expected type %s",
                            i + 1,
                            format_type_be(given_type_id),
                            format_type_be(expected_type_id)),
               errhint("You will need to rewrite or cast the expression.")));

        /* Take care of collations in the finished expression. */
        assign_expr_collations(pstate, expr);

        lfirst(l) = expr;
        i++;
    }

    /* Prepare the expressions for execution */
    exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);

    /* sizeof(ParamListInfoData) includes the first array element */
    paramLI = (ParamListInfo)
        palloc(sizeof(ParamListInfoData) +
               (num_params - 1) * sizeof(ParamExternData));
    /* we have static list of params, so no hooks needed */
    paramLI->paramFetch = NULL;
    paramLI->paramFetchArg = NULL;
    paramLI->parserSetup = NULL;
    paramLI->parserSetupArg = NULL;
    paramLI->numParams = num_params;

    i = 0;
    foreach(l, exprstates)
    {
        ExprState  *n = lfirst(l);
        ParamExternData *prm = &paramLI->params[i];

        prm->ptype = param_types[i];
        prm->pflags = PARAM_FLAG_CONST;
        prm->value = ExecEvalExprSwitchContext(n,
                                               GetPerTupleExprContext(estate),
                                               &prm->isnull,
                                               NULL);

        i++;
    }

    return paramLI;
}

void ExecuteQuery ( ExecuteStmt stmt,
IntoClause intoClause,
const char *  queryString,
ParamListInfo  params,
DestReceiver dest,
char *  completionTag 
)

Definition at line 188 of file prepare.c.

References CMD_SELECT, CachedPlanSource::commandTag, PlannedStmt::commandType, CreateExecutorState(), CreateNewPortal(), elog, ereport, errcode(), errmsg(), ERROR, EState::es_param_list_info, EvaluateParams(), FetchPreparedStatement(), CachedPlanSource::fixed_result, FreeExecutorState(), GetActiveSnapshot(), GetCachedPlan(), GetIntoRelEFlags(), IsA, linitial, list_length(), MemoryContextStrdup(), ExecuteStmt::name, NULL, CachedPlanSource::num_params, ExecuteStmt::params, PreparedStatement::plansource, PortalDefineQuery(), PortalDrop(), PortalGetHeapMemory, PortalRun(), PortalStart(), CachedPlanSource::query_string, IntoClause::skipData, CachedPlan::stmt_list, PlannedStmt::utilityStmt, and PortalData::visible.

Referenced by ExecCreateTableAs(), and standard_ProcessUtility().

{
    PreparedStatement *entry;
    CachedPlan *cplan;
    List       *plan_list;
    ParamListInfo paramLI = NULL;
    EState     *estate = NULL;
    Portal      portal;
    char       *query_string;
    int         eflags;
    long        count;

    /* Look it up in the hash table */
    entry = FetchPreparedStatement(stmt->name, true);

    /* Shouldn't find a non-fixed-result cached plan */
    if (!entry->plansource->fixed_result)
        elog(ERROR, "EXECUTE does not support variable-result cached plans");

    /* Evaluate parameters, if any */
    if (entry->plansource->num_params > 0)
    {
        /*
         * Need an EState to evaluate parameters; must not delete it till end
         * of query, in case parameters are pass-by-reference.  Note that the
         * passed-in "params" could possibly be referenced in the parameter
         * expressions.
         */
        estate = CreateExecutorState();
        estate->es_param_list_info = params;
        paramLI = EvaluateParams(entry, stmt->params,
                                 queryString, estate);
    }

    /* Create a new portal to run the query in */
    portal = CreateNewPortal();
    /* Don't display the portal in pg_cursors, it is for internal use only */
    portal->visible = false;

    /* Copy the plan's saved query string into the portal's memory */
    query_string = MemoryContextStrdup(PortalGetHeapMemory(portal),
                                       entry->plansource->query_string);

    /* Replan if needed, and increment plan refcount for portal */
    cplan = GetCachedPlan(entry->plansource, paramLI, false);
    plan_list = cplan->stmt_list;

    /*
     * For CREATE TABLE ... AS EXECUTE, we must verify that the prepared
     * statement is one that produces tuples.  Currently we insist that it be
     * a plain old SELECT.  In future we might consider supporting other
     * things such as INSERT ... RETURNING, but there are a couple of issues
     * to be settled first, notably how WITH NO DATA should be handled in such
     * a case (do we really want to suppress execution?) and how to pass down
     * the OID-determining eflags (PortalStart won't handle them in such a
     * case, and for that matter it's not clear the executor will either).
     *
     * For CREATE TABLE ... AS EXECUTE, we also have to ensure that the proper
     * eflags and fetch count are passed to PortalStart/PortalRun.
     */
    if (intoClause)
    {
        PlannedStmt *pstmt;

        if (list_length(plan_list) != 1)
            ereport(ERROR,
                    (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                     errmsg("prepared statement is not a SELECT")));
        pstmt = (PlannedStmt *) linitial(plan_list);
        if (!IsA(pstmt, PlannedStmt) ||
            pstmt->commandType != CMD_SELECT ||
            pstmt->utilityStmt != NULL)
            ereport(ERROR,
                    (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                     errmsg("prepared statement is not a SELECT")));

        /* Set appropriate eflags */
        eflags = GetIntoRelEFlags(intoClause);

        /* And tell PortalRun whether to run to completion or not */
        if (intoClause->skipData)
            count = 0;
        else
            count = FETCH_ALL;
    }
    else
    {
        /* Plain old EXECUTE */
        eflags = 0;
        count = FETCH_ALL;
    }

    PortalDefineQuery(portal,
                      NULL,
                      query_string,
                      entry->plansource->commandTag,
                      plan_list,
                      cplan);

    /*
     * Run the portal as appropriate.
     */
    PortalStart(portal, paramLI, eflags, GetActiveSnapshot());

    (void) PortalRun(portal, count, false, dest, dest, completionTag);

    PortalDrop(portal, false);

    if (estate)
        FreeExecutorState(estate);

    /* No need to pfree other memory, MemoryContext will be reset */
}

void ExplainExecuteQuery ( ExecuteStmt execstmt,
IntoClause into,
ExplainState es,
const char *  queryString,
ParamListInfo  params 
)

Definition at line 622 of file prepare.c.

References CreateExecutorState(), elog, ERROR, EState::es_param_list_info, EvaluateParams(), ExplainOnePlan(), ExplainOneUtility(), ExplainSeparatePlans(), FetchPreparedStatement(), CachedPlanSource::fixed_result, FreeExecutorState(), GetCachedPlan(), IsA, lfirst, lnext, ExecuteStmt::name, NULL, CachedPlanSource::num_params, ExecuteStmt::params, PreparedStatement::plansource, CachedPlanSource::query_string, ReleaseCachedPlan(), and CachedPlan::stmt_list.

Referenced by ExplainOneUtility().

{
    PreparedStatement *entry;
    const char *query_string;
    CachedPlan *cplan;
    List       *plan_list;
    ListCell   *p;
    ParamListInfo paramLI = NULL;
    EState     *estate = NULL;

    /* Look it up in the hash table */
    entry = FetchPreparedStatement(execstmt->name, true);

    /* Shouldn't find a non-fixed-result cached plan */
    if (!entry->plansource->fixed_result)
        elog(ERROR, "EXPLAIN EXECUTE does not support variable-result cached plans");

    query_string = entry->plansource->query_string;

    /* Evaluate parameters, if any */
    if (entry->plansource->num_params)
    {
        /*
         * Need an EState to evaluate parameters; must not delete it till end
         * of query, in case parameters are pass-by-reference.  Note that the
         * passed-in "params" could possibly be referenced in the parameter
         * expressions.
         */
        estate = CreateExecutorState();
        estate->es_param_list_info = params;
        paramLI = EvaluateParams(entry, execstmt->params,
                                 queryString, estate);
    }

    /* Replan if needed, and acquire a transient refcount */
    cplan = GetCachedPlan(entry->plansource, paramLI, true);

    plan_list = cplan->stmt_list;

    /* Explain each query */
    foreach(p, plan_list)
    {
        PlannedStmt *pstmt = (PlannedStmt *) lfirst(p);

        if (IsA(pstmt, PlannedStmt))
            ExplainOnePlan(pstmt, into, es, query_string, paramLI);
        else
            ExplainOneUtility((Node *) pstmt, into, es, query_string, paramLI);

        /* No need for CommandCounterIncrement, as ExplainOnePlan did it */

        /* Separate plans with an appropriate separator */
        if (lnext(p) != NULL)
            ExplainSeparatePlans(es);
    }

    if (estate)
        FreeExecutorState(estate);

    ReleaseCachedPlan(cplan, true);
}

PreparedStatement* FetchPreparedStatement ( const char *  stmt_name,
bool  throwError 
)

Definition at line 485 of file prepare.c.

References ereport, errcode(), errmsg(), ERROR, HASH_FIND, hash_search(), and NULL.

Referenced by DropPreparedStatement(), errdetail_execute(), exec_bind_message(), exec_describe_statement_message(), ExecuteQuery(), ExplainExecuteQuery(), FetchStatementTargetList(), GetCommandLogLevel(), UtilityReturnsTuples(), and UtilityTupleDescriptor().

{
    PreparedStatement *entry;

    /*
     * If the hash table hasn't been initialized, it can't be storing
     * anything, therefore it couldn't possibly store our plan.
     */
    if (prepared_queries)
        entry = (PreparedStatement *) hash_search(prepared_queries,
                                                  stmt_name,
                                                  HASH_FIND,
                                                  NULL);
    else
        entry = NULL;

    if (!entry && throwError)
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_PSTATEMENT),
                 errmsg("prepared statement \"%s\" does not exist",
                        stmt_name)));

    return entry;
}

TupleDesc FetchPreparedStatementResultDesc ( PreparedStatement stmt  ) 

Definition at line 517 of file prepare.c.

References Assert, CreateTupleDescCopy(), CachedPlanSource::fixed_result, PreparedStatement::plansource, and CachedPlanSource::resultDesc.

Referenced by UtilityTupleDescriptor().

{
    /*
     * Since we don't allow prepared statements' result tupdescs to change,
     * there's no need to worry about revalidating the cached plan here.
     */
    Assert(stmt->plansource->fixed_result);
    if (stmt->plansource->resultDesc)
        return CreateTupleDescCopy(stmt->plansource->resultDesc);
    else
        return NULL;
}

List* FetchPreparedStatementTargetList ( PreparedStatement stmt  ) 

Definition at line 540 of file prepare.c.

References CachedPlanGetTargetList(), copyObject(), and PreparedStatement::plansource.

Referenced by FetchStatementTargetList().

{
    List       *tlist;

    /* Get the plan's primary targetlist */
    tlist = CachedPlanGetTargetList(stmt->plansource);

    /* Copy into caller's context in case plan gets invalidated */
    return (List *) copyObject(tlist);
}

static void InitQueryHashTable ( void   )  [static]

Definition at line 421 of file prepare.c.

References HASHCTL::entrysize, hash_create(), HASH_ELEM, HASHCTL::keysize, and MemSet.

Referenced by StorePreparedStatement().

{
    HASHCTL     hash_ctl;

    MemSet(&hash_ctl, 0, sizeof(hash_ctl));

    hash_ctl.keysize = NAMEDATALEN;
    hash_ctl.entrysize = sizeof(PreparedStatement);

    prepared_queries = hash_create("Prepared Queries",
                                   32,
                                   &hash_ctl,
                                   HASH_ELEM);
}

Datum pg_prepared_statement ( PG_FUNCTION_ARGS   ) 

Definition at line 690 of file prepare.c.

References ReturnSetInfo::allowedModes, BoolGetDatum, BOOLOID, build_regtype_array(), CreateTemplateTupleDesc(), CStringGetTextDatum, ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, ereport, errcode(), errmsg(), ERROR, PreparedStatement::from_sql, hash_seq_init(), hash_seq_search(), IsA, MemoryContextSwitchTo(), MemSet, NULL, CachedPlanSource::num_params, CachedPlanSource::param_types, PreparedStatement::plansource, PreparedStatement::prepare_time, CachedPlanSource::query_string, REGTYPEARRAYOID, ReturnSetInfo::returnMode, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SFRM_Materialize_Random, PreparedStatement::stmt_name, TEXTOID, TimestampTzGetDatum, TIMESTAMPTZOID, TupleDescInitEntry(), tuplestore_begin_heap(), tuplestore_donestoring, tuplestore_putvalues(), values, and work_mem.

{
    ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    TupleDesc   tupdesc;
    Tuplestorestate *tupstore;
    MemoryContext per_query_ctx;
    MemoryContext oldcontext;

    /* check to see if caller supports us returning a tuplestore */
    if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("set-valued function called in context that cannot accept a set")));
    if (!(rsinfo->allowedModes & SFRM_Materialize))
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("materialize mode required, but it is not " \
                        "allowed in this context")));

    /* need to build tuplestore in query context */
    per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
    oldcontext = MemoryContextSwitchTo(per_query_ctx);

    /*
     * build tupdesc for result tuples. This must match the definition of the
     * pg_prepared_statements view in system_views.sql
     */
    tupdesc = CreateTemplateTupleDesc(5, false);
    TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
                       TEXTOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
                       TEXTOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepare_time",
                       TIMESTAMPTZOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber) 4, "parameter_types",
                       REGTYPEARRAYOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber) 5, "from_sql",
                       BOOLOID, -1, 0);

    /*
     * We put all the tuples into a tuplestore in one scan of the hashtable.
     * This avoids any issue of the hashtable possibly changing between calls.
     */
    tupstore =
        tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
                              false, work_mem);

    /* generate junk in short-term context */
    MemoryContextSwitchTo(oldcontext);

    /* hash table might be uninitialized */
    if (prepared_queries)
    {
        HASH_SEQ_STATUS hash_seq;
        PreparedStatement *prep_stmt;

        hash_seq_init(&hash_seq, prepared_queries);
        while ((prep_stmt = hash_seq_search(&hash_seq)) != NULL)
        {
            Datum       values[5];
            bool        nulls[5];

            MemSet(nulls, 0, sizeof(nulls));

            values[0] = CStringGetTextDatum(prep_stmt->stmt_name);
            values[1] = CStringGetTextDatum(prep_stmt->plansource->query_string);
            values[2] = TimestampTzGetDatum(prep_stmt->prepare_time);
            values[3] = build_regtype_array(prep_stmt->plansource->param_types,
                                          prep_stmt->plansource->num_params);
            values[4] = BoolGetDatum(prep_stmt->from_sql);

            tuplestore_putvalues(tupstore, tupdesc, values, nulls);
        }
    }

    /* clean up and return the tuplestore */
    tuplestore_donestoring(tupstore);

    rsinfo->returnMode = SFRM_Materialize;
    rsinfo->setResult = tupstore;
    rsinfo->setDesc = tupdesc;

    return (Datum) 0;
}

void PrepareQuery ( PrepareStmt stmt,
const char *  queryString 
)

Definition at line 55 of file prepare.c.

References PrepareStmt::argtypes, CMD_DELETE, CMD_INSERT, CMD_SELECT, CMD_UPDATE, Query::commandType, CompleteCachedPlan(), copyObject(), CreateCachedPlan(), CreateCommandTag(), ereport, errcode(), errmsg(), ERROR, i, InvalidOid, lfirst, list_length(), make_parsestate(), PrepareStmt::name, NULL, ParseState::p_sourcetext, palloc(), parse_analyze_varparams(), PrepareStmt::query, QueryRewrite(), StorePreparedStatement(), typenameTypeId(), and UNKNOWNOID.

Referenced by standard_ProcessUtility().

{
    CachedPlanSource *plansource;
    Oid        *argtypes = NULL;
    int         nargs;
    Query      *query;
    List       *query_list;
    int         i;

    /*
     * Disallow empty-string statement name (conflicts with protocol-level
     * unnamed statement).
     */
    if (!stmt->name || stmt->name[0] == '\0')
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
                 errmsg("invalid statement name: must not be empty")));

    /*
     * Create the CachedPlanSource before we do parse analysis, since it needs
     * to see the unmodified raw parse tree.
     */
    plansource = CreateCachedPlan(stmt->query, queryString,
                                  CreateCommandTag(stmt->query));

    /* Transform list of TypeNames to array of type OIDs */
    nargs = list_length(stmt->argtypes);

    if (nargs)
    {
        ParseState *pstate;
        ListCell   *l;

        /*
         * typenameTypeId wants a ParseState to carry the source query string.
         * Is it worth refactoring its API to avoid this?
         */
        pstate = make_parsestate(NULL);
        pstate->p_sourcetext = queryString;

        argtypes = (Oid *) palloc(nargs * sizeof(Oid));
        i = 0;

        foreach(l, stmt->argtypes)
        {
            TypeName   *tn = lfirst(l);
            Oid         toid = typenameTypeId(pstate, tn);

            argtypes[i++] = toid;
        }
    }

    /*
     * Analyze the statement using these parameter types (any parameters
     * passed in from above us will not be visible to it), allowing
     * information about unknown parameters to be deduced from context.
     *
     * Because parse analysis scribbles on the raw querytree, we must make a
     * copy to ensure we don't modify the passed-in tree.  FIXME someday.
     */
    query = parse_analyze_varparams((Node *) copyObject(stmt->query),
                                    queryString,
                                    &argtypes, &nargs);

    /*
     * Check that all parameter types were determined.
     */
    for (i = 0; i < nargs; i++)
    {
        Oid         argtype = argtypes[i];

        if (argtype == InvalidOid || argtype == UNKNOWNOID)
            ereport(ERROR,
                    (errcode(ERRCODE_INDETERMINATE_DATATYPE),
                     errmsg("could not determine data type of parameter $%d",
                            i + 1)));
    }

    /*
     * grammar only allows OptimizableStmt, so this check should be redundant
     */
    switch (query->commandType)
    {
        case CMD_SELECT:
        case CMD_INSERT:
        case CMD_UPDATE:
        case CMD_DELETE:
            /* OK */
            break;
        default:
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
                     errmsg("utility statements cannot be prepared")));
            break;
    }

    /* Rewrite the query. The result could be 0, 1, or many queries. */
    query_list = QueryRewrite(query);

    /* Finish filling in the CachedPlanSource */
    CompleteCachedPlan(plansource,
                       query_list,
                       NULL,
                       argtypes,
                       nargs,
                       NULL,
                       NULL,
                       0,       /* default cursor options */
                       true);   /* fixed result */

    /*
     * Save the results.
     */
    StorePreparedStatement(stmt->name,
                           plansource,
                           true);
}

void StorePreparedStatement ( const char *  stmt_name,
CachedPlanSource plansource,
bool  from_sql 
)

Definition at line 443 of file prepare.c.

References ereport, errcode(), errmsg(), ERROR, PreparedStatement::from_sql, GetCurrentStatementStartTimestamp(), HASH_ENTER, hash_search(), InitQueryHashTable(), PreparedStatement::plansource, PreparedStatement::prepare_time, and SaveCachedPlan().

Referenced by exec_parse_message(), and PrepareQuery().

{
    PreparedStatement *entry;
    TimestampTz cur_ts = GetCurrentStatementStartTimestamp();
    bool        found;

    /* Initialize the hash table, if necessary */
    if (!prepared_queries)
        InitQueryHashTable();

    /* Add entry to hash table */
    entry = (PreparedStatement *) hash_search(prepared_queries,
                                              stmt_name,
                                              HASH_ENTER,
                                              &found);

    /* Shouldn't get a duplicate entry */
    if (found)
        ereport(ERROR,
                (errcode(ERRCODE_DUPLICATE_PSTATEMENT),
                 errmsg("prepared statement \"%s\" already exists",
                        stmt_name)));

    /* Fill in the hash table entry */
    entry->plansource = plansource;
    entry->from_sql = from_sql;
    entry->prepare_time = cur_ts;

    /* Now it's safe to move the CachedPlanSource to permanent memory */
    SaveCachedPlan(plansource);
}


Variable Documentation

HTAB* prepared_queries = NULL [static]

Definition at line 44 of file prepare.c.