4444typedef struct
4545{
4646DestReceiver pub ;/* publicly-known function pointers */
47- Tuplestorestate * tstore ;/* where to put result tuples */
47+ Tuplestorestate * tstore ;/* where to put result tuples, or NULL */
4848JunkFilter * filter ;/* filter to convert tuple type */
4949}DR_sqlfunction ;
5050
@@ -145,11 +145,13 @@ typedef struct SQLFunctionCache
145145bool lazyEvalOK ;/* true if lazyEval is safe */
146146bool shutdown_reg ;/* true if registered shutdown callback */
147147bool lazyEval ;/* true if using lazyEval for result query */
148+ bool randomAccess ;/* true if tstore needs random access */
148149bool ownSubcontext ;/* is subcontext really a separate context? */
149150
150151ParamListInfo paramLI ;/* Param list representing current args */
151152
152- Tuplestorestate * tstore ;/* where we accumulate result tuples */
153+ Tuplestorestate * tstore ;/* where we accumulate result for a SRF */
154+ MemoryContext tscontext ;/* memory context that tstore should be in */
153155
154156JunkFilter * junkFilter ;/* will be NULL if function returns VOID */
155157int jf_generation ;/* tracks whether junkFilter is up-to-date */
@@ -1250,7 +1252,7 @@ static void
12501252postquel_start (execution_state * es ,SQLFunctionCachePtr fcache )
12511253{
12521254DestReceiver * dest ;
1253- MemoryContext oldcontext ;
1255+ MemoryContext oldcontext = CurrentMemoryContext ;
12541256
12551257Assert (es -> qd == NULL );
12561258
@@ -1296,12 +1298,27 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
12961298fcache -> ownSubcontext = false;
12971299}
12981300
1301+ /*
1302+ * Build a tuplestore if needed, that is if it's a set-returning function
1303+ * and we're producing the function result without using lazyEval mode.
1304+ */
1305+ if (es -> setsResult )
1306+ {
1307+ Assert (fcache -> tstore == NULL );
1308+ if (fcache -> func -> returnsSet && !es -> lazyEval )
1309+ {
1310+ MemoryContextSwitchTo (fcache -> tscontext );
1311+ fcache -> tstore = tuplestore_begin_heap (fcache -> randomAccess ,
1312+ false,work_mem );
1313+ }
1314+ }
1315+
12991316/* Switch into the selected subcontext (might be a no-op) */
1300- oldcontext = MemoryContextSwitchTo (fcache -> subcontext );
1317+ MemoryContextSwitchTo (fcache -> subcontext );
13011318
13021319/*
1303- * If this query produces the function result,send its outputto the
1304- *tuplestore ; else discard any output.
1320+ * If this query produces the function result,collect its outputusing
1321+ *our custom DestReceiver ; else discard any output.
13051322 */
13061323if (es -> setsResult )
13071324{
@@ -1311,8 +1328,11 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
13111328/* pass down the needed info to the dest receiver routines */
13121329myState = (DR_sqlfunction * )dest ;
13131330Assert (myState -> pub .mydest == DestSQLFunction );
1314- myState -> tstore = fcache -> tstore ;
1331+ myState -> tstore = fcache -> tstore ;/* might be NULL */
13151332myState -> filter = fcache -> junkFilter ;
1333+
1334+ /* Make very sure the junkfilter's result slot is empty */
1335+ ExecClearTuple (fcache -> junkFilter -> jf_resultSlot );
13161336}
13171337else
13181338dest = None_Receiver ;
@@ -1500,8 +1520,8 @@ postquel_get_single_result(TupleTableSlot *slot,
15001520/*
15011521 * Set up to return the function value. For pass-by-reference datatypes,
15021522 * be sure to copy the result into the current context. We can't leave
1503- * the data in the TupleTableSlot because weintend to clear the slot
1504- *before returning.
1523+ * the data in the TupleTableSlot because wemust clear the slot before
1524+ * returning.
15051525 */
15061526if (fcache -> func -> returnsTuple )
15071527{
@@ -1521,6 +1541,9 @@ postquel_get_single_result(TupleTableSlot *slot,
15211541value = datumCopy (value ,fcache -> func -> typbyval ,fcache -> func -> typlen );
15221542}
15231543
1544+ /* Clear the slot for next time */
1545+ ExecClearTuple (slot );
1546+
15241547return value ;
15251548}
15261549
@@ -1532,6 +1555,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
15321555{
15331556SQLFunctionCachePtr fcache ;
15341557ErrorContextCallback sqlerrcontext ;
1558+ MemoryContext tscontext ;
15351559bool randomAccess ;
15361560bool lazyEvalOK ;
15371561bool pushed_snapshot ;
@@ -1558,18 +1582,26 @@ fmgr_sql(PG_FUNCTION_ARGS)
15581582errmsg ("set-valued function called in context that cannot accept a set" )));
15591583randomAccess = rsi -> allowedModes & SFRM_Materialize_Random ;
15601584lazyEvalOK = !(rsi -> allowedModes & SFRM_Materialize_Preferred );
1585+ /* tuplestore, if used, must have query lifespan */
1586+ tscontext = rsi -> econtext -> ecxt_per_query_memory ;
15611587}
15621588else
15631589{
15641590randomAccess = false;
15651591lazyEvalOK = true;
1592+ /* we won't need a tuplestore */
1593+ tscontext = NULL ;
15661594}
15671595
15681596/*
15691597 * Initialize fcache if starting a fresh execution.
15701598 */
15711599fcache = init_sql_fcache (fcinfo ,lazyEvalOK );
15721600
1601+ /* Remember info that we might need later to construct tuplestore */
1602+ fcache -> tscontext = tscontext ;
1603+ fcache -> randomAccess = randomAccess ;
1604+
15731605/*
15741606 * Now we can set up error traceback support for ereport()
15751607 */
@@ -1578,20 +1610,6 @@ fmgr_sql(PG_FUNCTION_ARGS)
15781610sqlerrcontext .previous = error_context_stack ;
15791611error_context_stack = & sqlerrcontext ;
15801612
1581- /*
1582- * Build tuplestore to hold results, if we don't have one already. We
1583- * want to re-use the tuplestore across calls, so it needs to live in
1584- * fcontext.
1585- */
1586- if (!fcache -> tstore )
1587- {
1588- MemoryContext oldcontext ;
1589-
1590- oldcontext = MemoryContextSwitchTo (fcache -> fcontext );
1591- fcache -> tstore = tuplestore_begin_heap (randomAccess , false,work_mem );
1592- MemoryContextSwitchTo (oldcontext );
1593- }
1594-
15951613/*
15961614 * Find first unfinished execution_state. If none, advance to the next
15971615 * query in function.
@@ -1661,11 +1679,12 @@ fmgr_sql(PG_FUNCTION_ARGS)
16611679
16621680/*
16631681 * If we ran the command to completion, we can shut it down now. Any
1664- * row(s) we need to return are safely stashed in the tuplestore, and
1665- * we want to be sure that, for example, AFTER triggers get fired
1666- * before we return anything. Also, if the function doesn't return
1667- * set, we can shut it down anyway because it must be a SELECT and we
1668- * don't care about fetching any more result rows.
1682+ * row(s) we need to return are safely stashed in the result slot or
1683+ * tuplestore, and we want to be sure that, for example, AFTER
1684+ * triggers get fired before we return anything. Also, if the
1685+ * function doesn't return set, we can shut it down anyway because it
1686+ * must be a SELECT and we don't care about fetching any more result
1687+ * rows.
16691688 */
16701689if (completed || !fcache -> func -> returnsSet )
16711690postquel_end (es ,fcache );
@@ -1708,7 +1727,8 @@ fmgr_sql(PG_FUNCTION_ARGS)
17081727}
17091728
17101729/*
1711- * The tuplestore now contains whatever row(s) we are supposed to return.
1730+ * The result slot or tuplestore now contains whatever row(s) we are
1731+ * supposed to return.
17121732 */
17131733if (fcache -> func -> returnsSet )
17141734{
@@ -1721,16 +1741,12 @@ fmgr_sql(PG_FUNCTION_ARGS)
17211741 * row.
17221742 */
17231743Assert (es -> lazyEval );
1724- /*Re-use the junkfilter'soutput slotto fetch back the tuple */
1744+ /*The junkfilter'sresult slotcontains the query result tuple */
17251745Assert (fcache -> junkFilter );
17261746slot = fcache -> junkFilter -> jf_resultSlot ;
1727- if (!tuplestore_gettupleslot (fcache -> tstore , true, false,slot ))
1728- elog (ERROR ,"failed to fetch lazy-eval tuple" );
1747+ Assert (!TTS_EMPTY (slot ));
17291748/* Extract the result as a datum, and copy out from the slot */
17301749result = postquel_get_single_result (slot ,fcinfo ,fcache );
1731- /* Clear the tuplestore, but keep it for next time */
1732- /* NB: this might delete the slot's content, but we don't care */
1733- tuplestore_clear (fcache -> tstore );
17341750
17351751/*
17361752 * Let caller know we're not finished.
@@ -1752,12 +1768,8 @@ fmgr_sql(PG_FUNCTION_ARGS)
17521768else if (fcache -> lazyEval )
17531769{
17541770/*
1755- * We are done with a lazy evaluation. Clean up.
1756- */
1757- tuplestore_clear (fcache -> tstore );
1758-
1759- /*
1760- * Let caller know we're finished.
1771+ * We are done with a lazy evaluation. Let caller know we're
1772+ * finished.
17611773 */
17621774rsi -> isDone = ExprEndResult ;
17631775
@@ -1779,7 +1791,12 @@ fmgr_sql(PG_FUNCTION_ARGS)
17791791 * We are done with a non-lazy evaluation. Return whatever is in
17801792 * the tuplestore. (It is now caller's responsibility to free the
17811793 * tuplestore when done.)
1794+ *
1795+ * Note an edge case: we could get here without having made a
1796+ * tuplestore if the function is declared to return SETOF VOID.
1797+ * ExecMakeTableFunctionResult will cope with null setResult.
17821798 */
1799+ Assert (fcache -> tstore || fcache -> func -> rettype == VOIDOID );
17831800rsi -> returnMode = SFRM_Materialize ;
17841801rsi -> setResult = fcache -> tstore ;
17851802fcache -> tstore = NULL ;
@@ -1807,9 +1824,9 @@ fmgr_sql(PG_FUNCTION_ARGS)
18071824 */
18081825if (fcache -> junkFilter )
18091826{
1810- /*Re-use the junkfilter'soutput slotto fetch back the tuple */
1827+ /*The junkfilter'sresult slotcontains the query result tuple */
18111828slot = fcache -> junkFilter -> jf_resultSlot ;
1812- if (tuplestore_gettupleslot ( fcache -> tstore , true, false, slot ))
1829+ if (! TTS_EMPTY ( slot ))
18131830result = postquel_get_single_result (slot ,fcinfo ,fcache );
18141831else
18151832{
@@ -1824,9 +1841,6 @@ fmgr_sql(PG_FUNCTION_ARGS)
18241841fcinfo -> isnull = true;
18251842result = (Datum )0 ;
18261843}
1827-
1828- /* Clear the tuplestore, but keep it for next time */
1829- tuplestore_clear (fcache -> tstore );
18301844}
18311845
18321846/* Pop snapshot if we have pushed one */
@@ -2604,11 +2618,32 @@ sqlfunction_receive(TupleTableSlot *slot, DestReceiver *self)
26042618{
26052619DR_sqlfunction * myState = (DR_sqlfunction * )self ;
26062620
2607- /* Filter tuple as needed */
2608- slot = ExecFilterJunk (myState -> filter ,slot );
2621+ if (myState -> tstore )
2622+ {
2623+ /* We are collecting all of a set result into the tuplestore */
2624+
2625+ /* Filter tuple as needed */
2626+ slot = ExecFilterJunk (myState -> filter ,slot );
26092627
2610- /* Store the filtered tuple into the tuplestore */
2611- tuplestore_puttupleslot (myState -> tstore ,slot );
2628+ /* Store the filtered tuple into the tuplestore */
2629+ tuplestore_puttupleslot (myState -> tstore ,slot );
2630+ }
2631+ else
2632+ {
2633+ /*
2634+ * We only want the first tuple, which we'll save in the junkfilter's
2635+ * result slot. Ignore any additional tuples passed.
2636+ */
2637+ if (TTS_EMPTY (myState -> filter -> jf_resultSlot ))
2638+ {
2639+ /* Filter tuple as needed */
2640+ slot = ExecFilterJunk (myState -> filter ,slot );
2641+ Assert (slot == myState -> filter -> jf_resultSlot );
2642+
2643+ /* Materialize the slot so it preserves pass-by-ref values */
2644+ ExecMaterializeSlot (slot );
2645+ }
2646+ }
26122647
26132648return true;
26142649}