diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 39887015930a583e2e8260bf55a22b24e142eb27..2a4f9a6f50321dedd77d50640755da114b7adc55 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -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.
 	 *
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 4a05b40eea8d3f933206d41e4426b9e105be2266..8307e9b47d25dc58ccb24492ccf5be0dc488bb7d 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -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.
  */
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index b176a6c0c7bafb0720e52acd7d351deabd352696..509d9e0dfa12fb039b6417828e3a11b814fd4dd5 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -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;
-			CurrentResourceOwner = portal->resowner;
-			ExecutorEnd(queryDesc);
+			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,86 +327,95 @@ 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;
-	QueryContext = portal->queryContext;
+	PG_TRY();
+	{
+		ActivePortal = portal;
+		CurrentResourceOwner = portal->resowner;
+		PortalContext = PortalGetHeapMemory(portal);
+		QueryContext = portal->queryContext;
+
+		MemoryContextSwitchTo(PortalContext);
+
+		/*
+		 * Rewind the executor: we need to store the entire result set in the
+		 * tuplestore, so that subsequent backward FETCHs can be processed.
+		 */
+		ExecutorRewind(queryDesc);
+
+		/* Change the destination to output to the tuplestore */
+		queryDesc->dest = CreateDestReceiver(Tuplestore, portal);
+
+		/* Fetch the result set into the tuplestore */
+		ExecutorRun(queryDesc, ForwardScanDirection, 0L);
+
+		(*queryDesc->dest->rDestroy) (queryDesc->dest);
+		queryDesc->dest = NULL;
+
+		/*
+		 * Now shut down the inner executor.
+		 */
+		portal->queryDesc = NULL;	/* prevent double shutdown */
+		ExecutorEnd(queryDesc);
+
+		/*
+		 * Reset the position in the result set: ideally, this could be
+		 * implemented by just skipping straight to the tuple # that we need
+		 * to be at, but the tuplestore API doesn't support that. So we start
+		 * at the beginning of the tuplestore and iterate through it until we
+		 * reach where we need to be.  FIXME someday?
+		 */
+		MemoryContextSwitchTo(portal->holdContext);
+
+		if (!portal->atEnd)
+		{
+			long		store_pos;
 
-	MemoryContextSwitchTo(PortalContext);
+			if (portal->posOverflow)	/* oops, cannot trust portalPos */
+				ereport(ERROR,
+						(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						 errmsg("could not reposition held cursor")));
 
-	/*
-	 * Rewind the executor: we need to store the entire result set in the
-	 * tuplestore, so that subsequent backward FETCHs can be processed.
-	 */
-	ExecutorRewind(queryDesc);
+			tuplestore_rescan(portal->holdStore);
 
-	/* Change the destination to output to the tuplestore */
-	queryDesc->dest = CreateDestReceiver(Tuplestore, portal);
+			for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
+			{
+				HeapTuple	tup;
+				bool		should_free;
 
-	/* Fetch the result set into the tuplestore */
-	ExecutorRun(queryDesc, ForwardScanDirection, 0L);
+				tup = tuplestore_gettuple(portal->holdStore, true,
+										  &should_free);
 
-	(*queryDesc->dest->rDestroy) (queryDesc->dest);
-	queryDesc->dest = NULL;
+				if (tup == NULL)
+					elog(ERROR, "unexpected end of tuple stream");
 
-	/*
-	 * Now shut down the inner executor.
-	 */
-	portal->queryDesc = NULL;	/* prevent double shutdown */
-	ExecutorEnd(queryDesc);
-
-	/*
-	 * Reset the position in the result set: ideally, this could be
-	 * implemented by just skipping straight to the tuple # that we need
-	 * to be at, but the tuplestore API doesn't support that. So we start
-	 * at the beginning of the tuplestore and iterate through it until we
-	 * reach where we need to be.  FIXME someday?
-	 */
-	MemoryContextSwitchTo(portal->holdContext);
-
-	if (!portal->atEnd)
+				if (should_free)
+					pfree(tup);
+			}
+		}
+	}
+	PG_CATCH();
 	{
-		long		store_pos;
-
-		if (portal->posOverflow)	/* oops, cannot trust portalPos */
-			ereport(ERROR,
-					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-					 errmsg("could not reposition held cursor")));
-
-		tuplestore_rescan(portal->holdStore);
-
-		for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
-		{
-			HeapTuple	tup;
-			bool		should_free;
-
-			tup = tuplestore_gettuple(portal->holdStore, true,
-									  &should_free);
+		/* Uncaught error while executing portal: mark it dead */
+		portal->status = PORTAL_FAILED;
 
-			if (tup == NULL)
-				elog(ERROR, "unexpected end of tuple stream");
+		/* Restore global vars and propagate error */
+		ActivePortal = saveActivePortal;
+		CurrentResourceOwner = saveResourceOwner;
+		PortalContext = savePortalContext;
+		QueryContext = saveQueryContext;
 
-			if (should_free)
-				pfree(tup);
-		}
+		PG_RE_THROW();
 	}
+	PG_END_TRY();
 
 	MemoryContextSwitchTo(oldcxt);
 
-	/*
-	 * 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));
-
 	/* 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));
 }
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index dfc03ea461a75ec61b88da30a950cb3c0190125f..23d7996cf5372e10bf87aec765bb0d718f7baa15 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -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,57 +372,76 @@ vacuum(VacuumStmt *vacstmt)
 		CommitTransactionCommand();
 	}
 
-	/*
-	 * Loop to process each selected relation.
-	 */
-	foreach(cur, relations)
+	/* Turn vacuum cost accounting on or off */
+	PG_TRY();
 	{
-		Oid			relid = lfirst_oid(cur);
+		ListCell   *cur;
 
-		if (vacstmt->vacuum)
-		{
-			if (!vacuum_rel(relid, vacstmt, RELKIND_RELATION))
-				all_rels = false;		/* forget about updating dbstats */
-		}
-		if (vacstmt->analyze)
+		VacuumCostActive = (VacuumCostNaptime > 0);
+		VacuumCostBalance = 0;
+
+		/*
+		 * Loop to process each selected relation.
+		 */
+		foreach(cur, relations)
 		{
-			MemoryContext old_context = NULL;
+			Oid			relid = lfirst_oid(cur);
 
-			/*
-			 * If using separate xacts, start one for analyze. Otherwise,
-			 * we can use the outer transaction, but we still need to call
-			 * analyze_rel in a memory context that will be cleaned up on
-			 * return (else we leak memory while processing multiple
-			 * tables).
-			 */
-			if (use_own_xacts)
+			if (vacstmt->vacuum)
 			{
-				StartTransactionCommand();
-				SetQuerySnapshot();		/* might be needed for functions
-										 * in indexes */
+				if (!vacuum_rel(relid, vacstmt, RELKIND_RELATION))
+					all_rels = false; /* forget about updating dbstats */
 			}
-			else
-				old_context = MemoryContextSwitchTo(anl_context);
+			if (vacstmt->analyze)
+			{
+				MemoryContext old_context = NULL;
 
-			/*
-			 * Tell the buffer replacement strategy that vacuum is
-			 * causing the IO
-			 */
-			StrategyHintVacuum(true);
+				/*
+				 * If using separate xacts, start one for analyze. Otherwise,
+				 * we can use the outer transaction, but we still need to call
+				 * analyze_rel in a memory context that will be cleaned up on
+				 * return (else we leak memory while processing multiple
+				 * tables).
+				 */
+				if (use_own_xacts)
+				{
+					StartTransactionCommand();
+					SetQuerySnapshot();		/* might be needed for functions
+											 * in indexes */
+				}
+				else
+					old_context = MemoryContextSwitchTo(anl_context);
+
+				/*
+				 * Tell the buffer replacement strategy that vacuum is
+				 * causing the IO
+				 */
+				StrategyHintVacuum(true);
 
-			analyze_rel(relid, vacstmt);
+				analyze_rel(relid, vacstmt);
 
-			StrategyHintVacuum(false);
+				StrategyHintVacuum(false);
 
-			if (use_own_xacts)
-				CommitTransactionCommand();
-			else
-			{
-				MemoryContextSwitchTo(old_context);
-				MemoryContextResetAndDeleteChildren(anl_context);
+				if (use_own_xacts)
+					CommitTransactionCommand();
+				else
+				{
+					MemoryContextSwitchTo(old_context);
+					MemoryContextResetAndDeleteChildren(anl_context);
+				}
 			}
 		}
 	}
+	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;
 }
 
 /*
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index f0ecb13d27ab3edba1e0e78d623f1aba20d1702b..0da107f9e13811a8e017f4db8caf0d094cf043b7 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -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)
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index eace246071b354c7a4218f2d3bd1125855a6eff5..e144b6eaa93f6869971022cd5f88e8c91a2a86b0 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -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);
 	}
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index 470dcf11aa948905d120c48e3e56e2fb6354c351..97e60a2fccf17f340f824f0cb82f75d4a4b9f57d 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -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,14 +54,23 @@ open_lo_relation(void)
 
 	/* Arrange for the top xact to own these relation references */
 	currentOwner = CurrentResourceOwner;
