]> granicus.if.org Git - postgresql/commitdiff
Back-patch fix and test case for bug #7516.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 14 Sep 2012 15:50:10 +0000 (11:50 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 14 Sep 2012 15:50:10 +0000 (11:50 -0400)
Back-patch commits 9afc6481117d2dd936e752da0424a2b6b05f6459 and
b8fbbcf37f22c5e8361da939ad0fc4be18a34ca9.  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.)

src/pl/plperl/expected/plperl_elog.out
src/pl/plperl/plperl.c
src/pl/plperl/sql/plperl_elog.sql

index 02497d9e02bea1b865b35d148c6750a2f7649d0f..ad8fb38b8afb6ce83665641117a7c6831697d019 100644 (file)
@@ -58,3 +58,45 @@ select uses_global();
  uses_global worked
 (1 row)
 
+-- test recovery after "die"
+create or replace function just_die() returns void language plperl AS $$
+die "just die";
+$$;
+select just_die();
+ERROR:  just die at line 2.
+CONTEXT:  PL/Perl function "just_die"
+create or replace function die_caller() returns int language plpgsql as $$
+BEGIN
+  BEGIN
+    PERFORM just_die();
+  EXCEPTION WHEN OTHERS THEN
+    RAISE NOTICE 'caught die';
+  END;
+  RETURN 1;
+END;
+$$;
+select die_caller();
+NOTICE:  caught die
+ die_caller 
+------------
+          1
+(1 row)
+
+create or replace function indirect_die_caller() returns int language plperl as $$
+my $prepared = spi_prepare('SELECT die_caller() AS fx');
+my $a = spi_exec_prepared($prepared)->{rows}->[0]->{fx};
+my $b = spi_exec_prepared($prepared)->{rows}->[0]->{fx};
+return $a + $b;
+$$;
+select indirect_die_caller();
+NOTICE:  caught die
+CONTEXT:  SQL statement "SELECT die_caller() AS fx"
+PL/Perl function "indirect_die_caller"
+NOTICE:  caught die
+CONTEXT:  SQL statement "SELECT die_caller() AS fx"
+PL/Perl function "indirect_die_caller"
+ indirect_die_caller 
+---------------------
+                   2
+(1 row)
+
index 7c34f14b566d591d901e0b7972ffc84df2d4ed77..3334ca6d7a65f6511f8098a921e6f22662b9dc40 100644 (file)
@@ -1244,10 +1244,15 @@ plperl_call_handler(PG_FUNCTION_ARGS)
        Datum           retval;
        plperl_call_data *save_call_data = current_call_data;
        plperl_interp_desc *oldinterp = plperl_active_interp;
+       plperl_call_data this_call_data;
+
+       /* Initialize current-call status record */
+       MemSet(&this_call_data, 0, sizeof(this_call_data));
+       this_call_data.fcinfo = fcinfo;
 
        PG_TRY();
        {
-               current_call_data = NULL;
+               current_call_data = &this_call_data;
                if (CALLED_AS_TRIGGER(fcinfo))
                        retval = PointerGetDatum(plperl_trigger_handler(fcinfo));
                else
@@ -1255,16 +1260,16 @@ plperl_call_handler(PG_FUNCTION_ARGS)
        }
        PG_CATCH();
        {
-               if (current_call_data && current_call_data->prodesc)
-                       decrement_prodesc_refcount(current_call_data->prodesc);
+               if (this_call_data.prodesc)
+                       decrement_prodesc_refcount(this_call_data.prodesc);
                current_call_data = save_call_data;
                activate_interpreter(oldinterp);
                PG_RE_THROW();
        }
        PG_END_TRY();
 
-       if (current_call_data && current_call_data->prodesc)
-               decrement_prodesc_refcount(current_call_data->prodesc);
+       if (this_call_data.prodesc)
+               decrement_prodesc_refcount(this_call_data.prodesc);
        current_call_data = save_call_data;
        activate_interpreter(oldinterp);
        return retval;
@@ -1284,8 +1289,12 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
        plperl_proc_desc desc;
        plperl_call_data *save_call_data = current_call_data;
        plperl_interp_desc *oldinterp = plperl_active_interp;
+       plperl_call_data this_call_data;
        ErrorContextCallback pl_error_context;
 
+       /* Initialize current-call status record */
+       MemSet(&this_call_data, 0, sizeof(this_call_data));
+
        /* Set up a callback for error reporting */
        pl_error_context.callback = plperl_inline_callback;
        pl_error_context.previous = error_context_stack;
@@ -1316,14 +1325,15 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
        desc.nargs = 0;
        desc.reference = NULL;
 
+       this_call_data.fcinfo = &fake_fcinfo;
+       this_call_data.prodesc = &desc;
+       /* we do not bother with refcounting the fake prodesc */
+
        PG_TRY();
        {
                SV                 *perlret;
 
-               current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));
-               current_call_data->fcinfo = &fake_fcinfo;
-               current_call_data->prodesc = &desc;
-               /* we do not bother with refcounting the fake prodesc */
+               current_call_data = &this_call_data;
 
                if (SPI_connect() != SPI_OK_CONNECT)
                        elog(ERROR, "could not connect to SPI manager");
@@ -1672,13 +1682,6 @@ plperl_func_handler(PG_FUNCTION_ARGS)
        SV                 *array_ret = NULL;
        ErrorContextCallback pl_error_context;
 
-       /*
-        * Create the call_data beforing connecting to SPI, so that it is not
-        * allocated in the SPI memory context
-        */
-       current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));
-       current_call_data->fcinfo = fcinfo;
-
        if (SPI_connect() != SPI_OK_CONNECT)
                elog(ERROR, "could not connect to SPI manager");
 
@@ -1832,13 +1835,6 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
        HV                 *hvTD;
        ErrorContextCallback pl_error_context;
 
-       /*
-        * Create the call_data beforing connecting to SPI, so that it is not
-        * allocated in the SPI memory context
-        */
-       current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));
-       current_call_data->fcinfo = fcinfo;
-
        /* Connect to SPI manager */
        if (SPI_connect() != SPI_OK_CONNECT)
                elog(ERROR, "could not connect to SPI manager");
index 4f1c014efbdf2ba59d308a6cf19a771af345dc01..1d58182e1818178eb3bee720f4fe5b2c6e1636e9 100644 (file)
@@ -43,3 +43,33 @@ create or replace function uses_global() returns text language plperl as $$
 $$;
 
 select uses_global();
+
+-- test recovery after "die"
+
+create or replace function just_die() returns void language plperl AS $$
+die "just die";
+$$;
+
+select just_die();
+
+create or replace function die_caller() returns int language plpgsql as $$
+BEGIN
+  BEGIN
+    PERFORM just_die();
+  EXCEPTION WHEN OTHERS THEN
+    RAISE NOTICE 'caught die';
+  END;
+  RETURN 1;
+END;
+$$;
+
+select die_caller();
+
+create or replace function indirect_die_caller() returns int language plperl as $$
+my $prepared = spi_prepare('SELECT die_caller() AS fx');
+my $a = spi_exec_prepared($prepared)->{rows}->[0]->{fx};
+my $b = spi_exec_prepared($prepared)->{rows}->[0]->{fx};
+return $a + $b;
+$$;
+
+select indirect_die_caller();