Skip to content

Commit 97d8ad1

Browse files
committed
Back-patch fix and test case for bug #7516.
Back-patch commits 9afc648 and b8fbbcf. The first of these is really a minor code cleanup to save a few cycles, but it turns out to provide a workaround for the misoptimization problem described in bug #7516. The second commit adds a regression test case. Back-patch the fix to all active branches. The test case only works as far back as 9.0, because it relies on plpgsql which isn't installed by default before that. (I didn't have success modifying it into an all-plperl form that still provoked a crash, though this may just reflect my lack of Perl-fu.)
1 parent eb6e9b5 commit 97d8ad1

File tree

3 files changed

+91
-23
lines changed

3 files changed

+91
-23
lines changed

src/pl/plperl/expected/plperl_elog.out

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,45 @@ select uses_global();
6262
do language plperl $$ elog(NOTICE, ${^TAINT}); $$;
6363
NOTICE: 0
6464
CONTEXT: PL/Perl anonymous code block
65+
-- test recovery after "die"
66+
create or replace function just_die() returns void language plperl AS $$
67+
die "just die";
68+
$$;
69+
select just_die();
70+
ERROR: just die at line 2.
71+
CONTEXT: PL/Perl function "just_die"
72+
create or replace function die_caller() returns int language plpgsql as $$
73+
BEGIN
74+
BEGIN
75+
PERFORM just_die();
76+
EXCEPTION WHEN OTHERS THEN
77+
RAISE NOTICE 'caught die';
78+
END;
79+
RETURN 1;
80+
END;
81+
$$;
82+
select die_caller();
83+
NOTICE: caught die
84+
die_caller
85+
------------
86+
1
87+
(1 row)
88+
89+
create or replace function indirect_die_caller() returns int language plperl as $$
90+
my $prepared = spi_prepare('SELECT die_caller() AS fx');
91+
my $a = spi_exec_prepared($prepared)->{rows}->[0]->{fx};
92+
my $b = spi_exec_prepared($prepared)->{rows}->[0]->{fx};
93+
return $a + $b;
94+
$$;
95+
select indirect_die_caller();
96+
NOTICE: caught die
97+
CONTEXT: SQL statement "SELECT die_caller() AS fx"
98+
PL/Perl function "indirect_die_caller"
99+
NOTICE: caught die
100+
CONTEXT: SQL statement "SELECT die_caller() AS fx"
101+
PL/Perl function "indirect_die_caller"
102+
indirect_die_caller
103+
---------------------
104+
2
105+
(1 row)
106+

src/pl/plperl/plperl.c

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,27 +1696,32 @@ plperl_call_handler(PG_FUNCTION_ARGS)
16961696
Datum retval;
16971697
plperl_call_data *save_call_data = current_call_data;
16981698
plperl_interp_desc *oldinterp = plperl_active_interp;
1699+
plperl_call_data this_call_data;
1700+
1701+
/* Initialize current-call status record */
1702+
MemSet(&this_call_data, 0, sizeof(this_call_data));
1703+
this_call_data.fcinfo = fcinfo;
16991704

17001705
PG_TRY();
17011706
{
1702-
current_call_data = NULL;
1707+
current_call_data = &this_call_data;
17031708
if (CALLED_AS_TRIGGER(fcinfo))
17041709
retval = PointerGetDatum(plperl_trigger_handler(fcinfo));
17051710
else
17061711
retval = plperl_func_handler(fcinfo);
17071712
}
17081713
PG_CATCH();
17091714
{
1710-
if (current_call_data && current_call_data->prodesc)
1711-
decrement_prodesc_refcount(current_call_data->prodesc);
1715+
if (this_call_data.prodesc)
1716+
decrement_prodesc_refcount(this_call_data.prodesc);
17121717
current_call_data = save_call_data;
17131718
activate_interpreter(oldinterp);
17141719
PG_RE_THROW();
17151720
}
17161721
PG_END_TRY();
17171722