-	CurrentResourceOwner = TopTransactionResourceOwner;
-
-	/* Use RowExclusiveLock since we might either read or write */
-	if (lo_heap_r == NULL)
-		lo_heap_r = heap_openr(LargeObjectRelationName, RowExclusiveLock);
-	if (lo_index_r == NULL)
-		lo_index_r = index_openr(LargeObjectLOidPNIndex);
+	PG_TRY();
+	{
+		CurrentResourceOwner = TopTransactionResourceOwner;
 
+		/* Use RowExclusiveLock since we might either read or write */
+		if (lo_heap_r == NULL)
+			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;
-			CurrentResourceOwner = TopTransactionResourceOwner;
-
-			if (lo_index_r)
-				index_close(lo_index_r);
-			if (lo_heap_r)
-				heap_close(lo_heap_r, NoLock);
+			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;
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index a8a7e0197ae0db85b4b5b1af48ed08db30d5afe9..89ee40a53215141c806934442709303a9242570a 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 <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);
 
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 49e2a4b008210b48d1faf26b68b56697279633b3..8973cca6d2673ab9d9db61680d36ddd7a61d5917 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -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,94 +246,110 @@ 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;
-	PortalContext = PortalGetHeapMemory(portal);
+	PG_TRY();
+	{
+		ActivePortal = portal;
+		CurrentResourceOwner = portal->resowner;
+		PortalContext = PortalGetHeapMemory(portal);
 
-	oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+		oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
 
-	/* Must remember portal param list, if any */
-	portal->portalParams = params;
+		/* Must remember portal param list, if any */
+		portal->portalParams = params;
 
-	/*
-	 * Determine the portal execution strategy
-	 */
-	portal->strategy = ChoosePortalStrategy(portal->parseTrees);
+		/*
+		 * Determine the portal execution strategy
+		 */
+		portal->strategy = ChoosePortalStrategy(portal->parseTrees);
 
-	/*
-	 * Fire her up according to the strategy
-	 */
-	switch (portal->strategy)
-	{
-		case PORTAL_ONE_SELECT:
+		/*
+		 * Fire her up according to the strategy
+		 */
+		switch (portal->strategy)
+		{
+			case PORTAL_ONE_SELECT:
 
-			/*
-			 * Must set query snapshot before starting executor.
-			 */
-			SetQuerySnapshot();
+				/*
+				 * Must set query snapshot before starting executor.
+				 */
+				SetQuerySnapshot();
 
-			/*
-			 * Create QueryDesc in portal's context; for the moment, set
-			 * the destination to None.
-			 */
-			queryDesc = CreateQueryDesc((Query *) linitial(portal->parseTrees),
-										(Plan *) linitial(portal->planTrees),
-										None_Receiver,
-										params,
-										false);
+				/*
+				 * Create QueryDesc in portal's context; for the moment, set
+				 * the destination to None.
+				 */
+				queryDesc = CreateQueryDesc((Query *) linitial(portal->parseTrees),
+											(Plan *) linitial(portal->planTrees),
+											None_Receiver,
+											params,
+											false);
 
-			/*
-			 * Call ExecStart to prepare the plan for execution
-			 */
-			ExecutorStart(queryDesc, false, false);
+				/*
+				 * Call ExecStart to prepare the plan for execution
+				 */
+				ExecutorStart(queryDesc, false, false);
 
-			/*
-			 * This tells PortalCleanup to shut down the executor
-			 */
-			portal->queryDesc = queryDesc;
+				/*
+				 * This tells PortalCleanup to shut down the executor
+				 */
+				portal->queryDesc = queryDesc;
 
-			/*
-			 * Remember tuple descriptor (computed by ExecutorStart)
-			 */
-			portal->tupDesc = queryDesc->tupDesc;
+				/*
+				 * Remember tuple descriptor (computed by ExecutorStart)
+				 */
+				portal->tupDesc = queryDesc->tupDesc;
 
-			/*
-			 * Reset cursor position data to "start of query"
-			 */
-			portal->atStart = true;
-			portal->atEnd = false;		/* allow fetches */
-			portal->portalPos = 0;
-			portal->posOverflow = false;
-			break;
+				/*
+				 * Reset cursor position data to "start of query"
+				 */
+				portal->atStart = true;
+				portal->atEnd = false;		/* allow fetches */
+				portal->portalPos = 0;
+				portal->posOverflow = false;
+				break;
 
-		case PORTAL_UTIL_SELECT:
+			case PORTAL_UTIL_SELECT:
 
-			/*
-			 * We don't set query snapshot here, because PortalRunUtility
-			 * will take care of it.
-			 */
-			portal->tupDesc =
-				UtilityTupleDescriptor(((Query *) linitial(portal->parseTrees))->utilityStmt);
+				/*
+				 * We don't set query snapshot here, because PortalRunUtility
+				 * will take care of it.
+				 */
+				portal->tupDesc =
+					UtilityTupleDescriptor(((Query *) linitial(portal->parseTrees))->utilityStmt);
 
-			/*
-			 * Reset cursor position data to "start of query"
-			 */
-			portal->atStart = true;
-			portal->atEnd = false;		/* allow fetches */
-			portal->portalPos = 0;
-			portal->posOverflow = false;
-			break;
+				/*
+				 * Reset cursor position data to "start of query"
+				 */
+				portal->atStart = true;
+				portal->atEnd = false;		/* allow fetches */
+				portal->portalPos = 0;
+				portal->posOverflow = false;
+				break;
 
-		case PORTAL_MULTI_QUERY:
-			/* Need do nothing now */
-			portal->tupDesc = NULL;
-			break;
+			case PORTAL_MULTI_QUERY:
+				/* Need do nothing now */
+				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,91 +465,108 @@ 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;
-	QueryContext = portal->queryContext;
+	PG_TRY();
+	{
+		ActivePortal = portal;
+		CurrentResourceOwner = portal->resowner;
+		PortalContext = PortalGetHeapMemory(portal);
+		QueryContext = portal->queryContext;
 
-	oldContext = MemoryContextSwitchTo(PortalContext);
+		oldContext = MemoryContextSwitchTo(PortalContext);
 
-	switch (portal->strategy)
-	{
-		case PORTAL_ONE_SELECT:
-			(void) PortalRunSelect(portal, true, count, dest);
-			/* we know the query is supposed to set the tag */
-			if (completionTag && portal->commandTag)
-				strcpy(completionTag, portal->commandTag);
+		switch (portal->strategy)
+		{
+			case PORTAL_ONE_SELECT:
+				(void) PortalRunSelect(portal, true, count, dest);
+				/* we know the query is supposed to set the tag */
+				if (completionTag && portal->commandTag)
+					strcpy(completionTag, portal->commandTag);
 
-			/* Mark portal not active */
-			portal->status = PORTAL_READY;
+				/* Mark portal not active */
+				portal->status = PORTAL_READY;
 
-			/*
-			 * Since it's a forward fetch, say DONE iff atEnd is now true.
-			 */
-			result = portal->atEnd;
-			break;
+				/*
+				 * Since it's a forward fetch, say DONE iff atEnd is now true.
+				 */
+				result = portal->atEnd;
+				break;
 
-		case PORTAL_UTIL_SELECT:
+			case PORTAL_UTIL_SELECT:
 
-			/*
-			 * If we have not yet run the utility statement, do so,
-			 * storing its results in the portal's tuplestore.
-			 */
-			if (!portal->portalUtilReady)
-			{
-				DestReceiver *treceiver;
-
-				PortalCreateHoldStore(portal);
-				treceiver = CreateDestReceiver(Tuplestore, portal);
-				PortalRunUtility(portal, linitial(portal->parseTrees),
-								 treceiver, NULL);
-				(*treceiver->rDestroy) (treceiver);
-				portal->portalUtilReady = true;
-			}
+				/*
+				 * If we have not yet run the utility statement, do so,
+				 * storing its results in the portal's tuplestore.
+				 */
+				if (!portal->portalUtilReady)
+				{
+					DestReceiver *treceiver;
+
+					PortalCreateHoldStore(portal);
+					treceiver = CreateDestReceiver(Tuplestore, portal);
+					PortalRunUtility(portal, linitial(portal->parseTrees),
+									 treceiver, NULL);
+					(*treceiver->rDestroy) (treceiver);
+					portal->portalUtilReady = true;
+				}
 
-			/*
-			 * Now fetch desired portion of results.
-			 */
-			(void) PortalRunSelect(portal, true, count, dest);
+				/*
+				 * Now fetch desired portion of results.
+				 */
+				(void) PortalRunSelect(portal, true, count, dest);
 
-			/*
-			 * We know the query is supposed to set the tag; we assume
-			 * only the default tag is needed.
-			 */
-			if (completionTag && portal->commandTag)
-				strcpy(completionTag, portal->commandTag);
+				/*
+				 * We know the query is supposed to set the tag; we assume
+				 * only the default tag is needed.
+				 */
+				if (completionTag && portal->commandTag)
+					strcpy(completionTag, portal->commandTag);
 
-			/* Mark portal not active */
-			portal->status = PORTAL_READY;
+				/* Mark portal not active */
+				portal->status = PORTAL_READY;
 
-			/*
-			 * Since it's a forward fetch, say DONE iff atEnd is now true.
-			 */
-			result = portal->atEnd;
-			break;
+				/*
+				 * Since it's a forward fetch, say DONE iff atEnd is now true.
+				 */
+				result = portal->atEnd;
+				break;
 
-		case PORTAL_MULTI_QUERY:
-			PortalRunMulti(portal, dest, altdest, completionTag);
+			case PORTAL_MULTI_QUERY:
+				PortalRunMulti(portal, dest, altdest, completionTag);
 
-			/* Prevent portal's commands from being re-executed */
-			portal->status = PORTAL_DONE;
+				/* Prevent portal's commands from being re-executed */
+				portal->status = PORTAL_DONE;
 
-			/* Always complete at end of RunMulti */
-			result = true;
-			break;
+				/* Always complete at end of RunMulti */
+				result = true;
+				break;
 
-		default:
-			elog(ERROR, "unrecognized portal strategy: %d",
-				 (int) portal->strategy);
-			result = false;		/* keep compiler quiet */
-			break;
+			default:
+				elog(ERROR, "unrecognized portal strategy: %d",
+					 (int) portal->strategy);
+				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,30 +1003,47 @@ 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;
-	QueryContext = portal->queryContext;
+	PG_TRY();
+	{
+		ActivePortal = portal;
+		CurrentResourceOwner = portal->resowner;
+		PortalContext = PortalGetHeapMemory(portal);
+		QueryContext = portal->queryContext;
 
-	oldContext = MemoryContextSwitchTo(PortalContext);
+		oldContext = MemoryContextSwitchTo(PortalContext);
 
-	switch (portal->strategy)
+		switch (portal->strategy)
+		{
+			case PORTAL_ONE_SELECT:
+				result = DoPortalRunFetch(portal, fdirection, count, dest);
+				break;
+
+			default:
+				elog(ERROR, "unsupported portal strategy");
+				result = 0;			/* keep compiler quiet */
+				break;
+		}
+	}
+	PG_CATCH();
 	{
-		case PORTAL_ONE_SELECT:
-			result = DoPortalRunFetch(portal, fdirection, count, dest);
-			break;
+		/* Uncaught error while executing portal: mark it dead */
+		portal->status = PORTAL_FAILED;
 
-		default:
-			elog(ERROR, "unsupported portal strategy");
-			result = 0;			/* keep compiler quiet */
-			break;
+		/* Restore global vars and propagate error */
+		ActivePortal = saveActivePortal;
+		CurrentResourceOwner = saveResourceOwner;
+		PortalContext = savePortalContext;
+		QueryContext = saveQueryContext;
+
+		PG_RE_THROW();
 	}
+	PG_END_TRY();
 
 	MemoryContextSwitchTo(oldContext);
 
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index d2f9117059c17e62f010b7180f90fbaedef1d452..3eeee5ed8b2c996a539c73b395c651813820d20f 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -37,14 +37,13 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.143 2004/07/28 22:05:46 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.144 2004/07/31 00:45:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include <fcntl.h>
-#include <errno.h>
 #include <time.h>
 #include <unistd.h>
 #include <signal.h>
@@ -67,6 +66,8 @@
 /* Global variables */
 ErrorContextCallback *error_context_stack = NULL;
 
+sigjmp_buf *PG_exception_stack = NULL;
+
 /* GUC parameters */
 PGErrorVerbosity Log_error_verbosity = PGERROR_VERBOSE;
 char       *Log_line_prefix = NULL; /* format for extra log line info */
@@ -82,33 +83,6 @@ static void write_syslog(int level, const char *line);
 static void write_eventlog(int level, const char *line);
 #endif
 
-/*
- * ErrorData holds the data accumulated during any one ereport() cycle.
- * Any non-NULL pointers must point to palloc'd data in ErrorContext.
- * (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;
-
 /* We provide a small stack of ErrorData records for re-entrant cases */
 #define ERRORDATA_STACK_SIZE  5
 
@@ -166,7 +140,7 @@ errstart(int elevel, const char *filename, int lineno,
 
 	/*
 	 * Convert initialization errors into fatal errors. This is probably
-	 * redundant, because Warn_restart_ready won't be set anyway.
+	 * redundant, because PG_exception_stack will still be null anyway.
 	 */
 	if (elevel == ERROR && IsInitProcessingMode())
 		elevel = FATAL;
@@ -257,20 +231,13 @@ errstart(int elevel, const char *filename, int lineno,
 	}
 	if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
 	{
-		/* Wups, stack not big enough */
-		int			i;
-
-		elevel = Max(elevel, ERROR);
-
 		/*
-		 * Don't forget any FATAL/PANIC status on the stack (see comments
-		 * in errfinish)
+		 * Wups, stack not big enough.  We treat this as a PANIC condition
+		 * because it suggests an infinite loop of errors during error
+		 * recovery.
 		 */
-		for (i = 0; i < errordata_stack_depth; i++)
-			elevel = Max(elevel, errordata[i].elevel);
-		/* Clear the stack and try again */
-		errordata_stack_depth = -1;
-		ereport(elevel, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
+		errordata_stack_depth = -1;	/* make room on stack */
+		ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
 	}
 
 	/* Initialize data for this error frame */
@@ -331,23 +298,90 @@ errfinish(int dummy,...)
 		 econtext = econtext->previous)
 		(*econtext->callback) (econtext->arg);
 
-	/* Send to server log, if enabled */
-	if (edata->output_to_server)
-		send_message_to_server_log(edata);
+	/*
+	 * If the error level is ERROR or more, we are not going to return to
+	 * caller; therefore, if there is any stacked error already in
+	 * progress it will be lost.  This is more or less okay, except we do
+	 * not want to have a FATAL or PANIC error downgraded because the
+	 * reporting process was interrupted by a lower-grade error.  So check
+	 * the stack and make sure we panic if panic is warranted.
+	 */
+	if (elevel >= ERROR)
+	{
+		int			i;
+
+		for (i = 0; i <= errordata_stack_depth; i++)
+			elevel = Max(elevel, errordata[i].elevel);
+	}
+
+	/*
+	 * Check some other reasons for treating ERROR as FATAL:
+	 *
+	 * 1. we have no handler to pass the error to (implies we are in
+	 * the postmaster or in backend startup).
+	 *
+	 * 2. ExitOnAnyError mode switch is set (initdb uses this).
+	 *
+	 * 3. the error occurred after proc_exit has begun to run.	(It's
+	 * proc_exit's responsibility to see that this doesn't turn into
+	 * infinite recursion!)
+	 */
+	if (elevel == ERROR)
+	{
+		if (PG_exception_stack == NULL ||
+			ExitOnAnyError ||
+			proc_exit_inprogress)
+			elevel = FATAL;
+		else
+		{
+			/*
+			 * Otherwise we can pass the error off to the current handler.
+			 * Printing it and popping the stack is the responsibility of
+			 * the handler.
+			 *
+			 * We do some minimal cleanup before longjmp'ing so that handlers
+			 * can execute in a reasonably sane state.
+			 */
+
+			/* This is just in case the error came while waiting for input */
+			ImmediateInterruptOK = false;
+
+			/*
+			 * Reset InterruptHoldoffCount in case we ereport'd from inside an
+			 * interrupt holdoff section.  (We assume here that no handler
+			 * will itself be inside a holdoff section.  If necessary, such
+			 * a handler could save and restore InterruptHoldoffCount for
+			 * itself, but this should make life easier for most.)
+			 */
+			InterruptHoldoffCount = 0;
+
+			CritSectionCount = 0;	/* should be unnecessary, but... */
+
+			/*
+			 * Note that we leave CurrentMemoryContext set to ErrorContext.
+			 * The handler should reset it to something else soon.
+			 */
+
+			recursion_depth--;
+			PG_RE_THROW();
+		}
+	}
 
 	/*
-	 * Abort any old-style COPY OUT in progress when an error is detected.
+	 * If we are doing FATAL or PANIC, abort any old-style COPY OUT in
+	 * progress, so that we can report the message before dying.  (Without
+	 * this, pq_putmessage will refuse to send the message at all, which
+	 * is what we want for NOTICE messages, but not for fatal exits.)
 	 * This hack is necessary because of poor design of old-style copy
 	 * protocol.  Note we must do this even if client is fool enough to
-	 * have set client_min_messages above ERROR, so don't look at
+	 * have set client_min_messages above FATAL, so don't look at
 	 * output_to_client.
 	 */
-	if (elevel >= ERROR && whereToSendOutput == Remote)
+	if (elevel >= FATAL && whereToSendOutput == Remote)
 		pq_endcopyout(true);
 
-	/* Send to client, if enabled */
-	if (edata->output_to_client)
-		send_message_to_frontend(edata);
+	/* Emit the message to the right places */
+	EmitErrorReport();
 
 	/* Now free up subsidiary data attached to stack entry, and release it */
 	if (edata->message)
@@ -361,41 +395,20 @@ errfinish(int dummy,...)
 	if (edata->internalquery)
 		pfree(edata->internalquery);
 
-	MemoryContextSwitchTo(oldcontext);
-
 	errordata_stack_depth--;
+
+	/* Exit error-handling context */
+	MemoryContextSwitchTo(oldcontext);
 	recursion_depth--;
 
 	/*
-	 * If the error level is ERROR or more, we are not going to return to
-	 * caller; therefore, if there is any stacked error already in
-	 * progress it will be lost.  This is more or less okay, except we do
-	 * not want to have a FATAL or PANIC error downgraded because the
-	 * reporting process was interrupted by a lower-grade error.  So check
-	 * the stack and make sure we panic if panic is warranted.
+	 * Perform error recovery action as specified by elevel.
 	 */
-	if (elevel >= ERROR)
+	if (elevel == FATAL)
 	{
-		int			i;
-
-		for (i = 0; i <= errordata_stack_depth; i++)
-			elevel = Max(elevel, errordata[i].elevel);
-
 		/*
-		 * Also, be sure to reset the stack to empty.  We do not clear
-		 * ErrorContext here, though; PostgresMain does that later on.
+		 * For a FATAL error, we let proc_exit clean up and exit.
 		 */
-		errordata_stack_depth = -1;
-		recursion_depth = 0;
-		error_context_stack = NULL;
-	}
-
-	/*
-	 * Perform error recovery action as specified by elevel.
-	 */
-	if (elevel == ERROR || elevel == FATAL)
-	{
-		/* Prevent immediate interrupt while entering error recovery */
 		ImmediateInterruptOK = false;
 
 		/*
@@ -403,59 +416,27 @@ errfinish(int dummy,...)
 		 * disconnect on receiving it, so don't send any more to the
 		 * client.
 		 */
-		if (!Warn_restart_ready && whereToSendOutput == Remote)
+		if (PG_exception_stack == NULL && whereToSendOutput == Remote)
 			whereToSendOutput = None;
 
 		/*
-		 * For a FATAL error, we let proc_exit clean up and exit.
-		 *
-		 * There are several other cases in which we treat ERROR as FATAL and
-		 * go directly to proc_exit:
-		 *
-		 * 1. ExitOnAnyError mode switch is set (initdb uses this).
-		 *
-		 * 2. we have not yet entered the main backend loop (ie, we are in
-		 * the postmaster or in backend startup); we have noplace to
-		 * recover.
-		 *
-		 * 3. the error occurred after proc_exit has begun to run.	(It's
-		 * proc_exit's responsibility to see that this doesn't turn into
-		 * infinite recursion!)
-		 *
-		 * In the last case, we exit with nonzero exit code to indicate that
-		 * something's pretty wrong.  We also want to exit with nonzero
-		 * exit code if not running under the postmaster (for example, if
-		 * we are being run from the initdb script, we'd better return an
-		 * error status).
-		 */
-		if (elevel == FATAL ||
-			ExitOnAnyError ||
-			!Warn_restart_ready ||
-			proc_exit_inprogress)
-		{
-			/*
-			 * fflush here is just to improve the odds that we get to see
-			 * the error message, in case things are so hosed that
-			 * proc_exit crashes.  Any other code you might be tempted to
-			 * add here should probably be in an on_proc_exit callback
-			 * instead.
-			 */
-			fflush(stdout);
-			fflush(stderr);
-			proc_exit(proc_exit_inprogress || !IsUnderPostmaster);
-		}
-
-		/*
-		 * Guard against infinite loop from errors during error recovery.
+		 * fflush here is just to improve the odds that we get to see
+		 * the error message, in case things are so hosed that
+		 * proc_exit crashes.  Any other code you might be tempted to
+		 * add here should probably be in an on_proc_exit callback
+		 * instead.
 		 */
-		if (InError)
-			ereport(PANIC, (errmsg("error during error recovery, giving up")));
-		InError = true;
+		fflush(stdout);
+		fflush(stderr);
 
 		/*
-		 * Otherwise we can return to the main loop in postgres.c.
+		 * If proc_exit is already running, we exit with nonzero exit code to
+		 * indicate that something's pretty wrong.  We also want to exit with
+		 * nonzero exit code if not running under the postmaster (for example,
+		 * if we are being run from the initdb script, we'd better return an
+		 * error status).
 		 */
-		siglongjmp(Warn_restart, 1);
+		proc_exit(proc_exit_inprogress || !IsUnderPostmaster);
 	}
 
 	if (elevel >= PANIC)
@@ -923,6 +904,168 @@ elog_finish(int elevel, const char *fmt,...)
 	errfinish(0);
 }
 
+/*
+ * Actual output of the top-of-stack error message
+ *
+ * In the ereport(ERROR) case this is called from PostgresMain (or not at all,
+ * if the error is caught by somebody).  For all other severity levels this
+ * is called by errfinish.
+ */
+void
+EmitErrorReport(void)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+	MemoryContext oldcontext;
+
+	recursion_depth++;
+	CHECK_STACK_DEPTH();
+	oldcontext = MemoryContextSwitchTo(ErrorContext);
+
+	/* Send to server log, if enabled */
+	if (edata->output_to_server)
+		send_message_to_server_log(edata);
+
+	/* Send to client, if enabled */
+	if (edata->output_to_client)
+		send_message_to_frontend(edata);
+
+	MemoryContextSwitchTo(oldcontext);
+	recursion_depth--;
+}
+
+/*
+ * CopyErrorData --- obtain a copy of the topmost error stack entry
+ *
+ * This is only for use in error handler code.  The data is copied into the
+ * current memory context, so callers should always switch away from
+ * ErrorContext first; otherwise it will be lost when FlushErrorState is done.
+ */
+ErrorData *
+CopyErrorData(void)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+	ErrorData  *newedata;
+
+	/*
+	 * we don't increment recursion_depth because out-of-memory here does
+	 * not indicate a problem within the error subsystem.
+	 */
+	CHECK_STACK_DEPTH();
+
+	Assert(CurrentMemoryContext != ErrorContext);
+
+	/* Copy the struct itself */
+	newedata = (ErrorData *) palloc(sizeof(ErrorData));
+	memcpy(newedata, edata, sizeof(ErrorData));
+
+	/* Make copies of separately-allocated fields */
+	if (newedata->message)
+		newedata->message = pstrdup(newedata->message);
+	if (newedata->detail)
+		newedata->detail = pstrdup(newedata->detail);
+	if (newedata->hint)
+		newedata->hint = pstrdup(newedata->hint);
+	if (newedata->context)
+		newedata->context = pstrdup(newedata->context);
+	if (newedata->internalquery)
+		newedata->internalquery = pstrdup(newedata->internalquery);
+
+	return newedata;
+}
+
+/*
+ * FreeErrorData --- free the structure returned by CopyErrorData.
+ *
+ * Error handlers should use this in preference to assuming they know all
+ * the separately-allocated fields.
+ */
+void
+FreeErrorData(ErrorData *edata)
+{
+	if (edata->message)
+		pfree(edata->message);
+	if (edata->detail)
+		pfree(edata->detail);
+	if (edata->hint)
+		pfree(edata->hint);
+	if (edata->context)
+		pfree(edata->context);
+	if (edata->internalquery)
+		pfree(edata->internalquery);
+	pfree(edata);
+}
+
+/*
+ * FlushErrorState --- flush the error state after error recovery
+ *
+ * This should be called by an error handler after it's done processing
+ * the error; or as soon as it's done CopyErrorData, if it intends to
+ * do stuff that is likely to provoke another error.  You are not "out" of
+ * the error subsystem until you have done this.
+ */
+void
+FlushErrorState(void)
+{
+	/*
+	 * Reset stack to empty.  The only case where it would be more than
+	 * one deep is if we serviced an error that interrupted construction
+	 * of another message.  We assume control escaped out of that
+	 * message construction and won't ever go back.
+	 */
+	errordata_stack_depth = -1;
+	recursion_depth = 0;
+	/* Delete all data in ErrorContext */
+	MemoryContextResetAndDeleteChildren(ErrorContext);
+}
+
+/*
+ * ReThrowError --- re-throw a previously copied error
+ *
+ * A handler can do CopyErrorData/FlushErrorState to get out of the error
+ * subsystem, then do some processing, and finally ReThrowError to re-throw
+ * the original error.  This is slower than just PG_RE_THROW() but should
+ * be used if the "some processing" is likely to incur another error.
+ */
+void
+ReThrowError(ErrorData *edata)
+{
+	ErrorData  *newedata;
+
+	Assert(edata->elevel == ERROR);
+
+	/* Push the data back into the error context */
+	recursion_depth++;
+	MemoryContextSwitchTo(ErrorContext);
+
+	if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
+	{
+		/*
+		 * Wups, stack not big enough.  We treat this as a PANIC condition
+		 * because it suggests an infinite loop of errors during error
+		 * recovery.
+		 */
+		errordata_stack_depth = -1;	/* make room on stack */
+		ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
+	}
+
+	newedata = &errordata[errordata_stack_depth];
+	memcpy(newedata, edata, sizeof(ErrorData));
+
+	/* Make copies of separately-allocated fields */
+	if (newedata->message)
+		newedata->message = pstrdup(newedata->message);
+	if (newedata->detail)
+		newedata->detail = pstrdup(newedata->detail);
+	if (newedata->hint)
+		newedata->hint = pstrdup(newedata->hint);
+	if (newedata->context)
+		newedata->context = pstrdup(newedata->context);
+	if (newedata->internalquery)
+		newedata->internalquery = pstrdup(newedata->internalquery);
+
+	recursion_depth--;
+	PG_RE_THROW();
+}
 
 /*
  * Initialization of error output file
diff --git a/src/backend/utils/resowner/README b/src/backend/utils/resowner/README
index 27180f6aff70005bd783f0b5930cdc448aa91183..229a80a5a1e37afe3845c94e836e912c4970d146 100644
--- a/src/backend/utils/resowner/README
+++ b/src/backend/utils/resowner/README
@@ -1,4 +1,4 @@
-$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.
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index e2eb1183ef41bb5d8feb87cd7fe8ba4b6f064bfa..e3835971d91cd50057861956760561770dc36949 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -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;
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index 7af3747c8bfd77d0e46d9f67dd2451168169edc5..7f0effdc4eec859e2a9713c35c555512b1ece862 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -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;
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 99e2932f3a8a0304b996205c37581564740c784a..e9f2b0e879f296d894674776f3c3e2ce21308ee4 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -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
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index ad9de225544d9be96ba14ad21346fd7f00b7090f..2d368a68ef9b3bc375e01249952dc183b66da76e 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -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"
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index a91d9c53e46172616688dd45d24cd18be12469f5..75d0a1f8a82edf3053961b8890e1927f66ea9029 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -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"
 
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 0c9d865d17dafd692f5edb5b698cda1b88d84fa2..d6f8c6835ccc993418ab90d36efba206341876e4 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -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"
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index 2b35954efb882abfca77aa937b9b01ae459b15dd..db938c3395a9d9d665dd3588ca30735fddc30cbc 100644
--- a/src/pl/plpython/plpython.c
+++ b/src/pl/plpython/plpython.c
@@ -29,7 +29,7 @@
  * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * IDENTIFICATION
- *	$PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.49 2004/06/06 00:41:28 tgl Exp $
+ *	$PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.50 2004/07/31 00:45:52 tgl Exp $
  *
  *********************************************************************
  */
@@ -39,7 +39,6 @@
 /* system stuff */
 #include <unistd.h>
 #include <fcntl.h>
-#include <setjmp.h>
 
 /* postgreSQL stuff */
 #include "access/heapam.h"
@@ -58,7 +57,6 @@
 #include <Python.h>
 #include <compile.h>
 #include <eval.h>
-#include "plpython.h"
 
 /* convert Postgresql Datum or tuple into a PyObject.
  * input to Python.  Tuples are converted to dictionary
@@ -192,11 +190,6 @@ static void PLy_init_all(void);
 static void PLy_init_interp(void);
 static void PLy_init_plpy(void);
 
-/* error handler.  collects the current Python exception, if any,
- * and appends it to the error and sends it to elog
- */
-static void PLy_elog(int, const char *,...);
-
 /* call PyErr_SetString with a vprint interface
  */
 static void
@@ -209,6 +202,11 @@ static char *PLy_procedure_name(PLyProcedure *);
 
 /* some utility functions
  */
+static void PLy_elog(int, const char *,...);
+static char *PLy_traceback(int *);
+static char *PLy_vprintf(const char *fmt, va_list ap);
+static char *PLy_printf(const char *fmt,...);
+
 static void *PLy_malloc(size_t);
 static void *PLy_realloc(void *, size_t);
 static void PLy_free(void *);
@@ -259,18 +257,26 @@ static PyObject *PLyString_FromString(const char *);
 /* global data
  */
 static int	PLy_first_call = 1;
-static volatile int PLy_call_level = 0;
 
 /*
  * Last function called by postgres backend
+ *
+ * XXX replace this with errcontext mechanism
  */
 static PLyProcedure *PLy_last_procedure = NULL;
 
-/* this gets modified in plpython_call_handler and PLy_elog.
- * test it any old where, but do NOT modify it anywhere except
- * those two functions
+/*
+ * When a callback from Python into PG incurs an error, we temporarily store
+ * the error information here, and return NULL to the Python interpreter.
+ * Any further callback attempts immediately fail, and when the Python
+ * interpreter returns to the calling function, we re-throw the error (even if
+ * Python thinks it trapped the error and doesn't return NULL).  Eventually
+ * this ought to be improved to let Python code really truly trap the error,
+ * but that's more of a change from the pre-7.5 semantics than I have time for
+ * now --- it will only be possible if the callback query is executed inside a
+ * subtransaction.
  */
-static volatile int PLy_restart_in_progress = 0;
+static ErrorData *PLy_error_in_progress = NULL;
 
 static PyObject *PLy_interp_globals = NULL;
 static PyObject *PLy_interp_safe_globals = NULL;
@@ -293,13 +299,6 @@ static char PLy_result_doc[] = {
 };
 
 
-#if DEBUG_EXC
-volatile int exc_save_calls = 0;
-volatile int exc_restore_calls = 0;
-volatile int func_enter_calls = 0;
-volatile int func_leave_calls = 0;
-#endif
-
 /*
  * the function definitions
  */
@@ -324,65 +323,45 @@ perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
 Datum
 plpython_call_handler(PG_FUNCTION_ARGS)
 {
-	DECLARE_EXC();
 	Datum		retval;
 	PLyProcedure *volatile proc = NULL;
 
-	enter();
-
 	PLy_init_all();
 
 	if (SPI_connect() != SPI_OK_CONNECT)
 		elog(ERROR, "could not connect to SPI manager");
 
-	CALL_LEVEL_INC();
-
-	SAVE_EXC();
-	if (TRAP_EXC())
+	PG_TRY();
 	{
-		RESTORE_EXC();
-		CALL_LEVEL_DEC();
-		if (PLy_call_level == 0)
+		if (CALLED_AS_TRIGGER(fcinfo))
 		{
-			PLy_restart_in_progress = 0;
-			PyErr_Clear();
+			TriggerData *tdata = (TriggerData *) fcinfo->context;
+			HeapTuple	trv;
+
+			proc = PLy_procedure_get(fcinfo,
+									 RelationGetRelid(tdata->tg_relation));
+			trv = PLy_trigger_handler(fcinfo, proc);
+			retval = PointerGetDatum(trv);
 		}
 		else
-			PLy_restart_in_progress += 1;
+		{
+			proc = PLy_procedure_get(fcinfo, InvalidOid);
+			retval = PLy_function_handler(fcinfo, proc);
+		}
+	}
+	PG_CATCH();
+	{
 		if (proc)
 		{
 			/* note: Py_DECREF needs braces around it, as of 2003/08 */
 			Py_DECREF(proc->me);
 		}
-		RERAISE_EXC();
+		PyErr_Clear();
+		PG_RE_THROW();
 	}
-
-	/*
-	 * elog(DEBUG3, "PLy_restart_in_progress is %d",
-	 * PLy_restart_in_progress);
-	 */
-
-	if (CALLED_AS_TRIGGER(fcinfo))
-	{
-		TriggerData *tdata = (TriggerData *) fcinfo->context;
-		HeapTuple	trv;
-
-		proc = PLy_procedure_get(fcinfo,
-								 RelationGetRelid(tdata->tg_relation));
-		trv = PLy_trigger_handler(fcinfo, proc);
-		retval = PointerGetDatum(trv);
-	}
-	else
-	{
-		proc = PLy_procedure_get(fcinfo, InvalidOid);
-		retval = PLy_function_handler(fcinfo, proc);
-	}
-
-	CALL_LEVEL_DEC();
-	RESTORE_EXC();
+	PG_END_TRY();
 
 	Py_DECREF(proc->me);
-	refc(proc->me);
 
 	return retval;
 }
@@ -397,42 +376,27 @@ plpython_call_handler(PG_FUNCTION_ARGS)
  * BEFORE the event and is ROW level.  postgres expects the function
  * to take no arguments and return an argument of type trigger.
  */
-HeapTuple
+static HeapTuple
 PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
 {
-	DECLARE_EXC();
 	HeapTuple	rv = NULL;
 	PyObject   *volatile plargs = NULL;
 	PyObject   *volatile plrv = NULL;
 
-	enter();
-
-	SAVE_EXC();
-	if (TRAP_EXC())
+	PG_TRY();
 	{
-		RESTORE_EXC();
-
-		Py_XDECREF(plargs);
-		Py_XDECREF(plrv);
-
-		RERAISE_EXC();
-	}
-
 	plargs = PLy_trigger_build_args(fcinfo, proc, &rv);
 	plrv = PLy_procedure_call(proc, "TD", plargs);
 
+	Assert(plrv != NULL);
+	Assert(!PLy_error_in_progress);
+
 	/*
 	 * Disconnect from SPI manager
 	 */
 	if (SPI_finish() != SPI_OK_FINISH)
 		elog(ERROR, "SPI_finish failed");
 
-	if (plrv == NULL)
-		elog(FATAL, "PLy_procedure_call returned NULL");
-
-	if (PLy_restart_in_progress)
-		elog(FATAL, "restart in progress not expected");
-
 	/*
 	 * return of None means we're happy with the tuple
 	 */
@@ -466,20 +430,26 @@ PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
 			elog(ERROR, "expected return to be \"SKIP\" or \"MODIFY\"");
 		}
 	}
+	}
+	PG_CATCH();
+	{
+		Py_XDECREF(plargs);
+		Py_XDECREF(plrv);
+
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
 
 	Py_DECREF(plargs);
 	Py_DECREF(plrv);
 
-	RESTORE_EXC();
-
 	return rv;
 }
 
-HeapTuple
+static HeapTuple
 PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
 				 HeapTuple otup)
 {
-	DECLARE_EXC();
 	PyObject   *volatile plntup;
 	PyObject   *volatile plkeys;
 	PyObject   *volatile platt;
@@ -500,29 +470,8 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
 	modvalues = NULL;
 	modnulls = NULL;
 
-	enter();
-
-	SAVE_EXC();
-	if (TRAP_EXC())
+	PG_TRY();
 	{
-		RESTORE_EXC();
-
-		Py_XDECREF(plntup);
-		Py_XDECREF(plkeys);
-		Py_XDECREF(platt);
-		Py_XDECREF(plval);
-		Py_XDECREF(plstr);
-
-		if (modnulls)
-			pfree(modnulls);
-		if (modvalues)
-			pfree(modvalues);
-		if (modattrs)
-			pfree(modattrs);
-
-		RERAISE_EXC();
-	}
-
 	if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL)
 		elog(ERROR, "TD[\"new\"] deleted, unable to modify tuple");
 	if (!PyDict_Check(plntup))
@@ -585,31 +534,42 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
 
 	rtup = SPI_modifytuple(tdata->tg_relation, otup, natts,
 						   modattrs, modvalues, modnulls);
-
-	/*
-	 * FIXME -- these leak if not explicitly pfree'd by other elog calls,
-	 * no?  (No, I think, but might as well leave the pfrees here...)
-	 */
-	pfree(modattrs);
-	pfree(modvalues);
-	pfree(modnulls);
-
 	if (rtup == NULL)
 		elog(ERROR, "SPI_modifytuple failed -- error %d", SPI_result);
+	}
+	PG_CATCH();
+	{
+		Py_XDECREF(plntup);
+		Py_XDECREF(plkeys);
+		Py_XDECREF(platt);
+		Py_XDECREF(plval);
+		Py_XDECREF(plstr);
+
+		if (modnulls)
+			pfree(modnulls);
+		if (modvalues)
+			pfree(modvalues);
+		if (modattrs)
+			pfree(modattrs);
+
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
 
 	Py_DECREF(plntup);
 	Py_DECREF(plkeys);
 
-	RESTORE_EXC();
+	pfree(modattrs);
+	pfree(modvalues);
+	pfree(modnulls);
 
 	return rtup;
 }
 
