text: /*-------------------------------------------------------------------------
   *
   * elog.c
   *	  error logging and reporting
   *
   * Because of the extremely high rate at which log messages can be generated,
   * we need to be mindful of the performance cost of obtaining any information
   * that may be logged.  Also, it's important to keep in mind that this code may
   * get called from within an aborted transaction, in which case operations
   * such as syscache lookups are unsafe.
   *
   * Some notes about recursion and errors during error processing:
   *
   * We need to be robust about recursive-error scenarios --- for example,
   * if we run out of memory, it's important to be able to report that fact.
   * There are a number of considerations that go into this.
   *
   * First, distinguish between re-entrant use and actual recursion.  It
   * is possible for an error or warning message to be emitted while the
   * parameters for an error message are being computed.  In this case
   * errstart has been called for the outer message, and some field values
   * may have already been saved, but we are not actually recursing.  We handle
   * this by providing a (small) stack of ErrorData records.  The inner message
   * can be computed and sent without disturbing the state of the outer message.
   * (If the inner message is actually an error, this isn't very interesting
   * because control won't come back to the outer message generator ... but
   * if the inner message is only debug or log data, this is critical.)
   *
   * Second, actual recursion will occur if an error is reported by one of
   * the elog.c routines or something they call.  By far the most probable
   * scenario of this sort is "out of memory"; and it's also the nastiest
   * to handle because we'd likely also run out of memory while trying to
   * report this error!  Our escape hatch for this case is to reset the
   * ErrorContext to empty before trying to process the inner error.  Since
   * ErrorContext is guaranteed to have at least 8K of space in it (see mcxt.c),
   * we should be able to process an "out of memory" message successfully.
   * Since we lose the prior error state due to the reset, we won't be able
   * to return to processing the original error, but we wouldn't have anyway.
   * (NOTE: the escape hatch is not used for recursive situations where the
   * inner message is of less than ERROR severity; in that case we just
   * try to process it and return normally.  Usually this will work, but if
   * it ends up in infinite recursion, we will PANIC due to error stack
   * overflow.)
   *
   *
   * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
   *
   * IDENTIFICATION
   *	  src/backend/utils/error/elog.c
   *
   *-------------------------------------------------------------------------
   */
  #include "postgres.h"
  
  #include <fcntl.h>
  #include <time.h>
  #include <unistd.h>
  #include <signal.h>
  #include <ctype.h>
  #ifdef HAVE_SYSLOG
  #include <syslog.h>
  #endif
  #ifdef HAVE_EXECINFO_H
  #include <execinfo.h>
  #endif
  
  #include "access/transam.h"
  #include "access/xact.h"
  #include "libpq/libpq.h"
  #include "libpq/pqformat.h"
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
  #include "postmaster/bgworker.h"
  #include "postmaster/postmaster.h"
  #include "postmaster/syslogger.h"
  #include "storage/ipc.h"
  #include "storage/proc.h"
  #include "tcop/tcopprot.h"
  #include "utils/guc.h"
  #include "utils/memutils.h"
  #include "utils/ps_status.h"
  
  
  /* In this module, access gettext() via err_gettext() */
  #undef _
  #define _(x) err_gettext(x)
  
  
  /* Global variables */
  ErrorContextCallback *error_context_stack = NULL;
  
  sigjmp_buf *PG_exception_stack = NULL;
  
  extern bool redirection_done;
  
  /*
   * Hook for intercepting messages before they are sent to the server log.
   * Note that the hook will not get called for messages that are suppressed
   * by log_min_messages.  Also note that logging hooks implemented in preload
   * libraries will miss any log messages that are generated before the
   * library is loaded.
   */
  emit_log_hook_type emit_log_hook = NULL;
  
  /* GUC parameters */
  int			Log_error_verbosity = PGERROR_VERBOSE;
  char	   *Log_line_prefix = NULL; /* format for extra log line info */
  int			Log_destination = LOG_DESTINATION_STDERR;
  char	   *Log_destination_string = NULL;
  bool		syslog_sequence_numbers = true;
  bool		syslog_split_messages = true;
  
  #ifdef HAVE_SYSLOG
  
  /*
   * Max string length to send to syslog().  Note that this doesn't count the
   * sequence-number prefix we add, and of course it doesn't count the prefix
   * added by syslog itself.  Solaris and sysklogd truncate the final message
   * at 1024 bytes, so this value leaves 124 bytes for those prefixes.  (Most
   * other syslog implementations seem to have limits of 2KB or so.)
   */
  #ifndef PG_SYSLOG_LIMIT
  #define PG_SYSLOG_LIMIT 900
  #endif
  
  static bool openlog_done = false;
  static char *syslog_ident = NULL;
  static int	syslog_facility = LOG_LOCAL0;
  
  static void write_syslog(int level, const char *line);
  #endif
  
  #ifdef WIN32
  extern char *event_source;
  
  static void write_eventlog(int level, const char *line, int len);
  #endif
  
  /* We provide a small stack of ErrorData records for re-entrant cases */
  #define ERRORDATA_STACK_SIZE  5
  
  static ErrorData errordata[ERRORDATA_STACK_SIZE];
  
  static int	errordata_stack_depth = -1; /* index of topmost active frame */
  
  static int	recursion_depth = 0;	/* to detect actual recursion */
  
  /*
   * Saved timeval and buffers for formatted timestamps that might be used by
   * both log_line_prefix and csv logs.
   */
  static struct timeval saved_timeval;
  static bool saved_timeval_set = false;
  
  #define FORMATTED_TS_LEN 128
  static char formatted_start_time[FORMATTED_TS_LEN];
  static char formatted_log_time[FORMATTED_TS_LEN];
  
  
  /* Macro for checking errordata_stack_depth is reasonable */
  #define CHECK_STACK_DEPTH() \
  	do { \
  		if (errordata_stack_depth < 0) \
  		{ \
  			errordata_stack_depth = -1; \
  			ereport(ERROR, (errmsg_internal("errstart was not called"))); \
  		} \
  	} while (0)
  
  
  static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
  static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
  static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
  static void write_console(const char *line, int len);
  static void setup_formatted_log_time(void);
  static void setup_formatted_start_time(void);
  static const char *process_log_prefix_padding(const char *p, int *padding);
  static void log_line_prefix(StringInfo buf, ErrorData *edata);
  static void write_csvlog(ErrorData *edata);
  static void send_message_to_server_log(ErrorData *edata);
  static void write_pipe_chunks(char *data, int len, int dest);
  static void send_message_to_frontend(ErrorData *edata);
  static const char *error_severity(int elevel);
  static void append_with_tabs(StringInfo buf, const char *str);
  
  
  /*
   * is_log_level_output -- is elevel logically >= log_min_level?
   *
   * We use this for tests that should consider LOG to sort out-of-order,
   * between ERROR and FATAL.  Generally this is the right thing for testing
   * whether a message should go to the postmaster log, whereas a simple >=
   * test is correct for testing whether the message should go to the client.
   */
  static inline bool
  is_log_level_output(int elevel, int log_min_level)
  {
  	if (elevel == LOG || elevel == LOG_SERVER_ONLY)
  	{
  		if (log_min_level == LOG || log_min_level <= ERROR)
  			return true;
  	}
  	else if (log_min_level == LOG)
  	{
  		/* elevel != LOG */
  		if (elevel >= FATAL)
  			return true;
  	}
  	/* Neither is LOG */
  	else if (elevel >= log_min_level)
  		return true;
  
  	return false;
  }
  
  /*
   * Policy-setting subroutines.  These are fairly simple, but it seems wise
   * to have the code in just one place.
   */
  
  /*
   * should_output_to_server --- should message of given elevel go to the log?
   */
  static inline bool
  should_output_to_server(int elevel)
  {
  	return is_log_level_output(elevel, log_min_messages);
  }
  
  /*
   * should_output_to_client --- should message of given elevel go to the client?
   */
  static inline bool
  should_output_to_client(int elevel)
  {
  	if (whereToSendOutput == DestRemote && elevel != LOG_SERVER_ONLY)
  	{
  		/*
  		 * client_min_messages is honored only after we complete the
  		 * authentication handshake.  This is required both for security
  		 * reasons and because many clients can't handle NOTICE messages
  		 * during authentication.
  		 */
  		if (ClientAuthInProgress)
  			return (elevel >= ERROR);
  		else
  			return (elevel >= client_min_messages || elevel == INFO);
  	}
  	return false;
  }
  
  
  /*
   * message_level_is_interesting --- would ereport/elog do anything?
   *
   * Returns true if ereport/elog with this elevel will not be a no-op.
   * This is useful to short-circuit any expensive preparatory work that
   * might be needed for a logging message.  There is no point in
   * prepending this to a bare ereport/elog call, however.
   */
  bool
  message_level_is_interesting(int elevel)
  {
  	/*
  	 * Keep this in sync with the decision-making in errstart().
  	 */
  	if (elevel >= ERROR ||
  		should_output_to_server(elevel) ||
  		should_output_to_client(elevel))
  		return true;
  	return false;
  }
  
  
  /*
   * in_error_recursion_trouble --- are we at risk of infinite error recursion?
   *
   * This function exists to provide common control of various fallback steps
   * that we take if we think we are facing infinite error recursion.  See the
   * callers for details.
   */
  bool
  in_error_recursion_trouble(void)
  {
  	/* Pull the plug if recurse more than once */
  	return (recursion_depth > 2);
  }
  
  /*
   * One of those fallback steps is to stop trying to localize the error
   * message, since there's a significant probability that that's exactly
   * what's causing the recursion.
   */
  static inline const char *
  err_gettext(const char *str)
  {
  #ifdef ENABLE_NLS
  	if (in_error_recursion_trouble())
  		return str;
  	else
  		return gettext(str);
  #else
  	return str;
  #endif
  }
  
  /*
   * errstart_cold
   *		A simple wrapper around errstart, but hinted to be "cold".  Supporting
   *		compilers are more likely to move code for branches containing this
   *		function into an area away from the calling function's code.  This can
   *		result in more commonly executed code being more compact and fitting
   *		on fewer cache lines.
   */
  pg_attribute_cold bool
  errstart_cold(int elevel, const char *domain)
  {
  	return errstart(elevel, domain);
  }
  
  /*
   * errstart --- begin an error-reporting cycle
   *
   * Create and initialize error stack entry.  Subsequently, errmsg() and
   * perhaps other routines will be called to further populate the stack entry.
   * Finally, errfinish() will be called to actually process the error report.
   *
   * Returns true in normal case.  Returns false to short-circuit the error
   * report (if it's a warning or lower and not to be reported anywhere).
   */
  bool
  errstart(int elevel, const char *domain)
  {
  	ErrorData  *edata;
  	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.
  	 */
  	if (elevel >= ERROR)
  	{
  		/*
  		 * If we are inside a critical section, all errors become PANIC
  		 * errors.  See miscadmin.h.
  		 */
  		if (CritSectionCount > 0)
  			elevel = PANIC;
  
  		/*
  		 * Check 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;
  		}
  
  		/*
  		 * If the error level is ERROR or more, errfinish is 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.
  		 */
  		for (i = 0; i <= errordata_stack_depth; i++)
  			elevel = Max(elevel, errordata[i].elevel);
  	}
  
  	/*
  	 * Now decide whether we need to process this report at all; if it's
  	 * warning or less and not enabled for logging, just return false without
  	 * starting up any error logging machinery.
  	 */
  	output_to_server = should_output_to_server(elevel);
  	output_to_client = should_output_to_client(elevel);
  	if (elevel < ERROR && !output_to_server && !output_to_client)
  		return false;
  
  	/*
  	 * We need to do some actual work.  Make sure that memory context
  	 * initialization has finished, else we can't do anything useful.
  	 */
  	if (ErrorContext == NULL)
  	{
  		/* Oops, hard crash time; very little we can do safely here */
  		write_stderr("error occurred before error message processing is available\n");
  		exit(2);
  	}
  
  	/*
  	 * Okay, crank up a stack entry to store the info in.
  	 */
  
  	if (recursion_depth++ > 0 && elevel >= ERROR)
  	{
  		/*
  		 * Oops, error during error processing.  Clear ErrorContext as
  		 * discussed at top of file.  We will not return to the original
  		 * error's reporter or handler, so we don't need it.
  		 */
  		MemoryContextReset(ErrorContext);
  
  		/*
  		 * Infinite error recursion might be due to something broken in a
  		 * context traceback routine.  Abandon them too.  We also abandon
  		 * attempting to print the error statement (which, if long, could
  		 * itself be the source of the recursive failure).
  		 */
  		if (in_error_recursion_trouble())
  		{
  			error_context_stack = NULL;
  			debug_query_string = NULL;
  		}
  	}
  	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")));
  	}
  
  	/* Initialize data for this error frame */
  	edata = &errordata[errordata_stack_depth];
  	MemSet(edata, 0, sizeof(ErrorData));
  	edata->elevel = elevel;
  	edata->output_to_server = output_to_server;
  	edata->output_to_client = output_to_client;
  	/* the default text domain is the backend's */
  	edata->domain = domain ? domain : PG_TEXTDOMAIN("postgres");
  	/* initialize context_domain the same way (see set_errcontext_domain()) */
  	edata->context_domain = edata->domain;
  	/* Select default errcode based on elevel */
  	if (elevel >= ERROR)
  		edata->sqlerrcode = ERRCODE_INTERNAL_ERROR;
  	else if (elevel == WARNING)
  		edata->sqlerrcode = ERRCODE_WARNING;
  	else
  		edata->sqlerrcode = ERRCODE_SUCCESSFUL_COMPLETION;
  	/* errno is saved here so that error parameter eval can't change it */
  	edata->saved_errno = errno;
  
  	/*
  	 * Any allocations for this error state level should go into ErrorContext
  	 */
  	edata->assoc_context = ErrorContext;
  
  	recursion_depth--;
  	return true;
  }
  
  /*
   * Checks whether the given funcname matches backtrace_functions; see
   * check_backtrace_functions.
   */
  static bool
  matches_backtrace_functions(const char *funcname)
  {
  	char	   *p;
  
  	if (!backtrace_symbol_list || funcname == NULL || funcname[0] == '\0')
  		return false;
  
  	p = backtrace_symbol_list;
  	for (;;)
  	{
  		if (*p == '\0')			/* end of backtrace_symbol_list */
  			break;
  
  		if (strcmp(funcname, p) == 0)
  			return true;
  		p += strlen(p) + 1;
  	}
  
  	return false;
  }
  
  /*
   * errfinish --- end an error-reporting cycle
   *
   * Produce the appropriate error report(s) and pop the error stack.
   *
   * If elevel, as passed to errstart(), is ERROR or worse, control does not
   * return to the caller.  See elog.h for the error level definitions.
   */
  void
  errfinish(const char *filename, int lineno, const char *funcname)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  	int			elevel;
  	MemoryContext oldcontext;
  	ErrorContextCallback *econtext;
  
  	recursion_depth++;
  	CHECK_STACK_DEPTH();
  
  	/* Save the last few bits of error state into the stack entry */
  	if (filename)
  	{
  		const char *slash;
  
  		/* keep only base name, useful especially for vpath builds */
  		slash = strrchr(filename, '/');
  		if (slash)
  			filename = slash + 1;
  	}
  
  	edata->filename = filename;
  	edata->lineno = lineno;
  	edata->funcname = funcname;
  
  	elevel = edata->elevel;
  
  	/*
  	 * Do processing in ErrorContext, which we hope has enough reserved space
  	 * to report an error.
  	 */
  	oldcontext = MemoryContextSwitchTo(ErrorContext);
  
  	if (!edata->backtrace &&
  		edata->funcname &&
  		backtrace_functions &&
  		matches_backtrace_functions(edata->funcname))
  		set_backtrace(edata, 2);
  
  	/*
  	 * Call any context callback functions.  Errors occurring in callback
  	 * functions will be treated as recursive errors --- this ensures we will
  	 * avoid infinite recursion (see errstart).
  	 */
  	for (econtext = error_context_stack;
  		 econtext != NULL;
  		 econtext = econtext->previous)
  		econtext->callback(econtext->arg);
  
  	/*
  	 * If ERROR (not more nor less) we pass it off to the current handler.
  	 * Printing it and popping the stack is the responsibility of the handler.
  	 */
  	if (elevel == ERROR)
  	{
  		/*
  		 * We do some minimal cleanup before longjmp'ing so that handlers can
  		 * execute in a reasonably sane state.
  		 *
  		 * 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;
  		QueryCancelHoldoffCount = 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();
  	}
  
  	/*
  	 * 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.
  	 */
  	if (elevel >= FATAL && whereToSendOutput == DestRemote)
  		pq_endcopyout(true);
  
  	/* Emit the message to the right places */
  	EmitErrorReport();
  
  	/* Now free up subsidiary data attached to stack entry, and release it */
  	if (edata->message)
  		pfree(edata->message);
  	if (edata->detail)
  		pfree(edata->detail);
  	if (edata->detail_log)
  		pfree(edata->detail_log);
  	if (edata->hint)
  		pfree(edata->hint);
  	if (edata->context)
  		pfree(edata->context);
  	if (edata->backtrace)
  		pfree(edata->backtrace);
  	if (edata->schema_name)
  		pfree(edata->schema_name);
  	if (edata->table_name)
  		pfree(edata->table_name);
  	if (edata->column_name)
  		pfree(edata->column_name);
  	if (edata->datatype_name)
  		pfree(edata->datatype_name);
  	if (edata->constraint_name)
  		pfree(edata->constraint_name);
  	if (edata->internalquery)
  		pfree(edata->internalquery);
  
  	errordata_stack_depth--;
  
  	/* Exit error-handling context */
  	MemoryContextSwitchTo(oldcontext);
  	recursion_depth--;
  
  	/*
  	 * Perform error recovery action as specified by elevel.
  	 */
  	if (elevel == FATAL)
  	{
  		/*
  		 * For a FATAL error, we let proc_exit clean up and exit.
  		 *
  		 * If we just reported a startup failure, the client will disconnect
  		 * on receiving it, so don't send any more to the client.
  		 */
  		if (PG_exception_stack == NULL && whereToSendOutput == DestRemote)
  			whereToSendOutput = DestNone;
  
  		/*
  		 * 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 or on_shmem_exit callback instead.
  		 */
  		fflush(stdout);
  		fflush(stderr);
  
  		/*
  		 * Do normal process-exit cleanup, then return exit code 1 to indicate
  		 * FATAL termination.  The postmaster may or may not consider this
  		 * worthy of panic, depending on which subprocess returns it.
  		 */
  		proc_exit(1);
  	}
  
  	if (elevel >= PANIC)
  	{
  		/*
  		 * Serious crash time. Postmaster will observe SIGABRT process exit
  		 * status and kill the other backends too.
  		 *
  		 * XXX: what if we are *in* the postmaster?  abort() won't kill our
  		 * children...
  		 */
  		fflush(stdout);
  		fflush(stderr);
  		abort();
  	}
  
  	/*
  	 * Check for cancel/die interrupt first --- this is so that the user can
  	 * stop a query emitting tons of notice or warning messages, even if it's
  	 * in a loop that otherwise fails to check for interrupts.
  	 */
  	CHECK_FOR_INTERRUPTS();
  }
  
  
  /*
   * errcode --- add SQLSTATE error code to the current error
   *
   * The code is expected to be represented as per MAKE_SQLSTATE().
   */
  int
  errcode(int sqlerrcode)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  
  	/* we don't bother incrementing recursion_depth */
  	CHECK_STACK_DEPTH();
  
  	edata->sqlerrcode = sqlerrcode;
  
  	return 0;					/* return value does not matter */
  }
  
  
  /*
   * errcode_for_file_access --- add SQLSTATE error code to the current error
   *
   * The SQLSTATE code is chosen based on the saved errno value.  We assume
   * that the failing operation was some type of disk file access.
   *
   * NOTE: the primary error message string should generally include %m
   * when this is used.
   */
  int
  errcode_for_file_access(void)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  
  	/* we don't bother incrementing recursion_depth */
  	CHECK_STACK_DEPTH();
  
  	switch (edata->saved_errno)
  	{
  			/* Permission-denied failures */
  		case EPERM:				/* Not super-user */
  		case EACCES:			/* Permission denied */
  #ifdef EROFS
  		case EROFS:				/* Read only file system */
  #endif
  			edata->sqlerrcode = ERRCODE_INSUFFICIENT_PRIVILEGE;
  			break;
  
  			/* File not found */
  		case ENOENT:			/* No such file or directory */
  			edata->sqlerrcode = ERRCODE_UNDEFINED_FILE;
  			break;
  
  			/* Duplicate file */
  		case EEXIST:			/* File exists */
  			edata->sqlerrcode = ERRCODE_DUPLICATE_FILE;
  			break;
  
  			/* Wrong object type or state */
  		case ENOTDIR:			/* Not a directory */
  		case EISDIR:			/* Is a directory */
  #if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
  		case ENOTEMPTY:			/* Directory not empty */
  #endif
  			edata->sqlerrcode = ERRCODE_WRONG_OBJECT_TYPE;
  			break;
  
  			/* Insufficient resources */
  		case ENOSPC:			/* No space left on device */
  			edata->sqlerrcode = ERRCODE_DISK_FULL;
  			break;
  
  		case ENFILE:			/* File table overflow */
  		case EMFILE:			/* Too many open files */
  			edata->sqlerrcode = ERRCODE_INSUFFICIENT_RESOURCES;
  			break;
  
  			/* Hardware failure */
  		case EIO:				/* I/O error */
  			edata->sqlerrcode = ERRCODE_IO_ERROR;
  			break;
  
  			/* All else is classified as internal errors */
  		default:
  			edata->sqlerrcode = ERRCODE_INTERNAL_ERROR;
  			break;
  	}
  
  	return 0;					/* return value does not matter */
  }
  
  /*
   * errcode_for_socket_access --- add SQLSTATE error code to the current error
   *
   * The SQLSTATE code is chosen based on the saved errno value.  We assume
   * that the failing operation was some type of socket access.
   *
   * NOTE: the primary error message string should generally include %m
   * when this is used.
   */
  int
  errcode_for_socket_access(void)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  
  	/* we don't bother incrementing recursion_depth */
  	CHECK_STACK_DEPTH();
  
  	switch (edata->saved_errno)
  	{
  			/* Loss of connection */
  		case ALL_CONNECTION_FAILURE_ERRNOS:
  			edata->sqlerrcode = ERRCODE_CONNECTION_FAILURE;
  			break;
  
  			/* All else is classified as internal errors */
  		default:
  			edata->sqlerrcode = ERRCODE_INTERNAL_ERROR;
  			break;
  	}
  
  	return 0;					/* return value does not matter */
  }
  
  
  /*
   * This macro handles expansion of a format string and associated parameters;
   * it's common code for errmsg(), errdetail(), etc.  Must be called inside
   * a routine that is declared like "const char *fmt, ..." and has an edata
   * pointer set up.  The message is assigned to edata->targetfield, or
   * appended to it if appendval is true.  The message is subject to translation
   * if translateit is true.
   *
   * Note: we pstrdup the buffer rather than just transferring its storage
   * to the edata field because the buffer might be considerably larger than
   * really necessary.
   */
  #define EVALUATE_MESSAGE(domain, targetfield, appendval, translateit)	\
  	{ \
  		StringInfoData	buf; \
  		/* Internationalize the error format string */ \
  		if ((translateit) && !in_error_recursion_trouble()) \
  			fmt = dgettext((domain), fmt);				  \
  		initStringInfo(&buf); \
  		if ((appendval) && edata->targetfield) { \
  			appendStringInfoString(&buf, edata->targetfield); \
  			appendStringInfoChar(&buf, '\n'); \
  		} \
  		/* Generate actual output --- have to use appendStringInfoVA */ \
  		for (;;) \
  		{ \
  			va_list		args; \
  			int			needed; \
  			errno = edata->saved_errno; \
  			va_start(args, fmt); \
  			needed = appendStringInfoVA(&buf, fmt, args); \
  			va_end(args); \
  			if (needed == 0) \
  				break; \
  			enlargeStringInfo(&buf, needed); \
  		} \
  		/* Save the completed message into the stack item */ \
  		if (edata->targetfield) \
  			pfree(edata->targetfield); \
  		edata->targetfield = pstrdup(buf.data); \
  		pfree(buf.data); \
  	}
  
  /*
   * Same as above, except for pluralized error messages.  The calling routine
   * must be declared like "const char *fmt_singular, const char *fmt_plural,
   * unsigned long n, ...".  Translation is assumed always wanted.
   */
  #define EVALUATE_MESSAGE_PLURAL(domain, targetfield, appendval)  \
  	{ \
  		const char	   *fmt; \
  		StringInfoData	buf; \
  		/* Internationalize the error format string */ \
  		if (!in_error_recursion_trouble()) \
  			fmt = dngettext((domain), fmt_singular, fmt_plural, n); \
  		else \
  			fmt = (n == 1 ? fmt_singular : fmt_plural); \
  		initStringInfo(&buf); \
  		if ((appendval) && edata->targetfield) { \
  			appendStringInfoString(&buf, edata->targetfield); \
  			appendStringInfoChar(&buf, '\n'); \
  		} \
  		/* Generate actual output --- have to use appendStringInfoVA */ \
  		for (;;) \
  		{ \
  			va_list		args; \
  			int			needed; \
  			errno = edata->saved_errno; \
  			va_start(args, n); \
  			needed = appendStringInfoVA(&buf, fmt, args); \
  			va_end(args); \
  			if (needed == 0) \
  				break; \
  			enlargeStringInfo(&buf, needed); \
  		} \
  		/* Save the completed message into the stack item */ \
  		if (edata->targetfield) \
  			pfree(edata->targetfield); \
  		edata->targetfield = pstrdup(buf.data); \
  		pfree(buf.data); \
  	}
  
  
  /*
   * errmsg --- add a primary error message text to the current error
   *
   * In addition to the usual %-escapes recognized by printf, "%m" in
   * fmt is replaced by the error message for the caller's value of errno.
   *
   * Note: no newline is needed at the end of the fmt string, since
   * ereport will provide one for the output methods that need it.
   */
  int
  errmsg(const char *fmt,...)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  	MemoryContext oldcontext;
  
  	recursion_depth++;
  	CHECK_STACK_DEPTH();
  	oldcontext = MemoryContextSwitchTo(edata->assoc_context);
  
  	edata->message_id = fmt;
  	EVALUATE_MESSAGE(edata->domain, message, false, true);
  
  	MemoryContextSwitchTo(oldcontext);
  	recursion_depth--;
  	return 0;					/* return value does not matter */
  }
  
  /*
   * Add a backtrace to the containing ereport() call.  This is intended to be
   * added temporarily during debugging.
   */
  int
  errbacktrace(void)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  	MemoryContext oldcontext;
  
  	recursion_depth++;
  	CHECK_STACK_DEPTH();
  	oldcontext = MemoryContextSwitchTo(edata->assoc_context);
  
  	set_backtrace(edata, 1);
  
  	MemoryContextSwitchTo(oldcontext);
  	recursion_depth--;
  
  	return 0;
  }
  
  /*
   * Compute backtrace data and add it to the supplied ErrorData.  num_skip
   * specifies how many inner frames to skip.  Use this to avoid showing the
   * internal backtrace support functions in the backtrace.  This requires that
   * this and related functions are not inlined.
   */
  static void
  set_backtrace(ErrorData *edata, int num_skip)
  {
  	StringInfoData errtrace;
  
  	initStringInfo(&errtrace);
  
  #ifdef HAVE_BACKTRACE_SYMBOLS
  	{
  		void	   *buf[100];
  		int			nframes;
  		char	  **strfrms;
  
  		nframes = backtrace(buf, lengthof(buf));
  		strfrms = backtrace_symbols(buf, nframes);
  		if (strfrms == NULL)
  			return;
  
  		for (int i = num_skip; i < nframes; i++)
  			appendStringInfo(&errtrace, "\n%s", strfrms[i]);
  		free(strfrms);
  	}
  #else
  	appendStringInfoString(&errtrace,
  						   "backtrace generation is not supported by this installation");
  #endif
  
  	edata->backtrace = errtrace.data;
  }
  
  /*
   * errmsg_internal --- add a primary error message text to the current error
   *
   * This is exactly like errmsg() except that strings passed to errmsg_internal
   * are not translated, and are customarily left out of the
   * internationalization message dictionary.  This should be used for "can't
   * happen" cases that are probably not worth spending translation effort on.
   * We also use this for certain cases where we *must* not try to translate
   * the message because the translation would fail and result in infinite
   * error recursion.
   */
  int
  errmsg_internal(const char *fmt,...)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  	MemoryContext oldcontext;
  
  	recursion_depth++;
  	CHECK_STACK_DEPTH();
  	oldcontext = MemoryContextSwitchTo(edata->assoc_context);
  
  	edata->message_id = fmt;
  	EVALUATE_MESSAGE(edata->domain, message, false, false);
  
  	MemoryContextSwitchTo(oldcontext);
  	recursion_depth--;
  	return 0;					/* return value does not matter */
  }
  
  
  /*
   * errmsg_plural --- add a primary error message text to the current error,
   * with support for pluralization of the message text
   */
  int
  errmsg_plural(const char *fmt_singular, const char *fmt_plural,
  			  unsigned long n,...)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  	MemoryContext oldcontext;
  
  	recursion_depth++;
  	CHECK_STACK_DEPTH();
  	oldcontext = MemoryContextSwitchTo(edata->assoc_context);
  
  	edata->message_id = fmt_singular;
  	EVALUATE_MESSAGE_PLURAL(edata->domain, message, false);
  
  	MemoryContextSwitchTo(oldcontext);
  	recursion_depth--;
  	return 0;					/* return value does not matter */
  }
  
  
  /*
   * errdetail --- add a detail error message text to the current error
   */
  int
  errdetail(const char *fmt,...)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  	MemoryContext oldcontext;
  
  	recursion_depth++;
  	CHECK_STACK_DEPTH();
  	oldcontext = MemoryContextSwitchTo(edata->assoc_context);
  
  	EVALUATE_MESSAGE(edata->domain, detail, false, true);
  
  	MemoryContextSwitchTo(oldcontext);
  	recursion_depth--;
  	return 0;					/* return value does not matter */
  }
  
  
  /*
   * errdetail_internal --- add a detail error message text to the current error
   *
   * This is exactly like errdetail() except that strings passed to
   * errdetail_internal are not translated, and are customarily left out of the
   * internationalization message dictionary.  This should be used for detail
   * messages that seem not worth translating for one reason or another
   * (typically, that they don't seem to be useful to average users).
   */
  int
  errdetail_internal(const char *fmt,...)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  	MemoryContext oldcontext;
  
  	recursion_depth++;
  	CHECK_STACK_DEPTH();
  	oldcontext = MemoryContextSwitchTo(edata->assoc_context);
  
  	EVALUATE_MESSAGE(edata->domain, detail, false, false);
  
  	MemoryContextSwitchTo(oldcontext);
  	recursion_depth--;
  	return 0;					/* return value does not matter */
  }
  
  
  /*
   * errdetail_log --- add a detail_log error message text to the current error
   */
  int
  errdetail_log(const char *fmt,...)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  	MemoryContext oldcontext;
  
  	recursion_depth++;
  	CHECK_STACK_DEPTH();
  	oldcontext = MemoryContextSwitchTo(edata->assoc_context);
  
  	EVALUATE_MESSAGE(edata->domain, detail_log, false, true);
  
  	MemoryContextSwitchTo(oldcontext);
  	recursion_depth--;
  	return 0;					/* return value does not matter */
  }
  
  /*
   * errdetail_log_plural --- add a detail_log error message text to the current error
   * with support for pluralization of the message text
   */
  int
  errdetail_log_plural(const char *fmt_singular, const char *fmt_plural,
  					 unsigned long n,...)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  	MemoryContext oldcontext;
  
  	recursion_depth++;
  	CHECK_STACK_DEPTH();
  	oldcontext = MemoryContextSwitchTo(edata->assoc_context);
  
  	EVALUATE_MESSAGE_PLURAL(edata->domain, detail_log, false);
  
  	MemoryContextSwitchTo(oldcontext);
  	recursion_depth--;
  	return 0;					/* return value does not matter */
  }
  
  
  /*
   * errdetail_plural --- add a detail error message text to the current error,
   * with support for pluralization of the message text
   */
  int
  errdetail_plural(const char *fmt_singular, const char *fmt_plural,
  				 unsigned long n,...)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  	MemoryContext oldcontext;
  
  	recursion_depth++;
  	CHECK_STACK_DEPTH();
  	oldcontext = MemoryContextSwitchTo(edata->assoc_context);
  
  	EVALUATE_MESSAGE_PLURAL(edata->domain, detail, false);
  
  	MemoryContextSwitchTo(oldcontext);
  	recursion_depth--;
  	return 0;					/* return value does not matter */
  }
  
  
  /*
   * errhint --- add a hint error message text to the current error
   */
  int
  errhint(const char *fmt,...)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  	MemoryContext oldcontext;
  
  	recursion_depth++;
  	CHECK_STACK_DEPTH();
  	oldcontext = MemoryContextSwitchTo(edata->assoc_context);
  
  	EVALUATE_MESSAGE(edata->domain, hint, false, true);
  
  	MemoryContextSwitchTo(oldcontext);
  	recursion_depth--;
  	return 0;					/* return value does not matter */
  }
  
  
  /*
   * errcontext_msg --- add a context error message text to the current error
   *
   * Unlike other cases, multiple calls are allowed to build up a stack of
   * context information.  We assume earlier calls represent more-closely-nested
   * states.
   */
  int
  errcontext_msg(const char *fmt,...)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  	MemoryContext oldcontext;
  
  	recursion_depth++;
  	CHECK_STACK_DEPTH();
  	oldcontext = MemoryContextSwitchTo(edata->assoc_context);
  
  	EVALUATE_MESSAGE(edata->context_domain, context, true, true);
  
  	MemoryContextSwitchTo(oldcontext);
  	recursion_depth--;
  	return 0;					/* return value does not matter */
  }
  
  /*
   * set_errcontext_domain --- set message domain to be used by errcontext()
   *
   * errcontext_msg() can be called from a different module than the original
   * ereport(), so we cannot use the message domain passed in errstart() to
   * translate it.  Instead, each errcontext_msg() call should be preceded by
   * a set_errcontext_domain() call to specify the domain.  This is usually
   * done transparently by the errcontext() macro.
   */
  int
  set_errcontext_domain(const char *domain)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  
  	/* we don't bother incrementing recursion_depth */
  	CHECK_STACK_DEPTH();
  
  	/* the default text domain is the backend's */
  	edata->context_domain = domain ? domain : PG_TEXTDOMAIN("postgres");
  
  	return 0;					/* return value does not matter */
  }
  
  
  /*
   * errhidestmt --- optionally suppress STATEMENT: field of log entry
   *
   * This should be called if the message text already includes the statement.
   */
  int
  errhidestmt(bool hide_stmt)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  
  	/* we don't bother incrementing recursion_depth */
  	CHECK_STACK_DEPTH();
  
  	edata->hide_stmt = hide_stmt;
  
  	return 0;					/* return value does not matter */
  }
  
  /*
   * errhidecontext --- optionally suppress CONTEXT: field of log entry
   *
   * This should only be used for verbose debugging messages where the repeated
   * inclusion of context would bloat the log volume too much.
   */
  int
  errhidecontext(bool hide_ctx)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  
  	/* we don't bother incrementing recursion_depth */
  	CHECK_STACK_DEPTH();
  
  	edata->hide_ctx = hide_ctx;
  
  	return 0;					/* return value does not matter */
  }
  
  
  /*
   * errfunction --- add reporting function name to the current error
   *
   * This is used when backwards compatibility demands that the function
   * name appear in messages sent to old-protocol clients.  Note that the
   * passed string is expected to be a non-freeable constant string.
   */
  int
  errfunction(const char *funcname)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  
  	/* we don't bother incrementing recursion_depth */
  	CHECK_STACK_DEPTH();
  
  	edata->funcname = funcname;
  	edata->show_funcname = true;
  
  	return 0;					/* return value does not matter */
  }
  
  /*
   * errposition --- add cursor position to the current error
   */
  int
  errposition(int cursorpos)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  
  	/* we don't bother incrementing recursion_depth */
  	CHECK_STACK_DEPTH();
  
  	edata->cursorpos = cursorpos;
  
  	return 0;					/* return value does not matter */
  }
  
  /*
   * internalerrposition --- add internal cursor position to the current error
   */
  int
  internalerrposition(int cursorpos)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  
  	/* we don't bother incrementing recursion_depth */
  	CHECK_STACK_DEPTH();
  
  	edata->internalpos = cursorpos;
  
  	return 0;					/* return value does not matter */
  }
  
  /*
   * internalerrquery --- add internal query text to the current error
   *
   * Can also pass NULL to drop the internal query text entry.  This case
   * is intended for use in error callback subroutines that are editorializing
   * on the layout of the error report.
   */
  int
  internalerrquery(const char *query)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  
  	/* we don't bother incrementing recursion_depth */
  	CHECK_STACK_DEPTH();
  
  	if (edata->internalquery)
  	{
  		pfree(edata->internalquery);
  		edata->internalquery = NULL;
  	}
  
  	if (query)
  		edata->internalquery = MemoryContextStrdup(edata->assoc_context, query);
  
  	return 0;					/* return value does not matter */
  }
  
  /*
   * err_generic_string -- used to set individual ErrorData string fields
   * identified by PG_DIAG_xxx codes.
   *
   * This intentionally only supports fields that don't use localized strings,
   * so that there are no translation considerations.
   *
   * Most potential callers should not use this directly, but instead prefer
   * higher-level abstractions, such as errtablecol() (see relcache.c).
   */
  int
  err_generic_string(int field, const char *str)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  
  	/* we don't bother incrementing recursion_depth */
  	CHECK_STACK_DEPTH();
  
  	switch (field)
  	{
  		case PG_DIAG_SCHEMA_NAME:
  			set_errdata_field(edata->assoc_context, &edata->schema_name, str);
  			break;
  		case PG_DIAG_TABLE_NAME:
  			set_errdata_field(edata->assoc_context, &edata->table_name, str);
  			break;
  		case PG_DIAG_COLUMN_NAME:
  			set_errdata_field(edata->assoc_context, &edata->column_name, str);
  			break;
  		case PG_DIAG_DATATYPE_NAME:
  			set_errdata_field(edata->assoc_context, &edata->datatype_name, str);
  			break;
  		case PG_DIAG_CONSTRAINT_NAME:
  			set_errdata_field(edata->assoc_context, &edata->constraint_name, str);
  			break;
  		default:
  			elog(ERROR, "unsupported ErrorData field id: %d", field);
  			break;
  	}
  
  	return 0;					/* return value does not matter */
  }
  
  /*
   * set_errdata_field --- set an ErrorData string field
   */
  static void
  set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str)
  {
  	Assert(*ptr == NULL);
  	*ptr = MemoryContextStrdup(cxt, str);
  }
  
  /*
   * geterrcode --- return the currently set SQLSTATE error code
   *
   * This is only intended for use in error callback subroutines, since there
   * is no other place outside elog.c where the concept is meaningful.
   */
  int
  geterrcode(void)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  
  	/* we don't bother incrementing recursion_depth */
  	CHECK_STACK_DEPTH();
  
  	return edata->sqlerrcode;
  }
  
  /*
   * geterrposition --- return the currently set error position (0 if none)
   *
   * This is only intended for use in error callback subroutines, since there
   * is no other place outside elog.c where the concept is meaningful.
   */
  int
  geterrposition(void)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  
  	/* we don't bother incrementing recursion_depth */
  	CHECK_STACK_DEPTH();
  
  	return edata->cursorpos;
  }
  
  /*
   * getinternalerrposition --- same for internal error position
   *
   * This is only intended for use in error callback subroutines, since there
   * is no other place outside elog.c where the concept is meaningful.
   */
  int
  getinternalerrposition(void)
  {
  	ErrorData  *edata = &errordata[errordata_stack_depth];
  
  	/* we don't bother incrementing recursion_depth */
  	CHECK_STACK_DEPTH();
  
  	return edata->internalpos;
  }
  
  
  /*
   * Functions to allow construction of error message strings separately from
   * the ereport() call itself.
   *
   * The expected calling convention is
   *
   *	pre_format_elog_string(errno, domain), var = format_elog_string(format,...)
   *
   * which can be hidden behind a macro such as GUC_check_errdetail().  We
   * assume that any functions called in the arguments of format_elog_string()
   * cannot result in re-entrant use of these functions --- otherwise the wrong
   * text domain might be used, or the wrong errno substituted for %m.  This is
   * okay for the current usage with GUC check hooks, but might need further
   * effort someday.
   *
   * The result of format_elog_string() is stored in ErrorContext, and will
   * therefore survive until FlushErrorState() is called.
   */
  static int	save_format_errnumber;
  static const char *save_format_domain;
  
  void
  pre_format_elog_string(int errnumber, const char *domain)
  {
  	/* Save errno before evaluation of argument functions can change it */
  	save_format_errnumber = errnumber;
  	/* Save caller's text domain */
  	save_format_domain = domain;
  }
  
  char *
  format_elog_string(const char *fmt,...)
  {
  	ErrorData	errdata;
  	ErrorData  *edata;
  	MemoryContext oldcontext;
  
  	/* Initialize a mostly-dummy error frame */
  	edata = &errdata;
  	MemSet(edata, 0, sizeof(ErrorData));
  	/* the default text domain is the backend's */
  	edata->domain = save_format_domain ? save_format_domain : PG_TEXTDOMAIN("postgres");
  	/* set the errno to be used to interpret %m */
  	edata->saved_errno = save_format_errnumber;
  
  	oldcontext = MemoryContextSwitchTo(ErrorContext);
  
  	edata->message_id = fmt;
  	EVALUATE_MESSAGE(edata->domain, message, false, true);
  
  	MemoryContextSwitchTo(oldcontext);
  
  	return edata->message;
  }
  
  
  /*
   * 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(edata->assoc_context);
  
  	/*
  	 * Call hook before sending message to log.  The hook function is allowed
  	 * to turn off edata->output_to_server, so we must recheck that afterward.
  	 * Making any other change in the content of edata is not considered
  	 * supported.
  	 *
  	 * Note: the reason why the hook can only turn off output_to_server, and
  	 * not turn it on, is that it'd be unreliable: we will never get here at
  	 * all if errstart() deems the message uninteresting.  A hook that could
  	 * make decisions in that direction would have to hook into errstart(),
  	 * where it would have much less information available.  emit_log_hook is
  	 * intended for custom log filtering and custom log message transmission
  	 * mechanisms.
  	 *
  	 * The log hook has access to both the translated and original English
  	 * error message text, which is passed through to allow it to be used as a
  	 * message identifier. Note that the original text is not available for
  	 * detail, detail_log, hint and context text elements.
  	 */
  	if (edata->output_to_server && emit_log_hook)
  		(*emit_log_hook) (edata);
  
  	/* 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->detail_log)
  		newedata->detail_log = pstrdup(newedata->detail_log);
  	if (newedata->hint)
  		newedata->hint = pstrdup(newedata->hint);
  	if (newedata->context)
  		newedata->context = pstrdup(newedata->context);
  	if (newedata->backtrace)
  		newedata->backtrace = pstrdup(newedata->backtrace);
  	if (newedata->schema_name)
  		newedata->schema_name = pstrdup(newedata->schema_name);
  	if (newedata->table_name)
  		newedata->table_name = pstrdup(newedata->table_name);
  	if (newedata->column_name)
  		newedata->column_name = pstrdup(newedata->column_name);
  	if (newedata->datatype_name)
  		newedata->datatype_name = pstrdup(newedata->datatype_name);
  	if (newedata->constraint_name)
  		newedata->constraint_name = pstrdup(newedata->constraint_name);
  	if (newedata->internalquery)
  		newedata->internalquery = pstrdup(newedata->internalquery);
  
  	/* Use the calling context for string allocation */
  	newedata->assoc_context = CurrentMemoryContext;
  
  	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->detail_log)
  		pfree(edata->detail_log);
  	if (edata->hint)
  		pfree(edata->hint);
  	if (edata->context)
  		pfree(edata->context);
  	if (edata->backtrace)
  		pfree(edata->backtrace);
  	if (edata->schema_name)
  		pfree(edata->schema_name);
  	if (edata->table_name)
  		pfree(edata->table_name);
  	if (edata->column_name)
  		pfree(edata->column_name);
  	if (edata->datatype_name)
  		pfree(edata->datatype_name);
  	if (edata->constraint_name)
  		pfree(edata->constraint_name);
  	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);
  }
  
  /*
   * ThrowErrorData --- report an error described by an ErrorData structure
   *
   * This is somewhat like ReThrowError, but it allows elevels besides ERROR,
   * and the boolean flags such as output_to_server are computed via the
   * default rules rather than being copied from the given ErrorData.
   * This is primarily used to re-report errors originally reported by
   * background worker processes and then propagated (with or without
   * modification) to the backend responsible for them.
   */
  void
  ThrowErrorData(ErrorData *edata)
  {
  	ErrorData  *newedata;
  	MemoryContext oldcontext;
  
  	if (!errstart(edata->elevel, edata->domain))
  		return;					/* error is not to be reported at all */
  
  	newedata = &errordata[errordata_stack_depth];
  	recursion_depth++;
  	oldcontext = MemoryContextSwitchTo(newedata->assoc_context);
  
  	/* Copy the supplied fields to the error stack entry. */
  	if (edata->sqlerrcode != 0)
  		newedata->sqlerrcode = edata->sqlerrcode;
  	if (edata->message)
  		newedata->message = pstrdup(edata->message);
  	if (edata->detail)
  		newedata->detail = pstrdup(edata->detail);
  	if (edata->detail_log)
  		newedata->detail_log = pstrdup(edata->detail_log);
  	if (edata->hint)
  		newedata->hint = pstrdup(edata->hint);
  	if (edata->context)
  		newedata->context = pstrdup(edata->context);
  	if (edata->backtrace)
  		newedata->backtrace = pstrdup(edata->backtrace);
  	/* assume message_id is not available */
  	if (edata->schema_name)
  		newedata->schema_name = pstrdup(edata->schema_name);
  	if (edata->table_name)
  		newedata->table_name = pstrdup(edata->table_name);
  	if (edata->column_name)
  		newedata->column_name = pstrdup(edata->column_name);
  	if (edata->datatype_name)
  		newedata->datatype_name = pstrdup(edata->datatype_name);
  	if (edata->constraint_name)
  		newedata->constraint_name = pstrdup(edata->constraint_name);
  	newedata->cursorpos = edata->cursorpos;
  	newedata->internalpos = edata->internalpos;
  	if (edata->internalquery)
  		newedata->internalquery = pstrdup(edata->internalquery);
  
  	MemoryContextSwitchTo(oldcontext);
  	recursion_depth--;
  
  	/* Process the error. */
  	errfinish(edata->filename, edata->lineno, edata->funcname);
  }
  
  /*
   * 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->detail_log)
  		newedata->detail_log = pstrdup(newedata->detail_log);
  	if (newedata->hint)
  		newedata->hint = pstrdup(newedata->hint);
  	if (newedata->context)
  		newedata->context = pstrdup(newedata->context);
  	if (newedata->backtrace)
  		newedata->backtrace = pstrdup(newedata->backtrace);
  	if (newedata->schema_name)
  		newedata->schema_name = pstrdup(newedata->schema_name);
  	if (newedata->table_name)
  		newedata->table_name = pstrdup(newedata->table_name);
  	if (newedata->column_name)
  		newedata->column_name = pstrdup(newedata->column_name);
  	if (newedata->datatype_name)
  		newedata->datatype_name = pstrdup(newedata->datatype_name);
  	if (newedata->constraint_name)
  		newedata->constraint_name = pstrdup(newedata->constraint_name);
  	if (newedata->internalquery)
  		newedata->internalquery = pstrdup(newedata->internalquery);
  
  	/* Reset the assoc_context to be ErrorContext */
  	newedata->assoc_context = ErrorContext;
  
  	recursion_depth--;
  	PG_RE_THROW();
  }
  
  /*
   * pg_re_throw --- out-of-line implementation of PG_RE_THROW() macro
   */
  void
  pg_re_throw(void)
  {
  	/* If possible, throw the error to the next outer setjmp handler */
  	if (PG_exception_stack != NULL)
  		siglongjmp(*PG_exception_stack, 1);
  	else
  	{
  		/*
  		 * If we get here, elog(ERROR) was thrown inside a PG_TRY block, which
  		 * we have now exited only to discover that there is no outer setjmp
  		 * handler to pass the error to.  Had the error been thrown outside
  		 * the block to begin with, we'd have promoted the error to FATAL, so
  		 * the correct behavior is to make it FATAL now; that is, emit it and
  		 * then call proc_exit.
  		 */
  		ErrorData  *edata = &errordata[errordata_stack_depth];
  
  		Assert(errordata_stack_depth >= 0);
  		Assert(edata->elevel == ERROR);
  		edata->elevel = FATAL;
  
  		/*
  		 * At least in principle, the increase in severity could have changed
  		 * where-to-output decisions, so recalculate.
  		 */
  		edata->output_to_server = should_output_to_server(FATAL);
  		edata->output_to_client = should_output_to_client(FATAL);
  
  		/*
  		 * We can use errfinish() for the rest, but we don't want it to call
  		 * any error context routines a second time.  Since we know we are
  		 * about to exit, it should be OK to just clear the context stack.
  		 */
  		error_context_stack = NULL;
  
  		errfinish(edata->filename, edata->lineno, edata->funcname);
  	}
  
  	/* Doesn't return ... */
  	ExceptionalCondition("pg_re_throw tried to return", "FailedAssertion",
  						 __FILE__, __LINE__);
  }
  
  
  /*
   * GetErrorContextStack - Return the context stack, for display/diags
   *
   * Returns a pstrdup'd string in the caller's context which includes the PG
   * error call stack.  It is the caller's responsibility to ensure this string
   * is pfree'd (or its context cleaned up) when done.
   *
   * This information is collected by traversing the error contexts and calling
   * each context's callback function, each of which is expected to call
   * errcontext() to return a string which can be presented to the user.
   */
  char *
  GetErrorContextStack(void)
  {
  	ErrorData  *edata;
  	ErrorContextCallback *econtext;
  
  	/*
  	 * Okay, crank up a stack entry to store the info in.
  	 */
  	recursion_depth++;
  
  	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")));
  	}
  
  	/*
  	 * Things look good so far, so initialize our error frame
  	 */
  	edata = &errordata[errordata_stack_depth];
  	MemSet(edata, 0, sizeof(ErrorData));
  
  	/*
  	 * Set up assoc_context to be the caller's context, so any allocations
  	 * done (which will include edata->context) will use their context.
  	 */
  	edata->assoc_context = CurrentMemoryContext;
  
  	/*
  	 * Call any context callback functions to collect the context information
  	 * into edata->context.
  	 *
  	 * Errors occurring in callback functions should go through the regular
  	 * error handling code which should handle any recursive errors, though we
  	 * double-check above, just in case.
  	 */
  	for (econtext = error_context_stack;
  		 econtext != NULL;
  		 econtext = econtext->previous)
  		econtext->callback(econtext->arg);
  
  	/*
  	 * Clean ourselves off the stack, any allocations done should have been
  	 * using edata->assoc_context, which we set up earlier to be the caller's
  	 * context, so we're free to just remove our entry off the stack and
  	 * decrement recursion depth and exit.
  	 */
  	errordata_stack_depth--;
  	recursion_depth--;
  
  	/*
  	 * Return a pointer to the string the caller asked for, which should have
  	 * been allocated in their context.
  	 */
  	return edata->context;
  }
  
  
  /*
   * Initialization of error output file
   */
  void
  DebugFileOpen(void)
  {
  	int			fd,
  				istty;
  
  	if (OutputFileName[0])
  	{
  		/*
  		 * A debug-output file name was given.
  		 *
  		 * Make sure we can write the file, and find out if it's a tty.
  		 */
  		if ((fd = open(OutputFileName, O_CREAT | O_APPEND | O_WRONLY,
  					   0666)) < 0)
  			ereport(FATAL,
  					(errcode_for_file_access(),
  					 errmsg("could not open file \"%s\": %m", OutputFileName)));
  		istty = isatty(fd);
  		close(fd);
  
  		/*
  		 * Redirect our stderr to the debug output file.
  		 */
  		if (!freopen(OutputFileName, "a", stderr))
  			ereport(FATAL,
  					(errcode_for_file_access(),
  					 errmsg("could not reopen file \"%s\" as stderr: %m",
  							OutputFileName)));
  
  		/*
  		 * If the file is a tty and we're running under the postmaster, try to
  		 * send stdout there as well (if it isn't a tty then stderr will block
  		 * out stdout, so we may as well let stdout go wherever it was going
  		 * before).
  		 */
  		if (istty && IsUnderPostmaster)
  			if (!freopen(OutputFileName, "a", stdout))
  				ereport(FATAL,
  						(errcode_for_file_access(),
  						 errmsg("could not reopen file \"%s\" as stdout: %m",
  								OutputFileName)));
  	}
  }
  
  
  #ifdef HAVE_SYSLOG
  
  /*
   * Set or update the parameters for syslog logging
   */
  void
  set_syslog_parameters(const char *ident, int facility)
  {
  	/*
  	 * guc.c is likely to call us repeatedly with same parameters, so don't
  	 * thrash the syslog connection unnecessarily.  Also, we do not re-open
  	 * the connection until needed, since this routine will get called whether
  	 * or not Log_destination actually mentions syslog.
  	 *
  	 * Note that we make our own copy of the ident string rather than relying
  	 * on guc.c's.  This may be overly paranoid, but it ensures that we cannot
  	 * accidentally free a string that syslog is still using.
  	 */
  	if (syslog_ident == NULL || strcmp(syslog_ident, ident) != 0 ||
  		syslog_facility != facility)
  	{
  		if (openlog_done)
  		{
  			closelog();
  			openlog_done = false;
  		}
  		if (syslog_ident)
  			free(syslog_ident);
  		syslog_ident = strdup(ident);
  		/* if the strdup fails, we will cope in write_syslog() */
  		syslog_facility = facility;
  	}
  }
  
  
  /*
   * Write a message line to syslog
   */
  static void
  write_syslog(int level, const char *line)
  {
  	static unsigned long seq = 0;
  
  	int			len;
  	const char *nlpos;
  
  	/* Open syslog connection if not done yet */
  	if (!openlog_done)
  	{
  		openlog(syslog_ident ? syslog_ident : "postgres",
  				LOG_PID | LOG_NDELAY | LOG_NOWAIT,
  				syslog_facility);
  		openlog_done = true;
  	}
  
  	/*
  	 * We add a sequence number to each log message to suppress "same"
  	 * messages.
  	 */
  	seq++;
  
  	/*
  	 * Our problem here is that many syslog implementations don't handle long
  	 * messages in an acceptable manner. While this function doesn't help that
  	 * fact, it does work around by splitting up messages into smaller pieces.
  	 *
  	 * We divide into multiple syslog() calls if message is too long or if the
  	 * message contains embedded newline(s).
  	 */
  	len = strlen(line);
  	nlpos = strchr(line, '\n');
  	if (syslog_split_messages && (len > PG_SYSLOG_LIMIT || nlpos != NULL))
  	{
  		int			chunk_nr = 0;
  
  		while (len > 0)
  		{
  			char		buf[PG_SYSLOG_LIMIT + 1];
  			int			buflen;
  			int			i;
  
  			/* if we start at a newline, move ahead one char */
  			if (line[0] == '\n')
  			{
  				line++;
  				len--;
  				/* we need to recompute the next newline's position, too */
  				nlpos = strchr(line, '\n');
  				continue;
  			}
  
  			/* copy one line, or as much as will fit, to buf */
  			if (nlpos != NULL)
  				buflen = nlpos - line;
  			else
  				buflen = len;
  			buflen = Min(buflen, PG_SYSLOG_LIMIT);
  			memcpy(buf, line, buflen);
  			buf[buflen] = '\0';
  
  			/* trim to multibyte letter boundary */
  			buflen = pg_mbcliplen(buf, buflen, buflen);
  			if (buflen <= 0)
  				return;
  			buf[buflen] = '\0';
  
  			/* already word boundary? */
  			if (line[buflen] != '\0' &&
  				!isspace((unsigned char) line[buflen]))
  			{
  				/* try to divide at word boundary */
  				i = buflen - 1;
  				while (i > 0 && !isspace((unsigned char) buf[i]))
  					i--;
  
  				if (i > 0)		/* else couldn't divide word boundary */
  				{
  					buflen = i;
  					buf[i] = '\0';
  				}
  			}
  
  			chunk_nr++;
  
  			if (syslog_sequence_numbers)
  				syslog(level, "[%lu-%d] %s", seq, chunk_nr, buf);
  			else
  				syslog(level, "[%d] %s", chunk_nr, buf);
  
  			line += buflen;
  			len -= buflen;
  		}
  	}
  	else
  	{
  		/* message short enough */
  		if (syslog_sequence_numbers)
  			syslog(level, "[%lu] %s", seq, line);
  		else
  			syslog(level, "%s", line);
  	}
  }
  #endif							/* HAVE_SYSLOG */
  
  #ifdef WIN32
  /*
   * Get the PostgreSQL equivalent of the Windows ANSI code page.  "ANSI" system
   * interfaces (e.g. CreateFileA()) expect string arguments in this encoding.
   * Every process in a given system will find the same value at all times.
   */
  static int
  GetACPEncoding(void)
  {
  	static int	encoding = -2;
  
  	if (encoding == -2)
  		encoding = pg_codepage_to_encoding(GetACP());
  
  	return encoding;
  }
  
  /*
   * Write a message line to the windows event log
   */
  static void
  write_eventlog(int level, const char *line, int len)
  {
  	WCHAR	   *utf16;
  	int			eventlevel = EVENTLOG_ERROR_TYPE;
  	static HANDLE evtHandle = INVALID_HANDLE_VALUE;
  
  	if (evtHandle == INVALID_HANDLE_VALUE)
  	{
  		evtHandle = RegisterEventSource(NULL,
  										event_source ? event_source : DEFAULT_EVENT_SOURCE);
  		if (evtHandle == NULL)
  		{
  			evtHandle = INVALID_HANDLE_VALUE;
  			return;
  		}
  	}
  
  	switch (level)
  	{
  		case DEBUG5:
  		case DEBUG4:
  		case DEBUG3:
  		case DEBUG2:
  		case DEBUG1:
  		case LOG:
  		case LOG_SERVER_ONLY:
  		case INFO:
  		case NOTICE:
  			eventlevel = EVENTLOG_INFORMATION_TYPE;
  			break;
  		case WARNING:
  			eventlevel = EVENTLOG_WARNING_TYPE;
  			break;
  		case ERROR:
  		case FATAL:
  		case PANIC:
  		default:
  			eventlevel = EVENTLOG_ERROR_TYPE;
  			break;
  	}
  
  	/*
  	 * If message character encoding matches the encoding expected by
  	 * ReportEventA(), call it to avoid the hazards of conversion.  Otherwise,
  	 * try to convert the message to UTF16 and write it with ReportEventW().
  	 * Fall back on ReportEventA() if conversion failed.
  	 *
  	 * Since we palloc the structure required for conversion, also fall
  	 * through to writing unconverted if we have not yet set up
  	 * CurrentMemoryContext.
  	 *
  	 * Also verify that we are not on our way into error recursion trouble due
  	 * to error messages thrown deep inside pgwin32_message_to_UTF16().
  	 */
  	if (!in_error_recursion_trouble() &&
  		CurrentMemoryContext != NULL &&
  		GetMessageEncoding() != GetACPEncoding())
  	{
  		utf16 = pgwin32_message_to_UTF16(line, len, NULL);
  		if (utf16)
  		{
  			ReportEventW(evtHandle,
  						 eventlevel,
  						 0,
  						 0,		/* All events are Id 0 */
  						 NULL,
  						 1,
  						 0,
  						 (LPCWSTR *) &utf16,
  						 NULL);
  			/* XXX Try ReportEventA() when ReportEventW() fails? */
  
  			pfree(utf16);
  			return;
  		}
  	}
  	ReportEventA(evtHandle,
  				 eventlevel,
  				 0,
  				 0,				/* All events are Id 0 */
  				 NULL,
  				 1,
  				 0,
  				 &line,
  				 NULL);
  }
  #endif							/* WIN32 */
  
  static void
  write_console(const char *line, int len)
  {
  	int			rc;
  
  #ifdef WIN32
  
  	/*
  	 * Try to convert the message to UTF16 and write it with WriteConsoleW().
  	 * Fall back on write() if anything fails.
  	 *
  	 * In contrast to write_eventlog(), don't skip straight to write() based
  	 * on the applicable encodings.  Unlike WriteConsoleW(), write() depends
  	 * on the suitability of the console output code page.  Since we put
  	 * stderr into binary mode in SubPostmasterMain(), write() skips the
  	 * necessary translation anyway.
  	 *
  	 * WriteConsoleW() will fail if stderr is redirected, so just fall through
  	 * to writing unconverted to the logfile in this case.
  	 *
  	 * Since we palloc the structure required for conversion, also fall
  	 * through to writing unconverted if we have not yet set up
  	 * CurrentMemoryContext.
  	 */
  	if (!in_error_recursion_trouble() &&
  		!redirection_done &&
  		CurrentMemoryContext != NULL)
  	{
  		WCHAR	   *utf16;
  		int			utf16len;
  
  		utf16 = pgwin32_message_to_UTF16(line, len, &utf16len);
  		if (utf16 != NULL)
  		{
  			HANDLE		stdHandle;
  			DWORD		written;
  
  			stdHandle = GetStdHandle(STD_ERROR_HANDLE);
  			if (WriteConsoleW(stdHandle, utf16, utf16len, &written, NULL))
  			{
  				pfree(utf16);
  				return;
  			}
  
  			/*
  			 * In case WriteConsoleW() failed, fall back to writing the
  			 * message unconverted.
  			 */
  			pfree(utf16);
  		}
  	}
  #else
  
  	/*
  	 * Conversion on non-win32 platforms is not implemented yet. It requires
  	 * non-throw version of pg_do_encoding_conversion(), that converts
  	 * unconvertable characters to '?' without errors.
  	 */
  #endif
  
  	/*
  	 * We ignore any error from write() here.  We have no useful way to report
  	 * it ... certainly whining on stderr isn't likely to be productive.
  	 */
  	rc = write(fileno(stderr), line, len);
  	(void) rc;
  }
  
  /*
   * setup formatted_log_time, for consistent times between CSV and regular logs
   */
  static void
  setup_formatted_log_time(void)
  {
  	pg_time_t	stamp_time;
  	char		msbuf[13];
  
  	if (!saved_timeval_set)
  	{
  		gettimeofday(&saved_timeval, NULL);
  		saved_timeval_set = true;
  	}
  
  	stamp_time = (pg_time_t) saved_timeval.tv_sec;
  
  	/*
  	 * Note: we expect that guc.c will ensure that log_timezone is set up (at
  	 * least with a minimal GMT value) before Log_line_prefix can become
  	 * nonempty or CSV mode can be selected.
  	 */
  	pg_strftime(formatted_log_time, FORMATTED_TS_LEN,
  	/* leave room for milliseconds... */
  				"%Y-%m-%d %H:%M:%S     %Z",
  				pg_localtime(&stamp_time, log_timezone));
  
  	/* 'paste' milliseconds into place... */
  	sprintf(msbuf, ".%03d", (int) (saved_timeval.tv_usec / 1000));
  	memcpy(formatted_log_time + 19, msbuf, 4);
  }
  
  /*
   * setup formatted_start_time
   */
  static void
  setup_formatted_start_time(void)
  {
  	pg_time_t	stamp_time = (pg_time_t) MyStartTime;
  
  	/*
  	 * Note: we expect that guc.c will ensure that log_timezone is set up (at
  	 * least with a minimal GMT value) before Log_line_prefix can become
  	 * nonempty or CSV mode can be selected.
  	 */
  	pg_strftime(formatted_start_time, FORMATTED_TS_LEN,
  				"%Y-%m-%d %H:%M:%S %Z",
  				pg_localtime(&stamp_time, log_timezone));
  }
  
  /*
   * process_log_prefix_padding --- helper function for processing the format
   * string in log_line_prefix
   *
   * Note: This function returns NULL if it finds something which
   * it deems invalid in the format string.
   */
  static const char *
  process_log_prefix_padding(const char *p, int *ppadding)
  {
  	int			paddingsign = 1;
  	int			padding = 0;
  
  	if (*p == '-')
  	{
  		p++;
  
  		if (*p == '\0')			/* Did the buf end in %- ? */
  			return NULL;
  		paddingsign = -1;
  	}
  
  	/* generate an int version of the numerical string */
  	while (*p >= '0' && *p <= '9')
  		padding = padding * 10 + (*p++ - '0');
  
  	/* format is invalid if it ends with the padding number */
  	if (*p == '\0')
  		return NULL;
  
  	padding *= paddingsign;
  	*ppadding = padding;
  	return p;
  }
  
  /*
   * Format tag info for log lines; append to the provided buffer.
   */
  static void
  log_line_prefix(StringInfo buf, ErrorData *edata)
  {
  	/* static counter for line numbers */
  	static long log_line_number = 0;
  
  	/* has counter been reset in current process? */
  	static int	log_my_pid = 0;
  	int			padding;
  	const char *p;
  
  	/*
  	 * This is one of the few places where we'd rather not inherit a static
  	 * variable's value from the postmaster.  But since we will, reset it when
  	 * MyProcPid changes. MyStartTime also changes when MyProcPid does, so
  	 * reset the formatted start timestamp too.
  	 */
  	if (log_my_pid != MyProcPid)
  	{
  		log_line_number = 0;
  		log_my_pid = MyProcPid;
  		formatted_start_time[0] = '\0';
  	}
  	log_line_number++;
  
  	if (Log_line_prefix == NULL)
  		return;					/* in case guc hasn't run yet */
  
  	for (p = Log_line_prefix; *p != '\0'; p++)
  	{
  		if (*p != '%')
  		{
  			/* literal char, just copy */
  			appendStringInfoChar(buf, *p);
  			continue;
  		}
  
  		/* must be a '%', so skip to the next char */
  		p++;
  		if (*p == '\0')
  			break;				/* format error - ignore it */
  		else if (*p == '%')
  		{
  			/* string contains %% */
  			appendStringInfoChar(buf, '%');
  			continue;
  		}
  
  
  		/*
  		 * Process any formatting which may exist after the '%'.  Note that
  		 * process_log_prefix_padding moves p past the padding number if it
  		 * exists.
  		 *
  		 * Note: Since only '-', '0' to '9' are valid formatting characters we
  		 * can do a quick check here to pre-check for formatting. If the char
  		 * is not formatting then we can skip a useless function call.
  		 *
  		 * Further note: At least on some platforms, passing %*s rather than
  		 * %s to appendStringInfo() is substantially slower, so many of the
  		 * cases below avoid doing that unless non-zero padding is in fact
  		 * specified.
  		 */
  		if (*p > '9')
  			padding = 0;
  		else if ((p = process_log_prefix_padding(p, &padding)) == NULL)
  			break;
  
  		/* process the option */
  		switch (*p)
  		{
  			case 'a':
  				if (MyProcPort)
  				{
  					const char *appname = application_name;
  
  					if (appname == NULL || *appname == '\0')
  						appname = _("[unknown]");
  					if (padding != 0)
  						appendStringInfo(buf, "%*s", padding, appname);
  					else
  						appendStringInfoString(buf, appname);
  				}
  				else if (padding != 0)
  					appendStringInfoSpaces(buf,
  										   padding > 0 ? padding : -padding);
  
  				break;
  			case 'b':
  				{
  					const char *backend_type_str;
  
  					if (MyProcPid == PostmasterPid)
  						backend_type_str = "postmaster";
  					else if (MyBackendType == B_BG_WORKER)
  						backend_type_str = MyBgworkerEntry->bgw_type;
  					else
  						backend_type_str = GetBackendTypeDesc(MyBackendType);
  
  					if (padding != 0)
  						appendStringInfo(buf, "%*s", padding, backend_type_str);
  					else
  						appendStringInfoString(buf, backend_type_str);
  					break;
  				}
  			case 'u':
  				if (MyProcPort)
  				{
  					const char *username = MyProcPort->user_name;
  
  					if (username == NULL || *username == '\0')
  						username = _("[unknown]");
  					if (padding != 0)
  						appendStringInfo(buf, "%*s", padding, username);
  					else
  						appendStringInfoString(buf, username);
  				}
  				else if (padding != 0)
  					appendStringInfoSpaces(buf,
  										   padding > 0 ? padding : -padding);
  				break;
  			case 'd':
  				if (MyProcPort)
  				{
  					const char *dbname = MyProcPort->database_name;
  
  					if (dbname == NULL || *dbname == '\0')
  						dbname = _("[unknown]");
  					if (padding != 0)
  						appendStringInfo(buf, "%*s", padding, dbname);
  					else
  						appendStringInfoString(buf, dbname);
  				}
  				else if (padding != 0)
  					appendStringInfoSpaces(buf,
  										   padding > 0 ? padding : -padding);
  				break;
  			case 'c':
  				if (padding != 0)
  				{
  					char		strfbuf[128];
  
  					snprintf(strfbuf, sizeof(strfbuf) - 1, "%lx.%x",
  							 (long) (MyStartTime), MyProcPid);
  					appendStringInfo(buf, "%*s", padding, strfbuf);
  				}
  				else
  					appendStringInfo(buf, "%lx.%x", (long) (MyStartTime), MyProcPid);
  				break;
  			case 'p':
  				if (padding != 0)
  					appendStringInfo(buf, "%*d", padding, MyProcPid);
  				else
  					appendStringInfo(buf, "%d", MyProcPid);
  				break;
  
  			case 'P':
  				if (MyProc)
  				{
  					PGPROC	   *leader = MyProc->lockGroupLeader;
  
  					/*
  					 * Show the leader only for active parallel workers. This
  					 * leaves out the leader of a parallel group.
  					 */
  					if (leader == NULL || leader->pid == MyProcPid)
  						appendStringInfoSpaces(buf,
  											   padding > 0 ? padding : -padding);
  					else if (padding != 0)
  						appendStringInfo(buf, "%*d", padding, leader->pid);
  					else
  						appendStringInfo(buf, "%d", leader->pid);
  				}
  				else if (padding != 0)
  					appendStringInfoSpaces(buf,
  										   padding > 0 ? padding : -padding);
  				break;
  
  			case 'l':
  				if (padding != 0)
  					appendStringInfo(buf, "%*ld", padding, log_line_number);
  				else
  					appendStringInfo(buf, "%ld", log_line_number);
  				break;
  			case 'm':
  				setup_formatted_log_time();
  				if (padding != 0)
  					appendStringInfo(buf, "%*s", padding, formatted_log_time);
  				else
  					appendStringInfoString(buf, formatted_log_time);
  				break;
  			case 't':
  				{
  					pg_time_t	stamp_time = (pg_time_t) time(NULL);
  					char		strfbuf[128];
  
  					pg_strftime(strfbuf, sizeof(strfbuf),
  								"%Y-%m-%d %H:%M:%S %Z",
  								pg_localtime(&stamp_time, log_timezone));
  					if (padding != 0)
  						appendStringInfo(buf, "%*s", padding, strfbuf);
  					else
  						appendStringInfoString(buf, strfbuf);
  				}
  				break;
  			case 'n':
  				{
  					char		strfbuf[128];
  
  					if (!saved_timeval_set)
  					{
  						gettimeofday(&saved_timeval, NULL);
  						saved_timeval_set = true;
  					}
  
  					snprintf(strfbuf, sizeof(strfbuf), "%ld.%03d",
  							 (long) saved_timeval.tv_sec,
  							 (int) (saved_timeval.tv_usec / 1000));
  
  					if (padding != 0)
  						appendStringInfo(buf, "%*s", padding, strfbuf);
  					else
  						appendStringInfoString(buf, strfbuf);
  				}
  				break;
  			case 's':
  				if (formatted_start_time[0] == '\0')
  					setup_formatted_start_time();
  				if (padding != 0)
  					appendStringInfo(buf, "%*s", padding, formatted_start_time);
  				else
  					appendStringInfoString(buf, formatted_start_time);
  				break;
  			case 'i':
  				if (MyProcPort)
  				{
  					const char *psdisp;
  					int			displen;
  
  					psdisp = get_ps_display(&displen);
  					if (padding != 0)
  						appendStringInfo(buf, "%*s", padding, psdisp);
  					else
  						appendBinaryStringInfo(buf, psdisp, displen);
  
  				}
  				else if (padding != 0)
  					appendStringInfoSpaces(buf,
  										   padding > 0 ? padding : -padding);
  				break;
  			case 'r':
  				if (MyProcPort && MyProcPort->remote_host)
  				{
  					if (padding != 0)
  					{
  						if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0')
  						{
  							/*
  							 * This option is slightly special as the port
  							 * number may be appended onto the end. Here we
  							 * need to build 1 string which contains the
  							 * remote_host and optionally the remote_port (if
  							 * set) so we can properly align the string.
  							 */
  
  							char	   *hostport;
  
  							hostport = psprintf("%s(%s)", MyProcPort->remote_host, MyProcPort->remote_port);
  							appendStringInfo(buf, "%*s", padding, hostport);
  							pfree(hostport);
  						}
  						else
  							appendStringInfo(buf, "%*s", padding, MyProcPort->remote_host);
  					}
  					else
  					{
  						/* padding is 0, so we don't need a temp buffer */
  						appendStringInfoString(buf, MyProcPort->remote_host);
  						if (MyProcPort->remote_port &&
  							MyProcPort->remote_port[0] != '\0')
  							appendStringInfo(buf, "(%s)",
  											 MyProcPort->remote_port);
  					}
  
  				}
  				else if (padding != 0)
  					appendStringInfoSpaces(buf,
  										   padding > 0 ? padding : -padding);
  				break;
  			case 'h':
  				if (MyProcPort && MyProcPort->remote_host)
  				{
  					if (padding != 0)
  						appendStringInfo(buf, "%*s", padding, MyProcPort->remote_host);
  					else
  						appendStringInfoString(buf, MyProcPort->remote_host);
  				}
  				else if (padding != 0)
  					appendStringInfoSpaces(buf,
  										   padding > 0 ? padding : -padding);
  				break;
  			case 'q':
  				/* in postmaster and friends, stop if %q is seen */
  				/* in a backend, just ignore */
  				if (MyProcPort == NULL)
  					return;
  				break;
  			case 'v':
  				/* keep VXID format in sync with lockfuncs.c */
  				if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
  				{
  					if (padding != 0)
  					{
  						char		strfbuf[128];
  
  						snprintf(strfbuf, sizeof(strfbuf) - 1, "%d/%u",
  								 MyProc->backendId, MyProc->lxid);
  						appendStringInfo(buf, "%*s", padding, strfbuf);
  					}
  					else
  						appendStringInfo(buf, "%d/%u", MyProc->backendId, MyProc->lxid);
  				}
  				else if (padding != 0)
  					appendStringInfoSpaces(buf,
  										   padding > 0 ? padding : -padding);
  				break;
  			case 'x':
  				if (padding != 0)
  					appendStringInfo(buf, "%*u", padding, GetTopTransactionIdIfAny());
  				else
  					appendStringInfo(buf, "%u", GetTopTransactionIdIfAny());
  				break;
  			case 'e':
  				if (padding != 0)
  					appendStringInfo(buf, "%*s", padding, unpack_sql_state(edata->sqlerrcode));
  				else
  					appendStringInfoString(buf, unpack_sql_state(edata->sqlerrcode));
  				break;
  			default:
  				/* format error - ignore it */
  				break;
  		}
  	}
  }
  
  /*
   * append a CSV'd version of a string to a StringInfo
   * We use the PostgreSQL defaults for CSV, i.e. quote = escape = '"'
   * If it's NULL, append nothing.
   */
  static inline void
  appendCSVLiteral(StringInfo buf, const char *data)
  {
  	const char *p = data;
  	char		c;
  
  	/* avoid confusing an empty string with NULL */
  	if (p == NULL)
  		return;
  
  	appendStringInfoCharMacro(buf, '"');
  	while ((c = *p++) != '\0')
  	{
  		if (c == '"')
  			appendStringInfoCharMacro(buf, '"');
  		appendStringInfoCharMacro(buf, c);
  	}
  	appendStringInfoCharMacro(buf, '"');
  }
  
  /*
   * Constructs the error message, depending on the Errordata it gets, in a CSV
   * format which is described in doc/src/sgml/config.sgml.
   */
  static void
  write_csvlog(ErrorData *edata)
  {
  	StringInfoData buf;
  	bool		print_stmt = false;
  
  	/* static counter for line numbers */
  	static long log_line_number = 0;
  
  	/* has counter been reset in current process? */
  	static int	log_my_pid = 0;
  
  	/*
  	 * This is one of the few places where we'd rather not inherit a static
  	 * variable's value from the postmaster.  But since we will, reset it when
  	 * MyProcPid changes.
  	 */
  	if (log_my_pid != MyProcPid)
  	{
  		log_line_number = 0;
  		log_my_pid = MyProcPid;
  		formatted_start_time[0] = '\0';
  	}
  	log_line_number++;
  
  	initStringInfo(&buf);
  
  	/*
  	 * timestamp with milliseconds
  	 *
  	 * Check if the timestamp is already calculated for the syslog message,
  	 * and use it if so.  Otherwise, get the current timestamp.  This is done
  	 * to put same timestamp in both syslog and csvlog messages.
  	 */
  	if (formatted_log_time[0] == '\0')
  		setup_formatted_log_time();
  
  	appendStringInfoString(&buf, formatted_log_time);
  	appendStringInfoChar(&buf, ',');
  
  	/* username */
  	if (MyProcPort)
  		appendCSVLiteral(&buf, MyProcPort->user_name);
  	appendStringInfoChar(&buf, ',');
  
  	/* database name */
  	if (MyProcPort)
  		appendCSVLiteral(&buf, MyProcPort->database_name);
  	appendStringInfoChar(&buf, ',');
  
  	/* Process id  */
  	if (MyProcPid != 0)
  		appendStringInfo(&buf, "%d", MyProcPid);
  	appendStringInfoChar(&buf, ',');
  
  	/* Remote host and port */
  	if (MyProcPort && MyProcPort->remote_host)
  	{
  		appendStringInfoChar(&buf, '"');
  		appendStringInfoString(&buf, MyProcPort->remote_host);
  		if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0')
  		{
  			appendStringInfoChar(&buf, ':');
  			appendStringInfoString(&buf, MyProcPort->remote_port);
  		}
  		appendStringInfoChar(&buf, '"');
  	}
  	appendStringInfoChar(&buf, ',');
  
  	/* session id */
  	appendStringInfo(&buf, "%lx.%x", (long) MyStartTime, MyProcPid);
  	appendStringInfoChar(&buf, ',');
  
  	/* Line number */
  	appendStringInfo(&buf, "%ld", log_line_number);
  	appendStringInfoChar(&buf, ',');
  
  	/* PS display */
  	if (MyProcPort)
  	{
  		StringInfoData msgbuf;
  		const char *psdisp;
  		int			displen;
  
  		initStringInfo(&msgbuf);
  
  		psdisp = get_ps_display(&displen);
  		appendBinaryStringInfo(&msgbuf, psdisp, displen);
  		appendCSVLiteral(&buf, msgbuf.data);
  
  		pfree(msgbuf.data);
  	}
  	appendStringInfoChar(&buf, ',');
  
  	/* session start timestamp */
  	if (formatted_start_time[0] == '\0')
  		setup_formatted_start_time();
  	appendStringInfoString(&buf, formatted_start_time);
  	appendStringInfoChar(&buf, ',');
  
  	/* Virtual transaction id */
  	/* keep VXID format in sync with lockfuncs.c */
  	if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
  		appendStringInfo(&buf, "%d/%u", MyProc->backendId, MyProc->lxid);
  	appendStringInfoChar(&buf, ',');
  
  	/* Transaction id */
  	appendStringInfo(&buf, "%u", GetTopTransactionIdIfAny());
  	appendStringInfoChar(&buf, ',');
  
  	/* Error severity */
  	appendStringInfoString(&buf, _(error_severity(edata->elevel)));
  	appendStringInfoChar(&buf, ',');
  
  	/* SQL state code */
  	appendStringInfoString(&buf, unpack_sql_state(edata->sqlerrcode));
  	appendStringInfoChar(&buf, ',');
  
  	/* errmessage */
  	appendCSVLiteral(&buf, edata->message);
  	appendStringInfoChar(&buf, ',');
  
  	/* errdetail or errdetail_log */
  	if (edata->detail_log)
  		appendCSVLiteral(&buf, edata->detail_log);
  	else
  		appendCSVLiteral(&buf, edata->detail);
  	appendStringInfoChar(&buf, ',');
  
  	/* errhint */
  	appendCSVLiteral(&buf, edata->hint);
  	appendStringInfoChar(&buf, ',');
  
  	/* internal query */
  	appendCSVLiteral(&buf, edata->internalquery);
  	appendStringInfoChar(&buf, ',');
  
  	/* if printed internal query, print internal pos too */
  	if (edata->internalpos > 0 && edata->internalquery != NULL)
  		appendStringInfo(&buf, "%d", edata->internalpos);
  	appendStringInfoChar(&buf, ',');
  
  	/* errcontext */
  	if (!edata->hide_ctx)
  		appendCSVLiteral(&buf, edata->context);
  	appendStringInfoChar(&buf, ',');
  
  	/* user query --- only reported if not disabled by the caller */
  	if (is_log_level_output(edata->elevel, log_min_error_statement) &&
  		debug_query_string != NULL &&
  		!edata->hide_stmt)
  		print_stmt = true;
  	if (print_stmt)
  		appendCSVLiteral(&buf, debug_query_string);
  	appendStringInfoChar(&buf, ',');
  	if (print_stmt && edata->cursorpos > 0)
  		appendStringInfo(&buf, "%d", edata->cursorpos);
  	appendStringInfoChar(&buf, ',');
  
  	/* file error location */
  	if (Log_error_verbosity >= PGERROR_VERBOSE)
  	{
  		StringInfoData msgbuf;
  
  		initStringInfo(&msgbuf);
  
  		if (edata->funcname && edata->filename)
  			appendStringInfo(&msgbuf, "%s, %s:%d",
  							 edata->funcname, edata->filename,
  							 edata->lineno);
  		else if (edata->filename)
  			appendStringInfo(&msgbuf, "%s:%d",
  							 edata->filename, edata->lineno);
  		appendCSVLiteral(&buf, msgbuf.data);
  		pfree(msgbuf.data);
  	}
  	appendStringInfoChar(&buf, ',');
  
  	/* application name */
  	if (application_name)
  		appendCSVLiteral(&buf, application_name);
  
  	appendStringInfoChar(&buf, ',');
  
  	/* backend type */
  	if (MyProcPid == PostmasterPid)
  		appendCSVLiteral(&buf, "postmaster");
  	else if (MyBackendType == B_BG_WORKER)
  		appendCSVLiteral(&buf, MyBgworkerEntry->bgw_type);
  	else
  		appendCSVLiteral(&buf, GetBackendTypeDesc(MyBackendType));
  
  	appendStringInfoChar(&buf, ',');
  
  	/* leader PID */
  	if (MyProc)
  	{
  		PGPROC	   *leader = MyProc->lockGroupLeader;
  
  		/*
  		 * Show the leader only for active parallel workers.  This leaves out
  		 * the leader of a parallel group.
  		 */
  		if (leader && leader->pid != MyProcPid)
  			appendStringInfo(&buf, "%d", leader->pid);
  	}
  
  	appendStringInfoChar(&buf, '\n');
  
  	/* If in the syslogger process, try to write messages direct to file */
  	if (MyBackendType == B_LOGGER)
  		write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
  	else
  		write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
  
  	pfree(buf.data);
  }
  
  /*
   * Unpack MAKE_SQLSTATE code. Note that this returns a pointer to a
   * static buffer.
   */
  char *
  unpack_sql_state(int sql_state)
  {
  	static char buf[12];
  	int			i;
  
  	for (i = 0; i < 5; i++)
  	{
  		buf[i] = PGUNSIXBIT(sql_state);
  		sql_state >>= 6;
  	}
  
  	buf[i] = '\0';
  	return buf;
  }
  
  
  /*
   * Write error report to server's log
   */
  static void
  send_message_to_server_log(ErrorData *edata)
  {
  	StringInfoData buf;
  
  	initStringInfo(&buf);
  
  	saved_timeval_set = false;
  	formatted_log_time[0] = '\0';
  
  	log_line_prefix(&buf, edata);
  	appendStringInfo(&buf, "%s:  ", _(error_severity(edata->elevel)));
  
  	if (Log_error_verbosity >= PGERROR_VERBOSE)
  		appendStringInfo(&buf, "%s: ", unpack_sql_state(edata->sqlerrcode));
  
  	if (edata->message)
  		append_with_tabs(&buf, edata->message);
  	else
  		append_with_tabs(&buf, _("missing error text"));
  
  	if (edata->cursorpos > 0)
  		appendStringInfo(&buf, _(" at character %d"),
  						 edata->cursorpos);
  	else if (edata->internalpos > 0)
  		appendStringInfo(&buf, _(" at character %d"),
  						 edata->internalpos);
  
  	appendStringInfoChar(&buf, '\n');
  
  	if (Log_error_verbosity >= PGERROR_DEFAULT)
  	{
  		if (edata->detail_log)
  		{
  			log_line_prefix(&buf, edata);
  			appendStringInfoString(&buf, _("DETAIL:  "));
  			append_with_tabs(&buf, edata->detail_log);
  			appendStringInfoChar(&buf, '\n');
  		}
  		else if (edata->detail)
  		{
  			log_line_prefix(&buf, edata);
  			appendStringInfoString(&buf, _("DETAIL:  "));
  			append_with_tabs(&buf, edata->detail);
  			appendStringInfoChar(&buf, '\n');
  		}
  		if (edata->hint)
  		{
  			log_line_prefix(&buf, edata);
  			appendStringInfoString(&buf, _("HINT:  "));
  			append_with_tabs(&buf, edata->hint);
  			appendStringInfoChar(&buf, '\n');
  		}
  		if (edata->internalquery)
  		{
  			log_line_prefix(&buf, edata);
  			appendStringInfoString(&buf, _("QUERY:  "));
  			append_with_tabs(&buf, edata->internalquery);
  			appendStringInfoChar(&buf, '\n');
  		}
  		if (edata->context && !edata->hide_ctx)
  		{
  			log_line_prefix(&buf, edata);
  			appendStringInfoString(&buf, _("CONTEXT:  "));
  			append_with_tabs(&buf, edata->context);
  			appendStringInfoChar(&buf, '\n');
  		}
  		if (Log_error_verbosity >= PGERROR_VERBOSE)
  		{
  			/* assume no newlines in funcname or filename... */
  			if (edata->funcname && edata->filename)
  			{
  				log_line_prefix(&buf, edata);
  				appendStringInfo(&buf, _("LOCATION:  %s, %s:%d\n"),
  								 edata->funcname, edata->filename,
  								 edata->lineno);
  			}
  			else if (edata->filename)
  			{
  				log_line_prefix(&buf, edata);
  				appendStringInfo(&buf, _("LOCATION:  %s:%d\n"),
  								 edata->filename, edata->lineno);
  			}
  		}
  		if (edata->backtrace)
  		{
  			log_line_prefix(&buf, edata);
  			appendStringInfoString(&buf, _("BACKTRACE:  "));
  			append_with_tabs(&buf, edata->backtrace);
  			appendStringInfoChar(&buf, '\n');
  		}
  	}
  
  	/*
  	 * If the user wants the query that generated this error logged, do it.
  	 */
  	if (is_log_level_output(edata->elevel, log_min_error_statement) &&
  		debug_query_string != NULL &&
  		!edata->hide_stmt)
  	{
  		log_line_prefix(&buf, edata);
  		appendStringInfoString(&buf, _("STATEMENT:  "));
  		append_with_tabs(&buf, debug_query_string);
  		appendStringInfoChar(&buf, '\n');
  	}
  
  #ifdef HAVE_SYSLOG
  	/* Write to syslog, if enabled */
  	if (Log_destination & LOG_DESTINATION_SYSLOG)
  	{
  		int			syslog_level;
  
  		switch (edata->elevel)
  		{
  			case DEBUG5:
  			case DEBUG4:
  			case DEBUG3:
  			case DEBUG2:
  			case DEBUG1:
  				syslog_level = LOG_DEBUG;
  				break;
  			case LOG:
  			case LOG_SERVER_ONLY:
  			case INFO:
  				syslog_level = LOG_INFO;
  				break;
  			case NOTICE:
  			case WARNING:
  				syslog_level = LOG_NOTICE;
  				break;
  			case ERROR:
  				syslog_level = LOG_WARNING;
  				break;
  			case FATAL:
  				syslog_level = LOG_ERR;
  				break;
  			case PANIC:
  			default:
  				syslog_level = LOG_CRIT;
  				break;
  		}
  
  		write_syslog(syslog_level, buf.data);
  	}
  #endif							/* HAVE_SYSLOG */
  
  #ifdef WIN32
  	/* Write to eventlog, if enabled */
  	if (Log_destination & LOG_DESTINATION_EVENTLOG)
  	{
  		write_eventlog(edata->elevel, buf.data, buf.len);
  	}
  #endif							/* WIN32 */
  
  	/* Write to stderr, if enabled */
  	if ((Log_destination & LOG_DESTINATION_STDERR) || whereToSendOutput == DestDebug)
  	{
  		/*
  		 * Use the chunking protocol if we know the syslogger should be
  		 * catching stderr output, and we are not ourselves the syslogger.
  		 * Otherwise, just do a vanilla write to stderr.
  		 */
  		if (redirection_done && MyBackendType != B_LOGGER)
  			write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_STDERR);
  #ifdef WIN32
  
  		/*
  		 * In a win32 service environment, there is no usable stderr. Capture
  		 * anything going there and write it to the eventlog instead.
  		 *
  		 * If stderr redirection is active, it was OK to write to stderr above
  		 * because that's really a pipe to the syslogger process.
  		 */
  		else if (pgwin32_is_service())
  			write_eventlog(edata->elevel, buf.data, buf.len);
  #endif
  		else
  			write_console(buf.data, buf.len);
  	}
  
  	/* If in the syslogger process, try to write messages direct to file */
  	if (MyBackendType == B_LOGGER)
  		write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_STDERR);
  
  	/* Write to CSV log if enabled */
  	if (Log_destination & LOG_DESTINATION_CSVLOG)
  	{
  		if (redirection_done || MyBackendType == B_LOGGER)
  		{
  			/*
  			 * send CSV data if it's safe to do so (syslogger doesn't need the
  			 * pipe). First get back the space in the message buffer.
  			 */
  			pfree(buf.data);
  			write_csvlog(edata);
  		}
  		else
  		{
  			/*
  			 * syslogger not up (yet), so just dump the message to stderr,
  			 * unless we already did so above.
  			 */
  			if (!(Log_destination & LOG_DESTINATION_STDERR) &&
  				whereToSendOutput != DestDebug)
  				write_console(buf.data, buf.len);
  			pfree(buf.data);
  		}
  	}
  	else
  	{
  		pfree(buf.data);
  	}
  }
  
  /*
   * Send data to the syslogger using the chunked protocol
   *
   * Note: when there are multiple backends writing into the syslogger pipe,
   * it's critical that each write go into the pipe indivisibly, and not
   * get interleaved with data from other processes.  Fortunately, the POSIX
   * spec requires that writes to pipes be atomic so long as they are not
   * more than PIPE_BUF bytes long.  So we divide long messages into chunks
   * that are no more than that length, and send one chunk per write() call.
   * The collector process knows how to reassemble the chunks.
   *
   * Because of the atomic write requirement, there are only two possible
   * results from write() here: -1 for failure, or the requested number of
   * bytes.  There is not really anything we can do about a failure; retry would
   * probably be an infinite loop, and we can't even report the error usefully.
   * (There is noplace else we could send it!)  So we might as well just ignore
   * the result from write().  However, on some platforms you get a compiler
   * warning from ignoring write()'s result, so do a little dance with casting
   * rc to void to shut up the compiler.
   */
  static void
  write_pipe_chunks(char *data, int len, int dest)
  {
  	PipeProtoChunk p;
  	int			fd = fileno(stderr);
  	int			rc;
  
  	Assert(len > 0);
  
  	p.proto.nuls[0] = p.proto.nuls[1] = '\0';
  	p.proto.pid = MyProcPid;
  
  	/* write all but the last chunk */
  	while (len > PIPE_MAX_PAYLOAD)
  	{
  		p.proto.is_last = (dest == LOG_DESTINATION_CSVLOG ? 'F' : 'f');
  		p.proto.len = PIPE_MAX_PAYLOAD;
  		memcpy(p.proto.data, data, PIPE_MAX_PAYLOAD);
  		rc = write(fd, &p, PIPE_HEADER_SIZE + PIPE_MAX_PAYLOAD);
  		(void) rc;
  		data += PIPE_MAX_PAYLOAD;
  		len -= PIPE_MAX_PAYLOAD;
  	}
  
  	/* write the last chunk */
  	p.proto.is_last = (dest == LOG_DESTINATION_CSVLOG ? 'T' : 't');
  	p.proto.len = len;
  	memcpy(p.proto.data, data, len);
  	rc = write(fd, &p, PIPE_HEADER_SIZE + len);
  	(void) rc;
  }
  
  
  /*
   * Append a text string to the error report being built for the client.
   *
   * This is ordinarily identical to pq_sendstring(), but if we are in
   * error recursion trouble we skip encoding conversion, because of the
   * possibility that the problem is a failure in the encoding conversion
   * subsystem itself.  Code elsewhere should ensure that the passed-in
   * strings will be plain 7-bit ASCII, and thus not in need of conversion,
   * in such cases.  (In particular, we disable localization of error messages
   * to help ensure that's true.)
   */
  static void
  err_sendstring(StringInfo buf, const char *str)
  {
  	if (in_error_recursion_trouble())
  		pq_send_ascii_string(buf, str);
  	else
  		pq_sendstring(buf, str);
  }
  
  /*
   * Write error report to client
   */
  static void
  send_message_to_frontend(ErrorData *edata)
  {
  	StringInfoData msgbuf;
  
  	/* 'N' (Notice) is for nonfatal conditions, 'E' is for errors */
  	pq_beginmessage(&msgbuf, (edata->elevel < ERROR) ? 'N' : 'E');
  
  	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
  	{
  		/* New style with separate fields */
  		const char *sev;
  		char		tbuf[12];
  		int			ssval;
  		int			i;
  
  		sev = error_severity(edata->elevel);
  		pq_sendbyte(&msgbuf, PG_DIAG_SEVERITY);
  		err_sendstring(&msgbuf, _(sev));
  		pq_sendbyte(&msgbuf, PG_DIAG_SEVERITY_NONLOCALIZED);
  		err_sendstring(&msgbuf, sev);
  
  		/* unpack MAKE_SQLSTATE code */
  		ssval = edata->sqlerrcode;
  		for (i = 0; i < 5; i++)
  		{
  			tbuf[i] = PGUNSIXBIT(ssval);
  			ssval >>= 6;
  		}
  		tbuf[i] = '\0';
  
  		pq_sendbyte(&msgbuf, PG_DIAG_SQLSTATE);
  		err_sendstring(&msgbuf, tbuf);
  
  		/* M field is required per protocol, so always send something */
  		pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_PRIMARY);
  		if (edata->message)
  			err_sendstring(&msgbuf, edata->message);
  		else
  			err_sendstring(&msgbuf, _("missing error text"));
  
  		if (edata->detail)
  		{
  			pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_DETAIL);
  			err_sendstring(&msgbuf, edata->detail);
  		}
  
  		/* detail_log is intentionally not used here */
  
  		if (edata->hint)
  		{
  			pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_HINT);
  			err_sendstring(&msgbuf, edata->hint);
  		}
  
  		if (edata->context)
  		{
  			pq_sendbyte(&msgbuf, PG_DIAG_CONTEXT);
  			err_sendstring(&msgbuf, edata->context);
  		}
  
  		if (edata->schema_name)
  		{
  			pq_sendbyte(&msgbuf, PG_DIAG_SCHEMA_NAME);
  			err_sendstring(&msgbuf, edata->schema_name);
  		}
  
  		if (edata->table_name)
  		{
  			pq_sendbyte(&msgbuf, PG_DIAG_TABLE_NAME);
  			err_sendstring(&msgbuf, edata->table_name);
  		}
  
  		if (edata->column_name)
  		{
  			pq_sendbyte(&msgbuf, PG_DIAG_COLUMN_NAME);
  			err_sendstring(&msgbuf, edata->column_name);
  		}
  
  		if (edata->datatype_name)
  		{
  			pq_sendbyte(&msgbuf, PG_DIAG_DATATYPE_NAME);
  			err_sendstring(&msgbuf, edata->datatype_name);
  		}
  
  		if (edata->constraint_name)
  		{
  			pq_sendbyte(&msgbuf, PG_DIAG_CONSTRAINT_NAME);
  			err_sendstring(&msgbuf, edata->constraint_name);
  		}
  
  		if (edata->cursorpos > 0)
  		{
  			snprintf(tbuf, sizeof(tbuf), "%d", edata->cursorpos);
  			pq_sendbyte(&msgbuf, PG_DIAG_STATEMENT_POSITION);
  			err_sendstring(&msgbuf, tbuf);
  		}
  
  		if (edata->internalpos > 0)
  		{
  			snprintf(tbuf, sizeof(tbuf), "%d", edata->internalpos);
  			pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_POSITION);
  			err_sendstring(&msgbuf, tbuf);
  		}
  
  		if (edata->internalquery)
  		{
  			pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_QUERY);
  			err_sendstring(&msgbuf, edata->internalquery);
  		}
  
  		if (edata->filename)
  		{
  			pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_FILE);
  			err_sendstring(&msgbuf, edata->filename);
  		}
  
  		if (edata->lineno > 0)
  		{
  			snprintf(tbuf, sizeof(tbuf), "%d", edata->lineno);
  			pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_LINE);
  			err_sendstring(&msgbuf, tbuf);
  		}
  
  		if (edata->funcname)
  		{
  			pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_FUNCTION);
  			err_sendstring(&msgbuf, edata->funcname);
  		}
  
  		pq_sendbyte(&msgbuf, '\0'); /* terminator */
  	}
  	else
  	{
  		/* Old style --- gin up a backwards-compatible message */
  		StringInfoData buf;
  
  		initStringInfo(&buf);
  
  		appendStringInfo(&buf, "%s:  ", _(error_severity(edata->elevel)));
  
  		if (edata->show_funcname && edata->funcname)
  			appendStringInfo(&buf, "%s: ", edata->funcname);
  
  		if (edata->message)
  			appendStringInfoString(&buf, edata->message);
  		else
  			appendStringInfoString(&buf, _("missing error text"));
  
  		if (edata->cursorpos > 0)
  			appendStringInfo(&buf, _(" at character %d"),
  							 edata->cursorpos);
  		else if (edata->internalpos > 0)
  			appendStringInfo(&buf, _(" at character %d"),
  							 edata->internalpos);
  
  		appendStringInfoChar(&buf, '\n');
  
  		err_sendstring(&msgbuf, buf.data);
  
  		pfree(buf.data);
  	}
  
  	pq_endmessage(&msgbuf);
  
  	/*
  	 * This flush is normally not necessary, since postgres.c will flush out
  	 * waiting data when control returns to the main loop. But it seems best
  	 * to leave it here, so that the client has some clue what happened if the
  	 * backend dies before getting back to the main loop ... error/notice
  	 * messages should not be a performance-critical path anyway, so an extra
  	 * flush won't hurt much ...
  	 */
  	pq_flush();
  }
  
  
  /*
   * Support routines for formatting error messages.
   */
  
  
  /*
   * error_severity --- get string representing elevel
   *
   * The string is not localized here, but we mark the strings for translation
   * so that callers can invoke _() on the result.
   */
  static const char *
  error_severity(int elevel)
  {
  	const char *prefix;
  
  	switch (elevel)
  	{
  		case DEBUG1:
  		case DEBUG2:
  		case DEBUG3:
  		case DEBUG4:
  		case DEBUG5:
  			prefix = gettext_noop("DEBUG");
  			break;
  		case LOG:
  		case LOG_SERVER_ONLY:
  			prefix = gettext_noop("LOG");
  			break;
  		case INFO:
  			prefix = gettext_noop("INFO");
  			break;
  		case NOTICE:
  			prefix = gettext_noop("NOTICE");
  			break;
  		case WARNING:
  			prefix = gettext_noop("WARNING");
  			break;
  		case ERROR:
  			prefix = gettext_noop("ERROR");
  			break;
  		case FATAL:
  			prefix = gettext_noop("FATAL");
  			break;
  		case PANIC:
  			prefix = gettext_noop("PANIC");
  			break;
  		default:
  			prefix = "???";
  			break;
  	}
  
  	return prefix;
  }
  
  
  /*
   *	append_with_tabs
   *
   *	Append the string to the StringInfo buffer, inserting a tab after any
   *	newline.
   */
  static void
  append_with_tabs(StringInfo buf, const char *str)
  {
  	char		ch;
  
  	while ((ch = *str++) != '\0')
  	{
  		appendStringInfoCharMacro(buf, ch);
  		if (ch == '\n')
  			appendStringInfoCharMacro(buf, '\t');
  	}
  }
  
  
  /*
   * Write errors to stderr (or by equal means when stderr is
   * not available). Used before ereport/elog can be used
   * safely (memory context, GUC load etc)
   */
  void
  write_stderr(const char *fmt,...)
  {
  	va_list		ap;
  
  #ifdef WIN32
  	char		errbuf[2048];	/* Arbitrary size? */
  #endif
  
  	fmt = _(fmt);
  
  	va_start(ap, fmt);
  #ifndef WIN32
  	/* On Unix, we just fprintf to stderr */
  	vfprintf(stderr, fmt, ap);
  	fflush(stderr);
  #else
  	vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
  
  	/*
  	 * On Win32, we print to stderr if running on a console, or write to
  	 * eventlog if running as a service
  	 */
  	if (pgwin32_is_service())	/* Running as a service */
  	{
  		write_eventlog(ERROR, errbuf, strlen(errbuf));
  	}
  	else
  	{
  		/* Not running as service, write to stderr */
  		write_console(errbuf, strlen(errbuf));
  		fflush(stderr);
  	}
  #endif
  	va_end(ap);
  }
  
  
  /*
   * Adjust the level of a recovery-related message per trace_recovery_messages.
   *
   * The argument is the default log level of the message, eg, DEBUG2.  (This
   * should only be applied to DEBUGn log messages, otherwise it's a no-op.)
   * If the level is >= trace_recovery_messages, we return LOG, causing the
   * message to be logged unconditionally (for most settings of
   * log_min_messages).  Otherwise, we return the argument unchanged.
   * The message will then be shown based on the setting of log_min_messages.
   *
   * Intention is to keep this for at least the whole of the 9.0 production
   * release, so we can more easily diagnose production problems in the field.
   * It should go away eventually, though, because it's an ugly and
   * hard-to-explain kluge.
   */
  int
  trace_recovery(int trace_level)
  {
  	if (trace_level < LOG &&
  		trace_level >= trace_recovery_messages)
  		return LOG;
  
  	return trace_level;
  }
  