Add support for invoking parser callback hooks via SPI and in cached plans.
As proof of concept, modify plpgsql to use the hooks. plpgsql is still inserting $n symbols textually, but the "back end" of the parsing process now goes through the ParamRef hook instead of using a fixed parameter-type array, and then execution only fetches actually-referenced parameters, using a hook added to ParamListInfo. Although there's a lot left to be done in plpgsql, this already cures the "if (TG_OP = 'INSERT' and NEW.foo ...)" problem, as illustrated by the changed regression test.
This commit is contained in:
parent
48912acc08
commit
9bedd128d6
@ -1,4 +1,4 @@
|
|||||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.65 2009/08/05 19:31:50 alvherre Exp $ -->
|
<!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.66 2009/11/04 22:26:04 tgl Exp $ -->
|
||||||
|
|
||||||
<chapter id="spi">
|
<chapter id="spi">
|
||||||
<title>Server Programming Interface</title>
|
<title>Server Programming Interface</title>
|
||||||
@ -861,7 +861,7 @@ SPIPlanPtr SPI_prepare(const char * <parameter>command</parameter>, int <paramet
|
|||||||
|
|
||||||
<para>
|
<para>
|
||||||
<function>SPI_prepare</function> creates and returns an execution
|
<function>SPI_prepare</function> creates and returns an execution
|
||||||
plan for the specified command but doesn't execute the command.
|
plan for the specified command, but doesn't execute the command.
|
||||||
This function should only be called from a connected procedure.
|
This function should only be called from a connected procedure.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
@ -990,7 +990,7 @@ SPIPlanPtr SPI_prepare_cursor(const char * <parameter>command</parameter>, int <
|
|||||||
of the planner's <quote>cursor options</> parameter. This is a bitmask
|
of the planner's <quote>cursor options</> parameter. This is a bitmask
|
||||||
having the values shown in <filename>nodes/parsenodes.h</filename>
|
having the values shown in <filename>nodes/parsenodes.h</filename>
|
||||||
for the <structfield>options</> field of <structname>DeclareCursorStmt</>.
|
for the <structfield>options</> field of <structname>DeclareCursorStmt</>.
|
||||||
<function>SPI_prepare</function> always takes these options as zero.
|
<function>SPI_prepare</function> always takes the cursor options as zero.
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
@ -1061,6 +1061,94 @@ SPIPlanPtr SPI_prepare_cursor(const char * <parameter>command</parameter>, int <
|
|||||||
|
|
||||||
<!-- *********************************************** -->
|
<!-- *********************************************** -->
|
||||||
|
|
||||||
|
<refentry id="spi-spi-prepare-params">
|
||||||
|
<refmeta>
|
||||||
|
<refentrytitle>SPI_prepare_params</refentrytitle>
|
||||||
|
<manvolnum>3</manvolnum>
|
||||||
|
</refmeta>
|
||||||
|
|
||||||
|
<refnamediv>
|
||||||
|
<refname>SPI_prepare_params</refname>
|
||||||
|
<refpurpose>prepare a plan for a command, without executing it yet</refpurpose>
|
||||||
|
</refnamediv>
|
||||||
|
|
||||||
|
<indexterm><primary>SPI_prepare_params</primary></indexterm>
|
||||||
|
|
||||||
|
<refsynopsisdiv>
|
||||||
|
<synopsis>
|
||||||
|
SPIPlanPtr SPI_prepare_params(const char * <parameter>command</parameter>,
|
||||||
|
ParserSetupHook <parameter>parserSetup</parameter>,
|
||||||
|
void * <parameter>parserSetupArg</parameter>,
|
||||||
|
int <parameter>cursorOptions</parameter>)
|
||||||
|
</synopsis>
|
||||||
|
</refsynopsisdiv>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Description</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<function>SPI_prepare_params</function> creates and returns an execution
|
||||||
|
plan for the specified command, but doesn't execute the command.
|
||||||
|
This function is equivalent to <function>SPI_prepare_cursor</function>,
|
||||||
|
with the addition that the caller can specify parser hook functions
|
||||||
|
to control the parsing of external parameter references.
|
||||||
|
</para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Arguments</title>
|
||||||
|
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>const char * <parameter>command</parameter></literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
command string
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>ParserSetupHook <parameter>parserSetup</parameter></literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Parser hook setup function
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>void * <parameter>parserSetupArg</parameter></literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
passthrough argument for <parameter>parserSetup</parameter>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>int <parameter>cursorOptions</parameter></literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
integer bitmask of cursor options; zero produces default behavior
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Return Value</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<function>SPI_prepare_params</function> has the same return conventions as
|
||||||
|
<function>SPI_prepare</function>.
|
||||||
|
</para>
|
||||||
|
</refsect1>
|
||||||
|
</refentry>
|
||||||
|
|
||||||
|
<!-- *********************************************** -->
|
||||||
|
|
||||||
<refentry id="spi-spi-getargcount">
|
<refentry id="spi-spi-getargcount">
|
||||||
<refmeta>
|
<refmeta>
|
||||||
<refentrytitle>SPI_getargcount</refentrytitle>
|
<refentrytitle>SPI_getargcount</refentrytitle>
|
||||||
@ -1386,14 +1474,100 @@ int SPI_execute_plan(SPIPlanPtr <parameter>plan</parameter>, Datum * <parameter>
|
|||||||
<function>SPI_execute</function> if successful.
|
<function>SPI_execute</function> if successful.
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</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>
|
<refsect1>
|
||||||
<title>Notes</title>
|
<title>Description</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
If one of the objects (a table, function, etc.) referenced by the
|
<function>SPI_execute_plan_with_paramlist</function> executes a plan
|
||||||
prepared plan is dropped during the session then the result of
|
prepared by <function>SPI_prepare</function>.
|
||||||
<function>SPI_execute_plan</function> for this plan will be unpredictable.
|
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>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
</refentry>
|
</refentry>
|
||||||
@ -1543,7 +1717,7 @@ Portal SPI_cursor_open(const char * <parameter>name</parameter>, SPIPlanPtr <par
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The passed-in data will be copied into the cursor's portal, so it
|
The passed-in parameter data will be copied into the cursor's portal, so it
|
||||||
can be freed while the cursor still exists.
|
can be freed while the cursor still exists.
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
@ -1667,7 +1841,7 @@ Portal SPI_cursor_open_with_args(const char *<parameter>name</parameter>,
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The passed-in data will be copied into the cursor's portal, so it
|
The passed-in parameter data will be copied into the cursor's portal, so it
|
||||||
can be freed while the cursor still exists.
|
can be freed while the cursor still exists.
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
@ -1770,6 +1944,104 @@ Portal SPI_cursor_open_with_args(const char *<parameter>name</parameter>,
|
|||||||
|
|
||||||
<!-- *********************************************** -->
|
<!-- *********************************************** -->
|
||||||
|
|
||||||
|
<refentry id="spi-spi-cursor-open-with-paramlist">
|
||||||
|
<refmeta>
|
||||||
|
<refentrytitle>SPI_cursor_open_with_paramlist</refentrytitle>
|
||||||
|
<manvolnum>3</manvolnum>
|
||||||
|
</refmeta>
|
||||||
|
|
||||||
|
<refnamediv>
|
||||||
|
<refname>SPI_cursor_open_with_paramlist</refname>
|
||||||
|
<refpurpose>set up a cursor using parameters</refpurpose>
|
||||||
|
</refnamediv>
|
||||||
|
|
||||||
|
<indexterm><primary>SPI_cursor_open_with_paramlist</primary></indexterm>
|
||||||
|
|
||||||
|
<refsynopsisdiv>
|
||||||
|
<synopsis>
|
||||||
|
Portal SPI_cursor_open_with_paramlist(const char *<parameter>name</parameter>,
|
||||||
|
SPIPlanPtr <parameter>plan</parameter>,
|
||||||
|
ParamListInfo <parameter>params</parameter>,
|
||||||
|
bool <parameter>read_only</parameter>)
|
||||||
|
</synopsis>
|
||||||
|
</refsynopsisdiv>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Description</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<function>SPI_cursor_open_with_paramlist</function> sets up a cursor
|
||||||
|
(internally, a portal) that will execute a plan prepared by
|
||||||
|
<function>SPI_prepare</function>.
|
||||||
|
This function is equivalent to <function>SPI_cursor_open</function>
|
||||||
|
except that information about the parameter values to be passed to the
|
||||||
|
query is presented differently. The <literal>ParamListInfo</>
|
||||||
|
representation can be convenient for passing down values that are
|
||||||
|
already available in that format. It also supports use of dynamic
|
||||||
|
parameter sets via hook functions specified in <literal>ParamListInfo</>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The passed-in parameter data will be copied into the cursor's portal, so it
|
||||||
|
can be freed while the cursor still exists.
|
||||||
|
</para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Arguments</title>
|
||||||
|
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>const char * <parameter>name</parameter></literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
name for portal, or <symbol>NULL</symbol> to let the system
|
||||||
|
select a name
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>SPIPlanPtr <parameter>plan</parameter></literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
execution plan (returned by <function>SPI_prepare</function>)
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>ParamListInfo <parameter>params</parameter></literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
data structure containing parameter types and values; NULL if none
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>bool <parameter>read_only</parameter></literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>true</> for read-only execution
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Return Value</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Pointer to portal containing the cursor. Note there is no error
|
||||||
|
return convention; any error will be reported via <function>elog</>.
|
||||||
|
</para>
|
||||||
|
</refsect1>
|
||||||
|
</refentry>
|
||||||
|
|
||||||
|
<!-- *********************************************** -->
|
||||||
|
|
||||||
<refentry id="spi-spi-cursor-find">
|
<refentry id="spi-spi-cursor-find">
|
||||||
<refmeta>
|
<refmeta>
|
||||||
<refentrytitle>SPI_cursor_find</refentrytitle>
|
<refentrytitle>SPI_cursor_find</refentrytitle>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994-5, Regents of the University of California
|
* Portions Copyright (c) 1994-5, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.192 2009/10/12 18:10:41 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.193 2009/11/04 22:26:04 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -107,8 +107,6 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
|
|||||||
ParamListInfo params, DestReceiver *dest)
|
ParamListInfo params, DestReceiver *dest)
|
||||||
{
|
{
|
||||||
ExplainState es;
|
ExplainState es;
|
||||||
Oid *param_types;
|
|
||||||
int num_params;
|
|
||||||
TupOutputState *tstate;
|
TupOutputState *tstate;
|
||||||
List *rewritten;
|
List *rewritten;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
@ -150,9 +148,6 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
|
|||||||
opt->defname)));
|
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
|
* Run parse analysis and rewrite. Note this also acquires sufficient
|
||||||
* locks on the source table(s).
|
* locks on the source table(s).
|
||||||
@ -163,8 +158,10 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
|
|||||||
* executed repeatedly. (See also the same hack in DECLARE CURSOR and
|
* executed repeatedly. (See also the same hack in DECLARE CURSOR and
|
||||||
* PREPARE.) XXX FIXME someday.
|
* PREPARE.) XXX FIXME someday.
|
||||||
*/
|
*/
|
||||||
rewritten = pg_analyze_and_rewrite((Node *) copyObject(stmt->query),
|
rewritten = pg_analyze_and_rewrite_params((Node *) copyObject(stmt->query),
|
||||||
queryString, param_types, num_params);
|
queryString,
|
||||||
|
(ParserSetupHook) setupParserWithParamList,
|
||||||
|
params);
|
||||||
|
|
||||||
/* emit opening boilerplate */
|
/* emit opening boilerplate */
|
||||||
ExplainBeginOutput(&es);
|
ExplainBeginOutput(&es);
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* Copyright (c) 2002-2009, PostgreSQL Global Development Group
|
* Copyright (c) 2002-2009, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.99 2009/08/10 05:46:50 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.100 2009/11/04 22:26:05 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -379,6 +379,11 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
|
|||||||
paramLI = (ParamListInfo)
|
paramLI = (ParamListInfo)
|
||||||
palloc(sizeof(ParamListInfoData) +
|
palloc(sizeof(ParamListInfoData) +
|
||||||
(num_params - 1) *sizeof(ParamExternData));
|
(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;
|
paramLI->numParams = num_params;
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/execCurrent.c,v 1.12 2009/10/26 02:26:29 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/execCurrent.c,v 1.13 2009/11/04 22:26:05 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -217,9 +217,21 @@ fetch_param_value(ExprContext *econtext, int paramId)
|
|||||||
{
|
{
|
||||||
ParamExternData *prm = ¶mInfo->params[paramId - 1];
|
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)
|
if (OidIsValid(prm->ptype) && !prm->isnull)
|
||||||
{
|
{
|
||||||
Assert(prm->ptype == REFCURSOROID);
|
/* safety check in case hook did something unexpected */
|
||||||
|
if (prm->ptype != REFCURSOROID)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
|
||||||
|
paramId,
|
||||||
|
format_type_be(prm->ptype),
|
||||||
|
format_type_be(REFCURSOROID))));
|
||||||
|
|
||||||
/* We know that refcursor uses text's I/O routines */
|
/* We know that refcursor uses text's I/O routines */
|
||||||
return TextDatumGetCString(prm->value);
|
return TextDatumGetCString(prm->value);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.253 2009/10/26 02:26:29 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.254 2009/11/04 22:26:05 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -882,9 +882,21 @@ ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
|
|||||||
{
|
{
|
||||||
ParamExternData *prm = ¶mInfo->params[thisParamId - 1];
|
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))
|
if (OidIsValid(prm->ptype))
|
||||||
{
|
{
|
||||||
Assert(prm->ptype == expression->paramtype);
|
/* safety check in case hook did something unexpected */
|
||||||
|
if (prm->ptype != expression->paramtype)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
|
||||||
|
thisParamId,
|
||||||
|
format_type_be(prm->ptype),
|
||||||
|
format_type_be(expression->paramtype))));
|
||||||
|
|
||||||
*isNull = prm->isnull;
|
*isNull = prm->isnull;
|
||||||
return prm->value;
|
return prm->value;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.135 2009/06/11 17:25:38 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.136 2009/11/04 22:26:05 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -526,6 +526,11 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
|
|||||||
/* sizeof(ParamListInfoData) includes the first array element */
|
/* sizeof(ParamListInfoData) includes the first array element */
|
||||||
paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
|
paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
|
||||||
(nargs - 1) *sizeof(ParamExternData));
|
(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;
|
paramLI->numParams = nargs;
|
||||||
fcache->paramLI = paramLI;
|
fcache->paramLI = paramLI;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.210 2009/10/10 01:43:47 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.211 2009/11/04 22:26:06 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -45,8 +45,7 @@ static int _SPI_connected = -1;
|
|||||||
static int _SPI_curid = -1;
|
static int _SPI_curid = -1;
|
||||||
|
|
||||||
static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
|
static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
|
||||||
Datum *Values, const char *Nulls,
|
ParamListInfo paramLI, bool read_only);
|
||||||
bool read_only, int pflags);
|
|
||||||
|
|
||||||
static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan,
|
static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan,
|
||||||
ParamListInfo boundParams);
|
ParamListInfo boundParams);
|
||||||
@ -407,6 +406,28 @@ SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, long tcount)
|
|||||||
return SPI_execute_plan(plan, Values, Nulls, false, tcount);
|
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
|
* SPI_execute_snapshot -- identical to SPI_execute_plan, except that we allow
|
||||||
* the caller to specify exactly which snapshots to use, which will be
|
* the caller to specify exactly which snapshots to use, which will be
|
||||||
@ -483,6 +504,8 @@ SPI_execute_with_args(const char *src,
|
|||||||
plan.cursor_options = 0;
|
plan.cursor_options = 0;
|
||||||
plan.nargs = nargs;
|
plan.nargs = nargs;
|
||||||
plan.argtypes = argtypes;
|
plan.argtypes = argtypes;
|
||||||
|
plan.parserSetup = NULL;
|
||||||
|
plan.parserSetupArg = NULL;
|
||||||
|
|
||||||
paramLI = _SPI_convert_params(nargs, argtypes,
|
paramLI = _SPI_convert_params(nargs, argtypes,
|
||||||
Values, Nulls,
|
Values, Nulls,
|
||||||
@ -528,6 +551,45 @@ SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
|
|||||||
plan.cursor_options = cursorOptions;
|
plan.cursor_options = cursorOptions;
|
||||||
plan.nargs = nargs;
|
plan.nargs = nargs;
|
||||||
plan.argtypes = argtypes;
|
plan.argtypes = argtypes;
|
||||||
|
plan.parserSetup = NULL;
|
||||||
|
plan.parserSetupArg = NULL;
|
||||||
|
|
||||||
|
_SPI_prepare_plan(src, &plan, NULL);
|
||||||
|
|
||||||
|
/* copy plan to procedure context */
|
||||||
|
result = _SPI_copy_plan(&plan, _SPI_current->procCxt);
|
||||||
|
|
||||||
|
_SPI_end_call(true);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPIPlanPtr
|
||||||
|
SPI_prepare_params(const char *src,
|
||||||
|
ParserSetupHook parserSetup,
|
||||||
|
void *parserSetupArg,
|
||||||
|
int cursorOptions)
|
||||||
|
{
|
||||||
|
_SPI_plan plan;
|
||||||
|
SPIPlanPtr result;
|
||||||
|
|
||||||
|
if (src == NULL)
|
||||||
|
{
|
||||||
|
SPI_result = SPI_ERROR_ARGUMENT;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPI_result = _SPI_begin_call(true);
|
||||||
|
if (SPI_result < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
memset(&plan, 0, sizeof(_SPI_plan));
|
||||||
|
plan.magic = _SPI_PLAN_MAGIC;
|
||||||
|
plan.cursor_options = cursorOptions;
|
||||||
|
plan.nargs = 0;
|
||||||
|
plan.argtypes = NULL;
|
||||||
|
plan.parserSetup = parserSetup;
|
||||||
|
plan.parserSetupArg = parserSetupArg;
|
||||||
|
|
||||||
_SPI_prepare_plan(src, &plan, NULL);
|
_SPI_prepare_plan(src, &plan, NULL);
|
||||||
|
|
||||||
@ -954,8 +1016,21 @@ SPI_cursor_open(const char *name, SPIPlanPtr plan,
|
|||||||
Datum *Values, const char *Nulls,
|
Datum *Values, const char *Nulls,
|
||||||
bool read_only)
|
bool read_only)
|
||||||
{
|
{
|
||||||
return SPI_cursor_open_internal(name, plan, Values, Nulls,
|
Portal portal;
|
||||||
read_only, 0);
|
ParamListInfo paramLI;
|
||||||
|
|
||||||
|
/* build transient ParamListInfo in caller's context */
|
||||||
|
paramLI = _SPI_convert_params(plan->nargs, plan->argtypes,
|
||||||
|
Values, Nulls,
|
||||||
|
0);
|
||||||
|
|
||||||
|
portal = SPI_cursor_open_internal(name, plan, paramLI, read_only);
|
||||||
|
|
||||||
|
/* done with the transient ParamListInfo */
|
||||||
|
if (paramLI)
|
||||||
|
pfree(paramLI);
|
||||||
|
|
||||||
|
return portal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -992,7 +1067,10 @@ SPI_cursor_open_with_args(const char *name,
|
|||||||
plan.cursor_options = cursorOptions;
|
plan.cursor_options = cursorOptions;
|
||||||
plan.nargs = nargs;
|
plan.nargs = nargs;
|
||||||
plan.argtypes = argtypes;
|
plan.argtypes = argtypes;
|
||||||
|
plan.parserSetup = NULL;
|
||||||
|
plan.parserSetupArg = NULL;
|
||||||
|
|
||||||
|
/* build transient ParamListInfo in executor context */
|
||||||
paramLI = _SPI_convert_params(nargs, argtypes,
|
paramLI = _SPI_convert_params(nargs, argtypes,
|
||||||
Values, Nulls,
|
Values, Nulls,
|
||||||
PARAM_FLAG_CONST);
|
PARAM_FLAG_CONST);
|
||||||
@ -1007,8 +1085,7 @@ SPI_cursor_open_with_args(const char *name,
|
|||||||
/* SPI_cursor_open_internal must be called in procedure memory context */
|
/* SPI_cursor_open_internal must be called in procedure memory context */
|
||||||
_SPI_procmem();
|
_SPI_procmem();
|
||||||
|
|
||||||
result = SPI_cursor_open_internal(name, &plan, Values, Nulls,
|
result = SPI_cursor_open_internal(name, &plan, paramLI, read_only);
|
||||||
read_only, PARAM_FLAG_CONST);
|
|
||||||
|
|
||||||
/* And clean up */
|
/* And clean up */
|
||||||
_SPI_curid++;
|
_SPI_curid++;
|
||||||
@ -1018,25 +1095,36 @@ SPI_cursor_open_with_args(const char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SPI_cursor_open_with_paramlist()
|
||||||
|
*
|
||||||
|
* Same as SPI_cursor_open except that parameters (if any) are passed
|
||||||
|
* as a ParamListInfo, which supports dynamic parameter set determination
|
||||||
|
*/
|
||||||
|
Portal
|
||||||
|
SPI_cursor_open_with_paramlist(const char *name, SPIPlanPtr plan,
|
||||||
|
ParamListInfo params, bool read_only)
|
||||||
|
{
|
||||||
|
return SPI_cursor_open_internal(name, plan, params, read_only);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SPI_cursor_open_internal()
|
* SPI_cursor_open_internal()
|
||||||
*
|
*
|
||||||
* Common code for SPI_cursor_open and SPI_cursor_open_with_args
|
* Common code for SPI_cursor_open variants
|
||||||
*/
|
*/
|
||||||
static Portal
|
static Portal
|
||||||
SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
|
SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
|
||||||
Datum *Values, const char *Nulls,
|
ParamListInfo paramLI, bool read_only)
|
||||||
bool read_only, int pflags)
|
|
||||||
{
|
{
|
||||||
CachedPlanSource *plansource;
|
CachedPlanSource *plansource;
|
||||||
CachedPlan *cplan;
|
CachedPlan *cplan;
|
||||||
List *stmt_list;
|
List *stmt_list;
|
||||||
char *query_string;
|
char *query_string;
|
||||||
ParamListInfo paramLI;
|
|
||||||
Snapshot snapshot;
|
Snapshot snapshot;
|
||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
Portal portal;
|
Portal portal;
|
||||||
int k;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that the plan is something the Portal code will special-case as
|
* Check that the plan is something the Portal code will special-case as
|
||||||
@ -1082,54 +1170,15 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
|
|||||||
portal = CreatePortal(name, false, false);
|
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 */
|
/* Copy the plan's query string into the portal */
|
||||||
query_string = pstrdup(plansource->query_string);
|
query_string = MemoryContextStrdup(PortalGetHeapMemory(portal),
|
||||||
|
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);
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: we mustn't have any failure occur between RevalidateCachedPlan
|
||||||
|
* and PortalDefineQuery; that would result in leaking our plancache
|
||||||
|
* refcount.
|
||||||
|
*/
|
||||||
if (plan->saved)
|
if (plan->saved)
|
||||||
{
|
{
|
||||||
/* Replan if needed, and increment plan refcount for portal */
|
/* Replan if needed, and increment plan refcount for portal */
|
||||||
@ -1220,6 +1269,19 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
|
|||||||
snapshot = GetTransactionSnapshot();
|
snapshot = GetTransactionSnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the plan has parameters, copy them into the portal. Note that
|
||||||
|
* this must be done after revalidating the plan, because in dynamic
|
||||||
|
* parameter cases the set of parameters could have changed during
|
||||||
|
* re-parsing.
|
||||||
|
*/
|
||||||
|
if (paramLI)
|
||||||
|
{
|
||||||
|
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||||
|
paramLI = copyParamList(paramLI);
|
||||||
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start portal execution.
|
* Start portal execution.
|
||||||
*/
|
*/
|
||||||
@ -1588,11 +1650,12 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
|
|||||||
/*
|
/*
|
||||||
* Parse and plan a querystring.
|
* Parse and plan a querystring.
|
||||||
*
|
*
|
||||||
* At entry, plan->argtypes, plan->nargs, and plan->cursor_options must be
|
* At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup
|
||||||
* valid. If boundParams isn't NULL then it represents parameter values
|
* and plan->parserSetupArg) must be valid, as must plan->cursor_options.
|
||||||
* that are made available to the planner (as either estimates or hard values
|
* If boundParams isn't NULL then it represents parameter values that are made
|
||||||
* depending on their PARAM_FLAG_CONST marking). The boundParams had better
|
* available to the planner (as either estimates or hard values depending on
|
||||||
* match the param types embedded in the plan!
|
* 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).
|
* Results are stored into *plan (specifically, plan->plancache_list).
|
||||||
* Note however that the result trees are all in CurrentMemoryContext
|
* Note however that the result trees are all in CurrentMemoryContext
|
||||||
@ -1605,8 +1668,6 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams)
|
|||||||
List *plancache_list;
|
List *plancache_list;
|
||||||
ListCell *list_item;
|
ListCell *list_item;
|
||||||
ErrorContextCallback spierrcontext;
|
ErrorContextCallback spierrcontext;
|
||||||
Oid *argtypes = plan->argtypes;
|
|
||||||
int nargs = plan->nargs;
|
|
||||||
int cursor_options = plan->cursor_options;
|
int cursor_options = plan->cursor_options;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1623,8 +1684,8 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams)
|
|||||||
raw_parsetree_list = pg_parse_query(src);
|
raw_parsetree_list = pg_parse_query(src);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do parse analysis and rule rewrite for each raw parsetree, then cons up
|
* Do parse analysis, rule rewrite, and planning for each raw parsetree,
|
||||||
* a phony plancache entry for each one.
|
* then cons up a phony plancache entry for each one.
|
||||||
*/
|
*/
|
||||||
plancache_list = NIL;
|
plancache_list = NIL;
|
||||||
|
|
||||||
@ -1635,9 +1696,27 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams)
|
|||||||
CachedPlanSource *plansource;
|
CachedPlanSource *plansource;
|
||||||
CachedPlan *cplan;
|
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 */
|
/* Need a copyObject here to keep parser from modifying raw tree */
|
||||||
stmt_list = pg_analyze_and_rewrite(copyObject(parsetree),
|
stmt_list = pg_analyze_and_rewrite(copyObject(parsetree),
|
||||||
src, argtypes, nargs);
|
src,
|
||||||
|
plan->argtypes,
|
||||||
|
plan->nargs);
|
||||||
|
}
|
||||||
stmt_list = pg_plan_queries(stmt_list, cursor_options, boundParams);
|
stmt_list = pg_plan_queries(stmt_list, cursor_options, boundParams);
|
||||||
|
|
||||||
plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
|
plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
|
||||||
@ -1647,8 +1726,10 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams)
|
|||||||
/* cast-away-const here is a bit ugly, but there's no reason to copy */
|
/* cast-away-const here is a bit ugly, but there's no reason to copy */
|
||||||
plansource->query_string = (char *) src;
|
plansource->query_string = (char *) src;
|
||||||
plansource->commandTag = CreateCommandTag(parsetree);
|
plansource->commandTag = CreateCommandTag(parsetree);
|
||||||
plansource->param_types = argtypes;
|
plansource->param_types = plan->argtypes;
|
||||||
plansource->num_params = nargs;
|
plansource->num_params = plan->nargs;
|
||||||
|
plansource->parserSetup = plan->parserSetup;
|
||||||
|
plansource->parserSetupArg = plan->parserSetupArg;
|
||||||
plansource->fully_planned = true;
|
plansource->fully_planned = true;
|
||||||
plansource->fixed_result = false;
|
plansource->fixed_result = false;
|
||||||
/* no need to set search_path, generation or saved_xmin */
|
/* no need to set search_path, generation or saved_xmin */
|
||||||
@ -1921,7 +2002,7 @@ fail:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert query parameters to form wanted by planner and executor
|
* Convert arrays of query parameters to form wanted by planner and executor
|
||||||
*/
|
*/
|
||||||
static ParamListInfo
|
static ParamListInfo
|
||||||
_SPI_convert_params(int nargs, Oid *argtypes,
|
_SPI_convert_params(int nargs, Oid *argtypes,
|
||||||
@ -1937,6 +2018,11 @@ _SPI_convert_params(int nargs, Oid *argtypes,
|
|||||||
/* sizeof(ParamListInfoData) includes the first array element */
|
/* sizeof(ParamListInfoData) includes the first array element */
|
||||||
paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
|
paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
|
||||||
(nargs - 1) *sizeof(ParamExternData));
|
(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;
|
paramLI->numParams = nargs;
|
||||||
|
|
||||||
for (i = 0; i < nargs; i++)
|
for (i = 0; i < nargs; i++)
|
||||||
@ -2222,6 +2308,8 @@ _SPI_copy_plan(SPIPlanPtr plan, MemoryContext parentcxt)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
newplan->argtypes = NULL;
|
newplan->argtypes = NULL;
|
||||||
|
newplan->parserSetup = plan->parserSetup;
|
||||||
|
newplan->parserSetupArg = plan->parserSetupArg;
|
||||||
|
|
||||||
foreach(lc, plan->plancache_list)
|
foreach(lc, plan->plancache_list)
|
||||||
{
|
{
|
||||||
@ -2241,6 +2329,8 @@ _SPI_copy_plan(SPIPlanPtr plan, MemoryContext parentcxt)
|
|||||||
newsource->commandTag = plansource->commandTag;
|
newsource->commandTag = plansource->commandTag;
|
||||||
newsource->param_types = newplan->argtypes;
|
newsource->param_types = newplan->argtypes;
|
||||||
newsource->num_params = newplan->nargs;
|
newsource->num_params = newplan->nargs;
|
||||||
|
newsource->parserSetup = newplan->parserSetup;
|
||||||
|
newsource->parserSetupArg = newplan->parserSetupArg;
|
||||||
newsource->fully_planned = plansource->fully_planned;
|
newsource->fully_planned = plansource->fully_planned;
|
||||||
newsource->fixed_result = plansource->fixed_result;
|
newsource->fixed_result = plansource->fixed_result;
|
||||||
/* no need to worry about seach_path, generation or saved_xmin */
|
/* no need to worry about seach_path, generation or saved_xmin */
|
||||||
@ -2298,6 +2388,8 @@ _SPI_save_plan(SPIPlanPtr plan)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
newplan->argtypes = NULL;
|
newplan->argtypes = NULL;
|
||||||
|
newplan->parserSetup = plan->parserSetup;
|
||||||
|
newplan->parserSetupArg = plan->parserSetupArg;
|
||||||
|
|
||||||
foreach(lc, plan->plancache_list)
|
foreach(lc, plan->plancache_list)
|
||||||
{
|
{
|
||||||
@ -2317,6 +2409,10 @@ _SPI_save_plan(SPIPlanPtr plan)
|
|||||||
cplan->stmt_list,
|
cplan->stmt_list,
|
||||||
true,
|
true,
|
||||||
false);
|
false);
|
||||||
|
if (newplan->parserSetup != NULL)
|
||||||
|
CachedPlanSetParserHook(newsource,
|
||||||
|
newplan->parserSetup,
|
||||||
|
newplan->parserSetupArg);
|
||||||
|
|
||||||
newplan->plancache_list = lappend(newplan->plancache_list, newsource);
|
newplan->plancache_list = lappend(newplan->plancache_list, newsource);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/params.c,v 1.11 2009/01/01 17:23:43 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/params.c,v 1.12 2009/11/04 22:26:06 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -16,6 +16,7 @@
|
|||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "nodes/params.h"
|
#include "nodes/params.h"
|
||||||
|
#include "parser/parse_param.h"
|
||||||
#include "utils/datum.h"
|
#include "utils/datum.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
|
||||||
@ -24,6 +25,11 @@
|
|||||||
* Copy a ParamListInfo structure.
|
* Copy a ParamListInfo structure.
|
||||||
*
|
*
|
||||||
* The result is allocated in CurrentMemoryContext.
|
* 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
|
ParamListInfo
|
||||||
copyParamList(ParamListInfo from)
|
copyParamList(ParamListInfo from)
|
||||||
@ -40,54 +46,76 @@ copyParamList(ParamListInfo from)
|
|||||||
(from->numParams - 1) *sizeof(ParamExternData);
|
(from->numParams - 1) *sizeof(ParamExternData);
|
||||||
|
|
||||||
retval = (ParamListInfo) palloc(size);
|
retval = (ParamListInfo) palloc(size);
|
||||||
memcpy(retval, from, size);
|
retval->paramFetch = NULL;
|
||||||
|
retval->paramFetchArg = NULL;
|
||||||
|
retval->parserSetup = NULL;
|
||||||
|
retval->parserSetupArg = NULL;
|
||||||
|
retval->numParams = from->numParams;
|
||||||
|
|
||||||
/*
|
for (i = 0; i < from->numParams; i++)
|
||||||
* 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];
|
ParamExternData *oprm = &from->params[i];
|
||||||
|
ParamExternData *nprm = &retval->params[i];
|
||||||
int16 typLen;
|
int16 typLen;
|
||||||
bool typByVal;
|
bool typByVal;
|
||||||
|
|
||||||
if (prm->isnull || !OidIsValid(prm->ptype))
|
/* give hook a chance in case parameter is dynamic */
|
||||||
|
if (!OidIsValid(oprm->ptype) && from->paramFetch != NULL)
|
||||||
|
(*from->paramFetch) (from, i+1);
|
||||||
|
|
||||||
|
/* flat-copy the parameter info */
|
||||||
|
*nprm = *oprm;
|
||||||
|
|
||||||
|
/* need datumCopy in case it's a pass-by-reference datatype */
|
||||||
|
if (nprm->isnull || !OidIsValid(nprm->ptype))
|
||||||
continue;
|
continue;
|
||||||
get_typlenbyval(prm->ptype, &typLen, &typByVal);
|
get_typlenbyval(nprm->ptype, &typLen, &typByVal);
|
||||||
prm->value = datumCopy(prm->value, typByVal, typLen);
|
nprm->value = datumCopy(nprm->value, typByVal, typLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extract an array of parameter type OIDs from a ParamListInfo.
|
* Set up the parser to treat the given list of run-time parameters
|
||||||
|
* as available external parameters during parsing of a new query.
|
||||||
*
|
*
|
||||||
* The result is allocated in CurrentMemoryContext.
|
* Note that the parser doesn't actually care about the *values* of the given
|
||||||
|
* parameters, only about their *types*. Also, the code that originally
|
||||||
|
* provided the ParamListInfo may have provided a setupHook, which should
|
||||||
|
* override applying parse_fixed_parameters().
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
getParamListTypes(ParamListInfo params,
|
setupParserWithParamList(struct ParseState *pstate,
|
||||||
Oid **param_types, int *num_params)
|
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;
|
Oid *ptypes;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (params == NULL || params->numParams <= 0)
|
|
||||||
{
|
|
||||||
*param_types = NULL;
|
|
||||||
*num_params = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ptypes = (Oid *) palloc(params->numParams * sizeof(Oid));
|
ptypes = (Oid *) palloc(params->numParams * sizeof(Oid));
|
||||||
*param_types = ptypes;
|
|
||||||
*num_params = params->numParams;
|
|
||||||
|
|
||||||
for (i = 0; i < params->numParams; i++)
|
for (i = 0; i < params->numParams; i++)
|
||||||
{
|
{
|
||||||
ParamExternData *prm = ¶ms->params[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;
|
ptypes[i] = prm->ptype;
|
||||||
}
|
}
|
||||||
|
parse_fixed_parameters(pstate, ptypes, params->numParams);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.574 2009/10/08 22:34:57 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.575 2009/11/04 22:26:06 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* this is the "main" module of the postgres backend and
|
* this is the "main" module of the postgres backend and
|
||||||
@ -627,6 +627,52 @@ pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
|
|||||||
return querytree_list;
|
return querytree_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do parse analysis and rewriting. This is the same as pg_analyze_and_rewrite
|
||||||
|
* except that external-parameter resolution is determined by parser callback
|
||||||
|
* hooks instead of a fixed list of parameter datatypes.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
pg_analyze_and_rewrite_params(Node *parsetree,
|
||||||
|
const char *query_string,
|
||||||
|
ParserSetupHook parserSetup,
|
||||||
|
void *parserSetupArg)
|
||||||
|
{
|
||||||
|
ParseState *pstate;
|
||||||
|
Query *query;
|
||||||
|
List *querytree_list;
|
||||||
|
|
||||||
|
Assert(query_string != NULL); /* required as of 8.4 */
|
||||||
|
|
||||||
|
TRACE_POSTGRESQL_QUERY_REWRITE_START(query_string);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (1) Perform parse analysis.
|
||||||
|
*/
|
||||||
|
if (log_parser_stats)
|
||||||
|
ResetUsage();
|
||||||
|
|
||||||
|
pstate = make_parsestate(NULL);
|
||||||
|
pstate->p_sourcetext = query_string;
|
||||||
|
(*parserSetup) (pstate, parserSetupArg);
|
||||||
|
|
||||||
|
query = transformStmt(pstate, parsetree);
|
||||||
|
|
||||||
|
free_parsestate(pstate);
|
||||||
|
|
||||||
|
if (log_parser_stats)
|
||||||
|
ShowUsage("PARSE ANALYSIS STATISTICS");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (2) Rewrite the queries, as necessary
|
||||||
|
*/
|
||||||
|
querytree_list = pg_rewrite_query(query);
|
||||||
|
|
||||||
|
TRACE_POSTGRESQL_QUERY_REWRITE_DONE(query_string);
|
||||||
|
|
||||||
|
return querytree_list;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Perform rewriting of a query produced by parse analysis.
|
* Perform rewriting of a query produced by parse analysis.
|
||||||
*
|
*
|
||||||
@ -1536,6 +1582,11 @@ exec_bind_message(StringInfo input_message)
|
|||||||
/* sizeof(ParamListInfoData) includes the first array element */
|
/* sizeof(ParamListInfoData) includes the first array element */
|
||||||
params = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
|
params = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
|
||||||
(numParams - 1) *sizeof(ParamExternData));
|
(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;
|
params->numParams = numParams;
|
||||||
|
|
||||||
for (paramno = 0; paramno < numParams; paramno++)
|
for (paramno = 0; paramno < numParams; paramno++)
|
||||||
|
47
src/backend/utils/cache/plancache.c
vendored
47
src/backend/utils/cache/plancache.c
vendored
@ -35,7 +35,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.30 2009/10/26 02:26:41 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.31 2009/11/04 22:26:06 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -99,8 +99,8 @@ InitPlanCache(void)
|
|||||||
* raw_parse_tree: output of raw_parser()
|
* raw_parse_tree: output of raw_parser()
|
||||||
* query_string: original query text (as of PG 8.4, must not be NULL)
|
* 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
|
* commandTag: compile-time-constant tag for query, or NULL if empty query
|
||||||
* param_types: array of parameter type OIDs, or NULL if none
|
* param_types: array of fixed parameter type OIDs, or NULL if none
|
||||||
* num_params: number of parameters
|
* num_params: number of fixed parameters
|
||||||
* cursor_options: options bitmask that was/will be passed to planner
|
* cursor_options: options bitmask that was/will be passed to planner
|
||||||
* stmt_list: list of PlannedStmts/utility stmts, or list of Query trees
|
* stmt_list: list of PlannedStmts/utility stmts, or list of Query trees
|
||||||
* fully_planned: are we caching planner or rewriter output?
|
* fully_planned: are we caching planner or rewriter output?
|
||||||
@ -156,6 +156,9 @@ CreateCachedPlan(Node *raw_parse_tree,
|
|||||||
else
|
else
|
||||||
plansource->param_types = NULL;
|
plansource->param_types = NULL;
|
||||||
plansource->num_params = num_params;
|
plansource->num_params = num_params;
|
||||||
|
/* these can be set later with CachedPlanSetParserHook: */
|
||||||
|
plansource->parserSetup = NULL;
|
||||||
|
plansource->parserSetupArg = NULL;
|
||||||
plansource->cursor_options = cursor_options;
|
plansource->cursor_options = cursor_options;
|
||||||
plansource->fully_planned = fully_planned;
|
plansource->fully_planned = fully_planned;
|
||||||
plansource->fixed_result = fixed_result;
|
plansource->fixed_result = fixed_result;
|
||||||
@ -240,6 +243,9 @@ FastCreateCachedPlan(Node *raw_parse_tree,
|
|||||||
plansource->commandTag = commandTag; /* no copying needed */
|
plansource->commandTag = commandTag; /* no copying needed */
|
||||||
plansource->param_types = param_types;
|
plansource->param_types = param_types;
|
||||||
plansource->num_params = num_params;
|
plansource->num_params = num_params;
|
||||||
|
/* these can be set later with CachedPlanSetParserHook: */
|
||||||
|
plansource->parserSetup = NULL;
|
||||||
|
plansource->parserSetupArg = NULL;
|
||||||
plansource->cursor_options = cursor_options;
|
plansource->cursor_options = cursor_options;
|
||||||
plansource->fully_planned = fully_planned;
|
plansource->fully_planned = fully_planned;
|
||||||
plansource->fixed_result = fixed_result;
|
plansource->fixed_result = fixed_result;
|
||||||
@ -274,6 +280,27 @@ FastCreateCachedPlan(Node *raw_parse_tree,
|
|||||||
return plansource;
|
return plansource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CachedPlanSetParserHook: set up to use parser callback hooks
|
||||||
|
*
|
||||||
|
* Use this when a caller wants to manage parameter information via parser
|
||||||
|
* callbacks rather than a fixed parameter-types list. Beware that the
|
||||||
|
* information pointed to by parserSetupArg must be valid for as long as
|
||||||
|
* the cached plan might be replanned!
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
CachedPlanSetParserHook(CachedPlanSource *plansource,
|
||||||
|
ParserSetupHook parserSetup,
|
||||||
|
void *parserSetupArg)
|
||||||
|
{
|
||||||
|
/* Must not have specified a fixed parameter-types list */
|
||||||
|
Assert(plansource->param_types == NULL);
|
||||||
|
Assert(plansource->num_params == 0);
|
||||||
|
/* OK, save hook info */
|
||||||
|
plansource->parserSetup = parserSetup;
|
||||||
|
plansource->parserSetupArg = parserSetupArg;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* StoreCachedPlan: store a built or rebuilt plan into a plancache entry.
|
* StoreCachedPlan: store a built or rebuilt plan into a plancache entry.
|
||||||
*
|
*
|
||||||
@ -466,6 +493,7 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
|
|||||||
if (!plan)
|
if (!plan)
|
||||||
{
|
{
|
||||||
bool snapshot_set = false;
|
bool snapshot_set = false;
|
||||||
|
Node *rawtree;
|
||||||
List *slist;
|
List *slist;
|
||||||
TupleDesc resultDesc;
|
TupleDesc resultDesc;
|
||||||
|
|
||||||
@ -491,11 +519,16 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
|
|||||||
/*
|
/*
|
||||||
* Run parse analysis and rule rewriting. The parser tends to
|
* Run parse analysis and rule rewriting. The parser tends to
|
||||||
* scribble on its input, so we must copy the raw parse tree 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
|
* prevent corruption of the cache.
|
||||||
* 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),
|
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->query_string,
|
||||||
plansource->param_types,
|
plansource->param_types,
|
||||||
plansource->num_params);
|
plansource->num_params);
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.72 2009/06/11 14:49:11 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.73 2009/11/04 22:26:06 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -73,6 +73,9 @@ extern void SPI_restore_connection(void);
|
|||||||
extern int SPI_execute(const char *src, bool read_only, long tcount);
|
extern int SPI_execute(const char *src, bool read_only, long tcount);
|
||||||
extern int SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
|
extern int SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
|
||||||
bool read_only, long tcount);
|
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_exec(const char *src, long tcount);
|
||||||
extern int SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls,
|
extern int SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls,
|
||||||
long tcount);
|
long tcount);
|
||||||
@ -88,6 +91,10 @@ extern int SPI_execute_with_args(const char *src,
|
|||||||
extern SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes);
|
extern SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes);
|
||||||
extern SPIPlanPtr SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
|
extern SPIPlanPtr SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
|
||||||
int cursorOptions);
|
int cursorOptions);
|
||||||
|
extern SPIPlanPtr SPI_prepare_params(const char *src,
|
||||||
|
ParserSetupHook parserSetup,
|
||||||
|
void *parserSetupArg,
|
||||||
|
int cursorOptions);
|
||||||
extern SPIPlanPtr SPI_saveplan(SPIPlanPtr plan);
|
extern SPIPlanPtr SPI_saveplan(SPIPlanPtr plan);
|
||||||
extern int SPI_freeplan(SPIPlanPtr plan);
|
extern int SPI_freeplan(SPIPlanPtr plan);
|
||||||
|
|
||||||
@ -122,6 +129,8 @@ extern Portal SPI_cursor_open_with_args(const char *name,
|
|||||||
int nargs, Oid *argtypes,
|
int nargs, Oid *argtypes,
|
||||||
Datum *Values, const char *Nulls,
|
Datum *Values, const char *Nulls,
|
||||||
bool read_only, int cursorOptions);
|
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 Portal SPI_cursor_find(const char *name);
|
||||||
extern void SPI_cursor_fetch(Portal portal, bool forward, long count);
|
extern void SPI_cursor_fetch(Portal portal, bool forward, long count);
|
||||||
extern void SPI_cursor_move(Portal portal, bool forward, long count);
|
extern void SPI_cursor_move(Portal portal, bool forward, long count);
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.32 2009/01/01 17:23:59 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.33 2009/11/04 22:26:06 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -68,6 +68,8 @@ typedef struct _SPI_plan
|
|||||||
int cursor_options; /* Cursor options used for planning */
|
int cursor_options; /* Cursor options used for planning */
|
||||||
int nargs; /* number of plan arguments */
|
int nargs; /* number of plan arguments */
|
||||||
Oid *argtypes; /* Argument types (NULL if nargs is 0) */
|
Oid *argtypes; /* Argument types (NULL if nargs is 0) */
|
||||||
|
ParserSetupHook parserSetup; /* alternative parameter spec method */
|
||||||
|
void *parserSetupArg;
|
||||||
} _SPI_plan;
|
} _SPI_plan;
|
||||||
|
|
||||||
#endif /* SPI_PRIV_H */
|
#endif /* SPI_PRIV_H */
|
||||||
|
@ -7,13 +7,16 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.38 2009/01/01 17:24:00 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.39 2009/11/04 22:26:06 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
#ifndef PARAMS_H
|
#ifndef PARAMS_H
|
||||||
#define PARAMS_H
|
#define PARAMS_H
|
||||||
|
|
||||||
|
/* To avoid including a pile of parser headers, reference ParseState thus: */
|
||||||
|
struct ParseState;
|
||||||
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* ParamListInfo
|
* ParamListInfo
|
||||||
@ -26,10 +29,20 @@
|
|||||||
* Although parameter numbers are normally consecutive, we allow
|
* Although parameter numbers are normally consecutive, we allow
|
||||||
* ptype == InvalidOid to signal an unused array entry.
|
* 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
|
* 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
|
* as a constant (i.e., generate a plan that works only for this value
|
||||||
* of the parameter).
|
* 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
|
* Although the data structure is really an array, not a list, we keep
|
||||||
* the old typedef name to avoid unnecessary code changes.
|
* the old typedef name to avoid unnecessary code changes.
|
||||||
* ----------------
|
* ----------------
|
||||||
@ -45,14 +58,22 @@ typedef struct ParamExternData
|
|||||||
Oid ptype; /* parameter's datatype, or 0 */
|
Oid ptype; /* parameter's datatype, or 0 */
|
||||||
} ParamExternData;
|
} ParamExternData;
|
||||||
|
|
||||||
|
typedef struct ParamListInfoData *ParamListInfo;
|
||||||
|
|
||||||
|
typedef void (*ParamFetchHook) (ParamListInfo params, int paramid);
|
||||||
|
|
||||||
|
typedef void (*ParserSetupHook) (struct ParseState *pstate, void *arg);
|
||||||
|
|
||||||
typedef struct ParamListInfoData
|
typedef struct ParamListInfoData
|
||||||
{
|
{
|
||||||
|
ParamFetchHook paramFetch; /* parameter fetch hook */
|
||||||
|
void *paramFetchArg;
|
||||||
|
ParserSetupHook parserSetup; /* parser setup hook */
|
||||||
|
void *parserSetupArg;
|
||||||
int numParams; /* number of ParamExternDatas following */
|
int numParams; /* number of ParamExternDatas following */
|
||||||
ParamExternData params[1]; /* VARIABLE LENGTH ARRAY */
|
ParamExternData params[1]; /* VARIABLE LENGTH ARRAY */
|
||||||
} ParamListInfoData;
|
} ParamListInfoData;
|
||||||
|
|
||||||
typedef ParamListInfoData *ParamListInfo;
|
|
||||||
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* ParamExecData
|
* ParamExecData
|
||||||
@ -82,7 +103,7 @@ typedef struct ParamExecData
|
|||||||
/* Functions found in src/backend/nodes/params.c */
|
/* Functions found in src/backend/nodes/params.c */
|
||||||
extern ParamListInfo copyParamList(ParamListInfo from);
|
extern ParamListInfo copyParamList(ParamListInfo from);
|
||||||
|
|
||||||
extern void getParamListTypes(ParamListInfo params,
|
extern void setupParserWithParamList(struct ParseState *pstate,
|
||||||
Oid **param_types, int *num_params);
|
ParamListInfo params);
|
||||||
|
|
||||||
#endif /* PARAMS_H */
|
#endif /* PARAMS_H */
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.100 2009/09/01 00:09:42 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.101 2009/11/04 22:26:07 tgl Exp $
|
||||||
*
|
*
|
||||||
* OLD COMMENTS
|
* OLD COMMENTS
|
||||||
* This file was created so that other c files could get the two
|
* This file was created so that other c files could get the two
|
||||||
@ -49,6 +49,10 @@ extern List *pg_parse_and_rewrite(const char *query_string,
|
|||||||
extern List *pg_parse_query(const char *query_string);
|
extern List *pg_parse_query(const char *query_string);
|
||||||
extern List *pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
|
extern List *pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
|
||||||
Oid *paramTypes, int numParams);
|
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,
|
extern PlannedStmt *pg_plan_query(Query *querytree, int cursorOptions,
|
||||||
ParamListInfo boundParams);
|
ParamListInfo boundParams);
|
||||||
extern List *pg_plan_queries(List *querytrees, int cursorOptions,
|
extern List *pg_plan_queries(List *querytrees, int cursorOptions,
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.15 2009/01/01 17:24:02 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.16 2009/11/04 22:26:07 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -16,6 +16,7 @@
|
|||||||
#define PLANCACHE_H
|
#define PLANCACHE_H
|
||||||
|
|
||||||
#include "access/tupdesc.h"
|
#include "access/tupdesc.h"
|
||||||
|
#include "nodes/params.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CachedPlanSource represents the portion of a cached plan that persists
|
* CachedPlanSource represents the portion of a cached plan that persists
|
||||||
@ -50,6 +51,8 @@ typedef struct CachedPlanSource
|
|||||||
const char *commandTag; /* command tag (a constant!), or NULL */
|
const char *commandTag; /* command tag (a constant!), or NULL */
|
||||||
Oid *param_types; /* array of parameter type OIDs, or NULL */
|
Oid *param_types; /* array of parameter type OIDs, or NULL */
|
||||||
int num_params; /* length of param_types array */
|
int num_params; /* length of param_types array */
|
||||||
|
ParserSetupHook parserSetup; /* alternative parameter spec method */
|
||||||
|
void *parserSetupArg;
|
||||||
int cursor_options; /* cursor options used for planning */
|
int cursor_options; /* cursor options used for planning */
|
||||||
bool fully_planned; /* do we cache planner or rewriter output? */
|
bool fully_planned; /* do we cache planner or rewriter output? */
|
||||||
bool fixed_result; /* disallow change in result tupdesc? */
|
bool fixed_result; /* disallow change in result tupdesc? */
|
||||||
@ -105,6 +108,9 @@ extern CachedPlanSource *FastCreateCachedPlan(Node *raw_parse_tree,
|
|||||||
bool fully_planned,
|
bool fully_planned,
|
||||||
bool fixed_result,
|
bool fixed_result,
|
||||||
MemoryContext context);
|
MemoryContext context);
|
||||||
|
extern void CachedPlanSetParserHook(CachedPlanSource *plansource,
|
||||||
|
ParserSetupHook parserSetup,
|
||||||
|
void *parserSetupArg);
|
||||||
extern void DropCachedPlan(CachedPlanSource *plansource);
|
extern void DropCachedPlan(CachedPlanSource *plansource);
|
||||||
extern CachedPlan *RevalidateCachedPlan(CachedPlanSource *plansource,
|
extern CachedPlan *RevalidateCachedPlan(CachedPlanSource *plansource,
|
||||||
bool useResOwner);
|
bool useResOwner);
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.128 2009/09/29 20:05:29 tgl Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.129 2009/11/04 22:26:07 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -109,7 +109,7 @@ static List *read_raise_options(void);
|
|||||||
} loop_body;
|
} loop_body;
|
||||||
List *list;
|
List *list;
|
||||||
PLpgSQL_type *dtype;
|
PLpgSQL_type *dtype;
|
||||||
PLpgSQL_datum *scalar; /* a VAR, RECFIELD, or TRIGARG */
|
PLpgSQL_datum *scalar; /* a VAR or RECFIELD */
|
||||||
PLpgSQL_variable *variable; /* a VAR, REC, or ROW */
|
PLpgSQL_variable *variable; /* a VAR, REC, or ROW */
|
||||||
PLpgSQL_var *var;
|
PLpgSQL_var *var;
|
||||||
PLpgSQL_row *row;
|
PLpgSQL_row *row;
|
||||||
@ -236,7 +236,7 @@ static List *read_raise_options(void);
|
|||||||
*/
|
*/
|
||||||
%token T_STRING
|
%token T_STRING
|
||||||
%token T_NUMBER
|
%token T_NUMBER
|
||||||
%token T_SCALAR /* a VAR, RECFIELD, or TRIGARG */
|
%token T_SCALAR /* a VAR or RECFIELD */
|
||||||
%token T_ROW
|
%token T_ROW
|
||||||
%token T_RECORD
|
%token T_RECORD
|
||||||
%token T_DTYPE
|
%token T_DTYPE
|
||||||
@ -1903,44 +1903,6 @@ lno :
|
|||||||
%%
|
%%
|
||||||
|
|
||||||
|
|
||||||
#define MAX_EXPR_PARAMS 1024
|
|
||||||
|
|
||||||
/*
|
|
||||||
* determine the expression parameter position to use for a plpgsql datum
|
|
||||||
*
|
|
||||||
* It is important that any given plpgsql datum map to just one parameter.
|
|
||||||
* We used to be sloppy and assign a separate parameter for each occurrence
|
|
||||||
* of a datum reference, but that fails for situations such as "select DATUM
|
|
||||||
* from ... group by DATUM".
|
|
||||||
*
|
|
||||||
* The params[] array must be of size MAX_EXPR_PARAMS.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
assign_expr_param(int dno, int *params, int *nparams)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* already have an instance of this dno? */
|
|
||||||
for (i = 0; i < *nparams; i++)
|
|
||||||
{
|
|
||||||
if (params[i] == dno)
|
|
||||||
return i+1;
|
|
||||||
}
|
|
||||||
/* check for array overflow */
|
|
||||||
if (*nparams >= MAX_EXPR_PARAMS)
|
|
||||||
{
|
|
||||||
plpgsql_error_lineno = plpgsql_scanner_lineno();
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
|
||||||
errmsg("too many variables specified in SQL statement")));
|
|
||||||
}
|
|
||||||
/* add new parameter dno to array */
|
|
||||||
params[*nparams] = dno;
|
|
||||||
(*nparams)++;
|
|
||||||
return *nparams;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Convenience routine to read an expression with one possible terminator */
|
/* Convenience routine to read an expression with one possible terminator */
|
||||||
PLpgSQL_expr *
|
PLpgSQL_expr *
|
||||||
plpgsql_read_expression(int until, const char *expected)
|
plpgsql_read_expression(int until, const char *expected)
|
||||||
@ -1993,8 +1955,7 @@ read_sql_construct(int until,
|
|||||||
int lno;
|
int lno;
|
||||||
StringInfoData ds;
|
StringInfoData ds;
|
||||||
int parenlevel = 0;
|
int parenlevel = 0;
|
||||||
int nparams = 0;
|
Bitmapset *paramnos = NULL;
|
||||||
int params[MAX_EXPR_PARAMS];
|
|
||||||
char buf[32];
|
char buf[32];
|
||||||
PLpgSQL_expr *expr;
|
PLpgSQL_expr *expr;
|
||||||
|
|
||||||
@ -2047,24 +2008,21 @@ read_sql_construct(int until,
|
|||||||
switch (tok)
|
switch (tok)
|
||||||
{
|
{
|
||||||
case T_SCALAR:
|
case T_SCALAR:
|
||||||
snprintf(buf, sizeof(buf), " $%d ",
|
snprintf(buf, sizeof(buf), " $%d ", yylval.scalar->dno + 1);
|
||||||
assign_expr_param(yylval.scalar->dno,
|
|
||||||
params, &nparams));
|
|
||||||
appendStringInfoString(&ds, buf);
|
appendStringInfoString(&ds, buf);
|
||||||
|
paramnos = bms_add_member(paramnos, yylval.scalar->dno);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_ROW:
|
case T_ROW:
|
||||||
snprintf(buf, sizeof(buf), " $%d ",
|
snprintf(buf, sizeof(buf), " $%d ", yylval.row->dno + 1);
|
||||||
assign_expr_param(yylval.row->dno,
|
|
||||||
params, &nparams));
|
|
||||||
appendStringInfoString(&ds, buf);
|
appendStringInfoString(&ds, buf);
|
||||||
|
paramnos = bms_add_member(paramnos, yylval.row->dno);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_RECORD:
|
case T_RECORD:
|
||||||
snprintf(buf, sizeof(buf), " $%d ",
|
snprintf(buf, sizeof(buf), " $%d ", yylval.rec->dno + 1);
|
||||||
assign_expr_param(yylval.rec->dno,
|
|
||||||
params, &nparams));
|
|
||||||
appendStringInfoString(&ds, buf);
|
appendStringInfoString(&ds, buf);
|
||||||
|
paramnos = bms_add_member(paramnos, yylval.rec->dno);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -2076,13 +2034,11 @@ read_sql_construct(int until,
|
|||||||
if (endtoken)
|
if (endtoken)
|
||||||
*endtoken = tok;
|
*endtoken = tok;
|
||||||
|
|
||||||
expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
|
expr = palloc0(sizeof(PLpgSQL_expr));
|
||||||
expr->dtype = PLPGSQL_DTYPE_EXPR;
|
expr->dtype = PLPGSQL_DTYPE_EXPR;
|
||||||
expr->query = pstrdup(ds.data);
|
expr->query = pstrdup(ds.data);
|
||||||
expr->plan = NULL;
|
expr->plan = NULL;
|
||||||
expr->nparams = nparams;
|
expr->paramnos = paramnos;
|
||||||
while(nparams-- > 0)
|
|
||||||
expr->params[nparams] = params[nparams];
|
|
||||||
pfree(ds.data);
|
pfree(ds.data);
|
||||||
|
|
||||||
if (valid_sql)
|
if (valid_sql)
|
||||||
@ -2162,8 +2118,7 @@ static PLpgSQL_stmt *
|
|||||||
make_execsql_stmt(const char *sqlstart, int lineno)
|
make_execsql_stmt(const char *sqlstart, int lineno)
|
||||||
{
|
{
|
||||||
StringInfoData ds;
|
StringInfoData ds;
|
||||||
int nparams = 0;
|
Bitmapset *paramnos = NULL;
|
||||||
int params[MAX_EXPR_PARAMS];
|
|
||||||
char buf[32];
|
char buf[32];
|
||||||
PLpgSQL_stmt_execsql *execsql;
|
PLpgSQL_stmt_execsql *execsql;
|
||||||
PLpgSQL_expr *expr;
|
PLpgSQL_expr *expr;
|
||||||
@ -2214,24 +2169,21 @@ make_execsql_stmt(const char *sqlstart, int lineno)
|
|||||||
switch (tok)
|
switch (tok)
|
||||||
{
|
{
|
||||||
case T_SCALAR:
|
case T_SCALAR:
|
||||||
snprintf(buf, sizeof(buf), " $%d ",
|
snprintf(buf, sizeof(buf), " $%d ", yylval.scalar->dno + 1);
|
||||||
assign_expr_param(yylval.scalar->dno,
|
|
||||||
params, &nparams));
|
|
||||||
appendStringInfoString(&ds, buf);
|
appendStringInfoString(&ds, buf);
|
||||||
|
paramnos = bms_add_member(paramnos, yylval.scalar->dno);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_ROW:
|
case T_ROW:
|
||||||
snprintf(buf, sizeof(buf), " $%d ",
|
snprintf(buf, sizeof(buf), " $%d ", yylval.row->dno + 1);
|
||||||
assign_expr_param(yylval.row->dno,
|
|
||||||
params, &nparams));
|
|
||||||
appendStringInfoString(&ds, buf);
|
appendStringInfoString(&ds, buf);
|
||||||
|
paramnos = bms_add_member(paramnos, yylval.row->dno);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_RECORD:
|
case T_RECORD:
|
||||||
snprintf(buf, sizeof(buf), " $%d ",
|
snprintf(buf, sizeof(buf), " $%d ", yylval.rec->dno + 1);
|
||||||
assign_expr_param(yylval.rec->dno,
|
|
||||||
params, &nparams));
|
|
||||||
appendStringInfoString(&ds, buf);
|
appendStringInfoString(&ds, buf);
|
||||||
|
paramnos = bms_add_member(paramnos, yylval.rec->dno);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -2240,13 +2192,11 @@ make_execsql_stmt(const char *sqlstart, int lineno)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
|
expr = palloc0(sizeof(PLpgSQL_expr));
|
||||||
expr->dtype = PLPGSQL_DTYPE_EXPR;
|
expr->dtype = PLPGSQL_DTYPE_EXPR;
|
||||||
expr->query = pstrdup(ds.data);
|
expr->query = pstrdup(ds.data);
|
||||||
expr->plan = NULL;
|
expr->plan = NULL;
|
||||||
expr->nparams = nparams;
|
expr->paramnos = paramnos;
|
||||||
while(nparams-- > 0)
|
|
||||||
expr->params[nparams] = params[nparams];
|
|
||||||
pfree(ds.data);
|
pfree(ds.data);
|
||||||
|
|
||||||
check_sql_expr(expr->query);
|
check_sql_expr(expr->query);
|
||||||
@ -2600,9 +2550,6 @@ check_assignable(PLpgSQL_datum *datum)
|
|||||||
case PLPGSQL_DTYPE_ARRAYELEM:
|
case PLPGSQL_DTYPE_ARRAYELEM:
|
||||||
/* always assignable? */
|
/* always assignable? */
|
||||||
break;
|
break;
|
||||||
case PLPGSQL_DTYPE_TRIGARG:
|
|
||||||
yyerror("cannot assign to tg_argv");
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized dtype: %d", datum->dtype);
|
elog(ERROR, "unrecognized dtype: %d", datum->dtype);
|
||||||
break;
|
break;
|
||||||
@ -3095,24 +3042,10 @@ make_case(int lineno, PLpgSQL_expr *t_expr,
|
|||||||
{
|
{
|
||||||
PLpgSQL_case_when *cwt = (PLpgSQL_case_when *) lfirst(l);
|
PLpgSQL_case_when *cwt = (PLpgSQL_case_when *) lfirst(l);
|
||||||
PLpgSQL_expr *expr = cwt->expr;
|
PLpgSQL_expr *expr = cwt->expr;
|
||||||
int nparams = expr->nparams;
|
|
||||||
PLpgSQL_expr *new_expr;
|
|
||||||
StringInfoData ds;
|
StringInfoData ds;
|
||||||
|
|
||||||
/* Must add the CASE variable as an extra param to expression */
|
/* Must add the CASE variable as an extra param to expression */
|
||||||
if (nparams >= MAX_EXPR_PARAMS)
|
expr->paramnos = bms_add_member(expr->paramnos, t_varno);
|
||||||
{
|
|
||||||
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) */
|
/* copy expression query without SELECT keyword (expr->query + 7) */
|
||||||
Assert(strncmp(expr->query, "SELECT ", 7) == 0);
|
Assert(strncmp(expr->query, "SELECT ", 7) == 0);
|
||||||
@ -3121,16 +3054,13 @@ make_case(int lineno, PLpgSQL_expr *t_expr,
|
|||||||
initStringInfo(&ds);
|
initStringInfo(&ds);
|
||||||
|
|
||||||
appendStringInfo(&ds, "SELECT $%d IN (%s)",
|
appendStringInfo(&ds, "SELECT $%d IN (%s)",
|
||||||
nparams + 1,
|
t_varno + 1,
|
||||||
expr->query + 7);
|
expr->query + 7);
|
||||||
|
|
||||||
new_expr->query = pstrdup(ds.data);
|
pfree(expr->query);
|
||||||
|
expr->query = pstrdup(ds.data);
|
||||||
|
|
||||||
pfree(ds.data);
|
pfree(ds.data);
|
||||||
pfree(expr->query);
|
|
||||||
pfree(expr);
|
|
||||||
|
|
||||||
cwt->expr = new_expr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.139 2009/09/22 23:43:42 tgl Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.140 2009/11/04 22:26:07 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -624,20 +624,24 @@ do_compile(FunctionCallInfo fcinfo,
|
|||||||
true);
|
true);
|
||||||
function->tg_table_name_varno = var->dno;
|
function->tg_table_name_varno = var->dno;
|
||||||
|
|
||||||
|
/* add the variable tg_table_schema */
|
||||||
/* add variable tg_table_schema */
|
|
||||||
var = plpgsql_build_variable("tg_table_schema", 0,
|
var = plpgsql_build_variable("tg_table_schema", 0,
|
||||||
plpgsql_build_datatype(NAMEOID, -1),
|
plpgsql_build_datatype(NAMEOID, -1),
|
||||||
true);
|
true);
|
||||||
function->tg_table_schema_varno = var->dno;
|
function->tg_table_schema_varno = var->dno;
|
||||||
|
|
||||||
|
|
||||||
/* Add the variable tg_nargs */
|
/* Add the variable tg_nargs */
|
||||||
var = plpgsql_build_variable("tg_nargs", 0,
|
var = plpgsql_build_variable("tg_nargs", 0,
|
||||||
plpgsql_build_datatype(INT4OID, -1),
|
plpgsql_build_datatype(INT4OID, -1),
|
||||||
true);
|
true);
|
||||||
function->tg_nargs_varno = var->dno;
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -931,34 +935,6 @@ plpgsql_parse_word(const char *word)
|
|||||||
/* Do case conversion and word separation */
|
/* Do case conversion and word separation */
|
||||||
plpgsql_convert_ident(word, cp, 1);
|
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
|
* Do a lookup on the compiler's namestack
|
||||||
*/
|
*/
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.248 2009/08/06 20:44:31 tgl Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.249 2009/11/04 22:26:07 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -26,6 +26,7 @@
|
|||||||
#include "lib/stringinfo.h"
|
#include "lib/stringinfo.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
|
#include "parser/parse_node.h"
|
||||||
#include "parser/scansup.h"
|
#include "parser/scansup.h"
|
||||||
#include "storage/proc.h"
|
#include "storage/proc.h"
|
||||||
#include "tcop/tcopprot.h"
|
#include "tcop/tcopprot.h"
|
||||||
@ -154,10 +155,11 @@ static void exec_assign_value(PLpgSQL_execstate *estate,
|
|||||||
Datum value, Oid valtype, bool *isNull);
|
Datum value, Oid valtype, bool *isNull);
|
||||||
static void exec_eval_datum(PLpgSQL_execstate *estate,
|
static void exec_eval_datum(PLpgSQL_execstate *estate,
|
||||||
PLpgSQL_datum *datum,
|
PLpgSQL_datum *datum,
|
||||||
Oid expectedtypeid,
|
|
||||||
Oid *typeid,
|
Oid *typeid,
|
||||||
Datum *value,
|
Datum *value,
|
||||||
bool *isnull);
|
bool *isnull);
|
||||||
|
static Oid exec_get_datum_type(PLpgSQL_execstate *estate,
|
||||||
|
PLpgSQL_datum *datum);
|
||||||
static int exec_eval_integer(PLpgSQL_execstate *estate,
|
static int exec_eval_integer(PLpgSQL_execstate *estate,
|
||||||
PLpgSQL_expr *expr,
|
PLpgSQL_expr *expr,
|
||||||
bool *isNull);
|
bool *isNull);
|
||||||
@ -172,8 +174,11 @@ static int exec_run_select(PLpgSQL_execstate *estate,
|
|||||||
PLpgSQL_expr *expr, long maxtuples, Portal *portalP);
|
PLpgSQL_expr *expr, long maxtuples, Portal *portalP);
|
||||||
static int exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
|
static int exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
|
||||||
Portal portal, bool prefetch_ok);
|
Portal portal, bool prefetch_ok);
|
||||||
static void eval_expr_params(PLpgSQL_execstate *estate,
|
static ParamListInfo setup_param_list(PLpgSQL_execstate *estate,
|
||||||
PLpgSQL_expr *expr, Datum **p_values, char **p_nulls);
|
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,
|
static void exec_move_row(PLpgSQL_execstate *estate,
|
||||||
PLpgSQL_rec *rec,
|
PLpgSQL_rec *rec,
|
||||||
PLpgSQL_row *row,
|
PLpgSQL_row *row,
|
||||||
@ -514,12 +519,20 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Put the OLD and NEW tuples into record variables
|
* 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
|
||||||
|
* in functions that are used for multiple trigger types. For example,
|
||||||
|
* we might have a test like "if (TG_OP = 'INSERT' and NEW.foo = 'xyz')",
|
||||||
|
* which should parse regardless of the current trigger type.
|
||||||
*/
|
*/
|
||||||
rec_new = (PLpgSQL_rec *) (estate.datums[func->new_varno]);
|
rec_new = (PLpgSQL_rec *) (estate.datums[func->new_varno]);
|
||||||
rec_new->freetup = false;
|
rec_new->freetup = false;
|
||||||
|
rec_new->tupdesc = trigdata->tg_relation->rd_att;
|
||||||
rec_new->freetupdesc = false;
|
rec_new->freetupdesc = false;
|
||||||
rec_old = (PLpgSQL_rec *) (estate.datums[func->old_varno]);
|
rec_old = (PLpgSQL_rec *) (estate.datums[func->old_varno]);
|
||||||
rec_old->freetup = false;
|
rec_old->freetup = false;
|
||||||
|
rec_old->tupdesc = trigdata->tg_relation->rd_att;
|
||||||
rec_old->freetupdesc = false;
|
rec_old->freetupdesc = false;
|
||||||
|
|
||||||
if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
|
if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
|
||||||
@ -528,30 +541,22 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
|
|||||||
* Per-statement triggers don't use OLD/NEW variables
|
* Per-statement triggers don't use OLD/NEW variables
|
||||||
*/
|
*/
|
||||||
rec_new->tup = NULL;
|
rec_new->tup = NULL;
|
||||||
rec_new->tupdesc = NULL;
|
|
||||||
rec_old->tup = NULL;
|
rec_old->tup = NULL;
|
||||||
rec_old->tupdesc = NULL;
|
|
||||||
}
|
}
|
||||||
else if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
|
else if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
|
||||||
{
|
{
|
||||||
rec_new->tup = trigdata->tg_trigtuple;
|
rec_new->tup = trigdata->tg_trigtuple;
|
||||||
rec_new->tupdesc = trigdata->tg_relation->rd_att;
|
|
||||||
rec_old->tup = NULL;
|
rec_old->tup = NULL;
|
||||||
rec_old->tupdesc = NULL;
|
|
||||||
}
|
}
|
||||||
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
||||||
{
|
{
|
||||||
rec_new->tup = trigdata->tg_newtuple;
|
rec_new->tup = trigdata->tg_newtuple;
|
||||||
rec_new->tupdesc = trigdata->tg_relation->rd_att;
|
|
||||||
rec_old->tup = trigdata->tg_trigtuple;
|
rec_old->tup = trigdata->tg_trigtuple;
|
||||||
rec_old->tupdesc = trigdata->tg_relation->rd_att;
|
|
||||||
}
|
}
|
||||||
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
|
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
|
||||||
{
|
{
|
||||||
rec_new->tup = NULL;
|
rec_new->tup = NULL;
|
||||||
rec_new->tupdesc = NULL;
|
|
||||||
rec_old->tup = trigdata->tg_trigtuple;
|
rec_old->tup = trigdata->tg_trigtuple;
|
||||||
rec_old->tupdesc = trigdata->tg_relation->rd_att;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, or UPDATE");
|
elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, or UPDATE");
|
||||||
@ -631,19 +636,36 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
|
|||||||
var->isnull = false;
|
var->isnull = false;
|
||||||
var->freeval = false;
|
var->freeval = false;
|
||||||
|
|
||||||
|
var = (PLpgSQL_var *) (estate.datums[func->tg_argv_varno]);
|
||||||
|
if (trigdata->tg_trigger->tgnargs > 0)
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
* Store the trigger argument values into the special execution state
|
* For historical reasons, tg_argv[] subscripts start at zero not one.
|
||||||
* variables
|
* So we can't use construct_array().
|
||||||
*/
|
*/
|
||||||
estate.err_text = gettext_noop("while storing call arguments into local variables");
|
int nelems = trigdata->tg_trigger->tgnargs;
|
||||||
estate.trig_nargs = trigdata->tg_trigger->tgnargs;
|
Datum *elems;
|
||||||
if (estate.trig_nargs == 0)
|
int dims[1];
|
||||||
estate.trig_argv = NULL;
|
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
|
else
|
||||||
{
|
{
|
||||||
estate.trig_argv = palloc(sizeof(Datum) * estate.trig_nargs);
|
var->value = (Datum) 0;
|
||||||
for (i = 0; i < trigdata->tg_trigger->tgnargs; i++)
|
var->isnull = true;
|
||||||
estate.trig_argv[i] = CStringGetTextDatum(trigdata->tg_trigger->tgargs[i]);
|
var->freeval = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
estate.err_text = gettext_noop("during function entry");
|
estate.err_text = gettext_noop("during function entry");
|
||||||
@ -756,10 +778,6 @@ plpgsql_exec_error_callback(void *arg)
|
|||||||
{
|
{
|
||||||
PLpgSQL_execstate *estate = (PLpgSQL_execstate *) arg;
|
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 we are doing RAISE, don't report its location */
|
||||||
if (estate->err_text == raise_skip_msg)
|
if (estate->err_text == raise_skip_msg)
|
||||||
return;
|
return;
|
||||||
@ -784,7 +802,7 @@ plpgsql_exec_error_callback(void *arg)
|
|||||||
* local variable initialization"
|
* local variable initialization"
|
||||||
*/
|
*/
|
||||||
errcontext("PL/pgSQL function \"%s\" line %d %s",
|
errcontext("PL/pgSQL function \"%s\" line %d %s",
|
||||||
estate->err_func->fn_name,
|
estate->func->fn_name,
|
||||||
estate->err_stmt->lineno,
|
estate->err_stmt->lineno,
|
||||||
_(estate->err_text));
|
_(estate->err_text));
|
||||||
}
|
}
|
||||||
@ -795,7 +813,7 @@ plpgsql_exec_error_callback(void *arg)
|
|||||||
* arguments into local variables"
|
* arguments into local variables"
|
||||||
*/
|
*/
|
||||||
errcontext("PL/pgSQL function \"%s\" %s",
|
errcontext("PL/pgSQL function \"%s\" %s",
|
||||||
estate->err_func->fn_name,
|
estate->func->fn_name,
|
||||||
_(estate->err_text));
|
_(estate->err_text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -803,13 +821,13 @@ plpgsql_exec_error_callback(void *arg)
|
|||||||
{
|
{
|
||||||
/* translator: last %s is a plpgsql statement type name */
|
/* translator: last %s is a plpgsql statement type name */
|
||||||
errcontext("PL/pgSQL function \"%s\" line %d at %s",
|
errcontext("PL/pgSQL function \"%s\" line %d at %s",
|
||||||
estate->err_func->fn_name,
|
estate->func->fn_name,
|
||||||
estate->err_stmt->lineno,
|
estate->err_stmt->lineno,
|
||||||
plpgsql_stmt_typename(estate->err_stmt));
|
plpgsql_stmt_typename(estate->err_stmt));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
errcontext("PL/pgSQL function \"%s\"",
|
errcontext("PL/pgSQL function \"%s\"",
|
||||||
estate->err_func->fn_name);
|
estate->func->fn_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -856,7 +874,6 @@ copy_plpgsql_datum(PLpgSQL_datum *datum)
|
|||||||
case PLPGSQL_DTYPE_ROW:
|
case PLPGSQL_DTYPE_ROW:
|
||||||
case PLPGSQL_DTYPE_RECFIELD:
|
case PLPGSQL_DTYPE_RECFIELD:
|
||||||
case PLPGSQL_DTYPE_ARRAYELEM:
|
case PLPGSQL_DTYPE_ARRAYELEM:
|
||||||
case PLPGSQL_DTYPE_TRIGARG:
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These datum records are read-only at runtime, so no need to
|
* These datum records are read-only at runtime, so no need to
|
||||||
@ -977,10 +994,13 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
|
|||||||
if (rec->freetup)
|
if (rec->freetup)
|
||||||
{
|
{
|
||||||
heap_freetuple(rec->tup);
|
heap_freetuple(rec->tup);
|
||||||
FreeTupleDesc(rec->tupdesc);
|
|
||||||
rec->freetup = false;
|
rec->freetup = false;
|
||||||
}
|
}
|
||||||
|
if (rec->freetupdesc)
|
||||||
|
{
|
||||||
|
FreeTupleDesc(rec->tupdesc);
|
||||||
|
rec->freetupdesc = false;
|
||||||
|
}
|
||||||
rec->tup = NULL;
|
rec->tup = NULL;
|
||||||
rec->tupdesc = NULL;
|
rec->tupdesc = NULL;
|
||||||
}
|
}
|
||||||
@ -1885,10 +1905,9 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
|
|||||||
PLpgSQL_var *curvar;
|
PLpgSQL_var *curvar;
|
||||||
char *curname = NULL;
|
char *curname = NULL;
|
||||||
PLpgSQL_expr *query;
|
PLpgSQL_expr *query;
|
||||||
|
ParamListInfo paramLI;
|
||||||
Portal portal;
|
Portal portal;
|
||||||
int rc;
|
int rc;
|
||||||
Datum *values;
|
|
||||||
char *nulls;
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* Get the cursor variable and if it has an assigned name, check
|
* Get the cursor variable and if it has an assigned name, check
|
||||||
@ -1954,19 +1973,25 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
|
|||||||
exec_prepare_plan(estate, query, curvar->cursor_options);
|
exec_prepare_plan(estate, query, curvar->cursor_options);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now build up the values and nulls arguments for SPI_execute_plan()
|
* Set up ParamListInfo (note this is only carrying a hook function,
|
||||||
|
* not any actual data values, at this point)
|
||||||
*/
|
*/
|
||||||
eval_expr_params(estate, query, &values, &nulls);
|
paramLI = setup_param_list(estate, query);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open the cursor
|
* Open the cursor (the paramlist will get copied into the portal)
|
||||||
*/
|
*/
|
||||||
portal = SPI_cursor_open(curname, query->plan, values, nulls,
|
portal = SPI_cursor_open_with_paramlist(curname, query->plan,
|
||||||
|
paramLI,
|
||||||
estate->readonly_func);
|
estate->readonly_func);
|
||||||
if (portal == NULL)
|
if (portal == NULL)
|
||||||
elog(ERROR, "could not open cursor: %s",
|
elog(ERROR, "could not open cursor: %s",
|
||||||
SPI_result_code_string(SPI_result));
|
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
|
* If cursor variable was NULL, store the generated portal name in it
|
||||||
*/
|
*/
|
||||||
@ -1992,8 +2017,6 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
|
|||||||
curvar->isnull = true;
|
curvar->isnull = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pfree(values);
|
|
||||||
pfree(nulls);
|
|
||||||
if (curname)
|
if (curname)
|
||||||
pfree(curname);
|
pfree(curname);
|
||||||
|
|
||||||
@ -2599,6 +2622,11 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
|
|||||||
PLpgSQL_function *func,
|
PLpgSQL_function *func,
|
||||||
ReturnSetInfo *rsi)
|
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->retval = (Datum) 0;
|
||||||
estate->retisnull = true;
|
estate->retisnull = true;
|
||||||
estate->rettype = InvalidOid;
|
estate->rettype = InvalidOid;
|
||||||
@ -2616,9 +2644,6 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
|
|||||||
estate->tuple_store_cxt = NULL;
|
estate->tuple_store_cxt = NULL;
|
||||||
estate->rsi = rsi;
|
estate->rsi = rsi;
|
||||||
|
|
||||||
estate->trig_nargs = 0;
|
|
||||||
estate->trig_argv = NULL;
|
|
||||||
|
|
||||||
estate->found_varno = func->found_varno;
|
estate->found_varno = func->found_varno;
|
||||||
estate->ndatums = func->ndatums;
|
estate->ndatums = func->ndatums;
|
||||||
estate->datums = palloc(sizeof(PLpgSQL_datum *) * estate->ndatums);
|
estate->datums = palloc(sizeof(PLpgSQL_datum *) * estate->ndatums);
|
||||||
@ -2627,11 +2652,14 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
|
|||||||
estate->eval_tuptable = NULL;
|
estate->eval_tuptable = NULL;
|
||||||
estate->eval_processed = 0;
|
estate->eval_processed = 0;
|
||||||
estate->eval_lastoid = InvalidOid;
|
estate->eval_lastoid = InvalidOid;
|
||||||
|
estate->eval_econtext = NULL;
|
||||||
|
estate->cur_expr = NULL;
|
||||||
|
|
||||||
estate->err_func = func;
|
|
||||||
estate->err_stmt = NULL;
|
estate->err_stmt = NULL;
|
||||||
estate->err_text = NULL;
|
estate->err_text = NULL;
|
||||||
|
|
||||||
|
estate->plugin_info = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create an EState and ExprContext for evaluation of simple expressions.
|
* Create an EState and ExprContext for evaluation of simple expressions.
|
||||||
*/
|
*/
|
||||||
@ -2682,30 +2710,20 @@ static void
|
|||||||
exec_prepare_plan(PLpgSQL_execstate *estate,
|
exec_prepare_plan(PLpgSQL_execstate *estate,
|
||||||
PLpgSQL_expr *expr, int cursorOptions)
|
PLpgSQL_expr *expr, int cursorOptions)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
SPIPlanPtr plan;
|
SPIPlanPtr plan;
|
||||||
Oid *argtypes;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need a temporary argtypes array to load with data. (The finished
|
* The grammar can't conveniently set expr->func while building the
|
||||||
* plan structure will contain a copy of it.)
|
* parse tree, so make sure it's set before parser hooks need it.
|
||||||
*/
|
*/
|
||||||
argtypes = (Oid *) palloc(expr->nparams * sizeof(Oid));
|
expr->func = estate->func;
|
||||||
|
|
||||||
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
|
* Generate and save the plan
|
||||||
*/
|
*/
|
||||||
plan = SPI_prepare_cursor(expr->query, expr->nparams, argtypes,
|
plan = SPI_prepare_params(expr->query,
|
||||||
|
(ParserSetupHook) plpgsql_parser_setup,
|
||||||
|
(void *) expr,
|
||||||
cursorOptions);
|
cursorOptions);
|
||||||
if (plan == NULL)
|
if (plan == NULL)
|
||||||
{
|
{
|
||||||
@ -2722,17 +2740,13 @@ exec_prepare_plan(PLpgSQL_execstate *estate,
|
|||||||
errmsg("cannot begin/end transactions in PL/pgSQL"),
|
errmsg("cannot begin/end transactions in PL/pgSQL"),
|
||||||
errhint("Use a BEGIN block with an EXCEPTION clause instead.")));
|
errhint("Use a BEGIN block with an EXCEPTION clause instead.")));
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "SPI_prepare_cursor failed for \"%s\": %s",
|
elog(ERROR, "SPI_prepare_params failed for \"%s\": %s",
|
||||||
expr->query, SPI_result_code_string(SPI_result));
|
expr->query, SPI_result_code_string(SPI_result));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expr->plan = SPI_saveplan(plan);
|
expr->plan = SPI_saveplan(plan);
|
||||||
SPI_freeplan(plan);
|
SPI_freeplan(plan);
|
||||||
plan = expr->plan;
|
|
||||||
expr->plan_argtypes = plan->argtypes;
|
|
||||||
exec_simple_check_plan(expr);
|
exec_simple_check_plan(expr);
|
||||||
|
|
||||||
pfree(argtypes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2744,8 +2758,7 @@ static int
|
|||||||
exec_stmt_execsql(PLpgSQL_execstate *estate,
|
exec_stmt_execsql(PLpgSQL_execstate *estate,
|
||||||
PLpgSQL_stmt_execsql *stmt)
|
PLpgSQL_stmt_execsql *stmt)
|
||||||
{
|
{
|
||||||
Datum *values;
|
ParamListInfo paramLI;
|
||||||
char *nulls;
|
|
||||||
long tcount;
|
long tcount;
|
||||||
int rc;
|
int rc;
|
||||||
PLpgSQL_expr *expr = stmt->sqlstmt;
|
PLpgSQL_expr *expr = stmt->sqlstmt;
|
||||||
@ -2782,9 +2795,10 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now build up the values and nulls arguments for SPI_execute_plan()
|
* Set up ParamListInfo (note this is only carrying a hook function,
|
||||||
|
* not any actual data values, at this point)
|
||||||
*/
|
*/
|
||||||
eval_expr_params(estate, expr, &values, &nulls);
|
paramLI = setup_param_list(estate, expr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we have INTO, then we only need one row back ... but if we have INTO
|
* If we have INTO, then we only need one row back ... but if we have INTO
|
||||||
@ -2810,7 +2824,7 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
|
|||||||
/*
|
/*
|
||||||
* Execute the plan
|
* Execute the plan
|
||||||
*/
|
*/
|
||||||
rc = SPI_execute_plan(expr->plan, values, nulls,
|
rc = SPI_execute_plan_with_paramlist(expr->plan, paramLI,
|
||||||
estate->readonly_func, tcount);
|
estate->readonly_func, tcount);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2852,7 +2866,7 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "SPI_execute_plan failed executing query \"%s\": %s",
|
elog(ERROR, "SPI_execute_plan_with_paramlist failed executing query \"%s\": %s",
|
||||||
expr->query, SPI_result_code_string(rc));
|
expr->query, SPI_result_code_string(rc));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2919,8 +2933,8 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
|
|||||||
(rc == SPI_OK_SELECT) ? errhint("If you want to discard the results of a SELECT, use PERFORM instead.") : 0));
|
(rc == SPI_OK_SELECT) ? errhint("If you want to discard the results of a SELECT, use PERFORM instead.") : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
pfree(values);
|
if (paramLI)
|
||||||
pfree(nulls);
|
pfree(paramLI);
|
||||||
|
|
||||||
return PLPGSQL_RC_OK;
|
return PLPGSQL_RC_OK;
|
||||||
}
|
}
|
||||||
@ -3142,8 +3156,7 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
|
|||||||
char *curname = NULL;
|
char *curname = NULL;
|
||||||
PLpgSQL_expr *query;
|
PLpgSQL_expr *query;
|
||||||
Portal portal;
|
Portal portal;
|
||||||
Datum *values;
|
ParamListInfo paramLI;
|
||||||
char *nulls;
|
|
||||||
bool isnull;
|
bool isnull;
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
@ -3280,14 +3293,16 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now build up the values and nulls arguments for SPI_execute_plan()
|
* Set up ParamListInfo (note this is only carrying a hook function,
|
||||||
|
* not any actual data values, at this point)
|
||||||
*/
|
*/
|
||||||
eval_expr_params(estate, query, &values, &nulls);
|
paramLI = setup_param_list(estate, query);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open the cursor
|
* Open the cursor
|
||||||
*/
|
*/
|
||||||
portal = SPI_cursor_open(curname, query->plan, values, nulls,
|
portal = SPI_cursor_open_with_paramlist(curname, query->plan,
|
||||||
|
paramLI,
|
||||||
estate->readonly_func);
|
estate->readonly_func);
|
||||||
if (portal == NULL)
|
if (portal == NULL)
|
||||||
elog(ERROR, "could not open cursor: %s",
|
elog(ERROR, "could not open cursor: %s",
|
||||||
@ -3299,10 +3314,10 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
|
|||||||
if (curname == NULL)
|
if (curname == NULL)
|
||||||
assign_text_var(curvar, portal->name);
|
assign_text_var(curvar, portal->name);
|
||||||
|
|
||||||
pfree(values);
|
|
||||||
pfree(nulls);
|
|
||||||
if (curname)
|
if (curname)
|
||||||
pfree(curname);
|
pfree(curname);
|
||||||
|
if (paramLI)
|
||||||
|
pfree(paramLI);
|
||||||
|
|
||||||
return PLPGSQL_RC_OK;
|
return PLPGSQL_RC_OK;
|
||||||
}
|
}
|
||||||
@ -3755,7 +3770,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
|
|||||||
} while (target->dtype == PLPGSQL_DTYPE_ARRAYELEM);
|
} while (target->dtype == PLPGSQL_DTYPE_ARRAYELEM);
|
||||||
|
|
||||||
/* Fetch current value of array datum */
|
/* Fetch current value of array datum */
|
||||||
exec_eval_datum(estate, target, InvalidOid,
|
exec_eval_datum(estate, target,
|
||||||
&arraytypeid, &oldarraydatum, &oldarrayisnull);
|
&arraytypeid, &oldarraydatum, &oldarrayisnull);
|
||||||
|
|
||||||
arrayelemtypeid = get_element_type(arraytypeid);
|
arrayelemtypeid = get_element_type(arraytypeid);
|
||||||
@ -3860,8 +3875,6 @@ exec_assign_value(PLpgSQL_execstate *estate,
|
|||||||
*
|
*
|
||||||
* The type oid, value in Datum format, and null flag are returned.
|
* 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.
|
* At present this doesn't handle PLpgSQL_expr or PLpgSQL_arrayelem datums.
|
||||||
*
|
*
|
||||||
* NOTE: caller must not modify the returned value, since it points right
|
* NOTE: caller must not modify the returned value, since it points right
|
||||||
@ -3872,7 +3885,6 @@ exec_assign_value(PLpgSQL_execstate *estate,
|
|||||||
static void
|
static void
|
||||||
exec_eval_datum(PLpgSQL_execstate *estate,
|
exec_eval_datum(PLpgSQL_execstate *estate,
|
||||||
PLpgSQL_datum *datum,
|
PLpgSQL_datum *datum,
|
||||||
Oid expectedtypeid,
|
|
||||||
Oid *typeid,
|
Oid *typeid,
|
||||||
Datum *value,
|
Datum *value,
|
||||||
bool *isnull)
|
bool *isnull)
|
||||||
@ -3888,11 +3900,6 @@ exec_eval_datum(PLpgSQL_execstate *estate,
|
|||||||
*typeid = var->datatype->typoid;
|
*typeid = var->datatype->typoid;
|
||||||
*value = var->value;
|
*value = var->value;
|
||||||
*isnull = var->isnull;
|
*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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3913,11 +3920,6 @@ exec_eval_datum(PLpgSQL_execstate *estate,
|
|||||||
*typeid = row->rowtupdesc->tdtypeid;
|
*typeid = row->rowtupdesc->tdtypeid;
|
||||||
*value = HeapTupleGetDatum(tup);
|
*value = HeapTupleGetDatum(tup);
|
||||||
*isnull = false;
|
*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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3950,11 +3952,6 @@ exec_eval_datum(PLpgSQL_execstate *estate,
|
|||||||
*typeid = rec->tupdesc->tdtypeid;
|
*typeid = rec->tupdesc->tdtypeid;
|
||||||
*value = HeapTupleGetDatum(&worktup);
|
*value = HeapTupleGetDatum(&worktup);
|
||||||
*isnull = false;
|
*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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3979,36 +3976,6 @@ exec_eval_datum(PLpgSQL_execstate *estate,
|
|||||||
rec->refname, recfield->fieldname)));
|
rec->refname, recfield->fieldname)));
|
||||||
*typeid = SPI_gettypeid(rec->tupdesc, fno);
|
*typeid = SPI_gettypeid(rec->tupdesc, fno);
|
||||||
*value = SPI_getbinval(rec->tup, rec->tupdesc, fno, isnull);
|
*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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4017,6 +3984,90 @@ exec_eval_datum(PLpgSQL_execstate *estate,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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; specifically, we may have
|
||||||
|
* a tupdesc but no row value for a record variable. (This currently can
|
||||||
|
* happen only for a trigger's NEW/OLD records.)
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* exec_eval_integer Evaluate an expression, coerce result to int4
|
* exec_eval_integer Evaluate an expression, coerce result to int4
|
||||||
*
|
*
|
||||||
@ -4145,8 +4196,7 @@ static int
|
|||||||
exec_run_select(PLpgSQL_execstate *estate,
|
exec_run_select(PLpgSQL_execstate *estate,
|
||||||
PLpgSQL_expr *expr, long maxtuples, Portal *portalP)
|
PLpgSQL_expr *expr, long maxtuples, Portal *portalP)
|
||||||
{
|
{
|
||||||
Datum *values;
|
ParamListInfo paramLI;
|
||||||
char *nulls;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4156,29 +4206,31 @@ exec_run_select(PLpgSQL_execstate *estate,
|
|||||||
exec_prepare_plan(estate, expr, 0);
|
exec_prepare_plan(estate, expr, 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now build up the values and nulls arguments for SPI_execute_plan()
|
* Set up ParamListInfo (note this is only carrying a hook function,
|
||||||
|
* not any actual data values, at this point)
|
||||||
*/
|
*/
|
||||||
eval_expr_params(estate, expr, &values, &nulls);
|
paramLI = setup_param_list(estate, expr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If a portal was requested, put the query into the portal
|
* If a portal was requested, put the query into the portal
|
||||||
*/
|
*/
|
||||||
if (portalP != NULL)
|
if (portalP != NULL)
|
||||||
{
|
{
|
||||||
*portalP = SPI_cursor_open(NULL, expr->plan, values, nulls,
|
*portalP = SPI_cursor_open_with_paramlist(NULL, expr->plan,
|
||||||
|
paramLI,
|
||||||
estate->readonly_func);
|
estate->readonly_func);
|
||||||
if (*portalP == NULL)
|
if (*portalP == NULL)
|
||||||
elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
|
elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
|
||||||
expr->query, SPI_result_code_string(SPI_result));
|
expr->query, SPI_result_code_string(SPI_result));
|
||||||
pfree(values);
|
if (paramLI)
|
||||||
pfree(nulls);
|
pfree(paramLI);
|
||||||
return SPI_OK_CURSOR;
|
return SPI_OK_CURSOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execute the query
|
* Execute the query
|
||||||
*/
|
*/
|
||||||
rc = SPI_execute_plan(expr->plan, values, nulls,
|
rc = SPI_execute_plan_with_paramlist(expr->plan, paramLI,
|
||||||
estate->readonly_func, maxtuples);
|
estate->readonly_func, maxtuples);
|
||||||
if (rc != SPI_OK_SELECT)
|
if (rc != SPI_OK_SELECT)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -4191,8 +4243,8 @@ exec_run_select(PLpgSQL_execstate *estate,
|
|||||||
estate->eval_processed = SPI_processed;
|
estate->eval_processed = SPI_processed;
|
||||||
estate->eval_lastoid = SPI_lastoid;
|
estate->eval_lastoid = SPI_lastoid;
|
||||||
|
|
||||||
pfree(values);
|
if (paramLI)
|
||||||
pfree(nulls);
|
pfree(paramLI);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@ -4378,7 +4430,7 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
|
|||||||
CachedPlanSource *plansource;
|
CachedPlanSource *plansource;
|
||||||
CachedPlan *cplan;
|
CachedPlan *cplan;
|
||||||
ParamListInfo paramLI;
|
ParamListInfo paramLI;
|
||||||
int i;
|
PLpgSQL_expr *save_cur_expr;
|
||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4425,42 +4477,6 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
|
|||||||
expr->expr_simple_lxid = curlxid;
|
expr->expr_simple_lxid = curlxid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
* 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.
|
* particular advance the snapshot if we are in a non-read-only function.
|
||||||
@ -4476,6 +4492,22 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
|
|||||||
PushActiveSnapshot(GetTransactionSnapshot());
|
PushActiveSnapshot(GetTransactionSnapshot());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
* Finally we can call the executor to evaluate the expression
|
||||||
*/
|
*/
|
||||||
@ -4483,11 +4515,15 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
|
|||||||
econtext,
|
econtext,
|
||||||
isNull,
|
isNull,
|
||||||
NULL);
|
NULL);
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
|
/* Assorted cleanup */
|
||||||
|
estate->cur_expr = save_cur_expr;
|
||||||
|
|
||||||
if (!estate->readonly_func)
|
if (!estate->readonly_func)
|
||||||
PopActiveSnapshot();
|
PopActiveSnapshot();
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
SPI_pop();
|
SPI_pop();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4503,32 +4539,136 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Build up the values and nulls arguments for SPI_execute_plan()
|
* 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;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Also make sure this is set before parser hooks need it. There
|
||||||
|
* is no need to save and restore, since the value is always correct
|
||||||
|
* once set.
|
||||||
|
*/
|
||||||
|
expr->func = estate->func;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
paramLI = NULL;
|
||||||
|
return paramLI;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* plpgsql_parser_setup set up parser hooks for dynamic parameters
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
eval_expr_params(PLpgSQL_execstate *estate,
|
plpgsql_parser_setup(ParseState *pstate, PLpgSQL_expr *expr)
|
||||||
PLpgSQL_expr *expr, Datum **p_values, char **p_nulls)
|
|
||||||
{
|
{
|
||||||
Datum *values;
|
pstate->p_ref_hook_state = (void *) expr;
|
||||||
char *nulls;
|
pstate->p_paramref_hook = plpgsql_param_ref;
|
||||||
int i;
|
/* no need to use p_coerce_param_hook */
|
||||||
|
|
||||||
*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] = ' ';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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,7 +4850,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
|
|||||||
elog(ERROR, "dropped rowtype entry for non-dropped column");
|
elog(ERROR, "dropped rowtype entry for non-dropped column");
|
||||||
|
|
||||||
exec_eval_datum(estate, estate->datums[row->varnos[i]],
|
exec_eval_datum(estate, estate->datums[row->varnos[i]],
|
||||||
InvalidOid, &fieldtypeid, &dvalues[i], &nulls[i]);
|
&fieldtypeid, &dvalues[i], &nulls[i]);
|
||||||
if (fieldtypeid != tupdesc->attrs[i]->atttypid)
|
if (fieldtypeid != tupdesc->attrs[i]->atttypid)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.81 2009/09/29 20:05:29 tgl Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.82 2009/11/04 22:26:07 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1148,21 +1148,7 @@ dump_getdiag(PLpgSQL_stmt_getdiag *stmt)
|
|||||||
static void
|
static void
|
||||||
dump_expr(PLpgSQL_expr *expr)
|
dump_expr(PLpgSQL_expr *expr)
|
||||||
{
|
{
|
||||||
int i;
|
printf("'%s'", expr->query);
|
||||||
|
|
||||||
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
|
void
|
||||||
@ -1240,11 +1226,6 @@ plpgsql_dumptree(PLpgSQL_function *func)
|
|||||||
dump_expr(((PLpgSQL_arrayelem *) d)->subscript);
|
dump_expr(((PLpgSQL_arrayelem *) d)->subscript);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
break;
|
break;
|
||||||
case PLPGSQL_DTYPE_TRIGARG:
|
|
||||||
printf("TRIGARG ");
|
|
||||||
dump_expr(((PLpgSQL_trigarg *) d)->argnum);
|
|
||||||
printf("\n");
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
printf("??? unknown data type %d\n", d->dtype);
|
printf("??? unknown data type %d\n", d->dtype);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.46 2009/09/22 23:43:42 tgl Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.47 2009/11/04 22:26:07 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -68,6 +68,7 @@ Datum
|
|||||||
plpgsql_call_handler(PG_FUNCTION_ARGS)
|
plpgsql_call_handler(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
PLpgSQL_function *func;
|
PLpgSQL_function *func;
|
||||||
|
PLpgSQL_execstate *save_cur_estate;
|
||||||
Datum retval;
|
Datum retval;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@ -80,6 +81,9 @@ plpgsql_call_handler(PG_FUNCTION_ARGS)
|
|||||||
/* Find or compile the function */
|
/* Find or compile the function */
|
||||||
func = plpgsql_compile(fcinfo, false);
|
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 */
|
/* Mark the function as busy, so it can't be deleted from under us */
|
||||||
func->use_count++;
|
func->use_count++;
|
||||||
|
|
||||||
@ -97,14 +101,17 @@ plpgsql_call_handler(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
PG_CATCH();
|
PG_CATCH();
|
||||||
{
|
{
|
||||||
/* Decrement use-count and propagate error */
|
/* Decrement use-count, restore cur_estate, and propagate error */
|
||||||
func->use_count--;
|
func->use_count--;
|
||||||
|
func->cur_estate = save_cur_estate;
|
||||||
PG_RE_THROW();
|
PG_RE_THROW();
|
||||||
}
|
}
|
||||||
PG_END_TRY();
|
PG_END_TRY();
|
||||||
|
|
||||||
func->use_count--;
|
func->use_count--;
|
||||||
|
|
||||||
|
func->cur_estate = save_cur_estate;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Disconnect from SPI manager
|
* Disconnect from SPI manager
|
||||||
*/
|
*/
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.117 2009/09/29 20:05:29 tgl Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.118 2009/11/04 22:26:07 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -22,6 +22,7 @@
|
|||||||
#include "fmgr.h"
|
#include "fmgr.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
#include "executor/spi.h"
|
#include "executor/spi.h"
|
||||||
|
#include "nodes/bitmapset.h"
|
||||||
#include "utils/tuplestore.h"
|
#include "utils/tuplestore.h"
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
@ -58,8 +59,7 @@ enum
|
|||||||
PLPGSQL_DTYPE_REC,
|
PLPGSQL_DTYPE_REC,
|
||||||
PLPGSQL_DTYPE_RECFIELD,
|
PLPGSQL_DTYPE_RECFIELD,
|
||||||
PLPGSQL_DTYPE_ARRAYELEM,
|
PLPGSQL_DTYPE_ARRAYELEM,
|
||||||
PLPGSQL_DTYPE_EXPR,
|
PLPGSQL_DTYPE_EXPR
|
||||||
PLPGSQL_DTYPE_TRIGARG
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
@ -162,8 +162,7 @@ typedef struct
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* PLpgSQL_datum is the common supertype for PLpgSQL_expr, PLpgSQL_var,
|
* PLpgSQL_datum is the common supertype for PLpgSQL_expr, PLpgSQL_var,
|
||||||
* PLpgSQL_row, PLpgSQL_rec, PLpgSQL_recfield, PLpgSQL_arrayelem, and
|
* PLpgSQL_row, PLpgSQL_rec, PLpgSQL_recfield, and PLpgSQL_arrayelem
|
||||||
* PLpgSQL_trigarg
|
|
||||||
*/
|
*/
|
||||||
typedef struct
|
typedef struct
|
||||||
{ /* Generic datum array item */
|
{ /* Generic datum array item */
|
||||||
@ -189,7 +188,11 @@ typedef struct PLpgSQL_expr
|
|||||||
int dno;
|
int dno;
|
||||||
char *query;
|
char *query;
|
||||||
SPIPlanPtr plan;
|
SPIPlanPtr plan;
|
||||||
Oid *plan_argtypes;
|
Bitmapset *paramnos; /* all dnos referenced by this query */
|
||||||
|
|
||||||
|
/* function containing this expr (not set until we first parse query) */
|
||||||
|
struct PLpgSQL_function *func;
|
||||||
|
|
||||||
/* fields for "simple expression" fast-path execution: */
|
/* fields for "simple expression" fast-path execution: */
|
||||||
Expr *expr_simple_expr; /* NULL means not a simple expr */
|
Expr *expr_simple_expr; /* NULL means not a simple expr */
|
||||||
int expr_simple_generation; /* plancache generation we checked */
|
int expr_simple_generation; /* plancache generation we checked */
|
||||||
@ -202,10 +205,6 @@ typedef struct PLpgSQL_expr
|
|||||||
*/
|
*/
|
||||||
ExprState *expr_simple_state;
|
ExprState *expr_simple_state;
|
||||||
LocalTransactionId expr_simple_lxid;
|
LocalTransactionId expr_simple_lxid;
|
||||||
|
|
||||||
/* params to pass to expr */
|
|
||||||
int nparams;
|
|
||||||
int params[1]; /* VARIABLE SIZE ARRAY ... must be last */
|
|
||||||
} PLpgSQL_expr;
|
} PLpgSQL_expr;
|
||||||
|
|
||||||
|
|
||||||
@ -284,14 +283,6 @@ typedef struct
|
|||||||
} PLpgSQL_arrayelem;
|
} PLpgSQL_arrayelem;
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{ /* Positional argument to trigger */
|
|
||||||
int dtype;
|
|
||||||
int dno;
|
|
||||||
PLpgSQL_expr *argnum;
|
|
||||||
} PLpgSQL_trigarg;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{ /* Item in the compilers namestack */
|
{ /* Item in the compilers namestack */
|
||||||
int itemtype;
|
int itemtype;
|
||||||
@ -670,17 +661,22 @@ typedef struct PLpgSQL_function
|
|||||||
int tg_table_name_varno;
|
int tg_table_name_varno;
|
||||||
int tg_table_schema_varno;
|
int tg_table_schema_varno;
|
||||||
int tg_nargs_varno;
|
int tg_nargs_varno;
|
||||||
|
int tg_argv_varno;
|
||||||
|
|
||||||
int ndatums;
|
int ndatums;
|
||||||
PLpgSQL_datum **datums;
|
PLpgSQL_datum **datums;
|
||||||
PLpgSQL_stmt_block *action;
|
PLpgSQL_stmt_block *action;
|
||||||
|
|
||||||
|
/* these fields change when the function is used */
|
||||||
|
struct PLpgSQL_execstate *cur_estate;
|
||||||
unsigned long use_count;
|
unsigned long use_count;
|
||||||
} PLpgSQL_function;
|
} PLpgSQL_function;
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct PLpgSQL_execstate
|
||||||
{ /* Runtime execution data */
|
{ /* Runtime execution data */
|
||||||
|
PLpgSQL_function *func; /* function being executed */
|
||||||
|
|
||||||
Datum retval;
|
Datum retval;
|
||||||
bool retisnull;
|
bool retisnull;
|
||||||
Oid rettype; /* type of current retval */
|
Oid rettype; /* type of current retval */
|
||||||
@ -699,9 +695,6 @@ typedef struct
|
|||||||
MemoryContext tuple_store_cxt;
|
MemoryContext tuple_store_cxt;
|
||||||
ReturnSetInfo *rsi;
|
ReturnSetInfo *rsi;
|
||||||
|
|
||||||
int trig_nargs;
|
|
||||||
Datum *trig_argv;
|
|
||||||
|
|
||||||
int found_varno;
|
int found_varno;
|
||||||
int ndatums;
|
int ndatums;
|
||||||
PLpgSQL_datum **datums;
|
PLpgSQL_datum **datums;
|
||||||
@ -711,11 +704,12 @@ typedef struct
|
|||||||
uint32 eval_processed;
|
uint32 eval_processed;
|
||||||
Oid eval_lastoid;
|
Oid eval_lastoid;
|
||||||
ExprContext *eval_econtext; /* for executing simple expressions */
|
ExprContext *eval_econtext; /* for executing simple expressions */
|
||||||
|
PLpgSQL_expr *cur_expr; /* current query/expr being evaluated */
|
||||||
|
|
||||||
/* status information for error context reporting */
|
/* status information for error context reporting */
|
||||||
PLpgSQL_function *err_func; /* current func */
|
|
||||||
PLpgSQL_stmt *err_stmt; /* current stmt */
|
PLpgSQL_stmt *err_stmt; /* current stmt */
|
||||||
const char *err_text; /* additional state info */
|
const char *err_text; /* additional state info */
|
||||||
|
|
||||||
void *plugin_info; /* reserved for use by optional plugin */
|
void *plugin_info; /* reserved for use by optional plugin */
|
||||||
} PLpgSQL_execstate;
|
} PLpgSQL_execstate;
|
||||||
|
|
||||||
|
@ -285,13 +285,11 @@ begin
|
|||||||
if new.slotno < 1 or new.slotno > hubrec.nslots then
|
if new.slotno < 1 or new.slotno > hubrec.nslots then
|
||||||
raise exception ''no manual manipulation of HSlot'';
|
raise exception ''no manual manipulation of HSlot'';
|
||||||
end if;
|
end if;
|
||||||
if tg_op = ''UPDATE'' then
|
if tg_op = ''UPDATE'' and new.hubname != old.hubname then
|
||||||
if new.hubname != old.hubname then
|
|
||||||
if count(*) > 0 from Hub where name = old.hubname then
|
if count(*) > 0 from Hub where name = old.hubname then
|
||||||
raise exception ''no manual manipulation of HSlot'';
|
raise exception ''no manual manipulation of HSlot'';
|
||||||
end if;
|
end if;
|
||||||
end if;
|
end if;
|
||||||
end if;
|
|
||||||
sname := ''HS.'' || trim(new.hubname);
|
sname := ''HS.'' || trim(new.hubname);
|
||||||
sname := sname || ''.'';
|
sname := sname || ''.'';
|
||||||
sname := sname || new.slotno::text;
|
sname := sname || new.slotno::text;
|
||||||
|
@ -347,13 +347,11 @@ begin
|
|||||||
if new.slotno < 1 or new.slotno > hubrec.nslots then
|
if new.slotno < 1 or new.slotno > hubrec.nslots then
|
||||||
raise exception ''no manual manipulation of HSlot'';
|
raise exception ''no manual manipulation of HSlot'';
|
||||||
end if;
|
end if;
|
||||||
if tg_op = ''UPDATE'' then
|
if tg_op = ''UPDATE'' and new.hubname != old.hubname then
|
||||||
if new.hubname != old.hubname then
|
|
||||||
if count(*) > 0 from Hub where name = old.hubname then
|
if count(*) > 0 from Hub where name = old.hubname then
|
||||||
raise exception ''no manual manipulation of HSlot'';
|
raise exception ''no manual manipulation of HSlot'';
|
||||||
end if;
|
end if;
|
||||||
end if;
|
end if;
|
||||||
end if;
|
|
||||||
sname := ''HS.'' || trim(new.hubname);
|
sname := ''HS.'' || trim(new.hubname);
|
||||||
sname := sname || ''.'';
|
sname := sname || ''.'';
|
||||||
sname := sname || new.slotno::text;
|
sname := sname || new.slotno::text;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user