Commit fd9e2acc authored by Tom Lane's avatar Tom Lane

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.
parent 32032d42
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -41,6 +41,7 @@
* pq_sendcountedtext - append a counted text string (with character set conversion) * pq_sendcountedtext - append a counted text string (with character set conversion)
* pq_sendtext - append a text string (with conversion) * pq_sendtext - append a text string (with conversion)
* pq_sendstring - append a null-terminated 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 * pq_endmessage - send the completed message to the frontend
* Note: it is also possible to append data to the StringInfo buffer using * Note: it is also possible to append data to the StringInfo buffer using
* the regular StringInfo routines, but this is discouraged since required * the regular StringInfo routines, but this is discouraged since required
...@@ -184,7 +185,6 @@ void ...@@ -184,7 +185,6 @@ void
pq_sendstring(StringInfo buf, const char *str) pq_sendstring(StringInfo buf, const char *str)
{ {
int slen = strlen(str); int slen = strlen(str);
char *p; char *p;
p = pg_server_to_client(str, slen); p = pg_server_to_client(str, slen);
...@@ -198,6 +198,35 @@ pq_sendstring(StringInfo buf, const char *str) ...@@ -198,6 +198,35 @@ pq_sendstring(StringInfo buf, const char *str)
appendBinaryStringInfo(buf, str, slen + 1); 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 * pq_sendint - append a binary integer to a StringInfo buffer
* -------------------------------- * --------------------------------
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -72,6 +72,9 @@
#include "utils/ps_status.h" #include "utils/ps_status.h"
#undef _
#define _(x) err_gettext(x)
/* Global variables */ /* Global variables */
ErrorContextCallback *error_context_stack = NULL; ErrorContextCallback *error_context_stack = NULL;
...@@ -164,6 +167,25 @@ in_error_recursion_trouble(void) ...@@ -164,6 +167,25 @@ in_error_recursion_trouble(void)
return (recursion_depth > 2); 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 * errstart --- begin an error-reporting cycle
* *
...@@ -631,7 +653,7 @@ errcode_for_socket_access(void) ...@@ -631,7 +653,7 @@ errcode_for_socket_access(void)
char *fmtbuf; \ char *fmtbuf; \
StringInfoData buf; \ StringInfoData buf; \
/* Internationalize the error format string */ \ /* Internationalize the error format string */ \
if (translateit) \ if (translateit && !in_error_recursion_trouble()) \
fmt = dgettext(edata->domain, fmt); \ fmt = dgettext(edata->domain, fmt); \
/* Expand %m in format string */ \ /* Expand %m in format string */ \
fmtbuf = expand_fmt_string(fmt, edata); \ fmtbuf = expand_fmt_string(fmt, edata); \
...@@ -2137,7 +2159,7 @@ send_message_to_server_log(ErrorData *edata) ...@@ -2137,7 +2159,7 @@ send_message_to_server_log(ErrorData *edata)
} }
else 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)); write(fileno(stderr), msg, strlen(msg));
if (!(Log_destination & LOG_DESTINATION_STDERR) && if (!(Log_destination & LOG_DESTINATION_STDERR) &&
...@@ -2189,6 +2211,26 @@ write_pipe_chunks(char *data, int len, int dest) ...@@ -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 * Write error report to client
*/ */
...@@ -2208,7 +2250,7 @@ send_message_to_frontend(ErrorData *edata) ...@@ -2208,7 +2250,7 @@ send_message_to_frontend(ErrorData *edata)
int i; int i;
pq_sendbyte(&msgbuf, PG_DIAG_SEVERITY); pq_sendbyte(&msgbuf, PG_DIAG_SEVERITY);
pq_sendstring(&msgbuf, error_severity(edata->elevel)); err_sendstring(&msgbuf, error_severity(edata->elevel));
/* unpack MAKE_SQLSTATE code */ /* unpack MAKE_SQLSTATE code */
ssval = edata->sqlerrcode; ssval = edata->sqlerrcode;
...@@ -2220,19 +2262,19 @@ send_message_to_frontend(ErrorData *edata) ...@@ -2220,19 +2262,19 @@ send_message_to_frontend(ErrorData *edata)
tbuf[i] = '\0'; tbuf[i] = '\0';
pq_sendbyte(&msgbuf, PG_DIAG_SQLSTATE); pq_sendbyte(&msgbuf, PG_DIAG_SQLSTATE);
pq_sendstring(&msgbuf, tbuf); err_sendstring(&msgbuf, tbuf);
/* M field is required per protocol, so always send something */ /* M field is required per protocol, so always send something */
pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_PRIMARY); pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_PRIMARY);
if (edata->message) if (edata->message)
pq_sendstring(&msgbuf, edata->message); err_sendstring(&msgbuf, edata->message);
else else
pq_sendstring(&msgbuf, _("missing error text")); err_sendstring(&msgbuf, _("missing error text"));
if (edata->detail) if (edata->detail)
{ {
pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_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 */ /* detail_log is intentionally not used here */
...@@ -2240,52 +2282,52 @@ send_message_to_frontend(ErrorData *edata) ...@@ -2240,52 +2282,52 @@ send_message_to_frontend(ErrorData *edata)
if (edata->hint) if (edata->hint)
{ {
pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_HINT); pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_HINT);
pq_sendstring(&msgbuf, edata->hint); err_sendstring(&msgbuf, edata->hint);
} }
if (edata->context) if (edata->context)
{ {
pq_sendbyte(&msgbuf, PG_DIAG_CONTEXT); pq_sendbyte(&msgbuf, PG_DIAG_CONTEXT);
pq_sendstring(&msgbuf, edata->context); err_sendstring(&msgbuf, edata->context);
} }
if (edata->cursorpos > 0) if (edata->cursorpos > 0)
{ {
snprintf(tbuf, sizeof(tbuf), "%d", edata->cursorpos); snprintf(tbuf, sizeof(tbuf), "%d", edata->cursorpos);
pq_sendbyte(&msgbuf, PG_DIAG_STATEMENT_POSITION); pq_sendbyte(&msgbuf, PG_DIAG_STATEMENT_POSITION);
pq_sendstring(&msgbuf, tbuf); err_sendstring(&msgbuf, tbuf);
} }
if (edata->internalpos > 0) if (edata->internalpos > 0)
{ {
snprintf(tbuf, sizeof(tbuf), "%d", edata->internalpos); snprintf(tbuf, sizeof(tbuf), "%d", edata->internalpos);
pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_POSITION); pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_POSITION);
pq_sendstring(&msgbuf, tbuf); err_sendstring(&msgbuf, tbuf);
} }
if (edata->internalquery) if (edata->internalquery)
{ {
pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_QUERY); pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_QUERY);
pq_sendstring(&msgbuf, edata->internalquery); err_sendstring(&msgbuf, edata->internalquery);
} }
if (edata->filename) if (edata->filename)
{ {
pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_FILE); pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_FILE);
pq_sendstring(&msgbuf, edata->filename); err_sendstring(&msgbuf, edata->filename);
} }
if (edata->lineno > 0) if (edata->lineno > 0)
{ {
snprintf(tbuf, sizeof(tbuf), "%d", edata->lineno); snprintf(tbuf, sizeof(tbuf), "%d", edata->lineno);
pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_LINE); pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_LINE);
pq_sendstring(&msgbuf, tbuf); err_sendstring(&msgbuf, tbuf);
} }
if (edata->funcname) if (edata->funcname)
{ {
pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_FUNCTION); pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_FUNCTION);
pq_sendstring(&msgbuf, edata->funcname); err_sendstring(&msgbuf, edata->funcname);
} }
pq_sendbyte(&msgbuf, '\0'); /* terminator */ pq_sendbyte(&msgbuf, '\0'); /* terminator */
...@@ -2316,7 +2358,7 @@ send_message_to_frontend(ErrorData *edata) ...@@ -2316,7 +2358,7 @@ send_message_to_frontend(ErrorData *edata)
appendStringInfoChar(&buf, '\n'); appendStringInfoChar(&buf, '\n');
pq_sendstring(&msgbuf, buf.data); err_sendstring(&msgbuf, buf.data);
pfree(buf.data); pfree(buf.data);
} }
...@@ -2430,10 +2472,6 @@ useful_strerror(int errnum) ...@@ -2430,10 +2472,6 @@ useful_strerror(int errnum)
/* /*
* error_severity --- get localized string representing elevel * 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 * static const char *
error_severity(int elevel) error_severity(int elevel)
...@@ -2463,22 +2501,13 @@ error_severity(int elevel) ...@@ -2463,22 +2501,13 @@ error_severity(int elevel)
prefix = _("WARNING"); prefix = _("WARNING");
break; break;
case ERROR: case ERROR:
if (in_error_recursion_trouble()) prefix = _("ERROR");
prefix = "ERROR";
else
prefix = _("ERROR");
break; break;
case FATAL: case FATAL:
if (in_error_recursion_trouble()) prefix = _("FATAL");
prefix = "FATAL";
else
prefix = _("FATAL");
break; break;
case PANIC: case PANIC:
if (in_error_recursion_trouble()) prefix = _("PANIC");
prefix = "PANIC";
else
prefix = _("PANIC");
break; break;
default: default:
prefix = "???"; prefix = "???";
......
/* /*
* conversion functions between pg_wchar and multibyte streams. * conversion functions between pg_wchar and multibyte streams.
* Tatsuo Ishii * 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 */ /* can be used in either frontend or backend */
...@@ -1636,25 +1636,12 @@ report_untranslatable_char(int src_encoding, int dest_encoding, ...@@ -1636,25 +1636,12 @@ report_untranslatable_char(int src_encoding, int dest_encoding,
for (j = 0; j < jlimit; j++) for (j = 0; j < jlimit; j++)
p += sprintf(p, "%02x", (unsigned char) mbstr[j]); p += sprintf(p, "%02x", (unsigned char) mbstr[j]);
/* ereport(ERROR,
* In an error recursion situation, don't try to translate the message. (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
* This gets us out of trouble if the problem is failure to convert errmsg("character 0x%s of encoding \"%s\" has no equivalent in \"%s\"",
* this very message (after translation) to the client encoding. buf,
*/ pg_enc2name_tbl[src_encoding].name,
if (in_error_recursion_trouble()) pg_enc2name_tbl[dest_encoding].name)));
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 #endif
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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, ...@@ -22,6 +22,7 @@ extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen,
bool countincludesself); bool countincludesself);
extern void pq_sendtext(StringInfo buf, const char *str, int slen); extern void pq_sendtext(StringInfo buf, const char *str, int slen);
extern void pq_sendstring(StringInfo buf, const char *str); 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_sendint(StringInfo buf, int i, int b);
extern void pq_sendint64(StringInfo buf, int64 i); extern void pq_sendint64(StringInfo buf, int64 i);
extern void pq_sendfloat4(StringInfo buf, float4 f); extern void pq_sendfloat4(StringInfo buf, float4 f);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment