make tuplestore helper function
Attached is a patch to create a helper function which creates a
tuplestore to be used by FUNCAPI-callable functions.
It was suggested in [1]/messages/by-id/20200124195226.lth52iydq2n2uilq@alap3.anarazel.de that I start a separate thread for this patch.
A few notes:
There are a few places with very similar code to the new helper
(MakeTuplestore()) but which, for example, don't expect the return type
to be a row (pg_ls_dir(), pg_tablespace_databases(), text_to_table()). I
wasn't sure if it was worth modifying it for their benefit.
I wasn't sure if the elog() check for call result type should be changed
to an ereport().
All callers of MakeTuplestore() pass work_mem as the third parameter, so
I could just omit it and hard-code it in, but I wasn't sure that felt
right in a utility/helper function.
I inlined deflist_to_tuplestore() in foreign.c since it was billed as a
helper function but wasn't used elsewhere and it made it harder to use
MakeTuplestore() in this location.
-- melanie
[1]: /messages/by-id/20200124195226.lth52iydq2n2uilq@alap3.anarazel.de
Attachments:
v1-0001-Add-helper-to-make-tuplestore.patchtext/x-patch; charset=US-ASCII; name=v1-0001-Add-helper-to-make-tuplestore.patchDownload
From 69e41c23590637c171e985c700b8cfececbbf1f2 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Tue, 2 Nov 2021 12:20:55 -0400
Subject: [PATCH v1] Add helper to make tuplestore
And use it to refactor out existing duplicate code.
---
src/backend/access/transam/xlogfuncs.c | 27 +------
src/backend/commands/event_trigger.c | 56 +--------------
src/backend/commands/extension.c | 82 +---------------------
src/backend/commands/prepare.c | 34 ++-------
src/backend/foreign/foreign.c | 53 +++-----------
src/backend/libpq/hba.c | 31 +-------
src/backend/replication/logical/launcher.c | 27 +------
src/backend/replication/logical/origin.c | 25 +------
src/backend/replication/slotfuncs.c | 27 +------
src/backend/replication/walsender.c | 27 +------
src/backend/storage/ipc/shmem.c | 27 +------
src/backend/utils/adt/datetime.c | 27 +------
src/backend/utils/adt/genfile.c | 28 +-------
src/backend/utils/adt/mcxtfuncs.c | 29 +-------
src/backend/utils/adt/pgstatfuncs.c | 80 +--------------------
src/backend/utils/fmgr/funcapi.c | 34 +++++++++
src/backend/utils/misc/guc.c | 26 +------
src/backend/utils/mmgr/portalmem.c | 31 +-------
src/include/funcapi.h | 1 +
19 files changed, 72 insertions(+), 600 deletions(-)
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index b98deb72ec..9b87296f2b 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -165,11 +165,8 @@ pg_stop_backup(PG_FUNCTION_ARGS)
Datum
pg_stop_backup_v2(PG_FUNCTION_ARGS)
{
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
Datum values[3];
bool nulls[3];
@@ -178,29 +175,7 @@ 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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
MemSet(values, 0, sizeof(values));
MemSet(nulls, 0, sizeof(nulls));
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index df264329d8..3ea5a9b851 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1289,11 +1289,8 @@ EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool no
Datum
pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
{
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
slist_iter iter;
/*
@@ -1306,30 +1303,7 @@ 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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
slist_foreach(iter, &(currentEventTriggerState->SQLDropList))
{
@@ -1848,11 +1822,8 @@ EventTriggerCollectAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt)
Datum
pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
{
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
ListCell *lc;
/*
@@ -1864,30 +1835,7 @@ 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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
foreach(lc, currentEventTriggerState->commandList)
{
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index eaa76af47b..62b6a5d68a 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1920,39 +1920,14 @@ RemoveExtensionById(Oid extId)
Datum
pg_available_extensions(PG_FUNCTION_ARGS)
{
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
char *location;
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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
location = get_extension_control_directory();
dir = AllocateDir(location);
@@ -2028,39 +2003,13 @@ pg_available_extensions(PG_FUNCTION_ARGS)
Datum
pg_available_extension_versions(PG_FUNCTION_ARGS)
{
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
char *location;
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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
location = get_extension_control_directory();
dir = AllocateDir(location);
@@ -2310,11 +2259,8 @@ Datum
pg_extension_update_paths(PG_FUNCTION_ARGS)
{
Name extname = PG_GETARG_NAME(0);
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
List *evi_list;
ExtensionControlFile *control;
ListCell *lc1;
@@ -2322,30 +2268,8 @@ 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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
- MemoryContextSwitchTo(oldcontext);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
/* Read the extension's control file */
control = read_extension_control_file(NameStr(*extname));
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 5e03c7c5aa..b632021b1e 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"
@@ -700,25 +701,8 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
Datum
pg_prepared_statement(PG_FUNCTION_ARGS)
{
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- 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);
/*
* build tupdesc for result tuples. This must match the definition of the
@@ -740,18 +724,14 @@ pg_prepared_statement(PG_FUNCTION_ARGS)
TupleDescInitEntry(tupdesc, (AttrNumber) 7, "custom_plans",
INT8OID, -1, 0);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
+
/*
* 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.
+ *
+ * Handle if hash table is uninitialized.
*/
- tupstore =
- tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
- false, work_mem);
-
- /* generate junk in short-term context */
- MemoryContextSwitchTo(oldcontext);
-
- /* hash table might be uninitialized */
if (prepared_queries)
{
HASH_SEQ_STATUS hash_seq;
@@ -781,10 +761,6 @@ pg_prepared_statement(PG_FUNCTION_ARGS)
/* clean up and return the tuplestore */
tuplestore_donestoring(tupstore);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
return (Datum) 0;
}
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index e07cc57431..457563835a 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,42 +500,23 @@ 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)
{
+ ReturnSetInfo *rsinfo = (ReturnSetInfo*) fcinfo->resultinfo;
ListCell *cell;
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)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
+ Datum array = PG_GETARG_DATUM(0);
+ List *options = untransformRelOptions(array);
- /*
- * Now prepare the result set.
- */
tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
- tupstore = tuplestore_begin_heap(true, false, work_mem);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
foreach(cell, options)
{
@@ -555,25 +537,8 @@ deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options)
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
- /* clean up and return the tuplestore */
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 3be8778d21..aa7d2c048e 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -2701,42 +2701,13 @@ pg_hba_file_rules(PG_FUNCTION_ARGS)
{
Tuplestorestate *tuple_store;
TupleDesc tupdesc;
- MemoryContext old_cxt;
- ReturnSetInfo *rsi;
/*
* We must use the Materialize mode to be safe against HBA file changes
* while the cursor is open. It's also more efficient than having to look
* 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);
- rsi->setDesc = tupdesc;
- rsi->setResult = tuple_store;
-
- MemoryContextSwitchTo(old_cxt);
+ tuple_store = MakeTuplestore(fcinfo, &tupdesc, work_mem);
/* Fill the tuplestore */
fill_hba_view(tuple_store, tupdesc);
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 3fb4caa803..f6a9c41e94 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -929,35 +929,10 @@ pg_stat_get_subscription(PG_FUNCTION_ARGS)
#define PG_STAT_GET_SUBSCRIPTION_COLS 8
Oid subid = PG_ARGISNULL(0) ? InvalidOid : PG_GETARG_OID(0);
int i;
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- 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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
/* Make sure we get consistent view of the workers. */
LWLockAcquire(LogicalRepWorkerLock, LW_SHARED);
diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c
index 65dcd033fd..8884daf806 100644
--- a/src/backend/replication/logical/origin.c
+++ b/src/backend/replication/logical/origin.c
@@ -1480,42 +1480,19 @@ pg_replication_origin_progress(PG_FUNCTION_ARGS)
Datum
pg_show_replication_origin_status(PG_FUNCTION_ARGS)
{
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
int i;
#define REPLICATION_ORIGIN_PROGRESS_COLS 4
/* 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");
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
-
-
/* 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 17df99c2ac..78b3cf9d25 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -231,43 +231,18 @@ Datum
pg_get_replication_slots(PG_FUNCTION_ARGS)
{
#define PG_GET_REPLICATION_SLOTS_COLS 14
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
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
* name, which shouldn't contain anything particularly sensitive.
*/
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = tuplestore_begin_heap(true, false, work_mem);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
currlsn = GetXLogWriteRecPtr();
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index d9ab6d6de2..96c5fff486 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -3389,38 +3389,13 @@ Datum
pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
{
#define PG_STAT_GET_WAL_SENDERS_COLS 12
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
SyncRepStandbyData *sync_standbys;
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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
/*
* Get the currently active synchronous standbys. This could be out of
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 4425e99f17..de7c2e81a7 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -536,40 +536,15 @@ Datum
pg_get_shmem_allocations(PG_FUNCTION_ARGS)
{
#define PG_GET_SHMEM_SIZES_COLS 4
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
HASH_SEQ_STATUS hstat;
ShmemIndexEnt *ent;
Size named_allocated = 0;
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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
LWLockAcquire(ShmemIndexLock, LW_SHARED);
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index cb3fa85892..1d6d0400d4 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -4785,8 +4785,6 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS)
Datum
pg_timezone_names(PG_FUNCTION_ARGS)
{
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- bool randomAccess;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
pg_tzenum *tzenum;
@@ -4799,31 +4797,8 @@ pg_timezone_names(PG_FUNCTION_ARGS)
const char *tzn;
Interval *resInterval;
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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
/* initialize timezone scanning code */
tzenum = pg_tzenumerate_start();
diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index c436d9318b..33f0c4cf14 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -575,38 +575,12 @@ pg_ls_dir_1arg(PG_FUNCTION_ARGS)
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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
/*
* Now walk the directory. Note that we must do this within a single SRF
* call, not leave the directory open across multiple calls, since we
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 6ddbf70b30..45a2186486 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -119,35 +119,10 @@ PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
Datum
pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
{
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- 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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
PutMemoryContextsStatsTupleStore(tupstore, tupdesc,
TopMemoryContext, NULL, 0);
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index ff5aedc99c..ea3d2c45f2 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -463,23 +463,6 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
ProgressCommandType cmdtype;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- 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)
@@ -499,14 +482,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid command name: \"%s\"", cmd)));
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = tuplestore_begin_heap(true, false, work_mem);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
- MemoryContextSwitchTo(oldcontext);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
/* 1-based index */
for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
@@ -571,35 +547,10 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
int num_backends = pgstat_fetch_stat_numbackends();
int curr_backend;
int pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- 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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
/* 1-based index */
for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
@@ -1871,37 +1822,12 @@ Datum
pg_stat_get_slru(PG_FUNCTION_ARGS)
{
#define PG_STAT_GET_SLRU_COLS 9
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
/* request SLRU stats from the stat collector */
stats = pgstat_fetch_slru();
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index e94b8037ec..673dc0ed25 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -1758,6 +1758,40 @@ build_function_result_tupdesc_d(char prokind,
return desc;
}
+/*
+ * Helper function to construct tuplestore
+ */
+Tuplestorestate *MakeTuplestore(FunctionCallInfo fcinfo, TupleDesc *tupdesc, int maxKBytes)
+{
+ Tuplestorestate *tupstore;
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ 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, maxKBytes);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = *tupdesc;
+ MemoryContextSwitchTo(oldcontext);
+ return tupstore;
+}
/*
* RelationNameGetTupleDesc
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index e91d5a3cfd..0aece91654 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10031,31 +10031,14 @@ Datum
show_all_file_settings(PG_FUNCTION_ARGS)
{
#define NUM_PG_FILE_SETTINGS_ATTS 7
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
ConfigVariable *conf;
int seqno;
- 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);
- /* Switch into long-lived context to construct returned data structures */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
/* Build a tuple descriptor for our result type */
tupdesc = CreateTemplateTupleDesc(NUM_PG_FILE_SETTINGS_ATTS);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "sourcefile",
@@ -10073,14 +10056,7 @@ show_all_file_settings(PG_FUNCTION_ARGS)
TupleDescInitEntry(tupdesc, (AttrNumber) 7, "error",
TEXTOID, -1, 0);
- /* Build a tuplestore to return our results in */
- tupstore = tuplestore_begin_heap(true, false, work_mem);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- /* The rest can be done in short-lived context */
- MemoryContextSwitchTo(oldcontext);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
/* Process the results and create a tuplestore */
for (seqno = 1; conf != NULL; conf = conf->next, seqno++)
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 58674d611d..5235123bb9 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"
@@ -1130,28 +1131,11 @@ AtSubCleanup_Portals(SubTransactionId mySubid)
Datum
pg_cursor(PG_FUNCTION_ARGS)
{
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
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);
-
/*
* build tupdesc for result tuples. This must match the definition of the
* pg_cursors view in system_views.sql
@@ -1170,17 +1154,12 @@ pg_cursor(PG_FUNCTION_ARGS)
TupleDescInitEntry(tupdesc, (AttrNumber) 6, "creation_time",
TIMESTAMPTZOID, -1, 0);
+ tupstore = MakeTuplestore(fcinfo, &tupdesc, work_mem);
+
/*
* 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);
-
- /* generate junk in short-term context */
- MemoryContextSwitchTo(oldcontext);
-
hash_seq_init(&hash_seq, PortalHashTable);
while ((hentry = hash_seq_search(&hash_seq)) != NULL)
{
@@ -1207,10 +1186,6 @@ pg_cursor(PG_FUNCTION_ARGS)
/* clean up and return the tuplestore */
tuplestore_donestoring(tupstore);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
return (Datum) 0;
}
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index f1304d47e3..8afe4b7259 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -229,6 +229,7 @@ extern TupleDesc BlessTupleDesc(TupleDesc tupdesc);
extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
+extern Tuplestorestate *MakeTuplestore(FunctionCallInfo fcinfo, TupleDesc *tupdesc, int maxKBytes);
/*----------
--
2.30.2
On 2021-Nov-02, Melanie Plageman wrote:
Attached is a patch to create a helper function which creates a
tuplestore to be used by FUNCAPI-callable functions.
Looks good, and given the amount of code being removed, it seems a
no-brainer that some helper(s) is/are needed.
I think the name is a bit too generic. How about
MakeFuncResultTuplestore()?
I think the function should not modify rsinfo -- having that as
(undocumented) side effect is weird. I would suggest to just return the
tuplestore, and have the caller stash it in rsinfo->setResult and modify
the other struct members alongside that. It's a couple more lines per
caller, but the code is clearer that way.
There are a few places with very similar code to the new helper
(MakeTuplestore()) but which, for example, don't expect the return type
to be a row (pg_ls_dir(), pg_tablespace_databases(), text_to_table()). I
wasn't sure if it was worth modifying it for their benefit.
Is this just because of the tupdesc? If that's the case, then maybe the
tupdesc can be optionally passed in by caller, and when it's not, the
helper does get_call_result_type() and the tupdesc is used as output
argument.
I wasn't sure if the elog() check for call result type should be changed
to an ereport().
I don't see a reason for that: it's still an internal (not user-facing) error.
All callers of MakeTuplestore() pass work_mem as the third parameter, so
I could just omit it and hard-code it in, but I wasn't sure that felt
right in a utility/helper function.
No opinion. I would have used the global in the function instead of
passing it in.
I inlined deflist_to_tuplestore() in foreign.c since it was billed as a
helper function but wasn't used elsewhere and it made it harder to use
MakeTuplestore() in this location.
This is a bit strange. Does it work to pass rsinfo->expectedDesc?
--
Álvaro Herrera Valdivia, Chile — https://www.EnterpriseDB.com/
"No renuncies a nada. No te aferres a nada."
Several places have a conditional value for the first argument (randomAccess),
but your patch changes the behavior to a constant "true". I didn't review the
patch beyond that.
@@ -740,18 +724,14 @@ pg_prepared_statement(PG_FUNCTION_ARGS) - tupstore = - tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, - false, work_mem);
@@ -2701,42 +2701,13 @@ pg_hba_file_rules(PG_FUNCTION_ARGS) - tuple_store = - tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random, - false, work_mem);
@@ -4799,31 +4797,8 @@ pg_timezone_names(PG_FUNCTION_ARGS) - randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; - tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
@@ -575,38 +575,12 @@ pg_ls_dir_1arg(PG_FUNCTION_ARGS) - randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; - tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
@@ -1170,17 +1154,12 @@ pg_cursor(PG_FUNCTION_ARGS) - tupstore = - tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, - false, work_mem);
+++ b/src/backend/utils/fmgr/funcapi.c + tupstore = tuplestore_begin_heap(true, false, maxKBytes);
--
Justin
The attached patch contains a helper now named
MakeFuncResultTuplestore() which aims to eliminate a few lines of
redundant code that all FUNCAPI functions making tuplestores repeated.
While investigating all of these call sites and making the changes
suggested by Álvaro, I noticed that the functions making tuplestores
broke out into seven groups. For some of them, using my helper changes
what memory context the tuple descriptor is made in.
Explanation of how/why memory context tuple descriptor is in has
changed:
An earlier version of this patch changed into the per query memory
context inside of the helper function. This version does not do so but
instead asserts that the caller has changed into the appropriate memory
context.
For callers which previously did not call get_call_result_type() and
instead used a function like CreateTupleDescCopy() to create the tuple
descriptor which was used for the tuples in the tuplestore, most of them
made this copy in the per query memory context. For these callers it
seemed error-prone to switch to the per query memory context to create
the copied tuple descriptor and then switch back before calling the
helper function. Instead, the helper function will assert that the
caller has already switched into the per query memory context so that
the tuplestore is made in the per query context.
This means that for the group of functions which, prior to this patch,
called get_call_result_type() in a shorter-lived memory context but now
rely on the helper function to call get_call_result_type(), the tuple
descriptor will now be in the per query memory context.
I think that this makes sense since most of these are set as the
ResultSetInfo->setDesc (the tuple descriptor for returned tuples).
Explanation of the different groups of functions using the
MakeFuncResultTuplestore() helper:
The first group are functions which called get_call_result_type(),
allocating memory for the tuple descriptor in the short-lived memory
context, then switched into the per query memory context before
creating the tuplestore, then switched back to the short-lived memory
context directly after making the tuplestore.
Now that they use the helper function and switch into the per query
memory context before calling it, the tuple descriptor is allocated in
the per query memory context.
The first group:
pg_stop_backup_v2()
pg_event_trigger_dropped_objects()
pg_event_trigger_ddl_commands()
pg_available_extensions()
pg_available_extension_versions()
pg_extension_update_paths()
pg_hba_file_rules()
pg_stat_get_subscription()
pg_logical_slot_get_changes_guts()
pg_show_replication_origin_status()
pg_get_replication_slots()
pg_stat_get_wal_senders()
pg_get_shmem_allocations()
pg_timezone_names()
pg_ls_dir_files()
pg_get_backend_memory_contexts()
pg_stat_get_progress_info()
pg_stat_get_activity()
pg_stat_get_slru()
The second group are functions which, instead of calling
get_call_result_type(), call CreateTemplateTupleDesc() and
TupleDescInitEntry() to allocate and initialize the tuple descriptor in
the per query memory context.
Their memory usage is unchanged by this patch.
The second group:
pg_prepared_statement()
pg_ls_dir()
pg_tablespace_databases()
show_all_file_settings()
pg_cursor()
The third and fourth groups are functions that use CreateTupleDescCopy()
to make a tuple descriptor copy from ResultSetInfo->expectedDesc.
These functions made a copy of ResultSetInfo->expectedDesc but did not
check if it was present before doing so (and do not seem to call a
function which would call internal_get_result_type() -- which does this
check), so I have added that check.
Their memory usage is unchanged by this patch.
The third group:
text_to_table()
The fourth group differs from the third in that the values for the
tuples are actually created in the per query memory context -- seemingly
unnecessarily since tuplestore_putvalues() will eventually end up
copying everything when it is forming tuples. This behavior is not
altered at all by the MakeFuncResultTuplestore() helper function patch,
however I wanted to mention that I noticed that perhaps more was being
allocated in the per query memory context than is necessary.
The fourth group:
pg_options_to_table()
pg_config()
The fifth group is a function that uses CreateTupleDescCoy() to make a
copy of a tuple descriptor (NOT ResultSetInfo->expectedDesc) in the
function info memory context (fcinfo->flinfo->fn_mcxt) so that it can be
accessed later.
Their memory usage is unchanged by this patch.
The fifth group:
populate_recordset_worker()
The sixth group of functions use CreateTupleDescCopy() to make a copy of
the tuple descriptor that they set up using get_call_result_type().
They called get_call_result_type() in a short-lived memory context, then
switched to the per query memory context and made a copy of it right
away. It is unclear why they couldn't just call get_call_result_type()
in the per query context and not make a copy. Perhaps they needed some
specific behavior of the copy (like that it doesn't copy constraints and
defaults?).
With the attached patch, the initial tuple descriptor is now made in the
per query memory context (as well as the copy). I wonder if the initial
tuple descriptor being made in the per query memory context negated the
need for a copy, however I left the copy intact since I wasn't sure.
These functions also checked for the presence of
ResultSetInfo->expectedDesc which is not applicable for all callers of
MakeFuncResultTuplestore(), so I separated it into a separate check.
get_call_result_type() will call internal_get_result_type() which will
check for expectedDesc if the result type is TYPEFUNC_RECORD, so I'm not
sure if the expectedDesc check is required or not in the body of these
functions.
The sixth group:
each_worker()
each_worker_jsonb()
The seventh group of functions use CreateTupleDescCopy() to make a copy of
ResultSetInfo->expectedDesc in the per query memory context.
This patch does not change any of that. The only difference is that now
the check for expectedDesc is separate.
Their memory usage is unchanged by this patch.
The seventh group:
elements_worker_jsonb()
elements_worker()
Note that for all of the functions in which I added or changed the check for
ResultSetInfo->expectedDesc, it is a little bit different because I've changed
the error code.
I've used error code: ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED where they
previously would have been part of an ereport with error code
ERRCODE_FEATURE_NOT_SUPPORTED.
I'm not sure if the new one is correct. In fact, I'm not sure if an error
related to expectedDesc being set should be user-facing at all (looking at how
expectedDesc is used and set, I wasn't sure). Perhaps it should be an elog() or
an assert?
Feedback is addressed inline below.
On Tue, Nov 2, 2021 at 2:17 PM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
On 2021-Nov-02, Melanie Plageman wrote:
Attached is a patch to create a helper function which creates a
tuplestore to be used by FUNCAPI-callable functions.Looks good, and given the amount of code being removed, it seems a
no-brainer that some helper(s) is/are needed.I think the name is a bit too generic. How about
MakeFuncResultTuplestore()?
I've done this rename.
I think the function should not modify rsinfo -- having that as
(undocumented) side effect is weird. I would suggest to just return the
tuplestore, and have the caller stash it in rsinfo->setResult and modify
the other struct members alongside that. It's a couple more lines per
caller, but the code is clearer that way.
I've changed the helper not to modify rsinfo.
There are a few places with very similar code to the new helper
(MakeTuplestore()) but which, for example, don't expect the return type
to be a row (pg_ls_dir(), pg_tablespace_databases(), text_to_table()). I
wasn't sure if it was worth modifying it for their benefit.Is this just because of the tupdesc? If that's the case, then maybe the
tupdesc can be optionally passed in by caller, and when it's not, the
helper does get_call_result_type() and the tupdesc is used as output
argument.
I've parameterized the helper in this way and now am able to use it for
all FUNCAPI-callable functions making tuplestores except fmgr_sql (for
SQL functions) in functions.c because it didn't really seem worth it
given the way the code is written.
All callers of MakeTuplestore() pass work_mem as the third parameter, so
I could just omit it and hard-code it in, but I wasn't sure that felt
right in a utility/helper function.No opinion. I would have used the global in the function instead of
passing it in.
I've changed this to use the global.
I inlined deflist_to_tuplestore() in foreign.c since it was billed as a
helper function but wasn't used elsewhere and it made it harder to use
MakeTuplestore() in this location.This is a bit strange. Does it work to pass rsinfo->expectedDesc?
This is addressed by changing MakeFuncResultTuplestore() call
get_call_result_type() optionally and having pg_options_to_table() call
it with NULL as tuple descriptor. Now the behavior is the same -- it
makes a copy of expectedDesc in the per query context and sets that as
setDesc in the ReturnSetInfo.
- Melanie
Attachments:
v2-0001-Add-helper-to-make-tuplestore.patchtext/x-patch; charset=US-ASCII; name=v2-0001-Add-helper-to-make-tuplestore.patchDownload
From f4b8d63e5d49e7ce34b5722c5421ed1ed19daba1 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Tue, 2 Nov 2021 12:20:55 -0400
Subject: [PATCH v2] Add helper to make tuplestore
And use it to refactor out existing duplicate code.
---
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 | 82 ++++++-------------
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 | 32 ++++++++
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 | 1 +
24 files changed, 118 insertions(+), 458 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 3be8778d21..96dde746aa 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 2609a0a710..28f99d711d 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 a80298ba53..86f0d4c75a 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 fff7dfc640..b004207dde 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -3399,24 +3399,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 c436d9318b..b443870f92 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -482,7 +482,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;
@@ -500,24 +499,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;
@@ -576,31 +564,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..dbd5cf55bd 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,14 +2210,11 @@ 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;
@@ -2244,9 +2225,7 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
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,14 +2314,12 @@ 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;
@@ -2354,9 +2331,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 +3773,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 +3839,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 ff5aedc99c..1e8de981cd 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..3787ffe7b3 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"
@@ -1757,7 +1758,38 @@ build_function_result_tupdesc_d(char prokind,
return desc;
}
+/*
+ * Helper function to construct tuplestore
+ */
+Tuplestorestate *MakeFuncResultTuplestore(FunctionCallInfo fcinfo, TupleDesc *tupdesc)
+{
+ Tuplestorestate *tupstore;
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+
+ /* 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");
+ }
+ tupstore = tuplestore_begin_heap(true, false, work_mem);
+
+ return tupstore;
+}
/*
* RelationNameGetTupleDesc
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index e91d5a3cfd..4e2d537463 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10039,16 +10039,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);
@@ -10074,7 +10064,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..003e78fe40 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -229,6 +229,7 @@ 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
On Tue, Nov 2, 2021 at 4:23 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
Several places have a conditional value for the first argument (randomAccess),
but your patch changes the behavior to a constant "true". I didn't review the
patch beyond that.@@ -740,18 +724,14 @@ pg_prepared_statement(PG_FUNCTION_ARGS) - tupstore = - tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, - false, work_mem);@@ -2701,42 +2701,13 @@ pg_hba_file_rules(PG_FUNCTION_ARGS) - tuple_store = - tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random, - false, work_mem);@@ -4799,31 +4797,8 @@ pg_timezone_names(PG_FUNCTION_ARGS) - randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; - tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);@@ -575,38 +575,12 @@ pg_ls_dir_1arg(PG_FUNCTION_ARGS) - randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; - tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);@@ -1170,17 +1154,12 @@ pg_cursor(PG_FUNCTION_ARGS) - tupstore = - tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, - false, work_mem);+++ b/src/backend/utils/fmgr/funcapi.c + tupstore = tuplestore_begin_heap(true, false, maxKBytes);
I believe the patch has preserved the same behavior. All of the callers
for which I replaced tuplestore_begin_heap() which passed a variable for
the randomAccess parameter had set that variable to something which was
effectively the same as passing true -- SFRM_Materialize_Random.
- Melanie
On Mon, Nov 08, 2021 at 02:52:28PM -0500, Melanie Plageman wrote:
On Tue, Nov 2, 2021 at 4:23 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
Several places have a conditional value for the first argument (randomAccess),
but your patch changes the behavior to a constant "true". I didn't review the
patch beyond that.@@ -740,18 +724,14 @@ pg_prepared_statement(PG_FUNCTION_ARGS) - tupstore = - tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, - false, work_mem);@@ -2701,42 +2701,13 @@ pg_hba_file_rules(PG_FUNCTION_ARGS) - tuple_store = - tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random, - false, work_mem);@@ -4799,31 +4797,8 @@ pg_timezone_names(PG_FUNCTION_ARGS) - randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; - tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);@@ -575,38 +575,12 @@ pg_ls_dir_1arg(PG_FUNCTION_ARGS) - randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; - tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);@@ -1170,17 +1154,12 @@ pg_cursor(PG_FUNCTION_ARGS) - tupstore = - tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, - false, work_mem);+++ b/src/backend/utils/fmgr/funcapi.c + tupstore = tuplestore_begin_heap(true, false, maxKBytes);I believe the patch has preserved the same behavior. All of the callers
for which I replaced tuplestore_begin_heap() which passed a variable for
the randomAccess parameter had set that variable to something which was
effectively the same as passing true -- SFRM_Materialize_Random.
I don't think so ?
They callers aren't passing SFRM_Materialize_Random, but rather
(allowedModes & SFRM_Materialize_Random) != 0
Where allowedModes is determined EXEC_FLAG_BACKWARD.
src/include/executor/executor.h:extern Tuplestorestate *ExecMakeTableFunctionResult(SetExprState *setexpr,
src/include/executor/executor.h- ExprContext *econtext,
src/include/executor/executor.h- MemoryContext argContext,
src/include/executor/executor.h- TupleDesc expectedDesc,
src/include/executor/executor.h- bool randomAccess);
src/backend/executor/nodeFunctionscan.c=FunctionNext(FunctionScanState *node)
src/backend/executor/nodeFunctionscan.c: ExecMakeTableFunctionResult(node->funcstates[0].setexpr,
src/backend/executor/nodeFunctionscan.c- node->ss.ps.ps_ExprContext,
src/backend/executor/nodeFunctionscan.c- node->argcontext,
src/backend/executor/nodeFunctionscan.c- node->funcstates[0].tupdesc,
src/backend/executor/nodeFunctionscan.c- node->eflags & EXEC_FLAG_BACKWARD);
--
Justin
On Mon, Nov 8, 2021 at 3:13 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
On Mon, Nov 08, 2021 at 02:52:28PM -0500, Melanie Plageman wrote:
On Tue, Nov 2, 2021 at 4:23 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
Several places have a conditional value for the first argument (randomAccess),
but your patch changes the behavior to a constant "true". I didn't review the
patch beyond that.@@ -740,18 +724,14 @@ pg_prepared_statement(PG_FUNCTION_ARGS) - tupstore = - tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, - false, work_mem);@@ -2701,42 +2701,13 @@ pg_hba_file_rules(PG_FUNCTION_ARGS) - tuple_store = - tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random, - false, work_mem);@@ -4799,31 +4797,8 @@ pg_timezone_names(PG_FUNCTION_ARGS) - randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; - tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);@@ -575,38 +575,12 @@ pg_ls_dir_1arg(PG_FUNCTION_ARGS) - randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0; - tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);@@ -1170,17 +1154,12 @@ pg_cursor(PG_FUNCTION_ARGS) - tupstore = - tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, - false, work_mem);+++ b/src/backend/utils/fmgr/funcapi.c + tupstore = tuplestore_begin_heap(true, false, maxKBytes);I believe the patch has preserved the same behavior. All of the callers
for which I replaced tuplestore_begin_heap() which passed a variable for
the randomAccess parameter had set that variable to something which was
effectively the same as passing true -- SFRM_Materialize_Random.I don't think so ?
They callers aren't passing SFRM_Materialize_Random, but rather
(allowedModes & SFRM_Materialize_Random) != 0Where allowedModes is determined EXEC_FLAG_BACKWARD.
src/include/executor/executor.h:extern Tuplestorestate *ExecMakeTableFunctionResult(SetExprState *setexpr,
src/include/executor/executor.h- ExprContext *econtext,
src/include/executor/executor.h- MemoryContext argContext,
src/include/executor/executor.h- TupleDesc expectedDesc,
src/include/executor/executor.h- bool randomAccess);src/backend/executor/nodeFunctionscan.c=FunctionNext(FunctionScanState *node)
src/backend/executor/nodeFunctionscan.c: ExecMakeTableFunctionResult(node->funcstates[0].setexpr,
src/backend/executor/nodeFunctionscan.c- node->ss.ps.ps_ExprContext,
src/backend/executor/nodeFunctionscan.c- node->argcontext,
src/backend/executor/nodeFunctionscan.c- node->funcstates[0].tupdesc,
src/backend/executor/nodeFunctionscan.c- node->eflags & EXEC_FLAG_BACKWARD);
You are right. I misread it.
So, I've attached a patch where randomAccess is now an additional
parameter (and registered for the next fest).
I was thinking about how to add a test that would have broken when I
passed true for randomAccess to tuplestore_begin_heap() when false was
required. But, I don't fully understand the problem. If backward
accesses to a tuplestore are not allowed but randomAccess is mistakenly
passed as true, would the potential result be potentially wrong results
from accessing the tuplestore results backwards?
- Melanie
Attachments:
v3-0001-Add-helper-to-make-tuplestore.patchapplication/octet-stream; name=v3-0001-Add-helper-to-make-tuplestore.patchDownload
From c8c63275851bedcb6c34c9d5bfc57bd7d9a036a5 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Tue, 2 Nov 2021 12:20:55 -0400
Subject: [PATCH v3] Add helper to make tuplestore
And use it to refactor out existing duplicate code.
---
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 | 16 +---
src/backend/foreign/foreign.c | 51 +++++------
src/backend/libpq/hba.c | 20 +----
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 | 15 +---
src/backend/utils/adt/genfile.c | 27 +-----
src/backend/utils/adt/jsonfuncs.c | 87 +++++++------------
src/backend/utils/adt/mcxtfuncs.c | 16 +---
src/backend/utils/adt/misc.c | 12 +--
src/backend/utils/adt/pgstatfuncs.c | 48 +---------
src/backend/utils/adt/varlena.c | 15 ++--
src/backend/utils/fmgr/funcapi.c | 32 +++++++
src/backend/utils/misc/guc.c | 12 +--
src/backend/utils/misc/pg_config.c | 11 ++-
src/backend/utils/mmgr/portalmem.c | 16 +---
src/include/funcapi.h | 2 +
24 files changed, 127 insertions(+), 450 deletions(-)
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index dd9a45c186..2407628db2 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, true);
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..f1ea6a8796 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, true);
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, true);
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..ae4899e007 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, true);
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, true);
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, true);
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..c234629049 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,8 @@ 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,
+ rsinfo->allowedModes & SFRM_Materialize_Random);
/* generate junk in short-term context */
MemoryContextSwitchTo(oldcontext);
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index e07cc57431..c3c2915337 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, true);
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 3be8778d21..31b65c525e 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -2710,29 +2710,13 @@ 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->allowedModes & SFRM_Materialize_Random);
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..fabaf383cf 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, true);
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..b8bd1e5bcb 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, true);
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..620d8795b8 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, true);
+
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 46175b7007..8301f568c0 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, true);
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 7950afb173..bf3ffb79e7 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -3400,24 +3400,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, true);
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..2b882bdd28 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, true);
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..39ec2c6a14 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -4801,24 +4801,11 @@ 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, randomAccess);
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 c436d9318b..4f807deb95 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -500,16 +500,6 @@ 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);
@@ -517,7 +507,7 @@ pg_ls_dir(PG_FUNCTION_ARGS)
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, randomAccess);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
@@ -583,24 +573,11 @@ pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok)
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, randomAccess);
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..2180c32942 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -1926,30 +1926,21 @@ 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,
+ rsi->allowedModes & SFRM_Materialize_Random);
+
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 +2029,22 @@ 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,
+ rsi->allowedModes & SFRM_Materialize_Random);
+
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,14 +2212,11 @@ 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;
@@ -2244,9 +2227,8 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
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,
+ rsi->allowedModes & SFRM_Materialize_Random);
MemoryContextSwitchTo(old_cxt);
@@ -2335,14 +2317,12 @@ 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;
@@ -2354,9 +2334,8 @@ 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,
+ rsi->allowedModes & SFRM_Materialize_Random);
MemoryContextSwitchTo(old_cxt);
@@ -3798,13 +3777,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 +3843,8 @@ 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,
+ rsi->allowedModes & SFRM_Materialize_Random);
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..ffd657d566 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, true);
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..95f637223c 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -211,16 +211,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);
@@ -229,7 +219,7 @@ pg_tablespace_databases(PG_FUNCTION_ARGS)
OIDOID, -1, 0);
randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
- tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
+ tupstore = MakeFuncResultTuplestore(fcinfo, NULL, randomAccess);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index e64857e540..0934392712 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, true);
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, true);
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, true);
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..62f320a12d 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, true);
MemoryContextSwitchTo(old_cxt);
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index e94b8037ec..d07f1dee67 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"
@@ -1757,7 +1758,38 @@ build_function_result_tupdesc_d(char prokind,
return desc;
}
+/*
+ * Helper function to construct tuplestore
+ */
+Tuplestorestate *MakeFuncResultTuplestore(FunctionCallInfo fcinfo, TupleDesc *tupdesc, bool randomAccess)
+{
+ Tuplestorestate *tupstore;
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+
+ /* 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");
+ }
+ tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
+
+ return tupstore;
+}
/*
* RelationNameGetTupleDesc
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index e91d5a3cfd..d398c1353c 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10039,16 +10039,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);
@@ -10074,7 +10064,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, true);
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..795c90f001 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, true);
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..b4f6cac885 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,8 @@ 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,
+ rsinfo->allowedModes & SFRM_Materialize_Random);
/* generate junk in short-term context */
MemoryContextSwitchTo(oldcontext);
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index f1304d47e3..6e3594e0e0 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, bool randomAccess);
/*----------
--
2.32.0
On Thu, Nov 18, 2021 at 12:59:03PM -0500, Melanie Plageman wrote:
On Mon, Nov 8, 2021 at 3:13 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
On Mon, Nov 08, 2021 at 02:52:28PM -0500, Melanie Plageman wrote:
On Tue, Nov 2, 2021 at 4:23 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
Several places have a conditional value for the first argument (randomAccess),
but your patch changes the behavior to a constant "true". I didn't review the
patch beyond that.
...
I believe the patch has preserved the same behavior. All of the callers
for which I replaced tuplestore_begin_heap() which passed a variable for
the randomAccess parameter had set that variable to something which was
effectively the same as passing true -- SFRM_Materialize_Random.I don't think so ?
They callers aren't passing SFRM_Materialize_Random, but rather
(allowedModes & SFRM_Materialize_Random) != 0Where allowedModes is determined EXEC_FLAG_BACKWARD.
...
You are right. I misread it.
So, I've attached a patch where randomAccess is now an additional
parameter (and registered for the next fest).I was thinking about how to add a test that would have broken when I
passed true for randomAccess to tuplestore_begin_heap() when false was
required. But, I don't fully understand the problem. If backward
accesses to a tuplestore are not allowed but randomAccess is mistakenly
passed as true, would the potential result be potentially wrong results
from accessing the tuplestore results backwards?
No, see here
src/backend/utils/fmgr/README-The Tuplestore must be created with randomAccess = true if
src/backend/utils/fmgr/README:SFRM_Materialize_Random is set in allowedModes, but it can (and preferably
src/backend/utils/fmgr/README-should) be created with randomAccess = false if not. Callers that can support
src/backend/utils/fmgr/README-both ValuePerCall and Materialize mode will set SFRM_Materialize_Preferred,
src/backend/utils/fmgr/README-or not, depending on which mode they prefer.
If you use "randomAccess=true" when it's not needed, the result might be less
efficient.
Some callers specify "true" when they don't need to. I ran into that here.
/messages/by-id/21724.1583955158@sss.pgh.pa.us
Maybe you'd want to add an 0002 patch which changes those to conditional ?
BTW, there should be a newline before MakeFuncResultTuplestore().
--
Justin
On Thu, Nov 18, 2021 at 1:24 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
On Thu, Nov 18, 2021 at 12:59:03PM -0500, Melanie Plageman wrote:
On Mon, Nov 8, 2021 at 3:13 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
On Mon, Nov 08, 2021 at 02:52:28PM -0500, Melanie Plageman wrote:
On Tue, Nov 2, 2021 at 4:23 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
Several places have a conditional value for the first argument (randomAccess),
but your patch changes the behavior to a constant "true". I didn't review the
patch beyond that....
I believe the patch has preserved the same behavior. All of the callers
for which I replaced tuplestore_begin_heap() which passed a variable for
the randomAccess parameter had set that variable to something which was
effectively the same as passing true -- SFRM_Materialize_Random.I don't think so ?
They callers aren't passing SFRM_Materialize_Random, but rather
(allowedModes & SFRM_Materialize_Random) != 0Where allowedModes is determined EXEC_FLAG_BACKWARD.
...
You are right. I misread it.
So, I've attached a patch where randomAccess is now an additional
parameter (and registered for the next fest).I was thinking about how to add a test that would have broken when I
passed true for randomAccess to tuplestore_begin_heap() when false was
required. But, I don't fully understand the problem. If backward
accesses to a tuplestore are not allowed but randomAccess is mistakenly
passed as true, would the potential result be potentially wrong results
from accessing the tuplestore results backwards?No, see here
src/backend/utils/fmgr/README-The Tuplestore must be created with randomAccess = true if
src/backend/utils/fmgr/README:SFRM_Materialize_Random is set in allowedModes, but it can (and preferably
src/backend/utils/fmgr/README-should) be created with randomAccess = false if not. Callers that can support
src/backend/utils/fmgr/README-both ValuePerCall and Materialize mode will set SFRM_Materialize_Preferred,
src/backend/utils/fmgr/README-or not, depending on which mode they prefer.If you use "randomAccess=true" when it's not needed, the result might be less
efficient.Some callers specify "true" when they don't need to. I ran into that here.
/messages/by-id/21724.1583955158@sss.pgh.pa.usMaybe you'd want to add an 0002 patch which changes those to conditional ?
Done in attached v4
BTW, there should be a newline before MakeFuncResultTuplestore().
Fixed
- Melanie
Attachments:
v4-0002-Check-allowedModes-when-creating-tuplestores.patchapplication/octet-stream; name=v4-0002-Check-allowedModes-when-creating-tuplestores.patchDownload
From 1b1463ab6e3b3677f416ccec7b4b7d28f5c89fbb Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Mon, 13 Dec 2021 17:17:22 -0500
Subject: [PATCH v4 2/2] Check allowedModes when creating tuplestores
As stated in src/backend/utils/fmgr/README, 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"
Previously, many func-api callers of tuplestore_begin_heap() passed a
constant value instead of checking allowedModes. This commit passes true
or false based on if SFRM_Materialize_Random is set in allowedModes to
MakeFuncResultTuplestore() (which wraps tuplestore_begin_heap() for
func-api callers).
---
src/backend/access/transam/xlogfuncs.c | 3 ++-
src/backend/commands/event_trigger.c | 7 +++++--
src/backend/commands/extension.c | 9 ++++++---
src/backend/replication/logical/logicalfuncs.c | 3 ++-
src/backend/replication/logical/origin.c | 3 ++-
src/backend/replication/slotfuncs.c | 3 ++-
src/backend/replication/walsender.c | 3 ++-
src/backend/storage/ipc/shmem.c | 3 ++-
src/backend/utils/adt/varlena.c | 3 ++-
src/backend/utils/misc/guc.c | 3 ++-
src/backend/utils/misc/pg_config.c | 3 ++-
11 files changed, 29 insertions(+), 14 deletions(-)
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 2407628db2..cf64639de2 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -181,7 +181,8 @@ pg_stop_backup_v2(PG_FUNCTION_ARGS)
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, true);
+ tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, (bool)
+ rsinfo->allowedModes & SFRM_Materialize_Random);
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 f1ea6a8796..be8d1f0d14 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1310,7 +1310,9 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, true);
+ tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, (bool)
+ rsinfo->allowedModes & SFRM_Materialize_Random);
+
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
@@ -1854,7 +1856,8 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, true);
+ tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, (bool)
+ rsinfo->allowedModes & SFRM_Materialize_Random);
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 ae4899e007..75c06432b3 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1933,7 +1933,8 @@ pg_available_extensions(PG_FUNCTION_ARGS)
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, true);
+ tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, (bool)
+ rsinfo->allowedModes & SFRM_Materialize_Random);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
@@ -2027,7 +2028,8 @@ pg_available_extension_versions(PG_FUNCTION_ARGS)
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, true);
+ tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, (bool)
+ rsinfo->allowedModes & SFRM_Materialize_Random);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
@@ -2298,7 +2300,8 @@ pg_extension_update_paths(PG_FUNCTION_ARGS)
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, true);
+ tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, (bool)
+ rsinfo->allowedModes & SFRM_Materialize_Random);
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 b8bd1e5bcb..693e5e0a53 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -188,7 +188,8 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
}
}
- p->tupstore = MakeFuncResultTuplestore(fcinfo, &p->tupdesc, true);
+ p->tupstore = MakeFuncResultTuplestore(fcinfo, &p->tupdesc, (bool)
+ rsinfo->allowedModes & SFRM_Materialize_Random);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = p->tupstore;
rsinfo->setDesc = p->tupdesc;
diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c
index 620d8795b8..8ec3decf90 100644
--- a/src/backend/replication/logical/origin.c
+++ b/src/backend/replication/logical/origin.c
@@ -1494,7 +1494,8 @@ pg_show_replication_origin_status(PG_FUNCTION_ARGS)
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, true);
+ tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, (bool)
+ rsinfo->allowedModes & SFRM_Materialize_Random);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index b05371d8b6..e76bbbf47d 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -248,7 +248,8 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, true);
+ tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, (bool)
+ rsinfo->allowedModes & SFRM_Materialize_Random);
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 92a05b7447..62416a4ac8 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -3404,7 +3404,8 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, true);
+ tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, (bool)
+ rsinfo->allowedModes & SFRM_Materialize_Random);
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 2b882bdd28..95f5ddd81d 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -550,7 +550,8 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, true);
+ tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc, (bool)
+ rsinfo->allowedModes & SFRM_Materialize_Random);
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 62f320a12d..162f9f19d0 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -4856,7 +4856,8 @@ text_to_table(PG_FUNCTION_ARGS)
tstate.astate = NULL;
tstate.tupdesc = CreateTupleDescCopy(rsi->expectedDesc);
- tstate.tupstore = MakeFuncResultTuplestore(fcinfo, NULL, true);
+ tstate.tupstore = MakeFuncResultTuplestore(fcinfo, NULL, (bool)
+ rsi->allowedModes & SFRM_Materialize_Random);
MemoryContextSwitchTo(old_cxt);
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index fddec33a47..8aa8eba8d1 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10117,7 +10117,8 @@ show_all_file_settings(PG_FUNCTION_ARGS)
TEXTOID, -1, 0);
/* Build a tuplestore to return our results in */
- tupstore = MakeFuncResultTuplestore(fcinfo, NULL, true);
+ tupstore = MakeFuncResultTuplestore(fcinfo, NULL, (bool)
+ rsinfo->allowedModes & SFRM_Materialize_Random);
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 795c90f001..b80a0e18e5 100644
--- a/src/backend/utils/misc/pg_config.c
+++ b/src/backend/utils/misc/pg_config.c
@@ -66,7 +66,8 @@ pg_config(PG_FUNCTION_ARGS)
rsinfo->returnMode = SFRM_Materialize;
/* initialize our tuplestore */
- tupstore = MakeFuncResultTuplestore(fcinfo, NULL, true);
+ tupstore = MakeFuncResultTuplestore(fcinfo, NULL, (bool)
+ rsinfo->allowedModes & SFRM_Materialize_Random);
configdata = get_configdata(my_exec_path, &configdata_len);
for (i = 0; i < configdata_len; i++)
--
2.32.0
v4-0001-Add-helper-to-make-tuplestore.patchapplication/octet-stream; name=v4-0001-Add-helper-to-make-tuplestore.patchDownload
From 939e3e6ccf109dfaf45cc07aceb7f7dc0098628e Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Tue, 2 Nov 2021 12:20:55 -0400
Subject: [PATCH v4 1/2] Add helper to make tuplestore
And use it to refactor out existing duplicate code.
---
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 | 16 +---
src/backend/foreign/foreign.c | 51 +++++------
src/backend/libpq/hba.c | 20 +----
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 | 15 +---
src/backend/utils/adt/genfile.c | 27 +-----
src/backend/utils/adt/jsonfuncs.c | 87 +++++++------------
src/backend/utils/adt/mcxtfuncs.c | 16 +---
src/backend/utils/adt/misc.c | 12 +--
src/backend/utils/adt/pgstatfuncs.c | 48 +---------
src/backend/utils/adt/varlena.c | 15 ++--
src/backend/utils/fmgr/funcapi.c | 33 +++++++
src/backend/utils/misc/guc.c | 12 +--
src/backend/utils/misc/pg_config.c | 11 ++-
src/backend/utils/mmgr/portalmem.c | 16 +---
src/include/funcapi.h | 2 +
24 files changed, 128 insertions(+), 450 deletions(-)
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index dd9a45c186..2407628db2 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, true);
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..f1ea6a8796 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, true);
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, true);
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..ae4899e007 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, true);
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, true);
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, true);
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..c234629049 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,8 @@ 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,
+ rsinfo->allowedModes & SFRM_Materialize_Random);
/* generate junk in short-term context */
MemoryContextSwitchTo(oldcontext);
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index e07cc57431..c3c2915337 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, true);
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..d084a22b2d 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -2710,29 +2710,13 @@ 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->allowedModes & SFRM_Materialize_Random);
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..fabaf383cf 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, true);
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..b8bd1e5bcb 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, true);
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..620d8795b8 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, true);
+
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..b05371d8b6 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, true);
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..92a05b7447 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, true);
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..2b882bdd28 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, true);
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..39ec2c6a14 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -4801,24 +4801,11 @@ 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, randomAccess);
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..b849ec96f5 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -501,16 +501,6 @@ 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);
@@ -518,7 +508,7 @@ pg_ls_dir(PG_FUNCTION_ARGS)
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, randomAccess);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
@@ -584,24 +574,11 @@ pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok)
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, randomAccess);
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..2180c32942 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -1926,30 +1926,21 @@ 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,
+ rsi->allowedModes & SFRM_Materialize_Random);
+
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 +2029,22 @@ 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,
+ rsi->allowedModes & SFRM_Materialize_Random);
+
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,14 +2212,11 @@ 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;
@@ -2244,9 +2227,8 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
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,
+ rsi->allowedModes & SFRM_Materialize_Random);
MemoryContextSwitchTo(old_cxt);
@@ -2335,14 +2317,12 @@ 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;
@@ -2354,9 +2334,8 @@ 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,
+ rsi->allowedModes & SFRM_Materialize_Random);
MemoryContextSwitchTo(old_cxt);
@@ -3798,13 +3777,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 +3843,8 @@ 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,
+ rsi->allowedModes & SFRM_Materialize_Random);
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..ffd657d566 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, true);
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..95f637223c 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -211,16 +211,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);
@@ -229,7 +219,7 @@ pg_tablespace_databases(PG_FUNCTION_ARGS)
OIDOID, -1, 0);
randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
- tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
+ tupstore = MakeFuncResultTuplestore(fcinfo, NULL, randomAccess);
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..b3f83c7a08 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, true);
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, true);
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, true);
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..62f320a12d 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, true);
MemoryContextSwitchTo(old_cxt);
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index e94b8037ec..52177c7b58 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"
@@ -1758,6 +1759,38 @@ build_function_result_tupdesc_d(char prokind,
return desc;
}
+/*
+ * Helper function to construct tuplestore
+ */
+Tuplestorestate *MakeFuncResultTuplestore(FunctionCallInfo fcinfo, TupleDesc *tupdesc, bool randomAccess)
+{
+ Tuplestorestate *tupstore;
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+
+ /* 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");
+ }
+
+ tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
+
+ return tupstore;
+}
/*
* RelationNameGetTupleDesc
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index f736e8d872..fddec33a47 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10092,16 +10092,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);
@@ -10127,7 +10117,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, true);
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..795c90f001 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, true);
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..b4f6cac885 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,8 @@ 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,
+ rsinfo->allowedModes & SFRM_Materialize_Random);
/* generate junk in short-term context */
MemoryContextSwitchTo(oldcontext);
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index f1304d47e3..6e3594e0e0 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, bool randomAccess);
/*----------
--
2.32.0
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 */
You removed one call to tuplestore_donestoring(), but not the others.
I guess you could remove them all (optionally as a separate patch).
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).
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()
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.
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.
--
Justin
Thanks for the detailed review!
On Fri, Dec 17, 2021 at 3:04 PM Justin Pryzby <pryzby@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
Attachments:
v5-0001-Add-helper-to-make-tuplestore.patchtext/x-patch; charset=US-ASCII; name=v5-0001-Add-helper-to-make-tuplestore.patchDownload
From 8ea7f90162f73049541fb6f53e714298a5ecfc41 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@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 <melanieplageman@gmail.com>
Reviewed-by: Justin Pryzby <pryzby@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
On Wed, Jan 05, 2022 at 12:09:16PM -0500, Melanie Plageman wrote:
On Fri, Dec 17, 2021 at 3:04 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
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?
I don't know. The patch is already easy to review, since it's mostly limited
to removing code and fixing inconsistencies (NULL check) and possible
inefficiencies (randomAccess).
If the expectedDesc NULL check were an 0001 patch, then 0002 (the main patch)
would be even easier to review. Only foreign.c is different.
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.
What local variable ? I see now that logicalfuncs.c never had a local variable
called tupstore. I'm sure it was intended since 2014 (b89e15105) to say
tupestore_donestoring(p->tupstore). But donestoring(typo) causes no error,
since the define is a NOP.
src/include/utils/tuplestore.h:#define tuplestore_donestoring(state) ((void) 0)
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.
I assume Tom left it (actually, added it back in dd04e958c) to avoid breaking
extensions for no reason. And maybe to preserve the possbility of at some
point in the future doing something again during the "done" step.
I'd leave it for input from a committer about those:
- remove tuplestore_donestoring() calls ?
- split expectedDesc NULL check to an 0001 patch ?
- anything other opened questions ?
I'm marking this as RFC, with the caveat that the newline before
MakeFuncResultTuplestore went missing again :)
--
Justin
On Wed, Jan 5, 2022 at 7:57 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
On Wed, Jan 05, 2022 at 12:09:16PM -0500, Melanie Plageman wrote:
On Fri, Dec 17, 2021 at 3:04 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
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?I don't know. The patch is already easy to review, since it's mostly limited
to removing code and fixing inconsistencies (NULL check) and possible
inefficiencies (randomAccess).If the expectedDesc NULL check were an 0001 patch, then 0002 (the main patch)
would be even easier to review. Only foreign.c is different.
I'll wait to do that if preferred by committer.
Are you imagining that patch 0001 would only add the check for
expectedDesc that is missing from pg_config() and text_to_table()?
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.What local variable ? I see now that logicalfuncs.c never had a local variable
called tupstore. I'm sure it was intended since 2014 (b89e15105) to say
tupestore_donestoring(p->tupstore). But donestoring(typo) causes no error,
since the define is a NOP.src/include/utils/tuplestore.h:#define tuplestore_donestoring(state) ((void) 0)
Yes, I mean the local variable, tupstore.
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.I assume Tom left it (actually, added it back in dd04e958c) to avoid breaking
extensions for no reason. And maybe to preserve the possbility of at some
point in the future doing something again during the "done" step.I'd leave it for input from a committer about those:
- remove tuplestore_donestoring() calls ?
- split expectedDesc NULL check to an 0001 patch ?
- anything other opened questions ?I'm marking this as RFC, with the caveat that the newline before
MakeFuncResultTuplestore went missing again :)
oops. I've attached v6 with the newline.
- Melanie
Attachments:
v6-0001-Add-helper-to-make-tuplestore.patchtext/x-patch; charset=US-ASCII; name=v6-0001-Add-helper-to-make-tuplestore.patchDownload
From e57adc4950ed259a018e5a2c6fd21af127a39e44 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Tue, 2 Nov 2021 12:20:55 -0400
Subject: [PATCH v6] 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 <melanieplageman@gmail.com>
Reviewed-by: Justin Pryzby <pryzby@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 | 37 ++++++++
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, 124 insertions(+), 460 deletions(-)
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index d8af5aad58..6af2c6b909 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 93c2099735..531deaae2f 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 345787fe2c..30e0fb82a6 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 206d2bbbf9..ed5058210d 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 294e22c78c..4a333afa6a 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 ff57ffa61c..0c274fc3e8 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 7b473903a6..5a29960c80 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 4f633888b4..82e324af4c 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 eb040152f9..482afdb358 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 ae6316d908..a1fe5e283c 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 4cf95ce439..8d868a7c00 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 c682775db4..2d565e626f 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 7926258c06..6467f25895 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 542bbacaa2..314d8b3fe8 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 0273f883d4..0986a7e6f6 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 28cb9d3ff1..7b338bc783 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 fe4f180b6f..786b0f2b38 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 15cb17ace4..6bbbd598ba 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 b3eb39761d..e56e03ced4 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 5d913ae08d..71b97dc8e6 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"
@@ -1758,6 +1760,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 6fc5cbc09a..378ca7e3c8 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 7a13212f99..b36b49ef25 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 236f450a2b..a8bdf6a3d2 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 ba927c2f33..b9f9e92d1a 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
On Tue, Jan 11, 2022 at 10:19:33AM -0500, Melanie Plageman wrote:
On Wed, Jan 5, 2022 at 7:57 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
I'd leave it for input from a committer about those:
- remove tuplestore_donestoring() calls ?
This part has been raised on a different thread, as of:
/messages/by-id/CAP0=ZVJeeYfAeRfmzqAF2Lumdiv4S4FewyBnZd4DPTrsSQKJKw@mail.gmail.com
And it looks sensible from here to just remove any traces of this
stuff from the C code, while leaving the compatibility macro around
to avoid any breakages with any external modules.
- split expectedDesc NULL check to an 0001 patch ?
I am not sure that this is worth a split, as the same areas are
touched.
- anything other opened questions ?
I'm marking this as RFC, with the caveat that the newline before
MakeFuncResultTuplestore went missing again :)oops. I've attached v6 with the newline.
Saying that, the wrapper you are proposing here looks like a good
idea. I have wanted a centralized wrapper more than once, and this is
a nice cut overall.
--
Michael
On Tue, Jan 11, 2022 at 10:19:33AM -0500, Melanie Plageman wrote:
If the expectedDesc NULL check were an 0001 patch, then 0002 (the main patch)
would be even easier to review. Only foreign.c is different.I'll wait to do that if preferred by committer.
Are you imagining that patch 0001 would only add the check for
expectedDesc that is missing from pg_config() and text_to_table()?
Yes, that's what I meant, but I haven't been able to determine if a NULL check
should be added at all.
I found this commit message, which indicates that it shouldn't be used in every
case. I'm not clear when it should/not be used, though.
commit 60651e4cddbb77e8f1a0c7fc0be6a7e7bf626fe0
Author: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat Oct 28 14:02:21 2017 -0400
Support domains over composite types in PL/Perl.
In passing, don't insist on rsi->expectedDesc being set unless we
actually need it; this allows succeeding in a couple of cases where
PL/Perl functions returning setof composite would have failed before,
and makes the error message more apropos in other cases.
On Tue, Feb 15, 2022 at 11:57:56PM -0600, Justin Pryzby wrote:
On Tue, Jan 11, 2022 at 10:19:33AM -0500, Melanie Plageman wrote:
I'll wait to do that if preferred by committer.
Are you imagining that patch 0001 would only add the check for
expectedDesc that is missing from pg_config() and text_to_table()?
It took me some time to follow up on this point, but we clearly don't
expect a NULL expectedDesc in the context of these two code paths or
we finish with a NULL pointer dereference in CreateTupleDescCopy()
with a crash, so they had better be added. It also makes sense to me
to do that first and independently of the rest of the patch: it is a
separate issue.
Yes, that's what I meant, but I haven't been able to determine if a NULL check
should be added at all.
+ (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
+ errmsg("expected tuple format not specified as required for "
+ "set-returning function.")))
errmsgs should be in a single line, to ease the grepping of similar
patterns.
There is something else that I think could be cleaned up, while we are
working on this area. Some of the callers of
MakeFuncResultTuplestore() (prepare.c, portalmem.c, one in genfile.c,
guc.c, misc.c) pass down a NULL tupdesc while building their TupleDesc
using CreateTemplateTupleDesc() and a set of TupleDescInitEntry() for
each attribute. This data exists already in pg_proc.dat, duplicating
the effort. Except if I am missing something, wouldn't it be better
to let get_call_result_type() do this work for us, passing a TupleDesc
pointer to the new MakeFuncResultTuplestore() rather than NULL?
Once you do that, there is something else that stands out: all the
callers of MakeFuncResultTuplestore() passing NULL for tupledesc *have
to* set expectedDesc, as far as I can see from the remaining callers.
This brings an interesting effect, couldn't we move the check on
ReturnSetInfo->expectedDesc being not NULL within
MakeFuncResultTuplestore(), checking after it only when tupdesc ==
NULL?
One would say that this changes the error ordering in code paths like
pg_config(), but, actually, in this case, we don't have any need for
the part "Check to make sure we have a reasonable tuple descriptor" as
of pg_proc.dat defining the tuple description this function uses, no?
And we could switch to a tuplestore in this case, as well, reducing by
one the numbers of callers with tupdesc=NULL for the new routine.
This could be a separate cleanup, separate from the rest, done first.
each_worker_jsonb() does not really need its check on expectedDesc
being NULL, does it? HEAD grabs the tuple descriptor with
get_call_result_type(), the patch uses MakeFuncResultTuplestore(), so
we make nowhere use of the expected TupleDesc saved by the SRF
executor.
Asserting that we are in the correct memory context in when calling
MakeFuncResultTuplestore() sounds rather sensible from here as per the
magics done in the various json functions. Still, it really feels
like we could do a more centralized consolidation of the whole.
--
Michael
On Thu, Feb 17, 2022 at 04:10:01PM +0900, Michael Paquier wrote:
Asserting that we are in the correct memory context in when calling
MakeFuncResultTuplestore() sounds rather sensible from here as per the
magics done in the various json functions. Still, it really feels
like we could do a more centralized consolidation of the whole.
So, I got my hands on this area, and found myself applying 07daca5 as
a first piece of the puzzle. Anyway, after more review today, I have
bumped into more pieces that could be consolidated, and finished with
the following patch set:
- 0001 changes a couple of SRFs that I found worth simplifying. These
refer mostly to the second and fourth group mentioned upthread by
Melanie, with two exceptions: pg_tablespace_databases() where it is
not worth changing to keep it backward-compatible and pg_ls_dir() as
per its one-arg version. That's a nice first cut in itself.
- 0002 changes a couple of places to unify some existing SRF checks,
that I bumped into on the way. The value here is in using the same
error messages everywhere, reducing the translation effort and
generating the same errors for all cases based on the same conditions.
This eases the review of the next patch.
- 0003 is the actual refactoring meat, where I have been able to move
the check on expectedDesc into MakeFuncResultTuplestore(), shaving
more code than previously. If you discard the cases of patch 0001, it
should actually be possible to set setResult, setDesc and returnMode
within the new function, which would feel natural as that's the place
where we create the function's tuplestore and we want to materialize
its contents. The cases with the JSON code are also a bit hairy and
need more thoughts, but we could also cut this part of the code from
the initial refactoring effort.
So, for now, 0001 and 0002 look like rather committable pieces. 0003
needs a bit more thoughts about all the corner cases we need to
consider, mostly what I am mentioning above. Perhaps the pieces of
0003 related to pg_options_to_table() should be moved to 0001 as a
matter of clarity.
--
Michael
Attachments:
v7-0003-Introduce-MakeFuncResultTuplestore.patchtext/x-diff; charset=us-asciiDownload
From b8d8ad7e45b7783af7a8b15adca43557fbff0041 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Mon, 21 Feb 2022 16:07:49 +0900
Subject: [PATCH v7 3/3] Introduce MakeFuncResultTuplestore()
This is the main patch from Melanie, that I have tweaked a bit. Note
that I am not completely done with it ;p
---
src/include/funcapi.h | 2 +
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 | 17 +----
src/backend/foreign/foreign.c | 46 +++++--------
src/backend/libpq/hba.c | 19 +-----
src/backend/replication/logical/launcher.c | 16 +----
.../replication/logical/logicalfuncs.c | 16 +----
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 | 66 ++-----------------
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 | 14 +---
src/backend/utils/fmgr/funcapi.c | 38 +++++++++++
src/backend/utils/misc/guc.c | 15 +----
src/backend/utils/misc/pg_config.c | 13 +---
src/backend/utils/mmgr/portalmem.c | 17 +----
24 files changed, 92 insertions(+), 476 deletions(-)
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index ba927c2f33..b9f9e92d1a 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);
/*----------
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 12e2bf4135..2fc1ed023c 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 1e8587502e..68cad0a580 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;
@@ -1861,25 +1847,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 0e04304cb0..a30e91b328 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1940,25 +1940,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;
@@ -2045,25 +2031,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;
@@ -2327,25 +2299,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 dce30aed6c..040711ee75 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -707,19 +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")));
-
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
/* need to build tuplestore in query context */
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
@@ -728,9 +715,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, &tupdesc);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index d910bc2fbe..ed526be1bd 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,25 @@ 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)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
+ options = untransformRelOptions(array);
+ rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
@@ -531,7 +527,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;
@@ -539,6 +535,8 @@ deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options)
foreach(cell, options)
{
DefElem *def = lfirst(cell);
+ Datum values[2];
+ bool nulls[2];
values[0] = CStringGetTextDatum(def->defname);
nulls[0] = false;
@@ -556,20 +554,6 @@ deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options)
}
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 d84a40b726..02d43eb22e 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -2714,29 +2714,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 5a68d6dead..a1054edada 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 3609fa7d5b..1c00d4c0b4 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -142,25 +142,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);
@@ -203,7 +189,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;
diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c
index 76055a8a03..4edc4300a8 100644
--- a/src/backend/replication/logical/origin.c
+++ b/src/backend/replication/logical/origin.c
@@ -1492,30 +1492,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 886899afd2..3e9db7714e 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -240,20 +240,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
@@ -263,7 +249,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 5a718b1fe9..79e32f7ecf 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -3411,24 +3411,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 1f023a3460..02cdda7a73 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 7926258c06..6467f25895 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 fe6863d8b4..8678511c2a 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -477,7 +477,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;
@@ -495,24 +494,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;
@@ -571,31 +559,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 2457061f97..f2f0c4dc4d 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -1927,27 +1927,14 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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;
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
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);
@@ -2037,28 +2024,15 @@ each_worker(FunctionCallInfo fcinfo, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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;
- (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);
@@ -2227,29 +2201,15 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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) ||
- rsi->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
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);
@@ -2339,20 +2299,8 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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) ||
- rsi->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
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 */
@@ -2360,9 +2308,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);
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index c7c95adf97..9b89651808 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 e79eb6b478..d946f679e5 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 30e8dfa7c1..abde31b854 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;
@@ -574,24 +560,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;
@@ -1873,24 +1845,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 b2003f5672..bd1d3c70bb 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"
@@ -4834,23 +4835,12 @@ 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))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("set-valued function called in context that cannot accept a set")));
- if (!(rsi->allowedModes & SFRM_Materialize) ||
- rsi->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
/* 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 5d913ae08d..094228c767 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"
@@ -1758,6 +1760,42 @@ 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) ||
+ (tupdesc == NULL && rsinfo->expectedDesc == NULL))
+ 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 != NULL)
+ {
+ 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 6533101fbf..8387190372 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10114,19 +10114,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")));
-
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
/* Scan the config files using current context as workspace */
conf = ProcessConfigFileInternal(PGC_SIGHUP, false, DEBUG3);
@@ -10135,7 +10122,7 @@ show_all_file_settings(PG_FUNCTION_ARGS)
oldcontext = MemoryContextSwitchTo(per_query_ctx);
/* Build a tuplestore to return our results in */
- 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/misc/pg_config.c b/src/backend/utils/misc/pg_config.c
index e646a41910..bb0d531443 100644
--- a/src/backend/utils/misc/pg_config.c
+++ b/src/backend/utils/misc/pg_config.c
@@ -32,23 +32,14 @@ pg_config(PG_FUNCTION_ARGS)
size_t configdata_len;
int i = 0;
- /* 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 (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 */
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
- tupstore = tuplestore_begin_heap(true, false, work_mem);
+ /* initialize our tuplestore */
+ tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 21ad87c024..1432961b7f 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -1139,30 +1139,15 @@ 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);
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
/*
* 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, &tupdesc);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
--
2.34.1
v7-0002-Simplify-set-of-SRF-related-checks.patchtext/x-diff; charset=us-asciiDownload
From 431ef7e4d3c84e8ea8949b185e0e4cd17b2a9b2d Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Mon, 21 Feb 2022 16:00:34 +0900
Subject: [PATCH v7 2/3] Simplify set of SRF-related checks
It is worth noting here that each_worker_jsonb() in jsonfuncs.c was
doing a check on expectedDesc, that it does not need.
---
src/backend/utils/adt/jsonfuncs.c | 61 ++++++++++++++++++-------------
src/pl/plperl/plperl.c | 12 ++++--
src/pl/plpgsql/src/pl_exec.c | 19 +++++++---
src/pl/tcl/pltcl.c | 8 +++-
4 files changed, 63 insertions(+), 37 deletions(-)
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 0273f883d4..2457061f97 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -1927,21 +1927,19 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !IsA(rsi, ReturnSetInfo) ||
- (rsi->allowedModes & SFRM_Materialize) == 0 ||
- rsi->expectedDesc == NULL)
+ if (!rsi || !IsA(rsi, ReturnSetInfo))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("set-valued function called in context that "
- "cannot accept a set")));
+ 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;
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")));
+ elog(ERROR, "return type must be a row type");
old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
@@ -2039,13 +2037,15 @@ each_worker(FunctionCallInfo fcinfo, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !IsA(rsi, ReturnSetInfo) ||
- (rsi->allowedModes & SFRM_Materialize) == 0 ||
- rsi->expectedDesc == NULL)
+ if (!rsi || !IsA(rsi, ReturnSetInfo))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("set-valued function called in context that "
- "cannot accept a set")));
+ 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;
@@ -2227,13 +2227,16 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !IsA(rsi, ReturnSetInfo) ||
- (rsi->allowedModes & SFRM_Materialize) == 0 ||
+ if (!rsi || !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) ||
rsi->expectedDesc == NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("set-valued function called in context that "
- "cannot accept a set")));
+ errmsg("materialize mode required, but it is not allowed in this context")));
rsi->returnMode = SFRM_Materialize;
@@ -2336,13 +2339,16 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !IsA(rsi, ReturnSetInfo) ||
- (rsi->allowedModes & SFRM_Materialize) == 0 ||
+ if (!rsi || !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) ||
rsi->expectedDesc == NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("set-valued function called in context that "
- "cannot accept a set")));
+ errmsg("materialize mode required, but it is not allowed in this context")));
rsi->returnMode = SFRM_Materialize;
@@ -3798,12 +3804,15 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !IsA(rsi, ReturnSetInfo) ||
- (rsi->allowedModes & SFRM_Materialize) == 0)
+ if (!rsi || !IsA(rsi, ReturnSetInfo))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("set-valued function called in context that "
- "cannot accept a set")));
+ 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;
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 3f785b1e8d..9e899990f8 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -2413,13 +2413,17 @@ plperl_func_handler(PG_FUNCTION_ARGS)
if (prodesc->fn_retisset)
{
+
/* Check context before allowing the call to go through */
- if (!rsi || !IsA(rsi, ReturnSetInfo) ||
- (rsi->allowedModes & SFRM_Materialize) == 0)
+ if (!rsi || !IsA(rsi, ReturnSetInfo))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("set-valued function called in context that "
- "cannot accept a set")));
+ 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")));
}
activate_interpreter(prodesc->interp);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 70c4a75295..9674c29250 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -629,11 +629,16 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
ReturnSetInfo *rsi = estate.rsi;
/* Check caller can handle a set result */
- if (!rsi || !IsA(rsi, ReturnSetInfo) ||
- (rsi->allowedModes & SFRM_Materialize) == 0)
+ if (!rsi || !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;
/* If we produced any tuples, send back the result */
@@ -3645,13 +3650,17 @@ exec_init_tuple_store(PLpgSQL_execstate *estate)
/*
* Check caller can handle a set result in the way we want
*/
- if (!rsi || !IsA(rsi, ReturnSetInfo) ||
- (rsi->allowedModes & SFRM_Materialize) == 0 ||
- rsi->expectedDesc == NULL)
+ if (!rsi || !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) ||
+ rsi->expectedDesc == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("materialize mode required, but it is not allowed in this context")));
+
/*
* Switch to the right memory context and resource owner for storing the
* tuplestore for return set. If we're within a subtransaction opened for
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 7c045f4560..30dc22e8ab 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -829,12 +829,16 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
{
ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !IsA(rsi, ReturnSetInfo) ||
- (rsi->allowedModes & SFRM_Materialize) == 0)
+ if (!rsi || !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")));
+
call_state->rsi = rsi;
call_state->tuple_store_cxt = rsi->econtext->ecxt_per_query_memory;
call_state->tuple_store_owner = CurrentResourceOwner;
--
2.34.1
v7-0001-Clean-up-and-simplify-some-code-related-to-SRFs.patchtext/x-diff; charset=us-asciiDownload
From 0b72ccfe50dfb9e8a3d7f71fd15b6c653ead22a7 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Mon, 21 Feb 2022 16:15:26 +0900
Subject: [PATCH v7 1/3] Clean up and simplify some code related to SRFs
---
src/backend/commands/prepare.c | 31 +++-----------
src/backend/utils/misc/guc.c | 20 ++-------
src/backend/utils/misc/pg_config.c | 69 ++++++++----------------------
src/backend/utils/mmgr/portalmem.c | 23 +++-------
4 files changed, 35 insertions(+), 108 deletions(-)
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index e0c985ef8b..dce30aed6c 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"
@@ -716,30 +717,13 @@ pg_prepared_statement(PG_FUNCTION_ARGS)
(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");
+
/* need to build tuplestore in query context */
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
- /*
- * build tupdesc for result tuples. This must match the definition of the
- * pg_prepared_statements view in system_views.sql
- */
- tupdesc = CreateTemplateTupleDesc(7);
- TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
- TEXTOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
- TEXTOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepare_time",
- TIMESTAMPTZOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 4, "parameter_types",
- REGTYPEARRAYOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 5, "from_sql",
- BOOLOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 6, "generic_plans",
- INT8OID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 7, "custom_plans",
- INT8OID, -1, 0);
-
/*
* 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.
@@ -747,6 +731,9 @@ pg_prepared_statement(PG_FUNCTION_ARGS)
tupstore =
tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
false, work_mem);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = tupdesc;
/* generate junk in short-term context */
MemoryContextSwitchTo(oldcontext);
@@ -778,10 +765,6 @@ pg_prepared_statement(PG_FUNCTION_ARGS)
}
}
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
return (Datum) 0;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 01f373815e..6533101fbf 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10124,6 +10124,9 @@ show_all_file_settings(PG_FUNCTION_ARGS)
(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");
+
/* Scan the config files using current context as workspace */
conf = ProcessConfigFileInternal(PGC_SIGHUP, false, DEBUG3);
@@ -10131,23 +10134,6 @@ show_all_file_settings(PG_FUNCTION_ARGS)
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
- /* Build a tuple descriptor for our result type */
- tupdesc = CreateTemplateTupleDesc(NUM_PG_FILE_SETTINGS_ATTS);
- TupleDescInitEntry(tupdesc, (AttrNumber) 1, "sourcefile",
- TEXTOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 2, "sourceline",
- INT4OID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 3, "seqno",
- INT4OID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 4, "name",
- TEXTOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 5, "setting",
- TEXTOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 6, "applied",
- BOOLOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 7, "error",
- TEXTOID, -1, 0);
-
/* Build a tuplestore to return our results in */
tupstore = tuplestore_begin_heap(true, false, work_mem);
rsinfo->returnMode = SFRM_Materialize;
diff --git a/src/backend/utils/misc/pg_config.c b/src/backend/utils/misc/pg_config.c
index 2dc875ebfb..e646a41910 100644
--- a/src/backend/utils/misc/pg_config.c
+++ b/src/backend/utils/misc/pg_config.c
@@ -26,14 +26,10 @@ pg_config(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
Tuplestorestate *tupstore;
- HeapTuple tuple;
TupleDesc tupdesc;
- AttInMetadata *attinmeta;
- MemoryContext per_query_ctx;
MemoryContext oldcontext;
ConfigData *configdata;
size_t configdata_len;
- char *values[2];
int i = 0;
/* check to see if caller supports us returning a tuplestore */
@@ -41,65 +37,38 @@ pg_config(PG_FUNCTION_ARGS)
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)
+ if (!(rsinfo->allowedModes & SFRM_Materialize))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("materialize mode required, but it is not allowed in this context")));
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
- /* get the requested return tuple description */
- tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
+ /* Build tuplestore to hold the result rows */
+ oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
- /*
- * Check to make sure we have a reasonable tuple descriptor
- */
- if (tupdesc->natts != 2 ||
- TupleDescAttr(tupdesc, 0)->atttypid != TEXTOID ||
- TupleDescAttr(tupdesc, 1)->atttypid != TEXTOID)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("query-specified return tuple and "
- "function return type are not compatible")));
-
- /* OK to use it */
- attinmeta = TupleDescGetAttInMetadata(tupdesc);
-
- /* let the caller know we're sending back a tuplestore */
- rsinfo->returnMode = SFRM_Materialize;
-
- /* initialize our tuplestore */
tupstore = tuplestore_begin_heap(true, false, work_mem);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = tupdesc;
+
+ MemoryContextSwitchTo(oldcontext);
configdata = get_configdata(my_exec_path, &configdata_len);
for (i = 0; i < configdata_len; i++)
{
- values[0] = configdata[i].name;
- values[1] = configdata[i].setting;
+ Datum values[2];
+ bool nulls[2];
- tuple = BuildTupleFromCStrings(attinmeta, values);
- tuplestore_puttuple(tupstore, tuple);
+ memset(values, 0, sizeof(values));
+ memset(nulls, 0, sizeof(nulls));
+
+ values[0] = CStringGetTextDatum(configdata[i].name);
+ values[1] = CStringGetTextDatum(configdata[i].setting);
+
+ tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
- /*
- * no longer need the tuple descriptor reference created by
- * TupleDescGetAttInMetadata()
- */
- ReleaseTupleDesc(tupdesc);
-
- rsinfo->setResult = tupstore;
-
- /*
- * SFRM_Materialize mode expects us to return a NULL Datum. The actual
- * tuples are in our tuplestore and passed back through rsinfo->setResult.
- * rsinfo->setDesc is set to the tuple description that we actually used
- * to build our tuples with, so the caller can verify we did what it was
- * expecting.
- */
- rsinfo->setDesc = tupdesc;
- MemoryContextSwitchTo(oldcontext);
-
return (Datum) 0;
}
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 7885344164..21ad87c024 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"
@@ -1152,23 +1153,8 @@ pg_cursor(PG_FUNCTION_ARGS)
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
- /*
- * build tupdesc for result tuples. This must match the definition of the
- * pg_cursors view in system_views.sql
- */
- tupdesc = CreateTemplateTupleDesc(6);
- TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
- TEXTOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
- TEXTOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 3, "is_holdable",
- BOOLOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 4, "is_binary",
- BOOLOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 5, "is_scrollable",
- BOOLOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 6, "creation_time",
- TIMESTAMPTZOID, -1, 0);
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
/*
* We put all the tuples into a tuplestore in one scan of the hashtable.
@@ -1177,6 +1163,9 @@ pg_cursor(PG_FUNCTION_ARGS)
tupstore =
tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
false, work_mem);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = tupdesc;
/* generate junk in short-term context */
MemoryContextSwitchTo(oldcontext);
--
2.34.1
On Mon, Feb 21, 2022 at 04:41:17PM +0900, Michael Paquier wrote:
So, I got my hands on this area, and found myself applying 07daca5 as
a first piece of the puzzle. Anyway, after more review today, I have
bumped into more pieces that could be consolidated, and finished with
the following patch set:
- 0001 changes a couple of SRFs that I found worth simplifying. These
refer mostly to the second and fourth group mentioned upthread by
Melanie, with two exceptions: pg_tablespace_databases() where it is
not worth changing to keep it backward-compatible and pg_ls_dir() as
per its one-arg version. That's a nice first cut in itself.
- 0002 changes a couple of places to unify some existing SRF checks,
that I bumped into on the way. The value here is in using the same
error messages everywhere, reducing the translation effort and
generating the same errors for all cases based on the same conditions.
This eases the review of the next patch.
These two have been now applied, with some comment improvements and
the cleanup of pg_options_to_table() done in 0001, and a slight change
in 0002 for pageinspect where a check was not necessary for a BRIN
code path.
- 0003 is the actual refactoring meat, where I have been able to move
the check on expectedDesc into MakeFuncResultTuplestore(), shaving
more code than previously. If you discard the cases of patch 0001, it
should actually be possible to set setResult, setDesc and returnMode
within the new function, which would feel natural as that's the place
where we create the function's tuplestore and we want to materialize
its contents. The cases with the JSON code are also a bit hairy and
need more thoughts, but we could also cut this part of the code from
the initial refactoring effort.
This is the remaining piece, as attached, that I have not been able to
poke much at yet.
--
Michael
Attachments:
v8-0001-Introduce-MakeFuncResultTuplestore.patchtext/x-diff; charset=us-asciiDownload
From f23525ef3492ecaf310a59c431be7d3f3f5237af Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 24 Feb 2022 17:27:55 +0900
Subject: [PATCH v8] Introduce MakeFuncResultTuplestore()
This is the main patch from Melanie, that I have tweaked a bit. Note
that I am not completely done with it ;p
---
src/include/funcapi.h | 2 +
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 | 17 +----
src/backend/foreign/foreign.c | 14 +---
src/backend/libpq/hba.c | 19 +----
src/backend/replication/logical/launcher.c | 16 +---
.../replication/logical/logicalfuncs.c | 16 +---
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 | 73 +++----------------
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 | 14 +---
src/backend/utils/fmgr/funcapi.c | 38 ++++++++++
src/backend/utils/misc/guc.c | 15 +---
src/backend/utils/misc/pg_config.c | 13 +---
src/backend/utils/mmgr/portalmem.c | 17 +----
24 files changed, 82 insertions(+), 461 deletions(-)
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index ba927c2f33..b9f9e92d1a 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);
/*----------
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 12e2bf4135..2fc1ed023c 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 1e8587502e..68cad0a580 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;
@@ -1861,25 +1847,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 0e04304cb0..a30e91b328 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1940,25 +1940,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;
@@ -2045,25 +2031,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;
@@ -2327,25 +2299,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 dce30aed6c..040711ee75 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -707,19 +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")));
-
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
/* need to build tuplestore in query context */
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
@@ -728,9 +715,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, &tupdesc);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index c3406c3b9d..1f86340cc8 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"
@@ -515,17 +516,6 @@ pg_options_to_table(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) ||
- rsinfo->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
options = untransformRelOptions(array);
rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
@@ -535,8 +525,8 @@ pg_options_to_table(PG_FUNCTION_ARGS)
/*
* Now prepare the result set.
*/
+ tupstore = MakeFuncResultTuplestore(fcinfo, NULL);
tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
- tupstore = tuplestore_begin_heap(true, false, work_mem);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index d84a40b726..02d43eb22e 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -2714,29 +2714,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 5a68d6dead..a1054edada 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 3609fa7d5b..1c00d4c0b4 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -142,25 +142,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);
@@ -203,7 +189,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;
diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c
index 76055a8a03..4edc4300a8 100644
--- a/src/backend/replication/logical/origin.c
+++ b/src/backend/replication/logical/origin.c
@@ -1492,30 +1492,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 886899afd2..3e9db7714e 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -240,20 +240,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
@@ -263,7 +249,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 5a718b1fe9..79e32f7ecf 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -3411,24 +3411,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 1f023a3460..02cdda7a73 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 7926258c06..6467f25895 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 fe6863d8b4..8678511c2a 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -477,7 +477,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;
@@ -495,24 +494,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;
@@ -571,31 +559,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 2457061f97..6f5f2451fe 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -1927,27 +1927,14 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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;
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
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);
@@ -2037,28 +2024,15 @@ each_worker(FunctionCallInfo fcinfo, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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;
- (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);
@@ -2227,29 +2201,14 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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) ||
- rsi->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
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);
+ tuple_store = MakeFuncResultTuplestore(fcinfo, NULL);
+ tupdesc = rsi->expectedDesc;
ret_tdesc = CreateTupleDescCopy(tupdesc);
BlessTupleDesc(ret_tdesc);
- tuple_store =
- tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
- false, work_mem);
MemoryContextSwitchTo(old_cxt);
@@ -2339,30 +2298,16 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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) ||
- rsi->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
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 */
old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+ state->tuple_store = MakeFuncResultTuplestore(fcinfo, NULL);
+
+ tupdesc = rsi->expectedDesc;
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);
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index c7c95adf97..9b89651808 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 e79eb6b478..d946f679e5 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 30e8dfa7c1..abde31b854 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;
@@ -574,24 +560,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;
@@ -1873,24 +1845,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 b2003f5672..40e46540c5 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"
@@ -4834,23 +4835,12 @@ 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))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("set-valued function called in context that cannot accept a set")));
- if (!(rsi->allowedModes & SFRM_Materialize) ||
- rsi->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
/* OK, prepare tuplestore in per-query memory */
old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
tstate.astate = NULL;
+ tstate.tupstore = MakeFuncResultTuplestore(fcinfo, NULL);
tstate.tupdesc = CreateTupleDescCopy(rsi->expectedDesc);
- tstate.tupstore = tuplestore_begin_heap(true, false, work_mem);
MemoryContextSwitchTo(old_cxt);
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 5d913ae08d..094228c767 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"
@@ -1758,6 +1760,42 @@ 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) ||
+ (tupdesc == NULL && rsinfo->expectedDesc == NULL))
+ 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 != NULL)
+ {
+ 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 1e3650184b..21c9ec0122 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10164,19 +10164,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")));
-
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
/* Scan the config files using current context as workspace */
conf = ProcessConfigFileInternal(PGC_SIGHUP, false, DEBUG3);
@@ -10185,7 +10172,7 @@ show_all_file_settings(PG_FUNCTION_ARGS)
oldcontext = MemoryContextSwitchTo(per_query_ctx);
/* Build a tuplestore to return our results in */
- 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/misc/pg_config.c b/src/backend/utils/misc/pg_config.c
index e646a41910..bb0d531443 100644
--- a/src/backend/utils/misc/pg_config.c
+++ b/src/backend/utils/misc/pg_config.c
@@ -32,23 +32,14 @@ pg_config(PG_FUNCTION_ARGS)
size_t configdata_len;
int i = 0;
- /* 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 (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 */
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
- tupstore = tuplestore_begin_heap(true, false, work_mem);
+ /* initialize our tuplestore */
+ tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 21ad87c024..1432961b7f 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -1139,30 +1139,15 @@ 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);
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
/*
* 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, &tupdesc);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
--
2.34.1
On Thu, Feb 24, 2022 at 08:25:06PM +0900, Michael Paquier wrote:
This is the remaining piece, as attached, that I have not been able to
poke much at yet.
So, I have finally poked at this last part of the patch set, and I
found that we can be more aggressive with the refactoring, by moving
into MakeFuncResultTuplestore() the parts where we save the tuplestore
and the tupledesc in the per-query memory context. There are two
pieces that matter once things are reshaped:
- The tuple descriptor may need some extra validation via
BlessTupleDesc() when it comes from a transient record datatype,
something that happens for most of the subroutines related to the JSON
functions.
- expectedDesc is sometimes required by the caller, though most of the
time just needs to be built with the more expensive
get_call_result_type().
In order to keep things pluggable at will, MakeFuncResultTuplestore()
has been changed to access a set of bits32 flags, able to control the
two options above. With this facility in place, I have been able to
cut much more code than the initial patch, roughly twice as of:
24 files changed, 157 insertions(+), 893 deletions(-)
This seems in rather good shape to me, the changes are
straight-forward and the code cut is really good, so I'd like to move
on with that. 0001 is the initial patch, and 0002 is the extra
refactoring I have been working on. The plan would be to merge both,
but I am sending a split to ease any checks on what I have changed.
Comments or objections?
--
Michael
Attachments:
v9-0001-Introduce-MakeFuncResultTuplestore.patchtext/x-diff; charset=us-asciiDownload
From d45e9a7031017e13e5429ff985b36ddafdfdb443 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 24 Feb 2022 17:27:55 +0900
Subject: [PATCH v9 1/2] Introduce MakeFuncResultTuplestore()
This is the main patch from Melanie, that I have tweaked a bit. Note
that I am not completely done with it ;p
---
src/include/funcapi.h | 2 +
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 | 17 +----
src/backend/foreign/foreign.c | 14 +---
src/backend/libpq/hba.c | 19 +----
src/backend/replication/logical/launcher.c | 16 +---
.../replication/logical/logicalfuncs.c | 16 +---
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 | 73 +++----------------
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 | 14 +---
src/backend/utils/fmgr/funcapi.c | 38 ++++++++++
src/backend/utils/misc/guc.c | 15 +---
src/backend/utils/misc/pg_config.c | 13 +---
src/backend/utils/mmgr/portalmem.c | 17 +----
24 files changed, 82 insertions(+), 461 deletions(-)
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index ba927c2f33..b9f9e92d1a 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);
/*----------
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 12e2bf4135..2fc1ed023c 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 1e8587502e..68cad0a580 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;
@@ -1861,25 +1847,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 0e04304cb0..a30e91b328 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1940,25 +1940,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;
@@ -2045,25 +2031,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;
@@ -2327,25 +2299,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 dce30aed6c..040711ee75 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -707,19 +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")));
-
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
/* need to build tuplestore in query context */
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
@@ -728,9 +715,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, &tupdesc);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index c3406c3b9d..1f86340cc8 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"
@@ -515,17 +516,6 @@ pg_options_to_table(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) ||
- rsinfo->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
options = untransformRelOptions(array);
rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
@@ -535,8 +525,8 @@ pg_options_to_table(PG_FUNCTION_ARGS)
/*
* Now prepare the result set.
*/
+ tupstore = MakeFuncResultTuplestore(fcinfo, NULL);
tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
- tupstore = tuplestore_begin_heap(true, false, work_mem);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index d84a40b726..02d43eb22e 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -2714,29 +2714,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 5a68d6dead..a1054edada 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 3bd770a3ba..b7d056653b 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -142,25 +142,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);
@@ -203,7 +189,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;
diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c
index 76055a8a03..4edc4300a8 100644
--- a/src/backend/replication/logical/origin.c
+++ b/src/backend/replication/logical/origin.c
@@ -1492,30 +1492,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 886899afd2..3e9db7714e 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -240,20 +240,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
@@ -263,7 +249,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 5a718b1fe9..79e32f7ecf 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -3411,24 +3411,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 1f023a3460..02cdda7a73 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 7926258c06..6467f25895 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 fe6863d8b4..8678511c2a 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -477,7 +477,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;
@@ -495,24 +494,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;
@@ -571,31 +559,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 2457061f97..6f5f2451fe 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -1927,27 +1927,14 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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;
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
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);
@@ -2037,28 +2024,15 @@ each_worker(FunctionCallInfo fcinfo, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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;
- (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);
@@ -2227,29 +2201,14 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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) ||
- rsi->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
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);
+ tuple_store = MakeFuncResultTuplestore(fcinfo, NULL);
+ tupdesc = rsi->expectedDesc;
ret_tdesc = CreateTupleDescCopy(tupdesc);
BlessTupleDesc(ret_tdesc);
- tuple_store =
- tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
- false, work_mem);
MemoryContextSwitchTo(old_cxt);
@@ -2339,30 +2298,16 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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) ||
- rsi->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
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 */
old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+ state->tuple_store = MakeFuncResultTuplestore(fcinfo, NULL);
+
+ tupdesc = rsi->expectedDesc;
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);
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index c7c95adf97..9b89651808 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 e79eb6b478..d946f679e5 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 30e8dfa7c1..abde31b854 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;
@@ -574,24 +560,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;
@@ -1873,24 +1845,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 b2003f5672..40e46540c5 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"
@@ -4834,23 +4835,12 @@ 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))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("set-valued function called in context that cannot accept a set")));
- if (!(rsi->allowedModes & SFRM_Materialize) ||
- rsi->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
/* OK, prepare tuplestore in per-query memory */
old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
tstate.astate = NULL;
+ tstate.tupstore = MakeFuncResultTuplestore(fcinfo, NULL);
tstate.tupdesc = CreateTupleDescCopy(rsi->expectedDesc);
- tstate.tupstore = tuplestore_begin_heap(true, false, work_mem);
MemoryContextSwitchTo(old_cxt);
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 5d913ae08d..094228c767 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"
@@ -1758,6 +1760,42 @@ 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) ||
+ (tupdesc == NULL && rsinfo->expectedDesc == NULL))
+ 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 != NULL)
+ {
+ 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 1e3650184b..21c9ec0122 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10164,19 +10164,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")));
-
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
/* Scan the config files using current context as workspace */
conf = ProcessConfigFileInternal(PGC_SIGHUP, false, DEBUG3);
@@ -10185,7 +10172,7 @@ show_all_file_settings(PG_FUNCTION_ARGS)
oldcontext = MemoryContextSwitchTo(per_query_ctx);
/* Build a tuplestore to return our results in */
- 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/misc/pg_config.c b/src/backend/utils/misc/pg_config.c
index e646a41910..bb0d531443 100644
--- a/src/backend/utils/misc/pg_config.c
+++ b/src/backend/utils/misc/pg_config.c
@@ -32,23 +32,14 @@ pg_config(PG_FUNCTION_ARGS)
size_t configdata_len;
int i = 0;
- /* 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 (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 */
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
- tupstore = tuplestore_begin_heap(true, false, work_mem);
+ /* initialize our tuplestore */
+ tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 21ad87c024..1432961b7f 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -1139,30 +1139,15 @@ 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);
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
/*
* 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, &tupdesc);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
--
2.35.1
v9-0002-Introduce-more-simplifications-in-MakeFuncResultT.patchtext/x-diff; charset=us-asciiDownload
From c7fa0b3cc32c408366c3bb807a660389a7125b17 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Mon, 28 Feb 2022 16:35:08 +0900
Subject: [PATCH v9 2/2] Introduce more simplifications in
MakeFuncResultTuplestore()
This doubles the amount of code cut compared to the first patch, adding
more SRF-related logic into a central function.
---
src/include/funcapi.h | 7 +-
src/backend/access/transam/xlogfuncs.c | 17 +---
src/backend/commands/event_trigger.c | 34 ++------
src/backend/commands/extension.c | 51 ++---------
src/backend/commands/prepare.c | 19 +---
src/backend/foreign/foreign.c | 22 +----
src/backend/libpq/hba.c | 23 ++---
src/backend/replication/logical/launcher.c | 17 +---
.../replication/logical/logicalfuncs.c | 7 +-
src/backend/replication/logical/origin.c | 21 +----
src/backend/replication/slotfuncs.c | 17 +---
src/backend/replication/walsender.c | 17 +---
src/backend/storage/ipc/shmem.c | 21 ++---
src/backend/utils/adt/datetime.c | 15 +---
src/backend/utils/adt/genfile.c | 34 ++------
src/backend/utils/adt/jsonfuncs.c | 86 +++----------------
src/backend/utils/adt/mcxtfuncs.c | 17 +---
src/backend/utils/adt/misc.c | 21 +----
src/backend/utils/adt/pgstatfuncs.c | 49 ++---------
src/backend/utils/adt/varlena.c | 15 +---
src/backend/utils/fmgr/funcapi.c | 47 +++++++---
src/backend/utils/misc/guc.c | 18 +---
src/backend/utils/misc/pg_config.c | 18 +---
src/backend/utils/mmgr/portalmem.c | 22 +----
24 files changed, 129 insertions(+), 486 deletions(-)
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index b9f9e92d1a..11491a7c33 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -229,8 +229,11 @@ 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);
+
+/* flag bits for MakeFuncResultTuplestore() */
+#define TUPSTORE_SRF_USE_EXPECTED 0x01 /* use expectedDesc as tupdesc */
+#define TUPSTORE_SRF_BLESS 0x02 /* validate tuple for SRF */
+extern void MakeFuncResultTuplestore(FunctionCallInfo fcinfo, bits32 flags);
/*----------
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 2fc1ed023c..60c24b2bf3 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -166,10 +166,6 @@ Datum
pg_stop_backup_v2(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
Datum values[3];
bool nulls[3];
@@ -178,15 +174,7 @@ pg_stop_backup_v2(PG_FUNCTION_ARGS)
XLogRecPtr stoppoint;
SessionBackupState status = get_backup_status();
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
MemSet(values, 0, sizeof(values));
MemSet(nulls, 0, sizeof(nulls));
@@ -237,7 +225,8 @@ pg_stop_backup_v2(PG_FUNCTION_ARGS)
/* Stoppoint is included on both exclusive and nonexclusive backups */
values[0] = LSNGetDatum(stoppoint);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
return (Datum) 0;
}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 68cad0a580..58a6415b9e 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1290,10 +1290,6 @@ Datum
pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
slist_iter iter;
/*
@@ -1307,15 +1303,7 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
"pg_event_trigger_dropped_objects()")));
/* Build tuplestore to hold the result rows */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
slist_foreach(iter, &(currentEventTriggerState->SQLDropList))
{
@@ -1384,7 +1372,8 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
nulls[i++] = true;
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
return (Datum) 0;
@@ -1832,10 +1821,6 @@ Datum
pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
ListCell *lc;
/*
@@ -1848,15 +1833,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
"pg_event_trigger_ddl_commands()")));
/* Build tuplestore to hold the result rows */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
foreach(lc, currentEventTriggerState->commandList)
{
@@ -2027,7 +2004,8 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
break;
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
PG_RETURN_VOID();
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index a30e91b328..43a62ce8e0 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1932,24 +1932,12 @@ Datum
pg_available_extensions(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
char *location;
DIR *dir;
struct dirent *de;
/* Build tuplestore to hold the result rows */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
location = get_extension_control_directory();
dir = AllocateDir(location);
@@ -2001,7 +1989,8 @@ pg_available_extensions(PG_FUNCTION_ARGS)
else
values[2] = CStringGetTextDatum(control->comment);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
FreeDir(dir);
@@ -2023,24 +2012,12 @@ Datum
pg_available_extension_versions(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
char *location;
DIR *dir;
struct dirent *de;
/* Build tuplestore to hold the result rows */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
location = get_extension_control_directory();
dir = AllocateDir(location);
@@ -2075,7 +2052,8 @@ pg_available_extension_versions(PG_FUNCTION_ARGS)
control = read_extension_control_file(extname);
/* scan extension's script directory for install scripts */
- get_available_versions_for_extension(control, tupstore, tupdesc);
+ get_available_versions_for_extension(control, rsinfo->setResult,
+ rsinfo->setDesc);
}
FreeDir(dir);
@@ -2288,10 +2266,6 @@ pg_extension_update_paths(PG_FUNCTION_ARGS)
{
Name extname = PG_GETARG_NAME(0);
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
List *evi_list;
ExtensionControlFile *control;
ListCell *lc1;
@@ -2300,15 +2274,7 @@ pg_extension_update_paths(PG_FUNCTION_ARGS)
check_valid_extension_name(NameStr(*extname));
/* Build tuplestore to hold the result rows */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
/* Read the extension's control file */
control = read_extension_control_file(NameStr(*extname));
@@ -2365,7 +2331,8 @@ pg_extension_update_paths(PG_FUNCTION_ARGS)
pfree(pathbuf.data);
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
}
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 040711ee75..5fc04f31ff 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -702,26 +702,12 @@ Datum
pg_prepared_statement(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
-
- /* need to build tuplestore in query context */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
/*
* 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 = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- /* generate junk in short-term context */
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
/* hash table might be uninitialized */
if (prepared_queries)
@@ -746,7 +732,8 @@ pg_prepared_statement(PG_FUNCTION_ARGS)
values[5] = Int64GetDatumFast(prep_stmt->plansource->num_generic_plans);
values[6] = Int64GetDatumFast(prep_stmt->plansource->num_custom_plans);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
}
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index 1f86340cc8..a5d2e28f40 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -511,27 +511,12 @@ pg_options_to_table(PG_FUNCTION_ARGS)
ListCell *cell;
List *options;
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
options = untransformRelOptions(array);
rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- /*
- * Now prepare the result set.
- */
- tupstore = MakeFuncResultTuplestore(fcinfo, NULL);
- tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ /* prepare the result set */
+ MakeFuncResultTuplestore(fcinfo, TUPSTORE_SRF_USE_EXPECTED);
foreach(cell, options)
{
@@ -551,7 +536,8 @@ pg_options_to_table(PG_FUNCTION_ARGS)
values[1] = (Datum) 0;
nulls[1] = true;
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
return (Datum) 0;
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 02d43eb22e..b179c4e66d 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -2703,30 +2703,19 @@ fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
Datum
pg_hba_file_rules(PG_FUNCTION_ARGS)
{
- Tuplestorestate *tuple_store;
- TupleDesc tupdesc;
- MemoryContext old_cxt;
ReturnSetInfo *rsi;
/*
- * We must use the Materialize mode to be safe against HBA file changes
- * while the cursor is open. It's also more efficient than having to look
+ * Build tuplestore to hold the result rows. We must use the
+ * Materialize mode to be safe against HBA file changes while the
+ * cursor is open. It's also more efficient than having to look
* up our current position in the parsed list every time.
*/
- rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- rsi->returnMode = SFRM_Materialize;
-
- /* Build tuplestore to hold the result rows */
- old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
-
- tuple_store = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsi->setDesc = tupdesc;
- rsi->setResult = tuple_store;
-
- MemoryContextSwitchTo(old_cxt);
+ MakeFuncResultTuplestore(fcinfo, 0);
/* Fill the tuplestore */
- fill_hba_view(tuple_store, tupdesc);
+ rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+ fill_hba_view(rsi->setResult, rsi->setDesc);
PG_RETURN_NULL();
}
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index a1054edada..ba5df518be 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -930,20 +930,8 @@ pg_stat_get_subscription(PG_FUNCTION_ARGS)
Oid subid = PG_ARGISNULL(0) ? InvalidOid : PG_GETARG_OID(0);
int i;
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
/* Make sure we get consistent view of the workers. */
LWLockAcquire(LogicalRepWorkerLock, LW_SHARED);
@@ -996,7 +984,8 @@ pg_stat_get_subscription(PG_FUNCTION_ARGS)
else
values[7] = TimestampTzGetDatum(worker.reply_time);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
/*
* If only a single subscription was requested, and we found it,
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index b7d056653b..c95b115437 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -189,10 +189,9 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
}
}
- p->tupstore = MakeFuncResultTuplestore(fcinfo, &p->tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = p->tupstore;
- rsinfo->setDesc = p->tupdesc;
+ MakeFuncResultTuplestore(fcinfo, 0);
+ p->tupstore = rsinfo->setResult;
+ p->tupdesc = rsinfo->setDesc;
/*
* Compute the current end-of-wal.
diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c
index 4edc4300a8..17faeb6f22 100644
--- a/src/backend/replication/logical/origin.c
+++ b/src/backend/replication/logical/origin.c
@@ -1482,29 +1482,13 @@ Datum
pg_show_replication_origin_status(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
int i;
#define REPLICATION_ORIGIN_PROGRESS_COLS 4
/* we want to return 0 rows if slot is set to zero */
replorigin_check_prerequisites(false, true);
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- 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");
+ MakeFuncResultTuplestore(fcinfo, 0);
/* prevent slots from being concurrently dropped */
LWLockAcquire(ReplicationOriginLock, LW_SHARED);
@@ -1554,7 +1538,8 @@ pg_show_replication_origin_status(PG_FUNCTION_ARGS)
LWLockRelease(&state->lock);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
LWLockRelease(ReplicationOriginLock);
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index 3e9db7714e..f366e5e934 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -233,10 +233,6 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
{
#define PG_GET_REPLICATION_SLOTS_COLS 14
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
XLogRecPtr currlsn;
int slotno;
@@ -246,15 +242,7 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
* name, which shouldn't contain anything particularly sensitive.
*/
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
currlsn = GetXLogWriteRecPtr();
@@ -417,7 +405,8 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
Assert(i == PG_GET_REPLICATION_SLOTS_COLS);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
LWLockRelease(ReplicationSlotControlLock);
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 79e32f7ecf..71d56691b1 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -3403,23 +3403,11 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
{
#define PG_STAT_GET_WAL_SENDERS_COLS 12
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
SyncRepStandbyData *sync_standbys;
int num_standbys;
int i;
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
/*
* Get the currently active synchronous standbys. This could be out of
@@ -3563,7 +3551,8 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
values[11] = TimestampTzGetDatum(replyTime);
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
return (Datum) 0;
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 02cdda7a73..2bc7495ec3 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -537,25 +537,13 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
{
#define PG_GET_SHMEM_SIZES_COLS 4
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
HASH_SEQ_STATUS hstat;
ShmemIndexEnt *ent;
Size named_allocated = 0;
Datum values[PG_GET_SHMEM_SIZES_COLS];
bool nulls[PG_GET_SHMEM_SIZES_COLS];
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
LWLockAcquire(ShmemIndexLock, LW_SHARED);
@@ -571,7 +559,8 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
values[3] = Int64GetDatum(ent->allocated_size);
named_allocated += ent->allocated_size;
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
/* output shared memory allocated but not counted via the shmem index */
@@ -579,7 +568,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
nulls[1] = true;
values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
values[3] = values[2];
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
/* output as-of-yet unused shared memory */
nulls[0] = true;
@@ -587,7 +576,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
nulls[1] = false;
values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
values[3] = values[2];
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
LWLockRelease(ShmemIndexLock);
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 6467f25895..1d1310aecb 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -4786,8 +4786,6 @@ Datum
pg_timezone_names(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
pg_tzenum *tzenum;
pg_tz *tz;
Datum values[4];
@@ -4798,17 +4796,8 @@ pg_timezone_names(PG_FUNCTION_ARGS)
const char *tzn;
Interval *resInterval;
struct pg_tm itm;
- MemoryContext oldcontext;
- /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
- oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
/* initialize timezone scanning code */
tzenum = pg_tzenumerate_start();
@@ -4850,7 +4839,7 @@ pg_timezone_names(PG_FUNCTION_ARGS)
values[3] = BoolGetDatum(tm.tm_isdst > 0);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
pg_tzenumerate_end(tzenum);
diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index 8678511c2a..c74017bc3e 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -477,11 +477,8 @@ pg_ls_dir(PG_FUNCTION_ARGS)
char *location;
bool missing_ok = false;
bool include_dot_dirs = false;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
DIR *dirdesc;
struct dirent *de;
- MemoryContext oldcontext;
location = convert_and_check_filename(PG_GETARG_TEXT_PP(0));
@@ -494,18 +491,7 @@ pg_ls_dir(PG_FUNCTION_ARGS)
include_dot_dirs = PG_GETARG_BOOL(2);
}
- /* 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);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, NULL);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, TUPSTORE_SRF_USE_EXPECTED);
dirdesc = AllocateDir(location);
if (!dirdesc)
@@ -529,7 +515,8 @@ pg_ls_dir(PG_FUNCTION_ARGS)
values[0] = CStringGetTextDatum(de->d_name);
nulls[0] = false;
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
FreeDir(dirdesc);
@@ -559,21 +546,10 @@ static Datum
pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
DIR *dirdesc;
struct dirent *de;
- MemoryContext oldcontext;
- /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
- oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
/*
* Now walk the directory. Note that we must do this within a single SRF
@@ -621,7 +597,7 @@ pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok)
values[2] = TimestampTzGetDatum(time_t_to_timestamptz(attrib.st_mtime));
memset(nulls, 0, sizeof(nulls));
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
FreeDir(dirdesc);
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 6f5f2451fe..ca22f5b8c3 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -1909,9 +1909,6 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
{
Jsonb *jb = PG_GETARG_JSONB_P(0);
ReturnSetInfo *rsi;
- Tuplestorestate *tuple_store;
- TupleDesc tupdesc;
- TupleDesc ret_tdesc;
MemoryContext old_cxt,
tmp_cxt;
bool skipNested = false;
@@ -1926,17 +1923,7 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
funcname)));
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-
- rsi->returnMode = SFRM_Materialize;
-
- old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
-
- tuple_store = MakeFuncResultTuplestore(fcinfo, &tupdesc);
-
- ret_tdesc = CreateTupleDescCopy(tupdesc);
- BlessTupleDesc(ret_tdesc);
-
- MemoryContextSwitchTo(old_cxt);
+ MakeFuncResultTuplestore(fcinfo, TUPSTORE_SRF_BLESS);
tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
"jsonb_each temporary cxt",
@@ -1951,7 +1938,6 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
if (r == WJB_KEY)
{
text *key;
- HeapTuple tuple;
Datum values[2];
bool nulls[2] = {false, false};
@@ -1988,9 +1974,7 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
values[1] = PointerGetDatum(val);
}
- tuple = heap_form_tuple(ret_tdesc, values, nulls);
-
- tuplestore_puttuple(tuple_store, tuple);
+ tuplestore_putvalues(rsi->setResult, rsi->setDesc, values, nulls);
/* clean up and switch back */
MemoryContextSwitchTo(old_cxt);
@@ -2000,9 +1984,6 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
MemoryContextDelete(tmp_cxt);
- rsi->setResult = tuple_store;
- rsi->setDesc = ret_tdesc;
-
PG_RETURN_NULL();
}
@@ -2014,8 +1995,6 @@ each_worker(FunctionCallInfo fcinfo, bool as_text)
JsonLexContext *lex;
JsonSemAction *sem;
ReturnSetInfo *rsi;
- MemoryContext old_cxt;
- TupleDesc tupdesc;
EachState *state;
lex = makeJsonLexContext(json, true);
@@ -2024,17 +2003,9 @@ each_worker(FunctionCallInfo fcinfo, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- rsi->returnMode = SFRM_Materialize;
-
- /* 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);
-
- MemoryContextSwitchTo(old_cxt);
+ MakeFuncResultTuplestore(fcinfo, TUPSTORE_SRF_BLESS);
+ state->tuple_store = rsi->setResult;
+ state->ret_tdesc = rsi->setDesc;
sem->semstate = (void *) state;
sem->array_start = each_array_start;
@@ -2053,9 +2024,6 @@ each_worker(FunctionCallInfo fcinfo, bool as_text)
MemoryContextDelete(state->tmp_cxt);
- rsi->setResult = state->tuple_store;
- rsi->setDesc = state->ret_tdesc;
-
PG_RETURN_NULL();
}
@@ -2180,9 +2148,6 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
{
Jsonb *jb = PG_GETARG_JSONB_P(0);
ReturnSetInfo *rsi;
- Tuplestorestate *tuple_store;
- TupleDesc tupdesc;
- TupleDesc ret_tdesc;
MemoryContext old_cxt,
tmp_cxt;
bool skipNested = false;
@@ -2201,16 +2166,8 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- rsi->returnMode = SFRM_Materialize;
-
- old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
-
- tuple_store = MakeFuncResultTuplestore(fcinfo, NULL);
- tupdesc = rsi->expectedDesc;
- ret_tdesc = CreateTupleDescCopy(tupdesc);
- BlessTupleDesc(ret_tdesc);
-
- MemoryContextSwitchTo(old_cxt);
+ MakeFuncResultTuplestore(fcinfo,
+ TUPSTORE_SRF_USE_EXPECTED | TUPSTORE_SRF_BLESS);
tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
"jsonb_array_elements temporary cxt",
@@ -2224,7 +2181,6 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
if (r == WJB_ELEM)
{
- HeapTuple tuple;
Datum values[1];
bool nulls[1] = {false};
@@ -2250,9 +2206,7 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
values[0] = PointerGetDatum(val);
}
- tuple = heap_form_tuple(ret_tdesc, values, nulls);
-
- tuplestore_puttuple(tuple_store, tuple);
+ tuplestore_putvalues(rsi->setResult, rsi->setDesc, values, nulls);
/* clean up and switch back */
MemoryContextSwitchTo(old_cxt);
@@ -2262,9 +2216,6 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
MemoryContextDelete(tmp_cxt);
- rsi->setResult = tuple_store;
- rsi->setDesc = ret_tdesc;
-
PG_RETURN_NULL();
}
@@ -2289,27 +2240,15 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
JsonLexContext *lex = makeJsonLexContext(json, as_text);
JsonSemAction *sem;
ReturnSetInfo *rsi;
- MemoryContext old_cxt;
- TupleDesc tupdesc;
ElementsState *state;
state = palloc0(sizeof(ElementsState));
sem = palloc0(sizeof(JsonSemAction));
+ MakeFuncResultTuplestore(fcinfo, TUPSTORE_SRF_USE_EXPECTED | TUPSTORE_SRF_BLESS);
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-
- rsi->returnMode = SFRM_Materialize;
-
- /* make these in a sufficiently long-lived memory context */
- old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
-
- state->tuple_store = MakeFuncResultTuplestore(fcinfo, NULL);
-
- tupdesc = rsi->expectedDesc;
- state->ret_tdesc = CreateTupleDescCopy(tupdesc);
- BlessTupleDesc(state->ret_tdesc);
-
- MemoryContextSwitchTo(old_cxt);
+ state->tuple_store = rsi->setResult;
+ state->ret_tdesc = rsi->setDesc;
sem->semstate = (void *) state;
sem->object_start = elements_object_start;
@@ -2329,9 +2268,6 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
MemoryContextDelete(state->tmp_cxt);
- rsi->setResult = state->tuple_store;
- rsi->setDesc = state->ret_tdesc;
-
PG_RETURN_NULL();
}
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 9b89651808..3afd1edbab 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -120,22 +120,9 @@ Datum
pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
-
- PutMemoryContextsStatsTupleStore(tupstore, tupdesc,
+ MakeFuncResultTuplestore(fcinfo, 0);
+ PutMemoryContextsStatsTupleStore(rsinfo->setResult, rsinfo->setDesc,
TopMemoryContext, NULL, 0);
return (Datum) 0;
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index d946f679e5..e950fc898b 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -203,27 +203,11 @@ pg_tablespace_databases(PG_FUNCTION_ARGS)
{
Oid tablespaceOid = PG_GETARG_OID(0);
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
char *location;
DIR *dirdesc;
struct dirent *de;
- MemoryContext oldcontext;
- /* 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_tablespace_databases",
- OIDOID, -1, 0);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, NULL);
-
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, TUPSTORE_SRF_USE_EXPECTED);
if (tablespaceOid == GLOBALTABLESPACE_OID)
{
@@ -279,7 +263,8 @@ pg_tablespace_databases(PG_FUNCTION_ARGS)
values[0] = ObjectIdGetDatum(datOid);
nulls[0] = false;
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
FreeDir(dirdesc);
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index abde31b854..9097cb64e9 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -461,11 +461,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
int curr_backend;
char *cmd = text_to_cstring(PG_GETARG_TEXT_PP(0));
ProgressCommandType cmdtype;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
/* Translate command name into command type code. */
if (pg_strcasecmp(cmd, "VACUUM") == 0)
@@ -485,14 +481,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid command name: \"%s\"", cmd)));
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
/* 1-based index */
for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
@@ -538,7 +527,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
nulls[i + 3] = true;
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
return (Datum) 0;
@@ -555,20 +544,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
int curr_backend;
int pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
/* 1-based index */
for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
@@ -601,7 +578,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[5] = false;
values[5] = CStringGetTextDatum("<backend information not available>");
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
continue;
}
@@ -915,7 +892,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[29] = true;
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
/* If only a single backend was requested, and we found it, break. */
if (pid != -1)
@@ -1838,22 +1815,10 @@ pg_stat_get_slru(PG_FUNCTION_ARGS)
{
#define PG_STAT_GET_SLRU_COLS 9
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
int i;
PgStat_SLRUStats *stats;
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
/* request SLRU stats from the stat collector */
stats = pgstat_fetch_slru();
@@ -1885,7 +1850,7 @@ pg_stat_get_slru(PG_FUNCTION_ARGS)
values[7] = Int64GetDatum(stat.truncate);
values[8] = TimestampTzGetDatum(stat.stat_reset_timestamp);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
return (Datum) 0;
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 40e46540c5..64c949ae23 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -4833,23 +4833,14 @@ text_to_table(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
SplitTextOutputData tstate;
- MemoryContext old_cxt;
-
- /* OK, prepare tuplestore in per-query memory */
- old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
tstate.astate = NULL;
- tstate.tupstore = MakeFuncResultTuplestore(fcinfo, NULL);
- tstate.tupdesc = CreateTupleDescCopy(rsi->expectedDesc);
-
- MemoryContextSwitchTo(old_cxt);
+ MakeFuncResultTuplestore(fcinfo, TUPSTORE_SRF_USE_EXPECTED);
+ tstate.tupstore = rsi->setResult;
+ tstate.tupdesc = rsi->setDesc;
(void) split_text(fcinfo, &tstate);
- rsi->returnMode = SFRM_Materialize;
- rsi->setResult = tstate.tupstore;
- rsi->setDesc = tstate.tupdesc;
-
return (Datum) 0;
}
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 094228c767..73c9b42c9e 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -1761,40 +1761,61 @@ build_function_result_tupdesc_d(char prokind,
}
/*
- * Helper function to construct tuplestore
+ * MakeFuncResultTuplestore
+ *
+ * Helper function to construct a tuplestore for a set-returning
+ * function, storing the tuplestore and the tupledesc created into
+ * the function's ReturnSetInfo.
+ *
+ * "flags" can be set to TUPSTORE_SRF_USE_EXPECTED, so as the tuple
+ * descriptor used comes from expectedDesc, which is the one expected
+ * by the caller. TUPSTORE_SRF_BLESS would complete the information
+ * associated to the tuple descriptor, which is necessary when the
+ * tuple descriptor comes from a transient RECORD datatype.
*/
-Tuplestorestate *
-MakeFuncResultTuplestore(FunctionCallInfo fcinfo, TupleDesc *tupdesc)
+void
+MakeFuncResultTuplestore(FunctionCallInfo fcinfo, bits32 flags)
{
bool random_access;
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
Tuplestorestate *tupstore;
+ MemoryContext old_context, per_query_ctx;
+ TupleDesc stored_tupdesc;
- /* 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 */
+ /* check to see if caller supports 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) ||
- (tupdesc == NULL && rsinfo->expectedDesc == NULL))
+ ((flags & TUPSTORE_SRF_USE_EXPECTED) != 0 && rsinfo->expectedDesc == NULL))
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 != NULL)
+ if ((flags & TUPSTORE_SRF_USE_EXPECTED) != 0)
+ stored_tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
+ else
{
- if (get_call_result_type(fcinfo, NULL, tupdesc) != TYPEFUNC_COMPOSITE)
+ if (get_call_result_type(fcinfo, NULL, &stored_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);
+ /* If requested, bless the tuple descriptor */
+ if ((flags & TUPSTORE_SRF_BLESS) != 0)
+ BlessTupleDesc(stored_tupdesc);
- return tupstore;
+ random_access = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
+
+ /* This must be done in the per-query memory context */
+ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ old_context = MemoryContextSwitchTo(per_query_ctx);
+ tupstore = tuplestore_begin_heap(random_access, false, work_mem);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = stored_tupdesc;
+ MemoryContextSwitchTo(old_context);
}
/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 21c9ec0122..1272e377f7 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10157,28 +10157,14 @@ show_all_file_settings(PG_FUNCTION_ARGS)
{
#define NUM_PG_FILE_SETTINGS_ATTS 7
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
ConfigVariable *conf;
int seqno;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
/* Scan the config files using current context as workspace */
conf = ProcessConfigFileInternal(PGC_SIGHUP, false, DEBUG3);
- /* Switch into long-lived context to construct returned data structures */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
/* Build a tuplestore to return our results in */
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- /* The rest can be done in short-lived context */
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
/* Process the results and create a tuplestore */
for (seqno = 1; conf != NULL; conf = conf->next, seqno++)
@@ -10226,7 +10212,7 @@ show_all_file_settings(PG_FUNCTION_ARGS)
nulls[6] = true;
/* shove row into tuplestore */
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
return (Datum) 0;
diff --git a/src/backend/utils/misc/pg_config.c b/src/backend/utils/misc/pg_config.c
index bb0d531443..add2ca33d8 100644
--- a/src/backend/utils/misc/pg_config.c
+++ b/src/backend/utils/misc/pg_config.c
@@ -25,26 +25,12 @@ Datum
pg_config(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- Tuplestorestate *tupstore;
- TupleDesc tupdesc;
- MemoryContext oldcontext;
ConfigData *configdata;
size_t configdata_len;
int i = 0;
- 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 */
- oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
-
/* initialize our tuplestore */
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
configdata = get_configdata(my_exec_path, &configdata_len);
for (i = 0; i < configdata_len; i++)
@@ -58,7 +44,7 @@ pg_config(PG_FUNCTION_ARGS)
values[0] = CStringGetTextDatum(configdata[i].name);
values[1] = CStringGetTextDatum(configdata[i].setting);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
return (Datum) 0;
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 1432961b7f..aeac0b29f0 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -1132,28 +1132,14 @@ Datum
pg_cursor(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
HASH_SEQ_STATUS hash_seq;
PortalHashEnt *hentry;
- /* need to build tuplestore in query context */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
/*
* 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 = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- /* generate junk in short-term context */
- MemoryContextSwitchTo(oldcontext);
+ MakeFuncResultTuplestore(fcinfo, 0);
hash_seq_init(&hash_seq, PortalHashTable);
while ((hentry = hash_seq_search(&hash_seq)) != NULL)
@@ -1175,13 +1161,9 @@ pg_cursor(PG_FUNCTION_ARGS)
values[4] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_SCROLL);
values[5] = TimestampTzGetDatum(portal->creation_time);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
return (Datum) 0;
}
--
2.35.1
On Mon, Feb 28, 2022 at 04:49:41PM +0900, Michael Paquier wrote:
In order to keep things pluggable at will, MakeFuncResultTuplestore()
has been changed to access a set of bits32 flags, able to control the
two options above. With this facility in place, I have been able to
cut much more code than the initial patch, roughly twice as of:
24 files changed, 157 insertions(+), 893 deletions(-)
So, I have been thinking about this patch once again, and after
pondering more on it, MakeFuncResultTuplestore() is actually a wrong
name now that it does much more than just creating a tuplestore, by
assigning the TupleDesc and the TupleStore into the function's
ReturnSetInfo.
This is actually setting up a function in the context of a single call
where we fill the tuplestore with all its values, so instead I have
settled down to name that SetSingleFuncCall(), to make a parallel with
the existing MultiFuncCall*(). funcapi.c is the right place for
that, and I have added more documentation in the fmgr's README as well
as funcapi.h.
--
Michael
Attachments:
v10-0001-Introduce-MakeFuncResultTuplestore.patchtext/x-diff; charset=us-asciiDownload
From 5777ec80ee45fb2975a7e61224734892bbd3503e Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 24 Feb 2022 17:27:55 +0900
Subject: [PATCH v10 1/2] Introduce MakeFuncResultTuplestore()
This is the main patch from Melanie, that I have tweaked a bit. Note
that I am not completely done with it ;p
---
src/include/funcapi.h | 2 +
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 | 17 +----
src/backend/foreign/foreign.c | 14 +---
src/backend/libpq/hba.c | 19 +----
src/backend/replication/logical/launcher.c | 16 +---
.../replication/logical/logicalfuncs.c | 16 +---
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 | 73 +++----------------
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 | 14 +---
src/backend/utils/fmgr/funcapi.c | 38 ++++++++++
src/backend/utils/misc/guc.c | 15 +---
src/backend/utils/misc/pg_config.c | 13 +---
src/backend/utils/mmgr/portalmem.c | 17 +----
24 files changed, 82 insertions(+), 461 deletions(-)
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index ba927c2f33..b9f9e92d1a 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);
/*----------
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 12e2bf4135..2fc1ed023c 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 1e8587502e..68cad0a580 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;
@@ -1861,25 +1847,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 0e04304cb0..a30e91b328 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1940,25 +1940,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;
@@ -2045,25 +2031,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;
@@ -2327,25 +2299,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 dce30aed6c..040711ee75 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -707,19 +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")));
-
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
/* need to build tuplestore in query context */
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
@@ -728,9 +715,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, &tupdesc);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index c3406c3b9d..1f86340cc8 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"
@@ -515,17 +516,6 @@ pg_options_to_table(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) ||
- rsinfo->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
options = untransformRelOptions(array);
rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
@@ -535,8 +525,8 @@ pg_options_to_table(PG_FUNCTION_ARGS)
/*
* Now prepare the result set.
*/
+ tupstore = MakeFuncResultTuplestore(fcinfo, NULL);
tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
- tupstore = tuplestore_begin_heap(true, false, work_mem);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index d84a40b726..02d43eb22e 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -2714,29 +2714,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 5a68d6dead..a1054edada 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 3bd770a3ba..b7d056653b 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -142,25 +142,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);
@@ -203,7 +189,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;
diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c
index 76055a8a03..4edc4300a8 100644
--- a/src/backend/replication/logical/origin.c
+++ b/src/backend/replication/logical/origin.c
@@ -1492,30 +1492,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 886899afd2..3e9db7714e 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -240,20 +240,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
@@ -263,7 +249,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 5a718b1fe9..79e32f7ecf 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -3411,24 +3411,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 1f023a3460..02cdda7a73 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 7926258c06..6467f25895 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 fe6863d8b4..8678511c2a 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -477,7 +477,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;
@@ -495,24 +494,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;
@@ -571,31 +559,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 2457061f97..6f5f2451fe 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -1927,27 +1927,14 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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;
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
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);
@@ -2037,28 +2024,15 @@ each_worker(FunctionCallInfo fcinfo, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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;
- (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);
@@ -2227,29 +2201,14 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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) ||
- rsi->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
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);
+ tuple_store = MakeFuncResultTuplestore(fcinfo, NULL);
+ tupdesc = rsi->expectedDesc;
ret_tdesc = CreateTupleDescCopy(tupdesc);
BlessTupleDesc(ret_tdesc);
- tuple_store =
- tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
- false, work_mem);
MemoryContextSwitchTo(old_cxt);
@@ -2339,30 +2298,16 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- if (!rsi || !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) ||
- rsi->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
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 */
old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+ state->tuple_store = MakeFuncResultTuplestore(fcinfo, NULL);
+
+ tupdesc = rsi->expectedDesc;
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);
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index c7c95adf97..9b89651808 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 e79eb6b478..d946f679e5 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 fd993d0d5f..a96bdb3b3e 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;
@@ -574,24 +560,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;
@@ -1873,24 +1845,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 b2003f5672..40e46540c5 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"
@@ -4834,23 +4835,12 @@ 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))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("set-valued function called in context that cannot accept a set")));
- if (!(rsi->allowedModes & SFRM_Materialize) ||
- rsi->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not allowed in this context")));
-
/* OK, prepare tuplestore in per-query memory */
old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
tstate.astate = NULL;
+ tstate.tupstore = MakeFuncResultTuplestore(fcinfo, NULL);
tstate.tupdesc = CreateTupleDescCopy(rsi->expectedDesc);
- tstate.tupstore = tuplestore_begin_heap(true, false, work_mem);
MemoryContextSwitchTo(old_cxt);
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 5d913ae08d..094228c767 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"
@@ -1758,6 +1760,42 @@ 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) ||
+ (tupdesc == NULL && rsinfo->expectedDesc == NULL))
+ 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 != NULL)
+ {
+ 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 1e3650184b..21c9ec0122 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10164,19 +10164,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")));
-
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
/* Scan the config files using current context as workspace */
conf = ProcessConfigFileInternal(PGC_SIGHUP, false, DEBUG3);
@@ -10185,7 +10172,7 @@ show_all_file_settings(PG_FUNCTION_ARGS)
oldcontext = MemoryContextSwitchTo(per_query_ctx);
/* Build a tuplestore to return our results in */
- 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/misc/pg_config.c b/src/backend/utils/misc/pg_config.c
index e646a41910..bb0d531443 100644
--- a/src/backend/utils/misc/pg_config.c
+++ b/src/backend/utils/misc/pg_config.c
@@ -32,23 +32,14 @@ pg_config(PG_FUNCTION_ARGS)
size_t configdata_len;
int i = 0;
- /* 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 (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 */
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
- tupstore = tuplestore_begin_heap(true, false, work_mem);
+ /* initialize our tuplestore */
+ tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index afc03682d9..027f98928a 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -1139,30 +1139,15 @@ 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);
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
/*
* 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, &tupdesc);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
--
2.35.1
v10-0002-Introduce-more-simplifications-using-SetSingleFu.patchtext/x-diff; charset=us-asciiDownload
From 04a660979715c5645a727f3fdc296c72c9f3ff05 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 2 Mar 2022 15:41:44 +0900
Subject: [PATCH v10 2/2] Introduce more simplifications, using
SetSingleFuncCall()
This doubles the amount of code cut compared to the first patch, adding
more SRF-related logic into a central function make for the case of
functions called only once.
---
src/include/funcapi.h | 14 ++-
src/backend/access/transam/xlogfuncs.c | 17 +---
src/backend/commands/event_trigger.c | 34 ++-----
src/backend/commands/extension.c | 51 ++--------
src/backend/commands/prepare.c | 19 +---
src/backend/foreign/foreign.c | 22 +----
src/backend/libpq/hba.c | 29 ++----
src/backend/replication/logical/launcher.c | 17 +---
.../replication/logical/logicalfuncs.c | 7 +-
src/backend/replication/logical/origin.c | 21 +---
src/backend/replication/slotfuncs.c | 17 +---
src/backend/replication/walsender.c | 17 +---
src/backend/storage/ipc/shmem.c | 21 +---
src/backend/utils/adt/datetime.c | 15 +--
src/backend/utils/adt/genfile.c | 34 +------
src/backend/utils/adt/jsonfuncs.c | 86 +++-------------
src/backend/utils/adt/mcxtfuncs.c | 17 +---
src/backend/utils/adt/misc.c | 21 +---
src/backend/utils/adt/pgstatfuncs.c | 49 ++-------
src/backend/utils/adt/varlena.c | 15 +--
src/backend/utils/fmgr/README | 4 +
src/backend/utils/fmgr/funcapi.c | 99 ++++++++++++-------
src/backend/utils/misc/guc.c | 18 +---
src/backend/utils/misc/pg_config.c | 18 +---
src/backend/utils/mmgr/portalmem.c | 22 +----
25 files changed, 169 insertions(+), 515 deletions(-)
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index b9f9e92d1a..dc3d819a1c 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -229,8 +229,6 @@ 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);
/*----------
@@ -280,14 +278,20 @@ extern Tuplestorestate *MakeFuncResultTuplestore(FunctionCallInfo fcinfo,
* memory allocated in multi_call_memory_ctx, but holding file descriptors or
* other non-memory resources open across calls is a bug. SRFs that need
* such resources should not use these macros, but instead populate a
- * tuplestore during a single call, and return that using SFRM_Materialize
- * mode (see fmgr/README). Alternatively, set up a callback to release
- * resources at query shutdown, using RegisterExprContextCallback().
+ * tuplestore during a single call, as set up by SetSingleFuncCall() (see
+ * fmgr/README). Alternatively, set up a callback to release resources
+ * at query shutdown, using RegisterExprContextCallback().
*
*----------
*/
/* from funcapi.c */
+
+/* flag bits for SetSingleFuncCall() */
+#define SRF_SINGLE_USE_EXPECTED 0x01 /* use expectedDesc as tupdesc */
+#define SRF_SINGLE_BLESS 0x02 /* validate tuple for SRF */
+extern void SetSingleFuncCall(FunctionCallInfo fcinfo, bits32 flags);
+
extern FuncCallContext *init_MultiFuncCall(PG_FUNCTION_ARGS);
extern FuncCallContext *per_MultiFuncCall(PG_FUNCTION_ARGS);
extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 2fc1ed023c..4603de3144 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -166,10 +166,6 @@ Datum
pg_stop_backup_v2(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
Datum values[3];
bool nulls[3];
@@ -178,15 +174,7 @@ pg_stop_backup_v2(PG_FUNCTION_ARGS)
XLogRecPtr stoppoint;
SessionBackupState status = get_backup_status();
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
MemSet(values, 0, sizeof(values));
MemSet(nulls, 0, sizeof(nulls));
@@ -237,7 +225,8 @@ pg_stop_backup_v2(PG_FUNCTION_ARGS)
/* Stoppoint is included on both exclusive and nonexclusive backups */
values[0] = LSNGetDatum(stoppoint);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
return (Datum) 0;
}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 68cad0a580..3c3fc2515b 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1290,10 +1290,6 @@ Datum
pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
slist_iter iter;
/*
@@ -1307,15 +1303,7 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
"pg_event_trigger_dropped_objects()")));
/* Build tuplestore to hold the result rows */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
slist_foreach(iter, &(currentEventTriggerState->SQLDropList))
{
@@ -1384,7 +1372,8 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
nulls[i++] = true;
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
return (Datum) 0;
@@ -1832,10 +1821,6 @@ Datum
pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
ListCell *lc;
/*
@@ -1848,15 +1833,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
"pg_event_trigger_ddl_commands()")));
/* Build tuplestore to hold the result rows */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
foreach(lc, currentEventTriggerState->commandList)
{
@@ -2027,7 +2004,8 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
break;
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
PG_RETURN_VOID();
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index a30e91b328..d8df277630 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1932,24 +1932,12 @@ Datum
pg_available_extensions(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
char *location;
DIR *dir;
struct dirent *de;
/* Build tuplestore to hold the result rows */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
location = get_extension_control_directory();
dir = AllocateDir(location);
@@ -2001,7 +1989,8 @@ pg_available_extensions(PG_FUNCTION_ARGS)
else
values[2] = CStringGetTextDatum(control->comment);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
FreeDir(dir);
@@ -2023,24 +2012,12 @@ Datum
pg_available_extension_versions(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
char *location;
DIR *dir;
struct dirent *de;
/* Build tuplestore to hold the result rows */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
location = get_extension_control_directory();
dir = AllocateDir(location);
@@ -2075,7 +2052,8 @@ pg_available_extension_versions(PG_FUNCTION_ARGS)
control = read_extension_control_file(extname);
/* scan extension's script directory for install scripts */
- get_available_versions_for_extension(control, tupstore, tupdesc);
+ get_available_versions_for_extension(control, rsinfo->setResult,
+ rsinfo->setDesc);
}
FreeDir(dir);
@@ -2288,10 +2266,6 @@ pg_extension_update_paths(PG_FUNCTION_ARGS)
{
Name extname = PG_GETARG_NAME(0);
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
List *evi_list;
ExtensionControlFile *control;
ListCell *lc1;
@@ -2300,15 +2274,7 @@ pg_extension_update_paths(PG_FUNCTION_ARGS)
check_valid_extension_name(NameStr(*extname));
/* Build tuplestore to hold the result rows */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
/* Read the extension's control file */
control = read_extension_control_file(NameStr(*extname));
@@ -2365,7 +2331,8 @@ pg_extension_update_paths(PG_FUNCTION_ARGS)
pfree(pathbuf.data);
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
}
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 040711ee75..d2d8ee120c 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -702,26 +702,12 @@ Datum
pg_prepared_statement(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
-
- /* need to build tuplestore in query context */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
/*
* 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 = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- /* generate junk in short-term context */
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
/* hash table might be uninitialized */
if (prepared_queries)
@@ -746,7 +732,8 @@ pg_prepared_statement(PG_FUNCTION_ARGS)
values[5] = Int64GetDatumFast(prep_stmt->plansource->num_generic_plans);
values[6] = Int64GetDatumFast(prep_stmt->plansource->num_custom_plans);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
}
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index 1f86340cc8..cf222fc3e9 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -511,27 +511,12 @@ pg_options_to_table(PG_FUNCTION_ARGS)
ListCell *cell;
List *options;
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
options = untransformRelOptions(array);
rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- /*
- * Now prepare the result set.
- */
- tupstore = MakeFuncResultTuplestore(fcinfo, NULL);
- tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ /* prepare the result set */
+ SetSingleFuncCall(fcinfo, SRF_SINGLE_USE_EXPECTED);
foreach(cell, options)
{
@@ -551,7 +536,8 @@ pg_options_to_table(PG_FUNCTION_ARGS)
values[1] = (Datum) 0;
nulls[1] = true;
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
return (Datum) 0;
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 02d43eb22e..90953c38f3 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -1685,8 +1685,8 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
if (parsedline->auth_method == uaCert)
{
/*
- * For auth method cert, client certificate validation is mandatory, and it implies
- * the level of verify-full.
+ * For auth method cert, client certificate validation is mandatory,
+ * and it implies the level of verify-full.
*/
parsedline->clientcert = clientCertFull;
}
@@ -2703,30 +2703,19 @@ fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
Datum
pg_hba_file_rules(PG_FUNCTION_ARGS)
{
- Tuplestorestate *tuple_store;
- TupleDesc tupdesc;
- MemoryContext old_cxt;
ReturnSetInfo *rsi;
/*
- * We must use the Materialize mode to be safe against HBA file changes
- * while the cursor is open. It's also more efficient than having to look
- * up our current position in the parsed list every time.
+ * Build tuplestore to hold the result rows. We must use the Materialize
+ * mode to be safe against HBA file changes while the cursor is open.
+ * It's also more efficient than having to look up our current position in
+ * the parsed list every time.
*/
- rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- rsi->returnMode = SFRM_Materialize;
-
- /* Build tuplestore to hold the result rows */
- old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
-
- tuple_store = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsi->setDesc = tupdesc;
- rsi->setResult = tuple_store;
-
- MemoryContextSwitchTo(old_cxt);
+ SetSingleFuncCall(fcinfo, 0);
/* Fill the tuplestore */
- fill_hba_view(tuple_store, tupdesc);
+ rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+ fill_hba_view(rsi->setResult, rsi->setDesc);
PG_RETURN_NULL();
}
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index a1054edada..6f25b2c2ad 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -930,20 +930,8 @@ pg_stat_get_subscription(PG_FUNCTION_ARGS)
Oid subid = PG_ARGISNULL(0) ? InvalidOid : PG_GETARG_OID(0);
int i;
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
/* Make sure we get consistent view of the workers. */
LWLockAcquire(LogicalRepWorkerLock, LW_SHARED);
@@ -996,7 +984,8 @@ pg_stat_get_subscription(PG_FUNCTION_ARGS)
else
values[7] = TimestampTzGetDatum(worker.reply_time);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
/*
* If only a single subscription was requested, and we found it,
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index b7d056653b..6058d36e0d 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -189,10 +189,9 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
}
}
- p->tupstore = MakeFuncResultTuplestore(fcinfo, &p->tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = p->tupstore;
- rsinfo->setDesc = p->tupdesc;
+ SetSingleFuncCall(fcinfo, 0);
+ p->tupstore = rsinfo->setResult;
+ p->tupdesc = rsinfo->setDesc;
/*
* Compute the current end-of-wal.
diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c
index 4edc4300a8..0e38eff0f0 100644
--- a/src/backend/replication/logical/origin.c
+++ b/src/backend/replication/logical/origin.c
@@ -1482,29 +1482,13 @@ Datum
pg_show_replication_origin_status(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
int i;
#define REPLICATION_ORIGIN_PROGRESS_COLS 4
/* we want to return 0 rows if slot is set to zero */
replorigin_check_prerequisites(false, true);
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- 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");
+ SetSingleFuncCall(fcinfo, 0);
/* prevent slots from being concurrently dropped */
LWLockAcquire(ReplicationOriginLock, LW_SHARED);
@@ -1554,7 +1538,8 @@ pg_show_replication_origin_status(PG_FUNCTION_ARGS)
LWLockRelease(&state->lock);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
LWLockRelease(ReplicationOriginLock);
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index 3e9db7714e..ca945994ef 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -233,10 +233,6 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
{
#define PG_GET_REPLICATION_SLOTS_COLS 14
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
XLogRecPtr currlsn;
int slotno;
@@ -246,15 +242,7 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
* name, which shouldn't contain anything particularly sensitive.
*/
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
currlsn = GetXLogWriteRecPtr();
@@ -417,7 +405,8 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
Assert(i == PG_GET_REPLICATION_SLOTS_COLS);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
LWLockRelease(ReplicationSlotControlLock);
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 79e32f7ecf..2d0292a092 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -3403,23 +3403,11 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
{
#define PG_STAT_GET_WAL_SENDERS_COLS 12
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
SyncRepStandbyData *sync_standbys;
int num_standbys;
int i;
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
/*
* Get the currently active synchronous standbys. This could be out of
@@ -3563,7 +3551,8 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS)
values[11] = TimestampTzGetDatum(replyTime);
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
return (Datum) 0;
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 02cdda7a73..c1279960cd 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -537,25 +537,13 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
{
#define PG_GET_SHMEM_SIZES_COLS 4
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
HASH_SEQ_STATUS hstat;
ShmemIndexEnt *ent;
Size named_allocated = 0;
Datum values[PG_GET_SHMEM_SIZES_COLS];
bool nulls[PG_GET_SHMEM_SIZES_COLS];
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
LWLockAcquire(ShmemIndexLock, LW_SHARED);
@@ -571,7 +559,8 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
values[3] = Int64GetDatum(ent->allocated_size);
named_allocated += ent->allocated_size;
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
/* output shared memory allocated but not counted via the shmem index */
@@ -579,7 +568,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
nulls[1] = true;
values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
values[3] = values[2];
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
/* output as-of-yet unused shared memory */
nulls[0] = true;
@@ -587,7 +576,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
nulls[1] = false;
values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
values[3] = values[2];
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
LWLockRelease(ShmemIndexLock);
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 6467f25895..ba0ec35ac5 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -4786,8 +4786,6 @@ Datum
pg_timezone_names(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
pg_tzenum *tzenum;
pg_tz *tz;
Datum values[4];
@@ -4798,17 +4796,8 @@ pg_timezone_names(PG_FUNCTION_ARGS)
const char *tzn;
Interval *resInterval;
struct pg_tm itm;
- MemoryContext oldcontext;
- /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
- oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
/* initialize timezone scanning code */
tzenum = pg_tzenumerate_start();
@@ -4850,7 +4839,7 @@ pg_timezone_names(PG_FUNCTION_ARGS)
values[3] = BoolGetDatum(tm.tm_isdst > 0);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
pg_tzenumerate_end(tzenum);
diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index 8678511c2a..1ed01620a1 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -477,11 +477,8 @@ pg_ls_dir(PG_FUNCTION_ARGS)
char *location;
bool missing_ok = false;
bool include_dot_dirs = false;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
DIR *dirdesc;
struct dirent *de;
- MemoryContext oldcontext;
location = convert_and_check_filename(PG_GETARG_TEXT_PP(0));
@@ -494,18 +491,7 @@ pg_ls_dir(PG_FUNCTION_ARGS)
include_dot_dirs = PG_GETARG_BOOL(2);
}
- /* 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);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, NULL);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, SRF_SINGLE_USE_EXPECTED);
dirdesc = AllocateDir(location);
if (!dirdesc)
@@ -529,7 +515,8 @@ pg_ls_dir(PG_FUNCTION_ARGS)
values[0] = CStringGetTextDatum(de->d_name);
nulls[0] = false;
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
FreeDir(dirdesc);
@@ -559,21 +546,10 @@ static Datum
pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
DIR *dirdesc;
struct dirent *de;
- MemoryContext oldcontext;
- /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
- oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
/*
* Now walk the directory. Note that we must do this within a single SRF
@@ -621,7 +597,7 @@ pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok)
values[2] = TimestampTzGetDatum(time_t_to_timestamptz(attrib.st_mtime));
memset(nulls, 0, sizeof(nulls));
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
FreeDir(dirdesc);
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 6f5f2451fe..29664aa6e4 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -1909,9 +1909,6 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
{
Jsonb *jb = PG_GETARG_JSONB_P(0);
ReturnSetInfo *rsi;
- Tuplestorestate *tuple_store;
- TupleDesc tupdesc;
- TupleDesc ret_tdesc;
MemoryContext old_cxt,
tmp_cxt;
bool skipNested = false;
@@ -1926,17 +1923,7 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
funcname)));
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-
- rsi->returnMode = SFRM_Materialize;
-
- old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
-
- tuple_store = MakeFuncResultTuplestore(fcinfo, &tupdesc);
-
- ret_tdesc = CreateTupleDescCopy(tupdesc);
- BlessTupleDesc(ret_tdesc);
-
- MemoryContextSwitchTo(old_cxt);
+ SetSingleFuncCall(fcinfo, SRF_SINGLE_BLESS);
tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
"jsonb_each temporary cxt",
@@ -1951,7 +1938,6 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
if (r == WJB_KEY)
{
text *key;
- HeapTuple tuple;
Datum values[2];
bool nulls[2] = {false, false};
@@ -1988,9 +1974,7 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
values[1] = PointerGetDatum(val);
}
- tuple = heap_form_tuple(ret_tdesc, values, nulls);
-
- tuplestore_puttuple(tuple_store, tuple);
+ tuplestore_putvalues(rsi->setResult, rsi->setDesc, values, nulls);
/* clean up and switch back */
MemoryContextSwitchTo(old_cxt);
@@ -2000,9 +1984,6 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
MemoryContextDelete(tmp_cxt);
- rsi->setResult = tuple_store;
- rsi->setDesc = ret_tdesc;
-
PG_RETURN_NULL();
}
@@ -2014,8 +1995,6 @@ each_worker(FunctionCallInfo fcinfo, bool as_text)
JsonLexContext *lex;
JsonSemAction *sem;
ReturnSetInfo *rsi;
- MemoryContext old_cxt;
- TupleDesc tupdesc;
EachState *state;
lex = makeJsonLexContext(json, true);
@@ -2024,17 +2003,9 @@ each_worker(FunctionCallInfo fcinfo, bool as_text)
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- rsi->returnMode = SFRM_Materialize;
-
- /* 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);
-
- MemoryContextSwitchTo(old_cxt);
+ SetSingleFuncCall(fcinfo, SRF_SINGLE_BLESS);
+ state->tuple_store = rsi->setResult;
+ state->ret_tdesc = rsi->setDesc;
sem->semstate = (void *) state;
sem->array_start = each_array_start;
@@ -2053,9 +2024,6 @@ each_worker(FunctionCallInfo fcinfo, bool as_text)
MemoryContextDelete(state->tmp_cxt);
- rsi->setResult = state->tuple_store;
- rsi->setDesc = state->ret_tdesc;
-
PG_RETURN_NULL();
}
@@ -2180,9 +2148,6 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
{
Jsonb *jb = PG_GETARG_JSONB_P(0);
ReturnSetInfo *rsi;
- Tuplestorestate *tuple_store;
- TupleDesc tupdesc;
- TupleDesc ret_tdesc;
MemoryContext old_cxt,
tmp_cxt;
bool skipNested = false;
@@ -2201,16 +2166,8 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- rsi->returnMode = SFRM_Materialize;
-
- old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
-
- tuple_store = MakeFuncResultTuplestore(fcinfo, NULL);
- tupdesc = rsi->expectedDesc;
- ret_tdesc = CreateTupleDescCopy(tupdesc);
- BlessTupleDesc(ret_tdesc);
-
- MemoryContextSwitchTo(old_cxt);
+ SetSingleFuncCall(fcinfo,
+ SRF_SINGLE_USE_EXPECTED | SRF_SINGLE_BLESS);
tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
"jsonb_array_elements temporary cxt",
@@ -2224,7 +2181,6 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
if (r == WJB_ELEM)
{
- HeapTuple tuple;
Datum values[1];
bool nulls[1] = {false};
@@ -2250,9 +2206,7 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
values[0] = PointerGetDatum(val);
}
- tuple = heap_form_tuple(ret_tdesc, values, nulls);
-
- tuplestore_puttuple(tuple_store, tuple);
+ tuplestore_putvalues(rsi->setResult, rsi->setDesc, values, nulls);
/* clean up and switch back */
MemoryContextSwitchTo(old_cxt);
@@ -2262,9 +2216,6 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
MemoryContextDelete(tmp_cxt);
- rsi->setResult = tuple_store;
- rsi->setDesc = ret_tdesc;
-
PG_RETURN_NULL();
}
@@ -2289,27 +2240,15 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
JsonLexContext *lex = makeJsonLexContext(json, as_text);
JsonSemAction *sem;
ReturnSetInfo *rsi;
- MemoryContext old_cxt;
- TupleDesc tupdesc;
ElementsState *state;
state = palloc0(sizeof(ElementsState));
sem = palloc0(sizeof(JsonSemAction));
+ SetSingleFuncCall(fcinfo, SRF_SINGLE_USE_EXPECTED | SRF_SINGLE_BLESS);
rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-
- rsi->returnMode = SFRM_Materialize;
-
- /* make these in a sufficiently long-lived memory context */
- old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
-
- state->tuple_store = MakeFuncResultTuplestore(fcinfo, NULL);
-
- tupdesc = rsi->expectedDesc;
- state->ret_tdesc = CreateTupleDescCopy(tupdesc);
- BlessTupleDesc(state->ret_tdesc);
-
- MemoryContextSwitchTo(old_cxt);
+ state->tuple_store = rsi->setResult;
+ state->ret_tdesc = rsi->setDesc;
sem->semstate = (void *) state;
sem->object_start = elements_object_start;
@@ -2329,9 +2268,6 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
MemoryContextDelete(state->tmp_cxt);
- rsi->setResult = state->tuple_store;
- rsi->setDesc = state->ret_tdesc;
-
PG_RETURN_NULL();
}
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 9b89651808..bb7cc94024 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -120,22 +120,9 @@ Datum
pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
-
- PutMemoryContextsStatsTupleStore(tupstore, tupdesc,
+ SetSingleFuncCall(fcinfo, 0);
+ PutMemoryContextsStatsTupleStore(rsinfo->setResult, rsinfo->setDesc,
TopMemoryContext, NULL, 0);
return (Datum) 0;
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index d946f679e5..4568749d23 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -203,27 +203,11 @@ pg_tablespace_databases(PG_FUNCTION_ARGS)
{
Oid tablespaceOid = PG_GETARG_OID(0);
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
char *location;
DIR *dirdesc;
struct dirent *de;
- MemoryContext oldcontext;
- /* 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_tablespace_databases",
- OIDOID, -1, 0);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, NULL);
-
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, SRF_SINGLE_USE_EXPECTED);
if (tablespaceOid == GLOBALTABLESPACE_OID)
{
@@ -279,7 +263,8 @@ pg_tablespace_databases(PG_FUNCTION_ARGS)
values[0] = ObjectIdGetDatum(datOid);
nulls[0] = false;
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+ values, nulls);
}
FreeDir(dirdesc);
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index a96bdb3b3e..eff45b16f2 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -461,11 +461,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
int curr_backend;
char *cmd = text_to_cstring(PG_GETARG_TEXT_PP(0));
ProgressCommandType cmdtype;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
/* Translate command name into command type code. */
if (pg_strcasecmp(cmd, "VACUUM") == 0)
@@ -485,14 +481,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid command name: \"%s\"", cmd)));
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
/* 1-based index */
for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
@@ -538,7 +527,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
nulls[i + 3] = true;
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
return (Datum) 0;
@@ -555,20 +544,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
int curr_backend;
int pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
/* 1-based index */
for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
@@ -601,7 +578,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[5] = false;
values[5] = CStringGetTextDatum("<backend information not available>");
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
continue;
}
@@ -915,7 +892,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[29] = true;
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
/* If only a single backend was requested, and we found it, break. */
if (pid != -1)
@@ -1838,22 +1815,10 @@ pg_stat_get_slru(PG_FUNCTION_ARGS)
{
#define PG_STAT_GET_SLRU_COLS 9
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
int i;
PgStat_SLRUStats *stats;
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
/* request SLRU stats from the stat collector */
stats = pgstat_fetch_slru();
@@ -1885,7 +1850,7 @@ pg_stat_get_slru(PG_FUNCTION_ARGS)
values[7] = Int64GetDatum(stat.truncate);
values[8] = TimestampTzGetDatum(stat.stat_reset_timestamp);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
return (Datum) 0;
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 40e46540c5..22ab5a4329 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -4833,23 +4833,14 @@ text_to_table(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
SplitTextOutputData tstate;
- MemoryContext old_cxt;
-
- /* OK, prepare tuplestore in per-query memory */
- old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
tstate.astate = NULL;
- tstate.tupstore = MakeFuncResultTuplestore(fcinfo, NULL);
- tstate.tupdesc = CreateTupleDescCopy(rsi->expectedDesc);
-
- MemoryContextSwitchTo(old_cxt);
+ SetSingleFuncCall(fcinfo, SRF_SINGLE_USE_EXPECTED);
+ tstate.tupstore = rsi->setResult;
+ tstate.tupdesc = rsi->setDesc;
(void) split_text(fcinfo, &tstate);
- rsi->returnMode = SFRM_Materialize;
- rsi->setResult = tstate.tupstore;
- rsi->setDesc = tstate.tupdesc;
-
return (Datum) 0;
}
diff --git a/src/backend/utils/fmgr/README b/src/backend/utils/fmgr/README
index 1e4c4b94a9..9d8848106d 100644
--- a/src/backend/utils/fmgr/README
+++ b/src/backend/utils/fmgr/README
@@ -305,6 +305,10 @@ If available, the expected tuple descriptor is passed in ReturnSetInfo;
in other contexts the expectedDesc field will be NULL. The function need
not pay attention to expectedDesc, but it may be useful in special cases.
+SetSingleFuncCall() is a helper function able to setup the function's
+ReturnSetInfo for a single call, filling in the Tuplestore and the
+TupleDesc with the proper configuration for Materialize mode.
+
There is no support for functions accepting sets; instead, the function will
be called multiple times, once for each element of the input set.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 094228c767..f817b0b9b7 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -56,6 +56,69 @@ static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
static TypeFuncClass get_type_func_class(Oid typid, Oid *base_typeid);
+/*
+ * SetSingleFuncCall
+ *
+ * Helper function to construct a tuplestore for a set-returning
+ * function in the context of a single call, storing the tuplestore
+ * and the tupledesc created into the function's ReturnSetInfo.
+ *
+ * "flags" can be set to SRF_SINGLE_USE_EXPECTED, so as the tuple
+ * descriptor used comes from expectedDesc, which is the one expected
+ * by the caller. SRF_SINGLE_BLESS would complete the information
+ * associated to the tuple descriptor, which is necessary when the
+ * tuple descriptor comes from a transient RECORD datatype.
+ */
+void
+SetSingleFuncCall(FunctionCallInfo fcinfo, bits32 flags)
+{
+ bool random_access;
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ Tuplestorestate *tupstore;
+ MemoryContext old_context,
+ per_query_ctx;
+ TupleDesc stored_tupdesc;
+
+ /* check to see if caller supports 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) ||
+ ((flags & SRF_SINGLE_USE_EXPECTED) != 0 && rsinfo->expectedDesc == NULL))
+ 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 ((flags & SRF_SINGLE_USE_EXPECTED) != 0)
+ stored_tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
+ else
+ {
+ if (get_call_result_type(fcinfo, NULL, &stored_tupdesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
+ }
+
+ /* If requested, bless the tuple descriptor */
+ if ((flags & SRF_SINGLE_BLESS) != 0)
+ BlessTupleDesc(stored_tupdesc);
+
+ random_access = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
+
+ /*
+ * Store the tuplestore and the tuple descriptor in ReturnSetInfo. This
+ * must be done in the per-query memory context.
+ */
+ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ old_context = MemoryContextSwitchTo(per_query_ctx);
+ tupstore = tuplestore_begin_heap(random_access, false, work_mem);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = stored_tupdesc;
+ MemoryContextSwitchTo(old_context);
+}
+
+
/*
* init_MultiFuncCall
* Create an empty FuncCallContext data structure
@@ -1760,42 +1823,6 @@ 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) ||
- (tupdesc == NULL && rsinfo->expectedDesc == NULL))
- 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 != NULL)
- {
- 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 21c9ec0122..6d11f9c71b 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10157,28 +10157,14 @@ show_all_file_settings(PG_FUNCTION_ARGS)
{
#define NUM_PG_FILE_SETTINGS_ATTS 7
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
ConfigVariable *conf;
int seqno;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
/* Scan the config files using current context as workspace */
conf = ProcessConfigFileInternal(PGC_SIGHUP, false, DEBUG3);
- /* Switch into long-lived context to construct returned data structures */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
/* Build a tuplestore to return our results in */
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- /* The rest can be done in short-lived context */
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
/* Process the results and create a tuplestore */
for (seqno = 1; conf != NULL; conf = conf->next, seqno++)
@@ -10226,7 +10212,7 @@ show_all_file_settings(PG_FUNCTION_ARGS)
nulls[6] = true;
/* shove row into tuplestore */
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
return (Datum) 0;
diff --git a/src/backend/utils/misc/pg_config.c b/src/backend/utils/misc/pg_config.c
index bb0d531443..d9e18caf44 100644
--- a/src/backend/utils/misc/pg_config.c
+++ b/src/backend/utils/misc/pg_config.c
@@ -25,26 +25,12 @@ Datum
pg_config(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- Tuplestorestate *tupstore;
- TupleDesc tupdesc;
- MemoryContext oldcontext;
ConfigData *configdata;
size_t configdata_len;
int i = 0;
- 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 */
- oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
-
/* initialize our tuplestore */
- tupstore = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
configdata = get_configdata(my_exec_path, &configdata_len);
for (i = 0; i < configdata_len; i++)
@@ -58,7 +44,7 @@ pg_config(PG_FUNCTION_ARGS)
values[0] = CStringGetTextDatum(configdata[i].name);
values[1] = CStringGetTextDatum(configdata[i].setting);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
return (Datum) 0;
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 027f98928a..d549f66d4a 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -1132,28 +1132,14 @@ Datum
pg_cursor(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
HASH_SEQ_STATUS hash_seq;
PortalHashEnt *hentry;
- /* need to build tuplestore in query context */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
/*
* 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 = MakeFuncResultTuplestore(fcinfo, &tupdesc);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- /* generate junk in short-term context */
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
hash_seq_init(&hash_seq, PortalHashTable);
while ((hentry = hash_seq_search(&hash_seq)) != NULL)
@@ -1175,13 +1161,9 @@ pg_cursor(PG_FUNCTION_ARGS)
values[4] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_SCROLL);
values[5] = TimestampTzGetDatum(portal->creation_time);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
return (Datum) 0;
}
--
2.35.1
On Wed, Mar 02, 2022 at 03:43:17PM +0900, Michael Paquier wrote:
This is actually setting up a function in the context of a single call
where we fill the tuplestore with all its values, so instead I have
settled down to name that SetSingleFuncCall(), to make a parallel with
the existing MultiFuncCall*(). funcapi.c is the right place for
that, and I have added more documentation in the fmgr's README as well
as funcapi.h.
I have tortured all those code paths for the last couple of days, and
the new function name, as well as its options, still seemed fine to
me, so I have applied the patch. The buildfarm is cool with it. It
is worth noting that there are more code paths in contrib/ that could
be simplified with this new routine.
--
Michael
On Sun, Mar 6, 2022 at 9:29 PM Michael Paquier <michael@paquier.xyz> wrote:
On Wed, Mar 02, 2022 at 03:43:17PM +0900, Michael Paquier wrote:
This is actually setting up a function in the context of a single call
where we fill the tuplestore with all its values, so instead I have
settled down to name that SetSingleFuncCall(), to make a parallel with
the existing MultiFuncCall*(). funcapi.c is the right place for
that, and I have added more documentation in the fmgr's README as well
as funcapi.h.I have tortured all those code paths for the last couple of days, and
the new function name, as well as its options, still seemed fine to
me, so I have applied the patch. The buildfarm is cool with it. It
is worth noting that there are more code paths in contrib/ that could
be simplified with this new routine.
Wow! Thanks so much for taking the time to review, refine, and commit
this work.
I've attached a patch using the helper in most locations in contrib
modules that seemed useful.
The following I don't think we can use the helper or it is not worth it:
- pg_logdir_ls() in contrib/adminpack has return type TYPEFUNC_RECORD
and expectedDesc is not already created, so the helper can't be used.
But basically, since it doesn't specify OUT argument names, it has to
do TupleDescInitEntry() itself anyway, I think.
- contrib/tablefunc.c was also not simple to refactor. the various parts
of SetSingleFuncCall are spread throughout different functions in the
file.
- contrib/dblink has one function which returns a tuplestore that was
simple to change (dblink_get_notify()) and I've done so.
However, most of the other creation of tuplestore and tupledescriptors
is in helper functions (like materializeResult()) which separate the
tuplestore creation from the tuple descriptor initialization in a way
that made it hard to do a drop-in replacement with the helper function.
- Melanie
Attachments:
0001-contrib-SRFs-use-helper.patchtext/x-patch; charset=US-ASCII; name=0001-contrib-SRFs-use-helper.patchDownload
From a734a4dbf33230242f53bdc4ab6d23a98d6def8c Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Mon, 7 Mar 2022 14:22:22 -0500
Subject: [PATCH] contrib SRFs use helper
9e98583898c introduced a helper to centralize building their needed
state (tuplestore, tuple descriptors, etc) and checking for any errors.
Use this helper in contrib modules in which it is a simple drop-in
replacement.
---
contrib/amcheck/verify_heapam.c | 48 ++-----------
contrib/dblink/dblink.c | 26 +------
contrib/pageinspect/brinfuncs.c | 31 +-------
contrib/pageinspect/gistfuncs.c | 60 ++--------------
.../pg_stat_statements/pg_stat_statements.c | 33 +--------
contrib/pgrowlocks/pgrowlocks.c | 34 ++-------
contrib/postgres_fdw/connection.c | 31 +-------
contrib/xml2/xpath.c | 72 +++----------------
8 files changed, 34 insertions(+), 301 deletions(-)
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index f996f9a572..60e2f36183 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -165,7 +165,6 @@ static bool check_tuple_visibility(HeapCheckContext *ctx);
static void report_corruption(HeapCheckContext *ctx, char *msg);
static void report_toast_corruption(HeapCheckContext *ctx,
ToastedAttribute *ta, char *msg);
-static TupleDesc verify_heapam_tupdesc(void);
static FullTransactionId FullTransactionIdFromXidAndCtx(TransactionId xid,
const HeapCheckContext *ctx);
static void update_cached_xid_range(HeapCheckContext *ctx);
@@ -214,8 +213,6 @@ Datum
verify_heapam(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- MemoryContext old_context;
- bool random_access;
HeapCheckContext ctx;
Buffer vmbuffer = InvalidBuffer;
Oid relid;
@@ -227,16 +224,6 @@ verify_heapam(PG_FUNCTION_ARGS)
BlockNumber nblocks;
const char *skip;
- /* 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")));
-
/* Check supplied arguments */
if (PG_ARGISNULL(0))
ereport(ERROR,
@@ -290,15 +277,12 @@ verify_heapam(PG_FUNCTION_ARGS)
*/
ctx.attnum = -1;
- /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
- old_context = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
- random_access = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
- ctx.tupdesc = verify_heapam_tupdesc();
- ctx.tupstore = tuplestore_begin_heap(random_access, false, work_mem);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = ctx.tupstore;
- rsinfo->setDesc = ctx.tupdesc;
- MemoryContextSwitchTo(old_context);
+ /*
+ * Construct the tuplestore and tuple descriptor
+ */
+ SetSingleFuncCall(fcinfo, 0);
+ ctx.tupdesc = rsinfo->setDesc;
+ ctx.tupstore = rsinfo->setResult;
/* Open relation, check relkind and access method */
ctx.rel = relation_open(relid, AccessShareLock);
@@ -630,26 +614,6 @@ report_toast_corruption(HeapCheckContext *ctx, ToastedAttribute *ta,
ctx->is_corrupt = true;
}
-/*
- * Construct the TupleDesc used to report messages about corruptions found
- * while scanning the heap.
- */
-static TupleDesc
-verify_heapam_tupdesc(void)
-{
- TupleDesc tupdesc;
- AttrNumber a = 0;
-
- tupdesc = CreateTemplateTupleDesc(HEAPCHECK_RELATION_COLS);
- TupleDescInitEntry(tupdesc, ++a, "blkno", INT8OID, -1, 0);
- TupleDescInitEntry(tupdesc, ++a, "offnum", INT4OID, -1, 0);
- TupleDescInitEntry(tupdesc, ++a, "attnum", INT4OID, -1, 0);
- TupleDescInitEntry(tupdesc, ++a, "msg", TEXTOID, -1, 0);
- Assert(a == HEAPCHECK_RELATION_COLS);
-
- return BlessTupleDesc(tupdesc);
-}
-
/*
* Check for tuple header corruption.
*
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index efc4c94301..a06d4bd12d 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1928,12 +1928,6 @@ dblink_get_notify(PG_FUNCTION_ARGS)
PGconn *conn;
PGnotify *notify;
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
-
- prepTuplestoreResult(fcinfo);
dblink_init();
if (PG_NARGS() == 1)
@@ -1941,23 +1935,7 @@ dblink_get_notify(PG_FUNCTION_ARGS)
else
conn = pconn->conn;
- /* create the tuplestore in per-query memory */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupdesc = CreateTemplateTupleDesc(DBLINK_NOTIFY_COLS);
- TupleDescInitEntry(tupdesc, (AttrNumber) 1, "notify_name",
- TEXTOID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 2, "be_pid",
- INT4OID, -1, 0);
- TupleDescInitEntry(tupdesc, (AttrNumber) 3, "extra",
- TEXTOID, -1, 0);
-
- tupstore = tuplestore_begin_heap(true, false, work_mem);
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
PQconsumeInput(conn);
while ((notify = PQnotifies(conn)) != NULL)
@@ -1980,7 +1958,7 @@ dblink_get_notify(PG_FUNCTION_ARGS)
else
nulls[2] = true;
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
PQfreemem(notify);
PQconsumeInput(conn);
diff --git a/contrib/pageinspect/brinfuncs.c b/contrib/pageinspect/brinfuncs.c
index 683749a150..b7c8365218 100644
--- a/contrib/pageinspect/brinfuncs.c
+++ b/contrib/pageinspect/brinfuncs.c
@@ -126,9 +126,6 @@ brin_page_items(PG_FUNCTION_ARGS)
bytea *raw_page = PG_GETARG_BYTEA_P(0);
Oid indexRelid = PG_GETARG_OID(1);
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- MemoryContext oldcontext;
- Tuplestorestate *tupstore;
Relation indexRel;
brin_column_state **columns;
BrinDesc *bdesc;
@@ -143,29 +140,7 @@ brin_page_items(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to use raw page functions")));
- /* 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 */
- oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
-
- tupstore = tuplestore_begin_heap(true, false, work_mem);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
indexRel = index_open(indexRelid, AccessShareLock);
bdesc = brin_build_desc(indexRel);
@@ -251,7 +226,7 @@ brin_page_items(PG_FUNCTION_ARGS)
int att = attno - 1;
values[0] = UInt16GetDatum(offset);
- switch (TupleDescAttr(tupdesc, 1)->atttypid)
+ switch (TupleDescAttr(rsinfo->setDesc, 1)->atttypid)
{
case INT8OID:
values[1] = Int64GetDatum((int64) dtup->bt_blkno);
@@ -301,7 +276,7 @@ brin_page_items(PG_FUNCTION_ARGS)
}
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
/*
* If the item was unused, jump straight to the next one; otherwise,
diff --git a/contrib/pageinspect/gistfuncs.c b/contrib/pageinspect/gistfuncs.c
index 96e3cab1cc..10d6dd44d4 100644
--- a/contrib/pageinspect/gistfuncs.c
+++ b/contrib/pageinspect/gistfuncs.c
@@ -97,10 +97,6 @@ gist_page_items_bytea(PG_FUNCTION_ARGS)
{
bytea *raw_page = PG_GETARG_BYTEA_P(0);
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- bool randomAccess;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext oldcontext;
Page page;
OffsetNumber offset;
OffsetNumber maxoff = InvalidOffsetNumber;
@@ -110,29 +106,7 @@ gist_page_items_bytea(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to use raw page functions")));
- /* 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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
page = get_page_from_raw(raw_page);
@@ -173,7 +147,7 @@ gist_page_items_bytea(PG_FUNCTION_ARGS)
values[3] = BoolGetDatum(ItemIdIsDead(id));
values[4] = PointerGetDatum(tuple_bytea);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
return (Datum) 0;
@@ -185,11 +159,7 @@ gist_page_items(PG_FUNCTION_ARGS)
bytea *raw_page = PG_GETARG_BYTEA_P(0);
Oid indexRelid = PG_GETARG_OID(1);
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- bool randomAccess;
Relation indexRel;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext oldcontext;
Page page;
OffsetNumber offset;
OffsetNumber maxoff = InvalidOffsetNumber;
@@ -199,29 +169,7 @@ gist_page_items(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to use raw page functions")));
- /* 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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
/* Open the relation */
indexRel = index_open(indexRelid, AccessShareLock);
@@ -272,7 +220,7 @@ gist_page_items(PG_FUNCTION_ARGS)
nulls[4] = true;
}
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
relation_close(indexRel, AccessShareLock);
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index d803253cea..9e525a6ad3 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -1494,10 +1494,6 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
bool showtext)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
Oid userid = GetUserId();
bool is_allowed_role = false;
char *qbuffer = NULL;
@@ -1516,30 +1512,14 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
- /* 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")));
-
- /* Switch into long-lived context to construct returned data structures */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- /* 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");
+ SetSingleFuncCall(fcinfo, 0);
/*
* Check we have the expected number of output arguments. Aside from
* being a good safety check, we need a kluge here to detect API version
* 1.1, which was wedged into the code in an ill-considered way.
*/
- switch (tupdesc->natts)
+ switch (rsinfo->setDesc->natts)
{
case PG_STAT_STATEMENTS_COLS_V1_0:
if (api_version != PGSS_V1_0)
@@ -1571,13 +1551,6 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
elog(ERROR, "incorrect number of output arguments");
}
- tupstore = tuplestore_begin_heap(true, false, work_mem);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
-
/*
* We'd like to load the query text file (if needed) while not holding any
* lock on pgss->lock. In the worst case we'll have to do this again
@@ -1800,7 +1773,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
-1 /* fail if you forget to update this assert */ ));
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
LWLockRelease(pgss->lock);
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index d8946dc510..713a165203 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -66,42 +66,16 @@ pgrowlocks(PG_FUNCTION_ARGS)
{
text *relname = PG_GETARG_TEXT_PP(0);
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- bool randomAccess;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
AttInMetadata *attinmeta;
Relation rel;
RangeVar *relrv;
TableScanDesc scan;
HeapScanDesc hscan;
HeapTuple tuple;
- MemoryContext oldcontext;
AclResult aclresult;
char **values;
- /* 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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
/* Access the table */
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
@@ -140,9 +114,9 @@ pgrowlocks(PG_FUNCTION_ARGS)
scan = table_beginscan(rel, GetActiveSnapshot(), 0, NULL);
hscan = (HeapScanDesc) scan;
- attinmeta = TupleDescGetAttInMetadata(tupdesc);
+ attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc);
- values = (char **) palloc(tupdesc->natts * sizeof(char *));
+ values = (char **) palloc(rsinfo->setDesc->natts * sizeof(char *));
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
@@ -288,7 +262,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
/* build a tuple */
tuple = BuildTupleFromCStrings(attinmeta, values);
- tuplestore_puttuple(tupstore, tuple);
+ tuplestore_puttuple(rsinfo->setResult, tuple);
}
else
{
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8c64d42dda..74d3e73205 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -1661,37 +1661,10 @@ postgres_fdw_get_connections(PG_FUNCTION_ARGS)
{
#define POSTGRES_FDW_GET_CONNECTIONS_COLS 2
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
HASH_SEQ_STATUS scan;
ConnCacheEntry *entry;
- /* 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);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
+ SetSingleFuncCall(fcinfo, 0);
/* If cache doesn't exist, we return no records */
if (!ConnectionHash)
@@ -1757,7 +1730,7 @@ postgres_fdw_get_connections(PG_FUNCTION_ARGS)
values[1] = BoolGetDatum(!entry->invalidated);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index a2e5fb54e2..b8ee757674 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -491,15 +491,9 @@ xpath_table(PG_FUNCTION_ARGS)
HeapTuple spi_tuple;
TupleDesc spi_tupdesc;
- /* Output tuple (tuplestore) support */
- Tuplestorestate *tupstore = NULL;
- TupleDesc ret_tupdesc;
- HeapTuple ret_tuple;
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
AttInMetadata *attinmeta;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
char **values;
xmlChar **xpaths;
@@ -517,48 +511,10 @@ xpath_table(PG_FUNCTION_ARGS)
PgXmlErrorContext *xmlerrcxt;
volatile xmlDocPtr doctree = NULL;
- /* We only have a valid tuple description in table function mode */
- 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->expectedDesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("xpath_table must be called as a table function")));
-
- /*
- * We want to materialise because it means that we don't have to carry
- * libxml2 parser state between invocations of this function
- */
- if (!(rsinfo->allowedModes & SFRM_Materialize))
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("xpath_table requires Materialize mode, but it is not "
- "allowed in this context")));
-
- /*
- * The tuplestore must exist in a higher context than this function call
- * (per_query_ctx is used)
- */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- /*
- * Create the tuplestore - work_mem is the max in-memory size before a
- * file is created on disk to hold it.
- */
- tupstore =
- tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
- false, work_mem);
-
- MemoryContextSwitchTo(oldcontext);
-
- /* get the requested return tuple description */
- ret_tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
+ SetSingleFuncCall(fcinfo, SRF_SINGLE_USE_EXPECTED);
/* must have at least one output column (for the pkey) */
- if (ret_tupdesc->natts < 1)
+ if (rsinfo->setDesc->natts < 1)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("xpath_table must have at least one output column")));
@@ -571,14 +527,10 @@ xpath_table(PG_FUNCTION_ARGS)
* representation.
*/
- attinmeta = TupleDescGetAttInMetadata(ret_tupdesc);
-
- /* Set return mode and allocate value space. */
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setDesc = ret_tupdesc;
+ attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc);
- values = (char **) palloc(ret_tupdesc->natts * sizeof(char *));
- xpaths = (xmlChar **) palloc(ret_tupdesc->natts * sizeof(xmlChar *));
+ values = (char **) palloc(rsinfo->setDesc->natts * sizeof(char *));
+ xpaths = (xmlChar **) palloc(rsinfo->setDesc->natts * sizeof(xmlChar *));
/*
* Split XPaths. xpathset is a writable CString.
@@ -587,7 +539,7 @@ xpath_table(PG_FUNCTION_ARGS)
*/
numpaths = 0;
pos = xpathset;
- while (numpaths < (ret_tupdesc->natts - 1))
+ while (numpaths < (rsinfo->setDesc->natts - 1))
{
xpaths[numpaths++] = (xmlChar *) pos;
pos = strstr(pos, pathsep);
@@ -621,9 +573,6 @@ xpath_table(PG_FUNCTION_ARGS)
tuptable = SPI_tuptable;
spi_tupdesc = tuptable->tupdesc;
- /* Switch out of SPI context */
- MemoryContextSwitchTo(oldcontext);
-
/*
* Check that SPI returned correct result. If you put a comma into one of
* the function parameters, this will catch it when the SPI query returns
@@ -655,6 +604,7 @@ xpath_table(PG_FUNCTION_ARGS)
xmlXPathObjectPtr res;
xmlChar *resstr;
xmlXPathCompExprPtr comppath;
+ HeapTuple ret_tuple;
/* Extract the row data as C Strings */
spi_tuple = tuptable->vals[i];
@@ -666,7 +616,7 @@ xpath_table(PG_FUNCTION_ARGS)
* return NULL in all columns. Note that this also means that
* spare columns will be NULL.
*/
- for (j = 0; j < ret_tupdesc->natts; j++)
+ for (j = 0; j < rsinfo->setDesc->natts; j++)
values[j] = NULL;
/* Insert primary key */
@@ -682,7 +632,7 @@ xpath_table(PG_FUNCTION_ARGS)
{
/* not well-formed, so output all-NULL tuple */
ret_tuple = BuildTupleFromCStrings(attinmeta, values);
- tuplestore_puttuple(tupstore, ret_tuple);
+ tuplestore_puttuple(rsinfo->setResult, ret_tuple);
heap_freetuple(ret_tuple);
}
else
@@ -749,7 +699,7 @@ xpath_table(PG_FUNCTION_ARGS)
if (had_values)
{
ret_tuple = BuildTupleFromCStrings(attinmeta, values);
- tuplestore_puttuple(tupstore, ret_tuple);
+ tuplestore_puttuple(rsinfo->setResult, ret_tuple);
heap_freetuple(ret_tuple);
}
@@ -785,8 +735,6 @@ xpath_table(PG_FUNCTION_ARGS)
SPI_finish();
- rsinfo->setResult = tupstore;
-
/*
* SFRM_Materialize mode expects us to return a NULL Datum. The actual
* tuples are in our tuplestore and passed back through rsinfo->setResult.
--
2.30.2
On Mon, Mar 07, 2022 at 05:09:19PM -0500, Melanie Plageman wrote:
I've attached a patch using the helper in most locations in contrib
modules that seemed useful.
Thanks for the patch. I was also looking at that yesterday, and this
pretty much maps to what I have finished with, except for dblink and
xml2 where it was not looking that obvious to me at quick glance.
In xml2, my eyes hurt a bit on the meaning of doing the "Switch out of
SPI context" in xpath_table() after doing the two SPI calls, but I
agree that this extra switch back to the old context should not be
necessary. For dblink, I did not notice that the change for
dblink_get_notify() would be this straight-forward, good catch. It
would be nice to see prepTuplestoreResult() gone at the end, though I
am not sure how invasive/worth it would be for dblink/.
- pg_logdir_ls() in contrib/adminpack has return type TYPEFUNC_RECORD
and expectedDesc is not already created, so the helper can't be used.
But basically, since it doesn't specify OUT argument names, it has to
do TupleDescInitEntry() itself anyway, I think.
Here, actually, I thought first that a simplification should be
possible, noticing that the regression tests passed with the function
called until I saw what it was testing :)
I am fine to not poke at the beast, and it looks like we may run into
trouble if we wish to maintain compatibility down to adminpack 1.0 at
runtime. This function has a very limited usage, anyway.
- contrib/tablefunc.c was also not simple to refactor. the various parts
of SetSingleFuncCall are spread throughout different functions in the
file.- contrib/dblink has one function which returns a tuplestore that was
simple to change (dblink_get_notify()) and I've done so.
This matches my impression, so applied.
--
Michael