Commit a393fbf9 authored by Tom Lane's avatar Tom Lane

Restructure error handling as recently discussed. It is now really

possible to trap an error inside a function rather than letting it
propagate out to PostgresMain.  You still have to use AbortCurrentTransaction
to clean up, but at least the error handling itself will cooperate.
parent 94f8f63f
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.189 2004/07/21 20:34:45 momjian Exp $
* $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.190 2004/07/31 00:45:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -16,7 +16,6 @@
#include <unistd.h>
#include <signal.h>
#include <setjmp.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
......@@ -458,15 +457,6 @@ BootstrapMain(int argc, char *argv[])
for (i = 0; i < HASHTABLESIZE; ++i)
hashtable[i] = NULL;
/*
* abort processing resumes here (this is probably dead code?)
*/
if (sigsetjmp(Warn_restart, 1) != 0)
{
Warnings++;
AbortCurrentTransaction();
}
/*
* Process bootstrap input.
*
......
......@@ -8,13 +8,12 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.227 2004/06/16 01:26:42 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.228 2004/07/31 00:45:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <netinet/in.h>
......@@ -130,6 +129,9 @@ static StringInfoData line_buf;
static bool line_buf_converted;
/* non-export function prototypes */
static void DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
char *delim, char *null_print, bool csv_mode, char *quote,
char *escape, List *force_quote_atts, bool fe_copy);
static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
char *delim, char *null_print, bool csv_mode, char *quote, char *escape,
List *force_quote_atts);
......@@ -688,6 +690,7 @@ DoCopy(const CopyStmt *stmt)
ListCell *option;
List *attnamelist = stmt->attlist;
List *attnumlist;
bool fe_copy = false;
bool binary = false;
bool oids = false;
bool csv_mode = false;
......@@ -1062,7 +1065,7 @@ DoCopy(const CopyStmt *stmt)
if (pipe)
{
if (whereToSendOutput == Remote)
SendCopyBegin(binary, list_length(attnumlist));
fe_copy = true;
else
copy_file = stdout;
}
......@@ -1099,8 +1102,9 @@ DoCopy(const CopyStmt *stmt)
errmsg("\"%s\" is a directory", filename)));
}
}
CopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
quote, escape, force_quote_atts);
DoCopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
quote, escape, force_quote_atts, fe_copy);
}
if (!pipe)
......@@ -1112,8 +1116,6 @@ DoCopy(const CopyStmt *stmt)
errmsg("could not write to file \"%s\": %m",
filename)));
}
else if (whereToSendOutput == Remote && !is_from)
SendCopyEnd(binary);
pfree(attribute_buf.data);
pfree(line_buf.data);
......@@ -1127,6 +1129,39 @@ DoCopy(const CopyStmt *stmt)
}
/*
* This intermediate routine just exists to localize the effects of setjmp
* so we don't need to plaster a lot of variables with "volatile".
*/
static void
DoCopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
char *delim, char *null_print, bool csv_mode, char *quote,
char *escape, List *force_quote_atts, bool fe_copy)
{
PG_TRY();
{
if (fe_copy)
SendCopyBegin(binary, list_length(attnumlist));
CopyTo(rel, attnumlist, binary, oids, delim, null_print, csv_mode,
quote, escape, force_quote_atts);
if (fe_copy)
SendCopyEnd(binary);
}
PG_CATCH();
{
/*
* Make sure we turn off old-style COPY OUT mode upon error.
* It is okay to do this in all cases, since it does nothing
* if the mode is not on.
*/
pq_endcopyout(true);
PG_RE_THROW();
}
PG_END_TRY();
}
/*
* Copy from relation TO file.
*/
......
......@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.29 2004/07/17 03:28:47 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.30 2004/07/31 00:45:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -259,8 +259,18 @@ PortalCleanup(Portal portal)
/* We must make the portal's resource owner current */
saveResourceOwner = CurrentResourceOwner;
PG_TRY();
{
CurrentResourceOwner = portal->resowner;
ExecutorEnd(queryDesc);
}
PG_CATCH();
{
/* Ensure CurrentResourceOwner is restored on error */
CurrentResourceOwner = saveResourceOwner;
PG_RE_THROW();
}
PG_END_TRY();
CurrentResourceOwner = saveResourceOwner;
}
}
......@@ -317,15 +327,17 @@ PersistHoldablePortal(Portal portal)
portal->status = PORTAL_ACTIVE;
/*
* Set global portal context pointers.
* Set up global portal context pointers.
*/
saveActivePortal = ActivePortal;
ActivePortal = portal;
saveResourceOwner = CurrentResourceOwner;
CurrentResourceOwner = portal->resowner;
savePortalContext = PortalContext;
PortalContext = PortalGetHeapMemory(portal);
saveQueryContext = QueryContext;
PG_TRY();
{
ActivePortal = portal;
CurrentResourceOwner = portal->resowner;
PortalContext = PortalGetHeapMemory(portal);
QueryContext = portal->queryContext;
MemoryContextSwitchTo(PortalContext);
......@@ -386,16 +398,23 @@ PersistHoldablePortal(Portal portal)
pfree(tup);
}
}
}
PG_CATCH();
{
/* Uncaught error while executing portal: mark it dead */
portal->status = PORTAL_FAILED;
MemoryContextSwitchTo(oldcxt);
/* Restore global vars and propagate error */
ActivePortal = saveActivePortal;
CurrentResourceOwner = saveResourceOwner;
PortalContext = savePortalContext;
QueryContext = saveQueryContext;
/*
* We can now release any subsidiary memory of the portal's heap
* context; we'll never use it again. The executor already dropped
* its context, but this will clean up anything that glommed onto the
* portal's heap via PortalContext.
*/
MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
PG_RE_THROW();
}
PG_END_TRY();
MemoryContextSwitchTo(oldcxt);
/* Mark portal not active */
portal->status = PORTAL_READY;
......@@ -404,4 +423,12 @@ PersistHoldablePortal(Portal portal)
CurrentResourceOwner = saveResourceOwner;
PortalContext = savePortalContext;
QueryContext = saveQueryContext;
/*
* We can now release any subsidiary memory of the portal's heap
* context; we'll never use it again. The executor already dropped
* its context, but this will clean up anything that glommed onto the
* portal's heap via PortalContext.
*/
MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
}
......@@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.284 2004/07/21 22:31:21 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.285 2004/07/31 00:45:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -228,14 +228,13 @@ void
vacuum(VacuumStmt *vacstmt)
{
const char *stmttype = vacstmt->vacuum ? "VACUUM" : "ANALYZE";
MemoryContext anl_context = NULL;
TransactionId initialOldestXmin = InvalidTransactionId;
TransactionId initialFreezeLimit = InvalidTransactionId;
bool all_rels,
volatile MemoryContext anl_context = NULL;
volatile bool all_rels,
in_outer_xact,
use_own_xacts;
List *relations;
ListCell *cur;
if (vacstmt->verbose)
elevel = INFO;
......@@ -267,10 +266,6 @@ vacuum(VacuumStmt *vacstmt)
in_outer_xact = IsInTransactionChain((void *) vacstmt);
}
/* Turn vacuum cost accounting on or off */
VacuumCostActive = (VacuumCostNaptime > 0);
VacuumCostBalance = 0;
/*
* Send info about dead objects to the statistics collector
*/
......@@ -377,6 +372,14 @@ vacuum(VacuumStmt *vacstmt)
CommitTransactionCommand();
}
/* Turn vacuum cost accounting on or off */
PG_TRY();
{
ListCell *cur;
VacuumCostActive = (VacuumCostNaptime > 0);
VacuumCostBalance = 0;
/*
* Loop to process each selected relation.
*/
......@@ -428,6 +431,17 @@ vacuum(VacuumStmt *vacstmt)
}
}
}
}
PG_CATCH();
{
/* Make sure cost accounting is turned off after error */
VacuumCostActive = false;
PG_RE_THROW();
}
PG_END_TRY();
/* Turn off vacuum cost accounting */
VacuumCostActive = false;
/*
* Finish up processing.
......@@ -475,9 +489,6 @@ vacuum(VacuumStmt *vacstmt)
if (anl_context)
MemoryContextDelete(anl_context);
/* Turn off vacuum cost accounting */
VacuumCostActive = false;
}
/*
......
......@@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/bgwriter.c,v 1.3 2004/06/03 02:08:03 tgl Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/bgwriter.c,v 1.4 2004/07/31 00:45:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -57,6 +57,7 @@
#include "storage/smgr.h"
#include "tcop/tcopprot.h"
#include "utils/guc.h"
#include "utils/memutils.h"
/*----------
......@@ -153,6 +154,8 @@ static void ReqShutdownHandler(SIGNAL_ARGS);
void
BackgroundWriterMain(void)
{
sigjmp_buf local_sigjmp_buf;
Assert(BgWriterShmem != NULL);
BgWriterShmem->bgwriter_pid = MyProcPid;
am_bg_writer = true;
......@@ -201,19 +204,19 @@ BackgroundWriterMain(void)
/*
* If an exception is encountered, processing resumes here.
*
* See notes in postgres.c about the design of this coding.
*/
if (sigsetjmp(Warn_restart, 1) != 0)
if (sigsetjmp(local_sigjmp_buf, 1) != 0)
{
/*
* Make sure we're not interrupted while cleaning up. Also forget
* any pending QueryCancel request, since we're aborting anyway.
* Force InterruptHoldoffCount to a known state in case we
* ereport'd from inside a holdoff section.
*/
ImmediateInterruptOK = false;
QueryCancelPending = false;
InterruptHoldoffCount = 1;
CritSectionCount = 0; /* should be unnecessary, but... */
/* Since not using PG_TRY, must reset error stack by hand */
error_context_stack = NULL;
/* Prevent interrupts while cleaning up */
HOLD_INTERRUPTS();
/* Report the error to the server log */
EmitErrorReport();
/*
* These operations are really just a minimal subset of
......@@ -224,12 +227,6 @@ BackgroundWriterMain(void)
AbortBufferIO();
UnlockBuffers();
/*
* Clear flag to indicate that we got out of error recovery mode
* successfully. (Flag was set in elog.c before longjmp().)
*/
InError = false;
/* Warn any waiting backends that the checkpoint failed. */
if (ckpt_active)
{
......@@ -242,8 +239,13 @@ BackgroundWriterMain(void)
}
/*
* Exit interrupt holdoff section we implicitly established above.
* Now return to normal top-level context and clear ErrorContext
* for next time.
*/
MemoryContextSwitchTo(TopMemoryContext);
FlushErrorState();
/* Now we can allow interrupts again */
RESUME_INTERRUPTS();
/*
......@@ -255,7 +257,8 @@ BackgroundWriterMain(void)
pg_usleep(1000000L);
}
Warn_restart_ready = true; /* we can now handle ereport(ERROR) */
/* We can now handle ereport(ERROR) */
PG_exception_stack = &local_sigjmp_buf;
/*
* Unblock signals (they were blocked when the postmaster forked us)
......
......@@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.416 2004/07/27 01:46:03 tgl Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.417 2004/07/31 00:45:33 tgl Exp $
*
* NOTES
*
......@@ -73,7 +73,6 @@
#include <ctype.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/param.h>
#include <netinet/in.h>
......@@ -3226,6 +3225,11 @@ StartChildProcess(int xlop)
/* Lose the postmaster's on-exit routines and port connections */
on_exit_reset();
/* Release postmaster's working memory context */
MemoryContextSwitchTo(TopMemoryContext);
MemoryContextDelete(PostmasterContext);
PostmasterContext = NULL;
BootstrapMain(ac, av);
ExitPostmaster(0);
}
......
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.103 2004/07/28 14:23:29 tgl Exp $
* $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.104 2004/07/31 00:45:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -54,6 +54,8 @@ open_lo_relation(void)
/* Arrange for the top xact to own these relation references */
currentOwner = CurrentResourceOwner;
PG_TRY();
{
CurrentResourceOwner = TopTransactionResourceOwner;
/* Use RowExclusiveLock since we might either read or write */
......@@ -61,7 +63,14 @@ open_lo_relation(void)
lo_heap_r = heap_openr(LargeObjectRelationName, RowExclusiveLock);
if (lo_index_r == NULL)
lo_index_r = index_openr(LargeObjectLOidPNIndex);
}
PG_CATCH();
{
/* Ensure CurrentResourceOwner is restored on error */
CurrentResourceOwner = currentOwner;
PG_RE_THROW();
}
PG_END_TRY();
CurrentResourceOwner = currentOwner;
}
......@@ -82,13 +91,22 @@ close_lo_relation(bool isCommit)
ResourceOwner currentOwner;
currentOwner = CurrentResourceOwner;
PG_TRY();
{
CurrentResourceOwner = TopTransactionResourceOwner;
if (lo_index_r)
index_close(lo_index_r);
if (lo_heap_r)
heap_close(lo_heap_r, NoLock);
}
PG_CATCH();
{
/* Ensure CurrentResourceOwner is restored on error */
CurrentResourceOwner = currentOwner;
PG_RE_THROW();
}
PG_END_TRY();
CurrentResourceOwner = currentOwner;
}
lo_heap_r = NULL;
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.426 2004/07/28 22:05:46 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.427 2004/07/31 00:45:36 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
......@@ -23,7 +23,6 @@
#include <signal.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <errno.h>
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
......@@ -77,12 +76,6 @@ const char *debug_query_string; /* for pgmonitor and
/* Note: whereToSendOutput is initialized for the bootstrap/standalone case */
CommandDest whereToSendOutput = Debug;
/* note: these declarations had better match tcopprot.h */
sigjmp_buf Warn_restart;
bool Warn_restart_ready = false;
bool InError = false;
/* flag for logging end of session */
bool Log_disconnections = false;
......@@ -1876,7 +1869,7 @@ quickdie(SIGNAL_ARGS)
/*
* Ideally this should be ereport(FATAL), but then we'd not get
* control back (perhaps could fix by doing local sigsetjmp?)
* control back...
*/
ereport(WARNING,
(errcode(ERRCODE_CRASH_SHUTDOWN),
......@@ -1962,10 +1955,9 @@ StatementCancelHandler(SIGNAL_ARGS)
int save_errno = errno;
/*
* Don't joggle the elbow of proc_exit, nor an already-in-progress
* abort
* Don't joggle the elbow of proc_exit
*/
if (!proc_exit_inprogress && !InError)
if (!proc_exit_inprogress)
{
InterruptPending = true;
QueryCancelPending = true;
......@@ -2148,7 +2140,6 @@ usage(const char *progname)
}
/* ----------------------------------------------------------------
* PostgresMain
* postgres main loop -- all backends, interactive or otherwise start here
......@@ -2175,6 +2166,7 @@ PostgresMain(int argc, char *argv[], const char *username)
int firstchar;
char stack_base;
StringInfoData input_message;
sigjmp_buf local_sigjmp_buf;
volatile bool send_rfq = true;
/*
......@@ -2772,50 +2764,61 @@ PostgresMain(int argc, char *argv[], const char *username)
*
* If an exception is encountered, processing resumes here so we abort
* the current transaction and start a new one.
*
* You might wonder why this isn't coded as an infinite loop around
* a PG_TRY construct. The reason is that this is the bottom of the
* exception stack, and so with PG_TRY there would be no exception
* handler in force at all during the CATCH part. By leaving the
* outermost setjmp always active, we have at least some chance of
* recovering from an error during error recovery. (If we get into
* an infinite loop thereby, it will soon be stopped by overflow of
* elog.c's internal state stack.)
*/
if (sigsetjmp(Warn_restart, 1) != 0)
if (sigsetjmp(local_sigjmp_buf, 1) != 0)
{
/*
* NOTE: if you are tempted to add more code in this if-block,
* consider the probability that it should be in
* AbortTransaction() instead.
*
* Make sure we're not interrupted while cleaning up. Also forget
* any pending QueryCancel request, since we're aborting anyway.
* Force InterruptHoldoffCount to a known state in case we
* ereport'd from inside a holdoff section.
* consider the high probability that it should be in
* AbortTransaction() instead. The only stuff done directly here
* should be stuff that is guaranteed to apply *only* for outer-level
* error recovery, such as adjusting the FE/BE protocol status.
*/
/* Since not using PG_TRY, must reset error stack by hand */
error_context_stack = NULL;
/* Prevent interrupts while cleaning up */
HOLD_INTERRUPTS();
/*
* Forget any pending QueryCancel request, since we're returning
* to the idle loop anyway, and cancel the statement timer if running.
*/
ImmediateInterruptOK = false;
QueryCancelPending = false;
InterruptHoldoffCount = 1;
CritSectionCount = 0; /* should be unnecessary, but... */
disable_sig_alarm(true);
QueryCancelPending = false; /* again in case timeout occurred */
/*
* Turn off these interrupts too. This is only needed here and not
* in other exception-catching places since these interrupts are
* only enabled while we wait for client input.
*/
DisableNotifyInterrupt();
DisableCatchupInterrupt();
debug_query_string = NULL;
/* Report the error to the client and/or server log */
EmitErrorReport();
/*
* If there's an active portal, mark it as failed
* Make sure debug_query_string gets reset before we possibly clobber
* the storage it points at.
*/
if (ActivePortal)
ActivePortal->status = PORTAL_FAILED;
debug_query_string = NULL;
/*
* Make sure we are in a valid memory context during recovery.
*
* We use ErrorContext in hopes that it will have some free space
* even if we're otherwise up against it...
* Abort the current transaction in order to recover.
*/
MemoryContextSwitchTo(ErrorContext);
/* Make sure we are using a sane ResourceOwner, too */
CurrentResourceOwner = CurTransactionResourceOwner;
/* Do the recovery */
ereport(DEBUG2,
(errmsg_internal("AbortCurrentTransaction")));
AbortCurrentTransaction();
/*
......@@ -2823,23 +2826,9 @@ PostgresMain(int argc, char *argv[], const char *username)
* for next time.
*/
MemoryContextSwitchTo(TopMemoryContext);
MemoryContextResetAndDeleteChildren(ErrorContext);
ActivePortal = NULL;
PortalContext = NULL;
FlushErrorState();
QueryContext = NULL;
/*
* Clear flag to indicate that we got out of error recovery mode
* successfully. (Flag was set in elog.c before longjmp().)
*/
InError = false;
xact_started = false;
/*
* Clear flag that causes accounting for cost based vacuum.
*/
VacuumCostActive = false;
/*
* If we were handling an extended-query-protocol message,
* initiate skip till next Sync. This also causes us not to issue
......@@ -2848,13 +2837,15 @@ PostgresMain(int argc, char *argv[], const char *username)
if (doing_extended_query_message)
ignore_till_sync = true;
/*
* Exit interrupt holdoff section we implicitly established above.
*/
/* We don't have a transaction command open anymore */
xact_started = false;
/* Now we can allow interrupts again */
RESUME_INTERRUPTS();
}
Warn_restart_ready = true; /* we can now handle ereport(ERROR) */
/* We can now handle ereport(ERROR) */
PG_exception_stack = &local_sigjmp_buf;
PG_SETMASK(&UnBlockSig);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.81 2004/07/17 03:29:00 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.82 2004/07/31 00:45:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -246,13 +246,15 @@ PortalStart(Portal portal, ParamListInfo params)
AssertState(portal->status == PORTAL_NEW); /* else extra PortalStart */
/*
* Set global portal context pointers. (Should we set QueryContext?)
* Set up global portal context pointers. (Should we set QueryContext?)
*/
saveActivePortal = ActivePortal;
ActivePortal = portal;
saveResourceOwner = CurrentResourceOwner;
CurrentResourceOwner = portal->resowner;
savePortalContext = PortalContext;
PG_TRY();
{
ActivePortal = portal;
CurrentResourceOwner = portal->resowner;
PortalContext = PortalGetHeapMemory(portal);
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
......@@ -334,6 +336,20 @@ PortalStart(Portal portal, ParamListInfo params)
portal->tupDesc = NULL;
break;
}
}
PG_CATCH();
{
/* Uncaught error while executing portal: mark it dead */
portal->status = PORTAL_FAILED;
/* Restore global vars and propagate error */
ActivePortal = saveActivePortal;
CurrentResourceOwner = saveResourceOwner;
PortalContext = savePortalContext;
PG_RE_THROW();
}
PG_END_TRY();
MemoryContextSwitchTo(oldContext);
......@@ -449,15 +465,17 @@ PortalRun(Portal portal, long count,
portal->status = PORTAL_ACTIVE;
/*
* Set global portal context pointers.
* Set up global portal context pointers.
*/
saveActivePortal = ActivePortal;
ActivePortal = portal;
saveResourceOwner = CurrentResourceOwner;
CurrentResourceOwner = portal->resowner;
savePortalContext = PortalContext;
PortalContext = PortalGetHeapMemory(portal);
saveQueryContext = QueryContext;
PG_TRY();
{
ActivePortal = portal;
CurrentResourceOwner = portal->resowner;
PortalContext = PortalGetHeapMemory(portal);
QueryContext = portal->queryContext;
oldContext = MemoryContextSwitchTo(PortalContext);
......@@ -534,6 +552,21 @@ PortalRun(Portal portal, long count,
result = false; /* keep compiler quiet */
break;
}
}
PG_CATCH();
{
/* Uncaught error while executing portal: mark it dead */
portal->status = PORTAL_FAILED;
/* Restore global vars and propagate error */
ActivePortal = saveActivePortal;
CurrentResourceOwner = saveResourceOwner;
PortalContext = savePortalContext;
QueryContext = saveQueryContext;
PG_RE_THROW();
}
PG_END_TRY();
MemoryContextSwitchTo(oldContext);
......@@ -970,15 +1003,17 @@ PortalRunFetch(Portal portal,
portal->status = PORTAL_ACTIVE;
/*
* Set global portal context pointers.
* Set up global portal context pointers.
*/
saveActivePortal = ActivePortal;
ActivePortal = portal;
saveResourceOwner = CurrentResourceOwner;
CurrentResourceOwner = portal->resowner;
savePortalContext = PortalContext;
PortalContext = PortalGetHeapMemory(portal);
saveQueryContext = QueryContext;
PG_TRY();
{
ActivePortal = portal;
CurrentResourceOwner = portal->resowner;
PortalContext = PortalGetHeapMemory(portal);
QueryContext = portal->queryContext;
oldContext = MemoryContextSwitchTo(PortalContext);
......@@ -994,6 +1029,21 @@ PortalRunFetch(Portal portal,
result = 0; /* keep compiler quiet */
break;
}
}
PG_CATCH();
{
/* Uncaught error while executing portal: mark it dead */
portal->status = PORTAL_FAILED;
/* Restore global vars and propagate error */
ActivePortal = saveActivePortal;
CurrentResourceOwner = saveResourceOwner;
PortalContext = savePortalContext;
QueryContext = saveQueryContext;
PG_RE_THROW();
}
PG_END_TRY();
MemoryContextSwitchTo(oldContext);
......
This diff is collapsed.
$PostgreSQL: pgsql/src/backend/utils/resowner/README,v 1.1 2004/07/17 03:30:10 tgl Exp $
$PostgreSQL: pgsql/src/backend/utils/resowner/README,v 1.2 2004/07/31 00:45:40 tgl Exp $
Notes about resource owners
---------------------------
......@@ -72,3 +72,7 @@ CurrentResourceOwner must point to the same resource owner that was current
when the buffer, lock, or cache reference was acquired. It would be possible
to relax this restriction given additional bookkeeping effort, but at present
there seems no need.
Code that transiently changes CurrentResourceOwner should generally use a
PG_TRY construct to ensure that the previous value of CurrentResourceOwner
is restored if control is lost during an error exit.
......@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.1 2004/07/17 03:30:10 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.2 2004/07/31 00:45:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -99,6 +99,13 @@ typedef struct ResourceReleaseCallbackItem
static ResourceReleaseCallbackItem *ResourceRelease_callbacks = NULL;
/* Internal routines */
static void ResourceOwnerReleaseInternal(ResourceOwner owner,
ResourceReleasePhase phase,
bool isCommit,
bool isTopLevel);
/*****************************************************************************
* EXPORTED ROUTINES *
*****************************************************************************/
......@@ -161,6 +168,29 @@ ResourceOwnerRelease(ResourceOwner owner,
ResourceReleasePhase phase,
bool isCommit,
bool isTopLevel)
{
/* Rather than PG_TRY at every level of recursion, set it up once */
ResourceOwner save;
save = CurrentResourceOwner;
PG_TRY();
{
ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
}
PG_CATCH();
{
CurrentResourceOwner = save;
PG_RE_THROW();
}
PG_END_TRY();
CurrentResourceOwner = save;
}
static void
ResourceOwnerReleaseInternal(ResourceOwner owner,
ResourceReleasePhase phase,
bool isCommit,
bool isTopLevel)
{
ResourceOwner child;
ResourceOwner save;
......@@ -168,11 +198,12 @@ ResourceOwnerRelease(ResourceOwner owner,
/* Recurse to handle descendants */
for (child = owner->firstchild; child != NULL; child = child->nextchild)
ResourceOwnerRelease(child, phase, isCommit, isTopLevel);
ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);
/*
* Make CurrentResourceOwner point to me, so that ReleaseBuffer etc
* don't get confused.
* don't get confused. We needn't PG_TRY here because the outermost
* level will fix it on error abort.
*/
save = CurrentResourceOwner;
CurrentResourceOwner = owner;
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.68 2004/07/28 22:05:47 tgl Exp $
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.69 2004/07/31 00:45:41 tgl Exp $
*
* OLD COMMENTS
* This file was created so that other c files could get the two
......@@ -19,17 +19,12 @@
#ifndef TCOPPROT_H
#define TCOPPROT_H
#include <setjmp.h>
#include "executor/execdesc.h"
#include "nodes/params.h"
#include "tcop/dest.h"
#include "utils/guc.h"
extern DLLIMPORT sigjmp_buf Warn_restart;
extern bool Warn_restart_ready;
extern bool InError;
extern CommandDest whereToSendOutput;
extern DLLIMPORT const char *debug_query_string;
extern int max_stack_depth;
......
......@@ -7,13 +7,15 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.70 2004/07/06 19:51:59 momjian Exp $
* $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.71 2004/07/31 00:45:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef ELOG_H
#define ELOG_H
#include <setjmp.h>
/* Error level codes */
#define DEBUG5 10 /* Debugging messages, in categories of
* decreasing detail. */
......@@ -168,6 +170,93 @@ typedef struct ErrorContextCallback
extern DLLIMPORT ErrorContextCallback *error_context_stack;
/*----------
* API for catching ereport(ERROR) exits. Use these macros like so:
*
* PG_TRY();
* {
* ... code that might throw ereport(ERROR) ...
* }
* PG_CATCH();
* {
* ... error recovery code ...
* }
* PG_END_TRY();
*
* (The braces are not actually necessary, but are recommended so that
* pg_indent will indent the construct nicely.) The error recovery code
* can optionally do PG_RE_THROW() to propagate the same error outwards.
*
* Note: while the system will correctly propagate any new ereport(ERROR)
* occurring in the recovery section, there is a small limit on the number
* of levels this will work for. It's best to keep the error recovery
* section simple enough that it can't generate any new errors, at least
* not before popping the error stack.
*----------
*/
#define PG_TRY() \
do { \
sigjmp_buf *save_exception_stack = PG_exception_stack; \
ErrorContextCallback *save_context_stack = error_context_stack; \
sigjmp_buf local_sigjmp_buf; \
if (sigsetjmp(local_sigjmp_buf, 1) == 0) \
{ \
PG_exception_stack = &local_sigjmp_buf
#define PG_CATCH() \
} \
else \
{ \
PG_exception_stack = save_exception_stack; \
error_context_stack = save_context_stack
#define PG_END_TRY() \
} \
PG_exception_stack = save_exception_stack; \
error_context_stack = save_context_stack; \
} while (0)
#define PG_RE_THROW() \
siglongjmp(*PG_exception_stack, 1)
extern DLLIMPORT sigjmp_buf *PG_exception_stack;
/* Stuff that error handlers might want to use */
/*
* ErrorData holds the data accumulated during any one ereport() cycle.
* Any non-NULL pointers must point to palloc'd data.
* (The const pointers are an exception; we assume they point at non-freeable
* constant strings.)
*/
typedef struct ErrorData
{
int elevel; /* error level */
bool output_to_server; /* will report to server log? */
bool output_to_client; /* will report to client? */
bool show_funcname; /* true to force funcname inclusion */
const char *filename; /* __FILE__ of ereport() call */
int lineno; /* __LINE__ of ereport() call */
const char *funcname; /* __func__ of ereport() call */
int sqlerrcode; /* encoded ERRSTATE */
char *message; /* primary error message */
char *detail; /* detail error message */
char *hint; /* hint message */
char *context; /* context message */
int cursorpos; /* cursor index into query string */
int internalpos; /* cursor index into internalquery */
char *internalquery; /* text of internally-generated query */
int saved_errno; /* errno at entry */
} ErrorData;
extern void EmitErrorReport(void);
extern ErrorData *CopyErrorData(void);
extern void FreeErrorData(ErrorData *edata);
extern void FlushErrorState(void);
extern void ReThrowError(ErrorData *edata);
/* GUC-configurable parameters */
typedef enum
......
......@@ -33,16 +33,15 @@
* ENHANCEMENTS, OR MODIFICATIONS.
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.47 2004/07/21 20:45:54 momjian Exp $
* $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.48 2004/07/31 00:45:44 tgl Exp $
*
**********************************************************************/
#include "postgres.h"
/* system stuff */
#include <unistd.h>
#include <fcntl.h>
#include <setjmp.h>
#include <unistd.h>
/* postgreSQL stuff */
#include "access/heapam.h"
......
......@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.77 2004/06/06 00:41:28 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.78 2004/07/31 00:45:46 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
......@@ -38,7 +38,6 @@
#include "plpgsql.h"
#include <ctype.h>
#include <setjmp.h>
#include "pl.tab.h"
......
......@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.107 2004/06/09 19:08:19 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.108 2004/07/31 00:45:46 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
......@@ -39,7 +39,6 @@
#include "pl.tab.h"
#include <ctype.h>
#include <setjmp.h>
#include "access/heapam.h"
#include "catalog/pg_proc.h"
......
This diff is collapsed.
/* $PostgreSQL: pgsql/src/pl/plpython/plpython.h,v 1.9 2003/11/29 19:52:13 pgsql Exp $ */
#ifndef PLPYTHON_H
#define PLPYTHON_H
#define DEBUG_EXC 0
#define DEBUG_LEVEL 0
#define DECLARE_N_EXC(N) int rv_##N; sigjmp_buf buf_##N
#define TRAP_N_EXC(N) ((rv_##N = sigsetjmp(Warn_restart, 1)) != 0)
#if !DEBUG_EXC
#define RESTORE_N_EXC(N) memcpy(&Warn_restart, &(buf_##N), sizeof(sigjmp_buf))
#define SAVE_N_EXC(N) memcpy(&(buf_##N), &Warn_restart, sizeof(sigjmp_buf))
#define RERAISE_N_EXC(N) siglongjmp(Warn_restart, rv_##N)
#define RAISE_EXC(V) siglongjmp(Warn_restart, (V))
#else
#define RESTORE_N_EXC(N) do { \
elog(WARNING, "exception (%d,%d) restore at %s:%d",\
PLy_call_level, exc_save_calls, __FUNCTION__, (__LINE__));\
exc_save_calls -= 1; \
memcpy(&Warn_restart, &(buf_##N), sizeof(sigjmp_buf)); } while (0)
#define SAVE_N_EXC(N) do { \
exc_save_calls += 1; \
elog(WARNING, "exception (%d,%d) save at %s:%d", \
PLy_call_level, exc_save_calls, __FUNCTION__, (__LINE__)); \
memcpy(&(buf_##N), &Warn_restart, sizeof(sigjmp_buf)); } while (0)
#define RERAISE_N_EXC(N) do { \
elog(WARNING, "exception (%d,%d) reraise at %s:%d", \
PLy_call_level, exc_save_calls, __FUNCTION__, (__LINE__)); \
siglongjmp(Warn_restart, rv_##N); } while (0)
#define RAISE_EXC(V) do { \
elog(WARNING, "exception (%d,%d) raise at %s:%d", \
PLy_call_level, exc_save_calls, __FUNCTION__, (__LINE__)); \
siglongjmp(Warn_restart, (V)); } while (0)
#endif
#define DECLARE_EXC() DECLARE_N_EXC(save_restart)
#define SAVE_EXC() SAVE_N_EXC(save_restart)
#define RERAISE_EXC() RERAISE_N_EXC(save_restart)
#define RESTORE_EXC() RESTORE_N_EXC(save_restart)
#define TRAP_EXC() TRAP_N_EXC(save_restart)
#if DEBUG_LEVEL
#define CALL_LEVEL_INC() do { PLy_call_level += 1; \
elog(DEBUG4, "level: %d", PLy_call_level); } while (0)
#define CALL_LEVEL_DEC() do { elog(DEBUG4, "level: %d", PLy_call_level); \
PLy_call_level -= 1; } while (0)
#else
#define CALL_LEVEL_INC() do { PLy_call_level += 1; } while (0)
#define CALL_LEVEL_DEC() do { PLy_call_level -= 1; } while (0)
#endif
/* temporary debugging macros
*/
#if DEBUG_LEVEL
#define enter() elog(DEBUG4, "Enter(%d): %s", func_enter_calls++,__FUNCTION__)
#define leave() elog(DEBUG4, "Leave(%d): %s", func_leave_calls++,__FUNCTION__)
#define mark() elog(DEBUG4, "Mark: %s:%d", __FUNCTION__, __LINE__);
#define refc(O) elog(DEBUG4, "Ref<%p>:<%d>:%s:%d", (O), (((O) == NULL) ? -1 : (O)->ob_refcnt), __FUNCTION__, __LINE__)
#else
#define enter()
#define leave()
#define mark()
#define refc(O)
#endif
#endif /* PLPYTHON_H */
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment