windows-nt/Source/XPSP1/NT/inetsrv/query/bigtable/rowset.cxx

3153 lines
92 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 2000.
//
// File: rowset.cxx
//
// Contents: OLE DB IRowset implementation for file stores.
// Runs entirely in user space at the client machine.
//
// Classes: CRowset
//
// History: 07 Nov 94 AlanW Created
// 07 May 97 KrishnaN Added Ole-DB error support
//
//--------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <initguid.h> // needed for IServiceProperties
#include <rowset.hxx>
#include <query.hxx>
#include <rownotfy.hxx>
#include <tgrow.hxx>
#include "tabledbg.hxx"
const unsigned MAX_ROW_FETCH = 1000;
inline DBROWSTATUS ScodeToRowstatus( SCODE sc )
{
switch (sc)
{
case S_OK:
return DBROWSTATUS_S_OK;
case E_INVALIDARG:
case DB_E_BADBOOKMARK:
return DBROWSTATUS_E_INVALID;
case E_OUTOFMEMORY:
return DBROWSTATUS_E_OUTOFMEMORY;
default:
tbDebugOut(( DEB_ERROR, "ScodeToRowStatus: missing conversion for %x\n", sc ));
Win4Assert( FAILED( sc ) );
return DBROWSTATUS_E_INVALID;
}
}
// Rowset object Interfaces that support Ole DB error objects
static const IID * apRowsetErrorIFs[] =
{
&IID_IAccessor,
&IID_IChapteredRowset,
&IID_IColumnsInfo,
&IID_IColumnsRowset,
&IID_IConnectionPointContainer,
&IID_IConvertType,
&IID_IDBAsynchStatus,
&IID_IRowset,
//&IID_IRowsetAsynch,
&IID_IRowsetIdentity,
&IID_IRowsetInfo,
&IID_IRowsetLocate,
&IID_IRowsetQueryStatus,
//&IID_IRowsetResynch,
&IID_IRowsetScroll,
//&IID_IRowsetUpdate,
&IID_IRowsetWatchAll,
&IID_IRowsetWatchRegion,
//&IID_ISupportErrorInfo,
&IID_IServiceProperties,
};
static const ULONG cRowsetErrorIFs = sizeof(apRowsetErrorIFs)/sizeof(apRowsetErrorIFs[0]);
//+---------------------------------------------------------------------------
//
// Member: CRowset::CRowset, public
//
// Synopsis: Creates a locally accessible Table
//
// Arguments: [pUnkOuter] - Outer unknown
// [ppMyUnk] - OUT: filled in with pointer to non-delegated
// IUnknown on return
// [cols] - A reference to the output column set
// [pidmap] - a pid mapper for column IDs and names in cols
// [rQuery] - A reference to an instantiated query
// [rControllingQuery] - OLE controlling unknown (IQuery)
// [fIsCategorized] - TRUE if not the highest-level rowset
// [xProps] - Rowset properties, indicates special semantics,
// such as sequential cursor, use CI for prop
// queries.
// [hCursor] - table cursor handle.
// [aAccessors] -- Bag of accessors which rowsets need to inherit
//
// Notes: Ownership of the output column set may be transferred to
// the table cursor.
//
//----------------------------------------------------------------------------
CRowset::CRowset(
IUnknown * pUnkOuter,
IUnknown ** ppMyUnk,
CColumnSet const & cols,
CPidMapperWithNames const & pidmap,
PQuery & rQuery,
IUnknown & rControllingQuery,
BOOL fIsCategorized,
XPtr<CMRowsetProps> & xProps,
ULONG hCursor,
CAccessorBag & aAccessors,
IUnknown * pUnkCreator )
:_rQuery( rQuery ),
_xProperties( xProps.Acquire() ),
_hCursor( hCursor ),
_pRowBufs(0),
_pConnectionPointContainer( 0 ),
_pRowsetNotification( 0 ),
_pAsynchNotification( 0 ),
_fForwardOnly( (_xProperties->GetPropertyFlags() & eLocatable) == 0),
#pragma warning(disable : 4355) // 'this' in a constructor
_ColumnsInfo( cols,
pidmap,
_DBErrorObj,
* ((IUnknown *) (IRowsetScroll *) this),
_fForwardOnly ),
_aAccessors( (IUnknown *) (IRowset *)this ),
_DBErrorObj( * ((IUnknown *) (IRowset *) this), _mutex ),
_impIUnknown(rControllingQuery, this),
#pragma warning(default : 4355) // 'this' in a constructor
_PropInfo(),
_fIsCategorized( fIsCategorized ),
_fExtendedTypes( (_xProperties->GetPropertyFlags() & eExtendedTypes) != 0 ),
_fHoldRows( (_xProperties->GetPropertyFlags() & eHoldRows) != 0 ),
_fAsynchronous( (_xProperties->GetPropertyFlags() & eAsynchronous) != 0),
_pRelatedRowset( 0 ),
_pChapterRowbufs( 0 )
{
Win4Assert(_hCursor != 0);
if (_hCursor == 0)
THROW(CException(E_NOINTERFACE));
if (pUnkOuter)
_pControllingUnknown = pUnkOuter;
else
_pControllingUnknown = (IUnknown * )&_impIUnknown;
_DBErrorObj.SetInterfaceArray(cRowsetErrorIFs, apRowsetErrorIFs);
ULONG obRowRefcount, obRowWorkId;
ULONG obChaptRefcount, obChaptId;
_ColumnsInfo.SetColumnBindings( rQuery, _hCursor,
obRowRefcount, obRowWorkId,
obChaptRefcount, obChaptId );
//
// 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 = (CAccessor *)aAccessors.Next();
}
_pRowBufs = new CRowBufferSet( _fForwardOnly,
obRowRefcount,
obRowWorkId,
obChaptRefcount,
obChaptId );
*ppMyUnk = ((IUnknown *)&_impIUnknown);
// can't fail after this or _pRowBufs will leak
(*ppMyUnk)->AddRef();
rQuery.AddRef();
if ( 0 != pUnkCreator )
{
_xUnkCreator.Set( pUnkCreator );
_xUnkCreator->AddRef();
}
} //CRowset
//+---------------------------------------------------------------------------
//
// Member: CRowset::~CRowset, public
//
// Synopsis: Destroy the rowset and its component objects
//
//----------------------------------------------------------------------------
CRowset::~CRowset()
{
Win4Assert( _impIUnknown._ref == 0 );
Win4Assert( _hCursor != 0 );
delete _pRowBufs;
// free cursor will fail if the pipe is broken
TRY
{
if ( !_pRowsetNotification.IsNull() )
_pRowsetNotification->OnRowsetChange( this,
DBREASON_ROWSET_RELEASE,
DBEVENTPHASE_DIDEVENT,
TRUE);
_rQuery.FreeCursor( _hCursor );
}
CATCH( CException, e )
{
}
END_CATCH;
if ( !_pRowsetNotification.IsNull() )
_pRowsetNotification->StopNotifications();
delete _pConnectionPointContainer;
_rQuery.Release();
} //~CRowset
//+-------------------------------------------------------------------------
//
// Member: CRowset::RealQueryInterface, public
//
// Synopsis: Get a reference to another interface on the cursor
//
// Notes: ref count is incremented inside QueryInterface
//
//--------------------------------------------------------------------------
SCODE CRowset::RealQueryInterface(
REFIID ifid,
void * *ppiuk )
{
SCODE sc = S_OK;
*ppiuk = 0;
// note -- IID_IUnknown covered in QueryInterface
if ( IID_IRowset == ifid )
{
*ppiuk = (void *) (IRowset *) this;
}
else if ( IID_IAccessor == ifid )
{
*ppiuk = (void *) (IAccessor *) this;
}
else if ( IID_IRowsetInfo == ifid )
{
*ppiuk = (void *) (IRowsetInfo *) this;
}
else if ( IID_IColumnsInfo == ifid )
{
*ppiuk = (void *) (IColumnsInfo *) &_ColumnsInfo;
}
else if (IID_ISupportErrorInfo == ifid)
{
*ppiuk = (void *) ((IUnknown *) (ISupportErrorInfo *) &_DBErrorObj);
}
else if ( IID_IConvertType == ifid )
{
*ppiuk = (void *) (IConvertType *) this;
}
#if 0 // NEWFEATURE - not implemented now.
else if ( IID_IColumnsRowset == ifid )
{
*ppiuk = (void *) (IColumnsRowset *) &_ColumnsInfo;
}
#endif // 0 // NEWFEATURE - not implemented now.
else if ( IID_IRowsetQueryStatus == ifid )
{
*ppiuk = (void *) (IRowsetQueryStatus *) this;
}
else if ( IID_IServiceProperties == ifid )
{
*ppiuk = (void *) (IServiceProperties *) this;
}
else if ( IID_IConnectionPointContainer == ifid )
{
// Watch notifications are only supported over the
// bottom-most of a hierarchical rowset (the one with real rows)
BOOL fWatchable = ! _pRowBufs->IsChaptered() &&
(_xProperties->GetPropertyFlags() & eWatchable) != 0;
if ( 0 == _pConnectionPointContainer )
{
TRY
{
XPtr<CConnectionPointContainer> xCPC(
new CConnectionPointContainer(
_fAsynchronous ? 3 : 1,
* ((IUnknown *) (IRowsetScroll *) this),
_DBErrorObj) );
if (_fAsynchronous)
{
_pAsynchNotification =
new CRowsetAsynchNotification(
_rQuery, _hCursor, this, _DBErrorObj,
fWatchable );
_pRowsetNotification.Set( _pAsynchNotification );
}
else
{
_pRowsetNotification.Set ( new CRowsetNotification( ) );
}
_pRowsetNotification->AddConnectionPoints( xCPC.GetPointer() );
_pConnectionPointContainer = xCPC.Acquire();
}
CATCH( CException, e )
{
sc = GetOleError( e );
}
END_CATCH;
}
if ( S_OK == sc )
{
Win4Assert( 0 != _pConnectionPointContainer );
*ppiuk = (void *) (IConnectionPointContainer *)
_pConnectionPointContainer;
}
}
else if (! _fForwardOnly)
{
if ( IID_IRowsetScroll == ifid )
{
*ppiuk = (void *) (IRowsetScroll *) this;
}
else if ( IID_IRowsetExactScroll == ifid )
{
*ppiuk = (void *) (IRowsetExactScroll *) this;
}
else if ( IID_IRowsetLocate == ifid )
{
*ppiuk = (void *) (IRowsetLocate *) this;
}
else if ( IID_IRowsetIdentity == ifid )
{
*ppiuk = (void *) (IRowsetIdentity *) this;
}
else if ( IID_IChapteredRowset == ifid )
{
Win4Assert( (_pChapterRowbufs != 0) == _fIsCategorized );
if (_pChapterRowbufs)
{
*ppiuk = (void *) (IChapteredRowset *) this;
}
}
else if ( _fAsynchronous )
{
if ( IID_IDBAsynchStatus == ifid )
{
*ppiuk = (void *) (IDBAsynchStatus *) this;
}
else if ( IID_IRowsetAsynch == ifid )
{
*ppiuk = (void *) (IRowsetAsynch *) this;
}
else if (IID_IRowsetWatchRegion == ifid)
{
*ppiuk = (void *) (IRowsetWatchRegion *) this;
}
else if (IID_IRowsetWatchAll == ifid)
{
*ppiuk = (void *) (IRowsetWatchAll *) this;
}
}
}
if ( 0 == *ppiuk )
{
sc = E_NOINTERFACE;
}
return sc;
}
//+-------------------------------------------------------------------------
//
// Member: CRowset::CImpIUnknown::AddRef, public
//
// Synopsis: Reference the cursor.
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CRowset::CImpIUnknown::AddRef(void)
{
long ref = InterlockedIncrement( &_ref );
if ( ref > 0 )
_rControllingQuery.AddRef();
return ref ;
} //AddRef
//+-------------------------------------------------------------------------
//
// Member: CRowset::CImpIUnknown::Release, public
//
// Synopsis: De-Reference the cursor.
//
// Effects: If the ref count goes to 0 then the cursor is deleted.
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CRowset::CImpIUnknown::Release(void)
{
long ref = InterlockedDecrement( &_ref );
if ( ref >= 0 )
_rControllingQuery.Release(); // may cause a delete of the rowset
return ref;
} //Release
//+---------------------------------------------------------------------------
//
// Member: CRowset::GetProperties, public
//
// Synopsis: Return information about the capabilities of the rowset
//
// Arguments: [cPropertyIDSets] - number of property ID sets or zero
// [rgPropertyIDSets] - array of desired property ID sets or NULL
// [pcPropertySets] - number of DBPROPSET structures returned
// [prgPropertySets] - array of returned DBPROPSET structures
//
//
// Returns: SCODE
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::GetProperties(
const ULONG cPropertyIDSets,
const DBPROPIDSET rgPropertyIDSets[],
ULONG * pcPropertySets,
DBPROPSET ** prgPropertySets)
{
_DBErrorObj.ClearErrorInfo();
if ( (0 != cPropertyIDSets && 0 == rgPropertyIDSets) ||
0 == pcPropertySets ||
0 == prgPropertySets )
{
if (pcPropertySets)
*pcPropertySets = 0;
if (prgPropertySets)
*prgPropertySets = 0;
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetInfo);
}
SCODE scResult = S_OK;
*pcPropertySets = 0;
*prgPropertySets = 0;
TRY
{
//
// Update ROWSETQUERYSTATUS property
//
DWORD dwStatus;
_rQuery.GetQueryStatus( _hCursor, dwStatus );
_xProperties->SetValLong( CMRowsetProps::eid_DBPROPSET_MSIDXS_ROWSET_EXT,
CMRowsetProps::eid_MSIDXSPROPVAL_ROWSETQUERYSTATUS,
dwStatus );
scResult = _xProperties->GetProperties( cPropertyIDSets,
rgPropertyIDSets,
pcPropertySets,
prgPropertySets );
if (FAILED(scResult))
_DBErrorObj.PostHResult(scResult, IID_IRowsetInfo);
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetInfo);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::RatioFinished, public
//
// Synopsis: Returns the completion status of the query.
//
// Arguments: [pulDenominator] - on return, denominator of fraction
// [pulNumerator] - on return, numerator of fraction
// [pcRows] - on return, number of rows
// [pfNewRows] - on return, TRUE if new rows in the table
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::RatioFinished(
DBCOUNTITEM * pulDenominator,
DBCOUNTITEM * pulNumerator,
DBCOUNTITEM * pcRows,
BOOL * pfNewRows) /*const*/
{
_DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
if (0 == pulDenominator ||
0 == pulNumerator ||
0 == pcRows ||
0 == pfNewRows)
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetAsynch);
TRY
{
*pcRows = 0;
_rQuery.RatioFinished( _hCursor,
*pulDenominator,
*pulNumerator,
*pcRows,
*pfNewRows );
#if CIDBG
if ( _fForwardOnly )
Win4Assert( *pulDenominator == *pulNumerator );
else
Win4Assert( *pulDenominator >= *pulNumerator );
#endif // CIDBG
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetAsynch);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::AddRefRows, public
//
// Synopsis: Increment the ref. count of a set of row handles
//
// Arguments: [cRows] -- Number of row handles in rghRows
// [rghRows] -- Array of HROWs to be ref. counted
// [rgRefCounts] -- Remaining reference counts on rows (optional)
// [rgRowStatus] -- Status for each row (optional)
//
// Returns: SCODE, DB_E_BADROWHANDLE if a bad row handle is passed in.
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP CRowset::AddRefRows(
DBCOUNTITEM cRows,
const HROW rghRows [],
DBREFCOUNT rgRefCounts[],
DBROWSTATUS rgRowStatus[])
{
_DBErrorObj.ClearErrorInfo();
if (0 == _pRowBufs)
return _DBErrorObj.PostHResult(E_FAIL, IID_IRowset);
SCODE scResult = S_OK;
TRY
{
_pRowBufs->AddRefRows(cRows, rghRows, rgRefCounts, rgRowStatus);
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowset);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::ReleaseRows, public
//
// Synopsis: Release a set of row handles
//
// Arguments: [cRows] -- Number of row handles in rghRows
// [rghRows] -- Array of HROWs to be released
// [rgRowOptions] -- Reserved for future use (optional)
// [rgRefCounts] -- Remaining reference counts on rows (optional)
// [rgRowStatus] -- Status for each row (optional)
//
// Returns: SCODE, DB_E_BADROWHANDLE if a bad row handle is passed in.
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP CRowset::ReleaseRows(
DBCOUNTITEM cRows,
const HROW rghRows [],
DBROWOPTIONS rgRowOptions[],
DBREFCOUNT rgRefCounts[],
DBROWSTATUS rgRowStatus[])
{
_DBErrorObj.ClearErrorInfo();
if (0 == _pRowBufs)
return _DBErrorObj.PostHResult(E_FAIL, IID_IRowset);
// if (0 != rgRowOptions)
// return E_FAIL;
SCODE scResult = S_OK;
TRY
{
BOOL fNotify = FALSE;
ULONG * pRefCounts = rgRefCounts;
DBROWSTATUS * pRowStatus = rgRowStatus;
XArray<ULONG> xrgRefCounts;
XArray<DBROWSTATUS> xrgRowStatus;
if ( !_pRowsetNotification.IsNull() &&
_pRowsetNotification->IsNotifyActive() )
{
fNotify = TRUE;
if ( 0 == pRefCounts )
{
xrgRefCounts.Init( (unsigned) cRows);
pRefCounts = xrgRefCounts.GetPointer();
}
if ( 0 == pRowStatus )
{
xrgRowStatus.Init( (unsigned) cRows);
pRowStatus = xrgRowStatus.GetPointer();
}
}
scResult = _pRowBufs->ReleaseRows(cRows, rghRows, pRefCounts, pRowStatus);
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++;
}
_pRowsetNotification->OnRowChange( this,
cRowsToNotify,
xrghRows.Get(),
DBREASON_ROW_RELEASE,
DBEVENTPHASE_DIDEVENT,
TRUE);
}
}
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowset);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
//+---------------------------------------------------------------------------
//
// Macro: CheckCrowsArgs
//
// Synopsis: Check common error conditions on cRows and pcRowsObtained
// for GetRowsXxxx methods.
//
// Arguments: [cRows] -- Number of rows to return
// [pcRowsObtained] -- On return, number of rows actually
// fetched
//
// Returns: SCODE
//
// Notes: Needs to be a macro instead of an inline function because
// it returns from the calling method.
//
//--------------------------------------------------------------------------
#define CheckCrowsArgs(cRows, pcRowsObtained) \
if (0 == pcRowsObtained) \
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowset); \
*pcRowsObtained = 0; \
if (cRows == 0) \
return S_OK;
//+---------------------------------------------------------------------------
//
// Member: CRowset::GetRowsAt, public
//
// Synopsis: Fetch data starting at some starting bookmark
//
// Arguments: [hRegion] -- handle to watch region
// [hChapter] -- Chapter in a multiset cursor
// [cbBookmark] -- Size of bookmark for starting position
// [pBookmark] -- Pointer to bookmark for starting position
// [lRowsOffset] -- Number of row handles in rghRows
// [cRows] -- Number of rows to return
// [pcRowsObtained] -- On return, number of rows actually
// fetched
// [prghRows] -- Array of HROWs to be returned
//
// Returns: SCODE, E_INVALIDARG for bad parameters, DB_E_BADBOOKMARK
// if the starting bookmark is invalid
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP CRowset::GetRowsAt(
HWATCHREGION hRegion,
HCHAPTER hChapter,
DBBKMARK cbBookmark,
const BYTE* pBookmark,
DBROWOFFSET lRowsOffset,
DBROWCOUNT cRows,
DBCOUNTITEM * pcRowsObtained,
HROW * * prghRows
) {
_DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
CheckCrowsArgs( cRows, pcRowsObtained );
TRY
{
CI_TBL_BMK bmk = _MapBookmark(cbBookmark, pBookmark);
CI_TBL_CHAPT chapt = _MapChapter(hChapter);
CRowSeekAt rowSeek( hRegion, chapt, (LONG) lRowsOffset, bmk );
scResult = _FetchRows(rowSeek, cRows, pcRowsObtained, prghRows);
if (FAILED(scResult))
_DBErrorObj.PostHResult(scResult, IID_IRowsetLocate);
else if ( !_pRowsetNotification.IsNull() &&
*pcRowsObtained != 0 &&
_pRowsetNotification->IsNotifyActive() )
{
_pRowsetNotification->OnRowChange( this,
*pcRowsObtained,
*prghRows,
DBREASON_ROW_ACTIVATE,
DBEVENTPHASE_DIDEVENT,
TRUE);
}
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetLocate);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::GetRowsByBookmark, public
//
// Synopsis: Fetch data from a set of bookmarks
//
// Arguments: [hChapter] -- Chapter in a multiset cursor
// [cRows] -- Number of input bookmarks and rows to return
// [rgcbBookmark] -- Array of bookmark sizes
// [ppBookmarks] -- Array of pointers to bookmarks
// [rghRows] -- Array of HROWs returned
// [rgRowStatus] -- Array for per-row status (optional)
//
// Returns: SCODE, E_INVALIDARG for bad parameters, DB_E_BADBOOKMARK
// if the starting bookmark is invalid
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP CRowset::GetRowsByBookmark(
HCHAPTER hChapter,
DBCOUNTITEM cRows,
const DBBKMARK rgcbBookmark[],
const BYTE * ppBookmarks[],
HROW rghRows[],
DBROWSTATUS rgRowStatus[]
) {
_DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
if (0 == ppBookmarks ||
0 == rgcbBookmark ||
0 == rghRows)
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetLocate);
if (cRows == 0)
return scResult;
TRY
{
//
// Map the input bookmarks to work IDs. If we see an invalid
// bookmark, it will get turned into widInvalid, and its lookup
// will fail.
//
XArray<CI_TBL_BMK> paBmk( (unsigned) cRows );
for (unsigned i=0; i < cRows; i++)
{
DBROWSTATUS sc = _MapBookmarkNoThrow( rgcbBookmark[i],
ppBookmarks[i],
paBmk[i] );
}
CI_TBL_CHAPT chapt = _MapChapter(hChapter);
CRowSeekByBookmark rowSeek( chapt, (ULONG) cRows, paBmk.Acquire() );
DBCOUNTITEM cRowsObtained;
TRY
{
scResult = _FetchRows(rowSeek, cRows, &cRowsObtained, &rghRows);
}
CATCH( CException, e )
{
scResult = e.GetErrorCode();
}
END_CATCH
//
// Return the array of row statuses
//
unsigned cErrors = 0;
for (i=0; i < rowSeek.GetValidStatuses(); i++)
{
SCODE scTemp = rowSeek.GetStatus(i);
if (0 != rgRowStatus)
rgRowStatus[i] = ScodeToRowstatus( scTemp );
if (S_OK != scTemp)
{
//
// The HROW array returned by _FetchRows is compressed,
// skipping entries for rows that had errors. Insert
// a DB_NULL_HROW entry for this row.
//
if ( i != cRows-1 )
{
memmove( &rghRows[i+1], &rghRows[i], (unsigned) ((cRows-i)-1) * sizeof (HROW));
}
rghRows[i] = DB_NULL_HROW;
//
// If the returned error is DB_E_BADBOOKMARK,
// call MapBookmarkNoThrow again to distinguish
// E_INVALIDARG cases.
//
if (DB_E_BADBOOKMARK == scTemp && 0 != rgRowStatus)
{
CI_TBL_BMK bmkTemp;
DBROWSTATUS rsTemp = _MapBookmarkNoThrow(rgcbBookmark[i],
ppBookmarks[i],
bmkTemp);
if (rsTemp != DBROWSTATUS_S_OK)
{
rgRowStatus[i] = rsTemp;
}
}
cErrors++;
}
}
Win4Assert( rowSeek.GetValidStatuses() == cRows );
if (SUCCEEDED(scResult) && cErrors > 0)
scResult = (cErrors == cRows) ? DB_E_ERRORSOCCURRED :
DB_S_ERRORSOCCURRED;
if (FAILED(scResult))
_DBErrorObj.PostHResult(scResult, IID_IRowsetLocate);
else if ( !_pRowsetNotification.IsNull() &&
_pRowsetNotification->IsNotifyActive() )
_pRowsetNotification->OnRowChange( this,
cRows,
rghRows,
DBREASON_ROW_ACTIVATE,
DBEVENTPHASE_DIDEVENT,
TRUE);
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetLocate);
scResult = GetOleError(e);
Win4Assert(FAILED(scResult));
}
END_CATCH;
return scResult;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::Compare, public
//
// Synopsis: Compare two bookmarks
//
// Arguments: [hChapter] -- chapter
// [cbBookmark1] -- Size of first bookmark
// [pBookmark1] -- Pointer to first bookmark
// [cbBookmark2] -- Size of second bookmark
// [pBookmark2] -- Pointer to second bookmark
// [pdwComparison] - on return, hased value of bookmark
//
// Returns: SCODE, E_INVALIDARG if cbBookmark is zero or if pBookmark or
// pdwComparison is NULL, DB_E_BADBOOKMARK for other invalid
// bookmarks.
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP CRowset::Compare(
HCHAPTER hChapter,
DBBKMARK cbBookmark1,
const BYTE* pBookmark1,
DBBKMARK cbBookmark2,
const BYTE* pBookmark2,
DBCOMPARE * pdwComparison) /*const*/
{
_DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
if (0 == pdwComparison)
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetLocate);
TRY
{
ULONG dwHash1 = _MapBookmark(cbBookmark1, pBookmark1);
ULONG dwHash2 = _MapBookmark(cbBookmark2, pBookmark2);
//
// Set to non-comparable. This is used later to see if we've
// successfully determined the relative order.
//
*pdwComparison = DBCOMPARE_NOTCOMPARABLE;
if (dwHash1 == dwHash2)
{
*pdwComparison = DBCOMPARE_EQ;
}
else if ( 1 == cbBookmark1 || 1 == cbBookmark2 )
{
*pdwComparison = DBCOMPARE_NE;
}
else
{
CI_TBL_CHAPT chapt = _MapChapter(hChapter);
_rQuery.Compare( _hCursor,
chapt,
dwHash1,
dwHash2,
(DWORD) (*pdwComparison) );
}
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetLocate);
sc = GetOleError(e);
}
END_CATCH;
return sc;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::_MapChapter, private
//
// Synopsis: Map a chapter mark to a ULONG internal chapter mark.
//
// Arguments: [hChapter] -- handle of chapter
//
// Returns: Chapter as an I4
//
// Notes: A null chapter on a categorized rowset means to operate
// over the entire rowset, not an individual chapter.
//
//--------------------------------------------------------------------------
CI_TBL_CHAPT CRowset::_MapChapter(
HCHAPTER hChapter
) const
{
CI_TBL_CHAPT chapt = (CI_TBL_CHAPT) hChapter;
Win4Assert (DB_NULL_HCHAPTER == 0);
if ( !_fIsCategorized && DB_NULL_HCHAPTER != hChapter )
{
THROW( CException( DB_E_BADCHAPTER ));
}
return chapt;
} //_MapChapter
//+---------------------------------------------------------------------------
//
// Member: CRowset::_MapBookmarkNoThrow, private
//
// Synopsis: Return a 32 bit hash value for a particular bookmark.
// Don't throw on errors.
//
// Arguments: [cbBookmark] -- Size of bookmark
// [pBookmark] -- Pointer to bookmark
//
// Notes: For IRowsetLocate::Hash and IRowsetLocate::GetRowsByBookmark
// which want to continue processing on bookmark errors. Unlike
// _MapBookmark, DBBMK_FIRST and DBBMK_LAST are invalid.
//
// Returns: DBROWSTATUS, hash value (identity function, also the workid
// value for the table) is returned in rBmk
//
//--------------------------------------------------------------------------
DBROWSTATUS CRowset::_MapBookmarkNoThrow(
DBBKMARK cbBookmark,
const BYTE* pBookmark,
CI_TBL_BMK & rBmk) const
{
Win4Assert( !_fForwardOnly );
rBmk = widInvalid;
if (0 == cbBookmark || 0 == pBookmark)
return DBROWSTATUS_E_INVALID;
if (cbBookmark == 1)
{
if (*(BYTE *)pBookmark == DBBMK_FIRST ||
*(BYTE *)pBookmark == DBBMK_LAST ||
*(BYTE *)pBookmark == DBBMK_INVALID)
return DBROWSTATUS_E_INVALID;
else
return DBROWSTATUS_E_INVALID; //DB_E_BADBOOKMARK ???
}
else if (cbBookmark == sizeof (CI_TBL_BMK))
{
rBmk = *(UNALIGNED CI_TBL_BMK *) pBookmark;
if (rBmk == WORKID_TBLFIRST || rBmk == WORKID_TBLLAST)
return DBROWSTATUS_E_INVALID;
return DBROWSTATUS_S_OK;
}
return DBROWSTATUS_E_INVALID; //DB_E_BADBOOKMARK ???
} //_MapBookmarkNoThrow
//+---------------------------------------------------------------------------
//
// Member: CRowset::_MapBookmark, private
//
// Synopsis: Convert a bookmark into an internal form.
//
// Arguments: [cbBookmark] -- Size of bookmark
// [pBookmark] -- Pointer to bookmark
//
// Returns: ULONG, hash value (identity function, also the workid
// value for the table)
//
//--------------------------------------------------------------------------
CI_TBL_BMK CRowset::_MapBookmark(
DBBKMARK cbBookmark,
const BYTE* pBookmark) const
{
Win4Assert( !_fForwardOnly );
WORKID WorkID = widInvalid;
Win4Assert( sizeof WORKID == sizeof CI_TBL_BMK );
if (0 == cbBookmark || 0 == pBookmark)
THROW(CException(E_INVALIDARG));
if (cbBookmark == 1)
{
if (*(BYTE *)pBookmark == DBBMK_FIRST)
WorkID = WORKID_TBLFIRST;
else if (*(BYTE *)pBookmark == DBBMK_LAST)
WorkID = WORKID_TBLLAST;
}
else if (cbBookmark == sizeof (CI_TBL_BMK))
{
WorkID = *(UNALIGNED CI_TBL_BMK *) pBookmark;
}
if (WorkID == widInvalid)
THROW(CException(DB_E_BADBOOKMARK));
return WorkID;
} //_MapBookmark
//+---------------------------------------------------------------------------
//
// Member: CRowset::Hash, public
//
// Synopsis: Returns an array of 32 bit hash values for bookmarks
//
// Arguments: [hChapter] -- chapter
// [cBookmarks] -- # of bmks to hash
// [rgcbBM] -- Sizes of each bookmark
// [ppBM] -- Pointers to each bookmark
// [rgHashedValues] -- on return, hashed values of bookmarks
// [rgBookmarkStatus] -- per-bookmark status (optional)
//
// Returns: SCODE, E_INVALIDARG if any cbBookmark is zero,
// DB_E_BADBOOKMARK for other invalid bookmarks.
//
//--------------------------------------------------------------------------
STDMETHODIMP CRowset::Hash(
HCHAPTER hChapter,
DBBKMARK cBookmarks,
const DBBKMARK rgcbBM[],
const BYTE * ppBM[],
DBHASHVALUE rgHashedValues[],
DBROWSTATUS rgBookmarkStatus[]
)
{
_DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
ULONG cErrors = 0;
if (0 == rgcbBM || 0 == ppBM || 0 == rgHashedValues)
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetLocate);
TRY
{
CI_TBL_CHAPT chapt = _MapChapter(hChapter);
for (ULONG i = 0; i < cBookmarks; i++)
{
CI_TBL_BMK bmk;
DBROWSTATUS rs = _MapBookmarkNoThrow( rgcbBM[i], ppBM[i], bmk );
rgHashedValues[i] = bmk;
if (rs != DBROWSTATUS_S_OK)
{
rgHashedValues[i] = 0;
cErrors++;
}
if (0 != rgBookmarkStatus)
rgBookmarkStatus[i] = rs;
}
if (cErrors)
scResult = (cErrors == cBookmarks) ? DB_E_ERRORSOCCURRED :
DB_S_ERRORSOCCURRED;
if (FAILED(scResult))
_DBErrorObj.PostHResult(scResult, IID_IRowsetLocate);
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetLocate);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
} //Hash
//+---------------------------------------------------------------------------
//
// Member: CRowset::GetApproximatePosition, public
//
// Synopsis: Returns the approximate position of a bookmark
//
// Arguments: [hChapter] -- chapter
// [cbBookmark] -- size of bookmark
// [pBookmark] -- bookmark
// [pulPosition] -- return approx row number of bookmark
// [pulRows] -- returns approx # of rows in cursor or
// 1 + approx rows if not at quiescence
//
// Returns: SCODE - the status of the operation.
//
//--------------------------------------------------------------------------
STDMETHODIMP CRowset::GetApproximatePosition(
HCHAPTER hChapter,
DBBKMARK cbBookmark,
const BYTE * pBookmark,
DBCOUNTITEM * pulPosition,
DBCOUNTITEM * pulRows) /*const*/
{
_DBErrorObj.ClearErrorInfo();
Win4Assert( !_fForwardOnly );
SCODE sc = S_OK;
TRY
{
DBCOUNTITEM ulNumerator, ulDenominator;
CI_TBL_BMK bmk = WORKID_TBLFIRST;
if (cbBookmark != 0)
bmk = _MapBookmark(cbBookmark, pBookmark);
CI_TBL_CHAPT chapt = _MapChapter(hChapter);
_rQuery.GetApproximatePosition( _hCursor,
chapt,
bmk,
&ulNumerator,
&ulDenominator );
if (cbBookmark)
*pulPosition = ulNumerator;
if (pulRows)
*pulRows = ulDenominator;
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetScroll);
sc = GetOleError(e);
}
END_CATCH;
return sc;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::GetExactPosition, public
//
// Synopsis: Returns the exact position of a bookmark
//
// Arguments: [hChapter] -- chapter
// [cbBookmark] -- size of bookmark
// [pBookmark] -- bookmark
// [pulPosition] -- return approx row number of bookmark
// [pulRows] -- returns approx # of rows in cursor or
// 1 + approx rows if not at quiescence
//
// Returns: SCODE - the status of the operation.
//
// Notes: We don't distinguish between exact and approximate position.
// IRowsetExactScroll is implemented only because ADO 1.5
// started QI'ing for it.
//
//--------------------------------------------------------------------------
STDMETHODIMP CRowset::GetExactPosition(
HCHAPTER hChapter,
DBBKMARK cbBookmark,
const BYTE * pBookmark,
DBCOUNTITEM * pulPosition,
DBCOUNTITEM * pulRows) /*const*/
{
_DBErrorObj.ClearErrorInfo();
Win4Assert( !_fForwardOnly );
SCODE sc = S_OK;
TRY
{
DBCOUNTITEM ulNumerator, ulDenominator;
CI_TBL_BMK bmk = WORKID_TBLFIRST;
if (cbBookmark != 0)
bmk = _MapBookmark(cbBookmark, pBookmark);
CI_TBL_CHAPT chapt = _MapChapter(hChapter);
_rQuery.GetApproximatePosition( _hCursor,
chapt,
bmk,
&ulNumerator,
&ulDenominator );
if (cbBookmark)
*pulPosition = ulNumerator;
if (pulRows)
*pulRows = ulDenominator;
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetScroll);
sc = GetOleError(e);
}
END_CATCH;
return sc;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::GetRowsAtRatio, public
//
// Synopsis: Fetch data starting at some ratio in the cursor.
//
// Arguments: [hRegion] -- handle to watch region
// [hChapter] -- Chapter in a multiset cursor
// [ulNumerator] -- numerator or ratio fraction
// [ulDenominator] -- denominator or ratio fraction
// [cRows] -- Number of rows to return
// [pcRowsObtained] -- On return, number of rows actually
// fetched.
// [prghRows] -- Array of HROWs to be released
//
// Returns: SCODE, E_INVALIDARG for bad parameters, DB_E_BADBOOKMARK
// if the starting bookmark is invalid
//
// Notes:
//
// History: 14 Dec 1994 Alanw Created
//
//--------------------------------------------------------------------------
STDMETHODIMP CRowset::GetRowsAtRatio(
HWATCHREGION hRegion,
HCHAPTER hChapter,
DBCOUNTITEM ulNumerator,
DBCOUNTITEM ulDenominator,
DBROWCOUNT cRows,
DBCOUNTITEM * pcRowsObtained,
HROW * * prghRows
) {
_DBErrorObj.ClearErrorInfo();
CheckCrowsArgs( cRows, pcRowsObtained );
SCODE scResult = S_OK;
TRY
{
CI_TBL_CHAPT chapt = _MapChapter(hChapter);
CRowSeekAtRatio rowSeek( hRegion, chapt, (ULONG) ulNumerator, (ULONG) ulDenominator );
scResult = _FetchRows(rowSeek, cRows, pcRowsObtained, prghRows);
if (FAILED(scResult))
_DBErrorObj.PostHResult(scResult, IID_IRowsetScroll);
else if ( !_pRowsetNotification.IsNull() &&
*pcRowsObtained != 0 &&
_pRowsetNotification->IsNotifyActive() )
{
_pRowsetNotification->OnRowChange( this,
*pcRowsObtained,
*prghRows,
DBREASON_ROW_ACTIVATE,
DBEVENTPHASE_DIDEVENT,
TRUE);
}
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetScroll);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
#ifdef _WIN64
//+---------------------------------------------------------------------------
//
// Member: CRowset::_ConvertOffsetsToPointers, private
//
// Synopsis: Runs through a row buffer converting offsets to pointers
//
// Arguments: [pbRows] -- Buffer with row data
// [pbBias] -- Bias for offsets to pointers
// [cRows] -- Number of rows in the buffer
// [pArrayAlloc] -- buffer to hold extra array pointers
//
// History: 2 Aug 95 dlee created
// 1 Sep 99 KLam Reinstated
//
//----------------------------------------------------------------------------
void CRowset::_ConvertOffsetsToPointers(
BYTE * pbRows,
BYTE * pbBias,
unsigned cRows,
CFixedVarAllocator *pArrayAlloc )
{
if ( !_fPossibleOffsetConversions )
return;
BOOL fAnyOffsets = FALSE;
CTableColumnSet const & rCols = _ColumnsInfo.GetColumnBindings();
unsigned cbRowWidth = _ColumnsInfo.GetRowWidth();
for ( unsigned col = 0; col < rCols.Count(); col++ )
{
CTableColumn const & rColumn = *rCols.Get( col );
// if this assert isn't true someday, add an if on this condition
Win4Assert( rColumn.IsValueStored() );
VARTYPE vt = rColumn.GetStoredType();
if ( ( CTableVariant::IsByRef( vt ) ) && ( VT_CLSID != vt ) )
{
fAnyOffsets = TRUE;
BYTE *pbRow = pbRows;
BYTE *pbData = pbRow + rColumn.GetValueOffset();
for ( unsigned row = 0;
row < cRows;
row++, pbData += cbRowWidth, pbRow += cbRowWidth )
{
// Even stat props can be null if they came from a
// summary catalog.
tbDebugOut(( DEB_TRACE,
"CRowset::_ConvertOffsetsToPointer, Bias: 0x%I64x Row: 0x%I64x Data: 0x%I64x Type: %d New Alloc: 0x%I64x\n",
pbBias, pbRow, pbData, vt, pArrayAlloc ));
if (! rColumn.IsNull( pbRow ) )
{
if ( VT_VARIANT == vt )
{
if (! rColumn.IsDeferred( pbRow ) )
((CTableVariant *) pbData)->FixDataPointers( pbBias, pArrayAlloc );
}
else
{
Win4Assert( 0 == ( vt & VT_VECTOR ) );
* (BYTE **) pbData = pbBias + (* (ULONG *) pbData );
}
}
}
}
}
_fPossibleOffsetConversions = fAnyOffsets;
} //_ConvertOffsetsToPointers
#endif // _WIN64
//+---------------------------------------------------------------------------
//
// Member: CRowset::_FetchRows, private
//
// Synopsis: Return handles to rows in the table
//
// Effects: Rows are read from the table and buffered locally. An
// array of handles to the rows is returned.
//
// Arguments: [rSeekDesc] - seek method and parameters
// [cRows] - number of rows desired
// [pcRowsReturned] - pointer to where number of rows is returned
// [prghRows] - pointer to pointer to where row handles are
// returned, or pointer to zero if row handle
// array should be allocated
//
// Returns: error code
//
// Notes:
//
//----------------------------------------------------------------------------
SCODE CRowset::_FetchRows(
CRowSeekDescription & rSeekDesc,
DBROWCOUNT cRows,
DBCOUNTITEM * pcRowsReturned,
HROW * * prghRows
) {
SCODE scResult = S_OK;
Win4Assert( 0 != pcRowsReturned );
if (0 == prghRows)
{
THROW(CException(E_INVALIDARG));
}
if (0 == cRows)
{
*pcRowsReturned = 0;
return scResult;
}
BOOL fFwdFetch = TRUE;
if ( cRows < 0 )
{
fFwdFetch = FALSE;
cRows = -cRows; // cRows now has its absolute value
}
Win4Assert( cRows > 0 );
BOOL fRowCountTrimmed = FALSE;
if (cRows > MAX_ROW_FETCH)
{
cRows = MAX_ROW_FETCH;
fRowCountTrimmed = TRUE;
}
XArrayOLE<HROW> ahRows;
HROW *pHRows;
if ( 0 == *prghRows )
{
ahRows.Init( (unsigned) cRows );
pHRows = ahRows.GetPointer();
}
else
{
pHRows = *prghRows;
}
DBCOUNTITEM cRowsSoFar = 0; // number of rows successfully transferred.
CRowSeekDescription * pNextSeek = &rSeekDesc;
TRY
{
XPtr<CRowSeekDescription> pRowSeekPrevious(0);
ULONG cbRowWidth = _ColumnsInfo.GetRowWidth();
do
{
XPtr<CFixedVarAllocator> xAlloc( new
CFixedVarAllocator( FALSE,
FALSE,
cbRowWidth,
0 ));
CGetRowsParams FetchParams( (ULONG) (cRows - cRowsSoFar),
fFwdFetch,
cbRowWidth,
xAlloc.GetReference() );
// Get the row data
XPtr<CRowSeekDescription> xRowSeekOut(0);
scResult = _rQuery.GetRows( _hCursor,
*pNextSeek,
FetchParams,
xRowSeekOut );
if (FAILED(scResult))
{
#if CIDBG
if (E_FAIL == scResult)
tbDebugOut((DEB_WARN,
"CRowset::_FetchRows - E_FAIL ret'd by GetRows\n"));
#endif // CIDBG
break;
}
if ( 0 != FetchParams.RowsTransferred() )
{
Win4Assert( !xAlloc->IsBasedMemory() );
XPtr<CRowBuffer> xRowBuf ( new
CRowBuffer( _ColumnsInfo.GetColumnBindings(),
cbRowWidth,
FetchParams.RowsTransferred(),
xAlloc ));
#ifdef _WIN64
// if this is a Win64 client talking with a Win32 server
// then we need to fix the row buffer since we passed in 0
// as the base address.
if ( FetchParams.GetReplyBase() != 0 )
{
_fPossibleOffsetConversions = TRUE;
void *pvRows;
CTableColumnSet *pCol;
SCODE sc = xRowBuf->Lookup( (unsigned)cRowsSoFar,
&pCol,
&pvRows,
FALSE );
_ConvertOffsetsToPointers ( (BYTE *)pvRows,
FetchParams.GetReplyBase(),
FetchParams.RowsTransferred(),
_pRowBufs->GetArrayAlloc() );
}
#endif
_pRowBufs->Add( xRowBuf,
rSeekDesc.IsByBmkRowSeek(),
pHRows + cRowsSoFar );
cRowsSoFar += FetchParams.RowsTransferred();
}
else
{
// One row didn't fit into an fsctl buffer.
if (! ( scResult == DB_S_ENDOFROWSET ||
scResult == DB_S_STOPLIMITREACHED ||
( scResult == DB_S_ERRORSOCCURRED &&
rSeekDesc.IsByBmkRowSeek() ) ) )
{
tbDebugOut(( DEB_WARN,
"CRowset::_FetchRows, 0 rows, sc 0x%x\n",
scResult ));
}
Win4Assert( scResult == DB_S_ENDOFROWSET ||
scResult == DB_S_STOPLIMITREACHED ||
( scResult == DB_S_ERRORSOCCURRED &&
rSeekDesc.IsByBmkRowSeek() ) );
}
if ( 0 != xRowSeekOut.GetPointer() )
{
//
// Transfer results from the returned seek description
// (for the ByBookmark case), and update for the next
// transfer.
//
rSeekDesc.MergeResults( xRowSeekOut.GetPointer() );
delete pRowSeekPrevious.Acquire();
pRowSeekPrevious.Set( xRowSeekOut.Acquire() );
pNextSeek = pRowSeekPrevious.GetPointer();
}
Win4Assert( cRows >= 0 );
} while ( (DBCOUNTITEM) cRows > cRowsSoFar &&
0 != pNextSeek &&
!pNextSeek->IsDone() &&
(S_OK == scResult || DB_S_BLOCKLIMITEDROWS == scResult ) );
}
CATCH( CException, e )
{
scResult = e.GetErrorCode();
#if CIDBG
if (E_FAIL == scResult)
tbDebugOut((DEB_WARN, "CRowset::_FetchRows - E_FAIL from exception\n"));
#endif // CIDBG
}
END_CATCH;
if (DB_E_BADSTARTPOSITION == scResult)
scResult = DB_S_ENDOFROWSET;
if ( fRowCountTrimmed && scResult == S_OK )
scResult = DB_S_ROWLIMITEXCEEDED;
if ( FAILED(scResult))
{
ReleaseRows(cRowsSoFar, pHRows, 0, 0, 0);
*pcRowsReturned = 0;
tbDebugOut((DEB_ITRACE, "CRowset::_FetchRows - error %x thrown\n", scResult));
QUIETTHROW( CException( scResult ) );
}
else
{
if ( ( cRowsSoFar > 0 ) && ( 0 == *prghRows ) )
*prghRows = ahRows.Acquire();
*pcRowsReturned = cRowsSoFar;
}
return( scResult );
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::CreateAccessor, public
//
// Synopsis: Makes an accessor that a client can use to get data.
//
// Arguments: [dwAccessorFlags] -- read/write access requested
// [cBindings] -- # of bindings in rgBindings
// [rgBindings] -- array of bindings for the accessor to support
// [cbRowSize] -- ignored for IRowset
// [phAccessor] -- returns created accessor if all is ok
// [rgBindStatus] -- array of binding statuses
//
// Returns: SCODE error code
//
// History: 14 Dec 94 dlee Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::CreateAccessor(
DBACCESSORFLAGS dwAccessorFlags,
DBCOUNTITEM cBindings,
const DBBINDING rgBindings[],
DBLENGTH cbRowSize,
HACCESSOR * phAccessor,
DBBINDSTATUS rgBindStatus[])
{
_DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
if (0 == phAccessor || (0 != cBindings && 0 == rgBindings))
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IAccessor);
// Make sure pointer is good while zeroing in case of a later error
*phAccessor = 0;
TRY
{
XPtr<CAccessor> Accessor( CreateAnAccessor( dwAccessorFlags,
cBindings,
rgBindings,
rgBindStatus,
_fExtendedTypes,
(IUnknown *) (IRowset *)this,
&_ColumnsInfo ) );
CLock lock( _mutex );
_aAccessors.Add( Accessor.GetPointer() );
*phAccessor = (Accessor.Acquire())->Cast();
}
CATCH(CException, e)
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IAccessor);
sc = GetOleError(e);
}
END_CATCH;
return sc;
} //CreateAccessor
//+---------------------------------------------------------------------------
//
// Member: IsValidFromVariantType
//
// Synopsis: If DBCONVERTFLAGS_FROMVARIANT is requested, the source type
// has to be a valid VARIANT type.
//
// Arguments: [wTypeIn] -- the source type
//
// Returns: TRUE -- the type is a valid VARIANT type
// FALSE -- otherwise
//
//----------------------------------------------------------------------------
inline BOOL IsValidFromVariantType( DBTYPE wTypeIn )
{
DBTYPE wType = wTypeIn & VT_TYPEMASK;
return (! ((wType > VT_DECIMAL && wType < VT_I1) ||
(wType > VT_LPWSTR && wType < VT_FILETIME && wType != VT_RECORD) ||
(wType > VT_CLSID)) );
}
//+---------------------------------------------------------------------------
//
// Member: IsVariableLengthType
//
// Synopsis: checks to see DBCONVERTFLAGS_ISLONG is appropriate
//
// Arguments: [wTypeIn] -- the source type
//
// Returns: TRUE -- the type is variable length
// FALSE -- otherwise
//
//----------------------------------------------------------------------------
inline BOOL IsVariableLengthType( DBTYPE wTypeIn )
{
DBTYPE wType = wTypeIn & VT_TYPEMASK;
return wType == DBTYPE_STR ||
wType == DBTYPE_BYTES ||
wType == DBTYPE_WSTR ||
wType == DBTYPE_VARNUMERIC;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::CanConvert, public
//
// Synopsis: Indicate whether a type conversion is valid.
//
// Arguments: [wFromType] -- source type
// [wToType] -- destination type
// [dwConvertFlags] -- read/write access requested
//
// Returns: S_OK if the conversion is available, S_FALSE otherwise.
// E_FAIL, E_INVALIDARG or DB_E_BADCONVERTFLAG on errors.
//
// History: 20 Nov 96 AlanW Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::CanConvert(
DBTYPE wFromType,
DBTYPE wToType,
DBCONVERTFLAGS dwConvertFlags )
{
_DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
TRY
{
if (((dwConvertFlags & DBCONVERTFLAGS_COLUMN) &&
(dwConvertFlags & DBCONVERTFLAGS_PARAMETER)) ||
(dwConvertFlags & ~(DBCONVERTFLAGS_COLUMN |
DBCONVERTFLAGS_PARAMETER |
DBCONVERTFLAGS_ISFIXEDLENGTH |
DBCONVERTFLAGS_ISLONG |
DBCONVERTFLAGS_FROMVARIANT)))
{
sc = DB_E_BADCONVERTFLAG;
}
else if ( dwConvertFlags & DBCONVERTFLAGS_FROMVARIANT &&
!IsValidFromVariantType(wFromType) )
{
sc = DB_E_BADTYPE;
}
else
{
BOOL fOk = CAccessor::CanConvertType( wFromType,
wToType,
_fExtendedTypes, _xDataConvert );
sc = fOk ? S_OK : S_FALSE;
}
if (FAILED(sc))
_DBErrorObj.PostHResult(sc, IID_IConvertType);
}
CATCH(CException, e)
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IConvertType);
sc = GetOleError(e);
}
END_CATCH;
return sc;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::GetBindings, public
//
// Synopsis: Returns an accessor's bindings
//
// Arguments: [hAccessor] -- accessor being queried
// [dwBindIO] -- returns read/write access of accessor
// [pcBindings] -- returns # of bindings in rgBindings
// [prgBindings] -- returns array of bindings
//
// Returns: SCODE error code
//
// History: 14 Dec 94 dlee Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::GetBindings(
HACCESSOR hAccessor,
DBACCESSORFLAGS * pdwBindIO,
DBCOUNTITEM * pcBindings,
DBBINDING * * prgBindings) /*const*/
{
_DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
if (0 == pdwBindIO ||
0 == pcBindings ||
0 == prgBindings)
{
// fill in error values where possible
if (pdwBindIO)
*pdwBindIO = DBACCESSOR_INVALID;
if (pcBindings)
*pcBindings = 0;
if (prgBindings)
*prgBindings = 0;
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IAccessor);
}
*pdwBindIO = DBACCESSOR_INVALID;
*pcBindings = 0;
*prgBindings = 0;
TRY
{
CLock lock( _mutex );
CAccessor * pAccessor = (CAccessor *)_aAccessors.Convert( hAccessor );
pAccessor->GetBindings(pdwBindIO, pcBindings, prgBindings);
}
CATCH(CException, e)
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IAccessor);
sc = GetOleError(e);
}
END_CATCH;
return sc;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::AddRefAccessor, public
//
// Synopsis: Frees an accessor
//
// Arguments: [hAccessor] -- accessor being freed
// [pcRefCount] -- pointer to residual refcount (optional)
//
// Returns: SCODE error code
//
// History: 14 Dec 94 dlee Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::AddRefAccessor(
HACCESSOR hAccessor,
ULONG * pcRefCount
) {
_DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
TRY
{
CLock lock( _mutex );
_aAccessors.AddRef( hAccessor, pcRefCount );
}
CATCH(CException, e)
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IAccessor);
sc = GetOleError(e);
}
END_CATCH;
return sc;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::ReleaseAccessor, public
//
// Synopsis: Frees an accessor
//
// Arguments: [hAccessor] -- accessor being freed
// [pcRefCount] -- pointer to residual refcount (optional)
//
// Returns: SCODE error code
//
// History: 14 Dec 94 dlee Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::ReleaseAccessor(
HACCESSOR hAccessor,
ULONG * pcRefCount )
{
_DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
TRY
{
CLock lock( _mutex );
_aAccessors.Release( hAccessor, pcRefCount );
}
CATCH(CException, e)
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IAccessor);
sc = GetOleError(e);
}
END_CATCH;
return sc;
} //ReleaseAccessor
//+---------------------------------------------------------------------------
//
// Member: CRowset::GetData, public
//
// Synopsis: Returns row data using an accessor
//
// Arguments: [hRow] -- handle of row whose data is returned
// [hAccessor] -- accessor used to retrieve the data
// [pData] -- where the data is written
//
// Returns: SCODE error code
//
// History: 14 Dec 94 dlee Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::GetData(
HROW hRow,
HACCESSOR hAccessor,
void* pData) /*const*/
{
_DBErrorObj.ClearErrorInfo();
SCODE sc = S_OK;
// NOTE: Null accessors are not supported, so don't need to worry about
// special casing for that.
if ( 0 == pData )
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowset);
TRY
{
CLock lock( _mutex );
CAccessor * pAccessor = (CAccessor *)_aAccessors.Convert( hAccessor );
Win4Assert( pAccessor->IsRowDataAccessor() );
pAccessor->GetData(hRow, pData, *_pRowBufs, _rQuery, _ColumnsInfo, _xDataConvert );
}
CATCH(CException, e)
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowset);
sc = GetOleError(e);
}
END_CATCH;
return sc;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::GetNextRows, public
//
// Synopsis: Return row data from the table
//
// Arguments: [hChapter] -- chapter to start at
// [cRowsToSkip] -- # of rows to skip
// [cRows] -- # of rows to try to return
// [pcRowsObtained] -- returns # of rows obtained
// [prghRows] -- returns array of rows
//
// Returns: SCODE error code
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::GetNextRows(
HCHAPTER hChapter,
DBROWOFFSET cRowsToSkip,
DBROWCOUNT cRows,
DBCOUNTITEM * pcRowsObtained,
HROW * * prghRows
)
{
_DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
BOOL fNotified = FALSE;
CheckCrowsArgs( cRows, pcRowsObtained );
if (_fForwardOnly && cRowsToSkip < 0)
return _DBErrorObj.PostHResult(DB_E_CANTSCROLLBACKWARDS, IID_IRowset);
if (_fForwardOnly && cRows < 0 )
return _DBErrorObj.PostHResult(DB_E_CANTFETCHBACKWARDS, IID_IRowset);
TRY
{
if (! _fHoldRows)
_pRowBufs->CheckAllHrowsReleased();
CI_TBL_CHAPT chapt = _MapChapter(hChapter);
if ( !_pRowsetNotification.IsNull() &&
_pRowsetNotification->IsNotifyActive() )
{
scResult = _pRowsetNotification->OnRowsetChange(
this,
DBREASON_ROWSET_FETCHPOSITIONCHANGE,
DBEVENTPHASE_OKTODO,
FALSE);
if ( S_FALSE == scResult )
THROW(CException(DB_E_CANCELED));
fNotified = TRUE;
}
CRowSeekNext rowSeek( chapt, (LONG) cRowsToSkip );
scResult = _FetchRows(rowSeek, cRows, pcRowsObtained, prghRows);
if ( fNotified )
{
if (SUCCEEDED(scResult))
{
if ( *pcRowsObtained != 0 )
_pRowsetNotification->OnRowChange( this,
*pcRowsObtained,
*prghRows,
DBREASON_ROW_ACTIVATE,
DBEVENTPHASE_DIDEVENT,
TRUE);
_pRowsetNotification->OnRowsetChange( this,
DBREASON_ROWSET_FETCHPOSITIONCHANGE,
DBEVENTPHASE_DIDEVENT,
TRUE);
}
}
if (FAILED(scResult))
_DBErrorObj.PostHResult(scResult, IID_IRowset);
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowset);
scResult = GetOleError(e);
}
END_CATCH;
if ( fNotified && FAILED(scResult) )
_pRowsetNotification->OnRowsetChange( this,
DBREASON_ROWSET_FETCHPOSITIONCHANGE,
DBEVENTPHASE_FAILEDTODO,
TRUE);
Win4Assert( cRowsToSkip != 0 ||
DB_E_BADSTARTPOSITION != scResult );
return scResult;
}
//
// IRowsetIdentity methods
//
SCODE CRowset::IsSameRow ( HROW hThisRow, HROW hThatRow )
{
_DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
TRY
{
CTableColumnSet *pRowBufferColumns;
BYTE *pbSrc;
//
// Lookup the HROWs to validate them
//
_pRowBufs->Lookup( hThisRow,
&pRowBufferColumns,
(void **) &pbSrc );
_pRowBufs->Lookup( hThatRow,
&pRowBufferColumns,
(void **) &pbSrc );
if (hThisRow != hThatRow)
scResult = S_FALSE;
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetIdentity);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
//
// IRowsetWatchAll methods
//
SCODE CRowset::Acknowledge ( )
{
_DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
TRY
{
// Just use Refresh to acknowledge the notification.
_rQuery.Refresh();
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchAll);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
SCODE CRowset::Start ( )
{
_DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
TRY
{
_rQuery.StartWatching(_hCursor);
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchAll);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
SCODE CRowset::StopWatching ( )
{
_DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
TRY
{
_rQuery.StopWatching(_hCursor);
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchAll);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
//
// IRowsetWatchRegion methods
//
SCODE CRowset::ChangeWatchMode (
HWATCHREGION hRegion,
DBWATCHMODE mode)
{
_DBErrorObj.ClearErrorInfo();
if (hRegion == watchRegionInvalid)
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetWatchRegion);;
SCODE scResult = S_OK;
TRY
{
_rQuery.SetWatchMode(&hRegion, mode);
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchRegion);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
SCODE CRowset::CreateWatchRegion (
DBWATCHMODE mode,
HWATCHREGION* phRegion)
{
_DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
*phRegion = watchRegionInvalid;
TRY
{
_rQuery.SetWatchMode(phRegion, mode);
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchRegion);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
SCODE CRowset::DeleteWatchRegion (
HWATCHREGION hRegion)
{
_DBErrorObj.ClearErrorInfo();
if (hRegion == watchRegionInvalid)
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetWatchRegion);
SCODE scResult = S_OK;
TRY
{
_rQuery.ShrinkWatchRegion (hRegion, 0, 0, 0);
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchRegion);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
SCODE CRowset::GetWatchRegionInfo (
HWATCHREGION hRegion,
DBWATCHMODE * pMode,
HCHAPTER * phChapter,
DBBKMARK * pcbBookmark,
BYTE ** ppBookmark,
DBROWCOUNT * pcRows)
{
_DBErrorObj.ClearErrorInfo();
if (hRegion == watchRegionInvalid)
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetWatchRegion);;
SCODE scResult = S_OK;
// test pointers and prepare for failure
*phChapter = 0;
*pcbBookmark = 0;
*ppBookmark = 0;
*pcRows = 0;
TRY
{
CI_TBL_CHAPT chapter;
CI_TBL_BMK bookmark;
_rQuery.GetWatchInfo( hRegion, pMode, &chapter, &bookmark, (DBCOUNTITEM *)pcRows);
if (chapter != 0)
{
*phChapter = (HCHAPTER)chapter;
}
if (bookmark != 0)
{
XArrayOLE<CI_TBL_BMK> pOutBookmark (1);
*pcbBookmark = sizeof CI_TBL_BMK;
memcpy ( pOutBookmark.GetPointer(), &bookmark, sizeof CI_TBL_BMK);
*ppBookmark = (BYTE*) pOutBookmark.Acquire();
}
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchRegion);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
SCODE CRowset::ShrinkWatchRegion (
HWATCHREGION hRegion,
HCHAPTER hChapter,
DBBKMARK cbBookmark,
BYTE * pBookmark,
DBROWCOUNT cRows )
{
_DBErrorObj.ClearErrorInfo();
if (hRegion == watchRegionInvalid)
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetWatchRegion);;
SCODE scResult = S_OK;
TRY
{
CI_TBL_BMK bookmark = _MapBookmark(cbBookmark, pBookmark);
CI_TBL_CHAPT chapter = _MapChapter(hChapter);
_rQuery.ShrinkWatchRegion ( hRegion, chapter, bookmark, (LONG) cRows);
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchRegion);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
SCODE CRowset::Refresh (
DBCOUNTITEM* pCount,
DBROWWATCHCHANGE** ppRowChange )
{
_DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
TRY
{
// prepare the buffers
// fetch the script and
// a bunch of rows.
// If not all rows fit in
// the buffer, call again.
_rQuery.Refresh();
*pCount = 0;
scResult = DB_S_TOOMANYCHANGES;
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetWatchRegion);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::GetStatus, public
//
// Synopsis: Return query status
//
// Arguments: [pdwStatus] -- pointer to where query status is returned
//
// Returns: SCODE error code
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::GetStatus(
DWORD * pdwStatus
) {
_DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
TRY
{
*pdwStatus = 0;
_rQuery.GetQueryStatus( _hCursor, *pdwStatus );
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetQueryStatus);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::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
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::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;
TRY
{
*pdwStatus = 0;
CI_TBL_BMK bmk = WORKID_TBLFIRST;
if ( cbBmk != 0 )
bmk = _MapBookmark( cbBmk, pBmk );
_rQuery.GetQueryStatusEx( _hCursor,
*pdwStatus,
*pcFilteredDocuments,
*pcDocumentsToFilter,
*pdwRatioFinishedDenominator,
*pdwRatioFinishedNumerator,
bmk,
*piRowBmk,
*pcRowsTotal );
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowsetQueryStatus);
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::GetReferencedRowset, public
//
// Synopsis: Return the related rowset for a hierarchical query.
//
// Arguments: [iOrdinal] -- ordinal of column for bookmark
// [riid] -- IID of desired interface
// [ppReferencedRowset] -- interface pointer returned here
//
// Returns: SCODE error code
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::GetReferencedRowset(
DBORDINAL iOrdinal,
REFIID riid,
IUnknown ** ppReferencedRowset
) /*const*/ {
_DBErrorObj.ClearErrorInfo();
if (0 == ppReferencedRowset)
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetInfo);
*ppReferencedRowset = 0;
if ( iOrdinal > _ColumnsInfo.GetColumnCount() ||
(0 == _pRelatedRowset && 0 == iOrdinal && _fForwardOnly))
return _DBErrorObj.PostHResult(DB_E_BADORDINAL, IID_IRowsetInfo);
if ( 0 == _pRelatedRowset && 0 != iOrdinal )
return _DBErrorObj.PostHResult(DB_E_NOTAREFERENCECOLUMN, IID_IRowsetInfo);
//
// If it's the bookmark column, it's like a QI on this rowset.
//
if (0 == iOrdinal)
return QueryInterface( riid, (void **)ppReferencedRowset );
//
// Make sure the column is a valid chapter column.
//
const DBCOLUMNINFO & rColInfo = _ColumnsInfo.Get1ColumnInfo( (ULONG) iOrdinal );
if ( (rColInfo.dwFlags & DBCOLUMNFLAGS_ISBOOKMARK) == 0 ||
(rColInfo.dwFlags & DBCOLUMNFLAGS_ISROWID) != 0 )
return _DBErrorObj.PostHResult(DB_E_NOTAREFERENCECOLUMN, IID_IRowsetInfo);
return _pRelatedRowset->QueryInterface( riid, (void **)ppReferencedRowset );
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::RestartPosition, public
//
// Synopsis: Set up CRowset so next GetNextRows will restart from beginning
//
// Arguments: [hChapter] -- chapter which should restart
//
// Returns: SCODE error code
//
// Notes:
//
// History: 16-Apr-97 emilyb wrote
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::RestartPosition(
HCHAPTER hChapter
) {
_DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
BOOL fNotified = FALSE;
TRY
{
if (! _fHoldRows)
_pRowBufs->CheckAllHrowsReleased();
CI_TBL_CHAPT chapter = _MapChapter(hChapter); // is chapter valid?
if ( !_pRowsetNotification.IsNull() &&
_pRowsetNotification->IsNotifyActive() )
{
scResult = _pRowsetNotification->OnRowsetChange(
this,
DBREASON_ROWSET_FETCHPOSITIONCHANGE,
DBEVENTPHASE_OKTODO,
FALSE);
if ( S_FALSE == scResult )
THROW(CException(DB_E_CANCELED));
fNotified = TRUE;
}
_rQuery.RestartPosition(_hCursor, chapter);
if ( fNotified )
_pRowsetNotification->OnRowsetChange( this,
DBREASON_ROWSET_FETCHPOSITIONCHANGE,
DBEVENTPHASE_DIDEVENT,
TRUE);
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IRowset);
scResult = GetOleError(e);
if (E_NOTIMPL == scResult)
{
scResult = DB_E_CANNOTRESTART;
}
}
END_CATCH;
if ( fNotified && FAILED(scResult) )
_pRowsetNotification->OnRowsetChange( this,
DBREASON_ROWSET_FETCHPOSITIONCHANGE,
DBEVENTPHASE_FAILEDTODO,
TRUE);
return scResult;
}
//
// IDbAsynchStatus methods
//
//+---------------------------------------------------------------------------
//
// Member: CRowset::Abort, public
//
// Synopsis: Cancels an asynchronously executing operation.
//
// Arguments: [hChapter] -- chapter which should restart
// [ulOperation] -- operation for which status is being requested
//
// Returns: SCODE error code
//
// Notes:
//
// History: 09 Feb 1998 AlanW Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::Abort(
HCHAPTER hChapter,
ULONG ulOperation
) {
_DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
if ( DBASYNCHOP_OPEN != ulOperation )
{
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IDBAsynchStatus);
}
TRANSLATE_EXCEPTIONS;
TRY
{
CI_TBL_CHAPT chapter = _MapChapter(hChapter); // is chapter valid?
_rQuery.StopAsynch(_hCursor);
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IDBAsynchStatus);
scResult = GetOleError(e);
if (E_NOTIMPL == scResult)
{
scResult = DB_E_CANTCANCEL;
_DBErrorObj.PostHResult(scResult, IID_IDBAsynchStatus);
}
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return scResult;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::GetStatus, public
//
// Synopsis: Returns the status of an asynchronously executing operation.
//
// Arguments: [hChapter] -- chapter which should restart
// [ulOperation] -- operation for which status is being requested
//
// Returns: SCODE error code
//
// Notes:
//
// History: 09 Feb 1998 AlanW Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::GetStatus(
HCHAPTER hChapter,
DBASYNCHOP ulOperation,
DBCOUNTITEM * pulProgress,
DBCOUNTITEM * pulProgressMax,
DBASYNCHPHASE * pulAsynchPhase,
LPOLESTR * ppwszStatusText
) {
_DBErrorObj.ClearErrorInfo();
SCODE scResult = S_OK;
if ( DBASYNCHOP_OPEN != ulOperation )
{
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IDBAsynchStatus);
}
TRANSLATE_EXCEPTIONS;
TRY
{
// NOTE: we don't support getting the status of chapters
// independently.
// CI_TBL_CHAPT chapter = _MapChapter(hChapter); // is chapter valid?
if (DB_NULL_HCHAPTER != hChapter)
THROW( CException( DB_E_BADCHAPTER ));
DBCOUNTITEM ulNumerator = 0;
DBCOUNTITEM ulDenominator = 0;
DBCOUNTITEM cRows;
BOOL fNewRows;
_rQuery.RatioFinished( _hCursor,
ulDenominator,
ulNumerator,
cRows,
fNewRows );
if (pulProgress)
*pulProgress = ulNumerator;
if (pulProgressMax)
*pulProgressMax = ulDenominator;
if (pulAsynchPhase)
*pulAsynchPhase = (ulDenominator == ulNumerator) ?
DBASYNCHPHASE_COMPLETE :
DBASYNCHPHASE_POPULATION;
if (ppwszStatusText)
*ppwszStatusText = 0;
#if CIDBG
if ( _fForwardOnly )
Win4Assert( ulDenominator == ulNumerator );
else
Win4Assert( ulDenominator >= ulNumerator );
#endif // CIDBG
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IDBAsynchStatus);
scResult = GetOleError(e);
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return scResult;
}
//+---------------------------------------------------------------------------
//
// Member: CRowset::GetSpecification, public
//
// Synopsis: Return the session or command object used to create this rowset
//
// Arguments: [hChapter] -- chapter which should restart
//
// Notes:
//
// History: 01-28-98 danleg Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::GetSpecification(
REFIID riid,
IUnknown ** ppvSpecification
) {
_DBErrorObj.ClearErrorInfo();
if ( 0 == ppvSpecification )
return _DBErrorObj.PostHResult( E_INVALIDARG, IID_IRowsetInfo );
SCODE sc = S_OK;
TRANSLATE_EXCEPTIONS;
TRY
{
*ppvSpecification = 0;
if ( _xUnkCreator.IsNull() )
return S_FALSE;
sc = _xUnkCreator->QueryInterface( riid, (void ** )ppvSpecification );
if ( FAILED(sc) )
THROW( CException(sc) );
}
CATCH( CException, e )
{
sc = _DBErrorObj.PostHResult( e, IID_IRowsetInfo );
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//
// IServiceProperties methods
//
//+---------------------------------------------------------------------------
//
// Method: CRowset::GetPropertyInfo, public
//
// Synopsis: Get rowset properties
//
// Arguments: [cPropertySetIDs] - number of desired properties or 0
// [rgPropertySetIDs] - array of desired properties or NULL
// [pcPropertySets] - number of property sets returned
// [prgPropertySets] - array of returned property sets
// [ppwszDesc] - if non-zero, property descriptions are
// returneed
//
// History: 16 Nov 95 AlanW Created
//
//----------------------------------------------------------------------------
SCODE CRowset::GetPropertyInfo(
const ULONG cPropertySetIDs,
const DBPROPIDSET rgPropertySetIDs[],
ULONG * pcPropertySets,
DBPROPINFOSET ** prgPropertySets,
WCHAR ** ppwszDesc)
{
_DBErrorObj.ClearErrorInfo();
if ( (0 != cPropertySetIDs && 0 == rgPropertySetIDs) ||
0 == pcPropertySets ||
0 == prgPropertySets )
{
if (pcPropertySets)
*pcPropertySets = 0;
if (prgPropertySets)
*prgPropertySets = 0;
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IServiceProperties);
}
SCODE sc = S_OK;
*pcPropertySets = 0;
*prgPropertySets = 0;
if (ppwszDesc)
*ppwszDesc = 0;
TRANSLATE_EXCEPTIONS;
TRY
{
sc = _PropInfo.GetPropertyInfo( cPropertySetIDs,
rgPropertySetIDs,
pcPropertySets,
prgPropertySets,
ppwszDesc );
// Don't PostHResult here -- it's a good chance it's a scope
// property that we're expecting to fail. Spare the expense.
// The child object will post the error for us.
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IServiceProperties);
sc = GetOleError(e);
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return sc;
}
//+---------------------------------------------------------------------------
//
// Method: CRowset::SetRequestedProperties, public
//
// Synopsis: Set rowset properties via IServiceProperties (not supported)
//
// Arguments: [cPropertySets] - number of property sets
// [rgProperties] - array of property sets to be set
//
// History: 16 Nov 95 AlanW Created
//
//----------------------------------------------------------------------------
SCODE CRowset::SetRequestedProperties(
ULONG cPropertySets,
DBPROPSET rgPropertySets[])
{
_DBErrorObj.ClearErrorInfo();
if ( 0 != cPropertySets && 0 == rgPropertySets)
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IServiceProperties);;
return _DBErrorObj.PostHResult(E_NOTIMPL, IID_IServiceProperties);;
}
//+---------------------------------------------------------------------------
//
// Method: CRowset::SetSuppliedProperties, public
//
// Synopsis: Set rowset properties via IServiceProperties (not supported)
//
// Arguments: [cPropertySets] - number of property sets
// [rgProperties] - array of property sets to be set
//
// History: 16 Nov 95 AlanW Created
//
//----------------------------------------------------------------------------
SCODE CRowset::SetSuppliedProperties(
ULONG cPropertySets,
DBPROPSET rgPropertySets[])
{
_DBErrorObj.ClearErrorInfo();
if ( 0 != cPropertySets && 0 == rgPropertySets)
return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IServiceProperties);;
return _DBErrorObj.PostHResult(E_NOTIMPL, IID_IServiceProperties);;
}
//+---------------------------------------------------------------------------
//
// Method: CRowset::AddRefChapter, public
//
// Synopsis: Increment the ref. count to a chapter
//
// Arguments: [hChapter] - handle to chapter
// [pcRefCount] - pointer to chapter ref. count
//
// Notes: Chapter references are obtained via an accessor from the
// parent rowset, but they are addref'ed in the child rowset.
//
// History: 15 Mar 99 AlanW Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::AddRefChapter(
HCHAPTER hChapter,
ULONG * pcRefCount
) {
tbDebugOut(( DEB_TRACE, "CRowset::AddRefChapter called!\n" ));
_DBErrorObj.ClearErrorInfo();
if (0 == _pChapterRowbufs)
return _DBErrorObj.PostHResult(E_FAIL, IID_IChapteredRowset);
if (DB_NULL_HCHAPTER == hChapter)
return _DBErrorObj.PostHResult(DB_E_BADCHAPTER, IID_IChapteredRowset);
SCODE scResult = S_OK;
TRANSLATE_EXCEPTIONS;
TRY
{
_pChapterRowbufs->AddRefChapter(hChapter, pcRefCount);
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IChapteredRowset);
scResult = GetOleError(e);
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return scResult;
}
//+---------------------------------------------------------------------------
//
// Method: CRowset::ReleaseChapter, public
//
// Synopsis: Decrement the ref. count to a chapter
//
// Arguments: [hChapter] - handle to chapter
// [pcRefCount] - pointer to chapter ref. count
//
// Notes: Chapter references are obtained via an accessor from the
// parent rowset, but they are released in the child rowset.
//
// History: 15 Mar 99 AlanW Created
//
//----------------------------------------------------------------------------
STDMETHODIMP CRowset::ReleaseChapter(
HCHAPTER hChapter,
ULONG * pcRefCount
) {
tbDebugOut(( DEB_TRACE, "CRowset::ReleaseChapter called!\n" ));
_DBErrorObj.ClearErrorInfo();
if (0 == _pChapterRowbufs)
return _DBErrorObj.PostHResult(E_FAIL, IID_IChapteredRowset);
if (DB_NULL_HCHAPTER == hChapter)
return _DBErrorObj.PostHResult(DB_E_BADCHAPTER, IID_IChapteredRowset);
SCODE scResult = S_OK;
TRANSLATE_EXCEPTIONS;
TRY
{
_pChapterRowbufs->ReleaseChapter(hChapter, pcRefCount);
}
CATCH( CException, e )
{
_DBErrorObj.PostHResult(e.GetErrorCode(), IID_IChapteredRowset);
scResult = GetOleError(e);
}
END_CATCH;
UNTRANSLATE_EXCEPTIONS;
return scResult;
}
STDMETHODIMP CRowset::Stop( )
{
SCODE scResult = S_OK;
TRY
{
_rQuery.StopAsynch(_hCursor);
}
CATCH( CException, e )
{
scResult = GetOleError(e);
}
END_CATCH;
return scResult;
}