Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b0169bb

Browse files
committed
Install a more robust solution for the problem of infinite error-processing
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.
1 parent f3a0688 commit b0169bb

File tree

5 files changed

+82
-35
lines changed

5 files changed

+82
-35
lines changed

doc/src/sgml/sources.sgml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/sources.sgml,v 2.31 2008/09/07 02:01:04 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/sources.sgml,v 2.32 2008/10/27 19:37:21 tgl Exp $ -->
22

33
<chapter id="source">
44
<title>PostgreSQL Coding Conventions</title>
@@ -176,7 +176,7 @@ ereport(ERROR,
176176
<para>
177177
<function>errmsg_internal(const char *msg, ...)</function> is the same as
178178
<function>errmsg</>, except that the message string will not be
179-
included in the internationalization message dictionary.
179+
translated nor included in the internationalization message dictionary.
180180
This should be used for <quote>cannot happen</> cases that are probably
181181
not worth expending translation effort on.
182182
</para>
@@ -271,7 +271,7 @@ elog(level, "format string", ...);
271271
ereport(level, (errmsg_internal("format string", ...)));
272272
</programlisting>
273273
Notice that the SQLSTATE error code is always defaulted, and the message
274-
string is not included in the internationalization message dictionary.
274+
string is not subject to translation.
275275
Therefore, <function>elog</> should be used only for internal errors and
276276
low-level debug logging. Any message that is likely to be of interest to
277277
ordinary users should go through <function>ereport</>. Nonetheless,

src/backend/nls.mk

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
# $PostgreSQL: pgsql/src/backend/nls.mk,v 1.22 2008/03/24 18:08:47 tgl Exp $
1+
# $PostgreSQL: pgsql/src/backend/nls.mk,v 1.23 2008/10/27 19:37:21 tgl Exp $
22
CATALOG_NAME := postgres
33
AVAIL_LANGUAGES := af cs de es fr hr hu it ko nb nl pt_BR ro ru sk sl sv tr zh_CN zh_TW
44
GETTEXT_FILES := + gettext-files
5-
# you can add "elog:2" and "errmsg_internal" to this list if you want to
6-
# include internal messages in the translation list.
75
GETTEXT_TRIGGERS:= _ errmsg errdetail errdetail_log errhint errcontext write_stderr yyerror
86

97
gettext-files: distprep

src/backend/utils/error/elog.c

+56-21
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
*
4343
*
4444
* IDENTIFICATION
45-
* $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.208 2008/10/17 22:56:16 alvherre Exp $
45+
* $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.209 2008/10/27 19:37:21 tgl Exp $
4646
*
4747
*-------------------------------------------------------------------------
4848
*/
@@ -149,6 +149,21 @@ static void write_csvlog(ErrorData *edata);
149149
static void setup_formatted_log_time(void);
150150
static void setup_formatted_start_time(void);
151151

152+
153+
/*
154+
* in_error_recursion_trouble --- are we at risk of infinite error recursion?
155+
*
156+
* This function exists to provide common control of various fallback steps
157+
* that we take if we think we are facing infinite error recursion. See the
158+
* callers for details.
159+
*/
160+
bool
161+
in_error_recursion_trouble(void)
162+
{
163+
/* Pull the plug if recurse more than once */
164+
return (recursion_depth > 2);
165+
}
166+
152167
/*
153168
* errstart --- begin an error-reporting cycle
154169
*
@@ -261,12 +276,12 @@ errstart(int elevel, const char *filename, int lineno,
261276
MemoryContextReset(ErrorContext);
262277

263278
/*
264-
* If we recurse more than once, the problem might be something broken
279+
* Infinite error recursion might be due to something broken
265280
* in a context traceback routine. Abandon them too. We also abandon
266281
* attempting to print the error statement (which, if long, could
267282
* itself be the source of the recursive failure).
268283
*/
269-
if (recursion_depth > 2)
284+
if (in_error_recursion_trouble())
270285
{
271286
error_context_stack = NULL;
272287
debug_query_string = NULL;
@@ -604,18 +619,20 @@ errcode_for_socket_access(void)
604619
* it's common code for errmsg(), errdetail(), etc. Must be called inside
605620
* a routine that is declared like "const char *fmt, ..." and has an edata
606621
* pointer set up. The message is assigned to edata->targetfield, or
607-
* appended to it if appendval is true.
622+
* appended to it if appendval is true. The message is subject to translation
623+
* if translateit is true.
608624
*
609625
* Note: we pstrdup the buffer rather than just transferring its storage
610626
* to the edata field because the buffer might be considerably larger than
611627
* really necessary.
612628
*/
613-
#define EVALUATE_MESSAGE(targetfield, appendval) \
629+
#define EVALUATE_MESSAGE(targetfield, appendval, translateit) \
614630
{ \
615631
char *fmtbuf; \
616632
StringInfoData buf; \
617633
/* Internationalize the error format string */ \
618-
fmt = dgettext(edata->domain, fmt); \
634+
if (translateit) \
635+
fmt = dgettext(edata->domain, fmt); \
619636
/* Expand %m in format string */ \
620637
fmtbuf = expand_fmt_string(fmt, edata); \
621638
initStringInfo(&buf); \
@@ -662,7 +679,7 @@ errmsg(const char *fmt,...)
662679
CHECK_STACK_DEPTH();
663680
oldcontext = MemoryContextSwitchTo(ErrorContext);
664681

665-
EVALUATE_MESSAGE(message, false);
682+
EVALUATE_MESSAGE(message, false, true);
666683

667684
MemoryContextSwitchTo(oldcontext);
668685
recursion_depth--;
@@ -674,9 +691,12 @@ errmsg(const char *fmt,...)
674691
* errmsg_internal --- add a primary error message text to the current error
675692
*
676693
* This is exactly like errmsg() except that strings passed to errmsg_internal
677-
* are customarily left out of the internationalization message dictionary.
678-
* This should be used for "can't happen" cases that are probably not worth
679-
* spending translation effort on.
694+
* are not translated, and are customarily left out of the
695+
* internationalization message dictionary. This should be used for "can't
696+
* happen" cases that are probably not worth spending translation effort on.
697+
* We also use this for certain cases where we *must* not try to translate
698+
* the message because the translation would fail and result in infinite
699+
* error recursion.
680700
*/
681701
int
682702
errmsg_internal(const char *fmt,...)
@@ -688,7 +708,7 @@ errmsg_internal(const char *fmt,...)
688708
CHECK_STACK_DEPTH();
689709
oldcontext = MemoryContextSwitchTo(ErrorContext);
690710

691-
EVALUATE_MESSAGE(message, false);
711+
EVALUATE_MESSAGE(message, false, false);
692712

693713
MemoryContextSwitchTo(oldcontext);
694714
recursion_depth--;
@@ -709,7 +729,7 @@ errdetail(const char *fmt,...)
709729
CHECK_STACK_DEPTH();
710730
oldcontext = MemoryContextSwitchTo(ErrorContext);
711731

712-
EVALUATE_MESSAGE(detail, false);
732+
EVALUATE_MESSAGE(detail, false, true);
713733

714734
MemoryContextSwitchTo(oldcontext);
715735
recursion_depth--;
@@ -730,7 +750,7 @@ errdetail_log(const char *fmt,...)
730750
CHECK_STACK_DEPTH();
731751
oldcontext = MemoryContextSwitchTo(ErrorContext);
732752

733-
EVALUATE_MESSAGE(detail_log, false);
753+
EVALUATE_MESSAGE(detail_log, false, true);
734754

735755
MemoryContextSwitchTo(oldcontext);
736756
recursion_depth--;
@@ -751,7 +771,7 @@ errhint(const char *fmt,...)
751771
CHECK_STACK_DEPTH();
752772
oldcontext = MemoryContextSwitchTo(ErrorContext);
753773

754-
EVALUATE_MESSAGE(hint, false);
774+
EVALUATE_MESSAGE(hint, false, true);
755775

756776
MemoryContextSwitchTo(oldcontext);
757777
recursion_depth--;
@@ -776,7 +796,7 @@ errcontext(const char *fmt,...)
776796
CHECK_STACK_DEPTH();
777797
oldcontext = MemoryContextSwitchTo(ErrorContext);
778798

779-
EVALUATE_MESSAGE(context, true);
799+
EVALUATE_MESSAGE(context, true, true);
780800

781801
MemoryContextSwitchTo(oldcontext);
782802
recursion_depth--;
@@ -956,7 +976,9 @@ elog_start(const char *filename, int lineno, const char *funcname)
956976
/*
957977
* Wups, stack not big enough. We treat this as a PANIC condition
958978
* because it suggests an infinite loop of errors during error
959-
* recovery.
979+
* recovery. Note that the message is intentionally not localized,
980+
* else failure to convert it to client encoding could cause further
981+
* recursion.
960982
*/
961983
errordata_stack_depth = -1; /* make room on stack */
962984
ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
@@ -990,12 +1012,12 @@ elog_finish(int elevel, const char *fmt,...)
9901012
return; /* nothing to do */
9911013

9921014
/*
993-
* Format error message just like errmsg().
1015+
* Format error message just like errmsg_internal().
9941016
*/
9951017
recursion_depth++;
9961018
oldcontext = MemoryContextSwitchTo(ErrorContext);
9971019

998-
EVALUATE_MESSAGE(message, false);
1020+
EVALUATE_MESSAGE(message, false, false);
9991021

10001022
MemoryContextSwitchTo(oldcontext);
10011023
recursion_depth--;
@@ -2408,6 +2430,10 @@ useful_strerror(int errnum)
24082430

24092431
/*
24102432
* error_severity --- get localized string representing elevel
2433+
*
2434+
* Note: in an error recursion situation, we stop localizing the tags
2435+
* for ERROR and above. This is necessary because the problem might be
2436+
* failure to convert one of these strings to the client encoding.
24112437
*/
24122438
static const char *
24132439
error_severity(int elevel)
@@ -2437,13 +2463,22 @@ error_severity(int elevel)
24372463
prefix = _("WARNING");
24382464
break;
24392465
case ERROR:
2440-
prefix = _("ERROR");
2466+
if (in_error_recursion_trouble())
2467+
prefix = "ERROR";
2468+
else
2469+
prefix = _("ERROR");
24412470
break;
24422471
case FATAL:
2443-
prefix = _("FATAL");
2472+
if (in_error_recursion_trouble())
2473+
prefix = "FATAL";
2474+
else
2475+
prefix = _("FATAL");
24442476
break;
24452477
case PANIC:
2446-
prefix = _("PANIC");
2478+
if (in_error_recursion_trouble())
2479+
prefix = "PANIC";
2480+
else
2481+
prefix = _("PANIC");
24472482
break;
24482483
default:
24492484
prefix = "???";

src/backend/utils/mb/wchar.c

+20-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* conversion functions between pg_wchar and multibyte streams.
33
* Tatsuo Ishii
4-
* $PostgreSQL: pgsql/src/backend/utils/mb/wchar.c,v 1.66 2007/11/15 21:14:40 momjian Exp $
4+
* $PostgreSQL: pgsql/src/backend/utils/mb/wchar.c,v 1.67 2008/10/27 19:37:22 tgl Exp $
55
*
66
*/
77
/* can be used in either frontend or backend */
@@ -1567,12 +1567,25 @@ report_untranslatable_char(int src_encoding, int dest_encoding,
15671567
for (j = 0; j < jlimit; j++)
15681568
p += sprintf(p, "%02x", (unsigned char) mbstr[j]);
15691569

1570-
ereport(ERROR,
1571-
(errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
1572-
errmsg("character 0x%s of encoding \"%s\" has no equivalent in \"%s\"",
1573-
buf,
1574-
pg_enc2name_tbl[src_encoding].name,
1575-
pg_enc2name_tbl[dest_encoding].name)));
1570+
/*
1571+
* In an error recursion situation, don't try to translate the message.
1572+
* This gets us out of trouble if the problem is failure to convert
1573+
* this very message (after translation) to the client encoding.
1574+
*/
1575+
if (in_error_recursion_trouble())
1576+
ereport(ERROR,
1577+
(errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
1578+
errmsg_internal("character 0x%s of encoding \"%s\" has no equivalent in \"%s\"",
1579+
buf,
1580+
pg_enc2name_tbl[src_encoding].name,
1581+
pg_enc2name_tbl[dest_encoding].name)));
1582+
else
1583+
ereport(ERROR,
1584+
(errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
1585+
errmsg("character 0x%s of encoding \"%s\" has no equivalent in \"%s\"",
1586+
buf,
1587+
pg_enc2name_tbl[src_encoding].name,
1588+
pg_enc2name_tbl[dest_encoding].name)));
15761589
}
15771590

15781591
#endif

src/include/utils/elog.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.96 2008/10/09 22:22:31 alvherre Exp $
10+
* $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.97 2008/10/27 19:37:22 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -324,6 +324,7 @@ extern int Log_destination;
324324
/* Other exported functions */
325325
extern void DebugFileOpen(void);
326326
extern char *unpack_sql_state(int sql_state);
327+
extern bool in_error_recursion_trouble(void);
327328

328329
#ifdef HAVE_SYSLOG
329330
extern void set_syslog_parameters(const char *ident, int facility);

0 commit comments

Comments
 (0)