parser-hooks-part2.patch
text/x-patch
Filename: parser-hooks-part2.patch
Type: text/x-patch
Part: 0
Patch
Format: context
| File | + | − |
|---|---|---|
| doc/src/sgml/spi.sgml | 190 | 6 |
| src/backend/commands/explain.c | 4 | 7 |
| src/backend/commands/prepare.c | 5 | 0 |
| src/backend/executor/execCurrent.c | 13 | 1 |
| src/backend/executor/execQual.c | 13 | 1 |
| src/backend/executor/functions.c | 5 | 0 |
| src/backend/executor/spi.c | 168 | 72 |
| src/backend/nodes/params.c | 52 | 26 |
| src/backend/tcop/postgres.c | 51 | 0 |
| src/backend/utils/cache/plancache.c | 43 | 10 |
| src/include/executor/spi.h | 9 | 0 |
| src/include/executor/spi_priv.h | 2 | 0 |
| src/include/nodes/params.h | 25 | 4 |
| src/include/tcop/tcopprot.h | 4 | 0 |
| src/include/utils/plancache.h | 6 | 0 |
| src/pl/plpgsql/src/gram.y | 26 | 96 |
| src/pl/plpgsql/src/pl_comp.c | 7 | 31 |
| src/pl/plpgsql/src/pl_exec.c | 328 | 198 |
| src/pl/plpgsql/src/pl_funcs.c | 1 | 20 |
| src/pl/plpgsql/src/pl_handler.c | 8 | 1 |
| src/pl/plpgsql/src/plpgsql.h | 16 | 22 |
| src/test/regress/expected/plpgsql.out | 3 | 5 |
| src/test/regress/sql/plpgsql.sql | 3 | 5 |
Index: doc/src/sgml/spi.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/spi.sgml,v
retrieving revision 1.65
diff -c -r1.65 spi.sgml
*** doc/src/sgml/spi.sgml 5 Aug 2009 19:31:50 -0000 1.65
--- doc/src/sgml/spi.sgml 4 Nov 2009 16:05:28 -0000
***************
*** 1386,1399 ****
<function>SPI_execute</function> if successful.
</para>
</refsect1>
<refsect1>
! <title>Notes</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.
</para>
</refsect1>
</refentry>
--- 1386,1485 ----
<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>Description</title>
<para>
! <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,1549 ****
</para>
<para>
! The passed-in data will be copied into the cursor's portal, so it
can be freed while the cursor still exists.
</para>
</refsect1>
--- 1629,1635 ----
</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>
***************
*** 1667,1673 ****
</para>
<para>
! The passed-in data will be copied into the cursor's portal, so it
can be freed while the cursor still exists.
</para>
</refsect1>
--- 1753,1759 ----
</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>
***************
*** 1770,1775 ****
--- 1856,1959 ----
<!-- *********************************************** -->
+ <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>
Index: src/backend/commands/explain.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/explain.c,v
retrieving revision 1.192
diff -c -r1.192 explain.c
*** src/backend/commands/explain.c 12 Oct 2009 18:10:41 -0000 1.192
--- src/backend/commands/explain.c 4 Nov 2009 16:05:28 -0000
***************
*** 107,114 ****
ParamListInfo params, DestReceiver *dest)
{
ExplainState es;
- Oid *param_types;
- int num_params;
TupOutputState *tstate;
List *rewritten;
ListCell *lc;
--- 107,112 ----
***************
*** 150,158 ****
opt->defname)));
}
- /* Convert parameter type data to the form parser wants */
- getParamListTypes(params, ¶m_types, &num_params);
-
/*
* Run parse analysis and rewrite. Note this also acquires sufficient
* locks on the source table(s).
--- 148,153 ----
***************
*** 163,170 ****
* 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);
/* emit opening boilerplate */
ExplainBeginOutput(&es);
--- 158,167 ----
* executed repeatedly. (See also the same hack in DECLARE CURSOR and
* PREPARE.) XXX FIXME someday.
*/
! rewritten = pg_analyze_and_rewrite_params((Node *) copyObject(stmt->query),
! queryString,
! (ParserSetupHook) setupParserWithParams,
! params);
/* emit opening boilerplate */
ExplainBeginOutput(&es);
Index: src/backend/commands/prepare.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/prepare.c,v
retrieving revision 1.99
diff -c -r1.99 prepare.c
*** src/backend/commands/prepare.c 10 Aug 2009 05:46:50 -0000 1.99
--- src/backend/commands/prepare.c 4 Nov 2009 16:05:28 -0000
***************
*** 379,384 ****
--- 379,389 ----
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;
Index: src/backend/executor/execCurrent.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/executor/execCurrent.c,v
retrieving revision 1.12
diff -c -r1.12 execCurrent.c
*** src/backend/executor/execCurrent.c 26 Oct 2009 02:26:29 -0000 1.12
--- src/backend/executor/execCurrent.c 4 Nov 2009 16:05:28 -0000
***************
*** 217,225 ****
{
ParamExternData *prm = ¶mInfo->params[paramId - 1];
if (OidIsValid(prm->ptype) && !prm->isnull)
{
! Assert(prm->ptype == REFCURSOROID);
/* We know that refcursor uses text's I/O routines */
return TextDatumGetCString(prm->value);
}
--- 217,237 ----
{
ParamExternData *prm = ¶mInfo->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)
{
! /* 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);
}
Index: src/backend/executor/execQual.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/executor/execQual.c,v
retrieving revision 1.253
diff -c -r1.253 execQual.c
*** src/backend/executor/execQual.c 26 Oct 2009 02:26:29 -0000 1.253
--- src/backend/executor/execQual.c 4 Nov 2009 16:05:28 -0000
***************
*** 882,890 ****
{
ParamExternData *prm = ¶mInfo->params[thisParamId - 1];
if (OidIsValid(prm->ptype))
{
! Assert(prm->ptype == expression->paramtype);
*isNull = prm->isnull;
return prm->value;
}
--- 882,902 ----
{
ParamExternData *prm = ¶mInfo->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))
{
! /* 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;
}
Index: src/backend/executor/functions.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/executor/functions.c,v
retrieving revision 1.135
diff -c -r1.135 functions.c
*** src/backend/executor/functions.c 11 Jun 2009 17:25:38 -0000 1.135
--- src/backend/executor/functions.c 4 Nov 2009 16:05:28 -0000
***************
*** 526,531 ****
--- 526,536 ----
/* 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;
}
Index: src/backend/executor/spi.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/executor/spi.c,v
retrieving revision 1.210
diff -c -r1.210 spi.c
*** src/backend/executor/spi.c 10 Oct 2009 01:43:47 -0000 1.210
--- src/backend/executor/spi.c 4 Nov 2009 16:05:29 -0000
***************
*** 45,52 ****
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);
static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan,
ParamListInfo boundParams);
--- 45,51 ----
static int _SPI_curid = -1;
static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
! ParamListInfo paramLI, bool read_only);
static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan,
ParamListInfo boundParams);
***************
*** 407,412 ****
--- 406,433 ----
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,488 ****
--- 504,511 ----
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,533 ****
--- 551,595 ----
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_with_hook(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,961 ****
Datum *Values, const char *Nulls,
bool read_only)
{
! return SPI_cursor_open_internal(name, plan, Values, Nulls,
! read_only, 0);
}
--- 1016,1036 ----
Datum *Values, const char *Nulls,
bool read_only)
{
! 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,998 ****
--- 1067,1076 ----
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,1014 ****
/* 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);
/* And clean up */
_SPI_curid++;
--- 1085,1091 ----
/* SPI_cursor_open_internal must be called in procedure memory context */
_SPI_procmem();
! result = SPI_cursor_open_internal(name, &plan, paramLI, read_only);
/* And clean up */
_SPI_curid++;
***************
*** 1019,1042 ****
/*
* SPI_cursor_open_internal()
*
! * Common code for SPI_cursor_open and SPI_cursor_open_with_args
*/
static Portal
SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
! Datum *Values, const char *Nulls,
! bool read_only, int pflags)
{
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
--- 1096,1130 ----
/*
+ * 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 variants
*/
static Portal
SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
! ParamListInfo paramLI, bool read_only)
{
CachedPlanSource *plansource;
CachedPlan *cplan;
List *stmt_list;
char *query_string;
Snapshot snapshot;
MemoryContext oldcontext;
Portal portal;
/*
* Check that the plan is something the Portal code will special-case as
***************
*** 1082,1135 ****
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 = ¶mLI->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, ¶mTypLen, ¶mTypByVal);
! prm->value = datumCopy(Values[k],
! paramTypByVal, paramTypLen);
! }
! }
! }
! else
! paramLI = NULL;
!
! MemoryContextSwitchTo(oldcontext);
if (plan->saved)
{
/* Replan if needed, and increment plan refcount for portal */
--- 1170,1184 ----
portal = CreatePortal(name, false, false);
}
/* Copy the plan's query string into the portal */
! 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 */
***************
*** 1221,1226 ****
--- 1270,1288 ----
}
/*
+ * 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.
*/
PortalStart(portal, paramLI, snapshot);
***************
*** 1588,1598 ****
/*
* 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!
*
* Results are stored into *plan (specifically, plan->plancache_list).
* Note however that the result trees are all in CurrentMemoryContext
--- 1650,1661 ----
/*
* Parse and plan a querystring.
*
! * 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,1612 ****
List *plancache_list;
ListCell *list_item;
ErrorContextCallback spierrcontext;
- Oid *argtypes = plan->argtypes;
- int nargs = plan->nargs;
int cursor_options = plan->cursor_options;
/*
--- 1668,1673 ----
***************
*** 1623,1630 ****
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.
*/
plancache_list = NIL;
--- 1684,1691 ----
raw_parsetree_list = pg_parse_query(src);
/*
! * 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,1643 ****
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);
stmt_list = pg_plan_queries(stmt_list, cursor_options, boundParams);
plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
--- 1696,1722 ----
CachedPlanSource *plansource;
CachedPlan *cplan;
! /*
! * 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,1654 ****
/* 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->fully_planned = true;
plansource->fixed_result = false;
/* no need to set search_path, generation or saved_xmin */
--- 1726,1735 ----
/* 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 = 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,1927 ****
}
/*
! * Convert query parameters to form wanted by planner and executor
*/
static ParamListInfo
_SPI_convert_params(int nargs, Oid *argtypes,
--- 2002,2008 ----
}
/*
! * Convert arrays of query parameters to form wanted by planner and executor
*/
static ParamListInfo
_SPI_convert_params(int nargs, Oid *argtypes,
***************
*** 1937,1942 ****
--- 2018,2028 ----
/* 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,2227 ****
--- 2308,2315 ----
}
else
newplan->argtypes = NULL;
+ newplan->parserSetup = plan->parserSetup;
+ newplan->parserSetupArg = plan->parserSetupArg;
foreach(lc, plan->plancache_list)
{
***************
*** 2241,2246 ****
--- 2329,2336 ----
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,2303 ****
--- 2388,2395 ----
}
else
newplan->argtypes = NULL;
+ newplan->parserSetup = plan->parserSetup;
+ newplan->parserSetupArg = plan->parserSetupArg;
foreach(lc, plan->plancache_list)
{
***************
*** 2317,2322 ****
--- 2409,2418 ----
cplan->stmt_list,
true,
false);
+ if (newplan->parserSetup != NULL)
+ CachedPlanSetHook(newsource,
+ newplan->parserSetup,
+ newplan->parserSetupArg);
newplan->plancache_list = lappend(newplan->plancache_list, newsource);
}
Index: src/backend/nodes/params.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/nodes/params.c,v
retrieving revision 1.11
diff -c -r1.11 params.c
*** src/backend/nodes/params.c 1 Jan 2009 17:23:43 -0000 1.11
--- src/backend/nodes/params.c 4 Nov 2009 16:05:29 -0000
***************
*** 16,21 ****
--- 16,22 ----
#include "postgres.h"
#include "nodes/params.h"
+ #include "parser/parse_param.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
***************
*** 24,29 ****
--- 25,35 ----
* 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,93 ****
(from->numParams - 1) *sizeof(ParamExternData);
retval = (ParamListInfo) palloc(size);
! memcpy(retval, from, size);
! /*
! * 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++)
{
! ParamExternData *prm = &retval->params[i];
int16 typLen;
bool typByVal;
! if (prm->isnull || !OidIsValid(prm->ptype))
continue;
! get_typlenbyval(prm->ptype, &typLen, &typByVal);
! prm->value = datumCopy(prm->value, typByVal, typLen);
}
return retval;
}
/*
! * Extract an array of parameter type OIDs from a ParamListInfo.
*
! * The result is allocated in CurrentMemoryContext.
*/
void
! getParamListTypes(ParamListInfo params,
! Oid **param_types, int *num_params)
{
! Oid *ptypes;
! int i;
! if (params == NULL || params->numParams <= 0)
{
! *param_types = NULL;
! *num_params = 0;
return;
}
! ptypes = (Oid *) palloc(params->numParams * sizeof(Oid));
! *param_types = ptypes;
! *num_params = params->numParams;
!
! for (i = 0; i < params->numParams; i++)
{
! ParamExternData *prm = ¶ms->params[i];
! ptypes[i] = prm->ptype;
}
}
--- 46,119 ----
(from->numParams - 1) *sizeof(ParamExternData);
retval = (ParamListInfo) palloc(size);
! retval->paramFetch = NULL;
! retval->paramFetchArg = NULL;
! retval->parserSetup = NULL;
! retval->parserSetupArg = NULL;
! retval->numParams = from->numParams;
! for (i = 0; i < from->numParams; i++)
{
! ParamExternData *oprm = &from->params[i];
! ParamExternData *nprm = &retval->params[i];
int16 typLen;
bool typByVal;
! /* 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(nprm->ptype, &typLen, &typByVal);
! nprm->value = datumCopy(nprm->value, typByVal, typLen);
}
return retval;
}
/*
! * Set up the parser to treat the given list of run-time parameter values
! * as available external parameters during parsing of a new query.
*
! * Note that it's assumed that the input struct and any referenced by-ref
! * parameter values will survive until the end of parsing.
*/
void
! setupParserWithParams(struct ParseState *pstate,
! ParamListInfo params)
{
! if (params == NULL) /* no params, nothing to do */
! return;
! /* If there is a parserSetup hook, it gets to do this */
! if (params->parserSetup != NULL)
{
! (*params->parserSetup) (pstate, params->parserSetupArg);
return;
}
! /* Else, treat any available parameters as being of fixed type */
! if (params->numParams > 0)
{
! Oid *ptypes;
! int i;
! ptypes = (Oid *) palloc(params->numParams * sizeof(Oid));
! for (i = 0; i < params->numParams; i++)
! {
! ParamExternData *prm = ¶ms->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;
! }
! parse_fixed_parameters(pstate, ptypes, params->numParams);
}
}
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/tcop/postgres.c,v
retrieving revision 1.574
diff -c -r1.574 postgres.c
*** src/backend/tcop/postgres.c 8 Oct 2009 22:34:57 -0000 1.574
--- src/backend/tcop/postgres.c 4 Nov 2009 16:05:29 -0000
***************
*** 628,633 ****
--- 628,679 ----
}
/*
+ * 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.
*
* Note: query must just have come from the parser, because we do not do
***************
*** 1536,1541 ****
--- 1582,1592 ----
/* 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++)
Index: src/backend/utils/cache/plancache.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/cache/plancache.c,v
retrieving revision 1.30
diff -c -r1.30 plancache.c
*** src/backend/utils/cache/plancache.c 26 Oct 2009 02:26:41 -0000 1.30
--- src/backend/utils/cache/plancache.c 4 Nov 2009 16:05:29 -0000
***************
*** 99,106 ****
* 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
* 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?
--- 99,106 ----
* 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 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,161 ****
--- 156,164 ----
else
plansource->param_types = NULL;
plansource->num_params = num_params;
+ /* For historical reasons, these can be set later with CachedPlanSetHook */
+ plansource->parserSetup = NULL;
+ plansource->parserSetupArg = NULL;
plansource->cursor_options = cursor_options;
plansource->fully_planned = fully_planned;
plansource->fixed_result = fixed_result;
***************
*** 240,245 ****
--- 243,251 ----
plansource->commandTag = commandTag; /* no copying needed */
plansource->param_types = param_types;
plansource->num_params = num_params;
+ /* For historical reasons, these can be set later with CachedPlanSetHook */
+ plansource->parserSetup = NULL;
+ plansource->parserSetupArg = NULL;
plansource->cursor_options = cursor_options;
plansource->fully_planned = fully_planned;
plansource->fixed_result = fixed_result;
***************
*** 275,280 ****
--- 281,307 ----
}
/*
+ * CachedPlanSetHook: 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
+ CachedPlanSetHook(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.
*
* Common subroutine for CreateCachedPlan and RevalidateCachedPlan.
***************
*** 466,471 ****
--- 493,499 ----
if (!plan)
{
bool snapshot_set = false;
+ Node *rawtree;
List *slist;
TupleDesc resultDesc;
***************
*** 491,504 ****
/*
* 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.
! */
! slist = pg_analyze_and_rewrite(copyObject(plansource->raw_parse_tree),
! plansource->query_string,
! plansource->param_types,
! plansource->num_params);
if (plansource->fully_planned)
{
--- 519,537 ----
/*
* 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.
! */
! 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)
{
Index: src/include/executor/spi.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/executor/spi.h,v
retrieving revision 1.72
diff -c -r1.72 spi.h
*** src/include/executor/spi.h 11 Jun 2009 14:49:11 -0000 1.72
--- src/include/executor/spi.h 4 Nov 2009 16:05:29 -0000
***************
*** 73,78 ****
--- 73,81 ----
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,93 ****
--- 91,100 ----
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_with_hook(const char *src,
+ ParserSetupHook parserSetup,
+ void *parserSetupArg,
+ int cursorOptions);
extern SPIPlanPtr SPI_saveplan(SPIPlanPtr plan);
extern int SPI_freeplan(SPIPlanPtr plan);
***************
*** 122,127 ****
--- 129,136 ----
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);
Index: src/include/executor/spi_priv.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/executor/spi_priv.h,v
retrieving revision 1.32
diff -c -r1.32 spi_priv.h
*** src/include/executor/spi_priv.h 1 Jan 2009 17:23:59 -0000 1.32
--- src/include/executor/spi_priv.h 4 Nov 2009 16:05:29 -0000
***************
*** 68,73 ****
--- 68,75 ----
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 */
Index: src/include/nodes/params.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/params.h,v
retrieving revision 1.38
diff -c -r1.38 params.h
*** src/include/nodes/params.h 1 Jan 2009 17:24:00 -0000 1.38
--- src/include/nodes/params.h 4 Nov 2009 16:05:29 -0000
***************
*** 14,19 ****
--- 14,24 ----
#ifndef PARAMS_H
#define PARAMS_H
+ /* To avoid including a pile of parser headers, reference ParseState thus: */
+ struct ParseState;
+
+ typedef void (*ParserSetupHook) (struct ParseState *pstate, void *arg);
+
/* ----------------
* ParamListInfo
***************
*** 26,35 ****
--- 31,50 ----
* 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,58 ****
Oid ptype; /* parameter's datatype, or 0 */
} ParamExternData;
typedef struct ParamListInfoData
{
int numParams; /* number of ParamExternDatas following */
ParamExternData params[1]; /* VARIABLE LENGTH ARRAY */
} ParamListInfoData;
- typedef ParamListInfoData *ParamListInfo;
-
/* ----------------
* ParamExecData
--- 60,79 ----
Oid ptype; /* parameter's datatype, or 0 */
} ParamExternData;
+ typedef struct ParamListInfoData *ParamListInfo;
+
+ typedef void (*ParamFetchHook) (ParamListInfo params, int paramid);
+
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;
/* ----------------
* ParamExecData
***************
*** 82,88 ****
/* Functions found in src/backend/nodes/params.c */
extern ParamListInfo copyParamList(ParamListInfo from);
! extern void getParamListTypes(ParamListInfo params,
! Oid **param_types, int *num_params);
#endif /* PARAMS_H */
--- 103,109 ----
/* Functions found in src/backend/nodes/params.c */
extern ParamListInfo copyParamList(ParamListInfo from);
! extern void setupParserWithParams(struct ParseState *pstate,
! ParamListInfo params);
#endif /* PARAMS_H */
Index: src/include/tcop/tcopprot.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/tcop/tcopprot.h,v
retrieving revision 1.100
diff -c -r1.100 tcopprot.h
*** src/include/tcop/tcopprot.h 1 Sep 2009 00:09:42 -0000 1.100
--- src/include/tcop/tcopprot.h 4 Nov 2009 16:05:29 -0000
***************
*** 49,54 ****
--- 49,58 ----
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,
Index: src/include/utils/plancache.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/utils/plancache.h,v
retrieving revision 1.15
diff -c -r1.15 plancache.h
*** src/include/utils/plancache.h 1 Jan 2009 17:24:02 -0000 1.15
--- src/include/utils/plancache.h 4 Nov 2009 16:05:29 -0000
***************
*** 16,21 ****
--- 16,22 ----
#define PLANCACHE_H
#include "access/tupdesc.h"
+ #include "nodes/params.h"
/*
* CachedPlanSource represents the portion of a cached plan that persists
***************
*** 50,55 ****
--- 51,58 ----
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,110 ****
--- 108,116 ----
bool fully_planned,
bool fixed_result,
MemoryContext context);
+ extern void CachedPlanSetHook(CachedPlanSource *plansource,
+ ParserSetupHook parserSetup,
+ void *parserSetupArg);
extern void DropCachedPlan(CachedPlanSource *plansource);
extern CachedPlan *RevalidateCachedPlan(CachedPlanSource *plansource,
bool useResOwner);
Index: src/pl/plpgsql/src/gram.y
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v
retrieving revision 1.128
diff -c -r1.128 gram.y
*** src/pl/plpgsql/src/gram.y 29 Sep 2009 20:05:29 -0000 1.128
--- src/pl/plpgsql/src/gram.y 4 Nov 2009 16:05:29 -0000
***************
*** 109,115 ****
} loop_body;
List *list;
PLpgSQL_type *dtype;
! PLpgSQL_datum *scalar; /* a VAR, RECFIELD, or TRIGARG */
PLpgSQL_variable *variable; /* a VAR, REC, or ROW */
PLpgSQL_var *var;
PLpgSQL_row *row;
--- 109,115 ----
} loop_body;
List *list;
PLpgSQL_type *dtype;
! PLpgSQL_datum *scalar; /* a VAR or RECFIELD */
PLpgSQL_variable *variable; /* a VAR, REC, or ROW */
PLpgSQL_var *var;
PLpgSQL_row *row;
***************
*** 236,242 ****
*/
%token T_STRING
%token T_NUMBER
! %token T_SCALAR /* a VAR, RECFIELD, or TRIGARG */
%token T_ROW
%token T_RECORD
%token T_DTYPE
--- 236,242 ----
*/
%token T_STRING
%token T_NUMBER
! %token T_SCALAR /* a VAR or RECFIELD */
%token T_ROW
%token T_RECORD
%token T_DTYPE
***************
*** 1903,1946 ****
%%
- #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)
--- 1903,1908 ----
***************
*** 1993,2000 ****
int lno;
StringInfoData ds;
int parenlevel = 0;
! int nparams = 0;
! int params[MAX_EXPR_PARAMS];
char buf[32];
PLpgSQL_expr *expr;
--- 1955,1961 ----
int lno;
StringInfoData ds;
int parenlevel = 0;
! Bitmapset *paramnos = NULL;
char buf[32];
PLpgSQL_expr *expr;
***************
*** 2047,2070 ****
switch (tok)
{
case T_SCALAR:
! snprintf(buf, sizeof(buf), " $%d ",
! assign_expr_param(yylval.scalar->dno,
! params, &nparams));
appendStringInfoString(&ds, buf);
break;
case T_ROW:
! snprintf(buf, sizeof(buf), " $%d ",
! assign_expr_param(yylval.row->dno,
! params, &nparams));
appendStringInfoString(&ds, buf);
break;
case T_RECORD:
! snprintf(buf, sizeof(buf), " $%d ",
! assign_expr_param(yylval.rec->dno,
! params, &nparams));
appendStringInfoString(&ds, buf);
break;
default:
--- 2008,2028 ----
switch (tok)
{
case T_SCALAR:
! 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 ", yylval.row->dno + 1);
appendStringInfoString(&ds, buf);
+ paramnos = bms_add_member(paramnos, yylval.row->dno);
break;
case T_RECORD:
! snprintf(buf, sizeof(buf), " $%d ", yylval.rec->dno + 1);
appendStringInfoString(&ds, buf);
+ paramnos = bms_add_member(paramnos, yylval.rec->dno);
break;
default:
***************
*** 2076,2088 ****
if (endtoken)
*endtoken = tok;
! expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
! expr->nparams = nparams;
! while(nparams-- > 0)
! expr->params[nparams] = params[nparams];
pfree(ds.data);
if (valid_sql)
--- 2034,2044 ----
if (endtoken)
*endtoken = tok;
! expr = palloc0(sizeof(PLpgSQL_expr));
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
! expr->paramnos = paramnos;
pfree(ds.data);
if (valid_sql)
***************
*** 2162,2169 ****
make_execsql_stmt(const char *sqlstart, int lineno)
{
StringInfoData ds;
! int nparams = 0;
! int params[MAX_EXPR_PARAMS];
char buf[32];
PLpgSQL_stmt_execsql *execsql;
PLpgSQL_expr *expr;
--- 2118,2124 ----
make_execsql_stmt(const char *sqlstart, int lineno)
{
StringInfoData ds;
! Bitmapset *paramnos = NULL;
char buf[32];
PLpgSQL_stmt_execsql *execsql;
PLpgSQL_expr *expr;
***************
*** 2214,2237 ****
switch (tok)
{
case T_SCALAR:
! snprintf(buf, sizeof(buf), " $%d ",
! assign_expr_param(yylval.scalar->dno,
! params, &nparams));
appendStringInfoString(&ds, buf);
break;
case T_ROW:
! snprintf(buf, sizeof(buf), " $%d ",
! assign_expr_param(yylval.row->dno,
! params, &nparams));
appendStringInfoString(&ds, buf);
break;
case T_RECORD:
! snprintf(buf, sizeof(buf), " $%d ",
! assign_expr_param(yylval.rec->dno,
! params, &nparams));
appendStringInfoString(&ds, buf);
break;
default:
--- 2169,2189 ----
switch (tok)
{
case T_SCALAR:
! 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 ", yylval.row->dno + 1);
appendStringInfoString(&ds, buf);
+ paramnos = bms_add_member(paramnos, yylval.row->dno);
break;
case T_RECORD:
! snprintf(buf, sizeof(buf), " $%d ", yylval.rec->dno + 1);
appendStringInfoString(&ds, buf);
+ paramnos = bms_add_member(paramnos, yylval.rec->dno);
break;
default:
***************
*** 2240,2252 ****
}
}
! expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
! expr->nparams = nparams;
! while(nparams-- > 0)
! expr->params[nparams] = params[nparams];
pfree(ds.data);
check_sql_expr(expr->query);
--- 2192,2202 ----
}
}
! expr = palloc0(sizeof(PLpgSQL_expr));
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
! expr->paramnos = paramnos;
pfree(ds.data);
check_sql_expr(expr->query);
***************
*** 2600,2608 ****
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;
--- 2550,2555 ----
***************
*** 3095,3118 ****
{
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;
/* copy expression query without SELECT keyword (expr->query + 7) */
Assert(strncmp(expr->query, "SELECT ", 7) == 0);
--- 3042,3051 ----
{
PLpgSQL_case_when *cwt = (PLpgSQL_case_when *) lfirst(l);
PLpgSQL_expr *expr = cwt->expr;
StringInfoData ds;
/* Must add the CASE variable as an extra param to expression */
! 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,3136 ****
/* And do the string hacking */
initStringInfo(&ds);
! appendStringInfo(&ds, "SELECT $%d IN(%s)",
! nparams + 1,
! expr->query + 7);
- new_expr->query = pstrdup(ds.data);
-
- pfree(ds.data);
pfree(expr->query);
! pfree(expr);
! cwt->expr = new_expr;
}
}
--- 3053,3066 ----
/* And do the string hacking */
initStringInfo(&ds);
! appendStringInfo(&ds, "SELECT $%d IN (%s)",
! t_varno + 1,
! expr->query + 7);
pfree(expr->query);
! expr->query = pstrdup(ds.data);
! pfree(ds.data);
}
}
Index: src/pl/plpgsql/src/pl_comp.c
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v
retrieving revision 1.139
diff -c -r1.139 pl_comp.c
*** src/pl/plpgsql/src/pl_comp.c 22 Sep 2009 23:43:42 -0000 1.139
--- src/pl/plpgsql/src/pl_comp.c 4 Nov 2009 16:05:29 -0000
***************
*** 624,643 ****
true);
function->tg_table_name_varno = var->dno;
!
! /* add 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;
break;
default:
--- 624,647 ----
true);
function->tg_table_name_varno = var->dno;
! /* 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:
***************
*** 932,965 ****
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
*/
nse = plpgsql_ns_lookup(cp[0], NULL, NULL, NULL);
--- 936,941 ----
Index: src/pl/plpgsql/src/pl_exec.c
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v
retrieving revision 1.248
diff -c -r1.248 pl_exec.c
*** src/pl/plpgsql/src/pl_exec.c 6 Aug 2009 20:44:31 -0000 1.248
--- src/pl/plpgsql/src/pl_exec.c 4 Nov 2009 16:05:29 -0000
***************
*** 26,31 ****
--- 26,32 ----
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
+ #include "parser/parse_node.h"
#include "parser/scansup.h"
#include "storage/proc.h"
#include "tcop/tcopprot.h"
***************
*** 154,163 ****
Datum value, Oid valtype, bool *isNull);
static void exec_eval_datum(PLpgSQL_execstate *estate,
PLpgSQL_datum *datum,
- Oid expectedtypeid,
Oid *typeid,
Datum *value,
bool *isnull);
static int exec_eval_integer(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr,
bool *isNull);
--- 155,165 ----
Datum value, Oid valtype, bool *isNull);
static void exec_eval_datum(PLpgSQL_execstate *estate,
PLpgSQL_datum *datum,
Oid *typeid,
Datum *value,
bool *isnull);
+ static Oid exec_get_datum_type(PLpgSQL_execstate *estate,
+ PLpgSQL_datum *datum);
static int exec_eval_integer(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr,
bool *isNull);
***************
*** 172,179 ****
PLpgSQL_expr *expr, long maxtuples, Portal *portalP);
static int exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
Portal portal, bool prefetch_ok);
! static void eval_expr_params(PLpgSQL_execstate *estate,
! PLpgSQL_expr *expr, Datum **p_values, char **p_nulls);
static void exec_move_row(PLpgSQL_execstate *estate,
PLpgSQL_rec *rec,
PLpgSQL_row *row,
--- 174,184 ----
PLpgSQL_expr *expr, long maxtuples, Portal *portalP);
static int exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
Portal portal, bool prefetch_ok);
! static ParamListInfo setup_param_list(PLpgSQL_execstate *estate,
! PLpgSQL_expr *expr);
! static void plpgsql_parser_setup(ParseState *pstate, PLpgSQL_expr *expr);
! static Node *plpgsql_param_ref(ParseState *pstate, ParamRef *pref);
! static void plpgsql_param_fetch(ParamListInfo params, int paramid);
static void exec_move_row(PLpgSQL_execstate *estate,
PLpgSQL_rec *rec,
PLpgSQL_row *row,
***************
*** 514,525 ****
--- 519,535 ----
/*
* Put the OLD and NEW tuples into record variables
+ *
+ * We make the tupdescs available in both records even though only one
+ * may have a value. This allows parsing of record references to succeed.
*/
rec_new = (PLpgSQL_rec *) (estate.datums[func->new_varno]);
rec_new->freetup = false;
+ rec_new->tupdesc = trigdata->tg_relation->rd_att;
rec_new->freetupdesc = false;
rec_old = (PLpgSQL_rec *) (estate.datums[func->old_varno]);
rec_old->freetup = false;
+ rec_old->tupdesc = trigdata->tg_relation->rd_att;
rec_old->freetupdesc = false;
if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
***************
*** 528,557 ****
* Per-statement triggers don't use OLD/NEW variables
*/
rec_new->tup = NULL;
- rec_new->tupdesc = NULL;
rec_old->tup = NULL;
- rec_old->tupdesc = NULL;
}
else if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
{
rec_new->tup = trigdata->tg_trigtuple;
- rec_new->tupdesc = trigdata->tg_relation->rd_att;
rec_old->tup = NULL;
- rec_old->tupdesc = NULL;
}
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
{
rec_new->tup = trigdata->tg_newtuple;
- rec_new->tupdesc = trigdata->tg_relation->rd_att;
rec_old->tup = trigdata->tg_trigtuple;
- rec_old->tupdesc = trigdata->tg_relation->rd_att;
}
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
{
rec_new->tup = NULL;
- rec_new->tupdesc = NULL;
rec_old->tup = trigdata->tg_trigtuple;
- rec_old->tupdesc = trigdata->tg_relation->rd_att;
}
else
elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, or UPDATE");
--- 538,559 ----
***************
*** 631,649 ****
var->isnull = false;
var->freeval = false;
! /*
! * Store the trigger argument values into the special execution state
! * variables
! */
! estate.err_text = gettext_noop("while storing call arguments into local variables");
! estate.trig_nargs = trigdata->tg_trigger->tgnargs;
! if (estate.trig_nargs == 0)
! estate.trig_argv = NULL;
else
{
! estate.trig_argv = palloc(sizeof(Datum) * estate.trig_nargs);
! for (i = 0; i < trigdata->tg_trigger->tgnargs; i++)
! estate.trig_argv[i] = CStringGetTextDatum(trigdata->tg_trigger->tgargs[i]);
}
estate.err_text = gettext_noop("during function entry");
--- 633,668 ----
var->isnull = false;
var->freeval = false;
! var = (PLpgSQL_var *) (estate.datums[func->tg_argv_varno]);
! if (trigdata->tg_trigger->tgnargs > 0)
! {
! /*
! * For historical reasons, tg_argv[] subscripts start at zero not one.
! * So we can't use construct_array().
! */
! int nelems = trigdata->tg_trigger->tgnargs;
! Datum *elems;
! int dims[1];
! int lbs[1];
!
! elems = palloc(sizeof(Datum) * nelems);
! for (i = 0; i < nelems; i++)
! elems[i] = CStringGetTextDatum(trigdata->tg_trigger->tgargs[i]);
! dims[0] = nelems;
! lbs[0] = 0;
!
! var->value = PointerGetDatum(construct_md_array(elems, NULL,
! 1, dims, lbs,
! TEXTOID,
! -1, false, 'i'));
! var->isnull = false;
! var->freeval = true;
! }
else
{
! var->value = (Datum) 0;
! var->isnull = true;
! var->freeval = false;
}
estate.err_text = gettext_noop("during function entry");
***************
*** 756,765 ****
{
PLpgSQL_execstate *estate = (PLpgSQL_execstate *) arg;
- /* safety check, shouldn't happen */
- if (estate->err_func == NULL)
- return;
-
/* if we are doing RAISE, don't report its location */
if (estate->err_text == raise_skip_msg)
return;
--- 775,780 ----
***************
*** 784,790 ****
* local variable initialization"
*/
errcontext("PL/pgSQL function \"%s\" line %d %s",
! estate->err_func->fn_name,
estate->err_stmt->lineno,
_(estate->err_text));
}
--- 799,805 ----
* local variable initialization"
*/
errcontext("PL/pgSQL function \"%s\" line %d %s",
! estate->func->fn_name,
estate->err_stmt->lineno,
_(estate->err_text));
}
***************
*** 795,801 ****
* arguments into local variables"
*/
errcontext("PL/pgSQL function \"%s\" %s",
! estate->err_func->fn_name,
_(estate->err_text));
}
}
--- 810,816 ----
* arguments into local variables"
*/
errcontext("PL/pgSQL function \"%s\" %s",
! estate->func->fn_name,
_(estate->err_text));
}
}
***************
*** 803,815 ****
{
/* translator: last %s is a plpgsql statement type name */
errcontext("PL/pgSQL function \"%s\" line %d at %s",
! estate->err_func->fn_name,
estate->err_stmt->lineno,
plpgsql_stmt_typename(estate->err_stmt));
}
else
errcontext("PL/pgSQL function \"%s\"",
! estate->err_func->fn_name);
}
--- 818,830 ----
{
/* translator: last %s is a plpgsql statement type name */
errcontext("PL/pgSQL function \"%s\" line %d at %s",
! estate->func->fn_name,
estate->err_stmt->lineno,
plpgsql_stmt_typename(estate->err_stmt));
}
else
errcontext("PL/pgSQL function \"%s\"",
! estate->func->fn_name);
}
***************
*** 856,862 ****
case PLPGSQL_DTYPE_ROW:
case PLPGSQL_DTYPE_RECFIELD:
case PLPGSQL_DTYPE_ARRAYELEM:
- case PLPGSQL_DTYPE_TRIGARG:
/*
* These datum records are read-only at runtime, so no need to
--- 871,876 ----
***************
*** 977,986 ****
if (rec->freetup)
{
heap_freetuple(rec->tup);
- FreeTupleDesc(rec->tupdesc);
rec->freetup = false;
}
!
rec->tup = NULL;
rec->tupdesc = NULL;
}
--- 991,1003 ----
if (rec->freetup)
{
heap_freetuple(rec->tup);
rec->freetup = false;
}
! if (rec->freetupdesc)
! {
! FreeTupleDesc(rec->tupdesc);
! rec->freetupdesc = false;
! }
rec->tup = NULL;
rec->tupdesc = NULL;
}
***************
*** 1885,1894 ****
PLpgSQL_var *curvar;
char *curname = NULL;
PLpgSQL_expr *query;
Portal portal;
int rc;
- Datum *values;
- char *nulls;
/* ----------
* Get the cursor variable and if it has an assigned name, check
--- 1902,1910 ----
PLpgSQL_var *curvar;
char *curname = NULL;
PLpgSQL_expr *query;
+ ParamListInfo paramLI;
Portal portal;
int rc;
/* ----------
* Get the cursor variable and if it has an assigned name, check
***************
*** 1954,1972 ****
exec_prepare_plan(estate, query, curvar->cursor_options);
/*
! * Now build up the values and nulls arguments for SPI_execute_plan()
*/
! eval_expr_params(estate, query, &values, &nulls);
/*
! * Open the cursor
*/
! portal = SPI_cursor_open(curname, query->plan, values, nulls,
! estate->readonly_func);
if (portal == NULL)
elog(ERROR, "could not open cursor: %s",
SPI_result_code_string(SPI_result));
/*
* If cursor variable was NULL, store the generated portal name in it
*/
--- 1970,1994 ----
exec_prepare_plan(estate, query, curvar->cursor_options);
/*
! * Set up ParamListInfo (note this is only carrying a hook function,
! * not any actual data values, at this point)
*/
! paramLI = setup_param_list(estate, query);
/*
! * Open the cursor (the paramlist will get copied into the portal)
*/
! portal = SPI_cursor_open_with_paramlist(curname, query->plan,
! paramLI,
! estate->readonly_func);
if (portal == NULL)
elog(ERROR, "could not open cursor: %s",
SPI_result_code_string(SPI_result));
+ /* don't need paramlist any more */
+ if (paramLI)
+ pfree(paramLI);
+
/*
* If cursor variable was NULL, store the generated portal name in it
*/
***************
*** 1992,1999 ****
curvar->isnull = true;
}
- pfree(values);
- pfree(nulls);
if (curname)
pfree(curname);
--- 2014,2019 ----
***************
*** 2599,2604 ****
--- 2619,2629 ----
PLpgSQL_function *func,
ReturnSetInfo *rsi)
{
+ /* this link will be restored at exit from plpgsql_call_handler */
+ func->cur_estate = estate;
+
+ estate->func = func;
+
estate->retval = (Datum) 0;
estate->retisnull = true;
estate->rettype = InvalidOid;
***************
*** 2616,2624 ****
estate->tuple_store_cxt = NULL;
estate->rsi = rsi;
- estate->trig_nargs = 0;
- estate->trig_argv = NULL;
-
estate->found_varno = func->found_varno;
estate->ndatums = func->ndatums;
estate->datums = palloc(sizeof(PLpgSQL_datum *) * estate->ndatums);
--- 2641,2646 ----
***************
*** 2627,2637 ****
estate->eval_tuptable = NULL;
estate->eval_processed = 0;
estate->eval_lastoid = InvalidOid;
- estate->err_func = func;
estate->err_stmt = NULL;
estate->err_text = NULL;
/*
* Create an EState and ExprContext for evaluation of simple expressions.
*/
--- 2649,2662 ----
estate->eval_tuptable = NULL;
estate->eval_processed = 0;
estate->eval_lastoid = InvalidOid;
+ estate->eval_econtext = NULL;
+ estate->cur_expr = NULL;
estate->err_stmt = NULL;
estate->err_text = NULL;
+ estate->plugin_info = NULL;
+
/*
* Create an EState and ExprContext for evaluation of simple expressions.
*/
***************
*** 2682,2712 ****
exec_prepare_plan(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr, int cursorOptions)
{
- int i;
SPIPlanPtr plan;
- Oid *argtypes;
/*
! * We need a temporary argtypes array to load with data. (The finished
! * plan structure will contain a copy of it.)
*/
! argtypes = (Oid *) palloc(expr->nparams * sizeof(Oid));
!
! for (i = 0; i < expr->nparams; i++)
! {
! Datum paramval;
! bool paramisnull;
!
! exec_eval_datum(estate, estate->datums[expr->params[i]],
! InvalidOid,
! &argtypes[i], ¶mval, ¶misnull);
! }
/*
* Generate and save the plan
*/
! plan = SPI_prepare_cursor(expr->query, expr->nparams, argtypes,
! cursorOptions);
if (plan == NULL)
{
/* Some SPI errors deserve specific error messages */
--- 2707,2727 ----
exec_prepare_plan(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr, int cursorOptions)
{
SPIPlanPtr plan;
/*
! * The grammar can't conveniently set expr->func while building the
! * parse tree, so make sure it's set before parser hooks need it.
*/
! expr->func = estate->func;
/*
* Generate and save the plan
*/
! plan = SPI_prepare_with_hook(expr->query,
! (ParserSetupHook) plpgsql_parser_setup,
! (void *) expr,
! cursorOptions);
if (plan == NULL)
{
/* Some SPI errors deserve specific error messages */
***************
*** 2722,2738 ****
errmsg("cannot begin/end transactions in PL/pgSQL"),
errhint("Use a BEGIN block with an EXCEPTION clause instead.")));
default:
! elog(ERROR, "SPI_prepare_cursor failed for \"%s\": %s",
expr->query, SPI_result_code_string(SPI_result));
}
}
expr->plan = SPI_saveplan(plan);
SPI_freeplan(plan);
- plan = expr->plan;
- expr->plan_argtypes = plan->argtypes;
exec_simple_check_plan(expr);
-
- pfree(argtypes);
}
--- 2737,2749 ----
errmsg("cannot begin/end transactions in PL/pgSQL"),
errhint("Use a BEGIN block with an EXCEPTION clause instead.")));
default:
! elog(ERROR, "SPI_prepare_with_hook failed for \"%s\": %s",
expr->query, SPI_result_code_string(SPI_result));
}
}
expr->plan = SPI_saveplan(plan);
SPI_freeplan(plan);
exec_simple_check_plan(expr);
}
***************
*** 2744,2751 ****
exec_stmt_execsql(PLpgSQL_execstate *estate,
PLpgSQL_stmt_execsql *stmt)
{
! Datum *values;
! char *nulls;
long tcount;
int rc;
PLpgSQL_expr *expr = stmt->sqlstmt;
--- 2755,2761 ----
exec_stmt_execsql(PLpgSQL_execstate *estate,
PLpgSQL_stmt_execsql *stmt)
{
! ParamListInfo paramLI;
long tcount;
int rc;
PLpgSQL_expr *expr = stmt->sqlstmt;
***************
*** 2782,2790 ****
}
/*
! * Now build up the values and nulls arguments for SPI_execute_plan()
*/
! eval_expr_params(estate, expr, &values, &nulls);
/*
* If we have INTO, then we only need one row back ... but if we have INTO
--- 2792,2801 ----
}
/*
! * Set up ParamListInfo (note this is only carrying a hook function,
! * not any actual data values, at this point)
*/
! paramLI = setup_param_list(estate, expr);
/*
* If we have INTO, then we only need one row back ... but if we have INTO
***************
*** 2810,2817 ****
/*
* Execute the plan
*/
! rc = SPI_execute_plan(expr->plan, values, nulls,
! estate->readonly_func, tcount);
/*
* Check for error, and set FOUND if appropriate (for historical reasons
--- 2821,2828 ----
/*
* Execute the plan
*/
! rc = SPI_execute_plan_with_paramlist(expr->plan, paramLI,
! estate->readonly_func, tcount);
/*
* Check for error, and set FOUND if appropriate (for historical reasons
***************
*** 2919,2926 ****
(rc == SPI_OK_SELECT) ? errhint("If you want to discard the results of a SELECT, use PERFORM instead.") : 0));
}
! pfree(values);
! pfree(nulls);
return PLPGSQL_RC_OK;
}
--- 2930,2937 ----
(rc == SPI_OK_SELECT) ? errhint("If you want to discard the results of a SELECT, use PERFORM instead.") : 0));
}
! if (paramLI)
! pfree(paramLI);
return PLPGSQL_RC_OK;
}
***************
*** 3142,3149 ****
char *curname = NULL;
PLpgSQL_expr *query;
Portal portal;
! Datum *values;
! char *nulls;
bool isnull;
/* ----------
--- 3153,3159 ----
char *curname = NULL;
PLpgSQL_expr *query;
Portal portal;
! ParamListInfo paramLI;
bool isnull;
/* ----------
***************
*** 3280,3294 ****
}
/*
! * Now build up the values and nulls arguments for SPI_execute_plan()
*/
! eval_expr_params(estate, query, &values, &nulls);
/*
* Open the cursor
*/
! portal = SPI_cursor_open(curname, query->plan, values, nulls,
! estate->readonly_func);
if (portal == NULL)
elog(ERROR, "could not open cursor: %s",
SPI_result_code_string(SPI_result));
--- 3290,3306 ----
}
/*
! * Set up ParamListInfo (note this is only carrying a hook function,
! * not any actual data values, at this point)
*/
! paramLI = setup_param_list(estate, query);
/*
* Open the cursor
*/
! portal = SPI_cursor_open_with_paramlist(curname, query->plan,
! paramLI,
! estate->readonly_func);
if (portal == NULL)
elog(ERROR, "could not open cursor: %s",
SPI_result_code_string(SPI_result));
***************
*** 3299,3308 ****
if (curname == NULL)
assign_text_var(curvar, portal->name);
- pfree(values);
- pfree(nulls);
if (curname)
pfree(curname);
return PLPGSQL_RC_OK;
}
--- 3311,3320 ----
if (curname == NULL)
assign_text_var(curvar, portal->name);
if (curname)
pfree(curname);
+ if (paramLI)
+ pfree(paramLI);
return PLPGSQL_RC_OK;
}
***************
*** 3755,3761 ****
} while (target->dtype == PLPGSQL_DTYPE_ARRAYELEM);
/* Fetch current value of array datum */
! exec_eval_datum(estate, target, InvalidOid,
&arraytypeid, &oldarraydatum, &oldarrayisnull);
arrayelemtypeid = get_element_type(arraytypeid);
--- 3767,3773 ----
} while (target->dtype == PLPGSQL_DTYPE_ARRAYELEM);
/* Fetch current value of array datum */
! exec_eval_datum(estate, target,
&arraytypeid, &oldarraydatum, &oldarrayisnull);
arrayelemtypeid = get_element_type(arraytypeid);
***************
*** 3860,3867 ****
*
* The type oid, value in Datum format, and null flag are returned.
*
- * If expectedtypeid isn't InvalidOid, it is checked against the actual type.
- *
* At present this doesn't handle PLpgSQL_expr or PLpgSQL_arrayelem datums.
*
* NOTE: caller must not modify the returned value, since it points right
--- 3872,3877 ----
***************
*** 3872,3878 ****
static void
exec_eval_datum(PLpgSQL_execstate *estate,
PLpgSQL_datum *datum,
- Oid expectedtypeid,
Oid *typeid,
Datum *value,
bool *isnull)
--- 3882,3887 ----
***************
*** 3888,3898 ****
*typeid = var->datatype->typoid;
*value = var->value;
*isnull = var->isnull;
- if (expectedtypeid != InvalidOid && expectedtypeid != *typeid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("type of \"%s\" does not match that when preparing the plan",
- var->refname)));
break;
}
--- 3897,3902 ----
***************
*** 3913,3923 ****
*typeid = row->rowtupdesc->tdtypeid;
*value = HeapTupleGetDatum(tup);
*isnull = false;
- if (expectedtypeid != InvalidOid && expectedtypeid != *typeid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("type of \"%s\" does not match that when preparing the plan",
- row->refname)));
break;
}
--- 3917,3922 ----
***************
*** 3950,3960 ****
*typeid = rec->tupdesc->tdtypeid;
*value = HeapTupleGetDatum(&worktup);
*isnull = false;
- if (expectedtypeid != InvalidOid && expectedtypeid != *typeid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("type of \"%s\" does not match that when preparing the plan",
- rec->refname)));
break;
}
--- 3949,3954 ----
***************
*** 3979,4020 ****
rec->refname, recfield->fieldname)));
*typeid = SPI_gettypeid(rec->tupdesc, fno);
*value = SPI_getbinval(rec->tup, rec->tupdesc, fno, isnull);
! if (expectedtypeid != InvalidOid && expectedtypeid != *typeid)
ereport(ERROR,
! (errcode(ERRCODE_DATATYPE_MISMATCH),
! errmsg("type of \"%s.%s\" does not match that when preparing the plan",
! rec->refname, recfield->fieldname)));
break;
}
! case PLPGSQL_DTYPE_TRIGARG:
{
! PLpgSQL_trigarg *trigarg = (PLpgSQL_trigarg *) datum;
! int tgargno;
! *typeid = TEXTOID;
! tgargno = exec_eval_integer(estate, trigarg->argnum, isnull);
! if (*isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
! {
! *value = (Datum) 0;
! *isnull = true;
! }
! else
! {
! *value = estate->trig_argv[tgargno];
! *isnull = false;
! }
! if (expectedtypeid != InvalidOid && expectedtypeid != *typeid)
ereport(ERROR,
! (errcode(ERRCODE_DATATYPE_MISMATCH),
! errmsg("type of tg_argv[%d] does not match that when preparing the plan",
! tgargno)));
break;
}
default:
elog(ERROR, "unrecognized dtype: %d", datum->dtype);
}
}
/* ----------
--- 3973,4066 ----
rec->refname, recfield->fieldname)));
*typeid = SPI_gettypeid(rec->tupdesc, fno);
*value = SPI_getbinval(rec->tup, rec->tupdesc, fno, isnull);
! break;
! }
!
! default:
! elog(ERROR, "unrecognized dtype: %d", datum->dtype);
! }
! }
!
! /*
! * exec_get_datum_type Get datatype of a PLpgSQL_datum
! *
! * This is the same logic as in exec_eval_datum, except that it can handle
! * some cases where exec_eval_datum has to fail.
! */
! static Oid
! exec_get_datum_type(PLpgSQL_execstate *estate,
! PLpgSQL_datum *datum)
! {
! Oid typeid;
!
! switch (datum->dtype)
! {
! case PLPGSQL_DTYPE_VAR:
! {
! PLpgSQL_var *var = (PLpgSQL_var *) datum;
!
! typeid = var->datatype->typoid;
! break;
! }
!
! case PLPGSQL_DTYPE_ROW:
! {
! PLpgSQL_row *row = (PLpgSQL_row *) datum;
!
! if (!row->rowtupdesc) /* should not happen */
! elog(ERROR, "row variable has no tupdesc");
! /* Make sure we have a valid type/typmod setting */
! BlessTupleDesc(row->rowtupdesc);
! typeid = row->rowtupdesc->tdtypeid;
! break;
! }
!
! case PLPGSQL_DTYPE_REC:
! {
! PLpgSQL_rec *rec = (PLpgSQL_rec *) datum;
!
! if (rec->tupdesc == NULL)
ereport(ERROR,
! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
! errmsg("record \"%s\" is not assigned yet",
! rec->refname),
! errdetail("The tuple structure of a not-yet-assigned record is indeterminate.")));
! /* Make sure we have a valid type/typmod setting */
! BlessTupleDesc(rec->tupdesc);
! typeid = rec->tupdesc->tdtypeid;
break;
}
! case PLPGSQL_DTYPE_RECFIELD:
{
! PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) datum;
! PLpgSQL_rec *rec;
! int fno;
! rec = (PLpgSQL_rec *) (estate->datums[recfield->recparentno]);
! if (rec->tupdesc == NULL)
ereport(ERROR,
! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
! errmsg("record \"%s\" is not assigned yet",
! rec->refname),
! errdetail("The tuple structure of a not-yet-assigned record is indeterminate.")));
! fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
! if (fno == SPI_ERROR_NOATTRIBUTE)
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_COLUMN),
! errmsg("record \"%s\" has no field \"%s\"",
! rec->refname, recfield->fieldname)));
! typeid = SPI_gettypeid(rec->tupdesc, fno);
break;
}
default:
elog(ERROR, "unrecognized dtype: %d", datum->dtype);
+ typeid = InvalidOid; /* keep compiler quiet */
+ break;
}
+
+ return typeid;
}
/* ----------
***************
*** 4145,4152 ****
exec_run_select(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr, long maxtuples, Portal *portalP)
{
! Datum *values;
! char *nulls;
int rc;
/*
--- 4191,4197 ----
exec_run_select(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr, long maxtuples, Portal *portalP)
{
! ParamListInfo paramLI;
int rc;
/*
***************
*** 4156,4185 ****
exec_prepare_plan(estate, expr, 0);
/*
! * Now build up the values and nulls arguments for SPI_execute_plan()
*/
! eval_expr_params(estate, expr, &values, &nulls);
/*
* If a portal was requested, put the query into the portal
*/
if (portalP != NULL)
{
! *portalP = SPI_cursor_open(NULL, expr->plan, values, nulls,
! estate->readonly_func);
if (*portalP == NULL)
elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
expr->query, SPI_result_code_string(SPI_result));
! pfree(values);
! pfree(nulls);
return SPI_OK_CURSOR;
}
/*
* Execute the query
*/
! rc = SPI_execute_plan(expr->plan, values, nulls,
! estate->readonly_func, maxtuples);
if (rc != SPI_OK_SELECT)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
--- 4201,4232 ----
exec_prepare_plan(estate, expr, 0);
/*
! * Set up ParamListInfo (note this is only carrying a hook function,
! * not any actual data values, at this point)
*/
! paramLI = setup_param_list(estate, expr);
/*
* If a portal was requested, put the query into the portal
*/
if (portalP != NULL)
{
! *portalP = SPI_cursor_open_with_paramlist(NULL, expr->plan,
! paramLI,
! estate->readonly_func);
if (*portalP == NULL)
elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
expr->query, SPI_result_code_string(SPI_result));
! if (paramLI)
! pfree(paramLI);
return SPI_OK_CURSOR;
}
/*
* Execute the query
*/
! rc = SPI_execute_plan_with_paramlist(expr->plan, paramLI,
! estate->readonly_func, maxtuples);
if (rc != SPI_OK_SELECT)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
***************
*** 4191,4198 ****
estate->eval_processed = SPI_processed;
estate->eval_lastoid = SPI_lastoid;
! pfree(values);
! pfree(nulls);
return rc;
}
--- 4238,4245 ----
estate->eval_processed = SPI_processed;
estate->eval_lastoid = SPI_lastoid;
! if (paramLI)
! pfree(paramLI);
return rc;
}
***************
*** 4378,4384 ****
CachedPlanSource *plansource;
CachedPlan *cplan;
ParamListInfo paramLI;
! int i;
MemoryContext oldcontext;
/*
--- 4425,4431 ----
CachedPlanSource *plansource;
CachedPlan *cplan;
ParamListInfo paramLI;
! PLpgSQL_expr *save_cur_expr;
MemoryContext oldcontext;
/*
***************
*** 4426,4467 ****
}
/*
- * Param list can live in econtext's temporary memory context.
- *
- * XXX think about avoiding repeated palloc's for param lists? Beware
- * however that this routine is re-entrant: exec_eval_datum() can call it
- * back for subscript evaluation, and so there can be a need to have more
- * than one active param list.
- */
- if (expr->nparams > 0)
- {
- /* sizeof(ParamListInfoData) includes the first array element */
- paramLI = (ParamListInfo)
- MemoryContextAlloc(econtext->ecxt_per_tuple_memory,
- sizeof(ParamListInfoData) +
- (expr->nparams - 1) *sizeof(ParamExternData));
- paramLI->numParams = expr->nparams;
-
- for (i = 0; i < expr->nparams; i++)
- {
- ParamExternData *prm = ¶mLI->params[i];
- PLpgSQL_datum *datum = estate->datums[expr->params[i]];
-
- prm->pflags = 0;
- exec_eval_datum(estate, datum, expr->plan_argtypes[i],
- &prm->ptype,
- &prm->value, &prm->isnull);
- }
- }
- else
- paramLI = NULL;
-
- /*
- * Now we can safely make the econtext point to the param list.
- */
- econtext->ecxt_param_list_info = paramLI;
-
- /*
* We have to do some of the things SPI_execute_plan would do, in
* particular advance the snapshot if we are in a non-read-only function.
* Without this, stable functions within the expression would fail to see
--- 4473,4478 ----
***************
*** 4477,4493 ****
}
/*
* Finally we can call the executor to evaluate the expression
*/
*result = ExecEvalExpr(expr->expr_simple_state,
econtext,
isNull,
NULL);
! MemoryContextSwitchTo(oldcontext);
if (!estate->readonly_func)
PopActiveSnapshot();
SPI_pop();
/*
--- 4488,4524 ----
}
/*
+ * Create the param list in econtext's temporary memory context.
+ * We won't need to free it explicitly, since it will go away at the
+ * next reset of that context.
+ *
+ * XXX think about avoiding repeated palloc's for param lists? It should
+ * be possible --- this routine isn't re-entrant anymore.
+ *
+ * Just for paranoia's sake, save and restore the prior value of
+ * estate->cur_expr, which setup_param_list() sets.
+ */
+ save_cur_expr = estate->cur_expr;
+
+ paramLI = setup_param_list(estate, expr);
+ econtext->ecxt_param_list_info = paramLI;
+
+ /*
* Finally we can call the executor to evaluate the expression
*/
*result = ExecEvalExpr(expr->expr_simple_state,
econtext,
isNull,
NULL);
!
! /* Assorted cleanup */
! estate->cur_expr = save_cur_expr;
if (!estate->readonly_func)
PopActiveSnapshot();
+ MemoryContextSwitchTo(oldcontext);
+
SPI_pop();
/*
***************
*** 4503,4534 ****
/*
! * Build up the values and nulls arguments for SPI_execute_plan()
*/
! static void
! eval_expr_params(PLpgSQL_execstate *estate,
! PLpgSQL_expr *expr, Datum **p_values, char **p_nulls)
{
! Datum *values;
! char *nulls;
! int i;
!
! *p_values = values = (Datum *) palloc(expr->nparams * sizeof(Datum));
! *p_nulls = nulls = (char *) palloc(expr->nparams * sizeof(char));
! for (i = 0; i < expr->nparams; i++)
{
! PLpgSQL_datum *datum = estate->datums[expr->params[i]];
! Oid paramtypeid;
! bool paramisnull;
!
! exec_eval_datum(estate, datum, expr->plan_argtypes[i],
! ¶mtypeid, &values[i], ¶misnull);
! if (paramisnull)
! nulls[i] = 'n';
! else
! nulls[i] = ' ';
}
}
--- 4534,4664 ----
/*
! * Create a ParamListInfo to pass to SPI
! *
! * The ParamListInfo array is initially all zeroes, in particular the
! * ptype values are all InvalidOid. This causes the executor to call the
! * paramFetch hook each time it wants a value. We thus evaluate only the
! * parameters actually demanded.
! *
! * The result is a locally palloc'd array that should be pfree'd after use;
! * but note it can be NULL.
*/
! static ParamListInfo
! setup_param_list(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
{
! ParamListInfo paramLI;
! /*
! * Could we re-use these arrays instead of palloc'ing a new one each
! * time? However, we'd have to zero the array each time anyway,
! * since new values might have been assigned to the variables.
! */
! if (estate->ndatums > 0)
{
! /* sizeof(ParamListInfoData) includes the first array element */
! paramLI = (ParamListInfo)
! palloc0(sizeof(ParamListInfoData) +
! (estate->ndatums - 1) * sizeof(ParamExternData));
! paramLI->paramFetch = plpgsql_param_fetch;
! paramLI->paramFetchArg = (void *) estate;
! paramLI->parserSetup = (ParserSetupHook) plpgsql_parser_setup;
! paramLI->parserSetupArg = (void *) expr;
! /* make sure this is set before parser hooks need it */
! expr->func = estate->func;
! paramLI->numParams = estate->ndatums;
!
! /*
! * Set up link to active expr where the hook functions can find it.
! * Callers must save and restore cur_expr if there is any chance
! * that they are interrupting an active use of parameters.
! */
! estate->cur_expr = expr;
}
+ else
+ paramLI = NULL;
+ return paramLI;
+ }
+
+ /*
+ * plpgsql_parser_setup set up parser hooks for dynamic parameters
+ */
+ static void
+ plpgsql_parser_setup(ParseState *pstate, PLpgSQL_expr *expr)
+ {
+ pstate->p_ref_hook_state = (void *) expr;
+ pstate->p_paramref_hook = plpgsql_param_ref;
+ /* no need to use p_coerce_param_hook */
+ }
+
+ /*
+ * plpgsql_param_ref parser callback for ParamRefs ($n symbols)
+ */
+ static Node *
+ plpgsql_param_ref(ParseState *pstate, ParamRef *pref)
+ {
+ int paramno = pref->number;
+ PLpgSQL_expr *expr = (PLpgSQL_expr *) pstate->p_ref_hook_state;
+ PLpgSQL_execstate *estate;
+ Param *param;
+
+ /* Let's just check parameter number is in range */
+ if (!bms_is_member(paramno-1, expr->paramnos))
+ return NULL;
+
+ /*
+ * We use the function's current estate to resolve parameter data types.
+ * This is really pretty bogus because there is no provision for updating
+ * plans when those types change ...
+ */
+ estate = expr->func->cur_estate;
+ Assert(paramno <= estate->ndatums);
+
+ param = makeNode(Param);
+ param->paramkind = PARAM_EXTERN;
+ param->paramid = paramno;
+ param->paramtype = exec_get_datum_type(estate,
+ estate->datums[paramno-1]);
+ param->paramtypmod = -1;
+ param->location = pref->location;
+
+ return (Node *) param;
+ }
+
+ /*
+ * plpgsql_param_fetch paramFetch callback for dynamic parameter fetch
+ */
+ static void
+ plpgsql_param_fetch(ParamListInfo params, int paramid)
+ {
+ int dno;
+ PLpgSQL_execstate *estate;
+ PLpgSQL_expr *expr;
+ PLpgSQL_datum *datum;
+ ParamExternData *prm;
+
+ /* paramid's are 1-based, but dnos are 0-based */
+ dno = paramid - 1;
+ Assert(dno >= 0 && dno < params->numParams);
+
+ /* fetch back the hook data */
+ estate = (PLpgSQL_execstate *) params->paramFetchArg;
+ expr = estate->cur_expr;
+ Assert(params->numParams == estate->ndatums);
+
+ /*
+ * Do nothing if asked for a value that's not supposed to be used by
+ * this SQL expression. This avoids unwanted evaluations when functions
+ * such as copyParamList try to materialize all the values.
+ */
+ if (!bms_is_member(dno, expr->paramnos))
+ return;
+
+ /* OK, evaluate the value and store into the appropriate paramlist slot */
+ datum = estate->datums[dno];
+ prm = ¶ms->params[dno];
+ exec_eval_datum(estate, datum,
+ &prm->ptype, &prm->value, &prm->isnull);
}
***************
*** 4710,4716 ****
elog(ERROR, "dropped rowtype entry for non-dropped column");
exec_eval_datum(estate, estate->datums[row->varnos[i]],
! InvalidOid, &fieldtypeid, &dvalues[i], &nulls[i]);
if (fieldtypeid != tupdesc->attrs[i]->atttypid)
return NULL;
}
--- 4840,4846 ----
elog(ERROR, "dropped rowtype entry for non-dropped column");
exec_eval_datum(estate, estate->datums[row->varnos[i]],
! &fieldtypeid, &dvalues[i], &nulls[i]);
if (fieldtypeid != tupdesc->attrs[i]->atttypid)
return NULL;
}
Index: src/pl/plpgsql/src/pl_funcs.c
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v
retrieving revision 1.81
diff -c -r1.81 pl_funcs.c
*** src/pl/plpgsql/src/pl_funcs.c 29 Sep 2009 20:05:29 -0000 1.81
--- src/pl/plpgsql/src/pl_funcs.c 4 Nov 2009 16:05:29 -0000
***************
*** 1148,1168 ****
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("'");
}
void
--- 1148,1154 ----
static void
dump_expr(PLpgSQL_expr *expr)
{
! printf("'%s'", expr->query);
}
void
***************
*** 1240,1250 ****
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);
}
--- 1226,1231 ----
Index: src/pl/plpgsql/src/pl_handler.c
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/pl_handler.c,v
retrieving revision 1.46
diff -c -r1.46 pl_handler.c
*** src/pl/plpgsql/src/pl_handler.c 22 Sep 2009 23:43:42 -0000 1.46
--- src/pl/plpgsql/src/pl_handler.c 4 Nov 2009 16:05:29 -0000
***************
*** 68,73 ****
--- 68,74 ----
plpgsql_call_handler(PG_FUNCTION_ARGS)
{
PLpgSQL_function *func;
+ PLpgSQL_execstate *save_cur_estate;
Datum retval;
int rc;
***************
*** 80,85 ****
--- 81,89 ----
/* 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,110 ****
}
PG_CATCH();
{
! /* Decrement use-count and propagate error */
func->use_count--;
PG_RE_THROW();
}
PG_END_TRY();
func->use_count--;
/*
* Disconnect from SPI manager
*/
--- 101,117 ----
}
PG_CATCH();
{
! /* 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
*/
Index: src/pl/plpgsql/src/plpgsql.h
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v
retrieving revision 1.117
diff -c -r1.117 plpgsql.h
*** src/pl/plpgsql/src/plpgsql.h 29 Sep 2009 20:05:29 -0000 1.117
--- src/pl/plpgsql/src/plpgsql.h 4 Nov 2009 16:05:29 -0000
***************
*** 22,27 ****
--- 22,28 ----
#include "fmgr.h"
#include "commands/trigger.h"
#include "executor/spi.h"
+ #include "nodes/bitmapset.h"
#include "utils/tuplestore.h"
/**********************************************************************
***************
*** 58,65 ****
PLPGSQL_DTYPE_REC,
PLPGSQL_DTYPE_RECFIELD,
PLPGSQL_DTYPE_ARRAYELEM,
! PLPGSQL_DTYPE_EXPR,
! PLPGSQL_DTYPE_TRIGARG
};
/* ----------
--- 59,65 ----
PLPGSQL_DTYPE_REC,
PLPGSQL_DTYPE_RECFIELD,
PLPGSQL_DTYPE_ARRAYELEM,
! PLPGSQL_DTYPE_EXPR
};
/* ----------
***************
*** 162,169 ****
/*
* PLpgSQL_datum is the common supertype for PLpgSQL_expr, PLpgSQL_var,
! * PLpgSQL_row, PLpgSQL_rec, PLpgSQL_recfield, PLpgSQL_arrayelem, and
! * PLpgSQL_trigarg
*/
typedef struct
{ /* Generic datum array item */
--- 162,168 ----
/*
* PLpgSQL_datum is the common supertype for PLpgSQL_expr, PLpgSQL_var,
! * PLpgSQL_row, PLpgSQL_rec, PLpgSQL_recfield, and PLpgSQL_arrayelem
*/
typedef struct
{ /* Generic datum array item */
***************
*** 189,195 ****
int dno;
char *query;
SPIPlanPtr plan;
! Oid *plan_argtypes;
/* 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 */
--- 188,198 ----
int dno;
char *query;
SPIPlanPtr plan;
! 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,211 ****
*/
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;
--- 205,210 ----
***************
*** 285,298 ****
typedef struct
- { /* Positional argument to trigger */
- int dtype;
- int dno;
- PLpgSQL_expr *argnum;
- } PLpgSQL_trigarg;
-
-
- typedef struct
{ /* Item in the compilers namestack */
int itemtype;
int itemno;
--- 284,289 ----
***************
*** 670,686 ****
int tg_table_name_varno;
int tg_table_schema_varno;
int tg_nargs_varno;
int ndatums;
PLpgSQL_datum **datums;
PLpgSQL_stmt_block *action;
unsigned long use_count;
} PLpgSQL_function;
! typedef struct
{ /* Runtime execution data */
Datum retval;
bool retisnull;
Oid rettype; /* type of current retval */
--- 661,682 ----
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 PLpgSQL_execstate
{ /* Runtime execution data */
+ PLpgSQL_function *func; /* function being executed */
+
Datum retval;
bool retisnull;
Oid rettype; /* type of current retval */
***************
*** 699,707 ****
MemoryContext tuple_store_cxt;
ReturnSetInfo *rsi;
- int trig_nargs;
- Datum *trig_argv;
-
int found_varno;
int ndatums;
PLpgSQL_datum **datums;
--- 695,700 ----
***************
*** 711,721 ****
uint32 eval_processed;
Oid eval_lastoid;
ExprContext *eval_econtext; /* for executing simple expressions */
/* 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;
--- 704,715 ----
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_stmt *err_stmt; /* current stmt */
const char *err_text; /* additional state info */
+
void *plugin_info; /* reserved for use by optional plugin */
} PLpgSQL_execstate;
Index: src/test/regress/expected/plpgsql.out
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/expected/plpgsql.out,v
retrieving revision 1.75
diff -c -r1.75 plpgsql.out
*** src/test/regress/expected/plpgsql.out 29 Sep 2009 20:05:29 -0000 1.75
--- src/test/regress/expected/plpgsql.out 4 Nov 2009 16:05:30 -0000
***************
*** 285,295 ****
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;
end if;
end if;
sname := ''HS.'' || trim(new.hubname);
--- 285,293 ----
if new.slotno < 1 or new.slotno > hubrec.nslots 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);
Index: src/test/regress/sql/plpgsql.sql
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/sql/plpgsql.sql,v
retrieving revision 1.63
diff -c -r1.63 plpgsql.sql
*** src/test/regress/sql/plpgsql.sql 29 Sep 2009 20:05:29 -0000 1.63
--- src/test/regress/sql/plpgsql.sql 4 Nov 2009 16:05:30 -0000
***************
*** 347,357 ****
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;
end if;
end if;
sname := ''HS.'' || trim(new.hubname);
--- 347,355 ----
if new.slotno < 1 or new.slotno > hubrec.nslots 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);