mirror of
https://github.com/postgres/postgres.git
synced 2025-06-27 23:21:58 +03:00
Rethink recently-added SPI interfaces.
SPI_execute_with_receiver and SPI_cursor_parse_open_with_paramlist are
new in v14 (cf. commit 2f48ede08
). 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
This commit is contained in:
@ -632,6 +632,172 @@ int SPI_exec(const char * <parameter>command</parameter>, long <parameter>count<
|
|||||||
|
|
||||||
<!-- *********************************************** -->
|
<!-- *********************************************** -->
|
||||||
|
|
||||||
|
<refentry id="spi-spi-execute-extended">
|
||||||
|
<indexterm><primary>SPI_execute_extended</primary></indexterm>
|
||||||
|
|
||||||
|
<refmeta>
|
||||||
|
<refentrytitle>SPI_execute_extended</refentrytitle>
|
||||||
|
<manvolnum>3</manvolnum>
|
||||||
|
</refmeta>
|
||||||
|
|
||||||
|
<refnamediv>
|
||||||
|
<refname>SPI_execute_extended</refname>
|
||||||
|
<refpurpose>execute a command with out-of-line parameters</refpurpose>
|
||||||
|
</refnamediv>
|
||||||
|
|
||||||
|
<refsynopsisdiv>
|
||||||
|
<synopsis>
|
||||||
|
int SPI_execute_extended(const char *<parameter>command</parameter>,
|
||||||
|
const SPIExecuteOptions * <parameter>options</parameter>)
|
||||||
|
</synopsis>
|
||||||
|
</refsynopsisdiv>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Description</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<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 <parameter>options->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 <parameter>options->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>
|
||||||
|
If <parameter>options->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>
|
||||||
|
|
||||||
|
<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>const SPIExecuteOptions * <parameter>options</parameter></literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
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>ParamListInfo <parameter>params</parameter></literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
data structure containing query parameter types and values; NULL if none
|
||||||
|
</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>bool <parameter>no_snapshots</parameter></literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<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>
|
||||||
|
maximum number of rows to return,
|
||||||
|
or <literal>0</literal> for no limit
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>DestReceiver * <parameter>dest</parameter></literal></term>
|
||||||
|
<listitem>
|
||||||
|
<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>ResourceOwner <parameter>owner</parameter></literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
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>
|
||||||
|
</variablelist>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title>Return Value</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The return value is the same as for <function>SPI_execute</function>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
When <parameter>options->dest</parameter> is NULL,
|
||||||
|
<varname>SPI_processed</varname> and
|
||||||
|
<varname>SPI_tuptable</varname> are set as in
|
||||||
|
<function>SPI_execute</function>.
|
||||||
|
When <parameter>options->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-args">
|
<refentry id="spi-spi-execute-with-args">
|
||||||
<indexterm><primary>SPI_execute_with_args</primary></indexterm>
|
<indexterm><primary>SPI_execute_with_args</primary></indexterm>
|
||||||
|
|
||||||
@ -785,133 +951,6 @@ int SPI_execute_with_args(const char *<parameter>command</parameter>,
|
|||||||
|
|
||||||
<!-- *********************************************** -->
|
<!-- *********************************************** -->
|
||||||
|
|
||||||
<refentry id="spi-spi-execute-with-receiver">
|
|
||||||
<indexterm><primary>SPI_execute_with_receiver</primary></indexterm>
|
|
||||||
|
|
||||||
<refmeta>
|
|
||||||
<refentrytitle>SPI_execute_with_receiver</refentrytitle>
|
|
||||||
<manvolnum>3</manvolnum>
|
|
||||||
</refmeta>
|
|
||||||
|
|
||||||
<refnamediv>
|
|
||||||
<refname>SPI_execute_with_receiver</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>)
|
|
||||||
</synopsis>
|
|
||||||
</refsynopsisdiv>
|
|
||||||
|
|
||||||
<refsect1>
|
|
||||||
<title>Description</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
<function>SPI_execute_with_receiver</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.
|
|
||||||
<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.
|
|
||||||
</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.
|
|
||||||
</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>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</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>
|
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><literal>DestReceiver * <parameter>dest</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>
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
</variablelist>
|
|
||||||
</refsect1>
|
|
||||||
|
|
||||||
<refsect1>
|
|
||||||
<title>Return Value</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The return value is the same as for <function>SPI_execute</function>.
|
|
||||||
</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.
|
|
||||||
</para>
|
|
||||||
</refsect1>
|
|
||||||
</refentry>
|
|
||||||
|
|
||||||
<!-- *********************************************** -->
|
|
||||||
|
|
||||||
<refentry id="spi-spi-prepare">
|
<refentry id="spi-spi-prepare">
|
||||||
<indexterm><primary>SPI_prepare</primary></indexterm>
|
<indexterm><primary>SPI_prepare</primary></indexterm>
|
||||||
|
|
||||||
@ -1873,11 +1912,11 @@ int SPI_execute_plan_extended(SPIPlanPtr <parameter>plan</parameter>,
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
When <parameter>dest</parameter> is NULL,
|
When <parameter>options->dest</parameter> is NULL,
|
||||||
<varname>SPI_processed</varname> and
|
<varname>SPI_processed</varname> and
|
||||||
<varname>SPI_tuptable</varname> are set as in
|
<varname>SPI_tuptable</varname> are set as in
|
||||||
<function>SPI_execute_plan</function>.
|
<function>SPI_execute_plan</function>.
|
||||||
When <parameter>dest</parameter> is not NULL,
|
When <parameter>options->dest</parameter> is not NULL,
|
||||||
<varname>SPI_processed</varname> is set to zero and
|
<varname>SPI_processed</varname> is set to zero and
|
||||||
<varname>SPI_tuptable</varname> is set to NULL. If a tuple count
|
<varname>SPI_tuptable</varname> is set to NULL. If a tuple count
|
||||||
is required, the caller's <literal>DestReceiver</literal> object must
|
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
|
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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
<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">
|
<refentry id="spi-spi-cursor-parse-open">
|
||||||
<indexterm><primary>SPI_cursor_parse_open_with_paramlist</primary></indexterm>
|
<indexterm><primary>SPI_cursor_parse_open</primary></indexterm>
|
||||||
|
|
||||||
<refmeta>
|
<refmeta>
|
||||||
<refentrytitle>SPI_cursor_parse_open_with_paramlist</refentrytitle>
|
<refentrytitle>SPI_cursor_parse_open</refentrytitle>
|
||||||
<manvolnum>3</manvolnum>
|
<manvolnum>3</manvolnum>
|
||||||
</refmeta>
|
</refmeta>
|
||||||
|
|
||||||
<refnamediv>
|
<refnamediv>
|
||||||
<refname>SPI_cursor_parse_open_with_paramlist</refname>
|
<refname>SPI_cursor_parse_open</refname>
|
||||||
<refpurpose>set up a cursor using a query and parameters</refpurpose>
|
<refpurpose>set up a cursor using a query string and parameters</refpurpose>
|
||||||
</refnamediv>
|
</refnamediv>
|
||||||
|
|
||||||
<refsynopsisdiv>
|
<refsynopsisdiv>
|
||||||
<synopsis>
|
<synopsis>
|
||||||
Portal SPI_cursor_parse_open_with_paramlist(const char *<parameter>name</parameter>,
|
Portal SPI_cursor_parse_open(const char *<parameter>name</parameter>,
|
||||||
const char *<parameter>command</parameter>,
|
const char *<parameter>command</parameter>,
|
||||||
ParamListInfo <parameter>params</parameter>,
|
const SPIParseOpenOptions * <parameter>options</parameter>)
|
||||||
bool <parameter>read_only</parameter>,
|
|
||||||
int <parameter>cursorOptions</parameter>)
|
|
||||||
</synopsis>
|
</synopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
|
|
||||||
@ -2492,17 +2535,27 @@ Portal SPI_cursor_parse_open_with_paramlist(const char *<parameter>name</paramet
|
|||||||
<title>Description</title>
|
<title>Description</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
<function>SPI_cursor_parse_open_with_paramlist</function> sets up a cursor
|
<function>SPI_cursor_parse_open</function> sets up a cursor
|
||||||
(internally, a portal) that will execute the specified query. This
|
(internally, a portal) that will execute the specified query string.
|
||||||
function is equivalent to <function>SPI_cursor_open_with_args</function>
|
This is comparable to <function>SPI_prepare_cursor</function> followed
|
||||||
except that any parameters referenced by the query are provided by
|
by <function>SPI_cursor_open_with_paramlist</function>, except that
|
||||||
a <literal>ParamListInfo</literal> object, rather than in ad-hoc arrays.
|
parameter references within the query string are handled entirely by
|
||||||
|
supplying a <literal>ParamListInfo</literal> object.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The <parameter>params</parameter> object should normally mark each
|
For one-time query execution, this function should be preferred
|
||||||
parameter with the <literal>PARAM_FLAG_CONST</literal> flag, since
|
over <function>SPI_prepare_cursor</function> followed by
|
||||||
a one-shot plan is always used for the query.
|
<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>options->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>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -2535,18 +2588,30 @@ Portal SPI_cursor_parse_open_with_paramlist(const char *<parameter>name</paramet
|
|||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><literal>ParamListInfo <parameter>params</parameter></literal></term>
|
<term><literal>const SPIParseOpenOptions * <parameter>options</parameter></literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
data structure containing parameter types and values; NULL if none
|
struct containing optional arguments
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
<varlistentry>
|
||||||
<term><literal>bool <parameter>read_only</parameter></literal></term>
|
<term><literal>ParamListInfo <parameter>params</parameter></literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para><literal>true</literal> for read-only execution</para>
|
<para>
|
||||||
|
data structure containing query parameter types and values; NULL if none
|
||||||
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
@ -2558,6 +2623,13 @@ Portal SPI_cursor_parse_open_with_paramlist(const char *<parameter>name</paramet
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>bool <parameter>read_only</parameter></literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para><literal>true</literal> for read-only execution</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
@ -538,6 +538,43 @@ SPI_exec(const char *src, long tcount)
|
|||||||
return SPI_execute(src, false, 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 */
|
/* Execute a previously prepared plan */
|
||||||
int
|
int
|
||||||
SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
|
SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
|
||||||
@ -715,52 +752,6 @@ SPI_execute_with_args(const char *src,
|
|||||||
return res;
|
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
|
SPIPlanPtr
|
||||||
SPI_prepare(const char *src, int nargs, Oid *argtypes)
|
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);
|
return SPI_cursor_open_internal(name, plan, params, read_only);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Parse a query and open it as a cursor */
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
Portal
|
Portal
|
||||||
SPI_cursor_parse_open_with_paramlist(const char *name,
|
SPI_cursor_parse_open(const char *name,
|
||||||
const char *src,
|
const char *src,
|
||||||
ParamListInfo params,
|
const SPIParseOpenOptions *options)
|
||||||
bool read_only, int cursorOptions)
|
|
||||||
{
|
{
|
||||||
Portal result;
|
Portal result;
|
||||||
_SPI_plan plan;
|
_SPI_plan plan;
|
||||||
|
|
||||||
if (src == NULL)
|
if (src == NULL || options == NULL)
|
||||||
elog(ERROR, "SPI_cursor_parse_open_with_paramlist called with invalid arguments");
|
elog(ERROR, "SPI_cursor_parse_open called with invalid arguments");
|
||||||
|
|
||||||
SPI_result = _SPI_begin_call(true);
|
SPI_result = _SPI_begin_call(true);
|
||||||
if (SPI_result < 0)
|
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));
|
memset(&plan, 0, sizeof(_SPI_plan));
|
||||||
plan.magic = _SPI_PLAN_MAGIC;
|
plan.magic = _SPI_PLAN_MAGIC;
|
||||||
plan.parse_mode = RAW_PARSE_DEFAULT;
|
plan.parse_mode = RAW_PARSE_DEFAULT;
|
||||||
plan.cursor_options = cursorOptions;
|
plan.cursor_options = options->cursorOptions;
|
||||||
if (params)
|
if (options->params)
|
||||||
{
|
{
|
||||||
plan.parserSetup = params->parserSetup;
|
plan.parserSetup = options->params->parserSetup;
|
||||||
plan.parserSetupArg = params->parserSetupArg;
|
plan.parserSetupArg = options->params->parserSetupArg;
|
||||||
}
|
}
|
||||||
|
|
||||||
_SPI_prepare_plan(src, &plan);
|
_SPI_prepare_plan(src, &plan);
|
||||||
|
|
||||||
/* We needn't copy the plan; SPI_cursor_open_internal will do so */
|
/* 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 */
|
/* And clean up */
|
||||||
_SPI_end_call(true);
|
_SPI_end_call(true);
|
||||||
|
@ -42,7 +42,7 @@ typedef struct SPIPrepareOptions
|
|||||||
int cursorOptions;
|
int cursorOptions;
|
||||||
} SPIPrepareOptions;
|
} SPIPrepareOptions;
|
||||||
|
|
||||||
/* Optional arguments for SPI_execute_plan_extended */
|
/* Optional arguments for SPI_execute[_plan]_extended */
|
||||||
typedef struct SPIExecuteOptions
|
typedef struct SPIExecuteOptions
|
||||||
{
|
{
|
||||||
ParamListInfo params;
|
ParamListInfo params;
|
||||||
@ -53,6 +53,14 @@ typedef struct SPIExecuteOptions
|
|||||||
ResourceOwner owner;
|
ResourceOwner owner;
|
||||||
} SPIExecuteOptions;
|
} 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 */
|
/* Plans are opaque structs for standard users of SPI */
|
||||||
typedef struct _SPI_plan *SPIPlanPtr;
|
typedef struct _SPI_plan *SPIPlanPtr;
|
||||||
|
|
||||||
@ -105,6 +113,8 @@ extern int SPI_connect(void);
|
|||||||
extern int SPI_connect_ext(int options);
|
extern int SPI_connect_ext(int options);
|
||||||
extern int SPI_finish(void);
|
extern int SPI_finish(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_extended(const char *src,
|
||||||
|
const SPIExecuteOptions *options);
|
||||||
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_extended(SPIPlanPtr plan,
|
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,
|
int nargs, Oid *argtypes,
|
||||||
Datum *Values, const char *Nulls,
|
Datum *Values, const char *Nulls,
|
||||||
bool read_only, long tcount);
|
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(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);
|
||||||
@ -178,11 +184,9 @@ extern Portal SPI_cursor_open_with_args(const char *name,
|
|||||||
bool read_only, int cursorOptions);
|
bool read_only, int cursorOptions);
|
||||||
extern Portal SPI_cursor_open_with_paramlist(const char *name, SPIPlanPtr plan,
|
extern Portal SPI_cursor_open_with_paramlist(const char *name, SPIPlanPtr plan,
|
||||||
ParamListInfo params, bool read_only);
|
ParamListInfo params, bool read_only);
|
||||||
extern Portal SPI_cursor_parse_open_with_paramlist(const char *name,
|
extern Portal SPI_cursor_parse_open(const char *name,
|
||||||
const char *src,
|
const char *src,
|
||||||
ParamListInfo params,
|
const SPIParseOpenOptions *options);
|
||||||
bool read_only,
|
|
||||||
int cursorOptions);
|
|
||||||
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);
|
||||||
|
@ -3603,6 +3603,7 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
|
|||||||
Oid restype;
|
Oid restype;
|
||||||
int32 restypmod;
|
int32 restypmod;
|
||||||
char *querystr;
|
char *querystr;
|
||||||
|
SPIExecuteOptions options;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Evaluate the string expression after the EXECUTE keyword. Its
|
* Evaluate the string expression after the EXECUTE keyword. Its
|
||||||
@ -3625,14 +3626,15 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
|
|||||||
exec_eval_cleanup(estate);
|
exec_eval_cleanup(estate);
|
||||||
|
|
||||||
/* Execute query, passing params if necessary */
|
/* Execute query, passing params if necessary */
|
||||||
rc = SPI_execute_with_receiver(querystr,
|
memset(&options, 0, sizeof(options));
|
||||||
exec_eval_using_params(estate,
|
options.params = exec_eval_using_params(estate,
|
||||||
stmt->params),
|
stmt->params);
|
||||||
estate->readonly_func,
|
options.read_only = estate->readonly_func;
|
||||||
0,
|
options.dest = treceiver;
|
||||||
treceiver);
|
|
||||||
|
rc = SPI_execute_extended(querystr, &options);
|
||||||
if (rc < 0)
|
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));
|
querystr, SPI_result_code_string(rc));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4402,6 +4404,7 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
|
|||||||
char *querystr;
|
char *querystr;
|
||||||
int exec_res;
|
int exec_res;
|
||||||
ParamListInfo paramLI;
|
ParamListInfo paramLI;
|
||||||
|
SPIExecuteOptions options;
|
||||||
MemoryContext stmt_mcontext = get_stmt_mcontext(estate);
|
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.
|
* Execute the query without preparing a saved plan.
|
||||||
*/
|
*/
|
||||||
paramLI = exec_eval_using_params(estate, stmt->params);
|
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)
|
switch (exec_res)
|
||||||
{
|
{
|
||||||
@ -4479,7 +4486,7 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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));
|
querystr, SPI_result_code_string(exec_res));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -8582,6 +8589,7 @@ exec_dynquery_with_params(PLpgSQL_execstate *estate,
|
|||||||
Oid restype;
|
Oid restype;
|
||||||
int32 restypmod;
|
int32 restypmod;
|
||||||
char *querystr;
|
char *querystr;
|
||||||
|
SPIParseOpenOptions options;
|
||||||
MemoryContext stmt_mcontext = get_stmt_mcontext(estate);
|
MemoryContext stmt_mcontext = get_stmt_mcontext(estate);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -8603,16 +8611,16 @@ exec_dynquery_with_params(PLpgSQL_execstate *estate,
|
|||||||
exec_eval_cleanup(estate);
|
exec_eval_cleanup(estate);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open an implicit cursor for the query. We use
|
* Open an implicit cursor for the query. We use SPI_cursor_parse_open
|
||||||
* SPI_cursor_parse_open_with_paramlist even when there are no params,
|
* even when there are no params, because this avoids making and freeing
|
||||||
* because this avoids making and freeing one copy of the plan.
|
* one copy of the plan.
|
||||||
*/
|
*/
|
||||||
portal = SPI_cursor_parse_open_with_paramlist(portalname,
|
memset(&options, 0, sizeof(options));
|
||||||
querystr,
|
options.params = exec_eval_using_params(estate, params);
|
||||||
exec_eval_using_params(estate,
|
options.cursorOptions = cursorOptions;
|
||||||
params),
|
options.read_only = estate->readonly_func;
|
||||||
estate->readonly_func,
|
|
||||||
cursorOptions);
|
portal = SPI_cursor_parse_open(portalname, querystr, &options);
|
||||||
|
|
||||||
if (portal == NULL)
|
if (portal == NULL)
|
||||||
elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
|
elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
|
||||||
|
Reference in New Issue
Block a user