Skip to content

Commit 9e98583

Browse files
committed
Create routine able to set single-call SRFs for Materialize mode
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. fcc2817, 07daca5 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
1 parent 770011e commit 9e98583

24 files changed

+176
-872
lines changed

src/backend/commands/event_trigger.c

+6-56
Original file line numberDiff line numberDiff line change
@@ -1290,10 +1290,6 @@ Datum
12901290
pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
12911291
{
12921292
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1293-
TupleDesc tupdesc;
1294-
Tuplestorestate *tupstore;
1295-
MemoryContext per_query_ctx;
1296-
MemoryContext oldcontext;
12971293
slist_iter iter;
12981294

12991295
/*
@@ -1306,30 +1302,8 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
13061302
errmsg("%s can only be called in a sql_drop event trigger function",
13071303
"pg_event_trigger_dropped_objects()")));
13081304

1309-
/* check to see if caller supports us returning a tuplestore */
1310-
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
1311-
ereport(ERROR,
1312-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1313-
errmsg("set-valued function called in context that cannot accept a set")));
1314-
if (!(rsinfo->allowedModes & SFRM_Materialize))
1315-
ereport(ERROR,
1316-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1317-
errmsg("materialize mode required, but it is not allowed in this context")));
1318-
1319-
/* Build a tuple descriptor for our result type */
1320-
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1321-
elog(ERROR, "return type must be a row type");
1322-
13231305
/* Build tuplestore to hold the result rows */
1324-
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
1325-
oldcontext = MemoryContextSwitchTo(per_query_ctx);
1326-
1327-
tupstore = tuplestore_begin_heap(true, false, work_mem);
1328-
rsinfo->returnMode = SFRM_Materialize;
1329-
rsinfo->setResult = tupstore;
1330-
rsinfo->setDesc = tupdesc;
1331-
1332-
MemoryContextSwitchTo(oldcontext);
1306+
SetSingleFuncCall(fcinfo, 0);
13331307

13341308
slist_foreach(iter, &(currentEventTriggerState->SQLDropList))
13351309
{
@@ -1398,7 +1372,8 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
13981372
nulls[i++] = true;
13991373
}
14001374

1401-
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
1375+
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
1376+
values, nulls);
14021377
}
14031378

14041379
return (Datum) 0;
@@ -1846,10 +1821,6 @@ Datum
18461821
pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
18471822
{
18481823
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1849-
TupleDesc tupdesc;
1850-
Tuplestorestate *tupstore;
1851-
MemoryContext per_query_ctx;
1852-
MemoryContext oldcontext;
18531824
ListCell *lc;
18541825

18551826
/*
@@ -1861,30 +1832,8 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
18611832
errmsg("%s can only be called in an event trigger function",
18621833
"pg_event_trigger_ddl_commands()")));
18631834

1864-
/* check to see if caller supports us returning a tuplestore */
1865-
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
1866-
ereport(ERROR,
1867-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1868-
errmsg("set-valued function called in context that cannot accept a set")));
1869-
if (!(rsinfo->allowedModes & SFRM_Materialize))
1870-
ereport(ERROR,
1871-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1872-
errmsg("materialize mode required, but it is not allowed in this context")));
1873-
1874-
/* Build a tuple descriptor for our result type */
1875-
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1876-
elog(ERROR, "return type must be a row type");
1877-
18781835
/* Build tuplestore to hold the result rows */
1879-
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
1880-
oldcontext = MemoryContextSwitchTo(per_query_ctx);
1881-
1882-
tupstore = tuplestore_begin_heap(true, false, work_mem);
1883-
rsinfo->returnMode = SFRM_Materialize;
1884-
rsinfo->setResult = tupstore;
1885-
rsinfo->setDesc = tupdesc;
1886-
1887-
MemoryContextSwitchTo(oldcontext);
1836+
SetSingleFuncCall(fcinfo, 0);
18881837

18891838
foreach(lc, currentEventTriggerState->commandList)
18901839
{
@@ -2055,7 +2004,8 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
20552004
break;
20562005
}
20572006

2058-
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2007+
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2008+
values, nulls);
20592009
}
20602010

20612011
PG_RETURN_VOID();

src/backend/commands/extension.c

