diff options
author | Tom Lane | 2021-01-26 21:37:12 +0000 |
---|---|---|
committer | Tom Lane | 2021-01-26 21:37:12 +0000 |
commit | d5a83d79c9f9b660a6a5a77afafe146d3c8c6f46 (patch) | |
tree | e0c3626c5bd5369cf1433a42a1db1d9df9dd53ba | |
parent | 7292fd8f1c781278021407276474d9188845113d (diff) |
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
-rw-r--r-- | doc/src/sgml/spi.sgml | 306 | ||||
-rw-r--r-- | src/backend/executor/spi.c | 114 | ||||
-rw-r--r-- | src/include/executor/spi.h | 24 | ||||
-rw-r--r-- | src/pl/plpgsql/src/pl_exec.c | 46 |
4 files changed, 280 insertions, 210 deletions
diff --git a/doc/src/sgml/spi.sgml b/doc/src/sgml/spi.sgml index d8c121f5f35..6543eaa0343 100644 --- a/doc/src/sgml/spi.sgml +++ b/doc/src/sgml/spi.sgml @@ -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->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->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->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>' '</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->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->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>' '</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->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->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->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> diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 68a6bcea02d..00aa78ea539 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -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); diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h index 5740f8956e5..6455d100f50 100644 --- a/src/include/executor/spi.h +++ b/src/include/executor/spi.h @@ -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); diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 383d92fc1d0..b4c70aaa7fa 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -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", |