|
|
//
// 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 <pch.cxx>
#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; 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--; } }
_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; 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--; } } _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<IDBAsynchStatus> 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<IDBAsynchStatus> xIDBAsynchStatus;
if ( pulProgress ) *pulProgress = 0; if ( pulProgressMax ) *pulProgressMax = 0; if ( pulAsynchPhase ) *pulAsynchPhase = DBASYNCHPHASE_COMPLETE; if ( ppwszStatusText ) *ppwszStatusText = 0;
XCoMem<OLECHAR> 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; }
|