diff --git a/doc/src/sgml/sources.sgml b/doc/src/sgml/sources.sgml index 1c8d25e4476..2248f874d06 100644 --- a/doc/src/sgml/sources.sgml +++ b/doc/src/sgml/sources.sgml @@ -1,4 +1,4 @@ - + PostgreSQL Coding Conventions @@ -144,7 +144,7 @@ ereport(ERROR, errmsg_internal(const char *msg, ...) is the same as 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 can't happen cases that are probably not worth expending translation effort on. @@ -220,7 +220,7 @@ elog(level, "format string", ...); ereport(level, (errmsg_internal("format string", ...))); 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, 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 ereport. Nonetheless, diff --git a/src/backend/nls.mk b/src/backend/nls.mk index e7da89f005c..c936d4dad4f 100644 --- a/src/backend/nls.mk +++ b/src/backend/nls.mk @@ -1,9 +1,7 @@ -# $PostgreSQL: pgsql/src/backend/nls.mk,v 1.20 2005/10/04 11:14:03 petere Exp $ +# $PostgreSQL: pgsql/src/backend/nls.mk,v 1.20.4.1 2008/10/27 19:37:36 tgl Exp $ CATALOG_NAME := postgres AVAIL_LANGUAGES := af cs de es fr hr hu it ko nb 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 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 3855c60d3fd..60543f7ae09 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.178.2.5 2008/07/08 22:17:55 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.178.2.6 2008/10/27 19:37:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -125,6 +125,21 @@ static const char *error_severity(int elevel); static void append_with_tabs(StringInfo buf, const char *str); static void write_pipe_chunks(int fd, char *data, int len); + +/* + * 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 * @@ -260,12 +275,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; @@ -599,18 +614,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 = _(fmt); \ + if (translateit) \ + fmt = _(fmt); \ /* Expand %m in format string */ \ fmtbuf = expand_fmt_string(fmt, edata); \ initStringInfo(&buf); \ @@ -657,7 +674,7 @@ errmsg(const char *fmt,...) CHECK_STACK_DEPTH(); oldcontext = MemoryContextSwitchTo(ErrorContext); - EVALUATE_MESSAGE(message, false); + EVALUATE_MESSAGE(message, false, true); MemoryContextSwitchTo(oldcontext); recursion_depth--; @@ -669,9 +686,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,...) @@ -683,7 +703,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--; @@ -704,7 +724,7 @@ errdetail(const char *fmt,...) CHECK_STACK_DEPTH(); oldcontext = MemoryContextSwitchTo(ErrorContext); - EVALUATE_MESSAGE(detail, false); + EVALUATE_MESSAGE(detail, false, true); MemoryContextSwitchTo(oldcontext); recursion_depth--; @@ -725,7 +745,7 @@ errhint(const char *fmt,...) CHECK_STACK_DEPTH(); oldcontext = MemoryContextSwitchTo(ErrorContext); - EVALUATE_MESSAGE(hint, false); + EVALUATE_MESSAGE(hint, false, true); MemoryContextSwitchTo(oldcontext); recursion_depth--; @@ -750,7 +770,7 @@ errcontext(const char *fmt,...) CHECK_STACK_DEPTH(); oldcontext = MemoryContextSwitchTo(ErrorContext); - EVALUATE_MESSAGE(context, true); + EVALUATE_MESSAGE(context, true, true); MemoryContextSwitchTo(oldcontext); recursion_depth--; @@ -928,12 +948,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--; @@ -1082,7 +1102,9 @@ ReThrowError(ErrorData *edata) /* * 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"))); @@ -2019,6 +2041,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) @@ -2048,13 +2074,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 04706460fe7..7fb54077239 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.58.2.2 2007/03/26 11:35:42 ishii Exp $ + * $PostgreSQL: pgsql/src/backend/utils/mb/wchar.c,v 1.58.2.3 2008/10/27 19:37:36 tgl Exp $ * * WIN1250 client encoding updated by Pavel Behal * @@ -1556,12 +1556,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 707bc5b2fda..e0000f6ceb5 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.82.2.1 2008/04/17 00:00:01 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.82.2.2 2008/10/27 19:37:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -289,6 +289,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);