]> git.ipfire.org Git - thirdparty/postgresql.git/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:36 +0000 (19:37 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 27 Oct 2008 19:37:36 +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 1c8d25e44762c7dfaec0ec6d02c89086618efd8f..2248f874d06b5f45d87b7e2475bf52facf7138f5 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/sources.sgml,v 2.20 2006/10/23 18:10:32 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/sources.sgml,v 2.20.2.1 2008/10/27 19:37:36 tgl Exp $ -->
 
  <chapter id="source">
   <title>PostgreSQL Coding Conventions</title>
@@ -144,7 +144,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>can't happen</> cases that are probably
      not worth expending translation effort on.
     </para>
@@ -220,7 +220,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 e7da89f005c18d86443d49a1ff44b8856c047353..c936d4dad4f5d6f675a3bbcfd91329a52ec652cb 100644 (file)
@@ -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
index 3855c60d3fd5cd1096e677e8a01c7e03556e7419..60543f7ae09e1541a356f2200a3301ce9da413a5 100644 (file)
@@ -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 = "???";
index 04706460fe7e26066a11391afe023ec9f562761f..7fb5407723922d47f367e2b8975b60085cc3f29d 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.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
index 707bc5b2fda211c5f5d01f34630ee483858c453c..e0000f6ceb52fad1513460de5f8c105a5dd3af18 100644 (file)
@@ -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);