]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
When we are in error recursion trouble, arrange to suppress translation and
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 2 Mar 2009 21:18:57 +0000 (21:18 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 2 Mar 2009 21:18:57 +0000 (21:18 +0000)
encoding conversion of any elog/ereport message being sent to the frontend.
This generalizes a patch that I put in last October, which suppressed
translation of only specific messages known to be associated with recursive
can't-translate-the-message behavior.  As shown in bug #4680, we need a more
general answer in order to have some hope of coping with broken encoding
conversion setups.  This approach seems a good deal less klugy anyway.

Patch in all supported branches.

src/backend/libpq/pqformat.c
src/backend/utils/error/elog.c
src/backend/utils/mb/wchar.c
src/include/libpq/pqformat.h

index 0a2589fdb4d20f619304d63d601c77cabc67f832..0795eaa00429dafb2e42bca6d6aec1e1266bc7b1 100644 (file)
@@ -24,7 +24,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/libpq/pqformat.c,v 1.42 2006/07/14 05:28:27 tgl Exp $
+ *     $PostgreSQL: pgsql/src/backend/libpq/pqformat.c,v 1.42.2.1 2009/03/02 21:18:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,6 +41,7 @@
  *             pq_sendcountedtext - append a counted text string (with character set conversion)
  *             pq_sendtext             - append a text string (with conversion)
  *             pq_sendstring   - append a null-terminated text string (with conversion)
+ *             pq_send_ascii_string - append a null-terminated text string (without conversion)
  *             pq_endmessage   - send the completed message to the frontend
  * Note: it is also possible to append data to the StringInfo buffer using
  * the regular StringInfo routines, but this is discouraged since required
@@ -187,7 +188,6 @@ void
 pq_sendstring(StringInfo buf, const char *str)
 {
        int                     slen = strlen(str);
-
        char       *p;
 
        p = pg_server_to_client(str, slen);
@@ -201,6 +201,35 @@ pq_sendstring(StringInfo buf, const char *str)
                appendBinaryStringInfo(buf, str, slen + 1);
 }
 
+/* --------------------------------
+ *             pq_send_ascii_string    - append a null-terminated text string (without conversion)
+ *
+ * This function intentionally bypasses encoding conversion, instead just
+ * silently replacing any non-7-bit-ASCII characters with question marks.
+ * It is used only when we are having trouble sending an error message to
+ * the client with normal localization and encoding conversion.  The caller
+ * should already have taken measures to ensure the string is just ASCII;
+ * the extra work here is just to make certain we don't send a badly encoded
+ * string to the client (which might or might not be robust about that).
+ *
+ * NB: passed text string must be null-terminated, and so is the data
+ * sent to the frontend.
+ * --------------------------------
+ */
+void
+pq_send_ascii_string(StringInfo buf, const char *str)
+{
+       while (*str)
+       {
+               char    ch = *str++;
+
+               if (IS_HIGHBIT_SET(ch))
+                       ch = '?';
+               appendStringInfoCharMacro(buf, ch);
+       }
+       appendStringInfoChar(buf, '\0');
+}
+
 /* --------------------------------
  *             pq_sendint              - append a binary integer to a StringInfo buffer
  * --------------------------------
index 60543f7ae09e1541a356f2200a3301ce9da413a5..abafd506691a01b6e455b9fddda3e4c10713add3 100644 (file)
@@ -42,7 +42,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.178.2.6 2008/10/27 19:37:36 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.178.2.7 2009/03/02 21:18:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -71,6 +71,9 @@
 #include "utils/ps_status.h"
 
 
+#undef _
+#define _(x) err_gettext(x)
+
 /* Global variables */
 ErrorContextCallback *error_context_stack = NULL;
 
@@ -140,6 +143,25 @@ in_error_recursion_trouble(void)
        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
  *
@@ -626,7 +648,7 @@ errcode_for_socket_access(void)
                char               *fmtbuf; \
                StringInfoData  buf; \
                /* Internationalize the error format string */ \
-               if (translateit) \
+               if (translateit && !in_error_recursion_trouble()) \
                        fmt = _(fmt); \
                /* Expand %m in format string */ \
                fmtbuf = expand_fmt_string(fmt, edata); \
@@ -1802,6 +1824,26 @@ write_pipe_chunks(int fd, char *data, int len)
 }
 
 
+/*
+ * 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
  */
@@ -1821,7 +1863,7 @@ send_message_to_frontend(ErrorData *edata)
                int                     i;
 
                pq_sendbyte(&msgbuf, PG_DIAG_SEVERITY);
-               pq_sendstring(&msgbuf, error_severity(edata->elevel));
+               err_sendstring(&msgbuf, error_severity(edata->elevel));
 
                /* unpack MAKE_SQLSTATE code */
                ssval = edata->sqlerrcode;
@@ -1833,70 +1875,70 @@ send_message_to_frontend(ErrorData *edata)
                tbuf[i] = '\0';
 
                pq_sendbyte(&msgbuf, PG_DIAG_SQLSTATE);
-               pq_sendstring(&msgbuf, tbuf);
+               err_sendstring(&msgbuf, tbuf);
 
                /* M field is required per protocol, so always send something */
                pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_PRIMARY);
                if (edata->message)
