|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 2000.
//
// File: tblwindo.cxx
//
// Contents:
//
// Classes: CTableWindow - a window over a segment of a table
// XCompressFreeVariant - container for variant which must be freed
//
// Functions:
//
// Notes:
//
// History: 25 Jan 1994 AlanW Created
// 20 Jun 1995 BartoszM Added watch regions
//
//--------------------------------------------------------------------------
#include "pch.cxx"
#pragma hdrstop
#include <query.hxx> // For CGetRowsParams
#include <tbrowkey.hxx>
#include <singlcur.hxx>
#include "tblwindo.hxx"
#include "colcompr.hxx"
#include "tabledbg.hxx"
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::CTableWindow, public
//
// Synopsis: Constructor for a window. Allocates and fills
// in the column description. Computes size of
// per-row data and assigns column offsets.
// Allocates initial table row data and variable
// data. Allocates initial row index.
//
// Arguments: [pMasterColumns] -- a pointer to the master column set
//
// Notes: PERFFIX - vikasman: To save space, we can set up all columns
// which are not part of the sort set and which are not special
// columns as deferred columns.
//
//--------------------------------------------------------------------------
CTableWindow::CTableWindow( CSortSet const * pSortSet, CTableKeyCompare & comparator, CColumnMasterSet * pMasterColumns, ULONG segId, CCategorize *pCategorize, CSharedBuffer & sharedBuf, CQAsyncExecute & QExecute ) : CTableSegment( CTableSegment::eWindow, segId, *pSortSet, comparator ), _pSortSet( pSortSet ), _Columns( pMasterColumns->Size() ), _sharedBuf( sharedBuf ), _BookMarkMap(_visibleRowIndex), _cPendingDeletes(0), _cRowsHardDeleted(0), _cRowsAllocated(0), _fSplitInProgress(FALSE), _cbHeapUsed ( 0 ), _pPathStore(0), _dynRowIndex(), _QueryExecute(QExecute), _fCanPartialDefer(QExecute.CanPartialDefer()) { tbDebugOut(( DEB_ITRACE, "_CanPartialDefer = %d\n", _fCanPartialDefer ));
SetCategorizer(pCategorize);
int cCol = pMasterColumns->Size();
CTableRowAlloc RowMap(0);
unsigned maxAlignment = 0; _iOffsetWid = ULONG_MAX; _iOffsetRowStatus = ULONG_MAX; _iOffsetChapter = ULONG_MAX; _iOffsetRank = ULONG_MAX; _iOffsetHitCount = ULONG_MAX;
for (int iCol=0; iCol < cCol; iCol++) { CColumnMasterDesc& MasterCol = pMasterColumns->Get(iCol);
if (MasterCol.IsCompressedCol() && MasterCol.GetCompressMasterId() != 0) { //
// Global shared compression; make sure the referenced
// column is in the table column set.
//
BOOL fFound = FALSE; CTableColumn* pColumn = _Columns.Find(MasterCol.GetCompressMasterId(), fFound);
if (fFound != TRUE) { CColumnMasterDesc* pMasterComprCol = pMasterColumns->Find(MasterCol.GetCompressMasterId());
Win4Assert(pMasterComprCol != 0); _AddColumnDesc(*pMasterComprCol, RowMap, maxAlignment); } } else if ( pidName == MasterCol.PropId ) { //
// We must always add the pidPath before pidName if it is
// present in the MasterColumnSet. This is because we
// do shared compression for path and name.
//
BOOL fPathPresent = FALSE; CTableColumn * pWindowPathCol = _Columns.Find( pidPath , fPathPresent ); if ( !fPathPresent ) { //
// There is no column for pidPath in the window before
// this column. Check if there is pidPath in the master
// column set and if so, add pidPath to the window column
// set before pidName.
//
CColumnMasterDesc * pMasterPathCol = pMasterColumns->Find( pidPath ); if ( 0 != pMasterPathCol ) { _AddColumnDesc( *pMasterPathCol, RowMap, maxAlignment ); } } }
//
// See if the column is already there (added for a shared compr)
//
BOOL fFound = FALSE; CTableColumn* pColumn = _Columns.Find(MasterCol.PropId, fFound);
if (fFound) continue;
_AddColumnDesc(MasterCol, RowMap, maxAlignment); }
_FinishInit( RowMap, maxAlignment );
END_CONSTRUCTION(CTableWindow); }
//+---------------------------------------------------------------------------
//
// Function: CTableWindow ~ctor
//
// Synopsis: Constructor used for creating a new table window with the
// same structure as an existing table window.
//
// Arguments: [src] - Source window which should be used as a basis for
// the ROW FORMAT only; not the data.
//
// History: 2-07-95 srikants Created
//
// Notes: This is used during window splitting to create new windows
// from an existing window.
//
//----------------------------------------------------------------------------
CTableWindow::CTableWindow( CTableWindow & src, ULONG segId ) : CTableSegment( src, segId ), _Columns( src._Columns.Count() ), _sharedBuf( src._GetSharedBuf() ), _BookMarkMap(_visibleRowIndex), _pSortSet( src._pSortSet ), _cPendingDeletes(0), _cRowsHardDeleted(0), _cRowsAllocated(0), _fSplitInProgress(FALSE), _cbHeapUsed ( 0 ), _pPathStore(0), _dynRowIndex(), _QueryExecute(src._QueryExecute), _fCanPartialDefer(src._fCanPartialDefer) { SetCategorizer(src.GetCategorizer());
int cCol = src._Columns.Count();
CTableRowAlloc RowMap(0);
unsigned maxAlignment = 0; _iOffsetWid = ULONG_MAX; _iOffsetRowStatus = ULONG_MAX; _iOffsetChapter = ULONG_MAX; _iOffsetRank = ULONG_MAX; _iOffsetHitCount = ULONG_MAX;
//
// PERFFIX: if we don't do window local compression, then we
// can just copy the column format bit-by-bit and not worry
// about looking at each column individually. For now, I will leave
// the loop in place.
//
for (int iCol=0; iCol < cCol; iCol++) {
CTableColumn& tableCol = *src._Columns.Get(iCol);
if ( tableCol.IsCompressedCol() && 0 != tableCol.GetCompressMasterId() ) {
//
// Global shared compression; make sure the referenced
// column is in the table column set.
//
BOOL fAlreadyPresent = FALSE; CTableColumn* pColumn = _Columns.Find(tableCol.GetCompressMasterId(), fAlreadyPresent);
Win4Assert( fAlreadyPresent );
if (!fAlreadyPresent) { CTableColumn* pTableComprCol = src._Columns.Find( tableCol.GetCompressMasterId(), fAlreadyPresent );
Win4Assert(0 != pTableComprCol); _AddColumnDesc(*pTableComprCol, RowMap, maxAlignment); } }
//
// See if the column is already there (added for a shared compr)
//
BOOL fFound = FALSE; CTableColumn* pColumn = _Columns.Find(tableCol.GetPropId(), fFound);
if (!fFound) { _AddColumnDesc(tableCol, RowMap, maxAlignment); } }
_FinishInit( RowMap, maxAlignment ); // tbDebugOut(( DEB_WINSPLIT, "NewWindow-C with id 0x%X\n", segId ));
END_CONSTRUCTION(CTableWindow); }
//+---------------------------------------------------------------------------
//
// Function: _FinishInit
//
// Synopsis: Completes the initialization of the constructor.
//
// Arguments: [RowMap] - RowMap containing the allocation details for
// the column
// [maxAlignment] - Maximum alignment requirement
//
// History: 2-07-95 srikants Split from the ~ctor to move the common
// code to a function.
//
// Notes:
//
//----------------------------------------------------------------------------
void CTableWindow::_FinishInit( CTableRowAlloc & RowMap, unsigned & maxAlignment ) {
Win4Assert(_iOffsetWid != ULONG_MAX); // workid must be there
Win4Assert(_iOffsetRowStatus != ULONG_MAX); // row status must be there
_cbRowSize = RowMap.GetRowWidth();
//
// Be sure the alignment holds from row to row too.
//
if (maxAlignment && ALIGN(_cbRowSize, maxAlignment) != _cbRowSize) {
RowMap.ReserveRowSpace( _cbRowSize, ALIGN(_cbRowSize, maxAlignment) - _cbRowSize );
_cbRowSize = RowMap.GetRowWidth(); Win4Assert(ALIGN(_cbRowSize, maxAlignment) == _cbRowSize); }
Win4Assert(_cbRowSize >= sizeof (ULONG) && _cbRowSize < MAX_ROW_SIZE);
Win4Assert(TBL_PAGE_ALLOC_MIN > _cbRowSize); _DataAllocator.SetRowSize(_cbRowSize);
tbDebugOut(( DEB_ITRACE, "New CTableWindow %08x, Columns: %d\tRowSize: %d\n", this, _Columns.Count(), _cbRowSize ));
_InitSortComparators(); }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::~CTableWindow, public
//
// Synopsis: Destructor for a window. Deallocates any memory,
// and releases resources.
//
// Notes: All work is done by sub-object destructors
//
//--------------------------------------------------------------------------
CTableWindow::~CTableWindow(void) { tbDebugOut(( DEB_WINSPLIT, "Destroying Window 0x%X\n", GetSegId() ));
// Win4Assert(_cbHeapUsed == 0);
}
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::_AddColumnDesc, private
//
// Synopsis: Add a column description to a table
//
// Arguments: [MasterCol] - a reference to a master column description
// for the column to be added
// [RowMap] - a reference to a row allocation map for
// allocating row data
// [maxAlignment] - maximumn alignment constraint for the
// allocated row
//
// Returns: Nothing
//
// Effects: Space is allocated in the RowMap, maxAlignment is
// adjusted.
//
// Notes:
//
//--------------------------------------------------------------------------
void CTableWindow::_AddColumnDesc( CColumnMasterDesc& MasterCol, CTableRowAlloc& RowMap, unsigned& maxAlignment ) { VARTYPE vt = MasterCol.DataType; if (vt == VT_NULL) vt = VT_VARIANT;
//
// The data type VT_EMPTY is used for columns like bookmark which
// may occur in the master column set, but which has no data
// stored in the table. Don't even bother adding it to the table
// column set.
//
if (vt == VT_EMPTY) return;
XPtr<CTableColumn> xTableCol( new CTableColumn( MasterCol.PropId, vt ) );
_cbHeapUsed += sizeof CTableColumn;
// If we can make this partial deferred, do so and get out
if ( _fCanPartialDefer && _CanPartialDeferCol( xTableCol ) ) {
tbDebugOut(( DEB_ITRACE, "Setting col as partial deferred propid = %x\n", xTableCol->GetPropId()));
xTableCol->SetPartialDeferred( TRUE );
_Columns.Add( xTableCol.GetPointer(), _Columns.Count() ); xTableCol.Acquire();
return; }
USHORT cbData, cbAlignment, rgfFlags;
CTableVariant::VartypeInfo(vt, cbData, cbAlignment, rgfFlags); Win4Assert(cbData != 0);
if (cbData == 0) { tbDebugOut(( DEB_WARN, "Unknown variant type %4x for propid %d\n", vt, MasterCol.PropId )); }
if (MasterCol.IsCompressedCol()) { //
// Global compression; save a pointer to the
// compressor.
//
xTableCol->SetGlobalCompressor( MasterCol.GetCompressor(), MasterCol.GetCompressMasterId() );
if (xTableCol->GetCompressMasterId() != 0) { //
// Look up the masterID, point the data offset and width
// to it. Shadow the existing column.
//
BOOL fFound = FALSE; CTableColumn* pColumn = _Columns.Find(MasterCol.GetCompressMasterId(), fFound); Win4Assert(fFound == TRUE);
xTableCol->SetValueField( vt, pColumn->GetValueOffset(), pColumn->GetValueSize() ); cbData = 0; // indicate no new data needed
Win4Assert( pColumn->IsStatusStored() );
xTableCol->SetStatusField( pColumn->GetStatusOffset(), sizeof (BYTE) ); } else { cbData = cbAlignment = sizeof (ULONG); } }
if (cbAlignment) { if (cbAlignment > maxAlignment) maxAlignment = cbAlignment; } else { cbAlignment = 1; }
if (cbData != 0) { xTableCol->SetValueField( vt, RowMap.AllocOffset( cbData, cbAlignment, TRUE ), cbData);
// Always add a status byte -- even storage props may not be present
// for summary catalogs. For others, need to know if the value is
// deferred.
xTableCol->SetStatusField( RowMap.AllocOffset( sizeof (BYTE), sizeof (BYTE), TRUE ), sizeof (BYTE)); }
if (xTableCol->PropId == pidWorkId) { _iOffsetWid = xTableCol->GetValueOffset(); xTableCol->MarkAsSpecial(); } else if (xTableCol->PropId == pidRank) { _iOffsetRank = xTableCol->GetValueOffset(); } else if (xTableCol->PropId == pidHitCount) { _iOffsetHitCount = xTableCol->GetValueOffset(); } else if (xTableCol->PropId == pidRowStatus) { _iOffsetRowStatus = xTableCol->GetValueOffset(); xTableCol->MarkAsSpecial(); } else if (xTableCol->PropId == pidChapter) { _iOffsetChapter = xTableCol->GetValueOffset(); xTableCol->MarkAsSpecial(); } else if (xTableCol->PropId == pidSelf) { xTableCol->MarkAsSpecial(); } else if ( xTableCol->IsCompressedCol() && 0 != xTableCol->GetCompressMasterId() ) { xTableCol->MarkAsSpecial(); }
#if CIDBG==1
if ( xTableCol->IsValueStored() ) Win4Assert( xTableCol->IsStatusStored() ); #endif
_Columns.Add( xTableCol.GetPointer(), _Columns.Count() ); xTableCol.Acquire(); } //_AddColumnDesc
//+---------------------------------------------------------------------------
//
// Function: CTableWindow::_AddColumnDesc, private
//
// Synopsis: Add a column description to the table based on another
// column description.
//
// Arguments: [srcTableCol] - The source upon which the new column table
// description is based
// [RowMap] - a reference to a row allocation map for
// allocating row data
// [maxAlignment] - maximumn alignment constraint for the
// allocated row.
//
// History: 2-07-95 srikants Created
// 11-Nov-99 KLam size check only valid for non-compressed
// columns
//
// Notes:
//
//----------------------------------------------------------------------------
void CTableWindow::_AddColumnDesc( CTableColumn& srcTableCol, CTableRowAlloc& RowMap, unsigned& maxAlignment ) { XPtr<CTableColumn> xTableCol( new CTableColumn(srcTableCol) );
_cbHeapUsed += sizeof CTableColumn;
// If partial deferred, just add and return
if ( xTableCol->IsPartialDeferred() ) { _Columns.Add( xTableCol.GetPointer(), _Columns.Count() ); xTableCol.Acquire();
return; }
VARTYPE vt = srcTableCol.GetStoredType();
USHORT cbData, cbAlignment, rgfFlags;
CTableVariant::VartypeInfo(vt, cbData, cbAlignment, rgfFlags);
// Globally compressed columns either have a size of 0 or the size of the key
// in the column
Win4Assert( cbData != 0 || ( srcTableCol.IsGlobalCompressedCol() && srcTableCol.GetCompressMasterId() ) ); Win4Assert( cbData == srcTableCol.GetValueSize() || srcTableCol.IsGlobalCompressedCol() );
if ( 0 == cbData ) { tbDebugOut(( DEB_WARN, "Unknown variant type %4x for propid %d\n", vt, srcTableCol.GetPropId() )); }
//
// Store strings by reference
//
if ( (rgfFlags & CTableVariant::ByRef) && !(rgfFlags & CTableVariant::StoreDirect)) { Win4Assert( 0 == ( vt & VT_BYREF ) ); }
if ( srcTableCol.IsCompressedCol() ) { if ( srcTableCol.IsGlobalCompressedCol() ) { if ( 0 != srcTableCol.GetCompressMasterId() ) { cbData = 0; // indicate no new data needed
} else { // This size is refering to the size of the compression key
cbData = cbAlignment = sizeof (ULONG); } } else {
//
// There is local compression in the source table.
//
if ( (pidName == srcTableCol.PropId) || (pidPath == srcTableCol.PropId ) ) { } else { //
// How did the local compression got set when it
// is not yet implemented.
//
Win4Assert( !"TableWindow - Local Compression - NYI " ); } } }
if (cbAlignment) { if (cbAlignment > maxAlignment) maxAlignment = cbAlignment; } else { cbAlignment = 1; }
//
// This SetValueField call is not necessary for setting the
// values in the column, since those have already been copied
// from the source column, but this has the important side-effect
// of updating the RowMap (which will presumably give back the
// same values as the original allocation).
//
if (cbData != 0) { xTableCol->SetValueField( vt, RowMap.AllocOffset( cbData, cbAlignment, TRUE ), cbData);
Win4Assert( xTableCol->GetValueOffset() == srcTableCol.GetValueOffset() );
// Always add a status byte -- even storage props may not be present
// for summary catalogs. For others, need to know if the value is
// deferred.
xTableCol->SetStatusField( RowMap.AllocOffset( sizeof (BYTE), sizeof (BYTE), TRUE ), sizeof (BYTE)); }
if (xTableCol->PropId == pidWorkId) { _iOffsetWid = xTableCol->GetValueOffset(); xTableCol->MarkAsSpecial(); } else if (xTableCol->PropId == pidRank) { _iOffsetRank = xTableCol->GetValueOffset(); } else if (xTableCol->PropId == pidHitCount) { _iOffsetHitCount = xTableCol->GetValueOffset(); } else if (xTableCol->PropId == pidRowStatus) { _iOffsetRowStatus = xTableCol->GetValueOffset(); xTableCol->MarkAsSpecial(); } else if (xTableCol->PropId == pidChapter) { _iOffsetChapter = xTableCol->GetValueOffset(); xTableCol->MarkAsSpecial(); } else if (xTableCol->PropId == pidSelf) { xTableCol->MarkAsSpecial(); } else if ( xTableCol->IsCompressedCol() && 0 != xTableCol->GetCompressMasterId() ) { xTableCol->MarkAsSpecial(); }
#if CIDBG==1
if ( xTableCol->IsValueStored() ) Win4Assert( xTableCol->IsStatusStored() ); #endif
_Columns.Add(xTableCol.GetPointer(), _Columns.Count()); xTableCol.Acquire(); }
//+---------------------------------------------------------------------------
//
// Member: CTableWindow::_PopulateRow, private
//
// Synopsis: Extracts row data from an object cursor into row storage
//
// Arguments: [obj] -- source of data
// [pThisRow] -- where to put the data
// [wid] -- workid of the row
//
// Returns: Nothing
//
//--------------------------------------------------------------------------
void CTableWindow::_PopulateRow( CRetriever & obj, BYTE * pThisRow, WORKID wid ) { Win4Assert(!(wid & 0x80000000)); // Bad work ID?
_SetRowWorkid(pThisRow, wid);
//
// Temporary buffer for column data. If it doesn't fit in this
// buffer, defer the load. The buffer is owned by CLargeTable
// and is only accessed under the lock taken in CLargeTable::PutRow
//
XUseSharedBuffer xSharedBuf(_sharedBuf); XArray<BYTE> xBuf;
CTableVariant* pvarnt = (CTableVariant *) xSharedBuf.LokGetBuffer(); unsigned cbBuf = xSharedBuf.LokGetSize();
for (unsigned i=0; i < _Columns.Count(); i++) { CTableColumn* pColumn = _Columns[ i ];
// Ignore PartailDeferred columns here. Retrieve data only when
// requested by the client
if ( pColumn->IsPartialDeferred() ) { continue; }
Win4Assert( pColumn->IsStatusStored() );
// Some columns have special processing and don't need to go through
// this loop.
if ( pColumn->IsSpecial() ) { pColumn->SetStatus( pThisRow, CTableColumn::StoreStatusOK ); continue; }
ULONG cbLength = cbBuf;
VARTYPE vt = pColumn->GetStoredType();
// it'd be a "special" column handled above if it were VT_EMPTY
Win4Assert( VT_EMPTY != vt );
GetValueResult eGvr = obj.GetPropertyValue( pColumn->PropId, pvarnt, &cbLength );
CTableColumn::StoreStatus stat = CTableColumn::StoreStatusOK;
switch ( eGvr ) { case GVRSuccess: break;
case GVRNotAvailable: pvarnt->vt = VT_EMPTY; stat = CTableColumn::StoreStatusNull; break;
case GVRNotEnoughSpace: { tbDebugOut(( DEB_ITRACE, "variant data too large for propid %d\n", pColumn->PropId ));
stat = CTableColumn::StoreStatusDeferred;
if ( pColumn->IsLengthStored() ) * (ULONG *) (pThisRow + pColumn->GetLengthOffset()) = cbLength; } break;
case GVRSharingViolation: pvarnt->vt = VT_EMPTY; stat = CTableColumn::StoreStatusNull; break;
default: //
// There was an error while retrieving the column from the
// retriever.
//
THROW( CException(CRetriever::NtStatusFromGVR(eGvr)) ); break; }
BYTE* pRowColDataBuf = pThisRow + pColumn->GetValueOffset(); ULONG cbRowColDataBuf = pColumn->GetValueSize();
RtlZeroMemory(pRowColDataBuf, cbRowColDataBuf);
Win4Assert( pColumn->IsStatusStored() );
if ( ( CTableColumn::StoreStatusOK == stat ) && ( VT_EMPTY == pvarnt->vt ) ) pColumn->SetStatus( pThisRow, CTableColumn::StoreStatusNull ); else pColumn->SetStatus( pThisRow, stat );
if ( CTableColumn::StoreStatusOK == stat ) { if ( pColumn->IsLengthStored() ) * (ULONG *) (pThisRow + pColumn->GetLengthOffset()) = cbLength;
//
// Store the property value in the table. The main cases
// handled below are:
//
// 1. The column is compressed. In this case, the column
// compressor will handle it.
// 2. The column is stored as a variant. Just store the
// value, as long as it's not too big.
// 3. The column is stored as a data value, and the property
// value is of the same type. Just store the data value.
// 4. The column is stored as a data value, and the property
// value is of a different type. In this case, attempt to
// convert the value to another type and store it. Otherwise
// fail.
// BROKENCODE - failure here could be the wrong thing to do. Other
// things that could be done include converting the column
// (might be the correct thing to do if it's a range error
// on a column which has been range compressed), or storing
// the value as an exception (might be the correct thing to
// do when it is a column which has been type compressed).
//
if (pColumn->IsCompressedCol()) { pColumn->GetCompressor()->AddData(pvarnt, cbRowColDataBuf? (ULONG *)pRowColDataBuf: 0, eGvr); } else { DBLENGTH ulTemp; pvarnt->CopyOrCoerce( pRowColDataBuf, cbRowColDataBuf, vt, ulTemp, _DataAllocator ); } } } } //_PopulateRow
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::PutRow, public
//
// Synopsis: Add a row to a large table
//
// Arguments: [obj] -- a pointer to an accessor which can
// return object data
//
// Returns: FALSE -- Don't need progress indication
//
// Returns: Nothing
//
//--------------------------------------------------------------------------
BOOL CTableWindow::PutRow( CRetriever& obj, CTableRowKey & currKey ) { WORKID wid = obj.WorkId();
//
// NOTE: Even if in the initial fill, first check to see if the row
// is already in the table. We may see the same work ID
// multiple times due to links.
//
TBL_OFF obRow; ULONG iRowOrdinal; BOOL fExisting = _FindWorkId(wid, obRow, iRowOrdinal); // Win4Assert( !fExisting &&
// "Modifications must be treated as deletions followed by additions" );
if ( fExisting ) { BYTE * pbOldRow = _DataAllocator.FixedPointer( obRow ); if ( TBL_DATA_PENDING_DELETE != _RowStatus(pbOldRow) ) { tbDebugOut(( DEB_WARN, "Not adding a linked object with wid 0x%X \n", wid )); return FALSE; } else { //
// There can be a pending delete only if there is a watch
// region.
//
Win4Assert( IsWatched() ); } }
BYTE * pThisRow = _RowAlloc();
Win4Assert(pThisRow != NULL);
_PopulateRow( obj, pThisRow, wid );
TBL_OFF oTableRow = _DataAllocator.FixedOffset(pThisRow);
CRowIndex & rowIndex = _GetInvisibleRowIndex();
ULONG iRowPos = rowIndex.AddRow(oTableRow);
tbDebugOut(( DEB_BOOKMARK, "CTableWindow::PutRow Add new row Wid 0x%X -- Segment 0x%X -- oTable 0x%X \n", wid, GetSegId(), oTableRow ));
_SetRowStatus(pThisRow,TBL_DATA_ROWADDED);
if ( !IsWatched() ) { //
// There are no notifications enabled for this window. So, we
// should add the entry to the book mark mapping.
//
_BookMarkMap.AddReplaceBookMark( wid, oTableRow ); } else { _xDeltaBookMarkMap->AddReplaceBookMark( wid, oTableRow ); }
if ( IsCategorized() ) { CCategParams params = { wid, widInvalid, widInvalid, chaptInvalid, chaptInvalid, 0, 0 };
if ( 0 != iRowPos ) { BYTE * pb = (BYTE *) _DataAllocator.FixedPointer( rowIndex.GetRow( iRowPos - 1) ); params.widPrev = RowWorkid( pb ); params.catPrev = _RowChapter( pb ); } if ( (iRowPos + 1) != rowIndex.RowCount() ) { BYTE * pb = (BYTE *) _DataAllocator.FixedPointer( rowIndex.GetRow( iRowPos + 1) ); params.widNext = RowWorkid( pb ); params.catNext = _RowChapter( pb ); }
if ( ( chaptInvalid == params.catPrev ) || ( params.catNext != params.catPrev ) ) { // new row is not sandwiched between rows in same category,
// so come up with positive column # where the new row
// differs from its neighbors.
if ( widInvalid != params.widPrev ) params.icmpPrev = _CompareRows( rowIndex.GetRow( iRowPos ), rowIndex.GetRow( iRowPos - 1 )); if ( widInvalid != params.widNext ) params.icmpNext = _CompareRows( rowIndex.GetRow( iRowPos + 1), rowIndex.GetRow( iRowPos )); }
_SetChapter( pThisRow, LokCategorize( params ) ); }
// Update Low and High Keys, if necessary, based on the rowpos of the new row
BOOL fReady = FALSE;
if ( 0 == iRowPos ) { // need to update lowkey
currKey.MakeReady(); fReady = TRUE; _lowKey = currKey; }
if ( RowCount() - 1 == iRowPos ) { // need to update highKey
if ( !fReady ) currKey.MakeReady(); _highKey = currKey; }
return FALSE; // no need for progress calculation
}
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::_FindWorkId, public
//
// Synopsis: Find the row associated with a work ID
//
// Arguments: [wid] -- work ID to be found
// [obRow] - set to the row offset from the base of the
// row data if the workid was found
// [riRowOrdinal] - set to the ordinal row number if the
// workid was found (the row index ordinal)
//
// Returns: BOOL - TRUE if wid is represented in this window, FALSE
// otherwise
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL CTableWindow::_FindWorkId( WORKID wid, TBL_OFF & obRow, ULONG & riRowOrdinal ) { //
// A linear search is made in the dynamic row index based on whether
// an entry exists in the bookmark mapping or not. This will eliminate
// searches in cases where the wid does not exist in the window.
//
BOOL fFound = FALSE;
if ( !IsWatched() ) { //
// The notifications are not enabled. The "visible" bookmark mapping
// is uptodate and has all the entries.
//
fFound = _BookMarkMap.FindBookMark( wid, obRow, riRowOrdinal ); } else { BOOL fLookInDynRowIndex = FALSE; //
// Since notifications are enabled, we must first look for the wid
// in the "delta" bookmark mapping and then in the "visible" bookmark
// mapping. If a "valid" bookmark mapping exists, then we must locate the
// row ordinal in the "dynamic" row index.
//
if ( _xDeltaBookMarkMap->FindBookMark(wid, obRow) ) { fLookInDynRowIndex = TRUE; } else if ( _BookMarkMap.FindBookMark(wid, obRow) ) { //
// If it is in the static book mark mapping, then only the
// rows with the TBL_DATA_OKAY are valid for further look up.
//
BYTE * pbRow = (BYTE *) _DataAllocator.FixedPointer(obRow); fLookInDynRowIndex = TBL_DATA_OKAY == _RowStatus(pbRow); }
if (fLookInDynRowIndex) fFound = _dynRowIndex.FindRow(obRow, riRowOrdinal); }
#if 0
//
// This is an expensive test. Re-add it if there are bugs
//
if ( !fFound ) { CRowIndex & srchRowIndex = _GetInvisibleRowIndex();
for (unsigned i = 0; i < srchRowIndex.RowCount(); i++) { BYTE * pbRow = (BYTE *) _DataAllocator.FixedPointer(srchRowIndex.GetRow(i));
Win4Assert(0 != pbRow); Win4Assert( _RowStatus(pbRow) != TBL_DATA_PENDING_DELETE && _RowStatus(pbRow) != TBL_DATA_SOFT_DELETE );
if (RowWorkid(pbRow) == wid) { Win4Assert( !"Must not be found" );
obRow = srchRowIndex.GetRow(i); riRowOrdinal = i; return TRUE; } } }
#endif
return fFound;
}
//+---------------------------------------------------------------------------
//
// Function: CTableWindow::FindBookMark, private
//
// Synopsis: Locates the requested bookmark using the bookmark mapping.
//
// Arguments: [wid] -- WorkId to locate
// [obRow] -- set to the row offset from the base of the
// row data if the workid was found
// [riRowOrdinal] -- set to the ordinal row number if the
// workid was found (the row index ordinal)
//
// History: 11-30-94 srikants Created
//
// Notes: This method differs from the _FindWorkId in that this uses
// the book mark mapping and _FindWorkId does not. When
// notifications are enabled for this window, we add rows to the
// "dynamic" row index but do not add the corresponding entry
// to the book mark mapping. In that case, we cannot use the
// BookMarkMap to locate the work id.
//
//----------------------------------------------------------------------------
BOOL CTableWindow::FindBookMark( WORKID wid, TBL_OFF & obRow, ULONG & riRowOrdinal ) {
BOOL fFound = FALSE;
if ( _visibleRowIndex.RowCount() > 0 ) { if ( WORKID_TBLFIRST == wid ) { riRowOrdinal = 0; obRow = _visibleRowIndex.GetRow(riRowOrdinal); fFound = TRUE; } else if ( WORKID_TBLLAST == wid ) { riRowOrdinal = _visibleRowIndex.RowCount()-1; obRow = _visibleRowIndex.GetRow(riRowOrdinal); fFound = TRUE; } else { fFound = _BookMarkMap.FindBookMark( wid, obRow, riRowOrdinal ); } }
return fFound;
}
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::IsRowInSegment, inline
//
// Synopsis: Determine if a row for some work ID exists in the table.
//
// Arguments: [wid] -- work ID to be found
//
// Returns: BOOL - TRUE if wid is represented in this window, FALSE
// otherwise
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL CTableWindow::IsRowInSegment(WORKID wid) { TBL_OFF iRowOffset; ULONG iRow; return _FindWorkId(wid, iRowOffset, iRow); }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::GetRows
//
// Synopsis: Return a set of row data to the caller
//
// Arguments: [hRegion] - region to be expanded
// [widStart] - WORKID identifying first row to be
// transferred. If WORKID_TBLBEFOREFIRST or
// WORKID_TBLFIRST is used, the transfer will
// start at the first row in the segment.
// [chapt] - Chapter from which to fetch rows (if chaptered)
// [rOutColumns] - a CTableColumnSet that describes the
// output format of the data table.
// [rGetParams] - an CGetRowsParams structure which
// describes how many rows are to be fetched and
// other parameters of the operation.
// [rwidLastRowTransferred] - on return, the work ID of
// the next row to be transferred from this table.
// Can be used as widStart on next call.
//
// Returns: SCODE - status of the operation. DB_S_ENDOFROWSET if
// widStart is WORKID_TBLAFTERLAST at start of
// transfer, or if rwidLastRowTransferred is
// the last row in the table segment at the end
// of the transfer, and not all requested rows were
// transferred.
// DB_S_BLOCKLIMITEDROWS if insufficient space is
// available in the pVarAllocator.
//
// Notes: Extends the watch region (if handle non-zero)
//
//--------------------------------------------------------------------------
SCODE CTableWindow::GetRows( HWATCHREGION hRegion, WORKID widStart, CI_TBL_CHAPT chapt, ULONG & cRowsToGet, CTableColumnSet const & rOutColumns, CGetRowsParams & rGetParams, WORKID& rwidLastRowTransferred ) {
tbDebugOut(( DEB_BOOKMARK, "CTableWindow::GetRows called with starting wid 0x%X\n", widStart ));
if (widStart == WORKID_TBLAFTERLAST) { rwidLastRowTransferred = WORKID_TBLAFTERLAST; tbDebugOut(( DEB_BOOKMARK, "CTableWindow::GetRows returning with 0x%X\n", DB_S_ENDOFROWSET )); return DB_S_ENDOFROWSET; }
if (widStart == widInvalid) { tbDebugOut(( DEB_BOOKMARK, "CTableWindow::GetRows returning with 0x%X\n", E_FAIL )); return E_FAIL; // maybe the wid got deleted???
}
ULONG iRowIndex = 0; // Starting row as an index in the row index
CRowIndex * pRowIndex = _BookMarkMap.GetRowIndex(); Win4Assert( pRowIndex == &_visibleRowIndex );
if ( widStart == WORKID_TBLLAST ) iRowIndex = pRowIndex->RowCount() - 1;
if (widStart != WORKID_TBLFIRST && widStart != WORKID_TBLBEFOREFIRST && widStart != WORKID_TBLLAST ) { TBL_OFF iRowOffset = 0; // Starting row as an offset into row data
BOOL fFound = FindBookMark(widStart, iRowOffset, iRowIndex);
if (!fFound) return E_FAIL; } else if ( IsCategorized() && DB_NULL_HCHAPTER != chapt ) { // turn special wids (first/last) into real wids in the chapter
if ( !rGetParams.GetFwdFetch() ) Win4Assert( !"Backwards fetch not yet implemented for categorized rowsets" );
TBL_OFF iRowOffset = 0; if ( !_FindWorkId( GetCategorizer()->GetFirstWorkid( chapt ), iRowOffset, iRowIndex ) ) { // must be a continuation of getrows from a previous window
iRowIndex = 0; } }
SCODE scResult = S_OK;
//
// iRowIndex is a ulong, and so after row 0 is fetched (in
// backwards fetch) we set fBwdRowsExhausted to true, instead
// decrementing 0, which is the value of iRowIndex.
// fBwdRowsExhausted is initialy true if there are 0 rows in this
// segment.
//
BOOL fBwdRowsExhausted = pRowIndex->RowCount() == 0;
//
// This flag is set to true, if rOutColumns contains at least one
// partially deferred column
//
BOOL fPartialDeferredCols = FALSE;
Win4Assert( 0 != pRowIndex );
TRY { const COUNT_CACHED_TABLE_COLS = 10; // Size of cached column array
CTableColumn *apTableCol[COUNT_CACHED_TABLE_COLS];
CTableColumn **pTableCols = apTableCol;
XArray<CTableColumn *> xaTableCol; if ( rOutColumns.Count() > COUNT_CACHED_TABLE_COLS ) { xaTableCol.Init( rOutColumns.Count() ); pTableCols = xaTableCol.GetPointer(); }
for (unsigned i=0; i < rOutColumns.Count(); i++) { CTableColumn const & outColumn = *rOutColumns[ i ];
BOOL fFound = FALSE; pTableCols[i] = _Columns.Find( outColumn.PropId, fFound ); Win4Assert(fFound == TRUE);
fPartialDeferredCols = ( fPartialDeferredCols || pTableCols[i]->IsPartialDeferred() ); }
//
// FRowsRemaining tests whether there are rows remaining.
// The test differs for forward and backwards fetch.
//
BOOL fRowsRemaining; if ( rGetParams.GetFwdFetch() ) fRowsRemaining = iRowIndex < pRowIndex->RowCount(); else fRowsRemaining = !fBwdRowsExhausted;
//
// Set up the cursor in case we have partial deferred column(s)
//
BOOL fAbort = FALSE;
if ( fPartialDeferredCols && _xCursor.IsNull() ) { _xCursor.Set( _QueryExecute.GetSingletonCursor( fAbort ) ); Win4Assert( _xCursor.GetPointer() ); }
while ( 0 != cRowsToGet && fRowsRemaining ) { BYTE *pRow = (BYTE *) _DataAllocator.FixedPointer(pRowIndex->GetRow(iRowIndex));
// If we've moved on to another chapter, stop retrieving rows
if ( IsCategorized() && DB_NULL_HCHAPTER != chapt && (_RowChapter(pRow) != chapt) ) { scResult = DB_S_ENDOFROWSET; break; }
BYTE* pbOutRow = (BYTE *)rGetParams.GetRowBuffer();
//
// Update RowId in xCursor if we have partial deferred column(s)
//
if ( fPartialDeferredCols ) { _xCursor->SetCurrentWorkId( RowWorkid( pRow ) ); Win4Assert( _xCursor->WorkId() != widInvalid ); }
_CopyColumnData( pRow, pbOutRow, pTableCols, rOutColumns, rGetParams.GetVarAllocator(), _xCursor.GetPointer() );
//
// We've transferred a row. Update pointers and counters.
//
rwidLastRowTransferred = RowWorkid((BYTE *)_DataAllocator. FixedPointer(pRowIndex->GetRow(iRowIndex)));
if ( rGetParams.GetFwdFetch() ) iRowIndex++; else { if ( iRowIndex == 0 ) fBwdRowsExhausted = TRUE; else iRowIndex--; }
rGetParams.IncrementRowCount(); cRowsToGet--;
if ( rGetParams.GetFwdFetch() ) fRowsRemaining = iRowIndex < pRowIndex->RowCount(); else fRowsRemaining = !fBwdRowsExhausted; } }
CATCH( CException, e ) { scResult = e.GetErrorCode();
if ( STATUS_BUFFER_TOO_SMALL == scResult && rGetParams.RowsTransferred() > 0 ) scResult = DB_S_BLOCKLIMITEDROWS; } END_CATCH
if (! _xCursor.IsNull() ) _xCursor->Quiesce( );
if (iRowIndex >= pRowIndex->RowCount() || fBwdRowsExhausted ) { Win4Assert(scResult != DB_S_BLOCKLIMITEDROWS); tbDebugOut(( DEB_BOOKMARK, "CTableWindow::GetRows End of table window\n" )); return DB_S_ENDOFROWSET; } else { tbDebugOut(( DEB_BOOKMARK, "CTableWindow::GetRows next wid 0x%X\n", rwidLastRowTransferred )); return scResult; } } //GetRows
//+---------------------------------------------------------------------------
//
// Member: CTableWindow::_CopyColumnData, private
//
// Synopsis: Goes thru the list of output columns and transfers
// data to pbOutRow for them. The input data either comes
// from pbSrcRow or in case of partially deferred column
// we retrive it from the CRetriever object
//
// Arguments: [pSrcRow] -- the src row
// [pThisRow] -- where to put the data
// [pTableCols] -- table(input) column set corr. to output columns
// [rOutColumns] -- output column set
// [rDstPool] -- destination variable data allocator
// [obj] -- source of data
//
// Returns: Nothing
//
//--------------------------------------------------------------------------
void CTableWindow::_CopyColumnData( BYTE* pbSrcRow, BYTE* pbOutRow, CTableColumn **pTableCols, CTableColumnSet const& rOutColumns, PVarAllocator& rDstPool, CRetriever* obj /* = NULL */ ) {
//
// Temporary buffer for column data. If it doesn't fit in this
// buffer, defer the load. The buffer is owned by CLargeTable.
// and is only accessed under the lock taken in CLargeTable::GetRowsAt
//
XUseSharedBuffer xSharedBuf(_sharedBuf); CTableVariant* pvarnt = (CTableVariant *) xSharedBuf.LokGetBuffer(); unsigned cbBuf = xSharedBuf.LokGetSize();
for (unsigned i=0; i < rOutColumns.Count(); i++) { CTableColumn * pColumn = pTableCols[i]; Win4Assert(pColumn);
CTableColumn* pOutColumn = rOutColumns[ i ]; Win4Assert(pOutColumn);
if ( !pColumn->IsPartialDeferred() ) { pColumn->CopyColumnData( pbOutRow, *pOutColumn, rDstPool, pbSrcRow, _DataAllocator ); continue; }
ULONG cbLength = cbBuf;
VARTYPE vt = pOutColumn->GetStoredType();
Win4Assert( obj );
GetValueResult eGvr = obj->GetPropertyValue( pColumn->PropId, pvarnt, &cbLength );
CTableColumn::StoreStatus stat = CTableColumn::StoreStatusOK;
switch ( eGvr ) { case GVRSuccess: break;
case GVRNotAvailable: pvarnt->vt = VT_EMPTY; stat = CTableColumn::StoreStatusNull; break;
case GVRNotEnoughSpace: { tbDebugOut(( DEB_ITRACE, "variant data too large for propid %d\n", pColumn->PropId ));
stat = CTableColumn::StoreStatusDeferred;
if ( pOutColumn->IsLengthStored() ) * (ULONG *) (pbOutRow + pOutColumn->GetLengthOffset()) = cbLength; } break;
case GVRSharingViolation: pvarnt->vt = VT_EMPTY; stat = CTableColumn::StoreStatusNull; break;
default: //
// There was an error while retrieving the column from the
// retriever.
THROW( CException(CRetriever::NtStatusFromGVR(eGvr)) ); break; }
BYTE* pRowColDataBuf = pbOutRow + pOutColumn->GetValueOffset(); ULONG cbRowColDataBuf = pOutColumn->GetValueSize();
RtlZeroMemory(pRowColDataBuf, cbRowColDataBuf);
Win4Assert( pOutColumn->IsStatusStored() );
if ( ( CTableColumn::StoreStatusOK == stat ) && ( VT_EMPTY == pvarnt->vt ) ) pOutColumn->SetStatus( pbOutRow, CTableColumn::StoreStatusNull ); else pOutColumn->SetStatus( pbOutRow, stat );
if ( CTableColumn::StoreStatusOK == stat ) { if ( pOutColumn->IsLengthStored() ) * (ULONG *) (pbOutRow + pOutColumn->GetLengthOffset()) = cbLength;
if (pOutColumn->IsCompressedCol()) { pOutColumn->GetCompressor()->AddData(pvarnt, cbRowColDataBuf? (ULONG *)pRowColDataBuf: 0, eGvr); } else { DBLENGTH ulTemp; pvarnt->CopyOrCoerce( pRowColDataBuf, cbRowColDataBuf, vt, ulTemp, rDstPool ); } } } }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::RemoveRow, public
//
// Synopsis: Removes a row from the table, if it exists.
//
// Arguments: [varUnique] -- Variant that uniquely identifies the row.
// [widNext] -- Returns workid of row immediately after
// deleted row, used for categorization.
// [chapt] -- Returns chapter of deleted row.
//
// Returns: TRUE if the row existed or FALSE otherwise
//
// History: 24 Oct 1994 dlee Created
//
// BROKENCODE/NEWFEATURE: update min/max wid? This code won't be called...
//
//--------------------------------------------------------------------------
BOOL CTableWindow::RemoveRow( PROPVARIANT const & varUnique, WORKID & widNext, CI_TBL_CHAPT & chapt ) { Win4Assert(varUnique.vt == VT_I4);
WORKID wid = (WORKID) varUnique.lVal;
TBL_OFF obRow; ULONG iRowOrdinal; BOOL fExisting = _FindWorkId(wid, obRow, iRowOrdinal);
if (fExisting) { BOOL fFirstRow = (0 == iRowOrdinal); BOOL fLastRow = (RowCount() - 1 == iRowOrdinal);
CRowIndex & srchRowIndex = _GetInvisibleRowIndex();
BYTE * pRow = (BYTE *) _DataAllocator.FixedPointer(obRow);
if ( IsCategorized() ) { chapt = _RowChapter( pRow );
if ( iRowOrdinal < ( srchRowIndex.RowCount() - 1 ) ) widNext = RowWorkid( _DataAllocator.FixedPointer( srchRowIndex.GetRow(iRowOrdinal + 1))); else widNext = widInvalid; }
const ULONG rowStatus = _RowStatus(pRow);
if ( TBL_DATA_ROWADDED == rowStatus || !IsWatched() ) { //
// This row has only been added but not notified to the
// user. So, we can go ahead and do a hard delete.
//
BOOL fFound = FALSE; if ( IsWatched() ) { fFound = _xDeltaBookMarkMap->DeleteBookMark( wid ); } else { Win4Assert( _xDeltaBookMarkMap.IsNull() || !_xDeltaBookMarkMap->IsBookMarkPresent(wid) ); Win4Assert( 0 == _dynRowIndex.RowCount() ); fFound = _BookMarkMap.DeleteBookMark( wid ); }
Win4Assert( fFound && "BookMark to be deleted not found" );
Win4Assert( obRow == srchRowIndex.GetRow(iRowOrdinal) ); srchRowIndex.DeleteRow(iRowOrdinal);
_cRowsHardDeleted++; _SetRowStatus(pRow,TBL_DATA_HARD_DELETE);
tbDebugOut(( DEB_WATCH, "Hard Deleting Workid 0x%X oTable %#p oIndex 0x%X\n", wid, obRow, iRowOrdinal ));
// Update low and high keys
if ( fFirstRow && fLastRow ) { // this means now RowCount == 0
// we should probably delete this segment
} else if ( fFirstRow ) { // the first row got deleted, therefore update lowKey
GetSortKey( 0, _lowKey ); } else if ( fLastRow ) { // the last row got deleted, therefore update highKey
GetSortKey( (ULONG) ( RowCount() - 1 ), _highKey ); } } else { //
// The existence of this row is known to the user. It is left in
// a pending delete state until a refresh is done.
// Updation of _highKey and _lowKey is also done at that time
Win4Assert( _BookMarkMap.IsBookMarkPresent(wid) ); Win4Assert( _xDeltaBookMarkMap.IsNull() || !_xDeltaBookMarkMap->IsBookMarkPresent(wid) );
_cPendingDeletes++; _SetRowStatus(pRow,TBL_DATA_PENDING_DELETE);
tbDebugOut(( DEB_WATCH, "Soft Deleting Workid 0x%X oTable %#p oIndex 0x%X\n", wid, obRow, iRowOrdinal )); } }
return fExisting; }
//+---------------------------------------------------------------------------
//
// Member: CTableWindow::IsPendingDelete
//
// Synopsis:
//
// Arguments: [wid] -
//
// Returns:
//
// Modifies:
//
// History: 8-01-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL CTableWindow::IsPendingDelete( WORKID wid ) { TBL_OFF iRowOffset;
BOOL fFound = _BookMarkMap.FindBookMark( wid, iRowOffset ); if ( fFound ) { BYTE * pbRow = (BYTE *) _DataAllocator.FixedPointer(iRowOffset); return TBL_DATA_PENDING_DELETE == _RowStatus(pbRow) ; }
return FALSE; }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::CompareRange, public
//
// Synopsis: Checks to see how a given potential row object would fit
// in this window based on the current sort criteria.
//
// Arguments: [obj] -- accessor to the potential row object
//
// Returns: -1 if < min item
// 0 if (<= max and >= min) or no items in window
// 1 if > max item
//
// History: 2 Sep 1994 dlee Created
//
//--------------------------------------------------------------------------
void CTableWindow::CompareRange( CRetriever &obj, CCompareResult & rslt ) {
//
// If there is a comparator and any rows for comparison
//
CRowIndex & srchRowIndex = _GetInvisibleRowIndex();
if ((0 != _RowCompare.GetPointer()) && (0 != srchRowIndex.RowCount())) {
int iComp = _RowCompare->CompareObject(obj,srchRowIndex.GetRow(0));
if (iComp > 0) { //
// Compare to the maximum row
// If > min and < max, it belongs in the range
//
iComp = _RowCompare->CompareObject(obj, srchRowIndex.GetRow(srchRowIndex.RowCount()-1));
if (iComp < 0) iComp = 0; }
rslt.Set( iComp ); } else { rslt.Set( CCompareResult::eUnknown ); }
} //CompareRange
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::_InitSortComparators, private
//
// Synopsis: Establishes the sort order used by the table.
//
// History: 19 Jan 1995 dlee Created from old SetSortOrderCode
//
//--------------------------------------------------------------------------
void CTableWindow::_InitSortComparators() { Win4Assert( 0 == _RowCompare.GetPointer() && 0 == _QuickRowCompare.GetPointer() );
Win4Assert( 0 != _pSortSet->Count() );
// Special-case quick comparators for one or two comparisons
// of basic types.
if (_pSortSet->Count() == 1) { SSortKey Key = _pSortSet->Get(0); BOOL fFound = FALSE;
CTableColumn* pColumn = _Columns.Find(Key.pidColumn,fFound);
Win4Assert(fFound);
//
// If inline, not compressed, and not a vector, it is a
// candidate for a faster comparison.
//
// pidWorkid can be compressed and stored in the row for
// the downlevel case.
if ((pColumn->PropId == pidWorkId || ! pColumn->IsCompressedCol()) && (0 == (pColumn->GetStoredType() & VT_VECTOR))) { if (pColumn->GetStoredType() == VT_I8 || pColumn->GetStoredType() == VT_FILETIME) _QuickRowCompare.Set( new TRowCompare<LONGLONG> (_DataAllocator, pColumn->GetValueOffset(), Key.dwOrder )); else if (pColumn->GetStoredType() == VT_I4) _QuickRowCompare.Set( new TRowCompare<LONG> (_DataAllocator, pColumn->GetValueOffset(), Key.dwOrder )); } } else if (_pSortSet->Count() == 2) { SSortKey Key1 = _pSortSet->Get(0); SSortKey Key2 = _pSortSet->Get(1); BOOL fFound = FALSE;
CTableColumn* pColumn1 = _Columns.Find(Key1.pidColumn,fFound); Win4Assert(fFound);
CTableColumn* pColumn2 = _Columns.Find(Key2.pidColumn,fFound); Win4Assert(fFound);
//
// If inline, not compressed, and not a vector, it is a
// candidate for a faster comparison.
//
// pidWorkid can be compressed and stored in the row for
// the downlevel case.
if ((! pColumn1->IsCompressedCol()) && (0 == (pColumn1->GetStoredType() & VT_VECTOR)) && (! pColumn2->IsCompressedCol() || pColumn2->PropId == pidWorkId ) && (0 == (pColumn2->GetStoredType() & VT_VECTOR))) { if (((pColumn1->GetStoredType() == VT_I8) || (pColumn1->GetStoredType() == VT_FILETIME)) && (pColumn2->GetStoredType() == VT_I4)) _QuickRowCompare.Set( new TRowCompareTwo<LONGLONG,LONG> (_DataAllocator, pColumn1->GetValueOffset(), Key1.dwOrder, pColumn2->GetValueOffset(), Key2.dwOrder )); else if ((pColumn1->GetStoredType() == VT_I4) && (pColumn2->GetStoredType() == VT_I4)) _QuickRowCompare.Set( new TRowCompareTwo<LONG,LONG> (_DataAllocator, pColumn1->GetValueOffset(), Key1.dwOrder, pColumn2->GetValueOffset(), Key2.dwOrder )); else if ((pColumn1->GetStoredType() == VT_UI4) && (pColumn2->GetStoredType() == VT_I4)) _QuickRowCompare.Set( new TRowCompareTwo<ULONG,LONG> (_DataAllocator, pColumn1->GetValueOffset(), Key1.dwOrder, pColumn2->GetValueOffset(), Key2.dwOrder )); else if ((pColumn1->GetStoredType() == VT_LPWSTR) && (pColumn2->GetStoredType() == VT_I4)) _QuickRowCompare.Set( new TRowCompareStringPlus<LONG> (_DataAllocator, pColumn1->GetValueOffset(), Key1.dwOrder, pColumn2->GetValueOffset(), Key2.dwOrder )); } }
//
// Make the default comparator
//
_RowCompare.Set( new CRowCompareVariant(*this) );
//
// Use a faster comparator if available, or just use the default
//
_dynRowIndex.SetComparator((0 != _QuickRowCompare.GetPointer()) ? _QuickRowCompare.GetPointer() : _RowCompare.GetPointer());
_visibleRowIndex.SetComparator((0 != _QuickRowCompare.GetPointer()) ? _QuickRowCompare.GetPointer() : _RowCompare.GetPointer());
} //_InitSortComparators
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::SortOrder, private
//
// Synopsis: Only here because of class hierarchy artifact.
//
// Returns: CSortSet & -- sort set from parent object.
//
// History: 24 Oct 1994 dlee Created
//
//--------------------------------------------------------------------------
CSortSet const & CTableWindow::SortOrder() { return * _pSortSet; } //SortOrder
//+---------------------------------------------------------------------------
//
// Member: CTableWindow::_ReconcileRowIndexes
//
// Synopsis: Reconciles the source and destination row indexes. At the end
// of it both the source and destination will be identical. Also,
// all the deleted rows would have been removed from the row
// indexes.
//
// Arguments: [src] -
// [dst] -
//
// History: 7-31-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
void CTableWindow::_ReconcileRowIndexes( CRowIndex & dst , CRowIndex & src ) { ULONG cRowsInSrc = src.RowCount();
dst.ResizeAndInit( cRowsInSrc ); for ( unsigned i = 0; i < cRowsInSrc ; i++ ) { TBL_OFF oRow = src.GetRow(i); BYTE * pbRow = _GetRow( oRow ); const ULONG rowStatus = _RowStatus( pbRow );
//
// By the time reconcile is called, all pending deletes must be
// converted to hard deletes.
//
Win4Assert( TBL_DATA_PENDING_DELETE != rowStatus );
if ( TBL_DATA_OKAY == rowStatus ) { dst.AppendRow( oRow ); } else { tbDebugOut(( DEB_BOOKMARK, "Not Copying Row 0x%X because of status 0x%X\n", oRow, rowStatus )); } }
src.SyncUp( dst );
// Update Low and High Keys
if ( dst.RowCount() ) { GetSortKey( 0, _lowKey ); GetSortKey( dst.RowCount() - 1, _highKey ); }
}
//+---------------------------------------------------------------------------
//
// Member: CTableWindow::_CleanupAndReconcileRowIndexes
//
// Synopsis:
//
// Arguments: [fCreatingWatch] -
// [pScript] -
//
// Returns:
//
// Modifies:
//
// History: 7-27-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
void CTableWindow::_CleanupAndReconcileRowIndexes( BOOL fCreatingWatch ) {
Win4Assert( !_fSplitInProgress );
//
// First see how many rows have notification information
//
ULONG cAllocatedRows = _AllocatedRowCount();
BYTE *pbRow = (BYTE *) _DataAllocator.FirstRow();
ULONG cAdd = 0, cDelete = 0;
for (unsigned i = 0; i < cAllocatedRows; i++, pbRow += _cbRowSize) { switch (_RowStatus(pbRow)) { case TBL_DATA_ROWADDED : cAdd++; break; case TBL_DATA_PENDING_DELETE : cDelete++; break; } }
//
// Record the notification info
//
ULONG cChanges = cAdd + cDelete; ULONG iChange = 0;
for (i = 0, pbRow = (BYTE *) _DataAllocator.FirstRow(); iChange < cChanges && i < cAllocatedRows; i++, pbRow += _cbRowSize) {
ULONG currStatus = _RowStatus(pbRow); switch (currStatus) { case TBL_DATA_ROWADDED :
//
// Update the book mark mapping to reflect the new row if
// notifications were enabled before this.
//
if ( !fCreatingWatch ) { _BookMarkMap.AddReplaceBookMark( RowWorkid(pbRow), _DataAllocator.FixedOffset(pbRow) ); }
_SetRowStatus(pbRow, TBL_DATA_OKAY); tbDebugOut(( DEB_WATCH, "Notify:Add Wid 0x%X TblRow0x%X\n", RowWorkid(pbRow), _DataAllocator.FixedOffset(pbRow) )); iChange++; break;
case TBL_DATA_PENDING_DELETE :
Win4Assert( !fCreatingWatch );
//
// Mark the row so that it is considered a hard delete and
// also remove it from the bookmark mapping.
//
_SetRowStatus(pbRow,TBL_DATA_HARD_DELETE); _BookMarkMap.DeleteBookMark( RowWorkid(pbRow) );
_cPendingDeletes--; _cRowsHardDeleted++;
tbDebugOut(( DEB_WATCH, "Notify:Delete Wid 0x%X TblRow0x%X\n", RowWorkid(pbRow), _DataAllocator.FixedOffset(pbRow) ));
iChange++; break; }
#if DBG==1
//
// This is a very expensive test. Turn it off once code stabilizes.
//
{ //
// If any row in the tablewindow has a stats of TBL_DATA_OKAY, it
// must have an entry in the bookmark map now.
//
TBL_OFF oTableRow; ULONG status = _RowStatus(pbRow);
if ( TBL_DATA_OKAY == status ) { BOOL fFound = _BookMarkMap.FindBookMark( RowWorkid(pbRow), oTableRow ); if ( !fFound ) { _BookMarkMap.FindBookMark( RowWorkid(pbRow), oTableRow ); Win4Assert( !"Not Found in BookMarkMap" ); } TBL_OFF tbRow = _DataAllocator.FixedOffset(pbRow); if ( tbRow != oTableRow ) { Win4Assert( !"Wrong Data in BookMarkMap" ); } } } #endif // DBG
}
//
// We should have processed all the changes.
//
Win4Assert( iChange == cChanges ); Win4Assert( 0 == _cPendingDeletes );
//
// Synchronize the static and dynamic row indexes.
//
if ( fCreatingWatch ) { //
// Watch is being created for the first time. Selectively copy
// only the non-soft deleted rows from the visible row index to
// the dynamic row index.
//
_ReconcileRowIndexes( _dynRowIndex , _visibleRowIndex ); } else { //
// Reconcile the dynamic row index to the visible row index and
// then get rid of the soft deletion entries from the dynamic
// row index.
//
_ReconcileRowIndexes( _visibleRowIndex , _dynRowIndex ); }
_xDeltaBookMarkMap->DeleteAllEntries();
#if DBG==1
//
// This is a very expensive test. Take it out once code stabilizes.
//
for ( unsigned j = 0; j < _visibleRowIndex.RowCount(); j++ ) { TBL_OFF oTableRow = _visibleRowIndex.GetRow(j); BYTE * pbRow = (BYTE *) _DataAllocator.FixedPointer(oTableRow); WORKID wid = RowWorkid(pbRow); TBL_OFF bmTableRow; ULONG bmIndex; BOOL fFound = _BookMarkMap.FindBookMark( wid, bmTableRow, bmIndex ); if ( !fFound ) { _BookMarkMap.FindBookMark( RowWorkid(pbRow), bmTableRow, bmIndex ); tbDebugOut(( DEB_ERROR, "Workid 0x%X Not Found In BookMarkMap\n", wid )); Win4Assert( !"Verification:Not Found in BookMarkMap" ); }
if ( bmTableRow != oTableRow || bmIndex != j ) { tbDebugOut(( DEB_ERROR, "Mismatch in BookMarkMap oRow=%p:iRowIndex=%x ", bmTableRow, bmIndex )); tbDebugOut(( DEB_ERROR|DEB_NOCOMPNAME, "and RowIndex oRow=%p:iRowIndex=%x\n", oTableRow, j ));
Win4Assert( !"Verification:Wrong Data in BookMarkMap" ); } } #endif // DBG==1
}
//+---------------------------------------------------------------------------
//
// Member: CTableWindow::_StartStaticDynamicSplit
//
// Synopsis:
//
// Returns:
//
// Modifies:
//
// History: 7-27-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
void CTableWindow::_StartStaticDynamicSplit() { _CleanupAndReconcileRowIndexes(TRUE); }
//+---------------------------------------------------------------------------
//
// Member: CTableWindow::_EndStaticDynamicSplit
//
// Synopsis:
//
// Returns:
//
// Modifies:
//
// History: 7-27-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
void CTableWindow::_EndStaticDynamicSplit() { _CleanupAndReconcileRowIndexes(FALSE); _dynRowIndex.ClearAll(); }
//+---------------------------------------------------------------------------
//
// Member: CTableWindow::_Refresh
//
// Synopsis:
//
// Returns:
//
// Modifies:
//
// History: 7-27-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
void CTableWindow::_Refresh() { _CleanupAndReconcileRowIndexes(FALSE); }
//+---------------------------------------------------------------------------
//
// Method: CTableWindow::_CopyRow
//
// Synopsis: Given a source window and a row offset in the source window,
// this method makes a copy of the source row and adds it to the
// current window.
//
// Arguments: [srcWindow] - Reference to the source window.
// [oSrcTableRow] - Offset in the source window.
//
// Returns: The offset of the row added in this window.
//
// History: 2-07-95 srikants Created
//
// Notes: There are four kinds of variant data in the rows:
//
// 1. In place data (eg. I1, I2, I4, I8, etc )
// 2. Pointer to global compression data.
// 3. Pointer to window specific compression data.
// 4. Pointer to window specific non-compressed data.
//
// Data fields of types 1 and 2 can be copied directly from the
// source row but types 3 and 4 have to be extracted from the
// source row and then copied.
//
// An underlying assumption is that rows in the source and
// target windows have the same layout.
//
//----------------------------------------------------------------------------
TBL_OFF CTableWindow::_CopyRow( CTableWindow & srcWindow, TBL_OFF oSrcTableRow ) {
BYTE * pSrcRow = srcWindow._GetRow( oSrcTableRow ); WORKID wid = srcWindow.RowWorkid( pSrcRow );
BYTE * pThisRow = _RowAlloc();
Win4Assert( _cbRowSize == srcWindow._cbRowSize && _Columns.Count() == srcWindow._Columns.Count());
//
// First copy the entire row and then fix up the variable length
// variant parts.
//
RtlCopyMemory( pThisRow, pSrcRow, _cbRowSize ); TBL_OFF oTableRow = _DataAllocator.FixedOffset(pThisRow);
//
// For each column, determine its type. If it is an in-place value or a
// globally compressed value, just ignore it as we have already copied the
// data. O/W, extract the variant and copy its contents.
//
for ( unsigned i=0; i < _Columns.Count(); i++) { CTableColumn * pDstColumn = _Columns.Get(i); CTableColumn * pSrcColumn = srcWindow._Columns.Get(i); Win4Assert( 0 != pDstColumn && 0 != pSrcColumn );
if ( pSrcColumn->IsPartialDeferred() ) { // No Data to copy here
continue; }
Win4Assert( pDstColumn->GetStoredType() == pSrcColumn->GetStoredType() && pDstColumn->GetValueOffset() == pSrcColumn->GetValueOffset() && pDstColumn->GetStatusOffset() == pSrcColumn->GetStatusOffset() && pDstColumn->GetLengthOffset() == pSrcColumn->GetLengthOffset() );
VARTYPE vtSrc = pSrcColumn->GetStoredType(); Win4Assert( pDstColumn->GetStoredType() == vtSrc );
USHORT cbData, cbAlignment, rgfFlags; CTableVariant::VartypeInfo(vtSrc, cbData, cbAlignment, rgfFlags);
if ( CTableVariant::TableIsStoredInline( rgfFlags, vtSrc ) || pSrcColumn->IsGlobalCompressedCol() || ! pSrcColumn->IsValueStored()) { //
// This data is either stored in-line or is a globally compressed
// data. We can skip and go the next field.
//
continue; }
DBSTATUS CopyStatus = pSrcColumn->CopyColumnData( pThisRow, *pDstColumn, _DataAllocator, pSrcRow, srcWindow._DataAllocator);
Win4Assert(DBSTATUS_S_OK == CopyStatus || DBSTATUS_S_ISNULL == CopyStatus);
} // for loop
return oTableRow; }
//+---------------------------------------------------------------------------
//
// Function: _PutRowToVisibleRowIndex
//
// Synopsis: Copies the given row to the table and adds an entry to the
// visible row index.
//
// Arguments: [srcWindow] - Reference to the source window.
// [oSrcRow] - Row Offset in the source window to be copied.
//
// History: 1-24-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
void CTableWindow::_PutRowToVisibleRowIndex( CTableWindow & srcWindow, TBL_OFF oSrcRow ) {
BYTE * pbRow = srcWindow._GetRow( oSrcRow );
//
// Add the rowdata and make an entry in the visible row index
// for this.
//
WORKID wid = RowWorkid( pbRow ); Win4Assert( !_BookMarkMap.IsBookMarkPresent(wid) );
TBL_OFF oTableRow = _CopyRow( srcWindow, oSrcRow );
_visibleRowIndex.AddRow(oTableRow); _BookMarkMap.AddBookMark( wid, oTableRow );
const ULONG status = _RowStatus(pbRow);
Win4Assert( TBL_DATA_HARD_DELETE != status );
Win4Assert( TBL_DATA_OKAY == status || TBL_DATA_PENDING_DELETE == status || TBL_DATA_ROWADDED == status );
if ( TBL_DATA_PENDING_DELETE == status ) { _cPendingDeletes++; } }
//+---------------------------------------------------------------------------
//
// Function: _PutRowToDynRowIndex
//
// Synopsis: Adds the given row to the dynamic row index. In addition, if
// the row is a "new" row and is not present in the table
// already, it will be added to the table also.
//
// Arguments: [srcWindow] - Source window
// [oSrcRow] - Offset of the row in the source window.
//
// History: 1-24-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
void CTableWindow::_PutRowToDynRowIndex( CTableWindow & srcWindow, TBL_OFF oSrcRow ) { BYTE * pbRow = srcWindow._GetRow( oSrcRow ); WORKID wid = srcWindow.RowWorkid( pbRow );
TBL_OFF oTableRow;
const ULONG rowStatus = srcWindow._RowStatus(pbRow);
Win4Assert( TBL_DATA_HARD_DELETE != rowStatus );
if ( TBL_DATA_OKAY == rowStatus || TBL_DATA_PENDING_DELETE == rowStatus ) { //
// Since the row status is OKAY or pending delete, it MUST have
// already been added via the _visibleRowIndex.
//
BOOL fFound = _BookMarkMap.FindBookMark( wid, oTableRow ); Win4Assert( fFound ); } else { //
// This row has not been added already via the visible row
// index. We should add it to the table.
//
Win4Assert( TBL_DATA_ROWADDED == rowStatus ); oTableRow = _CopyRow( srcWindow, oSrcRow ); Win4Assert( _xDeltaBookMarkMap.IsNull() || !_xDeltaBookMarkMap->IsBookMarkPresent(wid) ); _xDeltaBookMarkMap->AddBookMark( wid, oTableRow ); }
_dynRowIndex.AddRow( oTableRow ); }
//+---------------------------------------------------------------------------
//
// Function: IsEmptyForQuery
//
// Synopsis: Checks if the window is empty from query's perspective.
//
// Returns: TRUE if there are no entries in the "invisible" row index.
// FALSE o/w
//
// History: 2-07-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL CTableWindow::IsEmptyForQuery() { return _GetInvisibleRowIndex().RowCount() == 0; }
//+---------------------------------------------------------------------------
//
// Function: IsGettingFull
//
// Synopsis: Checks if the current window is getting full.
//
// Returns: TRUE if it is getting full.
// FALSE o/w
//
// History: 2-07-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL CTableWindow::IsGettingFull() { //
// PERFFIX - need a better heuristic based on memory usage.
//
return _GetInvisibleRowIndex().RowCount() >= CTableSink::cWindowRowLimit; }
unsigned CTableWindow::PercentFull() { return (_GetInvisibleRowIndex().RowCount() * 100) / CTableSink::cWindowRowLimit; }
//+---------------------------------------------------------------------------
//
// Function: GetSortKey
//
// Synopsis: Fills the requested window row as a "bktRow" by extracting
// only the "sort columns".
//
// Arguments: [iRow] - Index of the requested window row.
// [pvtOut] - Variants of the sort set.
// [bktRow] - (Output) will have the row in a "bucket row"
// form.
//
// History: 2-16-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
void CTableWindow::GetSortKey( ULONG iRow, CTableRowKey & sortKey ) { Win4Assert( 0 != _RowCompare.GetPointer() && iRow < _GetInvisibleRowIndex().RowCount() );
TBL_OFF oTableRow = _GetInvisibleRowIndex().GetRow(iRow);
Win4Assert( 0 != _pSortSet );
BYTE * pRow = _GetRow( oTableRow );
for ( unsigned i = 0; i < _pSortSet->Count(); i++ ) { const SSortKey & key = _pSortSet->Get(i); PROPID pidColumn = key.pidColumn;
BOOL fFoundCol = FALSE; CTableColumn * pColumn = _Columns.Find(pidColumn, fFoundCol); Win4Assert(fFoundCol == TRUE);
//
// Create a variant out of the data in the column and copy its contents
// to the output row.
//
CTableVariant varnt; CTableVariant* pvarnt; XCompressFreeVariant xpvarnt;
pvarnt = &varnt; if ( pColumn->CreateVariant( *pvarnt, pRow, _DataAllocator ) ) { //
// Variant needs to be freed by the column compressor.
//
xpvarnt.Set(pColumn->GetCompressor(), pvarnt); }
//
// Initialize the column in the bucket row with the data in the
// variant just extracted.
//
sortKey.Init( i, *pvarnt ); } }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::FindRegion, private
//
// Synopsis: Delete watch region
//
// Returns: The number of rows deleted from watch
//
// History: 22-Jun-95 BartoszM Created
//
//--------------------------------------------------------------------------
unsigned CTableWindow::FindRegion (HWATCHREGION hRegion) const { for (unsigned i = 0; i < _aWindowWatch.Count(); i++) { if (_aWindowWatch.Get(i)._hRegion == hRegion) break; } return i; }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::AddWatch
//
// Synopsis: Add watch region starting at offset
//
// Returns: The number of rows added to the watch
//
// History: 26-Jul-95 BartoszM Created
//
// Notes: If this is the last segment, add all cRows
// otherwise don't overshoot the end of window
//--------------------------------------------------------------------------
long CTableWindow::AddWatch ( HWATCHREGION hRegion, long iStart, LONG cRows, BOOL isLast) { Win4Assert( cRows > 0 );
long cRowsHere = cRows; if (!isLast && cRows > (long)_visibleRowIndex.RowCount() - iStart) { cRowsHere = (long)_visibleRowIndex.RowCount() - iStart; }
Win4Assert( cRowsHere >= 0 );
CWindowWatch watch; watch._hRegion = hRegion; watch._iRowStart = iStart; watch._cRowsHere = cRowsHere; watch._cRowsLeft = cRows; _AddWatchItem (watch); return cRowsHere; }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::DeleteWatch
//
// Synopsis: Delete watch region
//
// Returns: The number of rows deleted from watch
//
// History: 20-Jun-95 BartoszM Created
//
//--------------------------------------------------------------------------
long CTableWindow::DeleteWatch (HWATCHREGION hRegion) { unsigned i = FindRegion(hRegion); Win4Assert (i < _aWindowWatch.Count()); long count = _aWindowWatch.Get(i)._cRowsHere; _RemoveWatchItem (i); return count; }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::ModifyWatch
//
// Synopsis: Modify watch region
//
// Returns: The number of rows in the modified watch
//
// History: 20-Jun-95 BartoszM Created
//
//--------------------------------------------------------------------------
long CTableWindow::ModifyWatch (HWATCHREGION hRegion, long iStart, long cRows, BOOL isLast) { Win4Assert( cRows > 0 ); Win4Assert( 0 == iStart || iStart < (long) _visibleRowIndex.RowCount() );
unsigned i = FindRegion(hRegion); Win4Assert (i < _aWindowWatch.Count()); CWindowWatch& watch = _aWindowWatch.Get(i); long cRowsHere = cRows; if ( !isLast && cRows > (long)_visibleRowIndex.RowCount() - iStart ) { cRowsHere = (long)_visibleRowIndex.RowCount() - iStart; }
Win4Assert( cRowsHere >= 0 );
watch._iRowStart = iStart; watch._cRowsHere = cRowsHere; watch._cRowsLeft = cRows; return cRowsHere; }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::ShrinkWatch
//
// Synopsis: Shrink watch region so that it starts with the
// bookmark and extends no farther than cRows
//
// Returns: The number of rows left in the region
//
// History: 21-Jun-95 BartoszM Created
//
//--------------------------------------------------------------------------
long CTableWindow::ShrinkWatch (HWATCHREGION hRegion, CI_TBL_BMK bookmark, LONG cRows) { Win4Assert( cRows > 0 );
// find the bookmark
TBL_OFF off; ULONG uiStart; if ( !FindBookMark( bookmark, off, uiStart)) { Win4Assert (!"CTableWindow::ShrinkWatch, bookmark not found" ); }
long iStart = (long)uiStart;
unsigned i = FindRegion(hRegion); Win4Assert (i < _aWindowWatch.Count());
CWindowWatch& watch = _aWindowWatch.Get(i); // Assert that bookmark is within the watch region
Win4Assert (watch._iRowStart <= iStart); Win4Assert (watch._iRowStart + watch._cRowsHere > iStart);
long cRowsLeftHere = watch._cRowsHere - (iStart - watch._iRowStart); long cRowsLeft = watch._cRowsLeft - (iStart - watch._iRowStart);
Win4Assert( cRowsLeftHere > 0 ); Win4Assert( cRowsLeft > 0 );
watch._iRowStart = iStart; watch._cRowsHere = min (cRowsLeftHere, cRows); watch._cRowsLeft = min (cRowsLeft, cRows); return watch._cRowsHere; }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::ShrinkWatch
//
// Synopsis: Shrink watch region so that it extends no farther than cRows
// The assumption is that the beginning of the region
// is at the beginning of the window.
//
// Returns: The number of rows left in the region
//
// History: 21-Jun-95 BartoszM Created
//
//--------------------------------------------------------------------------
long CTableWindow::ShrinkWatch (HWATCHREGION hRegion, LONG cRows) { unsigned i = FindRegion(hRegion); Win4Assert (i < _aWindowWatch.Count()); Win4Assert (_aWindowWatch.Get(i)._iRowStart == 0); CWindowWatch& watch = _aWindowWatch.Get(i);
Win4Assert( cRows > 0 ); Win4Assert( watch._cRowsHere >= 0 ); Win4Assert( watch._cRowsLeft > 0 );
watch._cRowsHere = min (watch._cRowsHere, cRows); watch._cRowsLeft = min (watch._cRowsLeft, cRows); return watch._cRowsHere; }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::IsWatched
//
// Synopsis: Returns TRUE if the window has the watch region hRegion
// and the bookmark is within this region
//
// History: 21-Jun-95 BartoszM Created
//
//--------------------------------------------------------------------------
BOOL CTableWindow::IsWatched (HWATCHREGION hRegion, CI_TBL_BMK bookmark) { unsigned i = FindRegion(hRegion);
if (i == _aWindowWatch.Count()) return FALSE; // find the bookmark
TBL_OFF off; ULONG iBmk; if ( !FindBookMark( bookmark, off, iBmk)) { return FALSE; } long iWatchStart = _aWindowWatch.Get(i)._iRowStart; long iWatchEnd = iWatchStart + _aWindowWatch.Get(i)._cRowsHere; return iWatchStart <= (long)iBmk && (long)iBmk < iWatchEnd; }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::HasWatch
//
// Synopsis: Returns TRUE if the window has the watch region hRegion
//
// History: 21-Jun-95 BartoszM Created
//
//--------------------------------------------------------------------------
BOOL CTableWindow::HasWatch (HWATCHREGION hRegion) { unsigned i = FindRegion(hRegion); return i < _aWindowWatch.Count(); }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::RowsWatched
//
// Synopsis: Returns the number of rows in the watch region hRegion
//
// History: 22-Jun-95 BartoszM Created
//
//--------------------------------------------------------------------------
long CTableWindow::RowsWatched (HWATCHREGION hRegion) { unsigned i = FindRegion(hRegion); if ( i < _aWindowWatch.Count() ) return _aWindowWatch.Get(i)._cRowsHere;
return 0; }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::_AddWatchItem
//
// Synopsis: Adds a new watch item to list and
// changes the state of the window if necessary
//
// History: 26-Jul-95 BartoszM Created
//
//--------------------------------------------------------------------------
void CTableWindow::_AddWatchItem (CWindowWatch& watch) { _aWindowWatch.Add (watch, _aWindowWatch.Count()); if ( _aWindowWatch.Count() == 1 && !_fSplitInProgress ) { _StartStaticDynamicSplit(); } }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::_RemoveWatcheItem
//
// Synopsis: Removes a watch item from the list and
// changes the state of the window if necessary
//
// History: 26-Jul-95 BartoszM Created
//
//--------------------------------------------------------------------------
void CTableWindow::_RemoveWatchItem (unsigned i) { _aWindowWatch.Remove(i); if (_aWindowWatch.Count() == 0) { _EndStaticDynamicSplit(); } }
BOOL CTableWindow::FindNearestDynamicBmk( CI_TBL_CHAPT chapter, CI_TBL_BMK & bookMark ) { // If the row corresponding to the bookmark exists in the
// dynamic state, return the original bookmark.
// If the row has been deleted from the dynamic state
// return the bookmark of the next available dynamic row.
// If you hit the end of the table, return the bookmark
// of the last row in the dynamic state of the table
//
// NEWFEATURE - does not understand categorization .
//
Win4Assert( IsWatched() );
if ( WORKID_TBLFIRST == bookMark || WORKID_TBLLAST == bookMark ) { return TRUE; }
TBL_OFF iRowOffset;
if ( _xDeltaBookMarkMap->FindBookMark(bookMark, iRowOffset) ) { //
// The row is present in the delta bookmark map. Just return
// it.
//
return TRUE; }
//
// Locate the closest row in the dynamic bookmark mapping
// for the row.
//
BOOL fFound = _BookMarkMap.FindBookMark(bookMark, iRowOffset); Win4Assert( fFound );
//
// Locate the row offset in the dynamic row index and find the
// closest row which is not in a pending delete state.
//
ULONG iRowOrdinal; fFound = _dynRowIndex.FindRow(iRowOffset,iRowOrdinal); Win4Assert( fFound );
for ( ULONG i = iRowOrdinal; i < _dynRowIndex.RowCount(); i++ ) { BYTE * pbRow = (BYTE *) _DataAllocator.FixedPointer( _dynRowIndex.GetRow(i) );
ULONG status = _RowStatus(pbRow); if ( TBL_DATA_PENDING_DELETE != status ) { Win4Assert( TBL_DATA_HARD_DELETE != status ); bookMark = RowWorkid(pbRow); return TRUE; } }
//
// Try to the left of the bookmark.
//
for ( long j = (long) iRowOrdinal-1; j >=0; j-- ) { BYTE * pbRow = (BYTE *) _DataAllocator.FixedPointer( _dynRowIndex.GetRow( (ULONG) j) );
ULONG status = _RowStatus(pbRow); if ( TBL_DATA_PENDING_DELETE != status ) { Win4Assert( TBL_DATA_HARD_DELETE != status ); bookMark = RowWorkid(pbRow); return TRUE; } }
return FALSE; }
BOOL CTableWindow::FindFirstNonDeleteDynamicBmk( CI_TBL_BMK & bmk ) {
CRowIndex & rowIndex = _GetInvisibleRowIndex();
for ( unsigned i = 0; i < rowIndex.RowCount(); i++ ) { BYTE * pbRow = (BYTE *) _DataAllocator.FixedPointer( rowIndex.GetRow(i) ); if ( TBL_DATA_PENDING_DELETE != _RowStatus(pbRow) ) { Win4Assert( TBL_DATA_HARD_DELETE != _RowStatus(pbRow) ); bmk = RowWorkid(pbRow); return TRUE; } }
return FALSE; }
BOOL CTableWindow::FindLastNonDeleteDynamicBmk( CI_TBL_BMK & bmk ) {
CRowIndex & rowIndex = _GetInvisibleRowIndex();
for ( long i = (long) rowIndex.RowCount()-1; i >= 0; i-- ) { BYTE * pbRow = (BYTE *) _DataAllocator.FixedPointer( rowIndex.GetRow( (ULONG) i) ); if ( TBL_DATA_PENDING_DELETE != _RowStatus(pbRow) ) { Win4Assert( TBL_DATA_HARD_DELETE != _RowStatus(pbRow) ); bmk = RowWorkid(pbRow); return TRUE; } }
return FALSE; }
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::LokGetOneColumn
//
// Synopsis: Fills a buffer with a particular column from a row
//
// Arguments: [wid] -- workid of the row to be queried
// [rOutColumn] -- layout of the output data
// [pbOut] -- where to write the column data
// [rVarAlloc] -- variable data allocator to use
//
// History: 22-Aug-95 dlee Created
//
//--------------------------------------------------------------------------
void CTableWindow::LokGetOneColumn( WORKID wid, CTableColumn const & rOutColumn, BYTE * pbOut, PVarAllocator & rVarAllocator ) { TBL_OFF oRow; ULONG iRow; BOOL fFound = FindBookMark( wid, oRow, iRow );
Win4Assert( fFound && "LokGetOneColumn could not find bmk" );
BYTE *pbRow = (BYTE *) _DataAllocator.FixedPointer( oRow );
CTableColumn * pColumn = _Columns.Find( rOutColumn.PropId );
pColumn->CopyColumnData( pbOut, rOutColumn, rVarAllocator, pbRow, _DataAllocator ); } //LokGetOneColumn
//+-------------------------------------------------------------------------
//
// Member: CTableWindow::IsFirstRowFirstOfCategory
//
// Synopsis: Returns TRUE if the first row in the window is also the
// first of a category. Needed for window selection in PutRow
//
// History: 6-Nov-95 dlee Created
//
//--------------------------------------------------------------------------
BOOL CTableWindow::IsFirstRowFirstOfCategory() { CRowIndex & ri = _GetInvisibleRowIndex();
if ( 0 != ri.RowCount() ) { BYTE *pbRow = _GetRowFromIndex( 0, ri ); ULONG chapt = _RowChapter( pbRow ); WORKID wid = RowWorkid( pbRow );
CCategorize & Cat = * GetCategorizer();
return ( Cat.GetFirstWorkid( chapt ) == wid ); }
return FALSE; } //IsFirstRowFirstOfCategory
|