summaryrefslogtreecommitdiffstats
path: root/src/backend/utils/error
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
commit46651ce6fe013220ed397add242004d764fc0153 (patch)
tree6e5299f990f88e60174a1d3ae6e48eedd2688b2b /src/backend/utils/error
parentInitial commit. (diff)
downloadpostgresql-14-46651ce6fe013220ed397add242004d764fc0153.tar.xz
postgresql-14-46651ce6fe013220ed397add242004d764fc0153.zip
Adding upstream version 14.5.upstream/14.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/utils/error')
-rw-r--r--src/backend/utils/error/Makefile19
-rw-r--r--src/backend/utils/error/assert.c70
-rw-r--r--src/backend/utils/error/elog.c3616
3 files changed, 3705 insertions, 0 deletions
diff --git a/src/backend/utils/error/Makefile b/src/backend/utils/error/Makefile
new file mode 100644
index 0000000..612da21
--- /dev/null
+++ b/src/backend/utils/error/Makefile
@@ -0,0 +1,19 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+# Makefile for utils/error
+#
+# IDENTIFICATION
+# src/backend/utils/error/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/utils/error
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = \
+ assert.o \
+ elog.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/error/assert.c b/src/backend/utils/error/assert.c
new file mode 100644
index 0000000..70a410a
--- /dev/null
+++ b/src/backend/utils/error/assert.c
@@ -0,0 +1,70 @@
+/*-------------------------------------------------------------------------
+ *
+ * assert.c
+ * Assert support code.
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/error/assert.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <unistd.h>
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+/*
+ * ExceptionalCondition - Handles the failure of an Assert()
+ *
+ * We intentionally do not go through elog() here, on the grounds of
+ * wanting to minimize the amount of infrastructure that has to be
+ * working to report an assertion failure.
+ */
+void
+ExceptionalCondition(const char *conditionName,
+ const char *errorType,
+ const char *fileName,
+ int lineNumber)
+{
+ /* Report the failure on stderr (or local equivalent) */
+ if (!PointerIsValid(conditionName)
+ || !PointerIsValid(fileName)
+ || !PointerIsValid(errorType))
+ write_stderr("TRAP: ExceptionalCondition: bad arguments in PID %d\n",
+ (int) getpid());
+ else
+ write_stderr("TRAP: %s(\"%s\", File: \"%s\", Line: %d, PID: %d)\n",
+ errorType, conditionName,
+ fileName, lineNumber, (int) getpid());
+
+ /* Usually this shouldn't be needed, but make sure the msg went out */
+ fflush(stderr);
+
+ /* If we have support for it, dump a simple backtrace */
+#ifdef HAVE_BACKTRACE_SYMBOLS
+ {
+ void *buf[100];
+ int nframes;
+
+ nframes = backtrace(buf, lengthof(buf));
+ backtrace_symbols_fd(buf, nframes, fileno(stderr));
+ }
+#endif
+
+ /*
+ * If configured to do so, sleep indefinitely to allow user to attach a
+ * debugger. It would be nice to use pg_usleep() here, but that can sleep
+ * at most 2G usec or ~33 minutes, which seems too short.
+ */
+#ifdef SLEEP_ON_ASSERT
+ sleep(1000000);
+#endif
+
+ abort();
+}
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
new file mode 100644
index 0000000..a3e1c59
--- /dev/null
+++ b/src/backend/utils/error/elog.c
@@ -0,0 +1,3616 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2021, 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 "pgstat.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 (elevel == WARNING_CLIENT_ONLY)
+ {
+ /* never sent to log, regardless of log_min_level */
+ return false;
+ }
+ 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;
+ /* Some Windows compilers use backslashes in __FILE__ strings */
+ 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();
+ }
+
+ /* 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);
+
+ /*
+ * Let the statistics collector know. Only mark the session as
+ * terminated by fatal error if there is no other known cause.
+ */
+ if (pgStatSessionEndCause == DISCONNECT_NORMAL)
+ pgStatSessionEndCause = DISCONNECT_FATAL;
+
+ /*
+ * 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 */
+}
+
+
+/*
+ * errhint_plural --- add a hint error message text to the current error,
+ * with support for pluralization of the message text
+ */
+int
+errhint_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, hint, false);
+
+ 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 */
+}
+
+/*
+ * 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:
+ case WARNING_CLIENT_ONLY:
+ 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.
+ *
+ * XXX: We have a no-throw version now. It doesn't convert to '?' though.
+ */
+#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;
+ case 'Q':
+ if (padding != 0)
+ appendStringInfo(buf, "%*lld", padding,
+ (long long) pgstat_get_my_query_id());
+ else
+ appendStringInfo(buf, "%lld",
+ (long long) pgstat_get_my_query_id());
+ 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, ',');
+
+ /* query id */
+ appendStringInfo(&buf, "%lld", (long long) pgstat_get_my_query_id());
+
+ 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:
+ case WARNING_CLIENT_ONLY:
+ 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;
+
+ /*
+ * We no longer support pre-3.0 FE/BE protocol, except here. If a client
+ * tries to connect using an older protocol version, it's nice to send the
+ * "protocol version not supported" error in a format the client
+ * understands. If protocol hasn't been set yet, early in backend
+ * startup, assume modern protocol.
+ */
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3 || FrontendProtocol == 0)
+ {
+ /* New style with separate fields */
+ const char *sev;
+ char tbuf[12];
+ int ssval;
+ int i;
+
+ /* 'N' (Notice) is for nonfatal conditions, 'E' is for errors */
+ pq_beginmessage(&msgbuf, (edata->elevel < ERROR) ? 'N' : 'E');
+
+ 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 */
+
+ pq_endmessage(&msgbuf);
+ }
+ else
+ {
+ /* Old style --- gin up a backwards-compatible message */
+ StringInfoData buf;
+
+ initStringInfo(&buf);
+
+ appendStringInfo(&buf, "%s: ", _(error_severity(edata->elevel)));
+
+ if (edata->message)
+ appendStringInfoString(&buf, edata->message);
+ else
+ appendStringInfoString(&buf, _("missing error text"));
+
+ appendStringInfoChar(&buf, '\n');
+
+ /* 'N' (Notice) is for nonfatal conditions, 'E' is for errors */
+ pq_putmessage_v2((edata->elevel < ERROR) ? 'N' : 'E', buf.data, buf.len + 1);
+
+ pfree(buf.data);
+ }
+
+ /*
+ * 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:
+ case WARNING_CLIENT_ONLY:
+ 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;
+}