mirror of
https://github.com/postgres/postgres.git
synced 2025-05-29 00:03:09 -04:00
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 26abb50c4, found more or less accidentally by the buildfarm thanks to the tests added in 961bed020. Report: https://postgr.es/m/30647.1483989734@sss.pgh.pa.us
This commit is contained in:
parent
3957b58b88
commit
8c5722948e
@ -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,
|
|||||||
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,52 +2111,63 @@ pltcl_returnnext(ClientData cdata, Tcl_Interp *interp,
|
|||||||
return TCL_ERROR;
|
return TCL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up tuple store if first output row */
|
/*
|
||||||
if (call_state->tuple_store == NULL)
|
* The rest might throw elog(ERROR), so must run in a subtransaction.
|
||||||
pltcl_init_tuple_store(call_state);
|
*
|
||||||
|
* A small advantage of using a subtransaction is that it provides a
|
||||||
/* Make short-lived context to run input functions in */
|
* short-lived memory context for free, so we needn't worry about leaking
|
||||||
tmpcxt = AllocSetContextCreate(CurrentMemoryContext,
|
* memory here. To use that context, call BeginInternalSubTransaction
|
||||||
"pltcl_returnnext",
|
* directly instead of going through pltcl_subtrans_begin.
|
||||||
ALLOCSET_SMALL_SIZES);
|
*/
|
||||||
oldcxt = MemoryContextSwitchTo(tmpcxt);
|
BeginInternalSubTransaction(NULL);
|
||||||
|
PG_TRY();
|
||||||
if (prodesc->fn_retistuple)
|
|
||||||
{
|
{
|
||||||
Tcl_Obj **rowObjv;
|
/* Set up tuple store if first output row */
|
||||||
int rowObjc;
|
if (call_state->tuple_store == NULL)
|
||||||
|
pltcl_init_tuple_store(call_state);
|
||||||
|
|
||||||
/* result should be a list, so break it down */
|
if (prodesc->fn_retistuple)
|
||||||
if (Tcl_ListObjGetElements(interp, objv[1], &rowObjc, &rowObjv) == TCL_ERROR)
|
{
|
||||||
result = TCL_ERROR;
|
Tcl_Obj **rowObjv;
|
||||||
|
int rowObjc;
|
||||||
|
|
||||||
|
/* result should be a list, so break it down */
|
||||||
|
if (Tcl_ListObjGetElements(interp, objv[1], &rowObjc, &rowObjv) == TCL_ERROR)
|
||||||
|
result = TCL_ERROR;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HeapTuple tuple;
|
||||||
|
|
||||||
|
tuple = pltcl_build_tuple_result(interp, rowObjv, rowObjc,
|
||||||
|
call_state);
|
||||||
|
tuplestore_puttuple(call_state->tuple_store, tuple);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
HeapTuple tuple;
|
Datum retval;
|
||||||
|
bool isNull = false;
|
||||||
|
|
||||||
tuple = pltcl_build_tuple_result(interp, rowObjv, rowObjc,
|
/* for paranoia's sake, check that tupdesc has exactly one column */
|
||||||
call_state);
|
if (call_state->ret_tupdesc->natts != 1)
|
||||||
tuplestore_puttuple(call_state->tuple_store, tuple);
|
elog(ERROR, "wrong result type supplied in return_next");
|
||||||
|
|
||||||
|
retval = InputFunctionCall(&prodesc->result_in_func,
|
||||||
|
utf_u2e((char *) Tcl_GetString(objv[1])),
|
||||||
|
prodesc->result_typioparam,
|
||||||
|
-1);
|
||||||
|
tuplestore_putvalues(call_state->tuple_store, call_state->ret_tupdesc,
|
||||||
|
&retval, &isNull);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pltcl_subtrans_commit(oldcontext, oldowner);
|
||||||
}
|
}
|
||||||
else
|
PG_CATCH();
|
||||||
{
|
{
|
||||||
Datum retval;
|
pltcl_subtrans_abort(interp, oldcontext, oldowner);
|
||||||
bool isNull = false;
|
return TCL_ERROR;
|
||||||
|
|
||||||
/* for paranoia's sake, check that tupdesc has exactly one column */
|
|
||||||
if (call_state->ret_tupdesc->natts != 1)
|
|
||||||
elog(ERROR, "wrong result type supplied in return_next");
|
|
||||||
|
|
||||||
retval = InputFunctionCall(&prodesc->result_in_func,
|
|
||||||
utf_u2e((char *) Tcl_GetString(objv[1])),
|
|
||||||
prodesc->result_typioparam,
|
|
||||||
-1);
|
|
||||||
tuplestore_putvalues(call_state->tuple_store, call_state->ret_tupdesc,
|
|
||||||
&retval, &isNull);
|
|
||||||
}
|
}
|
||||||
|
PG_END_TRY();
|
||||||
MemoryContextSwitchTo(oldcxt);
|
|
||||||
MemoryContextDelete(tmpcxt);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user