Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1427 lines
42 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 2000.
  5. //
  6. // File: tputget.cxx
  7. //
  8. // Contents: Classes to put, get and locate rows in the bigtable.
  9. //
  10. // Classes: CTableRowPutter, CTableRowGetter, CTableRowLocator
  11. //
  12. // Functions:
  13. //
  14. // History: 4-17-95 srikants Created
  15. //
  16. //----------------------------------------------------------------------------
  17. #include "pch.cxx"
  18. #pragma hdrstop
  19. #include "tputget.hxx"
  20. #include "tblwindo.hxx"
  21. #include "tblbuket.hxx"
  22. #include "winsplit.hxx"
  23. #include "tabledbg.hxx"
  24. #include "query.hxx"
  25. #include "regtrans.hxx"
  26. //+---------------------------------------------------------------------------
  27. //
  28. // Function: _LokShouldUseNext
  29. //
  30. // Synopsis: Tests whether the segment should be use for a putrow.
  31. //
  32. // Arguments: [icmp] -- compare result of current row to first of pSeg
  33. // [pSeg] -- the segment in question
  34. //
  35. // Returns: TRUE if new row is in same category as the first row in
  36. // [pSeg] and if the first row in [pSeg] is the first of its
  37. // category.
  38. //
  39. // History: 11-7-95 dlee Created
  40. //
  41. //----------------------------------------------------------------------------
  42. BOOL CTableRowPutter::_LokShouldUseNext(
  43. int icmp,
  44. CTableSegment * pSeg )
  45. {
  46. Win4Assert( _largeTable.IsCategorized() );
  47. CTableWindow *pWin = (CTableWindow *) pSeg;
  48. return ( ( ( (unsigned) (- icmp ) ) > _largeTable._cCategorizersTotal ) &&
  49. ( pWin->IsFirstRowFirstOfCategory() ) );
  50. }
  51. //+---------------------------------------------------------------------------
  52. //
  53. // Function: _IsHintGood
  54. //
  55. // Synopsis: Tests whether the segment passed in as a hint or one of
  56. // its immediate neighbors can be used to insert the new row.
  57. //
  58. // Arguments: [pSegHint] - The input segment which is the hint.
  59. //
  60. // Returns: Pointer to either the hint or one of its neighbors if the
  61. // row can be inserted into them. NULL if the hint is not good.
  62. //
  63. // History: 4-17-95 srikants Created
  64. //
  65. // Notes:
  66. //
  67. //----------------------------------------------------------------------------
  68. CTableSegment * CTableRowPutter::_IsHintGood( CTableSegment * pSegHint )
  69. {
  70. if ( 0 == pSegHint )
  71. return 0;
  72. CTableSegment * pResult = 0;
  73. Win4Assert( pSegHint->GetLowestKey().IsInitialized() );
  74. int icmp = _comparator.Compare( _currRow, pSegHint->GetLowestKey() );
  75. if ( icmp >= 0 )
  76. {
  77. //
  78. // The current key is >= the lowest key in the hint. See if this is
  79. // less than the key in the next segment.
  80. //
  81. if ( _segList.GetLast() != pSegHint )
  82. {
  83. CTableSegment * pNext = _segList.GetNext( pSegHint );
  84. int icmpNext = _comparator.Compare( _currRow,
  85. pNext->GetLowestKey() );
  86. if ( icmpNext < 0 )
  87. {
  88. // If categorized AND
  89. // new row in same category as first row of next window AND
  90. // first row of next window is first of its category
  91. // use the next window. Otherwise use the current window.
  92. //
  93. if ( ( _largeTable.IsCategorized() ) &&
  94. ( _LokShouldUseNext( icmpNext, pNext ) ) )
  95. {
  96. tbDebugOut(( DEB_ITRACE, "updated hint window for categorized putrow\n" ));
  97. pResult = pNext;
  98. }
  99. else
  100. {
  101. //
  102. // The current row is < the lowest key in the next segment.
  103. // It is also not in the same category as the lowest key
  104. // in the next segment.
  105. // So, it is a good candidate.
  106. //
  107. pResult = pSegHint;
  108. }
  109. }
  110. }
  111. else
  112. {
  113. pResult = pSegHint;
  114. }
  115. }
  116. else if ( _segList.GetFirst() == pSegHint )
  117. {
  118. //
  119. // We are at the first segment and the current row is < the first
  120. // segment. So, just use it.
  121. //
  122. pResult = pSegHint;
  123. }
  124. return pResult;
  125. }
  126. //+---------------------------------------------------------------------------
  127. //
  128. // Function: LokFindSegToInsert
  129. //
  130. // Synopsis: Finds the segment to insert the current row.
  131. //
  132. // Arguments: [pSegHint] - Segment used as a hint.
  133. //
  134. // Returns: The segment into which the row must be inserted.
  135. //
  136. // History: 4-17-95 srikants Created
  137. //
  138. //----------------------------------------------------------------------------
  139. CTableSegment * CTableRowPutter::LokFindSegToInsert( CTableSegment * pSegHint )
  140. {
  141. //
  142. // For the most common case, there will be only one segment. Just use
  143. // that and don't setup _currRow.
  144. //
  145. if ( 1 == _segList.GetSegmentsCount() )
  146. {
  147. Win4Assert( 0 == pSegHint || _segList.GetFirst() == pSegHint );
  148. return _segList.GetFirst();
  149. }
  150. //
  151. // Initialize the sort key and use that to compare with various segments.
  152. //
  153. _currRow.MakeReady();
  154. if ( _segList.IsEmpty() )
  155. {
  156. CTableWindow * pWindow = _largeTable._CreateNewWindow(
  157. _largeTable._AllocSegId(),
  158. _currRow,
  159. _currRow);
  160. _segListMgr.Push( pWindow );
  161. tbDebugOut(( DEB_WINSPLIT,
  162. " Adding Window 0x%X to an empty list\n", pWindow ));
  163. return pWindow;
  164. }
  165. Win4Assert( 0 != _largeTable.GetSortSet() );
  166. //
  167. // See if the hint is usable.
  168. //
  169. CTableSegment * pSeg = _IsHintGood( pSegHint );
  170. if ( 0 == pSeg )
  171. {
  172. //
  173. // Either there was no hint or the row did not belong there.
  174. //
  175. pSeg = _segListMgr.GetSegmentArray().LookUp( _currRow );
  176. if ( ( _largeTable.IsCategorized() ) &&
  177. ( _segList.GetLast() != pSeg ) )
  178. {
  179. // Pick a window so that the unique categorizer can make the
  180. // assumption that:
  181. // - an insert at the end of a window either belongs to the
  182. // same category as the previous last row OR is a new
  183. // category
  184. // - an insert at the beginning of a window is the new first
  185. // member of the category of the first row in the window
  186. CTableSegment * pNext = _segList.GetNext( pSeg );
  187. int icmpNext = _comparator.Compare( _currRow,
  188. pNext->GetLowestKey() );
  189. Win4Assert( icmpNext < 0 );
  190. if ( _LokShouldUseNext( icmpNext, pNext ) )
  191. {
  192. tbDebugOut(( DEB_ITRACE, "updated window for categorized putrow\n" ));
  193. pSeg = pNext;
  194. }
  195. }
  196. // This assert will not be true when a row is the new first row
  197. // of both a segment and a category.
  198. if ( ! _largeTable.IsCategorized() )
  199. {
  200. Win4Assert( _IsHintGood(pSeg) == pSeg );
  201. }
  202. #if 0
  203. CBackTableSegIter iter(_segList);
  204. Win4Assert( !_segList.AtEnd(iter) );
  205. do
  206. {
  207. CTableSegment & segment = *iter.GetSegment();
  208. Win4Assert( segment.GetLowestKey().IsInitialized() );
  209. if ( _comparator.Compare(_currRow, segment.GetLowestKey()) >= 0 )
  210. {
  211. //
  212. // The current row is >= the lowest key in this segment. We
  213. // can use it.
  214. //
  215. break;
  216. }
  217. else
  218. {
  219. _segList.BackUp(iter);
  220. }
  221. }
  222. while ( !_segList.AtEnd(iter) );
  223. if ( _segList.AtEnd(iter) )
  224. {
  225. //
  226. // We have gone past the end of the list. Just use the
  227. // first one.
  228. //
  229. pSeg = _segList.GetFirst();
  230. Win4Assert( _comparator.Compare( _currRow,
  231. pSeg->GetLowestKey() ) < 0 );
  232. }
  233. else
  234. {
  235. pSeg = iter.GetSegment();
  236. Win4Assert( _comparator.Compare( _currRow,
  237. pSeg->GetLowestKey() ) >= 0 );
  238. }
  239. #endif // 0
  240. }
  241. Win4Assert( 0 != pSeg );
  242. Win4Assert( pSeg->GetLowestKey().IsInitialized() );
  243. return pSeg;
  244. }
  245. //+---------------------------------------------------------------------------
  246. //
  247. // Function: LokSplitOrAddSegment
  248. //
  249. // Synopsis: When a segment gets too full, it needs to be either split
  250. // or a new one added, if possible.
  251. //
  252. // A segment can be split if it is a window and the window
  253. // contents permit a sorted split.
  254. //
  255. // If the segment is a window but either there is no sort set
  256. // or all rows in it are equal, then we have to either append
  257. // or insert a new segment before the current one.
  258. //
  259. // If the segment is bucket, then a new segment can either be
  260. // inserted or appended only if the current row falls outside
  261. // the range of the rows of the bucket.
  262. //
  263. // Arguments: [pSegToSplit] - The segment which we should check to split
  264. // or insert/append to.
  265. //
  266. // Returns: The segment into which the row must be inserted.
  267. //
  268. // History: 4-17-95 srikants Created
  269. //
  270. //----------------------------------------------------------------------------
  271. CTableSegment * CTableRowPutter::LokSplitOrAddSegment( CTableSegment * pSegToSplit )
  272. {
  273. Win4Assert( 0 != pSegToSplit );
  274. Win4Assert( !_fNewWindowCreated );
  275. _currRow.MakeReady();
  276. CTableSegment * pSegment = pSegToSplit;
  277. Win4Assert( pSegment->IsGettingFull() );
  278. if ( pSegment->IsWindow() )
  279. {
  280. ULONG iSplit;
  281. BOOL fSortedSplit = pSegment->IsSortedSplit(iSplit);
  282. Win4Assert( fSortedSplit && "Unsorted window is not legal" );
  283. CTableWindow * pWindow = (CTableWindow *) pSegment;
  284. //
  285. // This window will permit a sorted split.
  286. //
  287. pSegment = _largeTable._LokSplitWindow( &pWindow, iSplit );
  288. pSegment = LokFindSegToInsert( pSegment );
  289. _fNewWindowCreated = TRUE;
  290. }
  291. else
  292. {
  293. Win4Assert( pSegment->IsBucket() );
  294. //
  295. // The segment is a bucket. The row we have must be bigger than
  296. // the smallest in the bucket.
  297. //
  298. Win4Assert( _comparator.Compare( _currRow,
  299. pSegment->GetLowestKey() ) >= 0 );
  300. //
  301. // A new window can be added only if the current row is bigger than
  302. // the biggest row in the bucket.
  303. //
  304. CTableBucket * pBucket = (CTableBucket *) pSegment;
  305. if ( _comparator.Compare( _currRow, pBucket->GetHighestKey() ) > 0 )
  306. {
  307. //
  308. // It is either an un-sorted set or a sorted set will all
  309. // the rows in that window being equal.
  310. //
  311. CTableSegment * pNewSeg = _largeTable._CreateNewWindow(
  312. _largeTable._AllocSegId(),
  313. _currRow,
  314. _currRow );
  315. _fNewWindowCreated = TRUE;
  316. _segListMgr.InsertAfter( pSegment, pNewSeg );
  317. pSegment = pNewSeg;
  318. }
  319. }
  320. return pSegment;
  321. }
  322. CTableRowGetter::CTableRowGetter( CLargeTable & largeTable,
  323. CTableColumnSet const & outColumns,
  324. CGetRowsParams & getParams,
  325. CI_TBL_CHAPT chapt,
  326. HWATCHREGION hRegion)
  327. :
  328. _largeTable(largeTable),
  329. _segListMgr(largeTable._GetSegListMgr()),
  330. _segList(_segListMgr.GetList()),
  331. _outColumns(outColumns),
  332. _getParams(getParams),
  333. _hRegion(hRegion),
  334. _pBucketToExpand(0),
  335. _widLastTransferred(widInvalid),
  336. _chapt(chapt),
  337. _cRowsToTransfer(_getParams.RowsToTransfer())
  338. {
  339. }
  340. void CTableRowGetter::SetRowsToTransfer( ULONG cRowsToTransfer )
  341. {
  342. Win4Assert( cRowsToTransfer <= _getParams.RowsToTransfer() );
  343. _cRowsToTransfer = cRowsToTransfer;
  344. }
  345. //+---------------------------------------------------------------------------
  346. //
  347. // Function: _GetRowsFromWindow
  348. //
  349. // Synopsis:
  350. //
  351. // Arguments: [iter] -
  352. //
  353. // Returns:
  354. //
  355. // Modifies:
  356. //
  357. // History: 7-13-95 srikants Split from GetRowsAtSegment to make it
  358. // more modular
  359. //
  360. // Notes:
  361. //
  362. //----------------------------------------------------------------------------
  363. BOOL
  364. CTableRowGetter::_GetRowsFromWindow( CDoubleTableSegIter & iter )
  365. {
  366. Win4Assert( iter.GetSegment()->IsWindow() );
  367. CTableWindow * pWindow = iter.GetWindow();
  368. //
  369. // Real work done here!
  370. //
  371. _status = pWindow->GetRows( _hRegion,
  372. _widStart,
  373. _chapt,
  374. _cRowsToTransfer,
  375. _outColumns,
  376. _getParams,
  377. _widLastTransferred );
  378. if ( _getParams.GetFwdFetch() )
  379. _segList.Advance(iter);
  380. else
  381. _segList.BackUp(iter);
  382. BOOL fContinue = TRUE;
  383. if ( _status != DB_S_ENDOFROWSET && _status != S_OK )
  384. {
  385. fContinue = FALSE;
  386. if (_fAsync && _hRegion != 0 && _status == DB_S_BLOCKLIMITEDROWS)
  387. {
  388. //
  389. // NEWFEATURE - Finish extending the region
  390. //
  391. }
  392. }
  393. else if ( _segList.AtEnd(iter) ||
  394. 0 == _cRowsToTransfer )
  395. {
  396. //
  397. // Add the last workid retrieved to the MRU list of
  398. // wids.
  399. //
  400. ULONG endOffset;
  401. if ( pWindow->RowOffset( _widLastTransferred, endOffset ) )
  402. {
  403. _segListMgr.SetInUseByClient( _widLastTransferred,
  404. pWindow );
  405. }
  406. //
  407. // If transfer is complete, don't return DB_S_ENDOFROWSET
  408. //
  409. if ( 0 == _cRowsToTransfer )
  410. _status = S_OK;
  411. fContinue = FALSE;
  412. }
  413. else
  414. {
  415. //
  416. // Retrieve rows from the next/prev segment.
  417. //
  418. if ( _getParams.GetFwdFetch() )
  419. _widStart = WORKID_TBLFIRST;
  420. else
  421. _widStart = WORKID_TBLLAST;
  422. }
  423. return fContinue;
  424. }
  425. //+---------------------------------------------------------------------------
  426. //
  427. // Function: _ProcessBucket
  428. //
  429. // Synopsis:
  430. //
  431. // Arguments: [iter] -
  432. // [xBktsToExpand] -
  433. //
  434. // Returns:
  435. //
  436. // Modifies:
  437. //
  438. // History: 7-13-95 srikants Split from GetRowsAtSegment to make it
  439. // more modular
  440. //
  441. // Notes:
  442. //
  443. //----------------------------------------------------------------------------
  444. BOOL
  445. CTableRowGetter::_ProcessBucket( CDoubleTableSegIter & iter,
  446. XPtr<CTableBucket> & xBktToExplode )
  447. {
  448. CTableSegment * pSegment = iter.GetSegment();
  449. Win4Assert( pSegment->IsBucket() );
  450. Win4Assert( 0 == xBktToExplode.GetPointer() );
  451. BOOL fContinue = TRUE;
  452. if (!_fAsync)
  453. {
  454. //
  455. // Current segment is a bucket. It must be expanded before
  456. // we can continue to retrieve rows.
  457. //
  458. Win4Assert( pSegment->IsBucket() );
  459. CTableBucket * pBktToExplode =
  460. _largeTable._LokReplaceWithEmptyWindow( iter );
  461. xBktToExplode.Set( pBktToExplode );
  462. fContinue = FALSE;
  463. _status = DB_S_BLOCKLIMITEDROWS;
  464. }
  465. else
  466. {
  467. //
  468. // How did we end up with buckets in the fetch region. All buckets
  469. // in the fetch region should have been converted into windows and
  470. // scheduled for expansion. These windows cannot be converted into
  471. // buckets because they have watch regions on them.
  472. //
  473. Win4Assert( !"Found Buckets in the Fetch Region" );
  474. //
  475. // Just skip them ??
  476. //
  477. _segList.Advance( iter );
  478. fContinue = !_segList.AtEnd(iter);
  479. }
  480. return fContinue;
  481. }
  482. //+---------------------------------------------------------------------------
  483. //
  484. // Member: CTableRowGetter::LokGetRowsAtSegment
  485. //
  486. // Synopsis: Retrieves rows starting at the specified segment. It will
  487. // continue fetching until either all the requested rows are
  488. // returned or we are past the end of the list of segments or
  489. // we hit a bucket.
  490. //
  491. // Arguments: [pStartSeg] - The starting segment to use for retrieveing
  492. // rows.
  493. // [widStart] - The starting workid in that segment.
  494. // [fAsync] - Explode buckets asynchronously
  495. // [xBktToExplode] - Safe pointer for the bucket to be exploded
  496. // (if any)
  497. //
  498. // Returns: Status of the operation.
  499. //
  500. // History: 4-18-95 SrikantS Created (Moved from CLargeTable)
  501. // 6-28-95 BartoszM Added watch region support
  502. // 7-13-95 SrikantS Enhanced for watch region support
  503. //
  504. // Notes: The watch region handle has been verified!
  505. //
  506. //----------------------------------------------------------------------------
  507. SCODE CTableRowGetter::LokGetRowsAtSegment( CTableSegment * pStartSeg,
  508. WORKID widStart,
  509. BOOL fAsync,
  510. XPtr<CTableBucket> & xBktToExplode
  511. )
  512. {
  513. _InitForGetRows( widStart, fAsync );
  514. if ( 0 == pStartSeg )
  515. {
  516. return DB_S_ENDOFROWSET;
  517. }
  518. else
  519. {
  520. Win4Assert( !_segList.IsEmpty() );
  521. }
  522. //
  523. // Skip over the nodes upto the segment passed.
  524. //
  525. for ( CFwdTableSegIter iter( _segList ); !_segList.AtEnd(iter);
  526. _segList.Advance(iter) )
  527. {
  528. if ( iter.GetSegment() == pStartSeg )
  529. {
  530. break;
  531. }
  532. }
  533. Win4Assert( !_segList.AtEnd(iter) );
  534. Win4Assert( iter.GetSegment() == pStartSeg );
  535. //
  536. // Do the GetRows to a window, crossing segment boundaries as needed.
  537. //
  538. //
  539. // Add the starting wid to the "MRU" list of wid/segment pairs.
  540. //
  541. if ( !IsSpecialWid(widStart) && iter.GetSegment()->IsWindow() )
  542. {
  543. #if DBG==1
  544. CTableWindow * pTemp = iter.GetWindow();
  545. ULONG startOffset;
  546. BOOL fFound = pTemp->RowOffset( widStart, startOffset );
  547. Win4Assert( fFound );
  548. #endif // DBG==1
  549. _segListMgr.SetInUseByClient( widStart, iter.GetSegment() );
  550. }
  551. BOOL fContinue = TRUE;
  552. do
  553. {
  554. CTableSegment * pSegment = iter.GetSegment();
  555. if ( pSegment->IsWindow() )
  556. {
  557. fContinue = _GetRowsFromWindow( iter );
  558. }
  559. else
  560. {
  561. fContinue = _ProcessBucket( iter, xBktToExplode );
  562. }
  563. }
  564. while (fContinue);
  565. return _status;
  566. }
  567. //+---------------------------------------------------------------------------
  568. //
  569. // Member: CTableRowLocator::LokLocate
  570. //
  571. // Synopsis: Locates the starting bookmark in the list of segments.
  572. //
  573. // Arguments: [hRegion] -
  574. // [fAsync] -
  575. // [iter] -
  576. // [transformer] -
  577. //
  578. // Returns: Status of the operation.
  579. //
  580. // History: 4-18-95 srikants Created
  581. // 6-29-95 BartoszM Rewrote for notifications
  582. // Notes:
  583. //
  584. //----------------------------------------------------------------------------
  585. SCODE CTableRowLocator::LokLocate( HWATCHREGION hRegion,
  586. BOOL fAsync,
  587. CDoubleTableSegIter& iter,
  588. CRegionTransformer& transformer )
  589. {
  590. SCODE status = S_OK;
  591. if ( _segList.IsEmpty() )
  592. {
  593. if ( IsSpecialWid( _widStart ) )
  594. {
  595. _widFound = _widStart;
  596. return DB_S_ENDOFROWSET;
  597. }
  598. else
  599. {
  600. QUIETTHROW (CException( DB_E_BADSTARTPOSITION ));
  601. }
  602. }
  603. CTableSegment* pSegmentWatch = 0;
  604. if ( fAsync )
  605. {
  606. CWatchRegion *pRegion = transformer.Region();
  607. if (pRegion)
  608. {
  609. pSegmentWatch = pRegion->Segment();
  610. Win4Assert( 0 == pSegmentWatch || pSegmentWatch->IsWindow() );
  611. }
  612. }
  613. //
  614. // Do not modify the _widStart and _iRowOffset member variables here
  615. // as they are "input" values and must not be modified here. The client
  616. // of this class can modify them if needed.
  617. //
  618. WORKID widStart = _widStart;
  619. LONG iRowOffset = _iRowOffset;
  620. Win4Assert( WORKID_TBLBEFOREFIRST != widStart &&
  621. WORKID_TBLAFTERLAST != widStart );
  622. //
  623. // Locate either the segment in which the fetch bookmark is present
  624. // or the segment which is the beginning of the watch region -
  625. // whichever comes first in the segment list.
  626. //
  627. if (widStart != WORKID_TBLFIRST)
  628. {
  629. if (widStart == WORKID_TBLLAST)
  630. {
  631. //
  632. // PERFFIX - can we optimize by initializing the iterator with the
  633. // pSegmentWatch if it is not 0.
  634. //
  635. while ( !_segList.IsLast(iter) && iter.GetSegment() != pSegmentWatch)
  636. {
  637. _segList.Advance(iter);
  638. }
  639. }
  640. else
  641. {
  642. while ( !_segList.AtEnd(iter) && iter.GetSegment() != pSegmentWatch)
  643. {
  644. if ( iter.GetSegment()->IsRowInSegment(widStart) )
  645. {
  646. tbDebugOut(( DEB_REGTRANS, "found wid %#x in segment %#x\n",
  647. widStart, iter.GetSegment() ));
  648. break;
  649. }
  650. _segList.Advance(iter);
  651. }
  652. }
  653. }
  654. if (_segList.AtEnd(iter))
  655. {
  656. THROW (CException ( DB_E_BADBOOKMARK));
  657. }
  658. TBL_OFF obRow; // dummy
  659. ULONG iRowInSeg = 0; // position of bookmark in current segment
  660. //
  661. // The iterator is positioned either at the beginning of the watch region
  662. // or at the segment that contains widStart
  663. //
  664. if (iter.GetSegment() == pSegmentWatch)
  665. {
  666. //
  667. // We are in the watch region
  668. //
  669. Win4Assert (0 != pSegmentWatch && pSegmentWatch->IsWindow() );
  670. CTableWindow* pWindow = iter.GetWindow();
  671. transformer.SetWatchPos( pWindow->GetWatchStart(hRegion) );
  672. if ( pWindow->FindBookMark( widStart, obRow, iRowInSeg ))
  673. {
  674. //
  675. // both the watch and the starting wid are in the same window
  676. //
  677. transformer.SetFetchBmkPos (iRowInSeg);
  678. }
  679. else
  680. {
  681. DBROWCOUNT iFetch = 0;
  682. // skip segments to get to the starting bookmark
  683. do
  684. {
  685. iFetch += iter.GetSegment()->RowCount();
  686. _segList.Advance(iter);
  687. if (_segList.AtEnd(iter))
  688. {
  689. //
  690. // Bookmarks do become invalid.
  691. // For instance it could have
  692. // gone to an expanding bucket.
  693. //
  694. THROW (CException ( DB_E_BADBOOKMARK));
  695. }
  696. }
  697. while ( !iter.GetSegment()->IsRowInSegment(widStart));
  698. //
  699. // Is the starting bookmark in a bucket?
  700. //
  701. if (!iter.GetSegment()->IsWindow())
  702. {
  703. //
  704. // We hit a bucket. The bookmark became invisible
  705. // to the user. In the asynchronous world bookmarks
  706. // are not valid forever, buddy!
  707. //
  708. THROW (CException ( DB_E_BADBOOKMARK));
  709. }
  710. else
  711. {
  712. //
  713. // The starting bookmark is in a window.
  714. //
  715. pWindow = iter.GetWindow();
  716. pWindow->FindBookMark ( widStart, obRow, iRowInSeg );
  717. iFetch += iRowInSeg;
  718. }
  719. transformer.SetFetchBmkPos ( iFetch );
  720. }
  721. }
  722. else if ( 0 != hRegion )
  723. {
  724. //
  725. // Found widStart but not the watch region.
  726. // We now have to search for the watch region in the subsequent
  727. // segments.
  728. //
  729. if (!iter.GetSegment()->IsWindow())
  730. {
  731. //
  732. // We hit a bucket. The bookmark became invisible
  733. // to the user. In the asynchronous world bookmarks
  734. // are not valid forever, buddy!
  735. //
  736. THROW (CException ( DB_E_BADBOOKMARK));
  737. }
  738. else
  739. {
  740. CTableWindow* pWindow = iter.GetWindow ();
  741. ULONG iOffset, iRowInSeg;
  742. pWindow->FindBookMark ( widStart, obRow, iRowInSeg );
  743. //
  744. // iFetch is now the offset from the beginning of the bookmark
  745. // segment.
  746. //
  747. transformer.SetFetchBmkPos ( iRowInSeg );
  748. if ( 0 != pSegmentWatch )
  749. {
  750. DBROWCOUNT iWatch = 0;
  751. // Search for the beginning of watch region
  752. CDoubleTableSegIter iter2 (iter);
  753. do
  754. {
  755. iWatch += iter2.GetSegment()->RowCount();
  756. _segList.Advance (iter2);
  757. Win4Assert (!_segList.AtEnd(iter2));
  758. } while (iter2.GetSegment() != pSegmentWatch);
  759. pWindow = iter2.GetWindow();
  760. iWatch += pWindow->GetWatchStart (hRegion);
  761. transformer.SetWatchPos (iWatch);
  762. }
  763. }
  764. }
  765. // CLEANCODE: Can we make this function void since it's throwing exceptions?
  766. return S_OK;
  767. }
  768. //+---------------------------------------------------------------------------
  769. //
  770. // Member: CTableRowLocator::LokRelocate
  771. //
  772. // Synopsis: Finds the start of the fetch region by counting the offset
  773. // from the start bookmark.
  774. //
  775. // Arguments: [fAsync] -
  776. // [iter] -
  777. // [transformer] -
  778. // [xBktToExplode] -
  779. // [xBktToConvert] -
  780. //
  781. // Returns:
  782. //
  783. // Modifies:
  784. //
  785. // History: 7-25-95 srikants Created
  786. //
  787. // Notes:
  788. //
  789. //----------------------------------------------------------------------------
  790. void CTableRowLocator::LokRelocate( BOOL fAsync,
  791. CDoubleTableSegIter& iter,
  792. CRegionTransformer& transformer,
  793. XPtr<CTableBucket>& xBktToExplode,
  794. CDynStack<CTableBucket>& xBktToConvert )
  795. {
  796. #if CIDBG==1
  797. tbDebugOut(( DEB_REGTRANS, "Before Relocating\n" ));
  798. transformer.DumpState();
  799. #endif // CIDBG
  800. //
  801. // 1. We have located the "anchor" bookmark. The iterator
  802. // is positioned at that segment.
  803. //
  804. // 2. If there was a watch region, we have located the watch region
  805. //
  806. //
  807. // Now, we have to locate the segment which
  808. // is at the particular offset from the "anchor" bookmark.
  809. //
  810. if ( iter.GetSegment()->IsBucket() )
  811. {
  812. Win4Assert( !fAsync &&
  813. "Must be synchronous when a bookmark is in a bucket" );
  814. CTableBucket * pBucket =
  815. _largeTable._LokReplaceWithEmptyWindow( iter );
  816. xBktToExplode.Set( pBucket );
  817. return;
  818. }
  819. //
  820. // The first segment in the iterator is a window.
  821. //
  822. WORKID widStart = _widStart;
  823. DBCOUNTITEM cRowsInSeg = iter.GetSegment()->RowCount();
  824. DBROWCOUNT cRowsResidual = transformer.GetFetchOffsetFromAnchor();
  825. _cRowsBeyond = 0;
  826. Win4Assert( WORKID_TBLBEFOREFIRST != widStart &&
  827. WORKID_TBLAFTERLAST != widStart );
  828. BOOL fGoingForward = cRowsResidual >= 0;
  829. if ( !fGoingForward )
  830. {
  831. //
  832. // Make the residual row count always positive.
  833. //
  834. cRowsResidual = -cRowsResidual;
  835. }
  836. //
  837. // Do the computations for the first segment.
  838. // The residual row count will always be WRT the beginning of a segment
  839. // if going forward and will be WRT to the end of a segment if going
  840. // backward. It will always be positive.
  841. //
  842. ULONG iRowStart;
  843. if ( WORKID_TBLLAST == widStart )
  844. {
  845. iRowStart = (ULONG) (cRowsInSeg-1);
  846. }
  847. else if ( WORKID_TBLFIRST == widStart )
  848. {
  849. iRowStart = 0;
  850. }
  851. else
  852. {
  853. CTableWindow * pStartWindow = iter.GetWindow();
  854. BOOL fFound = pStartWindow->RowOffset( widStart, iRowStart );
  855. Win4Assert( fFound && "Must Find Starting BookMark" );
  856. }
  857. if ( fGoingForward )
  858. {
  859. // WRT to the beginning of the current segment
  860. cRowsResidual += iRowStart;
  861. }
  862. else
  863. {
  864. // WRT to the end of the current segment
  865. cRowsResidual += (cRowsInSeg-iRowStart)-1;
  866. }
  867. //
  868. // Look for the row in this segment.
  869. //
  870. while ( !IsOffsetFound(iter, cRowsResidual) )
  871. {
  872. cRowsResidual -= cRowsInSeg;
  873. Win4Assert( cRowsResidual >= 0 );
  874. if ( fGoingForward )
  875. {
  876. _segList.Advance(iter);
  877. }
  878. else
  879. {
  880. _segList.BackUp(iter);
  881. }
  882. if ( _segList.AtEnd(iter) )
  883. {
  884. break;
  885. }
  886. cRowsInSeg = iter.GetSegment()->RowCount();
  887. if ( iter.GetSegment()->IsBucket() &&
  888. fAsync &&
  889. transformer.IsContiguous() )
  890. {
  891. //
  892. // We are doing asynchronous get rows in a contiguous
  893. // watch region. So, we should schedule the buckets for
  894. // later expansion and NOT count the rows in them for the
  895. // offset computation.
  896. //
  897. CTableBucket * pBktToExpand =
  898. _largeTable._LokReplaceWithEmptyWindow( iter );
  899. xBktToConvert.Push( pBktToExpand );
  900. //
  901. // We should not count the rows in a bucket in async case.
  902. //
  903. cRowsInSeg = 0;
  904. }
  905. }
  906. if ( _segList.AtEnd( iter ) )
  907. {
  908. if ( fGoingForward )
  909. {
  910. _cRowsBeyond = -cRowsResidual;
  911. if ( 0 == _cRowsBeyond )
  912. {
  913. //
  914. // Should be the first row after the last segment.
  915. //
  916. _cRowsBeyond = 1;
  917. }
  918. }
  919. else
  920. {
  921. _cRowsBeyond = -cRowsResidual;
  922. if ( 0 == _cRowsBeyond )
  923. {
  924. //
  925. // Is the first row before this segment.
  926. //
  927. _cRowsBeyond = -1;
  928. }
  929. }
  930. _widFound = widInvalid;
  931. }
  932. else
  933. {
  934. if ( !iter.GetSegment()->IsWindow() )
  935. {
  936. _RelocateThruBuckets( fAsync, iter, transformer, xBktToExplode );
  937. }
  938. else
  939. {
  940. CTableWindow * pWindow = iter.GetWindow();
  941. Win4Assert( pWindow->RowCount() > (ULONG) cRowsResidual );
  942. if ( fGoingForward )
  943. {
  944. _widFound = pWindow->GetBookMarkAt( (ULONG) cRowsResidual );
  945. }
  946. else
  947. {
  948. _widFound = pWindow->GetBookMarkAt( (ULONG) (
  949. pWindow->RowCount() - cRowsResidual - 1 ) );
  950. }
  951. }
  952. }
  953. #if CIDBG==1
  954. tbDebugOut(( DEB_REGTRANS, "After Relocating\n" ));
  955. transformer.DumpState();
  956. #endif // CIDBG
  957. return;
  958. }
  959. //+---------------------------------------------------------------------------
  960. //
  961. // Member: CTableRowLocator::_RelocateThruBuckets
  962. //
  963. // Synopsis: Does the processing needed after locating the segment in
  964. // which the requested row is contained. If the current segment
  965. // is a bucket, the following actions must be taken:
  966. //
  967. // 1. If it is synchronous fetch, must EXPLODE the bucket.
  968. //
  969. // 2. O/W, it must a non-contiguous fetch. In this case, we
  970. // must locate the closest window.
  971. //
  972. // If the fetch is forward,
  973. // (+ve count of rows), we must locate the first window in
  974. // the forward direction and position at the beginning of the
  975. // window.
  976. //
  977. // If the fetch is backward (-ve count of rows), we must
  978. // locate the first window in the backward direction and
  979. // position at the end of the window.
  980. //
  981. // Arguments: [fAsync] -
  982. // [iter] -
  983. // [transformer] -
  984. // [xBktToExplode] -
  985. //
  986. // History: 7-25-95 srikants Created
  987. //
  988. // Notes:
  989. //
  990. //----------------------------------------------------------------------------
  991. void
  992. CTableRowLocator::_RelocateThruBuckets( BOOL fAsync,
  993. CDoubleTableSegIter& iter,
  994. CRegionTransformer& transformer,
  995. XPtr<CTableBucket>& xBktToExplode
  996. )
  997. {
  998. //
  999. // If the iterator is not at the end of the list, then the current
  1000. // segment MUST contain the row we are looking for.
  1001. //
  1002. Win4Assert( !iter.GetSegment()->IsWindow() );
  1003. if (!fAsync)
  1004. {
  1005. //
  1006. // We are in synchronous mode and the iterator is positioned at
  1007. // a bucket. Must explode the bucket.
  1008. //
  1009. CTableBucket * pBucket = _largeTable._LokReplaceWithEmptyWindow( iter );
  1010. xBktToExplode.Set( pBucket );
  1011. return;
  1012. }
  1013. //
  1014. // If the fetch is contiguous, we must be either at a window or past the
  1015. // end of the list. Both cases are eliminated above this.
  1016. //
  1017. Win4Assert( !transformer.IsContiguous() );
  1018. //
  1019. // Search for the nearest window - either in the positive
  1020. // or negative direction depending upon the count of the
  1021. // rows.
  1022. //
  1023. BOOL fForward = transformer.GetFetchCount() >= 0 ;
  1024. while ( !_segList.AtEnd(iter) )
  1025. {
  1026. CTableSegment * pSeg = iter.GetSegment();
  1027. if ( pSeg->IsBucket() ||
  1028. 0 == pSeg->RowCount() )
  1029. {
  1030. if ( fForward )
  1031. {
  1032. _segList.Advance(iter);
  1033. }
  1034. else
  1035. {
  1036. _segList.BackUp(iter);
  1037. }
  1038. }
  1039. else
  1040. {
  1041. Win4Assert( pSeg->IsWindow() );
  1042. break;
  1043. }
  1044. }
  1045. if ( !_segList.AtEnd(iter) )
  1046. {
  1047. if ( fForward )
  1048. {
  1049. _widFound = iter.GetWindow()->GetBookMarkAt(0);
  1050. }
  1051. else
  1052. {
  1053. _widFound = iter.GetWindow()->GetBookMarkAt( (ULONG) (iter.GetSegment()->RowCount()-1) );
  1054. }
  1055. }
  1056. return;
  1057. }
  1058. //+---------------------------------------------------------------------------
  1059. //
  1060. // Member: CTableRowLocator::LokSimulateFetch
  1061. //
  1062. // Synopsis: Simulates fetching rows from windows and schedules the buckets
  1063. // in the path to be converted to windows. This allows setting
  1064. // the watch region correctly over all the required segments.
  1065. //
  1066. // Arguments: [iter] - The iterator is positioned at the segment to
  1067. // start fetching from.
  1068. // [transformer] - The transformer is initialized with the old
  1069. // and new fetch/watch arguments.
  1070. // [xBktToConvert] - On output, will have buckets to be converted
  1071. // into windows. These buckets were in the fetch region and will be
  1072. // replace with empty windows here.
  1073. //
  1074. // History: 7-26-95 srikants Created
  1075. //
  1076. // Notes:
  1077. //
  1078. //----------------------------------------------------------------------------
  1079. void CTableRowLocator::LokSimulateFetch( CDoubleTableSegIter& iter,
  1080. CRegionTransformer& transformer,
  1081. CDynStack<CTableBucket>& xBktToConvert
  1082. )
  1083. {
  1084. #if CIDBG==1
  1085. tbDebugOut(( DEB_REGTRANS, " Before SimulateFetch\n" ));
  1086. transformer.DumpState();
  1087. #endif // CIDBG
  1088. Win4Assert( iter.GetSegment()->IsWindow() );
  1089. Win4Assert( widInvalid != _widFound );
  1090. // Win4Assert( IsRowFound(iter) );
  1091. //
  1092. // If it is asynchronous fetch, we do not have categorization and so
  1093. // we don't have to worry about checking for going beyond chapters.
  1094. //
  1095. BOOL fForward = transformer.GetFetchCount() >= 0;
  1096. DBROWCOUNT cRowsToRetrieve = 0; // tracks the number of rows still to retrieve.
  1097. if ( fForward )
  1098. cRowsToRetrieve = transformer.GetFetchCount();
  1099. else
  1100. cRowsToRetrieve = -transformer.GetFetchCount();
  1101. CTableWindow * pFirstWindow = iter.GetWindow();
  1102. ULONG iRowOffset;
  1103. TBL_OFF dummy;
  1104. BOOL fFound = pFirstWindow->FindBookMark( _widFound, dummy, iRowOffset );
  1105. Win4Assert( fFound );
  1106. //
  1107. // For determining the lowest fetch position.
  1108. //
  1109. ULONG iOffsetInFirstWindow = iRowOffset;
  1110. //
  1111. // Compute the number of rows that can be retrieved from current window.
  1112. //
  1113. ULONG cRowsFromCurrWindow = 0;
  1114. if ( fForward )
  1115. cRowsFromCurrWindow = (ULONG) (pFirstWindow->RowCount()-iRowOffset);
  1116. else
  1117. cRowsFromCurrWindow = iRowOffset+1;
  1118. if ( cRowsToRetrieve > (long) cRowsFromCurrWindow )
  1119. {
  1120. //
  1121. // We still have more rows to be retrieved.
  1122. //
  1123. cRowsToRetrieve -= cRowsFromCurrWindow;
  1124. }
  1125. else
  1126. {
  1127. //
  1128. // This window can give us all the rows to be fetched.
  1129. //
  1130. if ( !fForward )
  1131. {
  1132. Win4Assert( iRowOffset+1 >= (ULONG) cRowsToRetrieve );
  1133. iOffsetInFirstWindow = (ULONG) ( iRowOffset+1-cRowsToRetrieve );
  1134. }
  1135. cRowsToRetrieve = 0;
  1136. }
  1137. while ( cRowsToRetrieve > 0 )
  1138. {
  1139. Win4Assert( !_segList.AtEnd(iter) );
  1140. if ( fForward )
  1141. {
  1142. _segList.Advance( iter );
  1143. }
  1144. else
  1145. {
  1146. _segList.BackUp(iter);
  1147. }
  1148. if ( _segList.AtEnd(iter) )
  1149. {
  1150. break;
  1151. }
  1152. if ( iter.GetSegment()->IsBucket() )
  1153. {
  1154. CTableBucket * pBucket =
  1155. _largeTable._LokReplaceWithEmptyWindow(iter);
  1156. xBktToConvert.Push(pBucket);
  1157. }
  1158. else
  1159. {
  1160. CTableWindow * pWindow = iter.GetWindow();
  1161. cRowsFromCurrWindow = (ULONG) pWindow->RowCount();
  1162. if ( cRowsToRetrieve > (long) cRowsFromCurrWindow )
  1163. {
  1164. cRowsToRetrieve -= cRowsFromCurrWindow;
  1165. }
  1166. else
  1167. {
  1168. if ( !fForward )
  1169. {
  1170. iOffsetInFirstWindow = (ULONG) (
  1171. pWindow->RowCount()-cRowsToRetrieve );
  1172. pFirstWindow = pWindow;
  1173. }
  1174. cRowsToRetrieve = 0;
  1175. }
  1176. }
  1177. }
  1178. if ( _segList.AtEnd(iter) && !fForward )
  1179. {
  1180. //
  1181. // we have gone beyond the beginning of the table but still
  1182. // cannot satisfy the row count to be retrieved. Is it okay to assume
  1183. // that the first window and the first row are the lowest fetch points
  1184. //
  1185. Win4Assert( _segList.GetFirst() &&
  1186. _segList.GetFirst()->IsWindow() );
  1187. pFirstWindow = (CTableWindow *) _segList.GetFirst();
  1188. iOffsetInFirstWindow = 0;
  1189. }
  1190. Win4Assert( 0 != pFirstWindow );
  1191. Win4Assert( iOffsetInFirstWindow < pFirstWindow->RowCount() );
  1192. transformer.SetLowFetchPos( pFirstWindow, iOffsetInFirstWindow );
  1193. if ( transformer.GetFetchOffsetFromOrigin() < 0 )
  1194. {
  1195. transformer.MoveOrigin( transformer.GetFetchOffsetFromOrigin() -
  1196. iOffsetInFirstWindow );
  1197. }
  1198. #if CIDBG==1
  1199. tbDebugOut(( DEB_REGTRANS, " SimulateFetch Done\n" ));
  1200. #endif // CIDBG==1
  1201. }
  1202. void
  1203. CTableRowLocator::SeekAndSetFetchBmk( WORKID wid , CDoubleTableSegIter & iter )
  1204. {
  1205. _widFound = widInvalid;
  1206. if ( _segList.IsEmpty() )
  1207. return;
  1208. CTableWindow * pWindow;
  1209. ULONG iRowOffset;
  1210. if ( WORKID_TBLFIRST == wid )
  1211. {
  1212. //
  1213. // We want the first bookmark in the table.
  1214. //
  1215. _segList.SetToBeginning( iter );
  1216. pWindow = iter.GetWindow();
  1217. iRowOffset = 0;
  1218. }
  1219. else
  1220. {
  1221. Win4Assert( WORKID_TBLLAST == wid );
  1222. //
  1223. // We want the last bookmark in the table.
  1224. //
  1225. _segList.SetToEnd(iter);
  1226. pWindow = iter.GetWindow();
  1227. if ( pWindow->RowCount() > 0 )
  1228. iRowOffset = (ULONG) (pWindow->RowCount()-1);
  1229. }
  1230. if ( pWindow->RowCount() > 0 )
  1231. {
  1232. _widFound = pWindow->GetBookMarkAt( iRowOffset );
  1233. }
  1234. }