try/catch macros for Postgres backend

From: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
To: pgsql-hackers(at)postgreSQL(dot)org
Subject: try/catch macros for Postgres backend
Date: 2004-07-29 00:19:17
Message-ID: 1222.1091060357@sss.pgh.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: 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 might elog ...
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 like
extern 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 might elog ...
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 do
siglongjmp(*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

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Gavin Sherry 2004-07-29 03:46:20 Re: try/catch macros for Postgres backend
Previous Message Andrew Dunstan 2004-07-28 17:34:58 Re: [HACKERS] Cannot initdb in cvs tip