@@ -55,7 +55,9 @@ typedef struct
5555 *
5656 * The "next" fields chain together all the execution_state records generated
5757 * from a single original parsetree. (There will only be more than one in
58- * case of rule expansion of the original parsetree.)
58+ * case of rule expansion of the original parsetree.) The chain structure is
59+ * quite vestigial at this point, because we allocate the records in an array
60+ * for ease of memory management. But we'll get rid of it some other day.
5961 */
6062typedef enum
6163{
@@ -158,17 +160,20 @@ typedef struct SQLFunctionCache
158160
159161/*
160162 * While executing a particular query within the function, cplan is the
161- * CachedPlan we've obtained for that query, and eslist is alist of
163+ * CachedPlan we've obtained for that query, and eslist is achain of
162164 * execution_state records for the individual plans within the CachedPlan.
163165 * next_query_index is the 0-based index of the next CachedPlanSource to
164166 * get a CachedPlan from.
165167 */
166168CachedPlan * cplan ;/* Plan for current query, if any */
167169ResourceOwner cowner ;/* CachedPlan is registered with this owner */
168- execution_state * eslist ;/* execution_state records */
169170int next_query_index ;/* index of next CachedPlanSource to run */
170171
171- /* if positive, this is the index of the query we're processing */
172+ execution_state * eslist ;/* chain of execution_state records */
173+ execution_state * esarray ;/* storage for eslist */
174+ int esarray_len ;/* allocated length of esarray[] */
175+
176+ /* if positive, this is the 1-based index of the query we're processing */
172177int error_query_index ;
173178
174179MemoryContext fcontext ;/* memory context holding this struct and all
@@ -212,13 +217,12 @@ static void sql_delete_callback(CachedFunction *cfunc);
212217static void sql_postrewrite_callback (List * querytree_list ,void * arg );
213218static void postquel_start (execution_state * es ,SQLFunctionCachePtr fcache );
214219static bool postquel_getnext (execution_state * es ,SQLFunctionCachePtr fcache );
215- static void postquel_end (execution_state * es );
220+ static void postquel_end (execution_state * es , SQLFunctionCachePtr fcache );
216221static void postquel_sub_params (SQLFunctionCachePtr fcache ,
217222FunctionCallInfo fcinfo );
218223static Datum postquel_get_single_result (TupleTableSlot * slot ,
219224FunctionCallInfo fcinfo ,
220- SQLFunctionCachePtr fcache ,
221- MemoryContext resultcontext );
225+ SQLFunctionCachePtr fcache );
222226static void sql_compile_error_callback (void * arg );
223227static void sql_exec_error_callback (void * arg );
224228static void ShutdownSQLFunction (Datum arg );
@@ -658,12 +662,11 @@ init_execution_state(SQLFunctionCachePtr fcache)
658662CachedPlanSource * plansource ;
659663execution_state * preves = NULL ;
660664execution_state * lasttages = NULL ;
665+ int nstmts ;
661666ListCell * lc ;
662667
663668/*
664- * Clean up after previous query, if there was one. Note that we just
665- * leak the old execution_state records until end of function execution;
666- * there aren't likely to be enough of them to matter.
669+ * Clean up after previous query, if there was one.
667670 */
668671if (fcache -> cplan )
669672{
@@ -704,6 +707,22 @@ init_execution_state(SQLFunctionCachePtr fcache)
704707fcache -> cowner ,
705708NULL );
706709
710+ /*
711+ * If necessary, make esarray[] bigger to hold the needed state.
712+ */
713+ nstmts = list_length (fcache -> cplan -> stmt_list );
714+ if (nstmts > fcache -> esarray_len )
715+ {
716+ if (fcache -> esarray == NULL )
717+ fcache -> esarray = (execution_state * )
718+ MemoryContextAlloc (fcache -> fcontext ,
719+ sizeof (execution_state )* nstmts );
720+ else
721+ fcache -> esarray = repalloc_array (fcache -> esarray ,
722+ execution_state ,nstmts );
723+ fcache -> esarray_len = nstmts ;
724+ }
725+
707726/*
708727 * Build execution_state list to match the number of contained plans.
709728 */
@@ -740,7 +759,7 @@ init_execution_state(SQLFunctionCachePtr fcache)
740759CreateCommandName ((Node * )stmt ))));
741760
742761/* OK, build the execution_state for this query */
743- newes = ( execution_state * ) palloc ( sizeof ( execution_state )) ;
762+ newes = & fcache -> esarray [ foreach_current_index ( lc )] ;
744763if (preves )
745764preves -> next = newes ;
746765else
@@ -775,9 +794,14 @@ init_execution_state(SQLFunctionCachePtr fcache)
775794 */
776795if (fcache -> func -> rettype != VOIDOID )
777796{
778- TupleTableSlot * slot = MakeSingleTupleTableSlot (NULL ,
779- & TTSOpsMinimalTuple );
797+ TupleTableSlot * slot ;
780798List * resulttlist ;
799+ MemoryContext oldcontext ;
800+
801+ /* The result slot and JunkFilter must be in the fcontext */
802+ oldcontext = MemoryContextSwitchTo (fcache -> fcontext );
803+
804+ slot = MakeSingleTupleTableSlot (NULL ,& TTSOpsMinimalTuple );
781805
782806/*
783807 * Re-fetch the (possibly modified) output tlist of the final
@@ -811,14 +835,17 @@ init_execution_state(SQLFunctionCachePtr fcache)
811835 * pointer.
812836 */
813837fcache -> junkFilter -> jf_targetList = NIL ;
814- }
815838
816- if (fcache -> func -> returnsTuple )
817- {
818839/* Make sure output rowtype is properly blessed */
819- BlessTupleDesc (fcache -> junkFilter -> jf_resultSlot -> tts_tupleDescriptor );
840+ if (fcache -> func -> returnsTuple )
841+ BlessTupleDesc (fcache -> junkFilter -> jf_resultSlot -> tts_tupleDescriptor );
842+
843+ MemoryContextSwitchTo (oldcontext );
820844}
821- else if (fcache -> func -> returnsSet && type_is_rowtype (fcache -> func -> rettype ))
845+
846+ if (fcache -> func -> returnsSet &&
847+ !fcache -> func -> returnsTuple &&
848+ type_is_rowtype (fcache -> func -> rettype ))
822849{
823850/*
824851 * Returning rowtype as if it were scalar --- materialize won't work.
@@ -1230,12 +1257,23 @@ static void
12301257postquel_start (execution_state * es ,SQLFunctionCachePtr fcache )
12311258{
12321259DestReceiver * dest ;
1260+ MemoryContext oldcontext ;
12331261
12341262Assert (es -> qd == NULL );
12351263
12361264/* Caller should have ensured a suitable snapshot is active */
12371265Assert (ActiveSnapshotSet ());
12381266
1267+ /*
1268+ * Run the sub-executor in a child of fcontext. The sub-executor is
1269+ * responsible for deleting per-tuple information. (XXX in the case of a
1270+ * long-lived FmgrInfo, this policy potentially causes memory leakage, but
1271+ * it's not very clear where we could keep stuff instead. Fortunately,
1272+ * there are few if any cases where set-returning functions are invoked
1273+ * via FmgrInfos that would outlive the calling query.)
1274+ */
1275+ oldcontext = MemoryContextSwitchTo (fcache -> fcontext );
1276+
12391277/*
12401278 * If this query produces the function result, send its output to the
12411279 * tuplestore; else discard any output.
@@ -1285,6 +1323,8 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
12851323}
12861324
12871325es -> status = F_EXEC_RUN ;
1326+
1327+ MemoryContextSwitchTo (oldcontext );
12881328}
12891329
12901330/* Run one execution_state; either to completion or to first result row */
@@ -1293,6 +1333,10 @@ static bool
12931333postquel_getnext (execution_state * es ,SQLFunctionCachePtr fcache )
12941334{
12951335bool result ;
1336+ MemoryContext oldcontext ;
1337+
1338+ /* Run the sub-executor in a child of fcontext */
1339+ oldcontext = MemoryContextSwitchTo (fcache -> fcontext );
12961340
12971341if (es -> qd -> operation == CMD_UTILITY )
12981342{
@@ -1320,13 +1364,20 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
13201364result = (count == 0 || es -> qd -> estate -> es_processed == 0 );
13211365}
13221366
1367+ MemoryContextSwitchTo (oldcontext );
1368+
13231369return result ;
13241370}
13251371
13261372/* Shut down execution of one execution_state node */
13271373static void
1328- postquel_end (execution_state * es )
1374+ postquel_end (execution_state * es , SQLFunctionCachePtr fcache )
13291375{
1376+ MemoryContext oldcontext ;
1377+
1378+ /* Run the sub-executor in a child of fcontext */
1379+ oldcontext = MemoryContextSwitchTo (fcache -> fcontext );
1380+
13301381/* mark status done to ensure we don't do ExecutorEnd twice */
13311382es -> status = F_EXEC_DONE ;
13321383
@@ -1341,9 +1392,16 @@ postquel_end(execution_state *es)
13411392
13421393FreeQueryDesc (es -> qd );
13431394es -> qd = NULL ;
1395+
1396+ MemoryContextSwitchTo (oldcontext );
13441397}
13451398
1346- /* Build ParamListInfo array representing current arguments */
1399+ /*
1400+ * Build ParamListInfo array representing current arguments.
1401+ *
1402+ * This must be called in the fcontext so that the results have adequate
1403+ * lifespan.
1404+ */
13471405static void
13481406postquel_sub_params (SQLFunctionCachePtr fcache ,
13491407FunctionCallInfo fcinfo )
@@ -1398,25 +1456,22 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
13981456/*
13991457 * Extract the SQL function's value from a single result row. This is used
14001458 * both for scalar (non-set) functions and for each row of a lazy-eval set
1401- * result.
1459+ * result. We expect the current memory context to be that of the caller
1460+ * of fmgr_sql.
14021461 */
14031462static Datum
14041463postquel_get_single_result (TupleTableSlot * slot ,
14051464FunctionCallInfo fcinfo ,
1406- SQLFunctionCachePtr fcache ,
1407- MemoryContext resultcontext )
1465+ SQLFunctionCachePtr fcache )
14081466{
14091467Datum value ;
1410- MemoryContext oldcontext ;
14111468
14121469/*
14131470 * Set up to return the function value. For pass-by-reference datatypes,
1414- * be sure toallocate the resultin resultcontext, not the currentmemory
1415- *context (which has query lifespan). We can't leave the data in the
1416- *TupleTableSlot because we intend to clear the slot before returning.
1471+ * be sure tocopy the resultinto the currentcontext. We can't leave
1472+ *the data in the TupleTableSlot because we intend to clear the slot
1473+ * before returning.
14171474 */
1418- oldcontext = MemoryContextSwitchTo (resultcontext );
1419-
14201475if (fcache -> func -> returnsTuple )
14211476{
14221477/* We must return the whole tuple as a Datum. */
@@ -1427,16 +1482,14 @@ postquel_get_single_result(TupleTableSlot *slot,
14271482{
14281483/*
14291484 * Returning a scalar, which we have to extract from the first column
1430- * of the SELECT result, and then copy intoresult context if needed.
1485+ * of the SELECT result, and then copy intocurrent context if needed.
14311486 */
14321487value = slot_getattr (slot ,1 ,& (fcinfo -> isnull ));
14331488
14341489if (!fcinfo -> isnull )
14351490value = datumCopy (value ,fcache -> func -> typbyval ,fcache -> func -> typlen );
14361491}
14371492
1438- MemoryContextSwitchTo (oldcontext );
1439-
14401493return value ;
14411494}
14421495
@@ -1450,7 +1503,6 @@ fmgr_sql(PG_FUNCTION_ARGS)
14501503SQLFunctionLink * flink ;
14511504ErrorContextCallback sqlerrcontext ;
14521505MemoryContext tscontext ;
1453- MemoryContext oldcontext ;
14541506bool randomAccess ;
14551507bool lazyEvalOK ;
14561508bool pushed_snapshot ;
@@ -1507,20 +1559,14 @@ fmgr_sql(PG_FUNCTION_ARGS)
15071559 * Build tuplestore to hold results, if we don't have one already. Make
15081560 * sure it's in a suitable context.
15091561 */
1510- oldcontext = MemoryContextSwitchTo (tscontext );
1511-
15121562if (!fcache -> tstore )
1513- fcache -> tstore = tuplestore_begin_heap (randomAccess , false,work_mem );
1563+ {
1564+ MemoryContext oldcontext ;
15141565
1515- /*
1516- * Switch to context in which the fcache lives. The sub-executor is
1517- * responsible for deleting per-tuple information. (XXX in the case of a
1518- * long-lived FmgrInfo, this policy potentially causes memory leakage, but
1519- * it's not very clear where we could keep stuff instead. Fortunately,
1520- * there are few if any cases where set-returning functions are invoked
1521- * via FmgrInfos that would outlive the calling query.)
1522- */
1523- MemoryContextSwitchTo (fcache -> fcontext );
1566+ oldcontext = MemoryContextSwitchTo (tscontext );
1567+ fcache -> tstore = tuplestore_begin_heap (randomAccess , false,work_mem );
1568+ MemoryContextSwitchTo (oldcontext );
1569+ }
15241570
15251571/*
15261572 * Find first unfinished execution_state. If none, advance to the next
@@ -1598,7 +1644,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
15981644 * don't care about fetching any more result rows.
15991645 */
16001646if (completed || !fcache -> func -> returnsSet )
1601- postquel_end (es );
1647+ postquel_end (es , fcache );
16021648
16031649/*
16041650 * Break from loop if we didn't shut down (implying we got a
@@ -1657,8 +1703,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
16571703if (!tuplestore_gettupleslot (fcache -> tstore , true, false,slot ))
16581704elog (ERROR ,"failed to fetch lazy-eval tuple" );
16591705/* Extract the result as a datum, and copy out from the slot */
1660- result = postquel_get_single_result (slot ,fcinfo ,
1661- fcache ,oldcontext );
1706+ result = postquel_get_single_result (slot ,fcinfo ,fcache );
16621707/* Clear the tuplestore, but keep it for next time */
16631708/* NB: this might delete the slot's content, but we don't care */
16641709tuplestore_clear (fcache -> tstore );
@@ -1716,12 +1761,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
17161761fcache -> tstore = NULL ;
17171762/* must copy desc because execSRF.c will free it */
17181763if (fcache -> junkFilter )
1719- {
1720- /* setDesc must be allocated in suitable context */
1721- MemoryContextSwitchTo (tscontext );
17221764rsi -> setDesc = CreateTupleDescCopy (fcache -> junkFilter -> jf_cleanTupType );
1723- MemoryContextSwitchTo (fcache -> fcontext );
1724- }
17251765
17261766fcinfo -> isnull = true;
17271767result = (Datum )0 ;
@@ -1746,8 +1786,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
17461786/* Re-use the junkfilter's output slot to fetch back the tuple */
17471787slot = fcache -> junkFilter -> jf_resultSlot ;
17481788if (tuplestore_gettupleslot (fcache -> tstore , true, false,slot ))
1749- result = postquel_get_single_result (slot ,fcinfo ,
1750- fcache ,oldcontext );
1789+ result = postquel_get_single_result (slot ,fcinfo ,fcache );
17511790else
17521791{
17531792fcinfo -> isnull = true;
@@ -1770,8 +1809,6 @@ fmgr_sql(PG_FUNCTION_ARGS)
17701809if (pushed_snapshot )
17711810PopActiveSnapshot ();
17721811
1773- MemoryContextSwitchTo (oldcontext );
1774-
17751812/*
17761813 * If we've gone through every command in the function, we are done.
17771814 * Release the cache to start over again on next call.
@@ -1889,7 +1926,7 @@ ShutdownSQLFunction(Datum arg)
18891926if (!fcache -> func -> readonly_func )
18901927PushActiveSnapshot (es -> qd -> snapshot );
18911928
1892- postquel_end (es );
1929+ postquel_end (es , fcache );
18931930
18941931if (!fcache -> func -> readonly_func )
18951932PopActiveSnapshot ();