1718-
if (current_call_data && current_call_data->prodesc)
1719-
decrement_prodesc_refcount(current_call_data->prodesc);
1723+
if (this_call_data.prodesc)
1724+
decrement_prodesc_refcount(this_call_data.prodesc);
17201725
current_call_data = save_call_data;
17211726
activate_interpreter(oldinterp);
17221727
return retval;
@@ -1736,8 +1741,12 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
17361741
plperl_proc_desc desc;
17371742
plperl_call_data *save_call_data = current_call_data;
17381743
plperl_interp_desc *oldinterp = plperl_active_interp;
1744+
plperl_call_data this_call_data;
17391745
ErrorContextCallback pl_error_context;
17401746

1747+
/* Initialize current-call status record */
1748+
MemSet(&this_call_data, 0, sizeof(this_call_data));
1749+
17411750
/* Set up a callback for error reporting */
17421751
pl_error_context.callback = plperl_inline_callback;
17431752
pl_error_context.previous = error_context_stack;
@@ -1768,14 +1777,15 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
17681777
desc.nargs = 0;
17691778
desc.reference = NULL;
17701779

1780+
this_call_data.fcinfo = &fake_fcinfo;
1781+
this_call_data.prodesc = &desc;
1782+
/* we do not bother with refcounting the fake prodesc */
1783+
17711784
PG_TRY();
17721785
{
17731786
SV *perlret;
17741787

1775-
current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));
1776-
current_call_data->fcinfo = &fake_fcinfo;
1777-
current_call_data->prodesc = &desc;
1778-
/* we do not bother with refcounting the fake prodesc */
1788+
current_call_data = &this_call_data;
17791789

17801790
if (SPI_connect() != SPI_OK_CONNECT)
17811791
elog(ERROR, "could not connect to SPI manager");
@@ -2158,13 +2168,6 @@ plperl_func_handler(PG_FUNCTION_ARGS)
21582168
ReturnSetInfo *rsi;
21592169
ErrorContextCallback pl_error_context;
21602170

2161-
/*
2162-
* Create the call_data before connecting to SPI, so that it is not
2163-
* allocated in the SPI memory context
2164-
*/
2165-
current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));
2166-
current_call_data->fcinfo = fcinfo;
2167-
21682171
if (SPI_connect() != SPI_OK_CONNECT)
21692172
elog(ERROR, "could not connect to SPI manager");
21702173

@@ -2277,13 +2280,6 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
22772280
HV *hvTD;
22782281
ErrorContextCallback pl_error_context;
22792282

2280-
/*
2281-
* Create the call_data before connecting to SPI, so that it is not
2282-
* allocated in the SPI memory context
2283-
*/
2284-
current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));
2285-
current_call_data->fcinfo = fcinfo;
2286-
22872283
/* Connect to SPI manager */
22882284
if (SPI_connect() != SPI_OK_CONNECT)
22892285
elog(ERROR, "could not connect to SPI manager");

src/pl/plperl/sql/plperl_elog.sql

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,33 @@ select uses_global();
4646

4747
-- make sure we don't choke on readonly values
4848
do language plperl $$ elog(NOTICE, ${^TAINT}); $$;
49+
50+
-- test recovery after "die"
51+
52+
create or replace function just_die() returns void language plperl AS $$
53+
die "just die";
54+
$$;
55+
56+
select just_die();
57+
58+
create or replace function die_caller() returns int language plpgsql as $$
59+
BEGIN
60+
BEGIN
61+
PERFORM just_die();
62+
EXCEPTION WHEN OTHERS THEN
63+
RAISE NOTICE 'caught die';
64+
END;
65+
RETURN 1;
66+
END;
67+
$$;
68+
69+
select die_caller();
70+
71+
create or replace function indirect_die_caller() returns int language plperl as $$
72+
my $prepared = spi_prepare('SELECT die_caller() AS fx');
73+
my $a = spi_exec_prepared($prepared)->{rows}->[0]->{fx};
74+
my $b = spi_exec_prepared($prepared)->{rows}->[0]->{fx};
75+
return $a + $b;
76+
$$;
77+
78+
select indirect_die_caller();

0 commit comments

Comments
 (0)