-                       pq_sendstring(&msgbuf, edata->message);
+                       err_sendstring(&msgbuf, edata->message);
                else
-                       pq_sendstring(&msgbuf, _("missing error text"));
+                       err_sendstring(&msgbuf, _("missing error text"));
 
                if (edata->detail)
                {
                        pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_DETAIL);
-                       pq_sendstring(&msgbuf, edata->detail);
+                       err_sendstring(&msgbuf, edata->detail);
                }
 
                if (edata->hint)
                {
                        pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_HINT);
-                       pq_sendstring(&msgbuf, edata->hint);
+                       err_sendstring(&msgbuf, edata->hint);
                }
 
                if (edata->context)
                {
                        pq_sendbyte(&msgbuf, PG_DIAG_CONTEXT);
-                       pq_sendstring(&msgbuf, edata->context);
+                       err_sendstring(&msgbuf, edata->context);
                }
 
                if (edata->cursorpos > 0)
                {
                        snprintf(tbuf, sizeof(tbuf), "%d", edata->cursorpos);
                        pq_sendbyte(&msgbuf, PG_DIAG_STATEMENT_POSITION);
-                       pq_sendstring(&msgbuf, tbuf);
+                       err_sendstring(&msgbuf, tbuf);
                }
 
                if (edata->internalpos > 0)
                {
                        snprintf(tbuf, sizeof(tbuf), "%d", edata->internalpos);
                        pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_POSITION);
-                       pq_sendstring(&msgbuf, tbuf);
+                       err_sendstring(&msgbuf, tbuf);
                }
 
                if (edata->internalquery)
                {
                        pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_QUERY);
-                       pq_sendstring(&msgbuf, edata->internalquery);
+                       err_sendstring(&msgbuf, edata->internalquery);
                }
 
                if (edata->filename)
                {
                        pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_FILE);
-                       pq_sendstring(&msgbuf, edata->filename);
+                       err_sendstring(&msgbuf, edata->filename);
                }
 
                if (edata->lineno > 0)
                {
                        snprintf(tbuf, sizeof(tbuf), "%d", edata->lineno);
                        pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_LINE);
-                       pq_sendstring(&msgbuf, tbuf);
+                       err_sendstring(&msgbuf, tbuf);
                }
 
                if (edata->funcname)
                {
                        pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_FUNCTION);
-                       pq_sendstring(&msgbuf, edata->funcname);
+                       err_sendstring(&msgbuf, edata->funcname);
                }
 
                pq_sendbyte(&msgbuf, '\0');             /* terminator */
@@ -1927,7 +1969,7 @@ send_message_to_frontend(ErrorData *edata)
 
                appendStringInfoChar(&buf, '\n');
 
-               pq_sendstring(&msgbuf, buf.data);
+               err_sendstring(&msgbuf, buf.data);
 
                pfree(buf.data);
        }
@@ -2041,10 +2083,6 @@ 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)
@@ -2074,22 +2112,13 @@ error_severity(int elevel)
                        prefix = _("WARNING");
                        break;
                case ERROR:
-                       if (in_error_recursion_trouble())
-                               prefix = "ERROR";
-                       else
-                               prefix = _("ERROR");
+                       prefix = _("ERROR");
                        break;
                case FATAL:
-                       if (in_error_recursion_trouble())
-                               prefix = "FATAL";
-                       else
-                               prefix = _("FATAL");
+                       prefix = _("FATAL");
                        break;
                case PANIC:
-                       if (in_error_recursion_trouble())
-                               prefix = "PANIC";
-                       else
-                               prefix = _("PANIC");
+                       prefix = _("PANIC");
                        break;
                default:
                        prefix = "???";
index a99efcbadf56fe3fe2eb54da707c9314bfd995d1..97c69fb4cc28caffe7d85e414b15ed3e308db245 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.4 2009/01/29 19:24:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/mb/wchar.c,v 1.58.2.5 2009/03/02 21:18:57 tgl Exp $
  *
  * WIN1250 client encoding updated by Pavel Behal
  *
@@ -1589,25 +1589,12 @@ report_untranslatable_char(int src_encoding, int dest_encoding,
        for (j = 0; j < jlimit; j++)
                p += sprintf(p, "%02x", (unsigned char) mbstr[j]);
 
-       /*
-        * 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)));
+       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 08ba21eca187db63f86361ca7a6b342899f2aac5..267b9f7782c28357ea7a538359443b82b4ed3e6c 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/libpq/pqformat.h,v 1.24 2006/03/05 15:58:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/libpq/pqformat.h,v 1.24.2.1 2009/03/02 21:18:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,7 @@ extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen,
                                   bool countincludesself);
 extern void pq_sendtext(StringInfo buf, const char *str, int slen);
 extern void pq_sendstring(StringInfo buf, const char *str);
+extern void pq_send_ascii_string(StringInfo buf, const char *str);
 extern void pq_sendint(StringInfo buf, int i, int b);
 extern void pq_sendint64(StringInfo buf, int64 i);
 extern void pq_sendfloat4(StringInfo buf, float4 f);