//-------------------------------------------------------------------- // Microsoft OLE DB Test // // Copyright 1996-2000 Microsoft Corporation. // // @doc // // @module itranloc.CPP | Test Module for Local (Non Coordinated) Transactions // #include "modstandard.hpp" #define DBINITCONSTANTS // Must be defined to initialize constants in OLEDB.H #define INITGUID #include "itranloc.h" #include "txnbase.hpp" //Base classes for transacted rowsets #define DBINITCONSTANTS // Must be defined to initialize constants in OLEDB.H #define INITGUID // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Module Values // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // {{ TCW_MODULE_GLOBALS DECLARE_MODULE_CLSID = { 0xc9066071, 0x97e4, 0x11cf, { 0x97, 0x77, 0x00, 0xaa, 0x00, 0xbd, 0xf9, 0x52 }}; DECLARE_MODULE_NAME("ITransactionLocal"); DECLARE_MODULE_OWNER("Microsoft"); DECLARE_MODULE_DESCRIP("Test Module for Local Transactions"); DECLARE_MODULE_VERSION(839560026); // }} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Global Values // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //This must be defined so that the base class constructors //in txnbase.hpp know what to use for this test module's name. LPWSTR gwszThisModuleName = L"itranloc"; //-------------------------------------------------------------------- // @func Module level initialization routine // // @rdesc Success or Failure // @flag TRUE | Successful initialization // @flag FALSE | Initialization problems // BOOL ModuleInit(CThisTestModule * pThisTestModule) { ITransactionLocal *pITxnLocal = NULL; HRESULT hr; IConnectionPointContainer *pIConnectionPointContainer = NULL; if (ModuleCreateDBSession(pThisTestModule)) { //Fail gracefully and quit module if we don't support local transactions if (SUCCEEDED(hr = pThisTestModule->m_pIUnknown2->QueryInterface( IID_ITransactionLocal, (void **)&pITxnLocal))) { SAFE_RELEASE(pITxnLocal); } else { //Make sure we returned E_NOINTERFACE if we've failed if (pThisTestModule->m_pError->Validate(hr, LONGSTRING(__FILE__), __LINE__, E_NOINTERFACE)) odtLog <m_pVoid = new CTable( (IUnknown *)pThisTestModule->m_pIUnknown2, (LPWSTR)gwszModuleName ); if (!pThisTestModule->m_pVoid) { odtLog << wszMemoryAllocationError; return FALSE; } //Start with a table with rows if (FAILED(((CTable *)pThisTestModule->m_pVoid)->CreateTable(NUM_ROWS))) return FALSE; //Init last actual insert number to last row we inserted g_ulLastActualInsert = NUM_ROWS; //First row in table is 1 g_ulFirstRowInTable = 1; g_ulLastActualDelete = 0; //If we made it this far, everything has succeeded return TRUE; } return FALSE; } //-------------------------------------------------------------------- // @func Module level termination routine // // @rdesc Success or Failure // @flag TRUE | Successful initialization // @flag FALSE | Initialization problems // BOOL ModuleTerminate(CThisTestModule * pThisTestModule) { //We still own the table since all of our testcases //have only used it and not deleted it. if (pThisTestModule->m_pVoid) { ((CTable *)pThisTestModule->m_pVoid)->DropTable(); delete (CTable*)pThisTestModule->m_pVoid; pThisTestModule->m_pVoid = NULL; } return ModuleReleaseDBSession(pThisTestModule); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Base Class Declarations // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //-------------------------------------------------------------------- // @class CTxnChgUpdRowset | Transactable updateable buffered mode rowset // @base public | CTxnChgRowset //-------------------------------------------------------------------- class CTxnChgUpdRowset : public CTxnChgRowset { // @access public public: //@cmember IRowsetUpdate Interface for this rowset IRowsetUpdate *m_pIRowsetUpdate; //@cmember IRowset interface for this rowset IRowset *m_pIRowset; //@cmember Count of pending rows on update DBCOUNTITEM m_cPendingRows; //@cmember Array of pending rows HROW *m_rgPendingRows; //@cmember Array of pending rows' status DBPENDINGSTATUS *m_rgPendingStatus; //@cmember CTOR CTxnChgUpdRowset(LPWSTR tcName) : CTxnChgRowset(tcName){}; //@cmember Initialization - Creates an updateable buffered mode rowset virtual BOOL Init(); //@cmember Termination - Does termination for an updateable buffered mode rowset virtual BOOL Terminate(); //@cmember Creates an updateable buffered rowset and gets IRowsetUpdate interface virtual HRESULT MakeRowset(); //@cmember Creates an readonly rowset // virtual HRESULT MakeRowsetReadOnly(); //@cmember Frees an updateable buffered rowset and frees IRowsetUpdate interface virtual HRESULT FreeRowset(); //@cmember make sure properties are set virtual HRESULT ReSetProps(); //@cmember Commits current txn, fNewUnitOfWork sets whether //a new transaction is started, or we revert to autocommit mode. //fPreserveRowset sets whether or not the rowset is kept after the commit. //Also makes sure we have a current IRowsetUpdate on the preserved rowset virtual HRESULT Commit(BOOL fPreserveRowset = FALSE, BOOL fNewUnitOfWork = FALSE); //@cmember Aborts current txn, fNewUnitOfWork sets whether //a new transaction is started, or we revert to autocommit mode. //fPreserveRowset sets whether or not the rowset is kept after the abort. //Also makes sure we have a current IRowsetUpdate on the preserved rowset virtual HRESULT Abort(BOOL fPreserveRowset = FALSE, BOOL fNewUnitOfWork = FALSE); //@cmember Verifies pending change after a transaction commit or abort BOOL CheckPendingRow(ETXN_END_TYPE eTxnEndType, ULONG cPendingRows); }; //-------------------------------------------------------------------- // @class CUpdateTxn | Base Class for IRowsetUpdate / Transaction testing. // @base public | CTxn //-------------------------------------------------------------------- class CTxnUpdate : public CTxn { // @access public public: //@cmember Pointer to Rowset object that supports Changes in buffered mode CTxnChgUpdRowset * m_pChgUpdRowset1; //@cmember Pointer to Rowset object that supports Changes in buffered mode CTxnChgUpdRowset * m_pChgUpdRowset2; //@cmember CTOR CTxnUpdate(LPWSTR tcName) : CTxn(tcName){}; //@cmember Initialization virtual BOOL Init(); //@cmember Termination virtual BOOL Terminate(); //@cmember Releases all rowsets associated with any encapsulated objects virtual void ReleaseAllRowsetsAndTxns(); }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Base Class Function Definitions // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //-------------------------------------------------------------------- // Does init for buffered mode rowset. // @mfunc Init //-------------------------------------------------------------------- BOOL CTxnChgUpdRowset::Init() { const ULONG cProps = 5; DBPROP DBProp[cProps]; DBPROPSET RowsetPropSet; BOOL fInit = FALSE; ULONG i = 0; m_pIRowsetUpdate = NULL; m_pIRowset = NULL; m_cPendingRows = 0; m_rgPendingRows = NULL; m_rgPendingStatus = NULL; //Set properties for change ability and determine support fInit = CTxnChgRowset::Init(); // if (!m_rgPropSets) // { if (g_rgDBPrpt[IDX_IRowsetChange].fProviderSupported) { //Now reset all properties and include IRowsetUpdate DBProp[0].dwPropertyID = DBPROP_IRowsetChange; DBProp[0].dwOptions=DBPROPOPTIONS_REQUIRED; DBProp[0].colid=DB_NULLID; DBProp[0].vValue.vt=VT_BOOL; V_BOOL(&(DBProp[0].vValue))=VARIANT_TRUE; DBProp[1].dwPropertyID= DBPROP_IRowsetUpdate; DBProp[1].dwOptions=DBPROPOPTIONS_REQUIRED; DBProp[1].colid=DB_NULLID; DBProp[1].vValue.vt=VT_BOOL; V_BOOL(&(DBProp[1].vValue))=VARIANT_TRUE; DBProp[2].dwPropertyID= DBPROP_UPDATABILITY; DBProp[2].dwOptions=DBPROPOPTIONS_REQUIRED; DBProp[2].colid=DB_NULLID; DBProp[2].vValue.vt=VT_I4; DBProp[2].vValue.lVal= DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE; i=3; } if (g_rgDBPrpt[IDX_OtherInsert].fProviderSupported) { DBProp[i].dwPropertyID= DBPROP_OTHERINSERT; DBProp[i].dwOptions=DBPROPOPTIONS_REQUIRED; DBProp[i].colid=DB_NULLID; DBProp[i].vValue.vt=VT_BOOL; V_BOOL(&(DBProp[i].vValue))=VARIANT_TRUE; i++; } if (g_rgDBPrpt[IDX_OtherUpdateDelete].fProviderSupported) { DBProp[i].dwPropertyID= DBPROP_OTHERUPDATEDELETE; DBProp[i].dwOptions=DBPROPOPTIONS_REQUIRED; DBProp[i].colid=DB_NULLID; DBProp[i].vValue.vt=VT_BOOL; V_BOOL(&(DBProp[i].vValue))=VARIANT_TRUE; i++; } //Build one set of rowset properties RowsetPropSet.rgProperties = DBProp; RowsetPropSet.cProperties = i; RowsetPropSet.guidPropertySet = DBPROPSET_ROWSET; //Now we'll be ready to execute and get a buffered mode rowset SetRowsetProperties(&RowsetPropSet, 1); // } return fInit; } //-------------------------------------------------------------------- // makes sure props are set. // @mfunc //-------------------------------------------------------------------- HRESULT CTxnChgUpdRowset::ReSetProps() { const ULONG cProps = 3; DBPROP DBProp[cProps]; DBPROPSET RowsetPropSet; ULONG i = 0; if (g_rgDBPrpt[IDX_IRowsetChange].fProviderSupported) { //Now reset all properties and include IRowsetUpdate DBProp[0].dwPropertyID = DBPROP_IRowsetChange; DBProp[0].dwOptions=DBPROPOPTIONS_REQUIRED; DBProp[0].colid=DB_NULLID; DBProp[0].vValue.vt=VT_BOOL; V_BOOL(&(DBProp[0].vValue))=VARIANT_TRUE; DBProp[1].dwPropertyID= DBPROP_IRowsetUpdate; DBProp[1].dwOptions=DBPROPOPTIONS_REQUIRED; DBProp[1].colid=DB_NULLID; DBProp[1].vValue.vt=VT_BOOL; V_BOOL(&(DBProp[1].vValue))=VARIANT_TRUE; DBProp[2].dwPropertyID= DBPROP_UPDATABILITY; DBProp[2].dwOptions=DBPROPOPTIONS_REQUIRED; DBProp[2].colid=DB_NULLID; DBProp[2].vValue.vt=VT_I4; DBProp[2].vValue.lVal= DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE; //Build one set of rowset properties RowsetPropSet.rgProperties = DBProp; RowsetPropSet.cProperties = cProps; RowsetPropSet.guidPropertySet = DBPROPSET_ROWSET; //Now we'll be ready to execute and get a buffered mode rowset return SetRowsetProperties(&RowsetPropSet, 1); } else { return S_OK; } } //-------------------------------------------------------------------- // Does init for buffered mode rowset. // @mfunc CreateRowset //-------------------------------------------------------------------- HRESULT CTxnChgUpdRowset::MakeRowset() { m_hr = NOERROR; //We may already have an update and rowset interface, in which //case we are assuming that MakeRowset above did not create a new //rowset, and our interfaces will still be valid, so we'll just return if (!m_pIRowsetUpdate) { //Create the rowset and get IRowsetUpdate interface to it if (CHECK(m_hr = ReSetProps(), S_OK)) { //Create the rowset and get IRowsetUpdate interface to it if (CHECK(m_hr = CTxnChgRowset::MakeRowset(), S_OK)) { //If our Init passed, we know IRowsetUpdate should always be available if (VerifyInterface(m_pIAccessor, IID_IRowsetUpdate, ROWSET_INTERFACE, (IUnknown **)&m_pIRowsetUpdate)) { if (VerifyInterface(m_pIAccessor, IID_IRowset, ROWSET_INTERFACE, (IUnknown **)&m_pIRowset)) { return NOERROR; } else { m_hr = ResultFromScode(E_NOINTERFACE); } } else { m_hr = ResultFromScode(E_NOINTERFACE); } } } } return m_hr; } //-------------------------------------------------------------------- // Does init for buffered mode rowset. // @mfunc CreateRowset //-------------------------------------------------------------------- HRESULT CTxnChgUpdRowset::FreeRowset() { SAFE_RELEASE(m_pIRowsetUpdate); SAFE_RELEASE(m_pIRowset); ReleaseRowsetObject(); return NOERROR; } //-------------------------------------------------------------------- // Makes sure we have a current IRowsetUpdate even if we recreate the rowset // @mfunc Commit //-------------------------------------------------------------------- HRESULT CTxnChgUpdRowset::Commit(BOOL fPreserveRowset, BOOL fNewUnitOfWork) { //If the rowset won't last but we expect it to, release and //NULL out our interfaces, we'll create a new ones later if (!m_fCommitPreserve && fPreserveRowset) { SAFE_RELEASE(m_pIRowsetUpdate); SAFE_RELEASE(m_pIRowset); } //Do the commit return (CTxnChgRowset::Commit(fPreserveRowset, fNewUnitOfWork)); return m_hr; } //-------------------------------------------------------------------- // Makes sure we have a current IRowsetUpdate even if we recreate the rowset // @mfunc Abort //-------------------------------------------------------------------- HRESULT CTxnChgUpdRowset::Abort(BOOL fPreserveRowset, BOOL fNewUnitOfWork) { //If the rowset won't last but we expect it to, release and //NULL out IRowsetUpdate, we'll create a new one later if (!m_fAbortPreserve && fPreserveRowset) { SAFE_RELEASE(m_pIRowsetUpdate); SAFE_RELEASE(m_pIRowset); } //Do the abort return (CTxnChgRowset::Abort(fPreserveRowset, fNewUnitOfWork)); } //-------------------------------------------------------------------- // Verifies pending row from change, if rowset isn't zombied // @mfunc CheckPendingRow //-------------------------------------------------------------------- BOOL CTxnChgUpdRowset::CheckPendingRow(ETXN_END_TYPE eTxnType, ULONG cPendingRows) { BOOL fResults = FALSE; ULONG i; ULONG cPending; if (!cPendingRows) { cPending=S_FALSE;//no rows are pending } else { cPending=S_OK; } //Check the correct property to see if the end of the txn //zombied the rowset, in which case we can't check pending rows if (eTxnType == EABORT && !m_fAbortPreserve) return TRUE; if (eTxnType == ECOMMIT && !m_fCommitPreserve) return TRUE; //We know the rowset should be in tact, so we can check PendingRows if (CHECK(m_pIRowsetUpdate->GetPendingRows(NULL, DBPENDINGSTATUS_NEW | DBPENDINGSTATUS_CHANGED | DBPENDINGSTATUS_DELETED, &m_cPendingRows, &m_rgPendingRows, &m_rgPendingStatus), cPending)) { //THESE ARE COMMENTED OUT BECAUSE THESE >CAN< BE NULL & ZERO IF NO ROW ARE PENDING //Make sure our pointers are valid //if (!m_rgPendingRows || !m_rgPendingStatus) // goto CLEANUP; //Make sure we have the right number of pending changes if (COMPARE(m_cPendingRows, cPendingRows)) { //For each row, check the status for (i=0; iReleaseRows(m_cPendingRows, m_rgPendingRows, NULL, NULL, NULL), S_OK); if (m_rgPendingStatus) m_pIMalloc->Free(m_rgPendingStatus); return fResults; } //-------------------------------------------------------------------- // Does all termination, including releasing m_pIRowsetUpdate if needed. // @mfunc Terminate //-------------------------------------------------------------------- BOOL CTxnChgUpdRowset::Terminate() { SAFE_RELEASE(m_pIRowsetUpdate); SAFE_RELEASE(m_pIRowset); return CTxnChgRowset::Terminate(); } //-------------------------------------------------------------------- // Inits the CTxnUpdate Object. It inits the encapsulated objects // which are unique to CTxnUpdate // @mfunc Init //-------------------------------------------------------------------- BOOL CTxnUpdate::Init() { if (CTxn::Init()) { //Create our encapsulated ChgUpdRowset objects m_pChgUpdRowset1 = new CTxnChgUpdRowset((LPWSTR)gwszModuleName); m_pChgUpdRowset2 = new CTxnChgUpdRowset((LPWSTR)gwszModuleName); if (m_pChgUpdRowset1 && m_pChgUpdRowset2) { //Initialize pointers m_pChgUpdRowset1->m_pIRowsetUpdate = NULL; m_pChgUpdRowset1->m_pIRowset = NULL; m_pChgUpdRowset1->m_cPendingRows = 0; m_pChgUpdRowset1->m_rgPendingRows = NULL; m_pChgUpdRowset1->m_rgPendingStatus = NULL; m_pChgUpdRowset2->m_pIRowsetUpdate = NULL; m_pChgUpdRowset2->m_pIRowset = NULL; m_pChgUpdRowset2->m_cPendingRows = 0; m_pChgUpdRowset2->m_rgPendingRows = NULL; m_pChgUpdRowset2->m_rgPendingStatus = NULL; //Copy the stuff which normally gets initialized at //testcase initialization time, but didn't for these //objects since they are encapsulated rather than //inherited by the testcase object. CopyTestCaseInfo(m_pChgUpdRowset1); CopyTestCaseInfo(m_pChgUpdRowset2); //Set the same DSO and table for our encapsulated rowset objects //also create each rowset if (m_pChgUpdRowset1->Init() && m_pChgUpdRowset2->Init()) return TRUE; } } return FALSE; } //-------------------------------------------------------------------- // Releases all rowsets associated with encapsulated rowset objects // and does an abort of all open transactions // @mfunc ReleaseAllRowsetsAndTxns //-------------------------------------------------------------------- void CTxnUpdate::ReleaseAllRowsetsAndTxns() { //Clean up all rowset interfaces m_pChgUpdRowset1->ReleaseRowsetObject(); SAFE_RELEASE(m_pChgUpdRowset1->m_pIRowset); SAFE_RELEASE(m_pChgUpdRowset1->m_pIRowsetUpdate); m_pChgUpdRowset2->ReleaseRowsetObject(); SAFE_RELEASE(m_pChgUpdRowset2->m_pIRowset); SAFE_RELEASE(m_pChgUpdRowset2->m_pIRowsetUpdate); //We don't have a return value, since its just cleanup, //which may not succeed because its not necessary m_pChgUpdRowset1->Abort(FALSE, FALSE); m_pChgUpdRowset2->Abort(FALSE, FALSE); } //-------------------------------------------------------------------- // Cleans up the CTxn Object // @mfunc Terminate //-------------------------------------------------------------------- BOOL CTxnUpdate::Terminate() { //Clean up encapsulated objects if (m_pChgUpdRowset1) { m_pChgUpdRowset1->Terminate(); delete m_pChgUpdRowset1; } if (m_pChgUpdRowset2) { m_pChgUpdRowset2->Terminate(); delete m_pChgUpdRowset2; } return CTxn::Terminate(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Miscellaneous Function Definitions // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //-------------------------------------------------------------------- // Does a non retaining commit on the given rowset. This function is // intended to be envoked by CreateThread or ResumeThread, so the commit // will be done on a second thread. // // @mfunc ThreadCommit //-------------------------------------------------------------------- LRESULT ThreadCommit(LPVOID pChgRowset) { ULONG ul; //We want to make sure our other operation has a chance to start before we continue. //Specifying zero will immediately turn our time slice over to another thread. Sleep(0); if (FAILED(CoInitialize(NULL))) return ResultFromScode(E_FAIL); ul = (ULONG)((CTxnChgRowset *)pChgRowset)->m_pITxnLocal->Commit(FALSE, 0, 0); CoUninitialize(); return ul; } //-------------------------------------------------------------------- // Does a non retaining abort on the given rowset. This function is // intended to be envoked by CreateThread or ResumeThread, so the abort // will be done on a second thread. // // @mfunc ThreadAbort //-------------------------------------------------------------------- LRESULT ThreadAbort(LPVOID pChgRowset) { ULONG ul; //We want to make sure our other operation has a chance to start before we continue. //Specifying zero will immediately turn our time slice over to another thread. Sleep(0); if (FAILED(CoInitialize(NULL))) return ResultFromScode(E_FAIL); ul = (ULONG)((CTxnChgRowset *)pChgRowset)->m_pITxnLocal->Abort(NULL, FALSE, FALSE); CoUninitialize(); return ul; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Test Case Section // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // {{ TCW_TEST_CASE_MAP(TCIsoLevel) //-------------------------------------------------------------------- // @class Isolation Level Testing // class TCIsoLevel : public CTxnImmed { private: // @cmember Static array of variations DECLARE_TEST_CASE_DATA(); public: // {{ TCW_DECLARE_FUNCS // @cmember Execution Routine DECLARE_TEST_CASE_FUNCS(TCIsoLevel,CTxnImmed); // }} // @cmember Initialization Routine virtual BOOL Init(); // @cmember Termination Routine virtual BOOL Terminate(); // {{ TCW_TESTVARS() // @cmember Read Uncommitted int Variation_1(); // @cmember Read Committed int Variation_2(); // @cmember Repeatable Read int Variation_3(); // @cmember Serializable int Variation_4(); // @cmember Chaos int Variation_5(); // @cmember Unspecified int Variation_6(); // }} }; // {{ TCW_TESTCASE(TCIsoLevel) #define THE_CLASS TCIsoLevel BEG_TEST_CASE(TCIsoLevel, CTxnImmed, L"Isolation Level Testing") TEST_VARIATION(1, L"Read Uncommitted") TEST_VARIATION(2, L"Read Committed") TEST_VARIATION(3, L"Repeatable Read") TEST_VARIATION(4, L"Serializable") TEST_VARIATION(5, L"Chaos") TEST_VARIATION(6, L"Unspecified") END_TEST_CASE() #undef THE_CLASS // }} // }} // {{ TCW_TEST_CASE_MAP(TCRetainPreserve) //-------------------------------------------------------------------- // @class Retaining/Preserving Behavior // class TCRetainPreserve : public CTxnImmed { private: // @cmember Static array of variations DECLARE_TEST_CASE_DATA(); public: // {{ TCW_DECLARE_FUNCS // @cmember Execution Routine DECLARE_TEST_CASE_FUNCS(TCRetainPreserve,CTxnImmed); // }} // @cmember Initialization Routine virtual BOOL Init(); // @cmember Termination Routine virtual BOOL Terminate(); //@cmember Function to test Commit with fRetaining = TRUE BOOL CommitRetain(ISOLEVEL fIsoLevel); //@cmember Function to test Commit with fRetaining = FALSE BOOL CommitNonRetain(ISOLEVEL fIsoLevel); //@cmember Function to test Abort with fRetaining = TRUE BOOL AbortRetain(ISOLEVEL fIsoLevel); //@cmember Function to test Abort with fRetaining = FALSE BOOL AbortNonRetain(ISOLEVEL fIsoLevel); // {{ TCW_TESTVARS() // @cmember Commit(fRetaining = TRUE int Variation_1(); // @cmember Commit(fRetaining = FALSE int Variation_2(); // @cmember Abort(fRetaining = TRUE int Variation_3(); // @cmember Abort(fRetaining = FALSE int Variation_4(); // }} }; // {{ TCW_TESTCASE(TCRetainPreserve) #define THE_CLASS TCRetainPreserve BEG_TEST_CASE(TCRetainPreserve, CTxnImmed, L"Retaining/Preserving Behavior") TEST_VARIATION(1, L"Commit(fRetaining = TRUE") TEST_VARIATION(2, L"Commit(fRetaining = FALSE") TEST_VARIATION(3, L"Abort(fRetaining = TRUE") TEST_VARIATION(4, L"Abort(fRetaining = FALSE") END_TEST_CASE() #undef THE_CLASS // }} // }} // {{ TCW_TEST_CASE_MAP(TCITxnReturnVals) //-------------------------------------------------------------------- // @class ITransaction Return Values // class TCITxnReturnVals : public CTxnImmed { private: // @cmember Static array of variations DECLARE_TEST_CASE_DATA(); public: // {{ TCW_DECLARE_FUNCS // @cmember Execution Routine DECLARE_TEST_CASE_FUNCS(TCITxnReturnVals,CTxnImmed); // }} // @cmember Txn Object interface for session ITransactionOptions * m_pITxnOptions; // @cmember Initialization Routine virtual BOOL Init(); // @cmember Termination Routine virtual BOOL Terminate(); // {{ TCW_TESTVARS() // @cmember Abort with pboidReason = NULL - S_OK int Variation_1(); // @cmember Abort with pboidReason = BOID_NULL - S_OK int Variation_2(); // @cmember Abort with fAsync=TRUE - XACT_E_NOTSUPPORTED or S_OK int Variation_3(); // @cmember Abort with fAsync=FALSE - S_OK int Variation_4(); // @cmember Call commit on one thread, Abort on another - XACT_E_ALREADYINPROGRESS int Variation_5(); // @cmember Call abort on one thread and then on another - XACT_S_ABORTING int Variation_6(); // @cmember Commit with grfTC =XACTC_ASYNC - XACT_E_NOTSUPPORTED or S_OK int Variation_7(); // @cmember Commit with grfTC =XACTC_SYNC_PHASEONE - XACT_E_NOTSUPPORTED or S_OK int Variation_8(); // @cmember Commit with grfRM!=0 - XACT_E_NOTSUPPORTED int Variation_9(); // @cmember Call Abort on one thread, Commit on another - XACT_E_ALREADYINPROGRESS int Variation_10(); // @cmember Call commit on one thread, and then on another - XACT_E_ALREADYINPROGRESS int Variation_11(); // @cmember Commit with reported supported grfTC values - S_OK int Variation_12(); // @cmember GetTransactionInfo with pinfo = NULL int Variation_13(); // @cmember GetOptionsObject with ppOptions = NULL - E_INVALIDARG int Variation_14(); // @cmember StartTransaction twice in a row - S_OK or XACT_E_XTIONEXISTS int Variation_15(); // @cmember StartTransaction with a non zero timeout - S_OK or XACT_E_NOTIMEOUT int Variation_16(); // @cmember StartTransaction with zero timeout - S_OK int Variation_17(); // @cmember StartTransaction with pulTransactionLevel = NULL - S_OK int Variation_18(); // @cmember StartTransaction with isoFlags != 0 - XACT_E_NOISORETAIN int Variation_19(); // @cmember StartTransaction with isoLevel = invalid - XACT_E_ISOLATIONLEVEL int Variation_20(); // @cmember GetOptions with pOptions = NULL - E_INVALIDARG int Variation_21(); // @cmember GetOptions with pOptions allocated on stack - S_OK int Variation_22(); // @cmember SetOptions with max szDescription int Variation_23(); // @cmember SetOptions with ppOptions = NULL - E_INVALIDARG int Variation_24(); // @cmember Commit with grfTC =XACTC_SYNC_PHASETWO - S_OK int Variation_25(); // }} }; // {{ TCW_TESTCASE(TCITxnReturnVals) #define THE_CLASS TCITxnReturnVals BEG_TEST_CASE(TCITxnReturnVals, CTxnImmed, L"ITransaction Return Values") TEST_VARIATION(1, L"Abort with pboidReason = NULL - S_OK") TEST_VARIATION(2, L"Abort with pboidReason = BOID_NULL - S_OK") TEST_VARIATION(3, L"Abort with fAsync=TRUE - XACT_E_NOTSUPPORTED or S_OK") TEST_VARIATION(4, L"Abort with fAsync=FALSE - S_OK") TEST_VARIATION(5, L"Call commit on one thread, Abort on another - XACT_E_ALREADYINPROGRESS") TEST_VARIATION(6, L"Call abort on one thread and then on another - XACT_S_ABORTING") TEST_VARIATION(7, L"Commit with grfTC =XACTC_ASYNC - XACT_E_NOTSUPPORTED or S_OK") TEST_VARIATION(8, L"Commit with grfTC =XACTC_SYNC_PHASEONE - XACT_E_NOTSUPPORTED or S_OK") TEST_VARIATION(9, L"Commit with grfRM!=0 - XACT_E_NOTSUPPORTED") TEST_VARIATION(10, L"Call Abort on one thread, Commit on another - XACT_E_ALREADYINPROGRESS") TEST_VARIATION(11, L"Call commit on one thread, and then on another - XACT_E_ALREADYINPROGRESS") TEST_VARIATION(12, L"Commit with reported supported grfTC values - S_OK") TEST_VARIATION(13, L"GetTransactionInfo with pinfo = NULL") TEST_VARIATION(14, L"GetOptionsObject with ppOptions = NULL - E_INVALIDARG") TEST_VARIATION(15, L"StartTransaction twice in a row - S_OK or XACT_E_XTIONEXISTS") TEST_VARIATION(16, L"StartTransaction with a non zero timeout - S_OK or XACT_E_NOTIMEOUT") TEST_VARIATION(17, L"StartTransaction with zero timeout - S_OK") TEST_VARIATION(18, L"StartTransaction with pulTransactionLevel = NULL - S_OK") TEST_VARIATION(19, L"StartTransaction with isoFlags != 0 - XACT_E_NOISORETAIN") TEST_VARIATION(20, L"StartTransaction with isoLevel = invalid - XACT_E_ISOLATIONLEVEL") TEST_VARIATION(21, L"GetOptions with pOptions = NULL - E_INVALIDARG") TEST_VARIATION(22, L"GetOptions with pOptions allocated on stack - S_OK") TEST_VARIATION(23, L"SetOptions with max szDescription") TEST_VARIATION(24, L"SetOptions with ppOptions = NULL - E_INVALIDARG") TEST_VARIATION(25, L"Commit with grfTC =XACTC_SYNC_PHASETWO - S_OK") END_TEST_CASE() #undef THE_CLASS // }} // }} // {{ TCW_TEST_CASE_MAP(TCIRowsetUpdate) //-------------------------------------------------------------------- // @class Transacted rowsets in Buffered Update mode // class TCIRowsetUpdate : public CTxnUpdate { private: // @cmember Static array of variations DECLARE_TEST_CASE_DATA(); public: // {{ TCW_DECLARE_FUNCS // @cmember Execution Routine DECLARE_TEST_CASE_FUNCS(TCIRowsetUpdate,CTxnUpdate); // }} // @cmember Initialization Routine virtual BOOL Init(); // @cmember Termination Routine virtual BOOL Terminate(); // {{ TCW_TESTVARS() // @cmember Change, Abort with fRetaining = TRUE, Update int Variation_1(); // @cmember Change, Abort with fRetaining = FALSE, Update int Variation_2(); // @cmember Change, Update, Abort with fRetaining = TRUE int Variation_3(); // @cmember Change, Update, Abort with fRetaining = FALSE int Variation_4(); // @cmember Change, Commit with fRetaining = TRUE, Update int Variation_5(); // @cmember Change, Commit with fRetaining = FALSE, Update int Variation_6(); // @cmember Change, Update, Commit with fRetaining = TRUE int Variation_7(); // @cmember Change, Update, Commit with fRetaining = FALSE int Variation_8(); // @cmember Open read only rowset int Variation_9(); // }} }; // {{ TCW_TESTCASE(TCIRowsetUpdate) #define THE_CLASS TCIRowsetUpdate BEG_TEST_CASE(TCIRowsetUpdate, CTxnUpdate, L"Transacted rowsets in Buffered Update mode") TEST_VARIATION(1, L"Change, Abort with fRetaining = TRUE, Update") TEST_VARIATION(2, L"Change, Abort with fRetaining = FALSE, Update") TEST_VARIATION(3, L"Change, Update, Abort with fRetaining = TRUE") TEST_VARIATION(4, L"Change, Update, Abort with fRetaining = FALSE") TEST_VARIATION(5, L"Change, Commit with fRetaining = TRUE, Update") TEST_VARIATION(6, L"Change, Commit with fRetaining = FALSE, Update") TEST_VARIATION(7, L"Change, Update, Commit with fRetaining = TRUE") TEST_VARIATION(8, L"Change, Update, Commit with fRetaining = FALSE") TEST_VARIATION(9, L"Open read only rowset") END_TEST_CASE() #undef THE_CLASS // }} // }} // {{ TCW_TEST_CASE_MAP(CNoTxn) //-------------------------------------------------------------------- // @class Abort and Commit called before StartTransaction // class CNoTxn : public CSessionObject { private: // @cmember Static array of variations DECLARE_TEST_CASE_DATA(); //ITransactionLocal interface ITransactionLocal * m_pITxnLocal; public: // {{ TCW_DECLARE_FUNCS // @cmember Execution Routine DECLARE_TEST_CASE_FUNCS(CNoTxn,CSessionObject); // }} // @cmember Initialization Routine virtual BOOL Init(); // @cmember Termination Routine virtual BOOL Terminate(); // {{ TCW_TESTVARS() // @cmember Abort with fRetaining = FALSE before StartTransaction - XACT_E_NOTRANSACTION int Variation_1(); // @cmember Commit with fRetaining = FALSE before StartTransaction - XACT_E_NOTRANSACTION int Variation_2(); // @cmember GetTransactionInfo before StartTransaction - XACT_E_NOTRANSACTION int Variation_3(); // @cmember GetOptionsObject before StartTransaction - S_OK int Variation_4(); // @cmember Abort with fRetaining = TRUE before StartTransaction - XACT_E_NOTRANSACTION int Variation_5(); // @cmember Commit with fRetaining = TRUE before StartTransaction - XACT_E_NOTRANSACTION int Variation_6(); // }} }; // {{ TCW_TESTCASE(CNoTxn) #define THE_CLASS CNoTxn BEG_TEST_CASE(CNoTxn, CSessionObject, L"Abort and Commit called before StartTransaction") TEST_VARIATION(1, L"Abort with fRetaining = FALSE before StartTransaction - XACT_E_NOTRANSACTION ") TEST_VARIATION(2, L"Commit with fRetaining = FALSE before StartTransaction - XACT_E_NOTRANSACTION ") TEST_VARIATION(3, L"GetTransactionInfo before StartTransaction - XACT_E_NOTRANSACTION") TEST_VARIATION(4, L"GetOptionsObject before StartTransaction - S_OK") TEST_VARIATION(5, L"Abort with fRetaining = TRUE before StartTransaction - XACT_E_NOTRANSACTION ") TEST_VARIATION(6, L"Commit with fRetaining = TRUE before StartTransaction - XACT_E_NOTRANSACTION ") END_TEST_CASE() #undef THE_CLASS // }} // }} // {{ TCW_TEST_CASE_MAP(CMultipleTxns) //-------------------------------------------------------------------- // @class Multiple Transactions // class CMultipleTxns : public CTxnImmed { private: // @cmember Static array of variations DECLARE_TEST_CASE_DATA(); public: // {{ TCW_DECLARE_FUNCS // @cmember Execution Routine DECLARE_TEST_CASE_FUNCS(CMultipleTxns,CTxnImmed); // }} //@cmember Second table for second rowset CTable * m_pTable; //@cemember Actual delete count for second table ULONG m_ulCurrentDelete; //@cemember Actual insert count for second table ULONG m_ulCurrentInsert; // @cmember Initialization Routine virtual BOOL Init(); // @cmember Termination Routine virtual BOOL Terminate(); // {{ TCW_TESTVARS() // @cmember Abort two transactions on same DSO int Variation_1(); // @cmember Commit two transactions on same DSO int Variation_2(); // @cmember Abort one transaction, commit the other, on same DSO int Variation_3(); // @cmember Abort and Commit with multiple commands/rowsets active int Variation_4(); // @cmember Multiple Sessions, Transaction on only one int Variation_5(); // }} }; // {{ TCW_TESTCASE(CMultipleTxns) #define THE_CLASS CMultipleTxns BEG_TEST_CASE(CMultipleTxns, CTxnImmed, L"Multiple Transactions") TEST_VARIATION(1, L"Abort two transactions on same DSO") TEST_VARIATION(2, L"Commit two transactions on same DSO") TEST_VARIATION(3, L"Abort one transaction, commit the other, on same DSO") TEST_VARIATION(4, L"Abort and Commit with multiple commands/rowsets active") TEST_VARIATION(5, L"Multiple Sessions, Transaction on only one") END_TEST_CASE() #undef THE_CLASS // }} // }} // {{ TCW_TEST_CASE_MAP(CSequence) //-------------------------------------------------------------------- // @class Sequence testing for StartTransaction and ITransactionOptions // class CSequence : public CTxn { private: // @cmember Static array of variations DECLARE_TEST_CASE_DATA(); public: // {{ TCW_DECLARE_FUNCS // @cmember Execution Routine DECLARE_TEST_CASE_FUNCS(CSequence,CTxn); // }} //@cmember Local Transaction interface ITransactionLocal * m_pITxnLocal; //@cmember Transaction Options interface ITransactionOptions * m_pITxnOptions; //@cmember Options struct XACTOPT m_TxnOptions; // @cmember Initialization Routine virtual BOOL Init(); // @cmember Termination Routine virtual BOOL Terminate(); //@cmember Cleanup function for this derived class virtual void ReleaseAllRowsetsAndTxns() { //Cleanup our transaction, if we have one if (m_pITxnLocal) m_pITxnLocal->Commit(FALSE, 0, 0); } // {{ TCW_TESTVARS() // @cmember GetOptionsObject before StartTransaction int Variation_1(); // @cmember GetOptionsObject after StartTransaction int Variation_2(); // @cmember SetOptions twice int Variation_3(); // }} }; // {{ TCW_TESTCASE(CSequence) #define THE_CLASS CSequence BEG_TEST_CASE(CSequence, CTxn, L"Sequence testing for StartTransaction and ITransactionOptions") TEST_VARIATION(1, L"GetOptionsObject before StartTransaction") TEST_VARIATION(2, L"GetOptionsObject after StartTransaction") TEST_VARIATION(3, L"SetOptions twice") END_TEST_CASE() #undef THE_CLASS // }} // }} // {{ TCW_TEST_CASE_MAP(TCExtendedErrors) //-------------------------------------------------------------------- // @class Extended Errors // class TCExtendedErrors : public CTxnImmed { private: // @cmember Static array of variations DECLARE_TEST_CASE_DATA(); // @cmember Txn Object interface for session ITransactionOptions * m_pITxnOptions; public: // {{ TCW_DECLARE_FUNCS // @cmember Execution Routine DECLARE_TEST_CASE_FUNCS(TCExtendedErrors,CTxnImmed); // }} // @cmember Initialization Routine virtual BOOL Init(); // @cmember Termination Routine virtual BOOL Terminate(); // {{ TCW_TESTVARS() // @cmember Valid ITransactionLocal calls with previous error object existing. int Variation_1(); // @cmember Valid Abort call with previous error object existing. int Variation_2(); // @cmember Valid SetOptions call with previous error object existing. int Variation_3(); // @cmember Valid GetOptions call with previous error object existing. int Variation_4(); // @cmember XACT_E_NOTSUPPORTED Commit call with previous error object existing int Variation_5(); // @cmember XACT_E_NOISORETAIN StartTransaction call with previous error object existing int Variation_6(); // @cmember E_INVALIDARG GetTransactionInfo call with previous error object existing int Variation_7(); // @cmember E_INVALIDARG GetOptionObject call with previous error object existing int Variation_8(); // @cmember XACT_E_NOTRANSACTION Abort call with previous error object existing int Variation_9(); // @cmember E_INVALIDARG GetOptions call with previous error object existing int Variation_10(); // @cmember E_INVALIDARG SetOptions call with previous error object existing int Variation_11(); // @cmember XACT_E_ISOLATIONLEVEL StartTransaction calls no with previous error object existing int Variation_12(); // @cmember E_INVALIDARG GetOptionObject calls no with previous error object existing int Variation_13(); // @cmember XACT_E_NOTRANSACTION Abort calls with no previous error object existing int Variation_14(); // @cmember XACT_E_NOTRANSACTION Commit calls with no previous error object existing int Variation_15(); // @cmember XACT_E_NOTRANSACTION GetTransactionInfo calls with no previous error object existing int Variation_16(); // @cmember E_INVALIDARG SetOptions calls with no previous error object existing int Variation_17(); // @cmember E_INVALIDARG GetOptions calls with no previous error object existing int Variation_18(); // @cmember Valid Abort calls with previous error object existing, check error on ITransaction int Variation_19(); // @cmember Valid TransactionLocal calls with previous error object existing, check error on ITransaction int Variation_20(); // @cmember XACT_E_NOTSUPPORTED Commit calls with previous error object existing, check error on ITransaction int Variation_21(); // @cmember E_INVALIDARG GetTransactionInfo calls with previous error object existing, check error on ITransaction int Variation_22(); // @cmember XACT_E_NOTRANSACTION Abort calls with previous error object existing, check error on ITransaction int Variation_23(); // @cmember XACT_E_NOTRANSACTION Abort calls with no previous error object existing, check error on ITransaction int Variation_24(); // @cmember XACT_E_NOTRANSACTION Commit calls with no previous error object existing, check error on ITransaction int Variation_25(); // @cmember XACT_E_NOTRANSACTION GetTransactionInfo calls with no previous error object existing, check error on ITransaction int Variation_26(); // @cmember DB_E_OBJECTOPEN the provider does not support starting a new transaction with existing open rowset objects int Variation_27(); // }} }; // {{ TCW_TESTCASE(TCExtendedErrors) #define THE_CLASS TCExtendedErrors BEG_TEST_CASE(TCExtendedErrors, CTxnImmed, L"Extended Errors") TEST_VARIATION(1, L"Valid ITransactionLocal calls with previous error object existing.") TEST_VARIATION(2, L"Valid Abort call with previous error object existing.") TEST_VARIATION(3, L"Valid SetOptions call with previous error object existing.") TEST_VARIATION(4, L"Valid GetOptions call with previous error object existing.") TEST_VARIATION(5, L"XACT_E_NOTSUPPORTED Commit call with previous error object existing") TEST_VARIATION(6, L"XACT_E_NOISORETAIN StartTransaction call with previous error object existing") TEST_VARIATION(7, L"E_INVALIDARG GetTransactionInfo call with previous error object existing") TEST_VARIATION(8, L"E_INVALIDARG GetOptionObject call with previous error object existing") TEST_VARIATION(9, L"XACT_E_NOTRANSACTION Abort call with previous error object existing") TEST_VARIATION(10, L"E_INVALIDARG GetOptions call with previous error object existing") TEST_VARIATION(11, L"E_INVALIDARG SetOptions call with previous error object existing") TEST_VARIATION(12, L"XACT_E_ISOLATIONLEVEL StartTransaction calls no with previous error object existing") TEST_VARIATION(13, L"E_INVALIDARG GetOptionObject calls no with previous error object existing") TEST_VARIATION(14, L"XACT_E_NOTRANSACTION Abort calls with no previous error object existing") TEST_VARIATION(15, L"XACT_E_NOTRANSACTION Commit calls with no previous error object existing") TEST_VARIATION(16, L"XACT_E_NOTRANSACTION GetTransactionInfo calls with no previous error object existing") TEST_VARIATION(17, L"E_INVALIDARG SetOptions calls with no previous error object existing") TEST_VARIATION(18, L"E_INVALIDARG GetOptions calls with no previous error object existing") TEST_VARIATION(19, L"Valid Abort calls with previous error object existing, check error on ITransaction") TEST_VARIATION(20, L"Valid TransactionLocal calls with previous error object existing, check error on ITransaction") TEST_VARIATION(21, L"XACT_E_NOTSUPPORTED Commit calls with previous error object existing, check error on ITransaction") TEST_VARIATION(22, L"E_INVALIDARG GetTransactionInfo calls with previous error object existing, check error on ITransaction") TEST_VARIATION(23, L"XACT_E_NOTRANSACTION Abort calls with previous error object existing, check error on ITransaction") TEST_VARIATION(24, L"XACT_E_NOTRANSACTION Abort calls with no previous error object existing, check error on ITransaction") TEST_VARIATION(25, L"XACT_E_NOTRANSACTION Commit calls with no previous error object existing, check error on ITransaction") TEST_VARIATION(26, L"XACT_E_NOTRANSACTION GetTransactionInfo calls with no previous error object existing, check error on ITransaction") TEST_VARIATION(27, L"DB_E_OBJECTOPEN the provider does not support starting a new transaction with existing open rowset objects") END_TEST_CASE() #undef THE_CLASS // }} // }} // {{ TCW_TEST_CASE_MAP(TCMultipleCommands) //-------------------------------------------------------------------- // @class Tests mutliple commands with and without transactions // class TCMultipleCommands : public CRowsetObject { private: // @cmember Static array of variations DECLARE_TEST_CASE_DATA(); public: // {{ TCW_DECLARE_FUNCS // @cmember Execution Routine DECLARE_TEST_CASE_FUNCS(TCMultipleCommands,CRowsetObject); // }} //@cmember Flag used if we are running ODBC Provider, indicating whether we //are using the Microsoft SQL Server ODBC driver BOOL m_fMSSQLServer; //@cmember Flag used if we are running ODBC Provider, indicating whether our ODBC driver //supports multiple hstmts BOOL m_fMultipleHstmts; //@cmember Interface to second command object ICommand * m_pICommand2; //@cmember Flag telling if current command is in firehose mode BOOL m_fFireHoseMode; // @cmember Initialization Routine virtual BOOL Init(); // @cmember Termination Routine virtual BOOL Terminate(); //@cmember Turns backwards scrolling on or off to disable/enable firehose mode HRESULT SetFirehoseMode(ICommand * pICommand, BOOL fOn); //@cmember Verifies second command can be use to get data, then releases command HRESULT VerifyAndReleaseCommand(ICommand ** pICommand, BOOL fCanScrollBackwards); //@cmember Starts true firehose mode by getting a row, which ties up the TDS connection HRESULT GetRows(IUnknown * pRowset); // {{ TCW_TESTVARS() // @cmember First rowset in Firehose mode with no transaction int Variation_1(); // @cmember First rowset in Firehose mode with a transaction int Variation_2(); // @cmember Second rowset in firehose mode with no transaction int Variation_3(); // @cmember Second rowset in firehose mode with a transaction int Variation_4(); // }} }; // {{ TCW_TESTCASE(TCMultipleCommands) #define THE_CLASS TCMultipleCommands BEG_TEST_CASE(TCMultipleCommands, CRowsetObject, L"Tests mutliple commands with and without transactions") TEST_VARIATION(1, L"First rowset in Firehose mode with no transaction") TEST_VARIATION(2, L"First rowset in Firehose mode with a transaction") TEST_VARIATION(3, L"Second rowset in firehose mode with no transaction") TEST_VARIATION(4, L"Second rowset in firehose mode with a transaction") END_TEST_CASE() #undef THE_CLASS // }} // }} // {{ TCW_TEST_CASE_MAP(TCExtendedErrors2) //-------------------------------------------------------------------- // @class Check extended errors on multipla commands // class TCExtendedErrors2 : public TCMultipleCommands { private: // @cmember Static array of variations DECLARE_TEST_CASE_DATA(); public: // {{ TCW_DECLARE_FUNCS // @cmember Execution Routine DECLARE_TEST_CASE_FUNCS(TCExtendedErrors2,TCMultipleCommands); // }} // @cmember Initialization Routine virtual BOOL Init(); // @cmember Termination Routine virtual BOOL Terminate(); // {{ TCW_TESTVARS() // @cmember open a txn in firehose mode -- E_FAIL int Variation_1(); // @cmember second rowset with the first rowset in firehose mode within a txn -- E_FAIL int Variation_2(); // @cmember create two connection with one in firehose, start txn -- XACT_E_CONNECTION_DENIED int Variation_3(); // }} }; // {{ TCW_TESTCASE(TCExtendedErrors2) #define THE_CLASS TCExtendedErrors2 BEG_TEST_CASE(TCExtendedErrors2, TCMultipleCommands, L"Check extended errors on multipla commands") TEST_VARIATION(1, L"open a txn in firehose mode -- E_FAIL") TEST_VARIATION(2, L"second rowset with the first rowset in firehose mode within a txn -- E_FAIL") TEST_VARIATION(3, L"create two connection with one in firehose, start txn -- XACT_E_CONNECTION_DENIED") END_TEST_CASE() #undef THE_CLASS // }} // }} // {{ TCW_TEST_CASE_MAP(TCRetainPreserve) //-------------------------------------------------------------------- // @class Retaining/Preserving Behavior // class TCITransactionObject : public CTxnImmed { private: // @cmember Static array of variations DECLARE_TEST_CASE_DATA(); public: // {{ TCW_DECLARE_FUNCS // @cmember Execution Routine DECLARE_TEST_CASE_FUNCS(TCITransactionObject,CTxnImmed); // }} // @cmember Initialization Routine virtual BOOL Init(); // @cmember Termination Routine virtual BOOL Terminate(); // {{ TCW_TESTVARS() // @Legal - Legal - Nested and ITransactionObject int Variation_1(); // @Illegal Illegal ulTransactionLevel arg GetTransactionObject int Variation_2(); // @Illegal Illegal ppITransaction arg GetTransactionObject int Variation_3(); // @Illegal just Nested int Variation_4(); // }} }; // {{ TCW_TESTCASE(TCITransactionObject) #define THE_CLASS TCITransactionObject BEG_TEST_CASE(TCITransactionObject, CTxnImmed, L"ITransactionObject/Nested Transactions") TEST_VARIATION(1, L"Legal - Nested and ITransactionObject") TEST_VARIATION(2, L"Illegal ulTransactionLevel arg GetTransactionObject") TEST_VARIATION(3, L"Illegal ppITransaction arg GetTransactionObject") TEST_VARIATION(4, L"just Nested") END_TEST_CASE() #undef THE_CLASS // }} // }} // }} END_DECLARE_TEST_CASES() // {{ TCW_TESTMODULE(ThisModule) TEST_MODULE(11, ThisModule, gwszModuleDescrip) TEST_CASE(1, TCIsoLevel) TEST_CASE(2, TCRetainPreserve) TEST_CASE(3, TCITxnReturnVals) TEST_CASE(4, TCIRowsetUpdate) TEST_CASE(5, CNoTxn) TEST_CASE(6, CMultipleTxns) TEST_CASE(7, CSequence) TEST_CASE(8, TCExtendedErrors) TEST_CASE(9, TCMultipleCommands) TEST_CASE(10, TCExtendedErrors2) TEST_CASE(11, TCITransactionObject) END_TEST_MODULE() // }} // {{ TCW_TC_PROTOTYPE(TCIsoLevel) //*----------------------------------------------------------------------- //| Test Case: TCIsoLevel - Isolation Level Testing //| Created: 04/16/96 //*----------------------------------------------------------------------- //-------------------------------------------------------------------- // @mfunc TestCase Initialization Routine // // @rdesc TRUE or FALSE // BOOL TCIsoLevel::Init() { // {{ TCW_INIT_BASECLASS_CHECK if(CTxnImmed::Init()) // }} { return TRUE; } return FALSE; } // {{ TCW_VAR_PROTOTYPE(1) //*----------------------------------------------------------------------- // @mfunc Read Uncommitted // // @rdesc TEST_PASS or TEST_FAIL // int TCIsoLevel::Variation_1() { BOOL fResults = FALSE; XACTUOW uow1, uow2; //If no provider support for this isolation level, just return pass if (!m_fReadUncommitted) { odtLog << wszNoProviderSupport << L"READ UNCOMMITTED" << wszNewLine; return TEST_PASS; } //Start txn we're testing at Read Uncommitted Isolation Level if (CHECK(m_pChgRowset1->StartTxn(ISOLATIONLEVEL_READUNCOMMITTED), S_OK)) { //Make sure the Txn Info is correct VerifyTxnInfo(m_pChgRowset1->m_pITxnLocal, ISOLATIONLEVEL_READUNCOMMITTED, &uow1); //We'll verify by using a second txn in Read Committed Isolation Level if (CHECK(m_pChgRowset2->StartTxn(ISOLATIONLEVEL_READCOMMITTED), S_OK)) { //Make sure the Txn Info is correct VerifyTxnInfo(m_pChgRowset2->m_pITxnLocal, ISOLATIONLEVEL_READCOMMITTED, &uow2); //Make sure the Unit of Work Identifiers are unique if (memcmp(uow1.rgb, uow2.rgb, sizeof(uow1.rgb))) fResults = TRUE; } } //Release each rowset and end all transactions ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; return TEST_PASS; } // }} // {{ TCW_VAR_PROTOTYPE(2) //*----------------------------------------------------------------------- // @mfunc Read Committed // // @rdesc TEST_PASS or TEST_FAIL // int TCIsoLevel::Variation_2() { BOOL fResults = FALSE; XACTUOW uow1, uow2; //If no provider support for this isolation level, just return pass if (!m_fReadCommitted) { odtLog << wszNoProviderSupport << L"READ COMMITTED" << wszNewLine; return TEST_PASS; } //Start txn we're testing at Read Committed Isolation Level if (CHECK(m_pChgRowset1->StartTxn(ISOLATIONLEVEL_READCOMMITTED), S_OK)) { //Make sure the Txn Info is correct VerifyTxnInfo(m_pChgRowset1->m_pITxnLocal, ISOLATIONLEVEL_READCOMMITTED, &uow1); //We'll verify by using a second txn in Read Committed Isolation Level if (CHECK(m_pChgRowset2->StartTxn(ISOLATIONLEVEL_READCOMMITTED), S_OK)) { //Make sure the Txn Info is correct VerifyTxnInfo(m_pChgRowset2->m_pITxnLocal, ISOLATIONLEVEL_READCOMMITTED, &uow2); //Make sure the Unit of Work Identifiers are unique if (memcmp(uow1.rgb, uow2.rgb, sizeof(uow1.rgb))) fResults = TRUE; } } //Release each rowset and end all transactions ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(3) //*----------------------------------------------------------------------- // @mfunc Repeatable Read // // @rdesc TEST_PASS or TEST_FAIL // int TCIsoLevel::Variation_3() { HROW hRow = DB_NULL_HROW; BOOL fResults = FALSE; IRowset * pIRowset = NULL; XACTUOW uow1, uow2; //If no provider support for this isolation level, just return pass if (!m_fRepeatableRead) { odtLog << wszNoProviderSupport << L"REPEATABLE READ" << wszNewLine; return TEST_PASS; } //Start txn we're testing at Repeatable Read Isolation Level if (CHECK(m_pChgRowset1->StartTxn(ISOLATIONLEVEL_REPEATABLEREAD), S_OK)) { //Make sure the Txn Info is correct VerifyTxnInfo(m_pChgRowset1->m_pITxnLocal, ISOLATIONLEVEL_REPEATABLEREAD, &uow1); //We'll verify by using a second txn in Read Committed Isolation Level if (CHECK(m_pChgRowset2->StartTxn(ISOLATIONLEVEL_READCOMMITTED), S_OK)) { //Make sure the Txn Info is correct VerifyTxnInfo(m_pChgRowset2->m_pITxnLocal, ISOLATIONLEVEL_READCOMMITTED, &uow2); //Make sure the Unit of Work Identifiers are unique if (memcmp(uow1.rgb, uow2.rgb, sizeof(uow1.rgb))) fResults = TRUE; } } //CLEANUP: //Get rid of hRow and pIRowset if we haven't already if (hRow != DB_NULL_HROW && pIRowset) CHECK(pIRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL), S_OK); SAFE_RELEASE(pIRowset); //Release each rowset and end all transactions ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(4) //*----------------------------------------------------------------------- // @mfunc Serializable // // @rdesc TEST_PASS or TEST_FAIL // int TCIsoLevel::Variation_4() { HROW hRow = DB_NULL_HROW; BOOL fResults = FALSE; IRowset * pIRowset = NULL; XACTUOW uow1, uow2; //If no provider support for this isolation level, just return pass if (!m_fSerializable) { odtLog << wszNoProviderSupport << L"SERIALIZABLE" << wszNewLine; return TEST_PASS; } //Start txn we're testing at Repeatable Read Isolation Level if (CHECK(m_pChgRowset1->StartTxn(ISOLATIONLEVEL_SERIALIZABLE), S_OK)) { //Make sure the Txn Info is correct VerifyTxnInfo(m_pChgRowset1->m_pITxnLocal, ISOLATIONLEVEL_SERIALIZABLE, &uow1); //We'll verify by using a second txn in Read Committed Isolation Level if (CHECK(m_pChgRowset2->StartTxn(ISOLATIONLEVEL_READCOMMITTED), S_OK)) { //Make sure the Txn Info is correct VerifyTxnInfo(m_pChgRowset2->m_pITxnLocal, ISOLATIONLEVEL_READCOMMITTED, &uow2); //Make sure the Unit of Work Identifiers are unique if (memcmp(uow1.rgb, uow2.rgb, sizeof(uow1.rgb))) fResults = TRUE; } } //CLEANUP: //Get rid of hRow and pIRowset if we haven't already if (hRow != DB_NULL_HROW && pIRowset) CHECK(pIRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL), S_OK); SAFE_RELEASE(pIRowset); //Release each rowset and end all transactions ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(5) //*----------------------------------------------------------------------- // @mfunc Chaos // // @rdesc TEST_PASS or TEST_FAIL // int TCIsoLevel::Variation_5() { HROW hRow = DB_NULL_HROW; BOOL fResults = FALSE; IRowset * pIRowset = NULL; BOOL fReadCommitted = TRUE; //If no provider support for this isolation level, just return pass if (!m_fChaos) { odtLog << wszNoProviderSupport << L"CHAOS" << wszNewLine; return TEST_PASS; } //Start txn we're testing at Chaos Isolation Level if (!CHECK(m_pChgRowset1->StartTxn(ISOLATIONLEVEL_CHAOS), S_OK)) goto CLEANUP; //Make sure the Txn Info is correct VerifyTxnInfo(m_pChgRowset1->m_pITxnLocal, ISOLATIONLEVEL_CHAOS); //We'll verify by using a second txn in Read Committed Isolation Level if ((m_hr = m_pChgRowset2->StartTxn(ISOLATIONLEVEL_READCOMMITTED)) == XACT_E_ISOLATIONLEVEL) { //Try read uncommitted if that wasn't supported. We didn't try read uncommited //right away because SQL Server does not support updateable read uncommitted if (CHECK(m_pChgRowset2->StartTxn(ISOLATIONLEVEL_READUNCOMMITTED), S_OK)) fReadCommitted = FALSE; } else //We should have succeeded if we support it CHECK(m_hr, S_OK); //Rowset 2 should be able to change a row if(!COMPARE(Change(m_pChgRowset2), TRUE)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param //But we shouldn't be able to see the change or touch it //if we are at read committed on the second txn if (fReadCommitted) { if (!COMPARE(FindChange(m_pChgRowset1), FALSE)) goto CLEANUP; } //We will be able to see the change if we are in read uncommitted mode else { if (!COMPARE(FindChange(m_pChgRowset1), TRUE)) goto CLEANUP; } //Everything went OK if we got to here fResults = TRUE; CLEANUP: //Release each rowset and end all transactions ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(6) //*----------------------------------------------------------------------- // @mfunc Unspecified // // @rdesc TEST_PASS or TEST_FAIL // int TCIsoLevel::Variation_6() { //Isolation level UNSPECIFIED is not supported for any interface //other than ITransctionJoin, so try it on ITransactionLocal if (CHECK(m_pChgRowset1->StartTxn(ISOLATIONLEVEL_UNSPECIFIED), XACT_E_ISOLATIONLEVEL)) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_TERMINATE_METHOD //-------------------------------------------------------------------- // @mfunc TestCase Termination Routine // // @rdesc TRUE or FALSE // BOOL TCIsoLevel::Terminate() { //Try to set autocommit isolevel. This may or may not be supported, //but if it is, we want to set it here to make sure we are in a //low level isolation so that we see everything when in autocommit m_pChgRowset1->SetAutoCommitIsoLevel(DBPROPVAL_TI_READUNCOMMITTED); // {{ TCW_TERM_BASECLASS_CHECK2 return(CTxnImmed::Terminate()); } // }} // }} // }} // {{ TCW_TC_PROTOTYPE(TCRetainPreserve) //*----------------------------------------------------------------------- //| Test Case: TCRetainPreserve - Retaining/Preserving Behavior //| Created: 04/16/96 //*----------------------------------------------------------------------- //-------------------------------------------------------------------- // @mfunc TestCase Initialization Routine // // @rdesc TRUE or FALSE // BOOL TCRetainPreserve::Init() { // {{ TCW_INIT_BASECLASS_CHECK if(CTxnImmed::Init()) // }} { return TRUE; } return FALSE; } //-------------------------------------------------------------------- // @mfunc Tests retaining semantics for a specific isolation level // // @rdesc TRUE or FALSE // BOOL TCRetainPreserve::CommitRetain(ISOLEVEL fIsoLevel) { BOOL fResults = FALSE; HACCESSOR hAccessor = DB_NULL_HACCESSOR; IRowset *pIRowset = NULL; HROW hRow = DB_NULL_HROW; HRESULT hr; IRowset *pIRowset2 = NULL; //Start Txn if (!CHECK(m_pChgRowset1->StartTxn(fIsoLevel), S_OK)) goto CLEANUP; hr=m_pChgRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //if provider does not support IRowsetChange if (!m_pChgRowset1->m_fChange) { odtLog << L"IRowsetChange not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Do something in txn if(!COMPARE(Insert(m_pChgRowset1), TRUE)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param //If we are supposed to be in a zombie state after commit, get hRow, accessor //and pIRowset now so we can verify they can be released later if (!m_pChgRowset1->m_fCommitPreserve) { //Get hRow to hang onto of a row already existing if(!COMPARE(m_pChgRowset1->FindRow(g_ulLastActualInsert, &hRow), TRUE)) goto CLEANUP; if (!VerifyInterface(m_pChgRowset1->m_pIAccessor, IID_IRowset, ROWSET_INTERFACE, (IUnknown **)&pIRowset)) goto CLEANUP; if (!CHECK(m_pChgRowset1->m_pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, m_pChgRowset1->m_cReadBindings, m_pChgRowset1->m_rgReadBindings, m_pChgRowset1->m_cbReadRowSize, &hAccessor, NULL), S_OK)) goto CLEANUP; } //Now commit it if(XACT_E_NOTSUPPORTED==m_pChgRowset1->m_pITxnLocal->Commit(TRUE, XACTTC_ASYNC_PHASEONE, 0)) { if(XACT_E_NOTSUPPORTED==m_pChgRowset1->m_pITxnLocal->Commit(TRUE, XACTTC_SYNC_PHASEONE, 0)) { if(XACT_E_NOTSUPPORTED==m_pChgRowset1->m_pITxnLocal->Commit(TRUE, XACTTC_SYNC_PHASETWO, 0)) { if(XACT_E_NOTSUPPORTED==m_pChgRowset1->m_pITxnLocal->Commit(TRUE, XACTTC_SYNC, 0)) { if(XACT_E_NOTSUPPORTED==m_pChgRowset1->m_pITxnLocal->Commit(TRUE, 0, 0)) { //commit retaining not supported odtLog << L"Retaining Commit not supported, returning TEST_PASS. \n"; fResults = TRUE; goto CLEANUP; } } } } } //Our last insert was just committed, so increment row count m_pChgRowset1->m_fTxnPendingInsert = FALSE; g_InsertIncrement(); //Make sure the Txn Info is correct VerifyTxnInfo(m_pChgRowset1->m_pITxnLocal, fIsoLevel); //If we can't retain, its because its not supported, so just pass if (m_hr == XACT_E_CANTRETAIN) { odtLog << L"Retaining Commit not supported, returning TEST_PASS. \n"; fResults = TRUE; goto CLEANUP; } //Check commit return value if (CHECK(m_hr, S_OK)) //Verify the correct behavior, based on CommitPreserve property if (m_pChgRowset1->m_fCommitPreserve) { if (COMPARE(NewUnitOfWork(m_pChgRowset1, m_pChgRowset2), TRUE)) if (COMPARE(RowsetFunctional(m_pChgRowset1), TRUE)) fResults = TRUE; } else { if (COMPARE(RowsetZombied(m_pChgRowset1, &pIRowset, &hRow, hAccessor), TRUE)) if (COMPARE(NewUnitOfWork(m_pChgRowset1, m_pChgRowset2), TRUE)) fResults = TRUE; } CLEANUP: ReleaseAllRowsetsAndTxns(); //Get rid of hRow, accessor and pIRowset if we haven't already if ((hRow != DB_NULL_HROW) && pIRowset) CHECK(pIRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL), S_OK); if ((hAccessor != DB_NULL_HACCESSOR) && m_pChgRowset1->m_pIAccessor) CHECK(m_pChgRowset1->m_pIAccessor->ReleaseAccessor(hAccessor, NULL), S_OK); SAFE_RELEASE(pIRowset); return fResults; } //-------------------------------------------------------------------- // @mfunc Tests retaining semantics for a specific isolation level // // @rdesc TRUE or FALSE // BOOL TCRetainPreserve::CommitNonRetain(ISOLEVEL fIsoLevel) { BOOL fResults = FALSE; HACCESSOR hAccessor = DB_NULL_HACCESSOR; IRowset * pIRowset = NULL; HROW hRow = DB_NULL_HROW; HRESULT hr; //Start Txn if (!CHECK(m_pChgRowset1->StartTxn(fIsoLevel), S_OK)) goto CLEANUP; hr=m_pChgRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //if provider does not support IRowsetChange if (!m_pChgRowset1->m_fChange) { odtLog << L"IRowsetChange not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Do something in txn if(!COMPARE(Insert(m_pChgRowset1), TRUE)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param //If we are supposed to be in a zombie state after commit, get hRow, accessor //and pIRowset now so we can verify they can be released later if (!m_pChgRowset1->m_fCommitPreserve) { //Get hRow to hang on to of a row already existing if(!COMPARE(m_pChgRowset1->FindRow(g_ulLastActualInsert, &hRow), TRUE)) goto CLEANUP; if (!VerifyInterface(m_pChgRowset1->m_pIAccessor, IID_IRowset, ROWSET_INTERFACE, (IUnknown **)&pIRowset)) goto CLEANUP; if (!CHECK(m_pChgRowset1->m_pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, m_pChgRowset1->m_cReadBindings, m_pChgRowset1->m_rgReadBindings, m_pChgRowset1->m_cbReadRowSize, &hAccessor, NULL), S_OK)) goto CLEANUP; } //Make sure the Txn Info is correct VerifyTxnInfo(m_pChgRowset1->m_pITxnLocal, fIsoLevel); //Now commit it if (CHECK(m_pChgRowset1->m_pITxnLocal->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), S_OK)) { //We could successfully delete the row, return TRUE fResults = TRUE; } //Our last insert was just committed, so increment row count m_pChgRowset1->m_fTxnPendingInsert = FALSE; m_pChgRowset1->m_fTxnStarted = FALSE; g_InsertIncrement(); //Verify the correct behavior, based on CommitPreserve property if (m_pChgRowset1->m_fCommitPreserve) { if (COMPARE(NoNewUnitOfWork(m_pChgRowset1, m_pChgRowset2), TRUE)) if (COMPARE(RowsetFunctional(m_pChgRowset1), TRUE)) fResults = TRUE; } else { if (COMPARE(RowsetZombied(m_pChgRowset1, &pIRowset, &hRow, hAccessor), TRUE)) { if (COMPARE(NoNewUnitOfWork(m_pChgRowset1, m_pChgRowset2), TRUE)) fResults = TRUE; } } CLEANUP: ReleaseAllRowsetsAndTxns(); //Get rid of hRow, accessor and pIRowset if we haven't already if ((hRow != DB_NULL_HROW) && pIRowset) CHECK(pIRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL), S_OK); if ((hAccessor != DB_NULL_HACCESSOR) && m_pChgRowset1->m_pIAccessor) CHECK(m_pChgRowset1->m_pIAccessor->ReleaseAccessor(hAccessor, NULL), S_OK); SAFE_RELEASE(pIRowset); return fResults; } //-------------------------------------------------------------------- // @mfunc Tests retaining semantics for a specific isolation level // // @rdesc TRUE or FALSE // BOOL TCRetainPreserve::AbortRetain(ISOLEVEL fIsoLevel) { BOOL fResults = FALSE; HACCESSOR hAccessor = DB_NULL_HACCESSOR; IRowset *pIRowset = NULL; HROW hRow = DB_NULL_HROW; HRESULT hr; //Start Txn if (!CHECK(m_pChgRowset1->StartTxn(fIsoLevel), S_OK)) goto CLEANUP; hr=m_pChgRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //if provider does not support IRowsetChange if (!m_pChgRowset1->m_fChange) { odtLog << L"IRowsetChange not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Do something in txn if(!COMPARE(Insert(m_pChgRowset1), TRUE)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param //If we are supposed to be in a zombie state after commit, get hRow, accessor //and pIRowset now so we can verify they can be released later if (!m_pChgRowset1->m_fAbortPreserve) { //Get hRow to hang on to of a row already existing if(!COMPARE(m_pChgRowset1->FindRow(g_ulLastActualInsert, &hRow), TRUE)) goto CLEANUP; if (!VerifyInterface(m_pChgRowset1->m_pIAccessor, IID_IRowset, ROWSET_INTERFACE, (IUnknown **)&pIRowset)) goto CLEANUP; if (!CHECK(m_pChgRowset1->m_pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, m_pChgRowset1->m_cReadBindings, m_pChgRowset1->m_rgReadBindings, m_pChgRowset1->m_cbReadRowSize, &hAccessor, NULL), S_OK)) goto CLEANUP; } //Now abort it m_hr = m_pChgRowset1->m_pITxnLocal->Abort(NULL, TRUE, FALSE); //Our last insert was just aborted, so it's no longer pending m_pChgRowset1->m_fTxnPendingInsert = FALSE; //Make sure the Txn Info is correct VerifyTxnInfo(m_pChgRowset1->m_pITxnLocal, fIsoLevel); //If we can't retain, its because its not supported, so just pass if (m_hr == XACT_E_CANTRETAIN) { odtLog << L"Retaining Abort not supported, returning TEST_PASS. \n"; fResults = TRUE; goto CLEANUP; } //Check Abort return code if (CHECK(m_hr, S_OK)) //Verify the correct behavior, based on CommitPreserve property if (m_pChgRowset1->m_fAbortPreserve) { if (COMPARE(NewUnitOfWork(m_pChgRowset1, m_pChgRowset2), TRUE)) if (COMPARE(RowsetFunctional(m_pChgRowset1), TRUE)) fResults = TRUE; } else { if (COMPARE(RowsetZombied(m_pChgRowset1, &pIRowset, &hRow, hAccessor), TRUE)) if (COMPARE(NewUnitOfWork(m_pChgRowset1, m_pChgRowset2), TRUE)) fResults = TRUE; } CLEANUP: ReleaseAllRowsetsAndTxns(); //Get rid of hRow, accessor and pIRowset if we haven't already if ((hRow != DB_NULL_HROW) && pIRowset) CHECK(pIRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL), S_OK); if ((hAccessor != DB_NULL_HACCESSOR) && m_pChgRowset1->m_pIAccessor) CHECK(m_pChgRowset1->m_pIAccessor->ReleaseAccessor(hAccessor, NULL), S_OK); SAFE_RELEASE(pIRowset); return fResults; } //-------------------------------------------------------------------- // @mfunc Tests retaining semantics for a specific isolation level // // @rdesc TRUE or FALSE // BOOL TCRetainPreserve::AbortNonRetain(ISOLEVEL fIsoLevel) { BOOL fResults = FALSE; HACCESSOR hAccessor = DB_NULL_HACCESSOR; IRowset *pIRowset = NULL; HROW hRow = DB_NULL_HROW; HRESULT hr; //Start Txn if (!CHECK(m_pChgRowset1->StartTxn(fIsoLevel), S_OK)) goto CLEANUP; hr=m_pChgRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //if provider does not support IRowsetChange if (!m_pChgRowset1->m_fChange) { odtLog << L"IRowsetChange not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Do something in txn if(!COMPARE(Insert(m_pChgRowset1), TRUE)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param //If we are supposed to be in a zombie state after commit, get hRow, accessor //and pIRowset now so we can verify they can be released later if (!m_pChgRowset1->m_fAbortPreserve) { //Get hRow to hang on to of a row already existing if(!COMPARE(m_pChgRowset1->FindRow(g_ulLastActualInsert, &hRow), TRUE)) goto CLEANUP; if (!VerifyInterface(m_pChgRowset1->m_pIAccessor, IID_IRowset, ROWSET_INTERFACE, (IUnknown **)&pIRowset)) goto CLEANUP; if (!CHECK(m_pChgRowset1->m_pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, m_pChgRowset1->m_cReadBindings, m_pChgRowset1->m_rgReadBindings, m_pChgRowset1->m_cbReadRowSize, &hAccessor, NULL), S_OK)) goto CLEANUP; } //Make sure the Txn Info is correct VerifyTxnInfo(m_pChgRowset1->m_pITxnLocal, fIsoLevel); //Now abort it if (CHECK(m_pChgRowset1->m_pITxnLocal->Abort(NULL, FALSE, FALSE), S_OK)) //Our last insert was just aborted, so it's no longer pending m_pChgRowset1->m_fTxnPendingInsert = FALSE; m_pChgRowset1->m_fTxnStarted = FALSE; //Verify the correct behavior, based on CommitPreserve property if (m_pChgRowset1->m_fAbortPreserve) { if (COMPARE(NoNewUnitOfWork(m_pChgRowset1, m_pChgRowset2), TRUE)) if (COMPARE(RowsetFunctional(m_pChgRowset1), TRUE)) fResults = TRUE; } else { if (COMPARE(RowsetZombied(m_pChgRowset1, &pIRowset, &hRow, hAccessor), TRUE)) if (COMPARE(NoNewUnitOfWork(m_pChgRowset1, m_pChgRowset2), TRUE)) fResults = TRUE; } CLEANUP: ReleaseAllRowsetsAndTxns(); //Get rid of hRow, accessor and pIRowset if we haven't already if ((hRow != DB_NULL_HROW) && pIRowset) CHECK(pIRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL), S_OK); if ((hAccessor != DB_NULL_HACCESSOR) && m_pChgRowset1->m_pIAccessor) CHECK(m_pChgRowset1->m_pIAccessor->ReleaseAccessor(hAccessor, NULL), S_OK); SAFE_RELEASE(pIRowset); return fResults; } // {{ TCW_VAR_PROTOTYPE(1) //*----------------------------------------------------------------------- // @mfunc Commit(fRetaining = TRUE // // @rdesc TEST_PASS or TEST_FAIL // int TCRetainPreserve::Variation_1() { TBEGIN ISOLEVEL ulSupported = 0; DWORD i = 0; TESTC_(GetIsoLevels(&ulSupported), S_OK); for(i=0; i\tNot Supported"; else CHECK(CommitRetain(g_IsolationList[i].ulIsolation), true); odtLog << "\n"; } CLEANUP: TRETURN } // }} // {{ TCW_VAR_PROTOTYPE(2) //*----------------------------------------------------------------------- // @mfunc Commit(fRetaining = FALSE // // @rdesc TEST_PASS or TEST_FAIL // int TCRetainPreserve::Variation_2() { TBEGIN ISOLEVEL ulSupported = 0; DWORD i = 0; TESTC_(GetIsoLevels(&ulSupported), S_OK); for(i = 0; i < g_ulTotalIsolations; i++) { odtLog << g_IsolationList[i].pszDesc; if ((ulSupported & g_IsolationList[i].ulIsolation) == g_IsolationList[i].ulIsolation) CHECK(CommitNonRetain(g_IsolationList[i].ulIsolation), true); else odtLog << "\t->\tNot Supported"; odtLog << "\n"; } CLEANUP: TRETURN } // }} // {{ TCW_VAR_PROTOTYPE(3) //*----------------------------------------------------------------------- // @mfunc Abort(fRetaining = TRUE // // @rdesc TEST_PASS or TEST_FAIL // int TCRetainPreserve::Variation_3() { TBEGIN ISOLEVEL ulSupported = 0; DWORD i = 0; TESTC_(GetIsoLevels(&ulSupported), S_OK); for(i = 0; i < g_ulTotalIsolations; i++) { odtLog << g_IsolationList[i].pszDesc; if ((ulSupported & g_IsolationList[i].ulIsolation) == g_IsolationList[i].ulIsolation) CHECK(AbortRetain(g_IsolationList[i].ulIsolation), true); else odtLog << "\t->\tNot Supported"; odtLog << "\n"; } CLEANUP: TRETURN } // }} // {{ TCW_VAR_PROTOTYPE(4) //*----------------------------------------------------------------------- // @mfunc Abort(fRetaining = FALSE // // @rdesc TEST_PASS or TEST_FAIL // int TCRetainPreserve::Variation_4() { TBEGIN ISOLEVEL ulSupported = 0; DWORD i = 0; TESTC_(GetIsoLevels(&ulSupported), S_OK); for(i = 0; i < g_ulTotalIsolations; i++) { odtLog << g_IsolationList[i].pszDesc; if ((ulSupported & g_IsolationList[i].ulIsolation) == g_IsolationList[i].ulIsolation) CHECK(AbortNonRetain(g_IsolationList[i].ulIsolation), true); else odtLog << "\t->\tNot Supported"; odtLog << "\n"; } CLEANUP: TRETURN } // }} // {{ TCW_TERMINATE_METHOD //-------------------------------------------------------------------- // @mfunc TestCase Termination Routine // // @rdesc TRUE or FALSE // BOOL TCRetainPreserve::Terminate() { // TO DO: Add your own code here // {{ TCW_TERM_BASECLASS_CHECK2 return(CTxnImmed::Terminate()); } // }} // }} // }} // {{ TCW_TC_PROTOTYPE(TCITxnReturnVals) //*----------------------------------------------------------------------- //| Test Case: TCITxnReturnVals - ITransaction Return Values //| Created: 04/30/96 //*----------------------------------------------------------------------- //-------------------------------------------------------------------- // @mfunc TestCase Initialization Routine // // @rdesc TRUE or FALSE // BOOL TCITxnReturnVals::Init() { HRESULT hr = NULL; ITransactionObject *pITranObj = NULL; m_pITxnOptions = NULL; // {{ TCW_INIT_BASECLASS_CHECK if(CTxnImmed::Init()) // }} { //Try to get the txn options interface hr = m_pChgRowset1->m_pITxnLocal->GetOptionsObject(&m_pITxnOptions); if (S_OK==hr) { return TEST_PASS; } else { if (DB_E_NOTSUPPORTED==hr) { //test for IID_ITransactionObject OI here if (!VerifyInterface(m_pIOpenRowset, IID_ITransactionObject,SESSION_INTERFACE, (IUnknown **)&pITranObj)) { SAFE_RELEASE(pITranObj); return TEST_SKIPPED; } //free memory that was incorrectly allocated SAFE_RELEASE(pITranObj); } } } return FALSE; } // {{ TCW_VAR_PROTOTYPE(1) //*----------------------------------------------------------------------- // @mfunc Abort with pboidReason = NULL - S_OK // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_1() { BOOL fResults = FALSE; //Start Txn if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) //Check for proper return code for given parameters fResults = CHECK(m_pChgRowset1->m_pITxnLocal->Abort(NULL, FALSE, FALSE), S_OK); //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(2) //*----------------------------------------------------------------------- // @mfunc Abort with pboidReason = BOID_NULL - S_OK // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_2() { BOOL fResults = FALSE; BOID boidReason = BOID_NULL; //Start Txn if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) //Check for proper return code for given parameters fResults = CHECK(m_pChgRowset1->m_pITxnLocal->Abort(&boidReason, FALSE, FALSE), S_OK); //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(3) //*----------------------------------------------------------------------- // @mfunc Abort with fAsync=TRUE - XACT_E_NOTSUPPORTED or S_OK // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_3() { BOOL fResults = FALSE; //Start Txn if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) { //Check for proper return code for given parameters m_hr = m_pChgRowset1->m_pITxnLocal->Abort(NULL, FALSE, TRUE); switch (m_pChgRowset1->m_eAsyncAbort) { case EASYNCTRUE: fResults = CHECK(m_hr, S_OK); break; case EASYNCNOTSUPPORTED: odtLog << L"DBPROP_ASYNCTXNABORT is returning NOT SUPPORTED.\n"; //Don't break here as we want to fall through //to also check that we get XACT_E_NOSUPPORTED case EASYNCFALSE: //Async is not supported fResults = CHECK(m_hr, XACT_E_NOTSUPPORTED); break; default: //We have to add code for another ENUM Value ASSERT(FALSE); } } //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(4) //*----------------------------------------------------------------------- // @mfunc Abort with fAsync=FALSE - S_OK // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_4() { BOOL fResults = FALSE; BOID boidReason = BOID_NULL; //Just so we have some value //Start Txn if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) //Check for proper return code for given parameters fResults = CHECK(m_pChgRowset1->m_pITxnLocal->Abort(&boidReason, FALSE, FALSE), S_OK); //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(5) //*----------------------------------------------------------------------- // @mfunc Call commit on one thread, Abort on another - XACT_E_ALREADYINPROGRESS // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_5() { HANDLE hThread; ULONG ulThreadID; BOOL fResults = FALSE; HRESULT hr; //Start our transaction if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) { //Create a thread to do the abort hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadAbort, m_pChgRowset1, CREATE_SUSPENDED, &ulThreadID); if (hThread == NULL) goto CLEANUP; //Start the abort thread. Inside ThreadAbort we immediately //give the time slice back, which hopefully means commit will occur //before the abort in ThreadAbort ResumeThread(hThread); //Do the commit, this should occur first m_hr = m_pChgRowset1->m_pITxnLocal->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0); //Wait for abort to finish, and get its return value WaitForSingleObject(hThread, INFINITE); if (!GetExitCodeThread(hThread, (DWORD *)&hr)) goto CLEANUP; //Cleanup second thread if (hThread) CloseHandle(hThread); //Our commit should have succeeded CHECK(m_hr, S_OK); //Either we got the abort done in time, in which case we expect //XACT_E_ALREADYINPROGRESS; or the commit had finished before //we could do the abort, in which case we expect XACT_E_NOTRANSACTION if ((hr == XACT_E_ALREADYINPROGRESS) || (hr == XACT_E_NOTRANSACTION)) fResults = TRUE; else //Make sure we log a message that our hr didn't match anything we expected CHECK(hr, XACT_E_ALREADYINPROGRESS); } CLEANUP: ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(6) //*----------------------------------------------------------------------- // @mfunc Call abort on one thread and then on another - XACT_S_ABORTING // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_6() { HANDLE hThread; ULONG ulThreadID; BOOL fResults = FALSE; HRESULT hr; //Start our transaction if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) { //Create a thread to do the second abort hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadAbort, m_pChgRowset1, CREATE_SUSPENDED, &ulThreadID); if (hThread == NULL) goto CLEANUP; //Start the second thread. Inside ThreadAbort we immediately //give the time slice back, which hopefully means first Abort will occur //before the abort in ThreadAbort ResumeThread(hThread); //Do the abort, this should occur first m_hr = m_pChgRowset1->m_pITxnLocal->Abort(NULL, FALSE, FALSE); //Wait for abort to finish, and get its return value WaitForSingleObject(hThread, INFINITE); if (!GetExitCodeThread(hThread, (DWORD *)&hr)) goto CLEANUP; //Cleanup second thread if (hThread) CloseHandle(hThread); //Our first abort should have succeeded CHECK(m_hr, S_OK); //Either we got the second abort done in time, in which case we expect //XACT_S_ABORTING; or the first abort had finished before we could //do the second abort, in which case we expect XACT_E_NOTRANSACTION if ((hr == XACT_S_ABORTING) || (hr == XACT_E_NOTRANSACTION)) fResults = TRUE; else //Make sure we log a message that our hr didn't match anything we expected CHECK(hr, XACT_S_ABORTING); } CLEANUP: ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(7) //*----------------------------------------------------------------------- // @mfunc Commit with grfTC =XACTC_ASYNC - XACT_E_NOTSUPPORTED or S_OK // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_7() { BOOL fResults = FALSE; //Start Txn if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) { //Check for proper return code for given parameters m_hr = m_pChgRowset1->m_pITxnLocal->Commit(FALSE, XACTTC_ASYNC_PHASEONE, 0); //Make sure the right thing was returned per our property switch (m_pChgRowset1->m_eAsyncCommit) { case EASYNCTRUE: fResults = CHECK(m_hr, S_OK); break; case EASYNCNOTSUPPORTED: odtLog << L"DBPROP_ASYNCTXNCOMMIT is returning NOT SUPPORTED.\n"; //Don't break here as we want to fall through //and check that the right return code was generated case EASYNCFALSE: //Async is not supported fResults = CHECK(m_hr, XACT_E_NOTSUPPORTED); break; default: //We have to add code for another ENUM Value ASSERT(FALSE); } } //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else { //Log a message telling what the return code was CHECK(m_hr, S_OK); return TEST_FAIL; } } // }} // {{ TCW_VAR_PROTOTYPE(8) //*----------------------------------------------------------------------- // @mfunc Commit with grfTC =XACTC_SYNC_PHASEONE - XACT_E_NOTSUPPORTED or S_OK // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_8() { BOOL fResults = FALSE; //Start Txn if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) { //Check for proper return code for given parameters m_hr = m_pChgRowset1->m_pITxnLocal->Commit(FALSE, XACTTC_SYNC_PHASEONE, 0); fResults = (m_hr == S_OK) || (m_hr == XACT_E_NOTSUPPORTED); } //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else { //Log a message telling what the return code was CHECK(m_hr, S_OK); return TEST_FAIL; } } // }} // {{ TCW_VAR_PROTOTYPE(9) //*----------------------------------------------------------------------- // @mfunc Commit with grfRM!=0 - XACT_E_NOTSUPPORTED // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_9() { BOOL fResults = FALSE; //Start Txn if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) //Check for proper return code for given parameters fResults = CHECK(m_pChgRowset1->m_pITxnLocal->Commit(FALSE, 0, 1), XACT_E_NOTSUPPORTED); //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(10) //*----------------------------------------------------------------------- // @mfunc Call Abort on one thread, Commit on another - XACT_E_ALREADYINPROGRESS // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_10() { HANDLE hThread; ULONG ulThreadID; BOOL fResults = FALSE; HRESULT hr; //Start our transaction if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) { //Create a thread to do the commit hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadCommit, m_pChgRowset1, CREATE_SUSPENDED, &ulThreadID); if (hThread == NULL) goto CLEANUP; //Start the commit thread. Inside ThreadCommit we immediately //give the time slice back, which hopefully means Abort will occur //before the commit in ThreadCommit ResumeThread(hThread); //Do the abort, this should occur first m_hr = m_pChgRowset1->m_pITxnLocal->Abort(NULL, FALSE, FALSE); //Wait for commit to finish, and get its return value WaitForSingleObject(hThread, INFINITE); if (!GetExitCodeThread(hThread, (DWORD *)&hr)) goto CLEANUP; //Cleanup second thread if (hThread) CloseHandle(hThread); //Our abort should have succeeded CHECK(m_hr, S_OK); //Either we got the commit done in time, in which case we expect //XACT_E_ALREADYINPROGRESS; or the abort had finished before //we could do the commit, in which case we expect XACT_E_NOTRANSACTION if ((hr == XACT_E_ALREADYINPROGRESS) || (hr == XACT_E_NOTRANSACTION)) fResults = TRUE; else //Make sure we log a message that our hr didn't match anything we expected CHECK(hr, XACT_E_ALREADYINPROGRESS); } CLEANUP: ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(11) //*----------------------------------------------------------------------- // @mfunc Call commit on one thread, and then on another - XACT_E_ALREADYINPROGRESS // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_11() { HANDLE hThread; ULONG ulThreadID; BOOL fResults = FALSE; HRESULT hr; //Start our transaction if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) { //Create a thread to do the second commit hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadCommit, m_pChgRowset1, CREATE_SUSPENDED, &ulThreadID); if (hThread == NULL) goto CLEANUP; //Start the second thread. Inside ThreadCommit we immediately //give the time slice back, which hopefully means our first commit will occur //before the commit in ThreadCommit ResumeThread(hThread); //Do the commit, this should occur first m_hr = m_pChgRowset1->m_pITxnLocal->Commit(FALSE, 0 , 0); //Wait for commit to finish, and get its return value WaitForSingleObject(hThread, INFINITE); if (!GetExitCodeThread(hThread, (DWORD *)&hr)) goto CLEANUP; //Cleanup second thread if (hThread) CloseHandle(hThread); //Our first commit should have succeeded CHECK(m_hr, S_OK); //Either we got the second commit done in time, in which case we expect //XACT_E_ALREADYINPROGRESS; or the first commit had finished before we could //do the second commit, in which case we expect XACT_E_NOTRANSACTION if ((hr == XACT_E_ALREADYINPROGRESS) || (hr == XACT_E_NOTRANSACTION)) fResults = TRUE; else //Make sure we log a message that our hr didn't match anything we expected CHECK(hr, XACT_E_ALREADYINPROGRESS); } CLEANUP: ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(12) //*----------------------------------------------------------------------- // @mfunc Commit with reported supported grfTC values - S_OK // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_12() { BOOL fResults = FALSE; XACTTRANSINFO TxnInfo; //Start Txn if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) { //Check which grfTC values are supported if (CHECK(m_pChgRowset1->m_pITxnLocal->GetTransactionInfo(&TxnInfo), S_OK)) { //If both values are supported, try ASYNC first (since they are mutually //exclusive when called with Commit, we can only do one at a time) if ((TxnInfo.grfTCSupported & XACTTC_ASYNC_PHASEONE) && (TxnInfo.grfTCSupported & XACTTC_SYNC_PHASEONE)) { CHECK(m_pChgRowset1->m_pITxnLocal->Commit(FALSE, XACTTC_ASYNC_PHASEONE, 0), S_OK); //Now remove this option from our flags so we don't try to do it again TxnInfo.grfTCSupported &= ~(XACTTC_ASYNC_PHASEONE); //Reset another transaction CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK); } //Any remaining flags should also work on commit fResults = CHECK(m_pChgRowset1->m_pITxnLocal->Commit(FALSE, TxnInfo.grfTCSupported, 0), S_OK); } } //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(13) //*----------------------------------------------------------------------- // @mfunc GetTransactionInfo with pinfo = NULL // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_13() { BOOL fResults = FALSE; if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) //Check for invalid arg on pinfo = NULL if (CHECK(m_pChgRowset1->m_pITxnLocal->GetTransactionInfo(NULL), E_INVALIDARG)) fResults = TRUE; //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(14) //*----------------------------------------------------------------------- // @mfunc GetOptionsObject with ppOptions = NULL - E_INVALIDARG // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_14() { if (CHECK(m_pChgRowset1->m_pITxnLocal->GetOptionsObject(NULL), E_INVALIDARG)) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(15) //*----------------------------------------------------------------------- // @mfunc StartTransaction twice in a row - S_OK or XACT_E_XTIONEXISTS // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_15() { ULONG ulTxnLvl1 = 0; ULONG ulTxnLvl2 = 0; if (CHECK(m_pChgRowset1->m_pITxnLocal->StartTransaction(m_fIsoLevel, 0, NULL, &ulTxnLvl1), S_OK)) { //This will succeed if nested txns are supported, else return //XACT_E_XTIONEXISTS m_hr = m_pChgRowset1->m_pITxnLocal->StartTransaction(m_fIsoLevel, 0, NULL, &ulTxnLvl2); //First level must always be 1 COMPARE(ulTxnLvl1, 1); if (m_hr == S_OK) { //Make sure the nesting worked COMPARE(ulTxnLvl2, 2); } else { //Make sure we got the right return value CHECK(m_hr, XACT_E_XTIONEXISTS); } //End the txn -- this should end both levels, if applicable CHECK(m_pChgRowset1->m_pITxnLocal->Commit(FALSE, 0, 0), S_OK); return TEST_PASS; } return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(16) //*----------------------------------------------------------------------- // @mfunc StartTransaction with a non zero timeout - S_OK or XACT_E_NOTIMEOUT // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_16() { XACTOPT TxnOptions; BOOL fResults = FALSE; //Set time out to non zero TxnOptions.ulTimeout = MAX_ULONG; TxnOptions.szDescription[0] = '\0'; //Set our time out option if (CHECK(m_pITxnOptions->SetOptions(&TxnOptions), S_OK)) { m_hr = m_pChgRowset1->m_pITxnLocal->StartTransaction(m_fIsoLevel, 0, m_pITxnOptions, NULL); ReleaseAllRowsetsAndTxns(); //Either of these is acceptable if (m_hr == S_OK || m_hr == XACT_E_NOTIMEOUT) return TEST_PASS; } return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(17) //*----------------------------------------------------------------------- // @mfunc StartTransaction with zero timeout - S_OK // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_17() { XACTOPT TxnOptions; BOOL fResults = FALSE; //Set time out to non zero TxnOptions.ulTimeout = 0; TxnOptions.szDescription[0] = '\0'; //Set our time out option if (CHECK(m_pITxnOptions->SetOptions(&TxnOptions), S_OK)) { m_hr = m_pChgRowset1->m_pITxnLocal->StartTransaction(m_fIsoLevel, 0, m_pITxnOptions, NULL); ReleaseAllRowsetsAndTxns(); //Time out of zero should always work if (CHECK(m_hr, S_OK)) return TEST_PASS; } return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(18) //*----------------------------------------------------------------------- // @mfunc StartTransaction with pulTransactionLevel = NULL - S_OK // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_18() { if (CHECK(m_pChgRowset1->m_pITxnLocal->StartTransaction(m_fIsoLevel, 0, NULL, NULL), S_OK)) { ReleaseAllRowsetsAndTxns(); return TEST_PASS; } else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(19) //*----------------------------------------------------------------------- // @mfunc StartTransaction with isoFlags != 0 - XACT_E_NOISORETAIN // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_19() { //Make isoFlags 1, nothing but 0 is valid if (CHECK(m_pChgRowset1->m_pITxnLocal->StartTransaction(m_fIsoLevel, 1, NULL,NULL), XACT_E_NOISORETAIN)) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(20) //*----------------------------------------------------------------------- // @mfunc StartTransaction with isoLevel = invalid - XACT_E_ISOLATIONLEVEL // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_20() { //Make isoLevel invalid -- since these values are mutually exclusive, //or'ing them together will create an invalid value if (m_hr = CHECK(m_pChgRowset1->m_pITxnLocal->StartTransaction( ISOLATIONLEVEL_CHAOS | ISOLATIONLEVEL_READUNCOMMITTED | ISOLATIONLEVEL_READCOMMITTED | ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL), XACT_E_ISOLATIONLEVEL)) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(21) //*----------------------------------------------------------------------- // @mfunc GetOptions with pOptions = NULL - E_INVALIDARG // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_21() { ITransactionOptions * pITxnOptions = NULL; BOOL fResults = FALSE; if (CHECK(m_pChgRowset1->m_pITxnLocal->GetOptionsObject(&pITxnOptions), S_OK)) { //Make sure we can also get the options if (CHECK(pITxnOptions->GetOptions(NULL), E_INVALIDARG)) fResults = TRUE; SAFE_RELEASE(pITxnOptions); } if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(22) //*----------------------------------------------------------------------- // @mfunc GetOptions with pOptions allocated on stack - S_OK // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_22() { ITransactionOptions * pITxnOptions = NULL; BOOL fResults = FALSE; XACTOPT TxnOptions; if (CHECK(m_pChgRowset1->m_pITxnLocal->GetOptionsObject(&pITxnOptions), S_OK)) { //Make sure we can also get the options with //TxnOptions allocated on stack (so we know //provider isn't trying to free the memory) if (CHECK(pITxnOptions->GetOptions(&TxnOptions), S_OK)) fResults = TRUE; SAFE_RELEASE(pITxnOptions); } if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(23) //*----------------------------------------------------------------------- // @mfunc SetOptions with max szDescription // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_23() { ITransactionOptions * pITxnOptions = NULL; BOOL fResults = FALSE; XACTOPT TxnOptions; CHAR szDesc[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; //39 chars, plus null terminator //Make sure we are using exactly the whole portion of the struct ASSERT(sizeof(szDesc) == MAX_TRAN_DESC); strcpy((CHAR *)TxnOptions.szDescription, szDesc); TxnOptions.ulTimeout = 0; if (CHECK(m_pChgRowset1->m_pITxnLocal->GetOptionsObject(&pITxnOptions), S_OK)) { if (CHECK(pITxnOptions->SetOptions(&TxnOptions), S_OK)) if (CHECK(pITxnOptions->GetOptions(&TxnOptions), S_OK)) //Make sure we didn't loose any characters or forget null terminator if (COMPARE(strcmp((CHAR *)TxnOptions.szDescription, szDesc), 0)) fResults = TRUE; SAFE_RELEASE(pITxnOptions); } if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(24) //*----------------------------------------------------------------------- // @mfunc SetOptions with ppOptions = NULL - E_INVALIDARG // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_24() { ITransactionOptions * pITxnOptions = NULL; BOOL fResults = FALSE; if (CHECK(m_pChgRowset1->m_pITxnLocal->GetOptionsObject(&pITxnOptions), S_OK)) { //Expect invalid arg for NULL if (CHECK(pITxnOptions->SetOptions(NULL), E_INVALIDARG)) fResults = TRUE; SAFE_RELEASE(pITxnOptions); } if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(25) //*----------------------------------------------------------------------- // @mfunc Commit with grfTC =XACTC_SYNC_PHASETWO - S_OK // // @rdesc TEST_PASS or TEST_FAIL // int TCITxnReturnVals::Variation_25() { // TO DO: Add your own code here BOOL fResults = FALSE; //Start Txn if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) { //Check for proper return code for given parameters //XACTTC_SYNC_PHASETWO should be supported by ODBC Provider if(CHECK(m_pChgRowset1->m_pITxnLocal->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), S_OK)) fResults = TRUE; } //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_TERMINATE_METHOD //-------------------------------------------------------------------- // @mfunc TestCase Termination Routine // // @rdesc TRUE or FALSE // BOOL TCITxnReturnVals::Terminate() { // Release the Objects SAFE_RELEASE(m_pITxnOptions) // {{ TCW_TERM_BASECLASS_CHECK2 return(CTxnImmed::Terminate()); } // }} // }} // }} // {{ TCW_TC_PROTOTYPE(TCIRowsetUpdate) //*----------------------------------------------------------------------- //| Test Case: TCIRowsetUpdate - Transacted rowsets in Buffered Update mode //| Created: 05/06/96 //*----------------------------------------------------------------------- //-------------------------------------------------------------------- // @mfunc TestCase Initialization Routine // // @rdesc TRUE or FALSE // BOOL TCIRowsetUpdate::Init() { // {{ TCW_INIT_BASECLASS_CHECK if(CTxnUpdate::Init()) // }} { // TO DO: Add your own code here return TRUE; } return FALSE; } // {{ TCW_VAR_PROTOTYPE(1) //*----------------------------------------------------------------------- // @mfunc Change, Abort with fRetaining = TRUE, Update // // @rdesc TEST_PASS or TEST_FAIL // int TCIRowsetUpdate::Variation_1() { BOOL fResults = FALSE; HRESULT hr; //if provider does not support IRowsetChange //if provider does not support IRowsetUpdate if (!m_pChgUpdRowset1->m_fChange || !m_pChgUpdRowset1->m_fUpdate) { odtLog << L"IRowsetChange or IRowsetUpdate not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Start Txn if (!CHECK(m_pChgUpdRowset1->StartTxn(m_fIsoLevel), S_OK)) goto CLEANUP; hr=m_pChgUpdRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //Make a change if (!COMPARE(Change(m_pChgUpdRowset1), TRUE)) goto CLEANUP; //Abort the change if (!CHECK(m_pChgUpdRowset1->Abort(TRUE, TRUE), S_OK)) goto CLEANUP; //Verify pending row (or lack thereof) after Update() if (!m_pChgUpdRowset1->CheckPendingRow(EABORT, 1)) goto CLEANUP; //Use second rowset to verify we can't see the change if (!COMPARE(FindChange(m_pChgUpdRowset2), FALSE)) goto CLEANUP; //delete second rowset in case it will be used again in this variation if (!CHECK(m_pChgUpdRowset2->FreeRowset(), S_OK)) goto CLEANUP; //Call update for all pending changes if (!CHECK(m_pChgUpdRowset1->m_pIRowsetUpdate->Update(NULL, 0, NULL, NULL, NULL, NULL), S_OK)) goto CLEANUP; //Commit the update if (!CHECK(m_pChgUpdRowset1->Commit(TRUE, TRUE), S_OK)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param if (m_pChgUpdRowset1->m_fAbortPreserve) { //We should now be able to see the change since we updated //and our change from before the abort should have been //preserved if (!COMPARE(FindChange(m_pChgUpdRowset2), TRUE)) { goto CLEANUP; } else { //if this commits then the Update of the pending rows looks like a delete then //insert as far as the back end data is concerened g_ulLastActualInsert++; g_ulLastActualDelete++; } } else { //We shouldn't see a change because the rowset //should have zomibied on abort, losing the pending change if (!COMPARE(FindChange(m_pChgUpdRowset2), FALSE)) goto CLEANUP; } //Everything went OK fResults = TRUE; CLEANUP: CHECK(m_pChgUpdRowset1->FreeRowset(), S_OK); CHECK(m_pChgUpdRowset2->FreeRowset(), S_OK); ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(2) //*----------------------------------------------------------------------- // @mfunc Change, Abort with fRetaining = FALSE, Update // // @rdesc TEST_PASS or TEST_FAIL // int TCIRowsetUpdate::Variation_2() { BOOL fResults = FALSE; HRESULT hr; //if provider does not support IRowsetChange //if provider does not support IRowsetUpdate if (!m_pChgUpdRowset1->m_fChange || !m_pChgUpdRowset1->m_fUpdate) { odtLog << L"IRowsetChange or IRowsetUpdate not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Start Txn if (!CHECK(m_pChgUpdRowset1->StartTxn(m_fIsoLevel), S_OK)) goto CLEANUP; hr=m_pChgUpdRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //Make a change if (!COMPARE(Change(m_pChgUpdRowset1), TRUE)) goto CLEANUP; //Abort the change, with fRetaining = FALSE, we should then be in autocommit if (!CHECK(m_pChgUpdRowset1->Abort(TRUE, FALSE), S_OK)) goto CLEANUP; //Verify pending row (or lack thereof) after abort if (!m_pChgUpdRowset1->CheckPendingRow(EABORT, 1)) goto CLEANUP; //Use second rowset to verify we can't see the change if (!COMPARE(FindChange(m_pChgUpdRowset2), FALSE)) goto CLEANUP; //delete second rowset in case it will be used again in this variation if (!CHECK(m_pChgUpdRowset2->FreeRowset(), S_OK)) goto CLEANUP; //Call update for all pending changes if (!CHECK(m_pChgUpdRowset1->m_pIRowsetUpdate->Update(NULL, 0, NULL, NULL, NULL, NULL), S_OK)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param if (m_pChgUpdRowset1->m_fAbortPreserve) { //If the rowset was preserved, our change should have occured if (!COMPARE(FindChange(m_pChgUpdRowset2), TRUE)) { goto CLEANUP; } else { //if this commits then the Update of the pending rows looks like a delete then //insert as far as the back end data is concerened g_ulLastActualInsert++; g_ulLastActualDelete++; } } else { //We still shouldn't be able to see the change since we aborted //and our rowset wasn't preserved if (!COMPARE(FindChange(m_pChgUpdRowset2), FALSE)) goto CLEANUP; } //Everything went OK fResults = TRUE; CLEANUP: CHECK(m_pChgUpdRowset1->FreeRowset(), S_OK); CHECK(m_pChgUpdRowset2->FreeRowset(), S_OK); ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(3) //*----------------------------------------------------------------------- // @mfunc Change, Update, Abort with fRetaining = TRUE // // @rdesc TEST_PASS or TEST_FAIL // int TCIRowsetUpdate::Variation_3() { BOOL fResults = FALSE; HRESULT hr; //if provider does not support IRowsetChange //if provider does not support IRowsetUpdate if (!m_pChgUpdRowset1->m_fChange || !m_pChgUpdRowset1->m_fUpdate) { odtLog << L"IRowsetChange or IRowsetUpdate not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Start Txn if (!CHECK(m_pChgUpdRowset1->StartTxn(m_fIsoLevel), S_OK)) goto CLEANUP; hr=m_pChgUpdRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //Make a change if (!COMPARE(Change(m_pChgUpdRowset1), TRUE)) goto CLEANUP; //Call update for all pending changes if (!CHECK(m_pChgUpdRowset1->m_pIRowsetUpdate->Update(NULL, 0, NULL, NULL, NULL, NULL), S_OK)) goto CLEANUP; //Abort the change, with fRetaining = TRUE if (!CHECK(m_pChgUpdRowset1->Abort(TRUE, TRUE), S_OK)) goto CLEANUP; //Verify there are no pending rows after the abort since //we've already called update if (!m_pChgUpdRowset1->CheckPendingRow(EABORT, 0)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param //Use second rowset to verify we can't see the change if (!COMPARE(FindChange(m_pChgUpdRowset2), FALSE)) goto CLEANUP; //Everything went OK fResults = TRUE; CLEANUP: CHECK(m_pChgUpdRowset1->FreeRowset(), S_OK); CHECK(m_pChgUpdRowset2->FreeRowset(), S_OK); ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(4) //*----------------------------------------------------------------------- // @mfunc Change, Update, Abort with fRetaining = FALSE // // @rdesc TEST_PASS or TEST_FAIL // int TCIRowsetUpdate::Variation_4() { BOOL fResults = FALSE; HRESULT hr; //if provider does not support IRowsetChange //if provider does not support IRowsetUpdate if (!m_pChgUpdRowset1->m_fChange || !m_pChgUpdRowset1->m_fUpdate) { odtLog << L"IRowsetChange or IRowsetUpdate not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Start Txn if (!CHECK(m_pChgUpdRowset1->StartTxn(m_fIsoLevel), S_OK)) goto CLEANUP; hr=m_pChgUpdRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //Make a change if (!COMPARE(Change(m_pChgUpdRowset1), TRUE)) goto CLEANUP; if (!CHECK(m_pChgUpdRowset1->m_pIRowsetUpdate->Update(NULL, 0, NULL, NULL, NULL, NULL), S_OK)) goto CLEANUP; //Abort the change, with fRetaining = FALSE if (!CHECK(m_pChgUpdRowset1->Abort(TRUE, FALSE), S_OK)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param //Verify no pending row (or lack thereof) after the abort //since we already called update if (!m_pChgUpdRowset1->CheckPendingRow(EABORT, 0)) goto CLEANUP; //Use second rowset to verify we can't see the change if (!COMPARE(FindChange(m_pChgUpdRowset2), FALSE)) goto CLEANUP; //Everything went OK fResults = TRUE; CLEANUP: CHECK(m_pChgUpdRowset1->FreeRowset(), S_OK); CHECK(m_pChgUpdRowset2->FreeRowset(), S_OK); ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(5) //*----------------------------------------------------------------------- // @mfunc Change, Commit with fRetaining = TRUE, Update // // @rdesc TEST_PASS or TEST_FAIL // int TCIRowsetUpdate::Variation_5() { BOOL fResults = FALSE; HRESULT hr; //if provider does not support IRowsetChange //if provider does not support IRowsetUpdate if (!m_pChgUpdRowset1->m_fChange || !m_pChgUpdRowset1->m_fUpdate) { odtLog << L"IRowsetChange or IRowsetUpdate not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Start Txn if (!CHECK(m_pChgUpdRowset1->StartTxn(m_fIsoLevel), S_OK)) goto CLEANUP; hr=m_pChgUpdRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //Make a change if (!COMPARE(Change(m_pChgUpdRowset1), TRUE)) goto CLEANUP; //Commit if (!CHECK(m_pChgUpdRowset1->Commit(TRUE, TRUE), S_OK)) goto CLEANUP; //Verify a possible pending row after commit if (!m_pChgUpdRowset1->CheckPendingRow(ECOMMIT, 1)) goto CLEANUP; //Use second rowset to verify we can't see //the change since we haven't updated yet if (!COMPARE(FindChange(m_pChgUpdRowset2), FALSE)) goto CLEANUP; //Call update for all pending changes if (!CHECK(m_pChgUpdRowset1->m_pIRowsetUpdate->Update(NULL, 0, NULL, NULL, NULL, NULL), S_OK)) goto CLEANUP; //Commit our update if (!CHECK(m_pChgUpdRowset1->Commit(TRUE, TRUE), S_OK)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param if (m_pChgUpdRowset1->m_fCommitPreserve) { //We should now be able to see the change since we updated //and our change from before the commit should have been //preserved if (!COMPARE(FindChange(m_pChgUpdRowset2), TRUE)) { goto CLEANUP; } else { //if this commits then the Update of the pending rows looks like a delete then //insert as far as the back end data is concerened g_ulLastActualInsert++; g_ulLastActualDelete++; } } else { //We shouldn't see a change because the rowset //should have zomibied on commit, losing the pending change if (!COMPARE(FindChange(m_pChgUpdRowset2), FALSE)) goto CLEANUP; } //Everything went OK fResults = TRUE; CLEANUP: CHECK(m_pChgUpdRowset1->FreeRowset(), S_OK); CHECK(m_pChgUpdRowset2->FreeRowset(), S_OK); ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(6) //*----------------------------------------------------------------------- // @mfunc Change, Commit with fRetaining = FALSE, Update // // @rdesc TEST_PASS or TEST_FAIL // int TCIRowsetUpdate::Variation_6() { BOOL fResults = FALSE; HRESULT hr; //if provider does not support IRowsetChange //if provider does not support IRowsetUpdate if (!m_pChgUpdRowset1->m_fChange || !m_pChgUpdRowset1->m_fUpdate) { odtLog << L"IRowsetChange or IRowsetUpdate not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Start Txn if (!CHECK(m_pChgUpdRowset1->StartTxn(m_fIsoLevel), S_OK)) goto CLEANUP; hr=m_pChgUpdRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //Make a change if (!COMPARE(Change(m_pChgUpdRowset1), TRUE)) goto CLEANUP; //Commit with fRetaining = FALSE, we should then be in autocommit if (!CHECK(m_pChgUpdRowset1->Commit(TRUE, FALSE), S_OK)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param //Verify pending row after commit if (!m_pChgUpdRowset1->CheckPendingRow(ECOMMIT, 1)) goto CLEANUP; //Use second rowset to verify we can't see //the change since we haven't updated yet if (!COMPARE(FindChange(m_pChgUpdRowset2), FALSE)) goto CLEANUP; //delete second rowset in case it will be used again in this variation if (!CHECK(m_pChgUpdRowset2->FreeRowset(), S_OK)) goto CLEANUP; //Call update for all pending changes if (!CHECK(m_pChgUpdRowset1->m_pIRowsetUpdate->Update(NULL, 0, NULL, NULL, NULL, NULL), S_OK)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param if (m_pChgUpdRowset1->m_fCommitPreserve) { //We should now be able to see the change since we updated //and our change from before the commit should have been //preserved if (!COMPARE(FindChange(m_pChgUpdRowset2), TRUE)) { goto CLEANUP; } else { //if this commits then the Update of the pending rows looks like a delete then //insert as far as the back end data is concerened g_ulLastActualInsert++; g_ulLastActualDelete++; } } else { //We shouldn't see a change because the rowset //should have zomibied on commit, losing the pending change if (!COMPARE(FindChange(m_pChgUpdRowset2), FALSE)) goto CLEANUP; } //Everything went OK fResults = TRUE; CLEANUP: CHECK(m_pChgUpdRowset1->FreeRowset(), S_OK); CHECK(m_pChgUpdRowset2->FreeRowset(), S_OK); ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(7) //*----------------------------------------------------------------------- // @mfunc Change, Update, Commit with fRetaining = TRUE // // @rdesc TEST_PASS or TEST_FAIL // int TCIRowsetUpdate::Variation_7() { BOOL fResults = FALSE; HRESULT hr; //if provider does not support IRowsetChange //if provider does not support IRowsetUpdate if (!m_pChgUpdRowset1->m_fChange || !m_pChgUpdRowset1->m_fUpdate) { odtLog << L"IRowsetChange or IRowsetUpdate not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Start Txn if (!CHECK(m_pChgUpdRowset1->StartTxn(m_fIsoLevel), S_OK)) goto CLEANUP; hr=m_pChgUpdRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //Make a change if (!COMPARE(Change(m_pChgUpdRowset1), TRUE)) goto CLEANUP; //Call update for all pending changes if (!CHECK(m_pChgUpdRowset1->m_pIRowsetUpdate->Update(NULL, 0, NULL, NULL, NULL, NULL), S_OK)) goto CLEANUP; //Commit the change, with fRetaining = TRUE if (!CHECK(m_pChgUpdRowset1->Commit(TRUE, TRUE), S_OK)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param //Verify no pending row if (!m_pChgUpdRowset1->CheckPendingRow(ECOMMIT, 0)) goto CLEANUP; //Use second rowset to verify we can see the change if (!COMPARE(FindChange(m_pChgUpdRowset2), TRUE)) { goto CLEANUP; } else { //if this commits then the Update of the pending rows looks like a delete then //insert as far as the back end data is concerened g_ulLastActualInsert++; g_ulLastActualDelete++; } //Everything went OK fResults = TRUE; CLEANUP: CHECK(m_pChgUpdRowset1->FreeRowset(), S_OK); CHECK(m_pChgUpdRowset2->FreeRowset(), S_OK); ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(8) //*----------------------------------------------------------------------- // @mfunc Change, Update, Commit with fRetaining = FALSE // // @rdesc TEST_PASS or TEST_FAIL // int TCIRowsetUpdate::Variation_8() { BOOL fResults = FALSE; HRESULT hr; //if provider does not support IRowsetChange //if provider does not support IRowsetUpdate if (!m_pChgUpdRowset1->m_fChange || !m_pChgUpdRowset1->m_fUpdate) { odtLog << L"IRowsetChange or IRowsetUpdate not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Start Txn if (!CHECK(m_pChgUpdRowset1->StartTxn(m_fIsoLevel), S_OK)) goto CLEANUP; hr=m_pChgUpdRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //Make a change if (!COMPARE(Change(m_pChgUpdRowset1), TRUE)) goto CLEANUP; //Call update for all pending changes if (!CHECK(m_pChgUpdRowset1->m_pIRowsetUpdate->Update(NULL, 0, NULL, NULL, NULL, NULL), S_OK)) goto CLEANUP; //Commit the change, with fRetaining = TRUE if (!CHECK(m_pChgUpdRowset1->Commit(TRUE, FALSE), S_OK)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param //Verify no pending row if (!m_pChgUpdRowset1->CheckPendingRow(ECOMMIT, 0)) goto CLEANUP; //Use second rowset to verify we can see the change if (!COMPARE(FindChange(m_pChgUpdRowset2), TRUE)) { goto CLEANUP; } else { //if this commits then the Update of the pending rows looks like a delete then //insert as far as the back end data is concerened g_ulLastActualInsert++; g_ulLastActualDelete++; } //Everything went OK fResults = TRUE; CLEANUP: CHECK(m_pChgUpdRowset1->FreeRowset(), S_OK); CHECK(m_pChgUpdRowset2->FreeRowset(), S_OK); ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(9) //*----------------------------------------------------------------------- // @mfunc Open read only rowset // // @rdesc TEST_PASS or TEST_FAIL // int TCIRowsetUpdate::Variation_9() { BOOL fResults = FALSE; HRESULT hr; //if provider does not support IRowsetChange //if provider does not support IRowsetUpdate if (!m_pChgUpdRowset1->m_fChange || !m_pChgUpdRowset1->m_fUpdate) { odtLog << L"IRowsetChange or IRowsetUpdate not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Start Txn if (!CHECK(m_pChgUpdRowset1->StartTxn(ISOLATIONLEVEL_READUNCOMMITTED), S_OK)) { goto CLEANUP; } hr=m_pChgUpdRowset1->MakeRowsetReadOnly(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } fResults = TRUE; CLEANUP: CHECK(m_pChgUpdRowset1->FreeRowset(), S_OK); ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_TERMINATE_METHOD //-------------------------------------------------------------------- // @mfunc TestCase Termination Routine // // @rdesc TRUE or FALSE // BOOL TCIRowsetUpdate::Terminate() { // {{ TCW_TERM_BASECLASS_CHECK2 return(CTxnUpdate::Terminate()); } // }} // }} // }} // {{ TCW_TC_PROTOTYPE(CNoTxn) //*----------------------------------------------------------------------- //| Test Case: CNoTxn - Abort and Commit called before StartTransaction //| Created: 05/14/96 //*----------------------------------------------------------------------- //-------------------------------------------------------------------- // @mfunc TestCase Initialization Routine // // @rdesc TRUE or FALSE // BOOL CNoTxn::Init() { m_pITxnLocal = NULL; // {{ TCW_INIT_BASECLASS_CHECK if(CSessionObject::Init()) // }} { //We won't do a SetDataSource object here, just for variety -- //we'll create a new DSO if (CHECK(CreateDBSession(), S_OK)) //Txns must be supported if we passed ModuleInit if(CHECK(GetSessionObject(IID_ITransactionLocal, (IUnknown**)&m_pITxnLocal), S_OK)) return TRUE; } return FALSE; } // {{ TCW_VAR_PROTOTYPE(1) //*----------------------------------------------------------------------- // @mfunc Abort with fRetaining = FALSE before StartTransaction - XACT_E_NOTRANSACTION // // @rdesc TEST_PASS or TEST_FAIL // int CNoTxn::Variation_1() { //Try to abort when we've never called StartTransaction if (CHECK(m_pITxnLocal->Abort(NULL, FALSE, FALSE), XACT_E_NOTRANSACTION)) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(2) //*----------------------------------------------------------------------- // @mfunc Commit with fRetaining = FALSE before StartTransaction - XACT_E_NOTRANSACTION // // @rdesc TEST_PASS or TEST_FAIL // int CNoTxn::Variation_2() { //Try to commmit when we've never called StartTransaction if (CHECK(m_pITxnLocal->Commit(FALSE, 0, 0), XACT_E_NOTRANSACTION)) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(3) //*----------------------------------------------------------------------- // @mfunc GetTransactionInfo before StartTransaction - XACT_E_NOTRANSACTION // // @rdesc TEST_PASS or TEST_FAIL // int CNoTxn::Variation_3() { XACTTRANSINFO TxnInfo; //Try to GetTransactionInfo when we've never called StartTransaction if (CHECK(m_pITxnLocal->GetTransactionInfo(&TxnInfo), XACT_E_NOTRANSACTION)) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(4) //*----------------------------------------------------------------------- // @mfunc GetOptionsObject before StartTransaction - S_OK // // @rdesc TEST_PASS or TEST_FAIL // int CNoTxn::Variation_4() { BOOL fResults = TEST_FAIL; XACTOPT TxnOptions; ITransactionOptions *pITxnOptions = NULL; HRESULT hr; //Try to get the txn options interface hr = m_pITxnLocal->GetOptionsObject(&pITxnOptions); if (S_OK==hr) { //Make sure we can also get the options if (CHECK(pITxnOptions->GetOptions(&TxnOptions), S_OK)) { fResults = TEST_PASS; } SAFE_RELEASE(pITxnOptions); } else { if (DB_E_NOTSUPPORTED==hr) { //test for IID_ITransactionObject OI here if (!VerifyInterface(m_pIOpenRowset, IID_ITransactionObject,SESSION_INTERFACE, (IUnknown **)&pITxnOptions)) { SAFE_RELEASE(pITxnOptions); return TEST_SKIPPED; } //free memory that was incorrectly allocated SAFE_RELEASE(pITxnOptions); } } if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(5) //*----------------------------------------------------------------------- // @mfunc Abort with fRetaining = TRUE before StartTransaction - XACT_E_NOTRANSACTION // // @rdesc TEST_PASS or TEST_FAIL // int CNoTxn::Variation_5() { //Try to abort when we've never called StartTransaction if (CHECK(m_pITxnLocal->Abort(NULL, TRUE, FALSE), XACT_E_NOTRANSACTION)) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(6) //*----------------------------------------------------------------------- // @mfunc Commit with fRetaining = TRUE before StartTransaction - XACT_E_NOTRANSACTION // // @rdesc TEST_PASS or TEST_FAIL // int CNoTxn::Variation_6() { //Try to commmit when we've never called StartTransaction if (CHECK(m_pITxnLocal->Commit(TRUE, 0, 0), XACT_E_NOTRANSACTION)) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_TERMINATE_METHOD //-------------------------------------------------------------------- // @mfunc TestCase Termination Routine // // @rdesc TRUE or FALSE // BOOL CNoTxn::Terminate() { SAFE_RELEASE(m_pITxnLocal); ReleaseDBSession(); ReleaseDataSourceObject(); // {{ TCW_TERM_BASECLASS_CHECK2 return(CSessionObject::Terminate()); } // }} // }} // }} // {{ TCW_TC_PROTOTYPE(CMultipleTxns) //*----------------------------------------------------------------------- //| Test Case: CMultipleTxns - Multiple Transactions //| Created: 05/14/96 //*----------------------------------------------------------------------- //-------------------------------------------------------------------- // @mfunc TestCase Initialization Routine // // @rdesc TRUE or FALSE // BOOL CMultipleTxns::Init() { m_pTable = NULL; m_ulCurrentDelete = 1; m_ulCurrentInsert = NUM_ROWS+1; // {{ TCW_INIT_BASECLASS_CHECK if(CTxnImmed::Init()) // }} { CreateDBSession(); //Create a second table m_pTable = new CTable( m_pIOpenRowset, (LPWSTR)gwszModuleName); if (!m_pTable) { odtLog << wszMemoryAllocationError; return FALSE; } //Start with a table with rows if (FAILED(m_pTable->CreateTable(NUM_ROWS))) return FALSE; //Point second rowset at a second table so their are //no conflicting locks -- we are only testing //concurrent OLE DB objects here, not concurrent //access to the same data. //m_pChgRowset2 now owns the table in the db and will drop it. m_pChgRowset2->SetTable(m_pTable, DELETETABLE_YES); return TRUE; } return FALSE; } // {{ TCW_VAR_PROTOTYPE(1) //*----------------------------------------------------------------------- // @mfunc Abort two transactions on same DSO // // @rdesc TEST_PASS or TEST_FAIL // int CMultipleTxns::Variation_1() { BOOL fResults = FALSE; HRESULT hr; CLSID clsidMSDASQL = CLSID_MSDASQL; //Start Txns if (!CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) goto CLEANUP; if (!CHECK(m_pChgRowset2->StartTxn(m_fIsoLevel), S_OK)) goto CLEANUP; hr=m_pChgRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } // Hack: Sleep for 5 seconds, so changes are visible // MSDASQL/JET needs this time to make changes visible if (GetModInfo()->GetThisTestModule()->m_ProviderClsid==clsidMSDASQL) ::Sleep(5000); hr=m_pChgRowset2->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } ///////////////////////////////////////////////////////////////////// //Do something in each txn, abort it, and verify change was aborted ///////////////////////////////////////////////////////////////////// //Rowset 1 //if provider does not support IRowsetChange if (!m_pChgRowset1->m_fChange) { odtLog << L"IRowsetChange not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Make a change if(!COMPARE(Change(m_pChgRowset1), TRUE)) goto CLEANUP; //Now abort if (!CHECK(m_hr = m_pChgRowset1->Abort(TRUE, FALSE), S_OK)) goto CLEANUP; //Shouldn't be able to see the change if (!COMPARE(FindChange(m_pChgRowset1), FALSE)) goto CLEANUP; //Rowset 2, have to figure the count manually since we //aren't on the same table as m_pChgRowset1 anymore. //Make a change if(!COMPARE(m_pChgRowset2->Change(m_ulCurrentDelete, m_ulCurrentInsert), TRUE)) goto CLEANUP; //Now abort, preserving rowset if (!CHECK(m_hr = m_pChgRowset2->Abort(TRUE, FALSE), S_OK)) goto CLEANUP; //Shouldn't be able to see the change if (!COMPARE(m_pChgRowset2->FindRow(m_ulCurrentInsert, NULL), FALSE)) goto CLEANUP; fResults = TRUE; CLEANUP: ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(2) //*----------------------------------------------------------------------- // @mfunc Commit two transactions on same DSO // // @rdesc TEST_PASS or TEST_FAIL // int CMultipleTxns::Variation_2() { BOOL fResults = FALSE; HRESULT hr; //Start Txns if (!CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) goto CLEANUP; if (!CHECK(m_pChgRowset2->StartTxn(m_fIsoLevel), S_OK)) goto CLEANUP; //Make rowsets hr=m_pChgRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } hr=m_pChgRowset2->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } ///////////////////////////////////////////////////////////////////// //Do something in each txn, commit it, and verify change was committed ///////////////////////////////////////////////////////////////////// //Rowset 1 //if provider does not support IRowsetChange if (!m_pChgRowset1->m_fChange) { odtLog << L"IRowsetChange not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Make a change if(!COMPARE(Change(m_pChgRowset1), TRUE)) goto CLEANUP; //Now commit if (!CHECK(m_hr = m_pChgRowset1->Commit(TRUE, FALSE), S_OK)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param //Should be able to see the change if (!COMPARE(FindChange(m_pChgRowset1), TRUE)) goto CLEANUP; //Rowset 2, have to figure the count manually since we //aren't on the same table as m_pChgRowset1 anymore. //Make a change if(!COMPARE(m_pChgRowset2->Change(m_ulCurrentDelete, m_ulCurrentInsert), TRUE)) goto CLEANUP; //Now commit, preserving rowset if (!CHECK(m_hr = m_pChgRowset2->Commit(TRUE, FALSE), S_OK)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param //Should be able to see the change if (!COMPARE(m_pChgRowset2->FindRow(m_ulCurrentInsert, NULL), TRUE)) { //Increment our count since we already committed the delete/insert //but won't hit the increment before CLEANUP m_ulCurrentInsert++; m_ulCurrentDelete++; goto CLEANUP; } //Increment our count since we committed the delete/insert m_ulCurrentInsert++; m_ulCurrentDelete++; fResults = TRUE; CLEANUP: ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(3) //*----------------------------------------------------------------------- // @mfunc Abort one transaction, commit the other, on same DSO // // @rdesc TEST_PASS or TEST_FAIL // int CMultipleTxns::Variation_3() { BOOL fResults = FALSE; HRESULT hr; //Start Txns if (!CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) goto CLEANUP; if (!CHECK(m_pChgRowset2->StartTxn(m_fIsoLevel), S_OK)) goto CLEANUP; //Make rowsets hr=m_pChgRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } hr=m_pChgRowset2->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } ///////////////////////////////////////////////////////////////////// //Do something in each txn, commit one, abort the other and verify ///////////////////////////////////////////////////////////////////// //Rowset 1 //if provider does not support IRowsetChange if (!m_pChgRowset1->m_fChange) { odtLog << L"IRowsetChange not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } //Make a change if(!COMPARE(Change(m_pChgRowset1), TRUE)) goto CLEANUP; //Now commit if (!CHECK(m_hr = m_pChgRowset1->Commit(TRUE, FALSE), S_OK)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param //Should be able to see the change if (!COMPARE(FindChange(m_pChgRowset1), TRUE)) goto CLEANUP; //Rowset 2, have to figure the count manually since we //aren't on the same table as m_pChgRowset1 anymore. //Make a change if (!COMPARE(m_pChgRowset2->Change(m_ulCurrentDelete, m_ulCurrentInsert), TRUE)) goto CLEANUP; //Now abort, preserving rowset if (!CHECK(m_hr = m_pChgRowset2->Abort(TRUE, FALSE), S_OK)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param //Shouldn't be able to see the change if (!COMPARE(m_pChgRowset2->FindRow(m_ulCurrentInsert, NULL), FALSE)) goto CLEANUP; fResults = TRUE; CLEANUP: ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(4) //*----------------------------------------------------------------------- // @mfunc Abort and Commit with multiple commands/rowsets active // // @rdesc TEST_PASS or TEST_FAIL // int CMultipleTxns::Variation_4() { BOOL fResults = FALSE; IUnknown* pIUnknown = NULL; HRESULT hr; //Make first rowset hr=m_pChgRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //Get Session from first object if (!CHECK(m_pChgRowset1->GetSessionObject(IID_IUnknown, (IUnknown**)&pIUnknown), S_OK)) goto CLEANUP; /////////////////////////////////////////////////////// //Create a second command/rowset on the same DB Session /////////////////////////////////////////////////////// //First we must release original session our second object was set with m_pChgRowset2->ReleaseRowsetObject(); m_pChgRowset2->ReleaseCommandObject(); m_pChgRowset2->ReleaseDBSession(); SAFE_RELEASE(m_pChgRowset2->m_pITxnLocal); //Now set session to that of our first rowset m_pChgRowset2->SetDBSession(pIUnknown); //Make sure we use the same table so row numbering is right -- //m_pChgRowset2 won't own this table, just use it in this variation m_pChgRowset2->SetTable(m_pChgRowset1->m_pTable, DELETETABLE_NO); //Make second rowset m_pChgRowset2->SetTxnRowsetProperties(); hr=m_pChgRowset2->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //Start Txn on our DB Session m_hr = m_pChgRowset1->StartTxn(m_fIsoLevel); //Some providers such as ODBC Provider to SQL Server will not allow this //because they can't have mutliple commands per transaction if (m_hr == XACT_E_CONNECTION_DENIED) { odtLog << L"This provider does not support multiple commands in the same transaction, this is expected behavior for ODBC Provider to SQL Server. \n"; fResults = TRUE; goto CLEANUP; } else //Providers such as ODBC Provider to Access can't support //starting a transaction with a rowset open if (m_hr == E_FAIL) { //So close rowsets before creating txn m_pChgRowset1->ReleaseRowsetObject(); m_pChgRowset2->ReleaseRowsetObject(); m_hr = m_pChgRowset1->StartTxn(m_fIsoLevel); //Now recreate the rowsets m_pChgRowset1->MakeRowset(); m_pChgRowset2->MakeRowset(); //StartTxn should always work since rowsets were closed if (!CHECK(m_hr, S_OK)) goto CLEANUP; } else if (!CHECK(m_hr, S_OK)) goto CLEANUP; /////////////////////////////////////////////////////////////// //Make changes to both rowsets and verify they both get aborted /////////////////////////////////////////////////////////////// //if provider does not support IRowsetChange if (!m_pChgRowset1->m_fChange) { odtLog << L"IRowsetChange not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } if(!COMPARE(Change(m_pChgRowset1), TRUE)) goto CLEANUP; //Record all the txn related things that are now true about //rowset 2 because it shares a session with rowset 1 m_pChgRowset2->CopyTxnInfo(m_pChgRowset1); if(!COMPARE(Change(m_pChgRowset2), TRUE)) goto CLEANUP; //Now abort the whole session if (!CHECK(m_hr = m_pChgRowset1->Abort(FALSE, FALSE), S_OK)) goto CLEANUP; //Cleanup rowsets and start with new ones, in case the first ones //were zombied. Do this before StartTxn in case we don't support //StartTransaction after rowsets are open m_pChgRowset1->ReleaseRowsetObject(); m_pChgRowset2->ReleaseRowsetObject(); //Start a new Txn since we aborted the last one non retaining CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK); //Record all the txn related things that are now true about //rowset 2 because it shares a session with rowset 1 m_pChgRowset2->CopyTxnInfo(m_pChgRowset1); hr=m_pChgRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } hr=m_pChgRowset2->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //Shouldn't be able to see the changes if (!COMPARE(FindChange(m_pChgRowset1), FALSE)) goto CLEANUP; if (!COMPARE(FindChange(m_pChgRowset2), FALSE)) goto CLEANUP; ///////////////////////////////////////////////////////////////// //Make changes to both rowsets and verify they both get committed ///////////////////////////////////////////////////////////////// if(!COMPARE(Change(m_pChgRowset1), TRUE)) goto CLEANUP; //Record all the txn related things that are now true about //rowset 2 because it shares a session with rowset 1 m_pChgRowset2->CopyTxnInfo(m_pChgRowset1); if(!COMPARE(Change(m_pChgRowset2), TRUE)) goto CLEANUP; //Now commit the whole session if (!CHECK(m_hr = m_pChgRowset1->Commit(FALSE, FALSE), S_OK)) goto CLEANUP; //Sleep for a few seconds; this is to ensure that the back end has had //time to make this update visible to other transactions which //should see it. This is only necessary for Access, which only does //this every few seconds. if (m_fOnAccess) Sleep(SLEEP_TIME); //Takes milliseconds as param //Since we are sharing one session for the commit of two updates, and that //commit only assumes there is one change per commit, increment //our count for the second update on the same table //g_ulLastActualDelete++; //g_ulLastActualInsert++; g_DeleteAndInsertIncrement(); //Record all the txn related things that are now true about //rowset 2 because it shares a session with rowset 1 m_pChgRowset2->CopyTxnInfo(m_pChgRowset1); //Cleanup rowsets and start with new ones, in case the first ones //were zombied m_pChgRowset1->ReleaseRowsetObject(); m_pChgRowset2->ReleaseRowsetObject(); hr=m_pChgRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } hr=m_pChgRowset2->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //Should be able to see the changes if (!COMPARE(FindChange(m_pChgRowset1), TRUE)) goto CLEANUP; if (!COMPARE(FindChange(m_pChgRowset2), TRUE)) goto CLEANUP; fResults = TRUE; CLEANUP: SAFE_RELEASE(pIUnknown); //Give m_pChgRowset2 back the ownership of the table it got //in Init() so that it will clean it up in it's destructor m_pChgRowset2->SetTable(m_pTable, DELETETABLE_YES); ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(5) //*----------------------------------------------------------------------- // @mfunc Multiple Sessions, Transaction on only one // // @rdesc TEST_PASS or TEST_FAIL // int CMultipleTxns::Variation_5() { BOOL fResults = FALSE; IUnknown *pIUnknown = NULL; ULONG cPropSets = 0; DBPROPSET *rgPropSets = NULL; HRESULT hr; //Make first rowset hr=m_pChgRowset1->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } /////////////////////////////////////////////////////// //Create a second command/rowset on a different DB Session /////////////////////////////////////////////////////// //Clean up all objects existing m_pChgRowset2->Terminate(); //Make a new session if (!COMPARE(m_pChgRowset2->Init(), TRUE)) goto CLEANUP; //Set the second different table back into m_pChgRowset2 //since the call to m_pChgRowset2->Terminate wiped it out m_pChgRowset2->SetTable(m_pTable, DELETETABLE_YES); //Start Txn on our second DB Session if (!CHECK(m_pChgRowset2->StartTxn(m_fIsoLevel), S_OK)) goto CLEANUP; hr=m_pChgRowset2->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } /////////////////////////////////////////////////////////////// //Make changes to both rowsets and verify only one gets aborted /////////////////////////////////////////////////////////////// //if provider does not support IRowsetChange if (!m_pChgRowset1->m_fChange) { odtLog << L"IRowsetChange not supported, Variation skipped" << wszNewLine; fResults = TRUE; goto CLEANUP; } if(!COMPARE(Change(m_pChgRowset1), TRUE)) goto CLEANUP; //Release rowset so that weave any locks //on the table for the next execute don't h //m_pChgRowset1->Terminate(); if(!COMPARE(m_pChgRowset2->Change(m_ulCurrentDelete, m_ulCurrentInsert), TRUE)) goto CLEANUP; //Now abort the second session if (!CHECK(m_hr = m_pChgRowset2->Abort(TRUE, TRUE), S_OK)) goto CLEANUP; //Shouldn't be able to see the changes here if (!COMPARE(m_pChgRowset2->FindRow(m_ulCurrentInsert, NULL), FALSE)) goto CLEANUP; //Get us back to a good rowset //if (!COMPARE(m_pChgRowset1->Init(), TRUE)) // goto CLEANUP; //But should be able to see them on non transacted rowset //Note that we don't want to use FindChange() here because //that looks for the last changed row, which would have //been incremented for the second change we did, and //we want the first change we did (the one that actually //wasn't aborted). if (!COMPARE(m_pChgRowset1->FindRow(g_ulLastActualInsert, NULL), TRUE)) goto CLEANUP; fResults = TRUE; CLEANUP: ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_TERMINATE_METHOD //-------------------------------------------------------------------- // @mfunc TestCase Termination Routine // // @rdesc TRUE or FALSE // BOOL CMultipleTxns::Terminate() { ReleaseDBSession(); // {{ TCW_TERM_BASECLASS_CHECK2 return(CTxnImmed::Terminate()); } // }} // }} // }} // {{ TCW_TC_PROTOTYPE(CSequence) //*----------------------------------------------------------------------- //| Test Case: CSequence - Sequence testing for StartTransaction and ITransactionOptions //| Created: 05/17/96 //*----------------------------------------------------------------------- //-------------------------------------------------------------------- // @mfunc TestCase Initialization Routine // // @rdesc TRUE or FALSE // BOOL CSequence::Init() { // {{ TCW_INIT_BASECLASS_CHECK if(CTxn::Init()) // }} { if (CHECK(CreateDBSession(), S_OK)) if (CHECK(GetSessionObject(IID_ITransactionLocal, (IUnknown**)&m_pITxnLocal), S_OK)) return TRUE; } return FALSE; } // {{ TCW_VAR_PROTOTYPE(1) //*----------------------------------------------------------------------- // @mfunc GetOptionsObject before StartTransaction // // @rdesc TEST_PASS or TEST_FAIL // int CSequence::Variation_1() { BOOL fResults = FALSE; if (CHECK(m_pITxnLocal->GetOptionsObject(&m_pITxnOptions), S_OK)) { if (CHECK(m_pITxnOptions->GetOptions(&m_TxnOptions), S_OK)) { COMPARE(m_TxnOptions.ulTimeout, 0); COMPARE(m_TxnOptions.szDescription[0], '\0'); //Now try setting them to something new m_TxnOptions.ulTimeout = TIMEOUT; strcpy((CHAR *)m_TxnOptions.szDescription, szDESCRIPTION); //Set should also work if (CHECK(m_pITxnOptions->SetOptions(&m_TxnOptions), S_OK)) { m_hr = m_pITxnLocal->StartTransaction(m_fIsoLevel, 0, m_pITxnOptions, NULL); //Do Get one more time, this time while txn is active if (CHECK(m_pITxnOptions->GetOptions(&m_TxnOptions), S_OK)) { //Should now have new values COMPARE(m_TxnOptions.ulTimeout, TIMEOUT); COMPARE(strcmp((CHAR *)m_TxnOptions.szDescription, szDESCRIPTION), 0); } //Either of these is acceptable for StartTransaction if (m_hr == S_OK) { //Clean up txn CHECK(m_pITxnLocal->Commit(FALSE, 0, 0), S_OK); fResults = TRUE; } else { //This is OK, too if (m_hr == XACT_E_NOTIMEOUT) { fResults = TRUE; } } } } SAFE_RELEASE(m_pITxnOptions); } if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(2) //*----------------------------------------------------------------------- // @mfunc GetOptionsObject after StartTransaction // // @rdesc TEST_PASS or TEST_FAIL // int CSequence::Variation_2() { BOOL fResults = FALSE; ITransactionObject *pITranObj = NULL; HRESULT hr; if (CHECK(m_pITxnLocal->StartTransaction(m_fIsoLevel,0, NULL, NULL), S_OK)) { //Try to get the txn options interface hr = m_pITxnLocal->GetOptionsObject(&m_pITxnOptions); if (S_OK==hr) { if (CHECK(m_pITxnOptions->GetOptions(&m_TxnOptions), S_OK)) { //Now try setting them to something new m_TxnOptions.ulTimeout = TIMEOUT; strcpy((CHAR *)m_TxnOptions.szDescription, szDESCRIPTION); //Set should also work if (CHECK(m_pITxnOptions->SetOptions(&m_TxnOptions), S_OK)) { //End Txn and start again CHECK(m_pITxnLocal->Commit(FALSE, 0, 0), S_OK); m_hr = m_pITxnLocal->StartTransaction(m_fIsoLevel,0, m_pITxnOptions, NULL); //Either of these is acceptable if (m_hr == S_OK) { //Clean up txn CHECK(m_pITxnLocal->Commit(FALSE, 0, 0), S_OK); fResults = TRUE; } else { //This is OK, too if (m_hr == XACT_E_NOTIMEOUT) { fResults = TRUE; } } //Do Get one more time if (CHECK(m_pITxnOptions->GetOptions(&m_TxnOptions), S_OK)) { //Should now have new values COMPARE(m_TxnOptions.ulTimeout, TIMEOUT); COMPARE(strcmp((CHAR *)m_TxnOptions.szDescription, szDESCRIPTION), 0); } } else { CHECK(m_pITxnLocal->Commit(FALSE, 0, 0), S_OK); } } SAFE_RELEASE(m_pITxnOptions); } else { if (DB_E_NOTSUPPORTED==hr) { //test for IID_ITransactionObject OI here if (!VerifyInterface(m_pIOpenRowset, IID_ITransactionObject,SESSION_INTERFACE, (IUnknown **)&pITranObj)) { SAFE_RELEASE(pITranObj); return TEST_SKIPPED; } //free memory that was incorrectly allocated SAFE_RELEASE(pITranObj); } } } if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(3) //*----------------------------------------------------------------------- // @mfunc SetOptions twice // // @rdesc TEST_PASS or TEST_FAIL // int CSequence::Variation_3() { BOOL fResults = FALSE; ITransactionObject *pITranObj = NULL; HRESULT hr; if (CHECK(m_pITxnLocal->StartTransaction(m_fIsoLevel,0, NULL, NULL), S_OK)) { //Try to get the txn options interface hr = m_pITxnLocal->GetOptionsObject(&m_pITxnOptions); if (S_OK==hr) { //Set options m_TxnOptions.ulTimeout = TIMEOUT; strcpy((CHAR *)m_TxnOptions.szDescription, szDESCRIPTION); if (CHECK(m_pITxnOptions->SetOptions(&m_TxnOptions), S_OK)) { //Now try setting them to something new m_TxnOptions.ulTimeout = 0; m_TxnOptions.szDescription[0] = '\0'; if (CHECK(m_pITxnOptions->SetOptions(&m_TxnOptions), S_OK)) { if (CHECK(m_pITxnOptions->GetOptions(&m_TxnOptions), S_OK)) { //Should now have latest values COMPARE(m_TxnOptions.ulTimeout, 0); COMPARE(strcmp((CHAR *)m_TxnOptions.szDescription, ""), 0); } } //End Txn and start again CHECK(m_pITxnLocal->Commit(FALSE, 0, 0), S_OK); m_hr = m_pITxnLocal->StartTransaction(m_fIsoLevel,0, m_pITxnOptions, NULL); //Either of these is acceptable if (m_hr == S_OK) { //Clean up txn CHECK(m_pITxnLocal->Commit(FALSE, 0, 0), S_OK); fResults = TRUE; } else { //This is OK, too if (m_hr == XACT_E_NOTIMEOUT) { fResults = TRUE; } //Clean up txn CHECK(m_pITxnLocal->Commit(FALSE, 0, 0), S_OK); } } SAFE_RELEASE(m_pITxnOptions); } else { if (DB_E_NOTSUPPORTED==hr) { //test for IID_ITransactionObject OI here if (!VerifyInterface(m_pIOpenRowset, IID_ITransactionObject,SESSION_INTERFACE, (IUnknown **)&pITranObj)) { SAFE_RELEASE(pITranObj); return TEST_SKIPPED; } //free memory that was incorrectly allocated SAFE_RELEASE(pITranObj); } } } if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_TERMINATE_METHOD //-------------------------------------------------------------------- // @mfunc TestCase Termination Routine // // @rdesc TRUE or FALSE // BOOL CSequence::Terminate() { ReleaseDBSession(); SAFE_RELEASE(m_pITxnLocal); // {{ TCW_TERM_BASECLASS_CHECK2 return(CTxn::Terminate()); } // }} // }} // }} // {{ TCW_TC_PROTOTYPE(TCExtendedErrors) //*----------------------------------------------------------------------- //| Test Case: TCExtendedErrors - Extended Errors //| Created: 07/31/96 //*----------------------------------------------------------------------- //-------------------------------------------------------------------- // @mfunc TestCase Initialization Routine // // @rdesc TRUE or FALSE // BOOL TCExtendedErrors::Init() { HRESULT hr = NULL; ITransactionObject *pITranObj = NULL; m_pITxnOptions = NULL; // {{ TCW_INIT_BASECLASS_CHECK if(CTxnImmed::Init()) //}} { //Try to get the txn options interface hr = m_pChgRowset1->m_pITxnLocal->GetOptionsObject(&m_pITxnOptions); if (S_OK==hr) { return TEST_PASS; } else { if (DB_E_NOTSUPPORTED==hr) { //test for IID_ITransactionObject OI here if (!VerifyInterface(m_pIOpenRowset, IID_ITransactionObject,SESSION_INTERFACE, (IUnknown **)&pITranObj)) { SAFE_RELEASE(pITranObj); return TEST_SKIPPED; } //free memory that was incorrectly allocated SAFE_RELEASE(pITranObj); } } } return TEST_FAIL; } // {{ TCW_VAR_PROTOTYPE(1) //*----------------------------------------------------------------------- // @mfunc Valid ITransactionLocal calls with previous error object existing. // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_1() { ULONG ulTxnLvl1 = 0; ULONG ulTxnLvl2 = 0; BOOL fResults = FALSE; ITransactionOptions * pITxnOptions = NULL; XACTTRANSINFO TxnInfo; //For each method of the interface, first create an error object on //the current thread, then try get S_OK from the ITransactionLocal method. //We then check extended errors to verify nothing is set since an //error object shouldn't exist following a successful call. //create an error object m_pExtError->CauseError(); //Try to get the txn options interface if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->GetOptionsObject(&pITxnOptions), S_OK)) //Do extended check following GetOptionsObject fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransactionLocal, m_hr); //create an error object m_pExtError->CauseError(); if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->StartTransaction(m_fIsoLevel, 0, NULL, &ulTxnLvl1), S_OK)) { //Do extended check following StartTransaction fResults &= XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransactionLocal, m_hr); //create an error object m_pExtError->CauseError(); //call GeTransactionInfo if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->GetTransactionInfo(&TxnInfo), S_OK)) //Do extended check following GetTransactionInfo fResults &= XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransactionLocal, m_hr); //This will succeed if nested txns are supported, else return //XACT_E_XTIONEXISTS m_hr = m_pChgRowset1->m_pITxnLocal->StartTransaction(m_fIsoLevel,0, NULL, &ulTxnLvl2); //First level must always be 1 COMPARE(ulTxnLvl1, 1); if (m_hr == S_OK) { //Make sure the nesting worked COMPARE(ulTxnLvl2, 2); } else { //Make sure we got the right return value CHECK(m_hr, XACT_E_XTIONEXISTS); } //create an error object m_pExtError->CauseError(); //End the txn -- this should end both levels, if applicable if(CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->Commit(FALSE, 0, 0), S_OK)) //Do extended check following Commit fResults &= XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransactionLocal, m_hr); } ReleaseAllRowsetsAndTxns(); SAFE_RELEASE(pITxnOptions); if(fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(2) //*----------------------------------------------------------------------- // @mfunc Valid Abort call with previous error object existing. // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_2() { BOOL fResults = FALSE; BOID boidReason = BOID_NULL; //Just so we have some value //For each method of the interface, first create an error object on //the current thread, then try get S_OK from the ITransactionLocal method. //We then check extended errors to verify nothing is set since an //error object shouldn't exist following a successful call. //Start Txn if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) //Check for proper return code for given parameters { //create an error object m_pExtError->CauseError(); if(CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->Abort(&boidReason, FALSE, FALSE), S_OK)) //Do extended check following Abort fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransactionLocal, m_hr); } //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(3) //*----------------------------------------------------------------------- // @mfunc Valid SetOptions call with previous error object existing. // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_3() { XACTOPT TxnOptions; BOOL fResults = FALSE; //For each method of the interface, first create an error object on //the current thread, then try get S_OK from the ITransactionOptions method. //We then check extended errors to verify nothing is set since an //error object shouldn't exist following a successful call. //Set time out to non zero TxnOptions.ulTimeout = MAX_ULONG; TxnOptions.szDescription[0] = '\0'; //create an error object m_pExtError->CauseError(); //Set our time out option if (CHECK(m_hr=m_pITxnOptions->SetOptions(&TxnOptions), S_OK)) { //Do extended check following SetOptions fResults = XCHECK(m_pITxnOptions, IID_ITransactionOptions, m_hr); m_hr = m_pChgRowset1->m_pITxnLocal->StartTransaction(m_fIsoLevel, 0, m_pITxnOptions, NULL); ReleaseAllRowsetsAndTxns(); //Either of these is acceptable if (m_hr == S_OK || m_hr == XACT_E_NOTIMEOUT) fResults &= TRUE; } if(fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(4) //*----------------------------------------------------------------------- // @mfunc Valid GetOptions call with previous error object existing. // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_4() { ITransactionOptions * pITxnOptions = NULL; BOOL fResults = FALSE; XACTOPT TxnOptions; //For each method of the interface, first create an error object on //the current thread, then try get S_OK from the ITransactionOptions method. //We then check extended errors to verify nothing is set since an //error object shouldn't exist following a successful call. if (CHECK(m_pChgRowset1->m_pITxnLocal->GetOptionsObject(&pITxnOptions), S_OK)) { //create an error object m_pExtError->CauseError(); //Make sure we can also get the options with //TxnOptions allocated on stack (so we know //provider isn't trying to free the memory) if (CHECK(m_hr=pITxnOptions->GetOptions(&TxnOptions), S_OK)) //Do extended check following GetOptions fResults = XCHECK(pITxnOptions, IID_ITransactionOptions, m_hr); SAFE_RELEASE(pITxnOptions); } if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(5) //*----------------------------------------------------------------------- // @mfunc XACT_E_NOTSUPPORTED Commit call with previous error object existing // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_5() { BOOL fResults = FALSE; //For each method of the interface, first create an error object on //the current thread, then try get a failure from the ITransactionLocal method. //We then check extended errors to verify the right extended error behavior. //Start Txn if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) { //create an error object m_pExtError->CauseError(); //Check for proper return code for given parameters if( CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->Commit(FALSE, 0, 1), XACT_E_NOTSUPPORTED)) //Do extended check following Commit fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransactionLocal, m_hr); } //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(6) //*----------------------------------------------------------------------- // @mfunc XACT_E_NOISORETAIN StartTransaction call with previous error object existing // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_6() { BOOL fResults = FALSE; //For each method of the interface, first create an error object on //the current thread, then try get a failure from the ITransactionLocal method. //We then check extended errors to verify the right extended error behavior. //create an error object m_pExtError->CauseError(); //Make isoFlags 1, nothing but 0 is valid if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->StartTransaction(m_fIsoLevel, 1, NULL,NULL), XACT_E_NOISORETAIN)) //Do extended check following StartTransaction fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransactionLocal, m_hr); if(fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(7) //*----------------------------------------------------------------------- // @mfunc E_INVALIDARG GetTransactionInfo call with previous error object existing // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_7() { BOOL fResults = FALSE; //For each method of the interface, first create an error object on //the current thread, then try get a failure from the ITransactionLocal method. //We then check extended errors to verify the right extended error behavior. if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) { //create an error object m_pExtError->CauseError(); //Check for invalid arg on pinfo = NULL if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->GetTransactionInfo(NULL), E_INVALIDARG)) //Do extended check following GetTransactionInfo fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransactionLocal, m_hr); } //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(8) //*----------------------------------------------------------------------- // @mfunc E_INVALIDARG GetOptionObject call with previous error object existing // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_8() { BOOL fResults = FALSE; //For each method of the interface, first create an error object on //the current thread, then try get a failure from the ITransactionLocal method. //We then check extended errors to verify the right extended error behavior. //create an error object m_pExtError->CauseError(); if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->GetOptionsObject(NULL), E_INVALIDARG)) //Do extended check following GetOptionsObject fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransactionLocal, m_hr); if(fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(9) //*----------------------------------------------------------------------- // @mfunc XACT_E_NOTRANSACTION Abort call with previous error object existing // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_9() { BOOL fResults = FALSE; //For each method of the interface, first create an error object on //the current thread, then try get a failure from the ITransactionLocal method. //We then check extended errors to verify the right extended error behavior. //create an error object m_pExtError->CauseError(); //Try to abort when we've never called StartTransaction if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->Abort(NULL, FALSE, FALSE), XACT_E_NOTRANSACTION)) //Do extended check following Abort fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransactionLocal, m_hr); if(fResults) return TEST_PASS; else return TEST_FAIL; // CNoTxn } // }} // {{ TCW_VAR_PROTOTYPE(10) //*----------------------------------------------------------------------- // @mfunc E_INVALIDARG GetOptions call with previous error object existing // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_10() { BOOL fResults = FALSE; ITransactionOptions * pITxnOptions = NULL; //For each method of the interface, first create an error object on //the current thread, then try get a failure from the ITransitionOptions method. //We then check extended errors to verify the right extended error behavior. if (CHECK(m_pChgRowset1->m_pITxnLocal->GetOptionsObject(&pITxnOptions), S_OK)) { //create an error object m_pExtError->CauseError(); //Expect invalid arg for NULL if (CHECK(m_hr=pITxnOptions->SetOptions(NULL), E_INVALIDARG)) //Do extended check following SetOptions fResults = XCHECK(pITxnOptions, IID_ITransactionOptions, m_hr); SAFE_RELEASE(pITxnOptions); } if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(11) //*----------------------------------------------------------------------- // @mfunc E_INVALIDARG SetOptions call with previous error object existing // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_11() { ITransactionOptions * pITxnOptions = NULL; BOOL fResults = FALSE; //For each method of the interface, first create an error object on //the current thread, then try get a failure from the ITransactionOptions method. //We then check extended errors to verify the right extended error behavior. if (CHECK(m_pChgRowset1->m_pITxnLocal->GetOptionsObject(&pITxnOptions), S_OK)) { //create an error object m_pExtError->CauseError(); //Make sure we can also get the options if (CHECK(m_hr=pITxnOptions->GetOptions(NULL), E_INVALIDARG)) //Do extended check following GetOptions fResults = XCHECK(pITxnOptions, IID_ITransactionOptions, m_hr); SAFE_RELEASE(pITxnOptions); } if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(12) //*----------------------------------------------------------------------- // @mfunc XACT_E_ISOLATIONLEVEL StartTransaction calls no with previous error object existing // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_12() { BOOL fResults = FALSE; //For each method of the interface, with no error object on //the current thread, try get a failure from the ITransactionLocal method. //We then check extended errors to verify the right extended error behavior. //Make isoLevel invalid -- since these values are mutually exclusive, //or'ing them together will create an invalid value if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->StartTransaction( ISOLATIONLEVEL_CHAOS | ISOLATIONLEVEL_READUNCOMMITTED | ISOLATIONLEVEL_READCOMMITTED | ISOLATIONLEVEL_SERIALIZABLE, 0, NULL, NULL), XACT_E_ISOLATIONLEVEL)) //Do extended check following StartTransaction fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransactionLocal, m_hr); if(fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(13) //*----------------------------------------------------------------------- // @mfunc E_INVALIDARG GetOptionObject calls no with previous error object existing // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_13() { BOOL fResults = FALSE; //For each method of the interface, with no error object on //the current thread, try get a failure from the ITransactionLocal method. //We then check extended errors to verify the right extended error behavior. if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->GetOptionsObject(NULL), E_INVALIDARG)) //Do extended check following GetOptionsObject fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransactionLocal, m_hr); if(fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(14) //*----------------------------------------------------------------------- // @mfunc XACT_E_NOTRANSACTION Abort calls with no previous error object existing // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_14() { BOOL fResults = FALSE; //For each method of the interface, with no error object on //the current thread, try get a failure from the ITransactionLocal method. //We then check extended errors to verify the right extended error behavior. //Try to abort when we've never called StartTransaction // if (CHECK(m_pITxnLocal->Abort(NULL, FALSE, FALSE), XACT_E_NOTRANSACTION)) if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->Abort(NULL, FALSE, FALSE), XACT_E_NOTRANSACTION)) //Do extended check following Abort fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransactionLocal, m_hr); if(fResults) return TEST_PASS; else return TEST_FAIL; //CNoTxn } // }} // {{ TCW_VAR_PROTOTYPE(15) //*----------------------------------------------------------------------- // @mfunc XACT_E_NOTRANSACTION Commit calls with no previous error object existing // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_15() { BOOL fResults = FALSE; //For each method of the interface, with no error object on //the current thread, try get a failure from the IRowsetResynch method. //We then check extended errors to verify the right extended error behavior. //Try to commmit when we've never called StartTransaction //if (CHECK(m_pITxnLocal->Commit(FALSE, 0, 0), XACT_E_NOTRANSACTION)) if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->Commit(FALSE, 0, 0), XACT_E_NOTRANSACTION)) //Do extended check following Commit fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransactionLocal, m_hr); if(fResults) return TEST_PASS; else return TEST_FAIL; // CNoTxn } // }} // {{ TCW_VAR_PROTOTYPE(16) //*----------------------------------------------------------------------- // @mfunc XACT_E_NOTRANSACTION GetTransactionInfo calls with no previous error object existing // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_16() { XACTTRANSINFO TxnInfo; BOOL fResults = FALSE; //For each method of the interface, with no error object on //the current thread, try get a failure from the ITransactionLocal method. //We then check extended errors to verify the right extended error behavior. //Try to GetTransactionInfo when we've never called StartTransaction //if (CHECK(m_hr=m_pITxnLocal->GetTransactionInfo(&TxnInfo), XACT_E_NOTRANSACTION)) if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->GetTransactionInfo(&TxnInfo), XACT_E_NOTRANSACTION)) //Do extended check following GetTransactionInfo fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransactionLocal, m_hr); if(fResults) return TEST_PASS; else return TEST_FAIL; // CNoTxn } // }} // {{ TCW_VAR_PROTOTYPE(17) //*----------------------------------------------------------------------- // @mfunc E_INVALIDARG SetOptions calls with no previous error object existing // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_17() { BOOL fResults = FALSE; ITransactionOptions * pITxnOptions = NULL; //For each method of the interface, with no error object on //the current thread, try get a failure from the ITransactionOptions method. //We then check extended errors to verify the right extended error behavior. if (CHECK(m_pChgRowset1->m_pITxnLocal->GetOptionsObject(&pITxnOptions), S_OK)) { //Expect invalid arg for NULL if (CHECK(m_hr=pITxnOptions->SetOptions(NULL), E_INVALIDARG)) //Do extended check following SetOptions fResults = XCHECK(pITxnOptions, IID_ITransactionOptions, m_hr); SAFE_RELEASE(pITxnOptions); } if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(18) //*----------------------------------------------------------------------- // @mfunc E_INVALIDARG GetOptions calls with no previous error object existing // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_18() { ITransactionOptions * pITxnOptions = NULL; BOOL fResults = FALSE; //For each method of the interface, with no error object on //the current thread, try get a failure from the ITransactionOptions method. //We then check extended errors to verify the right extended error behavior. if (CHECK(m_pChgRowset1->m_pITxnLocal->GetOptionsObject(&pITxnOptions), S_OK)) { //Make sure we can also get the options if (CHECK(m_hr=pITxnOptions->GetOptions(NULL), E_INVALIDARG)) //Do extended check following GetOptions fResults = XCHECK(pITxnOptions, IID_ITransactionOptions, m_hr); SAFE_RELEASE(pITxnOptions); } if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(19) //*----------------------------------------------------------------------- // @mfunc Valid Abort calls with previous error object existing, check error on ITransaction // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_19() { BOOL fResults = FALSE; BOID boidReason = BOID_NULL; //Just so we have some value //For each method of the interface, first create an error object on //the current thread, then try get S_OK from the ITransactionLocal method. //We then check extended errors on ITransaction interface to verify nothing //is set since an error object shouldn't exist following a successful call. //Start Txn if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) //Check for proper return code for given parameters { //create an error object m_pExtError->CauseError(); if(CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->Abort(&boidReason, FALSE, FALSE), S_OK)) //Do extended check on ITransaction following Abort fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransaction, m_hr); } //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(20) //*----------------------------------------------------------------------- // @mfunc Valid TransactionLocal calls with previous error object existing, check error on ITransaction // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_20() { ULONG ulTxnLvl1 = 0; ULONG ulTxnLvl2 = 0; BOOL fResults = FALSE; XACTTRANSINFO TxnInfo; //For each method of the interface, first create an error object on //the current thread, then try get S_OK from the ITransactionLocal method. //We then check extended errors on ITransaction interface to verify nothing //is set since an error object shouldn't exist following a successful call. if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->StartTransaction(m_fIsoLevel, 0, NULL, &ulTxnLvl1), S_OK)) { //create an error object m_pExtError->CauseError(); //call GeTransactionInfo if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->GetTransactionInfo(&TxnInfo), S_OK)) //Do extended check following StartTransaction fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransaction, m_hr); //This will succeed if nested txns are supported, else return //XACT_E_XTIONEXISTS m_hr = m_pChgRowset1->m_pITxnLocal->StartTransaction(m_fIsoLevel, 0, NULL, &ulTxnLvl2); //First level must always be 1 COMPARE(ulTxnLvl1, 1); if (m_hr == S_OK) { //Make sure the nesting worked COMPARE(ulTxnLvl2, 2); } else { //Make sure we got the right return value CHECK(m_hr, XACT_E_XTIONEXISTS); } //create an error object m_pExtError->CauseError(); //End the txn -- this should end both levels, if applicable if(CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->Commit(FALSE, 0, 0), S_OK)) //Do extended check following Commit fResults &= XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransaction, m_hr); } ReleaseAllRowsetsAndTxns(); if(fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(21) //*----------------------------------------------------------------------- // @mfunc XACT_E_NOTSUPPORTED Commit calls with previous error object existing, check error on ITransaction // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_21() { BOOL fResults = FALSE; //For each method of the interface, first create an error object on //the current thread, then try get a failure from the ITransactionLocal method. //We then check extended errors on ITransaction interface to verify the right //extended error behavior. //Start Txn if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) { //create an error object m_pExtError->CauseError(); //Check for proper return code for given parameters if( CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->Commit(FALSE, 0, 1), XACT_E_NOTSUPPORTED)) //Do extended check following Commit fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransaction, m_hr); } //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(22) //*----------------------------------------------------------------------- // @mfunc E_INVALIDARG GetTransactionInfo calls with previous error object existing, check error on ITransaction // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_22() { BOOL fResults = FALSE; //For each method of the interface, first create an error object on //the current thread, then try get a failure from the ITransactionLocal method. //We then check extended errors on ITransaction interface to verify the right //extended error behavior. if (CHECK(m_pChgRowset1->StartTxn(m_fIsoLevel), S_OK)) { //create an error object m_pExtError->CauseError(); //Check for invalid arg on pinfo = NULL if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->GetTransactionInfo(NULL), E_INVALIDARG)) //Do extended check following GetTransactionInfo fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransaction, m_hr); } //Cleanup ReleaseAllRowsetsAndTxns(); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(23) //*----------------------------------------------------------------------- // @mfunc XACT_E_NOTRANSACTION Abort calls with previous error object existing, check error on ITransaction // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_23() { BOOL fResults = FALSE; //For each method of the interface, first create an error object on //the current thread, then try get a failure from the ITransactionLocal method. //We then check extended errors on ITransaction interface to verify the right //extended error behavior. //create an error object m_pExtError->CauseError(); //Try to abort when we've never called StartTransaction if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->Abort(NULL, FALSE, FALSE), XACT_E_NOTRANSACTION)) //Do extended check following Abort fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransaction, m_hr); if(fResults) return TEST_PASS; else return TEST_FAIL; // CNoTxn } // }} // {{ TCW_VAR_PROTOTYPE(24) //*----------------------------------------------------------------------- // @mfunc XACT_E_NOTRANSACTION Abort calls with no previous error object existing, check error on ITransaction // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_24() { BOOL fResults = FALSE; //For each method of the interface, with no error object on //the current thread, try get a failure from the ITransactionLocal method. //We then check extended errors on ITransaction interface to verify the right //extended error behavior. //Try to abort when we've never called StartTransaction if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->Abort(NULL, FALSE, FALSE), XACT_E_NOTRANSACTION)) //Do extended check following Abort fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransaction, m_hr); if(fResults) return TEST_PASS; else return TEST_FAIL; //CNoTxn } // }} // {{ TCW_VAR_PROTOTYPE(25) //*----------------------------------------------------------------------- // @mfunc XACT_E_NOTRANSACTION Commit calls with no previous error object existing, check error on ITransaction // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_25() { BOOL fResults = FALSE; //For each method of the interface, with no error object on //the current thread, try get a failure from the IRowsetResynch method. //We then check extended errors to verify the right extended error behavior. //Try to commmit when we've never called StartTransaction //if (CHECK(m_pITxnLocal->Commit(FALSE, 0, 0), XACT_E_NOTRANSACTION)) if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->Commit(FALSE, 0, 0), XACT_E_NOTRANSACTION)) //Do extended check following Commit fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransaction, m_hr); if(fResults) return TEST_PASS; else return TEST_FAIL; // CNoTxn } // }} // {{ TCW_VAR_PROTOTYPE(26) //*----------------------------------------------------------------------- // @mfunc XACT_E_NOTRANSACTION GetTransactionInfo calls with no previous error object existing, check error on ITransaction // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_26() { XACTTRANSINFO TxnInfo; BOOL fResults = FALSE; //For each method of the interface, with no error object on //the current thread, try get a failure from the ITransactionLocal method. //We then check extended errors on ITransaction interface to verify the //right extended error behavior. //Try to GetTransactionInfo when we've never called StartTransaction //if (CHECK(m_hr=m_pITxnLocal->GetTransactionInfo(&TxnInfo), XACT_E_NOTRANSACTION)) if (CHECK(m_hr=m_pChgRowset1->m_pITxnLocal->GetTransactionInfo(&TxnInfo), XACT_E_NOTRANSACTION)) //Do extended check following GetTransactionInfo fResults = XCHECK(m_pChgRowset1->m_pITxnLocal, IID_ITransaction, m_hr); if(fResults) return TEST_PASS; else return TEST_FAIL; //CNoTxn } // }} // {{ TCW_VAR_PROTOTYPE(27) //*----------------------------------------------------------------------- // @mfunc DB_E_OBJECTOPEN the provider does not support starting a new transaction with existing open rowset objects // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors::Variation_27() { HRESULT hr = S_OK; ULONG ulTxnLvl1 = 0; BOOL fResults = FALSE; hr=m_pChgRowset2->MakeRowset(); if (!(hr==S_OK||hr==DB_S_ERRORSOCCURRED)) { goto CLEANUP; } //Start Txn on our second DB Session hr=m_pChgRowset1->m_pITxnLocal->StartTransaction(m_fIsoLevel,0, NULL, &ulTxnLvl1); if (!(hr==S_OK||hr==DB_E_OBJECTOPEN)) { goto CLEANUP; } fResults = TRUE; CLEANUP: ReleaseAllRowsetsAndTxns(); if(fResults) { return TEST_PASS; } else { return TEST_FAIL; } } // }} // {{ TCW_TERMINATE_METHOD //-------------------------------------------------------------------- // @mfunc TestCase Termination Routine // // @rdesc TRUE or FALSE // BOOL TCExtendedErrors::Terminate() { // Release the Objects SAFE_RELEASE(m_pITxnOptions); //if (m_pExtError) // delete m_pExtError; //m_pExtError = NULL; // {{ TCW_TERM_BASECLASS_CHECK2 return(CTxnImmed::Terminate()); } // }} // }} // }} ////////////////////////////////////////////////////////////////////////////// // NOTE: This testcase is targeted towards testing ODBC Provider // thus comments are assuming that is our provider; If that // is not our provider, we will fail the Init. Firehose mode // refers to the mode which SQL Server uses to implement a read only // forward only cursor. This implementation ties up the TDS stream // to the server, and thus only one hstmt is allowed. ODBC Provider needs // to allocate a new hstmt for every new command, thus we will envoke // firehose mode (by disabling backwards scrolling and setting no other // properties) and then try to get additional commands. ////////////////////////////////////////////////////////////////////////////// // {{ TCW_TC_PROTOTYPE(TCMultipleCommands) //*----------------------------------------------------------------------- //| Test Case: TCMultipleCommands - Tests mutliple commands with and without transactions //| Created: 08/07/96 //*----------------------------------------------------------------------- //-------------------------------------------------------------------- // @mfunc TestCase Initialization Routine // // @rdesc TRUE or FALSE // BOOL TCMultipleCommands::Init() { //Firehose mode only applies to ODBC Provider if (m_pThisTestModule->m_ProviderClsid != CLSID_MSDASQL) { odtLog << L"This test case is ODBC Provider specific, and will not continue.\n"; return TRUE; } // {{ TCW_INIT_BASECLASS_CHECK if(CRowsetObject::Init()) // }} { m_pICommand2 = NULL; m_fFireHoseMode = FALSE; //This will create a table under the covers //Use order by so that if we change any rows in the table, //we still know what the first row in the rowset is CreateRowsetObject(); //this part of the test is kagera specific and ok to run with commands //since privlib now makes a rowset off of IOpenRowset we no longer //have a class member command object after calling CreateRowsetObject //so make one here if (VerifyInterface(m_pIAccessor, IID_IAccessor, COMMAND_INTERFACE, (IUnknown **)&m_pICommand)) //If we don't support commands, fail now as we //intend on stressing multiple commands //Find out if PASS BY REF Accessors are supported if (!m_pICommand) return FALSE; //Record if we are running against the Microsoft SQL Server ODBC Driver LPWSTR wszProviderName = NULL; GetProperty(DBPROP_DBMSNAME, DBPROPSET_DATASOURCEINFO, m_pIDBInitialize, &wszProviderName); if (!wcscmp((LPWSTR)wszProviderName, L"Microsoft SQL Server")) { m_fMSSQLServer = TRUE; m_fMultipleHstmts = FALSE; } else { m_fMSSQLServer = FALSE; m_fMultipleHstmts = TRUE; odtLog << L"Assuming that the ODBC Driver supports multiple Hstmts per connection.\n"; } PROVIDER_FREE(wszProviderName); //Now release the rowset and command, and use the //command which CTable generates automatically //so that only one total command exists on the session ReleaseRowsetObject(); ReleaseCommandObject(); SetCommandObject(m_pTable->m_pICommand); return TRUE; } return FALSE; } //-------------------------------------------------------------------- // @mfunc Enables/Disables Firehose mode by turning scrolling // backwards off or on. // // @rdesc TRUE or FALSE // HRESULT TCMultipleCommands::SetFirehoseMode(ICommand * pICommand, BOOL fOn) { ICommandProperties * pICmdProps = NULL; DBPROPSET DBPropSet; DBPROP DBProp; BOOL fResults = FALSE; HRESULT hr = ResultFromScode(S_OK); DBPropSet.rgProperties = &DBProp; DBPropSet.cProperties = 1; DBPropSet.guidPropertySet = DBPROPSET_ROWSET; DBProp.dwPropertyID = DBPROP_CANSCROLLBACKWARDS; DBProp.dwOptions = DBPROPOPTIONS_REQUIRED; DBProp.colid = DB_NULLID; DBProp.vValue.vt = VT_BOOL; //Set property on or off, depending on requested behavior //Note that backwards scrolling is OPPOSITE of firehose mode if (!fOn) V_BOOL(&(DBProp.vValue)) = VARIANT_TRUE; else V_BOOL(&(DBProp.vValue)) = VARIANT_FALSE; //set props hr = SetRowsetProperties(&DBPropSet, 1); //If everything went OK, we know we have backwards scrolling, //thus we can't be in firehose mode if (SUCCEEDED(hr) && !fOn) m_fFireHoseMode = FALSE; else //We assume we are in read only forward only (firehose) mode m_fFireHoseMode = TRUE; return hr; } //-------------------------------------------------------------------- // @mfunc Verifies you can get data on second command and then // releases this command. // // @rdesc TRUE or FALSE // HRESULT TCMultipleCommands::VerifyAndReleaseCommand(ICommand **ppICommand, BOOL fFireHoseMode) { ICommand *pTempCmd = NULL; IAccessor *pTempAcc = NULL; DB_LORDINAL *pTmprgTC = NULL; //Don't want to give out E_FAIL for any reason beyond that the TDS line is blocked HRESULT hr = ResultFromScode(E_INVALIDARG); //If we don't have a command, something went wrong if (!m_pICommand) { hr = ResultFromScode(E_INVALIDARG); goto DONE; } //Put us in the correct mode. SetFirehoseMode(*ppICommand, fFireHoseMode); //If we wanted non firehose mode but that isn't supported, //Just continue gracefully since its just a lack of support. //We don't care if we failed on setting firehose mode //because that will be the default anyway if setting //CANSCROLLBACKWARDS to false fails. if (!fFireHoseMode &&(fFireHoseMode!= m_fFireHoseMode)) { hr = NOERROR; goto DONE; } //Use our rowset framework for this command, so save what //we've got in there currently, and then replace it pTempCmd = m_pICommand; pTempAcc = m_pIAccessor; pTmprgTC = m_rgTableColOrds; m_pICommand = *ppICommand; m_pIAccessor = NULL; //Create a rowset object on this command hr = CreateRowsetObject(); if (SUCCEEDED(hr)) { hr = GetRows(m_pIAccessor); } //Now get rid of this command/rowset and replace the old ones ReleaseRowsetObject(); m_pICommand = pTempCmd; m_pIAccessor = pTempAcc; m_rgTableColOrds = pTmprgTC; DONE: //Now release the command the user passed us, since //they expect us to clean it up SAFE_RELEASE(*ppICommand); return hr; } //-------------------------------------------------------------------- // @mfunc Starts real firehose mode by getting a row. This function // cleans up after itself and releases the row. // // @rdesc TRUE or FALSE // HRESULT TCMultipleCommands::GetRows(IUnknown * pRowset) { IRowset *pIRowset = NULL; DBCOUNTITEM cRowsObtained = 0; HROW hRow; HROW *phRow = &hRow; HRESULT hr = ResultFromScode(E_FAIL); //Now make sure we can GetNextRows if (VerifyInterface(pRowset, IID_IRowset, ROWSET_INTERFACE, (IUnknown **)&pIRowset)) { hr = pIRowset->GetNextRows(NULL, 0, 1, &cRowsObtained, &phRow); if (SUCCEEDED(hr)) pIRowset->ReleaseRows(cRowsObtained, phRow, NULL, NULL, NULL); SAFE_RELEASE(pIRowset); } else { hr = E_NOINTERFACE; } return hr; } // {{ TCW_VAR_PROTOTYPE(1) //*----------------------------------------------------------------------- // @mfunc First rowset in Firehose mode with no transaction // // @rdesc TEST_PASS or TEST_FAIL // int TCMultipleCommands::Variation_1() { BOOL fResults = FALSE; //Firehose mode only applies to ODBC Provider if (m_pThisTestModule->m_ProviderClsid != CLSID_MSDASQL) { odtLog << L"This test case is ODBC Provider specific, and will not continue.\n"; return TRUE; } //We should be able to get a second command by getting another hstmt //if the rowset isn't in firehose mode, or if need be by allocating //another hdbc since we aren't in a transaction //Try to Set Firehose mode OFF. SetFirehoseMode(m_pICommand, FALSE); if (!CHECK(CreateRowsetObject(), S_OK)) goto CLEANUP; //Another hstmt should work, either on the same hdbc or a new one if (!CHECK(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pICommand2), S_OK)) goto CLEANUP; if (m_fFireHoseMode) { //Initiate tying up the server with command 1 if (FAILED(GetRows(m_pIAccessor))) goto CLEANUP; //Command 1 is in firehose mode, so Command2 should //be able to produce a rowset in non firehose mode if (FAILED(VerifyAndReleaseCommand(&m_pICommand2, FALSE))) goto CLEANUP; } else { //Command 1 is not in firehose mode, so command 2 should //be able to produce a rowset in firehose mode if (FAILED(VerifyAndReleaseCommand(&m_pICommand2, TRUE))) goto CLEANUP; } ReleaseRowsetObject(); //Now try to SetFirehose mode ON. SetFirehoseMode(m_pICommand, TRUE); if (!CHECK(CreateRowsetObject(), S_OK)) goto CLEANUP; //This should still succeed since we can get a new hdbc //if our active hstmts are limited to 1 by the odbc driver if (!CHECK(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pICommand2), S_OK)) goto CLEANUP; if (m_fFireHoseMode) { //Initiate tying up the server with command 1 if (FAILED(GetRows(m_pIAccessor))) goto CLEANUP; //Command 1 is in firehose mode, so Command2 should //be able to produce a rowset in firehose mode by some means if (FAILED(VerifyAndReleaseCommand(&m_pICommand2, TRUE))) goto CLEANUP; } else { //Command 1 is not in firehose mode, so command 2 should //be able to produce a rowset in firehose mode if (FAILED(VerifyAndReleaseCommand(&m_pICommand2, TRUE))) goto CLEANUP; } //Go back to just a command and try again ReleaseRowsetObject(); //We are no longer in active firehose mode, so this should work if (!CHECK(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pICommand2), S_OK)) goto CLEANUP; //Command2 should be able to produce a firehose mode rowset //since no rowset is open on command 1 if (FAILED(VerifyAndReleaseCommand(&m_pICommand2, TRUE))) goto CLEANUP; fResults = TRUE; CLEANUP: //Cleanup for subsequent variations ReleaseRowsetObject(); //Release the Command object SAFE_RELEASE(m_pICommand2) if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(2) //*----------------------------------------------------------------------- // @mfunc First rowset in Firehose mode with a transaction // // @rdesc TEST_PASS or TEST_FAIL // int TCMultipleCommands::Variation_2() { BOOL fResults = FALSE; ITransactionLocal *pITxnLocal = NULL; //Firehose mode only applies to ODBC Provider if (m_pThisTestModule->m_ProviderClsid != CLSID_MSDASQL) { odtLog << L"This test case is ODBC Provider specific, and will not continue.\n"; return TRUE; } if (!VerifyInterface(m_pIOpenRowset, IID_ITransactionLocal,SESSION_INTERFACE, (IUnknown **)&pITxnLocal)) { return TEST_PASS; } //Put us in a txn if (!CHECK(pITxnLocal->StartTransaction(ISOLATIONLEVEL_READCOMMITTED,0, NULL, NULL), S_OK)) { goto CLEANUP; } /* //Set Firehose mode OFF if it is supported SetFirehoseMode(m_pICommand, FALSE); //If setting fire hose mode off worked if (!m_fFireHoseMode) { if (!CHECK(CreateRowsetObject(), S_OK)) goto CLEANUP; //This should not tie up the server since we //shouldn't be in firehose mode if (FAILED(GetRows(m_pIAccessor))) goto CLEANUP; //If multiple hstmts on an hdbc are allowed, this will work. This //will also work on MS SQL Server since they should be clever about //knowing that we aren't in firehose mode, thus the 1 hstmt restriction //isn't necessary if (m_fMultipleHstmts || m_fMSSQLServer) { if (!CHECK(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pICommand2), S_OK)) goto CLEANUP; } //Otherwise we should fail since we can't get more hstmts on the current hdbc //and another hdbc isn't allowed in a txn else { if (!CHECK(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pICommand2), E_FAIL)) goto CLEANUP; } ReleaseRowsetObject(); } //Now SetFirehose mode ON. If this fails, it just means we //are in firehose mode by default since we can't scroll backwards */ SetFirehoseMode(m_pICommand, TRUE); if (!CHECK(CreateRowsetObject(), S_OK)) goto CLEANUP; //Initiate tying up the server with command 1 if (FAILED(GetRows(m_pIAccessor))) goto CLEANUP; //MS SQL Server should not let us to get an hstmt while in active firehose //mode. Also, if the driver does not allow multiple hstmts, this //should fail since we can't get another hdbc while in a txn if (!m_fMultipleHstmts || m_fMSSQLServer) { if (!CHECK(m_pIDBCreateCommand->CreateCommand ( NULL, IID_ICommand, (IUnknown **)&m_pICommand2), E_FAIL )) { goto CLEANUP; } } else { if (!CHECK(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pICommand2), S_OK)) goto CLEANUP; if (FAILED(VerifyAndReleaseCommand(&m_pICommand2, TRUE))) goto CLEANUP; } //Go back to just a command and try again ReleaseRowsetObject(); //If multiple hstmts on an hdbc are allowed, this will work. This //will also work on MS SQL Server since they should be clever about //knowing that we aren't in firehose mode, thus the 1 hstmt restriction //isn't necessary if (m_fMultipleHstmts || m_fMSSQLServer) { if (!CHECK(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pICommand2), S_OK)) goto CLEANUP; //We should be able to get another connection in firehose mode //since the first one is not in firehose mode if (FAILED(VerifyAndReleaseCommand(&m_pICommand2, TRUE))) goto CLEANUP; } //Otherwise we should fail since we can't get more hstmts on the current hdbc //and another hdbc isn't allowed in a txn else { if (!CHECK(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pICommand2), E_FAIL)) goto CLEANUP; } fResults = TRUE; CLEANUP: //Cleanup for subsequent variations ReleaseRowsetObject(); //Release the Command object SAFE_RELEASE(m_pICommand2); pITxnLocal->Abort(NULL, false, false); SAFE_RELEASE(pITxnLocal); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(3) //*----------------------------------------------------------------------- // @mfunc Second rowset in firehose mode with no transaction // // @rdesc TEST_PASS or TEST_FAIL // int TCMultipleCommands::Variation_3() { BOOL fResults = FALSE; //Firehose mode only applies to ODBC Provider if (m_pThisTestModule->m_ProviderClsid != CLSID_MSDASQL) { odtLog << L"This test case is ODBC Provider specific, and will not continue.\n"; return TRUE; } //Set Firehose mode OFF. SetFirehoseMode(m_pICommand, FALSE); //If fire hose mode can't get turned off, we have nothing to test if (!m_fFireHoseMode) { if (!CHECK(CreateRowsetObject(), S_OK)) goto CLEANUP; //This should not tie up the server since we shouldn't be //in firehose mode with command 1 if (FAILED(GetRows(m_pIAccessor))) goto CLEANUP; //This should always work because we can use another hdbc if we //need to since we aren't in a transaction if (!CHECK(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pICommand2), S_OK)) goto CLEANUP; //Firehose mode for the second rowset should be OK because //ODBC Provider should be smart enough to do it on a second hdbc //if it needs to if (FAILED(VerifyAndReleaseCommand(&m_pICommand2, TRUE))) goto CLEANUP; } fResults = TRUE; CLEANUP: //Cleanup for subsequent variations ReleaseRowsetObject(); //Release the Command object SAFE_RELEASE(m_pICommand2) if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(4) //*----------------------------------------------------------------------- // @mfunc Second rowset in firehose mode with a transaction // // @rdesc TEST_PASS or TEST_FAIL // int TCMultipleCommands::Variation_4() { BOOL fResults = FALSE; ITransactionLocal * pITxnLocal = NULL; //Firehose mode only applies to ODBC Provider if (m_pThisTestModule->m_ProviderClsid != CLSID_MSDASQL) { odtLog << L"This test case is ODBC Provider specific, and will not continue.\n"; return TRUE; } if (!VerifyInterface(m_pIDBCreateCommand, IID_ITransactionLocal, SESSION_INTERFACE, (IUnknown **)&pITxnLocal)) return TEST_PASS; //Put us in a txn if (!CHECK(pITxnLocal->StartTransaction(ISOLATIONLEVEL_READCOMMITTED, 0, NULL, NULL), S_OK)) goto CLEANUP; //Set Firehose mode OFF. SetFirehoseMode(m_pICommand, FALSE); //If fire hose mode can't get turned off, we have nothing to test if (!m_fFireHoseMode) { if (!CHECK(CreateRowsetObject(), S_OK)) goto CLEANUP; //This should not tie up the server since we shouldn't be //in firehose mode with command 1 if (FAILED(GetRows(m_pIAccessor))) goto CLEANUP; //If multiple hstmts on an hdbc are allowed, this will work. This //will also work on MS SQL Server since they should be clever about //knowing that we aren't in firehose mode, thus the 1 hstmt restriction //isn't necessary if (m_fMultipleHstmts || m_fMSSQLServer) { if (!CHECK(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pICommand2), S_OK)) goto CLEANUP; //When we ask for a second rowset in firehose mode, this should //succeed, since ODBC Provider should increase the rowset size //to prevent it, even though that would normally //be the default mode in this situation (with no properties set) if (m_fMSSQLServer) { if (FAILED(VerifyAndReleaseCommand(&m_pICommand2, TRUE))) goto CLEANUP; } else { //Firehose mode does not apply for multiple hstmt drivers //so this should succeed if (FAILED(VerifyAndReleaseCommand(&m_pICommand2, TRUE))) goto CLEANUP; } } //Otherwise we should fail since we can't get more hstmts on the current hdbc //and another hdbc isn't allowed in a txn else { if (!CHECK(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pICommand2), E_FAIL)) goto CLEANUP; } } fResults = TRUE; CLEANUP: //Cleanup for subsequent variations ReleaseRowsetObject(); //Release the Command object SAFE_RELEASE(m_pICommand2) SAFE_RELEASE(pITxnLocal); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_TERMINATE_METHOD //-------------------------------------------------------------------- // @mfunc TestCase Termination Routine // // @rdesc TRUE or FALSE // BOOL TCMultipleCommands::Terminate() { ReleaseRowsetObject(); ReleaseCommandObject(1); //Expect the command to have a ref count of 1 after //we've released it since this same command is still //in use by the CTable object in m_pTable // Drop the table if (m_pTable) { m_pTable->DropTable(); delete m_pTable; m_pTable = NULL; } ReleaseDBSession(); ReleaseDataSourceObject(); // {{ TCW_TERM_BASECLASS_CHECK2 return(CRowsetObject::Terminate()); } // }} // }} // }} // {{ TCW_TC_PROTOTYPE(TCExtendedErrors2) //*----------------------------------------------------------------------- //| Test Case: TCExtendedErrors2 - Check extended errors on multipla commands //| Created: 11/26/96 //*----------------------------------------------------------------------- //-------------------------------------------------------------------- // @mfunc TestCase Initialization Routine // // @rdesc TRUE or FALSE // BOOL TCExtendedErrors2::Init() { // {{ TCW_INIT_BASECLASS_CHECK if(TCMultipleCommands::Init()) // }} { // TO DO: Add your own code here return TRUE; } return FALSE; } // {{ TCW_VAR_PROTOTYPE(1) //*----------------------------------------------------------------------- // @mfunc open a txn in firehose mode -- E_FAIL // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors2::Variation_1() { BOOL fResults = FALSE; ITransactionLocal * pITxnLocal = NULL; HRESULT hr; //Firehose mode only applies to ODBC Provider if (m_pThisTestModule->m_ProviderClsid != CLSID_MSDASQL) { odtLog << L"This test case is ODBC Provider specific, and will not continue.\n"; return TRUE; } if (!VerifyInterface(m_pIDBCreateCommand, IID_ITransactionLocal,SESSION_INTERFACE, (IUnknown **)&pITxnLocal)) { return TEST_PASS; } //SetFirehose mode ON. If this fails, it just means we //are in firehose mode by default since we can't scroll backwards SetFirehoseMode(m_pICommand, TRUE); if (m_fFireHoseMode) { if (!CHECK(CreateRowsetObject(), S_OK)) goto CLEANUP; //We can not start a txn under firehose mode if (m_fMSSQLServer) { if (CHECK(hr=pITxnLocal->StartTransaction(ISOLATIONLEVEL_READCOMMITTED, 0, NULL, NULL), E_FAIL)) fResults = XCHECK(pITxnLocal, IID_ITransactionLocal, hr); else { pITxnLocal->Commit(FALSE, 0, 0); goto CLEANUP; } } //I am assuming other driver will not be affected by firehose mode else { if (!CHECK(hr=pITxnLocal->StartTransaction(ISOLATIONLEVEL_READCOMMITTED, 0, NULL, NULL), S_OK)) goto CLEANUP; pITxnLocal->Commit(FALSE, 0, 0); } } //if we cannot set firehose mode, it is fine. fResults = TRUE; CLEANUP: pITxnLocal->Commit(FALSE, 0, 0); ReleaseRowsetObject(); SAFE_RELEASE(pITxnLocal); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(2) //*----------------------------------------------------------------------- // @mfunc second rowset with the first rowset in firehose mode within a txn -- E_FAIL // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors2::Variation_2() { // TO DO: Add your own code here BOOL fResults = FALSE; ITransactionLocal * pITxnLocal = NULL; HRESULT hr; //Firehose mode only applies to ODBC Provider if (m_pThisTestModule->m_ProviderClsid != CLSID_MSDASQL) { odtLog << L"This test case is ODBC Provider specific, and will not continue.\n"; return TRUE; } if (!VerifyInterface(m_pIDBCreateCommand, IID_ITransactionLocal,SESSION_INTERFACE, (IUnknown **)&pITxnLocal)) return TEST_PASS; //Put us in a txn if (!CHECK(pITxnLocal->StartTransaction(ISOLATIONLEVEL_READCOMMITTED,0, NULL, NULL), S_OK)) goto CLEANUP; //Now SetFirehose mode ON. If this fails, it just means we //are in firehose mode by default since we can't scroll backwards SetFirehoseMode(m_pICommand, TRUE); if (m_fFireHoseMode) { if (!CHECK(CreateRowsetObject(), S_OK)) goto CLEANUP; //MS SQL Server should not let us to get an hstmt while in active firehose //mode. Also, if the driver does not allow multiple hstmts, this //should fail since we can't get another hdbc while in a txn if (!m_fMultipleHstmts || m_fMSSQLServer) { if (CHECK(hr=m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pICommand2), E_FAIL)) fResults = XCHECK(m_pIDBCreateCommand, IID_IDBCreateCommand, hr); else goto CLEANUP; } else { if (!CHECK(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pICommand2), S_OK)) goto CLEANUP; else fResults = TRUE; } } //if we cannot set firehose on, it is okay else { fResults = TRUE; } CLEANUP: ReleaseRowsetObject(); //Release the Command object SAFE_RELEASE(m_pICommand2) if (pITxnLocal) { pITxnLocal->Commit(FALSE, 0, 0); SAFE_RELEASE(pITxnLocal); } if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_VAR_PROTOTYPE(3) //*----------------------------------------------------------------------- // @mfunc create two connection with one in firehose, start txn -- XACT_E_CONNECTION_DENIED // // @rdesc TEST_PASS or TEST_FAIL // int TCExtendedErrors2::Variation_3() { BOOL fResults = FALSE; ITransactionLocal *pITxnLocal = NULL; HRESULT hr; //Firehose mode only applies to ODBC Provider if (m_pThisTestModule->m_ProviderClsid != CLSID_MSDASQL) { odtLog << L"This test case is ODBC Provider specific, and will not continue.\n"; return TRUE; } if (!VerifyInterface(m_pIDBCreateCommand, IID_ITransactionLocal,SESSION_INTERFACE, (IUnknown **)&pITxnLocal)) { return TEST_PASS; } //Now SetFirehose mode ON. If this fails, it just means we //are in firehose mode by default since we can't scroll backwards SetFirehoseMode(m_pICommand, TRUE); if (m_fFireHoseMode) { if (!CHECK(CreateRowsetObject(), S_OK)) { goto CLEANUP; } //This should still succeed since we can get a new hdbc if (!CHECK(m_pIDBCreateCommand->CreateCommand(NULL, IID_ICommand, (IUnknown **)&m_pICommand2), S_OK)) { goto CLEANUP; } //Put us in a txn hr=pITxnLocal->StartTransaction(ISOLATIONLEVEL_READCOMMITTED,0, NULL, NULL); if(hr==XACT_E_CONNECTION_DENIED) { fResults = XCHECK(pITxnLocal, IID_ITransactionLocal, hr); } else { if (hr==E_FAIL) { fResults = XCHECK(pITxnLocal, IID_ITransactionLocal, hr); } else { pITxnLocal->Commit(FALSE, 0, 0); } } fResults=TRUE; goto CLEANUP; } //if we cannot set firehose on, it is okay else fResults = TRUE; CLEANUP: ReleaseRowsetObject(); //Release the Command object SAFE_RELEASE(m_pICommand2) SAFE_RELEASE(pITxnLocal); if (fResults) return TEST_PASS; else return TEST_FAIL; } // }} // {{ TCW_TERMINATE_METHOD //-------------------------------------------------------------------- // @mfunc TestCase Termination Routine // // @rdesc TRUE or FALSE // BOOL TCExtendedErrors2::Terminate() { // {{ TCW_INIT_BASECLASS_CHECK if(TCMultipleCommands::Terminate())//Init()) // }} { // TO DO: Add your own code here return TRUE; } return FALSE; } // }} // }} // {{ TCW_TC_PROTOTYPE(TCITransactionObject) //*----------------------------------------------------------------------- //| Test Case: TCITransactionObject //| Created: 08/14/98 //*----------------------------------------------------------------------- //-------------------------------------------------------------------- // @mfunc TestCase Initialization Routine // // @rdesc TRUE or FALSE // BOOL TCITransactionObject::Init() { // {{ TCW_INIT_BASECLASS_CHECK if(CTxnImmed::Init()) // }} { return TEST_PASS; } return TEST_FAIL; } // {{ TCW_VAR_PROTOTYPE(1) //*----------------------------------------------------------------------- // @mfunc Legal - Nested and ITransactionObject // // @rdesc TEST_PASS or TEST_FAIL // int TCITransactionObject::Variation_1() { BOOL fResults = TEST_FAIL; const ULONG ulNestedTnxs = 10; ITransactionObject *pITransactionObject = NULL; ITransaction *rgpITransaction[ulNestedTnxs]; ULONG i = 0; ULONG ulTransactionLevel = 0; for (i=0;im_pITxnLocal, IID_ITransactionObject, SESSION_INTERFACE,(IUnknown **)&pITransactionObject)) { //ITransactionObject not supported so skip this test class fResults = TEST_SKIPPED; goto CLEANUP; } for (i=0;im_pITxnLocal->StartTransaction(m_fIsoLevel,0, NULL, &ulTransactionLevel),S_OK)) { goto CLEANUP; } if (!CHECK(pITransactionObject->GetTransactionObject(ulTransactionLevel,&(rgpITransaction[i])),S_OK)) { goto CLEANUP; } } //commit all the levels //hard coding is ulgy but this shows best what variation is trying to do if (CHECK((rgpITransaction[8])->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), S_OK)){goto CLEANUP;} //since 8 was committed 9 is implicity committed as well if (CHECK((rgpITransaction[9])->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), XACT_E_NOTRANSACTION)){goto CLEANUP;} if (CHECK((rgpITransaction[7])->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), S_OK)){goto CLEANUP;} if (CHECK((rgpITransaction[6])->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), S_OK)){goto CLEANUP;} if (CHECK((rgpITransaction[5])->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), S_OK)){goto CLEANUP;} if (CHECK((rgpITransaction[4])->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), S_OK)){goto CLEANUP;} if (CHECK((rgpITransaction[3])->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), S_OK)){goto CLEANUP;} if (CHECK((rgpITransaction[0])->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), S_OK)){goto CLEANUP;} //since 0 was committed 1 and 2 are implicity committed as well if (CHECK((rgpITransaction[2])->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), S_OK)){goto CLEANUP;} if (CHECK((rgpITransaction[1])->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), S_OK)){goto CLEANUP;} fResults = TEST_PASS; CLEANUP: ReleaseAllRowsetsAndTxns(); PROVIDER_FREE(pITransactionObject); for (i=0;im_pITxnLocal, IID_ITransactionObject, SESSION_INTERFACE,(IUnknown **)&pITransactionObject)) { //ITransactionObject not supported so skip this test class fResults = TEST_SKIPPED; goto CLEANUP; } //ulTransactionLevel can not be zero if (!CHECK(pITransactionObject->GetTransactionObject(0,ppITransaction),E_INVALIDARG)) { goto CLEANUP; } fResults = TEST_PASS; CLEANUP: PROVIDER_FREE(pITransactionObject); if (ppITransaction) { PROVIDER_FREE(*ppITransaction); } ReleaseAllRowsetsAndTxns(); return fResults; } // }} // {{ TCW_VAR_PROTOTYPE(3) //*----------------------------------------------------------------------- // @mfunc Illegal ppITransaction arg GetTransactionObject // // @rdesc TEST_PASS or TEST_FAIL // int TCITransactionObject::Variation_3() { BOOL fResults = TEST_FAIL; ITransactionObject *pITransactionObject = NULL; //try to get an interface to ITransactionObject //this should work if test is past init if (!VerifyInterface(m_pChgRowset1->m_pITxnLocal, IID_ITransactionObject, SESSION_INTERFACE,(IUnknown **)&pITransactionObject)) { //ITransactionObject not supported so skip this test class fResults = TEST_SKIPPED; goto CLEANUP; } //ulTransactionLevel can not be zero if (!CHECK(pITransactionObject->GetTransactionObject(1,NULL),E_INVALIDARG)) { goto CLEANUP; } fResults = TEST_PASS; CLEANUP: PROVIDER_FREE(pITransactionObject); ReleaseAllRowsetsAndTxns(); return fResults; } // }} // {{ TCW_VAR_PROTOTYPE(4) //*----------------------------------------------------------------------- // @mfunc just Nested // // @rdesc TEST_PASS or TEST_FAIL // int TCITransactionObject::Variation_4() { BOOL fResults = TEST_SKIPPED; const ULONG ulNestedTnxs = 100; ULONG i = 0; ULONG j = 0; ULONG k = 0; ULONG ulTransactionLevel[ulNestedTnxs]; //test to see if nested txns are supported, as of now there is no prop to tell whether nested txns are supported //so if the second StartTxns returns XACT_E_XTIONEXISTS then nested txns are not supported if (CHECK(m_pChgRowset1->m_pITxnLocal->StartTransaction(m_fIsoLevel,0, NULL, &ulTransactionLevel[i]),S_OK)) { m_hr = m_pChgRowset1->m_pITxnLocal->StartTransaction(m_fIsoLevel,0, NULL, &ulTransactionLevel[i]); if (XACT_E_XTIONEXISTS==m_hr) { goto CLEANUP; } fResults = TEST_FAIL; if (m_hr!=S_OK) { COMPARE(S_OK,m_hr); goto CLEANUP; } } else { fResults = TEST_FAIL; goto CLEANUP; } //nest x number of txns + 2 from above for (i=0;im_pITxnLocal->StartTransaction(m_fIsoLevel,0, NULL, &ulTransactionLevel[i]); //when XACT_E_XTIONEXISTS is returned we've reached the nesting limit on the provider if (XACT_E_XTIONEXISTS == m_hr) { break; } if (S_OK!=m_hr) { COMPARE(S_OK,m_hr); goto CLEANUP; } //skip the check the first time since there will only the one entry in the list for (j=0;jm_pITxnLocal->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), S_OK)) { COMPARE(S_OK,m_hr); goto CLEANUP; } } if (!CHECK(m_hr = m_pChgRowset1->m_pITxnLocal->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), S_OK)) { COMPARE(S_OK,m_hr); goto CLEANUP; } if (!CHECK(m_hr = m_pChgRowset1->m_pITxnLocal->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), S_OK)) { COMPARE(S_OK,m_hr); goto CLEANUP; } if (!CHECK(m_hr = m_pChgRowset1->m_pITxnLocal->Commit(FALSE, XACTTC_SYNC_PHASETWO, 0), XACT_E_NOTRANSACTION)) { COMPARE(XACT_E_NOTRANSACTION,m_hr); goto CLEANUP; } fResults = TEST_PASS; CLEANUP: ReleaseAllRowsetsAndTxns(); return fResults; } // }} // }} // {{ TCW_TERMINATE_METHOD //-------------------------------------------------------------------- // @mfunc TestCase Termination Routine // // @rdesc TRUE or FALSE // BOOL TCITransactionObject::Terminate() { // TO DO: Add your own code here // {{ TCW_TERM_BASECLASS_CHECK2 return(CTxnImmed::Terminate()); } // }} // }} // }}