Hi,

On Tuesday 19 October 2010 16:18:29 Kevin Grittner wrote:
> Andres Freund <and...@anarazel.de> wrote:
> > Here is a proposed patch which enables cancellation of $subject.
> 
> Cool.  Some enhancements we'd like to do to Serializable Snapshot
> Isolation (SSI), should the base patch make it in, would require
> this capability.
> 
> > Currently it does *not* report any special error message to the
> > client if it
> > 
> > starts sending commands in an (unbekownst to it) failed
> > transaction, but just the normal "25P02: current transaction is
> > aborted..." message.
> > 
> > It shouldn't be hard to add that and I will propose a patch if
> > people would like it (I personally am not very interested, but I
> > can see people validly wanting it)
> 
> For SSI purposes, it would be highly desirable to be able to set the
> SQLSTATE and message generated when the canceled transaction
> terminates.
Ok, I implemented that capability, but the patch feels somewhat wrong to me, 
so its a separate patch on top the others:

* it intermingles logic between elog.c and postgres.c far too much - requiring 
exporting variables which should not get exported. And it would still need 
more to allow some sensible Assert()s. I.e. Assert(DoingCommandRead) would 
need to be available in elog.c to avoid somebody using the re-throwing 
capability out of place....

* You wont see an error if the next command after the IDLE in transaction is a 
COMMIT/ROLLBACK. I don’t see any sensible way around that.

* the copied edata lives in TopMemoryContext and gets only reset in the next 
reported error, after the re-raised error was reported.

That’s how it looks like to raise one such error right now:

error = ERROR
if (DoingCommandRead)
{
        silent_error_while_idle = true;
        error |= LOG_NO_CLIENT|LOG_RE_THROW_AFTER_SYNC;
}

...

ereport(error,
        (errcode(ERRCODE_QUERY_CANCELED),
         errmsg("canceling statement due to user request")));



One could mingle together  LOG_NO_CLIENT|LOG_RE_THROW_AFTER_SYNC into a macro 
and set silent_error_while_idle in elog.c, but I don’t see many callsites 
coming up, so I think its better to be explicit.

Ill set this up for the next commitfest, I don't think I can do much more 
without further input.

Andres
From 06541b25fc11a8f17ec401de5a17eeae1bad57d1 Mon Sep 17 00:00:00 2001
From: Andres Freund <and...@anarazel.de>
Date: Fri, 21 May 2010 20:17:11 +0200
Subject: [PATCH 2/3] Implement cancellation of backends in IDLE IN TRANSACTION state.

Not having support for this was the reason for HS FATALing backends
which had a lock conflict for longer than
max_standby_(archive|standby)_delay as it couldnt cancel them without
them loosing sync with the frontend. As this is not the case anymore
fail the transaction "silently". Possibly one day the protocol can
cope with this...
---
 src/backend/tcop/postgres.c |   80 +++++++++++++++++++++++++++++++------------
 1 files changed, 58 insertions(+), 22 deletions(-)

diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index cba90a9..505d136 100644
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
*************** static bool RecoveryConflictPending = fa
*** 177,182 ****
--- 177,188 ----
  static bool RecoveryConflictRetryable = true;
  static ProcSignalReason RecoveryConflictReason;
  
+ /*
+  * Are we disallowed from sending a "ready for query" message right
+  * now because it would confuse the frontend?
+  */
+ static bool silent_error_while_idle = false;
+ 
  /* ----------------------------------------------------------------
   *		decls for routines only used in this file
   * ----------------------------------------------------------------
*************** RecoveryConflictInterrupt(ProcSignalReas
*** 2877,2882 ****
--- 2883,2890 ----
  void
  ProcessInterrupts(void)
  {
+ 	int error = ERROR;
+ 
  	/* OK to accept interrupt now? */
  	if (InterruptHoldoffCount != 0 || CritSectionCount != 0)
  		return;
*************** ProcessInterrupts(void)
*** 2949,2966 ****
  			RecoveryConflictPending = false;
  			DisableNotifyInterrupt();
  			DisableCatchupInterrupt();
