From fd9e2accefb1a1763c58c19e07e1e18d0424d459 Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Mon, 2 Mar 2009 21:18:43 +0000 Subject: [PATCH] When we are in error recursion trouble, arrange to suppress translation and 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 | 33 +++++++++++- src/backend/utils/error/elog.c | 95 ++++++++++++++++++++++------------ src/backend/utils/mb/wchar.c | 27 +++------- src/include/libpq/pqformat.h | 3 +- 4 files changed, 102 insertions(+), 56 deletions(-) diff --git a/src/backend/libpq/pqformat.c b/src/backend/libpq/pqformat.c index dc7eb96a721..83d7a2bbf76 100644 --- a/src/backend/libpq/pqformat.c +++ b/src/backend/libpq/pqformat.c @@ -24,7 +24,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/libpq/pqformat.c,v 1.48 2009/01/01 17:23:42 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/libpq/pqformat.c,v 1.49 2009/03/02 21:18:43 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 @@ -184,7 +185,6 @@ void pq_sendstring(StringInfo buf, const char *str) { int slen = strlen(str); - char *p; p = pg_server_to_client(str, slen); @@ -198,6 +198,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 * -------------------------------- diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 67f8c24b490..0439c4c1d18 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.212 2009/01/19 15:34:23 mha Exp $ + * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.213 2009/03/02 21:18:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -72,6 +72,9 @@ #include "utils/ps_status.h" +#undef _ +#define _(x) err_gettext(x) + /* Global variables */ ErrorContextCallback *error_context_stack = NULL; @@ -164,6 +167,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 * @@ -631,7 +653,7 @@ errcode_for_socket_access(void) char *fmtbuf; \ StringInfoData buf; \ /* Internationalize the error format string */ \ - if (translateit) \ + if (translateit && !in_error_recursion_trouble()) \ fmt = dgettext(edata->domain, fmt); \ /* Expand %m in format string */ \ fmtbuf = expand_fmt_string(fmt, edata); \ @@ -2137,7 +2159,7 @@ send_message_to_server_log(ErrorData *edata) } else { - char *msg = _("Not safe to send CSV data\n"); + const char *msg = _("Not safe to send CSV data\n"); write(fileno(stderr), msg, strlen(msg)); if (!(Log_destination & LOG_DESTINATION_STDERR) && @@ -2189,6 +2211,26 @@ write_pipe_chunks(char *data, int len, int dest) } +/* + * 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 */ @@ -2208,7 +2250,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; @@ -2220,19 +2262,19 @@ 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); } /* detail_log is intentionally not used here */ @@ -2240,52 +2282,52 @@ send_message_to_frontend(ErrorData *edata) 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 */ @@ -2316,7 +2358,7 @@ send_message_to_frontend(ErrorData *edata) appendStringInfoChar(&buf, '\n'); - pq_sendstring(&msgbuf, buf.data); + err_sendstring(&msgbuf, buf.data); pfree(buf.data); } @@ -2430,10 +2472,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) @@ -2463,22 +2501,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 = "???"; diff --git a/src/backend/utils/mb/wchar.c b/src/backend/utils/mb/wchar.c index 7b7ebf6bbf8..8da81b9f221 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.71 2009/02/10 19:29:39 petere Exp $ + * $PostgreSQL: pgsql/src/backend/utils/mb/wchar.c,v 1.72 2009/03/02 21:18:43 tgl Exp $ * */ /* can be used in either frontend or backend */ @@ -1636,25 +1636,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 diff --git a/src/include/libpq/pqformat.h b/src/include/libpq/pqformat.h index 6962b4eb97c..61692487a9b 100644 --- a/src/include/libpq/pqformat.h +++ b/src/include/libpq/pqformat.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/libpq/pqformat.h,v 1.27 2009/01/01 17:23:59 momjian Exp $ + * $PostgreSQL: pgsql/src/include/libpq/pqformat.h,v 1.28 2009/03/02 21:18:43 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); -- GitLab