From 748771379b9463ee7454630b3cb3f3087b3157cb Mon Sep 17 00:00:00 2001
From: Magnus Hagander <magnus@hagander.net>
Date: Sat, 17 Oct 2009 00:24:51 +0000
Subject: [PATCH] Write to the Windows eventlog in UTF16, converting the
 message encoding as necessary.

Itagaki Takahiro with some changes from me
---
 src/backend/utils/error/elog.c  | 111 ++++++++++++++++----
 src/backend/utils/mb/encnames.c | 175 +++++++++-----------------------
 src/backend/utils/mb/mbutils.c  |  66 +++++++++++-
 src/include/mb/pg_wchar.h       |  10 +-
 4 files changed, 215 insertions(+), 147 deletions(-)

diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 06a78f2d701..5f30e783756 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -42,7 +42,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.217 2009/07/03 19:14:25 petere Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.218 2009/10/17 00:24:50 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -111,8 +111,10 @@ static int	syslog_facility = LOG_LOCAL0;
 static void write_syslog(int level, const char *line);
 #endif
 
+static void write_console(const char *line, int len);
+
 #ifdef WIN32
-static void write_eventlog(int level, const char *line);
+static void write_eventlog(int level, const char *line, int len);
 #endif
 
 /* We provide a small stack of ErrorData records for re-entrant cases */
@@ -1567,10 +1569,11 @@ write_syslog(int level, const char *line)
  * Write a message line to the windows event log
  */
 static void