! 			if (DoingCommandRead)
! 				ereport(FATAL,
! 						(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
! 						 errmsg("terminating connection due to conflict with recovery"),
! 						 errdetail_recovery_conflict(),
! 				 errhint("In a moment you should be able to reconnect to the"
! 						 " database and repeat your command.")));
! 			else
! 				ereport(ERROR,
! 						(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
! 				 errmsg("canceling statement due to conflict with recovery"),
! 						 errdetail_recovery_conflict()));
  		}
  
  		/*
--- 2957,2980 ----
  			RecoveryConflictPending = false;
  			DisableNotifyInterrupt();
  			DisableCatchupInterrupt();
! 
! 			if (DoingCommandRead){
! 				/*
! 				 * We cant issue a normal ERROR here because the
! 				 * client doesnt expect the server to send an error at
! 				 * that point.
! 				 * We also may not send a "ready for query"/Z message
! 				 * because that would be unexpected as well.
! 				 */
! 				silent_error_while_idle = true;
! 				error |= LOG_NO_CLIENT;
! 
! 			}
! 
! 			ereport(error,
! 			        (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
! 			         errmsg("canceling statement due to conflict with recovery"),
! 			         errdetail_recovery_conflict()));
  		}
  
  		/*
*************** ProcessInterrupts(void)
*** 2968,2982 ****
  		 * request --- sending an extra error message won't accomplish
  		 * anything.  Otherwise, go ahead and throw the error.
  		 */
! 		if (!DoingCommandRead)
  		{
! 			ImmediateInterruptOK = false;		/* not idle anymore */
! 			DisableNotifyInterrupt();
! 			DisableCatchupInterrupt();
! 			ereport(ERROR,
! 					(errcode(ERRCODE_QUERY_CANCELED),
! 					 errmsg("canceling statement due to user request")));
  		}
  	}
  	/* If we get here, do nothing (probably, QueryCancelPending was reset) */
  }
--- 2982,2999 ----
  		 * request --- sending an extra error message won't accomplish
  		 * anything.  Otherwise, go ahead and throw the error.
  		 */
! 		if (DoingCommandRead)
  		{
! 			silent_error_while_idle = true;
! 			error |= LOG_NO_CLIENT;
  		}
+ 
+ 		ImmediateInterruptOK = false;		/* not idle anymore */
+ 		DisableNotifyInterrupt();
+ 		DisableCatchupInterrupt();
+ 		ereport(error,
+ 		        (errcode(ERRCODE_QUERY_CANCELED),
+ 		         errmsg("canceling statement due to user request")));
  	}
  	/* If we get here, do nothing (probably, QueryCancelPending was reset) */
  }
*************** PostgresMain(int argc, char *argv[], con
*** 3814,3824 ****
  				set_ps_display("idle", false);
  				pgstat_report_activity("<IDLE>");
  			}
! 
! 			ReadyForQuery(whereToSendOutput);
! 			send_ready_for_query = false;
  		}
  
  		/*
  		 * (2) Allow asynchronous signals to be executed immediately if they
  		 * come in while we are waiting for client input. (This must be
--- 3831,3843 ----
  				set_ps_display("idle", false);
  				pgstat_report_activity("<IDLE>");
  			}
! 			if(!silent_error_while_idle){
! 				ReadyForQuery(whereToSendOutput);
! 				send_ready_for_query = false;
! 			}
  		}
  
+ 
  		/*
  		 * (2) Allow asynchronous signals to be executed immediately if they
  		 * come in while we are waiting for client input. (This must be
*************** PostgresMain(int argc, char *argv[], con
*** 3854,3859 ****
--- 3873,3895 ----
  		if (ignore_till_sync && firstchar != EOF)
  			continue;
  
+ 		if(silent_error_while_idle && !doing_extended_query_message)
+ 		{
+ 			/*
+ 			 * When using the simple protocol:
+ 			 * At this point we have processed a ReadCommand which means
+ 			 * that we got some input from the frontend and thus can start
+ 			 * spewing errors again
+ 
+ 			 * When using the extended protocol:
+ 			 * doing_extended_query_message is false after a 'sync'
+ 			 * message - which is exactly the point after which we can
+ 			 * start sending errors again.
+ 			 */
+ 			silent_error_while_idle = false;
+ 		}
+ 
+ 
  		switch (firstchar)
  		{
  			case 'Q':			/* simple query */
-- 
1.7.3.rc1.5.g73aa2

From bd0df4139bf045edd3400ee1b321009a4d2754d3 Mon Sep 17 00:00:00 2001
From: Andres Freund <and...@anarazel.de>
Date: Sat, 13 Feb 2010 22:21:15 +0100
Subject: [PATCH 1/3] Support transporting flags in the 'elevel' argument of ereport(). The
 reason is the wish to support avoiding the error to the client which
 was only possible using the COMERROR level - which is not a error but
 just a log message.
 The only supported flag till now is LOG_NO_CLIENT which does what
 COMERROR did for all error levels

---
 src/backend/utils/error/elog.c |    8 +++++---
 src/include/utils/elog.h       |   29 ++++++++++++++++++++---------
 2 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index e321b99..6e174d8 100644
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
*************** errstart(int elevel, const char *filenam
*** 214,220 ****
  	bool		output_to_server;
  	bool		output_to_client = false;
  	int			i;
! 
  	/*
  	 * Check some cases in which we want to promote an error into a more
  	 * severe error.  None of this logic applies for non-error messages.
--- 214,221 ----
  	bool		output_to_server;
  	bool		output_to_client = false;
  	int			i;
! 	int			eflags = elevel & LOG_FLAG_MASK;
! 	elevel = elevel & ~LOG_FLAG_MASK;
  	/*
  	 * Check some cases in which we want to promote an error into a more
  	 * severe error.  None of this logic applies for non-error messages.
*************** errstart(int elevel, const char *filenam
*** 274,280 ****
  		output_to_server = (elevel >= log_min_messages);
  
  	/* Determine whether message is enabled for client output */
! 	if (whereToSendOutput == DestRemote && elevel != COMMERROR)
  	{
  		/*
  		 * client_min_messages is honored only after we complete the
--- 275,281 ----
  		output_to_server = (elevel >= log_min_messages);
  
  	/* Determine whether message is enabled for client output */
! 	if (whereToSendOutput == DestRemote)
  	{
  		/*
  		 * client_min_messages is honored only after we complete the
*************** errstart(int elevel, const char *filenam
*** 333,338 ****
--- 334,340 ----
  	edata = &errordata[errordata_stack_depth];
  	MemSet(edata, 0, sizeof(ErrorData));
  	edata->elevel = elevel;
+ 	edata->eflags = eflags;
  	edata->output_to_server = output_to_server;
  	edata->output_to_client = output_to_client;
  	edata->filename = filename;
*************** EmitErrorReport(void)
*** 1168,1174 ****
  		send_message_to_server_log(edata);
  
  	/* Send to client, if enabled */
! 	if (edata->output_to_client)
  		send_message_to_frontend(edata);
  
  	MemoryContextSwitchTo(oldcontext);
--- 1170,1176 ----
  		send_message_to_server_log(edata);
  
  	/* Send to client, if enabled */
! 	if (edata->output_to_client && !(edata->eflags & LOG_NO_CLIENT))
  		send_message_to_frontend(edata);
  
  	MemoryContextSwitchTo(oldcontext);
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 92641ba..1853e1d 100644
*** a/src/include/utils/elog.h
--- b/src/include/utils/elog.h
***************
*** 16,21 ****
--- 16,28 ----
  
  #include <setjmp.h>
  
+ #define LOG_FLAG_MASK ((~0)<<20)
+ 
+ /* logging flags */
+ #define LOG_NO_CLIENT (2<<30)   /* Don't send to the client. Helpfull
+                                  * if it would confuse the client at
+                                  * that moment */
+ 
  /* Error level codes */
  #define DEBUG5		10			/* Debugging messages, in categories of
  								 * decreasing detail. */
***************
*** 25,54 ****
  #define DEBUG1		14			/* used by GUC debug_* variables */
  #define LOG			15			/* Server operational messages; sent only to
  								 * server log by default. */
! #define COMMERROR	16			/* Client communication problems; same as LOG
  								 * for server reporting, but never sent to
! 								 * client. */
! #define INFO		17			/* Messages specifically requested by user (eg
  								 * VACUUM VERBOSE output); always sent to
  								 * client regardless of client_min_messages,
  								 * but by default not sent to server log. */
! #define NOTICE		18			/* Helpful messages to users about query
  								 * operation; sent to client and server log by
  								 * default. */
! #define WARNING		19			/* Warnings.  NOTICE is for expected messages
  								 * like implicit sequence creation by SERIAL.
  								 * WARNING is for unexpected messages. */
! #define ERROR		20			/* user error - abort transaction; return to
  								 * known state */
  /* Save ERROR value in PGERROR so it can be restored when Win32 includes
   * modify it.  We have to use a constant rather than ERROR because macros
   * are expanded only when referenced outside macros.
   */
  #ifdef WIN32
! #define PGERROR		20
  #endif
! #define FATAL		21			/* fatal error - abort process */
! #define PANIC		22			/* take down the other backends with me */
  
   /* #define DEBUG DEBUG1 */	/* Backward compatibility with pre-7.3 */
  
--- 32,64 ----
  #define DEBUG1		14			/* used by GUC debug_* variables */
  #define LOG			15			/* Server operational messages; sent only to
  								 * server log by default. */
! #define COMMERROR	(LOG|LOG_NO_CLIENT)/* Client communication problems; same as LOG
  								 * for server reporting, but never sent to
! 								 * client. For backward compatibility this is
! 								 * is available without explicitly specifying
! 								 * LOG_NO_CLIENT.*/
! #define INFO		16			/* Messages specifically requested by user (eg
  								 * VACUUM VERBOSE output); always sent to
  								 * client regardless of client_min_messages,
  								 * but by default not sent to server log. */
! #define NOTICE		17			/* Helpful messages to users about query
  								 * operation; sent to client and server log by
  								 * default. */
! #define WARNING		18			/* Warnings.  NOTICE is for expected messages
  								 * like implicit sequence creation by SERIAL.
  								 * WARNING is for unexpected messages. */
! #define ERROR		19			/* user error - abort transaction; return to
  								 * known state */
  /* Save ERROR value in PGERROR so it can be restored when Win32 includes
   * modify it.  We have to use a constant rather than ERROR because macros
   * are expanded only when referenced outside macros.
   */
  #ifdef WIN32
! #define PGERROR		19
  #endif
! #define FATAL		20			/* fatal error - abort process */
! #define PANIC		21			/* take down the other backends with me */
! 
  
   /* #define DEBUG DEBUG1 */	/* Backward compatibility with pre-7.3 */
  
*************** extern PGDLLIMPORT sigjmp_buf *PG_except
*** 291,296 ****
--- 301,307 ----
  typedef struct ErrorData
  {
  	int			elevel;			/* error level */
+ 	int			eflags;			/* error flags */
  	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 */
-- 
1.7.3.rc1.5.g73aa2

From 8d39abbeea8362048eb437289fab718ea8ef23f3 Mon Sep 17 00:00:00 2001
From: Andres Freund <and...@anarazel.de>
Date: Sat, 30 Oct 2010 10:36:41 +0200
Subject: [PATCH 3/3] Enable rethrowing errors which occured while IDLE IN TRANSACTION state when explicitly told so via a new error flag.

This makes cancelling an idle transactions more visible to the cancelled connection so it can react more sensibly than just reacting to an ominous "in failed transaction" error.
---
 src/backend/tcop/fastpath.c    |    5 +--
 src/backend/tcop/postgres.c    |   61 +++++++++++++++++----------------------
 src/backend/utils/error/elog.c |   23 +++++++++++++++
 src/include/tcop/tcopprot.h    |    4 ++-
 src/include/utils/elog.h       |    2 +
 5 files changed, 56 insertions(+), 39 deletions(-)

diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index af58e4e..ed6f02a 100644
*** a/src/backend/tcop/fastpath.c
--- b/src/backend/tcop/fastpath.c
*************** HandleFunctionRequest(StringInfo msgBuf)
*** 298,307 ****
  	 * won't lose sync with the frontend.
  	 */
  	if (IsAbortedTransactionBlockState())
! 		ereport(ERROR,
! 				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
! 				 errmsg("current transaction is aborted, "
! 						"commands ignored until end of transaction block")));
  
  	/*
  	 * Now that we know we are in a valid transaction, set snapshot in case
--- 298,304 ----
  	 * won't lose sync with the frontend.
  	 */
  	if (IsAbortedTransactionBlockState())
! 		RaiseInFailedTransactionError();
  
  	/*
  	 * Now that we know we are in a valid transaction, set snapshot in case
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 505d136..f144101 100644
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
*************** static ProcSignalReason RecoveryConflict
*** 183,188 ****
--- 183,195 ----
   */
  static bool silent_error_while_idle = false;
  
+ /*
+  * Which error occured while we were idle? We want to rethrow it in
+  * the face of the next query - except if it is a ROLLBACK/COMMIT - it
+  * will silently be swallowed there...
+  */
+ ErrorData *silent_error_while_idle_edata;
+ 
  /* ----------------------------------------------------------------
   *		decls for routines only used in this file
   * ----------------------------------------------------------------
*************** exec_simple_query(const char *query_stri
*** 952,962 ****
  		 */
  		if (IsAbortedTransactionBlockState() &&
  			!IsTransactionExitStmt(parsetree))
! 			ereport(ERROR,
! 					(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
! 					 errmsg("current transaction is aborted, "
! 						  "commands ignored until end of transaction block"),
! 					 errdetail_abort()));
  
  		/* Make sure we are in a transaction command */
  		start_xact_command();
--- 959,965 ----
  		 */
  		if (IsAbortedTransactionBlockState() &&
  			!IsTransactionExitStmt(parsetree))
! 			RaiseInFailedTransactionError();
  
  		/* Make sure we are in a transaction command */
  		start_xact_command();
*************** exec_parse_message(const char *query_str
*** 1262,1273 ****
  		 */
  		if (IsAbortedTransactionBlockState() &&
  			!IsTransactionExitStmt(raw_parse_tree))
! 			ereport(ERROR,
! 					(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
! 					 errmsg("current transaction is aborted, "
! 						  "commands ignored until end of transaction block"),
! 					 errdetail_abort()));
! 
  		/*
  		 * Set up a snapshot if parse analysis/planning will need one.
  		 */
--- 1265,1271 ----
  		 */
  		if (IsAbortedTransactionBlockState() &&
  			!IsTransactionExitStmt(raw_parse_tree))
! 			RaiseInFailedTransactionError();
  		/*
  		 * Set up a snapshot if parse analysis/planning will need one.
  		 */
*************** exec_bind_message(StringInfo input_messa
*** 1543,1554 ****
  	if (IsAbortedTransactionBlockState() &&
  		(!IsTransactionExitStmt(psrc->raw_parse_tree) ||
  		 numParams != 0))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
! 				 errmsg("current transaction is aborted, "
! 						"commands ignored until end of transaction block"),
! 				 errdetail_abort()));
! 
  	/*
  	 * Create the portal.  Allow silent replacement of an existing portal only
  	 * if the unnamed portal is specified.
--- 1541,1547 ----
  	if (IsAbortedTransactionBlockState() &&
  		(!IsTransactionExitStmt(psrc->raw_parse_tree) ||
  		 numParams != 0))
! 		RaiseInFailedTransactionError();
  	/*
  	 * Create the portal.  Allow silent replacement of an existing portal only
  	 * if the unnamed portal is specified.
*************** exec_execute_message(const char *portal_
*** 1985,1995 ****
  	 */
  	if (IsAbortedTransactionBlockState() &&
  		!IsTransactionExitStmtList(portal->stmts))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
! 				 errmsg("current transaction is aborted, "
! 						"commands ignored until end of transaction block"),
! 				 errdetail_abort()));
  
  	/* Check for cancel signal before we start execution */
  	CHECK_FOR_INTERRUPTS();
