Commit 8c572294 authored by Tom Lane's avatar Tom Lane

Fix error handling in pltcl_returnnext.

We can't throw elog(ERROR) out of a Tcl command procedure; we have
to catch the error and return TCL_ERROR to the Tcl interpreter.
pltcl_returnnext failed to meet this requirement, so that errors
detected by pltcl_build_tuple_result or other functions called here
led to longjmp'ing out of the Tcl interpreter and thereby leaving it
in a bad state.  Use the existing subtransaction support to prevent
that.  Oversight in commit 26abb50c, found more or less accidentally
by the buildfarm thanks to the tests added in 961bed02.

Report: https://postgr.es/m/30647.1483989734@sss.pgh.pa.us
parent 3957b58b
...@@ -299,6 +299,14 @@ static int pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp, ...@@ -299,6 +299,14 @@ static int pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
static int pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp, static int pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]); int objc, Tcl_Obj *const objv[]);
static void pltcl_subtrans_begin(MemoryContext oldcontext,
ResourceOwner oldowner);
static void pltcl_subtrans_commit(MemoryContext oldcontext,
ResourceOwner oldowner);
static void pltcl_subtrans_abort(Tcl_Interp *interp,
MemoryContext oldcontext,
ResourceOwner oldowner);
static void pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname, static void pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
uint64 tupno, HeapTuple tuple, TupleDesc tupdesc); uint64 tupno, HeapTuple tuple, TupleDesc tupdesc);
static Tcl_Obj *pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc); static Tcl_Obj *pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc);
...@@ -2073,9 +2081,9 @@ pltcl_returnnext(ClientData cdata, Tcl_Interp *interp, ...@@ -2073,9 +2081,9 @@ pltcl_returnnext(ClientData cdata, Tcl_Interp *interp,
pltcl_call_state *call_state = pltcl_current_call_state; pltcl_call_state *call_state = pltcl_current_call_state;
FunctionCallInfo fcinfo = call_state->fcinfo; FunctionCallInfo fcinfo = call_state->fcinfo;
pltcl_proc_desc *prodesc = call_state->prodesc; pltcl_proc_desc *prodesc = call_state->prodesc;
int result = TCL_OK; MemoryContext oldcontext = CurrentMemoryContext;
MemoryContext tmpcxt; ResourceOwner oldowner = CurrentResourceOwner;
MemoryContext oldcxt; volatile int result = TCL_OK;
/* /*
* Check that we're called as a set-returning function * Check that we're called as a set-returning function
...@@ -2103,16 +2111,21 @@ pltcl_returnnext(ClientData cdata, Tcl_Interp *interp, ...@@ -2103,16 +2111,21 @@ pltcl_returnnext(ClientData cdata, Tcl_Interp *interp,
return TCL_ERROR; return TCL_ERROR;
} }
/*
* The rest might throw elog(ERROR), so must run in a subtransaction.
*
* A small advantage of using a subtransaction is that it provides a
* short-lived memory context for free, so we needn't worry about leaking
* memory here. To use that context, call BeginInternalSubTransaction
* directly instead of going through pltcl_subtrans_begin.
*/
BeginInternalSubTransaction(NULL);
PG_TRY();
{
/* Set up tuple store if first output row */ /* Set up tuple store if first output row */
if (call_state->tuple_store == NULL) if (call_state->tuple_store == NULL)
pltcl_init_tuple_store(call_state); pltcl_init_tuple_store(call_state);
/* Make short-lived context to run input functions in */
tmpcxt = AllocSetContextCreate(CurrentMemoryContext,
"pltcl_returnnext",
ALLOCSET_SMALL_SIZES);
oldcxt = MemoryContextSwitchTo(tmpcxt);
if (prodesc->fn_retistuple) if (prodesc->fn_retistuple)
{ {
Tcl_Obj **rowObjv; Tcl_Obj **rowObjv;
...@@ -2147,8 +2160,14 @@ pltcl_returnnext(ClientData cdata, Tcl_Interp *interp, ...@@ -2147,8 +2160,14 @@ pltcl_returnnext(ClientData cdata, Tcl_Interp *interp,
&retval, &isNull); &retval, &isNull);
} }
MemoryContextSwitchTo(oldcxt); pltcl_subtrans_commit(oldcontext, oldowner);
MemoryContextDelete(tmpcxt); }
PG_CATCH();
{
pltcl_subtrans_abort(interp, oldcontext, oldowner);
return TCL_ERROR;
}
PG_END_TRY();
return result; return result;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment