1 /* Module:			msdtc_enlist.cpp
2  *
3  * Description:
4  *		This module contains routines related to
5  *			the enlistment in MSDTC.
6  *
7  *-------
8  */
9 
10 #ifdef	_HANDLE_ENLIST_IN_DTC_
11 
12 #undef	_MEMORY_DEBUG_
13 #ifndef	_WIN32_WINNT
14 #define	_WIN32_WINNT	0x0400
15 #endif	/* _WIN32_WINNT */
16 
17 #define	WIN32_LEAN_AND_MEAN
18 #include <oleTx2xa.h>
19 #include <XOLEHLP.h>
20 /*#include <Txdtc.h>*/
21 #define	_PGDTC_FUNCS_IMPORT_
22 #include "connexp.h"
23 
24 /*#define	_SLEEP_FOR_TEST_*/
25 #include <stdio.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <process.h>
29 #include <map>
30 #ifndef	WIN32
31 #include <errno.h>
32 #endif /* WIN32 */
33 
34 #include <sql.h>
35 #define	_MYLOG_FUNCS_IMPORT_
36 #include "mylog.h"
37 #define	_PGENLIST_FUNCS_IMPLEMENT_
38 #include "pgenlist.h"
39 #include "xalibname.h"
40 
41 #ifdef WIN32
42 #ifndef snprintf
43 #define snprintf _snprintf
44 #endif /* snprintf */
45 #endif /* WIN32 */
46 
47 /* Define a type for defining a constant string expression */
48 #ifndef CSTR
49 #define CSTR static const char * const
50 #endif /* CSTR */
51 
52 EXTERN_C {
53 HINSTANCE s_hModule;               /* Saved module handle. */
54 }
55 /*      This is where the Driver Manager attaches to this Driver */
56 BOOL    WINAPI
57 DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
58 {
59         switch (ul_reason_for_call)
60         {
61                 case DLL_PROCESS_ATTACH:
62                         s_hModule = (HINSTANCE) hInst;  /* Save for dialog boxes */
63 			break;
64                 case DLL_PROCESS_DETACH:
65                         mylog("DETACHING pgenlist\n");
66 			break;
67 	}
68 	return TRUE;
69 }
70 
71 /*
72  *	A comment About locks used in this module
73  *
74  *	  the locks should be acquired with stronger to weaker order.
75  *
76  *	1:ELOCK -- the strongest per IAsyncPG object lock
77  *	  When the *isolated* or *dtcconn* member of an IAsyncPG object
78  *	  is changed, this lock should be held.
79  *	  While an IAsyncPG object accesses a psqlodbc connection,
80  *	  this lock should be held.
81  *
82  *	2:[CONN_CS] -- per psqlodbc connection lock
83  *	  This lock would be held for a pretty long time while accessing
84  *	  the psqlodbc connection assigned to an IAsyncPG object. You
85  *	  can use the connecion safely by holding a ELOCK for the
86  *	  IAsyncPG object because the assignment is ensured to be
87  *	  fixed while the ELOCK is held.
88  *
89  *	3:LIFELOCK -- a global lock to ensure the lives of IAsyncPG objects
90  *	  While this lock is held, IAsyncPG objects would never die.
91  *
92  *	4:SLOCK -- the short term per IAsyncPG object lock
93  *	  When any member of an IAsyncPG object is changed, this lock
94  *	  should be held.
95  */
96 
97 // #define	_LOCK_DEBUG_
98 static class INIT_CRIT
99 {
100 public:
101 	CRITICAL_SECTION	life_cs; /* for asdum member of ConnectionClass */
INIT_CRIT()102 	INIT_CRIT() {
103 		InitializeCriticalSection(&life_cs);
104 		}
~INIT_CRIT()105 	~INIT_CRIT() {
106 			DeleteCriticalSection(&life_cs);
107 			}
108 } init_crit;
109 #define	LIFELOCK_ACQUIRE EnterCriticalSection(&init_crit.life_cs)
110 #define	LIFELOCK_RELEASE LeaveCriticalSection(&init_crit.life_cs)
111 
112 /*
113  *	Some helper macros about connection handling.
114  */
115 #define	CONN_CS_ACQUIRE(conn)	PgDtc_lock_cntrl((conn), TRUE, FALSE)
116 #define	TRY_CONN_CS_ACQUIRE(conn) PgDtc_lock_cntrl((conn), TRUE, TRUE)
117 #define	CONN_CS_RELEASE(conn)	PgDtc_lock_cntrl((conn), FALSE, FALSE)
118 
119 #define	CONN_IS_IN_TRANS(conn)	PgDtc_get_property((conn), inTrans)
120 
121 
XidToText(const XID & xid,char * rtext)122 static const char *XidToText(const XID &xid, char *rtext)
123 {
124 	int	glen = xid.gtrid_length, blen = xid.bqual_length;
125 	int	i, j;
126 
127 	for (i = 0, j = 0; i < glen; i++, j += 2)
128 		sprintf(rtext + j, "%02x", (unsigned char) xid.data[i]);
129 	strcat(rtext, "-"); j++;
130 	for (; i < glen + blen; i++, j += 2)
131 		sprintf(rtext + j, "%02x", (unsigned char) xid.data[i]);
132 	return rtext;
133 }
134 
135 static LONG	g_cComponents = 0;
136 static LONG	g_cServerLocks = 0;
137 
138 //
139 //	�ȉ���ITransactionResourceAsync�I�u�W�F�N�g�͔C�ӂ̃X���b�h����
140 //	���R�ɃA�N�Z�X�”\�Ȃ悤�Ɏ�������B�eRequest�̌��ʂ�Ԃ����߂�
141 //	�g�p����ITransactionEnlistmentAsync�C���^�[�t�F�C�X�����̂悤��
142 //	��������Ă���i�Ǝv����A���L�Q�Ɓj�̂ŌĂяo����COM�̃A�p�[
143 //	�g�����g���ӎ�����(CoMarshalInterThreadInterfaceInStream/CoGetIn
144 //	terfaceAndReleaseStream���g�p����j�K�v�͂Ȃ��B
145 //	����DLL���Ŏg�p����ITransactionResourceAsync��ITransactionEnlist
146 //	mentAsync�̃C���^�[�t�F�C�X�|�C���^�[�͔C�ӂ̃X���b�h���璼�ڎg�p
147 //	���邱�Ƃ��ł���B
148 //
149 
150 // OLE Transactions Standard
151 //
152 // OLE Transactions is the Microsoft interface standard for transaction
153 // management. Applications use OLE Transactions-compliant interfaces to
154 // initiate, commit, abort, and inquire about transactions. Resource
155 // managers use OLE Transactions-compliant interfaces to enlist in
156 // transactions, to propagate transactions to other resource managers,
157 // to propagate transactions from process to process or from system to
158 // system, and to participate in the two-phase commit protocol.
159 //
160 // The Microsoft DTC system implements most OLE Transactions-compliant
161 // objects, interfaces, and methods. Resource managers that wish to use
162 // OLE Transactions must implement some OLE Transactions-compliant objects,
163 // interfaces, and methods.
164 //
165 // The OLE Transactions specification is based on COM but it differs in the
166 // following respects:
167 //
168 // OLE Transactions objects cannot be created using the COM CoCreate APIs.
169 // References to OLE Transactions objects are always direct. Therefore,
170 // no proxies or stubs are created for inter-apartment, inter-process,
171 // or inter-node calls and OLE Transactions references cannot be marshaled
172 // using standard COM marshaling.
173 // All references to OLE Transactions objects and their sinks are completely
174 // free threaded and cannot rely upon COM concurrency control models.
175 // For example, you cannot pass a reference to an IResourceManagerSink
176 // interface on a single-threaded apartment and expect the callback to occur
177 // only on the same single-threaded apartment.
178 
179 class	IAsyncPG : public ITransactionResourceAsync
180 {
181 private:
182 	IDtcToXaHelperSinglePipe	*helper;
183 	DWORD				RMCookie;
184 	void				*dtcconn;
185 	LONG				refcnt;
186 	CRITICAL_SECTION		as_spin; // to make this object Both
187 	CRITICAL_SECTION		as_exec; // to make this object Both
188 	XID				xid;
189 	bool				isolated;
190 	bool				prepared;
191 	bool				done;
192 	bool				abort;
193 	HANDLE				eThread[3];
194 	bool				eFin[3];
195 	bool				requestAccepted;
196 	HRESULT				prepare_result;
197 	HRESULT				commit_result;
198 #ifdef	_LOCK_DEBUG_
199 	int				spin_cnt;
200 	int				cs_cnt;
201 #endif /* _LOCK_DEBUG_ */
202 
203 public:
204 	enum {
205 		PrepareExec = 0
206 		,CommitExec
207 		,AbortExec
208 		};
209 
210 	ITransactionEnlistmentAsync	*enlist;
211 
212 	HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void ** ppvObject);
213 	ULONG	STDMETHODCALLTYPE AddRef(void);
214 	ULONG	STDMETHODCALLTYPE Release(void);
215 
216 	HRESULT STDMETHODCALLTYPE PrepareRequest(BOOL fRetaining,
217 				DWORD grfRM,
218 				BOOL fWantMoniker,
219 				BOOL fSinglePhase);
220 	HRESULT STDMETHODCALLTYPE CommitRequest(DWORD grfRM, XACTUOW * pNewUOW);
221 	HRESULT STDMETHODCALLTYPE AbortRequest(BOID * pboidReason,
222 				BOOL fRetaining,
223 				XACTUOW * pNewUOW);
224 	HRESULT STDMETHODCALLTYPE TMDown(void);
225 
226 	IAsyncPG();
SetHelper(IDtcToXaHelperSinglePipe * pHelper,DWORD dwRMCookie)227 	void SetHelper(IDtcToXaHelperSinglePipe *pHelper, DWORD dwRMCookie) {helper = pHelper; RMCookie = dwRMCookie;}
228 
229 	HRESULT RequestExec(DWORD type, HRESULT res);
230 	HRESULT ReleaseConnection(void);
SetConnection(void * sconn)231 	void SetConnection(void *sconn) {SLOCK_ACQUIRE(); dtcconn = sconn; SLOCK_RELEASE();}
SetXid(const XID * ixid)232 	void SetXid(const XID *ixid) {SLOCK_ACQUIRE(); xid = *ixid; SLOCK_RELEASE();}
233 	void	*separateXAConn(bool spinAcquired, bool continueConnection);
234 	bool CloseThread(DWORD type);
235 private:
236 	~IAsyncPG();
SLOCK_ACQUIRE()237 	void SLOCK_ACQUIRE() {EnterCriticalSection(&as_spin);}
SLOCK_RELEASE()238 	void SLOCK_RELEASE() {LeaveCriticalSection(&as_spin);}
ELOCK_ACQUIRE()239 	void ELOCK_ACQUIRE() {EnterCriticalSection(&as_exec);}
ELOCK_RELEASE()240 	void ELOCK_RELEASE() {LeaveCriticalSection(&as_exec);}
241 	void	*getLockedXAConn(void);
242 	void	*generateXAConn(bool spinAcquired);
243 	void	*isolateXAConn(bool spinAcquired, bool continueConnection);
SetPrepareResult(HRESULT res)244 	void SetPrepareResult(HRESULT res) {SLOCK_ACQUIRE(); prepared = true; prepare_result = res; SLOCK_RELEASE();}
245 	void SetDone(HRESULT);
246 	void Wait_pThread(bool slock_hold);
247 	void Wait_cThread(bool slock_hold, bool once);
248 };
249 
250 
IAsyncPG(void)251 IAsyncPG::IAsyncPG(void) : helper(NULL), RMCookie(0), enlist(NULL), dtcconn(NULL), refcnt(1), isolated(false), done(false), abort(false), prepared(false), requestAccepted(false)
252 {
253 	InterlockedIncrement(&g_cComponents);
254 	InitializeCriticalSection(&as_spin);
255 	InitializeCriticalSection(&as_exec);
256 	eThread[0] = eThread[1] = eThread[2] = NULL;
257 	eFin[0] = eFin[1] = eFin[2] = false;
258 	memset(&xid, 0, sizeof(xid));
259 #ifdef	_LOCK_DEBUG_
260 	spin_cnt = 0;
261 	cs_cnt = 0;
262 #endif /* _LOCK_DEBUG_ */
263 }
264 
265 //
266 //	invoked from *delete*.
267 //	When entered ELOCK -> LIFELOCK -> SLOCK are held
268 //	and they are released.
269 //
~IAsyncPG(void)270 IAsyncPG::~IAsyncPG(void)
271 {
272 	void *fconn = NULL;
273 
274 	if (dtcconn)
275 	{
276 		if (isolated)
277 			fconn = dtcconn;
278 		PgDtc_set_async(dtcconn, NULL);
279 		dtcconn = NULL;
280 	}
281 	SLOCK_RELEASE();
282 	LIFELOCK_RELEASE;
283 	if (fconn)
284 	{
285 		mylog("IAsyncPG Destructor is freeing the connection\n");
286 		PgDtc_free_connect(fconn);
287 	}
288 	DeleteCriticalSection(&as_spin);
289 	ELOCK_RELEASE();
290 	DeleteCriticalSection(&as_exec);
291 	InterlockedDecrement(&g_cComponents);
292 }
QueryInterface(REFIID riid,void ** ppvObject)293 HRESULT STDMETHODCALLTYPE IAsyncPG::QueryInterface(REFIID riid, void ** ppvObject)
294 {
295 	mylog("%p QueryInterface called\n", this);
296 	if (riid == IID_IUnknown || riid == IID_ITransactionResourceAsync)
297 	{
298 		*ppvObject = this;
299 		AddRef();
300 		return S_OK;
301 	}
302 	*ppvObject = NULL;
303 	return E_NOINTERFACE;
304 }
305 //
306 //	acquire/releases SLOCK.
307 //
AddRef(void)308 ULONG	STDMETHODCALLTYPE IAsyncPG::AddRef(void)
309 {
310 	mylog("%p->AddRef called\n", this);
311 	SLOCK_ACQUIRE();
312 	refcnt++;
313 	SLOCK_RELEASE();
314 	return refcnt;
315 }
316 //
317 //	acquire/releases [ELOCK -> LIFELOCK -> ] SLOCK.
318 //
Release(void)319 ULONG	STDMETHODCALLTYPE IAsyncPG::Release(void)
320 {
321 	mylog("%p->Release called refcnt=%d\n", this, refcnt);
322 	SLOCK_ACQUIRE();
323 	refcnt--;
324 	if (refcnt <= 0)
325 	{
326 		SLOCK_RELEASE();
327 		ELOCK_ACQUIRE();
328 		LIFELOCK_ACQUIRE;
329 		SLOCK_ACQUIRE();
330 		if (refcnt <=0)
331 		{
332 			const int refcnt_copy = refcnt;
333 			mylog("delete %p\n", this);
334 			delete this;
335 			return refcnt_copy;
336 		}
337 		else
338 		{
339 			SLOCK_RELEASE();
340 			LIFELOCK_RELEASE;
341 			ELOCK_RELEASE();
342 		}
343 	}
344 	else
345 		SLOCK_RELEASE();
346 	return refcnt;
347 }
348 
349 //
350 //	Acquire/release SLOCK.
351 //
Wait_pThread(bool slock_hold)352 void IAsyncPG::Wait_pThread(bool slock_hold)
353 {
354 	mylog("Wait_pThread %d in\n", slock_hold);
355 	HANDLE	wThread;
356 	int	wait_idx = PrepareExec;
357 	DWORD	ret;
358 
359 	if (!slock_hold)
360 		SLOCK_ACQUIRE();
361 	while (NULL != (wThread = eThread[wait_idx]) && !eFin[wait_idx])
362 	{
363 		SLOCK_RELEASE();
364 		ret = WaitForSingleObject(wThread, 2000);
365 		SLOCK_ACQUIRE();
366 		if (WAIT_TIMEOUT != ret)
367 			eFin[wait_idx] = true;
368 	}
369 	if (!slock_hold)
370 		SLOCK_RELEASE();
371 	mylog("Wait_pThread out\n");
372 }
373 
374 //
375 //	Acquire/releases SLOCK.
376 //
Wait_cThread(bool slock_hold,bool once)377 void IAsyncPG::Wait_cThread(bool slock_hold, bool once)
378 {
379 	HANDLE	wThread;
380 	int	wait_idx;
381 	DWORD	ret;
382 
383 	mylog("Wait_cThread %d,%d in\n", slock_hold, once);
384 	if (!slock_hold)
385 		SLOCK_ACQUIRE();
386 	if (NULL != eThread[CommitExec])
387 		wait_idx = CommitExec;
388 	else
389 		wait_idx = AbortExec;
390 	while (NULL != (wThread = eThread[wait_idx]) && !eFin[wait_idx])
391 	{
392 		SLOCK_RELEASE();
393 		ret = WaitForSingleObject(wThread, 2000);
394 		SLOCK_ACQUIRE();
395 		if (WAIT_TIMEOUT != ret)
396 			eFin[wait_idx] = true;
397 		else if (once)
398 			break;
399 	}
400 	if (!slock_hold)
401 		SLOCK_RELEASE();
402 	mylog("Wait_cThread out\n");
403 }
404 
405 /* Processing Prepare/Commit Request */
406 typedef
407 struct RequestPara {
408 	DWORD	type;
409 	LPVOID	lpr;
410 	HRESULT	res;
411 } RequestPara;
412 
413 //
414 //	Acquire/releases LIFELOCK -> SLOCK.
415 //	may acquire/release ELOCK.
416 //
SetDone(HRESULT res)417 void	IAsyncPG::SetDone(HRESULT res)
418 {
419 	LIFELOCK_ACQUIRE;
420 	SLOCK_ACQUIRE();
421 	done = true;
422 	if (E_FAIL == res ||
423 	    E_UNEXPECTED == res)
424 		abort = true;
425 	requestAccepted = true;
426 	commit_result = res;
427 	if (dtcconn)
428 	{
429 		PgDtc_set_async(dtcconn, NULL);
430 		if (isolated)
431 		{
432 			SLOCK_RELEASE();
433 			LIFELOCK_RELEASE;
434 			ELOCK_ACQUIRE();
435 			if (dtcconn)
436 			{
437 				mylog("Freeing isolated connection=%p\n", dtcconn);
438 				PgDtc_free_connect(dtcconn);
439 				SetConnection(NULL);
440 			}
441 			ELOCK_RELEASE();
442 		}
443 		else
444 		{
445 			dtcconn = NULL;
446 			SLOCK_RELEASE();
447 			LIFELOCK_RELEASE;
448 		}
449 	}
450 	else
451 	{
452 		SLOCK_RELEASE();
453 		LIFELOCK_RELEASE;
454 	}
455 }
456 
457 //
458 //	Acquire/releases [ELOCK -> LIFELOCK -> ] SLOCK.
459 //
generateXAConn(bool spinAcquired)460 void	*IAsyncPG::generateXAConn(bool spinAcquired)
461 {
462 mylog("generateXAConn isolated=%d dtcconn=%p\n", isolated, dtcconn);
463 	if (!spinAcquired)
464 		SLOCK_ACQUIRE();
465 	if (isolated || done)
466 	{
467 		SLOCK_RELEASE();
468 		return dtcconn;
469 	}
470 	SLOCK_RELEASE();
471 	ELOCK_ACQUIRE();
472 	LIFELOCK_ACQUIRE;
473 	SLOCK_ACQUIRE();
474 	if (dtcconn && !isolated && !done && prepared)
475 	{
476 		void	*sconn = dtcconn;
477 
478 		dtcconn = PgDtc_isolate(sconn, useAnotherRoom);
479 		isolated = true;
480 		SLOCK_RELEASE();
481 		LIFELOCK_RELEASE;
482 		// PgDtc_connect(dtcconn); may be called in getLockedXAConn
483 	}
484 	else
485 	{
486 		SLOCK_RELEASE();
487 		LIFELOCK_RELEASE;
488 	}
489 	ELOCK_RELEASE();
490 	return dtcconn;
491 }
492 
493 //
494 //	Acquire/releases [ELOCK -> LIFELOCK -> ] SLOCK.
495 //
isolateXAConn(bool spinAcquired,bool continueConnection)496 void	*IAsyncPG::isolateXAConn(bool spinAcquired, bool continueConnection)
497 {
498 	void *sconn;
499 
500 mylog("isolateXAConn isolated=%d dtcconn=%p\n", isolated, dtcconn);
501 	if (!spinAcquired)
502 		SLOCK_ACQUIRE();
503 	if (isolated || done || NULL == dtcconn)
504 	{
505 		SLOCK_RELEASE();
506 		return dtcconn;
507 	}
508 	SLOCK_RELEASE();
509 	ELOCK_ACQUIRE();
510 	LIFELOCK_ACQUIRE;
511 	SLOCK_ACQUIRE();
512 	if (isolated || done || NULL == dtcconn)
513 	{
514 		SLOCK_RELEASE();
515 		LIFELOCK_RELEASE;
516 		ELOCK_RELEASE();
517 		return dtcconn;
518 	}
519 	sconn = dtcconn;
520 
521 	dtcconn = PgDtc_isolate(sconn, continueConnection ? 0 : disposingConnection);
522 
523 	isolated = true;
524 	SLOCK_RELEASE();
525 	LIFELOCK_RELEASE;
526 	if (continueConnection)
527 	{
528 		PgDtc_connect(sconn);
529 	}
530 	ELOCK_RELEASE();
531 	return dtcconn;
532 }
533 
534 //
535 //	Acquire/releases [ELOCK -> LIFELOCK -> ] SLOCK.
536 //
separateXAConn(bool spinAcquired,bool continueConnection)537 void	*IAsyncPG::separateXAConn(bool spinAcquired, bool continueConnection)
538 {
539 	mylog("%s isolated=%d dtcconn=%p\n", __FUNCTION__, isolated, dtcconn);
540 	if (!spinAcquired)
541 		SLOCK_ACQUIRE();
542 	if (prepared)
543 		return generateXAConn(true);
544 	else
545 		return isolateXAConn(true, continueConnection);
546 }
547 
548 //
549 //	[when entered]
550 //	ELOCK is held.
551 //
552 //	Acquire/releases SLOCK.
553 //	Try to acquire CONN_CS also.
554 //
555 //	[on exit]
556 //	ELOCK is kept held.
557 //	If the return connection != NULL
558 //		the CONN_CS lock for the connection is held.
559 //
getLockedXAConn()560 void	*IAsyncPG::getLockedXAConn()
561 {
562 	SLOCK_ACQUIRE();
563 	while (!done && !isolated && NULL != dtcconn)
564 	{
565 		/*
566 		 * Note that COMMIT/ROLLBACK PREPARED command should be
567 		 * issued outside the transaction.
568 		 */
569 		if (!prepared || !CONN_IS_IN_TRANS(dtcconn))
570 		{
571 			if (TRY_CONN_CS_ACQUIRE(dtcconn))
572 			{
573 				if (prepared && CONN_IS_IN_TRANS(dtcconn))
574 				{
575 					CONN_CS_RELEASE(dtcconn);
576 				}
577 				else
578 					break;
579 			}
580 		}
581 		separateXAConn(true, true);
582 		SLOCK_ACQUIRE(); // SLOCK was released by separateXAConn()
583 	}
584 	SLOCK_RELEASE();
585 	if (isolated && NULL != dtcconn)
586 	{
587 		CONN_CS_ACQUIRE(dtcconn);
588 		if (!PgDtc_get_property(dtcconn, connected))
589 			PgDtc_connect(dtcconn);
590 	}
591 	return dtcconn;
592 }
593 
594 //
595 //	Acquire/release ELOCK -> SLOCK.
596 //
RequestExec(DWORD type,HRESULT res)597 HRESULT IAsyncPG::RequestExec(DWORD type, HRESULT res)
598 {
599 	HRESULT		ret;
600 	bool		bReleaseEnlist = false;
601 	void	*econn;
602 	char		pgxid[258];
603 
604 	mylog("%p->RequestExec type=%d conn=%p\n", this, type, dtcconn);
605 	XidToText(xid, pgxid);
606 #ifdef	_SLEEP_FOR_TEST_
607 	/*Sleep(2000);*/
608 #endif	/* _SLEEP_FOR_TEST_ */
609 	ELOCK_ACQUIRE();
610 	switch (type)
611 	{
612 		case PrepareExec:
613 			if (done || NULL == dtcconn)
614 			{
615 				res = E_UNEXPECTED;
616 				break;
617 			}
618 			if (econn = getLockedXAConn(), NULL != econn)
619 			{
620 				PgDtc_set_property(econn, inprogress, (void *) 1);
621 				if (E_FAIL == res)
622 					PgDtc_one_phase_operation(econn, ABORT_GLOBAL_TRANSACTION);
623 				else if (XACT_S_SINGLEPHASE == res)
624 				{
625 					if (!PgDtc_one_phase_operation(econn, ONE_PHASE_COMMIT))
626 						res = E_FAIL;
627 				}
628 				else
629 				{
630 					if (!PgDtc_two_phase_operation(econn, PREPARE_TRANSACTION, pgxid))
631 						res = E_FAIL;
632 				}
633 				PgDtc_set_property(econn, inprogress, (void *) 0);
634 				CONN_CS_RELEASE(econn);
635 			}
636 			if (S_OK != res)
637 			{
638 				SetDone(res);
639 				bReleaseEnlist = true;
640 			}
641 			ret = enlist->PrepareRequestDone(res, NULL, NULL);
642 			SetPrepareResult(res);
643 			break;
644 		case CommitExec:
645 			Wait_pThread(false);
646 			if (E_FAIL != res)
647 			{
648 				econn = getLockedXAConn();
649 				if (econn)
650 				{
651 					PgDtc_set_property(econn, inprogress, (void *) 1);
652 					if (!PgDtc_two_phase_operation(econn, COMMIT_PREPARED, pgxid))
653 						res = E_FAIL;
654 					PgDtc_set_property(econn, inprogress, (void *) 0);
655 					CONN_CS_RELEASE(econn);
656 				}
657 			}
658 			SetDone(res);
659 			ret = enlist->CommitRequestDone(res);
660 			bReleaseEnlist = true;
661 			break;
662 		case AbortExec:
663 			Wait_pThread(false);
664 			if (prepared && !done)
665 			{
666 				econn = getLockedXAConn();
667 				if (econn)
668 				{
669 					PgDtc_set_property(econn, inprogress, (void *) 1);
670 					if (!PgDtc_two_phase_operation(econn, ROLLBACK_PREPARED, pgxid))
671 						res = E_FAIL;
672 					PgDtc_set_property(econn, inprogress, (void *) 0);
673 					CONN_CS_RELEASE(econn);
674 				}
675 			}
676 			SetDone(res);
677 			ret = enlist->AbortRequestDone(res);
678 			bReleaseEnlist = true;
679 			break;
680 		default:
681 			ret = -1;
682 	}
683 	if (bReleaseEnlist)
684 	{
685 		helper->ReleaseRMCookie(RMCookie, TRUE);
686 		enlist->Release();
687 	}
688 	ELOCK_RELEASE();
689 	mylog("%p->Done ret=%d\n", this, ret);
690 	return ret;
691 }
692 
693 //
694 //	Acquire/releses SLOCK
695 //	 	or 	[ELOCK -> LIFELOCK -> ] SLOCK.
696 //
ReleaseConnection(void)697 HRESULT IAsyncPG::ReleaseConnection(void)
698 {
699 	mylog("%p->ReleaseConnection\n", this);
700 
701 	SLOCK_ACQUIRE();
702 	if (isolated || NULL == dtcconn)
703 	{
704 		SLOCK_RELEASE();
705 		return SQL_SUCCESS;
706 	}
707 	Wait_pThread(true);
708 	if (NULL != eThread[CommitExec] || NULL != eThread[AbortExec] || requestAccepted)
709 	{
710 		if (!done)
711 			Wait_cThread(true, true);
712 	}
713 	if (!isolated && !done && dtcconn && PgDtc_get_property(dtcconn, connected))
714 	{
715 		isolateXAConn(true, false);
716 	}
717 	else
718 		SLOCK_RELEASE();
719 	mylog("%p->ReleaseConnection exit\n", this);
720 	return SQL_SUCCESS;
721 }
722 
723 EXTERN_C static unsigned WINAPI DtcRequestExec(LPVOID para);
724 EXTERN_C static void __cdecl ClosePrepareThread(LPVOID para);
725 EXTERN_C static void __cdecl CloseCommitThread(LPVOID para);
726 EXTERN_C static void __cdecl CloseAbortThread(LPVOID para);
727 
728 //
729 //	Acquire/release [ELOCK -> ] SLOCK.
730 //
PrepareRequest(BOOL fRetaining,DWORD grfRM,BOOL fWantMoniker,BOOL fSinglePhase)731 HRESULT STDMETHODCALLTYPE IAsyncPG::PrepareRequest(BOOL fRetaining, DWORD grfRM,
732 				BOOL fWantMoniker, BOOL fSinglePhase)
733 {
734 	HRESULT	ret, res;
735 	RequestPara	*reqp;
736 	const DWORD	reqtype = PrepareExec;
737 
738 	mylog("%p PrepareRequest called grhRM=%d enl=%p\n", this, grfRM, enlist);
739 	SLOCK_ACQUIRE();
740 	if (dtcconn && 0 != PgDtc_get_property(dtcconn, errorNumber))
741 		res = ret = E_FAIL;
742 	else
743 	{
744 		ret = S_OK;
745 		if (fSinglePhase)
746 		{
747 			res = XACT_S_SINGLEPHASE;
748 			mylog("XACT is singlePhase\n");
749 		}
750 		else
751 			res = S_OK;
752 	}
753 	SLOCK_RELEASE();
754 	ELOCK_ACQUIRE();
755 #ifdef	_SLEEP_FOR_TEST_
756 	Sleep(2000);
757 #endif	/* _SLEEP_FOR_TEST_ */
758 	reqp = new RequestPara;
759 	reqp->type = reqtype;
760 	reqp->lpr = (LPVOID) this;
761 	reqp->res = res;
762 #define	DONT_CALL_RETURN_FROM_HERE ???
763 	AddRef();
764 	HANDLE hThread = (HANDLE) _beginthreadex(NULL, 0, DtcRequestExec, reqp, 0, NULL);
765 	if (NULL == hThread)
766 	{
767 		delete(reqp);
768 		ret = E_FAIL;
769 	}
770 	else
771 	{
772 		SLOCK_ACQUIRE();
773 		eThread[reqtype] = hThread;
774 		SLOCK_RELEASE();
775 		/*
776 		 * We call here _beginthread not _beginthreadex
777 		 * so as not to call CloseHandle() to clean up
778 		 * the thread.
779 		 */
780 		_beginthread(ClosePrepareThread, 0, (void *) this);
781 	}
782 	ELOCK_RELEASE();
783 	Release();
784 #undef	return
785 	return ret;
786 }
787 //
788 //	Acquire/release [ELOCK -> ] SLOCK.
789 //
CommitRequest(DWORD grfRM,XACTUOW * pNewUOW)790 HRESULT STDMETHODCALLTYPE IAsyncPG::CommitRequest(DWORD grfRM, XACTUOW * pNewUOW)
791 {
792 	HRESULT		res = S_OK, ret = S_OK;
793 	RequestPara	*reqp;
794 	const DWORD	reqtype = CommitExec;
795 
796 	mylog("%p CommitRequest called grfRM=%d enl=%p\n", this, grfRM, enlist);
797 
798 	SLOCK_ACQUIRE();
799 	if (!prepared || done)
800 		ret = E_UNEXPECTED;
801 	else if (S_OK != prepare_result)
802 		ret = E_UNEXPECTED;
803 	SLOCK_RELEASE();
804 	if (S_OK != ret)
805 		return ret;
806 #define	DONT_CALL_RETURN_FROM_HERE ???
807 	AddRef();
808 	ELOCK_ACQUIRE();
809 #ifdef	_SLEEP_FOR_TEST_
810 	Sleep(1000);
811 #endif	/* _SLEEP_FOR_TEST_ */
812 	reqp = new RequestPara;
813 	reqp->type = reqtype;
814 	reqp->lpr = (LPVOID) this;
815 	reqp->res = res;
816 	enlist->AddRef();
817 	HANDLE hThread = (HANDLE) _beginthreadex(NULL, 0, DtcRequestExec, reqp, 0, NULL);
818 	if (NULL == hThread)
819 	{
820 		delete(reqp);
821 		enlist->Release();
822 		ret = E_FAIL;
823 	}
824 	else
825 	{
826 		SLOCK_ACQUIRE();
827 		eThread[reqtype] = hThread;
828 		SLOCK_RELEASE();
829 		/*
830 		 * We call here _beginthread not _beginthreadex
831 		 * so as not to call CloseHandle() to clean up
832 		 * the thread.
833 		 */
834 		_beginthread(CloseCommitThread, 0, (void *) this);
835 	}
836 	mylog("CommitRequest ret=%d\n", ret);
837 	requestAccepted = true;
838 	ELOCK_RELEASE();
839 	Release();
840 #undef	return
841 	return ret;
842 }
843 //
844 //	Acquire/release [ELOCK -> ] SLOCK.
845 //
AbortRequest(BOID * pboidReason,BOOL fRetaining,XACTUOW * pNewUOW)846 HRESULT STDMETHODCALLTYPE IAsyncPG::AbortRequest(BOID * pboidReason, BOOL fRetaining,
847 				XACTUOW * pNewUOW)
848 {
849 	HRESULT		res = S_OK, ret = S_OK;
850 	RequestPara	*reqp;
851 	const DWORD	reqtype = AbortExec;
852 
853 	mylog("%p AbortRequest called\n", this);
854 	SLOCK_ACQUIRE();
855 	if (done)
856 		ret = E_UNEXPECTED;
857 	else if (prepared && S_OK != prepare_result)
858 		ret = E_UNEXPECTED;
859 	SLOCK_RELEASE();
860 	if (S_OK != ret)
861 		return ret;
862 #define	return	DONT_CALL_RETURN_FROM_HERE ???
863 	AddRef();
864 	ELOCK_ACQUIRE();
865 	if (!prepared && dtcconn)
866 	{
867 		PgDtc_set_property(dtcconn, inprogress, (void *) 1);
868 		PgDtc_one_phase_operation(dtcconn, ONE_PHASE_ROLLBACK);
869 		PgDtc_set_property(dtcconn, inprogress, (void *) 0);
870 	}
871 	reqp = new RequestPara;
872 	reqp->type = reqtype;
873 	reqp->lpr = (LPVOID) this;
874 	reqp->res = res;
875 	enlist->AddRef();
876 	HANDLE hThread = (HANDLE) _beginthreadex(NULL, 0, DtcRequestExec, reqp, 0, NULL);
877 	if (NULL == hThread)
878 	{
879 		delete(reqp);
880 		enlist->Release();
881 		ret = E_FAIL;
882 	}
883 	else
884 	{
885 		SLOCK_ACQUIRE();
886 		eThread[reqtype] = hThread;
887 		SLOCK_RELEASE();
888 		/*
889 		 * We call here _beginthread not _beginthreadex
890 		 * so as not to call CloseHandle() to clean up
891 		 * the thread.
892 		 */
893 		_beginthread(CloseAbortThread, 0, (void *) this);
894 	}
895 	mylog("AbortRequest ret=%d\n", ret);
896 	requestAccepted = true;
897 	ELOCK_RELEASE();
898 	Release();
899 #undef	return
900 	return	ret;
901 }
TMDown(void)902 HRESULT STDMETHODCALLTYPE IAsyncPG::TMDown(void)
903 {
904 	mylog("%p TMDown called\n", this);
905 	return	S_OK;
906 }
907 
CloseThread(DWORD type)908 bool IAsyncPG::CloseThread(DWORD type)
909 {
910 	CSTR		func = "CloseThread";
911 	HANDLE		th;
912 	DWORD		ret, excode = S_OK;
913 	bool		rls_async = false;
914 
915 	mylog("%s for %p thread=%d\n", func, this, eThread[type]);
916 	if (th = eThread[type], NULL == th || eFin[type])
917 		return false;
918 	ret = WaitForSingleObject(th, INFINITE);
919 	if (WAIT_OBJECT_0 == ret)
920 	{
921 		switch (type)
922 		{
923 			case IAsyncPG::AbortExec:
924 			case IAsyncPG::CommitExec:
925 				rls_async = true;
926 				break;
927 			default:
928 				GetExitCodeThread(th, &excode);
929 				if (S_OK != excode)
930 					rls_async = true;
931 		}
932 		SLOCK_ACQUIRE();
933 		eThread[type] = NULL;
934 		eFin[type] = true;
935 		SLOCK_RELEASE();
936 		CloseHandle(th);
937 	}
938 	mylog("%s ret=%d\n", func, ret);
939 	return rls_async;
940 }
941 
ClosePrepareThread(LPVOID para)942 EXTERN_C static void __cdecl ClosePrepareThread(LPVOID para)
943 {
944 	CSTR		func = "ClosePrepareThread";
945 	IAsyncPG	*async = (IAsyncPG *) para;
946 	bool		release;
947 
948 	mylog("%s for %p", func, async);
949 	if (release = async->CloseThread(IAsyncPG::PrepareExec), release)
950 		async->Release();
951 	mylog("%s release=%d\n", func, release);
952 }
953 
CloseCommitThread(LPVOID para)954 EXTERN_C static void __cdecl CloseCommitThread(LPVOID para)
955 {
956 	CSTR		func = "CloseCommitThread";
957 	IAsyncPG	*async = (IAsyncPG *) para;
958 	bool		release;
959 
960 	mylog("%s for %p", func, async);
961 	if (release = async->CloseThread(IAsyncPG::CommitExec), release)
962 		async->Release();
963 	mylog("%s release=%d\n", func, release);
964 }
965 
CloseAbortThread(LPVOID para)966 EXTERN_C static void __cdecl CloseAbortThread(LPVOID para)
967 {
968 	CSTR		func = "CloseAbortThread";
969 	IAsyncPG	*async = (IAsyncPG *) para;
970 	bool		release;
971 
972 	mylog("%s for %p", func, async);
973 	if (release = async->CloseThread(IAsyncPG::AbortExec), release)
974 		async->Release();
975 	mylog("%s release=%d\n", func, release);
976 }
977 
DtcRequestExec(LPVOID para)978 EXTERN_C static unsigned WINAPI DtcRequestExec(LPVOID para)
979 {
980 	RequestPara	*reqp = (RequestPara *) para;
981 	DWORD		type = reqp->type;
982 	IAsyncPG *async = (IAsyncPG *) reqp->lpr;
983 	HRESULT	res = reqp->res, ret;
984 
985 	mylog("DtcRequestExec type=%d", reqp->type);
986 	delete(reqp);
987 	ret = async->RequestExec(type, res);
988 	mylog(" Done ret=%d\n", ret);
989 	return ret;
990 }
991 
992 CSTR	regKey = "SOFTWARE\\Microsoft\\MSDTC\\XADLL";
993 
regkeyCheck(const char * xalibname,const char * xalibpath)994 static int regkeyCheck(const char *xalibname, const char *xalibpath)
995 {
996 	int	retcode = 0;
997 	LONG	ret;
998 	HKEY	sKey;
999 	DWORD	rSize;
1000 
1001 	ret = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, regKey, 0, KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_WOW64_64KEY, &sKey);
1002 	switch (ret)
1003 	{
1004 		case ERROR_SUCCESS:
1005 			break;
1006 		case ERROR_FILE_NOT_FOUND:
1007 			ret = ::RegCreateKeyEx(HKEY_LOCAL_MACHINE, regKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &sKey, NULL);
1008 			mylog("%s:CreateKeyEx ret=%d\n", __FUNCTION__, ret);
1009 			break;
1010 		default:
1011 			mylog("%s:OpenKeyEx ret=%d\n", __FUNCTION__, ret);
1012 	}
1013 	if (ERROR_SUCCESS != ret)
1014 		return -1;
1015 	else
1016 	{
1017 		char keyval[1024];
1018 
1019 		rSize = sizeof(keyval);
1020 		switch (ret = ::RegQueryValueEx(sKey, xalibname, NULL, NULL, (LPBYTE) keyval, &rSize))
1021 		{
1022 			case ERROR_SUCCESS:
1023 				if (rSize > 0)
1024 				{
1025 					if (0 == _stricmp(keyval, xalibpath))
1026 						break;
1027 					mylog("%s:XADLL value %s is different from %s\n", __FUNCTION__, keyval, xalibpath);
1028 					if (IsWow64())
1029 					{
1030 						mylog("%s:avoid RegSetValue operation from wow64 process\n", __FUNCTION__);
1031 						break;
1032 					}
1033 				}
1034 			case ERROR_FILE_NOT_FOUND:
1035 				mylog("%s:Setting value %s\n", __FUNCTION__, xalibpath);
1036 				ret = ::RegSetValueEx(sKey, xalibname, 0, REG_SZ, (CONST BYTE *) xalibpath, (DWORD) strlen(xalibpath) + 1);
1037 				if (ERROR_SUCCESS == ret)
1038 					retcode = 1;
1039 				else
1040 				{
1041 					retcode = -1;
1042 					mylog("%s:SetValuEx ret=%d\n", __FUNCTION__, ret);
1043 				}
1044 				break;
1045 			default:
1046 				retcode = -1;
1047 				mylog("%s:QueryValuEx ret=%d\n", __FUNCTION__, ret);
1048 				break;
1049 		}
1050 		::RegCloseKey(sKey);
1051 	}
1052 	return retcode;
1053 }
1054 
EnlistInDtc_1pipe(void * conn,ITransaction * pTra,ITransactionDispenser * pDtc,int method)1055 RETCODE static EnlistInDtc_1pipe(void *conn, ITransaction *pTra, ITransactionDispenser *pDtc, int method)
1056 {
1057 	CSTR	func = "EnlistInDtc_1pipe";
1058 	static	IDtcToXaHelperSinglePipe	*pHelper = NULL;
1059 	ITransactionResourceAsync		*pRes = NULL;
1060 	IAsyncPG				*asdum;
1061 	HRESULT	res;
1062 	DWORD	dwRMCookie;
1063 	XID	xid;
1064 	const char *xalibname = GetXaLibName();
1065 	const char *xalibpath = GetXaLibPath();
1066 
1067 	int	recovLvl;
1068 	char	errmsg[256];
1069 	char	reason[128];
1070 
1071 	if (!pHelper)
1072 	{
1073 		res = pDtc->QueryInterface(IID_IDtcToXaHelperSinglePipe, (void **) &pHelper);
1074 		if (res != S_OK || !pHelper)
1075 		{
1076 			mylog("DtcToXaHelperSingelPipe get error %d\n", res);
1077 			pHelper = NULL;
1078 			return SQL_ERROR;
1079 		}
1080 	}
1081 	res = (NULL != (asdum = new IAsyncPG)) ? S_OK : E_FAIL;
1082 	if (S_OK != res)
1083 	{
1084 		mylog("CoCreateInstance error %d\n", res);
1085 		return SQL_ERROR;
1086 	}
1087 
1088 	recovLvl = PgDtc_is_recovery_available(conn, reason, sizeof(reason));
1089 	switch (method)
1090 	{
1091 		case DTC_CHECK_BEFORE_LINK:
1092 			if (0 == recovLvl)
1093 			{
1094 				snprintf(errmsg, sizeof(errmsg), "%s is unavailable in distributed transactions", reason);
1095 				PgDtc_set_error(conn, errmsg, func);
1096 				return SQL_ERROR;
1097 			}
1098 	}
1099 /*mylog("dllname=%s dsn=%s\n", xalibname, conn->connInfo.dsn); res = 0;*/
1100 	char	dtcname[1024];
1101 	PgDtc_create_connect_string(conn, dtcname, sizeof(dtcname));
1102 
1103 	bool	confirmedRegkey = false, confirmingLink = false, xarmerr = false;
1104 	char	error_header[64];
1105 	while (true)
1106 	{
1107 		res = pHelper->XARMCreate(dtcname, (char *) xalibname, &dwRMCookie);
1108 
1109 		mylog("XARMcreate error code=%x (%d %d)\n", res, confirmedRegkey, confirmingLink);
1110 		xarmerr = true;
1111 		if (!confirmingLink)
1112 			snprintf(error_header, sizeof(error_header), "XARMcreate error code=%x", res);
1113 		switch (res)
1114 		{
1115 			case S_OK:
1116 				if (confirmingLink)
1117 				{
1118 					switch (recovLvl)
1119 					{
1120 						case 0:
1121 							snprintf(errmsg, sizeof(errmsg), "%s:%s is currently unavailable in distributed transactions", error_header, reason);
1122 							break;
1123 						case -1:
1124 							snprintf(errmsg, sizeof(errmsg), "%s:Possibly you connect to the database whose authentication method is %s or ident", error_header, reason);
1125 							break;
1126 						case 1:
1127 							snprintf(errmsg, sizeof(errmsg), "%s:Are you trying to connect to the database whose authentication method is ident?", error_header);
1128 							break;
1129 					}
1130 				}
1131 				else
1132 					xarmerr = false;
1133 				break;
1134 			case XACT_E_XA_TX_DISABLED:
1135 				snprintf(errmsg, sizeof(errmsg), "%s:Please enable XA transaction in MSDTC security configuration", error_header);
1136 				break;
1137 			case XACT_E_TMNOTAVAILABLE:
1138 				snprintf(errmsg, sizeof(errmsg), "%s:Please start Distributed Transaction Coordinator service", error_header);
1139 				break;
1140 			case E_FAIL:
1141 				if (!confirmedRegkey)
1142 				{
1143 					int retcode = regkeyCheck(xalibname, xalibpath);
1144 					confirmedRegkey = true;
1145 					if (retcode > 0)
1146 						continue;
1147 				}
1148 				switch (method)
1149 				{
1150 					case DTC_CHECK_RM_CONNECTION:
1151 						if (!confirmingLink)
1152 						{
1153 							confirmingLink = true;
1154 							strcat(dtcname, ";" KEYWORD_DTC_CHECK "=0");
1155 							continue;
1156 						}
1157 					default:
1158 						snprintf(errmsg, sizeof(errmsg), "%s:Failed to link with DTC service. Please look at the log of Event Viewer etc.", error_header);
1159 				}
1160 				break;
1161 			case XACT_E_CONNECTION_DOWN:
1162 				snprintf(errmsg, sizeof(errmsg), "%s:Lost connection with DTC transaction manager\nMSDTC has some trouble?", error_header);
1163 				break;
1164 			default:
1165 				snprintf(errmsg, sizeof(errmsg), "%s\n", error_header);
1166 				break;
1167 		}
1168 		break;
1169 	}
1170 	if (xarmerr)
1171 	{
1172 		PgDtc_set_error(conn, errmsg, func);
1173 		return SQL_ERROR;
1174 	}
1175 
1176 	res = pHelper->ConvertTridToXID((DWORD *) pTra, dwRMCookie, &xid);
1177 	if (res != S_OK)
1178 	{
1179 		mylog("ConvertTridToXid error %d\n", res);
1180 		return SQL_ERROR;
1181 	}
1182 {
1183 char	pgxid[258];
1184 XidToText(xid, pgxid);
1185 mylog("ConvertTridToXID -> %s\n", pgxid);
1186 }
1187 	asdum->SetXid(&xid);
1188 	/* Create an IAsyncPG instance by myself */
1189 	/* DLLGetClassObject(GUID_IAsyncPG, IID_ITransactionResourceAsync, (void **) &asdum); */
1190 
1191 	asdum->SetHelper(pHelper, dwRMCookie);
1192 	res = pHelper->EnlistWithRM(dwRMCookie, pTra, asdum, &asdum->enlist);
1193 	if (res != S_OK)
1194 	{
1195 		mylog("EnlistWithRM error %d\n", res);
1196 		pHelper->ReleaseRMCookie(dwRMCookie, TRUE);
1197 		return SQL_ERROR;
1198 	}
1199 
1200 	mylog("asdum=%p start transaction\n", asdum);
1201 	asdum->SetConnection(conn);
1202 	LIFELOCK_ACQUIRE;
1203 	PgDtc_set_async(conn, asdum);
1204 	LIFELOCK_RELEASE;
1205 
1206 	return 	SQL_SUCCESS;
1207 }
1208 
1209 
1210 EXTERN_C RETCODE
IsolateDtcConn(void * conn,BOOL continueConnection)1211 IsolateDtcConn(void *conn, BOOL continueConnection)
1212 {
1213 	IAsyncPG *async;
1214 
1215 	LIFELOCK_ACQUIRE;
1216 	if (async = (IAsyncPG *) PgDtc_get_async(conn), NULL != async)
1217 	{
1218 		if (PgDtc_get_property(conn, idleInGlobalTransaction))
1219 		{
1220 			async->AddRef();
1221 			LIFELOCK_RELEASE;
1222 			async->separateXAConn(false, continueConnection ? true : false);
1223 			async->Release();
1224 		}
1225 		else
1226 			LIFELOCK_RELEASE;
1227 	}
1228 	else
1229 		LIFELOCK_RELEASE;
1230 	return SQL_SUCCESS;
1231 }
1232 
1233 
getITransactionDispenser(DWORD grfOptions,HRESULT * hres)1234 static ITransactionDispenser *getITransactionDispenser(DWORD grfOptions, HRESULT *hres)
1235 {
1236 	static	ITransactionDispenser	*pDtc = NULL;
1237 	HRESULT	res = S_OK;
1238 
1239 	if (!pDtc)
1240 	{
1241 		res = DtcGetTransactionManagerEx(NULL, NULL, IID_ITransactionDispenser,
1242 
1243 			grfOptions, NULL, (void **) &pDtc);
1244 		if (FAILED(res))
1245 		{
1246 			mylog("DtcGetTransactionManager error %x\n", res);
1247 			pDtc = NULL;
1248 		}
1249 	}
1250 	if (hres)
1251 		*hres = res;
1252 
1253 	return pDtc;
1254 }
1255 
GetTransactionObject(HRESULT * hres)1256 EXTERN_C void	*GetTransactionObject(HRESULT *hres)
1257 {
1258 
1259 	ITransaction	*pTra = NULL;
1260 	ITransactionDispenser	*pDtc = NULL;
1261 
1262 	if (pDtc = getITransactionDispenser(OLE_TM_FLAG_NONE, hres), NULL == pDtc)
1263 		return pTra;
1264 	HRESULT res = pDtc->BeginTransaction(NULL, ISOLATIONLEVEL_READCOMMITTED,
1265 		0, NULL, &pTra);
1266 	switch (res)
1267 	{
1268 		case S_OK:
1269 			break;
1270 		default:
1271 			pTra = NULL;
1272 	}
1273 	if (hres)
1274 		*hres = res;
1275 	return pTra;
1276 }
1277 
ReleaseTransactionObject(void * pObj)1278 EXTERN_C void	ReleaseTransactionObject(void *pObj)
1279 {
1280 	ITransaction	*pTra = (ITransaction *) pObj;
1281 
1282 	if (!pTra)	return;
1283 	pTra->Release();
1284 }
1285 
EnlistInDtc(void * conn,void * pTra,int method)1286 EXTERN_C RETCODE EnlistInDtc(void *conn, void *pTra, int method)
1287 {
1288 	ITransactionDispenser	*pDtc = NULL;
1289 	RETCODE	ret;
1290 
1291 	if (!pTra)
1292 	{
1293 		IAsyncPG *asdum = (IAsyncPG *) PgDtc_get_async(conn);
1294 		PgDtc_set_property(conn, enlisted, (void *) 0);
1295 		return SQL_SUCCESS;
1296 	}
1297 	if (CONN_IS_IN_TRANS(conn))
1298 	{
1299 		PgDtc_one_phase_operation(conn, SHUTDOWN_LOCAL_TRANSACTION);
1300 	}
1301 	HRESULT	hres;
1302 	pDtc = getITransactionDispenser(OLE_TM_FLAG_NODEMANDSTART, &hres);
1303 	if (!pDtc)
1304 	{
1305 		char	errmsg[128];
1306 		snprintf(errmsg, sizeof(errmsg), "enlistment error:DtcGetTransactionManager error code=%x", hres);
1307 		PgDtc_set_error(conn, errmsg, __FUNCTION__);
1308 		return SQL_ERROR;
1309 	}
1310 	ret = EnlistInDtc_1pipe(conn, (ITransaction *) pTra, pDtc, method);
1311 	if (SQL_SUCCEEDED(ret))
1312 		PgDtc_set_property(conn, enlisted, (void *) 1);
1313 	return ret;
1314 }
1315 
DtcOnDisconnect(void * conn)1316 EXTERN_C RETCODE DtcOnDisconnect(void *conn)
1317 {
1318 	mylog("DtcOnDisconnect\n");
1319 	LIFELOCK_ACQUIRE;
1320 	IAsyncPG *asdum = (IAsyncPG *) PgDtc_get_async(conn);
1321 	if (asdum)
1322 	{
1323 		asdum->AddRef();
1324 		LIFELOCK_RELEASE;
1325 		asdum->ReleaseConnection();
1326 		asdum->Release();
1327 	}
1328 	else
1329 		LIFELOCK_RELEASE;
1330 	return SQL_SUCCESS;
1331 }
1332 
1333 #endif /* _HANDLE_ENLIST_IN_DTC_ */
1334