-PyObject *
+static PyObject *
 PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *rv)
 {
-	DECLARE_EXC();
-	TriggerData *tdata;
+	TriggerData *tdata = (TriggerData *) fcinfo->context;
 	PyObject   *pltname,
 			   *pltevent,
 			   *pltwhen,
@@ -621,20 +581,8 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *
 	PyObject   *volatile pltdata = NULL;
 	char	   *stroid;
 
-	enter();
-
-	SAVE_EXC();
-	if (TRAP_EXC())
+	PG_TRY();
 	{
-		RESTORE_EXC();
-
-		Py_XDECREF(pltdata);
-
-		RERAISE_EXC();
-	}
-
-	tdata = (TriggerData *) fcinfo->context;
-
 	pltdata = PyDict_New();
 	if (!pltdata)
 		PLy_elog(ERROR, "could not build arguments for trigger procedure");
@@ -767,8 +715,13 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *
 	}
 	PyDict_SetItemString(pltdata, "args", pltargs);
 	Py_DECREF(pltargs);
-
-	RESTORE_EXC();
+	}
+	PG_CATCH();
+	{
+		Py_XDECREF(pltdata);
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
 
 	return pltdata;
 }
@@ -777,37 +730,23 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *
 
 /* function handler and friends
  */
-Datum
+static Datum
 PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
 {
-	DECLARE_EXC();
 	Datum		rv;
 	PyObject   *volatile plargs = NULL;
 	PyObject   *volatile plrv = NULL;
 	PyObject   *volatile plrv_so = NULL;
 	char	   *plrv_sc;
 
-	enter();
-
-	/*
-	 * setup to catch elog in while building function arguments, and
-	 * DECREF the plargs if the function call fails
-	 */
-	SAVE_EXC();
-	if (TRAP_EXC())
+	PG_TRY();
 	{
-		RESTORE_EXC();
-
-		Py_XDECREF(plargs);
-		Py_XDECREF(plrv);
-		Py_XDECREF(plrv_so);
-
-		RERAISE_EXC();
-	}
-
 	plargs = PLy_function_build_args(fcinfo, proc);
 	plrv = PLy_procedure_call(proc, "args", plargs);
 
+	Assert(plrv != NULL);
+	Assert(!PLy_error_in_progress);
+
 	/*
 	 * Disconnect from SPI manager and then create the return values datum
 	 * (if the input function does a palloc for it this must not be
@@ -817,28 +756,9 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
 	if (SPI_finish() != SPI_OK_FINISH)
 		elog(ERROR, "SPI_finish failed");
 
-	if (plrv == NULL)
-	{
-		elog(FATAL, "PLy_procedure_call returned NULL");
-#ifdef NOT_USED
-		if (!PLy_restart_in_progress)
-			PLy_elog(ERROR, "function \"%s\" failed", proc->proname);
-
-		/*
-		 * FIXME is this dead code?  i'm pretty sure it is for unnested
-		 * calls, but not for nested calls
-		 */
-		RAISE_EXC(1);
-#endif
-	}
-
 	/*
-	 * convert the python PyObject to a postgresql Datum FIXME returning a
-	 * NULL, ie PG_RETURN_NULL() blows the backend to small messy bits...
-	 * it this a bug or expected?  so just call with the string value of
-	 * None for now
+	 * convert the python PyObject to a postgresql Datum
 	 */
-
 	if (plrv == Py_None)
 	{
 		fcinfo->isnull = true;
@@ -855,7 +775,16 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
 						   Int32GetDatum(-1));
 	}
 
-	RESTORE_EXC();
+	}
+	PG_CATCH();
+	{
+		Py_XDECREF(plargs);
+		Py_XDECREF(plrv);
+		Py_XDECREF(plrv_so);
+
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
 
 	Py_XDECREF(plargs);
 	Py_DECREF(plrv);
@@ -864,56 +793,49 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
 	return rv;
 }
 
-PyObject *
+static PyObject *
 PLy_procedure_call(PLyProcedure * proc, char *kargs, PyObject * vargs)
 {
 	PyObject   *rv;
 	PLyProcedure *current;
 
-	enter();
-
 	current = PLy_last_procedure;
 	PLy_last_procedure = proc;
 	PyDict_SetItemString(proc->globals, kargs, vargs);
-	rv = PyEval_EvalCode((PyCodeObject *) proc->code, proc->globals, proc->globals);
+	rv = PyEval_EvalCode((PyCodeObject *) proc->code,
+						 proc->globals, proc->globals);
 	PLy_last_procedure = current;
 
+	/*
+	 * If there was an error in a PG callback, propagate that
+	 * no matter what Python claims about its success.
+	 */
+	if (PLy_error_in_progress)
+	{
+		ErrorData *edata = PLy_error_in_progress;
+
+		PLy_error_in_progress = NULL;
+		ReThrowError(edata);
+	}
+
 	if ((rv == NULL) || (PyErr_Occurred()))
 	{
 		Py_XDECREF(rv);
-		if (!PLy_restart_in_progress)
-			PLy_elog(ERROR, "function \"%s\" failed", proc->proname);
-		RAISE_EXC(1);
+		PLy_elog(ERROR, "function \"%s\" failed", proc->proname);
 	}
 
 	return rv;
 }
 
-PyObject *
+static PyObject *
 PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
 {
-	DECLARE_EXC();
 	PyObject   *volatile arg = NULL;
 	PyObject   *volatile args = NULL;
 	int			i;
 
-	enter();
-
-	/*
-	 * FIXME -- if the setjmp setup is expensive, add the arg and args
-	 * field to the procedure struct and cleanup at the start of the next
-	 * call
-	 */
-	SAVE_EXC();
-	if (TRAP_EXC())
+	PG_TRY();
 	{
-		RESTORE_EXC();
-		Py_XDECREF(arg);
-		Py_XDECREF(args);
-
-		RERAISE_EXC();
-	}
-
 	args = PyList_New(proc->nargs);
 	for (i = 0; i < proc->nargs; i++)
 	{
@@ -976,8 +898,15 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
 		 */
 		PyList_SetItem(args, i, arg);
 	}
+	}
+	PG_CATCH();
+	{
+		Py_XDECREF(arg);
+		Py_XDECREF(args);
 
-	RESTORE_EXC();
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
 
 	return args;
 }
@@ -1002,8 +931,6 @@ PLy_procedure_get(FunctionCallInfo fcinfo, Oid tgreloid)
 	PLyProcedure *proc = NULL;
 	int			rv;
 
-	enter();
-
 	fn_oid = fcinfo->flinfo->fn_oid;
 	procTup = SearchSysCache(PROCOID,
 							 ObjectIdGetDatum(fn_oid),
@@ -1023,8 +950,6 @@ PLy_procedure_get(FunctionCallInfo fcinfo, Oid tgreloid)
 		if (!PyCObject_Check(plproc))
 			elog(FATAL, "expected a PyCObject, didn't get one");
 
-		mark();
-
 		proc = PyCObject_AsVoidPtr(plproc);
 		if (proc->me != plproc)
 			elog(FATAL, "proc->me != plproc");
@@ -1051,7 +976,6 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
 {
 	char		procName[NAMEDATALEN + 256];
 
-	DECLARE_EXC();
 	Form_pg_proc procStruct;
 	PLyProcedure *volatile proc;
 	char	   *volatile procSource = NULL;
@@ -1060,8 +984,6 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
 	int			i,
 				rv;
 
-	enter();
-
 	procStruct = (Form_pg_proc) GETSTRUCT(procTup);
 
 	if (OidIsValid(tgreloid))
@@ -1092,16 +1014,8 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
 	proc->code = proc->statics = NULL;
 	proc->globals = proc->me = NULL;
 
-	SAVE_EXC();
-	if (TRAP_EXC())
+	PG_TRY();
 	{
-		RESTORE_EXC();
-		PLy_procedure_delete(proc);
-		if (procSource)
-			pfree(procSource);
-		RERAISE_EXC();
-	}
-
 	/*
 	 * get information required for output conversion of the return value,
 	 * but only if this isn't a trigger.
@@ -1185,20 +1099,26 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
 
 	proc->me = PyCObject_FromVoidPtr(proc, NULL);
 	PyDict_SetItemString(PLy_procedure_cache, key, proc->me);
+	}
+	PG_CATCH();
+	{
+		PLy_procedure_delete(proc);
+		if (procSource)
+			pfree(procSource);
 
-	RESTORE_EXC();
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
 
 	return proc;
 }
 
-void
+static void
 PLy_procedure_compile(PLyProcedure * proc, const char *src)
 {
 	PyObject   *crv = NULL;
 	char	   *msrc;
 
-	enter();
-
 	proc->globals = PyDict_Copy(PLy_interp_globals);
 
 	/*
@@ -1238,7 +1158,7 @@ PLy_procedure_compile(PLyProcedure * proc, const char *src)
 	PLy_elog(ERROR, "could not compile function \"%s\"", proc->proname);
 }
 
-char *
+static char *
 PLy_procedure_munge_source(const char *name, const char *src)
 {
 	char	   *mrc,
@@ -1247,8 +1167,6 @@ PLy_procedure_munge_source(const char *name, const char *src)
 	size_t		mlen,
 				plen;
 
-	enter();
-
 	/*
 	 * room for function source and the def statement
 	 */
@@ -1281,13 +1199,11 @@ PLy_procedure_munge_source(const char *name, const char *src)
 	return mrc;
 }
 
-void
+static void
 PLy_procedure_delete(PLyProcedure * proc)
 {
 	int			i;
 
-	enter();
-
 	Py_XDECREF(proc->code);
 	Py_XDECREF(proc->statics);
 	Py_XDECREF(proc->globals);
@@ -1304,20 +1220,16 @@ PLy_procedure_delete(PLyProcedure * proc)
 			if (proc->args[i].out.r.atts)
 				PLy_free(proc->args[i].out.r.atts);
 		}
-
-	leave();
 }
 
 /* conversion functions.  remember output from python is
  * input to postgresql, and vis versa.
  */
-void
+static void
 PLy_input_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
 {
 	int			i;
 
-	enter();
-
 	if (arg->is_rowtype == 0)
 		elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
 
@@ -1347,13 +1259,11 @@ PLy_input_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
 	}
 }
 
-void
+static void
 PLy_output_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
 {
 	int			i;
 
-	enter();
-
 	if (arg->is_rowtype == 0)
 		elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
 
@@ -1381,41 +1291,35 @@ PLy_output_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
 	}
 }
 
-void
+static void
 PLy_output_datum_func(PLyTypeInfo * arg, HeapTuple typeTup)
 {
-	enter();
-
 	if (arg->is_rowtype > 0)
 		elog(ERROR, "PLyTypeInfo struct is initialized for a Tuple");
 	arg->is_rowtype = 0;
 	PLy_output_datum_func2(&(arg->out.d), typeTup);
 }
 
-void
+static void
 PLy_output_datum_func2(PLyObToDatum * arg, HeapTuple typeTup)
 {
 	Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
 
-	enter();
-
 	perm_fmgr_info(typeStruct->typinput, &arg->typfunc);
 	arg->typioparam = getTypeIOParam(typeTup);
 	arg->typbyval = typeStruct->typbyval;
 }
 
-void
+static void
 PLy_input_datum_func(PLyTypeInfo * arg, Oid typeOid, HeapTuple typeTup)
 {
-	enter();
-
 	if (arg->is_rowtype > 0)
 		elog(ERROR, "PLyTypeInfo struct is initialized for Tuple");
 	arg->is_rowtype = 0;
 	PLy_input_datum_func2(&(arg->in.d), typeOid, typeTup);
 }
 
-void
+static void
 PLy_input_datum_func2(PLyDatumToOb * arg, Oid typeOid, HeapTuple typeTup)
 {
 	Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
@@ -1449,7 +1353,7 @@ PLy_input_datum_func2(PLyDatumToOb * arg, Oid typeOid, HeapTuple typeTup)
 	}
 }
 
-void
+static void
 PLy_typeinfo_init(PLyTypeInfo * arg)
 {
 	arg->is_rowtype = -1;
@@ -1458,7 +1362,7 @@ PLy_typeinfo_init(PLyTypeInfo * arg)
 	arg->out.r.atts = NULL;
 }
 
-void
+static void
 PLy_typeinfo_dealloc(PLyTypeInfo * arg)
 {
 	if (arg->is_rowtype == 1)
@@ -1472,24 +1376,20 @@ PLy_typeinfo_dealloc(PLyTypeInfo * arg)
 
 /* assumes that a bool is always returned as a 't' or 'f'
  */
-PyObject *
+static PyObject *
 PLyBool_FromString(const char *src)
 {
-	enter();
-
 	if (src[0] == 't')
 		return PyInt_FromLong(1);
 	return PyInt_FromLong(0);
 }
 
-PyObject *
+static PyObject *
 PLyFloat_FromString(const char *src)
 {
 	double		v;
 	char	   *eptr;
 
-	enter();
-
 	errno = 0;
 	v = strtod(src, &eptr);
 	if ((*eptr != '\0') || (errno))
@@ -1497,14 +1397,12 @@ PLyFloat_FromString(const char *src)
 	return PyFloat_FromDouble(v);
 }
 
-PyObject *
+static PyObject *
 PLyInt_FromString(const char *src)
 {
 	long		v;
 	char	   *eptr;
 
-	enter();
-
 	errno = 0;
 	v = strtol(src, &eptr, 0);
 	if ((*eptr != '\0') || (errno))
@@ -1512,27 +1410,24 @@ PLyInt_FromString(const char *src)
 	return PyInt_FromLong(v);
 }
 
-PyObject *
+static PyObject *
 PLyLong_FromString(const char *src)
 {
 	return PyLong_FromString((char *) src, NULL, 0);
 }
 
-PyObject *
+static PyObject *
 PLyString_FromString(const char *src)
 {
 	return PyString_FromString(src);
 }
 
-PyObject *
+static PyObject *
 PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
 {
-	DECLARE_EXC();
 	PyObject   *volatile dict;
 	int			i;
 
-	enter();
-
 	if (info->is_rowtype != 1)
 		elog(ERROR, "PLyTypeInfo structure describes a datum");
 
@@ -1540,15 +1435,8 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
 	if (dict == NULL)
 		PLy_elog(ERROR, "could not create tuple dictionary");
 
-	SAVE_EXC();
-	if (TRAP_EXC())
+	PG_TRY();
 	{
-		RESTORE_EXC();
-		Py_DECREF(dict);
-
-		RERAISE_EXC();
-	}
-
 	for (i = 0; i < info->in.r.natts; i++)
 	{
 		char	   *key,
@@ -1583,8 +1471,13 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
 			Py_DECREF(value);
 		}
 	}
-
-	RESTORE_EXC();
+	}
+	PG_CATCH();
+	{
+		Py_DECREF(dict);
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
 
 	return dict;
 }
