Skip to content
Snippets Groups Projects
Commit fd9e2acc authored by Tom Lane's avatar Tom Lane
Browse files

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
No related branches found
No related tags found
No related merge requests found
......@@ -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
* --------------------------------
......
......@@ -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 = "???";
......
/*
* 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
......@@ -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);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment