Re: ERRORDATA_STACK_SIZE exceeded (server crash)

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: ERRORDATA_STACK_SIZE exceeded (server crash)
Дата
Msg-id 18251.1225124940@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: ERRORDATA_STACK_SIZE exceeded (server crash)  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: ERRORDATA_STACK_SIZE exceeded (server crash)  (Alvaro Herrera <alvherre@commandprompt.com>)
Список pgsql-hackers
I wrote:
> This is still not very nice because what the user would get is
> a complaint about ERRORDATA_STACK_SIZE exceeded with no hint that
> he's got an encoding problem.  It'd be better if we could get the
> disable-gettext-and-FATAL-out behavior to apply to the "character
> has no equivalent" error message, but I'm not sure how we do that
> without bollixing up less-critical occurrences of that message.

After poking around a bit I decided that this could be done in a not
horrendously ugly way if we are willing to make a couple more places
know about escaping from error recursion situations.  Attached is a
proposed patch that prevents the crash shown previously.  BTW, a better
stress test for this is to set LANG = tr_TR.utf8, client_encoding =
latin1, and then try "select E'\305\237';".  That's because the
"character has no equivalent" message isn't itself translated in the
present ko translation, but it is in the tr translation.  My first-cut
patch worked for the ko case and not the tr case :-(

One thing that is still a bit ugly about this patch is the hack in
wchar.c to ensure that the "character has no equivalent" message
doesn't get translated:

     * ...  Note that we have to
     * spell the message slightly differently, which we do by sticking a
     * space on the end --- using errmsg_internal() doesn't actually keep
     * elog.c from calling gettext, it only prevents the string from being
     * entered into the translation lists.

It might be better to modify elog.c so that errmsg_internal really
doesn't call gettext.  This would require kluging up EVALUATE_MESSAGE()
a bit, so I'm not quite sure which is cleaner.  Thoughts?

            regards, tom lane

Index: src/backend/utils/error/elog.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/error/elog.c,v
retrieving revision 1.208
diff -c -r1.208 elog.c
*** src/backend/utils/error/elog.c    17 Oct 2008 22:56:16 -0000    1.208
--- src/backend/utils/error/elog.c    27 Oct 2008 16:16:28 -0000
***************
*** 149,154 ****
--- 149,169 ----
  static void setup_formatted_log_time(void);
  static void setup_formatted_start_time(void);

+
+ /*
+  * in_error_recursion_trouble --- are we at risk of infinite error recursion?
+  *
+  * This function exists to provide common control of various fallback steps
+  * that we take if we think we are facing infinite error recursion.  See the
+  * callers for details.
+  */
+ bool
+ in_error_recursion_trouble(void)
+ {
+     /* Pull the plug if recurse more than once */
+     return (recursion_depth > 2);
+ }
+
  /*
   * errstart --- begin an error-reporting cycle
   *
***************
*** 261,272 ****
          MemoryContextReset(ErrorContext);

          /*
!          * If we recurse more than once, the problem might be something broken
           * in a context traceback routine.    Abandon them too.  We also abandon
           * attempting to print the error statement (which, if long, could
           * itself be the source of the recursive failure).
           */
!         if (recursion_depth > 2)
          {
              error_context_stack = NULL;
              debug_query_string = NULL;
--- 276,287 ----
          MemoryContextReset(ErrorContext);

          /*
!          * Infinite error recursion might be due to something broken
           * in a context traceback routine.    Abandon them too.  We also abandon
           * attempting to print the error statement (which, if long, could
           * itself be the source of the recursive failure).
           */
!         if (in_error_recursion_trouble())
          {
              error_context_stack = NULL;
              debug_query_string = NULL;
***************
*** 2408,2413 ****
--- 2423,2432 ----

  /*
   * 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)
***************
*** 2437,2449 ****
              prefix = _("WARNING");
              break;
          case ERROR:
!             prefix = _("ERROR");
              break;
          case FATAL:
!             prefix = _("FATAL");
              break;
          case PANIC:
!             prefix = _("PANIC");
              break;
          default:
              prefix = "???";
--- 2456,2477 ----
              prefix = _("WARNING");
              break;
          case ERROR:
!             if (in_error_recursion_trouble())
!                 prefix = "ERROR";
!             else
!                 prefix = _("ERROR");
              break;
          case FATAL:
!             if (in_error_recursion_trouble())
!                 prefix = "FATAL";
!             else
!                 prefix = _("FATAL");
              break;
          case PANIC:
!             if (in_error_recursion_trouble())
!                 prefix = "PANIC";
!             else
!                 prefix = _("PANIC");
              break;
          default:
              prefix = "???";
Index: src/backend/utils/mb/wchar.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/mb/wchar.c,v
retrieving revision 1.66
diff -c -r1.66 wchar.c
*** src/backend/utils/mb/wchar.c    15 Nov 2007 21:14:40 -0000    1.66
--- src/backend/utils/mb/wchar.c    27 Oct 2008 16:16:28 -0000
***************
*** 1567,1578 ****
      for (j = 0; j < jlimit; j++)
          p += sprintf(p, "%02x", (unsigned char) mbstr[j]);

!     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
--- 1567,1595 ----
      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
!      * the translated message to the client encoding.  Note that we have to
!      * spell the message slightly differently, which we do by sticking a
!      * space on the end --- using errmsg_internal() doesn't actually keep
!      * elog.c from calling gettext, it only prevents the string from being
!      * entered into the translation lists.
!      */
!     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)));
  }

  #endif
Index: src/include/utils/elog.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/utils/elog.h,v
retrieving revision 1.96
diff -c -r1.96 elog.h
*** src/include/utils/elog.h    9 Oct 2008 22:22:31 -0000    1.96
--- src/include/utils/elog.h    27 Oct 2008 16:16:28 -0000
***************
*** 324,329 ****
--- 324,330 ----
  /* Other exported functions */
  extern void DebugFileOpen(void);
  extern char *unpack_sql_state(int sql_state);
+ extern bool in_error_recursion_trouble(void);

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

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Jeff Davis
Дата:
Сообщение: Re: array_agg and array_accum (patch)
Следующее
От: Alvaro Herrera
Дата:
Сообщение: Re: ERRORDATA_STACK_SIZE exceeded (server crash)