@@ -1747,13 +1640,11 @@ static PyMethodDef PLy_methods[] = {
 
 /* plan object methods
  */
-PyObject *
+static PyObject *
 PLy_plan_new(void)
 {
 	PLyPlanObject *ob;
 
-	enter();
-
 	if ((ob = PyObject_NEW(PLyPlanObject, &PLy_PlanType)) == NULL)
 		return NULL;
 
@@ -1766,13 +1657,11 @@ PLy_plan_new(void)
 }
 
 
-void
+static void
 PLy_plan_dealloc(PyObject * arg)
 {
 	PLyPlanObject *ob = (PLyPlanObject *) arg;
 
-	enter();
-
 	if (ob->plan)
 		SPI_freeplan(ob->plan);
 	if (ob->types)
@@ -1787,18 +1676,16 @@ PLy_plan_dealloc(PyObject * arg)
 	}
 
 	PyMem_DEL(arg);
-
-	leave();
 }
 
 
-PyObject *
+static PyObject *
 PLy_plan_getattr(PyObject * self, char *name)
 {
 	return Py_FindMethod(PLy_plan_methods, self, name);
 }
 
-PyObject *
+static PyObject *
 PLy_plan_status(PyObject * self, PyObject * args)
 {
 	if (PyArg_ParseTuple(args, ""))
@@ -1816,13 +1703,11 @@ PLy_plan_status(PyObject * self, PyObject * args)
 /* result object methods
  */
 
-PyObject *
+static PyObject *
 PLy_result_new(void)
 {
 	PLyResultObject *ob;
 
-	enter();
-
 	if ((ob = PyObject_NEW(PLyResultObject, &PLy_ResultType)) == NULL)
 		return NULL;
 
@@ -1836,13 +1721,11 @@ PLy_result_new(void)
 	return (PyObject *) ob;
 }
 
-void
+static void
 PLy_result_dealloc(PyObject * arg)
 {
 	PLyResultObject *ob = (PLyResultObject *) arg;
 
-	enter();
-
 	Py_XDECREF(ob->nrows);
 	Py_XDECREF(ob->rows);
 	Py_XDECREF(ob->status);
@@ -1850,7 +1733,7 @@ PLy_result_dealloc(PyObject * arg)
 	PyMem_DEL(ob);
 }
 
-PyObject *
+static PyObject *
 PLy_result_getattr(PyObject * self, char *attr)
 {
 	return NULL;
@@ -1858,13 +1741,13 @@ PLy_result_getattr(PyObject * self, char *attr)
 
 #ifdef NOT_USED
 /* Appear to be unused */
-PyObject *
+static PyObject *
 PLy_result_fetch(PyObject * self, PyObject * args)
 {
 	return NULL;
 }
 
-PyObject *
+static PyObject *
 PLy_result_nrows(PyObject * self, PyObject * args)
 {
 	PLyResultObject *ob = (PLyResultObject *) self;
@@ -1873,7 +1756,7 @@ PLy_result_nrows(PyObject * self, PyObject * args)
 	return ob->nrows;
 }
 
-PyObject *
+static PyObject *
 PLy_result_status(PyObject * self, PyObject * args)
 {
 	PLyResultObject *ob = (PLyResultObject *) self;
@@ -1882,7 +1765,8 @@ PLy_result_status(PyObject * self, PyObject * args)
 	return ob->status;
 }
 #endif
-int
+
+static int
 PLy_result_length(PyObject * arg)
 {
 	PLyResultObject *ob = (PLyResultObject *) arg;
@@ -1890,7 +1774,7 @@ PLy_result_length(PyObject * arg)
 	return PyList_Size(ob->rows);
 }
 
-PyObject *
+static PyObject *
 PLy_result_item(PyObject * arg, int idx)
 {
 	PyObject   *rv;
@@ -1902,7 +1786,7 @@ PLy_result_item(PyObject * arg, int idx)
 	return rv;
 }
 
-int
+static int
 PLy_result_ass_item(PyObject * arg, int idx, PyObject * item)
 {
 	int			rv;
@@ -1913,7 +1797,7 @@ PLy_result_ass_item(PyObject * arg, int idx, PyObject * item)
 	return rv;
 }
 
-PyObject *
+static PyObject *
 PLy_result_slice(PyObject * arg, int lidx, int hidx)
 {
 	PyObject   *rv;
@@ -1926,7 +1810,7 @@ PLy_result_slice(PyObject * arg, int lidx, int hidx)
 	return rv;
 }
 
-int
+static int
 PLy_result_ass_slice(PyObject * arg, int lidx, int hidx, PyObject * slice)
 {
 	int			rv;
@@ -1938,17 +1822,22 @@ PLy_result_ass_slice(PyObject * arg, int lidx, int hidx, PyObject * slice)
 
 /* SPI interface
  */
-PyObject *
+static PyObject *
 PLy_spi_prepare(PyObject * self, PyObject * args)
 {
-	DECLARE_EXC();
 	PLyPlanObject *plan;
 	PyObject   *list = NULL;
 	PyObject   *volatile optr = NULL;
 	char	   *query;
 	void	   *tmpplan;
+	MemoryContext oldcontext;
 
-	enter();
+	/* Can't execute more if we have an unhandled error */
+	if (PLy_error_in_progress)
+	{
+		PyErr_SetString(PLy_exc_error, "Transaction aborted.");
+		return NULL;
+	}
 
 	if (!PyArg_ParseTuple(args, "s|O", &query, &list))
 	{
@@ -1964,24 +1853,12 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
 		return NULL;
 	}
 
-
 	if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL)
 		return NULL;
 
-	SAVE_EXC();
-	if (TRAP_EXC())
+	oldcontext = CurrentMemoryContext;
+	PG_TRY();
 	{
-		RESTORE_EXC();
-		Py_DECREF(plan);
-		Py_XDECREF(optr);
-		if (!PyErr_Occurred())
-			PyErr_SetString(PLy_exc_spi_error,
-							"Unknown error in PLy_spi_prepare");
-		/* XXX this oughta be replaced with errcontext mechanism */
-		PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure));
-		RERAISE_EXC();
-	}
-
 	if (list != NULL)
 	{
 		int			nargs,
@@ -2014,11 +1891,7 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
 
 				optr = PySequence_GetItem(list, i);
 				if (!PyString_Check(optr))
-				{
-					PyErr_SetString(PLy_exc_spi_error,
-									"Type names must be strings.");
-					RAISE_EXC(1);
-				}
+					elog(ERROR, "Type names must be strings.");
 				sptr = PyString_AsString(optr);
 				/* XXX should extend this to allow qualified type names */
 				typeTup = typenameType(makeTypeName(sptr));
@@ -2030,11 +1903,7 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
 				if (typeStruct->typtype != 'c')
 					PLy_output_datum_func(&plan->args[i], typeTup);
 				else
-				{
-					PyErr_SetString(PLy_exc_spi_error,
-							 "tuples not handled in plpy.prepare, yet.");
-					RAISE_EXC(1);
-				}
+					elog(ERROR, "tuples not handled in plpy.prepare, yet.");
 				ReleaseSysCache(typeTup);
 			}
 		}
@@ -2042,26 +1911,32 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
 
 	plan->plan = SPI_prepare(query, plan->nargs, plan->types);
 	if (plan->plan == NULL)
-	{
-		PLy_exception_set(PLy_exc_spi_error,
-					 "Unable to prepare plan. SPI_prepare failed -- %s.",
-						  PLy_spi_error_string(SPI_result));
-		RAISE_EXC(1);
-	}
+		elog(ERROR, "Unable to prepare plan. SPI_prepare failed -- %s.",
+			 PLy_spi_error_string(SPI_result));
 
 	/* transfer plan from procCxt to topCxt */
 	tmpplan = plan->plan;
 	plan->plan = SPI_saveplan(tmpplan);
 	SPI_freeplan(tmpplan);
 	if (plan->plan == NULL)
+		elog(ERROR, "Unable to save plan. SPI_saveplan failed -- %s.",
+			 PLy_spi_error_string(SPI_result));
+	}
+	PG_CATCH();
 	{
-		PLy_exception_set(PLy_exc_spi_error,
-					   "Unable to save plan. SPI_saveplan failed -- %s.",
-						  PLy_spi_error_string(SPI_result));
-		RAISE_EXC(1);
+		MemoryContextSwitchTo(oldcontext);
+		PLy_error_in_progress = CopyErrorData();
+		FlushErrorState();
+		Py_DECREF(plan);
+		Py_XDECREF(optr);
+		if (!PyErr_Occurred())
+			PyErr_SetString(PLy_exc_spi_error,
+							"Unknown error in PLy_spi_prepare");
+		/* XXX this oughta be replaced with errcontext mechanism */
+		PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure));
+		return NULL;
 	}
-
-	RESTORE_EXC();
+	PG_END_TRY();
 
 	return (PyObject *) plan;
 }
@@ -2069,7 +1944,7 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
 /* execute(query="select * from foo", limit=5)
  * execute(plan=plan, values=(foo, bar), limit=5)
  */
-PyObject *
+static PyObject *
 PLy_spi_execute(PyObject * self, PyObject * args)
 {
 	char	   *query;
@@ -2077,17 +1952,12 @@ PLy_spi_execute(PyObject * self, PyObject * args)
 	PyObject   *list = NULL;
 	int			limit = 0;
 
-	enter();
-
-#ifdef NOT_USED
-
-	/*
-	 * there should - hahaha - be an python exception set so just return
-	 * NULL.  FIXME -- is this needed?
-	 */
-	if (PLy_restart_in_progress)
+	/* Can't execute more if we have an unhandled error */
+	if (PLy_error_in_progress)
+	{
+		PyErr_SetString(PLy_exc_error, "Transaction aborted.");
 		return NULL;
-#endif
+	}
 
 	if (PyArg_ParseTuple(args, "s|i", &query, &limit))
 		return PLy_spi_execute_query(query, limit);
@@ -2096,27 +1966,21 @@ PLy_spi_execute(PyObject * self, PyObject * args)
 
 	if ((PyArg_ParseTuple(args, "O|Oi", &plan, &list, &limit)) &&
 		(is_PLyPlanObject(plan)))
-	{
-		PyObject   *rv = PLy_spi_execute_plan(plan, list, limit);
-
-		return rv;
-	}
+		return PLy_spi_execute_plan(plan, list, limit);
 
 	PyErr_SetString(PLy_exc_error, "Expected a query or plan.");
 	return NULL;
 }
 
-PyObject *
+static PyObject *
 PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit)
 {
-	DECLARE_EXC();
 	volatile int nargs;
 	int			i,
 				rv;
 	PLyPlanObject *plan;
 	char	   *nulls;
-
-	enter();
+	MemoryContext oldcontext;
 
 	if (list != NULL)
 	{
@@ -2149,31 +2013,9 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit)
 		return NULL;
 	}
 
-	SAVE_EXC();
-	if (TRAP_EXC())
+	oldcontext = CurrentMemoryContext;
+	PG_TRY();
 	{
-		RESTORE_EXC();
-
-		/*
-		 * cleanup plan->values array
-		 */
-		for (i = 0; i < nargs; i++)
-		{
-			if (!plan->args[i].out.d.typbyval &&
-				(plan->values[i] != (Datum) NULL))
-			{
-				pfree(DatumGetPointer(plan->values[i]));
-				plan->values[i] = (Datum) NULL;
-			}
-		}
-
-		if (!PyErr_Occurred())
-			PyErr_SetString(PLy_exc_error,
-							"Unknown error in PLy_spi_execute_plan");
-		PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure));
-		RERAISE_EXC();
-	}
-
 	nulls = palloc(nargs * sizeof(char));
 
 	for (i = 0; i < nargs; i++)
@@ -2189,7 +2031,7 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit)
 			sv = PyString_AsString(so);
 
 			/*
-			 * FIXME -- if this can elog, we have leak
+			 * FIXME -- if this elogs, we have Python reference leak
 			 */
 			plan->values[i] =
 				FunctionCall3(&(plan->args[i].out.d.typfunc),
@@ -2213,8 +2055,32 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit)
 	rv = SPI_execp(plan->plan, plan->values, nulls, limit);
 
 	pfree(nulls);
+	}
+	PG_CATCH();
+	{
+		MemoryContextSwitchTo(oldcontext);
+		PLy_error_in_progress = CopyErrorData();
+		FlushErrorState();
+		/*
+		 * cleanup plan->values array
+		 */
+		for (i = 0; i < nargs; i++)
+		{
+			if (!plan->args[i].out.d.typbyval &&
+				(plan->values[i] != (Datum) NULL))
+			{
+				pfree(DatumGetPointer(plan->values[i]));
+				plan->values[i] = (Datum) NULL;
+			}
+		}
 
-	RESTORE_EXC();
+		if (!PyErr_Occurred())
+			PyErr_SetString(PLy_exc_error,
+							"Unknown error in PLy_spi_execute_plan");
+		PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure));
+		return NULL;
+	}
+	PG_END_TRY();
 
 	for (i = 0; i < nargs; i++)
 	{
@@ -2237,25 +2103,30 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit)
 	return PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
 }
 
-PyObject *
+static PyObject *
 PLy_spi_execute_query(char *query, int limit)
 {
-	DECLARE_EXC();
 	int			rv;
+	MemoryContext oldcontext;
 
-	SAVE_EXC();
-	if (TRAP_EXC())
+	oldcontext = CurrentMemoryContext;
+	PG_TRY();
+	{
+		rv = SPI_exec(query, limit);
+	}
+	PG_CATCH();
 	{
-		RESTORE_EXC();
-		if ((!PLy_restart_in_progress) && (!PyErr_Occurred()))
+		MemoryContextSwitchTo(oldcontext);
+		PLy_error_in_progress = CopyErrorData();
+		FlushErrorState();
+		if (!PyErr_Occurred())
 			PyErr_SetString(PLy_exc_spi_error,
 							"Unknown error in PLy_spi_execute_query");
 		PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure));
-		RERAISE_EXC();
+		return NULL;
 	}
+	PG_END_TRY();
 
