diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2021-01-26 16:37:12 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2021-01-26 16:37:12 -0500 |
commit | d5a83d79c9f9b660a6a5a77afafe146d3c8c6f46 (patch) | |
tree | e0c3626c5bd5369cf1433a42a1db1d9df9dd53ba /src | |
parent | 7292fd8f1c781278021407276474d9188845113d (diff) | |
download | postgresql-d5a83d79c9f9b660a6a5a77afafe146d3c8c6f46.tar.gz |
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
Diffstat (limited to 'src')
-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 |
3 files changed, 91 insertions, 93 deletions
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 68a6bcea02..00aa78ea53 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 5740f8956e..6455d100f5 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 383d92fc1d..b4c70aaa7f 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", |