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.
2171 lines
70 KiB
2171 lines
70 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
}
|
|
|