+9-84
Original file line numberDiff line numberDiff line change
@@ -1932,38 +1932,12 @@ Datum
19321932
pg_available_extensions(PG_FUNCTION_ARGS)
19331933
{
19341934
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1935-
TupleDesc tupdesc;
1936-
Tuplestorestate *tupstore;
1937-
MemoryContext per_query_ctx;
1938-
MemoryContext oldcontext;
19391935
char *location;
19401936
DIR *dir;
19411937
struct dirent *de;
19421938

1943-
/* check to see if caller supports us returning a tuplestore */
1944-
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
1945-
ereport(ERROR,
1946-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1947-
errmsg("set-valued function called in context that cannot accept a set")));
1948-
if (!(rsinfo->allowedModes & SFRM_Materialize))
1949-
ereport(ERROR,
1950-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1951-
errmsg("materialize mode required, but it is not allowed in this context")));
1952-
1953-
/* Build a tuple descriptor for our result type */
1954-
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1955-
elog(ERROR, "return type must be a row type");
1956-
19571939
/* Build tuplestore to hold the result rows */
1958-
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
1959-
oldcontext = MemoryContextSwitchTo(per_query_ctx);
1960-
1961-
tupstore = tuplestore_begin_heap(true, false, work_mem);
1962-
rsinfo->returnMode = SFRM_Materialize;
1963-
rsinfo->setResult = tupstore;
1964-
rsinfo->setDesc = tupdesc;
1965-
1966-
MemoryContextSwitchTo(oldcontext);
1940+
SetSingleFuncCall(fcinfo, 0);
19671941

19681942
location = get_extension_control_directory();
19691943
dir = AllocateDir(location);
@@ -2015,7 +1989,8 @@ pg_available_extensions(PG_FUNCTION_ARGS)
20151989
else
20161990
values[2] = CStringGetTextDatum(control->comment);
20171991

2018-
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
1992+
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
1993+
values, nulls);
20191994
}
20201995

20211996
FreeDir(dir);
@@ -2037,38 +2012,12 @@ Datum
20372012
pg_available_extension_versions(PG_FUNCTION_ARGS)
20382013
{
20392014
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2040-
TupleDesc tupdesc;
2041-
Tuplestorestate *tupstore;
2042-
MemoryContext per_query_ctx;
2043-
MemoryContext oldcontext;
20442015
char *location;
20452016
DIR *dir;
20462017
struct dirent *de;
20472018

2048-
/* check to see if caller supports us returning a tuplestore */
2049-
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
2050-
ereport(ERROR,
2051-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2052-
errmsg("set-valued function called in context that cannot accept a set")));
2053-
if (!(rsinfo->allowedModes & SFRM_Materialize))
2054-
ereport(ERROR,
2055-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2056-
errmsg("materialize mode required, but it is not allowed in this context")));
2057-
2058-
/* Build a tuple descriptor for our result type */
2059-
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
2060-
elog(ERROR, "return type must be a row type");
2061-
20622019
/* Build tuplestore to hold the result rows */
2063-
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
2064-
oldcontext = MemoryContextSwitchTo(per_query_ctx);
2065-
2066-
tupstore = tuplestore_begin_heap(true, false, work_mem);
2067-
rsinfo->returnMode = SFRM_Materialize;
2068-
rsinfo->setResult = tupstore;
2069-
rsinfo->setDesc = tupdesc;
2070-
2071-
MemoryContextSwitchTo(oldcontext);
2020+
SetSingleFuncCall(fcinfo, 0);
20722021

20732022
location = get_extension_control_directory();
20742023
dir = AllocateDir(location);
@@ -2103,7 +2052,8 @@ pg_available_extension_versions(PG_FUNCTION_ARGS)
21032052
control = read_extension_control_file(extname);
21042053

21052054
/* scan extension's script directory for install scripts */
2106-
get_available_versions_for_extension(control, tupstore, tupdesc);
2055+
get_available_versions_for_extension(control, rsinfo->setResult,
2056+
rsinfo->setDesc);
21072057
}
21082058

21092059
FreeDir(dir);
@@ -2316,41 +2266,15 @@ pg_extension_update_paths(PG_FUNCTION_ARGS)
23162266
{
23172267
Name extname = PG_GETARG_NAME(0);
23182268
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2319-
TupleDesc tupdesc;
2320-
Tuplestorestate *tupstore;
2321-
MemoryContext per_query_ctx;
2322-
MemoryContext oldcontext;
23232269
List *evi_list;
23242270
ExtensionControlFile *control;
23252271
ListCell *lc1;
23262272

23272273
/* Check extension name validity before any filesystem access */
23282274
check_valid_extension_name(NameStr(*extname));
23292275

2330-
/* check to see if caller supports us returning a tuplestore */
2331-
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
2332-
ereport(ERROR,
2333-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2334-
errmsg("set-valued function called in context that cannot accept a set")));
2335-
if (!(rsinfo->allowedModes & SFRM_Materialize))
2336-
ereport(ERROR,
2337-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2338-
errmsg("materialize mode required, but it is not allowed in this context")));
2339-
2340-
/* Build a tuple descriptor for our result type */
2341-
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
2342-
elog(ERROR, "return type must be a row type");
2343-
23442276
/* Build tuplestore to hold the result rows */
2345-
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
2346-
oldcontext = MemoryContextSwitchTo(per_query_ctx);
2347-
2348-
tupstore = tuplestore_begin_heap(true, false, work_mem);
2349-
rsinfo->returnMode = SFRM_Materialize;
2350-
rsinfo->setResult = tupstore;
2351-
rsinfo->setDesc = tupdesc;
2352-
2353-
MemoryContextSwitchTo(oldcontext);
2277+
SetSingleFuncCall(fcinfo, 0);
23542278