-write_eventlog(int level, const char *line)
+write_eventlog(int level, const char *line, int len)
 {
-	int			eventlevel = EVENTLOG_ERROR_TYPE;
-	static HANDLE evtHandle = INVALID_HANDLE_VALUE;
+	WCHAR		   *utf16;
+	int				eventlevel = EVENTLOG_ERROR_TYPE;
+	static HANDLE	evtHandle = INVALID_HANDLE_VALUE;
 
 	if (evtHandle == INVALID_HANDLE_VALUE)
 	{
@@ -1606,8 +1609,34 @@ write_eventlog(int level, const char *line)
 			break;
 	}
 
-
-	ReportEvent(evtHandle,
+	/*
+	 * Convert message to UTF16 text and write it with ReportEventW,
+	 * but fall-back into 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().
+	 */
+	if (GetDatabaseEncoding() != GetPlatformEncoding() &&
+		!in_error_recursion_trouble())
+	{
+		utf16 = pgwin32_toUTF16(line, len, NULL);
+		if (utf16)
+		{
+			ReportEventW(evtHandle,
+					eventlevel,
+					0,
+					0,				/* All events are Id 0 */
+					NULL,
+					1,
+					0,
+					(LPCWSTR *) &utf16,
+					NULL);
+
+			pfree(utf16);
+			return;
+		}
+	}
+	ReportEventA(evtHandle,
 				eventlevel,
 				0,
 				0,				/* All events are Id 0 */
@@ -1619,6 +1648,52 @@ write_eventlog(int level, const char *line)
 }
 #endif   /* WIN32 */
 
+static void
+write_console(const char *line, int len)
+{
+#ifdef WIN32
+	/*
+	 * WriteConsoleW() will fail of stdout is redirected, so just fall through
+	 * to writing unconverted to the logfile in this case.
+	 */
+	if (GetDatabaseEncoding() != GetPlatformEncoding() &&
+		!in_error_recursion_trouble() &&
+		!redirection_done)
+	{
+		WCHAR	   *utf16;
+		int			utf16len;
+
+		utf16 = pgwin32_toUTF16(line, len, &utf16len);
+		if (utf16 != NULL)
+		{
+			HANDLE		stdHandle;
+			DWORD		written;
+
+			stdHandle = GetStdHandle(STD_ERROR_HANDLE);
+			if (WriteConsoleW(stdHandle, utf16, utf16len, &written, NULL))
+			{
+				pfree(utf16);
+				return;
+			}
+
+			/*
+			 * In case WriteConsoleW() failed, fall back to writing the message
+			 * unconverted.
+			 */
+			pfree(utf16);
+		}
+	}
+#else
+	/*
+	 * Conversion on non-win32 platform is not implemented yet.
+	 * It requires non-throw version of pg_do_encoding_conversion(),
+	 * that converts unconvertable characters to '?' without errors.
+	 */
+#endif
+
+	write(fileno(stderr), line, len);
+}
+
 /*
  * setup formatted_log_time, for consistent times between CSV and regular logs
  */
@@ -2206,7 +2281,7 @@ send_message_to_server_log(ErrorData *edata)
 	/* Write to eventlog, if enabled */
 	if (Log_destination & LOG_DESTINATION_EVENTLOG)
 	{
-		write_eventlog(edata->elevel, buf.data);
+		write_eventlog(edata->elevel, buf.data, buf.len);
 	}
 #endif   /* WIN32 */
 
@@ -2230,10 +2305,10 @@ send_message_to_server_log(ErrorData *edata)
 		 * because that's really a pipe to the syslogger process.
 		 */
 		else if (pgwin32_is_service())
-			write_eventlog(edata->elevel, buf.data);
+			write_eventlog(edata->elevel, buf.data, buf.len);
 #endif
 		else
-			write(fileno(stderr), buf.data, buf.len);
+			write_console(buf.data, buf.len);
 	}
 
 	/* If in the syslogger process, try to write messages direct to file */
@@ -2256,12 +2331,12 @@ send_message_to_server_log(ErrorData *edata)
 		{
 			const char *msg = _("Not safe to send CSV data\n");
 
-			write(fileno(stderr), msg, strlen(msg));
+			write_console(msg, strlen(msg));
 			if (!(Log_destination & LOG_DESTINATION_STDERR) &&
 				whereToSendOutput != DestDebug)
 			{
 				/* write message to stderr unless we just sent it above */
-				write(fileno(stderr), buf.data, buf.len);
+				write_console(buf.data, buf.len);
 			}
 			pfree(buf.data);
 		}
@@ -2642,6 +2717,9 @@ void
 write_stderr(const char *fmt,...)
 {
 	va_list		ap;
+#ifdef WIN32
+	char		errbuf[2048];		/* Arbitrary size? */
+#endif
 
 	fmt = _(fmt);
 
@@ -2651,6 +2729,7 @@ write_stderr(const char *fmt,...)
 	vfprintf(stderr, fmt, ap);
 	fflush(stderr);
 #else
+	vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
 
 	/*
 	 * On Win32, we print to stderr if running on a console, or write to
@@ -2658,16 +2737,12 @@ write_stderr(const char *fmt,...)
 	 */
 	if (pgwin32_is_service())	/* Running as a service */
 	{
-		char		errbuf[2048];		/* Arbitrary size? */
-
-		vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
-
-		write_eventlog(ERROR, errbuf);
+		write_eventlog(ERROR, errbuf, strlen(errbuf));
 	}
 	else
 	{
 		/* Not running as service, write to stderr */
-		vfprintf(stderr, fmt, ap);
+		write_console(errbuf, strlen(errbuf));
 		fflush(stderr);
 	}
 #endif
diff --git a/src/backend/utils/mb/encnames.c b/src/backend/utils/mb/encnames.c
index def8c84bad1..04df4cb5af6 100644
--- a/src/backend/utils/mb/encnames.c
+++ b/src/backend/utils/mb/encnames.c
@@ -2,7 +2,7 @@
  * Encoding names and routines for work with it. All
  * in this file is shared bedween FE and BE.
  *
- * $PostgreSQL: pgsql/src/backend/utils/mb/encnames.c,v 1.39 2009/04/24 08:43:50 mha Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/mb/encnames.c,v 1.40 2009/10/17 00:24:51 mha Exp $
  */
 #ifdef FRONTEND
 #include "postgres_fe.h"
@@ -300,134 +300,55 @@ sizeof(pg_encname_tbl) / sizeof(pg_encname_tbl[0]) - 1;
  * XXX must be sorted by the same order as enum pg_enc (in mb/pg_wchar.h)
  * ----------
  */
+#ifndef WIN32
+#define DEF_ENC2NAME(name, codepage) { #name, PG_##name }
+#else
+#define DEF_ENC2NAME(name, codepage) { #name, PG_##name, codepage }
+#endif
 pg_enc2name pg_enc2name_tbl[] =
 {
-	{
-		"SQL_ASCII", PG_SQL_ASCII
-	},
-	{
-		"EUC_JP", PG_EUC_JP
-	},
-	{
-		"EUC_CN", PG_EUC_CN
-	},
-	{
-		"EUC_KR", PG_EUC_KR
-	},
-	{
-		"EUC_TW", PG_EUC_TW
-	},
-	{
-		"EUC_JIS_2004", PG_EUC_JIS_2004
-	},
-	{
-		"UTF8", PG_UTF8
-	},
-	{
-		"MULE_INTERNAL", PG_MULE_INTERNAL
-	},
-	{
-		"LATIN1", PG_LATIN1
-	},
-	{
-		"LATIN2", PG_LATIN2
-	},
-	{
-		"LATIN3", PG_LATIN3
-	},
-	{
-		"LATIN4", PG_LATIN4
-	},
-	{
-		"LATIN5", PG_LATIN5
-	},
-	{
-		"LATIN6", PG_LATIN6
-	},
-	{
-		"LATIN7", PG_LATIN7
-	},
-	{
-		"LATIN8", PG_LATIN8
-	},
-	{
-		"LATIN9", PG_LATIN9
-	},
-	{
-		"LATIN10", PG_LATIN10
-	},
-	{
-		"WIN1256", PG_WIN1256
-	},
-	{
-		"WIN1258", PG_WIN1258
-	},
-	{
-		"WIN866", PG_WIN866
-	},
-	{
-		"WIN874", PG_WIN874
-	},
-	{
-		"KOI8R", PG_KOI8R
-	},
-	{
-		"WIN1251", PG_WIN1251
-	},
-	{
-		"WIN1252", PG_WIN1252
-	},
-	{
-		"ISO_8859_5", PG_ISO_8859_5
-	},
-	{
-		"ISO_8859_6", PG_ISO_8859_6
-	},
-	{
-		"ISO_8859_7", PG_ISO_8859_7
-	},
-	{
-		"ISO_8859_8", PG_ISO_8859_8
-	},
-	{
-		"WIN1250", PG_WIN1250
-	},
-	{
-		"WIN1253", PG_WIN1253
-	},
-	{
-		"WIN1254", PG_WIN1254
-	},
-	{
-		"WIN1255", PG_WIN1255
-	},
-	{
-		"WIN1257", PG_WIN1257
-	},
-	{
-		"KOI8U", PG_KOI8U
-	},
-	{
-		"SJIS", PG_SJIS
-	},
-	{
-		"BIG5", PG_BIG5
-	},
-	{
-		"GBK", PG_GBK
-	},
-	{
-		"UHC", PG_UHC
-	},
-	{
-		"GB18030", PG_GB18030
-	},
-	{
-		"JOHAB", PG_JOHAB
-	},
-	{
-		"SHIFT_JIS_2004", PG_SHIFT_JIS_2004
-	}
+	DEF_ENC2NAME(SQL_ASCII, 0),
+	DEF_ENC2NAME(EUC_JP, 20932),
+	DEF_ENC2NAME(EUC_CN, 20936),
+	DEF_ENC2NAME(EUC_KR, 51949),
+	DEF_ENC2NAME(EUC_TW, 0),
+	DEF_ENC2NAME(EUC_JIS_2004, 20932),
+	DEF_ENC2NAME(UTF8, 65001),
+	DEF_ENC2NAME(MULE_INTERNAL, 0),
+	DEF_ENC2NAME(LATIN1, 28591),
+	DEF_ENC2NAME(LATIN2, 28592),
+	DEF_ENC2NAME(LATIN3, 28593),
+	DEF_ENC2NAME(LATIN4, 28594),
+	DEF_ENC2NAME(LATIN5, 28599),
+	DEF_ENC2NAME(LATIN6, 0),
+	DEF_ENC2NAME(LATIN7, 0),
+	DEF_ENC2NAME(LATIN8, 0),
+	DEF_ENC2NAME(LATIN9, 28605),
+	DEF_ENC2NAME(LATIN10, 0),
+	DEF_ENC2NAME(WIN1256, 1256),
+	DEF_ENC2NAME(WIN1258, 1258),
+	DEF_ENC2NAME(WIN866, 866),
+	DEF_ENC2NAME(WIN874, 874),
+	DEF_ENC2NAME(KOI8R, 20866),
+	DEF_ENC2NAME(WIN1251, 1251),
+	DEF_ENC2NAME(WIN1252, 1252),
+	DEF_ENC2NAME(ISO_8859_5, 28595),
+	DEF_ENC2NAME(ISO_8859_6, 28596),
+	DEF_ENC2NAME(ISO_8859_7, 28597),
+	DEF_ENC2NAME(ISO_8859_8, 28598),
+	DEF_ENC2NAME(WIN1250, 1250),
+	DEF_ENC2NAME(WIN1253, 1253),
+	DEF_ENC2NAME(WIN1254, 1254),
+	DEF_ENC2NAME(WIN1255, 1255),
+	DEF_ENC2NAME(WIN1257, 1257),
+	DEF_ENC2NAME(KOI8U, 21866),
+	DEF_ENC2NAME(SJIS, 932),
+	DEF_ENC2NAME(BIG5, 950),
+	DEF_ENC2NAME(GBK, 936),
+	DEF_ENC2NAME(UHC, 0),
+	DEF_ENC2NAME(GB18030, 54936),
+	DEF_ENC2NAME(JOHAB, 0),
+	DEF_ENC2NAME(SHIFT_JIS_2004, 932)
 };
 
 /* ----------
diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c
index cd3ebb219e2..c6e32ef6643 100644
--- a/src/backend/utils/mb/mbutils.c
+++ b/src/backend/utils/mb/mbutils.c
@@ -4,7 +4,7 @@
  *
  * Tatsuo Ishii
  *
- * $PostgreSQL: pgsql/src/backend/utils/mb/mbutils.c,v 1.89 2009/07/07 19:28:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/mb/mbutils.c,v 1.90 2009/10/17 00:24:51 mha Exp $
  */
 #include "postgres.h"
 
@@ -58,6 +58,7 @@ static FmgrInfo *ToClientConvProc = NULL;
  */
 static pg_enc2name *ClientEncoding = &pg_enc2name_tbl[PG_SQL_ASCII];
 static pg_enc2name *DatabaseEncoding = &pg_enc2name_tbl[PG_SQL_ASCII];
+static pg_enc2name *PlatformEncoding = NULL;
 
 /*
  * During backend startup we can't set client encoding because we (a)
@@ -978,3 +979,66 @@ pg_client_encoding(PG_FUNCTION_ARGS)
 	Assert(ClientEncoding);
 	return DirectFunctionCall1(namein, CStringGetDatum(ClientEncoding->name));
 }
+
+int
+GetPlatformEncoding(void)
+{
+	if (PlatformEncoding == NULL)
+		PlatformEncoding = &pg_enc2name_tbl[pg_get_encoding_from_locale("")];
+	return PlatformEncoding->encoding;
+}
+
+#ifdef WIN32
+
+/*
+ * Result is palloc'ed null-terminated utf16 string. The character length
+ * is also passed to utf16len if not null. Returns NULL iff failed.
+ */
+WCHAR *
+pgwin32_toUTF16(const char *str, int len, int *utf16len)
+{
+	WCHAR	   *utf16;
+	int			dstlen;
+	UINT		codepage;
+
+	codepage = pg_enc2name_tbl[GetDatabaseEncoding()].codepage;
+
+	/*
+	 * Use MultiByteToWideChar directly if there is a corresponding codepage,
+	 * or double conversion through UTF8 if not.
+	 */
+	if (codepage != 0)
+	{
+		utf16 = (WCHAR *) palloc(sizeof(WCHAR) * (len + 1));
+		dstlen = MultiByteToWideChar(codepage, 0, str, len, utf16, len);
+		utf16[dstlen] = L'\0';
+	}
+	else
+	{
+		char	   *utf8;
+
+		utf8 = (char *) pg_do_encoding_conversion((unsigned char *) str,
+										len, GetDatabaseEncoding(), PG_UTF8);
+		if (utf8 != str)
+			len = strlen(utf8);
+
+		utf16 = (WCHAR *) palloc(sizeof(WCHAR) * (len + 1));
+		dstlen = MultiByteToWideChar(CP_UTF8, 0, utf8, len, utf16, len);
+		utf16[dstlen] = L'\0';
+
+		if (utf8 != str)
+			pfree(utf8);
+	}
+
+	if (dstlen == 0 && len > 0)
+	{
+		pfree(utf16);
+		return NULL;	/* error */
+	}
+
+	if (utf16len)
+		*utf16len = len;
+	return utf16;
+}
+
+#endif
diff --git a/src/include/mb/pg_wchar.h b/src/include/mb/pg_wchar.h
index e9faff00130..353d612f758 100644
--- a/src/include/mb/pg_wchar.h
+++ b/src/include/mb/pg_wchar.h
@@ -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/mb/pg_wchar.h,v 1.91 2009/06/11 14:49:11 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/mb/pg_wchar.h,v 1.92 2009/10/17 00:24:51 mha Exp $
  *
  *	NOTES
  *		This is used both by the backend and by libpq, but should not be
@@ -257,6 +257,9 @@ typedef struct pg_enc2name
 {
 	char	   *name;
 	pg_enc		encoding;
+#ifdef WIN32
+	unsigned	codepage;	/* codepage for WIN32 */
+#endif
 } pg_enc2name;
 
 extern pg_enc2name pg_enc2name_tbl[];
@@ -402,6 +405,7 @@ extern const char *pg_get_client_encoding_name(void);
 extern void SetDatabaseEncoding(int encoding);
 extern int	GetDatabaseEncoding(void);
 extern const char *GetDatabaseEncodingName(void);
+extern int	GetPlatformEncoding(void);
 extern void pg_bind_textdomain_codeset(const char *domainname);
 
 extern int	pg_valid_client_encoding(const char *name);
@@ -458,4 +462,8 @@ extern void mic2latin_with_table(const unsigned char *mic, unsigned char *p,
 
 extern bool pg_utf8_islegal(const unsigned char *source, int length);
 
+#ifdef WIN32
+extern WCHAR *pgwin32_toUTF16(const char *str, int len, int *utf16len);
+#endif
+
 #endif   /* PG_WCHAR_H */
-- 
GitLab