You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2129 lines
66 KiB
2129 lines
66 KiB
//
|
|
// 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;
|
|
}
|
|
|