]> granicus.if.org Git - postgresql/commitdiff
Add a nonlocalized version of the severity field to client error messages.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 26 Aug 2016 20:20:17 +0000 (16:20 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 26 Aug 2016 20:20:24 +0000 (16:20 -0400)
This has been requested a few times, but the use-case for it was never
entirely clear.  The reason for adding it now is that transmission of
error reports from parallel workers fails when NLS is active, because
pq_parse_errornotice() wrongly assumes that the existing severity field
is nonlocalized.  There are other ways we could have fixed that, but the
other options were basically kluges, whereas this way provides something
that's at least arguably a useful feature along with the bug fix.

Per report from Jakob Egger.  Back-patch into 9.6, because otherwise
parallel query is essentially unusable in non-English locales.  The
problem exists in 9.5 as well, but we don't want to risk changing
on-the-wire behavior in 9.5 (even though the possibility of new error
fields is specifically called out in the protocol document).  It may
be sufficient to leave the issue unfixed in 9.5, given the very limited
usefulness of pq_parse_errornotice in that version.

Discussion: <A88E0006-13CB-49C6-95CC-1A77D717213C@eggerapps.at>

doc/src/sgml/libpq.sgml
doc/src/sgml/protocol.sgml
src/backend/libpq/pqmq.c
src/backend/utils/error/elog.c
src/include/postgres_ext.h
src/interfaces/libpq/fe-exec.c

index f22e3da0475804166c0c8915dc92088c7ad7d995..2f9350b10e1c4d03a6d4afd5d59de7dc87416da7 100644 (file)
@@ -2767,6 +2767,22 @@ char *PQresultErrorField(const PGresult *res, int fieldcode);
           </listitem>
          </varlistentry>
 
+         <varlistentry id="libpq-pg-diag-severity-nonlocalized">
+          <term><symbol>PG_DIAG_SEVERITY_NONLOCALIZED</></term>
+          <listitem>
+           <para>
+            The severity; the field contents are <literal>ERROR</>,
+            <literal>FATAL</>, or <literal>PANIC</> (in an error message),
+            or <literal>WARNING</>, <literal>NOTICE</>, <literal>DEBUG</>,
+            <literal>INFO</>, or <literal>LOG</> (in a notice message).
+            This is identical to the <symbol>PG_DIAG_SEVERITY</> field except
+            that the contents are never localized.  This is present only in
+            reports generated by <productname>PostgreSQL</> versions 9.6
+            and later.
+           </para>
+          </listitem>
+         </varlistentry>
+
          <varlistentry id="libpq-pg-diag-sqlstate">
           <term>
            <symbol>PG_DIAG_SQLSTATE</>
index 9c96d8fc44dce0423ceacdf71342494f2ddf1959..68b0941029973fc3fadf4d76e288bbcfa1e37fce 100644 (file)
@@ -4882,6 +4882,25 @@ message.
 </listitem>
 </varlistentry>
 
+<varlistentry>
+<term>
+<literal>V</>
+</term>
+<listitem>
+<para>
+        Severity: the field contents are
+        <literal>ERROR</>, <literal>FATAL</>, or
+        <literal>PANIC</> (in an error message), or
+        <literal>WARNING</>, <literal>NOTICE</>, <literal>DEBUG</>,
+        <literal>INFO</>, or <literal>LOG</> (in a notice message).
+        This is identical to the <literal>S</> field except
+        that the contents are never localized.  This is present only in
+        messages generated by <productname>PostgreSQL</> versions 9.6
+        and later.
+</para>
+</listitem>
+</varlistentry>
+
 <varlistentry>
 <term>
 <literal>C</>
index 921242fbc4e1dbe449e5f25ea2260442964d808e..bfe66c6c44a50ea96bae5b5b1a0a0776a6abf860 100644 (file)
@@ -237,10 +237,26 @@ pq_parse_errornotice(StringInfo msg, ErrorData *edata)
                switch (code)
                {
                        case PG_DIAG_SEVERITY:
+                               /* ignore, trusting we'll get a nonlocalized version */
+                               break;
+                       case PG_DIAG_SEVERITY_NONLOCALIZED:
                                if (strcmp(value, "DEBUG") == 0)
-                                       edata->elevel = DEBUG1;         /* or some other DEBUG level */
+                               {
+                                       /*
+                                        * We can't reconstruct the exact DEBUG level, but
+                                        * presumably it was >= client_min_messages, so select
+                                        * DEBUG1 to ensure we'll pass it on to the client.
+                                        */
+                                       edata->elevel = DEBUG1;
+                               }
                                else if (strcmp(value, "LOG") == 0)
-                                       edata->elevel = LOG;            /* can't be COMMERROR */
+                               {
+                                       /*
+                                        * It can't be LOG_SERVER_ONLY, or the worker wouldn't
+                                        * have sent it to us; so LOG is the correct value.
+                                        */
+                                       edata->elevel = LOG;
+                               }
                                else if (strcmp(value, "INFO") == 0)
                                        edata->elevel = INFO;
                                else if (strcmp(value, "NOTICE") == 0)
@@ -254,11 +270,11 @@ pq_parse_errornotice(StringInfo msg, ErrorData *edata)
                                else if (strcmp(value, "PANIC") == 0)
                                        edata->elevel = PANIC;
                                else
-                                       elog(ERROR, "unknown error severity");
+                                       elog(ERROR, "unrecognized error severity: \"%s\"", value);
                                break;
                        case PG_DIAG_SQLSTATE:
                                if (strlen(value) != 5)
-                                       elog(ERROR, "malformed sql state");
+                                       elog(ERROR, "invalid SQLSTATE: \"%s\"", value);
                                edata->sqlerrcode = MAKE_SQLSTATE(value[0], value[1], value[2],
                                                                                                  value[3], value[4]);
                                break;
@@ -308,7 +324,7 @@ pq_parse_errornotice(StringInfo msg, ErrorData *edata)
                                edata->funcname = pstrdup(value);
                                break;
                        default:
-                               elog(ERROR, "unknown error field: %d", (int) code);
+                               elog(ERROR, "unrecognized error field code: %d", (int) code);
                                break;
                }
        }
