>From 85e208db45cd675d69aad44a95f8c79854682f5f Mon Sep 17 00:00:00 2001
From: Craig Ringer <craig@2ndquadrant.com>
Date: Wed, 25 Jun 2014 15:17:00 +0800
Subject: [PATCH] Fix driver name mismatch between 32-bit ODBC app and 64-bit
 MSDTC host

If the 32-bit driver runs on a 64-bit host within a 32-bit app
running under SysWow64 then XA transactions will fail to recover.

That's because recovery is done by pgxalib.dll which runs within
MSDTC.exe - a 64-bit application. It connects to a DSN supplied
by psqlODBC in EnlistInDtc_1pipe during DTC enlistment by the
XA resource manager. This DSN uses the driver name of the client's
ODBC driver - which is fine so long as the client and MSDTC
architectures match. If they don't then MSDTC will fail to connect
to Pg, so it'll never resolve transactions.

Traces of MSDTC will show RM_COMMIT_DELIVERY_FAILED_DUE_TO_CONNECTION_DOWN
events.

Traces of pgxalib.dll will show xa_recover(..) being entered, followed by
"SQLDriverConnect return=-1".

To fix this, we detect if we're running under SysWow64 by examining the
PROCESSOR_ARCHITEW6432 environment variable and supply the 64-bit driver name
if so.

See mailing list thread:

  http://www.postgresql.org/message-id/53A45B59.70303@2ndquadrant.com

for details.

Craig Ringer
---
 connection.c | 46 ++++++++++++++++++++++++++++++++++++++++++++--
 psqlodbc.h   |  8 ++++++--
 2 files changed, 50 insertions(+), 4 deletions(-)

diff --git a/connection.c b/connection.c
index 794ddde..3aeb71a 100644
--- a/connection.c
+++ b/connection.c
@@ -4362,10 +4362,52 @@ const char *CurrCatString(const ConnectionClass *conn)
 DLL_DECLARE void PgDtc_create_connect_string(void *self, char *connstr, int strsize)
 {
 	ConnectionClass	*conn = (ConnectionClass *) self;
-
+	SYSTEM_INFO si;
 	ConnInfo *ci = &(conn->connInfo);
+	const char *drivername = ci->drivername;
+
+#if defined(_WIN32) && !defined(_WIN64)
+	/*
+	 * If this is an x86 driver running on an x64 host then the driver name
+	 * passed to MSDTC must be the (x64) driver but the client app will be
+	 * using the 32-bit driver name. So MSDTC.exe will fail to find the driver
+	 * and we'll fail to recover XA transactions.
+	 *
+	 * IsWow64Process(...) would be the ideal function for this, but is only
+	 * available on Windows 6+ (Vista/2k8). We'd use GetNativeSystemInfo, which
+	 * is supported on XP and 2k3, instead, but that won't link with older
+	 * SDKs.
+	 *
+	 * It's impler to just test the PROCESSOR_ARCHITEW6432 environment
+	 * variable.
+	 *
+	 * See http://www.postgresql.org/message-id/53A45B59.70303@2ndquadrant.com
+	 * for details on this issue.
+	 */
+	const char * const procenv = getenv("PROCESSOR_ARCHITEW6432");
+	if (procenv != NULL && strcmp(procenv, "AMD64") == 0)
+	{
+		/*
+		 * We're a 32-bit binary running under SysWow64 on a 64-bit host and need
+		 * to pass a different driver name.
+		 *
+		 * To avoid playing memory management games, just return a different
+		 * string constant depending on the unicode-ness of the driver.
+		 *
+		 * It probably doesn't matter whether we use the Unicode or ANSI driver
+		 * for the XA transaction manager, but pick the same as the client driver
+		 * to keep things as similar as possible.
+		 */
+#ifdef UNICODE_SUPPORT
+		drivername = DBMS_NAME_UNICODE"(x64)";
+#else
+		drivername = DBMS_NAME_ANSI"(x64)";
+#endif
+	}
+#endif // _WIN32 &&  !_WIN64
+
 	snprintf(connstr, strsize, "DRIVER={%s};SERVER=%s;PORT=%s;DATABASE=%s;UID=%s;PWD=%s;" ABBR_SSLMODE "=%s",
-		ci->drivername, ci->server, ci->port, ci->database, ci->username, SAFE_NAME(ci->password), ci->sslmode);
+		drivername, ci->server, ci->port, ci->database, ci->username, SAFE_NAME(ci->password), ci->sslmode);
 }
 
 DLL_DECLARE void PgDtc_set_async(void *self, void *async)
diff --git a/psqlodbc.h b/psqlodbc.h
index c0c895f..05dcc9e 100644
--- a/psqlodbc.h
+++ b/psqlodbc.h
@@ -249,6 +249,10 @@ typedef double SDOUBLE;
 /* Driver stuff */
 
 #define DRIVERNAME				"PostgreSQL ODBC"
+
+#define DBMS_NAME_UNICODE		"PostgreSQL Unicode"
+#define DBMS_NAME_ANSI			"PostgreSQL ANSI"
+
 #if (ODBCVER >= 0x0300)
 #if (ODBCVER >= 0x0351)
 #define DRIVER_ODBC_VER				"03.51"
@@ -257,9 +261,9 @@ typedef double SDOUBLE;
 #endif /* ODBCVER 0x0351 */
 #ifndef DBMS_NAME
 #ifdef	UNICODE_SUPPORT
-#define DBMS_NAME				"PostgreSQL Unicode"
+#define DBMS_NAME				DBMS_NAME_UNICODE
 #else
-#define DBMS_NAME				"PostgreSQL ANSI"
+#define DBMS_NAME				DBMS_NAME_ANSI
 #endif /* UNICODE_SUPPORT */
 #endif /* DBMS_NAME */
 #else
-- 
1.9.0

