Create routine able to set single-call SRFs for Materialize mode
authorMichael Paquier
Mon, 7 Mar 2022 01:26:29 +0000 (10:26 +0900)
committerMichael Paquier
Mon, 7 Mar 2022 01:26:29 +0000 (10:26 +0900)
Set-returning functions that use the Materialize mode, creating a
tuplestore to include all the tuples returned in a set rather than doing
so in multiple calls, use roughly the same set of steps to prepare
ReturnSetInfo for this job:
- Check if ReturnSetInfo supports returning a tuplestore and if the
materialize mode is enabled.
- Create a tuplestore for all the tuples part of the returned set in the
per-query memory context, stored in ReturnSetInfo->setResult.
- Build a tuple descriptor mostly from get_call_result_type(), then
stored in ReturnSetInfo->setDesc.  Note that there are some cases where
the SRF's tuple descriptor has to be the one specified by the function
caller.

This refactoring is done so as there are (well, should be) no behavior
changes in any of the in-core functions refactored, and the centralized
function that checks and sets up the function's ReturnSetInfo can be
controlled with a set of bits32 options.  Two of them prove to be
necessary now:
- SRF_SINGLE_USE_EXPECTED to use expectedDesc as tuple descriptor, as
expected by the function's caller.
- SRF_SINGLE_BLESS to validate the tuple descriptor for the SRF.

The same initialization pattern is simplified in 28 places per my
count as of src/backend/, shaving up to ~900 lines of code.  These
mostly come from the removal of the per-query initializations and the
sanity checks now grouped in a single location.  There are more
locations that could be simplified in contrib/, that are left for a
follow-up cleanup.

fcc281707daca5 and d61a361 have prepared the areas of the code related
to this change, to ease this refactoring.

Author: Melanie Plageman, Michael Paquier
Reviewed-by: Álvaro Herrera, Justin Pryzby
Discussion: https://postgr.es/m/CAAKRu_azyd1Z3W_r7Ou4sorTjRCs+PxeHw1CWJeXKofkE6TuZg@mail.gmail.com

24 files changed:
src/backend/commands/event_trigger.c
src/backend/commands/extension.c
src/backend/commands/prepare.c
src/backend/foreign/foreign.c
src/backend/libpq/hba.c
src/backend/replication/logical/launcher.c
src/backend/replication/logical/logicalfuncs.c
src/backend/replication/logical/origin.c
src/backend/replication/slotfuncs.c
src/backend/replication/walsender.c
src/backend/storage/ipc/shmem.c
src/backend/utils/adt/datetime.c
src/backend/utils/adt/genfile.c
src/backend/utils/adt/jsonfuncs.c
src/backend/utils/adt/mcxtfuncs.c
src/backend/utils/adt/misc.c
src/backend/utils/adt/pgstatfuncs.c
src/backend/utils/adt/varlena.c
src/backend/utils/fmgr/README
src/backend/utils/fmgr/funcapi.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/pg_config.c
src/backend/utils/mmgr/portalmem.c
src/include/funcapi.h

index 1e8587502e5878feb7cc370644561503becd6666..3c3fc2515b7f8c1ba0753eed70486b77156174d7 100644 (file)
@@ -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;
 
    /*
@@ -1306,30 +1302,8 @@ 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);
+   SetSingleFuncCall(fcinfo, 0);
 
    slist_foreach(iter, &(currentEventTriggerState->SQLDropList))
    {
@@ -1398,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;
@@ -1846,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;
 
    /*
@@ -1861,30 +1832,8 @@ 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);
+   SetSingleFuncCall(fcinfo, 0);
 
    foreach(lc, currentEventTriggerState->commandList)
    {
@@ -2055,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();
index 42503ef4543d72cc3791162c3350b520ed0b9e7d..1013790dbb342960010fb02732ef72ea0549acf3 100644 (file)
@@ -1932,38 +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;
 
-   /* check to see if caller supports us returning a tuplestore */
-   if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-       ereport(ERROR,
-               (errcode(ERRCODE_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);
 
    location = get_extension_control_directory();
    dir = AllocateDir(location);
@@ -2015,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);
@@ -2037,38 +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;
 
-   /* check to see if caller supports us returning a tuplestore */
-   if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-       ereport(ERROR,
-               (errcode(ERRCODE_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);
 
    location = get_extension_control_directory();
    dir = AllocateDir(location);
@@ -2103,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);
@@ -2316,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;
@@ -2327,30 +2273,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);
+   SetSingleFuncCall(fcinfo, 0);
 
    /* Read the extension's control file */
    control = read_extension_control_file(NameStr(*extname));
@@ -2407,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);
        }
    }
 
