Hi,
plperl_call_perl_func() invokes the user's sub with call_sv(G_EVAL).
A die inside the sub becomes ERRSV and we report it as ERROR.
After the sub returns we convert the result in C. That path can
dispatch Perl magic: SvGETMAGIC(), av_fetch(), hv_iternext(), and
transform functions such as plperl_to_hstore(). A croak from FETCH
then finds no CXt_EVAL on Perl's cx stack. die_where() treats it as
uncaught, writes the message to stderr, and my_exit_jump() does
JMPENV_JUMP(2). The backend exits with status 2. The postmaster
runs crash recovery.
A tied scalar that RETURNS text already becomes ERROR, because
return $s runs FETCH inside pp_leavesub while G_EVAL is still
active. return \@tied or \%tied only returns a reference. The
element FETCHes happen later, after call_sv has returned.
The hole is any Perl croak from that C conversion, not only tie.
A trigger that returns an overloaded object dies in sv2cstr().
JMPENV_PUSH around the handler is not an eval context. It would
catch JMPENV_JUMP(2) after Perl has already unwound its stacks.
The interpreter is then in exit state, so the honest mapping is
FATAL, not ERROR.
The conversion needs a live Perl eval context. Register
PostgreSQL::InServer::_eval with newXS, next to
SPI::bootstrap. After SPI_finish, plperl_func_handler and
plperl_trigger_handler call that XSUB with call_sv(G_VOID | G_EVAL).
The XSUB runs the existing conversion. Postgres errors are turned
into croak_cstr(), the same pattern as plperl_spi_exec(). After
call_sv returns we restore PG_exception_stack and
error_context_stack, because a croak from FETCH longjmps to call_sv
and skips PG_TRY inside the XSUB. Nested conversion from
return_next() is already under the user's eval, so the trampoline
just calls the C function.
A croak from FETCH is then JMPENV_JUMP(3). call_sv returns with
ERRSV set. We report ERROR. The interpreter stays usable. The
same session can run another PL/Perl function.
A regress case is included. It covers die in FETCH, a missing
FETCH method, and die in FIRSTKEY. A plperlu case covers croak
from overload stringify on a trigger return.