-	rv = SPI_exec(query, limit);
-	RESTORE_EXC();
 	if (rv < 0)
 	{
 		PLy_exception_set(PLy_exc_spi_error,
@@ -2267,12 +2138,11 @@ PLy_spi_execute_query(char *query, int limit)
 	return PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
 }
 
-PyObject *
+static PyObject *
 PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
 {
 	PLyResultObject *result;
-
-	enter();
+	MemoryContext oldcontext;
 
 	result = (PLyResultObject *) PLy_result_new();
 	Py_DECREF(result->status);
@@ -2290,7 +2160,6 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
 	}
 	else
 	{
-		DECLARE_EXC();
 		PLyTypeInfo args;
 		int			i;
 
@@ -2298,43 +2167,46 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
 		Py_DECREF(result->nrows);
 		result->nrows = PyInt_FromLong(rows);
 
-		SAVE_EXC();
-		if (TRAP_EXC())
+		oldcontext = CurrentMemoryContext;
+		PG_TRY();
 		{
-			RESTORE_EXC();
-
-			if (!PyErr_Occurred())
-				PyErr_SetString(PLy_exc_error,
-						"Unknown error in PLy_spi_execute_fetch_result");
-			Py_DECREF(result);
-			PLy_typeinfo_dealloc(&args);
-			RERAISE_EXC();
-		}
+			if (rows)
+			{
+				Py_DECREF(result->rows);
+				result->rows = PyList_New(rows);
 
-		if (rows)
-		{
-			Py_DECREF(result->rows);
-			result->rows = PyList_New(rows);
+				PLy_input_tuple_funcs(&args, tuptable->tupdesc);
+				for (i = 0; i < rows; i++)
+				{
+					PyObject   *row = PLyDict_FromTuple(&args, tuptable->vals[i],
+														tuptable->tupdesc);
 
-			PLy_input_tuple_funcs(&args, tuptable->tupdesc);
-			for (i = 0; i < rows; i++)
-			{
-				PyObject   *row = PLyDict_FromTuple(&args, tuptable->vals[i],
-													tuptable->tupdesc);
+					PyList_SetItem(result->rows, i, row);
+				}
+				PLy_typeinfo_dealloc(&args);
 
-				PyList_SetItem(result->rows, i, row);
+				SPI_freetuptable(tuptable);
 			}
+		}
+		PG_CATCH();
+		{
+			MemoryContextSwitchTo(oldcontext);
+			PLy_error_in_progress = CopyErrorData();
+			FlushErrorState();
+			if (!PyErr_Occurred())
+				PyErr_SetString(PLy_exc_error,
+								"Unknown error in PLy_spi_execute_fetch_result");
+			Py_DECREF(result);
 			PLy_typeinfo_dealloc(&args);
-
-			SPI_freetuptable(tuptable);
+			return NULL;
 		}
-		RESTORE_EXC();
+		PG_END_TRY();
 	}
 
 	return (PyObject *) result;
 }
 
-const char *
+static const char *
 PLy_spi_error_string(int code)
 {
 	switch (code)
@@ -2384,8 +2256,6 @@ plpython_init(void)
 	if (!PLy_first_call)
 		return;
 
-	enter();
-
 	if (init_active)
 		elog(FATAL, "initialization of language module failed");
 	init_active = 1;
@@ -2400,8 +2270,6 @@ plpython_init(void)
 		PLy_elog(ERROR, "could not create procedure cache");
 
 	PLy_first_call = 0;
-
-	leave();
 }
 
 static void
@@ -2418,13 +2286,11 @@ PLy_init_all(void)
 
 }
 
-void
+static void
 PLy_init_interp(void)
 {
 	PyObject   *mainmod;
 
-	enter();
-
 	mainmod = PyImport_AddModule("__main__");
 	if ((mainmod == NULL) || (PyErr_Occurred()))
 		PLy_elog(ERROR, "could not import \"__main__\" module.");
@@ -2437,7 +2303,7 @@ PLy_init_interp(void)
 		PLy_elog(ERROR, "could not initialize globals");
 }
 
-void
+static void
 PLy_init_plpy(void)
 {
 	PyObject   *main_mod,
@@ -2446,8 +2312,6 @@ PLy_init_plpy(void)
 	PyObject   *plpy,
 			   *plpy_dict;
 
-	enter();
-
 	/*
 	 * initialize plpy module
 	 */
@@ -2480,60 +2344,55 @@ PLy_init_plpy(void)
  */
 static PyObject *PLy_output(int, PyObject *, PyObject *);
 
-PyObject *
+static PyObject *
 PLy_debug(PyObject * self, PyObject * args)
 {
 	return PLy_output(DEBUG2, self, args);
 }
 
-PyObject *
+static PyObject *
 PLy_log(PyObject * self, PyObject * args)
 {
 	return PLy_output(LOG, self, args);
 }
 
-PyObject *
+static PyObject *
 PLy_info(PyObject * self, PyObject * args)
 {
 	return PLy_output(INFO, self, args);
 }
 
-PyObject *
+static PyObject *
 PLy_notice(PyObject * self, PyObject * args)
 {
 	return PLy_output(NOTICE, self, args);
 }
 
-PyObject *
+static PyObject *
 PLy_warning(PyObject * self, PyObject * args)
 {
 	return PLy_output(WARNING, self, args);
 }
 
-PyObject *
+static PyObject *
 PLy_error(PyObject * self, PyObject * args)
 {
 	return PLy_output(ERROR, self, args);
 }
 
-PyObject *
+static PyObject *
 PLy_fatal(PyObject * self, PyObject * args)
 {
 	return PLy_output(FATAL, self, args);
 }
 
 
-PyObject *
+static PyObject *
 PLy_output(volatile int level, PyObject * self, PyObject * args)
 {
-	DECLARE_EXC();
 	PyObject   *so;
 	char	   *volatile sv;
-
-	enter();
-
-	if (args == NULL)
-		elog(WARNING, "args is NULL");
+	MemoryContext oldcontext;
 
 	so = PyObject_Str(args);
 	if ((so == NULL) || ((sv = PyString_AsString(so)) == NULL))
@@ -2542,54 +2401,34 @@ PLy_output(volatile int level, PyObject * self, PyObject * args)
 		sv = "Unable to parse error message in `plpy.elog'";
 	}
 
-	/*
-	 * returning NULL here causes the python interpreter to bail. when
-	 * control passes back into plpython_*_handler, we check for python
-	 * exceptions and do the actual elog call.	actually PLy_elog.
-	 */
-	if (level == ERROR)
+	oldcontext = CurrentMemoryContext;
+	PG_TRY();
 	{
-		PyErr_SetString(PLy_exc_error, sv);
-		return NULL;
+		elog(level, "%s", sv);
 	}
-	else if (level >= FATAL)
+	PG_CATCH();
 	{
-		PyErr_SetString(PLy_exc_fatal, sv);
-		return NULL;
-	}
-
-	/*
-	 * ok, this is a WARNING, or LOG message
-	 *
-	 * but just in case DON'T long jump out of the interpreter!
-	 */
-	SAVE_EXC();
-	if (TRAP_EXC())
-	{
-		RESTORE_EXC();
-
+		MemoryContextSwitchTo(oldcontext);
+		PLy_error_in_progress = CopyErrorData();
+		FlushErrorState();
 		Py_XDECREF(so);
-
 		/*
-		 * the real error message should already be written into the
-		 * postgresql log, no?	whatever, this shouldn't happen so die
-		 * hideously.
+		 * returning NULL here causes the python interpreter to bail. when
+		 * control passes back to PLy_procedure_call, we check for PG
+		 * exceptions and re-throw the error.
 		 */
-		elog(FATAL, "elog threw an unknown exception");
-		RERAISE_EXC();
+		PyErr_SetString(PLy_exc_error, sv);
+		return NULL;
 	}
-
-	elog(level, "%s", sv);
-
-	RESTORE_EXC();
+	PG_END_TRY();
 
 	Py_XDECREF(so);
-	Py_INCREF(Py_None);
 
 	/*
 	 * return a legal object so the interpreter will continue on its merry
 	 * way
 	 */
+	Py_INCREF(Py_None);
 	return Py_None;
 }
 
@@ -2602,7 +2441,7 @@ PLy_output(volatile int level, PyObject * self, PyObject * args)
  * NB: this returns SQL name, not the internal Python procedure name
  */
 
-char *
+static char *
 PLy_procedure_name(PLyProcedure * proc)
 {
 	if (proc == NULL)
@@ -2613,12 +2452,7 @@ PLy_procedure_name(PLyProcedure * proc)
 /* output a python traceback/exception via the postgresql elog
  * function.  not pretty.
  */
-
-static char *PLy_traceback(int *);
-static char *PLy_vprintf(const char *fmt, va_list ap);
-static char *PLy_printf(const char *fmt,...);
-
-void
+static void
 PLy_exception_set(PyObject * exc, const char *fmt,...)
 {
 	char		buf[1024];
@@ -2631,53 +2465,45 @@ PLy_exception_set(PyObject * exc, const char *fmt,...)
 	PyErr_SetString(exc, buf);
 }
 
-void
+/* Emit a PG error or notice, together with any available info about the
+ * current Python error.  This should be used to propagate Python errors
+ * into PG.
+ */
+static void
 PLy_elog(int elevel, const char *fmt,...)
 {
-	DECLARE_EXC();
 	va_list		ap;
 	char	   *xmsg,
 			   *emsg;
 	int			xlevel;
 
-	enter();
-
 	xmsg = PLy_traceback(&xlevel);
 
 	va_start(ap, fmt);
 	emsg = PLy_vprintf(fmt, ap);
 	va_end(ap);
 
-	SAVE_EXC();
-	if (TRAP_EXC())
+	PG_TRY();
+	{
+		ereport(elevel,
+				(errmsg("plpython: %s", emsg),
+				 (xmsg) ? errdetail("%s", xmsg) : 0));
+	}
+	PG_CATCH();
 	{
-		RESTORE_EXC();
-		mark();
-
-		/*
-		 * elog called siglongjmp. cleanup, restore and reraise
-		 */
-		PLy_restart_in_progress += 1;
 		PLy_free(emsg);
 		if (xmsg)
 			PLy_free(xmsg);
-		RERAISE_EXC();
+		PG_RE_THROW();
 	}
-
-	ereport(elevel,
-			(errmsg("plpython: %s", emsg),
-			 (xmsg) ? errdetail("%s", xmsg) : 0));
+	PG_END_TRY();
 
 	PLy_free(emsg);
 	if (xmsg)
 		PLy_free(xmsg);
-
-	leave();
-
-	RESTORE_EXC();
 }
 
-char *
+static char *
 PLy_traceback(int *xlevel)
 {
 	PyObject   *e,
@@ -2689,8 +2515,6 @@ PLy_traceback(int *xlevel)
 			   *estr,
 			   *xstr = NULL;
 
-	enter();
-
 	/*
 	 * get the current exception
 	 */
@@ -2729,12 +2553,10 @@ PLy_traceback(int *xlevel)
 	else
 		*xlevel = ERROR;
 
-	leave();
-
 	return xstr;
 }
 
-char *
+static char *
 PLy_printf(const char *fmt,...)
 {
 	va_list		ap;
@@ -2746,7 +2568,7 @@ PLy_printf(const char *fmt,...)
 	return emsg;
 }
 
-char *
+static char *
 PLy_vprintf(const char *fmt, va_list ap)
 {
 	size_t		blen;
@@ -2783,7 +2605,7 @@ PLy_vprintf(const char *fmt, va_list ap)
 /* some dumb utility functions
  */
 
-void *
+static void *
 PLy_malloc(size_t bytes)
 {
 	void	   *ptr = malloc(bytes);
@@ -2795,7 +2617,7 @@ PLy_malloc(size_t bytes)
 	return ptr;
 }
 
-void *
+static void *
 PLy_realloc(void *optr, size_t bytes)
 {
 	void	   *nptr = realloc(optr, bytes);
@@ -2809,7 +2631,7 @@ PLy_realloc(void *optr, size_t bytes)
 
 /* define this away
  */
-void
+static void
 PLy_free(void *ptr)
 {
 	free(ptr);
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
deleted file mode 100644
index edd517675a50518c71eaca5fb170510a332d8bbf..0000000000000000000000000000000000000000
--- a/src/pl/plpython/plpython.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* $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 */
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index e51e56e6d7423fc7068ad851b982e64d5b021157..05c906f30f2b72cfccfd3237f021aad073cfe4d1 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -31,7 +31,7 @@
  *	  ENHANCEMENTS, OR MODIFICATIONS.
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.86 2004/06/06 00:41:28 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.87 2004/07/31 00:45:57 tgl Exp $
  *
  **********************************************************************/
 
@@ -41,7 +41,6 @@
 
 #include <unistd.h>
 #include <fcntl.h>
-#include <setjmp.h>
 
 /* Hack to deal with Tcl 8.4 const-ification without losing compatibility */
 #ifndef CONST84
@@ -132,8 +131,6 @@ typedef struct pltcl_query_desc
  **********************************************************************/
 static bool pltcl_pm_init_done = false;
 static bool pltcl_be_init_done = false;
-static int	pltcl_call_level = 0;
-static int	pltcl_restart_in_progress = 0;
 static Tcl_Interp *pltcl_hold_interp = NULL;
 static Tcl_Interp *pltcl_norm_interp = NULL;
 static Tcl_Interp *pltcl_safe_interp = NULL;
@@ -142,6 +139,19 @@ static Tcl_HashTable *pltcl_norm_query_hash = NULL;
 static Tcl_HashTable *pltcl_safe_query_hash = NULL;
 static FunctionCallInfo pltcl_current_fcinfo = NULL;
 
+/*
+ * When a callback from Tcl into PG incurs an error, we temporarily store
+ * the error information here, and return TCL_ERROR to the Tcl interpreter.
+ * Any further callback attempts immediately fail, and when the Tcl interpreter
+ * returns to the calling function, we re-throw the error (even if Tcl
+ * thinks it trapped the error and doesn't return TCL_ERROR).  Eventually
+ * this ought to be improved to let Tcl code really truly trap the error,
+ * but that's more of a change from the pre-7.5 semantics than I have time
+ * for now --- it will only be possible if the callback query is executed
+ * inside a subtransaction.
+ */
+static ErrorData *pltcl_error_in_progress = NULL;
+
 /**********************************************************************
  * Forward declarations
  **********************************************************************/
@@ -397,20 +407,10 @@ pltcl_call_handler(PG_FUNCTION_ARGS)
 	FunctionCallInfo save_fcinfo;
 
 	/************************************************************
-	 * Initialize interpreters
+	 * Initialize interpreters if first time through
 	 ************************************************************/
 	pltcl_init_all();
 
-	/************************************************************
-	 * Connect to SPI manager
-	 ************************************************************/
-	if (SPI_connect() != SPI_OK_CONNECT)
-		elog(ERROR, "could not connect to SPI manager");
-	/************************************************************
-	 * Keep track about the nesting of Tcl-SPI-Tcl-... calls
-	 ************************************************************/
-	pltcl_call_level++;
-
 	/************************************************************
 	 * Determine if called as function or trigger and
 	 * call appropriate subhandler
@@ -430,8 +430,6 @@ pltcl_call_handler(PG_FUNCTION_ARGS)
 
 	pltcl_current_fcinfo = save_fcinfo;
 
-	pltcl_call_level--;
-
 	return retval;
 }
 
@@ -461,7 +459,10 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
 	int			i;
 	int			tcl_rc;
 	Datum		retval;
-	sigjmp_buf	save_restart;
+
+	/* Connect to SPI manager */
+	if (SPI_connect() != SPI_OK_CONNECT)
+		elog(ERROR, "could not connect to SPI manager");
 
 	/* Find or compile the function */
 	prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid);
@@ -479,24 +480,11 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
 	Tcl_DStringInit(&list_tmp);
 	Tcl_DStringAppendElement(&tcl_cmd, prodesc->proname);
 
-	/************************************************************
-	 * Catch elog(ERROR) during build of the Tcl command
-	 ************************************************************/
-	memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
-	if (sigsetjmp(Warn_restart, 1) != 0)
-	{
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-		Tcl_DStringFree(&tcl_cmd);
-		Tcl_DStringFree(&list_tmp);
-		pltcl_restart_in_progress = 1;
-		if (--pltcl_call_level == 0)
-			pltcl_restart_in_progress = 0;
-		siglongjmp(Warn_restart, 1);
-	}
-
 	/************************************************************
 	 * Add all call arguments to the command
 	 ************************************************************/
+	PG_TRY();
+	{
 	for (i = 0; i < prodesc->nargs; i++)
 	{
 		if (prodesc->arg_is_rowtype[i])
@@ -552,60 +540,53 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
 			}
 		}
 	}
+	}
+	PG_CATCH();
+	{
+		Tcl_DStringFree(&tcl_cmd);
+		Tcl_DStringFree(&list_tmp);
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
 	Tcl_DStringFree(&list_tmp);
-	memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
 
 	/************************************************************
 	 * Call the Tcl function
+	 *
+	 * We assume no PG error can be thrown directly from this call.
 	 ************************************************************/
 	tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
 	Tcl_DStringFree(&tcl_cmd);
 
 	/************************************************************
-	 * Check the return code from Tcl and handle
-	 * our special restart mechanism to get rid
-	 * of all nested call levels on transaction
-	 * abort.
+	 * If there was an error in a PG callback, propagate that
+	 * no matter what Tcl claims about its success.
 	 ************************************************************/
-	if (tcl_rc != TCL_OK || pltcl_restart_in_progress)
+	if (pltcl_error_in_progress)
 	{
-		if (!pltcl_restart_in_progress)
-		{
-			pltcl_restart_in_progress = 1;
-			if (--pltcl_call_level == 0)
-				pltcl_restart_in_progress = 0;
-			UTF_BEGIN;
-			ereport(ERROR,
-					(errmsg("pltcl: %s", interp->result),
-					 errdetail("%s",
-							   UTF_U2E(Tcl_GetVar(interp, "errorInfo",
-												  TCL_GLOBAL_ONLY)))));
-			UTF_END;
-		}
-		if (--pltcl_call_level == 0)
-			pltcl_restart_in_progress = 0;
-		siglongjmp(Warn_restart, 1);
+		ErrorData *edata = pltcl_error_in_progress;
+
+		pltcl_error_in_progress = NULL;
+		ReThrowError(edata);
 	}
 
 	/************************************************************
-	 * Convert the result value from the Tcl interpreter
-	 * into its PostgreSQL data format and return it.
-	 * Again, the function call could fire an elog and we
-	 * have to count for the current interpreter level we are
-	 * on. The save_restart from above is still good.
+	 * Check for errors reported by Tcl itself.
 	 ************************************************************/
-	if (sigsetjmp(Warn_restart, 1) != 0)
+	if (tcl_rc != TCL_OK)
 	{
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-		pltcl_restart_in_progress = 1;
-		if (--pltcl_call_level == 0)
-			pltcl_restart_in_progress = 0;
-		siglongjmp(Warn_restart, 1);
+		UTF_BEGIN;
+		ereport(ERROR,
+				(errmsg("pltcl: %s", interp->result),
+				 errdetail("%s",
+						   UTF_U2E(Tcl_GetVar(interp, "errorInfo",
+											  TCL_GLOBAL_ONLY)))));
+		UTF_END;
 	}
 
 	/************************************************************
 	 * Disconnect from SPI manager and then create the return
-	 * values datum (if the input function does a palloc for it
+	 * value datum (if the input function does a palloc for it
 	 * this must not be allocated in the SPI memory context
 	 * because SPI_finish would free it).  But don't try to call
 	 * the result_in_func if we've been told to return a NULL;
@@ -627,11 +608,6 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
 		UTF_END;
 	}
 
-	/************************************************************
-	 * Finally we may restore normal error handling.
-	 ************************************************************/
-	memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-
 	return retval;
 }
 
@@ -653,15 +629,15 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
 	Tcl_DString tcl_newtup;
 	int			tcl_rc;
 	int			i;
-
 	int		   *modattrs;
 	Datum	   *modvalues;
 	char	   *modnulls;
-
 	int			ret_numvals;
 	CONST84 char **ret_values;
 
-	sigjmp_buf	save_restart;
+	/* Connect to SPI manager */
+	if (SPI_connect() != SPI_OK_CONNECT)
+		elog(ERROR, "could not connect to SPI manager");
 
 	/* Find or compile the function */
 	prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
@@ -681,23 +657,8 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
 	Tcl_DStringInit(&tcl_cmd);
 	Tcl_DStringInit(&tcl_trigtup);
 	Tcl_DStringInit(&tcl_newtup);
-
-	/************************************************************
-	 * We call external functions below - care for elog(ERROR)
-	 ************************************************************/
-	memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
-	if (sigsetjmp(Warn_restart, 1) != 0)
+	PG_TRY();
 	{
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-		Tcl_DStringFree(&tcl_cmd);
-		Tcl_DStringFree(&tcl_trigtup);
-		Tcl_DStringFree(&tcl_newtup);
-		pltcl_restart_in_progress = 1;
-		if (--pltcl_call_level == 0)
-			pltcl_restart_in_progress = 0;
-		siglongjmp(Warn_restart, 1);
-	}
-
 	/* The procedure name */
 	Tcl_DStringAppendElement(&tcl_cmd, prodesc->proname);
 
@@ -799,55 +760,54 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
 	else
 		elog(ERROR, "unrecognized LEVEL tg_event: %u", trigdata->tg_event);
 
-	memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-	Tcl_DStringFree(&tcl_trigtup);
-	Tcl_DStringFree(&tcl_newtup);
-
-	/************************************************************
-	 * Finally append the arguments from CREATE TRIGGER
-	 ************************************************************/
+	/* Finally append the arguments from CREATE TRIGGER */
 	for (i = 0; i < trigdata->tg_trigger->tgnargs; i++)
 		Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgargs[i]);
 
