]> granicus.if.org Git - postgresql/commitdiff
Install a more robust solution for the problem of infinite error-processing
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 27 Oct 2008 19:37:22 +0000 (19:37 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 27 Oct 2008 19:37:22 +0000 (19:37 +0000)
recursion when we are unable to convert a localized error message to the
client's encoding.  We've been over this ground before, but as reported by
Ibrar Ahmed, it still didn't work in the case of conversion failures for
the conversion-failure message itself :-(.  Fix by installing a "circuit
breaker" that disables attempts to localize this message once we get into
recursion trouble.

Patch all supported branches, because it is in fact broken in all of them;
though I had to add some missing translations to the older branches in
order to expose the failure in the particular test case I was using.

doc/src/sgml/sources.sgml
src/backend/nls.mk
src/backend/utils/error/elog.c
src/backend/utils/mb/wchar.c
src/include/utils/elog.h

index e78e59ad9000b72a4c74d145ff98e2febdca0c72..fe581f39be930364d4e43188c94d13958afa57db 100644 (file)
@@ -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,
index 99e7e9f90a852deab9673bf77d087924977653d2..bfca4f1220f48a9cd1c3b709288e584e35a5b3f0 100644 (file)
@@ -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
index c458ecf31944101515d5053cb480fab9bab670f5..057ca5df82380a044115e922f14c0e893e1b41f5 100644 (file)
@@ -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 = "???";
index 348a57e4d6e64fd11151a6cbbca4acc1fb30b36e..2f11b3aa9b0db267c19cd2925886162640034c93 100644 (file)
@@ -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
index 437252e8d8344c9a282fdfc72343b5e1ab51a639..ba01a1386cd7b5fcd771c68843a5bfbf851bc6b2 100644 (file)
@@ -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);