index dce30aed6c1c19aaaf06b177996d2ce361b38681..d2d8ee120c32c8365b7a13e679447dc4eb3f3e75 100644 (file)
@@ -702,41 +702,12 @@ 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")));
-
-   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);
 
    /*
     * 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);
-   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)
@@ -761,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);
        }
    }
 
index c3406c3b9d9ba5316b40d922c8cea4a884167686..cf222fc3e998fcf5293bf72f64771f7e6d97f945 100644 (file)
@@ -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"
@@ -510,38 +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;
-
-   /* check to see if caller supports us returning a tuplestore */
-   if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-       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);
-
-   /*
-    * 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;
-
-   MemoryContextSwitchTo(oldcontext);
+   /* prepare the result set */
+   SetSingleFuncCall(fcinfo, SRF_SINGLE_USE_EXPECTED);
 
    foreach(cell, options)
    {
@@ -561,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;
index d84a40b7265bd2ea8e84030bdd6a3b18a30f4a91..90953c38f3dee8998e81439f4192fd6218012026 100644 (file)
@@ -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,47 +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;
-
-   /* 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);
+   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();
 }
index 5a68d6deadc8d8d999dcdeb296dda49de0571532..6f25b2c2ad5c6be9e8acda0c2430c4ea7e5a29c2 100644 (file)
@@ -930,34 +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;
 
-   /* check to see if caller supports us returning a tuplestore */
-   if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-       ereport(ERROR,
-               (errcode(ERRCODE_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);
+   SetSingleFuncCall(fcinfo, 0);
 
    /* Make sure we get consistent view of the workers. */
    LWLockAcquire(LogicalRepWorkerLock, LW_SHARED);
@@ -1010,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,
index 3bd770a3ba6b593eb59cbfd5ddecf64b72a60a0e..6058d36e0d5ed5a9e8457f6f09535cb11be008d7 100644 (file)
@@ -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,10 +189,9 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
        }
    }
 
-   p->tupstore = tuplestore_begin_heap(true, false, work_mem);
-   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.
index 76055a8a036bb4f853cf26dc6c69710a8817c1be..0e38eff0f003067a0610fec48fd999f6a52d9a51 100644 (file)
@@ -1482,40 +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);
 
-   if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-       ereport(ERROR,
-               (errcode(ERRCODE_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);
-   rsinfo->returnMode = SFRM_Materialize;
-   rsinfo->setResult = tupstore;
-   rsinfo->setDesc = tupdesc;
-
-   MemoryContextSwitchTo(oldcontext);
-
+   SetSingleFuncCall(fcinfo, 0);
 
    /* prevent slots from being concurrently dropped */
    LWLockAcquire(ReplicationOriginLock, LW_SHARED);
@@ -1565,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);
index 886899afd22a7151d238d6503fc9f7f5a6300c89..ca945994ef018f5a32511b3479703613fc768bde 100644 (file)
@@ -233,42 +233,16 @@ 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.
     */
 
-   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);
 
    currlsn = GetXLogWriteRecPtr();
 
@@ -431,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);
index 5a718b1fe9b595019106595c95e2b04a1395e6be..2d0292a092edc260cdb988fdf677433a248edb68 100644 (file)
@@ -3403,37 +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;
 