+	}
+	PG_CATCH();
+	{
+		Tcl_DStringFree(&tcl_cmd);
+		Tcl_DStringFree(&tcl_trigtup);
+		Tcl_DStringFree(&tcl_newtup);
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
+	Tcl_DStringFree(&tcl_trigtup);
+	Tcl_DStringFree(&tcl_newtup);
+
 	/************************************************************
 	 * Call the Tcl function
+	 *
+	 * We assume no PG error can be thrown directly from this call.
 	 ************************************************************/
 	tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
 	Tcl_DStringFree(&tcl_cmd);
 
 	/************************************************************
-	 * Check the return code from Tcl and handle
-	 * our special restart mechanism to get rid
-	 * of all nested call levels on transaction
-	 * abort.
+	 * If there was an error in a PG callback, propagate that
+	 * no matter what Tcl claims about its success.
 	 ************************************************************/
-	if (tcl_rc == TCL_ERROR || pltcl_restart_in_progress)
+	if (pltcl_error_in_progress)
 	{
-		if (!pltcl_restart_in_progress)
-		{
-			pltcl_restart_in_progress = 1;
-			if (--pltcl_call_level == 0)
-				pltcl_restart_in_progress = 0;
-			UTF_BEGIN;
-			ereport(ERROR,
-					(errmsg("pltcl: %s", interp->result),
-					 errdetail("%s",
-							   UTF_U2E(Tcl_GetVar(interp, "errorInfo",
-												  TCL_GLOBAL_ONLY)))));
-			UTF_END;
-		}
-		if (--pltcl_call_level == 0)
-			pltcl_restart_in_progress = 0;
-		siglongjmp(Warn_restart, 1);
+		ErrorData *edata = pltcl_error_in_progress;
+
+		pltcl_error_in_progress = NULL;
+		ReThrowError(edata);
 	}
 
-	switch (tcl_rc)
+	/************************************************************
+	 * Check for errors reported by Tcl itself.
+	 ************************************************************/
+	if (tcl_rc != TCL_OK)
 	{
-		case TCL_OK:
-			break;
-
-		default:
-			elog(ERROR, "unsupported TCL return code: %d", tcl_rc);
+		UTF_BEGIN;
+		ereport(ERROR,
+				(errmsg("pltcl: %s", interp->result),
+				 errdetail("%s",
+						   UTF_U2E(Tcl_GetVar(interp, "errorInfo",
+											  TCL_GLOBAL_ONLY)))));
+		UTF_END;
 	}
 
 	/************************************************************
@@ -871,11 +831,12 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
 		elog(ERROR, "could not split return value from trigger: %s",
 			 interp->result);
 
-	if (ret_numvals % 2 != 0)
+	/* Use a TRY to ensure ret_values will get freed */
+	PG_TRY();
 	{
-		ckfree((char *) ret_values);
+
+	if (ret_numvals % 2 != 0)
 		elog(ERROR, "invalid return list from trigger - must have even # of elements");
-	}
 
 	modattrs = (int *) palloc(tupdesc->natts * sizeof(int));
 	modvalues = (Datum *) palloc(tupdesc->natts * sizeof(Datum));
@@ -888,19 +849,6 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
 	modnulls = palloc(tupdesc->natts);
 	memset(modnulls, 'n', tupdesc->natts);
 
-	/************************************************************
-	 * Care for possible elog(ERROR)'s below
-	 ************************************************************/
-	if (sigsetjmp(Warn_restart, 1) != 0)
-	{
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-		ckfree((char *) ret_values);
-		pltcl_restart_in_progress = 1;
-		if (--pltcl_call_level == 0)
-			pltcl_restart_in_progress = 0;
-		siglongjmp(Warn_restart, 1);
-	}
-
 	for (i = 0; i < ret_numvals; i += 2)
 	{
 		CONST84 char *ret_name = ret_values[i];
@@ -970,8 +918,14 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
 	if (rettup == NULL)
 		elog(ERROR, "SPI_modifytuple() failed - RC = %d", SPI_result);
 
+	}
+	PG_CATCH();
+	{
+		ckfree((char *) ret_values);
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
 	ckfree((char *) ret_values);
-	memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
 
 	return rettup;
 }
@@ -1317,13 +1271,16 @@ pltcl_elog(ClientData cdata, Tcl_Interp *interp,
 		   int argc, CONST84 char *argv[])
 {
 	volatile int level;
-	sigjmp_buf	save_restart;
+	MemoryContext oldcontext;
 
 	/************************************************************
-	 * Suppress messages during the restart process
+	 * Suppress messages if an error is already declared
 	 ************************************************************/
-	if (pltcl_restart_in_progress)
+	if (pltcl_error_in_progress)
+	{
+		Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
 		return TCL_ERROR;
+	}
 
 	if (argc != 3)
 	{
@@ -1354,26 +1311,25 @@ pltcl_elog(ClientData cdata, Tcl_Interp *interp,
 	}
 
 	/************************************************************
-	 * Catch the longjmp from elog() and begin a controlled
-	 * return though all interpreter levels if it happens
+	 * If elog() throws an error, catch and save it, then return
+	 * error indication to Tcl interpreter.
 	 ************************************************************/
-	memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
-	if (sigsetjmp(Warn_restart, 1) != 0)
+	oldcontext = CurrentMemoryContext;
+	PG_TRY();
+	{
+		UTF_BEGIN;
+		elog(level, "%s", UTF_U2E(argv[2]));
+		UTF_END;
+	}
+	PG_CATCH();
 	{
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-		pltcl_restart_in_progress = 1;
+		MemoryContextSwitchTo(oldcontext);
+		pltcl_error_in_progress = CopyErrorData();
+		FlushErrorState();
 		return TCL_ERROR;
 	}
+	PG_END_TRY();
 
-	/************************************************************
-	 * Call elog(), restore the original restart address
-	 * and return to the caller (if no longjmp)
-	 ************************************************************/
-	UTF_BEGIN;
-	elog(level, "%s", UTF_U2E(argv[2]));
-	UTF_END;
-
-	memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
 	return TCL_OK;
 }
 
@@ -1535,6 +1491,7 @@ static int
 pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp,
 			   int argc, CONST84 char *argv[])
 {
+	volatile int my_rc;
 	int			spi_rc;
 	char		buf[64];
 	int			count = 0;
@@ -1546,17 +1503,20 @@ pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp,
 	HeapTuple  *volatile tuples;
 	volatile TupleDesc tupdesc = NULL;
 	SPITupleTable *tuptable;
-	sigjmp_buf	save_restart;
+	MemoryContext oldcontext;
 
 	char	   *usage = "syntax error - 'SPI_exec "
 	"?-count n? "
 	"?-array name? query ?loop body?";
 
 	/************************************************************
-	 * Don't do anything if we are already in restart mode
+	 * Don't do anything if we are already in error mode
 	 ************************************************************/
-	if (pltcl_restart_in_progress)
+	if (pltcl_error_in_progress)
+	{
+		Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
 		return TCL_ERROR;
+	}
 
 	/************************************************************
 	 * Check the call syntax and get the count option
@@ -1604,25 +1564,24 @@ pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp,
 	}
 
 	/************************************************************
-	 * Prepare to start a controlled return through all
-	 * interpreter levels on transaction abort
+	 * Execute the query and handle return codes
 	 ************************************************************/
-	memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
-	if (sigsetjmp(Warn_restart, 1) != 0)
+	oldcontext = CurrentMemoryContext;
+	PG_TRY();
 	{
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-		pltcl_restart_in_progress = 1;
-		Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE);
+		UTF_BEGIN;
+		spi_rc = SPI_exec(UTF_U2E(argv[query_idx]), count);
+		UTF_END;
+	}
+	PG_CATCH();
+	{
+		MemoryContextSwitchTo(oldcontext);
+		pltcl_error_in_progress = CopyErrorData();
+		FlushErrorState();
+		Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
 		return TCL_ERROR;
 	}
-
-	/************************************************************
-	 * Execute the query and handle return codes
-	 ************************************************************/
-	UTF_BEGIN;
-	spi_rc = SPI_exec(UTF_U2E(argv[query_idx]), count);
-	UTF_END;
-	memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
+	PG_END_TRY();
 
 	switch (spi_rc)
 	{
@@ -1687,83 +1646,80 @@ pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp,
 	}
 
 	/************************************************************
-	 * Only SELECT queries fall through to here - remember the
-	 * tuples we got
+	 * Only SELECT queries fall through to here - process the tuples we got
 	 ************************************************************/
-
 	ntuples = SPI_processed;
+	tuptable = SPI_tuptable;
 	if (ntuples > 0)
 	{
-		tuples = SPI_tuptable->vals;
-		tupdesc = SPI_tuptable->tupdesc;
-	}
-
-	/************************************************************
-	 * Again prepare for elog(ERROR)
-	 ************************************************************/
-	if (sigsetjmp(Warn_restart, 1) != 0)
-	{
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-		pltcl_restart_in_progress = 1;
-		Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE);
-		return TCL_ERROR;
-	}
-
-	/************************************************************
-	 * If there is no loop body given, just set the variables
-	 * from the first tuple (if any) and return the number of
-	 * tuples selected
-	 ************************************************************/
-	if (argc == query_idx + 1)
-	{
-		if (ntuples > 0)
-			pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc);
-		snprintf(buf, sizeof(buf), "%d", ntuples);
-		Tcl_SetResult(interp, buf, TCL_VOLATILE);
-		SPI_freetuptable(SPI_tuptable);
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-		return TCL_OK;
+		tuples = tuptable->vals;
+		tupdesc = tuptable->tupdesc;
 	}
 