--- 1978,1984 ----
  	 */
  	if (IsAbortedTransactionBlockState() &&
  		!IsTransactionExitStmtList(portal->stmts))
! 		RaiseInFailedTransactionError();
  
  	/* Check for cancel signal before we start execution */
  	CHECK_FOR_INTERRUPTS();
*************** exec_describe_statement_message(const ch
*** 2353,2363 ****
  	 */
  	if (IsAbortedTransactionBlockState() &&
  		psrc->resultDesc)
! 		ereport(ERROR,
! 				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
! 				 errmsg("current transaction is aborted, "
! 						"commands ignored until end of transaction block"),
! 				 errdetail_abort()));
  
  	if (whereToSendOutput != DestRemote)
  		return;					/* can't actually do anything... */
--- 2342,2348 ----
  	 */
  	if (IsAbortedTransactionBlockState() &&
  		psrc->resultDesc)
! 		RaiseInFailedTransactionError();
  
  	if (whereToSendOutput != DestRemote)
  		return;					/* can't actually do anything... */
*************** exec_describe_portal_message(const char
*** 2434,2444 ****
  	 */
  	if (IsAbortedTransactionBlockState() &&
  		portal->tupDesc)
! 		ereport(ERROR,
! 				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
! 				 errmsg("current transaction is aborted, "
! 						"commands ignored until end of transaction block"),
! 				 errdetail_abort()));
  
  	if (whereToSendOutput != DestRemote)
  		return;					/* can't actually do anything... */
--- 2419,2425 ----
  	 */
  	if (IsAbortedTransactionBlockState() &&
  		portal->tupDesc)
! 		RaiseInFailedTransactionError();
  
  	if (whereToSendOutput != DestRemote)
  		return;					/* can't actually do anything... */
*************** IsTransactionStmtList(List *parseTrees)
*** 2571,2576 ****
--- 2552,2569 ----
  	return false;
  }
  
+ void
+ RaiseInFailedTransactionError(void){
+ 	if(silent_error_while_idle_edata){
+ 		ReThrowError(silent_error_while_idle_edata);
+ 	}
+ 	ereport(ERROR,
+ 	        (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+ 	         errmsg("current transaction is aborted, "
+ 	                "commands ignored until end of transaction block"),
+ 	         errdetail_abort()));
+ }
+ 
  /* Release any existing unnamed prepared statement */
  static void
  drop_unnamed_stmt(void)
*************** ProcessInterrupts(void)
*** 2967,2973 ****
  				 * because that would be unexpected as well.
  				 */
  				silent_error_while_idle = true;
! 				error |= LOG_NO_CLIENT;
  
  			}
  
--- 2960,2966 ----
  				 * because that would be unexpected as well.
  				 */
  				silent_error_while_idle = true;
! 				error |= LOG_NO_CLIENT|LOG_RE_THROW_AFTER_SYNC;
  
  			}
  
