Thanks for the detailed review! On Fri, Dec 17, 2021 at 3:04 PM Justin Pryzby <pry...@telsasoft.com> wrote: > > On Mon, Dec 13, 2021 at 05:27:52PM -0500, Melanie Plageman wrote: > > Done in attached v4 > > Thanks. > > I think these comments can be removed from the callers: > /* it's a simple type, so don't use get_call_result_type */
Done in attached v5 > You removed one call to tuplestore_donestoring(), but not the others. > I guess you could remove them all (optionally as a separate patch). I removed it in that one location because I wanted to get rid of the local variable it used. I am fine with removing the other occurrences, but I thought there might be some reason why instead of removing it, they made it into a no-op. > The rest of these are questions more than comments, and I'm not necessarily > suggesting to change the patch: > > This isn't new in your patch, but for me, it's more clear if the callers have > a > separate boolean for randomAccess, rather than having the long expression in > the function call: > > + tupstore = MakeFuncResultTuplestore(fcinfo, NULL, > + rsinfo->allowedModes & SFRM_Materialize_Random); > > vs > > randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; > - tupstore = tuplestore_begin_heap(randomAccess, false, work_mem); > + tupstore = MakeFuncResultTuplestore(fcinfo, NULL, randomAccess); > > One could also argue for passing rsinfo->allowedModes, instead of > (rsinfo->allowedModes & SFRM_Materialize_Random). I've changed the patch to have MakeFuncResultTuplestore() check rsinfo->allowedModes and pass in the resulting boolean to tuplestore_begin_heap(). Because I modified the MakeFuncResultTuplestore() function signature to accommodate this, I had to collapse the two patches into one, as I couldn't maintain the call sites which passed in a constant. > There's a couples places that you're checking expectedDesc where it wasn't > being checked before. Is that some kind of live bug ? > pg_config() text_to_table() Yes, expectedDesc was accessed in these locations without checking that it is not NULL. Should that be a separate patch? > It'd be nice if the "expected tuple format" error didn't need to be duplicated > for each SRFs. I suppose the helper function could just take a boolean > determining whether or not to throw an error (passing "expectedDesc==NULL"). > You'd have to call the helper before CreateTupleDescCopy() - which you're > already doing in a couple places for similar reasons. So, I don't think it makes sense to move that error message into MakeFuncResultTuplestore() for the cases in which NULL is passed in for the tuple descriptor. expectedDesc is not accessed at all in these cases inside the function (since get_call_result_type()) is not called. For the cases when a tuple descriptor is passed in, only two callers actually check for expectedDesc -- each_worker_jsonb and each_worker. Therefore, I don't think it makes sense to add another parameter just to move that check inside for two callers. > I noticed that tuplestore.h isn't included most places. I suppose it's > included via execnodes.h. Your patch doesn't change that, arguably it > should've always been included. I've now included it in funcapi.c. - Melanie
From 8ea7f90162f73049541fb6f53e714298a5ecfc41 Mon Sep 17 00:00:00 2001 From: Melanie Plageman <melanieplage...@gmail.com> Date: Tue, 2 Nov 2021 12:20:55 -0400 Subject: [PATCH v5] Add helper to make tuplestore And use it to refactor out existing duplicate code. Note that for func-api callers previously calling tuplestore_begin_heap() with a constant value for the randomAccess parameter, by using MakeFuncResultTuplestore(), they are now passing true or false based on whether or not SFRM_Materialize_Random is set in rsinfo->allowedModes. This is consistent with the instructions in src/backend/utils/fmgr/README, which state that tuplestores "must be created with randomAccess = true if SFRM_Materialize_Random is set in allowedModes, but it can (and preferably should) be created with randomAccess = false if not". Author: Melanie Plageman <melanieplage...@gmail.com> Reviewed-by: Justin Pryzby <pry...@telsasoft.com> Discussion: https://www.postgresql.org/message-id/flat/CAAKRu_azyd1Z3W_r7Ou4sorTjRCs%2BPxeHw1CWJeXKofkE6TuZg%40mail.gmail.com --- src/backend/access/transam/xlogfuncs.c | 16 +--- src/backend/commands/event_trigger.c | 32 +------ src/backend/commands/extension.c | 48 +---------- src/backend/commands/prepare.c | 15 +--- src/backend/foreign/foreign.c | 51 +++++------ src/backend/libpq/hba.c | 19 +---- src/backend/replication/logical/launcher.c | 16 +--- .../replication/logical/logicalfuncs.c | 18 +--- src/backend/replication/logical/origin.c | 19 +---- src/backend/replication/slotfuncs.c | 16 +--- src/backend/replication/walsender.c | 16 +--- src/backend/storage/ipc/shmem.c | 16 +--- src/backend/utils/adt/datetime.c | 17 +--- src/backend/utils/adt/genfile.c | 31 +------ src/backend/utils/adt/jsonfuncs.c | 84 ++++++------------- src/backend/utils/adt/mcxtfuncs.c | 16 +--- src/backend/utils/adt/misc.c | 14 +--- src/backend/utils/adt/pgstatfuncs.c | 48 +---------- src/backend/utils/adt/varlena.c | 15 ++-- src/backend/utils/fmgr/funcapi.c | 36 ++++++++ src/backend/utils/misc/guc.c | 12 +-- src/backend/utils/misc/pg_config.c | 11 ++- src/backend/utils/mmgr/portalmem.c | 15 +--- src/include/funcapi.h | 2 + 24 files changed, 123 insertions(+), 460 deletions(-) diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c index dd9a45c186..59d77adf6e 100644 --- a/src/backend/access/transam/xlogfuncs.c +++ b/src/backend/access/transam/xlogfuncs.c @@ -178,24 +178,10 @@ pg_stop_backup_v2(PG_FUNCTION_ARGS) XLogRecPtr stoppoint; SessionBackupState status = get_backup_status(); - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index df264329d8..c20473cbb1 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -1306,25 +1306,11 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS) errmsg("%s can only be called in a sql_drop event trigger function", "pg_event_trigger_dropped_objects()"))); - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - /* Build tuplestore to hold the result rows */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; @@ -1864,25 +1850,11 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS) errmsg("%s can only be called in an event trigger function", "pg_event_trigger_ddl_commands()"))); - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - /* Build tuplestore to hold the result rows */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index eaa76af47b..fe9bf52ca8 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -1929,25 +1929,11 @@ pg_available_extensions(PG_FUNCTION_ARGS) DIR *dir; struct dirent *de; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - /* Build tuplestore to hold the result rows */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; @@ -2037,25 +2023,11 @@ pg_available_extension_versions(PG_FUNCTION_ARGS) DIR *dir; struct dirent *de; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - /* Build tuplestore to hold the result rows */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; @@ -2322,25 +2294,11 @@ pg_extension_update_paths(PG_FUNCTION_ARGS) /* Check extension name validity before any filesystem access */ check_valid_extension_name(NameStr(*extname)); - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - /* Build tuplestore to hold the result rows */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index 5e03c7c5aa..32fc055792 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -22,6 +22,7 @@ #include "catalog/pg_type.h" #include "commands/createas.h" #include "commands/prepare.h" +#include "funcapi.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/analyze.h" @@ -706,16 +707,6 @@ pg_prepared_statement(PG_FUNCTION_ARGS) MemoryContext per_query_ctx; MemoryContext oldcontext; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - /* need to build tuplestore in query context */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); @@ -744,9 +735,7 @@ pg_prepared_statement(PG_FUNCTION_ARGS) * We put all the tuples into a tuplestore in one scan of the hashtable. * This avoids any issue of the hashtable possibly changing between calls. */ - tupstore = - tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, - false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, NULL); /* generate junk in short-term context */ MemoryContextSwitchTo(oldcontext); diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index e07cc57431..9bfc5f57f9 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -20,6 +20,7 @@ #include "catalog/pg_user_mapping.h" #include "foreign/fdwapi.h" #include "foreign/foreign.h" +#include "funcapi.h" #include "lib/stringinfo.h" #include "miscadmin.h" #include "utils/builtins.h" @@ -499,30 +500,34 @@ IsImportableForeignTable(const char *tablename, /* - * deflist_to_tuplestore - Helper function to convert DefElem list to - * tuplestore usable in SRF. + * Convert options array to name/value table. Useful for information + * schema and pg_dump. */ -static void -deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options) +Datum +pg_options_to_table(PG_FUNCTION_ARGS) { + Datum array = PG_GETARG_DATUM(0); ListCell *cell; + List *options; + + ReturnSetInfo *rsinfo; TupleDesc tupdesc; Tuplestorestate *tupstore; - Datum values[2]; - bool nulls[2]; + MemoryContext per_query_ctx; MemoryContext oldcontext; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize) || - rsinfo->expectedDesc == NULL) + Datum values[2]; + bool nulls[2]; + + options = untransformRelOptions(array); + rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + if (!rsinfo->expectedDesc) ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); + (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), + errmsg("expected tuple format not specified as required for " + "set-returning function."))); + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); @@ -531,7 +536,7 @@ deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options) * Now prepare the result set. */ tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, NULL); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; @@ -559,20 +564,6 @@ deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options) tuplestore_donestoring(tupstore); MemoryContextSwitchTo(oldcontext); -} - - -/* - * Convert options array to name/value table. Useful for information - * schema and pg_dump. - */ -Datum -pg_options_to_table(PG_FUNCTION_ARGS) -{ - Datum array = PG_GETARG_DATUM(0); - - deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, - untransformRelOptions(array)); return (Datum) 0; } diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 4328eb74fe..986f9be09d 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -2710,29 +2710,12 @@ pg_hba_file_rules(PG_FUNCTION_ARGS) * up our current position in the parsed list every time. */ rsi = (ReturnSetInfo *) fcinfo->resultinfo; - - /* Check to see if caller supports us returning a tuplestore */ - if (rsi == NULL || !IsA(rsi, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsi->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - rsi->returnMode = SFRM_Materialize; - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - /* Build tuplestore to hold the result rows */ old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory); - tuple_store = - tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random, - false, work_mem); + tuple_store = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsi->setDesc = tupdesc; rsi->setResult = tuple_store; diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c index 3fb4caa803..f1d2665dce 100644 --- a/src/backend/replication/logical/launcher.c +++ b/src/backend/replication/logical/launcher.c @@ -935,24 +935,10 @@ pg_stat_get_subscription(PG_FUNCTION_ARGS) MemoryContext per_query_ctx; MemoryContext oldcontext; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c index 6cd2279a2e..662006e518 100644 --- a/src/backend/replication/logical/logicalfuncs.c +++ b/src/backend/replication/logical/logicalfuncs.c @@ -141,25 +141,11 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin errmsg("options array must not be null"))); arr = PG_GETARG_ARRAYTYPE_P(3); - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - /* state to write output to */ p = palloc0(sizeof(DecodingOutputState)); p->binary_output = binary; - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); @@ -202,7 +188,7 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin } } - p->tupstore = tuplestore_begin_heap(true, false, work_mem); + p->tupstore = MakeFuncResultTuplestore(fcinfo, &p->tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = p->tupstore; rsinfo->setDesc = p->tupdesc; @@ -295,8 +281,6 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin CHECK_FOR_INTERRUPTS(); } - tuplestore_donestoring(tupstore); - /* * Logical decoding could have clobbered CurrentResourceOwner during * transaction management, so restore the executor's value. (This is diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c index 65dcd033fd..a88cdc2f17 100644 --- a/src/backend/replication/logical/origin.c +++ b/src/backend/replication/logical/origin.c @@ -1491,30 +1491,19 @@ pg_show_replication_origin_status(PG_FUNCTION_ARGS) /* we want to return 0 rows if slot is set to zero */ replorigin_check_prerequisites(false, true); - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - - if (tupdesc->natts != REPLICATION_ORIGIN_PROGRESS_COLS) - elog(ERROR, "wrong function definition"); - per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); + rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); + if (tupdesc->natts != REPLICATION_ORIGIN_PROGRESS_COLS) + elog(ERROR, "wrong function definition"); /* prevent slots from being concurrently dropped */ LWLockAcquire(ReplicationOriginLock, LW_SHARED); diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c index d11daeb1fc..fb2123e789 100644 --- a/src/backend/replication/slotfuncs.c +++ b/src/backend/replication/slotfuncs.c @@ -239,20 +239,6 @@ pg_get_replication_slots(PG_FUNCTION_ARGS) XLogRecPtr currlsn; int slotno; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - /* * We don't require any special permission to see this function's data * because nothing should be sensitive. The most critical being the slot @@ -262,7 +248,7 @@ pg_get_replication_slots(PG_FUNCTION_ARGS) per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index 84915ed95b..cc3e6185df 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -3401,24 +3401,10 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS) int num_standbys; int i; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c index 4425e99f17..ec4dd9caf8 100644 --- a/src/backend/storage/ipc/shmem.c +++ b/src/backend/storage/ipc/shmem.c @@ -547,24 +547,10 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS) Datum values[PG_GET_SHMEM_SIZES_COLS]; bool nulls[PG_GET_SHMEM_SIZES_COLS]; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index cb3fa85892..83b6b2bb33 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -4786,7 +4786,6 @@ Datum pg_timezone_names(PG_FUNCTION_ARGS) { ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; - bool randomAccess; TupleDesc tupdesc; Tuplestorestate *tupstore; pg_tzenum *tzenum; @@ -4801,24 +4800,10 @@ pg_timezone_names(PG_FUNCTION_ARGS) struct pg_tm itm; MemoryContext oldcontext; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("materialize mode required, but it is not allowed in this context"))); - /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */ oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory); - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - - randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; - tupstore = tuplestore_begin_heap(randomAccess, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c index 027ed86400..4b08e12527 100644 --- a/src/backend/utils/adt/genfile.c +++ b/src/backend/utils/adt/genfile.c @@ -483,7 +483,6 @@ pg_ls_dir(PG_FUNCTION_ARGS) char *location; bool missing_ok = false; bool include_dot_dirs = false; - bool randomAccess; TupleDesc tupdesc; Tuplestorestate *tupstore; DIR *dirdesc; @@ -501,24 +500,13 @@ pg_ls_dir(PG_FUNCTION_ARGS) include_dot_dirs = PG_GETARG_BOOL(2); } - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("materialize mode required, but it is not allowed in this context"))); - /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */ oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory); tupdesc = CreateTemplateTupleDesc(1); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pg_ls_dir", TEXTOID, -1, 0); - randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; - tupstore = tuplestore_begin_heap(randomAccess, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, NULL); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; @@ -577,31 +565,16 @@ static Datum pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok) { ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; - bool randomAccess; TupleDesc tupdesc; Tuplestorestate *tupstore; DIR *dirdesc; struct dirent *de; MemoryContext oldcontext; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("materialize mode required, but it is not allowed in this context"))); - /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */ oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory); - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - - randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; - tupstore = tuplestore_begin_heap(randomAccess, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index 6335845d08..d0f5e61c58 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -1926,30 +1926,20 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text) funcname))); rsi = (ReturnSetInfo *) fcinfo->resultinfo; - - if (!rsi || !IsA(rsi, ReturnSetInfo) || - (rsi->allowedModes & SFRM_Materialize) == 0 || - rsi->expectedDesc == NULL) + if (!rsi->expectedDesc) ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that " - "cannot accept a set"))); + (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), + errmsg("expected tuple format not specified as required for " + "set-returning function."))); rsi->returnMode = SFRM_Materialize; - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("function returning record called in context " - "that cannot accept type record"))); - old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory); + tuple_store = MakeFuncResultTuplestore(fcinfo, &tupdesc); + ret_tdesc = CreateTupleDescCopy(tupdesc); BlessTupleDesc(ret_tdesc); - tuple_store = - tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random, - false, work_mem); MemoryContextSwitchTo(old_cxt); @@ -2038,27 +2028,21 @@ each_worker(FunctionCallInfo fcinfo, bool as_text) sem = palloc0(sizeof(JsonSemAction)); rsi = (ReturnSetInfo *) fcinfo->resultinfo; - - if (!rsi || !IsA(rsi, ReturnSetInfo) || - (rsi->allowedModes & SFRM_Materialize) == 0 || - rsi->expectedDesc == NULL) + if (!rsi->expectedDesc) ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that " - "cannot accept a set"))); + (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), + errmsg("expected tuple format not specified as required for " + "set-returning function."))); rsi->returnMode = SFRM_Materialize; - (void) get_call_result_type(fcinfo, NULL, &tupdesc); - /* make these in a sufficiently long-lived memory context */ old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory); + state->tuple_store = MakeFuncResultTuplestore(fcinfo, &tupdesc); + state->ret_tdesc = CreateTupleDescCopy(tupdesc); BlessTupleDesc(state->ret_tdesc); - state->tuple_store = - tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random, - false, work_mem); MemoryContextSwitchTo(old_cxt); @@ -2226,27 +2210,21 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, errmsg("cannot extract elements from an object"))); rsi = (ReturnSetInfo *) fcinfo->resultinfo; - - if (!rsi || !IsA(rsi, ReturnSetInfo) || - (rsi->allowedModes & SFRM_Materialize) == 0 || - rsi->expectedDesc == NULL) + if (!rsi->expectedDesc) ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that " - "cannot accept a set"))); + (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), + errmsg("expected tuple format not specified as required for " + "set-returning function."))); rsi->returnMode = SFRM_Materialize; - /* it's a simple type, so don't use get_call_result_type() */ tupdesc = rsi->expectedDesc; old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory); ret_tdesc = CreateTupleDescCopy(tupdesc); BlessTupleDesc(ret_tdesc); - tuple_store = - tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random, - false, work_mem); + tuple_store = MakeFuncResultTuplestore(fcinfo, NULL); MemoryContextSwitchTo(old_cxt); @@ -2335,18 +2313,15 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text) sem = palloc0(sizeof(JsonSemAction)); rsi = (ReturnSetInfo *) fcinfo->resultinfo; - - if (!rsi || !IsA(rsi, ReturnSetInfo) || - (rsi->allowedModes & SFRM_Materialize) == 0 || - rsi->expectedDesc == NULL) + if (!rsi->expectedDesc) ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that " - "cannot accept a set"))); + (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), + errmsg("expected tuple format not specified as required for " + "set-returning function."))); + rsi->returnMode = SFRM_Materialize; - /* it's a simple type, so don't use get_call_result_type() */ tupdesc = rsi->expectedDesc; /* make these in a sufficiently long-lived memory context */ @@ -2354,9 +2329,7 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text) state->ret_tdesc = CreateTupleDescCopy(tupdesc); BlessTupleDesc(state->ret_tdesc); - state->tuple_store = - tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random, - false, work_mem); + state->tuple_store = MakeFuncResultTuplestore(fcinfo, NULL); MemoryContextSwitchTo(old_cxt); @@ -3798,13 +3771,6 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname, rsi = (ReturnSetInfo *) fcinfo->resultinfo; - if (!rsi || !IsA(rsi, ReturnSetInfo) || - (rsi->allowedModes & SFRM_Materialize) == 0) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that " - "cannot accept a set"))); - rsi->returnMode = SFRM_Materialize; /* @@ -3871,9 +3837,7 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname, /* make tuplestore in a sufficiently long-lived memory context */ old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory); - state->tuple_store = tuplestore_begin_heap(rsi->allowedModes & - SFRM_Materialize_Random, - false, work_mem); + state->tuple_store = MakeFuncResultTuplestore(fcinfo, NULL); MemoryContextSwitchTo(old_cxt); state->function_name = funcname; diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c index 6ddbf70b30..bc1ba622b0 100644 --- a/src/backend/utils/adt/mcxtfuncs.c +++ b/src/backend/utils/adt/mcxtfuncs.c @@ -125,24 +125,10 @@ pg_get_backend_memory_contexts(PG_FUNCTION_ARGS) MemoryContext per_query_ctx; MemoryContext oldcontext; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index 88faf4dfd7..8398657a93 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -203,7 +203,6 @@ pg_tablespace_databases(PG_FUNCTION_ARGS) { Oid tablespaceOid = PG_GETARG_OID(0); ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; - bool randomAccess; TupleDesc tupdesc; Tuplestorestate *tupstore; char *location; @@ -211,16 +210,6 @@ pg_tablespace_databases(PG_FUNCTION_ARGS) struct dirent *de; MemoryContext oldcontext; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("materialize mode required, but it is not allowed in this context"))); - /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */ oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory); @@ -228,8 +217,7 @@ pg_tablespace_databases(PG_FUNCTION_ARGS) TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pg_tablespace_databases", OIDOID, -1, 0); - randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; - tupstore = tuplestore_begin_heap(randomAccess, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, NULL); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index f529c1561a..37c5c9d765 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -467,20 +467,6 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS) MemoryContext per_query_ctx; MemoryContext oldcontext; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - /* Translate command name into command type code. */ if (pg_strcasecmp(cmd, "VACUUM") == 0) cmdtype = PROGRESS_COMMAND_VACUUM; @@ -502,7 +488,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS) per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; @@ -577,24 +563,10 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) MemoryContext per_query_ctx; MemoryContext oldcontext; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; @@ -1879,24 +1851,10 @@ pg_stat_get_slru(PG_FUNCTION_ARGS) int i; PgStat_SLRUStats *stats; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - - /* Build a tuple descriptor for our result type */ - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) - elog(ERROR, "return type must be a row type"); - per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index bd3091bbfb..07cfae983d 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -24,6 +24,7 @@ #include "common/hashfn.h" #include "common/int.h" #include "common/unicode_norm.h" +#include "funcapi.h" #include "lib/hyperloglog.h" #include "libpq/pqformat.h" #include "miscadmin.h" @@ -4844,22 +4845,18 @@ text_to_table(PG_FUNCTION_ARGS) SplitTextOutputData tstate; MemoryContext old_cxt; - /* check to see if caller supports us returning a tuplestore */ - if (rsi == NULL || !IsA(rsi, ReturnSetInfo)) + if (!rsi->expectedDesc) ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsi->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); + (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), + errmsg("expected tuple format not specified as required for " + "set-returning function."))); /* OK, prepare tuplestore in per-query memory */ old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory); tstate.astate = NULL; tstate.tupdesc = CreateTupleDescCopy(rsi->expectedDesc); - tstate.tupstore = tuplestore_begin_heap(true, false, work_mem); + tstate.tupstore = MakeFuncResultTuplestore(fcinfo, NULL); MemoryContextSwitchTo(old_cxt); diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index e94b8037ec..105219314c 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -13,6 +13,7 @@ */ #include "postgres.h" +#include "miscadmin.h" #include "access/htup_details.h" #include "access/relation.h" #include "catalog/namespace.h" @@ -27,6 +28,7 @@ #include "utils/regproc.h" #include "utils/rel.h" #include "utils/syscache.h" +#include "utils/tuplestore.h" #include "utils/typcache.h" @@ -1757,7 +1759,41 @@ build_function_result_tupdesc_d(char prokind, return desc; } +/* + * Helper function to construct tuplestore + */ +Tuplestorestate * +MakeFuncResultTuplestore(FunctionCallInfo fcinfo, TupleDesc *tupdesc) +{ + bool random_access; + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + Tuplestorestate *tupstore; + + /* Must be called in per query memory context */ + Assert(CurrentMemoryContext == rsinfo->econtext->ecxt_per_query_memory); + + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not allowed in this context"))); + + /* If needed, build a tuple descriptor for our result type */ + if (tupdesc) + { + if (get_call_result_type(fcinfo, NULL, tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + } + random_access = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; + tupstore = tuplestore_begin_heap(random_access, false, work_mem); + + return tupstore; +} /* * RelationNameGetTupleDesc diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index f9504d3aec..b113870533 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10052,16 +10052,6 @@ show_all_file_settings(PG_FUNCTION_ARGS) MemoryContext per_query_ctx; MemoryContext oldcontext; - /* Check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - /* Scan the config files using current context as workspace */ conf = ProcessConfigFileInternal(PGC_SIGHUP, false, DEBUG3); @@ -10087,7 +10077,7 @@ show_all_file_settings(PG_FUNCTION_ARGS) TEXTOID, -1, 0); /* Build a tuplestore to return our results in */ - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, NULL); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; diff --git a/src/backend/utils/misc/pg_config.c b/src/backend/utils/misc/pg_config.c index 34d77db75a..6d75d3ebcb 100644 --- a/src/backend/utils/misc/pg_config.c +++ b/src/backend/utils/misc/pg_config.c @@ -36,12 +36,11 @@ pg_config(PG_FUNCTION_ARGS) char *values[2]; int i = 0; - /* check to see if caller supports us returning a tuplestore */ - if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize)) + if (!rsinfo->expectedDesc) ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("materialize mode required, but it is not " - "allowed in this context"))); + (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), + errmsg("expected tuple format not specified as required for " + "set-returning function."))); per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); @@ -67,7 +66,7 @@ pg_config(PG_FUNCTION_ARGS) rsinfo->returnMode = SFRM_Materialize; /* initialize our tuplestore */ - tupstore = tuplestore_begin_heap(true, false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, NULL); configdata = get_configdata(my_exec_path, &configdata_len); for (i = 0; i < configdata_len; i++) diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c index 58674d611d..c68317d038 100644 --- a/src/backend/utils/mmgr/portalmem.c +++ b/src/backend/utils/mmgr/portalmem.c @@ -21,6 +21,7 @@ #include "access/xact.h" #include "catalog/pg_type.h" #include "commands/portalcmds.h" +#include "funcapi.h" #include "miscadmin.h" #include "storage/ipc.h" #include "utils/builtins.h" @@ -1138,16 +1139,6 @@ pg_cursor(PG_FUNCTION_ARGS) HASH_SEQ_STATUS hash_seq; PortalHashEnt *hentry; - /* check to see if caller supports us returning a tuplestore */ - if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (!(rsinfo->allowedModes & SFRM_Materialize)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialize mode required, but it is not allowed in this context"))); - /* need to build tuplestore in query context */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); @@ -1174,9 +1165,7 @@ pg_cursor(PG_FUNCTION_ARGS) * We put all the tuples into a tuplestore in one scan of the hashtable. * This avoids any issue of the hashtable possibly changing between calls. */ - tupstore = - tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, - false, work_mem); + tupstore = MakeFuncResultTuplestore(fcinfo, NULL); /* generate junk in short-term context */ MemoryContextSwitchTo(oldcontext); diff --git a/src/include/funcapi.h b/src/include/funcapi.h index f1304d47e3..09d8e8dd91 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -229,6 +229,8 @@ extern TupleDesc BlessTupleDesc(TupleDesc tupdesc); extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc); extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values); extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple); +extern Tuplestorestate *MakeFuncResultTuplestore(FunctionCallInfo fcinfo, + TupleDesc *result_tupdesc); /*---------- -- 2.30.2