try/catch macros for Postgres backend - Mailing list pgsql-hackers

From Tom Lane
Subject try/catch macros for Postgres backend
Date
Msg-id 1222.1091060357@sss.pgh.pa.us
Whole thread Raw
Responses Re: try/catch macros for Postgres backend  (Gavin Sherry <swm@linuxworld.com.au>)
Re: try/catch macros for Postgres backend  (Alvaro Herrera Munoz <alvherre@dcc.uchile.cl>)
Re: try/catch macros for Postgres backend  (Fabien COELHO <coelho@cri.ensmp.fr>)
Re: try/catch macros for Postgres backend  (Joe Conway <mail@joeconway.com>)
Re: try/catch macros for Postgres backend  (James William Pye <flaw@rhid.com>)
List pgsql-hackers
In service of the refactoring of error handling that I was talking about
a few days ago, I'm finding that there are several places that really
ought to catch longjmps and clean up after themselves, instead of
expecting that whatever mess they've made will be cleaned up for them
when control gets back to PostgresMain().  If we have functions that can
catch errors, control might *not* go right back to PostgresMain(), and
so throwing random cleanup actions into the sigsetjmp branch there is
No Good.

This is no big deal since pltcl and plpython already do much the same
thing, but I'm starting to think that instead of directly hacking on
Warn_restart, it would be good to have some macros hiding the details.
The syntax I'm toying with is
PG_TRY();{    ... code that might elog ...}PG_CATCH();{    ... recovery code here ...    PG_RE_THROW();        //
optional}PG_END_CATCH();

The braces in this are not actually necessary, but will be good style
since they help visually set off the controlled code.  (You can't just
indent the controlled code without adding braces, because pg_indent will
helpfully undo it.)

This would expand to something on the close order of
do {    sigjmp_buf local_save_restart;
    memcpy(local_save_restart, Warn_restart, sizeof());    if (sigsetjmp(warn_restart) == 0)    {        ... code that
mightelog ...        memcpy(Warn_restart, local_save_restart, sizeof());    }    else    {        memcpy(Warn_restart,
local_save_restart,sizeof());        ... recovery code here ...    }
 
} while(0)

and of course PG_RE_THROW is just a siglongjmp call.

Does anyone have a problem with this macro syntax?  The try/catch names
are stolen from Java, so I'm figuring they won't terribly surprise any
modern programmer, but I'm open to different names if anyone has a
better idea.

Also, the memcpy technique for saving/restoring Warn_restart is what
pltcl and plpython currently use, and it works, but it seems
unnecessarily inefficient.  A further improvement would be to replace
Warn_restart by a pointer defined likeextern sigjmp_buf *exception_stack_top;

and then the macro expansion would be something more like
do {    sigjmp_buf *save_exception_stack = exception_stack_top;    sigjmp_buf local_sigjmp_buf;
    if (sigsetjmp(local_sigjmp_buf) == 0)    {        exception_stack_top = &local_sigjmp_buf;        ... code that
mightelog ...        exception_stack_top = save_exception_stack;    }    else    {        exception_stack_top =
save_exception_stack;       ... recovery code here ...    }
 
} while(0)

while elog.c and PG_RE_THROW would need to dosiglongjmp(*exception_stack_top, 1);

I think that this should work but does anyone know of any machines where
it would have portability issues?
        regards, tom lane


pgsql-hackers by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: [ADMIN] Point in Time Recovery
Next
From: Gavin Sherry
Date:
Subject: Re: try/catch macros for Postgres backend