diff options
author | Noah Misch <noah@leadboat.com> | 2013-06-26 11:17:33 -0400 |
---|---|---|
committer | Noah Misch <noah@leadboat.com> | 2013-06-26 11:17:33 -0400 |
commit | 5f538ad004aa00cf0881f179f0cde789aad4f47e (patch) | |
tree | 4a84500c39ff82734078f1c9169879decd163bf7 /src/backend/utils/error/elog.c | |
parent | 2c1031bd8602f749a81672015811f365a129acff (diff) | |
download | postgresql-5f538ad004aa00cf0881f179f0cde789aad4f47e.tar.gz postgresql-5f538ad004aa00cf0881f179f0cde789aad4f47e.zip |
Renovate display of non-ASCII messages on Windows.
GNU gettext selects a default encoding for the messages it emits in a
platform-specific manner; it uses the Windows ANSI code page on Windows
and follows LC_CTYPE on other platforms. This is inconvenient for
PostgreSQL server processes, so realize consistent cross-platform
behavior by calling bind_textdomain_codeset() on Windows each time we
permanently change LC_CTYPE. This primarily affects SQL_ASCII databases
and processes like the postmaster that do not attach to a database,
making their behavior consistent with PostgreSQL on non-Windows
platforms. Messages from SQL_ASCII databases use the encoding implied
by the database LC_CTYPE, and messages from non-database processes use
LC_CTYPE from the postmaster system environment. PlatformEncoding
becomes unused, so remove it.
Make write_console() prefer WriteConsoleW() to write() regardless of the
encodings in use. In this situation, write() will invariably mishandle
non-ASCII characters.
elog.c has assumed that messages conform to the database encoding.
While usually true, this does not hold for SQL_ASCII and MULE_INTERNAL.
Introduce MessageEncoding to track the actual encoding of message text.
The present consumers are Windows-specific code for converting messages
to UTF16 for use in system interfaces. This fixes the appearance in
Windows event logs and consoles of translated messages from SQL_ASCII
processes like the postmaster. Note that SQL_ASCII inherently disclaims
a strong notion of encoding, so non-ASCII byte sequences interpolated
into messages by %s may yet yield a nonsensical message. MULE_INTERNAL
has similar problems at present, albeit for a different reason: its lack
of libiconv support or a conversion to UTF8.
Consequently, one need no longer restart Windows with a different
Windows ANSI code page to broadly test backend logging under a given
language. Changing the user's locale ("Format") is enough. Several
accounts can simultaneously run postmasters under different locales, all
correctly logging localized messages to Windows event logs and consoles.
Alexander Law and Noah Misch
Diffstat (limited to 'src/backend/utils/error/elog.c')
-rw-r--r-- | src/backend/utils/error/elog.c | 47 |
1 files changed, 37 insertions, 10 deletions
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 7f03f419dea..706c01eca55 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -1814,6 +1814,22 @@ write_syslog(int level, const char *line) #ifdef WIN32 /* + * Get the PostgreSQL equivalent of the Windows ANSI code page. "ANSI" system + * interfaces (e.g. CreateFileA()) expect string arguments in this encoding. + * Every process in a given system will find the same value at all times. + */ +static int +GetACPEncoding(void) +{ + static int encoding = -2; + + if (encoding == -2) + encoding = pg_codepage_to_encoding(GetACP()); + + return encoding; +} + +/* * Write a message line to the windows event log */ static void @@ -1858,16 +1874,18 @@ write_eventlog(int level, const char *line, int len) } /* - * Convert message to UTF16 text and write it with ReportEventW, but - * fall-back into ReportEventA if conversion failed. + * If message character encoding matches the encoding expected by + * ReportEventA(), call it to avoid the hazards of conversion. Otherwise, + * try to convert the message to UTF16 and write it with ReportEventW(). + * Fall back on ReportEventA() if conversion failed. * * Also verify that we are not on our way into error recursion trouble due - * to error messages thrown deep inside pgwin32_toUTF16(). + * to error messages thrown deep inside pgwin32_message_to_UTF16(). */ - if (GetDatabaseEncoding() != GetPlatformEncoding() && - !in_error_recursion_trouble()) + if (!in_error_recursion_trouble() && + GetMessageEncoding() != GetACPEncoding()) { - utf16 = pgwin32_toUTF16(line, len, NULL); + utf16 = pgwin32_message_to_UTF16(line, len, NULL); if (utf16) { ReportEventW(evtHandle, @@ -1879,6 +1897,7 @@ write_eventlog(int level, const char *line, int len) 0, (LPCWSTR *) &utf16, NULL); + /* XXX Try ReportEventA() when ReportEventW() fails? */ pfree(utf16); return; @@ -1904,22 +1923,30 @@ write_console(const char *line, int len) #ifdef WIN32 /* - * WriteConsoleW() will fail if stdout is redirected, so just fall through + * Try to convert the message to UTF16 and write it with WriteConsoleW(). + * Fall back on write() if anything fails. + * + * In contrast to write_eventlog(), don't skip straight to write() based + * on the applicable encodings. Unlike WriteConsoleW(), write() depends + * on the suitability of the console output code page. Since we put + * stderr into binary mode in SubPostmasterMain(), write() skips the + * necessary translation anyway. + * + * WriteConsoleW() will fail if stderr is redirected, so just fall through * to writing unconverted to the logfile in this case. * * Since we palloc the structure required for conversion, also fall * through to writing unconverted if we have not yet set up * CurrentMemoryContext. */ - if (GetDatabaseEncoding() != GetPlatformEncoding() && - !in_error_recursion_trouble() && + if (!in_error_recursion_trouble() && !redirection_done && CurrentMemoryContext != NULL) { WCHAR *utf16; int utf16len; - utf16 = pgwin32_toUTF16(line, len, &utf16len); + utf16 = pgwin32_message_to_UTF16(line, len, &utf16len); if (utf16 != NULL) { HANDLE stdHandle; |