Skip to content

Commit 8c57229

Browse files
committed
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 26abb50, found more or less accidentally by the buildfarm thanks to the tests added in 961bed0. Report: https://postgr.es/m/30647.1483989734@sss.pgh.pa.us
1 parent 3957b58 commit 8c57229

File tree

1 file changed

+59
-40
lines changed

1 file changed

+59
-40
lines changed

src/pl/tcl/pltcl.c

Lines changed: 59 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,14 @@ static int pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
299299
static 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+
302310
static void pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
303311
uint64 tupno, HeapTuple tuple, TupleDesc tupdesc);
304312
static 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

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy