提交 9bedd128 编写于 作者: T Tom Lane

Add support for invoking parser callback hooks via SPI and in cached plans.

As proof of concept, modify plpgsql to use the hooks.  plpgsql is still
inserting $n symbols textually, but the "back end" of the parsing process now
goes through the ParamRef hook instead of using a fixed parameter-type array,
and then execution only fetches actually-referenced parameters, using a hook
added to ParamListInfo.

Although there's a lot left to be done in plpgsql, this already cures the
"if (TG_OP = 'INSERT' and NEW.foo ...)"  problem, as illustrated by the
changed regression test.
上级 48912acc
<!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.65 2009/08/05 19:31:50 alvherre Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.66 2009/11/04 22:26:04 tgl Exp $ -->
<chapter id="spi">
<title>Server Programming Interface</title>
......@@ -861,7 +861,7 @@ SPIPlanPtr SPI_prepare(const char * <parameter>command</parameter>, int <paramet
<para>
<function>SPI_prepare</function> creates and returns an execution
plan for the specified command but doesn't execute the command.
plan for the specified command, but doesn't execute the command.
This function should only be called from a connected procedure.
</para>
......@@ -990,7 +990,7 @@ SPIPlanPtr SPI_prepare_cursor(const char * <parameter>command</parameter>, int <
of the planner's <quote>cursor options</> parameter. This is a bitmask
having the values shown in <filename>nodes/parsenodes.h</filename>
for the <structfield>options</> field of <structname>DeclareCursorStmt</>.
<function>SPI_prepare</function> always takes these options as zero.
<function>SPI_prepare</function> always takes the cursor options as zero.
</para>
</refsect1>
......@@ -1061,6 +1061,94 @@ SPIPlanPtr SPI_prepare_cursor(const char * <parameter>command</parameter>, int <
<!-- *********************************************** -->
<refentry id="spi-spi-prepare-params">
<refmeta>
<refentrytitle>SPI_prepare_params</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>SPI_prepare_params</refname>
<refpurpose>prepare a plan for a command, without executing it yet</refpurpose>
</refnamediv>
<indexterm><primary>SPI_prepare_params</primary></indexterm>
<refsynopsisdiv>
<synopsis>
SPIPlanPtr SPI_prepare_params(const char * <parameter>command</parameter>,
ParserSetupHook <parameter>parserSetup</parameter>,
void * <parameter>parserSetupArg</parameter>,
int <parameter>cursorOptions</parameter>)
</synopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<function>SPI_prepare_params</function> creates and returns an execution
plan for the specified command, but doesn't execute the command.
This function is equivalent to <function>SPI_prepare_cursor</function>,
with the addition that the caller can specify parser hook functions
to control the parsing of external parameter references.
</para>
</refsect1>
<refsect1>
<title>Arguments</title>
<variablelist>
<varlistentry>
<term><literal>const char * <parameter>command</parameter></literal></term>
<listitem>
<para>
command string
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>ParserSetupHook <parameter>parserSetup</parameter></literal></term>
<listitem>
<para>
Parser hook setup function
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>void * <parameter>parserSetupArg</parameter></literal></term>
<listitem>
<para>
passthrough argument for <parameter>parserSetup</parameter>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>int <parameter>cursorOptions</parameter></literal></term>
<listitem>
<para>
integer bitmask of cursor options; zero produces default behavior
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Return Value</title>
<para>
<function>SPI_prepare_params</function> has the same return conventions as
<function>SPI_prepare</function>.
</para>
</refsect1>
</refentry>
<!-- *********************************************** -->
<refentry id="spi-spi-getargcount">
<refmeta>
<refentrytitle>SPI_getargcount</refentrytitle>
......@@ -1386,14 +1474,100 @@ int SPI_execute_plan(SPIPlanPtr <parameter>plan</parameter>, Datum * <parameter>
<function>SPI_execute</function> if successful.
</para>
</refsect1>
</refentry>
<!-- *********************************************** -->
<refentry id="spi-spi-execute-plan-with-paramlist">
<refmeta>
<refentrytitle>SPI_execute_plan_with_paramlist</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>SPI_execute_plan_with_paramlist</refname>
<refpurpose>execute a plan prepared by <function>SPI_prepare</function></refpurpose>
</refnamediv>
<indexterm><primary>SPI_execute_plan_with_paramlist</primary></indexterm>
<refsynopsisdiv>
<synopsis>
int SPI_execute_plan_with_paramlist(SPIPlanPtr <parameter>plan</parameter>,
ParamListInfo <parameter>params</parameter>,
bool <parameter>read_only</parameter>,
long <parameter>count</parameter>)
</synopsis>
</refsynopsisdiv>
<refsect1>
<title>Notes</title>
<title>Description</title>
<para>
If one of the objects (a table, function, etc.) referenced by the
prepared plan is dropped during the session then the result of
<function>SPI_execute_plan</function> for this plan will be unpredictable.
<function>SPI_execute_plan_with_paramlist</function> executes a plan
prepared by <function>SPI_prepare</function>.
This function is equivalent to <function>SPI_execute_plan</function>
except that information about the parameter values to be passed to the
query is presented differently. The <literal>ParamListInfo</>
representation can be convenient for passing down values that are
already available in that format. It also supports use of dynamic
parameter sets via hook functions specified in <literal>ParamListInfo</>.
</para>
</refsect1>
<refsect1>
<title>Arguments</title>
<variablelist>
<varlistentry>
<term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
<listitem>
<para>
execution plan (returned by <function>SPI_prepare</function>)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>ParamListInfo <parameter>params</parameter></literal></term>
<listitem>
<para>
data structure containing parameter types and values; NULL if none
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>bool <parameter>read_only</parameter></literal></term>
<listitem>
<para>
<literal>true</> for read-only execution
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>long <parameter>count</parameter></literal></term>
<listitem>
<para>
maximum number of rows to process or return
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Return Value</title>
<para>
The return value is the same as for <function>SPI_execute_plan</function>.
</para>
<para>
<varname>SPI_processed</varname> and
<varname>SPI_tuptable</varname> are set as in
<function>SPI_execute_plan</function> if successful.
</para>
</refsect1>
</refentry>
......@@ -1543,7 +1717,7 @@ Portal SPI_cursor_open(const char * <parameter>name</parameter>, SPIPlanPtr <par
</para>
<para>
The passed-in data will be copied into the cursor's portal, so it
The passed-in parameter data will be copied into the cursor's portal, so it
can be freed while the cursor still exists.
</para>
</refsect1>
......@@ -1667,7 +1841,7 @@ Portal SPI_cursor_open_with_args(const char *<parameter>name</parameter>,
</para>
<para>
The passed-in data will be copied into the cursor's portal, so it
The passed-in parameter data will be copied into the cursor's portal, so it
can be freed while the cursor still exists.
</para>
</refsect1>
......@@ -1770,6 +1944,104 @@ Portal SPI_cursor_open_with_args(const char *<parameter>name</parameter>,
<!-- *********************************************** -->
<refentry id="spi-spi-cursor-open-with-paramlist">
<refmeta>
<refentrytitle>SPI_cursor_open_with_paramlist</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>SPI_cursor_open_with_paramlist</refname>
<refpurpose>set up a cursor using parameters</refpurpose>
</refnamediv>
<indexterm><primary>SPI_cursor_open_with_paramlist</primary></indexterm>
<refsynopsisdiv>
<synopsis>
Portal SPI_cursor_open_with_paramlist(const char *<parameter>name</parameter>,
SPIPlanPtr <parameter>plan</parameter>,
ParamListInfo <parameter>params</parameter>,
bool <parameter>read_only</parameter>)
</synopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<function>SPI_cursor_open_with_paramlist</function> sets up a cursor
(internally, a portal) that will execute a plan prepared by
<function>SPI_prepare</function>.
This function is equivalent to <function>SPI_cursor_open</function>
except that information about the parameter values to be passed to the
query is presented differently. The <literal>ParamListInfo</>
representation can be convenient for passing down values that are
already available in that format. It also supports use of dynamic
parameter sets via hook functions specified in <literal>ParamListInfo</>.
</para>
<para>
The passed-in parameter data will be copied into the cursor's portal, so it
can be freed while the cursor still exists.
</para>
</refsect1>
<refsect1>
<title>Arguments</title>
<variablelist>
<varlistentry>
<term><literal>const char * <parameter>name</parameter></literal></term>
<listitem>
<para>
name for portal, or <symbol>NULL</symbol> to let the system
select a name
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
<listitem>
<para>
execution plan (returned by <function>SPI_prepare</function>)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>ParamListInfo <parameter>params</parameter></literal></term>
<listitem>
<para>
data structure containing parameter types and values; NULL if none
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>bool <parameter>read_only</parameter></literal></term>
<listitem>
<para>
<literal>true</> for read-only execution
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Return Value</title>
<para>
Pointer to portal containing the cursor. Note there is no error
return convention; any error will be reported via <function>elog</>.
</para>
</refsect1>
</refentry>
<!-- *********************************************** -->
<refentry id="spi-spi-cursor-find">
<refmeta>
<refentrytitle>SPI_cursor_find</refentrytitle>
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.192 2009/10/12 18:10:41 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.193 2009/11/04 22:26:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -107,8 +107,6 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
ParamListInfo params, DestReceiver *dest)
{
ExplainState es;
Oid *param_types;
int num_params;
TupOutputState *tstate;
List *rewritten;
ListCell *lc;
......@@ -150,9 +148,6 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
opt->defname)));
}
/* Convert parameter type data to the form parser wants */
getParamListTypes(params, &param_types, &num_params);
/*
* Run parse analysis and rewrite. Note this also acquires sufficient
* locks on the source table(s).
......@@ -163,8 +158,10 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
* executed repeatedly. (See also the same hack in DECLARE CURSOR and
* PREPARE.) XXX FIXME someday.
*/
rewritten = pg_analyze_and_rewrite((Node *) copyObject(stmt->query),
queryString, param_types, num_params);
rewritten = pg_analyze_and_rewrite_params((Node *) copyObject(stmt->query),
queryString,
(ParserSetupHook) setupParserWithParamList,
params);
/* emit opening boilerplate */
ExplainBeginOutput(&es);
......
......@@ -10,7 +10,7 @@
* Copyright (c) 2002-2009, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.99 2009/08/10 05:46:50 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.100 2009/11/04 22:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -379,6 +379,11 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
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;
......
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/executor/execCurrent.c,v 1.12 2009/10/26 02:26:29 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execCurrent.c,v 1.13 2009/11/04 22:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -217,9 +217,21 @@ fetch_param_value(ExprContext *econtext, int paramId)
{
ParamExternData *prm = &paramInfo->params[paramId - 1];
/* give hook a chance in case parameter is dynamic */
if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
(*paramInfo->paramFetch) (paramInfo, paramId);
if (OidIsValid(prm->ptype) && !prm->isnull)
{
Assert(prm->ptype == REFCURSOROID);
/* safety check in case hook did something unexpected */
if (prm->ptype != REFCURSOROID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
paramId,
format_type_be(prm->ptype),
format_type_be(REFCURSOROID))));
/* We know that refcursor uses text's I/O routines */
return TextDatumGetCString(prm->value);
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.253 2009/10/26 02:26:29 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.254 2009/11/04 22:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -882,9 +882,21 @@ ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
{
ParamExternData *prm = &paramInfo->params[thisParamId - 1];
/* give hook a chance in case parameter is dynamic */
if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
(*paramInfo->paramFetch) (paramInfo, thisParamId);
if (OidIsValid(prm->ptype))
{
Assert(prm->ptype == expression->paramtype);
/* safety check in case hook did something unexpected */
if (prm->ptype != expression->paramtype)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
thisParamId,
format_type_be(prm->ptype),
format_type_be(expression->paramtype))));
*isNull = prm->isnull;
return prm->value;
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.135 2009/06/11 17:25:38 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.136 2009/11/04 22:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -526,6 +526,11 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
/* sizeof(ParamListInfoData) includes the first array element */
paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
(nargs - 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 = nargs;
fcache->paramLI = paramLI;
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.210 2009/10/10 01:43:47 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.211 2009/11/04 22:26:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -45,8 +45,7 @@ static int _SPI_connected = -1;
static int _SPI_curid = -1;
static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
Datum *Values, const char *Nulls,
bool read_only, int pflags);
ParamListInfo paramLI, bool read_only);
static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan,
ParamListInfo boundParams);
......@@ -407,6 +406,28 @@ SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, long tcount)
return SPI_execute_plan(plan, Values, Nulls, false, tcount);
}
/* Execute a previously prepared plan */
int
SPI_execute_plan_with_paramlist(SPIPlanPtr plan, ParamListInfo params,
bool read_only, long tcount)
{
int res;
if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
return SPI_ERROR_ARGUMENT;
res = _SPI_begin_call(true);
if (res < 0)
return res;
res = _SPI_execute_plan(plan, params,
InvalidSnapshot, InvalidSnapshot,
read_only, true, tcount);
_SPI_end_call(true);
return res;
}
/*
* SPI_execute_snapshot -- identical to SPI_execute_plan, except that we allow
* the caller to specify exactly which snapshots to use, which will be
......@@ -483,6 +504,8 @@ SPI_execute_with_args(const char *src,
plan.cursor_options = 0;
plan.nargs = nargs;
plan.argtypes = argtypes;
plan.parserSetup = NULL;
plan.parserSetupArg = NULL;
paramLI = _SPI_convert_params(nargs, argtypes,
Values, Nulls,
......@@ -528,6 +551,45 @@ SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
plan.cursor_options = cursorOptions;
plan.nargs = nargs;
plan.argtypes = argtypes;
plan.parserSetup = NULL;
plan.parserSetupArg = NULL;
_SPI_prepare_plan(src, &plan, NULL);
/* copy plan to procedure context */
result = _SPI_copy_plan(&plan, _SPI_current->procCxt);
_SPI_end_call(true);
return result;
}
SPIPlanPtr
SPI_prepare_params(const char *src,
ParserSetupHook parserSetup,
void *parserSetupArg,
int cursorOptions)
{
_SPI_plan plan;
SPIPlanPtr result;
if (src == NULL)
{
SPI_result = SPI_ERROR_ARGUMENT;
return NULL;
}
SPI_result = _SPI_begin_call(true);
if (SPI_result < 0)
return NULL;
memset(&plan, 0, sizeof(_SPI_plan));
plan.magic = _SPI_PLAN_MAGIC;
plan.cursor_options = cursorOptions;
plan.nargs = 0;
plan.argtypes = NULL;
plan.parserSetup = parserSetup;
plan.parserSetupArg = parserSetupArg;
_SPI_prepare_plan(src, &plan, NULL);
......@@ -954,8 +1016,21 @@ SPI_cursor_open(const char *name, SPIPlanPtr plan,
Datum *Values, const char *Nulls,
bool read_only)
{
return SPI_cursor_open_internal(name, plan, Values, Nulls,
read_only, 0);
Portal portal;
ParamListInfo paramLI;
/* build transient ParamListInfo in caller's context */
paramLI = _SPI_convert_params(plan->nargs, plan->argtypes,
Values, Nulls,
0);
portal = SPI_cursor_open_internal(name, plan, paramLI, read_only);
/* done with the transient ParamListInfo */
if (paramLI)
pfree(paramLI);
return portal;
}
......@@ -992,7 +1067,10 @@ SPI_cursor_open_with_args(const char *name,
plan.cursor_options = cursorOptions;
plan.nargs = nargs;
plan.argtypes = argtypes;
plan.parserSetup = NULL;
plan.parserSetupArg = NULL;
/* build transient ParamListInfo in executor context */
paramLI = _SPI_convert_params(nargs, argtypes,
Values, Nulls,
PARAM_FLAG_CONST);
......@@ -1007,8 +1085,7 @@ SPI_cursor_open_with_args(const char *name,
/* SPI_cursor_open_internal must be called in procedure memory context */
_SPI_procmem();
result = SPI_cursor_open_internal(name, &plan, Values, Nulls,
read_only, PARAM_FLAG_CONST);
result = SPI_cursor_open_internal(name, &plan, paramLI, read_only);
/* And clean up */
_SPI_curid++;
......@@ -1018,25 +1095,36 @@ SPI_cursor_open_with_args(const char *name,
}
/*
* SPI_cursor_open_with_paramlist()
*
* Same as SPI_cursor_open except that parameters (if any) are passed
* as a ParamListInfo, which supports dynamic parameter set determination
*/
Portal
SPI_cursor_open_with_paramlist(const char *name, SPIPlanPtr plan,
ParamListInfo params, bool read_only)
{
return SPI_cursor_open_internal(name, plan, params, read_only);
}
/*
* SPI_cursor_open_internal()
*
* Common code for SPI_cursor_open and SPI_cursor_open_with_args
* Common code for SPI_cursor_open variants
*/
static Portal
SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
Datum *Values, const char *Nulls,
bool read_only, int pflags)
ParamListInfo paramLI, bool read_only)
{
CachedPlanSource *plansource;
CachedPlan *cplan;
List *stmt_list;
char *query_string;
ParamListInfo paramLI;
Snapshot snapshot;
MemoryContext oldcontext;
Portal portal;
int k;
/*
* Check that the plan is something the Portal code will special-case as
......@@ -1082,54 +1170,15 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
portal = CreatePortal(name, false, false);
}
/*
* Prepare to copy stuff into the portal's memory context. We do all this
* copying first, because it could possibly fail (out-of-memory) and we
* don't want a failure to occur between RevalidateCachedPlan and
* PortalDefineQuery; that would result in leaking our plancache refcount.
*/
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
/* Copy the plan's query string into the portal */
query_string = pstrdup(plansource->query_string);
/* If the plan has parameters, copy them into the portal */
if (plan->nargs > 0)
{
/* sizeof(ParamListInfoData) includes the first array element */
paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
(plan->nargs - 1) *sizeof(ParamExternData));
paramLI->numParams = plan->nargs;
for (k = 0; k < plan->nargs; k++)
{
ParamExternData *prm = &paramLI->params[k];
prm->ptype = plan->argtypes[k];
prm->pflags = pflags;
prm->isnull = (Nulls && Nulls[k] == 'n');
if (prm->isnull)
{
/* nulls just copy */
prm->value = Values[k];
}
else
{
/* pass-by-ref values must be copied into portal context */
int16 paramTypLen;
bool paramTypByVal;
get_typlenbyval(prm->ptype, &paramTypLen, &paramTypByVal);
prm->value = datumCopy(Values[k],
paramTypByVal, paramTypLen);
}
}
}
else
paramLI = NULL;
MemoryContextSwitchTo(oldcontext);
query_string = MemoryContextStrdup(PortalGetHeapMemory(portal),
plansource->query_string);
/*
* Note: we mustn't have any failure occur between RevalidateCachedPlan
* and PortalDefineQuery; that would result in leaking our plancache
* refcount.
*/
if (plan->saved)
{
/* Replan if needed, and increment plan refcount for portal */
......@@ -1220,6 +1269,19 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
snapshot = GetTransactionSnapshot();
}
/*
* If the plan has parameters, copy them into the portal. Note that
* this must be done after revalidating the plan, because in dynamic
* parameter cases the set of parameters could have changed during
* re-parsing.
*/
if (paramLI)
{
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
paramLI = copyParamList(paramLI);
MemoryContextSwitchTo(oldcontext);
}
/*
* Start portal execution.
*/
......@@ -1588,11 +1650,12 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
/*
* Parse and plan a querystring.
*
* At entry, plan->argtypes, plan->nargs, and plan->cursor_options must be
* valid. If boundParams isn't NULL then it represents parameter values
* that are made available to the planner (as either estimates or hard values
* depending on their PARAM_FLAG_CONST marking). The boundParams had better
* match the param types embedded in the plan!
* At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup
* and plan->parserSetupArg) must be valid, as must plan->cursor_options.
* If boundParams isn't NULL then it represents parameter values that are made
* available to the planner (as either estimates or hard values depending on
* their PARAM_FLAG_CONST marking). The boundParams had better match the
* param type information embedded in the plan!
*
* Results are stored into *plan (specifically, plan->plancache_list).
* Note however that the result trees are all in CurrentMemoryContext
......@@ -1605,8 +1668,6 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams)
List *plancache_list;
ListCell *list_item;
ErrorContextCallback spierrcontext;
Oid *argtypes = plan->argtypes;
int nargs = plan->nargs;
int cursor_options = plan->cursor_options;
/*
......@@ -1623,8 +1684,8 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams)
raw_parsetree_list = pg_parse_query(src);
/*
* Do parse analysis and rule rewrite for each raw parsetree, then cons up
* a phony plancache entry for each one.
* Do parse analysis, rule rewrite, and planning for each raw parsetree,
* then cons up a phony plancache entry for each one.
*/
plancache_list = NIL;
......@@ -1635,9 +1696,27 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams)
CachedPlanSource *plansource;
CachedPlan *cplan;
/* Need a copyObject here to keep parser from modifying raw tree */
stmt_list = pg_analyze_and_rewrite(copyObject(parsetree),
src, argtypes, nargs);
/*
* Parameter datatypes are driven by parserSetup hook if provided,
* otherwise we use the fixed parameter list.
*/
if (plan->parserSetup != NULL)
{
Assert(plan->nargs == 0);
/* Need a copyObject here to keep parser from modifying raw tree */
stmt_list = pg_analyze_and_rewrite_params(copyObject(parsetree),
src,
plan->parserSetup,
plan->parserSetupArg);
}
else
{
/* Need a copyObject here to keep parser from modifying raw tree */
stmt_list = pg_analyze_and_rewrite(copyObject(parsetree),
src,
plan->argtypes,
plan->nargs);
}
stmt_list = pg_plan_queries(stmt_list, cursor_options, boundParams);
plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
......@@ -1647,8 +1726,10 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams)
/* cast-away-const here is a bit ugly, but there's no reason to copy */
plansource->query_string = (char *) src;
plansource->commandTag = CreateCommandTag(parsetree);
plansource->param_types = argtypes;
plansource->num_params = nargs;
plansource->param_types = plan->argtypes;
plansource->num_params = plan->nargs;
plansource->parserSetup = plan->parserSetup;
plansource->parserSetupArg = plan->parserSetupArg;
plansource->fully_planned = true;
plansource->fixed_result = false;
/* no need to set search_path, generation or saved_xmin */
......@@ -1921,7 +2002,7 @@ fail:
}
/*
* Convert query parameters to form wanted by planner and executor
* Convert arrays of query parameters to form wanted by planner and executor
*/
static ParamListInfo
_SPI_convert_params(int nargs, Oid *argtypes,
......@@ -1937,6 +2018,11 @@ _SPI_convert_params(int nargs, Oid *argtypes,
/* sizeof(ParamListInfoData) includes the first array element */
paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
(nargs - 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 = nargs;
for (i = 0; i < nargs; i++)
......@@ -2222,6 +2308,8 @@ _SPI_copy_plan(SPIPlanPtr plan, MemoryContext parentcxt)
}
else
newplan->argtypes = NULL;
newplan->parserSetup = plan->parserSetup;
newplan->parserSetupArg = plan->parserSetupArg;
foreach(lc, plan->plancache_list)
{
......@@ -2241,6 +2329,8 @@ _SPI_copy_plan(SPIPlanPtr plan, MemoryContext parentcxt)
newsource->commandTag = plansource->commandTag;
newsource->param_types = newplan->argtypes;
newsource->num_params = newplan->nargs;
newsource->parserSetup = newplan->parserSetup;
newsource->parserSetupArg = newplan->parserSetupArg;
newsource->fully_planned = plansource->fully_planned;
newsource->fixed_result = plansource->fixed_result;
/* no need to worry about seach_path, generation or saved_xmin */
......@@ -2298,6 +2388,8 @@ _SPI_save_plan(SPIPlanPtr plan)
}
else
newplan->argtypes = NULL;
newplan->parserSetup = plan->parserSetup;
newplan->parserSetupArg = plan->parserSetupArg;
foreach(lc, plan->plancache_list)
{
......@@ -2317,6 +2409,10 @@ _SPI_save_plan(SPIPlanPtr plan)
cplan->stmt_list,
true,
false);
if (newplan->parserSetup != NULL)
CachedPlanSetParserHook(newsource,
newplan->parserSetup,
newplan->parserSetupArg);
newplan->plancache_list = lappend(newplan->plancache_list, newsource);
}
......
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/params.c,v 1.11 2009/01/01 17:23:43 momjian Exp $
* $PostgreSQL: pgsql/src/backend/nodes/params.c,v 1.12 2009/11/04 22:26:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -16,6 +16,7 @@
#include "postgres.h"
#include "nodes/params.h"
#include "parser/parse_param.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
......@@ -24,6 +25,11 @@
* Copy a ParamListInfo structure.
*
* The result is allocated in CurrentMemoryContext.
*
* Note: the intent of this function is to make a static, self-contained
* set of parameter values. If dynamic parameter hooks are present, we
* intentionally do not copy them into the result. Rather, we forcibly
* instantiate all available parameter values and copy the datum values.
*/
ParamListInfo
copyParamList(ParamListInfo from)
......@@ -40,54 +46,76 @@ copyParamList(ParamListInfo from)
(from->numParams - 1) *sizeof(ParamExternData);
retval = (ParamListInfo) palloc(size);
memcpy(retval, from, size);
retval->paramFetch = NULL;
retval->paramFetchArg = NULL;
retval->parserSetup = NULL;
retval->parserSetupArg = NULL;
retval->numParams = from->numParams;
/*
* Flat-copy is not good enough for pass-by-ref data values, so make a
* pass over the array to copy those.
*/
for (i = 0; i < retval->numParams; i++)
for (i = 0; i < from->numParams; i++)
{
ParamExternData *prm = &retval->params[i];
ParamExternData *oprm = &from->params[i];
ParamExternData *nprm = &retval->params[i];
int16 typLen;
bool typByVal;
if (prm->isnull || !OidIsValid(prm->ptype))
/* give hook a chance in case parameter is dynamic */
if (!OidIsValid(oprm->ptype) && from->paramFetch != NULL)
(*from->paramFetch) (from, i+1);
/* flat-copy the parameter info */
*nprm = *oprm;
/* need datumCopy in case it's a pass-by-reference datatype */
if (nprm->isnull || !OidIsValid(nprm->ptype))
continue;
get_typlenbyval(prm->ptype, &typLen, &typByVal);
prm->value = datumCopy(prm->value, typByVal, typLen);
get_typlenbyval(nprm->ptype, &typLen, &typByVal);
nprm->value = datumCopy(nprm->value, typByVal, typLen);
}
return retval;
}
/*
* Extract an array of parameter type OIDs from a ParamListInfo.
* Set up the parser to treat the given list of run-time parameters
* as available external parameters during parsing of a new query.
*
* The result is allocated in CurrentMemoryContext.
* Note that the parser doesn't actually care about the *values* of the given
* parameters, only about their *types*. Also, the code that originally
* provided the ParamListInfo may have provided a setupHook, which should
* override applying parse_fixed_parameters().
*/
void
getParamListTypes(ParamListInfo params,
Oid **param_types, int *num_params)
setupParserWithParamList(struct ParseState *pstate,
ParamListInfo params)
{
Oid *ptypes;
int i;
if (params == NULL) /* no params, nothing to do */
return;
if (params == NULL || params->numParams <= 0)
/* If there is a parserSetup hook, it gets to do this */
if (params->parserSetup != NULL)
{
*param_types = NULL;
*num_params = 0;
(*params->parserSetup) (pstate, params->parserSetupArg);
return;
}
ptypes = (Oid *) palloc(params->numParams * sizeof(Oid));
*param_types = ptypes;
*num_params = params->numParams;
for (i = 0; i < params->numParams; i++)
/* Else, treat any available parameters as being of fixed type */
if (params->numParams > 0)
{
ParamExternData *prm = &params->params[i];
Oid *ptypes;
int i;
ptypes = (Oid *) palloc(params->numParams * sizeof(Oid));
for (i = 0; i < params->numParams; i++)
{
ParamExternData *prm = &params->params[i];
/* give hook a chance in case parameter is dynamic */
if (!OidIsValid(prm->ptype) && params->paramFetch != NULL)
(*params->paramFetch) (params, i+1);
ptypes[i] = prm->ptype;
ptypes[i] = prm->ptype;
}
parse_fixed_parameters(pstate, ptypes, params->numParams);
}
}
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.574 2009/10/08 22:34:57 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.575 2009/11/04 22:26:06 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
......@@ -627,6 +627,52 @@ pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
return querytree_list;
}
/*
* Do parse analysis and rewriting. This is the same as pg_analyze_and_rewrite
* except that external-parameter resolution is determined by parser callback
* hooks instead of a fixed list of parameter datatypes.
*/
List *
pg_analyze_and_rewrite_params(Node *parsetree,
const char *query_string,
ParserSetupHook parserSetup,
void *parserSetupArg)
{
ParseState *pstate;
Query *query;
List *querytree_list;
Assert(query_string != NULL); /* required as of 8.4 */
TRACE_POSTGRESQL_QUERY_REWRITE_START(query_string);
/*
* (1) Perform parse analysis.
*/
if (log_parser_stats)
ResetUsage();
pstate = make_parsestate(NULL);
pstate->p_sourcetext = query_string;
(*parserSetup) (pstate, parserSetupArg);
query = transformStmt(pstate, parsetree);
free_parsestate(pstate);
if (log_parser_stats)
ShowUsage("PARSE ANALYSIS STATISTICS");
/*
* (2) Rewrite the queries, as necessary
*/
querytree_list = pg_rewrite_query(query);
TRACE_POSTGRESQL_QUERY_REWRITE_DONE(query_string);
return querytree_list;
}
/*
* Perform rewriting of a query produced by parse analysis.
*
......@@ -1536,6 +1582,11 @@ exec_bind_message(StringInfo input_message)
/* sizeof(ParamListInfoData) includes the first array element */
params = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
(numParams - 1) *sizeof(ParamExternData));
/* we have static list of params, so no hooks needed */
params->paramFetch = NULL;
params->paramFetchArg = NULL;
params->parserSetup = NULL;
params->parserSetupArg = NULL;
params->numParams = numParams;
for (paramno = 0; paramno < numParams; paramno++)
......
......@@ -35,7 +35,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.30 2009/10/26 02:26:41 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.31 2009/11/04 22:26:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -99,8 +99,8 @@ InitPlanCache(void)
* raw_parse_tree: output of raw_parser()
* query_string: original query text (as of PG 8.4, must not be NULL)
* commandTag: compile-time-constant tag for query, or NULL if empty query
* param_types: array of parameter type OIDs, or NULL if none
* num_params: number of parameters
* param_types: array of fixed parameter type OIDs, or NULL if none
* num_params: number of fixed parameters
* cursor_options: options bitmask that was/will be passed to planner
* stmt_list: list of PlannedStmts/utility stmts, or list of Query trees
* fully_planned: are we caching planner or rewriter output?
......@@ -156,6 +156,9 @@ CreateCachedPlan(Node *raw_parse_tree,
else
plansource->param_types = NULL;
plansource->num_params = num_params;
/* these can be set later with CachedPlanSetParserHook: */
plansource->parserSetup = NULL;
plansource->parserSetupArg = NULL;
plansource->cursor_options = cursor_options;
plansource->fully_planned = fully_planned;
plansource->fixed_result = fixed_result;
......@@ -240,6 +243,9 @@ FastCreateCachedPlan(Node *raw_parse_tree,
plansource->commandTag = commandTag; /* no copying needed */
plansource->param_types = param_types;
plansource->num_params = num_params;
/* these can be set later with CachedPlanSetParserHook: */
plansource->parserSetup = NULL;
plansource->parserSetupArg = NULL;
plansource->cursor_options = cursor_options;
plansource->fully_planned = fully_planned;
plansource->fixed_result = fixed_result;
......@@ -274,6 +280,27 @@ FastCreateCachedPlan(Node *raw_parse_tree,
return plansource;
}
/*
* CachedPlanSetParserHook: set up to use parser callback hooks
*
* Use this when a caller wants to manage parameter information via parser
* callbacks rather than a fixed parameter-types list. Beware that the
* information pointed to by parserSetupArg must be valid for as long as
* the cached plan might be replanned!
*/
void
CachedPlanSetParserHook(CachedPlanSource *plansource,
ParserSetupHook parserSetup,
void *parserSetupArg)
{
/* Must not have specified a fixed parameter-types list */
Assert(plansource->param_types == NULL);
Assert(plansource->num_params == 0);
/* OK, save hook info */
plansource->parserSetup = parserSetup;
plansource->parserSetupArg = parserSetupArg;
}
/*
* StoreCachedPlan: store a built or rebuilt plan into a plancache entry.
*
......@@ -466,6 +493,7 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
if (!plan)
{
bool snapshot_set = false;
Node *rawtree;
List *slist;
TupleDesc resultDesc;
......@@ -491,14 +519,19 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
/*
* Run parse analysis and rule rewriting. The parser tends to
* scribble on its input, so we must copy the raw parse tree to
* prevent corruption of the cache. Note that we do not use
* parse_analyze_varparams(), assuming that the caller never wants the
* parameter types to change from the original values.
* prevent corruption of the cache.
*/
slist = pg_analyze_and_rewrite(copyObject(plansource->raw_parse_tree),
plansource->query_string,
plansource->param_types,
plansource->num_params);
rawtree = copyObject(plansource->raw_parse_tree);
if (plansource->parserSetup != NULL)
slist = pg_analyze_and_rewrite_params(rawtree,
plansource->query_string,
plansource->parserSetup,
plansource->parserSetupArg);
else
slist = pg_analyze_and_rewrite(rawtree,
plansource->query_string,
plansource->param_types,
plansource->num_params);
if (plansource->fully_planned)
{
......
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.72 2009/06/11 14:49:11 momjian Exp $
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.73 2009/11/04 22:26:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -73,6 +73,9 @@ extern void SPI_restore_connection(void);
extern int SPI_execute(const char *src, bool read_only, long tcount);
extern int SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
bool read_only, long tcount);
extern int SPI_execute_plan_with_paramlist(SPIPlanPtr plan,
ParamListInfo params,
bool read_only, long tcount);
extern int SPI_exec(const char *src, long tcount);
extern int SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls,
long tcount);
......@@ -88,6 +91,10 @@ extern int SPI_execute_with_args(const char *src,
extern SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes);
extern SPIPlanPtr SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
int cursorOptions);
extern SPIPlanPtr SPI_prepare_params(const char *src,
ParserSetupHook parserSetup,
void *parserSetupArg,
int cursorOptions);
extern SPIPlanPtr SPI_saveplan(SPIPlanPtr plan);
extern int SPI_freeplan(SPIPlanPtr plan);
......@@ -122,6 +129,8 @@ extern Portal SPI_cursor_open_with_args(const char *name,
int nargs, Oid *argtypes,
Datum *Values, const char *Nulls,
bool read_only, int cursorOptions);
extern Portal SPI_cursor_open_with_paramlist(const char *name, SPIPlanPtr plan,
ParamListInfo params, bool read_only);
extern Portal SPI_cursor_find(const char *name);
extern void SPI_cursor_fetch(Portal portal, bool forward, long count);
extern void SPI_cursor_move(Portal portal, bool forward, long count);
......
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.32 2009/01/01 17:23:59 momjian Exp $
* $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.33 2009/11/04 22:26:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -68,6 +68,8 @@ typedef struct _SPI_plan
int cursor_options; /* Cursor options used for planning */
int nargs; /* number of plan arguments */
Oid *argtypes; /* Argument types (NULL if nargs is 0) */
ParserSetupHook parserSetup; /* alternative parameter spec method */
void *parserSetupArg;
} _SPI_plan;
#endif /* SPI_PRIV_H */
......@@ -7,13 +7,16 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.38 2009/01/01 17:24:00 momjian Exp $
* $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.39 2009/11/04 22:26:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef PARAMS_H
#define PARAMS_H
/* To avoid including a pile of parser headers, reference ParseState thus: */
struct ParseState;
/* ----------------
* ParamListInfo
......@@ -26,10 +29,20 @@
* Although parameter numbers are normally consecutive, we allow
* ptype == InvalidOid to signal an unused array entry.
*
* pflags is a flags field. Currently the only used bit is:
* PARAM_FLAG_CONST signals the planner that it may treat this parameter
* as a constant (i.e., generate a plan that works only for this value
* of the parameter).
*
* There are two hook functions that can be associated with a ParamListInfo
* array to support dynamic parameter handling. First, if paramFetch
* isn't null and the executor requires a value for an invalid parameter
* (one with ptype == InvalidOid), the paramFetch hook is called to give
* it a chance to fill in the parameter value. Second, a parserSetup
* hook can be supplied to re-instantiate the original parsing hooks if
* a query needs to be re-parsed/planned (as a substitute for supposing
* that the current ptype values represent a fixed set of parameter types).
* Although the data structure is really an array, not a list, we keep
* the old typedef name to avoid unnecessary code changes.
* ----------------
......@@ -45,14 +58,22 @@ typedef struct ParamExternData
Oid ptype; /* parameter's datatype, or 0 */
} ParamExternData;
typedef struct ParamListInfoData *ParamListInfo;
typedef void (*ParamFetchHook) (ParamListInfo params, int paramid);
typedef void (*ParserSetupHook) (struct ParseState *pstate, void *arg);
typedef struct ParamListInfoData
{
ParamFetchHook paramFetch; /* parameter fetch hook */
void *paramFetchArg;
ParserSetupHook parserSetup; /* parser setup hook */
void *parserSetupArg;
int numParams; /* number of ParamExternDatas following */
ParamExternData params[1]; /* VARIABLE LENGTH ARRAY */
} ParamListInfoData;
typedef ParamListInfoData *ParamListInfo;
/* ----------------
* ParamExecData
......@@ -82,7 +103,7 @@ typedef struct ParamExecData
/* Functions found in src/backend/nodes/params.c */
extern ParamListInfo copyParamList(ParamListInfo from);
extern void getParamListTypes(ParamListInfo params,
Oid **param_types, int *num_params);
extern void setupParserWithParamList(struct ParseState *pstate,
ParamListInfo params);
#endif /* PARAMS_H */
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.100 2009/09/01 00:09:42 tgl Exp $
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.101 2009/11/04 22:26:07 tgl Exp $
*
* OLD COMMENTS
* This file was created so that other c files could get the two
......@@ -49,6 +49,10 @@ extern List *pg_parse_and_rewrite(const char *query_string,
extern List *pg_parse_query(const char *query_string);
extern List *pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
Oid *paramTypes, int numParams);
extern List *pg_analyze_and_rewrite_params(Node *parsetree,
const char *query_string,
ParserSetupHook parserSetup,
void *parserSetupArg);
extern PlannedStmt *pg_plan_query(Query *querytree, int cursorOptions,
ParamListInfo boundParams);
extern List *pg_plan_queries(List *querytrees, int cursorOptions,
......
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.15 2009/01/01 17:24:02 momjian Exp $
* $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.16 2009/11/04 22:26:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -16,6 +16,7 @@
#define PLANCACHE_H
#include "access/tupdesc.h"
#include "nodes/params.h"
/*
* CachedPlanSource represents the portion of a cached plan that persists
......@@ -50,6 +51,8 @@ typedef struct CachedPlanSource
const char *commandTag; /* command tag (a constant!), or NULL */
Oid *param_types; /* array of parameter type OIDs, or NULL */
int num_params; /* length of param_types array */
ParserSetupHook parserSetup; /* alternative parameter spec method */
void *parserSetupArg;
int cursor_options; /* cursor options used for planning */
bool fully_planned; /* do we cache planner or rewriter output? */
bool fixed_result; /* disallow change in result tupdesc? */
......@@ -105,6 +108,9 @@ extern CachedPlanSource *FastCreateCachedPlan(Node *raw_parse_tree,
bool fully_planned,
bool fixed_result,
MemoryContext context);
extern void CachedPlanSetParserHook(CachedPlanSource *plansource,
ParserSetupHook parserSetup,
void *parserSetupArg);
extern void DropCachedPlan(CachedPlanSource *plansource);
extern CachedPlan *RevalidateCachedPlan(CachedPlanSource *plansource,
bool useResOwner);
......
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.128 2009/09/29 20:05:29 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.129 2009/11/04 22:26:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -109,7 +109,7 @@ static List *read_raise_options(void);
} loop_body;
List *list;
PLpgSQL_type *dtype;
PLpgSQL_datum *scalar; /* a VAR, RECFIELD, or TRIGARG */
PLpgSQL_datum *scalar; /* a VAR or RECFIELD */
PLpgSQL_variable *variable; /* a VAR, REC, or ROW */
PLpgSQL_var *var;
PLpgSQL_row *row;
......@@ -236,7 +236,7 @@ static List *read_raise_options(void);
*/
%token T_STRING
%token T_NUMBER
%token T_SCALAR /* a VAR, RECFIELD, or TRIGARG */
%token T_SCALAR /* a VAR or RECFIELD */
%token T_ROW
%token T_RECORD
%token T_DTYPE
......@@ -1903,44 +1903,6 @@ lno :
%%
#define MAX_EXPR_PARAMS 1024
/*
* determine the expression parameter position to use for a plpgsql datum
*
* It is important that any given plpgsql datum map to just one parameter.
* We used to be sloppy and assign a separate parameter for each occurrence
* of a datum reference, but that fails for situations such as "select DATUM
* from ... group by DATUM".
*
* The params[] array must be of size MAX_EXPR_PARAMS.
*/
static int
assign_expr_param(int dno, int *params, int *nparams)
{
int i;
/* already have an instance of this dno? */
for (i = 0; i < *nparams; i++)
{
if (params[i] == dno)
return i+1;
}
/* check for array overflow */
if (*nparams >= MAX_EXPR_PARAMS)
{
plpgsql_error_lineno = plpgsql_scanner_lineno();
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many variables specified in SQL statement")));
}
/* add new parameter dno to array */
params[*nparams] = dno;
(*nparams)++;
return *nparams;
}
/* Convenience routine to read an expression with one possible terminator */
PLpgSQL_expr *
plpgsql_read_expression(int until, const char *expected)
......@@ -1993,8 +1955,7 @@ read_sql_construct(int until,
int lno;
StringInfoData ds;
int parenlevel = 0;
int nparams = 0;
int params[MAX_EXPR_PARAMS];
Bitmapset *paramnos = NULL;
char buf[32];
PLpgSQL_expr *expr;
......@@ -2047,24 +2008,21 @@ read_sql_construct(int until,
switch (tok)
{
case T_SCALAR:
snprintf(buf, sizeof(buf), " $%d ",
assign_expr_param(yylval.scalar->dno,
params, &nparams));
snprintf(buf, sizeof(buf), " $%d ", yylval.scalar->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.scalar->dno);
break;
case T_ROW:
snprintf(buf, sizeof(buf), " $%d ",
assign_expr_param(yylval.row->dno,
params, &nparams));
snprintf(buf, sizeof(buf), " $%d ", yylval.row->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.row->dno);
break;
case T_RECORD:
snprintf(buf, sizeof(buf), " $%d ",
assign_expr_param(yylval.rec->dno,
params, &nparams));
snprintf(buf, sizeof(buf), " $%d ", yylval.rec->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.rec->dno);
break;
default:
......@@ -2076,13 +2034,11 @@ read_sql_construct(int until,
if (endtoken)
*endtoken = tok;
expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr = palloc0(sizeof(PLpgSQL_expr));
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
expr->nparams = nparams;
while(nparams-- > 0)
expr->params[nparams] = params[nparams];
expr->paramnos = paramnos;
pfree(ds.data);
if (valid_sql)
......@@ -2162,8 +2118,7 @@ static PLpgSQL_stmt *
make_execsql_stmt(const char *sqlstart, int lineno)
{
StringInfoData ds;
int nparams = 0;
int params[MAX_EXPR_PARAMS];
Bitmapset *paramnos = NULL;
char buf[32];
PLpgSQL_stmt_execsql *execsql;
PLpgSQL_expr *expr;
......@@ -2214,24 +2169,21 @@ make_execsql_stmt(const char *sqlstart, int lineno)
switch (tok)
{
case T_SCALAR:
snprintf(buf, sizeof(buf), " $%d ",
assign_expr_param(yylval.scalar->dno,
params, &nparams));
snprintf(buf, sizeof(buf), " $%d ", yylval.scalar->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.scalar->dno);
break;
case T_ROW:
snprintf(buf, sizeof(buf), " $%d ",
assign_expr_param(yylval.row->dno,
params, &nparams));
snprintf(buf, sizeof(buf), " $%d ", yylval.row->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.row->dno);
break;
case T_RECORD:
snprintf(buf, sizeof(buf), " $%d ",
assign_expr_param(yylval.rec->dno,
params, &nparams));
snprintf(buf, sizeof(buf), " $%d ", yylval.rec->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.rec->dno);
break;
default:
......@@ -2240,13 +2192,11 @@ make_execsql_stmt(const char *sqlstart, int lineno)
}
}
expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr = palloc0(sizeof(PLpgSQL_expr));
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
expr->nparams = nparams;
while(nparams-- > 0)
expr->params[nparams] = params[nparams];
expr->paramnos = paramnos;
pfree(ds.data);
check_sql_expr(expr->query);
......@@ -2600,9 +2550,6 @@ check_assignable(PLpgSQL_datum *datum)
case PLPGSQL_DTYPE_ARRAYELEM:
/* always assignable? */
break;
case PLPGSQL_DTYPE_TRIGARG:
yyerror("cannot assign to tg_argv");
break;
default:
elog(ERROR, "unrecognized dtype: %d", datum->dtype);
break;
......@@ -3095,24 +3042,10 @@ make_case(int lineno, PLpgSQL_expr *t_expr,
{
PLpgSQL_case_when *cwt = (PLpgSQL_case_when *) lfirst(l);
PLpgSQL_expr *expr = cwt->expr;
int nparams = expr->nparams;
PLpgSQL_expr *new_expr;
StringInfoData ds;
/* Must add the CASE variable as an extra param to expression */
if (nparams >= MAX_EXPR_PARAMS)
{
plpgsql_error_lineno = cwt->lineno;
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many variables specified in SQL statement")));
}
new_expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * (nparams + 1) - sizeof(int));
memcpy(new_expr, expr,
sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
new_expr->nparams = nparams + 1;
new_expr->params[nparams] = t_varno;
expr->paramnos = bms_add_member(expr->paramnos, t_varno);
/* copy expression query without SELECT keyword (expr->query + 7) */
Assert(strncmp(expr->query, "SELECT ", 7) == 0);
......@@ -3120,17 +3053,14 @@ make_case(int lineno, PLpgSQL_expr *t_expr,
/* And do the string hacking */
initStringInfo(&ds);
appendStringInfo(&ds, "SELECT $%d IN(%s)",
nparams + 1,
expr->query + 7);
appendStringInfo(&ds, "SELECT $%d IN (%s)",
t_varno + 1,
expr->query + 7);
new_expr->query = pstrdup(ds.data);
pfree(ds.data);
pfree(expr->query);
pfree(expr);
expr->query = pstrdup(ds.data);
cwt->expr = new_expr;
pfree(ds.data);
}
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.139 2009/09/22 23:43:42 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.140 2009/11/04 22:26:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -624,20 +624,24 @@ do_compile(FunctionCallInfo fcinfo,
true);
function->tg_table_name_varno = var->dno;
/* add variable tg_table_schema */
/* add the variable tg_table_schema */
var = plpgsql_build_variable("tg_table_schema", 0,
plpgsql_build_datatype(NAMEOID, -1),
true);
function->tg_table_schema_varno = var->dno;
/* Add the variable tg_nargs */
var = plpgsql_build_variable("tg_nargs", 0,
plpgsql_build_datatype(INT4OID, -1),
true);
function->tg_nargs_varno = var->dno;
/* Add the variable tg_argv */
var = plpgsql_build_variable("tg_argv", 0,
plpgsql_build_datatype(TEXTARRAYOID, -1),
true);
function->tg_argv_varno = var->dno;
break;
default:
......@@ -931,34 +935,6 @@ plpgsql_parse_word(const char *word)
/* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 1);
/*
* Recognize tg_argv when compiling triggers (XXX this sucks, it should be
* a regular variable in the namestack)
*/
if (plpgsql_curr_compile->fn_is_trigger)
{
if (strcmp(cp[0], "tg_argv") == 0)
{
bool save_spacescanned = plpgsql_SpaceScanned;
PLpgSQL_trigarg *trigarg;
trigarg = palloc0(sizeof(PLpgSQL_trigarg));
trigarg->dtype = PLPGSQL_DTYPE_TRIGARG;
if (plpgsql_yylex() != '[')
plpgsql_yyerror("expected \"[\"");
trigarg->argnum = plpgsql_read_expression(']', "]");
plpgsql_adddatum((PLpgSQL_datum *) trigarg);
plpgsql_yylval.scalar = (PLpgSQL_datum *) trigarg;
plpgsql_SpaceScanned = save_spacescanned;
pfree(cp[0]);
return T_SCALAR;
}
}
/*
* Do a lookup on the compiler's namestack
*/
......
此差异已折叠。
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.81 2009/09/29 20:05:29 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.82 2009/11/04 22:26:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1148,21 +1148,7 @@ dump_getdiag(PLpgSQL_stmt_getdiag *stmt)
static void
dump_expr(PLpgSQL_expr *expr)
{
int i;
printf("'%s", expr->query);
if (expr->nparams > 0)
{
printf(" {");
for (i = 0; i < expr->nparams; i++)
{
if (i > 0)
printf(", ");
printf("$%d=%d", i + 1, expr->params[i]);
}
printf("}");
}
printf("'");
printf("'%s'", expr->query);
}
void
......@@ -1240,11 +1226,6 @@ plpgsql_dumptree(PLpgSQL_function *func)
dump_expr(((PLpgSQL_arrayelem *) d)->subscript);
printf("\n");
break;
case PLPGSQL_DTYPE_TRIGARG:
printf("TRIGARG ");
dump_expr(((PLpgSQL_trigarg *) d)->argnum);
printf("\n");
break;
default:
printf("??? unknown data type %d\n", d->dtype);
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.46 2009/09/22 23:43:42 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.47 2009/11/04 22:26:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -68,6 +68,7 @@ Datum
plpgsql_call_handler(PG_FUNCTION_ARGS)
{
PLpgSQL_function *func;
PLpgSQL_execstate *save_cur_estate;
Datum retval;
int rc;
......@@ -80,6 +81,9 @@ plpgsql_call_handler(PG_FUNCTION_ARGS)
/* Find or compile the function */
func = plpgsql_compile(fcinfo, false);
/* Must save and restore prior value of cur_estate */
save_cur_estate = func->cur_estate;
/* Mark the function as busy, so it can't be deleted from under us */
func->use_count++;
......@@ -97,14 +101,17 @@ plpgsql_call_handler(PG_FUNCTION_ARGS)
}
PG_CATCH();
{
/* Decrement use-count and propagate error */
/* Decrement use-count, restore cur_estate, and propagate error */
func->use_count--;
func->cur_estate = save_cur_estate;
PG_RE_THROW();
}
PG_END_TRY();
func->use_count--;
func->cur_estate = save_cur_estate;
/*
* Disconnect from SPI manager
*/
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.117 2009/09/29 20:05:29 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.118 2009/11/04 22:26:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -22,6 +22,7 @@
#include "fmgr.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "nodes/bitmapset.h"
#include "utils/tuplestore.h"
/**********************************************************************
......@@ -58,8 +59,7 @@ enum
PLPGSQL_DTYPE_REC,
PLPGSQL_DTYPE_RECFIELD,
PLPGSQL_DTYPE_ARRAYELEM,
PLPGSQL_DTYPE_EXPR,
PLPGSQL_DTYPE_TRIGARG
PLPGSQL_DTYPE_EXPR
};
/* ----------
......@@ -162,8 +162,7 @@ typedef struct
/*
* PLpgSQL_datum is the common supertype for PLpgSQL_expr, PLpgSQL_var,
* PLpgSQL_row, PLpgSQL_rec, PLpgSQL_recfield, PLpgSQL_arrayelem, and
* PLpgSQL_trigarg
* PLpgSQL_row, PLpgSQL_rec, PLpgSQL_recfield, and PLpgSQL_arrayelem
*/
typedef struct
{ /* Generic datum array item */
......@@ -189,7 +188,11 @@ typedef struct PLpgSQL_expr
int dno;
char *query;
SPIPlanPtr plan;
Oid *plan_argtypes;
Bitmapset *paramnos; /* all dnos referenced by this query */
/* function containing this expr (not set until we first parse query) */
struct PLpgSQL_function *func;
/* fields for "simple expression" fast-path execution: */
Expr *expr_simple_expr; /* NULL means not a simple expr */
int expr_simple_generation; /* plancache generation we checked */
......@@ -202,10 +205,6 @@ typedef struct PLpgSQL_expr
*/
ExprState *expr_simple_state;
LocalTransactionId expr_simple_lxid;
/* params to pass to expr */
int nparams;
int params[1]; /* VARIABLE SIZE ARRAY ... must be last */
} PLpgSQL_expr;
......@@ -284,14 +283,6 @@ typedef struct
} PLpgSQL_arrayelem;
typedef struct
{ /* Positional argument to trigger */
int dtype;
int dno;
PLpgSQL_expr *argnum;
} PLpgSQL_trigarg;
typedef struct
{ /* Item in the compilers namestack */
int itemtype;
......@@ -670,17 +661,22 @@ typedef struct PLpgSQL_function
int tg_table_name_varno;
int tg_table_schema_varno;
int tg_nargs_varno;
int tg_argv_varno;
int ndatums;
PLpgSQL_datum **datums;
PLpgSQL_stmt_block *action;
/* these fields change when the function is used */
struct PLpgSQL_execstate *cur_estate;
unsigned long use_count;
} PLpgSQL_function;
typedef struct
typedef struct PLpgSQL_execstate
{ /* Runtime execution data */
PLpgSQL_function *func; /* function being executed */
Datum retval;
bool retisnull;
Oid rettype; /* type of current retval */
......@@ -699,9 +695,6 @@ typedef struct
MemoryContext tuple_store_cxt;
ReturnSetInfo *rsi;
int trig_nargs;
Datum *trig_argv;
int found_varno;
int ndatums;
PLpgSQL_datum **datums;
......@@ -711,11 +704,12 @@ typedef struct
uint32 eval_processed;
Oid eval_lastoid;
ExprContext *eval_econtext; /* for executing simple expressions */
PLpgSQL_expr *cur_expr; /* current query/expr being evaluated */
/* status information for error context reporting */
PLpgSQL_function *err_func; /* current func */
PLpgSQL_stmt *err_stmt; /* current stmt */
const char *err_text; /* additional state info */
void *plugin_info; /* reserved for use by optional plugin */
} PLpgSQL_execstate;
......
......@@ -285,11 +285,9 @@ begin
if new.slotno < 1 or new.slotno > hubrec.nslots then
raise exception ''no manual manipulation of HSlot'';
end if;
if tg_op = ''UPDATE'' then
if new.hubname != old.hubname then
if count(*) > 0 from Hub where name = old.hubname then
raise exception ''no manual manipulation of HSlot'';
end if;
if tg_op = ''UPDATE'' and new.hubname != old.hubname then
if count(*) > 0 from Hub where name = old.hubname then
raise exception ''no manual manipulation of HSlot'';
end if;
end if;
sname := ''HS.'' || trim(new.hubname);
......
......@@ -347,11 +347,9 @@ begin
if new.slotno < 1 or new.slotno > hubrec.nslots then
raise exception ''no manual manipulation of HSlot'';
end if;
if tg_op = ''UPDATE'' then
if new.hubname != old.hubname then
if count(*) > 0 from Hub where name = old.hubname then
raise exception ''no manual manipulation of HSlot'';
end if;
if tg_op = ''UPDATE'' and new.hubname != old.hubname then
if count(*) > 0 from Hub where name = old.hubname then
raise exception ''no manual manipulation of HSlot'';
end if;
end if;
sname := ''HS.'' || trim(new.hubname);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册