23552279
/* Read the extension's control file */
23562280
control = read_extension_control_file(NameStr(*extname));
@@ -2407,7 +2331,8 @@ pg_extension_update_paths(PG_FUNCTION_ARGS)
24072331
pfree(pathbuf.data);
24082332
}
24092333

2410-
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
2334+
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2335+
values, nulls);
24112336
}
24122337
}
24132338

src/backend/commands/prepare.c

+3-31
Original file line numberDiff line numberDiff line change
@@ -702,41 +702,12 @@ Datum
702702
pg_prepared_statement(PG_FUNCTION_ARGS)
703703
{
704704
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
705-
TupleDesc tupdesc;
706-
Tuplestorestate *tupstore;
707-
MemoryContext per_query_ctx;
708-
MemoryContext oldcontext;
709-
710-
/* check to see if caller supports us returning a tuplestore */
711-
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
712-
ereport(ERROR,
713-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
714-
errmsg("set-valued function called in context that cannot accept a set")));
715-
if (!(rsinfo->allowedModes & SFRM_Materialize))
716-
ereport(ERROR,
717-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
718-
errmsg("materialize mode required, but it is not allowed in this context")));
719-
720-
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
721-
elog(ERROR, "return type must be a row type");
722-
723-
/* need to build tuplestore in query context */
724-
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
725-
oldcontext = MemoryContextSwitchTo(per_query_ctx);
726705

727706
/*
728707
* We put all the tuples into a tuplestore in one scan of the hashtable.
729708
* This avoids any issue of the hashtable possibly changing between calls.
730709
*/
731-
tupstore =
732-
tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
733-
false, work_mem);
734-
rsinfo->returnMode = SFRM_Materialize;
735-
rsinfo->setResult = tupstore;
736-
rsinfo->setDesc = tupdesc;
737-
738-
/* generate junk in short-term context */
739-
MemoryContextSwitchTo(oldcontext);
710+
SetSingleFuncCall(fcinfo, 0);
740711

741712
/* hash table might be uninitialized */
742713
if (prepared_queries)
@@ -761,7 +732,8 @@ pg_prepared_statement(PG_FUNCTION_ARGS)
761732
values[5] = Int64GetDatumFast(prep_stmt->plansource->num_generic_plans);
762733
values[6] = Int64GetDatumFast(prep_stmt->plansource->num_custom_plans);
763734

764-
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
735+
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
736+
values, nulls);
765737
}
766738
}
767739

src/backend/foreign/foreign.c

+5-29
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "catalog/pg_user_mapping.h"
2121
#include "foreign/fdwapi.h"
2222
#include "foreign/foreign.h"
23+
#include "funcapi.h"
2324
#include "lib/stringinfo.h"
2425
#include "miscadmin.h"
2526
#include "utils/builtins.h"
@@ -510,38 +511,12 @@ pg_options_to_table(PG_FUNCTION_ARGS)
510511
ListCell *cell;
511512
List *options;
512513
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
513-
TupleDesc tupdesc;
514-
Tuplestorestate *tupstore;
515-
MemoryContext per_query_ctx;
516-
MemoryContext oldcontext;
517-
518-
/* check to see if caller supports us returning a tuplestore */
519-
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
520-
ereport(ERROR,
521-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
522-
errmsg("set-valued function called in context that cannot accept a set")));
523-
if (!(rsinfo->allowedModes & SFRM_Materialize) ||
524-
rsinfo->expectedDesc == NULL)
525-
ereport(ERROR,
526-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
527-
errmsg("materialize mode required, but it is not allowed in this context")));
528514

529515
options = untransformRelOptions(array);
530516
rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
531517

532-
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
533-
oldcontext = MemoryContextSwitchTo(per_query_ctx);
534-
535-
/*
536-
* Now prepare the result set.
537-
*/
538-
tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
539-
tupstore = tuplestore_begin_heap(true, false, work_mem);
540-
rsinfo->returnMode = SFRM_Materialize;
541-
rsinfo->setResult = tupstore;
542-
rsinfo->setDesc = tupdesc;
543-
544-
MemoryContextSwitchTo(oldcontext);
518+
/* prepare the result set */
519+
SetSingleFuncCall(fcinfo, SRF_SINGLE_USE_EXPECTED);
545520

546521
foreach(cell, options)
547522
{
@@ -561,7 +536,8 @@ pg_options_to_table(PG_FUNCTION_ARGS)
561536
values[1] = (Datum) 0;
562537
nulls[1] = true;
563538
}
564-
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
539+
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
540+
values, nulls);
565541
}
566542

567543
return (Datum) 0;

0 commit comments

Comments
 (0)