// // Microsoft Windows // Copyright (C) Microsoft Corporation, 1995 - 2000 // // File: ScrlSort.cxx // // Contents: Sorted, fully scrollable, distributed rowset. // // Classes: CScrollableSorted // // History: 05-Jun-95 KyleP Created // 14-JAN-97 KrishnaN Brought it back to conform to 1.0 spec // // Notes: Some of the distributed versions of the Ole DB interfaces simply // call into the regular implementations. In such cases, we'll avoid // posting oledb errors because the underlying call had already done // that. // //---------------------------------------------------------------------------- #include #pragma hdrstop #include "scrlsort.hxx" #include "disacc.hxx" #include "disbmk.hxx" // Rowset object Interfaces that support Ole DB error objects static const IID * apRowsetErrorIFs[] = { &IID_IAccessor, &IID_IColumnsInfo, &IID_IConvertType, &IID_IRowset, &IID_IRowsetInfo, &IID_IDBAsynchStatus, &IID_IRowsetWatchRegion, &IID_IRowsetAsynch, &IID_IRowsetQueryStatus, //&IID_IColumnsRowset, &IID_IConnectionPointContainer, &IID_IRowsetIdentity, &IID_IRowsetLocate, //&IID_IRowsetResynch, &IID_IRowsetScroll, //&IID_IRowsetUpdate, //&IID_ISupportErrorInfo }; static const ULONG cRowsetErrorIFs = sizeof(apRowsetErrorIFs)/sizeof(apRowsetErrorIFs[0]); // // IUnknown methods. // //+------------------------------------------------------------------------- // // Method: CScrollableSorted::RealQueryInterface // // Synopsis: Rebind to other interface // // Arguments: [riid] -- IID of new interface // [ppiuk] -- New interface * returned here // // Returns: S_OK if bind succeeded, E_NOINTERFACE if bind failed // // History: 10-Apr-1995 KyleP Created // // Notes: ref count is incremented inside QueryInterface // //-------------------------------------------------------------------------- SCODE CScrollableSorted::RealQueryInterface( REFIID riid, VOID **ppiuk ) { SCODE sc = S_OK; *ppiuk = 0; // note -- IID_IUnknown covered in QueryInterface if ( riid == IID_IRowset ) { *ppiuk = (void *)((IRowset *)this); } else if (IID_ISupportErrorInfo == riid) { *ppiuk = (void *) ((IUnknown *) (ISupportErrorInfo *) &_DBErrorObj); } else if ( riid == IID_IRowsetLocate ) { *ppiuk = (void *)((IRowsetLocate *)this); } else if ( riid == IID_IRowsetScroll ) { *ppiuk = (void *)((IRowsetScroll *)this); } else if ( riid == IID_IRowsetExactScroll ) { *ppiuk = (void *)((IRowsetExactScroll *)this); } else if ( riid == IID_IColumnsInfo ) { *ppiuk = (void *)((IColumnsInfo *)this); } else if ( riid == IID_IAccessor ) { *ppiuk = (void *)((IAccessor *)this); } else if ( riid == IID_IRowsetIdentity ) { *ppiuk = (void *)((IRowsetIdentity *)this); } else if ( riid == IID_IRowsetInfo ) { *ppiuk = (void *)((IRowsetInfo *)this); } else if ( riid == IID_IRowsetAsynch ) { sc = E_NOINTERFACE; // // Support IRowsetAsynch if any of the child rowsets do. // IRowsetAsynch * pra = 0; for (unsigned iChild=0; iChild < _rowset._cChild; iChild++ ) { sc = Get(iChild)->QueryInterface (IID_IRowsetAsynch, (void**) &pra); if (SUCCEEDED(sc)) { pra->Release(); *ppiuk = (void *)((IRowsetAsynch *)this); break; } } } else if ( riid == IID_IRowsetWatchRegion ) { *ppiuk = (void *) (IRowsetWatchRegion *) this; } else if ( riid == IID_IRowsetWatchAll ) { *ppiuk = (void *) (IRowsetWatchAll *) this; } else if ( riid == IID_IDBAsynchStatus ) { *ppiuk = (void *) (IDBAsynchStatus *) this; } else if ( riid == IID_IConnectionPointContainer ) { sc = _rowset._SetupConnectionPointContainer( this, ppiuk ); } else if ( riid == IID_IRowsetQueryStatus ) { *ppiuk = (void *)((IRowsetQueryStatus *)this); } else { sc = E_NOINTERFACE; } return sc; } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::GetData, public // // Synopsis: Fetch data for a row. // // Arguments: [hRow] -- Handle to row // [hAccessor] -- Accessor to use for fetch. // [pData] -- Data goes here. // // History: 03-Apr-95 KyleP Created. // // Notes: This method is virtually identical to the one in its base // class. The difference is that this class tracks HROWs // for all child cursors, to use as bookmark hints. // // Notes: Need to have Ole DB error handling here because an exception could // happen, resulting in a local error. // //---------------------------------------------------------------------------- SCODE CScrollableSorted::GetData( HROW hRow, HACCESSOR hAccessor, void * pData ) { _DBErrorObj.ClearErrorInfo(); SCODE sc; TRY { unsigned iChild; HROW * ahrow = _rowset._RowManager.GetChildAndHROWs( hRow, iChild ); CDistributedAccessor * pAcc = (CDistributedAccessor *)_rowset._aAccessors.Convert(hAccessor); sc = pAcc->GetData( iChild, ahrow, pData ); } CATCH( CException, e ) { vqDebugOut(( DEB_ERROR, "CScrollableSorted::GetData -- caught 0x%x\n", e.GetErrorCode() )); sc = e.GetErrorCode(); } END_CATCH if (FAILED(sc)) _DBErrorObj.PostHResult(sc, IID_IRowset); return sc; } // // IRowsetLocate methods // //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::Compare, public // // Synopsis: Compare bookmarks. // // Arguments: [hChapter] -- Chapter. // [cbBM1] -- Size of [pBM1]. // [pBM1] -- First bookmark. // [cbBM2] -- Size of [pBM2]. // [pBM2] -- Second bookmark. // [pdwComparison] -- Result of comparison returned here. // // History: 03-Apr-95 KyleP Created. // // Notes: Only equality test supported. // // Notes: Need to have Ole DB error handling here because an exception could // happen, resulting in a local error. // //---------------------------------------------------------------------------- STDMETHODIMP CScrollableSorted::Compare( HCHAPTER hChapter, DBBKMARK cbBM1, const BYTE * pBM1, DBBKMARK cbBM2, const BYTE * pBM2, DBCOMPARE * pdwComparison ) { _DBErrorObj.ClearErrorInfo(); SCODE sc = S_OK; TRY { CDistributedBookmark bmk1( cbBM1, (BYTE const *)pBM1, _rowset._cbBookmark, _rowset._cChild ); CDistributedBookmark bmk2( cbBM2, (BYTE const *)pBM2, _rowset._cbBookmark, _rowset._cChild ); if ( bmk1.Index() != bmk2.Index() ) *pdwComparison = DBCOMPARE_NE; else { sc = Get( bmk1.Index() )->Compare( hChapter, bmk1.GetSize(), bmk1.Get(), bmk2.GetSize(), bmk2.Get(), pdwComparison ); if ( SUCCEEDED(sc) ) { if ( *pdwComparison != DBCOMPARE_EQ && *pdwComparison != DBCOMPARE_NOTCOMPARABLE ) { *pdwComparison = DBCOMPARE_NE; } } } } CATCH( CException, e ) { sc = e.GetErrorCode(); vqDebugOut(( DEB_ERROR, "CScrollableSorted::Compare returned 0x%x\n", sc )); } END_CATCH if (FAILED(sc)) _DBErrorObj.PostHResult(sc, IID_IRowsetLocate); return sc; } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::GetRowsAt, public // // Synopsis: Fetch rows from specified starting location. // // Arguments: [hChapter] -- Chapter. // [cbBookmark] -- Size of [pBookmark] // [pBookmark] -- Bookmark of starting fetch position. // [lRowsOffset] -- Offset from bookmark to start fetch. // [cRows] -- Count of rows requested // [pcRowsObtained] -- Count of rows in [rrghRows] returned here. // [rrghRows] -- HROWs returned here. // // History: 03-Apr-95 KyleP Created. // // Notes: Backwards fetch not supported. // // Notes: Need to have Ole DB error handling here because an exception could // happen, resulting in a local error. // //---------------------------------------------------------------------------- STDMETHODIMP CScrollableSorted::GetRowsAt( HWATCHREGION hRegion, HCHAPTER hChapter, DBBKMARK cbBookmark, const BYTE * pBookmark, DBROWOFFSET lRowsOffset, DBROWCOUNT cRows, DBCOUNTITEM * pcRowsObtained, HROW * rrghRows[]) { _DBErrorObj.ClearErrorInfo(); SCODE sc = S_OK; BOOL fAllocated = FALSE; ULONG cTotalRowsObtained = 0; Win4Assert( 0 == hChapter && "Chapter support not yet implemented" ); if (0 == cRows) // nothing to fetch return S_OK; // Underlying routines are not checking for these errors, so have to if (0 == cbBookmark || 0 == pBookmark || 0 == pcRowsObtained || 0 == rrghRows ) { vqDebugOut((DEB_IERROR, "CScrollableSorted::GetRowsAt: Invalid Argument(s)\n")); return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetLocate); } *pcRowsObtained = 0; TRY { fAllocated = SetupFetch( cRows, rrghRows ); // // Seek to position. Special cases are beginning and end of rowset. // sc = Seek( cbBookmark, pBookmark, lRowsOffset ); if ( SUCCEEDED( sc ) ) { sc = StandardFetch( cRows, pcRowsObtained, *rrghRows ); if ( SUCCEEDED( sc ) && !_rowset._xChildNotify.IsNull() && *pcRowsObtained != 0 ) { _rowset._xChildNotify->OnRowChange( *pcRowsObtained, *rrghRows, DBREASON_ROW_ACTIVATE, DBEVENTPHASE_DIDEVENT, TRUE); } } else if ( DB_E_BADSTARTPOSITION == sc ) { // According to OLE DB 2.0 spec we should return the following sc = DB_S_ENDOFROWSET; } } CATCH( CException, e ) { sc = e.GetErrorCode(); vqDebugOut(( DEB_ERROR, "Exception 0x%x calling IRowset::GetRowsAt\n", sc )); // // If we already have some rows, then we can't 'unfetch' them, so we have // to mask this error. Presumably it won't be transient and we'll get it // again later. // if ( *pcRowsObtained > 0 ) { if ( FAILED(sc) ) sc = DB_S_ROWLIMITEXCEEDED; } else if ( fAllocated ) CoTaskMemFree( *rrghRows ); } END_CATCH if (FAILED(sc)) _DBErrorObj.PostHResult(sc, IID_IRowsetLocate); return( sc ); } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::GetRowsByBookmark, public // // Synopsis: Fetch rows at specified location(s). // // Arguments: [hChapter] -- Chapter. // [cRows] -- Number of input bookmarks. // [rgcbBookmarks] -- Count of element(s) in [ppBookmarks] // [ppBookmarks] -- Bookmarks. // [pcRowsObtained] -- Count of rows in [rrghRows] returned here. // [rghRows] -- HROWs returned here. // [rgRowStatus] -- Row fetch statuses returned here // // History: 03-Apr-95 KyleP Created. // // Notes: Need to have Ole DB error handling here because errors are being // translated. // //---------------------------------------------------------------------------- STDMETHODIMP CScrollableSorted::GetRowsByBookmark( HCHAPTER hChapter, DBCOUNTITEM cRows, const DBBKMARK rgcbBookmarks [], const BYTE * ppBookmarks[], HROW rghRows[], DBROWSTATUS rgRowStatus[] ) { _DBErrorObj.ClearErrorInfo(); SCODE sc = S_OK; ULONG cRowsObtained = 0; Win4Assert( 0 == hChapter && "Chapter support not yet implemented" ); if (0 == rgcbBookmarks || 0 == ppBookmarks || 0 == rghRows) { vqDebugOut((DEB_IERROR, "CScrollableSorted::GetRowsByBookmark: Invalid Argument(s)\n")); return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetLocate); } SetupFetch( cRows, &rghRows ); // // Note: This code could be optimized to fetch more bookmarks at // once, but only by complicating the logic. Until we find // it's worth the pain, let's keep it simple! // unsigned cRowsProcessed; for ( cRowsProcessed = 0; cRowsProcessed < cRows; cRowsProcessed++ ) { CDistributedBookmark bmk( rgcbBookmarks[cRowsProcessed], ppBookmarks[cRowsProcessed], _rowset._cbBookmark, _rowset._cChild ); DBBKMARK cbBookmark = bmk.GetSize(); BYTE const * pbBookmark = bmk.Get(); sc = Get( bmk.Index() )->GetRowsByBookmark( hChapter, 1, &cbBookmark, (BYTE const **)&pbBookmark, &rghRows[cRowsProcessed], (0 == rgRowStatus) ? 0 : &rgRowStatus[cRowsProcessed] ); if ( FAILED(sc) ) { continue; } else { rghRows[cRowsProcessed] = _rowset._RowManager.Add( bmk.Index(), rghRows[cRowsProcessed] ); cRowsObtained++; } } if (cRowsProcessed == cRowsObtained) sc = S_OK; else if (cRowsObtained > 0) // and not all rows were successfully processed sc = DB_S_ERRORSOCCURRED; else // no rows were successfully processed sc = DB_E_ERRORSOCCURRED; if ( SUCCEEDED( sc ) && cRowsObtained > 0 && !_rowset._xChildNotify.IsNull() ) { _rowset._xChildNotify->OnRowChange( cRowsObtained, rghRows, DBREASON_ROW_ACTIVATE, DBEVENTPHASE_DIDEVENT, TRUE); } return ( S_OK == sc ? S_OK : _DBErrorObj.PostHResult(sc, IID_IRowsetLocate) ); } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::Hash, public // // Synopsis: Hash bookmark // // Arguments: [hChapter] -- Chapter. // [cBookmarks] -- Number of bookmarks. // [rgcbBookmarks] -- Size of bookmark(s) // [rgpBookmarks] -- Bookmark(s) to hash. // [rgHashedValues] -- Hash(s) returned here. // [rgRowStatus] -- Row fetch statuses returned here // // History: 03-Apr-95 KyleP Created. // // Notes: Need to have Ole DB error handling here because errors are being // translated. // //---------------------------------------------------------------------------- STDMETHODIMP CScrollableSorted::Hash( HCHAPTER hChapter, DBBKMARK cBookmarks, const DBBKMARK rgcbBookmarks[], const BYTE * rgpBookmarks[], DBHASHVALUE rgHashedValues[], DBROWSTATUS rgRowStatus[] ) { _DBErrorObj.ClearErrorInfo(); SCODE sc = S_OK; ULONG cSuccessfulHashes = 0; Win4Assert( 0 == hChapter && "Chapter support not yet implemented" ); Win4Assert( rgHashedValues != 0 ); // We ignore error conditions returned on calls to individual bookmarks to be // able to process all the bookmarks. That means invalid arguments will never // be detected and reported without this explicit validation. if (0 == rgHashedValues || (cBookmarks && (0 == rgcbBookmarks || 0 == rgpBookmarks ))) { vqDebugOut((DEB_IERROR, "CScrollableSorted::Hash: Invalid Argument(s)\n")); return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetLocate); } TRY { ULONG partition = 0xFFFFFFFF / _rowset._cChild; for ( ULONG i = 0; i < cBookmarks; i++ ) { // // Special bookmarks hash 'as-is'. // if ( rgcbBookmarks[i] == 1 ) { rgHashedValues[i] = (ULONG)*rgpBookmarks[i]; continue; } // This throws, so we need the try/catch around it CDistributedBookmark bmk( rgcbBookmarks[i], rgpBookmarks[i], _rowset._cbBookmark, _rowset._cChild ); BYTE const * pBmk = bmk.Get(); DBBKMARK cbBmk = bmk.GetSize(); DBHASHVALUE hash; ULONG cErrs = 0; DBROWSTATUS * pErr = 0; sc = Get(bmk.Index())->Hash( 0, 1, &cbBmk, &pBmk, &hash, (rgRowStatus == 0) ? 0 : &rgRowStatus[i] ); if ( FAILED(sc) ) { continue; // continue processing other bookmarks } rgHashedValues[i] = hash % partition + partition * bmk.Index(); cSuccessfulHashes++; } } CATCH( CException, e ) { sc = e.GetErrorCode(); vqDebugOut(( DEB_ERROR, "CScrollableSorted::Hash caught exception 0x%x\n", sc )); } END_CATCH // if we see an error other than DB_E_ERRORSOCCURRED, pass it straight through if (FAILED(sc) && sc != DB_E_ERRORSOCCURRED) return _DBErrorObj.PostHResult(sc, IID_IRowsetLocate); if (cSuccessfulHashes == cBookmarks) return S_OK; if (cSuccessfulHashes > 0) // and not all bookmarks were successfully processed sc = DB_S_ERRORSOCCURRED; else // no hashes were successfully processed sc = DB_E_ERRORSOCCURRED; return _DBErrorObj.PostHResult(sc, IID_IRowsetLocate); } // // IRowsetScroll methods // //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::GetApproximatePosition, public // // Synopsis: Determine approximate position of bookmark. // // Arguments: [hChapter] -- Chapter. // [cbBookmark] -- Size of [pBookmark] // [pBookmark] -- Bookmark of starting fetch position. // [pulPosition] -- Approximate offset from beginning returned // here. // [pulRows] -- Approximate count of rows in table // returned here. // // History: 03-Apr-95 KyleP Created. // // Notes: Need to have Ole DB error handling here because an exception // could result in a local error. // //---------------------------------------------------------------------------- STDMETHODIMP CScrollableSorted::GetApproximatePosition( HCHAPTER hChapter, DBBKMARK cbBookmark, const BYTE * pBookmark, DBCOUNTITEM * pulPosition, DBCOUNTITEM * pulRows ) { _DBErrorObj.ClearErrorInfo(); SCODE sc = S_OK; Win4Assert( 0 == hChapter && "Chapter support not yet implemented" ); if (cbBookmark !=0 && 0 == pBookmark ) { vqDebugOut((DEB_IERROR, "CScrollableSorted::GetApproximatePosition: Invalid Argument(s)\n")); return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetScroll); } TRY { CDistributedBookmark bmk( cbBookmark, (BYTE const *)pBookmark, _rowset._cbBookmark, _rowset._cChild ); if ( 0 != pulPosition ) *pulPosition = 0; if ( 0 != pulRows ) *pulRows = 0; DBCOUNTITEM ulTotalRows = 0; DBCOUNTITEM ulValidRows = 0; unsigned cValidBmk = 0; for ( unsigned i = 0; i < _rowset._cChild; i++ ) { DBCOUNTITEM ulPosition = 0; DBCOUNTITEM ulRows; BOOL fValid; DBBKMARK cb; if ( bmk.IsValid( i ) ) { cValidBmk++; fValid = TRUE; cb = bmk.GetSize(); } else { fValid = FALSE; cb = 0; } sc = Get(i)->GetApproximatePosition( hChapter, cb, bmk.Get(i), (0 == pulPosition) ? 0 : &ulPosition, &ulRows ); if ( FAILED(sc) ) { vqDebugOut(( DEB_ERROR, "CScrollableSorted: GetApproximatePosition(%u) returned 0x%x\n", i, sc )); break; } if ( 0 != pulPosition ) { Win4Assert( ulPosition <= ulRows ); *pulPosition += fValid ? ulPosition : ulRows; // If not valid bookmark, // assume its at end } ulTotalRows += ulRows; if ( fValid ) ulValidRows += ulRows; } if ( pulPosition && cValidBmk > 1 ) { *pulPosition -= ( cValidBmk - 1 ); } // // Special cases (speced in doc) // if ( cbBookmark == 1 && *(BYTE *)pBookmark == DBBMK_FIRST && 0 != pulPosition ) *pulPosition = 1; else if ( cbBookmark == 1 && *(BYTE *)pBookmark == DBBMK_LAST && 0 != pulPosition ) *pulPosition = ulTotalRows; //else if ( 0 != pulPosition && cValidBmk < _rowset._cChild ) //{ // *pulPosition += *pulPosition * (ulTotalRows - ulValidRows) / ulValidRows; //} if ( 0 != pulRows ) *pulRows = ulTotalRows; } CATCH( CException, e ) { sc = e.GetErrorCode(); vqDebugOut(( DEB_ERROR, "CScrollableSorted::GetApproximatePosition caught exception 0x%x\n", sc )); } END_CATCH if (FAILED(sc)) _DBErrorObj.PostHResult(sc, IID_IRowsetScroll); return sc; } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::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 CScrollableSorted::GetExactPosition( HCHAPTER hChapter, DBBKMARK cbBookmark, const BYTE * pBookmark, DBCOUNTITEM * pulPosition, DBCOUNTITEM * pulRows) /*const*/ { return GetApproximatePosition( hChapter, cbBookmark, pBookmark, pulPosition, pulRows ); } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::GetRowsAtRatio, public // // Synopsis: Fetch rows from approximate position. // // Arguments: [hRegion] -- Watch region // Arguments: [hChapter] -- Chapter. // [ulNumerator] -- Numerator of position. // [ulDenominator] -- Denominator of position. // [cRows] -- Count of rows requested // [pcRowsObtained] -- Count of rows in [rrghRows] returned here. // [rrghRows] -- HROWs returned here. // // History: 03-Apr-95 KyleP Created. // // Notes: Need to have Ole DB error handling here because an exception // could result in a local error. // //---------------------------------------------------------------------------- STDMETHODIMP CScrollableSorted::GetRowsAtRatio( HWATCHREGION hRegion, HCHAPTER hChapter, DBCOUNTITEM ulNumerator, DBCOUNTITEM ulDenominator, DBROWCOUNT cRows, DBCOUNTITEM * pcRowsObtained, HROW ** rrghRows ) { _DBErrorObj.ClearErrorInfo(); SCODE sc = S_OK; BOOL fAllocated = FALSE; Win4Assert( 0 == hChapter && "Chapter support not yet implemented" ); if (0 == pcRowsObtained || 0 == rrghRows ) { vqDebugOut((DEB_IERROR, "CScrollableSorted::GetRowsAtRatio: Invalid Argument(s)")); return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetScroll); } TRY { *pcRowsObtained = 0; fAllocated = SetupFetch( cRows, rrghRows ); // // Seek to position. Special cases are beginning and end of rowset. // Seek( ulNumerator, ulDenominator ); sc = StandardFetch( cRows, pcRowsObtained, *rrghRows ); if ( SUCCEEDED( sc ) && !_rowset._xChildNotify.IsNull() && *pcRowsObtained != 0 ) { _rowset._xChildNotify->OnRowChange( *pcRowsObtained, *rrghRows, DBREASON_ROW_ACTIVATE, DBEVENTPHASE_DIDEVENT, TRUE); } } CATCH( CException, e ) { sc = e.GetErrorCode(); vqDebugOut(( DEB_ERROR, "CScrollableSorted::GetRowsAtRatio: Exception 0x%x\n", sc )); // // If we already have some rows, then we can't 'unfetch' them, so we have // to mask this error. Presumably it won't be transient and we'll get it // again later. // if ( *pcRowsObtained > 0 ) { if (FAILED(sc)) sc = DB_S_ROWLIMITEXCEEDED; } else if ( fAllocated ) { CoTaskMemFree( *rrghRows ); *rrghRows = 0; } } END_CATCH if (FAILED(sc)) _DBErrorObj.PostHResult(sc, IID_IRowsetScroll); return( sc ); } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::CScrollableSorted, public // // Synopsis: Initialize rowset. // // Arguments: [pUnkOuter] -- outer unknown // [ppMyUnk] -- OUT: on return, filled with pointer to my // non-delegating IUnknown // [aChild] -- Array of child cursors (rowsets). // [cChild] -- Count of elements in [aChild]. // [Props] -- Rowset properties. // [cCol] -- Number of original columns. // [Sort] -- Sort specification. // // History: 03-Apr-95 KyleP Created. // //---------------------------------------------------------------------------- CScrollableSorted::CScrollableSorted( IUnknown * pUnkOuter, IUnknown ** ppMyUnk, IRowsetScroll ** aChild, unsigned cChild, CMRowsetProps const & Props, unsigned cCol, CSort const & Sort, CAccessorBag & aAccessors ) : _rowset( 0, ppMyUnk, (IRowset **)aChild, cChild, Props, cCol + 1, // Add 1 col. for bookmark Sort, aAccessors ), _apPosCursor(cChild), _heap( cChild ), #pragma warning(disable : 4355) // 'this' in a constructor _impIUnknown(this), _DBErrorObj( * ((IUnknown *) (IRowset *) this), _mutex ) #pragma warning(default : 4355) // 'this' in a constructor { unsigned iChild = 0; TRY { _DBErrorObj.SetInterfaceArray(cRowsetErrorIFs, apRowsetErrorIFs); _rowset._RowManager.TrackSiblings( cChild ); if (pUnkOuter) _pControllingUnknown = pUnkOuter; else _pControllingUnknown = (IUnknown * )&_impIUnknown; // // Create accessors // for ( ; iChild < _rowset._cChild; iChild++ ) { _apPosCursor[iChild] = new CMiniPositionableCache( iChild, Get(iChild), Sort.Count(), _rowset._bindSort.GetPointer(), _rowset._cbSort, _rowset._iColumnBookmark, _rowset._cbBookmark ); } // // Initialize heap // _heap.Init( &_rowset._Comparator, GetCacheArray() ); *ppMyUnk = ((IUnknown *)&_impIUnknown); (*ppMyUnk)->AddRef(); } CATCH( CException, e ) { long lChild = iChild; for ( lChild--; lChild >= 0; lChild-- ) delete _apPosCursor[lChild]; RETHROW(); } END_CATCH END_CONSTRUCTION( CScrollableSorted ); } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::~CScrollableSorted, private // // Synopsis: Destructor. // // History: 03-Apr-95 KyleP Created. // //---------------------------------------------------------------------------- CScrollableSorted::~CScrollableSorted() { unsigned ii; for ( ii = _rowset._cChild ; ii > 0; ii-- ) { delete _apPosCursor[ii-1]; } } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::_GetMaxPrevRowChild, private // // Synopsis: From all the child cursors, return the index which has the // max. prev. row. If none of the cursor has a prev. row (they are // all at the top), the index returned is the total no. of cursors. // // Arguments: none // // History: 10-Sep-98 VikasMan Created. // //---------------------------------------------------------------------------- unsigned CScrollableSorted::_GetMaxPrevRowChild() { unsigned iMaxRowChild = _rowset._cChild; unsigned iChild; BYTE * pMaxPrevData; BYTE * pPrevData; for ( iChild = 0; iChild < _rowset._cChild; iChild++ ) { pMaxPrevData = _apPosCursor[iChild]->GetPrevData(); if ( pMaxPrevData ) { iMaxRowChild = iChild; break; } } for ( iChild++; iChild < _rowset._cChild; iChild++ ) { pPrevData = _apPosCursor[iChild]->GetPrevData(); if ( pPrevData ) { if ( _rowset._Comparator.IsLT( pMaxPrevData, _apPosCursor[iMaxRowChild]->DataLength(), _apPosCursor[iMaxRowChild]->Index(), pPrevData, _apPosCursor[iChild]->DataLength(), _apPosCursor[iChild]->Index() ) ) { iMaxRowChild = iChild; pMaxPrevData = pPrevData; } } } return iMaxRowChild; } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::Seek, private // // Synopsis: Position heap to specified bookmark + offset. // // Arguments: [cbBookmark] -- Size of [pbBookmark]. // [pbBookmark] -- Bookmark. // [lOffset] -- Offset from bookmark. // // History: 03-Apr-95 KyleP Created. // //---------------------------------------------------------------------------- SCODE CScrollableSorted::Seek( DBBKMARK cbBookmark, BYTE const * pbBookmark, DBROWOFFSET lOffset ) { unsigned cValid = _rowset._cChild; unsigned i; SCODE sc = S_OK; // // Special case: Beginning of table // if ( cbBookmark == 1 && *pbBookmark == DBBMK_FIRST ) { // // Seek all cursors to beginning of table. // for ( i = 0; i < cValid; i++ ) _apPosCursor[ i ]->Seek( cbBookmark, pbBookmark ); } else if (cbBookmark == 1 && *pbBookmark == DBBMK_LAST) { // // First Seek all cursors to end of table. // for ( i = 0; i < cValid; i++ ) _apPosCursor[ i ]->Seek( cbBookmark, pbBookmark ); for ( i = 0; i < cValid; i++ ) { if ( _apPosCursor[i]->IsAtEnd() ) { _apPosCursor[i]->Seek(1); // pushes it beyond the end // // Move unseekable cursor to end of array. // cValid--; SwapCursor(i, cValid); i--; } } // Find the cursor with the maximum value unsigned iCurWithMaxVal = 0; for (i = 1; i < cValid; i++) { if ( _rowset._Comparator.IsLT( _apPosCursor[iCurWithMaxVal]-> GetData(), _apPosCursor[iCurWithMaxVal]->DataLength(), _apPosCursor[iCurWithMaxVal]->Index(), _apPosCursor[i]->GetData(), _apPosCursor[i]->DataLength(), _apPosCursor[i]->Index() ) ) iCurWithMaxVal = i; } // Now set all cursors except _apPosCursor[iCurWithMaxVal] to end of table for (i = 0; i < cValid; i++) if (i != iCurWithMaxVal) _apPosCursor[i]->Seek(1); // pushes it beyond the end } else { CDistributedBookmark bmk( cbBookmark, pbBookmark, _rowset._cbBookmark, _rowset._cChild ); // // Seek target cursor. We have to do this one first. // for ( unsigned iTarget = 0; iTarget < _rowset._cChild; iTarget++ ) { if ( _apPosCursor[iTarget]->Index() == (int)bmk.Index() ) { _apPosCursor[ iTarget ]->Seek( bmk.GetSize(), bmk.Get() ); break; } } // // Seek child cursor(s), other than target. // for ( i = 0; i < cValid; i++ ) { // // Ignore target cursor // if ( i == iTarget ) continue; // // Seek to 'hint' position. // _apPosCursor[i]->FlushCache(); _apPosCursor[i]->SetCacheSize( 1 ); PMiniRowCache::ENext next; if ( bmk.IsValid( _apPosCursor[i]->Index() ) ) next = _apPosCursor[i]->Seek( bmk.GetSize(), bmk.Get( _apPosCursor[i]->Index() ) ); else { BYTE bStart = DBBMK_FIRST; next = _apPosCursor[i]->Seek( sizeof(bStart), &bStart ); } // // And adjust so that the cursor is positioned just after the target cursor. // if ( next != PMiniRowCache::Ok || AdjustPosition( i, iTarget ) != PMiniRowCache::Ok ) { BYTE bEnd = DBBMK_LAST; _apPosCursor[i]->Seek( sizeof(bEnd), &bEnd ); _apPosCursor[i]->Seek(1); // pushes it beyond the end // // Move unseekable cursor to end of array. // cValid--; SwapCursor(i, cValid); if ( cValid == iTarget ) { iTarget = i; } i--; } } } for ( i = 0; iIsAtEnd() ) { _apPosCursor[i]->Seek(1); // pushes it beyond the end // // Move unseekable cursor to end of array. // cValid--; SwapCursor(i, cValid); i--; } } _heap.ReInit( cValid ); if ( lOffset < 0 ) { cValid = _rowset._cChild; // Load Previous rows from all valid for ( i = 0; i < cValid; i++ ) { _apPosCursor[i]->LoadPrevRowData(); } unsigned iMaxRowChild; BOOL fReInit = FALSE; for( ;; ) { // Find out which rowset has the max. prev. row iMaxRowChild = _GetMaxPrevRowChild(); if ( iMaxRowChild >= _rowset._cChild ) { break; // no previous row } fReInit = TRUE; // Move the rowset with the max. prev. row back _apPosCursor[iMaxRowChild]->MovePrev(); if ( 0 == ++lOffset ) { break; } // Reload prev. row for the rowset which we moved back _apPosCursor[iMaxRowChild]->LoadPrevRowData(); } if ( fReInit) { for ( i = 0; iIsAtEnd() ) { _apPosCursor[i]->Seek(1); // pushes it beyond the end // // Move unseekable cursor to end of array. // cValid--; SwapCursor(i, cValid); i--; } } _heap.ReInit( cValid ); } if ( lOffset < 0 ) { // NTRAID#DB-NTBUG9-84055-2000/07/31-dlee Failed distribued query row fetches don't restore previous seek position // Do we need to reset the position of rowset back to // where it was before call to Seek ? sc = DB_E_BADSTARTPOSITION; } } else { for ( ; lOffset > 0; lOffset-- ) { // // Release top HROW. // HROW hrow = _heap.Top()->GetHROW(); SCODE sc2 = Get( _heap.Top()->Index() )->ReleaseRows( 1, &hrow, 0, 0, 0 ); Win4Assert( SUCCEEDED(sc2) ); PMiniRowCache::ENext next = _heap.Next(); Win4Assert( PMiniRowCache::NotNow != next ); if ( PMiniRowCache::EndOfRows == next ) break; } } return sc; } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::AdjustPosition, private // // Synopsis: Adjust position of iChild-th cursor to just after the // iTarget-th cursor. // // Arguments: [iChild] -- Index of child in _apPosCursor. // [iTarget] -- Index of target in _apPosCursor. // // Returns: Seek result. // // History: 03-Apr-95 KyleP Created. // //---------------------------------------------------------------------------- PMiniRowCache::ENext CScrollableSorted::AdjustPosition( unsigned iChild, int iTarget ) { vqDebugOut(( DEB_ITRACE, "Child: %d data = 0x%x, size = %u\n", _apPosCursor[iChild]->Index(), _apPosCursor[iChild]-> GetData(), _apPosCursor[iChild]->DataLength() )); vqDebugOut(( DEB_ITRACE, "Target: %d data = 0x%x, size = %u\n", _apPosCursor[iTarget]->Index(), _apPosCursor[iTarget]-> GetData(), _apPosCursor[iTarget]->DataLength() )); PMiniRowCache::ENext next; // Used to report error states. int iJump; // Seek offset (positive or negative) from starting point. int iNextInc; // Next seek increment. int iDirection; // Direction of seek from initial position to target. if ( _rowset._Comparator.IsLT( _apPosCursor[iTarget]-> GetData(), _apPosCursor[iTarget]->DataLength(), _apPosCursor[iTarget]->Index(), _apPosCursor[iChild]->GetData(), _apPosCursor[iChild]->DataLength(), _apPosCursor[iChild]->Index() ) ) { next = InitialSeek( iChild, iTarget, -1, iJump, iNextInc, iDirection ); // // Running into end (actually beginning) here is ok. // if ( next == PMiniRowCache::EndOfRows ) return PMiniRowCache::Ok; } else next = InitialSeek( iChild, iTarget, 1, iJump, iNextInc, iDirection ); if ( next != PMiniRowCache::Ok ) return next; // // At this point, iChild is at least 1 row < iTarget (or 1 row > iTarget). // vqDebugOut(( DEB_ITRACE, "Final positioning:\n" )); while ( iNextInc > 0 ) { iJump += (iNextInc * iDirection); vqDebugOut(( DEB_ITRACE, "Backward %d\n", -iJump )); PMiniRowCache::ENext next = _apPosCursor[iChild]->Seek( iJump ); iNextInc /= 2; if ( _rowset._Comparator.IsLT( _apPosCursor[iTarget]-> GetData(), _apPosCursor[iTarget]->DataLength(), _apPosCursor[iTarget]->Index(), _apPosCursor[iChild]->GetData(), _apPosCursor[iChild]->DataLength(), _apPosCursor[iChild]->Index() ) ) iDirection = -1; else iDirection = 1; } // // Either the row we are on is correct, or off by one. // if ( _rowset._Comparator.IsLT( _apPosCursor[iChild]-> GetData(), _apPosCursor[iChild]->DataLength(), _apPosCursor[iChild]->Index(), _apPosCursor[iTarget]->GetData(), _apPosCursor[iTarget]->DataLength(), _apPosCursor[iTarget]->Index() ) ) return _apPosCursor[iChild]->Seek( iJump + 1 ); else return PMiniRowCache::Ok; } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::InitialSeek, private // // Synopsis: Worker routine for AdjustPosition. Binary searches until // iChild is at least one row on the opposite side of iTarget // from where it started. // // Arguments: [iChild] -- Index of child in _apPosCursor. // [iTarget] -- Index of target in _apPosCursor. // [InitialDirection] -- Direction to start moving. // [iJump] -- Offset from starting point returned // here. // [iNextInc] -- Size of last jump returned here. // [iDirection] -- Direction of last jump returned here. // // Returns: Seek result. // // History: 03-Apr-95 KyleP Created. // //---------------------------------------------------------------------------- PMiniRowCache::ENext CScrollableSorted::InitialSeek( unsigned iChild, int iTarget, int InitialDirection, int & iJump, int & iNextInc, int & iDirection ) { // // If the child starts out > target, then go backward until we pass // the target, and then forward until *just* after target, otherwise // do the opposite. The goal is to go a known distance past the target // so we can seek back towards it logarithmically. // iJump = 1 * InitialDirection; iNextInc = 1 * InitialDirection; int LTa; int LTb; if ( InitialDirection == 1 ) { LTa = iChild; LTb = iTarget; } else { LTa = iTarget; LTb = iChild; } vqDebugOut(( DEB_ITRACE, "Initial positioning:\n" )); PMiniRowCache::ENext next = PMiniRowCache::EndOfRows; while ( iNextInc != 0 ) { do { vqDebugOut(( DEB_ITRACE, "%s %u\n", iJump < 0 ? "Backward" : "Forward", iJump < 0 ? -iJump : iJump )); next = _apPosCursor[iChild]->Seek( iJump ); if ( 0 == iNextInc ) { Win4Assert( next != PMiniRowCache::EndOfRows ); next = PMiniRowCache::EndOfRows; break; } switch ( next ) { case PMiniRowCache::Ok: iNextInc *= 2; break; case PMiniRowCache::EndOfRows: iJump -= iNextInc; iNextInc /= 2; iJump += iNextInc; break; default: return next; } } while ( next == PMiniRowCache::EndOfRows ); Win4Assert( iJump * InitialDirection >= 0 ); //if ( iJump * InitialDirection > 0 && if ( iJump != 0 && _rowset._Comparator.IsLT( _apPosCursor[LTa]-> GetData(), _apPosCursor[LTa]->DataLength(), _apPosCursor[LTa]->Index(), _apPosCursor[LTb]->GetData(), _apPosCursor[LTb]->DataLength(), _apPosCursor[LTb]->Index() ) ) iJump += iNextInc; else break; } iNextInc = ((iJump*InitialDirection) / 2) + 1; iDirection = -InitialDirection; return next; } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::Seek, private // // Synopsis: Position heap to approximate position. // // Arguments: [ulNumerator] -- Numerator. // [ulDenominator] -- Denominator. // // History: 03-Apr-95 KyleP Created. // //---------------------------------------------------------------------------- void CScrollableSorted::Seek( DBCOUNTITEM ulNumerator, DBCOUNTITEM ulDenominator ) { unsigned cValid = _rowset._cChild; // // Special case: Beginning of table // if ( 0 == ulNumerator ) { // // Seek all cursors to beginning of table. // BYTE bmkStart = DBBMK_FIRST; for ( unsigned i = 0; i < _rowset._cChild; i++ ) _apPosCursor[ i ]->Seek( sizeof(bmkStart), &bmkStart ); _heap.ReInit( cValid ); } // // Special case: End of table // else if ( ulNumerator == ulDenominator ) { // // Seek all cursors to end of table. // BYTE bmkEnd = DBBMK_LAST; for ( unsigned i = 0; i < _rowset._cChild; i++ ) _apPosCursor[ i ]->Seek( sizeof(bmkEnd), &bmkEnd ); _heap.ReInit( cValid ); } // // Normal case: Middle of table // else { // // Seek all cursors to ratio. // // Get the total # of rows DBCOUNTITEM ulRows = 0; SCODE sc = GetApproximatePosition( NULL, 0, NULL, NULL, &ulRows ); if ( SUCCEEDED (sc ) && ulRows > 0 ) { DBROWOFFSET lSeekPos = (( ulNumerator * ulRows ) / ulDenominator ); BYTE bmk; if ( (lSeekPos * 100 / ulRows) > 50 ) { // seek from bottom bmk = DBBMK_LAST; lSeekPos = lSeekPos - (LONG) ulRows + 1; } else { // seek from top bmk = DBBMK_FIRST; } sc = Seek( sizeof(bmk), &bmk, lSeekPos ); } #if 0 for ( unsigned i = 0; i < _rowset._cChild; i++ ) { _apPosCursor[ i ]->FlushCache(); _apPosCursor[ i ]->SetCacheSize( 1 ); _apPosCursor[ i ]->Seek( ulNumerator, ulDenominator ); } // // Heapify, then pick the cursor ulNumerator / ulDenominator from // top of the heap. // _heap.ReInit( _rowset._cChild ); _heap.NthToTop( _rowset._cChild * ulNumerator / ulDenominator ); unsigned iTarget = 0; // // Adjust position of all other cursors to follow target. // for ( i = 0; i < cValid; i++ ) { // // Ignore target cursor // if ( i == iTarget ) continue; if ( AdjustPosition( i, iTarget ) != PMiniRowCache::Ok ) { // // Move unseekable cursor to end of array. // cValid--; SwapCursor(i, cValid); i--; } } #endif } } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::SetupFetch, private // // Synopsis: Common operations before seek in Get* routines. // // Arguments: [cRows] -- Number of rows requested. // [rrghRows] -- Rows returned here. May have to allocate. // // Returns: TRUE if *rrghRows was allocated. // // History: 03-Apr-95 KyleP Created. // //---------------------------------------------------------------------------- BOOL CScrollableSorted::SetupFetch( DBROWCOUNT cRows, HROW * rrghRows[] ) { // // We may have reached some temporary condition such as // DB_S_ROWLIMITEXCEEDED on the last pass. Iterate until we // have a valid heap. // PMiniRowCache::ENext next = _heap.Validate(); if ( next == PMiniRowCache::NotNow ) { THROW( CException( DB_E_ROWLIMITEXCEEDED ) ); } // // We may have to allocate memory, if the caller didn't. // BOOL fAllocated = FALSE; if ( 0 == *rrghRows ) { *rrghRows = (HROW *)CoTaskMemAlloc( (ULONG) ( abs(((LONG) cRows)) * sizeof(HROW) ) ); fAllocated = TRUE; } if ( 0 == *rrghRows ) { vqDebugOut(( DEB_ERROR, "CScrollableSorted::SetupFetch: Out of memory.\n" )); THROW( CException( E_OUTOFMEMORY ) ); } return fAllocated; } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::SetupFetch, private // // Synopsis: Common operations before seek in Get* routines. // // Arguments: [cRows] -- Number of rows requested. // [pcRowsObtained] -- Count actually fetched. // [rghRows] -- Rows returned here. Already allocated // if needed. // // Returns: Status code. // // History: 03-Apr-95 KyleP Created. // //---------------------------------------------------------------------------- SCODE CScrollableSorted::StandardFetch( DBROWCOUNT cRows, DBCOUNTITEM * pcRowsObtained, HROW rghRows[] ) { SCODE sc = S_OK; int iDir = 1; if ( cRows < 0 ) { cRows = -cRows; iDir = -1; } unsigned ucRows = (unsigned) cRows; // // Adjust cache size if necessary. // _heap.AdjustCacheSize( ucRows ); // // Fetch from top of heap. // while ( *pcRowsObtained < ucRows ) { // // We may be entirely out of rows. // if ( _heap.IsHeapEmpty() ) { sc = DB_S_ENDOFROWSET; break; } (rghRows)[*pcRowsObtained] = _rowset._RowManager.Add( _heap.Top()->Index(), _heap.TopHROWs() ); (*pcRowsObtained)++; // // Fetch the next row. // PMiniRowCache::ENext next = _heap.Next( iDir ); if ( CMiniRowCache::NotNow == next ) { sc = DB_S_ROWLIMITEXCEEDED; break; } } return sc; } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::RatioFinished, public // // Synopsis: Ratio finished for asynchronously populated rowsets. // // Arguments: [pulDenominator] -- Denominator returned here. // [pulNumerator] -- Numerator returned here. // [pcRows] -- Count of rows returned here // [pfNewRows] -- TRUE if rows added since last call. // // History: 03-Apr-95 KyleP Created. // // Notes: Need to have Ole DB error handling here because an exception // could result in a local error. // //---------------------------------------------------------------------------- STDMETHODIMP CScrollableSorted::RatioFinished( DBCOUNTITEM * pulDenominator, DBCOUNTITEM * pulNumerator, DBCOUNTITEM * pcRows, BOOL * pfNewRows ) { _DBErrorObj.ClearErrorInfo(); IRowsetAsynch * pra = 0; SCODE scResult = S_OK; *pulDenominator = 0; *pulNumerator = 0; *pcRows = 0; *pfNewRows = FALSE; for ( unsigned iChild = 0; iChild < _rowset._cChild; iChild++ ) { DBCOUNTITEM ulDenom; DBCOUNTITEM ulNum; DBCOUNTITEM cRows; BOOL fNew; SCODE sc = Get(iChild)->QueryInterface( IID_IRowsetAsynch, (void **) &pra ); if ( SUCCEEDED(sc) ) { sc = pra->RatioFinished( &ulDenom, &ulNum, &cRows, &fNew ); pra->Release(); } if ( FAILED(sc) && E_NOTIMPL != sc && E_NOINTERFACE != sc ) { vqDebugOut(( DEB_ERROR, "IRowsetAsynch::RatioFinished(child %d) returned 0x%x\n", iChild, sc )); scResult = sc; break; } if ( SUCCEEDED(sc) ) { Win4Assert( *pulDenominator + ulDenom > *pulDenominator ); *pulDenominator += ulDenom; *pulNumerator += ulNum; *pcRows += cRows; *pfNewRows = *pfNewRows || fNew; } } if ( 0 == *pulDenominator ) scResult = E_NOTIMPL; if (FAILED(scResult)) _DBErrorObj.PostHResult(scResult, IID_IRowsetAsynch); return( scResult ); } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::Stop, public // // Synopsis: Stop population of asynchronously populated rowsets. // // Arguments: - None - // // History: 16 Jun 95 Alanw Created. // // Notes: Need to have Ole DB error handling here because errors are // being translated. // //---------------------------------------------------------------------------- STDMETHODIMP CScrollableSorted::Stop( ) { _DBErrorObj.ClearErrorInfo(); IRowsetAsynch * pra = 0; SCODE scResult = S_OK; for ( unsigned iChild = 0; iChild < _rowset._cChild; iChild++ ) { SCODE sc = Get(iChild)->QueryInterface( IID_IRowsetAsynch, (void **) &pra ); if ( SUCCEEDED(sc) ) { sc = pra->Stop( ); pra->Release(); } if ( FAILED(sc) && (S_OK == scResult || E_NOTIMPL != sc || E_NOINTERFACE != sc)) { vqDebugOut(( DEB_ERROR, "IRowsetAsynch::Stop (child %d) returned 0x%x\n", iChild, sc )); scResult = sc; } } if (FAILED(scResult)) _DBErrorObj.PostHResult(scResult, IID_IRowsetAsynch); return( scResult ); } // // IDbAsynchStatus methods // //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::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 // // History: 03 Sep 1998 VikasMan Created // //---------------------------------------------------------------------------- STDMETHODIMP CScrollableSorted::Abort( HCHAPTER hChapter, ULONG ulOperation ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; XInterface xIDBAsynchStatus; for ( unsigned iChild = 0; iChild < _rowset._cChild; iChild++ ) { SCODE sc = Get(iChild)->QueryInterface( IID_IDBAsynchStatus, xIDBAsynchStatus.GetQIPointer() ); if ( SUCCEEDED( sc ) ) { sc = xIDBAsynchStatus->Abort( hChapter, ulOperation ); if ( S_OK == scResult ) { scResult = sc; } } xIDBAsynchStatus.Free(); } return scResult; } //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::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 // // History: 03 Sep 1998 VikasMan Created // //---------------------------------------------------------------------------- STDMETHODIMP CScrollableSorted::GetStatus( HCHAPTER hChapter, DBASYNCHOP ulOperation, DBCOUNTITEM * pulProgress, DBCOUNTITEM * pulProgressMax, DBASYNCHPHASE * pulAsynchPhase, LPOLESTR * ppwszStatusText ) { _DBErrorObj.ClearErrorInfo(); SCODE scResult = S_OK; XInterface xIDBAsynchStatus; if ( pulProgress ) *pulProgress = 0; if ( pulProgressMax ) *pulProgressMax = 0; if ( pulAsynchPhase ) *pulAsynchPhase = DBASYNCHPHASE_COMPLETE; if ( ppwszStatusText ) *ppwszStatusText = 0; XCoMem xStatusText; double dRatio = 0; for ( unsigned iChild = 0; iChild < _rowset._cChild; iChild++ ) { SCODE sc = Get(iChild)->QueryInterface( IID_IDBAsynchStatus, xIDBAsynchStatus.GetQIPointer() ); if ( SUCCEEDED( sc ) ) { DBCOUNTITEM ulProgress, ulProgressMax; DBASYNCHPHASE ulAsynchPhase; scResult = xIDBAsynchStatus->GetStatus ( hChapter, ulOperation, &ulProgress, &ulProgressMax, &ulAsynchPhase, 0 == iChild ? ppwszStatusText : 0 ); if ( S_OK != scResult ) { return scResult; } if ( 0 == iChild && ppwszStatusText ) { xStatusText.Set( *ppwszStatusText ); } if ( ulProgressMax ) { dRatio += ( (double)ulProgress / (double)ulProgressMax ); } if ( pulAsynchPhase && *pulAsynchPhase != DBASYNCHPHASE_POPULATION ) *pulAsynchPhase = ulAsynchPhase; } xIDBAsynchStatus.Free(); } DWORD dwNum = 0; DWORD dwDen = 0; if ( dRatio ) { Win4Assert( _rowset._cChild ); dRatio /= _rowset._cChild; dwDen = 1; while ( dRatio < 1.0 ) { dRatio *= 10; dwDen *= 10; } dwNum = (DWORD)dRatio; } if ( pulProgress ) *pulProgress = dwNum; if ( pulProgressMax ) *pulProgressMax = dwDen; if ( SUCCEEDED( scResult ) ) { // Let memory pass thru to the client xStatusText.Acquire(); } return scResult; } // // IRowsetWatchRegion methods // //+--------------------------------------------------------------------------- // // Member: CScrollableSorted::Refresh, public // // Synopsis: Implementation of IRowsetWatchRegion::Refresh. Calls refresh on // all the child rowsets // // Arguments: pChangesObtained // prgChanges // // Returns: Always returns DB_S_TOOMAYCHANGES // // History: 03 Sep 1998 VikasMan Created // //---------------------------------------------------------------------------- STDMETHODIMP CScrollableSorted::Refresh( DBCOUNTITEM* pChangesObtained, DBROWWATCHCHANGE** prgChanges ) { _DBErrorObj.ClearErrorInfo(); for ( unsigned iChild = 0; iChild < _rowset._cChild; iChild++ ) { if ( _rowset._xArrChildRowsetWatchRegion[iChild].GetPointer() ) { _rowset._xArrChildRowsetWatchRegion[iChild]->Refresh( pChangesObtained, prgChanges ); } } *pChangesObtained = 0; return DB_S_TOOMANYCHANGES; }