@@ -299,6 +299,14 @@ static int pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
299299static int pltcl_SPI_lastoid (ClientData cdata , Tcl_Interp * interp ,
300300 int objc , Tcl_Obj * const objv []);
301301
302+ static void pltcl_subtrans_begin (MemoryContext oldcontext ,
303+ ResourceOwner oldowner );
304+ static void pltcl_subtrans_commit (MemoryContext oldcontext ,
305+ ResourceOwner oldowner );
306+ static void pltcl_subtrans_abort (Tcl_Interp * interp ,
307+ MemoryContext oldcontext ,
308+ ResourceOwner oldowner );
309+
302310static void pltcl_set_tuple_values (Tcl_Interp * interp , const char * arrayname ,
303311 uint64 tupno , HeapTuple tuple , TupleDesc tupdesc );
304312static Tcl_Obj * pltcl_build_tuple_argument (HeapTuple tuple , TupleDesc tupdesc );
@@ -2073,9 +2081,9 @@ pltcl_returnnext(ClientData cdata, Tcl_Interp *interp,
20732081 pltcl_call_state * call_state = pltcl_current_call_state ;
20742082 FunctionCallInfo fcinfo = call_state -> fcinfo ;
20752083 pltcl_proc_desc * prodesc = call_state -> prodesc ;
2076- int result = TCL_OK ;
2077- MemoryContext tmpcxt ;
2078- MemoryContext oldcxt ;
2084+ MemoryContext oldcontext = CurrentMemoryContext ;
2085+ ResourceOwner oldowner = CurrentResourceOwner ;
2086+ volatile int result = TCL_OK ;
20792087
20802088 /*
20812089 * Check that we're called as a set-returning function
@@ -2103,52 +2111,63 @@ pltcl_returnnext(ClientData cdata, Tcl_Interp *interp,
21032111 return TCL_ERROR ;
21042112 }
21052113
2106- /* Set up tuple store if first output row */
2107- if (call_state -> tuple_store == NULL )
2108- pltcl_init_tuple_store (call_state );
2114+ /*
2115+ * The rest might throw elog(ERROR), so must run in a subtransaction.
2116+ *
2117+ * A small advantage of using a subtransaction is that it provides a
2118+ * short-lived memory context for free, so we needn't worry about leaking
2119+ * memory here. To use that context, call BeginInternalSubTransaction
2120+ * directly instead of going through pltcl_subtrans_begin.
2121+ */
2122+ BeginInternalSubTransaction (NULL );
2123+ PG_TRY ();
2124+ {
2125+ /* Set up tuple store if first output row */
2126+ if (call_state -> tuple_store == NULL )
2127+ pltcl_init_tuple_store (call_state );
21092128
2110- /* Make short-lived context to run input functions in */
2111- tmpcxt = AllocSetContextCreate (CurrentMemoryContext ,
2112- "pltcl_returnnext" ,
2113- ALLOCSET_SMALL_SIZES );
2114- oldcxt = MemoryContextSwitchTo (tmpcxt );
2129+ if (prodesc -> fn_retistuple )
2130+ {
2131+ Tcl_Obj * * rowObjv ;
2132+ int rowObjc ;
21152133
2116- if (prodesc -> fn_retistuple )
2117- {
2118- Tcl_Obj * * rowObjv ;
2119- int rowObjc ;
2134+ /* result should be a list, so break it down */
2135+ if (Tcl_ListObjGetElements (interp , objv [1 ], & rowObjc , & rowObjv ) == TCL_ERROR )
2136+ result = TCL_ERROR ;
2137+ else
2138+ {
2139+ HeapTuple tuple ;
21202140
2121- /* result should be a list, so break it down */
2122- if (Tcl_ListObjGetElements (interp , objv [1 ], & rowObjc , & rowObjv ) == TCL_ERROR )
2123- result = TCL_ERROR ;
2141+ tuple = pltcl_build_tuple_result (interp , rowObjv , rowObjc ,
2142+ call_state );
2143+ tuplestore_puttuple (call_state -> tuple_store , tuple );
2144+ }
2145+ }
21242146 else
21252147 {
2126- HeapTuple tuple ;
2127-
2128- tuple = pltcl_build_tuple_result (interp , rowObjv , rowObjc ,
2129- call_state );
2130- tuplestore_puttuple (call_state -> tuple_store , tuple );
2148+ Datum retval ;
2149+ bool isNull = false;
2150+
2151+ /* for paranoia's sake, check that tupdesc has exactly one column */
2152+ if (call_state -> ret_tupdesc -> natts != 1 )
2153+ elog (ERROR , "wrong result type supplied in return_next" );
2154+
2155+ retval = InputFunctionCall (& prodesc -> result_in_func ,
2156+ utf_u2e ((char * ) Tcl_GetString (objv [1 ])),
2157+ prodesc -> result_typioparam ,
2158+ -1 );
2159+ tuplestore_putvalues (call_state -> tuple_store , call_state -> ret_tupdesc ,
2160+ & retval , & isNull );
21312161 }
2162+
2163+ pltcl_subtrans_commit (oldcontext , oldowner );
21322164 }
2133- else
2165+ PG_CATCH ();
21342166 {
2135- Datum retval ;
2136- bool isNull = false;
2137-
2138- /* for paranoia's sake, check that tupdesc has exactly one column */
2139- if (call_state -> ret_tupdesc -> natts != 1 )
2140- elog (ERROR , "wrong result type supplied in return_next" );
2141-
2142- retval = InputFunctionCall (& prodesc -> result_in_func ,
2143- utf_u2e ((char * ) Tcl_GetString (objv [1 ])),
2144- prodesc -> result_typioparam ,
2145- -1 );
2146- tuplestore_putvalues (call_state -> tuple_store , call_state -> ret_tupdesc ,
2147- & retval , & isNull );
2167+ pltcl_subtrans_abort (interp , oldcontext , oldowner );
2168+ return TCL_ERROR ;
21482169 }
2149-
2150- MemoryContextSwitchTo (oldcxt );
2151- MemoryContextDelete (tmpcxt );
2170+ PG_END_TRY ();
21522171
21532172 return result ;
21542173}
0 commit comments