Commit d5a83d79 authored by Tom Lane's avatar Tom Lane

Rethink recently-added SPI interfaces.

SPI_execute_with_receiver and SPI_cursor_parse_open_with_paramlist are
new in v14 (cf. commit 2f48ede0).  Before they can get out the door,
let's change their APIs to follow the practice recently established by
SPI_prepare_extended etc: shove all optional arguments into a struct
that callers are supposed to pre-zero.  The hope is to allow future
addition of more options without either API breakage or a continuing
proliferation of new SPI entry points.  With that in mind, choose
slightly more generic names for them: SPI_execute_extended and
SPI_cursor_parse_open respectively.

Discussion: https://postgr.es/m/CAFj8pRCLPdDAETvR7Po7gC5y_ibkn_-bOzbeJb39WHms01194Q@mail.gmail.com
parent 7292fd8f
......@@ -632,25 +632,23 @@ int SPI_exec(const char * <parameter>command</parameter>, long <parameter>count<
<!-- *********************************************** -->
<refentry id="spi-spi-execute-with-args">
<indexterm><primary>SPI_execute_with_args</primary></indexterm>
<refentry id="spi-spi-execute-extended">
<indexterm><primary>SPI_execute_extended</primary></indexterm>
<refmeta>
<refentrytitle>SPI_execute_with_args</refentrytitle>
<refentrytitle>SPI_execute_extended</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>SPI_execute_with_args</refname>
<refname>SPI_execute_extended</refname>
<refpurpose>execute a command with out-of-line parameters</refpurpose>
</refnamediv>
<refsynopsisdiv>
<synopsis>
int SPI_execute_with_args(const char *<parameter>command</parameter>,
int <parameter>nargs</parameter>, Oid *<parameter>argtypes</parameter>,
Datum *<parameter>values</parameter>, const char *<parameter>nulls</parameter>,
bool <parameter>read_only</parameter>, long <parameter>count</parameter>)
int SPI_execute_extended(const char *<parameter>command</parameter>,
const SPIExecuteOptions * <parameter>options</parameter>)
</synopsis>
</refsynopsisdiv>
......@@ -658,30 +656,28 @@ int SPI_execute_with_args(const char *<parameter>command</parameter>,
<title>Description</title>
<para>
<function>SPI_execute_with_args</function> executes a command that might
<function>SPI_execute_extended</function> executes a command that might
include references to externally supplied parameters. The command text
refers to a parameter as <literal>$<replaceable>n</replaceable></literal>, and
the call specifies data types and values for each such symbol.
<parameter>read_only</parameter> and <parameter>count</parameter> have
the same interpretation as in <function>SPI_execute</function>.
refers to a parameter as <literal>$<replaceable>n</replaceable></literal>,
and the <parameter>options-&gt;params</parameter> object (if supplied)
provides values and type information for each such symbol.
Various execution options can be specified
in the <parameter>options</parameter> struct, too.
</para>
<para>
The main advantage of this routine compared to
<function>SPI_execute</function> is that data values can be inserted
into the command without tedious quoting/escaping, and thus with much
less risk of SQL-injection attacks.
The <parameter>options-&gt;params</parameter> object should normally
mark each parameter with the <literal>PARAM_FLAG_CONST</literal> flag,
since a one-shot plan is always used for the query.
</para>
<para>
Similar results can be achieved with <function>SPI_prepare</function> followed by
<function>SPI_execute_plan</function>; however, when using this function
the query plan is always customized to the specific parameter values
provided.
For one-time query execution, this function should be preferred.
If the same command is to be executed with many different parameters,
either method might be faster, depending on the cost of re-planning
versus the benefit of custom plans.
If <parameter>options-&gt;dest</parameter> is not NULL, then result
tuples are passed to that object as they are generated by the executor,
instead of being accumulated in <varname>SPI_tuptable</varname>. Using
a caller-supplied <literal>DestReceiver</literal> object is particularly
helpful for queries that might generate many tuples, since the data can
be processed on-the-fly instead of being accumulated in memory.
</para>
</refsect1>
......@@ -699,69 +695,80 @@ int SPI_execute_with_args(const char *<parameter>command</parameter>,
</varlistentry>
<varlistentry>
<term><literal>int <parameter>nargs</parameter></literal></term>
<term><literal>const SPIExecuteOptions * <parameter>options</parameter></literal></term>
<listitem>
<para>
number of input parameters (<literal>$1</literal>, <literal>$2</literal>, etc.)
struct containing optional arguments
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
Callers should always zero out the entire <parameter>options</parameter>
struct, then fill whichever fields they want to set. This ensures forward
compatibility of code, since any fields that are added to the struct in
future will be defined to behave backwards-compatibly if they are zero.
The currently available <parameter>options</parameter> fields are:
</para>
<variablelist>
<varlistentry>
<term><literal>Oid * <parameter>argtypes</parameter></literal></term>
<term><literal>ParamListInfo <parameter>params</parameter></literal></term>
<listitem>
<para>
an array of length <parameter>nargs</parameter>, containing the
<acronym>OID</acronym>s of the data types of the parameters
data structure containing query parameter types and values; NULL if none
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>Datum * <parameter>values</parameter></literal></term>
<term><literal>bool <parameter>read_only</parameter></literal></term>
<listitem>
<para>
an array of length <parameter>nargs</parameter>, containing the actual
parameter values
</para>
<para><literal>true</literal> for read-only execution</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>const char * <parameter>nulls</parameter></literal></term>
<term><literal>bool <parameter>no_snapshots</parameter></literal></term>
<listitem>
<para>
an array of length <parameter>nargs</parameter>, describing which
parameters are null
<literal>true</literal> prevents SPI from managing snapshots for
execution of the query; use with extreme caution
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>uint64 <parameter>tcount</parameter></literal></term>
<listitem>
<para>
If <parameter>nulls</parameter> is <symbol>NULL</symbol> then
<function>SPI_execute_with_args</function> assumes that no parameters
are null. Otherwise, each entry of the <parameter>nulls</parameter>
array should be <literal>'&nbsp;'</literal> if the corresponding parameter
value is non-null, or <literal>'n'</literal> if the corresponding parameter
value is null. (In the latter case, the actual value in the
corresponding <parameter>values</parameter> entry doesn't matter.) Note
that <parameter>nulls</parameter> is not a text string, just an array:
it does not need a <literal>'\0'</literal> terminator.
maximum number of rows to return,
or <literal>0</literal> for no limit
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>bool <parameter>read_only</parameter></literal></term>
<term><literal>DestReceiver * <parameter>dest</parameter></literal></term>
<listitem>
<para><literal>true</literal> for read-only execution</para>
<para>
<literal>DestReceiver</literal> object that will receive any tuples
emitted by the query; if NULL, result tuples are accumulated into
a <varname>SPI_tuptable</varname> structure, as
in <function>SPI_execute</function>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>long <parameter>count</parameter></literal></term>
<term><literal>ResourceOwner <parameter>owner</parameter></literal></term>
<listitem>
<para>
maximum number of rows to return,
or <literal>0</literal> for no limit
This field is present for consistency
with <function>SPI_execute_plan_extended</function>, but it is
ignored, since the plan used
by <function>SPI_execute_extended</function> is never saved.
</para>
</listitem>
</varlistentry>
......@@ -776,35 +783,40 @@ int SPI_execute_with_args(const char *<parameter>command</parameter>,
</para>
<para>
When <parameter>options-&gt;dest</parameter> is NULL,
<varname>SPI_processed</varname> and
<varname>SPI_tuptable</varname> are set as in
<function>SPI_execute</function> if successful.
<function>SPI_execute</function>.
When <parameter>options-&gt;dest</parameter> is not NULL,
<varname>SPI_processed</varname> is set to zero and
<varname>SPI_tuptable</varname> is set to NULL. If a tuple count
is required, the caller's <literal>DestReceiver</literal> object must
calculate it.
</para>
</refsect1>
</refentry>
<!-- *********************************************** -->
<refentry id="spi-spi-execute-with-receiver">
<indexterm><primary>SPI_execute_with_receiver</primary></indexterm>
<refentry id="spi-spi-execute-with-args">
<indexterm><primary>SPI_execute_with_args</primary></indexterm>
<refmeta>
<refentrytitle>SPI_execute_with_receiver</refentrytitle>
<refentrytitle>SPI_execute_with_args</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>SPI_execute_with_receiver</refname>
<refname>SPI_execute_with_args</refname>
<refpurpose>execute a command with out-of-line parameters</refpurpose>
</refnamediv>
<refsynopsisdiv>
<synopsis>
int SPI_execute_with_receiver(const char *<parameter>command</parameter>,
ParamListInfo <parameter>params</parameter>,
bool <parameter>read_only</parameter>,
long <parameter>count</parameter>,
DestReceiver *<parameter>dest</parameter>)
int SPI_execute_with_args(const char *<parameter>command</parameter>,
int <parameter>nargs</parameter>, Oid *<parameter>argtypes</parameter>,
Datum *<parameter>values</parameter>, const char *<parameter>nulls</parameter>,
bool <parameter>read_only</parameter>, long <parameter>count</parameter>)
</synopsis>
</refsynopsisdiv>
......@@ -812,28 +824,30 @@ int SPI_execute_with_args(const char *<parameter>command</parameter>,
<title>Description</title>
<para>
<function>SPI_execute_with_receiver</function> executes a command that might
<function>SPI_execute_with_args</function> executes a command that might
include references to externally supplied parameters. The command text
refers to a parameter as <literal>$<replaceable>n</replaceable></literal>,
and the <parameter>params</parameter> object provides values and type
information for each such symbol.
refers to a parameter as <literal>$<replaceable>n</replaceable></literal>, and
the call specifies data types and values for each such symbol.
<parameter>read_only</parameter> and <parameter>count</parameter> have
the same interpretation as in <function>SPI_execute</function>.
</para>
<para>
If <parameter>dest</parameter> is not NULL, then result tuples are passed
to that object as they are generated by the executor, instead of being
accumulated in <varname>SPI_tuptable</varname>. Using a
caller-supplied <literal>DestReceiver</literal> object is particularly
helpful for queries that might generate many tuples, since the data can
be processed on-the-fly instead of being accumulated in memory.
The main advantage of this routine compared to
<function>SPI_execute</function> is that data values can be inserted
into the command without tedious quoting/escaping, and thus with much
less risk of SQL-injection attacks.
</para>
<para>
The <parameter>params</parameter> object should normally mark each
parameter with the <literal>PARAM_FLAG_CONST</literal> flag, since
a one-shot plan is always used for the query.
Similar results can be achieved with <function>SPI_prepare</function> followed by
<function>SPI_execute_plan</function>; however, when using this function
the query plan is always customized to the specific parameter values
provided.
For one-time query execution, this function should be preferred.
If the same command is to be executed with many different parameters,
either method might be faster, depending on the cost of re-planning
versus the benefit of custom plans.
</para>
</refsect1>
......@@ -851,38 +865,69 @@ int SPI_execute_with_args(const char *<parameter>command</parameter>,
</varlistentry>
<varlistentry>
<term><literal>ParamListInfo <parameter>params</parameter></literal></term>
<term><literal>int <parameter>nargs</parameter></literal></term>
<listitem>
<para>
data structure containing parameter types and values; NULL if none
number of input parameters (<literal>$1</literal>, <literal>$2</literal>, etc.)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>bool <parameter>read_only</parameter></literal></term>
<term><literal>Oid * <parameter>argtypes</parameter></literal></term>
<listitem>
<para><literal>true</literal> for read-only execution</para>
<para>
an array of length <parameter>nargs</parameter>, containing the
<acronym>OID</acronym>s of the data types of the parameters
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>long <parameter>count</parameter></literal></term>
<term><literal>Datum * <parameter>values</parameter></literal></term>
<listitem>
<para>
maximum number of rows to return,
or <literal>0</literal> for no limit
an array of length <parameter>nargs</parameter>, containing the actual
parameter values
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>DestReceiver * <parameter>dest</parameter></literal></term>
<term><literal>const char * <parameter>nulls</parameter></literal></term>
<listitem>
<para>
<literal>DestReceiver</literal> object that will receive any tuples
emitted by the query; if NULL, tuples are returned
in <varname>SPI_tuptable</varname>
an array of length <parameter>nargs</parameter>, describing which
parameters are null
</para>
<para>
If <parameter>nulls</parameter> is <symbol>NULL</symbol> then
<function>SPI_execute_with_args</function> assumes that no parameters
are null. Otherwise, each entry of the <parameter>nulls</parameter>
array should be <literal>'&nbsp;'</literal> if the corresponding parameter
value is non-null, or <literal>'n'</literal> if the corresponding parameter
value is null. (In the latter case, the actual value in the
corresponding <parameter>values</parameter> entry doesn't matter.) Note
that <parameter>nulls</parameter> is not a text string, just an array:
it does not need a <literal>'\0'</literal> terminator.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>bool <parameter>read_only</parameter></literal></term>
<listitem>
<para><literal>true</literal> for read-only execution</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>long <parameter>count</parameter></literal></term>
<listitem>
<para>
maximum number of rows to return,
or <literal>0</literal> for no limit
</para>
</listitem>
</varlistentry>
......@@ -897,15 +942,9 @@ int SPI_execute_with_args(const char *<parameter>command</parameter>,
</para>
<para>
When <parameter>dest</parameter> is NULL,
<varname>SPI_processed</varname> and
<varname>SPI_tuptable</varname> are set as in
<function>SPI_execute</function>.
When <parameter>dest</parameter> is not NULL,
<varname>SPI_processed</varname> is set to zero and
<varname>SPI_tuptable</varname> is set to NULL. If a tuple count
is required, the caller's <literal>DestReceiver</literal> object must
calculate it.
<function>SPI_execute</function> if successful.
</para>
</refsect1>
</refentry>
......@@ -1873,11 +1912,11 @@ int SPI_execute_plan_extended(SPIPlanPtr <parameter>plan</parameter>,
</para>
<para>
When <parameter>dest</parameter> is NULL,
When <parameter>options-&gt;dest</parameter> is NULL,
<varname>SPI_processed</varname> and
<varname>SPI_tuptable</varname> are set as in
<function>SPI_execute_plan</function>.
When <parameter>dest</parameter> is not NULL,
When <parameter>options-&gt;dest</parameter> is not NULL,
<varname>SPI_processed</varname> is set to zero and
<varname>SPI_tuptable</varname> is set to NULL. If a tuple count
is required, the caller's <literal>DestReceiver</literal> object must
......@@ -2263,6 +2302,12 @@ Portal SPI_cursor_open_with_args(const char *<parameter>name</parameter>,
The passed-in parameter data will be copied into the cursor's portal, so it
can be freed while the cursor still exists.
</para>
<para>
This function is now deprecated in favor
of <function>SPI_cursor_parse_open</function>, which provides equivalent
functionality using a more modern API for handling query parameters.
</para>
</refsect1>
<refsect1>
......@@ -2465,26 +2510,24 @@ Portal SPI_cursor_open_with_paramlist(const char *<parameter>name</parameter>,
<!-- *********************************************** -->
<refentry id="spi-spi-cursor-parse-open-with-paramlist">
<indexterm><primary>SPI_cursor_parse_open_with_paramlist</primary></indexterm>
<refentry id="spi-spi-cursor-parse-open">
<indexterm><primary>SPI_cursor_parse_open</primary></indexterm>
<refmeta>
<refentrytitle>SPI_cursor_parse_open_with_paramlist</refentrytitle>
<refentrytitle>SPI_cursor_parse_open</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>SPI_cursor_parse_open_with_paramlist</refname>
<refpurpose>set up a cursor using a query and parameters</refpurpose>
<refname>SPI_cursor_parse_open</refname>
<refpurpose>set up a cursor using a query string and parameters</refpurpose>
</refnamediv>
<refsynopsisdiv>
<synopsis>
Portal SPI_cursor_parse_open_with_paramlist(const char *<parameter>name</parameter>,
const char *<parameter>command</parameter>,
ParamListInfo <parameter>params</parameter>,
bool <parameter>read_only</parameter>,
int <parameter>cursorOptions</parameter>)
Portal SPI_cursor_parse_open(const char *<parameter>name</parameter>,
const char *<parameter>command</parameter>,
const SPIParseOpenOptions * <parameter>options</parameter>)
</synopsis>
</refsynopsisdiv>
......@@ -2492,17 +2535,27 @@ Portal SPI_cursor_parse_open_with_paramlist(const char *<parameter>name</paramet
<title>Description</title>
<para>
<function>SPI_cursor_parse_open_with_paramlist</function> sets up a cursor
(internally, a portal) that will execute the specified query. This
function is equivalent to <function>SPI_cursor_open_with_args</function>
except that any parameters referenced by the query are provided by
a <literal>ParamListInfo</literal> object, rather than in ad-hoc arrays.
<function>SPI_cursor_parse_open</function> sets up a cursor
(internally, a portal) that will execute the specified query string.
This is comparable to <function>SPI_prepare_cursor</function> followed
by <function>SPI_cursor_open_with_paramlist</function>, except that
parameter references within the query string are handled entirely by
supplying a <literal>ParamListInfo</literal> object.
</para>
<para>
For one-time query execution, this function should be preferred
over <function>SPI_prepare_cursor</function> followed by
<function>SPI_cursor_open_with_paramlist</function>.
If the same command is to be executed with many different parameters,
either method might be faster, depending on the cost of re-planning
versus the benefit of custom plans.
</para>
<para>
The <parameter>params</parameter> object should normally mark each
parameter with the <literal>PARAM_FLAG_CONST</literal> flag, since
a one-shot plan is always used for the query.
The <parameter>options-&gt;params</parameter> object should normally
mark each parameter with the <literal>PARAM_FLAG_CONST</literal> flag,
since a one-shot plan is always used for the query.
</para>
<para>
......@@ -2535,18 +2588,30 @@ Portal SPI_cursor_parse_open_with_paramlist(const char *<parameter>name</paramet
</varlistentry>
<varlistentry>
<term><literal>ParamListInfo <parameter>params</parameter></literal></term>
<term><literal>const SPIParseOpenOptions * <parameter>options</parameter></literal></term>
<listitem>
<para>
data structure containing parameter types and values; NULL if none
struct containing optional arguments
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
Callers should always zero out the entire <parameter>options</parameter>
struct, then fill whichever fields they want to set. This ensures forward
compatibility of code, since any fields that are added to the struct in
future will be defined to behave backwards-compatibly if they are zero.
The currently available <parameter>options</parameter> fields are:
</para>
<variablelist>
<varlistentry>
<term><literal>bool <parameter>read_only</parameter></literal></term>
<term><literal>ParamListInfo <parameter>params</parameter></literal></term>
<listitem>
<para><literal>true</literal> for read-only execution</para>
<para>
data structure containing query parameter types and values; NULL if none
</para>
</listitem>
</varlistentry>
......@@ -2558,6 +2623,13 @@ Portal SPI_cursor_parse_open_with_paramlist(const char *<parameter>name</paramet
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>bool <parameter>read_only</parameter></literal></term>
<listitem>
<para><literal>true</literal> for read-only execution</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
......
......@@ -538,6 +538,43 @@ SPI_exec(const char *src, long tcount)
return SPI_execute(src, false, tcount);
}
/* Parse, plan, and execute a query string, with extensible options */
int
SPI_execute_extended(const char *src,
const SPIExecuteOptions *options)
{
int res;
_SPI_plan plan;
if (src == NULL || options == NULL)
return SPI_ERROR_ARGUMENT;
res = _SPI_begin_call(true);
if (res < 0)
return res;
memset(&plan, 0, sizeof(_SPI_plan));
plan.magic = _SPI_PLAN_MAGIC;
plan.parse_mode = RAW_PARSE_DEFAULT;
plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
if (options->params)
{
plan.parserSetup = options->params->parserSetup;
plan.parserSetupArg = options->params->parserSetupArg;
}
_SPI_prepare_oneshot_plan(src, &plan);
res = _SPI_execute_plan(&plan, options->params,
InvalidSnapshot, InvalidSnapshot,
options->read_only, options->no_snapshots,
true, options->tcount,
options->dest, options->owner);
_SPI_end_call(true);
return res;
}
/* Execute a previously prepared plan */
int
SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
......@@ -715,52 +752,6 @@ SPI_execute_with_args(const char *src,
return res;
}
/*
* SPI_execute_with_receiver -- plan and execute a query with arguments
*
* This is the same as SPI_execute_with_args except that parameters are
* supplied through a ParamListInfo, and (if dest isn't NULL) we send
* result tuples to the caller-supplied DestReceiver rather than through
* the usual SPI output arrangements.
*/
int
SPI_execute_with_receiver(const char *src,
ParamListInfo params,
bool read_only, long tcount,
DestReceiver *dest)
{
int res;
_SPI_plan plan;
if (src == NULL || tcount < 0)
return SPI_ERROR_ARGUMENT;
res = _SPI_begin_call(true);
if (res < 0)
return res;
memset(&plan, 0, sizeof(_SPI_plan));
plan.magic = _SPI_PLAN_MAGIC;
plan.parse_mode = RAW_PARSE_DEFAULT;
plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
if (params)
{
plan.parserSetup = params->parserSetup;
plan.parserSetupArg = params->parserSetupArg;
}
_SPI_prepare_oneshot_plan(src, &plan);
res = _SPI_execute_plan(&plan, params,
InvalidSnapshot, InvalidSnapshot,
read_only, false,
true, tcount,
dest, NULL);
_SPI_end_call(true);
return res;
}
SPIPlanPtr
SPI_prepare(const char *src, int nargs, Oid *argtypes)
{
......@@ -1433,43 +1424,38 @@ SPI_cursor_open_with_paramlist(const char *name, SPIPlanPtr plan,
return SPI_cursor_open_internal(name, plan, params, read_only);
}
/*
* SPI_cursor_parse_open_with_paramlist()
*
* Same as SPI_cursor_open_with_args except that parameters (if any) are passed
* as a ParamListInfo, which supports dynamic parameter set determination
*/
/* Parse a query and open it as a cursor */
Portal
SPI_cursor_parse_open_with_paramlist(const char *name,
const char *src,
ParamListInfo params,
bool read_only, int cursorOptions)
SPI_cursor_parse_open(const char *name,
const char *src,
const SPIParseOpenOptions *options)
{
Portal result;
_SPI_plan plan;
if (src == NULL)
elog(ERROR, "SPI_cursor_parse_open_with_paramlist called with invalid arguments");
if (src == NULL || options == NULL)
elog(ERROR, "SPI_cursor_parse_open called with invalid arguments");
SPI_result = _SPI_begin_call(true);
if (SPI_result < 0)
elog(ERROR, "SPI_cursor_parse_open_with_paramlist called while not connected");
elog(ERROR, "SPI_cursor_parse_open called while not connected");
memset(&plan, 0, sizeof(_SPI_plan));
plan.magic = _SPI_PLAN_MAGIC;
plan.parse_mode = RAW_PARSE_DEFAULT;
plan.cursor_options = cursorOptions;
if (params)
plan.cursor_options = options->cursorOptions;
if (options->params)
{
plan.parserSetup = params->parserSetup;
plan.parserSetupArg = params->parserSetupArg;
plan.parserSetup = options->params->parserSetup;
plan.parserSetupArg = options->params->parserSetupArg;
}
_SPI_prepare_plan(src, &plan);
/* We needn't copy the plan; SPI_cursor_open_internal will do so */
result = SPI_cursor_open_internal(name, &plan, params, read_only);
result = SPI_cursor_open_internal(name, &plan,
options->params, options->read_only);
/* And clean up */
_SPI_end_call(true);
......
......@@ -42,7 +42,7 @@ typedef struct SPIPrepareOptions
int cursorOptions;
} SPIPrepareOptions;
/* Optional arguments for SPI_execute_plan_extended */
/* Optional arguments for SPI_execute[_plan]_extended */
typedef struct SPIExecuteOptions
{
ParamListInfo params;
......@@ -53,6 +53,14 @@ typedef struct SPIExecuteOptions
ResourceOwner owner;
} SPIExecuteOptions;
/* Optional arguments for SPI_cursor_parse_open */
typedef struct SPIParseOpenOptions
{
ParamListInfo params;
int cursorOptions;
bool read_only;
} SPIParseOpenOptions;
/* Plans are opaque structs for standard users of SPI */
typedef struct _SPI_plan *SPIPlanPtr;
......@@ -105,6 +113,8 @@ extern int SPI_connect(void);
extern int SPI_connect_ext(int options);
extern int SPI_finish(void);
extern int SPI_execute(const char *src, bool read_only, long tcount);
extern int SPI_execute_extended(const char *src,
const SPIExecuteOptions *options);
extern int SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
bool read_only, long tcount);
extern int SPI_execute_plan_extended(SPIPlanPtr plan,
......@@ -124,10 +134,6 @@ extern int SPI_execute_with_args(const char *src,
int nargs, Oid *argtypes,
Datum *Values, const char *Nulls,
bool read_only, long tcount);
extern int SPI_execute_with_receiver(const char *src,
ParamListInfo params,
bool read_only, long tcount,
DestReceiver *dest);
extern SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes);
extern SPIPlanPtr SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
int cursorOptions);
......@@ -178,11 +184,9 @@ extern Portal SPI_cursor_open_with_args(const char *name,
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_parse_open_with_paramlist(const char *name,
const char *src,
ParamListInfo params,
bool read_only,
int cursorOptions);
extern Portal SPI_cursor_parse_open(const char *name,
const char *src,
const SPIParseOpenOptions *options);
extern Portal SPI_cursor_find(const char *name);
extern void SPI_cursor_fetch(Portal portal, bool forward, long count);
extern void SPI_cursor_move(Portal portal, bool forward, long count);
......
......@@ -3603,6 +3603,7 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
Oid restype;
int32 restypmod;
char *querystr;
SPIExecuteOptions options;
/*
* Evaluate the string expression after the EXECUTE keyword. Its
......@@ -3625,14 +3626,15 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
exec_eval_cleanup(estate);
/* Execute query, passing params if necessary */
rc = SPI_execute_with_receiver(querystr,
exec_eval_using_params(estate,
stmt->params),
estate->readonly_func,
0,
treceiver);
memset(&options, 0, sizeof(options));
options.params = exec_eval_using_params(estate,
stmt->params);
options.read_only = estate->readonly_func;
options.dest = treceiver;
rc = SPI_execute_extended(querystr, &options);
if (rc < 0)
elog(ERROR, "SPI_execute_with_receiver failed executing query \"%s\": %s",
elog(ERROR, "SPI_execute_extended failed executing query \"%s\": %s",
querystr, SPI_result_code_string(rc));
}
......@@ -4402,6 +4404,7 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
char *querystr;
int exec_res;
ParamListInfo paramLI;
SPIExecuteOptions options;
MemoryContext stmt_mcontext = get_stmt_mcontext(estate);
/*
......@@ -4426,8 +4429,12 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
* Execute the query without preparing a saved plan.
*/
paramLI = exec_eval_using_params(estate, stmt->params);
exec_res = SPI_execute_with_receiver(querystr, paramLI,
estate->readonly_func, 0, NULL);
memset(&options, 0, sizeof(options));
options.params = paramLI;
options.read_only = estate->readonly_func;
exec_res = SPI_execute_extended(querystr, &options);
switch (exec_res)
{
......@@ -4479,7 +4486,7 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
break;
default:
elog(ERROR, "SPI_execute_with_receiver failed executing query \"%s\": %s",
elog(ERROR, "SPI_execute_extended failed executing query \"%s\": %s",
querystr, SPI_result_code_string(exec_res));
break;
}
......@@ -8582,6 +8589,7 @@ exec_dynquery_with_params(PLpgSQL_execstate *estate,
Oid restype;
int32 restypmod;
char *querystr;
SPIParseOpenOptions options;
MemoryContext stmt_mcontext = get_stmt_mcontext(estate);
/*
......@@ -8603,16 +8611,16 @@ exec_dynquery_with_params(PLpgSQL_execstate *estate,
exec_eval_cleanup(estate);
/*
* Open an implicit cursor for the query. We use
* SPI_cursor_parse_open_with_paramlist even when there are no params,
* because this avoids making and freeing one copy of the plan.
* Open an implicit cursor for the query. We use SPI_cursor_parse_open
* even when there are no params, because this avoids making and freeing
* one copy of the plan.
*/
portal = SPI_cursor_parse_open_with_paramlist(portalname,
querystr,
exec_eval_using_params(estate,
params),
estate->readonly_func,
cursorOptions);
memset(&options, 0, sizeof(options));
options.params = exec_eval_using_params(estate, params);
options.cursorOptions = cursorOptions;
options.read_only = estate->readonly_func;
portal = SPI_cursor_parse_open(portalname, querystr, &options);
if (portal == NULL)
elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment