From a393fbf93763709f90ba1f968e50a35bd4cabcfb Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 31 Jul 2004 00:45:57 +0000 Subject: 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. --- src/backend/tcop/postgres.c | 109 ++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 59 deletions(-) (limited to 'src/backend/tcop/postgres.c') diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index a8a7e0197ae..89ee40a5321 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -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 #include #include -#include #if HAVE_SYS_SELECT_H #include #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); -- cgit v1.2.3