diff --git a/doc/src/sgml/sources.sgml b/doc/src/sgml/sources.sgml index e78e59ad9000b72a4c74d145ff98e2febdca0c72..fe581f39be930364d4e43188c94d13958afa57db 100644 --- a/doc/src/sgml/sources.sgml +++ b/doc/src/sgml/sources.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/sources.sgml,v 2.31 2008/09/07 02:01:04 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/sources.sgml,v 2.32 2008/10/27 19:37:21 tgl Exp $ --> <chapter id="source"> <title>PostgreSQL Coding Conventions</title> @@ -176,7 +176,7 @@ ereport(ERROR, <para> <function>errmsg_internal(const char *msg, ...)</function> is the same as <function>errmsg</>, except that the message string will not be - included in the internationalization message dictionary. + translated nor included in the internationalization message dictionary. This should be used for <quote>cannot happen</> cases that are probably not worth expending translation effort on. </para> @@ -271,7 +271,7 @@ elog(level, "format string", ...); ereport(level, (errmsg_internal("format string", ...))); </programlisting> Notice that the SQLSTATE error code is always defaulted, and the message - string is not included in the internationalization message dictionary. + string is not subject to translation. Therefore, <function>elog</> should be used only for internal errors and low-level debug logging. Any message that is likely to be of interest to ordinary users should go through <function>ereport</>. Nonetheless, diff --git a/src/backend/nls.mk b/src/backend/nls.mk index 99e7e9f90a852deab9673bf77d087924977653d2..bfca4f1220f48a9cd1c3b709288e584e35a5b3f0 100644 --- a/src/backend/nls.mk +++ b/src/backend/nls.mk @@ -1,9 +1,7 @@ -# $PostgreSQL: pgsql/src/backend/nls.mk,v 1.22 2008/03/24 18:08:47 tgl Exp $ +# $PostgreSQL: pgsql/src/backend/nls.mk,v 1.23 2008/10/27 19:37:21 tgl Exp $ CATALOG_NAME := postgres AVAIL_LANGUAGES := af cs de es fr hr hu it ko nb nl pt_BR ro ru sk sl sv tr zh_CN zh_TW GETTEXT_FILES := + gettext-files -# you can add "elog:2" and "errmsg_internal" to this list if you want to -# include internal messages in the translation list. GETTEXT_TRIGGERS:= _ errmsg errdetail errdetail_log errhint errcontext write_stderr yyerror gettext-files: distprep diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index c458ecf31944101515d5053cb480fab9bab670f5..057ca5df82380a044115e922f14c0e893e1b41f5 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -42,7 +42,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.208 2008/10/17 22:56:16 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.209 2008/10/27 19:37:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -149,6 +149,21 @@ 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); +} + /* * errstart --- begin an error-reporting cycle * @@ -261,12 +276,12 @@ errstart(int elevel, const char *filename, int lineno, MemoryContextReset(ErrorContext); /* - * If we recurse more than once, the problem might be something broken + * 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 (recursion_depth > 2) + if (in_error_recursion_trouble()) { error_context_stack = NULL; debug_query_string = NULL; @@ -604,18 +619,20 @@ errcode_for_socket_access(void) * 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. + * 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(targetfield, appendval) \ +#define EVALUATE_MESSAGE(targetfield, appendval, translateit) \ { \ char *fmtbuf; \ StringInfoData buf; \ /* Internationalize the error format string */ \ - fmt = dgettext(edata->domain, fmt); \ + if (translateit) \ + fmt = dgettext(edata->domain, fmt); \ /* Expand %m in format string */ \ fmtbuf = expand_fmt_string(fmt, edata); \ initStringInfo(&buf); \ @@ -662,7 +679,7 @@ errmsg(const char *fmt,...) CHECK_STACK_DEPTH(); oldcontext = MemoryContextSwitchTo(ErrorContext); - EVALUATE_MESSAGE(message, false); + EVALUATE_MESSAGE(message, false, true); MemoryContextSwitchTo(oldcontext); recursion_depth--; @@ -674,9 +691,12 @@ errmsg(const char *fmt,...) * 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 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. + * 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,...) @@ -688,7 +708,7 @@ errmsg_internal(const char *fmt,...) CHECK_STACK_DEPTH(); oldcontext = MemoryContextSwitchTo(ErrorContext); - EVALUATE_MESSAGE(message, false); + EVALUATE_MESSAGE(message, false, false); MemoryContextSwitchTo(oldcontext); recursion_depth--; @@ -709,7 +729,7 @@ errdetail(const char *fmt,...) CHECK_STACK_DEPTH(); oldcontext = MemoryContextSwitchTo(ErrorContext); - EVALUATE_MESSAGE(detail, false); + EVALUATE_MESSAGE(detail, false, true); MemoryContextSwitchTo(oldcontext); recursion_depth--; @@ -730,7 +750,7 @@ errdetail_log(const char *fmt,...) CHECK_STACK_DEPTH(); oldcontext = MemoryContextSwitchTo(ErrorContext); - EVALUATE_MESSAGE(detail_log, false); + EVALUATE_MESSAGE(detail_log, false, true); MemoryContextSwitchTo(oldcontext); recursion_depth--; @@ -751,7 +771,7 @@ errhint(const char *fmt,...) CHECK_STACK_DEPTH(); oldcontext = MemoryContextSwitchTo(ErrorContext); - EVALUATE_MESSAGE(hint, false); + EVALUATE_MESSAGE(hint, false, true); MemoryContextSwitchTo(oldcontext); recursion_depth--; @@ -776,7 +796,7 @@ errcontext(const char *fmt,...) CHECK_STACK_DEPTH(); oldcontext = MemoryContextSwitchTo(ErrorContext); - EVALUATE_MESSAGE(context, true); + EVALUATE_MESSAGE(context, true, true); MemoryContextSwitchTo(oldcontext); recursion_depth--; @@ -956,7 +976,9 @@ elog_start(const char *filename, int lineno, const char *funcname) /* * Wups, stack not big enough. We treat this as a PANIC condition * because it suggests an infinite loop of errors during error - * recovery. + * 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"))); @@ -990,12 +1012,12 @@ elog_finish(int elevel, const char *fmt,...) return; /* nothing to do */ /* - * Format error message just like errmsg(). + * Format error message just like errmsg_internal(). */ recursion_depth++; oldcontext = MemoryContextSwitchTo(ErrorContext); - EVALUATE_MESSAGE(message, false); + EVALUATE_MESSAGE(message, false, false); MemoryContextSwitchTo(oldcontext); recursion_depth--; @@ -2408,6 +2430,10 @@ useful_strerror(int errnum) /* * error_severity --- get localized string representing elevel + * + * Note: in an error recursion situation, we stop localizing the tags + * for ERROR and above. This is necessary because the problem might be + * failure to convert one of these strings to the client encoding. */ static const char * error_severity(int elevel) @@ -2437,13 +2463,22 @@ error_severity(int elevel) prefix = _("WARNING"); break; case ERROR: - prefix = _("ERROR"); + if (in_error_recursion_trouble()) + prefix = "ERROR"; + else + prefix = _("ERROR"); break; case FATAL: - prefix = _("FATAL"); + if (in_error_recursion_trouble()) + prefix = "FATAL"; + else + prefix = _("FATAL"); break; case PANIC: - prefix = _("PANIC"); + if (in_error_recursion_trouble()) + prefix = "PANIC"; + else + prefix = _("PANIC"); break; default: prefix = "???"; diff --git a/src/backend/utils/mb/wchar.c b/src/backend/utils/mb/wchar.c index 348a57e4d6e64fd11151a6cbbca4acc1fb30b36e..2f11b3aa9b0db267c19cd2925886162640034c93 100644 --- a/src/backend/utils/mb/wchar.c +++ b/src/backend/utils/mb/wchar.c @@ -1,7 +1,7 @@ /* * conversion functions between pg_wchar and multibyte streams. * Tatsuo Ishii - * $PostgreSQL: pgsql/src/backend/utils/mb/wchar.c,v 1.66 2007/11/15 21:14:40 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/mb/wchar.c,v 1.67 2008/10/27 19:37:22 tgl Exp $ * */ /* can be used in either frontend or backend */ @@ -1567,12 +1567,25 @@ report_untranslatable_char(int src_encoding, int dest_encoding, for (j = 0; j < jlimit; j++) p += sprintf(p, "%02x", (unsigned char) mbstr[j]); - ereport(ERROR, - (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER), - errmsg("character 0x%s of encoding \"%s\" has no equivalent in \"%s\"", - buf, - pg_enc2name_tbl[src_encoding].name, - pg_enc2name_tbl[dest_encoding].name))); + /* + * In an error recursion situation, don't try to translate the message. + * This gets us out of trouble if the problem is failure to convert + * this very message (after translation) to the client encoding. + */ + if (in_error_recursion_trouble()) + ereport(ERROR, + (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER), + errmsg_internal("character 0x%s of encoding \"%s\" has no equivalent in \"%s\"", + buf, + pg_enc2name_tbl[src_encoding].name, + pg_enc2name_tbl[dest_encoding].name))); + else + ereport(ERROR, + (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER), + errmsg("character 0x%s of encoding \"%s\" has no equivalent in \"%s\"", + buf, + pg_enc2name_tbl[src_encoding].name, + pg_enc2name_tbl[dest_encoding].name))); } #endif diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 437252e8d8344c9a282fdfc72343b5e1ab51a639..ba01a1386cd7b5fcd771c68843a5bfbf851bc6b2 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.96 2008/10/09 22:22:31 alvherre Exp $ + * $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.97 2008/10/27 19:37:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -324,6 +324,7 @@ extern int Log_destination; /* Other exported functions */ extern void DebugFileOpen(void); extern char *unpack_sql_state(int sql_state); +extern bool in_error_recursion_trouble(void); #ifdef HAVE_SYSLOG extern void set_syslog_parameters(const char *ident, int facility);