7708 lines
208 KiB
C++
7708 lines
208 KiB
C++
//--------------------------------------------------------------------
|
|
// 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 <<L"ITransactionLocal is not supported.\n";
|
|
return TEST_SKIPPED;
|
|
}
|
|
|
|
//Create a table we'll use for the whole test module,
|
|
//store it in pVoid for now
|
|
pThisTestModule->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; i<cPendingRows; i++)
|
|
{
|
|
COMPARE(m_rgPendingStatus[i], DBPENDINGSTATUS_CHANGED);
|
|
}
|
|
|
|
fResults = TRUE;
|
|
}
|
|
}
|
|
|
|
//CLEANUP:
|
|
//Release the row
|
|
if (m_rgPendingRows)
|
|
CHECK(m_pIRowset->ReleaseRows(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<g_ulTotalIsolations; i++) {
|
|
odtLog << g_IsolationList[i].pszDesc;
|
|
|
|
if ((ulSupported & g_IsolationList[i].ulIsolation) != g_IsolationList[i].ulIsolation)
|
|
odtLog << "\t->\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;i<ulNestedTnxs;i++)
|
|
{
|
|
rgpITransaction[i] = 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;
|
|
}
|
|
|
|
for (i=0;i<ulNestedTnxs;i++)
|
|
{
|
|
if (!CHECK(m_hr = m_pChgRowset1->m_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;i<ulNestedTnxs;i++)
|
|
{
|
|
if (rgpITransaction[i])
|
|
{
|
|
SAFE_RELEASE(rgpITransaction[i]);
|
|
PROVIDER_FREE(rgpITransaction[i]);
|
|
}
|
|
}
|
|
PROVIDER_FREE(pITransactionObject);
|
|
return fResults;
|
|
}
|
|
// }}
|
|
|
|
// {{ TCW_VAR_PROTOTYPE(2)
|
|
//*-----------------------------------------------------------------------
|
|
// @mfunc Illegal ulTransactionLevel arg GetTransactionObject
|
|
//
|
|
// @rdesc TEST_PASS or TEST_FAIL
|
|
//
|
|
int TCITransactionObject::Variation_2()
|
|
{
|
|
BOOL fResults = TEST_FAIL;
|
|
ITransactionObject *pITransactionObject = NULL;
|
|
ITransaction **ppITransaction = 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(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;i<ulNestedTnxs;i++)
|
|
{
|
|
m_hr = m_pChgRowset1->m_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;j<i;j++)
|
|
{
|
|
//if this level was already in the array then is was obtained from previous StartTxn call
|
|
//levels must be unique
|
|
if (ulTransactionLevel[i]==ulTransactionLevel[j])
|
|
{
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Now commit all the levels
|
|
for (k=0;k<i;k++)
|
|
{
|
|
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), 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());
|
|
} // }}
|
|
// }}
|
|
// }}
|