index 03c4a39761215ad3149be9d584f5b0141c89c4db..224ee7801c1b39a6d6472d179ab29c444e3dad3d 100644 (file)
@@ -2753,7 +2753,7 @@ write_csvlog(ErrorData *edata)
        appendStringInfoChar(&buf, ',');
 
        /* Error severity */
-       appendStringInfoString(&buf, error_severity(edata->elevel));
+       appendStringInfoString(&buf, _(error_severity(edata->elevel)));
        appendStringInfoChar(&buf, ',');
 
        /* SQL state code */
@@ -2870,7 +2870,7 @@ send_message_to_server_log(ErrorData *edata)
        formatted_log_time[0] = '\0';
 
        log_line_prefix(&buf, edata);
-       appendStringInfo(&buf, "%s:  ", error_severity(edata->elevel));
+       appendStringInfo(&buf, "%s:  ", _(error_severity(edata->elevel)));
 
        if (Log_error_verbosity >= PGERROR_VERBOSE)
                appendStringInfo(&buf, "%s: ", unpack_sql_state(edata->sqlerrcode));
@@ -3153,12 +3153,16 @@ send_message_to_frontend(ErrorData *edata)
        if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
        {
                /* New style with separate fields */
+               const char *sev;
                char            tbuf[12];
                int                     ssval;
                int                     i;
 
+               sev = error_severity(edata->elevel);
                pq_sendbyte(&msgbuf, PG_DIAG_SEVERITY);
-               err_sendstring(&msgbuf, error_severity(edata->elevel));
+               err_sendstring(&msgbuf, _(sev));
+               pq_sendbyte(&msgbuf, PG_DIAG_SEVERITY_NONLOCALIZED);
+               err_sendstring(&msgbuf, sev);
 
                /* unpack MAKE_SQLSTATE code */
                ssval = edata->sqlerrcode;
@@ -3277,7 +3281,7 @@ send_message_to_frontend(ErrorData *edata)
 
                initStringInfo(&buf);
 
-               appendStringInfo(&buf, "%s:  ", error_severity(edata->elevel));
+               appendStringInfo(&buf, "%s:  ", _(error_severity(edata->elevel)));
 
                if (edata->show_funcname && edata->funcname)
                        appendStringInfo(&buf, "%s: ", edata->funcname);
@@ -3587,7 +3591,10 @@ get_errno_symbol(int errnum)
 
 
 /*
- * error_severity --- get localized string representing elevel
+ * error_severity --- get string representing elevel
+ *
+ * The string is not localized here, but we mark the strings for translation
+ * so that callers can invoke _() on the result.
  */
 static const char *
 error_severity(int elevel)
@@ -3601,29 +3608,29 @@ error_severity(int elevel)
                case DEBUG3:
                case DEBUG4:
                case DEBUG5:
-                       prefix = _("DEBUG");
+                       prefix = gettext_noop("DEBUG");
                        break;
                case LOG:
                case LOG_SERVER_ONLY:
-                       prefix = _("LOG");
+                       prefix = gettext_noop("LOG");
                        break;
                case INFO:
-                       prefix = _("INFO");
+                       prefix = gettext_noop("INFO");
                        break;
                case NOTICE:
-                       prefix = _("NOTICE");
+                       prefix = gettext_noop("NOTICE");
                        break;
                case WARNING:
-                       prefix = _("WARNING");
+                       prefix = gettext_noop("WARNING");
                        break;
                case ERROR:
-                       prefix = _("ERROR");
+                       prefix = gettext_noop("ERROR");
                        break;
                case FATAL:
-                       prefix = _("FATAL");
+                       prefix = gettext_noop("FATAL");
                        break;
                case PANIC:
-                       prefix = _("PANIC");
+                       prefix = gettext_noop("PANIC");
                        break;
                default:
                        prefix = "???";
index 74c344c704094fa3b930ab39012cbf414c394c2c..ae2f0877988cf8bc0c75e9080b94da13500cafe0 100644 (file)
@@ -49,6 +49,7 @@ typedef PG_INT64_TYPE pg_int64;
  * applications.
  */
 #define PG_DIAG_SEVERITY               'S'
+#define PG_DIAG_SEVERITY_NONLOCALIZED 'V'
 #define PG_DIAG_SQLSTATE               'C'
 #define PG_DIAG_MESSAGE_PRIMARY 'M'
 #define PG_DIAG_MESSAGE_DETAIL 'D'
index d1b91c841c383519dc082cfb179cf14a4264822e..a9ba54628fd0107b44d6469d972ab878505fa181 100644 (file)
@@ -824,6 +824,7 @@ pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
         */
        pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, msgBuf);
        pqSaveMessageField(res, PG_DIAG_SEVERITY, libpq_gettext("NOTICE"));
+       pqSaveMessageField(res, PG_DIAG_SEVERITY_NONLOCALIZED, "NOTICE");
        /* XXX should provide a SQLSTATE too? */
 
        /*