*************** ProcessInterrupts(void)
*** 2985,2991 ****
  		if (DoingCommandRead)
  		{
  			silent_error_while_idle = true;
! 			error |= LOG_NO_CLIENT;
  		}
  
  		ImmediateInterruptOK = false;		/* not idle anymore */
--- 2978,2984 ----
  		if (DoingCommandRead)
  		{
  			silent_error_while_idle = true;
! 			error |= LOG_NO_CLIENT|LOG_RE_THROW_AFTER_SYNC;
  		}
  
  		ImmediateInterruptOK = false;		/* not idle anymore */
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 6e174d8..d8895c6 100644
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
*************** EmitErrorReport(void)
*** 1173,1178 ****
--- 1173,1201 ----
  	if (edata->output_to_client && !(edata->eflags & LOG_NO_CLIENT))
  		send_message_to_frontend(edata);
  
+ 	if(silent_error_while_idle_edata){
+ 		FreeErrorData(silent_error_while_idle_edata);
+ 		silent_error_while_idle_edata = 0;
+ 	}
+ 
+ 	if (edata->eflags & LOG_RE_THROW_AFTER_SYNC){
+ 		Assert(edata->elevel >= ERROR);
+ 
+ 		/* The old context is already saved above and reset
+ 		 * below... We cannot store the ErrorData in any other context
+ 		 * than TopMemoryContext - they all may get reset inbetween
+ 		 * two EmitErrorReport() calls.
+ 		 * As there is no convenient point to reset the variable using
+ 		 * any transaction bound variable is not possible without
+ 		 * leaving a stray pointer.
+ 		 */
+ 		MemoryContextSwitchTo(TopMemoryContext);
+ 		silent_error_while_idle_edata = CopyErrorData();
+ 
+ 		/* We dont want to save/rethrow that error again */
+ 		silent_error_while_idle_edata->eflags &= ~(LOG_RE_THROW_AFTER_SYNC|LOG_NO_CLIENT);
+ 	}
+ 
  	MemoryContextSwitchTo(oldcontext);
  	recursion_depth--;
  }
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index f4026ec..b529ce1 100644
*** a/src/include/tcop/tcopprot.h
--- b/src/include/tcop/tcopprot.h
*************** typedef enum
*** 45,50 ****
--- 45,52 ----
  
  extern int	log_statement;
  
+ extern ErrorData *silent_error_while_idle_edata;
+ 
  extern List *pg_parse_and_rewrite(const char *query_string,
  					 Oid *paramTypes, int numParams);
  extern List *pg_parse_query(const char *query_string);
*************** extern void set_debug_options(int debug_
*** 81,85 ****
  extern bool set_plan_disabling_options(const char *arg,
  						   GucContext context, GucSource source);
  extern const char *get_stats_option_name(const char *arg);
! 
  #endif   /* TCOPPROT_H */
--- 83,87 ----
  extern bool set_plan_disabling_options(const char *arg,
  						   GucContext context, GucSource source);
  extern const char *get_stats_option_name(const char *arg);
! extern void RaiseInFailedTransactionError(void);
  #endif   /* TCOPPROT_H */
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 1853e1d..529265a 100644
*** a/src/include/utils/elog.h
--- b/src/include/utils/elog.h
***************
*** 23,28 ****
--- 23,30 ----
                                   * if it would confuse the client at
                                   * that moment */
  
+ #define LOG_RE_THROW_AFTER_SYNC (2<<29)
+ 
  /* Error level codes */
  #define DEBUG5		10			/* Debugging messages, in categories of
  								 * decreasing detail. */
-- 
1.7.3.rc1.5.g73aa2

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to