-	tuptable = SPI_tuptable;
-
-	/************************************************************
-	 * There is a loop body - process all tuples and evaluate
-	 * the body on each
-	 ************************************************************/
-	query_idx++;
-	for (i = 0; i < ntuples; i++)
+	my_rc = TCL_OK;
+	PG_TRY();
 	{
-		pltcl_set_tuple_values(interp, arrayname, i, tuples[i], tupdesc);
+		if (argc == query_idx + 1)
+		{
+			/************************************************************
+			 * If there is no loop body given, just set the variables
+			 * from the first tuple (if any)
+			 ************************************************************/
+			if (ntuples > 0)
+				pltcl_set_tuple_values(interp, arrayname, 0,
+									   tuples[0], tupdesc);
+		}
+		else
+		{
+			/************************************************************
+			 * There is a loop body - process all tuples and evaluate
+			 * the body on each
+			 ************************************************************/
+			query_idx++;
+			for (i = 0; i < ntuples; i++)
+			{
+				pltcl_set_tuple_values(interp, arrayname, i,
+									   tuples[i], tupdesc);
 
-		loop_rc = Tcl_Eval(interp, argv[query_idx]);
+				loop_rc = Tcl_Eval(interp, argv[query_idx]);
 
-		if (loop_rc == TCL_OK)
-			continue;
-		if (loop_rc == TCL_CONTINUE)
-			continue;
-		if (loop_rc == TCL_RETURN)
-		{
-			SPI_freetuptable(tuptable);
-			memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-			return TCL_RETURN;
+				if (loop_rc == TCL_OK)
+					continue;
+				if (loop_rc == TCL_CONTINUE)
+					continue;
+				if (loop_rc == TCL_RETURN)
+				{
+					my_rc = TCL_RETURN;
+					break;
+				}
+				if (loop_rc == TCL_BREAK)
+					break;
+				my_rc = TCL_ERROR;
+				break;
+			}
 		}
-		if (loop_rc == TCL_BREAK)
-			break;
+
 		SPI_freetuptable(tuptable);
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
+	}
+	PG_CATCH();
+	{
+		MemoryContextSwitchTo(oldcontext);
+		pltcl_error_in_progress = CopyErrorData();
+		FlushErrorState();
+		Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
 		return TCL_ERROR;
 	}
-
-	SPI_freetuptable(tuptable);
+	PG_END_TRY();
 
 	/************************************************************
 	 * Finally return the number of tuples
 	 ************************************************************/
-	memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-	snprintf(buf, sizeof(buf), "%d", ntuples);
-	Tcl_SetResult(interp, buf, TCL_VOLATILE);
-	return TCL_OK;
+	if (my_rc == TCL_OK)
+	{
+		snprintf(buf, sizeof(buf), "%d", ntuples);
+		Tcl_SetResult(interp, buf, TCL_VOLATILE);
+	}
+	return my_rc;
 }
 
 
@@ -1787,14 +1743,17 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
 	HeapTuple	typeTup;
 	Tcl_HashEntry *hashent;
 	int			hashnew;
-	sigjmp_buf	save_restart;
 	Tcl_HashTable *query_hash;
+	MemoryContext oldcontext;
 
 	/************************************************************
-	 * Don't do anything if we are already in restart mode
+	 * Don't do anything if we are already in error mode
 	 ************************************************************/
-	if (pltcl_restart_in_progress)
+	if (pltcl_error_in_progress)
+	{
+		Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
 		return TCL_ERROR;
+	}
 
 	/************************************************************
 	 * Check the call syntax
@@ -1822,23 +1781,9 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
 	qdesc->arginfuncs = (FmgrInfo *) malloc(nargs * sizeof(FmgrInfo));
 	qdesc->argtypioparams = (Oid *) malloc(nargs * sizeof(Oid));
 
-	/************************************************************
-	 * Prepare to start a controlled return through all
-	 * interpreter levels on transaction abort
-	 ************************************************************/
-	memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
-	if (sigsetjmp(Warn_restart, 1) != 0)
+	oldcontext = CurrentMemoryContext;
+	PG_TRY();
 	{
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-		pltcl_restart_in_progress = 1;
-		free(qdesc->argtypes);
-		free(qdesc->arginfuncs);
-		free(qdesc->argtypioparams);
-		free(qdesc);
-		ckfree((char *) args);
-		return TCL_ERROR;
-	}
-
 	/************************************************************
 	 * Lookup the argument types by name in the system cache
 	 * and remember the required information for input conversion
@@ -1882,10 +1827,7 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
 	UTF_END;
 
 	if (plan == NULL)
-	{
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
 		elog(ERROR, "SPI_prepare() failed");
-	}
 
 	/************************************************************
 	 * Save the plan into permanent memory (right now it's in the
@@ -1893,10 +1835,8 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
 	 ************************************************************/
 	qdesc->plan = SPI_saveplan(plan);
 	if (qdesc->plan == NULL)
-	{
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
 		elog(ERROR, "SPI_saveplan() failed");
-	}
+
 	/* Release the procCxt copy to avoid within-function memory leak */
 	SPI_freeplan(plan);
 
@@ -1909,7 +1849,21 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
 	else
 		query_hash = pltcl_safe_query_hash;
 
-	memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
+	}
+	PG_CATCH();
+	{
+		MemoryContextSwitchTo(oldcontext);
+		pltcl_error_in_progress = CopyErrorData();
+		FlushErrorState();
+		free(qdesc->argtypes);
+		free(qdesc->arginfuncs);
+		free(qdesc->argtypioparams);
+		free(qdesc);
+		ckfree((char *) args);
+		Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
+		return TCL_ERROR;
+	}
+	PG_END_TRY();
 
 	hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
 	Tcl_SetHashValue(hashent, (ClientData) qdesc);
@@ -1928,6 +1882,7 @@ static int
 pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
 				int argc, CONST84 char *argv[])
 {
+	volatile int my_rc;
 	int			spi_rc;
 	char		buf[64];
 	volatile int i;
@@ -1940,13 +1895,13 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
 	CONST84 char *volatile arrayname = NULL;
 	int			count = 0;
 	int			callnargs;
-	static CONST84 char **callargs = NULL;
+	CONST84 char **callargs;
 	int			loop_rc;
 	int			ntuples;
 	HeapTuple  *volatile tuples = NULL;
 	volatile TupleDesc tupdesc = NULL;
 	SPITupleTable *tuptable;
-	sigjmp_buf	save_restart;
+	volatile MemoryContext oldcontext;
 	Tcl_HashTable *query_hash;
 
 	char	   *usage = "syntax error - 'SPI_execp "
@@ -1954,19 +1909,13 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
 	"?-array name? query ?args? ?loop body?";
 
 	/************************************************************
-	 * Tidy up from an earlier abort
+	 * Don't do anything if we are already in error mode
 	 ************************************************************/
-	if (callargs != NULL)
+	if (pltcl_error_in_progress)
 	{
-		ckfree((char *) callargs);
-		callargs = NULL;
-	}
-
-	/************************************************************
-	 * Don't do anything if we are already in restart mode
-	 ************************************************************/
-	if (pltcl_restart_in_progress)
+		Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
 		return TCL_ERROR;
+	}
 
 	/************************************************************
 	 * Get the options and check syntax
@@ -2074,27 +2023,7 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
 			Tcl_SetResult(interp,
 			"argument list length doesn't match # of arguments for query",
 						  TCL_VOLATILE);
-			if (callargs != NULL)
-			{
-				ckfree((char *) callargs);
-				callargs = NULL;
-			}
-			return TCL_ERROR;
-		}
-
-		/************************************************************
-		 * Prepare to start a controlled return through all
-		 * interpreter levels on transaction abort during the
-		 * parse of the arguments
-		 ************************************************************/
-		memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
-		if (sigsetjmp(Warn_restart, 1) != 0)
-		{
-			memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
 			ckfree((char *) callargs);
-			callargs = NULL;
-			pltcl_restart_in_progress = 1;
-			Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE);
 			return TCL_ERROR;
 		}
 
@@ -2102,33 +2031,42 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
 		 * Setup the value array for the SPI_execp() using
 		 * the type specific input functions
 		 ************************************************************/
-		argvalues = (Datum *) palloc(callnargs * sizeof(Datum));
-
-		for (j = 0; j < callnargs; j++)
+		oldcontext = CurrentMemoryContext;
+		PG_TRY();
 		{
-			if (nulls && nulls[j] == 'n')
-			{
-				/* don't try to convert the input for a null */
-				argvalues[j] = (Datum) 0;
-			}
-			else
+			argvalues = (Datum *) palloc(callnargs * sizeof(Datum));
+
+			for (j = 0; j < callnargs; j++)
 			{
-				UTF_BEGIN;
-				argvalues[j] =
-					FunctionCall3(&qdesc->arginfuncs[j],
-								  CStringGetDatum(UTF_U2E(callargs[j])),
-								  ObjectIdGetDatum(qdesc->argtypioparams[j]),
-								  Int32GetDatum(-1));
-				UTF_END;
+				if (nulls && nulls[j] == 'n')
+				{
+					/* don't try to convert the input for a null */
+					argvalues[j] = (Datum) 0;
+				}
+				else
+				{
+					UTF_BEGIN;
+					argvalues[j] =
+						FunctionCall3(&qdesc->arginfuncs[j],
+									  CStringGetDatum(UTF_U2E(callargs[j])),
+									  ObjectIdGetDatum(qdesc->argtypioparams[j]),
+									  Int32GetDatum(-1));
+					UTF_END;
+				}
 			}
 		}
+		PG_CATCH();
+		{
+			ckfree((char *) callargs);
+			MemoryContextSwitchTo(oldcontext);
+			pltcl_error_in_progress = CopyErrorData();
+			FlushErrorState();
+			Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
+			return TCL_ERROR;
+		}
+		PG_END_TRY();
 
-		/************************************************************
-		 * Free the splitted argument value list
-		 ************************************************************/
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
 		ckfree((char *) callargs);
-		callargs = NULL;
 	}
 	else
 		callnargs = 0;
@@ -2140,23 +2078,22 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
 	loop_body = i;
 
 	/************************************************************
-	 * Prepare to start a controlled return through all
-	 * interpreter levels on transaction abort
+	 * Execute the plan
 	 ************************************************************/
-	memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
-	if (sigsetjmp(Warn_restart, 1) != 0)
+	oldcontext = CurrentMemoryContext;
+	PG_TRY();
 	{
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-		pltcl_restart_in_progress = 1;
-		Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE);
+		spi_rc = SPI_execp(qdesc->plan, argvalues, nulls, count);
+	}
+	PG_CATCH();
+	{
+		MemoryContextSwitchTo(oldcontext);
+		pltcl_error_in_progress = CopyErrorData();
+		FlushErrorState();
+		Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
 		return TCL_ERROR;
 	}
-
-	/************************************************************
-	 * Execute the plan
-	 ************************************************************/
-	spi_rc = SPI_execp(qdesc->plan, argvalues, nulls, count);
-	memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
+	PG_END_TRY();
 
 	/************************************************************
 	 * Check the return code from SPI_execp()
@@ -2224,85 +2161,79 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
 	}
 
 	/************************************************************
-	 * Only SELECT queries fall through to here - remember the
-	 * tuples we got
+	 * Only SELECT queries fall through to here - process the tuples we got
 	 ************************************************************/
-
 	ntuples = SPI_processed;
+	tuptable = SPI_tuptable;
 	if (ntuples > 0)
 	{
-		tuples = SPI_tuptable->vals;
-		tupdesc = SPI_tuptable->tupdesc;
+		tuples = tuptable->vals;
+		tupdesc = tuptable->tupdesc;
 	}
 
-	/************************************************************
-	 * Prepare to start a controlled return through all
-	 * interpreter levels on transaction abort during
-	 * the ouput conversions of the results
-	 ************************************************************/
-	memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
-	if (sigsetjmp(Warn_restart, 1) != 0)
+	my_rc = TCL_OK;
+	PG_TRY();
 	{
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-		pltcl_restart_in_progress = 1;
-		Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE);
-		return TCL_ERROR;
-	}
-
-	/************************************************************
-	 * If there is no loop body given, just set the variables
-	 * from the first tuple (if any) and return the number of
-	 * tuples selected
-	 ************************************************************/
-	if (loop_body >= argc)
-	{
-		if (ntuples > 0)
-			pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc);
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-		snprintf(buf, sizeof(buf), "%d", ntuples);
-		Tcl_SetResult(interp, buf, TCL_VOLATILE);
-		SPI_freetuptable(SPI_tuptable);
-		return TCL_OK;
-	}
-
-	tuptable = SPI_tuptable;
-
-	/************************************************************
-	 * There is a loop body - process all tuples and evaluate
-	 * the body on each
-	 ************************************************************/
-	for (i = 0; i < ntuples; i++)
-	{
-		pltcl_set_tuple_values(interp, arrayname, i, tuples[i], tupdesc);
+		if (loop_body >= argc)
+		{
+			/************************************************************
+			 * If there is no loop body given, just set the variables
+			 * from the first tuple (if any)
+			 ************************************************************/
+			if (ntuples > 0)
+				pltcl_set_tuple_values(interp, arrayname, 0,
+									   tuples[0], tupdesc);
+		}
+		else
+		{
+			/************************************************************
+			 * There is a loop body - process all tuples and evaluate
+			 * the body on each
+			 ************************************************************/
+			for (i = 0; i < ntuples; i++)
+			{
+				pltcl_set_tuple_values(interp, arrayname, i,
+									   tuples[i], tupdesc);
 
-		loop_rc = Tcl_Eval(interp, argv[loop_body]);
+				loop_rc = Tcl_Eval(interp, argv[loop_body]);
 
-		if (loop_rc == TCL_OK)
-			continue;
-		if (loop_rc == TCL_CONTINUE)
-			continue;
-		if (loop_rc == TCL_RETURN)
-		{
-			SPI_freetuptable(tuptable);
-			memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-			return TCL_RETURN;
+				if (loop_rc == TCL_OK)
+					continue;
+				if (loop_rc == TCL_CONTINUE)
+					continue;
+				if (loop_rc == TCL_RETURN)
+				{
+					my_rc = TCL_RETURN;
+					break;
+				}
+				if (loop_rc == TCL_BREAK)
+					break;
+				my_rc = TCL_ERROR;
+				break;
+			}
 		}
-		if (loop_rc == TCL_BREAK)
-			break;
+
 		SPI_freetuptable(tuptable);
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
+	}
+	PG_CATCH();
+	{
+		MemoryContextSwitchTo(oldcontext);
+		pltcl_error_in_progress = CopyErrorData();
+		FlushErrorState();
+		Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
 		return TCL_ERROR;
 	}
-
-	SPI_freetuptable(tuptable);
+	PG_END_TRY();
 
 	/************************************************************
 	 * Finally return the number of tuples
 	 ************************************************************/
-	memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-	snprintf(buf, sizeof(buf), "%d", ntuples);
-	Tcl_SetResult(interp, buf, TCL_VOLATILE);
-	return TCL_OK;
+	if (my_rc == TCL_OK)
+	{
+		snprintf(buf, sizeof(buf), "%d", ntuples);
+		Tcl_SetResult(interp, buf, TCL_VOLATILE);
+	}
+	return my_rc;
 }