[PATCH] Catch croak during PL/Perl result conversion
Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.
You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:
docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t253814psql -h localhost -U postgresBuilt from patchset v1 (message #1), September 19, 2026 at 09:09 PM.
Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:
git clone --branch t253814_1 https://github.com/hackorum-dev/postgres.gitIn a checkout you already have, add the fork once:
git remote add hackorum https://github.com/hackorum-dev/postgres.gitthen, for this patchset and every later one:
git fetch hackorum t253814_1 && git checkout t253814_1Patchset v1 (message #1) is on t253814_1
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.
--
Regards,
Rachitskiy Andrey