/src/backend/utils/error/elog.c
https://bitbucket.org/gencer/postgres · C · 3632 lines · 2149 code · 444 blank · 1039 comment · 437 complexity · 510896950c351b1d229f235678bc8735 MD5 · raw file
- /*-------------------------------------------------------------------------
- *
- * 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-2014, 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
- #include "access/transam.h"
- #include "access/xact.h"
- #include "libpq/libpq.h"
- #include "libpq/pqformat.h"
- #include "mb/pg_wchar.h"
- #include "miscadmin.h"
- #include "postmaster/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"
- #undef _
- #define _(x) err_gettext(x)
- static const char *
- err_gettext(const char *str)
- /* This extension allows gcc to check the format string for consistency with
- the supplied arguments. */
- __attribute__((format_arg(1)));
- static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
- /* 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;
- #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
- static void write_console(const char *line, int len);
- #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 */
- /* buffers for formatted timestamps that might be used by both
- * log_line_prefix and csv logs.
- */
- #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 *process_log_prefix_padding(const char *p, int *padding);
- static void log_line_prefix(StringInfo buf, ErrorData *edata);
- static void send_message_to_server_log(ErrorData *edata);
- static void send_message_to_frontend(ErrorData *edata);
- static char *expand_fmt_string(const char *fmt, ErrorData *edata);
- static const char *useful_strerror(int errnum);
- static const char *get_errno_symbol(int errnum);
- static const char *error_severity(int elevel);
- static void append_with_tabs(StringInfo buf, const char *str);
- static bool is_log_level_output(int elevel, int log_min_level);
- static void write_pipe_chunks(char *data, int len, int dest);
- static void write_csvlog(ErrorData *edata);
- static void setup_formatted_log_time(void);
- static void setup_formatted_start_time(void);
- /*
- * 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 --- begin an error-reporting cycle
- *
- * Create a stack entry and store the given parameters in it. 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 *filename, int lineno,
- const char *funcname, 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.
- */
- /* Determine whether message is enabled for server log output */
- output_to_server = is_log_level_output(elevel, log_min_messages);
- /* Determine whether message is enabled for client output */
- if (whereToSendOutput == DestRemote && elevel != COMMERROR)
- {
- /*
- * client_min_messages is honored only after we complete the
- * authentication handshake. This is required both for security
- * reasons and because many clients can't handle NOTICE messages
- * during authentication.
- */
- if (ClientAuthInProgress)
- output_to_client = (elevel >= ERROR);
- else
- output_to_client = (elevel >= client_min_messages ||
- elevel == INFO);
- }
- /* Skip processing effort if non-error message will not be output */
- 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)
- {
- /* Ooops, hard crash time; very little we can do safely here */
- write_stderr("error occurred at %s:%d before error message processing is available\n",
- filename ? filename : "(unknown file)", lineno);
- exit(2);
- }
- /*
- * Okay, crank up a stack entry to store the info in.
- */
- if (recursion_depth++ > 0 && elevel >= ERROR)
- {
- /*
- * Ooops, 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;
- if (filename)
- {
- const char *slash;
- /* keep only base name, useful especially for vpath builds */
- slash = strrchr(filename, '/');
- if (slash)
- filename = slash + 1;
- }
- edata->filename = filename;
- edata->lineno = lineno;
- edata->funcname = funcname;
- /* the default text domain is the backend's */
- edata->domain = domain ? domain : PG_TEXTDOMAIN("postgres");
- /* 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;
- }
- /*
- * errfinish --- end an error-reporting cycle
- *
- * Produce the appropriate error report(s) and pop the error stack.
- *
- * If elevel is ERROR or worse, control does not return to the caller.
- * See elog.h for the error level definitions.
- */
- void
- errfinish(int dummy,...)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- int elevel;
- bool save_ImmediateInterruptOK;
- MemoryContext oldcontext;
- ErrorContextCallback *econtext;
- recursion_depth++;
- CHECK_STACK_DEPTH();
- elevel = edata->elevel;
- /*
- * Ensure we can't get interrupted while performing error reporting. This
- * is needed to prevent recursive entry to functions like syslog(), which
- * may not be re-entrant.
- *
- * Note: other places that save-and-clear ImmediateInterruptOK also do
- * HOLD_INTERRUPTS(), but that should not be necessary here since we
- * don't call anything that could turn on ImmediateInterruptOK.
- */
- save_ImmediateInterruptOK = ImmediateInterruptOK;
- ImmediateInterruptOK = false;
- /*
- * Do processing in ErrorContext, which we hope has enough reserved space
- * to report an error.
- */
- oldcontext = MemoryContextSwitchTo(ErrorContext);
- /*
- * 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.)
- *
- * Note that we intentionally don't restore ImmediateInterruptOK here,
- * even if it was set at entry. We definitely don't want that on
- * while doing error cleanup.
- */
- InterruptHoldoffCount = 0;
- CritSectionCount = 0; /* should be unnecessary, but... */
- /*
- * Note that we leave CurrentMemoryContext set to ErrorContext. The
- * handler should reset it to something else soon.
- */
- recursion_depth--;
- PG_RE_THROW();
- }
- /*
- * If we are doing FATAL or PANIC, abort any old-style COPY OUT in
- * progress, so that we can report the message before dying. (Without
- * this, pq_putmessage will refuse to send the message at all, which is
- * what we want for NOTICE messages, but not for fatal exits.) This hack
- * is necessary because of poor design of old-style copy protocol. Note
- * we must do this even if client is fool enough to have set
- * client_min_messages above FATAL, so don't look at output_to_client.
- */
- if (elevel >= FATAL && whereToSendOutput == DestRemote)
- pq_endcopyout(true);
- /* Emit the message to the right places */
- EmitErrorReport();
- /* Now free up subsidiary data attached to stack entry, and release it */
- if (edata->message)
- pfree(edata->message);
- if (edata->detail)
- pfree(edata->detail);
- if (edata->detail_log)
- pfree(edata->detail_log);
- if (edata->hint)
- pfree(edata->hint);
- if (edata->context)
- pfree(edata->context);
- if (edata->schema_name)
- pfree(edata->schema_name);
- if (edata->table_name)
- pfree(edata->table_name);
- if (edata->column_name)
- pfree(edata->column_name);
- if (edata->datatype_name)
- pfree(edata->datatype_name);
- if (edata->constraint_name)
- pfree(edata->constraint_name);
- if (edata->internalquery)
- pfree(edata->internalquery);
- errordata_stack_depth--;
- /* Exit error-handling context */
- MemoryContextSwitchTo(oldcontext);
- recursion_depth--;
- /*
- * Perform error recovery action as specified by elevel.
- */
- if (elevel == FATAL)
- {
- /*
- * For a FATAL error, we let proc_exit clean up and exit.
- *
- * If we just reported a startup failure, the client will disconnect
- * on receiving it, so don't send any more to the client.
- */
- if (PG_exception_stack == NULL && whereToSendOutput == DestRemote)
- whereToSendOutput = DestNone;
- /*
- * fflush here is just to improve the odds that we get to see the
- * error message, in case things are so hosed that proc_exit crashes.
- * Any other code you might be tempted to add here should probably be
- * in an on_proc_exit or on_shmem_exit callback instead.
- */
- fflush(stdout);
- fflush(stderr);
- /*
- * Do normal process-exit cleanup, then return exit code 1 to indicate
- * FATAL termination. The postmaster may or may not consider this
- * worthy of panic, depending on which subprocess returns it.
- */
- proc_exit(1);
- }
- if (elevel >= PANIC)
- {
- /*
- * Serious crash time. Postmaster will observe SIGABRT process exit
- * status and kill the other backends too.
- *
- * XXX: what if we are *in* the postmaster? abort() won't kill our
- * children...
- */
- fflush(stdout);
- fflush(stderr);
- abort();
- }
- /*
- * We reach here if elevel <= WARNING. OK to return to caller, so restore
- * caller's setting of ImmediateInterruptOK.
- */
- ImmediateInterruptOK = save_ImmediateInterruptOK;
- /*
- * But 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 EPIPE:
- #ifdef ECONNRESET
- case ECONNRESET:
- #endif
- 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) \
- { \
- char *fmtbuf; \
- StringInfoData buf; \
- /* Internationalize the error format string */ \
- if (translateit && !in_error_recursion_trouble()) \
- fmt = dgettext((domain), fmt); \
- /* Expand %m in format string */ \
- fmtbuf = expand_fmt_string(fmt, edata); \
- 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; \
- va_start(args, fmt); \
- needed = appendStringInfoVA(&buf, fmtbuf, args); \
- va_end(args); \
- if (needed == 0) \
- break; \
- enlargeStringInfo(&buf, needed); \
- } \
- /* Done with expanded fmt */ \
- pfree(fmtbuf); \
- /* 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; \
- char *fmtbuf; \
- 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); \
- /* Expand %m in format string */ \
- fmtbuf = expand_fmt_string(fmt, edata); \
- 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; \
- va_start(args, n); \
- needed = appendStringInfoVA(&buf, fmtbuf, args); \
- va_end(args); \
- if (needed == 0) \
- break; \
- enlargeStringInfo(&buf, needed); \
- } \
- /* Done with expanded fmt */ \
- pfree(fmtbuf); \
- /* 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);
- EVALUATE_MESSAGE(edata->domain, message, false, true);
- MemoryContextSwitchTo(oldcontext);
- recursion_depth--;
- return 0; /* return value does not matter */
- }
- /*
- * 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);
- 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);
- EVALUATE_MESSAGE_PLURAL(edata->domain, message, false);
- MemoryContextSwitchTo(oldcontext);
- recursion_depth--;
- return 0; /* return value does not matter */
- }
- /*
- * errdetail --- add a detail error message text to the current error
- */
- int
- errdetail(const char *fmt,...)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- MemoryContext oldcontext;
- recursion_depth++;
- CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(edata->assoc_context);
- EVALUATE_MESSAGE(edata->domain, detail, false, true);
- MemoryContextSwitchTo(oldcontext);
- recursion_depth--;
- return 0; /* return value does not matter */
- }
- /*
- * errdetail_internal --- add a detail error message text to the current error
- *
- * This is exactly like errdetail() except that strings passed to
- * errdetail_internal are not translated, and are customarily left out of the
- * internationalization message dictionary. This should be used for detail
- * messages that seem not worth translating for one reason or another
- * (typically, that they don't seem to be useful to average users).
- */
- int
- errdetail_internal(const char *fmt,...)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- MemoryContext oldcontext;
- recursion_depth++;
- CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(edata->assoc_context);
- EVALUATE_MESSAGE(edata->domain, detail, false, false);
- MemoryContextSwitchTo(oldcontext);
- recursion_depth--;
- return 0; /* return value does not matter */
- }
- /*
- * errdetail_log --- add a detail_log error message text to the current error
- */
- int
- errdetail_log(const char *fmt,...)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- MemoryContext oldcontext;
- recursion_depth++;
- CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(edata->assoc_context);
- EVALUATE_MESSAGE(edata->domain, detail_log, false, true);
- MemoryContextSwitchTo(oldcontext);
- recursion_depth--;
- return 0; /* return value does not matter */
- }
- /*
- * errdetail_log_plural --- add a detail_log error message text to the current error
- * with support for pluralization of the message text
- */
- int
- errdetail_log_plural(const char *fmt_singular, const char *fmt_plural,
- unsigned long n,...)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- MemoryContext oldcontext;
- recursion_depth++;
- CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(edata->assoc_context);
- EVALUATE_MESSAGE_PLURAL(edata->domain, detail_log, false);
- MemoryContextSwitchTo(oldcontext);
- recursion_depth--;
- return 0; /* return value does not matter */
- }
- /*
- * errdetail_plural --- add a detail error message text to the current error,
- * with support for pluralization of the message text
- */
- int
- errdetail_plural(const char *fmt_singular, const char *fmt_plural,
- unsigned long n,...)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- MemoryContext oldcontext;
- recursion_depth++;
- CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(edata->assoc_context);
- EVALUATE_MESSAGE_PLURAL(edata->domain, detail, false);
- MemoryContextSwitchTo(oldcontext);
- recursion_depth--;
- return 0; /* return value does not matter */
- }
- /*
- * errhint --- add a hint error message text to the current error
- */
- int
- errhint(const char *fmt,...)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- MemoryContext oldcontext;
- recursion_depth++;
- CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(edata->assoc_context);
- EVALUATE_MESSAGE(edata->domain, hint, false, true);
- MemoryContextSwitchTo(oldcontext);
- recursion_depth--;
- return 0; /* return value does not matter */
- }
- /*
- * errcontext_msg --- add a context error message text to the current error
- *
- * Unlike other cases, multiple calls are allowed to build up a stack of
- * context information. We assume earlier calls represent more-closely-nested
- * states.
- */
- int
- errcontext_msg(const char *fmt,...)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- MemoryContext oldcontext;
- recursion_depth++;
- CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(edata->assoc_context);
- EVALUATE_MESSAGE(edata->context_domain, context, true, true);
- MemoryContextSwitchTo(oldcontext);
- recursion_depth--;
- return 0; /* return value does not matter */
- }
- /*
- * set_errcontext_domain --- set message domain to be used by errcontext()
- *
- * errcontext_msg() can be called from a different module than the original
- * ereport(), so we cannot use the message domain passed in errstart() to
- * translate it. Instead, each errcontext_msg() call should be preceded by
- * a set_errcontext_domain() call to specify the domain. This is usually
- * done transparently by the errcontext() macro.
- */
- int
- set_errcontext_domain(const char *domain)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- /* we don't bother incrementing recursion_depth */
- CHECK_STACK_DEPTH();
- edata->context_domain = domain;
- 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 */
- }
- /*
- * errfunction --- add reporting function name to the current error
- *
- * This is used when backwards compatibility demands that the function
- * name appear in messages sent to old-protocol clients. Note that the
- * passed string is expected to be a non-freeable constant string.
- */
- int
- errfunction(const char *funcname)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- /* we don't bother incrementing recursion_depth */
- CHECK_STACK_DEPTH();
- edata->funcname = funcname;
- edata->show_funcname = true;
- return 0; /* return value does not matter */
- }
- /*
- * errposition --- add cursor position to the current error
- */
- int
- errposition(int cursorpos)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- /* we don't bother incrementing recursion_depth */
- CHECK_STACK_DEPTH();
- edata->cursorpos = cursorpos;
- return 0; /* return value does not matter */
- }
- /*
- * internalerrposition --- add internal cursor position to the current error
- */
- int
- internalerrposition(int cursorpos)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- /* we don't bother incrementing recursion_depth */
- CHECK_STACK_DEPTH();
- edata->internalpos = cursorpos;
- return 0; /* return value does not matter */
- }
- /*
- * internalerrquery --- add internal query text to the current error
- *
- * Can also pass NULL to drop the internal query text entry. This case
- * is intended for use in error callback subroutines that are editorializing
- * on the layout of the error report.
- */
- int
- internalerrquery(const char *query)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- /* we don't bother incrementing recursion_depth */
- CHECK_STACK_DEPTH();
- if (edata->internalquery)
- {
- pfree(edata->internalquery);
- edata->internalquery = NULL;
- }
- if (query)
- edata->internalquery = MemoryContextStrdup(edata->assoc_context, query);
- return 0; /* return value does not matter */
- }
- /*
- * err_generic_string -- used to set individual ErrorData string fields
- * identified by PG_DIAG_xxx codes.
- *
- * This intentionally only supports fields that don't use localized strings,
- * so that there are no translation considerations.
- *
- * Most potential callers should not use this directly, but instead prefer
- * higher-level abstractions, such as errtablecol() (see relcache.c).
- */
- int
- err_generic_string(int field, const char *str)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- /* we don't bother incrementing recursion_depth */
- CHECK_STACK_DEPTH();
- switch (field)
- {
- case PG_DIAG_SCHEMA_NAME:
- set_errdata_field(edata->assoc_context, &edata->schema_name, str);
- break;
- case PG_DIAG_TABLE_NAME:
- set_errdata_field(edata->assoc_context, &edata->table_name, str);
- break;
- case PG_DIAG_COLUMN_NAME:
- set_errdata_field(edata->assoc_context, &edata->column_name, str);
- break;
- case PG_DIAG_DATATYPE_NAME:
- set_errdata_field(edata->assoc_context, &edata->datatype_name, str);
- break;
- case PG_DIAG_CONSTRAINT_NAME:
- set_errdata_field(edata->assoc_context, &edata->constraint_name, str);
- break;
- default:
- elog(ERROR, "unsupported ErrorData field id: %d", field);
- break;
- }
- return 0; /* return value does not matter */
- }
- /*
- * set_errdata_field --- set an ErrorData string field
- */
- static void
- set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str)
- {
- Assert(*ptr == NULL);
- *ptr = MemoryContextStrdup(cxt, str);
- }
- /*
- * geterrcode --- return the currently set SQLSTATE error code
- *
- * This is only intended for use in error callback subroutines, since there
- * is no other place outside elog.c where the concept is meaningful.
- */
- int
- geterrcode(void)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- /* we don't bother incrementing recursion_depth */
- CHECK_STACK_DEPTH();
- return edata->sqlerrcode;
- }
- /*
- * geterrposition --- return the currently set error position (0 if none)
- *
- * This is only intended for use in error callback subroutines, since there
- * is no other place outside elog.c where the concept is meaningful.
- */
- int
- geterrposition(void)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- /* we don't bother incrementing recursion_depth */
- CHECK_STACK_DEPTH();
- return edata->cursorpos;
- }
- /*
- * getinternalerrposition --- same for internal error position
- *
- * This is only intended for use in error callback subroutines, since there
- * is no other place outside elog.c where the concept is meaningful.
- */
- int
- getinternalerrposition(void)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- /* we don't bother incrementing recursion_depth */
- CHECK_STACK_DEPTH();
- return edata->internalpos;
- }
- /*
- * elog_start --- startup for old-style API
- *
- * All that we do here is stash the hidden filename/lineno/funcname
- * arguments into a stack entry, along with the current value of errno.
- *
- * We need this to be separate from elog_finish because there's no other
- * C89-compliant way to deal with inserting extra arguments into the elog
- * call. (When using C99's __VA_ARGS__, we could possibly merge this with
- * elog_finish, but there doesn't seem to be a good way to save errno before
- * evaluating the format arguments if we do that.)
- */
- void
- elog_start(const char *filename, int lineno, const char *funcname)
- {
- ErrorData *edata;
- /* Make sure that memory context initialization has finished */
- if (ErrorContext == NULL)
- {
- /* Ooops, hard crash time; very little we can do safely here */
- write_stderr("error occurred at %s:%d before error message processing is available\n",
- filename ? filename : "(unknown file)", lineno);
- exit(2);
- }
- 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. Note that the message is intentionally not localized,
- * else failure to convert it to client encoding could cause further
- * recursion.
- */
- errordata_stack_depth = -1; /* make room on stack */
- ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
- }
- edata = &errordata[errordata_stack_depth];
- if (filename)
- {
- const char *slash;
- /* keep only base name, useful especially for vpath builds */
- slash = strrchr(filename, '/');
- if (slash)
- filename = slash + 1;
- }
- edata->filename = filename;
- edata->lineno = lineno;
- edata->funcname = funcname;
- /* errno is saved now so that error parameter eval can't change it */
- edata->saved_errno = errno;
- /* Use ErrorContext for any allocations done at this level. */
- edata->assoc_context = ErrorContext;
- }
- /*
- * elog_finish --- finish up for old-style API
- */
- void
- elog_finish(int elevel, const char *fmt,...)
- {
- ErrorData *edata = &errordata[errordata_stack_depth];
- MemoryContext oldcontext;
- CHECK_STACK_DEPTH();
- /*
- * Do errstart() to see if we actually want to report the message.
- */
- errordata_stack_depth--;
- errno = edata->saved_errno;
- if (!errstart(elevel, edata->filename, edata->lineno, edata->funcname, NULL))
- return; /* nothing to do */
- /*
- * Format error message just like errmsg_internal().
- */
- recursion_depth++;
- oldcontext = MemoryContextSwitchTo(edata->assoc_context);
- EVALUATE_MESSAGE(edata->domain, message, false, false);
- MemoryContextSwitchTo(oldcontext);
- recursion_depth--;
- /*
- * And let errfinish() finish up.
- */
- errfinish(0);
- }
- /*
- * 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);
- 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.
- */
- 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->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->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);
- }
- /*
- * 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->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. This should stay in
- * sync with errstart(), which see for comments.
- */
- if (IsPostmasterEnvironment)
- edata->output_to_server = is_log_level_output(FATAL,
- log_min_messages);
- else
- edata->output_to_server = (FATAL >= log_min_messages);
- if (whereToSendOutput == DestRemote)
- {
- if (ClientAuthInProgress)
- edata->output_to_client = true;
- else
- edata->output_to_client = (FATAL >= client_min_messages);
- }
- /*
- * 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(0);
- }
- /* 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 (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++;
- syslog(level, "[%lu-%d] %s", seq, chunk_nr, buf);
- line += buflen;
- len -= buflen;
- }
- }
- else
- {
- /* message short enough */
- syslog(level, "[%lu] %s", seq, 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 : "PostgreSQL");
- if (evtHandle == NULL)
- {
- evtHandle = INVALID_HANDLE_VALUE;
- return;
- }
- }
- switch (level)
- {
- case DEBUG5:
- case DEBUG4:
- case DEBUG3:
- case DEBUG2:
- case DEBUG1:
- case LOG:
- case COMMERROR:
- case INFO:
- case NOTICE:
- eventlevel = EVENTLOG_INFORMATION_TYPE;
- break;
- case WARNING:
- eventlevel = EVENTLOG_WARNING_TYPE;
- break;
- case ERROR:
- case FATAL:
- case PANIC:
- default:
- eventlevel = EVENTLOG_ERROR_TYPE;
- break;
- }
- /*
- * If message character encoding matches the encoding expected by
- * ReportEventA(), call it to avoid the hazards of conversion. Otherwise,
- * try to convert the message to UTF16 and write it with ReportEventW().
- * Fall back on ReportEventA() if conversion failed.
- *
- * 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() &&
- GetMessageEncoding() != GetACPEncoding())
- {
- utf16 = pgwin32_message_to_UTF16(line, len, NULL);
- if (utf16)
- {
- ReportEventW(evtHandle,
- eventlevel,
- 0,
- 0, /* All events are Id 0 */
- NULL,
- 1,
- 0,
- (LPCWSTR *) &utf16,
- NULL);
- /* XXX Try ReportEventA() when ReportEventW() fails? */
- pfree(utf16);
- return;
- }
- }
- ReportEventA(evtHandle,
- eventlevel,
- 0,
- 0, /* All events are Id 0 */
- NULL,
- 1,
- 0,
- &line,
- NULL);
- }
- #endif /* WIN32 */
- static void
- write_console(const char *line, int len)
- {
- int rc;
- #ifdef WIN32
- /*
- * Try to convert the message to UTF16 and write it with WriteConsoleW().
- * Fall back on write() if anything fails.
- *
- * In contrast to write_eventlog(), don't skip straight to write() based
- * on the applicable encodings. Unlike WriteConsoleW(), write() depends
- * on the suitability of the console output code page. Since we put
- * stderr into binary mode in SubPostmasterMain(), write() skips the
- * necessary translation anyway.
- *
- * WriteConsoleW() will fail if stderr is redirected, so just fall through
- * to writing unconverted to the logfile in this case.
- *
- * Since we palloc the structure required for conversion, also fall
- * through to writing unconverted if we have not yet set up
- * CurrentMemoryContext.
- */
- if (!in_error_recursion_trouble() &&
- !redirection_done &&
- CurrentMemoryContext != NULL)
- {
- WCHAR *utf16;
- int utf16len;
- utf16 = pgwin32_message_to_UTF16(line, len, &utf16len);
- if (utf16 != NULL)
- {
- HANDLE stdHandle;
- DWORD written;
- stdHandle = GetStdHandle(STD_ERROR_HANDLE);
- if (WriteConsoleW(stdHandle, utf16, utf16len, &written, NULL))
- {
- pfree(utf16);
- return;
- }
- /*
- * In case WriteConsoleW() failed, fall back to writing the
- * message unconverted.
- */
- pfree(utf16);
- }
- }
- #else
- /*
- * Conversion on non-win32 platforms is not implemented yet. It requires
- * non-throw version of pg_do_encoding_conversion(), that converts
- * unconvertable characters to '?' without errors.
- */
- #endif
- /*
- * We ignore any error from write() here. We have no useful way to report
- * it ... certainly whining on stderr isn't likely to be productive.
- */
- rc = write(fileno(stderr), line, len);
- (void) rc;
- }
- /*
- * setup formatted_log_time, for consistent times between CSV and regular logs
- */
- static void
- setup_formatted_log_time(void)
- {
- struct timeval tv;
- pg_time_t stamp_time;
- char msbuf[8];
- gettimeofday(&tv, NULL);
- stamp_time = (pg_time_t) tv.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) (tv.tv_usec / 1000));
- strncpy(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 '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 '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 's':
- if (formatted_start_time[0] == '\0')
- setup_formatted_start_time();
- if (padding != 0)
- appendStringInfo(buf, "%*s", padding, formatted_start_time);
- else
- appendStringInfoString(buf, formatted_start_time);
- break;
- case 'i':
- if (MyProcPort)
- {
- const char *psdisp;
- int displen;
- psdisp = get_ps_display(&displen);
- if (padding != 0)
- appendStringInfo(buf, "%*s", padding, psdisp);
- else
- appendBinaryStringInfo(buf, psdisp, displen);
- }
- else if (padding != 0)
- appendStringInfoSpaces(buf,
- padding > 0 ? padding : -padding);
- break;
- case 'r':
- if (MyProcPort && MyProcPort->remote_host)
- {
- if (padding != 0)
- {
- if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0')
- {
- /*
- * This option is slightly special as the port number
- * may be appended onto the end. Here we need to build
- * 1 string which contains the remote_host and optionally
- * the remote_port (if set) so we can properly align the
- * string.
- */
- char *hostport;
- hostport = psprintf("%s(%s)", MyProcPort->remote_host, MyProcPort->remote_port);
- appendStringInfo(buf, "%*s", padding, hostport);
- pfree(hostport);
- }
- else
- appendStringInfo(buf, "%*s", padding, MyProcPort->remote_host);
- }
- else
- {
- /* padding is 0, so we don't need a temp buffer */
- appendStringInfoString(buf, MyProcPort->remote_host);
- if (MyProcPort->remote_port &&
- MyProcPort->remote_port[0] != '\0')
- appendStringInfo(buf, "(%s)",
- MyProcPort->remote_port);
- }
- }
- else if (padding != 0)
- appendStringInfoSpaces(buf,
- padding > 0 ? padding : -padding);
- break;
- case 'h':
- if (MyProcPort && MyProcPort->remote_host)
- {
- if (padding != 0)
- appendStringInfo(buf, "%*s", padding, MyProcPort->remote_host);
- else
- appendStringInfoString(buf, MyProcPort->remote_host);
- }
- else if (padding != 0)
- appendStringInfoSpaces(buf,
- padding > 0 ? padding : -padding);
- break;
- case 'q':
- /* in postmaster and friends, stop if %q is seen */
- /* in a backend, just ignore */
- if (MyProcPort == NULL)
- return;
- break;
- case 'v':
- /* keep VXID format in sync with lockfuncs.c */
- if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
- {
- if (padding != 0)
- {
- char strfbuf[128];
- snprintf(strfbuf, sizeof(strfbuf) - 1, "%d/%u",
- MyProc->backendId, MyProc->lxid);
- appendStringInfo(buf, "%*s", padding, strfbuf);
- }
- else
- appendStringInfo(buf, "%d/%u", MyProc->backendId, MyProc->lxid);
- }
- else if (padding != 0)
- appendStringInfoSpaces(buf,
- padding > 0 ? padding : -padding);
- break;
- case 'x':
- if (padding != 0)
- appendStringInfo(buf, "%*u", padding, GetTopTransactionIdIfAny());
- else
- appendStringInfo(buf, "%u", GetTopTransactionIdIfAny());
- break;
- case 'e':
- if (padding != 0)
- appendStringInfo(buf, "%*s", padding, unpack_sql_state(edata->sqlerrcode));
- else
- appendStringInfoString(buf, unpack_sql_state(edata->sqlerrcode));
- break;
- default:
- /* format error - ignore it */
- break;
- }
- }
- }
- /*
- * append a CSV'd version of a string to a StringInfo
- * We use the PostgreSQL defaults for CSV, i.e. quote = escape = '"'
- * If it's NULL, append nothing.
- */
- static inline void
- appendCSVLiteral(StringInfo buf, const char *data)
- {
- const char *p = data;
- char c;
- /* avoid confusing an empty string with NULL */
- if (p == NULL)
- return;
- appendStringInfoCharMacro(buf, '"');
- while ((c = *p++) != '\0')
- {
- if (c == '"')
- appendStringInfoCharMacro(buf, '"');
- appendStringInfoCharMacro(buf, c);
- }
- appendStringInfoCharMacro(buf, '"');
- }
- /*
- * Constructs the error message, depending on the Errordata it gets, in a CSV
- * format which is described in doc/src/sgml/config.sgml.
- */
- static void
- write_csvlog(ErrorData *edata)
- {
- StringInfoData buf;
- bool print_stmt = false;
- /* static counter for line numbers */
- static long log_line_number = 0;
- /* has counter been reset in current process? */
- static int log_my_pid = 0;
- /*
- * This is one of the few places where we'd rather not inherit a static
- * variable's value from the postmaster. But since we will, reset it when
- * MyProcPid changes.
- */
- if (log_my_pid != MyProcPid)
- {
- log_line_number = 0;
- log_my_pid = MyProcPid;
- formatted_start_time[0] = '\0';
- }
- log_line_number++;
- initStringInfo(&buf);
- /*
- * timestamp with milliseconds
- *
- * Check if the timestamp is already calculated for the syslog message,
- * and use it if so. Otherwise, get the current timestamp. This is done
- * to put same timestamp in both syslog and csvlog messages.
- */
- if (formatted_log_time[0] == '\0')
- setup_formatted_log_time();
- appendStringInfoString(&buf, formatted_log_time);
- appendStringInfoChar(&buf, ',');
- /* username */
- if (MyProcPort)
- appendCSVLiteral(&buf, MyProcPort->user_name);
- appendStringInfoChar(&buf, ',');
- /* database name */
- if (MyProcPort)
- appendCSVLiteral(&buf, MyProcPort->database_name);
- appendStringInfoChar(&buf, ',');
- /* Process id */
- if (MyProcPid != 0)
- appendStringInfo(&buf, "%d", MyProcPid);
- appendStringInfoChar(&buf, ',');
- /* Remote host and port */
- if (MyProcPort && MyProcPort->remote_host)
- {
- appendStringInfoChar(&buf, '"');
- appendStringInfoString(&buf, MyProcPort->remote_host);
- if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0')
- {
- appendStringInfoChar(&buf, ':');
- appendStringInfoString(&buf, MyProcPort->remote_port);
- }
- appendStringInfoChar(&buf, '"');
- }
- appendStringInfoChar(&buf, ',');
- /* session id */
- appendStringInfo(&buf, "%lx.%x", (long) MyStartTime, MyProcPid);
- appendStringInfoChar(&buf, ',');
- /* Line number */
- appendStringInfo(&buf, "%ld", log_line_number);
- appendStringInfoChar(&buf, ',');
- /* PS display */
- if (MyProcPort)
- {
- StringInfoData msgbuf;
- const char *psdisp;
- int displen;
- initStringInfo(&msgbuf);
- psdisp = get_ps_display(&displen);
- appendBinaryStringInfo(&msgbuf, psdisp, displen);
- appendCSVLiteral(&buf, msgbuf.data);
- pfree(msgbuf.data);
- }
- appendStringInfoChar(&buf, ',');
- /* session start timestamp */
- if (formatted_start_time[0] == '\0')
- setup_formatted_start_time();
- appendStringInfoString(&buf, formatted_start_time);
- appendStringInfoChar(&buf, ',');
- /* Virtual transaction id */
- /* keep VXID format in sync with lockfuncs.c */
- if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
- appendStringInfo(&buf, "%d/%u", MyProc->backendId, MyProc->lxid);
- appendStringInfoChar(&buf, ',');
- /* Transaction id */
- appendStringInfo(&buf, "%u", GetTopTransactionIdIfAny());
- appendStringInfoChar(&buf, ',');
- /* Error severity */
- appendStringInfoString(&buf, error_severity(edata->elevel));
- appendStringInfoChar(&buf, ',');
- /* SQL state code */
- appendStringInfoString(&buf, unpack_sql_state(edata->sqlerrcode));
- appendStringInfoChar(&buf, ',');
- /* errmessage */
- appendCSVLiteral(&buf, edata->message);
- appendStringInfoChar(&buf, ',');
- /* errdetail or errdetail_log */
- if (edata->detail_log)
- appendCSVLiteral(&buf, edata->detail_log);
- else
- appendCSVLiteral(&buf, edata->detail);
- appendStringInfoChar(&buf, ',');
- /* errhint */
- appendCSVLiteral(&buf, edata->hint);
- appendStringInfoChar(&buf, ',');
- /* internal query */
- appendCSVLiteral(&buf, edata->internalquery);
- appendStringInfoChar(&buf, ',');
- /* if printed internal query, print internal pos too */
- if (edata->internalpos > 0 && edata->internalquery != NULL)
- appendStringInfo(&buf, "%d", edata->internalpos);
- appendStringInfoChar(&buf, ',');
- /* errcontext */
- 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, '\n');
- /* If in the syslogger process, try to write messages direct to file */
- if (am_syslogger)
- 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);
- 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)
- {
- 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 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 COMMERROR:
- case INFO:
- syslog_level = LOG_INFO;
- break;
- case NOTICE:
- case WARNING:
- syslog_level = LOG_NOTICE;
- break;
- case ERROR:
- syslog_level = LOG_WARNING;
- break;
- case FATAL:
- syslog_level = LOG_ERR;
- break;
- case PANIC:
- default:
- syslog_level = LOG_CRIT;
- break;
- }
- write_syslog(syslog_level, buf.data);
- }
- #endif /* HAVE_SYSLOG */
- #ifdef WIN32
- /* Write to eventlog, if enabled */
- if (Log_destination & LOG_DESTINATION_EVENTLOG)
- {
- write_eventlog(edata->elevel, buf.data, buf.len);
- }
- #endif /* WIN32 */
- /* Write to stderr, if enabled */
- if ((Log_destination & LOG_DESTINATION_STDERR) || whereToSendOutput == DestDebug)
- {
- /*
- * Use the chunking protocol if we know the syslogger should be
- * catching stderr output, and we are not ourselves the syslogger.
- * Otherwise, just do a vanilla write to stderr.
- */
- if (redirection_done && !am_syslogger)
- 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 (am_syslogger)
- 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 || am_syslogger)
- {
- /*
- * send CSV data if it's safe to do so (syslogger doesn't need the
- * pipe). First get back the space in the message buffer.
- */
- pfree(buf.data);
- write_csvlog(edata);
- }
- else
- {
- /*
- * syslogger not up (yet), so just dump the message to stderr,
- * unless we already did so above.
- */
- if (!(Log_destination & LOG_DESTINATION_STDERR) &&
- whereToSendOutput != DestDebug)
- write_console(buf.data, buf.len);
- pfree(buf.data);
- }
- }
- else
- {
- pfree(buf.data);
- }
- }
- /*
- * Send data to the syslogger using the chunked protocol
- *
- * Note: when there are multiple backends writing into the syslogger pipe,
- * it's critical that each write go into the pipe indivisibly, and not
- * get interleaved with data from other processes. Fortunately, the POSIX
- * spec requires that writes to pipes be atomic so long as they are not
- * more than PIPE_BUF bytes long. So we divide long messages into chunks
- * that are no more than that length, and send one chunk per write() call.
- * The collector process knows how to reassemble the chunks.
- *
- * Because of the atomic write requirement, there are only two possible
- * results from write() here: -1 for failure, or the requested number of
- * bytes. There is not really anything we can do about a failure; retry would
- * probably be an infinite loop, and we can't even report the error usefully.
- * (There is noplace else we could send it!) So we might as well just ignore
- * the result from write(). However, on some platforms you get a compiler
- * warning from ignoring write()'s result, so do a little dance with casting
- * rc to void to shut up the compiler.
- */
- static void
- write_pipe_chunks(char *data, int len, int dest)
- {
- PipeProtoChunk p;
- int fd = fileno(stderr);
- int rc;
- Assert(len > 0);
- p.proto.nuls[0] = p.proto.nuls[1] = '\0';
- p.proto.pid = MyProcPid;
- /* write all but the last chunk */
- while (len > PIPE_MAX_PAYLOAD)
- {
- p.proto.is_last = (dest == LOG_DESTINATION_CSVLOG ? 'F' : 'f');
- p.proto.len = PIPE_MAX_PAYLOAD;
- memcpy(p.proto.data, data, PIPE_MAX_PAYLOAD);
- rc = write(fd, &p, PIPE_HEADER_SIZE + PIPE_MAX_PAYLOAD);
- (void) rc;
- data += PIPE_MAX_PAYLOAD;
- len -= PIPE_MAX_PAYLOAD;
- }
- /* write the last chunk */
- p.proto.is_last = (dest == LOG_DESTINATION_CSVLOG ? 'T' : 't');
- p.proto.len = len;
- memcpy(p.proto.data, data, len);
- rc = write(fd, &p, PIPE_HEADER_SIZE + len);
- (void) rc;
- }
- /*
- * Append a text string to the error report being built for the client.
- *
- * This is ordinarily identical to pq_sendstring(), but if we are in
- * error recursion trouble we skip encoding conversion, because of the
- * possibility that the problem is a failure in the encoding conversion
- * subsystem itself. Code elsewhere should ensure that the passed-in
- * strings will be plain 7-bit ASCII, and thus not in need of conversion,
- * in such cases. (In particular, we disable localization of error messages
- * to help ensure that's true.)
- */
- static void
- err_sendstring(StringInfo buf, const char *str)
- {
- if (in_error_recursion_trouble())
- pq_send_ascii_string(buf, str);
- else
- pq_sendstring(buf, str);
- }
- /*
- * Write error report to client
- */
- static void
- send_message_to_frontend(ErrorData *edata)
- {
- StringInfoData msgbuf;
- /* 'N' (Notice) is for nonfatal conditions, 'E' is for errors */
- pq_beginmessage(&msgbuf, (edata->elevel < ERROR) ? 'N' : 'E');
- if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
- {
- /* New style with separate fields */
- char tbuf[12];
- int ssval;
- int i;
- pq_sendbyte(&msgbuf, PG_DIAG_SEVERITY);
- err_sendstring(&msgbuf, error_severity(edata->elevel));
- /* unpack MAKE_SQLSTATE code */
- ssval = edata->sqlerrcode;
- for (i = 0; i < 5; i++)
- {
- tbuf[i] = PGUNSIXBIT(ssval);
- ssval >>= 6;
- }
- tbuf[i] = '\0';
- pq_sendbyte(&msgbuf, PG_DIAG_SQLSTATE);
- err_sendstring(&msgbuf, tbuf);
- /* M field is required per protocol, so always send something */
- pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_PRIMARY);
- if (edata->message)
- err_sendstring(&msgbuf, edata->message);
- else
- err_sendstring(&msgbuf, _("missing error text"));
- if (edata->detail)
- {
- pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_DETAIL);
- err_sendstring(&msgbuf, edata->detail);
- }
- /* detail_log is intentionally not used here */
- if (edata->hint)
- {
- pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_HINT);
- err_sendstring(&msgbuf, edata->hint);
- }
- if (edata->context)
- {
- pq_sendbyte(&msgbuf, PG_DIAG_CONTEXT);
- err_sendstring(&msgbuf, edata->context);
- }
- if (edata->schema_name)
- {
- pq_sendbyte(&msgbuf, PG_DIAG_SCHEMA_NAME);
- err_sendstring(&msgbuf, edata->schema_name);
- }
- if (edata->table_name)
- {
- pq_sendbyte(&msgbuf, PG_DIAG_TABLE_NAME);
- err_sendstring(&msgbuf, edata->table_name);
- }
- if (edata->column_name)
- {
- pq_sendbyte(&msgbuf, PG_DIAG_COLUMN_NAME);
- err_sendstring(&msgbuf, edata->column_name);
- }
- if (edata->datatype_name)
- {
- pq_sendbyte(&msgbuf, PG_DIAG_DATATYPE_NAME);
- err_sendstring(&msgbuf, edata->datatype_name);
- }
- if (edata->constraint_name)
- {
- pq_sendbyte(&msgbuf, PG_DIAG_CONSTRAINT_NAME);
- err_sendstring(&msgbuf, edata->constraint_name);
- }
- if (edata->cursorpos > 0)
- {
- snprintf(tbuf, sizeof(tbuf), "%d", edata->cursorpos);
- pq_sendbyte(&msgbuf, PG_DIAG_STATEMENT_POSITION);
- err_sendstring(&msgbuf, tbuf);
- }
- if (edata->internalpos > 0)
- {
- snprintf(tbuf, sizeof(tbuf), "%d", edata->internalpos);
- pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_POSITION);
- err_sendstring(&msgbuf, tbuf);
- }
- if (edata->internalquery)
- {
- pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_QUERY);
- err_sendstring(&msgbuf, edata->internalquery);
- }
- if (edata->filename)
- {
- pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_FILE);
- err_sendstring(&msgbuf, edata->filename);
- }
- if (edata->lineno > 0)
- {
- snprintf(tbuf, sizeof(tbuf), "%d", edata->lineno);
- pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_LINE);
- err_sendstring(&msgbuf, tbuf);
- }
- if (edata->funcname)
- {
- pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_FUNCTION);
- err_sendstring(&msgbuf, edata->funcname);
- }
- pq_sendbyte(&msgbuf, '\0'); /* terminator */
- }
- else
- {
- /* Old style --- gin up a backwards-compatible message */
- StringInfoData buf;
- initStringInfo(&buf);
- appendStringInfo(&buf, "%s: ", error_severity(edata->elevel));
- if (edata->show_funcname && edata->funcname)
- appendStringInfo(&buf, "%s: ", edata->funcname);
- if (edata->message)
- appendStringInfoString(&buf, edata->message);
- else
- appendStringInfoString(&buf, _("missing error text"));
- if (edata->cursorpos > 0)
- appendStringInfo(&buf, _(" at character %d"),
- edata->cursorpos);
- else if (edata->internalpos > 0)
- appendStringInfo(&buf, _(" at character %d"),
- edata->internalpos);
- appendStringInfoChar(&buf, '\n');
- err_sendstring(&msgbuf, buf.data);
- pfree(buf.data);
- }
- pq_endmessage(&msgbuf);
- /*
- * This flush is normally not necessary, since postgres.c will flush out
- * waiting data when control returns to the main loop. But it seems best
- * to leave it here, so that the client has some clue what happened if the
- * backend dies before getting back to the main loop ... error/notice
- * messages should not be a performance-critical path anyway, so an extra
- * flush won't hurt much ...
- */
- pq_flush();
- }
- /*
- * Support routines for formatting error messages.
- */
- /*
- * expand_fmt_string --- process special format codes in a format string
- *
- * We must replace %m with the appropriate strerror string, since vsnprintf
- * won't know what to do with it.
- *
- * The result is a palloc'd string.
- */
- static char *
- expand_fmt_string(const char *fmt, ErrorData *edata)
- {
- StringInfoData buf;
- const char *cp;
- initStringInfo(&buf);
- for (cp = fmt; *cp; cp++)
- {
- if (cp[0] == '%' && cp[1] != '\0')
- {
- cp++;
- if (*cp == 'm')
- {
- /*
- * Replace %m by system error string. If there are any %'s in
- * the string, we'd better double them so that vsnprintf won't
- * misinterpret.
- */
- const char *cp2;
- cp2 = useful_strerror(edata->saved_errno);
- for (; *cp2; cp2++)
- {
- if (*cp2 == '%')
- appendStringInfoCharMacro(&buf, '%');
- appendStringInfoCharMacro(&buf, *cp2);
- }
- }
- else
- {
- /* copy % and next char --- this avoids trouble with %%m */
- appendStringInfoCharMacro(&buf, '%');
- appendStringInfoCharMacro(&buf, *cp);
- }
- }
- else
- appendStringInfoCharMacro(&buf, *cp);
- }
- return buf.data;
- }
- /*
- * A slightly cleaned-up version of strerror()
- */
- static const char *
- useful_strerror(int errnum)
- {
- /* this buffer is only used if strerror() and get_errno_symbol() fail */
- static char errorstr_buf[48];
- const char *str;
- #ifdef WIN32
- /* Winsock error code range, per WinError.h */
- if (errnum >= 10000 && errnum <= 11999)
- return pgwin32_socket_strerror(errnum);
- #endif
- str = strerror(errnum);
- /*
- * Some strerror()s return an empty string for out-of-range errno. This
- * is ANSI C spec compliant, but not exactly useful. Also, we may get
- * back strings of question marks if libc cannot transcode the message to
- * the codeset specified by LC_CTYPE. If we get nothing useful, first try
- * get_errno_symbol(), and if that fails, print the numeric errno.
- */
- if (str == NULL || *str == '\0' || *str == '?')
- str = get_errno_symbol(errnum);
- if (str == NULL)
- {
- snprintf(errorstr_buf, sizeof(errorstr_buf),
- /*------
- translator: This string will be truncated at 47
- characters expanded. */
- _("operating system error %d"), errnum);
- str = errorstr_buf;
- }
- return str;
- }
- /*
- * Returns a symbol (e.g. "ENOENT") for an errno code.
- * Returns NULL if the code is unrecognized.
- */
- static const char *
- get_errno_symbol(int errnum)
- {
- switch (errnum)
- {
- case E2BIG:
- return "E2BIG";
- case EACCES:
- return "EACCES";
- #ifdef EADDRINUSE
- case EADDRINUSE:
- return "EADDRINUSE";
- #endif
- #ifdef EADDRNOTAVAIL
- case EADDRNOTAVAIL:
- return "EADDRNOTAVAIL";
- #endif
- case EAFNOSUPPORT:
- return "EAFNOSUPPORT";
- #ifdef EAGAIN
- case EAGAIN:
- return "EAGAIN";
- #endif
- #ifdef EALREADY
- case EALREADY:
- return "EALREADY";
- #endif
- case EBADF:
- return "EBADF";
- #ifdef EBADMSG
- case EBADMSG:
- return "EBADMSG";
- #endif
- case EBUSY:
- return "EBUSY";
- case ECHILD:
- return "ECHILD";
- #ifdef ECONNABORTED
- case ECONNABORTED:
- return "ECONNABORTED";
- #endif
- case ECONNREFUSED:
- return "ECONNREFUSED";
- #ifdef ECONNRESET
- case ECONNRESET:
- return "ECONNRESET";
- #endif
- case EDEADLK:
- return "EDEADLK";
- case EDOM:
- return "EDOM";
- case EEXIST:
- return "EEXIST";
- case EFAULT:
- return "EFAULT";
- case EFBIG:
- return "EFBIG";
- #ifdef EHOSTUNREACH
- case EHOSTUNREACH:
- return "EHOSTUNREACH";
- #endif
- case EIDRM:
- return "EIDRM";
- case EINPROGRESS:
- return "EINPROGRESS";
- case EINTR:
- return "EINTR";
- case EINVAL:
- return "EINVAL";
- case EIO:
- return "EIO";
- #ifdef EISCONN
- case EISCONN:
- return "EISCONN";
- #endif
- case EISDIR:
- return "EISDIR";
- #ifdef ELOOP
- case ELOOP:
- return "ELOOP";
- #endif
- case EMFILE:
- return "EMFILE";
- case EMLINK:
- return "EMLINK";
- case EMSGSIZE:
- return "EMSGSIZE";
- case ENAMETOOLONG:
- return "ENAMETOOLONG";
- case ENFILE:
- return "ENFILE";
- case ENOBUFS:
- return "ENOBUFS";
- case ENODEV:
- return "ENODEV";
- case ENOENT:
- return "ENOENT";
- case ENOEXEC:
- return "ENOEXEC";
- case ENOMEM:
- return "ENOMEM";
- case ENOSPC:
- return "ENOSPC";
- case ENOSYS:
- return "ENOSYS";
- #ifdef ENOTCONN
- case ENOTCONN:
- return "ENOTCONN";
- #endif
- case ENOTDIR:
- return "ENOTDIR";
- #if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
- case ENOTEMPTY:
- return "ENOTEMPTY";
- #endif
- #ifdef ENOTSOCK
- case ENOTSOCK:
- return "ENOTSOCK";
- #endif
- #ifdef ENOTSUP
- case ENOTSUP:
- return "ENOTSUP";
- #endif
- case ENOTTY:
- return "ENOTTY";
- case ENXIO:
- return "ENXIO";
- #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
- case EOPNOTSUPP:
- return "EOPNOTSUPP";
- #endif
- #ifdef EOVERFLOW
- case EOVERFLOW:
- return "EOVERFLOW";
- #endif
- case EPERM:
- return "EPERM";
- case EPIPE:
- return "EPIPE";
- case EPROTONOSUPPORT:
- return "EPROTONOSUPPORT";
- case ERANGE:
- return "ERANGE";
- #ifdef EROFS
- case EROFS:
- return "EROFS";
- #endif
- case ESRCH:
- return "ESRCH";
- #ifdef ETIMEDOUT
- case ETIMEDOUT:
- return "ETIMEDOUT";
- #endif
- #ifdef ETXTBSY
- case ETXTBSY:
- return "ETXTBSY";
- #endif
- #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
- case EWOULDBLOCK:
- return "EWOULDBLOCK";
- #endif
- case EXDEV:
- return "EXDEV";
- }
- return NULL;
- }
- /*
- * error_severity --- get localized string representing elevel
- */
- static const char *
- error_severity(int elevel)
- {
- const char *prefix;
- switch (elevel)
- {
- case DEBUG1:
- case DEBUG2:
- case DEBUG3:
- case DEBUG4:
- case DEBUG5:
- prefix = _("DEBUG");
- break;
- case LOG:
- case COMMERROR:
- prefix = _("LOG");
- break;
- case INFO:
- prefix = _("INFO");
- break;
- case NOTICE:
- prefix = _("NOTICE");
- break;
- case WARNING:
- prefix = _("WARNING");
- break;
- case ERROR:
- prefix = _("ERROR");
- break;
- case FATAL:
- prefix = _("FATAL");
- break;
- case PANIC:
- prefix = _("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);
- }
- /*
- * 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 bool
- is_log_level_output(int elevel, int log_min_level)
- {
- if (elevel == LOG || elevel == COMMERROR)
- {
- if (log_min_level == LOG || log_min_level <= ERROR)
- return true;
- }
- else if (log_min_level == LOG)
- {
- /* elevel != LOG */
- if (elevel >= FATAL)
- return true;
- }
- /* Neither is LOG */
- else if (elevel >= log_min_level)
- return true;
- return false;
- }
- /*
- * 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;
- }