//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 2000. // // File: tputget.cxx // // Contents: Classes to put, get and locate rows in the bigtable. // // Classes: CTableRowPutter, CTableRowGetter, CTableRowLocator // // Functions: // // History: 4-17-95 srikants Created // //---------------------------------------------------------------------------- #include "pch.cxx" #pragma hdrstop #include "tputget.hxx" #include "tblwindo.hxx" #include "tblbuket.hxx" #include "winsplit.hxx" #include "tabledbg.hxx" #include "query.hxx" #include "regtrans.hxx" //+--------------------------------------------------------------------------- // // Function: _LokShouldUseNext // // Synopsis: Tests whether the segment should be use for a putrow. // // Arguments: [icmp] -- compare result of current row to first of pSeg // [pSeg] -- the segment in question // // Returns: TRUE if new row is in same category as the first row in // [pSeg] and if the first row in [pSeg] is the first of its // category. // // History: 11-7-95 dlee Created // //---------------------------------------------------------------------------- BOOL CTableRowPutter::_LokShouldUseNext( int icmp, CTableSegment * pSeg ) { Win4Assert( _largeTable.IsCategorized() ); CTableWindow *pWin = (CTableWindow *) pSeg; return ( ( ( (unsigned) (- icmp ) ) > _largeTable._cCategorizersTotal ) && ( pWin->IsFirstRowFirstOfCategory() ) ); } //+--------------------------------------------------------------------------- // // Function: _IsHintGood // // Synopsis: Tests whether the segment passed in as a hint or one of // its immediate neighbors can be used to insert the new row. // // Arguments: [pSegHint] - The input segment which is the hint. // // Returns: Pointer to either the hint or one of its neighbors if the // row can be inserted into them. NULL if the hint is not good. // // History: 4-17-95 srikants Created // // Notes: // //---------------------------------------------------------------------------- CTableSegment * CTableRowPutter::_IsHintGood( CTableSegment * pSegHint ) { if ( 0 == pSegHint ) return 0; CTableSegment * pResult = 0; Win4Assert( pSegHint->GetLowestKey().IsInitialized() ); int icmp = _comparator.Compare( _currRow, pSegHint->GetLowestKey() ); if ( icmp >= 0 ) { // // The current key is >= the lowest key in the hint. See if this is // less than the key in the next segment. // if ( _segList.GetLast() != pSegHint ) { CTableSegment * pNext = _segList.GetNext( pSegHint ); int icmpNext = _comparator.Compare( _currRow, pNext->GetLowestKey() ); if ( icmpNext < 0 ) { // If categorized AND // new row in same category as first row of next window AND // first row of next window is first of its category // use the next window. Otherwise use the current window. // if ( ( _largeTable.IsCategorized() ) && ( _LokShouldUseNext( icmpNext, pNext ) ) ) { tbDebugOut(( DEB_ITRACE, "updated hint window for categorized putrow\n" )); pResult = pNext; } else { // // The current row is < the lowest key in the next segment. // It is also not in the same category as the lowest key // in the next segment. // So, it is a good candidate. // pResult = pSegHint; } } } else { pResult = pSegHint; } } else if ( _segList.GetFirst() == pSegHint ) { // // We are at the first segment and the current row is < the first // segment. So, just use it. // pResult = pSegHint; } return pResult; } //+--------------------------------------------------------------------------- // // Function: LokFindSegToInsert // // Synopsis: Finds the segment to insert the current row. // // Arguments: [pSegHint] - Segment used as a hint. // // Returns: The segment into which the row must be inserted. // // History: 4-17-95 srikants Created // //---------------------------------------------------------------------------- CTableSegment * CTableRowPutter::LokFindSegToInsert( CTableSegment * pSegHint ) { // // For the most common case, there will be only one segment. Just use // that and don't setup _currRow. // if ( 1 == _segList.GetSegmentsCount() ) { Win4Assert( 0 == pSegHint || _segList.GetFirst() == pSegHint ); return _segList.GetFirst(); } // // Initialize the sort key and use that to compare with various segments. // _currRow.MakeReady(); if ( _segList.IsEmpty() ) { CTableWindow * pWindow = _largeTable._CreateNewWindow( _largeTable._AllocSegId(), _currRow, _currRow); _segListMgr.Push( pWindow ); tbDebugOut(( DEB_WINSPLIT, " Adding Window 0x%X to an empty list\n", pWindow )); return pWindow; } Win4Assert( 0 != _largeTable.GetSortSet() ); // // See if the hint is usable. // CTableSegment * pSeg = _IsHintGood( pSegHint ); if ( 0 == pSeg ) { // // Either there was no hint or the row did not belong there. // pSeg = _segListMgr.GetSegmentArray().LookUp( _currRow ); if ( ( _largeTable.IsCategorized() ) && ( _segList.GetLast() != pSeg ) ) { // Pick a window so that the unique categorizer can make the // assumption that: // - an insert at the end of a window either belongs to the // same category as the previous last row OR is a new // category // - an insert at the beginning of a window is the new first // member of the category of the first row in the window CTableSegment * pNext = _segList.GetNext( pSeg ); int icmpNext = _comparator.Compare( _currRow, pNext->GetLowestKey() ); Win4Assert( icmpNext < 0 ); if ( _LokShouldUseNext( icmpNext, pNext ) ) { tbDebugOut(( DEB_ITRACE, "updated window for categorized putrow\n" )); pSeg = pNext; } } // This assert will not be true when a row is the new first row // of both a segment and a category. if ( ! _largeTable.IsCategorized() ) { Win4Assert( _IsHintGood(pSeg) == pSeg ); } #if 0 CBackTableSegIter iter(_segList); Win4Assert( !_segList.AtEnd(iter) ); do { CTableSegment & segment = *iter.GetSegment(); Win4Assert( segment.GetLowestKey().IsInitialized() ); if ( _comparator.Compare(_currRow, segment.GetLowestKey()) >= 0 ) { // // The current row is >= the lowest key in this segment. We // can use it. // break; } else { _segList.BackUp(iter); } } while ( !_segList.AtEnd(iter) ); if ( _segList.AtEnd(iter) ) { // // We have gone past the end of the list. Just use the // first one. // pSeg = _segList.GetFirst(); Win4Assert( _comparator.Compare( _currRow, pSeg->GetLowestKey() ) < 0 ); } else { pSeg = iter.GetSegment(); Win4Assert( _comparator.Compare( _currRow, pSeg->GetLowestKey() ) >= 0 ); } #endif // 0 } Win4Assert( 0 != pSeg ); Win4Assert( pSeg->GetLowestKey().IsInitialized() ); return pSeg; } //+--------------------------------------------------------------------------- // // Function: LokSplitOrAddSegment // // Synopsis: When a segment gets too full, it needs to be either split // or a new one added, if possible. // // A segment can be split if it is a window and the window // contents permit a sorted split. // // If the segment is a window but either there is no sort set // or all rows in it are equal, then we have to either append // or insert a new segment before the current one. // // If the segment is bucket, then a new segment can either be // inserted or appended only if the current row falls outside // the range of the rows of the bucket. // // Arguments: [pSegToSplit] - The segment which we should check to split // or insert/append to. // // Returns: The segment into which the row must be inserted. // // History: 4-17-95 srikants Created // //---------------------------------------------------------------------------- CTableSegment * CTableRowPutter::LokSplitOrAddSegment( CTableSegment * pSegToSplit ) { Win4Assert( 0 != pSegToSplit ); Win4Assert( !_fNewWindowCreated ); _currRow.MakeReady(); CTableSegment * pSegment = pSegToSplit; Win4Assert( pSegment->IsGettingFull() ); if ( pSegment->IsWindow() ) { ULONG iSplit; BOOL fSortedSplit = pSegment->IsSortedSplit(iSplit); Win4Assert( fSortedSplit && "Unsorted window is not legal" ); CTableWindow * pWindow = (CTableWindow *) pSegment; // // This window will permit a sorted split. // pSegment = _largeTable._LokSplitWindow( &pWindow, iSplit ); pSegment = LokFindSegToInsert( pSegment ); _fNewWindowCreated = TRUE; } else { Win4Assert( pSegment->IsBucket() ); // // The segment is a bucket. The row we have must be bigger than // the smallest in the bucket. // Win4Assert( _comparator.Compare( _currRow, pSegment->GetLowestKey() ) >= 0 ); // // A new window can be added only if the current row is bigger than // the biggest row in the bucket. // CTableBucket * pBucket = (CTableBucket *) pSegment; if ( _comparator.Compare( _currRow, pBucket->GetHighestKey() ) > 0 ) { // // It is either an un-sorted set or a sorted set will all // the rows in that window being equal. // CTableSegment * pNewSeg = _largeTable._CreateNewWindow( _largeTable._AllocSegId(), _currRow, _currRow ); _fNewWindowCreated = TRUE; _segListMgr.InsertAfter( pSegment, pNewSeg ); pSegment = pNewSeg; } } return pSegment; } CTableRowGetter::CTableRowGetter( CLargeTable & largeTable, CTableColumnSet const & outColumns, CGetRowsParams & getParams, CI_TBL_CHAPT chapt, HWATCHREGION hRegion) : _largeTable(largeTable), _segListMgr(largeTable._GetSegListMgr()), _segList(_segListMgr.GetList()), _outColumns(outColumns), _getParams(getParams), _hRegion(hRegion), _pBucketToExpand(0), _widLastTransferred(widInvalid), _chapt(chapt), _cRowsToTransfer(_getParams.RowsToTransfer()) { } void CTableRowGetter::SetRowsToTransfer( ULONG cRowsToTransfer ) { Win4Assert( cRowsToTransfer <= _getParams.RowsToTransfer() ); _cRowsToTransfer = cRowsToTransfer; } //+--------------------------------------------------------------------------- // // Function: _GetRowsFromWindow // // Synopsis: // // Arguments: [iter] - // // Returns: // // Modifies: // // History: 7-13-95 srikants Split from GetRowsAtSegment to make it // more modular // // Notes: // //---------------------------------------------------------------------------- BOOL CTableRowGetter::_GetRowsFromWindow( CDoubleTableSegIter & iter ) { Win4Assert( iter.GetSegment()->IsWindow() ); CTableWindow * pWindow = iter.GetWindow(); // // Real work done here! // _status = pWindow->GetRows( _hRegion, _widStart, _chapt, _cRowsToTransfer, _outColumns, _getParams, _widLastTransferred ); if ( _getParams.GetFwdFetch() ) _segList.Advance(iter); else _segList.BackUp(iter); BOOL fContinue = TRUE; if ( _status != DB_S_ENDOFROWSET && _status != S_OK ) { fContinue = FALSE; if (_fAsync && _hRegion != 0 && _status == DB_S_BLOCKLIMITEDROWS) { // // NEWFEATURE - Finish extending the region // } } else if ( _segList.AtEnd(iter) || 0 == _cRowsToTransfer ) { // // Add the last workid retrieved to the MRU list of // wids. // ULONG endOffset; if ( pWindow->RowOffset( _widLastTransferred, endOffset ) ) { _segListMgr.SetInUseByClient( _widLastTransferred, pWindow ); } // // If transfer is complete, don't return DB_S_ENDOFROWSET // if ( 0 == _cRowsToTransfer ) _status = S_OK; fContinue = FALSE; } else { // // Retrieve rows from the next/prev segment. // if ( _getParams.GetFwdFetch() ) _widStart = WORKID_TBLFIRST; else _widStart = WORKID_TBLLAST; } return fContinue; } //+--------------------------------------------------------------------------- // // Function: _ProcessBucket // // Synopsis: // // Arguments: [iter] - // [xBktsToExpand] - // // Returns: // // Modifies: // // History: 7-13-95 srikants Split from GetRowsAtSegment to make it // more modular // // Notes: // //---------------------------------------------------------------------------- BOOL CTableRowGetter::_ProcessBucket( CDoubleTableSegIter & iter, XPtr & xBktToExplode ) { CTableSegment * pSegment = iter.GetSegment(); Win4Assert( pSegment->IsBucket() ); Win4Assert( 0 == xBktToExplode.GetPointer() ); BOOL fContinue = TRUE; if (!_fAsync) { // // Current segment is a bucket. It must be expanded before // we can continue to retrieve rows. // Win4Assert( pSegment->IsBucket() ); CTableBucket * pBktToExplode = _largeTable._LokReplaceWithEmptyWindow( iter ); xBktToExplode.Set( pBktToExplode ); fContinue = FALSE; _status = DB_S_BLOCKLIMITEDROWS; } else { // // How did we end up with buckets in the fetch region. All buckets // in the fetch region should have been converted into windows and // scheduled for expansion. These windows cannot be converted into // buckets because they have watch regions on them. // Win4Assert( !"Found Buckets in the Fetch Region" ); // // Just skip them ?? // _segList.Advance( iter ); fContinue = !_segList.AtEnd(iter); } return fContinue; } //+--------------------------------------------------------------------------- // // Member: CTableRowGetter::LokGetRowsAtSegment // // Synopsis: Retrieves rows starting at the specified segment. It will // continue fetching until either all the requested rows are // returned or we are past the end of the list of segments or // we hit a bucket. // // Arguments: [pStartSeg] - The starting segment to use for retrieveing // rows. // [widStart] - The starting workid in that segment. // [fAsync] - Explode buckets asynchronously // [xBktToExplode] - Safe pointer for the bucket to be exploded // (if any) // // Returns: Status of the operation. // // History: 4-18-95 SrikantS Created (Moved from CLargeTable) // 6-28-95 BartoszM Added watch region support // 7-13-95 SrikantS Enhanced for watch region support // // Notes: The watch region handle has been verified! // //---------------------------------------------------------------------------- SCODE CTableRowGetter::LokGetRowsAtSegment( CTableSegment * pStartSeg, WORKID widStart, BOOL fAsync, XPtr & xBktToExplode ) { _InitForGetRows( widStart, fAsync ); if ( 0 == pStartSeg ) { return DB_S_ENDOFROWSET; } else { Win4Assert( !_segList.IsEmpty() ); } // // Skip over the nodes upto the segment passed. // for ( CFwdTableSegIter iter( _segList ); !_segList.AtEnd(iter); _segList.Advance(iter) ) { if ( iter.GetSegment() == pStartSeg ) { break; } } Win4Assert( !_segList.AtEnd(iter) ); Win4Assert( iter.GetSegment() == pStartSeg ); // // Do the GetRows to a window, crossing segment boundaries as needed. // // // Add the starting wid to the "MRU" list of wid/segment pairs. // if ( !IsSpecialWid(widStart) && iter.GetSegment()->IsWindow() ) { #if DBG==1 CTableWindow * pTemp = iter.GetWindow(); ULONG startOffset; BOOL fFound = pTemp->RowOffset( widStart, startOffset ); Win4Assert( fFound ); #endif // DBG==1 _segListMgr.SetInUseByClient( widStart, iter.GetSegment() ); } BOOL fContinue = TRUE; do { CTableSegment * pSegment = iter.GetSegment(); if ( pSegment->IsWindow() ) { fContinue = _GetRowsFromWindow( iter ); } else { fContinue = _ProcessBucket( iter, xBktToExplode ); } } while (fContinue); return _status; } //+--------------------------------------------------------------------------- // // Member: CTableRowLocator::LokLocate // // Synopsis: Locates the starting bookmark in the list of segments. // // Arguments: [hRegion] - // [fAsync] - // [iter] - // [transformer] - // // Returns: Status of the operation. // // History: 4-18-95 srikants Created // 6-29-95 BartoszM Rewrote for notifications // Notes: // //---------------------------------------------------------------------------- SCODE CTableRowLocator::LokLocate( HWATCHREGION hRegion, BOOL fAsync, CDoubleTableSegIter& iter, CRegionTransformer& transformer ) { SCODE status = S_OK; if ( _segList.IsEmpty() ) { if ( IsSpecialWid( _widStart ) ) { _widFound = _widStart; return DB_S_ENDOFROWSET; } else { QUIETTHROW (CException( DB_E_BADSTARTPOSITION )); } } CTableSegment* pSegmentWatch = 0; if ( fAsync ) { CWatchRegion *pRegion = transformer.Region(); if (pRegion) { pSegmentWatch = pRegion->Segment(); Win4Assert( 0 == pSegmentWatch || pSegmentWatch->IsWindow() ); } } // // Do not modify the _widStart and _iRowOffset member variables here // as they are "input" values and must not be modified here. The client // of this class can modify them if needed. // WORKID widStart = _widStart; LONG iRowOffset = _iRowOffset; Win4Assert( WORKID_TBLBEFOREFIRST != widStart && WORKID_TBLAFTERLAST != widStart ); // // Locate either the segment in which the fetch bookmark is present // or the segment which is the beginning of the watch region - // whichever comes first in the segment list. // if (widStart != WORKID_TBLFIRST) { if (widStart == WORKID_TBLLAST) { // // PERFFIX - can we optimize by initializing the iterator with the // pSegmentWatch if it is not 0. // while ( !_segList.IsLast(iter) && iter.GetSegment() != pSegmentWatch) { _segList.Advance(iter); } } else { while ( !_segList.AtEnd(iter) && iter.GetSegment() != pSegmentWatch) { if ( iter.GetSegment()->IsRowInSegment(widStart) ) { tbDebugOut(( DEB_REGTRANS, "found wid %#x in segment %#x\n", widStart, iter.GetSegment() )); break; } _segList.Advance(iter); } } } if (_segList.AtEnd(iter)) { THROW (CException ( DB_E_BADBOOKMARK)); } TBL_OFF obRow; // dummy ULONG iRowInSeg = 0; // position of bookmark in current segment // // The iterator is positioned either at the beginning of the watch region // or at the segment that contains widStart // if (iter.GetSegment() == pSegmentWatch) { // // We are in the watch region // Win4Assert (0 != pSegmentWatch && pSegmentWatch->IsWindow() ); CTableWindow* pWindow = iter.GetWindow(); transformer.SetWatchPos( pWindow->GetWatchStart(hRegion) ); if ( pWindow->FindBookMark( widStart, obRow, iRowInSeg )) { // // both the watch and the starting wid are in the same window // transformer.SetFetchBmkPos (iRowInSeg); } else { DBROWCOUNT iFetch = 0; // skip segments to get to the starting bookmark do { iFetch += iter.GetSegment()->RowCount(); _segList.Advance(iter); if (_segList.AtEnd(iter)) { // // Bookmarks do become invalid. // For instance it could have // gone to an expanding bucket. // THROW (CException ( DB_E_BADBOOKMARK)); } } while ( !iter.GetSegment()->IsRowInSegment(widStart)); // // Is the starting bookmark in a bucket? // if (!iter.GetSegment()->IsWindow()) { // // We hit a bucket. The bookmark became invisible // to the user. In the asynchronous world bookmarks // are not valid forever, buddy! // THROW (CException ( DB_E_BADBOOKMARK)); } else { // // The starting bookmark is in a window. // pWindow = iter.GetWindow(); pWindow->FindBookMark ( widStart, obRow, iRowInSeg ); iFetch += iRowInSeg; } transformer.SetFetchBmkPos ( iFetch ); } } else if ( 0 != hRegion ) { // // Found widStart but not the watch region. // We now have to search for the watch region in the subsequent // segments. // if (!iter.GetSegment()->IsWindow()) { // // We hit a bucket. The bookmark became invisible // to the user. In the asynchronous world bookmarks // are not valid forever, buddy! // THROW (CException ( DB_E_BADBOOKMARK)); } else { CTableWindow* pWindow = iter.GetWindow (); ULONG iOffset, iRowInSeg; pWindow->FindBookMark ( widStart, obRow, iRowInSeg ); // // iFetch is now the offset from the beginning of the bookmark // segment. // transformer.SetFetchBmkPos ( iRowInSeg ); if ( 0 != pSegmentWatch ) { DBROWCOUNT iWatch = 0; // Search for the beginning of watch region CDoubleTableSegIter iter2 (iter); do { iWatch += iter2.GetSegment()->RowCount(); _segList.Advance (iter2); Win4Assert (!_segList.AtEnd(iter2)); } while (iter2.GetSegment() != pSegmentWatch); pWindow = iter2.GetWindow(); iWatch += pWindow->GetWatchStart (hRegion); transformer.SetWatchPos (iWatch); } } } // CLEANCODE: Can we make this function void since it's throwing exceptions? return S_OK; } //+--------------------------------------------------------------------------- // // Member: CTableRowLocator::LokRelocate // // Synopsis: Finds the start of the fetch region by counting the offset // from the start bookmark. // // Arguments: [fAsync] - // [iter] - // [transformer] - // [xBktToExplode] - // [xBktToConvert] - // // Returns: // // Modifies: // // History: 7-25-95 srikants Created // // Notes: // //---------------------------------------------------------------------------- void CTableRowLocator::LokRelocate( BOOL fAsync, CDoubleTableSegIter& iter, CRegionTransformer& transformer, XPtr& xBktToExplode, CDynStack& xBktToConvert ) { #if CIDBG==1 tbDebugOut(( DEB_REGTRANS, "Before Relocating\n" )); transformer.DumpState(); #endif // CIDBG // // 1. We have located the "anchor" bookmark. The iterator // is positioned at that segment. // // 2. If there was a watch region, we have located the watch region // // // Now, we have to locate the segment which // is at the particular offset from the "anchor" bookmark. // if ( iter.GetSegment()->IsBucket() ) { Win4Assert( !fAsync && "Must be synchronous when a bookmark is in a bucket" ); CTableBucket * pBucket = _largeTable._LokReplaceWithEmptyWindow( iter ); xBktToExplode.Set( pBucket ); return; } // // The first segment in the iterator is a window. // WORKID widStart = _widStart; DBCOUNTITEM cRowsInSeg = iter.GetSegment()->RowCount(); DBROWCOUNT cRowsResidual = transformer.GetFetchOffsetFromAnchor(); _cRowsBeyond = 0; Win4Assert( WORKID_TBLBEFOREFIRST != widStart && WORKID_TBLAFTERLAST != widStart ); BOOL fGoingForward = cRowsResidual >= 0; if ( !fGoingForward ) { // // Make the residual row count always positive. // cRowsResidual = -cRowsResidual; } // // Do the computations for the first segment. // The residual row count will always be WRT the beginning of a segment // if going forward and will be WRT to the end of a segment if going // backward. It will always be positive. // ULONG iRowStart; if ( WORKID_TBLLAST == widStart ) { iRowStart = (ULONG) (cRowsInSeg-1); } else if ( WORKID_TBLFIRST == widStart ) { iRowStart = 0; } else { CTableWindow * pStartWindow = iter.GetWindow(); BOOL fFound = pStartWindow->RowOffset( widStart, iRowStart ); Win4Assert( fFound && "Must Find Starting BookMark" ); } if ( fGoingForward ) { // WRT to the beginning of the current segment cRowsResidual += iRowStart; } else { // WRT to the end of the current segment cRowsResidual += (cRowsInSeg-iRowStart)-1; } // // Look for the row in this segment. // while ( !IsOffsetFound(iter, cRowsResidual) ) { cRowsResidual -= cRowsInSeg; Win4Assert( cRowsResidual >= 0 ); if ( fGoingForward ) { _segList.Advance(iter); } else { _segList.BackUp(iter); } if ( _segList.AtEnd(iter) ) { break; } cRowsInSeg = iter.GetSegment()->RowCount(); if ( iter.GetSegment()->IsBucket() && fAsync && transformer.IsContiguous() ) { // // We are doing asynchronous get rows in a contiguous // watch region. So, we should schedule the buckets for // later expansion and NOT count the rows in them for the // offset computation. // CTableBucket * pBktToExpand = _largeTable._LokReplaceWithEmptyWindow( iter ); xBktToConvert.Push( pBktToExpand ); // // We should not count the rows in a bucket in async case. // cRowsInSeg = 0; } } if ( _segList.AtEnd( iter ) ) { if ( fGoingForward ) { _cRowsBeyond = -cRowsResidual; if ( 0 == _cRowsBeyond ) { // // Should be the first row after the last segment. // _cRowsBeyond = 1; } } else { _cRowsBeyond = -cRowsResidual; if ( 0 == _cRowsBeyond ) { // // Is the first row before this segment. // _cRowsBeyond = -1; } } _widFound = widInvalid; } else { if ( !iter.GetSegment()->IsWindow() ) { _RelocateThruBuckets( fAsync, iter, transformer, xBktToExplode ); } else { CTableWindow * pWindow = iter.GetWindow(); Win4Assert( pWindow->RowCount() > (ULONG) cRowsResidual ); if ( fGoingForward ) { _widFound = pWindow->GetBookMarkAt( (ULONG) cRowsResidual ); } else { _widFound = pWindow->GetBookMarkAt( (ULONG) ( pWindow->RowCount() - cRowsResidual - 1 ) ); } } } #if CIDBG==1 tbDebugOut(( DEB_REGTRANS, "After Relocating\n" )); transformer.DumpState(); #endif // CIDBG return; } //+--------------------------------------------------------------------------- // // Member: CTableRowLocator::_RelocateThruBuckets // // Synopsis: Does the processing needed after locating the segment in // which the requested row is contained. If the current segment // is a bucket, the following actions must be taken: // // 1. If it is synchronous fetch, must EXPLODE the bucket. // // 2. O/W, it must a non-contiguous fetch. In this case, we // must locate the closest window. // // If the fetch is forward, // (+ve count of rows), we must locate the first window in // the forward direction and position at the beginning of the // window. // // If the fetch is backward (-ve count of rows), we must // locate the first window in the backward direction and // position at the end of the window. // // Arguments: [fAsync] - // [iter] - // [transformer] - // [xBktToExplode] - // // History: 7-25-95 srikants Created // // Notes: // //---------------------------------------------------------------------------- void CTableRowLocator::_RelocateThruBuckets( BOOL fAsync, CDoubleTableSegIter& iter, CRegionTransformer& transformer, XPtr& xBktToExplode ) { // // If the iterator is not at the end of the list, then the current // segment MUST contain the row we are looking for. // Win4Assert( !iter.GetSegment()->IsWindow() ); if (!fAsync) { // // We are in synchronous mode and the iterator is positioned at // a bucket. Must explode the bucket. // CTableBucket * pBucket = _largeTable._LokReplaceWithEmptyWindow( iter ); xBktToExplode.Set( pBucket ); return; } // // If the fetch is contiguous, we must be either at a window or past the // end of the list. Both cases are eliminated above this. // Win4Assert( !transformer.IsContiguous() ); // // Search for the nearest window - either in the positive // or negative direction depending upon the count of the // rows. // BOOL fForward = transformer.GetFetchCount() >= 0 ; while ( !_segList.AtEnd(iter) ) { CTableSegment * pSeg = iter.GetSegment(); if ( pSeg->IsBucket() || 0 == pSeg->RowCount() ) { if ( fForward ) { _segList.Advance(iter); } else { _segList.BackUp(iter); } } else { Win4Assert( pSeg->IsWindow() ); break; } } if ( !_segList.AtEnd(iter) ) { if ( fForward ) { _widFound = iter.GetWindow()->GetBookMarkAt(0); } else { _widFound = iter.GetWindow()->GetBookMarkAt( (ULONG) (iter.GetSegment()->RowCount()-1) ); } } return; } //+--------------------------------------------------------------------------- // // Member: CTableRowLocator::LokSimulateFetch // // Synopsis: Simulates fetching rows from windows and schedules the buckets // in the path to be converted to windows. This allows setting // the watch region correctly over all the required segments. // // Arguments: [iter] - The iterator is positioned at the segment to // start fetching from. // [transformer] - The transformer is initialized with the old // and new fetch/watch arguments. // [xBktToConvert] - On output, will have buckets to be converted // into windows. These buckets were in the fetch region and will be // replace with empty windows here. // // History: 7-26-95 srikants Created // // Notes: // //---------------------------------------------------------------------------- void CTableRowLocator::LokSimulateFetch( CDoubleTableSegIter& iter, CRegionTransformer& transformer, CDynStack& xBktToConvert ) { #if CIDBG==1 tbDebugOut(( DEB_REGTRANS, " Before SimulateFetch\n" )); transformer.DumpState(); #endif // CIDBG Win4Assert( iter.GetSegment()->IsWindow() ); Win4Assert( widInvalid != _widFound ); // Win4Assert( IsRowFound(iter) ); // // If it is asynchronous fetch, we do not have categorization and so // we don't have to worry about checking for going beyond chapters. // BOOL fForward = transformer.GetFetchCount() >= 0; DBROWCOUNT cRowsToRetrieve = 0; // tracks the number of rows still to retrieve. if ( fForward ) cRowsToRetrieve = transformer.GetFetchCount(); else cRowsToRetrieve = -transformer.GetFetchCount(); CTableWindow * pFirstWindow = iter.GetWindow(); ULONG iRowOffset; TBL_OFF dummy; BOOL fFound = pFirstWindow->FindBookMark( _widFound, dummy, iRowOffset ); Win4Assert( fFound ); // // For determining the lowest fetch position. // ULONG iOffsetInFirstWindow = iRowOffset; // // Compute the number of rows that can be retrieved from current window. // ULONG cRowsFromCurrWindow = 0; if ( fForward ) cRowsFromCurrWindow = (ULONG) (pFirstWindow->RowCount()-iRowOffset); else cRowsFromCurrWindow = iRowOffset+1; if ( cRowsToRetrieve > (long) cRowsFromCurrWindow ) { // // We still have more rows to be retrieved. // cRowsToRetrieve -= cRowsFromCurrWindow; } else { // // This window can give us all the rows to be fetched. // if ( !fForward ) { Win4Assert( iRowOffset+1 >= (ULONG) cRowsToRetrieve ); iOffsetInFirstWindow = (ULONG) ( iRowOffset+1-cRowsToRetrieve ); } cRowsToRetrieve = 0; } while ( cRowsToRetrieve > 0 ) { Win4Assert( !_segList.AtEnd(iter) ); if ( fForward ) { _segList.Advance( iter ); } else { _segList.BackUp(iter); } if ( _segList.AtEnd(iter) ) { break; } if ( iter.GetSegment()->IsBucket() ) { CTableBucket * pBucket = _largeTable._LokReplaceWithEmptyWindow(iter); xBktToConvert.Push(pBucket); } else { CTableWindow * pWindow = iter.GetWindow(); cRowsFromCurrWindow = (ULONG) pWindow->RowCount(); if ( cRowsToRetrieve > (long) cRowsFromCurrWindow ) { cRowsToRetrieve -= cRowsFromCurrWindow; } else { if ( !fForward ) { iOffsetInFirstWindow = (ULONG) ( pWindow->RowCount()-cRowsToRetrieve ); pFirstWindow = pWindow; } cRowsToRetrieve = 0; } } } if ( _segList.AtEnd(iter) && !fForward ) { // // we have gone beyond the beginning of the table but still // cannot satisfy the row count to be retrieved. Is it okay to assume // that the first window and the first row are the lowest fetch points // Win4Assert( _segList.GetFirst() && _segList.GetFirst()->IsWindow() ); pFirstWindow = (CTableWindow *) _segList.GetFirst(); iOffsetInFirstWindow = 0; } Win4Assert( 0 != pFirstWindow ); Win4Assert( iOffsetInFirstWindow < pFirstWindow->RowCount() ); transformer.SetLowFetchPos( pFirstWindow, iOffsetInFirstWindow ); if ( transformer.GetFetchOffsetFromOrigin() < 0 ) { transformer.MoveOrigin( transformer.GetFetchOffsetFromOrigin() - iOffsetInFirstWindow ); } #if CIDBG==1 tbDebugOut(( DEB_REGTRANS, " SimulateFetch Done\n" )); #endif // CIDBG==1 } void CTableRowLocator::SeekAndSetFetchBmk( WORKID wid , CDoubleTableSegIter & iter ) { _widFound = widInvalid; if ( _segList.IsEmpty() ) return; CTableWindow * pWindow; ULONG iRowOffset; if ( WORKID_TBLFIRST == wid ) { // // We want the first bookmark in the table. // _segList.SetToBeginning( iter ); pWindow = iter.GetWindow(); iRowOffset = 0; } else { Win4Assert( WORKID_TBLLAST == wid ); // // We want the last bookmark in the table. // _segList.SetToEnd(iter); pWindow = iter.GetWindow(); if ( pWindow->RowCount() > 0 ) iRowOffset = (ULONG) (pWindow->RowCount()-1); } if ( pWindow->RowCount() > 0 ) { _widFound = pWindow->GetBookMarkAt( iRowOffset ); } }