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,6 +632,172 @@ int SPI_exec(const char * command, long count<
+
+ SPI_execute_extended
+
+
+ SPI_execute_extended
+ 3
+
+
+
+ SPI_execute_extended
+ execute a command with out-of-line parameters
+
+
+
+
+int SPI_execute_extended(const char *command,
+ const SPIExecuteOptions * options)
+
+
+
+
+ Description
+
+
+ SPI_execute_extended executes a command that might
+ include references to externally supplied parameters. The command text
+ refers to a parameter as $n,
+ and the options->params object (if supplied)
+ provides values and type information for each such symbol.
+ Various execution options can be specified
+ in the options struct, too.
+
+
+
+ The options->params object should normally
+ mark each parameter with the PARAM_FLAG_CONST flag,
+ since a one-shot plan is always used for the query.
+
+
+
+ If options->dest is not NULL, then result
+ tuples are passed to that object as they are generated by the executor,
+ instead of being accumulated in SPI_tuptable. Using
+ a caller-supplied DestReceiver 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.
+
+
+
+
+ Arguments
+
+
+
+ const char * command
+
+
+ command string
+
+
+
+
+
+ const SPIExecuteOptions * options
+
+
+ struct containing optional arguments
+
+
+
+
+
+
+ Callers should always zero out the entire options
+ 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 options fields are:
+
+
+
+
+ ParamListInfo params
+
+
+ data structure containing query parameter types and values; NULL if none
+
+
+
+
+
+ bool read_only
+
+ true for read-only execution
+
+
+
+
+ bool no_snapshots
+
+
+ true prevents SPI from managing snapshots for
+ execution of the query; use with extreme caution
+
+
+
+
+
+ uint64 tcount
+
+
+ maximum number of rows to return,
+ or 0 for no limit
+
+
+
+
+
+ DestReceiver * dest
+
+
+ DestReceiver object that will receive any tuples
+ emitted by the query; if NULL, result tuples are accumulated into
+ a SPI_tuptable structure, as
+ in SPI_execute
+
+
+
+
+
+ ResourceOwner owner
+
+
+ This field is present for consistency
+ with SPI_execute_plan_extended, but it is
+ ignored, since the plan used
+ by SPI_execute_extended is never saved.
+
+
+
+
+
+
+
+ Return Value
+
+
+ The return value is the same as for SPI_execute.
+
+
+
+ When options->dest is NULL,
+ SPI_processed and
+ SPI_tuptable are set as in
+ SPI_execute.
+ When options->dest is not NULL,
+ SPI_processed is set to zero and
+ SPI_tuptable is set to NULL. If a tuple count
+ is required, the caller's DestReceiver object must
+ calculate it.
+
+
+
+
+
+
SPI_execute_with_args
@@ -785,133 +951,6 @@ int SPI_execute_with_args(const char *command,
-
- SPI_execute_with_receiver
-
-
- SPI_execute_with_receiver
- 3
-
-
-
- SPI_execute_with_receiver
- execute a command with out-of-line parameters
-
-
-
-
- int SPI_execute_with_receiver(const char *command,
- ParamListInfo params,
- bool read_only,
- long count,
- DestReceiver *dest)
-
-
-
-
- Description
-
-
- SPI_execute_with_receiver executes a command that might
- include references to externally supplied parameters. The command text
- refers to a parameter as $n,
- and the params object provides values and type
- information for each such symbol.
- read_only and count have
- the same interpretation as in SPI_execute.
-
-
-
- If dest is not NULL, then result tuples are passed
- to that object as they are generated by the executor, instead of being
- accumulated in SPI_tuptable. Using a
- caller-supplied DestReceiver 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 params object should normally mark each
- parameter with the PARAM_FLAG_CONST flag, since
- a one-shot plan is always used for the query.
-
-
-
-
- Arguments
-
-
-
- const char * command
-
-
- command string
-
-
-
-
-
- ParamListInfo params
-
-
- data structure containing parameter types and values; NULL if none
-
-
-
-
-
- bool read_only
-
- true for read-only execution
-
-
-
-
- long count
-
-
- maximum number of rows to return,
- or 0 for no limit
-
-
-
-
-
- DestReceiver * dest
-
-
- DestReceiver object that will receive any tuples
- emitted by the query; if NULL, tuples are returned
- in SPI_tuptable
-
-
-
-
-
-
-
- Return Value
-
-
- The return value is the same as for SPI_execute.
-
-
-
- When dest is NULL,
- SPI_processed and
- SPI_tuptable are set as in
- SPI_execute.
- When dest is not NULL,
- SPI_processed is set to zero and
- SPI_tuptable is set to NULL. If a tuple count
- is required, the caller's DestReceiver object must
- calculate it.
-
-
-
-
-
-
SPI_prepare
@@ -1873,11 +1912,11 @@ int SPI_execute_plan_extended(SPIPlanPtr plan,
- When dest is NULL,
+ When options->dest is NULL,
SPI_processed and
SPI_tuptable are set as in
SPI_execute_plan.
- When dest is not NULL,
+ When options->dest is not NULL,
SPI_processed is set to zero and
SPI_tuptable is set to NULL. If a tuple count
is required, the caller's DestReceiver object must
@@ -2263,6 +2302,12 @@ Portal SPI_cursor_open_with_args(const char *name,
The passed-in parameter data will be copied into the cursor's portal, so it
can be freed while the cursor still exists.
+
+
+ This function is now deprecated in favor
+ of SPI_cursor_parse_open, which provides equivalent
+ functionality using a more modern API for handling query parameters.
+
@@ -2465,26 +2510,24 @@ Portal SPI_cursor_open_with_paramlist(const char *name,
-
- SPI_cursor_parse_open_with_paramlist
+
+ SPI_cursor_parse_open
- SPI_cursor_parse_open_with_paramlist
+ SPI_cursor_parse_open
3
- SPI_cursor_parse_open_with_paramlist
- set up a cursor using a query and parameters
+ SPI_cursor_parse_open
+ set up a cursor using a query string and parameters
-Portal SPI_cursor_parse_open_with_paramlist(const char *name,
- const char *command,
- ParamListInfo params,
- bool read_only,
- int cursorOptions)
+Portal SPI_cursor_parse_open(const char *name,
+ const char *command,
+ const SPIParseOpenOptions * options)
@@ -2492,17 +2535,27 @@ Portal SPI_cursor_parse_open_with_paramlist(const char *nameDescription
- SPI_cursor_parse_open_with_paramlist sets up a cursor
- (internally, a portal) that will execute the specified query. This
- function is equivalent to SPI_cursor_open_with_args
- except that any parameters referenced by the query are provided by
- a ParamListInfo object, rather than in ad-hoc arrays.
+ SPI_cursor_parse_open sets up a cursor
+ (internally, a portal) that will execute the specified query string.
+ This is comparable to SPI_prepare_cursor followed
+ by SPI_cursor_open_with_paramlist, except that
+ parameter references within the query string are handled entirely by
+ supplying a ParamListInfo object.
- The params object should normally mark each
- parameter with the PARAM_FLAG_CONST flag, since
- a one-shot plan is always used for the query.
+ For one-time query execution, this function should be preferred
+ over SPI_prepare_cursor followed by
+ SPI_cursor_open_with_paramlist.
+ 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.
+
+
+
+ The options->params object should normally
+ mark each parameter with the PARAM_FLAG_CONST flag,
+ since a one-shot plan is always used for the query.
@@ -2535,18 +2588,30 @@ Portal SPI_cursor_parse_open_with_paramlist(const char *name
- ParamListInfo params
+ const SPIParseOpenOptions * options
- data structure containing parameter types and values; NULL if none
+ struct containing optional arguments
+
+
+ Callers should always zero out the entire options
+ 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 options fields are:
+
+
+
- bool read_only
+ ParamListInfo params
- true for read-only execution
+
+ data structure containing query parameter types and values; NULL if none
+
@@ -2558,6 +2623,13 @@ Portal SPI_cursor_parse_open_with_paramlist(const char *name
+
+
+ bool read_only
+
+ true for read-only execution
+
+
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",