5 * This module implements XA like routines
6 * invoked from MSDTC process.
8 * xa_open(), xa_close(), xa_commit(),
9 * xa_rollback() and xa_recover()
10 * are really invoked AFAIC.
15 /*#define _SLEEP_FOR_TEST_*/
29 EXTERN_C
static void mylog(const char *fmt
,...);
41 XAConnection(LPCTSTR str
) : connstr(str
), xaconn(NULL
), pos(-1) {}
43 HDBC
ActivateConnection(void);
44 void SetPos(int spos
) {pos
= spos
;}
45 HDBC
GetConnection(void) const {return xaconn
;}
46 vector
<string
> &GetResultVec(void) {return qvec
;}
47 int GetPos(void) {return pos
;}
48 const string
&GetConnstr(void) {return connstr
;}
51 static class INIT_CRIT
56 CRITICAL_SECTION map_cs
;
57 CRITICAL_SECTION mylog_cs
;
58 map
<int, XAConnection
> xatab
;
62 INIT_CRIT() : LOGFP(NULL
), env(NULL
)
64 InitializeCriticalSection(&map_cs
);
65 InitializeCriticalSection(&mylog_cs
);
70 // mylog("Leaving INIT_CRIT\n");
77 DeleteCriticalSection(&mylog_cs
);
78 DeleteCriticalSection(&map_cs
);
90 DeleteCriticalSection(&mylog_cs
);
91 DeleteCriticalSection(&map_cs
);
99 SQLFreeHandle(SQL_HANDLE_ENV
, env
);
104 #define MLOCK_ACQUIRE EnterCriticalSection(&init_crit.map_cs)
105 #define MLOCK_RELEASE LeaveCriticalSection(&init_crit.map_cs)
107 static int dtclog
= 0;
109 XAConnection::~XAConnection()
113 SQLFreeHandle(SQL_HANDLE_DBC
, xaconn
);
116 HDBC
XAConnection::ActivateConnection(void)
123 ret
= SQLAllocHandle(SQL_HANDLE_ENV
, SQL_NULL_HANDLE
, &init_crit
.env
);
124 if (SQL_SUCCESS
!= ret
&& SQL_SUCCESS_WITH_INFO
!= ret
)
130 ret
= SQLSetEnvAttr(init_crit
.env
, SQL_ATTR_ODBC_VERSION
, (PTR
) SQL_OV_ODBC3
, 0);
131 ret
= SQLAllocHandle(SQL_HANDLE_DBC
, init_crit
.env
, &xaconn
);
132 if (SQL_SUCCESS
== ret
|| SQL_SUCCESS_WITH_INFO
)
134 string cstr
= connstr
;
140 ret
= SQLDriverConnect(xaconn
, NULL
,
141 (SQLCHAR
*) cstr
.c_str(), SQL_NTS
, NULL
, SQL_NULL_DATA
, NULL
, SQL_DRIVER_COMPLETE
);
142 if (SQL_SUCCESS
!= ret
&& SQL_SUCCESS_WITH_INFO
!= ret
)
144 mylog("SQLDriverConnect return=%d\n", ret
);
145 SQLFreeHandle(SQL_HANDLE_DBC
, xaconn
);
158 #define DIRSEPARATOR "\\"
159 #define PG_BINARY_A "ab"
160 #define MYLOGDIR "c:"
161 #define MYLOGFILE "mylog_"
164 generate_filename(const char *dirname
, const char *prefix
, char *filename
)
169 if (dirname
== 0 || filename
== 0)
172 strcpy(filename
, dirname
);
173 strcat(filename
, DIRSEPARATOR
);
175 strcat(filename
, prefix
);
176 sprintf(filename
, "%s%u%s", filename
, pid
, ".log");
180 static void FreeEnv()
185 #define DTCLOGDIR "c:\\pgdtclog"
188 static const char * const DBMSNAME
= "PostgreSQL";
189 static const char * const KEY_NAME
= "MsdtcLog";
190 static const char * const ODBCINST_INI
= "ODBCINST.INI";
191 INT_PTR FAR WINAPI
GetMsdtclog()
195 SQLGetPrivateProfileString(DBMSNAME
, KEY_NAME
, "", temp
, sizeof(temp
), ODBCINST_INI
);
199 INT_PTR FAR WINAPI
SetMsdtclog(int dtclog
)
203 sprintf(temp
, "%d", dtclog
);
204 SQLWritePrivateProfileString(DBMSNAME
, KEY_NAME
, temp
, ODBCINST_INI
);
211 return (0 != dtclog
);
215 mylog(const char *fmt
,...)
219 static BOOL init
= TRUE
;
220 FILE *logfp
= init_crit
.LOGFP
;
224 EnterCriticalSection(&init_crit
.mylog_cs
);
231 generate_filename(MYLOGDIR
, MYLOGFILE
, filebuf
);
232 logfp
= fopen(filebuf
, PG_BINARY_A
);
236 generate_filename(DTCLOGDIR
, MYLOGFILE
, filebuf
);
237 logfp
= fopen(filebuf
, PG_BINARY_A
);
241 if (0 == _mkdir(DTCLOGDIR
))
242 logfp
= fopen(filebuf
, PG_BINARY_A
);
249 init_crit
.LOGFP
= logfp
;
259 strcpy(ctim
, ctime(&ntime
));
260 ctim
[strlen(ctim
) - 1] = '\0';
261 fprintf(logfp
, "[%d.%d(%s)]", GetCurrentProcessId(), GetCurrentThreadId(), ctim
);
262 vfprintf(logfp
, fmt
, args
);
265 LeaveCriticalSection(&init_crit
.mylog_cs
);
267 static int initialize_globals(void)
277 static void XatabClear(void);
278 static void finalize_globals(void)
280 /* my(q)log is unavailable from here */
281 mylog("DETACHING PROCESS\n");
282 init_crit
.finalize();
285 HINSTANCE s_hModule
; /* Saved module handle. */
286 /* This is where the Driver Manager attaches to this Driver */
288 DllMain(HANDLE hInst
, ULONG ul_reason_for_call
, LPVOID lpReserved
)
290 WORD wVersionRequested
;
293 switch (ul_reason_for_call
)
295 case DLL_PROCESS_ATTACH
:
296 s_hModule
= (HINSTANCE
) hInst
; /* Save for dialog boxes */
298 /* Load the WinSock Library */
299 wVersionRequested
= MAKEWORD(1, 1);
301 if (WSAStartup(wVersionRequested
, &wsaData
))
304 /* Verify that this is the minimum version of WinSock */
305 if (LOBYTE(wsaData
.wVersion
) != 1 ||
306 HIBYTE(wsaData
.wVersion
) != 1)
311 initialize_globals();
314 case DLL_THREAD_ATTACH
:
317 case DLL_PROCESS_DETACH
:
322 case DLL_THREAD_DETACH
:
331 } /* end of EXTERN_C */
333 #endif /* _BUILD_DLL_ */
335 static void XatabClear(void)
337 init_crit
.xatab
.clear();
340 static const char *XidToText(const XID
&xid
, char *rtext
)
342 int glen
= xid
.gtrid_length
, blen
= xid
.bqual_length
;
345 for (i
= 0, j
= 0; i
< glen
; i
++, j
+= 2)
346 sprintf(rtext
+ j
, "%02x", (unsigned char) xid
.data
[i
]);
347 strcat(rtext
, "-"); j
++;
348 for (; i
< glen
+ blen
; i
++, j
+= 2)
349 sprintf(rtext
+ j
, "%02x", (unsigned char) xid
.data
[i
]);
354 pg_hex2bin(const UCHAR
*src
, UCHAR
*dst
, int length
)
362 for (i
= 0, src_wk
= src
, dst_wk
= dst
; i
< length
; i
++, src_wk
++)
367 if (chr
>= 'a' && chr
<= 'f')
368 val
= chr
- 'a' + 10;
369 else if (chr
>= 'A' && chr
<= 'F')
370 val
= chr
- 'A' + 10;
374 *dst_wk
= (val
<< 4);
385 static int TextToXid(XID
&xid
, const char *rtext
)
387 int slen
, glen
, blen
;
390 slen
= strlen(rtext
);
391 sptr
= (char *)strchr(rtext
, '-');
394 glen
= (int) (sptr
- rtext
);
395 blen
= slen
- glen
- 1;
402 xid
.gtrid_length
= glen
/ 2;
403 xid
.bqual_length
= blen
/ 2;
404 pg_hex2bin((const UCHAR
*) rtext
, (UCHAR
*) &xid
.data
[0], glen
);
405 pg_hex2bin((const UCHAR
*) sptr
+ 1, (UCHAR
*) &xid
.data
[glen
/ 2], blen
);
406 return (glen
+ blen
) / 2;
410 EXTERN_C
static int __cdecl
xa_open(char *xa_info
, int rmid
, long flags
)
412 mylog("xa_open %s rmid=%d flags=%ld\n", xa_info
, rmid
, flags
);
415 init_crit
.xatab
.insert(pair
<int, XAConnection
>(rmid
, XAConnection(xa_info
)));
419 EXTERN_C
static int __cdecl
xa_close(char *xa_info
, int rmid
, long flags
)
421 mylog("xa_close rmid=%d flags=%ld\n", rmid
, flags
);
423 init_crit
.xatab
.erase(rmid
);
424 if (init_crit
.xatab
.size() == 0)
432 // Dummy implementation (not called from MSDTC).
434 EXTERN_C
static int __cdecl
xa_start(XID
*xid
, int rmid
, long flags
)
438 XidToText(*xid
, pgxid
);
439 mylog("xa_start %s rmid=%d flags=%ld\n", pgxid
, rmid
, flags
);
440 init_crit
.xatab
.find(rmid
)->second
.ActivateConnection();
444 // Dummy implementation (not called from MSDTC).
446 EXTERN_C
static int __cdecl
xa_end(XID
*xid
, int rmid
, long flags
)
450 XidToText(*xid
, pgxid
);
451 mylog("xa_end %s rmid=%d flags=%ld\n", pgxid
, rmid
, flags
);
455 EXTERN_C
static int __cdecl
xa_rollback(XID
*xid
, int rmid
, long flags
)
457 int rmcode
= XAER_RMERR
;
460 XidToText(*xid
, pgxid
);
461 mylog("xa_rollback %s rmid=%d flags=%ld\n", pgxid
, rmid
, flags
);
462 map
<int, XAConnection
>::iterator p
;
463 p
= init_crit
.xatab
.find(rmid
);
464 if (p
!= init_crit
.xatab
.end())
466 HDBC conn
= p
->second
.ActivateConnection();
469 SQLCHAR cmdmsg
[512], sqlstate
[8];
473 ret
= SQLAllocHandle(SQL_HANDLE_STMT
, conn
, &stmt
);
474 if (SQL_SUCCESS
!= ret
&& SQL_SUCCESS_WITH_INFO
!= ret
)
476 mylog("Statement allocation error\n");
479 _snprintf((char *) cmdmsg
, sizeof(cmdmsg
), "ROLLBACK PREPARED '%s'", pgxid
);
480 ret
= SQLExecDirect(stmt
, (SQLCHAR
*) cmdmsg
, SQL_NTS
);
484 case SQL_SUCCESS_WITH_INFO
:
488 SQLGetDiagRec(SQL_HANDLE_STMT
, stmt
,
489 1, sqlstate
, NULL
, cmdmsg
,
490 sizeof(cmdmsg
), NULL
);
491 mylog("xa_commit error %s '%s'\n", sqlstate
, cmdmsg
);
492 if (_stricmp((char *) sqlstate
, "42704") == 0)
496 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
502 // Dummy implementation (not called from MSDTC).
503 // ANway it's almost impossible to implement this routine properly.
505 EXTERN_C
static int __cdecl
xa_prepare(XID
*xid
, int rmid
, long flags
)
509 XidToText(*xid
, pgxid
);
510 mylog("xa_prepare %s rmid=%d\n", pgxid
, rmid
);
511 #ifdef _SLEEP_FOR_TEST_
513 #endif /* _SLEEP_FOR_TEST_ */
514 map
<int, XAConnection
>::iterator p
;
515 p
= init_crit
.xatab
.find(rmid
);
516 if (p
!= init_crit
.xatab
.end())
518 HDBC conn
= p
->second
.GetConnection();
525 EXTERN_C
static int __cdecl
xa_commit(XID
*xid
, int rmid
, long flags
)
527 int rmcode
= XAER_RMERR
;
530 XidToText(*xid
, pgxid
);
531 mylog("xa_commit %s rmid=%d flags=%ld\n", pgxid
, rmid
, flags
);
532 #ifdef _SLEEP_FOR_TEST_
534 #endif /* _SLEEP_FOR_TEST_ */
535 map
<int, XAConnection
>::iterator p
;
536 p
= init_crit
.xatab
.find(rmid
);
537 if (p
!= init_crit
.xatab
.end())
539 HDBC conn
= p
->second
.ActivateConnection();
542 SQLCHAR cmdmsg
[512], sqlstate
[8];
546 SQLAllocHandle(SQL_HANDLE_STMT
, conn
, &stmt
);
547 _snprintf((char *) cmdmsg
, sizeof(cmdmsg
), "COMMIT PREPARED '%s'", pgxid
);
548 ret
= SQLExecDirect(stmt
, (SQLCHAR
*) cmdmsg
, SQL_NTS
);
552 case SQL_SUCCESS_WITH_INFO
:
556 SQLGetDiagRec(SQL_HANDLE_STMT
, stmt
,
557 1, sqlstate
, NULL
, cmdmsg
,
558 sizeof(cmdmsg
), NULL
);
559 if (_stricmp((char *) sqlstate
, "42704") == 0)
563 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
568 EXTERN_C
static int __cdecl
xa_recover(XID
*xids
, long count
, int rmid
, long flags
)
570 int rmcode
= XAER_RMERR
, rcount
;
572 mylog("xa_recover rmid=%d count=%d flags=%ld\n", rmid
, count
, flags
);
573 map
<int, XAConnection
>::iterator p
;
574 p
= init_crit
.xatab
.find(rmid
);
575 if (p
== init_crit
.xatab
.end())
577 HDBC conn
= p
->second
.ActivateConnection();
580 vector
<string
> &vec
= p
->second
.GetResultVec();
581 int pos
= p
->second
.GetPos();
582 if ((flags
& TMSTARTRSCAN
) != 0)
589 SQLAllocHandle(SQL_HANDLE_STMT
, conn
, &stmt
);
590 ret
= SQLExecDirect(stmt
, (SQLCHAR
*) "select gid from pg_prepared_xacts", SQL_NTS
);
591 if (SQL_SUCCESS
!= ret
&& SQL_SUCCESS_WITH_INFO
!= ret
)
593 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
597 SQLBindCol(stmt
, 1, SQL_C_CHAR
, buf
, sizeof(buf
), NULL
);
598 ret
= SQLFetch(stmt
);
599 while (SQL_NO_DATA_FOUND
!= ret
)
602 ret
= SQLFetch(stmt
);
604 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
608 rmcode
= rcount
- pos
;
611 for (int i
= 0; i
< rmcode
; i
++, pos
++)
612 TextToXid(xids
[i
], vec
[pos
].c_str());
614 if ((flags
& TMENDRSCAN
) != 0)
619 mylog("return count=%d\n", rmcode
);
621 p
->second
.SetPos(pos
);
626 // I'm not sure if this is invoked from MSDTC
627 // Anyway there's nothing to do with it.
629 EXTERN_C
static int __cdecl
xa_forget(XID
*xid
, int rmid
, long flags
)
633 XidToText(*xid
, pgxid
);
634 mylog("xa_forget %s rmid=%d\n", pgxid
, rmid
);
638 // I'm not sure if this can be invoked from MSDTC.
640 EXTERN_C
static int __cdecl
xa_complete(int *handle
, int *retval
, int rmid
, long flags
)
642 mylog("xa_complete rmid=%d\n", rmid
);
646 EXTERN_C
static xa_switch_t xapsw
= { "psotgres_xa", TMNOMIGRATE
,
647 0, xa_open
, xa_close
, xa_start
, xa_end
, xa_rollback
,
648 xa_prepare
, xa_commit
, xa_recover
, xa_forget
,
651 EXTERN_C HRESULT __cdecl
GetXaSwitch (XA_SWITCH_FLAGS XaSwitchFlags
,
652 xa_switch_t
** ppXaSwitch
)
654 mylog("GetXaSwitch called\n");
657 *ppXaSwitch
= &xapsw
;