|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1995 - 2000.
//
// File: Distrib.cxx
//
// Contents: Top level distribution API
//
// Classes: CDistributedRowset
//
// History: 23-Feb-95 KyleP Created
// 14-JAN-97 KrishnaN Undefined CI_INETSRV and related changes
// 29-Mar-2000 KLam Fixed Bookmarks
//
// 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.
//
// NTRAID#DB-NTBUG9-84041-2000/07/31-dlee Distributed queries don't supported hierarcical rowsets
//
//----------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include "distrib.hxx"
#include "disacc.hxx"
#include "seqser.hxx"
#include "seqsort.hxx"
#include "scrlsort.hxx"
//
// IUnknown methods.
//
//+-------------------------------------------------------------------------
//
// Method: CDistributedRowset::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 CDistributedRowset::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_IRowsetInfo ) { *ppiuk = (void *)((IRowsetInfo *)this); } else if ( riid == IID_IAccessor ) { *ppiuk = (void *)((IAccessor *)this); } else if ( riid == IID_IColumnsInfo ) { *ppiuk = (void *)((IColumnsInfo *)this); } else if ( riid == IID_IRowsetIdentity ) { *ppiuk = (void *)((IRowsetIdentity *)this); } else if ( riid == IID_IConvertType ) { *ppiuk = (void *)((IConvertType *)this); } else if ( riid == IID_IRowsetQueryStatus ) { *ppiuk = (void *)((IRowsetQueryStatus *)this); } else if ( riid == IID_IConnectionPointContainer ) { sc = _SetupConnectionPointContainer( this, ppiuk ); } else { sc = E_NOINTERFACE; }
return sc; }
//
// IRowset methods
//
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::CreateAccessor, public
//
// Synopsis: Creates accessor
//
// Arguments: [dwAccessorFlags] -- Read/Write/ReadWrite
// [cBindings] -- # Columns (bindings)
// [rgBindings] -- Bindings
// [cbRowWidth] -- row width (for IReadData)
// [phAccessor] -- Accessor returned here
// [rgStatus ] -- Set to index of failed binding
//
// History: 28-Mar-95 KyleP Created.
// 22-Apr-97 EmilyB Changed to use accessorbag _aAccessors
//
// Notes: Need to have Ole DB error handling here because the error from the
// underlying call is translated.
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::CreateAccessor( DBACCESSORFLAGS dwAccessorFlags, DBCOUNTITEM cBindings, const DBBINDING rgBindings[], DBLENGTH cbRowWidth, HACCESSOR * phAccessor, DBBINDSTATUS * rgStatus ) { _DBErrorObj.ClearErrorInfo();
if (0 == phAccessor || (0 != cBindings && 0 == rgBindings)) return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IAccessor); if ( 0 == cBindings ) return _DBErrorObj.PostHResult(DB_E_NULLACCESSORNOTSUPPORTED, IID_IAccessor);
SCODE sc = S_OK; LONG iFirstValidBookmark = -1; unsigned iBinding; XArray<DBBINDSTATUS> xStatusCopy;
TRY { BOOL fCreateCopy = FALSE; BOOL fHasBookmark = FALSE;
ULONG iMinColOrdinal = 1, iMaxColOrdinal = _cColumns;
if ( -1 != _iColumnBookmark ) { iMinColOrdinal--; iMaxColOrdinal--; }
if ( rgStatus ) { Win4Assert( 0 == DBSTATUS_S_OK ); RtlZeroMemory( rgStatus, sizeof( rgStatus[0] ) * cBindings ); }
//
// Create a copy of bindings iff it has bookmark column or
// if it has invalid column, so that we can then mess around
// with them ;-)
//
for ( iBinding = 0; iBinding < cBindings; iBinding++ ) { fHasBookmark = ( _iColumnBookmark == rgBindings[iBinding].iOrdinal );
if ( fHasBookmark || rgBindings[iBinding].iOrdinal > iMaxColOrdinal || rgBindings[iBinding].iOrdinal < iMinColOrdinal ) { fCreateCopy = TRUE; break; }
} const DBBINDING * pActualBindings = rgBindings; DBCOUNTITEM cActualBindings = cBindings;
XArray<DBBINDING> xBindingCopy;
if ( fCreateCopy ) { unsigned iBindingCopy = 0;
xBindingCopy.Init( (unsigned) cBindings );
for ( iBinding = 0; iBinding < cBindings; iBinding++ ) { // check for a valid bookmark
if ( _iColumnBookmark == rgBindings[iBinding].iOrdinal && DBTYPE_BYTES == ( rgBindings[iBinding].wType & ~DBTYPE_BYREF ) && DBMEMOWNER_CLIENTOWNED == rgBindings[iBinding].dwMemOwner ) { if ( -1 == iFirstValidBookmark ) { iFirstValidBookmark = iBinding; } continue; }
xBindingCopy[iBindingCopy] = rgBindings[iBinding];
if ( rgBindings[iBinding].iOrdinal > iMaxColOrdinal || rgBindings[iBinding].iOrdinal < iMinColOrdinal || _iColumnBookmark == rgBindings[iBinding].iOrdinal ) { // Invalid column -> set it to DB_INVALIDCOLUMN so
// that we an appropriate error is returned
xBindingCopy[iBindingCopy].iOrdinal = DB_INVALIDCOLUMN; } iBindingCopy++; }
pActualBindings = xBindingCopy.GetPointer(); cActualBindings = iBindingCopy; }
if ( -1 != iFirstValidBookmark ) { if ( rgStatus ) { xStatusCopy.Init( (unsigned) cActualBindings ); } // Create the bookmark accessors if needed
if ( _xDistBmkAcc.IsNull() ) { _xDistBmkAcc.Set( new CDistributedBookmarkAccessor( _aChild, _cChild, dwAccessorFlags, rgStatus ? &rgStatus[iFirstValidBookmark] : NULL, _iColumnBookmark, _cbBookmark ) ); } Win4Assert( _xDistBmkAcc.GetPointer() );
}
*phAccessor = (HACCESSOR)new CDistributedAccessor( _aChild, _cChild, dwAccessorFlags, cActualBindings, pActualBindings, cbRowWidth, -1 != iFirstValidBookmark ? xStatusCopy.GetPointer() : rgStatus, (IUnknown *) (IRowset *) this, rgBindings, cBindings );
CLock lock( _mutex ); _aAccessors.Add( (CAccessorBase *)*phAccessor );
if ( -1 != iFirstValidBookmark ) { if ( rgStatus ) { unsigned iBindingCopy = 0; for( iBinding = 0; iBinding < cBindings; iBinding++ ) { if ( _iColumnBookmark != rgBindings[iBinding].iOrdinal ) { rgStatus[iBinding] = xStatusCopy[iBindingCopy++]; } } } ((CDistributedAccessor *)*phAccessor)->SetupBookmarkAccessor( _xDistBmkAcc.GetPointer(), rgStatus, _iColumnBookmark, _cbBookmark ); } } CATCH( CException, e ) { if ( -1 != iFirstValidBookmark ) { if ( rgStatus ) { unsigned iBindingCopy = 0; for( iBinding = 0; iBinding < cBindings; iBinding++ ) { if ( _iColumnBookmark != rgBindings[iBinding].iOrdinal ) { rgStatus[iBinding] = xStatusCopy[iBindingCopy++]; } } } } sc = e.GetErrorCode();
vqDebugOut(( DEB_ERROR, "CDistributedRowset::CreateAccessor caught exception 0x%x\n", sc )); } END_CATCH
if (FAILED(sc)) _DBErrorObj.PostHResult(sc, IID_IAccessor); return( sc ); }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::GetBindings, public
//
// Synopsis: Creates accessor
//
// Arguments: [hAccessor] -- Handle to Accessor
// [pdwAccessorFlags] -- Type of binding returned here (read/write)
// [pcBindings] -- Count of bindings in [prgBindings] retuned
// here.
// [prgBindings] -- Bindings returned here.
//
// History: 03-Apr-95 KyleP Created.
// 22-Apr-97 EmilyB Changed to use accessorbag _aAccessors
//
// Notes: Need to have Ole DB error handling here because the error from the
// underlying call is translated.
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::GetBindings( HACCESSOR hAccessor, DBACCESSORFLAGS * pdwAccessorFlags, DBCOUNTITEM * pcBindings, DBBINDING * prgBindings[]) { _DBErrorObj.ClearErrorInfo();
SCODE sc;
TRY { CLock lock( _mutex );
CDistributedAccessor * pAcc = (CDistributedAccessor *)_aAccessors.Convert(hAccessor);
sc = pAcc->GetBindings( pdwAccessorFlags, pcBindings, prgBindings ); } CATCH( CException, e ) { vqDebugOut((DEB_ERROR, "CDistributedRowset: GetBindings caught exception 0x%x\n", e.GetErrorCode()) ); // The reason we have an exception here is 'cos pAcc is invalid
sc = DB_E_BADACCESSORHANDLE; } END_CATCH
if (FAILED(sc)) _DBErrorObj.PostHResult(sc, IID_IAccessor);
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::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: Need to have Ole DB error handling here because an exception could
// happen.
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::GetData( HROW hRow, HACCESSOR hAccessor, void * pData ) { _DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
TRY { HROW hrowChild;
int iChild = _RowManager.GetChildAndHROW( hRow, hrowChild );
CLock lock( _mutex ); CDistributedAccessor * pAcc = (CDistributedAccessor *)_aAccessors.Convert(hAccessor);
sc = pAcc->GetData( iChild, hrowChild, pData ); } CATCH( CException, e ) { sc = e.GetErrorCode();
vqDebugOut(( DEB_ERROR, "CDistributedRowset::GetData caught exception 0x%x\n", sc )); } END_CATCH
if (FAILED(sc)) _DBErrorObj.PostHResult(sc, IID_IRowset); return sc; }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::GetReferencedRowset, public
//
// Synopsis: Traverse 'link' to associated rowset.
//
// Arguments: [iColumn] -- Column of 'link'.
// [ppReferencedRowset] -- Link target goes here.
//
// History: 03-Apr-95 KyleP Created.
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::GetReferencedRowset( DBORDINAL iColumn, REFIID riid, IUnknown ** ppReferencedRowset ) { _DBErrorObj.ClearErrorInfo();
// NTRAID#DB-NTBUG9-84041-2000/07/31-dlee Distributed queries don't supported hierarcical rowsets
// This can only be implemented when the distributed rowset gains
// support for hierarchical rowsets.
//
Win4Assert( !"CDistributedRowset::GetReferencedRowset not yet implemented" ); return _DBErrorObj.PostHResult(E_NOTIMPL, IID_IRowsetInfo); }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::GetProperties, public
//
// Synopsis: Return information about the capabilities of the rowset
//
// Arguments: [cProperties] - number of desired properties or 0
// [rgProperties] - array of desired properties or NULL
// [pcProperties] - number of properties returned
// [prgProperties] - array of returned properties
//
// Returns: SCODE
//
// History: 20 Nov 1995 AlanW Created.
//
// Notes: Need to have Ole DB error handling here because the underlying
// call doesn't provide that.
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::GetProperties( const ULONG cProperties, const DBPROPIDSET rgPropertyIDSets[], ULONG * pcProperties, DBPROPSET ** prgProperties) { _DBErrorObj.ClearErrorInfo();
// inner layer doesn't validate params...
if ( (0 != cProperties && 0 == rgPropertyIDSets) || 0 == pcProperties || 0 == prgProperties ) { vqDebugOut((DEB_IERROR, "CDistributedRowset::GetProperties: Invalid Argument(s)\n"));
if (pcProperties) *pcProperties = 0; if (prgProperties) *prgProperties = 0; return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetInfo); }
SCODE scResult = S_OK; *pcProperties = 0; *prgProperties = 0;
TRY { //
// Update ROWSETQUERYSTATUS property
//
DWORD dwStatus; scResult = GetStatus( & dwStatus );
if ( FAILED( scResult ) ) THROW( CException( scResult ) );
_Props.SetValLong( CMRowsetProps::eid_DBPROPSET_MSIDXS_ROWSET_EXT, CMRowsetProps::eid_MSIDXSPROPVAL_ROWSETQUERYSTATUS, dwStatus );
scResult = _Props.GetProperties( cProperties, rgPropertyIDSets, pcProperties, prgProperties ); } CATCH( CException, e ) { scResult = GetOleError(e); vqDebugOut(( DEB_ERROR, "CDistributedRowset::GetProperties -- caught 0x%x\n", scResult )); } END_CATCH;
if (FAILED(scResult)) _DBErrorObj.PostHResult(scResult, IID_IRowsetInfo);
return scResult; }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::GetSpecification, public
//
// Synopsis: Fetch query object corresponding to rowset.
//
// Arguments: [riid] -- Bind spec to this interface.
// [ppSpecification] -- Spec returned here.
//
// History: 03-Apr-95 KyleP Created.
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::GetSpecification( REFIID riid, IUnknown ** ppSpecification ) { _DBErrorObj.ClearErrorInfo();
//
// NTRAID#DB-NTBUG9-84043-2000/07/31-dlee Distributed rowset doesn't implement GetSpecification()
//
// This is now implemented by the base rowset. We need to pass it on
// through in a distributed fasion.
//
Win4Assert( !"CDistributedRowset::GetSpecification not yet implemented" ); return _DBErrorObj.PostHResult(E_NOTIMPL, IID_IAccessor); }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::MapColumnIDs, public
//
// Synopsis: Maps DBID to column ordinal.
//
// Arguments: [cColumnIDs] -- Count of elements in [rgColumnIDs]
// [rgColumnIDs] -- DBID to be mapped.
// [rgColumns] -- Output column ordinals.
//
// History: 03-Apr-95 KyleP Created.
//
// Notes: No need to have Ole DB error handling here because the underlying
// call provides that.
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::MapColumnIDs( DBORDINAL cColumnIDs, DBID const rgColumnIDs[], DBORDINAL rgColumns[] ) { _DBErrorObj.ClearErrorInfo();
IColumnsInfo * pci = 0;
SCODE sc = _aChild[0]->QueryInterface( IID_IColumnsInfo, (void **) &pci );
if ( SUCCEEDED(sc) ) { sc = pci->MapColumnIDs( cColumnIDs, rgColumnIDs, rgColumns ); pci->Release(); }
ULONG iMaxColOrdinal = _cColumns;
if ( -1 != _iColumnBookmark ) { iMaxColOrdinal--; }
//
// Be sure none of the potentially added columns was returned.
//
unsigned cErrors = 0; if ( SUCCEEDED(sc) ) { for (unsigned i=0; i<cColumnIDs; i++) { if (DB_INVALIDCOLUMN == rgColumns[i]) { cErrors++; } else if ( rgColumns[i] > iMaxColOrdinal ) { rgColumns[i] = DB_INVALIDCOLUMN; cErrors++; } } sc = cErrors == 0 ? S_OK : (cErrors != cColumnIDs) ? DB_S_ERRORSOCCURRED : DB_E_ERRORSOCCURRED; }
if (FAILED(sc)) _DBErrorObj.PostHResult(sc, IID_IColumnsInfo); return sc; }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::ReleaseAccessor, public
//
// Synopsis: Releases accessor(s)
//
// Arguments: [hAccessor] -- Handle of accessor,
// [pcRefCount] -- Ptr to ref count.
//
// History: 28-Mar-95 KyleP Created.
// 22-Apr-97 EmilyB Changed to use accessorbag _aAccessors
//
// Notes: Need to have Ole DB error handling here because the underlying call
// doesn't provide that.
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::ReleaseAccessor( HACCESSOR hAccessor, ULONG * pcRefCount) { _DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK; TRY { CLock lock( _mutex ); _aAccessors.Release(hAccessor, pcRefCount); } CATCH(CException, e) { vqDebugOut((DEB_ERROR, "CDistributedRowset::ReleaseAccessor caught exception 0x%x\n", e.GetErrorCode()) ); // The reason we have an exception here is 'cos the accessor is invalid
sc = DB_E_BADACCESSORHANDLE; } END_CATCH
if (FAILED(sc)) _DBErrorObj.PostHResult(sc, IID_IAccessor);
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::AddRefAccessor, public
//
// Synopsis: AddRef accessor(s)
//
// Arguments: [hAccessor] -- Handle of accessor,
// [pcRefCount] -- Ptr to ref count.
//
// History: 16-Jan-97 KrishnaN Created.
// 22-Apr-97 EmilyB Changed to use accessorbag
// _aAccessors
// Notes: Need to have Ole DB error handling here because the underlying call
// doesn't provide that.
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::AddRefAccessor( HACCESSOR hAccessor, ULONG * pcRefCount) { _DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
TRY { CLock lock( _mutex ); _aAccessors.AddRef(hAccessor, pcRefCount); } CATCH(CException, e) { vqDebugOut((DEB_ERROR, "CDistributedRowset::AddRefAccessor caught exception 0x%x\n", e.GetErrorCode()) ); // The reason we have an exception here is 'cos the accessor is invalid.
sc = DB_E_BADACCESSORHANDLE; } END_CATCH;
if (FAILED(sc)) _DBErrorObj.PostHResult(sc, IID_IAccessor);
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::ReleaseChapter, public
//
// Synopsis: Release chapter.
//
// Arguments: [hChapter] -- Chapter
//
// History: 03-Apr-95 KyleP Created.
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::ReleaseChapter( HCHAPTER hChapter ) { _DBErrorObj.ClearErrorInfo();
// NTRAID#DB-NTBUG9-84041-2000/07/31-dlee Distributed queries don't supported hierarcical rowsets
// Currently there is no support for chapters.
Win4Assert( !"CDistributedRowset::ReleaseChapter not yet implemented" ); return _DBErrorObj.PostHResult(E_NOTIMPL, IID_IRowset); }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::RestartPosition, public
//
// Synopsis: Reset cursor for GetNextRows
//
// Arguments: [hChapter] -- Chapter
//
// History: 16 Jun 95 AlanW Created.
//
// Notes: NO need to have Ole DB error handling here because the underlying
// call provides that.
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::RestartPosition( HCHAPTER hChapter ) { SCODE sc = S_OK; BOOL fNotified = FALSE;
TRY { _iCurrentRow = 0;
if ( !_xChildNotify.IsNull() ) { sc = _xChildNotify->OnRowsetChange( DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_OKTODO, FALSE );
if ( S_FALSE == sc ) THROW(CException(DB_E_CANCELED)); fNotified = TRUE; }
for ( unsigned i = 0; i < _cChild; i++ ) { sc = _aChild[i]->RestartPosition( hChapter);
if ( FAILED(sc) ) { vqDebugOut(( DEB_ERROR, "IRowset::RestartPosition returned 0x%x\n", sc )); break; } }
if ( fNotified ) { _xChildNotify->OnRowsetChange( DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_DIDEVENT, TRUE ); } } CATCH( CException, e ) { sc = e.GetErrorCode(); } END_CATCH if ( fNotified && FAILED(sc) ) { _xChildNotify->OnRowsetChange( DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_FAILEDTODO, TRUE ); } return sc; }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::AddRefRows, public
//
// Synopsis: Incs ref. count on row(s)
//
// Arguments: [cRows] -- Number of HROWs in rghRows.
// [rghRows] -- Rows to be refcounted.
// [pcRefCounted] -- Count of rows *successfully* refcounted.
// [rgRefCounts] -- Remaining refcounts on HROWs.
//
// History: 10-Apr-95 KyleP Created.
//
// Notes: Need to have Ole DB error handling here because the underlying
// errors are being translated.
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::AddRefRows( DBCOUNTITEM cRows, const HROW rghRows[], DBREFCOUNT rgRefCounts[], DBROWSTATUS rgRowStatus[] ) { _DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK; ULONG cRefCounted, cSuccessfullyRefCounted;
if (cRows > 0 && 0 == rghRows) { vqDebugOut((DEB_IERROR, "CDistributedRowset::AddRefRows: Invalid Argument(s)\n")); return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowset); }
TRY { for ( cRefCounted = cSuccessfullyRefCounted = 0; cRefCounted < cRows; cRefCounted++ ) { HROW hrowChild;
int iChild = _RowManager.GetChildAndHROW( rghRows[cRefCounted], hrowChild );
sc = _aChild[iChild]->AddRefRows( 1, &hrowChild, 0, (0 == rgRefCounts) ? 0 : &rgRefCounts[cRefCounted] );
if ( FAILED(sc) ) { vqDebugOut(( DEB_ERROR, "IRowset::AddRefRows returned 0x%x\n", sc )); continue; }
_RowManager.AddRef( rghRows[cRefCounted] );
cSuccessfullyRefCounted++; }
if ( 0 != rgRefCounts ) rgRefCounts[0] = cRefCounted;
} CATCH( CException, e ) { sc = e.GetErrorCode(); vqDebugOut(( DEB_ERROR, "CDistributedRowset::AddRefRows -- caught 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_IRowset);
if (cSuccessfullyRefCounted == cRefCounted) return S_OK;
if (cSuccessfullyRefCounted > 0) sc = DB_S_ERRORSOCCURRED; else sc = DB_E_ERRORSOCCURRED;
return _DBErrorObj.PostHResult(sc, IID_IRowset); }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::ReleaseRows, public
//
// Synopsis: Releases row(s)
//
// Arguments: [cRows] -- Number of HROWs in rghRows.
// [rghRows] -- Rows to be released.
// [rgRowOptions]-- row options
// [rgRefCounts] -- Remaining refcounts on HROWs.
// [rgRowStatus] -- Status values.
//
// History: 10-Apr-95 KyleP Created.
// 30-Jan-97 KrishnaN Modified to conform with 1.0 spec
//
// Notes: Need to have Ole DB error handling here because the underlying
// errors are being translated.
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::ReleaseRows( DBCOUNTITEM cRows, const HROW rghRows[], DBROWOPTIONS rgRowOptions[], DBREFCOUNT rgRefCounts[], DBROWSTATUS rgRowStatus[]) { _DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK; ULONG cReleased, cSuccessfullyReleased; DBROWSTATUS rowStatus;
TRY { BOOL fNotify = FALSE; ULONG * pRefCounts = rgRefCounts; DBROWSTATUS * pRowStatus = rgRowStatus;
XArray<ULONG> xrgRefCounts; XArray<DBROWSTATUS> xrgRowStatus;
if ( !_xChildNotify.IsNull() ) { fNotify = TRUE; if ( 0 == pRefCounts ) { xrgRefCounts.Init( (unsigned) cRows ); pRefCounts = xrgRefCounts.GetPointer(); } if ( 0 == pRowStatus ) { xrgRowStatus.Init( (unsigned) cRows ); pRowStatus = xrgRowStatus.GetPointer(); } }
for ( cReleased = cSuccessfullyReleased = 0; cReleased < cRows; cReleased++ ) { HROW hrowChild;
Win4Assert( DB_NULL_HROW != rghRows[cReleased] );
if ( DB_NULL_HROW != rghRows[cReleased] ) { int iChild = _RowManager.GetChildAndHROW( rghRows[cReleased], hrowChild );
sc = _aChild[iChild]->ReleaseRows( 1, &hrowChild, (0 == rgRowOptions) ? 0 : &rgRowOptions[cReleased], (0 == rgRefCounts) ? 0 : &rgRefCounts[cReleased], &rowStatus ); } // At this time, there is no need to translate the error returned by the child.
if (rgRowStatus) rgRowStatus[cReleased] = rowStatus;
if ( FAILED(sc) ) { vqDebugOut(( DEB_ERROR, "IRowset::ReleaseRows returned 0x%x\n", sc )); continue; }
if ( DB_NULL_HROW != rghRows[cReleased] ) _RowManager.Release( rghRows[cReleased] );
cSuccessfullyReleased++; }
if ( fNotify ) { ULONG cRowsToNotify = 0; for (ULONG i=0; i<cRows; i++) if ( 0 == pRefCounts[i] && DBROWSTATUS_S_OK == pRowStatus[i] ) cRowsToNotify++;
if (cRowsToNotify) { XGrowable<HROW,20> xrghRows(cRowsToNotify);
for (cRowsToNotify=0, i=0; i<cRows; i++) if ( 0 == pRefCounts[i] && DBROWSTATUS_S_OK == pRowStatus[i] ) { xrghRows[cRowsToNotify] = rghRows[i]; cRowsToNotify++; }
_xChildNotify->OnRowChange( cRowsToNotify, xrghRows.Get(), DBREASON_ROW_RELEASE, DBEVENTPHASE_DIDEVENT, TRUE); } } } CATCH( CException, e ) { sc = e.GetErrorCode(); vqDebugOut(( DEB_ERROR, "CDistributedRowset::ReleaseRows -- caught 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_IRowset);
if (cSuccessfullyReleased == cReleased) return S_OK; else if (cSuccessfullyReleased > 0) sc = DB_S_ERRORSOCCURRED; else sc = DB_E_ERRORSOCCURRED;
return _DBErrorObj.PostHResult(sc, IID_IRowset); }
//
// IColumnsInfo methods
//
//+---------------------------------------------------------------------------
//
// Function: IsEqual, private
//
// Arguments: [col1] -- First DBID
// [col2] -- Second DBID
//
// Returns: TRUE if col1 == col2
//
// History: 03-Apr-95 KyleP Created.
//
//----------------------------------------------------------------------------
BOOL IsEqual( DBID const & col1, DBID const & col2 ) { if ( col1.eKind != col2.eKind ) return( FALSE );
switch ( col1.eKind ) { case DBKIND_GUID_PROPID: return ( col1.uGuid.guid == col2.uGuid.guid && col1.uName.ulPropid == col2.uName.ulPropid );
case DBKIND_GUID_NAME: return( col1.uGuid.guid == col2.uGuid.guid && _wcsicmp( col1.uName.pwszName, col2.uName.pwszName ) == 0);
case DBKIND_NAME: return( _wcsicmp( col1.uName.pwszName, col2.uName.pwszName ) == 0 );
case DBKIND_PGUID_PROPID: return ( *col1.uGuid.pguid == *col2.uGuid.pguid && col1.uName.ulPropid == col2.uName.ulPropid );
case DBKIND_PGUID_NAME: return( *col1.uGuid.pguid == *col2.uGuid.pguid && _wcsicmp( col1.uName.pwszName, col2.uName.pwszName ) == 0 );
default: Win4Assert( !"Unknown eKind" ); return( FALSE ); }
Win4Assert( !"How did we get here?" ); return( TRUE ); }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::_GetFullColumnInfo, protected
//
// Synopsis: Returns basic info about columns in table.
//
// Arguments: [pcColumns] -- Count returned here.
// [ppColInfo] -- Array of column descriptors returned here.
// [ppwchInfo] -- Pointer to storage for all string values.
//
// History: 02-Mar-95 KyleP Created.
// 24-Jan-97 KrishnaN Modified to conform to 1.0 spec
//
// Notes: Since each child cursor was given the same initial query,
// we know all the columnid and column ordinals must be the
// same. Other fields will vary, and will need to be set
// to the most relaxed value from any child cursor.
//
// Need to have Ole DB error handling here because the underlying
// errors are being translated.
//
// This implementation returns all the columns including the columns
// that we added for sorting. The client will always call
// GetColumnInfo, which trims out these extra columns
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::_GetFullColumnInfo( DBORDINAL * pcColumns, DBCOLUMNINFO ** ppColInfo, WCHAR ** ppwchInfo) { _DBErrorObj.ClearErrorInfo();
if (0 == pcColumns || 0 == ppColInfo || 0 == ppwchInfo) { vqDebugOut((DEB_IERROR, "CDistributedRowset::GetColumnInfo: Invalid Argument(s)\n")); return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IColumnsInfo); } SCODE sc = S_OK;
//
// Get rid of cached copy, if any.
//
if ( 0 != _aColInfo ) { *pcColumns = _cColInfo; *ppColInfo = _aColInfo; *ppwchInfo = _awchInfo;
_aColInfo = 0; _awchInfo = 0; } else { *ppColInfo = 0; *ppwchInfo = 0; *pcColumns = 0; TRY { //
// Fetch from first child. We'll use this allocation for return, and
// modify fields as needed.
//
IColumnsInfo * pci = 0; sc = _aChild[0]->QueryInterface( IID_IColumnsInfo, (void **) &pci ); if ( FAILED(sc) ) { vqDebugOut(( DEB_ERROR, "CDistributedRowset: QI to IColumnsInfo returned 0x%x\n", sc )); THROW( CException(sc) ); } XInterface<IColumnsInfo> pColInfo(pci); DBCOLUMNINFO * paColTemp; WCHAR * pwchTemp; sc = pColInfo->GetColumnInfo( pcColumns, &paColTemp, &pwchTemp ); XCoMem<DBCOLUMNINFO> pBaseline(paColTemp); XCoMem<WCHAR> pBaseStr(pwchTemp); if ( FAILED(sc) ) { vqDebugOut(( DEB_ERROR, "IColumnsInfo::GetColumnInfo returned 0x%x\n", sc )); THROW( CException(sc) ); } //
// Now go through remaining cursors and adjust values. The same
// columns must be in the same ordinal positions, but some values
// (ex: cbMaxLength) may differ between providers.
//
// Any string data is just taken from the first returned value.
//
for ( unsigned i = 1; i < _cChild; i++ ) { sc = _aChild[i]->QueryInterface( IID_IColumnsInfo, (void **) &pci ); if ( FAILED(sc) ) { vqDebugOut(( DEB_ERROR, "CDistributedRowset: QI to IColumnsInfo returned 0x%x\n", sc )); THROW( CException(sc) ); } XInterface<IColumnsInfo> pColInfo(pci); DBCOLUMNINFO * pTempCols = 0; WCHAR * pwchTemp2 = 0; DBORDINAL cCols; sc = pColInfo->GetColumnInfo( &cCols, &pTempCols, &pwchTemp2 ); if ( FAILED(sc) ) { vqDebugOut(( DEB_ERROR, "IColumnsInfo::GetColumnInfo returned 0x%x\n", sc )); THROW( CException(sc) ); } XCoMem<DBCOLUMNINFO> pCols( pTempCols ); XCoMem<WCHAR> pStr( pwchTemp2 ); if ( cCols != *pcColumns ) { Win4Assert( cCols == *pcColumns ); THROW( CException( E_FAIL ) ); } //
// Review all columns.
//
for ( unsigned j = 0; j < cCols; j++ ) { //
// DBID has to match.
//
if ( !IsEqual( pBaseline[j].columnid, pCols[j].columnid ) ) { Win4Assert( !"Mismatched columns" ); THROW( CException( E_FAIL ) ); } //
// pwszName should be returned if all names are the same.
//
if ( 0 != pBaseline[j].pwszName && 0 != pCols[j].pwszName && wcscmp( pBaseline[j].pwszName, pCols[j].pwszName ) != 0 ) pBaseline[j].pwszName = 0; //
// iOrdinal must match.
//
if ( pBaseline[j].iOrdinal != pCols[j].iOrdinal ) { Win4Assert( !"Mismatched columns" ); THROW( CException( E_FAIL ) ); } //
// If dwType doesn't match, we go to DBTYPE_VARIANT
//
if ( pBaseline[j].wType != pCols[j].wType ) pBaseline[j].wType = DBTYPE_VARIANT; //
// Take the maximum cbMaxLength, except for bookmark where we sum
// the lengths.
//
Win4Assert( 0 == (pBaseline[j].dwFlags & DBCOLUMNFLAGS_ISBOOKMARK) || ( pBaseline[j].ulColumnSize == pCols[j].ulColumnSize && pBaseline[j].dwFlags & DBCOLUMNFLAGS_ISFIXEDLENGTH ) ); if ( pBaseline[j].dwFlags & DBCOLUMNFLAGS_ISBOOKMARK && i == (_cChild - 1) ) { //
// Last pass through, add room for child id and give room
// to store ulColumnSize for *each* child.
//
pBaseline[j].ulColumnSize *= _cChild; pBaseline[j].ulColumnSize += sizeof(ULONG); } else if ( pBaseline[j].ulColumnSize < pCols[j].ulColumnSize ) pBaseline[j].ulColumnSize = pCols[j].ulColumnSize; if ( pBaseline[j].wType == DBTYPE_NUMERIC ) { //
// Precision and scale have to match?
//
if ( pBaseline[j].bPrecision != pCols[j].bPrecision || pBaseline[j].bScale != pCols[j].bScale ) { Win4Assert( !"Mismatched columns" ); THROW( CException( E_FAIL ) ); } } //
// All the conditions in dwFlags are positive (e.g. IsXXX), so the
// final flag is the bitwise and of all children.
//
#if CIDBG == 1
if ( pBaseline[j].dwFlags != pCols[j].dwFlags ) { vqDebugOut(( DEB_WARN, "GetColumnInfo: Mismatched dwFlags 0x%x, 0x%x\n", pBaseline[j].dwFlags, pCols[j].dwFlags )); } #endif // CIDBG == 1
pBaseline[j].dwFlags &= pCols[j].dwFlags; } // for each column
} // for each cursor
*ppColInfo = pBaseline.Acquire(); // Allow memory to pass back to client.
*ppwchInfo = pBaseStr.Acquire(); } CATCH( CException, e ) { sc = e.GetErrorCode(); if ( sc != E_INVALIDARG && sc != E_OUTOFMEMORY ) { vqDebugOut(( DEB_ERROR, "CDistributedRowset::GetColumnInfo: bogus error code 0x%x\n", sc )); sc = E_FAIL; } } END_CATCH }
if (FAILED(sc)) _DBErrorObj.PostHResult(sc, IID_IColumnsInfo);
return( sc ); }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::GetColumnInfo, public
//
// Synopsis: Returns basic info about columns in table.
//
// Arguments: [pcColumns] -- Count returned here.
// [ppColInfo] -- Array of column descriptors returned here.
// [ppwchInfo] -- Pointer to storage for all string values.
//
// History: 27-Sep-98 VikasMan Created
//
// Notes: Calls _GetFullColumnInfo and then trims out the extra columns
// that we added for sorting
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::GetColumnInfo( DBORDINAL * pcColumns, DBCOLUMNINFO ** ppColInfo, WCHAR ** ppwchInfo) { SCODE sc = _GetFullColumnInfo( pcColumns, ppColInfo, ppwchInfo );
if ( SUCCEEDED( sc ) && pcColumns && *pcColumns ) { if ( *pcColumns < _cColumns ) return E_INVALIDARG;
Win4Assert( *pcColumns >= _cColumns );
*pcColumns = _cColumns; }
return( sc ); }
//
// IRowsetIdentity methods
//
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::IsSameRow, public
//
// Arguments: [hThisRow] -- First row.
// [hThatRow] -- Second row.
//
// Returns: S_OK if the rows are the same, else S_FALSE.
//
// History: 02-Mar-95 KyleP Created.
//
// Notes: Assumes child rowset(s) support DBROWSETFLAGS_TRUEIDENTITY.
//
// Notes: Need to have Ole DB error handling here because the underlying
// errors are being translated.
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::IsSameRow( HROW hThisRow, HROW hThatRow ) { _DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
TRY { HROW hrowChild1, hrowChild2;
int iChild1 = _RowManager.GetChildAndHROW( hThisRow, hrowChild1 ); int iChild2 = _RowManager.GetChildAndHROW( hThatRow, hrowChild2 );
if ( iChild1 == iChild2 && hrowChild1 == hrowChild2 ) sc = S_OK; else sc = S_FALSE; } CATCH( CException, e ) { sc = e.GetErrorCode(); vqDebugOut(( DEB_ERROR, "Exception 0x%x caught in CDistributedRowset::IsSameRow\n", sc )); } END_CATCH
if (FAILED(sc)) _DBErrorObj.PostHResult(sc, IID_IRowsetIdentity);
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::CDistributedRowset, public
//
// Synopsis: Initialize distributed rowset.
//
// Arguments: [pUnkOuter] -- outer unknown
// [ppMyUnk] -- OUT: on return, filled with pointer to my
// non-delegating IUnknown
// [aChild] -- Array of child rowsets.
// [cChild] -- Count of [aChild].
// [Props] -- Rowset properties
// [cColumns] -- Number of original columns
// [aAccessors] -- Bag of accessors which rowsets need to inherit
//
// History: 03-Apr-95 KyleP Created.
//
// Notes: Ownership of aChild is transferred.
//
//----------------------------------------------------------------------------
CDistributedRowset::CDistributedRowset( IUnknown * pUnkOuter, IUnknown ** ppMyUnk, IRowset ** aChild, unsigned cChild, CMRowsetProps const & Props, unsigned cColumns, CAccessorBag & aAccessors, CCIOleDBError & DBErrorObj) : #pragma warning(disable : 4355) // 'this' in a constructor
_aAccessors( (IUnknown *) (IRowset *)this ), _impIUnknown(this), #pragma warning(default : 4355) // 'this' in a constructor
_aChild( aChild ), _cChild( cChild ), _cColumns( cColumns ), _aColInfo( 0 ), _awchInfo( 0 ), _iColumnBookmark( -1 ), _cbBookmark( 0 ), _Props( Props ), _DBErrorObj( DBErrorObj ), _cMaxResults( _Props.GetMaxResults() ), _cFirstRows( _Props.GetFirstRows() ), _iCurrentRow( 0 ) { // NTRAID#DB-NTBUG9-84049-2000/07/31-dlee Distributed queries need to support MaxResults for scrollable sorted rowsets
Win4Assert( _cChild > 0 );
if (pUnkOuter) _pControllingUnknown = pUnkOuter; else _pControllingUnknown = (IUnknown * )&_impIUnknown;
//
// We need to get column info once in order to figure out which column (if any)
// is the bookmark column. We'll cache the column info in _aColInfo just in
// case the client also needs it.
//
SCODE sc = _GetFullColumnInfo( &_cColInfo, &_aColInfo, &_awchInfo );
if ( FAILED(sc) ) { vqDebugOut(( DEB_ERROR, "CDistributedRowset: Error 0x%x fetching column info\n", sc )); THROW( CException( sc ) ); }
for ( unsigned i = 0; i < _cColInfo; i++ ) { if ( _aColInfo[i].dwFlags & DBCOLUMNFLAGS_ISBOOKMARK ) { _iColumnBookmark = _aColInfo[i].iOrdinal; _cbBookmark = _aColInfo[i].ulColumnSize; break; } }
//
// GetBindings for each accessor in bag, and use them to create accessor
// in IRowset
//
// only CAccessors can be used by commands
CAccessorBase * pAccBase = (CAccessorBase *)aAccessors.First(); while ( 0 != pAccBase ) { DBCOUNTITEM cBindings; DBBINDING * rgBindings; DBACCESSORFLAGS dwAccessorFlags;
SCODE sc = pAccBase->GetBindings(&dwAccessorFlags, &cBindings, &rgBindings); if (FAILED(sc)) THROW(CException(sc)); HACCESSOR hAccessor; sc = CreateAccessor(dwAccessorFlags, cBindings, rgBindings, 0, &hAccessor, 0); CoTaskMemFree(rgBindings); //cleanup from GetBindings
if (FAILED(sc)) THROW(CException(sc)); //
// inherited accessors are accessed through same hAccessor as original.
// Set parent of newly created accessor so that we can link the 2 copies.
// Client never knows the direct HACESSOR for the inherited accessor.
// All accessor methods check bag for an accessor with a match on
// the parent or the creator.
//
((CAccessorBase *)hAccessor)->SetParent(pAccBase); //
// Increment inheritor count for parent accessor
//
pAccBase->IncInheritors(); pAccBase = (CDistributedAccessor *)aAccessors.Next(); } #if CIDBG == 1
for ( i++; i < _cColInfo; i++ ) { Win4Assert( 0 == (_aColInfo[i].dwFlags & DBCOLUMNFLAGS_ISBOOKMARK) ); } #endif
*ppMyUnk = ((IUnknown *)&_impIUnknown); (*ppMyUnk)->AddRef(); }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::~CDistributedRowset, public
//
// Synopsis: Destructor
//
// History: 03-Apr-95 KyleP Created.
//
//----------------------------------------------------------------------------
CDistributedRowset::~CDistributedRowset() { unsigned ii;
if ( !_xChildNotify.IsNull() ) { _xChildNotify->OnRowsetChange( DBREASON_ROWSET_RELEASE, DBEVENTPHASE_DIDEVENT, TRUE); }
for ( ii = _xArrChildAsynchCP.Count(); ii > 0; ii-- ) { if ( _xArrChildAsynchCP[ii-1].GetPointer() ) { _xArrChildAsynchCP[ii-1]->Unadvise( _xArrAsynchAdvise[ii-1] ); } }
for ( ii = _xArrChildWatchCP.Count(); ii > 0; ii-- ) { if ( _xArrChildWatchCP[ii-1].GetPointer() ) { _xArrChildWatchCP[ii-1]->Unadvise( _xArrWatchAdvise[ii-1] ); } }
_xServerCPC.Free();
if ( 0 != _aColInfo ) CoTaskMemFree( _aColInfo );
if ( 0 != _awchInfo ) CoTaskMemFree( _awchInfo );
for ( unsigned i = 0; i < _cChild; i++ ) { if ( 0 != _aChild[i] ) _aChild[i]->Release(); }
delete [] _aChild; }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::CanConvertType, public
//
// Synopsis: Gives info. on the availability of type conversions.
//
// History: 29-Jan-97 KrishnaN Created.
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::CanConvert( DBTYPE wFromType, DBTYPE wToType, DBCONVERTFLAGS dwConvertFlags) { _DBErrorObj.ClearErrorInfo();
SCODE sc;
for (unsigned iChild = 0; iChild < _cChild; iChild++) {
IConvertType* pIConvertType = 0; sc = _aChild[iChild]->QueryInterface( IID_IConvertType, (void **) &pIConvertType );
if (SUCCEEDED( sc )) { sc = pIConvertType->CanConvert(wFromType, wToType, dwConvertFlags); pIConvertType->Release(); }
if (sc != S_OK) return _DBErrorObj.PostHResult(sc, IID_IConvertType); }
return S_OK; }
//
// Helper function used by GetStatus and GetStatusEx
//
inline void ParseQueryFillStatus( DWORD dwQueryFillStatus, DWORD & dwError, DWORD & dwBusy, DWORD & dwRefresh, DWORD & dwDone ) { if ( STAT_ERROR == dwQueryFillStatus ) { dwError = 1; dwDone = 0; } else if ( STAT_BUSY == dwQueryFillStatus ) { dwBusy = 1; dwDone = 0; } else if ( STAT_REFRESH == dwQueryFillStatus ) { dwRefresh = 1; dwDone = 0; } else if ( STAT_DONE == dwQueryFillStatus ) { dwDone &= 1; } }
//
// Helper function used by GetStatus and GetStatusEx
//
inline void SetQueryFillStatus( DWORD & dwStatus, DWORD dwError, DWORD dwBusy, DWORD dwRefresh, DWORD dwDone ) { if ( dwError ) { dwStatus |= STAT_ERROR; } else if ( dwBusy ) { dwStatus |= STAT_BUSY; } else if ( dwRefresh ) { dwStatus |= STAT_REFRESH; } else if ( dwDone ) { dwStatus |= STAT_DONE; } }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::GetStatus, public
//
// Synopsis: Return query status
//
// Arguments: [pdwStatus] -- pointer to where query status is returned
//
// Returns: SCODE error code
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::GetStatus( DWORD * pdwStatus ) { _DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
if ( pdwStatus ) { *pdwStatus = 0; DWORD dwBusy = 0; DWORD dwError = 0; DWORD dwRefresh = 0; DWORD dwDone = 1; XInterface<IRowsetQueryStatus> xIRowsetQueryStatus; for (unsigned iChild = 0; iChild < _cChild; iChild++) { scResult = _aChild[iChild]->QueryInterface( IID_IRowsetQueryStatus, xIRowsetQueryStatus.GetQIPointer() ); DWORD dwChildStatus; if ( SUCCEEDED( scResult ) ) scResult = xIRowsetQueryStatus->GetStatus( &dwChildStatus );
xIRowsetQueryStatus.Free(); if (scResult != S_OK) return scResult; // OR the query reliability status
*pdwStatus |= QUERY_RELIABILITY_STATUS(dwChildStatus); ParseQueryFillStatus( QUERY_FILL_STATUS( dwChildStatus ), dwError, dwBusy, dwRefresh, dwDone ); } // Now set the QUERY_FILL_STATUS. If we have at in the following order:
// ERROR > BUSY > REFRESH > DONE
SetQueryFillStatus( *pdwStatus, dwError, dwBusy, dwRefresh, dwDone ); }
return scResult; }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::GetStatusEx, public
//
// Synopsis: Return query status
//
// Arguments: [pdwStatus] - returns query status
// [pcFilteredDocuments] - # of documents filtered
// [pcDocumentsToFilter] - # of docsuments yet to filter
// [pdwRatioFinishedDenominator] - ratio finished denominator
// [pdwRatioFinishedNumerator] - ratio finished numerator
// [cbBmk] - # of bytes in pBmk
// [pBmk] - bookmark for piRowBmk
// [piRowBmk] - returns index of bookmark in table
// [pcRowsTotal] - current # of rows in table
//
// Returns: SCODE error code
//
//----------------------------------------------------------------------------
STDMETHODIMP CDistributedRowset::GetStatusEx( DWORD * pdwStatus, DWORD * pcFilteredDocuments, DWORD * pcDocumentsToFilter, DBCOUNTITEM * pdwRatioFinishedDenominator, DBCOUNTITEM * pdwRatioFinishedNumerator, DBBKMARK cbBmk, const BYTE * pBmk, DBCOUNTITEM * piRowBmk, DBCOUNTITEM * pcRowsTotal ) { _DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
if ( pdwStatus ) *pdwStatus = 0;
if ( pcFilteredDocuments ) *pcFilteredDocuments = 0;
*pcDocumentsToFilter = 0;
if ( pdwRatioFinishedDenominator ) *pdwRatioFinishedDenominator = 0;
if ( pdwRatioFinishedNumerator ) *pdwRatioFinishedNumerator = 0;
if ( piRowBmk ) *piRowBmk = 0;
if ( pcRowsTotal ) *pcRowsTotal = 0;
DWORD dwBusy = 0; DWORD dwError = 0; DWORD dwRefresh = 0; DWORD dwDone = 1;
ULONG iBmkCursor = 0; BYTE const * pChildBmk = NULL;
// Parse bookmark
if ( cbBmk != 0 ) { iBmkCursor = *( (ULONG*)pBmk ); pChildBmk = pBmk + sizeof( ULONG ) + ( iBmkCursor * sizeof( ULONG ) ); cbBmk = sizeof( ULONG ); }
double dRatio = 0;
XInterface<IRowsetQueryStatus> xIRowsetQueryStatus; for (unsigned iChild = 0; iChild < _cChild; iChild++) { scResult = _aChild[iChild]->QueryInterface( IID_IRowsetQueryStatus, xIRowsetQueryStatus.GetQIPointer() ); DWORD dwChildStatus; DWORD cFilteredDocuments; DWORD cDocumentsToFilter; DBCOUNTITEM dwRatioFinishedDenominator; DBCOUNTITEM dwRatioFinishedNumerator; DBCOUNTITEM iRowBmk; DBCOUNTITEM cRowsTotal;
if ( SUCCEEDED( scResult ) ) { scResult = xIRowsetQueryStatus->GetStatusEx( &dwChildStatus, &cFilteredDocuments, &cDocumentsToFilter, &dwRatioFinishedDenominator, &dwRatioFinishedNumerator, ( cbBmk > 0 && iBmkCursor == iChild ) ? cbBmk : 0, ( cbBmk > 0 && iBmkCursor == iChild ) ? pChildBmk : 0, &iRowBmk, &cRowsTotal ); } xIRowsetQueryStatus.Free();
if (scResult != S_OK) { return scResult; }
// OR the query reliability status
if ( pdwStatus ) { *pdwStatus |= QUERY_RELIABILITY_STATUS(dwChildStatus); }
ParseQueryFillStatus( QUERY_FILL_STATUS( dwChildStatus ), dwError, dwBusy, dwRefresh, dwDone );
if ( pcFilteredDocuments ) *pcFilteredDocuments += cFilteredDocuments;
if ( pcDocumentsToFilter ) *pcDocumentsToFilter += cDocumentsToFilter;
if ( dwRatioFinishedDenominator ) { dRatio += ( (double)dwRatioFinishedNumerator / (double)dwRatioFinishedDenominator ); }
if ( pcRowsTotal ) *pcRowsTotal += cRowsTotal; }
DWORD dwNum = 0; DWORD dwDen = 0;
if ( dRatio ) { Win4Assert( _cChild );
dRatio /= _cChild; dwDen = 1;
while ( dRatio < 1.0 ) { dRatio *= 10; dwDen *= 10; } dwNum = (DWORD)dRatio; }
if ( pdwRatioFinishedDenominator ) *pdwRatioFinishedDenominator = dwNum;
if ( pdwRatioFinishedNumerator ) *pdwRatioFinishedNumerator = dwDen;
// Now set the QUERY_FILL_STATUS. If we have at in the following order:
// ERROR > BUSY > REFRESH > DONE
if ( pdwStatus ) SetQueryFillStatus( *pdwStatus, dwError, dwBusy, dwRefresh, dwDone );
return scResult; }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::_SetupChildNotifications, private
//
// Synopsis: Set up connection points to receive notifications from the
// child rowsets
//
// Arguments: fAsynchOnly - TRUE if Asynch only,
// FLASE if Asynch and Watchable
//
// Returns: void
//
// History: 03 Sep 1998 VikasMan Created
//
//----------------------------------------------------------------------------
void CDistributedRowset::_SetupChildNotifications( BOOL fAsynchOnly ) { SCODE sc; XInterface<IConnectionPointContainer> xChildCPC;
// various arrays for the child rowsets
_xArrAsynchAdvise.Init( _cChild ); _xArrChildAsynchCP.Init( _cChild );
if ( !fAsynchOnly ) { _xArrWatchAdvise.Init( _cChild ); _xArrChildWatchCP.Init( _cChild ); }
_xArrChildRowsetWatchRegion.Init( _cChild );
for (unsigned iChild = 0; iChild < _cChild; iChild++) { sc = _aChild[iChild]->QueryInterface( IID_IConnectionPointContainer, xChildCPC.GetQIPointer() ); if ( SUCCEEDED( sc ) ) { sc = xChildCPC->FindConnectionPoint( IID_IDBAsynchNotify, (IConnectionPoint**)_xArrChildAsynchCP[iChild].GetQIPointer() );
if (SUCCEEDED(sc)) { sc = _xArrChildAsynchCP[iChild]->Advise( (IUnknown *)(void*)(_xChildNotify.GetPointer()), &_xArrAsynchAdvise[iChild] ); }
if ( !fAsynchOnly ) { sc = xChildCPC->FindConnectionPoint( IID_IRowsetWatchNotify, (IConnectionPoint**)_xArrChildWatchCP[iChild].GetQIPointer() ); if (SUCCEEDED(sc)) { sc = _xArrChildWatchCP[iChild]->Advise( (IUnknown *)(void*)(_xChildNotify.GetPointer()), &_xArrWatchAdvise[iChild] ); } }
xChildCPC.Free();
_aChild[iChild]->QueryInterface( IID_IRowsetWatchRegion, _xArrChildRowsetWatchRegion[iChild].GetQIPointer() ); } } }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::_SetupConnectionPointContainer, private
//
// Synopsis: Setups the connection point conatiner and other notification stuff
// for the distributed rowset. Handles both asynchronous ( for
// scrollable sorted rowset ) and synchronous ( for sequential rowset )
// cases.
//
// Arguments: pRowset - Rowset for which notifications are to be handled
// ppiuk - IConnectionPointContainer interface is returned here
//
// Returns: SCODE
//
// History: 22 Sep 1998 VikasMan Created
//
//----------------------------------------------------------------------------
SCODE CDistributedRowset::_SetupConnectionPointContainer( IRowset * pRowset, VOID * * ppiuk ) { SCODE sc = S_OK;
BOOL fWatchable = (_Props.GetPropertyFlags() & eWatchable) != 0; BOOL fAsynchronous = (_Props.GetPropertyFlags() & eAsynchronous) != 0;
if ( _xServerCPC.IsNull() ) { TRY { _xServerCPC.Set( new CConnectionPointContainer( fAsynchronous ? 3 : 1, * ((IUnknown *) pRowset), _DBErrorObj ) );
// Create an instance of CDistributedRowsetWatchNotify which acts as
// sink for the child rowsets and connection point for this rowset's clients
_xChildNotify.Set( new CDistributedRowsetWatchNotify( pRowset, _cChild ) ); if ( fWatchable || fAsynchronous ) { // set up to recieve notifications from all child rowsets
_SetupChildNotifications( !fWatchable ); }
_xChildNotify->AddConnectionPoints( _xServerCPC.GetPointer(), fAsynchronous, fWatchable );
} CATCH( CException, e ) { sc = GetOleError( e ); } END_CATCH; }
if ( S_OK == sc ) { Win4Assert( _xServerCPC.GetPointer() ); *ppiuk = (void *) (IConnectionPointContainer *) _xServerCPC.GetPointer(); }
return sc; }
//+---------------------------------------------------------------------------
//
// Member: CDistributedRowset::GetNextRows, public
//
// Synopsis: Calls _GetNextRows(which is overridden by derived classes)
// and handles notifications
//
// Arguments: [hChapter] -- Chapter
// [cRowsToSkip] -- Skip this many rows before beginning.
// [cRows] -- Try to fetch this many rows.
// [pcRowsObtained] -- Actually fetched this many.
// [pphRows] -- Store HROWs here. Allocate memory if
// [pphRows] is zero.
//
// History: 22-Sep-98 VikasMan Created.
//
//----------------------------------------------------------------------------
SCODE CDistributedRowset::GetNextRows( HCHAPTER hChapter, DBROWOFFSET cRowsToSkip, DBROWCOUNT cRows, DBCOUNTITEM * pcRowsObtained, HROW * * pphRows ) { SCODE scResult = S_OK; BOOL fNotified = FALSE;
TRY { Win4Assert( pcRowsObtained );
*pcRowsObtained = 0;
DBROWCOUNT cRowLimit = _cFirstRows > 0 ? _cFirstRows : _cMaxResults;
// check for max rows
if ( cRowLimit ) { if ( _iCurrentRow + cRowsToSkip > cRowLimit ) { return DB_S_ROWLIMITEXCEEDED; }
if ( _iCurrentRow + cRowsToSkip + cRows > cRowLimit ) { cRows = cRowLimit - (_iCurrentRow + cRowsToSkip);
if ( 0 == cRows ) { return DB_S_ROWLIMITEXCEEDED; } } }
if ( !_xChildNotify.IsNull() ) { scResult = _xChildNotify->OnRowsetChange( DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_OKTODO, FALSE); if ( S_FALSE == scResult ) THROW(CException(DB_E_CANCELED)); fNotified = TRUE; } scResult = _GetNextRows( hChapter, cRowsToSkip, cRows, pcRowsObtained, pphRows );
if ( SUCCEEDED( scResult ) && *pcRowsObtained ) { _iCurrentRow += *pcRowsObtained; }
if ( fNotified ) { if ( SUCCEEDED(scResult) ) { if ( *pcRowsObtained != 0 ) { _xChildNotify->OnRowChange( *pcRowsObtained, *pphRows, DBREASON_ROW_ACTIVATE, DBEVENTPHASE_DIDEVENT, TRUE); } _xChildNotify->OnRowsetChange( DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_DIDEVENT, TRUE); } } } CATCH( CException, e ) { scResult = GetOleError(e); } END_CATCH;
if ( fNotified && FAILED(scResult) ) { _xChildNotify->OnRowsetChange( DBREASON_ROWSET_FETCHPOSITIONCHANGE, DBEVENTPHASE_FAILEDTODO, TRUE); } return scResult; }
|