mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +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:
@ -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",
|
||||
|
Reference in New Issue
Block a user