//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1994 - 2000. // // File: rowset.cxx // // Contents: OLE DB IRowset implementation for file stores. // Runs entirely in user space at the client machine. // // Classes: CRowset // // History: 07 Nov 94 AlanW Created // 07 May 97 KrishnaN Added Ole-DB error support // //-------------------------------------------------------------------------- #include #pragma hdrstop #include // needed for IServiceProperties #include #include #include #include #include "tabledbg.hxx" const unsigned MAX_ROW_FETCH = 1000; inline DBROWSTATUS ScodeToRowstatus( SCODE sc ) { switch (sc) { case S_OK: return DBROWSTATUS_S_OK; case E_INVALIDARG: case DB_E_BADBOOKMARK: return DBROWSTATUS_E_INVALID; case E_OUTOFMEMORY: return DBROWSTATUS_E_OUTOFMEMORY; default: tbDebugOut(( DEB_ERROR, "ScodeToRowStatus: missing conversion for %x\n", sc )); Win4Assert( FAILED( sc ) ); return DBROWSTATUS_E_INVALID; } } // Rowset object Interfaces that support Ole DB error objects static const IID * apRowsetErrorIFs[] = { &IID_IAccessor, &IID_IChapteredRowset, &IID_IColumnsInfo, &IID_IColumnsRowset, &IID_IConnectionPointContainer, &IID_IConvertType, &IID_IDBAsynchStatus, &IID_IRowset, //&IID_IRowsetAsynch, &IID_IRowsetIdentity, &IID_IRowsetInfo, &IID_IRowsetLocate, &IID_IRowsetQueryStatus, //&IID_IRowsetResynch, &IID_IRowsetScroll, //&IID_IRowsetUpdate, &IID_IRowsetWatchAll, &IID_IRowsetWatchRegion, //&IID_ISupportErrorInfo, &IID_IServiceProperties, }; static const ULONG cRowsetErrorIFs = sizeof(apRowsetErrorIFs)/sizeof(apRowsetErrorIFs[0]); //+--------------------------------------------------------------------------- // // Member: CRowset::CRowset, public // // Synopsis: Creates a locally accessible Table // // Arguments: [pUnkOuter] - Outer unknown // [ppMyUnk] - OUT: filled in with pointer to non-delegated // IUnknown on return // [cols] - A reference to the output column set // [pidmap] - a pid mapper for column IDs and names in cols // [rQuery] - A reference to an instantiated query // [rControllingQuery] - OLE controlling unknown (IQuery) // [fIsCategorized] - TRUE if not the highest-level rowset // [xProps] - Rowset properties, indicates special semantics, // such as sequential cursor, use CI for prop // queries. // [hCursor] - table cursor handle. // [aAccessors] -- Bag of accessors which rowsets need to inherit // // Notes: Ownership of the output column set may be transferred to // the table cursor. // //---------------------------------------------------------------------------- CRowset::CRowset( IUnknown * pUnkOuter, IUnknown ** ppMyUnk, CColumnSet const & cols, CPidMapperWithNames const & pidmap, PQuery & rQuery, IUnknown & rControllingQuery, BOOL fIsCategorized, XPtr & xProps, ULONG hCursor, CAccessorBag & aAccessors, IUnknown * pUnkCreator ) :_rQuery( rQuery ), _xProperties( xProps.Acquire() ), _hCursor( hCursor ), _pRowBufs(0), _pConnectionPointContainer( 0 ), _pRowsetNotification( 0 ), _pAsynchNotification( 0 ), _fForwardOnly( (_xProperties->GetPropertyFlags() & eLocatable) == 0), #pragma warning(disable : 4355) // 'this' in a constructor _ColumnsInfo( cols, pidmap, _DBErrorObj, * ((IUnknown *) (IRowsetScroll *) this), _fForwardOnly ), _aAccessors( (IUnknown *) (IRowset *)this ), _DBErrorObj( * ((IUnknown *) (IRowset *) this), _mutex ), _impIUnknown(rControllingQuery, this), #pragma warning(default : 4355) // 'this' in a constructor _PropInfo(), _fIsCategorized( fIsCategorized ), _fExtendedTypes( (_xProperties->GetPropertyFlags() & eExtendedTypes) != 0 ), _fHoldRows( (_xProperties->GetPropertyFlags() & eHoldRows) != 0 ), _fAsynchronous( (_xProperties->GetPropertyFlags() & eAsynchronous) != 0), _pRelatedRowset( 0 ), _pChapterRowbufs( 0 ) { Win4Assert(_hCursor != 0); if (_hCursor == 0) THROW(CException(E_NOINTERFACE)); if (pUnkOuter) _pControllingUnknown = pUnkOuter; else _pControllingUnknown = (IUnknown * )&_impIUnknown; _DBErrorObj.SetInterfaceArray(cRowsetErrorIFs, apRowsetErrorIFs); ULONG obRowRefcount, obRowWorkId; ULONG obChaptRefcount, obChaptId; _ColumnsInfo.SetColumnBindings( rQuery, _hCursor, obRowRefcount, obRowWorkId, obChaptRefcount, obChaptId ); // // GetBindings for each accessor in bag, and use them to create accessor // in IRowset // // only CAccessors can be used by commands CAccessorBase * pAccBase = (CAccessorBase *)aAccessors.First(); while ( 0 != pAccBase ) { DBCOUNTITEM cBindings; DBBINDING * rgBindings; DBACCESSORFLAGS dwAccessorFlags; SCODE sc = pAccBase->GetBindings( &dwAccessorFlags, &cBindings, &rgBindings); if ( FAILED( sc ) ) THROW( CException( sc ) ); HACCESSOR hAccessor; sc = CreateAccessor(dwAccessorFlags, cBindings, rgBindings, 0, &hAccessor, 0); CoTaskMemFree(rgBindings); //cleanup from GetBindings if (FAILED(sc)) THROW( CException( sc ) ); // // inherited accessors are accessed through same hAccessor as original. // Set parent of newly created accessor so that we can link the 2 copies. // Client never knows the direct HACESSOR for the inherited accessor. // All accessor methods check bag for an accessor with a match on // the parent or the creator. // ((CAccessorBase *)hAccessor)->SetParent(pAccBase); // // Increment inheritor count for parent accessor // pAccBase->IncInheritors(); pAccBase = (CAccessor *)aAccessors.Next(); } _pRowBufs = new CRowBufferSet( _fForwardOnly, obRowRefcount, obRowWorkId, obChaptRefcount, obChaptId ); *ppMyUnk = ((IUnknown *)&_impIUnknown); // can't fail after this or _pRowBufs will leak (*ppMyUnk)->AddRef(); rQuery.AddRef(); if ( 0 != pUnkCreator ) { _xUnkCreator.Set( pUnkCreator ); _xUnkCreator->AddRef(); } } //CRowset //+--------------------------------------------------------------------------- // // Member: CRowset::~CRowset, public // // Synopsis: Destroy the rowset and its component objects // //---------------------------------------------------------------------------- CRowset::~CRowset() { Win4Assert( _impIUnknown._ref == 0 ); Win4Assert( _hCursor != 0 ); delete _pRowBufs; // free cursor will fail if the pipe is broken TRY { if ( !_pRowsetNotification.IsNull() ) _pRowsetNotification->OnRowsetChange( this, DBREASON_ROWSET_RELEASE, DBEVENTPHASE_DIDEVENT, TRUE); _rQuery.FreeCursor( _hCursor ); } CATCH( CException, e ) { } END_CATCH; if ( !_pRowsetNotification.IsNull() ) _pRowsetNotification->StopNotifications(); delete _pConnectionPointContainer; _rQuery.Release(); } //~CRowset //+------------------------------------------------------------------------- // // Member: CRowset::RealQueryInterface, public // // Synopsis: Get a reference to another interface on the cursor // // Notes: ref count is incremented inside QueryInterface // //-------------------------------------------------------------------------- SCODE CRowset::RealQueryInterface( REFIID ifid, void * *ppiuk ) { SCODE sc = S_OK; *ppiuk = 0; // note -- IID_IUnknown covered in QueryInterface if ( IID_IRowset == ifid ) { *ppiuk = (void *) (IRowset *) this; } else if ( IID_IAccessor == ifid ) { *ppiuk = (void *) (IAccessor *) this; } else if ( IID_IRowsetInfo == ifid ) { *ppiuk = (void *) (IRowsetInfo *) this; } else if ( IID_IColumnsInfo == ifid ) { *ppiuk = (void *) (IColumnsInfo *) &_ColumnsInfo; } else if (IID_ISupportErrorInfo == ifid) { *ppiuk = (void *) ((IUnknown *) (ISupportErrorInfo *) &_DBErrorObj); } else if ( IID_IConvertType == ifid ) { *ppiuk = (void *) (IConvertType *) this; } #if 0 // NEWFEATURE - not implemented now. else if ( IID_IColumnsRowset == ifid ) { *ppiuk = (void *) (IColumnsRowset *) &_ColumnsInfo; } #endif // 0 // NEWFEATURE - not implemented now. else if ( IID_IRowsetQueryStatus == ifid ) { *ppiuk = (void *) (IRowsetQueryStatus *) this; } else if ( IID_IServiceProperties == ifid ) { *ppiuk = (void *) (IServiceProperties *) this; } else if ( IID_IConnectionPointContainer == ifid ) { // Watch notifications are only supported over the // bottom-most of a hierarchical rowset (the one with real rows) BOOL fWatchable = ! _pRowBufs->IsChaptered() && (_xProperties->GetPropertyFlags() & eWatchable) != 0; if ( 0 == _pConnectionPointContainer ) { TRY { XPtr xCPC( new CConnectionPointContainer( _fAsynchronous ? 3 : 1, * ((IUnknown *) (IRowsetScroll *) this), _DBErrorObj) ); if (_fAsynchronous) { _pAsynchNotification = new CRowsetAsynchNotification( _rQuery, _hCursor, this, _DBErrorObj, fWatchable ); _pRowsetNotification.Set( _pAsynchNotification ); } else { _pRowsetNotification.Set ( new CRowsetNotification( ) ); } _pRowsetNotification->AddConnectionPoints( xCPC.GetPointer() ); _pConnectionPointContainer = xCPC.Acquire(); } CATCH( CException, e ) { sc = GetOleError( e ); } END_CATCH; } if ( S_OK == sc ) { Win4Assert( 0 != _pConnectionPointContainer ); *ppiuk = (void *) (IConnectionPointContainer *) _pConnectionPointContainer; } } else if (! _fForwardOnly) { if ( IID_IRowsetScroll == ifid ) { *ppiuk = (void *) (IRowsetScroll *) this; } else if ( IID_IRowsetExactScroll == ifid ) { *ppiuk = (void *) (IRowsetExactScroll *) this; } else if ( IID_IRowsetLocate == ifid ) { *ppiuk = (void *) (IRowsetLocate *) this; } else if ( IID_IRowsetIdentity == ifid ) { *ppiuk = (void *) (IRowsetIdentity *) this; } else if ( IID_IChapteredRowset == ifid ) { Win4Assert( (_pChapterRowbufs != 0) == _fIsCategorized ); if (_pChapterRowbufs) { *ppiuk = (void *) (IChapteredRowset *) this; } } else if ( _fAsynchronous ) { if ( IID_IDBAsynchStatus == ifid ) { *ppiuk = (void *) (IDBAsynchStatus *) this; } else if ( IID_IRowsetAsynch == ifid ) { *ppiuk = (void *) (IRowsetAsynch *) this; } else if (IID_IRowsetWatchRegion == ifid) { *ppiuk = (void *) (IRowsetWatchRegion *) this; } else if (IID_IRowsetWatchAll == ifid) { *ppiuk = (void *) (IRowsetWatchAll *) this; } } } if ( 0 == *ppiuk ) { sc = E_NOINTERFACE; } return sc; } //+------------------------------------------------------------------------- // // Member: CRowset::CImpIUnknown::AddRef, public // // Synopsis: Reference the cursor. // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CRowset::CImpIUnknown::AddRef(void) { long ref = InterlockedIncrement( &_ref ); if ( ref > 0 ) _rControllingQuery.AddRef(); return ref ; } //AddRef //+------------------------------------------------------------------------- // // Member: CRowset::CImpIUnknown::Release, public // // Synopsis: De-Reference the cursor. // // Effects: If the ref count goes to 0 then the cursor is deleted. // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CRowset::CImpIUnknown::Release(void) { long ref = InterlockedDecrement( &_ref ); if ( ref >= 0 ) _rControllingQuery.Release(); // may cause a delete of the rowset return ref; } //Release //+--------------------------------------------------------------------------- // // Member: CRowset::GetProperties, public // // Synopsis: Return information about the capabilities of the rowset // // Arguments: [cPropertyIDSets] - number of property ID sets or zero // [rgPropertyIDSets] - array of desired property ID sets or NULL // [pcPropertySets] - number of DBPROPSET structures returned // [prgPropertySets] - array of returned DBPROPSET structures // // // Returns: SCODE // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::GetProperties( const ULONG cPropertyIDSets, const DBPROPIDSET rgPropertyIDSets[], ULONG * pcPropertySets, DBPROPSET ** prgPropertySets) { _DBErrorObj.ClearErrorInfo(); if ( (0 != cPropertyIDSets && 0 == rgPropertyIDSets) || 0 == pcPropertySets || 0 == prgPropertySets ) { if (pcPropertySets) *pcPropertySets = 0; if (prgPropertySets) *prgPropertySets = 0; return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetInfo); } SCODE scResult = S_OK; *pcPropertySets = 0; *prgPropertySets = 0; TRY { // // Update ROWSETQUERYSTATUS property // DWORD dwStatus; _rQuery.GetQueryStatus( _hCursor, dwStatus ); _xProperties->SetValLong( CMRowsetProps::eid_DBPROPSET_MSIDXS_ROWSET_EXT, CMRowsetProps::eid_MSIDXSPROPVAL_ROWSETQUERYSTATUS, dwStatus ); scResult = _xProperties->GetProperties( cPropertyIDSets, rgPropertyIDSets, pcPropertySets, prgPropertySets ); if (FAILED(scResult)) _DBErrorObj.PostHResult(scResult, IID_IRowsetInfo); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetInfo); scResult = GetOleError(e); } END_CATCH; return scResult; } //+--------------------------------------------------------------------------- // // Member: CRowset::RatioFinished, public // // Synopsis: Returns the completion status of the query. // // Arguments: [pulDenominator] - on return, denominator of fraction // [pulNumerator] - on return, numerator of fraction // [pcRows] - on return, number of rows // [pfNewRows] - on return, TRUE if new rows in the table // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::RatioFinished( DBCOUNTITEM * pulDenominator, DBCOUNTITEM * pulNumerator, DBCOUNTITEM * pcRows, BOOL * pfNewRows) /*const*/ { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; if (0 == pulDenominator || 0 == pulNumerator || 0 == pcRows || 0 == pfNewRows) return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetAsynch); TRY { *pcRows = 0; _rQuery.RatioFinished( _hCursor, *pulDenominator, *pulNumerator, *pcRows, *pfNewRows ); #if CIDBG if ( _fForwardOnly ) Win4Assert( *pulDenominator == *pulNumerator ); else Win4Assert( *pulDenominator >= *pulNumerator ); #endif // CIDBG } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetAsynch); scResult = GetOleError(e); } END_CATCH; return scResult; } //+--------------------------------------------------------------------------- // // Member: CRowset::AddRefRows, public // // Synopsis: Increment the ref. count of a set of row handles // // Arguments: [cRows] -- Number of row handles in rghRows // [rghRows] -- Array of HROWs to be ref. counted // [rgRefCounts] -- Remaining reference counts on rows (optional) // [rgRowStatus] -- Status for each row (optional) // // Returns: SCODE, DB_E_BADROWHANDLE if a bad row handle is passed in. // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CRowset::AddRefRows( DBCOUNTITEM cRows, const HROW rghRows [], DBREFCOUNT rgRefCounts[], DBROWSTATUS rgRowStatus[]) { _DBErrorObj.ClearErrorInfo(); if (0 == _pRowBufs) return _DBErrorObj.PostHResult(E_FAIL, IID_IRowset); SCODE scResult = S_OK; TRY { _pRowBufs->AddRefRows(cRows, rghRows, rgRefCounts, rgRowStatus); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowset); scResult = GetOleError(e); } END_CATCH; return scResult; } //+--------------------------------------------------------------------------- // // Member: CRowset::ReleaseRows, public // // Synopsis: Release a set of row handles // // Arguments: [cRows] -- Number of row handles in rghRows // [rghRows] -- Array of HROWs to be released // [rgRowOptions] -- Reserved for future use (optional) // [rgRefCounts] -- Remaining reference counts on rows (optional) // [rgRowStatus] -- Status for each row (optional) // // Returns: SCODE, DB_E_BADROWHANDLE if a bad row handle is passed in. // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CRowset::ReleaseRows( DBCOUNTITEM cRows, const HROW rghRows [], DBROWOPTIONS rgRowOptions[], DBREFCOUNT rgRefCounts[], DBROWSTATUS rgRowStatus[]) { _DBErrorObj.ClearErrorInfo(); if (0 == _pRowBufs) return _DBErrorObj.PostHResult(E_FAIL, IID_IRowset); // if (0 != rgRowOptions) // return E_FAIL; SCODE scResult = S_OK; TRY { BOOL fNotify = FALSE; ULONG * pRefCounts = rgRefCounts; DBROWSTATUS * pRowStatus = rgRowStatus; XArray xrgRefCounts; XArray xrgRowStatus; if ( !_pRowsetNotification.IsNull() && _pRowsetNotification->IsNotifyActive() ) { fNotify = TRUE; if ( 0 == pRefCounts ) { xrgRefCounts.Init( (unsigned) cRows); pRefCounts = xrgRefCounts.GetPointer(); } if ( 0 == pRowStatus ) { xrgRowStatus.Init( (unsigned) cRows); pRowStatus = xrgRowStatus.GetPointer(); } } scResult = _pRowBufs->ReleaseRows(cRows, rghRows, pRefCounts, pRowStatus); if ( fNotify ) { ULONG cRowsToNotify = 0; for (ULONG i=0; i xrghRows(cRowsToNotify); for (cRowsToNotify=0, i=0; iOnRowChange( this, cRowsToNotify, xrghRows.Get(), DBREASON_ROW_RELEASE, DBEVENTPHASE_DIDEVENT, TRUE); } } } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowset); scResult = GetOleError(e); } END_CATCH; return scResult; } //+--------------------------------------------------------------------------- // // Macro: CheckCrowsArgs // // Synopsis: Check common error conditions on cRows and pcRowsObtained // for GetRowsXxxx methods. // // Arguments: [cRows] -- Number of rows to return // [pcRowsObtained] -- On return, number of rows actually // fetched // // Returns: SCODE // // Notes: Needs to be a macro instead of an inline function because // it returns from the calling method. // //-------------------------------------------------------------------------- #define CheckCrowsArgs(cRows, pcRowsObtained) \ if (0 == pcRowsObtained) \ return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowset); \ *pcRowsObtained = 0; \ if (cRows == 0) \ return S_OK; //+--------------------------------------------------------------------------- // // Member: CRowset::GetRowsAt, public // // Synopsis: Fetch data starting at some starting bookmark // // Arguments: [hRegion] -- handle to watch region // [hChapter] -- Chapter in a multiset cursor // [cbBookmark] -- Size of bookmark for starting position // [pBookmark] -- Pointer to bookmark for starting position // [lRowsOffset] -- Number of row handles in rghRows // [cRows] -- Number of rows to return // [pcRowsObtained] -- On return, number of rows actually // fetched // [prghRows] -- Array of HROWs to be returned // // Returns: SCODE, E_INVALIDARG for bad parameters, DB_E_BADBOOKMARK // if the starting bookmark is invalid // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CRowset::GetRowsAt( HWATCHREGION hRegion, HCHAPTER hChapter, DBBKMARK cbBookmark, const BYTE* pBookmark, DBROWOFFSET lRowsOffset, DBROWCOUNT cRows, DBCOUNTITEM * pcRowsObtained, HROW * * prghRows ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; CheckCrowsArgs( cRows, pcRowsObtained ); TRY { CI_TBL_BMK bmk = _MapBookmark(cbBookmark, pBookmark); CI_TBL_CHAPT chapt = _MapChapter(hChapter); CRowSeekAt rowSeek( hRegion, chapt, (LONG) lRowsOffset, bmk ); scResult = _FetchRows(rowSeek, cRows, pcRowsObtained, prghRows); if (FAILED(scResult)) _DBErrorObj.PostHResult(scResult, IID_IRowsetLocate); else if ( !_pRowsetNotification.IsNull() && *pcRowsObtained != 0 && _pRowsetNotification->IsNotifyActive() ) { _pRowsetNotification->OnRowChange( this, *pcRowsObtained, *prghRows, DBREASON_ROW_ACTIVATE, DBEVENTPHASE_DIDEVENT, TRUE); } } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetLocate); scResult = GetOleError(e); } END_CATCH; return scResult; } //+--------------------------------------------------------------------------- // // Member: CRowset::GetRowsByBookmark, public // // Synopsis: Fetch data from a set of bookmarks // // Arguments: [hChapter] -- Chapter in a multiset cursor // [cRows] -- Number of input bookmarks and rows to return // [rgcbBookmark] -- Array of bookmark sizes // [ppBookmarks] -- Array of pointers to bookmarks // [rghRows] -- Array of HROWs returned // [rgRowStatus] -- Array for per-row status (optional) // // Returns: SCODE, E_INVALIDARG for bad parameters, DB_E_BADBOOKMARK // if the starting bookmark is invalid // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CRowset::GetRowsByBookmark( HCHAPTER hChapter, DBCOUNTITEM cRows, const DBBKMARK rgcbBookmark[], const BYTE * ppBookmarks[], HROW rghRows[], DBROWSTATUS rgRowStatus[] ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; if (0 == ppBookmarks || 0 == rgcbBookmark || 0 == rghRows) return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetLocate); if (cRows == 0) return scResult; TRY { // // Map the input bookmarks to work IDs. If we see an invalid // bookmark, it will get turned into widInvalid, and its lookup // will fail. // XArray paBmk( (unsigned) cRows ); for (unsigned i=0; i < cRows; i++) { DBROWSTATUS sc = _MapBookmarkNoThrow( rgcbBookmark[i], ppBookmarks[i], paBmk[i] ); } CI_TBL_CHAPT chapt = _MapChapter(hChapter); CRowSeekByBookmark rowSeek( chapt, (ULONG) cRows, paBmk.Acquire() ); DBCOUNTITEM cRowsObtained; TRY { scResult = _FetchRows(rowSeek, cRows, &cRowsObtained, &rghRows); } CATCH( CException, e ) { scResult = e.GetErrorCode(); } END_CATCH // // Return the array of row statuses // unsigned cErrors = 0; for (i=0; i < rowSeek.GetValidStatuses(); i++) { SCODE scTemp = rowSeek.GetStatus(i); if (0 != rgRowStatus) rgRowStatus[i] = ScodeToRowstatus( scTemp ); if (S_OK != scTemp) { // // The HROW array returned by _FetchRows is compressed, // skipping entries for rows that had errors. Insert // a DB_NULL_HROW entry for this row. // if ( i != cRows-1 ) { memmove( &rghRows[i+1], &rghRows[i], (unsigned) ((cRows-i)-1) * sizeof (HROW)); } rghRows[i] = DB_NULL_HROW; // // If the returned error is DB_E_BADBOOKMARK, // call MapBookmarkNoThrow again to distinguish // E_INVALIDARG cases. // if (DB_E_BADBOOKMARK == scTemp && 0 != rgRowStatus) { CI_TBL_BMK bmkTemp; DBROWSTATUS rsTemp = _MapBookmarkNoThrow(rgcbBookmark[i], ppBookmarks[i], bmkTemp); if (rsTemp != DBROWSTATUS_S_OK) { rgRowStatus[i] = rsTemp; } } cErrors++; } } Win4Assert( rowSeek.GetValidStatuses() == cRows ); if (SUCCEEDED(scResult) && cErrors > 0) scResult = (cErrors == cRows) ? DB_E_ERRORSOCCURRED : DB_S_ERRORSOCCURRED; if (FAILED(scResult)) _DBErrorObj.PostHResult(scResult, IID_IRowsetLocate); else if ( !_pRowsetNotification.IsNull() && _pRowsetNotification->IsNotifyActive() ) _pRowsetNotification->OnRowChange( this, cRows, rghRows, DBREASON_ROW_ACTIVATE, DBEVENTPHASE_DIDEVENT, TRUE); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetLocate); scResult = GetOleError(e); Win4Assert(FAILED(scResult)); } END_CATCH; return scResult; } //+--------------------------------------------------------------------------- // // Member: CRowset::Compare, public // // Synopsis: Compare two bookmarks // // Arguments: [hChapter] -- chapter // [cbBookmark1] -- Size of first bookmark // [pBookmark1] -- Pointer to first bookmark // [cbBookmark2] -- Size of second bookmark // [pBookmark2] -- Pointer to second bookmark // [pdwComparison] - on return, hased value of bookmark // // Returns: SCODE, E_INVALIDARG if cbBookmark is zero or if pBookmark or // pdwComparison is NULL, DB_E_BADBOOKMARK for other invalid // bookmarks. // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CRowset::Compare( HCHAPTER hChapter, DBBKMARK cbBookmark1, const BYTE* pBookmark1, DBBKMARK cbBookmark2, const BYTE* pBookmark2, DBCOMPARE * pdwComparison) /*const*/ { _DBErrorObj.ClearErrorInfo(); SCODE sc = S_OK; if (0 == pdwComparison) return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetLocate); TRY { ULONG dwHash1 = _MapBookmark(cbBookmark1, pBookmark1); ULONG dwHash2 = _MapBookmark(cbBookmark2, pBookmark2); // // Set to non-comparable. This is used later to see if we've // successfully determined the relative order. // *pdwComparison = DBCOMPARE_NOTCOMPARABLE; if (dwHash1 == dwHash2) { *pdwComparison = DBCOMPARE_EQ; } else if ( 1 == cbBookmark1 || 1 == cbBookmark2 ) { *pdwComparison = DBCOMPARE_NE; } else { CI_TBL_CHAPT chapt = _MapChapter(hChapter); _rQuery.Compare( _hCursor, chapt, dwHash1, dwHash2, (DWORD) (*pdwComparison) ); } } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetLocate); sc = GetOleError(e); } END_CATCH; return sc; } //+--------------------------------------------------------------------------- // // Member: CRowset::_MapChapter, private // // Synopsis: Map a chapter mark to a ULONG internal chapter mark. // // Arguments: [hChapter] -- handle of chapter // // Returns: Chapter as an I4 // // Notes: A null chapter on a categorized rowset means to operate // over the entire rowset, not an individual chapter. // //-------------------------------------------------------------------------- CI_TBL_CHAPT CRowset::_MapChapter( HCHAPTER hChapter ) const { CI_TBL_CHAPT chapt = (CI_TBL_CHAPT) hChapter; Win4Assert (DB_NULL_HCHAPTER == 0); if ( !_fIsCategorized && DB_NULL_HCHAPTER != hChapter ) { THROW( CException( DB_E_BADCHAPTER )); } return chapt; } //_MapChapter //+--------------------------------------------------------------------------- // // Member: CRowset::_MapBookmarkNoThrow, private // // Synopsis: Return a 32 bit hash value for a particular bookmark. // Don't throw on errors. // // Arguments: [cbBookmark] -- Size of bookmark // [pBookmark] -- Pointer to bookmark // // Notes: For IRowsetLocate::Hash and IRowsetLocate::GetRowsByBookmark // which want to continue processing on bookmark errors. Unlike // _MapBookmark, DBBMK_FIRST and DBBMK_LAST are invalid. // // Returns: DBROWSTATUS, hash value (identity function, also the workid // value for the table) is returned in rBmk // //-------------------------------------------------------------------------- DBROWSTATUS CRowset::_MapBookmarkNoThrow( DBBKMARK cbBookmark, const BYTE* pBookmark, CI_TBL_BMK & rBmk) const { Win4Assert( !_fForwardOnly ); rBmk = widInvalid; if (0 == cbBookmark || 0 == pBookmark) return DBROWSTATUS_E_INVALID; if (cbBookmark == 1) { if (*(BYTE *)pBookmark == DBBMK_FIRST || *(BYTE *)pBookmark == DBBMK_LAST || *(BYTE *)pBookmark == DBBMK_INVALID) return DBROWSTATUS_E_INVALID; else return DBROWSTATUS_E_INVALID; //DB_E_BADBOOKMARK ??? } else if (cbBookmark == sizeof (CI_TBL_BMK)) { rBmk = *(UNALIGNED CI_TBL_BMK *) pBookmark; if (rBmk == WORKID_TBLFIRST || rBmk == WORKID_TBLLAST) return DBROWSTATUS_E_INVALID; return DBROWSTATUS_S_OK; } return DBROWSTATUS_E_INVALID; //DB_E_BADBOOKMARK ??? } //_MapBookmarkNoThrow //+--------------------------------------------------------------------------- // // Member: CRowset::_MapBookmark, private // // Synopsis: Convert a bookmark into an internal form. // // Arguments: [cbBookmark] -- Size of bookmark // [pBookmark] -- Pointer to bookmark // // Returns: ULONG, hash value (identity function, also the workid // value for the table) // //-------------------------------------------------------------------------- CI_TBL_BMK CRowset::_MapBookmark( DBBKMARK cbBookmark, const BYTE* pBookmark) const { Win4Assert( !_fForwardOnly ); WORKID WorkID = widInvalid; Win4Assert( sizeof WORKID == sizeof CI_TBL_BMK ); if (0 == cbBookmark || 0 == pBookmark) THROW(CException(E_INVALIDARG)); if (cbBookmark == 1) { if (*(BYTE *)pBookmark == DBBMK_FIRST) WorkID = WORKID_TBLFIRST; else if (*(BYTE *)pBookmark == DBBMK_LAST) WorkID = WORKID_TBLLAST; } else if (cbBookmark == sizeof (CI_TBL_BMK)) { WorkID = *(UNALIGNED CI_TBL_BMK *) pBookmark; } if (WorkID == widInvalid) THROW(CException(DB_E_BADBOOKMARK)); return WorkID; } //_MapBookmark //+--------------------------------------------------------------------------- // // Member: CRowset::Hash, public // // Synopsis: Returns an array of 32 bit hash values for bookmarks // // Arguments: [hChapter] -- chapter // [cBookmarks] -- # of bmks to hash // [rgcbBM] -- Sizes of each bookmark // [ppBM] -- Pointers to each bookmark // [rgHashedValues] -- on return, hashed values of bookmarks // [rgBookmarkStatus] -- per-bookmark status (optional) // // Returns: SCODE, E_INVALIDARG if any cbBookmark is zero, // DB_E_BADBOOKMARK for other invalid bookmarks. // //-------------------------------------------------------------------------- STDMETHODIMP CRowset::Hash( HCHAPTER hChapter, DBBKMARK cBookmarks, const DBBKMARK rgcbBM[], const BYTE * ppBM[], DBHASHVALUE rgHashedValues[], DBROWSTATUS rgBookmarkStatus[] ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; ULONG cErrors = 0; if (0 == rgcbBM || 0 == ppBM || 0 == rgHashedValues) return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetLocate); TRY { CI_TBL_CHAPT chapt = _MapChapter(hChapter); for (ULONG i = 0; i < cBookmarks; i++) { CI_TBL_BMK bmk; DBROWSTATUS rs = _MapBookmarkNoThrow( rgcbBM[i], ppBM[i], bmk ); rgHashedValues[i] = bmk; if (rs != DBROWSTATUS_S_OK) { rgHashedValues[i] = 0; cErrors++; } if (0 != rgBookmarkStatus) rgBookmarkStatus[i] = rs; } if (cErrors) scResult = (cErrors == cBookmarks) ? DB_E_ERRORSOCCURRED : DB_S_ERRORSOCCURRED; if (FAILED(scResult)) _DBErrorObj.PostHResult(scResult, IID_IRowsetLocate); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetLocate); scResult = GetOleError(e); } END_CATCH; return scResult; } //Hash //+--------------------------------------------------------------------------- // // Member: CRowset::GetApproximatePosition, public // // Synopsis: Returns the approximate position of a bookmark // // Arguments: [hChapter] -- chapter // [cbBookmark] -- size of bookmark // [pBookmark] -- bookmark // [pulPosition] -- return approx row number of bookmark // [pulRows] -- returns approx # of rows in cursor or // 1 + approx rows if not at quiescence // // Returns: SCODE - the status of the operation. // //-------------------------------------------------------------------------- STDMETHODIMP CRowset::GetApproximatePosition( HCHAPTER hChapter, DBBKMARK cbBookmark, const BYTE * pBookmark, DBCOUNTITEM * pulPosition, DBCOUNTITEM * pulRows) /*const*/ { _DBErrorObj.ClearErrorInfo(); Win4Assert( !_fForwardOnly ); SCODE sc = S_OK; TRY { DBCOUNTITEM ulNumerator, ulDenominator; CI_TBL_BMK bmk = WORKID_TBLFIRST; if (cbBookmark != 0) bmk = _MapBookmark(cbBookmark, pBookmark); CI_TBL_CHAPT chapt = _MapChapter(hChapter); _rQuery.GetApproximatePosition( _hCursor, chapt, bmk, &ulNumerator, &ulDenominator ); if (cbBookmark) *pulPosition = ulNumerator; if (pulRows) *pulRows = ulDenominator; } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetScroll); sc = GetOleError(e); } END_CATCH; return sc; } //+--------------------------------------------------------------------------- // // Member: CRowset::GetExactPosition, public // // Synopsis: Returns the exact position of a bookmark // // Arguments: [hChapter] -- chapter // [cbBookmark] -- size of bookmark // [pBookmark] -- bookmark // [pulPosition] -- return approx row number of bookmark // [pulRows] -- returns approx # of rows in cursor or // 1 + approx rows if not at quiescence // // Returns: SCODE - the status of the operation. // // Notes: We don't distinguish between exact and approximate position. // IRowsetExactScroll is implemented only because ADO 1.5 // started QI'ing for it. // //-------------------------------------------------------------------------- STDMETHODIMP CRowset::GetExactPosition( HCHAPTER hChapter, DBBKMARK cbBookmark, const BYTE * pBookmark, DBCOUNTITEM * pulPosition, DBCOUNTITEM * pulRows) /*const*/ { _DBErrorObj.ClearErrorInfo(); Win4Assert( !_fForwardOnly ); SCODE sc = S_OK; TRY { DBCOUNTITEM ulNumerator, ulDenominator; CI_TBL_BMK bmk = WORKID_TBLFIRST; if (cbBookmark != 0) bmk = _MapBookmark(cbBookmark, pBookmark); CI_TBL_CHAPT chapt = _MapChapter(hChapter); _rQuery.GetApproximatePosition( _hCursor, chapt, bmk, &ulNumerator, &ulDenominator ); if (cbBookmark) *pulPosition = ulNumerator; if (pulRows) *pulRows = ulDenominator; } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetScroll); sc = GetOleError(e); } END_CATCH; return sc; } //+--------------------------------------------------------------------------- // // Member: CRowset::GetRowsAtRatio, public // // Synopsis: Fetch data starting at some ratio in the cursor. // // Arguments: [hRegion] -- handle to watch region // [hChapter] -- Chapter in a multiset cursor // [ulNumerator] -- numerator or ratio fraction // [ulDenominator] -- denominator or ratio fraction // [cRows] -- Number of rows to return // [pcRowsObtained] -- On return, number of rows actually // fetched. // [prghRows] -- Array of HROWs to be released // // Returns: SCODE, E_INVALIDARG for bad parameters, DB_E_BADBOOKMARK // if the starting bookmark is invalid // // Notes: // // History: 14 Dec 1994 Alanw Created // //-------------------------------------------------------------------------- STDMETHODIMP CRowset::GetRowsAtRatio( HWATCHREGION hRegion, HCHAPTER hChapter, DBCOUNTITEM ulNumerator, DBCOUNTITEM ulDenominator, DBROWCOUNT cRows, DBCOUNTITEM * pcRowsObtained, HROW * * prghRows ) { _DBErrorObj.ClearErrorInfo(); CheckCrowsArgs( cRows, pcRowsObtained ); SCODE scResult = S_OK; TRY { CI_TBL_CHAPT chapt = _MapChapter(hChapter); CRowSeekAtRatio rowSeek( hRegion, chapt, (ULONG) ulNumerator, (ULONG) ulDenominator ); scResult = _FetchRows(rowSeek, cRows, pcRowsObtained, prghRows); if (FAILED(scResult)) _DBErrorObj.PostHResult(scResult, IID_IRowsetScroll); else if ( !_pRowsetNotification.IsNull() && *pcRowsObtained != 0 && _pRowsetNotification->IsNotifyActive() ) { _pRowsetNotification->OnRowChange( this, *pcRowsObtained, *prghRows, DBREASON_ROW_ACTIVATE, DBEVENTPHASE_DIDEVENT, TRUE); } } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetScroll); scResult = GetOleError(e); } END_CATCH; return scResult; } #ifdef _WIN64 //+--------------------------------------------------------------------------- // // Member: CRowset::_ConvertOffsetsToPointers, private // // Synopsis: Runs through a row buffer converting offsets to pointers // // Arguments: [pbRows] -- Buffer with row data // [pbBias] -- Bias for offsets to pointers // [cRows] -- Number of rows in the buffer // [pArrayAlloc] -- buffer to hold extra array pointers // // History: 2 Aug 95 dlee created // 1 Sep 99 KLam Reinstated // //---------------------------------------------------------------------------- void CRowset::_ConvertOffsetsToPointers( BYTE * pbRows, BYTE * pbBias, unsigned cRows, CFixedVarAllocator *pArrayAlloc ) { if ( !_fPossibleOffsetConversions ) return; BOOL fAnyOffsets = FALSE; CTableColumnSet const & rCols = _ColumnsInfo.GetColumnBindings(); unsigned cbRowWidth = _ColumnsInfo.GetRowWidth(); for ( unsigned col = 0; col < rCols.Count(); col++ ) { CTableColumn const & rColumn = *rCols.Get( col ); // if this assert isn't true someday, add an if on this condition Win4Assert( rColumn.IsValueStored() ); VARTYPE vt = rColumn.GetStoredType(); if ( ( CTableVariant::IsByRef( vt ) ) && ( VT_CLSID != vt ) ) { fAnyOffsets = TRUE; BYTE *pbRow = pbRows; BYTE *pbData = pbRow + rColumn.GetValueOffset(); for ( unsigned row = 0; row < cRows; row++, pbData += cbRowWidth, pbRow += cbRowWidth ) { // Even stat props can be null if they came from a // summary catalog. tbDebugOut(( DEB_TRACE, "CRowset::_ConvertOffsetsToPointer, Bias: 0x%I64x Row: 0x%I64x Data: 0x%I64x Type: %d New Alloc: 0x%I64x\n", pbBias, pbRow, pbData, vt, pArrayAlloc )); if (! rColumn.IsNull( pbRow ) ) { if ( VT_VARIANT == vt ) { if (! rColumn.IsDeferred( pbRow ) ) ((CTableVariant *) pbData)->FixDataPointers( pbBias, pArrayAlloc ); } else { Win4Assert( 0 == ( vt & VT_VECTOR ) ); * (BYTE **) pbData = pbBias + (* (ULONG *) pbData ); } } } } } _fPossibleOffsetConversions = fAnyOffsets; } //_ConvertOffsetsToPointers #endif // _WIN64 //+--------------------------------------------------------------------------- // // Member: CRowset::_FetchRows, private // // Synopsis: Return handles to rows in the table // // Effects: Rows are read from the table and buffered locally. An // array of handles to the rows is returned. // // Arguments: [rSeekDesc] - seek method and parameters // [cRows] - number of rows desired // [pcRowsReturned] - pointer to where number of rows is returned // [prghRows] - pointer to pointer to where row handles are // returned, or pointer to zero if row handle // array should be allocated // // Returns: error code // // Notes: // //---------------------------------------------------------------------------- SCODE CRowset::_FetchRows( CRowSeekDescription & rSeekDesc, DBROWCOUNT cRows, DBCOUNTITEM * pcRowsReturned, HROW * * prghRows ) { SCODE scResult = S_OK; Win4Assert( 0 != pcRowsReturned ); if (0 == prghRows) { THROW(CException(E_INVALIDARG)); } if (0 == cRows) { *pcRowsReturned = 0; return scResult; } BOOL fFwdFetch = TRUE; if ( cRows < 0 ) { fFwdFetch = FALSE; cRows = -cRows; // cRows now has its absolute value } Win4Assert( cRows > 0 ); BOOL fRowCountTrimmed = FALSE; if (cRows > MAX_ROW_FETCH) { cRows = MAX_ROW_FETCH; fRowCountTrimmed = TRUE; } XArrayOLE ahRows; HROW *pHRows; if ( 0 == *prghRows ) { ahRows.Init( (unsigned) cRows ); pHRows = ahRows.GetPointer(); } else { pHRows = *prghRows; } DBCOUNTITEM cRowsSoFar = 0; // number of rows successfully transferred. CRowSeekDescription * pNextSeek = &rSeekDesc; TRY { XPtr pRowSeekPrevious(0); ULONG cbRowWidth = _ColumnsInfo.GetRowWidth(); do { XPtr xAlloc( new CFixedVarAllocator( FALSE, FALSE, cbRowWidth, 0 )); CGetRowsParams FetchParams( (ULONG) (cRows - cRowsSoFar), fFwdFetch, cbRowWidth, xAlloc.GetReference() ); // Get the row data XPtr xRowSeekOut(0); scResult = _rQuery.GetRows( _hCursor, *pNextSeek, FetchParams, xRowSeekOut ); if (FAILED(scResult)) { #if CIDBG if (E_FAIL == scResult) tbDebugOut((DEB_WARN, "CRowset::_FetchRows - E_FAIL ret'd by GetRows\n")); #endif // CIDBG break; } if ( 0 != FetchParams.RowsTransferred() ) { Win4Assert( !xAlloc->IsBasedMemory() ); XPtr xRowBuf ( new CRowBuffer( _ColumnsInfo.GetColumnBindings(), cbRowWidth, FetchParams.RowsTransferred(), xAlloc )); #ifdef _WIN64 // if this is a Win64 client talking with a Win32 server // then we need to fix the row buffer since we passed in 0 // as the base address. if ( FetchParams.GetReplyBase() != 0 ) { _fPossibleOffsetConversions = TRUE; void *pvRows; CTableColumnSet *pCol; SCODE sc = xRowBuf->Lookup( (unsigned)cRowsSoFar, &pCol, &pvRows, FALSE ); _ConvertOffsetsToPointers ( (BYTE *)pvRows, FetchParams.GetReplyBase(), FetchParams.RowsTransferred(), _pRowBufs->GetArrayAlloc() ); } #endif _pRowBufs->Add( xRowBuf, rSeekDesc.IsByBmkRowSeek(), pHRows + cRowsSoFar ); cRowsSoFar += FetchParams.RowsTransferred(); } else { // One row didn't fit into an fsctl buffer. if (! ( scResult == DB_S_ENDOFROWSET || scResult == DB_S_STOPLIMITREACHED || ( scResult == DB_S_ERRORSOCCURRED && rSeekDesc.IsByBmkRowSeek() ) ) ) { tbDebugOut(( DEB_WARN, "CRowset::_FetchRows, 0 rows, sc 0x%x\n", scResult )); } Win4Assert( scResult == DB_S_ENDOFROWSET || scResult == DB_S_STOPLIMITREACHED || ( scResult == DB_S_ERRORSOCCURRED && rSeekDesc.IsByBmkRowSeek() ) ); } if ( 0 != xRowSeekOut.GetPointer() ) { // // Transfer results from the returned seek description // (for the ByBookmark case), and update for the next // transfer. // rSeekDesc.MergeResults( xRowSeekOut.GetPointer() ); delete pRowSeekPrevious.Acquire(); pRowSeekPrevious.Set( xRowSeekOut.Acquire() ); pNextSeek = pRowSeekPrevious.GetPointer(); } Win4Assert( cRows >= 0 ); } while ( (DBCOUNTITEM) cRows > cRowsSoFar && 0 != pNextSeek && !pNextSeek->IsDone() && (S_OK == scResult || DB_S_BLOCKLIMITEDROWS == scResult ) ); } CATCH( CException, e ) { scResult = e.GetErrorCode(); #if CIDBG if (E_FAIL == scResult) tbDebugOut((DEB_WARN, "CRowset::_FetchRows - E_FAIL from exception\n")); #endif // CIDBG } END_CATCH; if (DB_E_BADSTARTPOSITION == scResult) scResult = DB_S_ENDOFROWSET; if ( fRowCountTrimmed && scResult == S_OK ) scResult = DB_S_ROWLIMITEXCEEDED; if ( FAILED(scResult)) { ReleaseRows(cRowsSoFar, pHRows, 0, 0, 0); *pcRowsReturned = 0; tbDebugOut((DEB_ITRACE, "CRowset::_FetchRows - error %x thrown\n", scResult)); QUIETTHROW( CException( scResult ) ); } else { if ( ( cRowsSoFar > 0 ) && ( 0 == *prghRows ) ) *prghRows = ahRows.Acquire(); *pcRowsReturned = cRowsSoFar; } return( scResult ); } //+--------------------------------------------------------------------------- // // Member: CRowset::CreateAccessor, public // // Synopsis: Makes an accessor that a client can use to get data. // // Arguments: [dwAccessorFlags] -- read/write access requested // [cBindings] -- # of bindings in rgBindings // [rgBindings] -- array of bindings for the accessor to support // [cbRowSize] -- ignored for IRowset // [phAccessor] -- returns created accessor if all is ok // [rgBindStatus] -- array of binding statuses // // Returns: SCODE error code // // History: 14 Dec 94 dlee Created // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::CreateAccessor( DBACCESSORFLAGS dwAccessorFlags, DBCOUNTITEM cBindings, const DBBINDING rgBindings[], DBLENGTH cbRowSize, HACCESSOR * phAccessor, DBBINDSTATUS rgBindStatus[]) { _DBErrorObj.ClearErrorInfo(); SCODE sc = S_OK; if (0 == phAccessor || (0 != cBindings && 0 == rgBindings)) return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IAccessor); // Make sure pointer is good while zeroing in case of a later error *phAccessor = 0; TRY { XPtr Accessor( CreateAnAccessor( dwAccessorFlags, cBindings, rgBindings, rgBindStatus, _fExtendedTypes, (IUnknown *) (IRowset *)this, &_ColumnsInfo ) ); CLock lock( _mutex ); _aAccessors.Add( Accessor.GetPointer() ); *phAccessor = (Accessor.Acquire())->Cast(); } CATCH(CException, e) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IAccessor); sc = GetOleError(e); } END_CATCH; return sc; } //CreateAccessor //+--------------------------------------------------------------------------- // // Member: IsValidFromVariantType // // Synopsis: If DBCONVERTFLAGS_FROMVARIANT is requested, the source type // has to be a valid VARIANT type. // // Arguments: [wTypeIn] -- the source type // // Returns: TRUE -- the type is a valid VARIANT type // FALSE -- otherwise // //---------------------------------------------------------------------------- inline BOOL IsValidFromVariantType( DBTYPE wTypeIn ) { DBTYPE wType = wTypeIn & VT_TYPEMASK; return (! ((wType > VT_DECIMAL && wType < VT_I1) || (wType > VT_LPWSTR && wType < VT_FILETIME && wType != VT_RECORD) || (wType > VT_CLSID)) ); } //+--------------------------------------------------------------------------- // // Member: IsVariableLengthType // // Synopsis: checks to see DBCONVERTFLAGS_ISLONG is appropriate // // Arguments: [wTypeIn] -- the source type // // Returns: TRUE -- the type is variable length // FALSE -- otherwise // //---------------------------------------------------------------------------- inline BOOL IsVariableLengthType( DBTYPE wTypeIn ) { DBTYPE wType = wTypeIn & VT_TYPEMASK; return wType == DBTYPE_STR || wType == DBTYPE_BYTES || wType == DBTYPE_WSTR || wType == DBTYPE_VARNUMERIC; } //+--------------------------------------------------------------------------- // // Member: CRowset::CanConvert, public // // Synopsis: Indicate whether a type conversion is valid. // // Arguments: [wFromType] -- source type // [wToType] -- destination type // [dwConvertFlags] -- read/write access requested // // Returns: S_OK if the conversion is available, S_FALSE otherwise. // E_FAIL, E_INVALIDARG or DB_E_BADCONVERTFLAG on errors. // // History: 20 Nov 96 AlanW Created // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::CanConvert( DBTYPE wFromType, DBTYPE wToType, DBCONVERTFLAGS dwConvertFlags ) { _DBErrorObj.ClearErrorInfo(); SCODE sc = S_OK; TRY { if (((dwConvertFlags & DBCONVERTFLAGS_COLUMN) && (dwConvertFlags & DBCONVERTFLAGS_PARAMETER)) || (dwConvertFlags & ~(DBCONVERTFLAGS_COLUMN | DBCONVERTFLAGS_PARAMETER | DBCONVERTFLAGS_ISFIXEDLENGTH | DBCONVERTFLAGS_ISLONG | DBCONVERTFLAGS_FROMVARIANT))) { sc = DB_E_BADCONVERTFLAG; } else if ( dwConvertFlags & DBCONVERTFLAGS_FROMVARIANT && !IsValidFromVariantType(wFromType) ) { sc = DB_E_BADTYPE; } else { BOOL fOk = CAccessor::CanConvertType( wFromType, wToType, _fExtendedTypes, _xDataConvert ); sc = fOk ? S_OK : S_FALSE; } if (FAILED(sc)) _DBErrorObj.PostHResult(sc, IID_IConvertType); } CATCH(CException, e) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IConvertType); sc = GetOleError(e); } END_CATCH; return sc; } //+--------------------------------------------------------------------------- // // Member: CRowset::GetBindings, public // // Synopsis: Returns an accessor's bindings // // Arguments: [hAccessor] -- accessor being queried // [dwBindIO] -- returns read/write access of accessor // [pcBindings] -- returns # of bindings in rgBindings // [prgBindings] -- returns array of bindings // // Returns: SCODE error code // // History: 14 Dec 94 dlee Created // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::GetBindings( HACCESSOR hAccessor, DBACCESSORFLAGS * pdwBindIO, DBCOUNTITEM * pcBindings, DBBINDING * * prgBindings) /*const*/ { _DBErrorObj.ClearErrorInfo(); SCODE sc = S_OK; if (0 == pdwBindIO || 0 == pcBindings || 0 == prgBindings) { // fill in error values where possible if (pdwBindIO) *pdwBindIO = DBACCESSOR_INVALID; if (pcBindings) *pcBindings = 0; if (prgBindings) *prgBindings = 0; return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IAccessor); } *pdwBindIO = DBACCESSOR_INVALID; *pcBindings = 0; *prgBindings = 0; TRY { CLock lock( _mutex ); CAccessor * pAccessor = (CAccessor *)_aAccessors.Convert( hAccessor ); pAccessor->GetBindings(pdwBindIO, pcBindings, prgBindings); } CATCH(CException, e) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IAccessor); sc = GetOleError(e); } END_CATCH; return sc; } //+--------------------------------------------------------------------------- // // Member: CRowset::AddRefAccessor, public // // Synopsis: Frees an accessor // // Arguments: [hAccessor] -- accessor being freed // [pcRefCount] -- pointer to residual refcount (optional) // // Returns: SCODE error code // // History: 14 Dec 94 dlee Created // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::AddRefAccessor( HACCESSOR hAccessor, ULONG * pcRefCount ) { _DBErrorObj.ClearErrorInfo(); SCODE sc = S_OK; TRY { CLock lock( _mutex ); _aAccessors.AddRef( hAccessor, pcRefCount ); } CATCH(CException, e) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IAccessor); sc = GetOleError(e); } END_CATCH; return sc; } //+--------------------------------------------------------------------------- // // Member: CRowset::ReleaseAccessor, public // // Synopsis: Frees an accessor // // Arguments: [hAccessor] -- accessor being freed // [pcRefCount] -- pointer to residual refcount (optional) // // Returns: SCODE error code // // History: 14 Dec 94 dlee Created // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::ReleaseAccessor( HACCESSOR hAccessor, ULONG * pcRefCount ) { _DBErrorObj.ClearErrorInfo(); SCODE sc = S_OK; TRY { CLock lock( _mutex ); _aAccessors.Release( hAccessor, pcRefCount ); } CATCH(CException, e) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IAccessor); sc = GetOleError(e); } END_CATCH; return sc; } //ReleaseAccessor //+--------------------------------------------------------------------------- // // Member: CRowset::GetData, public // // Synopsis: Returns row data using an accessor // // Arguments: [hRow] -- handle of row whose data is returned // [hAccessor] -- accessor used to retrieve the data // [pData] -- where the data is written // // Returns: SCODE error code // // History: 14 Dec 94 dlee Created // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::GetData( HROW hRow, HACCESSOR hAccessor, void* pData) /*const*/ { _DBErrorObj.ClearErrorInfo(); SCODE sc = S_OK; // NOTE: Null accessors are not supported, so don't need to worry about // special casing for that. if ( 0 == pData ) return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowset); TRY { CLock lock( _mutex ); CAccessor * pAccessor = (CAccessor *)_aAccessors.Convert( hAccessor ); Win4Assert( pAccessor->IsRowDataAccessor() ); pAccessor->GetData(hRow, pData, *_pRowBufs, _rQuery, _ColumnsInfo, _xDataConvert ); } CATCH(CException, e) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowset); sc = GetOleError(e); } END_CATCH; return sc; } //+--------------------------------------------------------------------------- // // Member: CRowset::GetNextRows, public // // Synopsis: Return row data from the table // // Arguments: [hChapter] -- chapter to start at // [cRowsToSkip] -- # of rows to skip // [cRows] -- # of rows to try to return // [pcRowsObtained] -- returns # of rows obtained // [prghRows] -- returns array of rows // // Returns: SCODE error code // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::GetNextRows( HCHAPTER hChapter, DBROWOFFSET cRowsToSkip, DBROWCOUNT cRows, DBCOUNTITEM * pcRowsObtained, HROW * * prghRows ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; BOOL fNotified = FALSE; CheckCrowsArgs( cRows, pcRowsObtained ); if (_fForwardOnly && cRowsToSkip < 0) return _DBErrorObj.PostHResult(DB_E_CANTSCROLLBACKWARDS, IID_IRowset); if (_fForwardOnly && cRows < 0 ) return _DBErrorObj.PostHResult(DB_E_CANTFETCHBACKWARDS, IID_IRowset); TRY { if (! _fHoldRows) _pRowBufs->CheckAllHrowsReleased(); CI_TBL_CHAPT chapt = _MapChapter(hChapter); if ( !_pRowsetNotification.IsNull() && _pRowsetNotification->IsNotifyActive() ) { scResult = _pRowsetNotification->OnRowsetChange( this, DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_OKTODO, FALSE); if ( S_FALSE == scResult ) THROW(CException(DB_E_CANCELED)); fNotified = TRUE; } CRowSeekNext rowSeek( chapt, (LONG) cRowsToSkip ); scResult = _FetchRows(rowSeek, cRows, pcRowsObtained, prghRows); if ( fNotified ) { if (SUCCEEDED(scResult)) { if ( *pcRowsObtained != 0 ) _pRowsetNotification->OnRowChange( this, *pcRowsObtained, *prghRows, DBREASON_ROW_ACTIVATE, DBEVENTPHASE_DIDEVENT, TRUE); _pRowsetNotification->OnRowsetChange( this, DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_DIDEVENT, TRUE); } } if (FAILED(scResult)) _DBErrorObj.PostHResult(scResult, IID_IRowset); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowset); scResult = GetOleError(e); } END_CATCH; if ( fNotified && FAILED(scResult) ) _pRowsetNotification->OnRowsetChange( this, DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_FAILEDTODO, TRUE); Win4Assert( cRowsToSkip != 0 || DB_E_BADSTARTPOSITION != scResult ); return scResult; } // // IRowsetIdentity methods // SCODE CRowset::IsSameRow ( HROW hThisRow, HROW hThatRow ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; TRY { CTableColumnSet *pRowBufferColumns; BYTE *pbSrc; // // Lookup the HROWs to validate them // _pRowBufs->Lookup( hThisRow, &pRowBufferColumns, (void **) &pbSrc ); _pRowBufs->Lookup( hThatRow, &pRowBufferColumns, (void **) &pbSrc ); if (hThisRow != hThatRow) scResult = S_FALSE; } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetIdentity); scResult = GetOleError(e); } END_CATCH; return scResult; } // // IRowsetWatchAll methods // SCODE CRowset::Acknowledge ( ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; TRY { // Just use Refresh to acknowledge the notification. _rQuery.Refresh(); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchAll); scResult = GetOleError(e); } END_CATCH; return scResult; } SCODE CRowset::Start ( ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; TRY { _rQuery.StartWatching(_hCursor); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchAll); scResult = GetOleError(e); } END_CATCH; return scResult; } SCODE CRowset::StopWatching ( ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; TRY { _rQuery.StopWatching(_hCursor); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchAll); scResult = GetOleError(e); } END_CATCH; return scResult; } // // IRowsetWatchRegion methods // SCODE CRowset::ChangeWatchMode ( HWATCHREGION hRegion, DBWATCHMODE mode) { _DBErrorObj.ClearErrorInfo(); if (hRegion == watchRegionInvalid) return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetWatchRegion);; SCODE scResult = S_OK; TRY { _rQuery.SetWatchMode(&hRegion, mode); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchRegion); scResult = GetOleError(e); } END_CATCH; return scResult; } SCODE CRowset::CreateWatchRegion ( DBWATCHMODE mode, HWATCHREGION* phRegion) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; *phRegion = watchRegionInvalid; TRY { _rQuery.SetWatchMode(phRegion, mode); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchRegion); scResult = GetOleError(e); } END_CATCH; return scResult; } SCODE CRowset::DeleteWatchRegion ( HWATCHREGION hRegion) { _DBErrorObj.ClearErrorInfo(); if (hRegion == watchRegionInvalid) return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetWatchRegion); SCODE scResult = S_OK; TRY { _rQuery.ShrinkWatchRegion (hRegion, 0, 0, 0); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchRegion); scResult = GetOleError(e); } END_CATCH; return scResult; } SCODE CRowset::GetWatchRegionInfo ( HWATCHREGION hRegion, DBWATCHMODE * pMode, HCHAPTER * phChapter, DBBKMARK * pcbBookmark, BYTE ** ppBookmark, DBROWCOUNT * pcRows) { _DBErrorObj.ClearErrorInfo(); if (hRegion == watchRegionInvalid) return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetWatchRegion);; SCODE scResult = S_OK; // test pointers and prepare for failure *phChapter = 0; *pcbBookmark = 0; *ppBookmark = 0; *pcRows = 0; TRY { CI_TBL_CHAPT chapter; CI_TBL_BMK bookmark; _rQuery.GetWatchInfo( hRegion, pMode, &chapter, &bookmark, (DBCOUNTITEM *)pcRows); if (chapter != 0) { *phChapter = (HCHAPTER)chapter; } if (bookmark != 0) { XArrayOLE pOutBookmark (1); *pcbBookmark = sizeof CI_TBL_BMK; memcpy ( pOutBookmark.GetPointer(), &bookmark, sizeof CI_TBL_BMK); *ppBookmark = (BYTE*) pOutBookmark.Acquire(); } } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchRegion); scResult = GetOleError(e); } END_CATCH; return scResult; } SCODE CRowset::ShrinkWatchRegion ( HWATCHREGION hRegion, HCHAPTER hChapter, DBBKMARK cbBookmark, BYTE * pBookmark, DBROWCOUNT cRows ) { _DBErrorObj.ClearErrorInfo(); if (hRegion == watchRegionInvalid) return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetWatchRegion);; SCODE scResult = S_OK; TRY { CI_TBL_BMK bookmark = _MapBookmark(cbBookmark, pBookmark); CI_TBL_CHAPT chapter = _MapChapter(hChapter); _rQuery.ShrinkWatchRegion ( hRegion, chapter, bookmark, (LONG) cRows); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchRegion); scResult = GetOleError(e); } END_CATCH; return scResult; } SCODE CRowset::Refresh ( DBCOUNTITEM* pCount, DBROWWATCHCHANGE** ppRowChange ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; TRY { // prepare the buffers // fetch the script and // a bunch of rows. // If not all rows fit in // the buffer, call again. _rQuery.Refresh(); *pCount = 0; scResult = DB_S_TOOMANYCHANGES; } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchRegion); scResult = GetOleError(e); } END_CATCH; return scResult; } //+--------------------------------------------------------------------------- // // Member: CRowset::GetStatus, public // // Synopsis: Return query status // // Arguments: [pdwStatus] -- pointer to where query status is returned // // Returns: SCODE error code // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::GetStatus( DWORD * pdwStatus ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; TRY { *pdwStatus = 0; _rQuery.GetQueryStatus( _hCursor, *pdwStatus ); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetQueryStatus); scResult = GetOleError(e); } END_CATCH; return scResult; } //+--------------------------------------------------------------------------- // // Member: CRowset::GetStatusEx, public // // Synopsis: Return query status // // Arguments: [pdwStatus] - returns query status // [pcFilteredDocuments] - # of documents filtered // [pcDocumentsToFilter] - # of docsuments yet to filter // [pdwRatioFinishedDenominator] - ratio finished denominator // [pdwRatioFinishedNumerator] - ratio finished numerator // [cbBmk] - # of bytes in pBmk // [pBmk] - bookmark for piRowBmk // [piRowBmk] - returns index of bookmark in table // [pcRowsTotal] - current # of rows in table // // Returns: SCODE error code // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::GetStatusEx( DWORD * pdwStatus, DWORD * pcFilteredDocuments, DWORD * pcDocumentsToFilter, DBCOUNTITEM * pdwRatioFinishedDenominator, DBCOUNTITEM * pdwRatioFinishedNumerator, DBBKMARK cbBmk, const BYTE * pBmk, DBCOUNTITEM * piRowBmk, DBCOUNTITEM * pcRowsTotal ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; TRY { *pdwStatus = 0; CI_TBL_BMK bmk = WORKID_TBLFIRST; if ( cbBmk != 0 ) bmk = _MapBookmark( cbBmk, pBmk ); _rQuery.GetQueryStatusEx( _hCursor, *pdwStatus, *pcFilteredDocuments, *pcDocumentsToFilter, *pdwRatioFinishedDenominator, *pdwRatioFinishedNumerator, bmk, *piRowBmk, *pcRowsTotal ); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetQueryStatus); scResult = GetOleError(e); } END_CATCH; return scResult; } //+--------------------------------------------------------------------------- // // Member: CRowset::GetReferencedRowset, public // // Synopsis: Return the related rowset for a hierarchical query. // // Arguments: [iOrdinal] -- ordinal of column for bookmark // [riid] -- IID of desired interface // [ppReferencedRowset] -- interface pointer returned here // // Returns: SCODE error code // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::GetReferencedRowset( DBORDINAL iOrdinal, REFIID riid, IUnknown ** ppReferencedRowset ) /*const*/ { _DBErrorObj.ClearErrorInfo(); if (0 == ppReferencedRowset) return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetInfo); *ppReferencedRowset = 0; if ( iOrdinal > _ColumnsInfo.GetColumnCount() || (0 == _pRelatedRowset && 0 == iOrdinal && _fForwardOnly)) return _DBErrorObj.PostHResult(DB_E_BADORDINAL, IID_IRowsetInfo); if ( 0 == _pRelatedRowset && 0 != iOrdinal ) return _DBErrorObj.PostHResult(DB_E_NOTAREFERENCECOLUMN, IID_IRowsetInfo); // // If it's the bookmark column, it's like a QI on this rowset. // if (0 == iOrdinal) return QueryInterface( riid, (void **)ppReferencedRowset ); // // Make sure the column is a valid chapter column. // const DBCOLUMNINFO & rColInfo = _ColumnsInfo.Get1ColumnInfo( (ULONG) iOrdinal ); if ( (rColInfo.dwFlags & DBCOLUMNFLAGS_ISBOOKMARK) == 0 || (rColInfo.dwFlags & DBCOLUMNFLAGS_ISROWID) != 0 ) return _DBErrorObj.PostHResult(DB_E_NOTAREFERENCECOLUMN, IID_IRowsetInfo); return _pRelatedRowset->QueryInterface( riid, (void **)ppReferencedRowset ); } //+--------------------------------------------------------------------------- // // Member: CRowset::RestartPosition, public // // Synopsis: Set up CRowset so next GetNextRows will restart from beginning // // Arguments: [hChapter] -- chapter which should restart // // Returns: SCODE error code // // Notes: // // History: 16-Apr-97 emilyb wrote // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::RestartPosition( HCHAPTER hChapter ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; BOOL fNotified = FALSE; TRY { if (! _fHoldRows) _pRowBufs->CheckAllHrowsReleased(); CI_TBL_CHAPT chapter = _MapChapter(hChapter); // is chapter valid? if ( !_pRowsetNotification.IsNull() && _pRowsetNotification->IsNotifyActive() ) { scResult = _pRowsetNotification->OnRowsetChange( this, DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_OKTODO, FALSE); if ( S_FALSE == scResult ) THROW(CException(DB_E_CANCELED)); fNotified = TRUE; } _rQuery.RestartPosition(_hCursor, chapter); if ( fNotified ) _pRowsetNotification->OnRowsetChange( this, DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_DIDEVENT, TRUE); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowset); scResult = GetOleError(e); if (E_NOTIMPL == scResult) { scResult = DB_E_CANNOTRESTART; } } END_CATCH; if ( fNotified && FAILED(scResult) ) _pRowsetNotification->OnRowsetChange( this, DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_FAILEDTODO, TRUE); return scResult; } // // IDbAsynchStatus methods // //+--------------------------------------------------------------------------- // // Member: CRowset::Abort, public // // Synopsis: Cancels an asynchronously executing operation. // // Arguments: [hChapter] -- chapter which should restart // [ulOperation] -- operation for which status is being requested // // Returns: SCODE error code // // Notes: // // History: 09 Feb 1998 AlanW Created // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::Abort( HCHAPTER hChapter, ULONG ulOperation ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; if ( DBASYNCHOP_OPEN != ulOperation ) { return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IDBAsynchStatus); } TRANSLATE_EXCEPTIONS; TRY { CI_TBL_CHAPT chapter = _MapChapter(hChapter); // is chapter valid? _rQuery.StopAsynch(_hCursor); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IDBAsynchStatus); scResult = GetOleError(e); if (E_NOTIMPL == scResult) { scResult = DB_E_CANTCANCEL; _DBErrorObj.PostHResult(scResult, IID_IDBAsynchStatus); } } END_CATCH; UNTRANSLATE_EXCEPTIONS; return scResult; } //+--------------------------------------------------------------------------- // // Member: CRowset::GetStatus, public // // Synopsis: Returns the status of an asynchronously executing operation. // // Arguments: [hChapter] -- chapter which should restart // [ulOperation] -- operation for which status is being requested // // Returns: SCODE error code // // Notes: // // History: 09 Feb 1998 AlanW Created // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::GetStatus( HCHAPTER hChapter, DBASYNCHOP ulOperation, DBCOUNTITEM * pulProgress, DBCOUNTITEM * pulProgressMax, DBASYNCHPHASE * pulAsynchPhase, LPOLESTR * ppwszStatusText ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; if ( DBASYNCHOP_OPEN != ulOperation ) { return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IDBAsynchStatus); } TRANSLATE_EXCEPTIONS; TRY { // NOTE: we don't support getting the status of chapters // independently. // CI_TBL_CHAPT chapter = _MapChapter(hChapter); // is chapter valid? if (DB_NULL_HCHAPTER != hChapter) THROW( CException( DB_E_BADCHAPTER )); DBCOUNTITEM ulNumerator = 0; DBCOUNTITEM ulDenominator = 0; DBCOUNTITEM cRows; BOOL fNewRows; _rQuery.RatioFinished( _hCursor, ulDenominator, ulNumerator, cRows, fNewRows ); if (pulProgress) *pulProgress = ulNumerator; if (pulProgressMax) *pulProgressMax = ulDenominator; if (pulAsynchPhase) *pulAsynchPhase = (ulDenominator == ulNumerator) ? DBASYNCHPHASE_COMPLETE : DBASYNCHPHASE_POPULATION; if (ppwszStatusText) *ppwszStatusText = 0; #if CIDBG if ( _fForwardOnly ) Win4Assert( ulDenominator == ulNumerator ); else Win4Assert( ulDenominator >= ulNumerator ); #endif // CIDBG } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IDBAsynchStatus); scResult = GetOleError(e); } END_CATCH; UNTRANSLATE_EXCEPTIONS; return scResult; } //+--------------------------------------------------------------------------- // // Member: CRowset::GetSpecification, public // // Synopsis: Return the session or command object used to create this rowset // // Arguments: [hChapter] -- chapter which should restart // // Notes: // // History: 01-28-98 danleg Created // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::GetSpecification( REFIID riid, IUnknown ** ppvSpecification ) { _DBErrorObj.ClearErrorInfo(); if ( 0 == ppvSpecification ) return _DBErrorObj.PostHResult( E_INVALIDARG, IID_IRowsetInfo ); SCODE sc = S_OK; TRANSLATE_EXCEPTIONS; TRY { *ppvSpecification = 0; if ( _xUnkCreator.IsNull() ) return S_FALSE; sc = _xUnkCreator->QueryInterface( riid, (void ** )ppvSpecification ); if ( FAILED(sc) ) THROW( CException(sc) ); } CATCH( CException, e ) { sc = _DBErrorObj.PostHResult( e, IID_IRowsetInfo ); } END_CATCH; UNTRANSLATE_EXCEPTIONS; return sc; } // // IServiceProperties methods // //+--------------------------------------------------------------------------- // // Method: CRowset::GetPropertyInfo, public // // Synopsis: Get rowset properties // // Arguments: [cPropertySetIDs] - number of desired properties or 0 // [rgPropertySetIDs] - array of desired properties or NULL // [pcPropertySets] - number of property sets returned // [prgPropertySets] - array of returned property sets // [ppwszDesc] - if non-zero, property descriptions are // returneed // // History: 16 Nov 95 AlanW Created // //---------------------------------------------------------------------------- SCODE CRowset::GetPropertyInfo( const ULONG cPropertySetIDs, const DBPROPIDSET rgPropertySetIDs[], ULONG * pcPropertySets, DBPROPINFOSET ** prgPropertySets, WCHAR ** ppwszDesc) { _DBErrorObj.ClearErrorInfo(); if ( (0 != cPropertySetIDs && 0 == rgPropertySetIDs) || 0 == pcPropertySets || 0 == prgPropertySets ) { if (pcPropertySets) *pcPropertySets = 0; if (prgPropertySets) *prgPropertySets = 0; return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IServiceProperties); } SCODE sc = S_OK; *pcPropertySets = 0; *prgPropertySets = 0; if (ppwszDesc) *ppwszDesc = 0; TRANSLATE_EXCEPTIONS; TRY { sc = _PropInfo.GetPropertyInfo( cPropertySetIDs, rgPropertySetIDs, pcPropertySets, prgPropertySets, ppwszDesc ); // Don't PostHResult here -- it's a good chance it's a scope // property that we're expecting to fail. Spare the expense. // The child object will post the error for us. } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IServiceProperties); sc = GetOleError(e); } END_CATCH; UNTRANSLATE_EXCEPTIONS; return sc; } //+--------------------------------------------------------------------------- // // Method: CRowset::SetRequestedProperties, public // // Synopsis: Set rowset properties via IServiceProperties (not supported) // // Arguments: [cPropertySets] - number of property sets // [rgProperties] - array of property sets to be set // // History: 16 Nov 95 AlanW Created // //---------------------------------------------------------------------------- SCODE CRowset::SetRequestedProperties( ULONG cPropertySets, DBPROPSET rgPropertySets[]) { _DBErrorObj.ClearErrorInfo(); if ( 0 != cPropertySets && 0 == rgPropertySets) return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IServiceProperties);; return _DBErrorObj.PostHResult(E_NOTIMPL, IID_IServiceProperties);; } //+--------------------------------------------------------------------------- // // Method: CRowset::SetSuppliedProperties, public // // Synopsis: Set rowset properties via IServiceProperties (not supported) // // Arguments: [cPropertySets] - number of property sets // [rgProperties] - array of property sets to be set // // History: 16 Nov 95 AlanW Created // //---------------------------------------------------------------------------- SCODE CRowset::SetSuppliedProperties( ULONG cPropertySets, DBPROPSET rgPropertySets[]) { _DBErrorObj.ClearErrorInfo(); if ( 0 != cPropertySets && 0 == rgPropertySets) return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IServiceProperties);; return _DBErrorObj.PostHResult(E_NOTIMPL, IID_IServiceProperties);; } //+--------------------------------------------------------------------------- // // Method: CRowset::AddRefChapter, public // // Synopsis: Increment the ref. count to a chapter // // Arguments: [hChapter] - handle to chapter // [pcRefCount] - pointer to chapter ref. count // // Notes: Chapter references are obtained via an accessor from the // parent rowset, but they are addref'ed in the child rowset. // // History: 15 Mar 99 AlanW Created // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::AddRefChapter( HCHAPTER hChapter, ULONG * pcRefCount ) { tbDebugOut(( DEB_TRACE, "CRowset::AddRefChapter called!\n" )); _DBErrorObj.ClearErrorInfo(); if (0 == _pChapterRowbufs) return _DBErrorObj.PostHResult(E_FAIL, IID_IChapteredRowset); if (DB_NULL_HCHAPTER == hChapter) return _DBErrorObj.PostHResult(DB_E_BADCHAPTER, IID_IChapteredRowset); SCODE scResult = S_OK; TRANSLATE_EXCEPTIONS; TRY { _pChapterRowbufs->AddRefChapter(hChapter, pcRefCount); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IChapteredRowset); scResult = GetOleError(e); } END_CATCH; UNTRANSLATE_EXCEPTIONS; return scResult; } //+--------------------------------------------------------------------------- // // Method: CRowset::ReleaseChapter, public // // Synopsis: Decrement the ref. count to a chapter // // Arguments: [hChapter] - handle to chapter // [pcRefCount] - pointer to chapter ref. count // // Notes: Chapter references are obtained via an accessor from the // parent rowset, but they are released in the child rowset. // // History: 15 Mar 99 AlanW Created // //---------------------------------------------------------------------------- STDMETHODIMP CRowset::ReleaseChapter( HCHAPTER hChapter, ULONG * pcRefCount ) { tbDebugOut(( DEB_TRACE, "CRowset::ReleaseChapter called!\n" )); _DBErrorObj.ClearErrorInfo(); if (0 == _pChapterRowbufs) return _DBErrorObj.PostHResult(E_FAIL, IID_IChapteredRowset); if (DB_NULL_HCHAPTER == hChapter) return _DBErrorObj.PostHResult(DB_E_BADCHAPTER, IID_IChapteredRowset); SCODE scResult = S_OK; TRANSLATE_EXCEPTIONS; TRY { _pChapterRowbufs->ReleaseChapter(hChapter, pcRefCount); } CATCH( CException, e ) { _DBErrorObj.PostHResult(e.GetErrorCode(), IID_IChapteredRowset); scResult = GetOleError(e); } END_CATCH; UNTRANSLATE_EXCEPTIONS; return scResult; } STDMETHODIMP CRowset::Stop( ) { SCODE scResult = S_OK; TRY { _rQuery.StopAsynch(_hCursor); } CATCH( CException, e ) { scResult = GetOleError(e); } END_CATCH; return scResult; }