-   /* check to see if caller supports us returning a tuplestore */
-   if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-       ereport(ERROR,
-               (errcode(ERRCODE_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);
+   SetSingleFuncCall(fcinfo, 0);
 
    /*
     * Get the currently active synchronous standbys.  This could be out of
@@ -3577,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;
index 1f023a34604249c8169702e278f175a7ef077790..c1279960cd0bc0cd20c70623718aa7a5553e07a4 100644 (file)
@@ -537,39 +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];
 
-   /* check to see if caller supports us returning a tuplestore */
-   if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-       ereport(ERROR,
-               (errcode(ERRCODE_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);
+   SetSingleFuncCall(fcinfo, 0);
 
    LWLockAcquire(ShmemIndexLock, LW_SHARED);
 
@@ -585,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 */
@@ -593,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;
@@ -601,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);
 
index 7926258c064fc55b01e94613d1d1abb2e0792e46..ba0ec35ac5ddc1ad768878c0cd41fea12e227a13 100644 (file)
@@ -4786,9 +4786,6 @@ Datum
 pg_timezone_names(PG_FUNCTION_ARGS)
 {
    ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
-   bool        randomAccess;
-   TupleDesc   tupdesc;
-   Tuplestorestate *tupstore;
    pg_tzenum  *tzenum;
    pg_tz      *tz;
    Datum       values[4];
@@ -4799,31 +4796,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);
+   SetSingleFuncCall(fcinfo, 0);
 
    /* initialize timezone scanning code */
    tzenum = pg_tzenumerate_start();
@@ -4865,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);
index fe6863d8b4466ebc5b22b799b71538aab6e988c8..1ed01620a1b05a361acf807bb5552ce4dcab46c9 100644 (file)
@@ -477,12 +477,8 @@ 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;
    struct dirent *de;
-   MemoryContext oldcontext;
 
    location = convert_and_check_filename(PG_GETARG_TEXT_PP(0));
 
@@ -495,29 +491,7 @@ 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);
-   rsinfo->returnMode = SFRM_Materialize;
-   rsinfo->setResult = tupstore;
-   rsinfo->setDesc = tupdesc;
-
-   MemoryContextSwitchTo(oldcontext);
+   SetSingleFuncCall(fcinfo, SRF_SINGLE_USE_EXPECTED);
 
    dirdesc = AllocateDir(location);
    if (!dirdesc)
@@ -541,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);
@@ -571,36 +546,10 @@ 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);
+   SetSingleFuncCall(fcinfo, 0);
 
    /*
     * Now walk the directory.  Note that we must do this within a single SRF
@@ -648,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);
index 2457061f97eefb467d3123c7bcecc9f181e5841d..29664aa6e40ebfad4a68d9e3e3ca9ae8b1bce346 100644 (file)
@@ -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,30 +1923,7 @@ each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
                        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))
-       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);
-
-   ret_tdesc = CreateTupleDescCopy(tupdesc);
-   BlessTupleDesc(ret_tdesc);
-   tuple_store =
-       tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
-                             false, work_mem);
-
-   MemoryContextSwitchTo(old_cxt);
+   SetSingleFuncCall(fcinfo, SRF_SINGLE_BLESS);
 
    tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
                                    "jsonb_each temporary cxt",
@@ -1964,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};
 
@@ -2001,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);
@@ -2013,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();
 }
 
@@ -2027,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);
@@ -2037,30 +2003,9 @@ 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->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);
+   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;
@@ -2079,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();
 }
 
@@ -2206,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;
@@ -2227,31 +2166,8 @@ 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);
-
-   MemoryContextSwitchTo(old_cxt);
+   SetSingleFuncCall(fcinfo,
+                     SRF_SINGLE_USE_EXPECTED | SRF_SINGLE_BLESS);
 
    tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
                                    "jsonb_array_elements temporary cxt",
@@ -2265,7 +2181,6 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
 
        if (r == WJB_ELEM)
        {
-           HeapTuple   tuple;
            Datum       values[1];
            bool        nulls[1] = {false};
 
@@ -2291,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);
@@ -2303,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();
 }
 
@@ -2330,41 +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;
-
-   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->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);
+   state->tuple_store = rsi->setResult;
+   state->ret_tdesc = rsi->setDesc;
 
    sem->semstate = (void *) state;
    sem->object_start = elements_object_start;
@@ -2384,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();
 }
 
index c7c95adf97d453c762c5a7b9f705f62d9b28c5c4..bb7cc940249b90a362928ea2da85cad5eff0c2aa 100644 (file)
@@ -120,36 +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;
-
-   /* check to see if caller supports us returning a tuplestore */
-   if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-       ereport(ERROR,
-               (errcode(ERRCODE_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);
-
-   PutMemoryContextsStatsTupleStore(tupstore, tupdesc,
+
+   SetSingleFuncCall(fcinfo, 0);
+   PutMemoryContextsStatsTupleStore(rsinfo->setResult, rsinfo->setDesc,
                                     TopMemoryContext, NULL, 0);
 
    return (Datum) 0;
index e79eb6b4788a907e074eb9350e38f4abe7f3f756..4568749d230bcb33215b8909c92068ebf9f18f62 100644 (file)
@@ -203,39 +203,11 @@ 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;
    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);
-
-   tupdesc = CreateTemplateTupleDesc(1);
-   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);
-
-   rsinfo->returnMode = SFRM_Materialize;
-   rsinfo->setResult = tupstore;
-   rsinfo->setDesc = tupdesc;
-
-   MemoryContextSwitchTo(oldcontext);
+   SetSingleFuncCall(fcinfo, SRF_SINGLE_USE_EXPECTED);
 
    if (tablespaceOid == GLOBALTABLESPACE_OID)
    {
@@ -291,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);
index fd993d0d5fb3e4aabf96092e4936afce393d2df6..eff45b16f2c113e3887cf84c6e6f9714a7506d24 100644 (file)
@@ -461,25 +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;
-
-   /* check to see if caller supports us returning a tuplestore */
-   if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-       ereport(ERROR,
-               (errcode(ERRCODE_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 +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 = tuplestore_begin_heap(true, false, work_mem);
-   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++)
@@ -552,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;
@@ -569,34 +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;
-
-   /* check to see if caller supports us returning a tuplestore */
-   if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-       ereport(ERROR,
-               (errcode(ERRCODE_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);
+   SetSingleFuncCall(fcinfo, 0);
 
    /* 1-based index */
    for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
@@ -629,7 +578,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
            nulls[5] = false;
            values[5] = CStringGetTextDatum("");
 
-           tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+           tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
            continue;
        }
 
@@ -943,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)
@@ -1866,36 +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;
 
-   /* check to see if caller supports us returning a tuplestore */
-   if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-       ereport(ERROR,
-               (errcode(ERRCODE_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);
+   SetSingleFuncCall(fcinfo, 0);
 
    /* request SLRU stats from the stat collector */
    stats = pgstat_fetch_slru();
@@ -1927,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;
index b2003f5672e83cfbb345959d1ebabb91a49e0bc3..22ab5a4329f2756e908f386bd1cfaedaa0f8a63b 100644 (file)
@@ -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"
@@ -4832,34 +4833,14 @@ text_to_table(PG_FUNCTION_ARGS)
 {
    ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
    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);
-
-   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;
 }
 
index 1e4c4b94a95f75f73860a8f314c9a1a63d6f90a1..9d8848106dfe0613d1c90941c7fec4d7709e8288 100644 (file)
@@ -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.
 
index 5d913ae08d809b90b0ca1faf6acdee8cc2567c1c..d269662ad8e088bed54f4feac537b2e0ed496334 100644 (file)
@@ -19,6 +19,7 @@
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
+#include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "utils/array.h"
 #include "utils/builtins.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"
 
 
@@ -54,6 +56,73 @@ static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
 static TypeFuncClass get_type_func_class(Oid typid, Oid *base_typeid);
 
 
+/*
+ * SetSingleFuncCall
+ *
+ * Helper function to build the state of a set-returning function used
+ * in the context of a single call with materialize mode.  This code
+ * includes sanity checks on ReturnSetInfo, creates the Tuplestore and
+ * the TupleDesc used with the function and stores them into the
+ * function's ReturnSetInfo.
+ *
+ * "flags" can be set to SRF_SINGLE_USE_EXPECTED, to use the tuple
+ * descriptor coming from expectedDesc, which is the tuple descriptor
+ * expected by the caller.  SRF_SINGLE_BLESS can be set to complete the
+ * information associated to the tuple descriptor, which is necessary
+ * in some cases  where 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")));
+
+   /*
+    * 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);
+
+   /* 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;
+
+   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
index 1e3650184b1336659e9027f600dea1b80e88ffda..6d11f9c71b9997acc89b2e6bd88cfdc8ba95afd1 100644 (file)
@@ -10157,41 +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;
-
-   /* Check to see if caller supports us returning a tuplestore */
-   if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-       ereport(ERROR,
-               (errcode(ERRCODE_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);
 
-   /* 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 = 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);
+   SetSingleFuncCall(fcinfo, 0);
 
    /* Process the results and create a tuplestore */
    for (seqno = 1; conf != NULL; conf = conf->next, seqno++)
@@ -10239,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;
index e646a419106a92b68b1ca9e45cfdc946ad9da607..d9e18caf448c9a0943f64a86f21c2c07e846d12c 100644 (file)
@@ -25,35 +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;
 
-   /* check to see if caller supports us returning a tuplestore */
-   if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-       ereport(ERROR,
-               (errcode(ERRCODE_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);
-   rsinfo->returnMode = SFRM_Materialize;
-   rsinfo->setResult = tupstore;
-   rsinfo->setDesc = tupdesc;
-
-   MemoryContextSwitchTo(oldcontext);
+   /* initialize our tuplestore */
+   SetSingleFuncCall(fcinfo, 0);
 
    configdata = get_configdata(my_exec_path, &configdata_len);
    for (i = 0; i < configdata_len; i++)
@@ -67,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;
index afc03682d9cec0838a57737e15022bffc190571d..d549f66d4afd1466d1b5114723846480d5c62dfd 100644 (file)
@@ -1132,43 +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;
 
-   /* check to see if caller supports us returning a tuplestore */
-   if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-       ereport(ERROR,
-               (errcode(ERRCODE_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);
-   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)
@@ -1190,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;
 }
 
index ba927c2f33024fb0e4b6985755fc864a90c2d4de..dc3d819a1c7eca066511205ed94f4aa845010b42 100644 (file)
@@ -278,14 +278,20 @@ extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
  * 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);