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.

4027 lines
123 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1994 - 2000.
  5. //
  6. // File: bigtable.cxx
  7. //
  8. // Contents:
  9. //
  10. // Classes: CLargeTable - top-level class for large tables
  11. // CTableSegIter - iterator of table segments
  12. //
  13. // Functions:
  14. //
  15. // History: 01 Feb 1994 AlanW Created
  16. //
  17. //--------------------------------------------------------------------------
  18. #include "pch.cxx"
  19. #pragma hdrstop
  20. #include <query.hxx>
  21. #include <srequest.hxx>
  22. #include <cifailte.hxx>
  23. #include <tbrowkey.hxx>
  24. #include "tabledbg.hxx"
  25. #include "tblwindo.hxx"
  26. #include "winsplit.hxx"
  27. #include "buketize.hxx"
  28. #include "tputget.hxx"
  29. #include "regtrans.hxx"
  30. static inline ULONG AbsDiff( ULONG num1, ULONG num2 )
  31. {
  32. return num1 >= num2 ? num1-num2 : num2-num1;
  33. }
  34. unsigned CTableSink::LokCategorize(
  35. CCategParams & params )
  36. {
  37. return _pCategorizer->LokAssignCategory( params );
  38. } //LokCategorize
  39. //+-------------------------------------------------------------------------
  40. //
  41. // Member: CLargeTable::CLargeTable, public
  42. //
  43. // Synopsis: Constructor for a large table. Allocates and fills
  44. // in the master column description.
  45. // Allocates initial window to collect data.
  46. //
  47. // Arguments: [col] - A description of initial output column set
  48. // [sort] - A description of the initial sort order
  49. // [cCategorizers] - Total count of categorizers over table
  50. // [mutex] - CAsyncQuery's mutex for serialization
  51. // [fUniqueWorkid] - TRUE if workid (from iterator) is unique
  52. //
  53. // Notes:
  54. //
  55. // History: 01-Jan-96 KyleP Optional unique wid in user-mode.
  56. //
  57. //--------------------------------------------------------------------------
  58. CLargeTable::CLargeTable( XColumnSet & col,
  59. XSortSet & sort,
  60. unsigned cCategorizers,
  61. CMutexSem & mutex,
  62. BOOL fUniqueWorkid,
  63. CRequestServer * pQuiesce )
  64. : CTableSink(),
  65. _sigLargeTable(eSigLargeTable),
  66. _cbMemoryUsage( 0 ),
  67. _cbMemoryTarget( DEFAULT_MEM_TARGET ),
  68. _MasterColumnSet( col.GetPointer() ),
  69. _fUniqueWorkid( fUniqueWorkid ),
  70. _segListMgr(cMaxClientEntriesToPin),
  71. _segList(_segListMgr.GetList()),
  72. _watchList(_segList),
  73. _nextSegId(1),
  74. _pCategorization(0),
  75. _fAbort(FALSE),
  76. _pSortSet( 0 ),
  77. _fProgressNeeded (FALSE),
  78. _bitNotifyEnabled(0),
  79. _bitClientNotified(0),
  80. _bitChangeQuiesced(0),
  81. _bitRefresh(0),
  82. _bitIsWatched(0),
  83. _bitQuiesced(0),
  84. _pDeferredRows(0),
  85. _fQuiescent (FALSE),
  86. _ulProgressNum(0),
  87. _ulProgressDenom(1),
  88. _cCategorizersTotal( cCategorizers ),
  89. _fRankVectorBound( FALSE ),
  90. _mutex( mutex ),
  91. _widCurrent( WORKID_TBLBEFOREFIRST ),
  92. _hNotifyEvent( 0 ),
  93. _pRequestServer( 0 ),
  94. _pQExecute(0),
  95. _pQuiesce( pQuiesce ),
  96. _fSortDefined( FALSE )
  97. {
  98. tbDebugOut (( DEB_NOTIFY, "lt: CLargeTable\n" ));
  99. TRY // use exception generating new
  100. {
  101. // Don't bucketize when rank vector is bound
  102. _fRankVectorBound = ( 0 != _MasterColumnSet.Find( pidRankVector ) );
  103. //
  104. // Be sure the workid column is in the master column set. The
  105. // output column set was added in the constructor above.
  106. //
  107. _MasterColumnSet.Add( CColumnMasterDesc(pidWorkId, TYPE_WORKID) );
  108. //
  109. // Add the status column, which is used internally and may
  110. // be bound to at some point. Status is stored as a byte to save
  111. // space, and is translated to an HRESULT when passed out to a
  112. // client.
  113. //
  114. CColumnMasterDesc *pRowStatus =
  115. _MasterColumnSet.Add( CColumnMasterDesc(pidRowStatus, VT_UI1) );
  116. pRowStatus->SetComputed(TRUE);
  117. pRowStatus->SetUniform(TRUE);
  118. //
  119. // If categorization is turned on, create an I4 category column
  120. //
  121. if ( 0 != cCategorizers )
  122. _MasterColumnSet.Add( CColumnMasterDesc(pidChapter, VT_I4) );
  123. //
  124. // Set up file path and name as global, shared compressions with
  125. // the WorkId as key. Only needed if it's inconvenient to fetch
  126. // name and path from workid (e.g. workid isn't unique).
  127. //
  128. if ( !_fUniqueWorkid )
  129. {
  130. _MasterColumnSet.Add( CColumnMasterDesc(pidPath, TYPE_PATH) );
  131. _MasterColumnSet.Add( CColumnMasterDesc(pidName, TYPE_NAME) );
  132. CCompressedCol * pPathCompression = new CPathStore();
  133. CColumnMasterDesc* pMastCol;
  134. pMastCol = _MasterColumnSet.Find(pidWorkId);
  135. Win4Assert(pMastCol != 0);
  136. pMastCol->SetCompression(pPathCompression);
  137. pMastCol = _MasterColumnSet.Find(pidPath);
  138. Win4Assert(pMastCol != 0);
  139. pMastCol->SetCompression(pPathCompression, pidWorkId);
  140. pMastCol = _MasterColumnSet.Find(pidName);
  141. Win4Assert(pMastCol != 0);
  142. pMastCol->SetCompression(pPathCompression, pidWorkId);
  143. }
  144. //
  145. // Add the sort keys to the master column set.
  146. //
  147. if ( ! sort.IsNull() )
  148. {
  149. for ( unsigned iCol = 0; iCol < sort->Count(); iCol++ )
  150. _MasterColumnSet.Add( CColumnMasterDesc(sort->Get(iCol)) );
  151. _fSortDefined = TRUE; // set it to true since sorting is defined
  152. }
  153. //
  154. // Master column set is constructed. Acquire the sortset, but first
  155. // make sure workid is in the sort set.
  156. //
  157. _pSortSet = _CheckAndAddWidToSortSet( sort );
  158. Win4Assert ( 0 != _pSortSet );
  159. tbDebugOut(( DEB_ITRACE, "New Big Table with %d columns\n",
  160. _MasterColumnSet.Size() ));
  161. }
  162. CATCH(CException, e)
  163. {
  164. delete _pSortSet;
  165. RETHROW();
  166. }
  167. END_CATCH;
  168. }
  169. //+---------------------------------------------------------------------------
  170. //
  171. // Function: _CheckAndAddWidToSortSet
  172. //
  173. // Synopsis: Tests if the sort specification(if any) already had the
  174. // "pidWorkId" as part of the sort set. If not, it adds one to
  175. // the end of sort set.
  176. //
  177. // Arguments: [sort] - Input sort set.
  178. //
  179. // Returns: The sort set to be used.
  180. //
  181. // History: 3-22-95 srikants Created
  182. //
  183. // Notes:
  184. //
  185. //----------------------------------------------------------------------------
  186. CSortSet * CLargeTable::_CheckAndAddWidToSortSet( XSortSet & sort )
  187. {
  188. //
  189. // Check if the pidWorkId is already a field in the sortset.
  190. //
  191. BOOL fPresent = FALSE;
  192. if ( sort.IsNull() )
  193. {
  194. sort.Set( new CSortSet(1) );
  195. }
  196. else
  197. {
  198. for ( unsigned i = 0; i < sort->Count(); i++ )
  199. {
  200. SSortKey & key = sort->Get(i);
  201. if ( pidWorkId == key.pidColumn )
  202. {
  203. fPresent = TRUE;
  204. break;
  205. }
  206. }
  207. }
  208. if ( !fPresent )
  209. {
  210. SSortKey keyWid( pidWorkId, QUERY_SORTASCEND, 0 );
  211. sort->Add( keyWid, sort->Count() );
  212. }
  213. //
  214. // Initialize the variant types array for the sort columns
  215. //
  216. _vtInfoSortKey.Init( sort->Count() );
  217. for ( unsigned i = 0; i < sort->Count(); i++ )
  218. {
  219. SSortKey & key = sort->Get(i);
  220. PROPID pid = key.pidColumn;
  221. CColumnMasterDesc *pMasterCol = _MasterColumnSet.Find(pid);
  222. Win4Assert( 0 != pMasterCol );
  223. _vtInfoSortKey[i] = pMasterCol->DataType;
  224. }
  225. _keyCompare.Set( new CTableKeyCompare( sort.GetReference() ) );
  226. _currRow.Set( new CTableRowKey( sort.GetReference() ) );
  227. _segListMgr.GetSegmentArray().SetComparator( _keyCompare.GetPointer() );
  228. return sort.Acquire();
  229. }
  230. //+-------------------------------------------------------------------------
  231. //
  232. // Member: CLargeTable::~CLargeTable, public
  233. //
  234. // Synopsis: Destructor for a large table.
  235. //
  236. // Notes: The query execution object along with
  237. // the worker threads has been already
  238. // destroyed (see CAsyncQuery) so there
  239. // is no race condition here.
  240. //
  241. //--------------------------------------------------------------------------
  242. CLargeTable::~CLargeTable( )
  243. {
  244. //
  245. // Cancel the notification. Insure that no notifications will be
  246. // picked up as the thread leaves. In almost all cases, this will
  247. // never be called, because the notification thread will already
  248. // be killed when the last connection point goes away.
  249. //
  250. Win4Assert( 0 == _pQExecute );
  251. CancelAsyncNotification();
  252. delete _pSortSet;
  253. // make sure the waiter wakes up
  254. if ( 0 != _pQuiesce )
  255. {
  256. // only called on failure cases -- success cases quiesce ok
  257. _pQuiesce->QueryQuiesced( _fQuiescent, _scStatus );
  258. _pQuiesce = 0;
  259. }
  260. }
  261. //+-------------------------------------------------------------------------
  262. //
  263. // Member: CLargeTable::_LokFindTableSegment, private
  264. //
  265. // Synopsis: Find the appropriate table segment for a request which
  266. // operates on only a single table segment.
  267. //
  268. // Arguments: [wid] - WORKID which identifies the table segment of interest
  269. // [fMustExist] - TRUE if wid must exist in some segment
  270. // [pSegHint] - optional, possible segment in which wid will be
  271. // found.
  272. //
  273. // Returns: CTableSegment* - the selected table segment, 0 if not found.
  274. //
  275. // Notes: The method cannot be used for any of the special workIDs used
  276. // as sentinels (WORKID_TBLBEFOREFIRST, etc).
  277. // Use _LokLocateTableSegment instead.
  278. //
  279. //--------------------------------------------------------------------------
  280. CTableSegment *
  281. CLargeTable::_LokFindTableSegment(
  282. WORKID wid
  283. )
  284. {
  285. //
  286. // Iterate over all the segments and locate the segment in
  287. // which the given workid is present.
  288. //
  289. for ( CFwdTableSegIter iter(_segList); !_segList.AtEnd(iter) ;
  290. _segList.Advance(iter) )
  291. {
  292. CTableSegment & segment = *iter.GetSegment();
  293. if ( segment.IsRowInSegment( wid ) )
  294. break;
  295. }
  296. CTableSegment * pSeg = 0;
  297. if ( !_segList.AtEnd(iter) )
  298. {
  299. pSeg = iter.GetSegment();
  300. }
  301. return pSeg;
  302. }
  303. //+---------------------------------------------------------------------------
  304. //
  305. // Function: _LokSplitWindow
  306. //
  307. // Synopsis: Splits the given window into two and replaces the source
  308. // window with two windows.
  309. //
  310. // Arguments: [ppWindow] - Source window that needs to be split.
  311. // If successful, will be set to NULL on
  312. // return.
  313. // [iSplitQuery] - Offset in the rowIndex that is used by
  314. // query as the split point.
  315. //
  316. // Returns: Pointer to the "left" window after split.
  317. //
  318. // History: 2-06-95 srikants Created
  319. //
  320. // Notes: Destroys the source window after split.
  321. //
  322. //----------------------------------------------------------------------------
  323. CTableSegment * CLargeTable::_LokSplitWindow( CTableWindow ** ppWindow,
  324. ULONG iSplitQuery )
  325. {
  326. Win4Assert( 0 != ppWindow );
  327. CTableWindow * pWindow = *ppWindow;
  328. Win4Assert( 0 != pWindow );
  329. CTableWindow * pLeft = 0;
  330. CTableWindow * pRight = 0;
  331. {
  332. CTableWindowSplit split( *pWindow,
  333. iSplitQuery,
  334. pWindow->GetSegId(), _AllocSegId(),
  335. _segList.IsLast( *pWindow ) );
  336. //
  337. // Create empty target windows.
  338. //
  339. split.CreateTargetWindows();
  340. tbDebugOut(( DEB_WINSPLIT, "CLargeTable::Splitting Window\n" ));
  341. //
  342. // Do the actual split.
  343. //
  344. split.DoSplit();
  345. //
  346. // Take ownership of the newly created windows.
  347. //
  348. split.TransferTargetWindows( &pLeft, &pRight );
  349. Win4Assert( 0 != pLeft && 0 != pRight );
  350. }
  351. //
  352. // Replace the pWindow in the list with the two new ones.
  353. //
  354. CTableSegList windowList;
  355. windowList.Push( pRight );
  356. windowList.Push( pLeft );
  357. _segListMgr.Replace( pWindow, windowList );
  358. Win4Assert( windowList.IsEmpty() );
  359. //
  360. // Update the watch regions from the source window to destination
  361. // window.
  362. //
  363. for ( CWatchIter iter(_watchList) ;
  364. !_watchList.AtEnd(iter);
  365. _watchList.Advance(iter) )
  366. {
  367. HWATCHREGION hWatch = iter->Handle();
  368. CWatchRegion * pRegion = iter.Get();
  369. if ( pRegion->Segment() == pWindow )
  370. {
  371. CTableWindow * pNewStartWindow;
  372. if ( pLeft->HasWatch(hWatch) )
  373. {
  374. pNewStartWindow = pLeft;
  375. }
  376. else
  377. {
  378. Win4Assert( pRight->HasWatch(hWatch) );
  379. pNewStartWindow = pRight;
  380. }
  381. ULONG iWatch = (ULONG) pNewStartWindow->GetWatchStart(hWatch);
  382. CI_TBL_BMK bmkNew = pNewStartWindow->GetBookMarkAt( iWatch );
  383. iter->UpdateSegment( pWindow, pNewStartWindow, bmkNew );
  384. }
  385. #if CIDBG==1
  386. _watchList.CheckRegionConsistency( pRegion );
  387. #endif // CIDBG==1
  388. }
  389. //
  390. // Destroy the source window.
  391. //
  392. delete pWindow;
  393. *ppWindow = 0;
  394. //
  395. // Update the mru cache to reflect the split.
  396. //
  397. _segListMgr.UpdateSegsInUse( pLeft );
  398. _segListMgr.UpdateSegsInUse( pRight );
  399. return pRight;
  400. }
  401. //+-------------------------------------------------------------------------
  402. //
  403. // Member: CLargeTable::PathToWorkID, public
  404. //
  405. // Synopsis: Convert a file path name to a work ID. For down-level
  406. // stores, the file systems do not return a value which
  407. // can be reliably used as a WorkID, so we use the file
  408. // path name as the unique identifier of a file. This
  409. // method will use the path column compressor to provide
  410. // a unique ID over the table given the input path name.
  411. //
  412. // Arguments: [obj] -- a reference to an object retriever which can
  413. // return object data
  414. // [eRowType] -- the type of row being added.
  415. //
  416. // Returns: WORKID - a unique value over the table for this row.
  417. //
  418. // Notes:
  419. //
  420. //--------------------------------------------------------------------------
  421. WORKID CLargeTable::PathToWorkID( CRetriever& obj,
  422. CTableSink::ERowType eRowType )
  423. {
  424. Win4Assert( !_fUniqueWorkid );
  425. if ( _fUniqueWorkid )
  426. {
  427. PROPVARIANT var;
  428. ULONG cb = sizeof(var);
  429. if ( obj.GetPropertyValue( pidWorkId, &var, &cb ) != GVRSuccess )
  430. return widInvalid;
  431. else
  432. {
  433. Win4Assert( var.vt == VT_I4 );
  434. return var.lVal;
  435. }
  436. }
  437. else
  438. {
  439. WORKID ulRet = 0;
  440. GetValueResult eGvr;
  441. CColumnMasterDesc* pMastCol;
  442. CLock lock(_mutex);
  443. pMastCol = _MasterColumnSet.Find( pidPath );
  444. Win4Assert(pMastCol != NULL && pMastCol->IsCompressedCol());
  445. struct
  446. {
  447. PROPVARIANT v;
  448. WCHAR awch[512]; // don't force new every time
  449. } varnt;
  450. PROPVARIANT* pVarnt = &(varnt.v);
  451. ULONG cbBuf = sizeof varnt;
  452. XArray<BYTE> xByte;
  453. eGvr = obj.GetPropertyValue(pMastCol->PropId, pVarnt, &cbBuf);
  454. if (eGvr == GVRNotEnoughSpace)
  455. {
  456. Win4Assert(cbBuf <= TBL_MAX_DATA + sizeof (PROPVARIANT));
  457. pVarnt = (CTableVariant *) new BYTE[cbBuf];
  458. xByte.Set( cbBuf, (BYTE *)pVarnt );
  459. Win4Assert (pVarnt != NULL);
  460. eGvr = obj.GetPropertyValue(pMastCol->PropId,
  461. pVarnt, &cbBuf);
  462. }
  463. if ( GVRSuccess != eGvr )
  464. {
  465. THROW( CException(CRetriever::NtStatusFromGVR(eGvr)) );
  466. }
  467. BOOL fFound = FALSE;
  468. if ( CTableSink::eNewRow != eRowType )
  469. {
  470. // try to find an existing path before adding it
  471. fFound = pMastCol->GetCompressor()->FindData( pVarnt, ulRet );
  472. }
  473. if ( ! fFound )
  474. pMastCol->GetCompressor()->AddData(pVarnt, &ulRet, eGvr);
  475. Win4Assert(eGvr == GVRSuccess && ulRet != 0);
  476. return ulRet;
  477. }
  478. } //PathToWorkID
  479. //+---------------------------------------------------------------------------
  480. //
  481. // Function: WorkIdToPath
  482. //
  483. // Synopsis: Converts a workid to a path.
  484. //
  485. // Arguments: [wid] - WID whose path is needed
  486. // [outVarnt] - on output will have the path as a variant
  487. // [cbVarnt] - in/out max len on input; actual len on
  488. // output. If the return value is FALSE, this will
  489. // indicate the lenght of variant needed. If on
  490. // output this is 0 and the return value is FALSE,
  491. // wid->path operation failed.
  492. //
  493. // Returns: TRUE if we succeeded in getting the path.
  494. // FALSE if we failed.
  495. //
  496. // History: 3-24-95 srikants Created
  497. //
  498. //----------------------------------------------------------------------------
  499. BOOL CLargeTable::WorkIdToPath( WORKID wid, CInlineVariant & outVarnt,
  500. ULONG & cbVarnt )
  501. {
  502. Win4Assert( !_fUniqueWorkid );
  503. if ( _fUniqueWorkid )
  504. return FALSE;
  505. CLock lock( _mutex );
  506. CColumnMasterDesc* pMastCol = _MasterColumnSet.Find( pidPath );
  507. Win4Assert( pMastCol );
  508. CCompressedCol & pathCompressor = *(pMastCol->GetCompressor());
  509. CTableVariant pathVarnt;
  510. XCompressFreeVariant xpvarnt;
  511. BOOL fStatus = FALSE;
  512. if ( GVRSuccess ==
  513. pathCompressor.GetData( &pathVarnt, VT_LPWSTR, wid, pidPath ) )
  514. {
  515. xpvarnt.Set( &pathCompressor, &pathVarnt );
  516. //
  517. // Copy the data from the variant to the buffer.
  518. //
  519. const ULONG cbHeader = sizeof(CInlineVariant);
  520. ULONG cbVarData = pathVarnt.VarDataSize();
  521. ULONG cbTotal = cbVarData + cbHeader;
  522. if ( cbVarnt >= cbTotal )
  523. {
  524. CVarBufferAllocator bufAlloc( outVarnt.GetVarBuffer(), cbVarData );
  525. bufAlloc.SetBase(0);
  526. pathVarnt.Copy( &outVarnt, bufAlloc, (USHORT) cbVarData, 0 );
  527. fStatus = TRUE;
  528. }
  529. cbVarnt = cbTotal;
  530. }
  531. else
  532. {
  533. cbVarnt = 0;
  534. }
  535. return fStatus;
  536. } //WorkIdToPath
  537. //+-------------------------------------------------------------------------
  538. //
  539. // Member: CLargeTable::_LokRemoveCategorizedRow, private
  540. //
  541. // Synopsis: Removes a row from the categorization.
  542. //
  543. // Arguments: [chapt] -- chapter from which removal is done
  544. // [wid] -- wid to remove
  545. // [widNext] -- the next wid in the table, can be widInvalid
  546. // [pSegment] -- segment from which widNext can be computed if
  547. // not specified.
  548. //
  549. // History: ? dlee Created
  550. //
  551. //--------------------------------------------------------------------------
  552. void CLargeTable::_LokRemoveCategorizedRow(
  553. CI_TBL_CHAPT chapt,
  554. WORKID wid,
  555. WORKID widNext,
  556. CTableSegment * pSegment )
  557. {
  558. if ( IsCategorized() )
  559. {
  560. if ( widInvalid == widNext )
  561. {
  562. // sigh. We need to find the workid of the row after the
  563. // row just deleted in the case that the deleted row was the
  564. // first row in a category and not the only row in the category,
  565. // since the categorizers need to keep track of the first wid
  566. // in a category. widNext is widInvalid if the deleted row
  567. // was the last row in the window.
  568. for ( CFwdTableSegIter iter( _segList );
  569. !_segList.AtEnd( iter );
  570. _segList.Advance( iter ) )
  571. {
  572. CTableSegment * pNextSeg = iter.GetSegment();
  573. if ( pNextSeg == pSegment )
  574. {
  575. _segList.Advance( iter );
  576. if ( !_segList.AtEnd( iter ) )
  577. {
  578. CTableWindow * pWindow = iter.GetWindow();
  579. widNext = pWindow->GetFirstBookMark();
  580. }
  581. break;
  582. }
  583. _segList.Advance(iter);
  584. }
  585. }
  586. pSegment->GetCategorizer()->RemoveRow( chapt, wid, widNext );
  587. }
  588. } //_LokRemoveCategorizedRow
  589. //+-------------------------------------------------------------------------
  590. //
  591. // Member: CLargeTable::PutRow, public
  592. //
  593. // Synopsis: Add a row to a large table
  594. //
  595. // Arguments: [obj] -- a reference to an object retriever which can
  596. // return object data
  597. // [eRowType] -- the type of row being added.
  598. //
  599. // Returns: TRUE if progress report needed
  600. //
  601. //--------------------------------------------------------------------------
  602. BOOL CLargeTable::PutRow( CRetriever& obj, CTableSink::ERowType eRowType )
  603. {
  604. CReleasableLock relLock( _mutex, FALSE );
  605. if ( FALSE == relLock.Try() )
  606. {
  607. // Unable to get bigtable lock. Maybe GetRows is holding bigtable
  608. // lock and is waiting on propstore lock which this thread might
  609. // be holding. So rlease that and try again.
  610. obj.Quiesce();
  611. relLock.Request(); // If we deadlock now, then we need to fix that !
  612. }
  613. //
  614. // The query may be in the process of being deleted if it has been
  615. // cancelled during query execution. In this case, the _pQExecute will
  616. // have been set to 0 by the call to ReleaseQueryExecute() in
  617. // ~CAsyncQuery.
  618. //
  619. if ( 0 == _pQExecute )
  620. return FALSE;
  621. TRY
  622. {
  623. WORKID widRow = obj.WorkId();
  624. //
  625. // Check if it already exists in one of the segments based
  626. // on its workid.
  627. //
  628. CTableSegment *pSegment = 0;
  629. if ( CTableSink::eNotificationRow == eRowType )
  630. {
  631. //
  632. // If the table is not watched, do not process notifications.
  633. //
  634. if ( !_LokIsWatched() )
  635. {
  636. return _fProgressNeeded;
  637. }
  638. else if ( _LokIsPutRowDeferred( widRow, obj ) )
  639. {
  640. _bitRefresh = 1;
  641. LokCompleteAsyncNotification();
  642. return _fProgressNeeded;
  643. }
  644. }
  645. else if ( CTableSink::eNewRow != eRowType )
  646. {
  647. //
  648. // NOSUPPORT: This will not work with LINKS. We have to look at
  649. // table irrespective of whether this is a notification or not.
  650. // Of course, we don't support links.
  651. //
  652. //
  653. pSegment = _LokFindTableSegment( obj.WorkId() );
  654. }
  655. if ( 0 != pSegment )
  656. {
  657. //
  658. // Modifications are to be treated as deletions followed by
  659. // additions.
  660. //
  661. //
  662. // First delete the current row and then add the new row.
  663. // If the "key" of this row is different from the one already
  664. // in the table, the new row may end up in a different bucket
  665. // than the original.
  666. //
  667. PROPVARIANT varWid;
  668. varWid.lVal = (LONG) obj.WorkId();
  669. varWid.vt = VT_I4;
  670. tbDebugOut(( DEB_BOOKMARK, "CLargeTable - Delete And ReAdd WorkId 0x%X\n",
  671. varWid.lVal ));
  672. WORKID widNext;
  673. CI_TBL_CHAPT chapt;
  674. //
  675. // Delete the row and then add a new one.
  676. //
  677. pSegment->RemoveRow( varWid, widNext, chapt );
  678. _LokRemoveCategorizedRow( chapt,
  679. obj.WorkId(),
  680. widNext,
  681. pSegment );
  682. }
  683. else
  684. {
  685. pSegment = _segListMgr.GetCachedPutRowSeg();
  686. }
  687. CTableRowPutter rowPutter( *this, obj );
  688. pSegment = rowPutter.LokFindSegToInsert( pSegment );
  689. Win4Assert( 0 != pSegment );
  690. //
  691. // If the current segment is getting full, we should either split it
  692. // or create a new one.
  693. //
  694. if ( pSegment->IsGettingFull() )
  695. pSegment = rowPutter.LokSplitOrAddSegment( pSegment );
  696. Win4Assert( 0 != pSegment );
  697. Win4Assert( pSegment->GetLowestKey().IsInitialized() );
  698. Win4Assert( pSegment->GetHighestKey().IsInitialized() );
  699. BOOL fRowThrownAway = FALSE;
  700. // Check for Row limit...
  701. ULONG cRowLimit = FirstRows();
  702. tbDebugOut(( DEB_ITRACE, "CLargeTable::PutRow: FirstRows is %d, MaxRows is %d\n", FirstRows(), MaxRows() ));
  703. BOOL fFirstRows = cRowLimit > 0;
  704. if ( !fFirstRows )
  705. cRowLimit = MaxRows();
  706. tbDebugOut(( DEB_ITRACE, "CLargeTable::PutRow: RowCount() is %d\n", RowCount() ));
  707. tbDebugOut(( DEB_ITRACE, "CLargeTable::PutRow: cRowLimit is %d\n", cRowLimit ));
  708. if ( 0 == cRowLimit || RowCount() < cRowLimit )
  709. {
  710. pSegment->PutRow( obj, _currRow.GetReference() );
  711. }
  712. else
  713. {
  714. // We are here. Therefore it means that:
  715. // There is a maxrow limit set AND rowcount >= maxrows AND
  716. // we have at least one segment which has at least one row...
  717. // Note: The special case of sort by rank descending is handled
  718. // by CQAsyncExecute::Resolve in which case we only get rows less
  719. // than equal to MaxRows (in case MaxRows is defined)
  720. if ( !fFirstRows )
  721. {
  722. if ( !_fSortDefined )
  723. {
  724. // Since not sort order is defined, we can stop processing of
  725. // rows here, since we have all the data that we need
  726. _fNoMoreData = fRowThrownAway = TRUE;
  727. }
  728. else
  729. {
  730. _currRow->MakeReady();
  731. // There is a sort defined. So we need to process all the
  732. // rows and put the best results in the maxrow rows
  733. CTableSegment* pLastSegment = _segListMgr.GetList().GetLast();
  734. Win4Assert( pLastSegment );
  735. // Now we need to make sure that the last segment is a window
  736. // and it has at least one row in it. This is done because
  737. // a bucket does not support the kind of operations that
  738. // we are planning to do here on...
  739. while ( !pLastSegment->IsWindow() || 0 == pLastSegment->RowCount() )
  740. {
  741. if ( 0 == pLastSegment->RowCount() )
  742. {
  743. // delete this segment
  744. _segListMgr.RemoveFromList( pLastSegment );
  745. delete pLastSegment;
  746. pLastSegment = _segListMgr.GetList().GetLast();
  747. Win4Assert( pLastSegment );
  748. continue;
  749. }
  750. if ( !pLastSegment->IsWindow() )
  751. {
  752. // Convert it to a window
  753. Win4Assert( pLastSegment->IsBucket() );
  754. obj.Quiesce();
  755. XPtr<CTableBucket> xBktToExpand( (CTableBucket*)pLastSegment );
  756. CDoubleTableSegIter iter( pLastSegment );
  757. _LokReplaceWithEmptyWindow( iter );
  758. _NoLokBucketToWindows( xBktToExpand, 0, FALSE, FALSE );
  759. pLastSegment = _segListMgr.GetList().GetLast();
  760. Win4Assert( pLastSegment );
  761. // pSegment may no longer exist -- look it up again
  762. CTableRowPutter rp( *this, obj );
  763. pSegment = rp.LokFindSegToInsert( 0 );
  764. Win4Assert( 0 != pSegment );
  765. }
  766. }
  767. if ( ( pLastSegment == pSegment ) &&
  768. ( _keyCompare->Compare( _currRow.GetReference(),
  769. pSegment->GetHighestKey() ) > 0 ) )
  770. {
  771. // Since the current row is worse than the our worst row,
  772. // we can throw it away
  773. fRowThrownAway = TRUE;
  774. // NEWFEATURE: update counter of thrown rows
  775. }
  776. else
  777. {
  778. // CurrRow is better than (at least) our worst row
  779. // Delete the last row in the last segment and insert
  780. // the new row. This would keep RowCount == MaxRows
  781. PROPVARIANT varWid;
  782. varWid.lVal = (LONG) ((CTableWindow*)pLastSegment)->
  783. _GetLastWorkId();
  784. Win4Assert( widInvalid != varWid.lVal );
  785. varWid.vt = VT_I4;
  786. WORKID widNext;
  787. CI_TBL_CHAPT chapt;
  788. pLastSegment->RemoveRow( varWid, widNext, chapt );
  789. _LokRemoveCategorizedRow( chapt,
  790. varWid.lVal,
  791. widNext,
  792. pLastSegment );
  793. // Insert the new row
  794. pSegment->PutRow( obj, _currRow.GetReference() );
  795. if ( 0 == pLastSegment->RowCount() )
  796. {
  797. // remove this segment
  798. _segListMgr.RemoveFromList( pLastSegment );
  799. delete pLastSegment;
  800. }
  801. }
  802. }
  803. }
  804. }
  805. if ( !fRowThrownAway )
  806. {
  807. _segListMgr.SetCachedPutRowSeg( pSegment );
  808. if ( rowPutter.LokIsNewWindowCreated() &&
  809. ( ! _fRankVectorBound ) &&
  810. ( ! IsCategorized() ) &&
  811. ( _fUniqueWorkid ) ) // don't bucketize ::_noindex_:: catalogs
  812. {
  813. _LokConvertWindowsToBucket();
  814. }
  815. _bitRefresh = 1;
  816. LokCompleteAsyncNotification();
  817. }
  818. }
  819. CATCH( CException, e )
  820. {
  821. if ( e.GetErrorCode() != STATUS_FILE_DELETED )
  822. {
  823. RETHROW();
  824. }
  825. }
  826. END_CATCH
  827. return _fProgressNeeded;
  828. } //PutRow
  829. //+---------------------------------------------------------------------------
  830. //
  831. // Member: CLargeTable::_LokDeferPutRow
  832. //
  833. // Synopsis:
  834. //
  835. // Arguments: [wid] -
  836. //
  837. // Returns:
  838. //
  839. // History: 8-01-95 srikants Created
  840. //
  841. // Notes:
  842. //
  843. //----------------------------------------------------------------------------
  844. inline
  845. void CLargeTable::_LokDeferPutRow(
  846. WORKID wid,
  847. CRetriever & obj )
  848. {
  849. Win4Assert( 0 != _pSortSet );
  850. if ( 0 == _pDeferredRows )
  851. {
  852. _pDeferredRows = new CTableBucket( *_pSortSet,
  853. _keyCompare.GetReference(),
  854. _MasterColumnSet,
  855. _AllocSegId() );
  856. }
  857. PROPVARIANT vRank;
  858. ULONG cbRank = sizeof vRank;
  859. obj.GetPropertyValue( pidRank, &vRank, &cbRank );
  860. Win4Assert( VT_I4 == vRank.vt );
  861. PROPVARIANT vHitCount;
  862. ULONG cbHitCount = sizeof vHitCount;
  863. obj.GetPropertyValue( pidHitCount, &vHitCount, &cbHitCount );
  864. Win4Assert( VT_I4 == vHitCount.vt );
  865. _pDeferredRows->_AddWorkId( wid,
  866. vRank.lVal,
  867. vHitCount.lVal );
  868. }
  869. //+---------------------------------------------------------------------------
  870. //
  871. // Member: CLargeTable::_LokIsPutRowDeferred
  872. //
  873. // Synopsis: If the workid given is being watched and the client knows
  874. // about its existence, then we must defer the addition of
  875. // this row until later.
  876. //
  877. // Arguments: [wid] -
  878. //
  879. // Returns:
  880. //
  881. // History: 8-01-95 srikants Created
  882. //
  883. // Notes:
  884. //
  885. //----------------------------------------------------------------------------
  886. BOOL CLargeTable::_LokIsPutRowDeferred( WORKID widRow, CRetriever &obj )
  887. {
  888. Win4Assert( _LokIsWatched() );
  889. PROPVARIANT varWid;
  890. varWid.lVal = (LONG) widRow;
  891. varWid.vt = VT_I4;
  892. WORKID widNext;
  893. BOOL fDeferred = FALSE;
  894. for ( CFwdTableSegIter iter(_segList); !_segList.AtEnd(iter);
  895. _segList.Advance(iter) )
  896. {
  897. CTableSegment * pSegment = iter.GetSegment();
  898. WORKID widNext;
  899. CI_TBL_CHAPT chapt;
  900. //
  901. // NEWFEATURE: (windowed notifications)
  902. // This is not correct. We should remove it only if soft
  903. // deletions are being done on a window. If it is a hard delete,
  904. // we must wait until a refresh is called. May need a different
  905. // data structure for the case of watch all - a bucket will not
  906. // suffice.
  907. //
  908. if ( pSegment->RemoveRow(varWid, widNext, chapt) )
  909. {
  910. _LokRemoveCategorizedRow( chapt,
  911. widRow,
  912. widNext,
  913. pSegment );
  914. if ( pSegment->IsWindow() )
  915. {
  916. CTableWindow * pWindow = iter.GetWindow();
  917. if ( pWindow->IsPendingDelete( widRow ) )
  918. {
  919. _LokDeferPutRow( widRow, obj );
  920. fDeferred = TRUE;
  921. }
  922. }
  923. break;
  924. }
  925. }
  926. return fDeferred;
  927. }
  928. //+---------------------------------------------------------------------------
  929. //
  930. // Member: CLargeTable::_LokRemoveIfDeferred
  931. //
  932. // Synopsis:
  933. //
  934. // Arguments: [wid] -
  935. //
  936. // Returns:
  937. //
  938. // History: 8-01-95 srikants Created
  939. //
  940. // Notes:
  941. //
  942. //----------------------------------------------------------------------------
  943. BOOL CLargeTable::_LokRemoveIfDeferred( WORKID wid )
  944. {
  945. BOOL fRemoved = FALSE;
  946. if ( 0 != _pDeferredRows )
  947. {
  948. PROPVARIANT varWid;
  949. varWid.lVal = (LONG) wid;
  950. varWid.vt = VT_I4;
  951. WORKID widNext;
  952. CI_TBL_CHAPT chapt;
  953. fRemoved = _pDeferredRows->RemoveRow( varWid, widNext, chapt );
  954. }
  955. return fRemoved;
  956. }
  957. //+-------------------------------------------------------------------------
  958. //
  959. // Member: CLargeTable::_LokCheckQueryStatus, private
  960. //
  961. // Synopsis: Fail a request if the query has encountered an error.
  962. //
  963. // Arguments: - NONE -
  964. //
  965. // Returns: Nothing, throws E_FAIL on error.
  966. //
  967. //--------------------------------------------------------------------------
  968. void CLargeTable::_LokCheckQueryStatus( )
  969. {
  970. if (QUERY_FILL_STATUS( Status() ) == STAT_ERROR)
  971. {
  972. NTSTATUS sc = GetStatusError();
  973. Win4Assert( sc != STATUS_SUCCESS );
  974. tbDebugOut(( DEB_WARN,
  975. "Bigtable 0x%x Query failed, sc = %x\n",
  976. this, sc));
  977. if (sc == STATUS_SUCCESS)
  978. sc = E_FAIL;
  979. THROW( CException( sc ));
  980. }
  981. else if ( _fAbort )
  982. {
  983. THROW( CException( STATUS_TOO_LATE ) );
  984. }
  985. }
  986. //+-------------------------------------------------------------------------
  987. //
  988. // Member: CLargeTable::GetRows, public
  989. //
  990. // Synopsis: Retrieve row data from a large table.
  991. //
  992. // Arguments: [widStart] - WORKID identifying first row to be
  993. // transferred. If WORKID_TBLFIRST is
  994. // used, the transfer will start at the first
  995. // row in the segment.
  996. // [chapt] - Chapter from which to fetch rows (if chaptered)
  997. // [rOutColumns] - a CTableColumnSet that describes the
  998. // output format of the data table.
  999. // [rGetParams] - an CGetRowsParams structure which
  1000. // describes how many rows are to be fetched and
  1001. // other parameters of the operation.
  1002. // [rwidLastRowTransferred] - on return, the work ID of
  1003. // the last row to be transferred from this table.
  1004. // Can be used to initialize widStart on next call.
  1005. //
  1006. // Returns: SCODE - status of the operation. DB_S_ENDOFROWSET if
  1007. // widStart is WORKID_TBLAFTERLAST at start of
  1008. // transfer, or if rwidLastRowTransferred is the
  1009. // last row in the segment at the end of the transfer.
  1010. //
  1011. // DB_S_BUFFERTOOSMALL is returned if the available
  1012. // space in the out-of-line data was exhausted during
  1013. // the transfer.
  1014. //
  1015. // Notes: To transfer successive rows, as in GetNextRows, the
  1016. // rwidLastRowTransferred must be advanced by one prior
  1017. // to the next transfer.
  1018. //
  1019. //--------------------------------------------------------------------------
  1020. SCODE CLargeTable::GetRows(
  1021. HWATCHREGION hRegion,
  1022. WORKID widStart,
  1023. CI_TBL_CHAPT chapt,
  1024. CTableColumnSet const & rOutColumns,
  1025. CGetRowsParams & rGetParams,
  1026. WORKID & rwidLastRowTransferred
  1027. )
  1028. {
  1029. return GetRowsAt( hRegion, widStart, chapt, 0, rOutColumns,
  1030. rGetParams, rwidLastRowTransferred );
  1031. }
  1032. //+-------------------------------------------------------------------------
  1033. //
  1034. // Member: CLargeTable::RestartPosition, public
  1035. //
  1036. // Synopsis: Set next fetch position for the chapter to the start
  1037. //
  1038. // Arguments: [chapt] - Chapter from which to fetch rows (if chaptered)
  1039. //
  1040. // Returns: SCODE - status of the operation.
  1041. //
  1042. //--------------------------------------------------------------------------
  1043. void CLargeTable::RestartPosition(
  1044. CI_TBL_CHAPT chapt)
  1045. {
  1046. SetCurrentPosition( chapt, WORKID_TBLBEFOREFIRST );
  1047. CTableSource::RestartPosition( chapt );
  1048. }
  1049. //+-------------------------------------------------------------------------
  1050. //
  1051. // Member: CLargeTable::RowCount, public
  1052. //
  1053. // Synopsis: Return the total row count in the table
  1054. //
  1055. // Returns: ULONG - row count aggregated over all segments in the
  1056. // table.
  1057. //
  1058. //--------------------------------------------------------------------------
  1059. DBCOUNTITEM CLargeTable::RowCount()
  1060. {
  1061. CLock lock(_mutex);
  1062. _LokCheckQueryStatus();
  1063. return _LokRowCount();
  1064. }
  1065. //+-------------------------------------------------------------------------
  1066. //
  1067. // Member: CLargeTable::_LokRowCount, public
  1068. //
  1069. // Synopsis: Return the total row count in the table
  1070. //
  1071. // Returns: ULONG - row count aggregated over all segments in the
  1072. // table.
  1073. //
  1074. //--------------------------------------------------------------------------
  1075. DBCOUNTITEM CLargeTable::_LokRowCount()
  1076. {
  1077. DBCOUNTITEM cRowsTotal = 0;
  1078. for ( CFwdTableSegIter iter(_segList); !_segList.AtEnd(iter); _segList.Advance(iter) )
  1079. {
  1080. cRowsTotal += iter.GetSegment()->RowCount();
  1081. }
  1082. return cRowsTotal;
  1083. }
  1084. //+-------------------------------------------------------------------------
  1085. //
  1086. // Member: CLargeTable::RatioFinished
  1087. //
  1088. // Synopsis: Return query progress
  1089. //
  1090. // Arguments: [ulDeneominator] - on return, denominator of fraction
  1091. // [ulNumerator] - on return, numerator of fraction
  1092. // [cRows] - on return, number of rows in table
  1093. //
  1094. // Notes: For the fQuick case, we could try doing a quick
  1095. // synchronization with the CAsyncExecute to compute
  1096. // a good value for the ratio, but the implementation
  1097. // below is fine for the Gibraltar query since no callers
  1098. // will use the ratio anyway.
  1099. //
  1100. // A sketch of the code needed to do the quick synchronization
  1101. // is below:
  1102. // BOOL CAsyncExecute::QuickRF( ULONG &ulDen, ULONG &ulNum )
  1103. // {
  1104. // CLock lock(_mutex);
  1105. // if (_fRunning)
  1106. // return FALSE;
  1107. // else
  1108. // {
  1109. // _pCurResolve->RatioFinished( ulDen, ulNum );
  1110. // return TRUE;
  1111. // }
  1112. // }
  1113. //
  1114. //
  1115. // History: Mar-20-1995 BartoszM Created
  1116. //
  1117. //--------------------------------------------------------------------------
  1118. void CLargeTable::RatioFinished (
  1119. DBCOUNTITEM& ulDenominator,
  1120. DBCOUNTITEM& ulNumerator,
  1121. DBCOUNTITEM& cRows )
  1122. {
  1123. CLock lock(_mutex);
  1124. _LokCheckQueryStatus();
  1125. if (_fQuiescent)
  1126. {
  1127. cRows = _LokRowCount();
  1128. ulDenominator = ulNumerator = 100;
  1129. return;
  1130. }
  1131. _fProgressNeeded = TRUE;
  1132. ulDenominator = _ulProgressDenom;
  1133. ulNumerator = _ulProgressNum;
  1134. cRows = _LokRowCount();
  1135. }
  1136. //+-------------------------------------------------------------------------
  1137. //
  1138. // Member: CLargeTable::ProgressDone, public
  1139. //
  1140. // Synopsis: Sets the progress indicators and wakes up
  1141. // the client
  1142. //
  1143. // Arguments: [ulDenominator]
  1144. // [ulNumerator]
  1145. //
  1146. // History: Mar-21-95 BartoszM Created
  1147. //
  1148. //--------------------------------------------------------------------------
  1149. void CLargeTable::ProgressDone (ULONG ulDenominator, ULONG ulNumerator)
  1150. {
  1151. tbDebugOut(( DEB_ITRACE, "CLargeTable reporting progress %ld / %ld\n",
  1152. ulNumerator, ulDenominator ));
  1153. CLock lock(_mutex);
  1154. _fProgressNeeded = FALSE;
  1155. _ulProgressDenom = ulDenominator;
  1156. _ulProgressNum = ulNumerator;
  1157. }
  1158. void CLargeTable::Quiesce ()
  1159. {
  1160. TRY
  1161. {
  1162. CLock lock(_mutex);
  1163. tbDebugOut(( DEB_NOTIFY, "CLargeTable reached quiescent state\n" ));
  1164. Win4Assert( QUERY_FILL_STATUS( Status() ) == STAT_DONE ||
  1165. QUERY_FILL_STATUS( Status() ) == STAT_ERROR );
  1166. _fProgressNeeded = FALSE;
  1167. _fQuiescent = TRUE;
  1168. _ulProgressDenom = 100;
  1169. _ulProgressNum = 100;
  1170. // don't tell the client we quiesced more than once
  1171. tbDebugOut(( DEB_ITRACE, "CLargeTable::Quiesce: _bitQuiesced is %d\n", _bitQuiesced ));
  1172. if ( 0 == _bitQuiesced )
  1173. {
  1174. _bitChangeQuiesced = 1;
  1175. _bitQuiesced = 1;
  1176. LokCompleteAsyncNotification();
  1177. }
  1178. // inform the client once that we're complete
  1179. if ( 0 != _pQuiesce )
  1180. {
  1181. _pQuiesce->QueryQuiesced( TRUE, _scStatus );
  1182. _pQuiesce = 0;
  1183. }
  1184. }
  1185. CATCH( CException, e )
  1186. {
  1187. // ignore the exception; it may be in a unwind path
  1188. }
  1189. END_CATCH;
  1190. }
  1191. //+-------------------------------------------------------------------------
  1192. //
  1193. // Member: CLargeTable::GetApproximatePosition, public
  1194. //
  1195. // Synopsis: Return the approximate current position as a fraction
  1196. //
  1197. // Arguments: [chapt] - chapter
  1198. // [bmk] - bookmark
  1199. // [pulNumerator] - on return, numerator of fraction
  1200. // [pulRowCount] - on return, denominator of fraction (row count)
  1201. //
  1202. // Returns: SCODE - the status of the operation.
  1203. //
  1204. // Notes: The denominator of the fraction is the approximate
  1205. // row count in the table or chapter.
  1206. //
  1207. //--------------------------------------------------------------------------
  1208. SCODE
  1209. CLargeTable::GetApproximatePosition(
  1210. CI_TBL_CHAPT chapt,
  1211. CI_TBL_BMK bmk,
  1212. DBCOUNTITEM * pulNumerator,
  1213. DBCOUNTITEM * pulRowCount
  1214. )
  1215. {
  1216. CLock lock(_mutex);
  1217. _LokCheckQueryStatus();
  1218. if (bmk == widInvalid)
  1219. return DB_E_BADBOOKMARK;
  1220. Win4Assert (bmk != WORKID_TBLBEFOREFIRST && bmk != WORKID_TBLAFTERLAST);
  1221. DBCOUNTITEM iBmkPosition = ULONG_MAX;
  1222. DBCOUNTITEM cRows = 0;
  1223. if ( IsCategorized() && DB_NULL_HCHAPTER != chapt )
  1224. {
  1225. cRows = GetCategorizer()->GetRowCount( chapt );
  1226. if ( WORKID_TBLFIRST == bmk )
  1227. {
  1228. iBmkPosition = 1;
  1229. if (cRows == 0)
  1230. iBmkPosition = 0;
  1231. }
  1232. else if ( WORKID_TBLLAST == bmk )
  1233. {
  1234. iBmkPosition = cRows;
  1235. }
  1236. else
  1237. {
  1238. WORKID widFirst = GetCategorizer()->GetFirstWorkid( chapt );
  1239. CFwdTableSegIter iter( _segList );
  1240. DBCOUNTITEM cChaptRows = 0;
  1241. BOOL fFoundFirstYet = FALSE;
  1242. while ( !_segList.AtEnd(iter) )
  1243. {
  1244. ULONG iRow = 0;
  1245. CTableSegment * pSegment = iter.GetSegment();
  1246. if ( pSegment->IsWindow() )
  1247. {
  1248. CTableWindow * pWindow = iter.GetWindow();
  1249. ULONG iFirstRow;
  1250. if ( !fFoundFirstYet &&
  1251. pWindow->RowOffset( widFirst, iFirstRow ) )
  1252. {
  1253. if ( pWindow->RowOffset(bmk, iRow) )
  1254. {
  1255. iBmkPosition = iRow - iFirstRow + 1;
  1256. break;
  1257. }
  1258. else
  1259. {
  1260. cChaptRows = pSegment->RowCount() - iFirstRow;
  1261. fFoundFirstYet = TRUE;
  1262. }
  1263. }
  1264. else if ( pWindow->RowOffset(bmk, iRow) )
  1265. {
  1266. // We can't have set the numerator previously.
  1267. Win4Assert(iBmkPosition == ULONG_MAX);
  1268. iBmkPosition = cChaptRows + iRow + 1;
  1269. break;
  1270. }
  1271. else if ( fFoundFirstYet )
  1272. {
  1273. cChaptRows += pSegment->RowCount();
  1274. }
  1275. }
  1276. else
  1277. {
  1278. // The chapter is in a bucket. All rows in the
  1279. // bucket are in the chapter, and the chapter
  1280. // spans no other segments.
  1281. CTableBucket * pBucket = iter.GetBucket();
  1282. if ( pBucket->IsRowInSegment(bmk) )
  1283. {
  1284. Win4Assert( iBmkPosition == ULONG_MAX );
  1285. if ( pBucket->RowOffset(bmk, iRow) )
  1286. {
  1287. iBmkPosition = iRow + 1;
  1288. }
  1289. else
  1290. {
  1291. iBmkPosition = ((ULONG)pBucket->RowCount() + 1)/2;
  1292. }
  1293. }
  1294. }
  1295. _segList.Advance(iter);
  1296. }
  1297. }
  1298. }
  1299. else
  1300. {
  1301. if (bmk == WORKID_TBLFIRST)
  1302. {
  1303. iBmkPosition = 1;
  1304. cRows = RowCount();
  1305. if (cRows == 0)
  1306. iBmkPosition = 0;
  1307. }
  1308. else if (bmk == WORKID_TBLLAST)
  1309. {
  1310. cRows = RowCount();
  1311. iBmkPosition = cRows;
  1312. }
  1313. else
  1314. {
  1315. //
  1316. // Iterate over all table segments prior to the table seg.
  1317. // in which the bookmark occurs, adding their row counts to
  1318. // iBmkPosition. Accumulate the total row count at the same
  1319. // time.
  1320. //
  1321. CFwdTableSegIter iter( _segList );
  1322. while ( !_segList.AtEnd(iter) )
  1323. {
  1324. ULONG iRow = 0;
  1325. CTableSegment * pSegment = iter.GetSegment();
  1326. if ( pSegment->IsWindow() )
  1327. {
  1328. CTableWindow * pWindow = iter.GetWindow();
  1329. if ( pWindow->RowOffset(bmk, iRow) )
  1330. {
  1331. // We can't have set the numerator previously.
  1332. Win4Assert(iBmkPosition == ULONG_MAX);
  1333. iBmkPosition = cRows + iRow + 1;
  1334. }
  1335. }
  1336. else
  1337. {
  1338. CTableBucket * pBucket = iter.GetBucket();
  1339. if ( pBucket->IsRowInSegment(bmk) )
  1340. {
  1341. Win4Assert( iBmkPosition == ULONG_MAX );
  1342. iBmkPosition = cRows;
  1343. if ( pBucket->RowOffset(bmk, iRow) )
  1344. {
  1345. iBmkPosition += iRow + 1;
  1346. }
  1347. else
  1348. {
  1349. iBmkPosition += ((ULONG)pBucket->RowCount() + 1)/2;
  1350. }
  1351. }
  1352. }
  1353. cRows += pSegment->RowCount();
  1354. _segList.Advance(iter);
  1355. }
  1356. }
  1357. }
  1358. if (iBmkPosition == ULONG_MAX)
  1359. return DB_E_BADBOOKMARK;
  1360. Win4Assert(iBmkPosition <= cRows);
  1361. *pulNumerator = iBmkPosition;
  1362. *pulRowCount = cRows;
  1363. return S_OK;
  1364. } //GetApproximagePosition
  1365. //+-------------------------------------------------------------------------
  1366. //
  1367. // Member: CLargeTable::IsColumnInTable, public
  1368. //
  1369. // Synopsis: Check whether some column can be added to the table.
  1370. // Used in support of CQuery::SetBindings; added columns
  1371. // may only refeerence columns which already exist in the
  1372. // table.
  1373. //
  1374. // Arguments: [PropId] - the property ID to be added to the table.
  1375. //
  1376. // Returns: BOOL - TRUE if it's okay to add the column. False
  1377. // otherwise.
  1378. //
  1379. // Notes:
  1380. //
  1381. //--------------------------------------------------------------------------
  1382. BOOL
  1383. CLargeTable::IsColumnInTable(
  1384. PROPID PropId
  1385. ) {
  1386. CLock lock(_mutex);
  1387. //
  1388. // See if the column already exists in the master column set
  1389. //
  1390. if ( _MasterColumnSet.Find( PropId ) != 0 ) {
  1391. return TRUE;
  1392. } else {
  1393. return FALSE;
  1394. }
  1395. }
  1396. #if 0
  1397. //+-------------------------------------------------------------------------
  1398. //
  1399. // Member: CLargeTable::_LokLocateTableSegment, private
  1400. //
  1401. // Synopsis: Position a table segment iterator to the table segment
  1402. // in which some work ID is found.
  1403. //
  1404. // Arguments: [rIter] - An iterator over table segments
  1405. // [chapt] - Chapter in which to search for row
  1406. // [wid] - value to be searched for
  1407. //
  1408. // Returns: BOOL - TRUE if the work ID was found, FALSE if not.
  1409. //
  1410. // Notes:
  1411. //
  1412. //--------------------------------------------------------------------------
  1413. WORKID
  1414. CLargeTable::_LokLocateTableSegment(
  1415. CDoubleTableSegIter& rIter,
  1416. CI_TBL_CHAPT chapt,
  1417. WORKID wid
  1418. )
  1419. {
  1420. Win4Assert( !_segList.AtEnd(rIter) );
  1421. if ( IsSpecialWid( wid ) )
  1422. {
  1423. if ( IsCategorized() && DB_NULL_HCHAPTER != chapt )
  1424. {
  1425. if ( WORKID_TBLFIRST == wid ||
  1426. WORKID_TBLBEFOREFIRST == wid )
  1427. {
  1428. wid = GetCategorizer()->GetFirstWorkid( chapt );
  1429. }
  1430. else
  1431. {
  1432. // Heuristic: assume last row of [chapt] is in same window
  1433. // as first row of [ next chapt ]
  1434. wid = GetCategorizer()->GetFirstWorkidOfNextCategory( chapt );
  1435. if ( widInvalid == wid )
  1436. {
  1437. // chapt was the last chapter -- get the last segment
  1438. while ( !_segList.IsLast(rIter) )
  1439. _segList.Advance(rIter);
  1440. return TRUE;
  1441. }
  1442. else
  1443. {
  1444. // check if this row (the first of the next chapter) is
  1445. // the first in the segment. If so, back up to the prev
  1446. // segment.
  1447. Win4Assert( _segList.IsFirst(rIter) );
  1448. while ( !_segList.AtEnd(rIter) )
  1449. {
  1450. if ( rIter.GetSegment()->IsRowInSegment(wid) )
  1451. {
  1452. ULONG iRow;
  1453. CTableWindow * pWindow = rIter.GetWindow();
  1454. pWindow->RowOffset( wid, iRow );
  1455. if ( 0 == iRow )
  1456. {
  1457. // uh, oh. Back up a segment. The first row of
  1458. // the next category is the first row in the seg,
  1459. // so the last row of the categ must be in the
  1460. // prev segment.
  1461. Win4Assert( !_segList.IsFirst(rIter) );
  1462. _segList.BackUp(rIter);
  1463. }
  1464. return TRUE;
  1465. }
  1466. _segList.Advance(rIter);
  1467. }
  1468. tbDebugOut((DEB_WARN, "Got confused looking for %x\n", wid));
  1469. return FALSE;
  1470. }
  1471. }
  1472. }
  1473. else
  1474. {
  1475. //
  1476. // For the special cases of Beginning and End, just position to the
  1477. // correct end.
  1478. //
  1479. if (wid == WORKID_TBLBEFOREFIRST || wid == WORKID_TBLFIRST)
  1480. {
  1481. while ( !_segList.IsFirst(rIter) )
  1482. _segList.BackUp(rIter);
  1483. }
  1484. else
  1485. {
  1486. Win4Assert( wid == WORKID_TBLLAST ||
  1487. wid == WORKID_TBLAFTERLAST );
  1488. while ( !_segList.IsLast(rIter) )
  1489. _segList.Advance(rIter);
  1490. }
  1491. return TRUE;
  1492. }
  1493. }
  1494. //
  1495. // Locate the appropriate segment
  1496. // NOTE: we assume we start with a fresh iterator, so just go forward
  1497. //
  1498. Win4Assert( _segList.IsFirst(rIter) );
  1499. while ( !_segList.AtEnd(rIter) )
  1500. {
  1501. if ( rIter.GetSegment()->IsRowInSegment(wid) )
  1502. {
  1503. return TRUE;
  1504. }
  1505. _segList.Advance(rIter);
  1506. }
  1507. tbDebugOut((DEB_WARN, "Work ID %x not found in table\n", wid));
  1508. return FALSE;
  1509. }
  1510. #endif
  1511. //
  1512. // Methods which call through to the constituent table segments.
  1513. // These will in general just select one segment to call, or
  1514. // iterate over all segments.
  1515. //
  1516. //+---------------------------------------------------------------------------
  1517. //
  1518. // Function: IsRowInSegment
  1519. //
  1520. // Synopsis:
  1521. //
  1522. // Arguments: [wid] - Workid of row to be found
  1523. //
  1524. // Returns: BOOL - TRUE if row is found, FALSE otherwise
  1525. //
  1526. // History: 3-24-95 srikants Created
  1527. //
  1528. //----------------------------------------------------------------------------
  1529. BOOL CLargeTable::IsRowInSegment( WORKID wid )
  1530. {
  1531. CLock lock(_mutex);
  1532. return (_LokFindTableSegment(wid) != 0);
  1533. }
  1534. //+---------------------------------------------------------------------------
  1535. //
  1536. // Function: SortOrder
  1537. //
  1538. // Synopsis:
  1539. //
  1540. // Returns:
  1541. //
  1542. // History: 3-24-95 srikants Created
  1543. //
  1544. // Notes:
  1545. //
  1546. //----------------------------------------------------------------------------
  1547. CSortSet const & CLargeTable::SortOrder()
  1548. {
  1549. //CLock lock(_mutex);
  1550. return( *_pSortSet );
  1551. }
  1552. //+---------------------------------------------------------------------------
  1553. //
  1554. // Function: RemoveRow
  1555. //
  1556. // Synopsis:
  1557. //
  1558. // Arguments: [varUnique] -
  1559. //
  1560. // Returns:
  1561. //
  1562. // History: 3-24-95 srikants Created
  1563. //
  1564. // Notes:
  1565. //
  1566. //----------------------------------------------------------------------------
  1567. void CLargeTable::RemoveRow( PROPVARIANT const & varUnique )
  1568. {
  1569. CLock lock(_mutex);
  1570. if ( !_LokIsWatched() )
  1571. {
  1572. //
  1573. // Do not process deletions unless the table is watched.
  1574. //
  1575. return;
  1576. }
  1577. WORKID widRow = widInvalid;
  1578. if ( _fUniqueWorkid )
  1579. widRow = varUnique.lVal;
  1580. else
  1581. {
  1582. CColumnMasterDesc* pMastCol = _MasterColumnSet.Find( pidPath );
  1583. Win4Assert(0 != pMastCol && pMastCol->IsCompressedCol());
  1584. //
  1585. // Convert the filename to a wid before deletion.
  1586. //
  1587. BOOL fFound = pMastCol->GetCompressor()->FindData( &varUnique,
  1588. widRow );
  1589. // See if the delete wasn't in the table to begin with
  1590. if ( !fFound )
  1591. return;
  1592. }
  1593. if ( !_LokRemoveIfDeferred( widRow ) )
  1594. {
  1595. //
  1596. // This row was not a deferred update. We must iterate through
  1597. // the segments and remove it.
  1598. //
  1599. PROPVARIANT varWid;
  1600. varWid.lVal = (LONG) widRow;
  1601. varWid.vt = VT_I4;
  1602. for ( CFwdTableSegIter iter(_segList); !_segList.AtEnd(iter);
  1603. _segList.Advance(iter) )
  1604. {
  1605. CTableSegment * pSegment = iter.GetSegment();
  1606. WORKID widNext;
  1607. CI_TBL_CHAPT chapt;
  1608. // No Hard Delete.
  1609. if ( pSegment->RemoveRow( varWid, widNext, chapt ) )
  1610. {
  1611. _LokRemoveCategorizedRow( chapt,
  1612. widRow,
  1613. widNext,
  1614. pSegment );
  1615. break;
  1616. }
  1617. }
  1618. }
  1619. _bitRefresh = 1;
  1620. LokCompleteAsyncNotification();
  1621. }
  1622. //+-------------------------------------------------------------------------
  1623. //
  1624. // Member: CLargeTable::NeedToNotifyReset, private
  1625. //
  1626. // Synopsis: Checks if there is a need to notify the client
  1627. // Resets the changeQuiesced bit if needed
  1628. //
  1629. // Arguments: [changeType] -- out, what change type
  1630. //
  1631. // History: Arp-4-95 BartoszM Created
  1632. //
  1633. //--------------------------------------------------------------------------
  1634. BOOL CLargeTable::NeedToNotifyReset (DBWATCHNOTIFY& changeType)
  1635. {
  1636. CLock lock (_mutex);
  1637. return LokNeedToNotifyReset(changeType);
  1638. }
  1639. //+-------------------------------------------------------------------------
  1640. //
  1641. // Member: CLargeTable::LokNeedToNotifyReset, private
  1642. //
  1643. // Synopsis: Checks if there is a need to notify the client
  1644. // Resets the changeQuiesced bit if needed
  1645. //
  1646. // Arguments: [changeType] -- out, what change type
  1647. //
  1648. // History: Arp-4-95 BartoszM Created
  1649. //
  1650. //--------------------------------------------------------------------------
  1651. BOOL CLargeTable::LokNeedToNotifyReset(DBWATCHNOTIFY& changeType)
  1652. {
  1653. if (_bitRefresh )
  1654. {
  1655. if (!_bitClientNotified)
  1656. {
  1657. changeType = DBWATCHNOTIFY_ROWSCHANGED;
  1658. return TRUE;
  1659. }
  1660. }
  1661. else // no need to run changes
  1662. {
  1663. if (_bitChangeQuiesced)
  1664. {
  1665. changeType = (_bitQuiesced)? DBWATCHNOTIFY_QUERYDONE: DBWATCHNOTIFY_QUERYREEXECUTED;
  1666. tbDebugOut(( DEB_NOTIFY, "changetype set to %d\n", changeType ));
  1667. //
  1668. // Reset the bit!
  1669. //
  1670. _bitChangeQuiesced = 0;
  1671. return TRUE;
  1672. }
  1673. }
  1674. return FALSE;
  1675. }
  1676. //+-------------------------------------------------------------------------
  1677. //
  1678. // Member: CLargeTable::NeedToNotify, private
  1679. //
  1680. // Synopsis: Checks if there is a need to notify the client
  1681. //
  1682. // History: 29-Aug-95 dlee Created
  1683. //
  1684. //--------------------------------------------------------------------------
  1685. BOOL CLargeTable::NeedToNotify()
  1686. {
  1687. CLock lock (_mutex);
  1688. return LokNeedToNotify();
  1689. }
  1690. //+-------------------------------------------------------------------------
  1691. //
  1692. // Member: CLargeTable::LokNeedToNotify, private
  1693. //
  1694. // Synopsis: Checks if there is a need to notify the client
  1695. //
  1696. // History: 29-Aug-95 dlee Created
  1697. //
  1698. //--------------------------------------------------------------------------
  1699. BOOL CLargeTable::LokNeedToNotify()
  1700. {
  1701. if (_bitRefresh )
  1702. {
  1703. if (!_bitClientNotified)
  1704. return TRUE;
  1705. }
  1706. else if (_bitChangeQuiesced)
  1707. {
  1708. return TRUE;
  1709. }
  1710. return FALSE;
  1711. }
  1712. //+---------------------------------------------------------------------------
  1713. //
  1714. // Member: CLargeTable::GetNotifications, private
  1715. //
  1716. // Synopsis: Retrieves the notification info from the query object
  1717. // row data.
  1718. //
  1719. // Arguments: [rSync] -- notification synchronization info
  1720. // [rParams] -- notification data info
  1721. //
  1722. // Returns: SCODE
  1723. //
  1724. // History: 10-24-94 dlee created
  1725. //
  1726. //----------------------------------------------------------------------------
  1727. SCODE CLargeTable::GetNotifications(
  1728. CNotificationSync & rSync,
  1729. DBWATCHNOTIFY & changeType )
  1730. {
  1731. tbDebugOut (( DEB_NOTIFY, "lt: GetNotifications\n" ));
  1732. {
  1733. CLock lock(_mutex);
  1734. _bitNotifyEnabled = 1;
  1735. // don't fail if the query failed -- report the notification that
  1736. // the query completed.
  1737. // _LokCheckQueryStatus();
  1738. }
  1739. SCODE sc = S_OK;
  1740. {
  1741. CLock lock(_mutex);
  1742. Win4Assert( 0 == _pRequestServer );
  1743. Win4Assert( 0 == _hNotifyEvent );
  1744. if ( LokNeedToNotifyReset( changeType ) )
  1745. {
  1746. _bitClientNotified = 1;
  1747. tbDebugOut (( DEB_NOTIFY, "complete in getnotify: %d\n",
  1748. changeType ));
  1749. return S_OK;
  1750. }
  1751. else
  1752. {
  1753. if ( rSync.IsSvcMode() )
  1754. {
  1755. Win4Assert( 0 == _pRequestServer );
  1756. _pRequestServer = rSync.GetRequestServer();
  1757. Win4Assert( 0 != _pRequestServer );
  1758. tbDebugOut (( DEB_NOTIFY, "getnotify returning pending\n" ));
  1759. return STATUS_PENDING;
  1760. }
  1761. // Block on an event until notifications
  1762. // arrive (or the table is going away). If notifications
  1763. // exist, grab them. Also block on the notification cancel
  1764. // event and report if that was received.
  1765. Win4Assert( 0 == _hNotifyEvent );
  1766. _hNotifyEvent = CreateEvent( 0, TRUE, FALSE, 0 );
  1767. if ( 0 == _hNotifyEvent )
  1768. {
  1769. tbDebugOut(( DEB_ERROR, "Create event returned 0d\n",
  1770. GetLastError() ));
  1771. THROW( CException() );
  1772. }
  1773. }
  1774. }
  1775. HANDLE aEvents[2];
  1776. aEvents[0] = _hNotifyEvent;
  1777. aEvents[1] = rSync.GetCancelEvent();
  1778. ULONG wait = WaitForMultipleObjects( 2,
  1779. aEvents,
  1780. FALSE,
  1781. INFINITE );
  1782. CloseHandle( _hNotifyEvent );
  1783. _hNotifyEvent = 0;
  1784. if ( STATUS_WAIT_0 == wait )
  1785. {
  1786. CLock lock(_mutex);
  1787. changeType = _changeType;
  1788. }
  1789. else if ( STATUS_WAIT_1 == wait )
  1790. {
  1791. sc = STATUS_CANCELLED;
  1792. }
  1793. else
  1794. {
  1795. Win4Assert(!"Unexpected return from WaitForMultipleObjects()");
  1796. }
  1797. return sc;
  1798. }
  1799. //+-------------------------------------------------------------------------
  1800. //
  1801. // Member: CLargeTable::CreateWatchRegion
  1802. //
  1803. // Synopsis: Creates a new watch region
  1804. //
  1805. // Arguments: [mode] -- initial mode
  1806. // [phRegion] -- (out) region handle
  1807. //
  1808. // History: Jun-16-95 BartoszM Created
  1809. //
  1810. //--------------------------------------------------------------------------
  1811. void CLargeTable::CreateWatchRegion (ULONG mode, HWATCHREGION* phRegion)
  1812. {
  1813. CLock lock( _mutex );
  1814. _bitIsWatched = 1;
  1815. *phRegion = _watchList.NewRegion (mode);
  1816. }
  1817. //+-------------------------------------------------------------------------
  1818. //
  1819. // Member: CLargeTable::ChangeWatchMode
  1820. //
  1821. // Synopsis: Changes watch mode of a region
  1822. //
  1823. // Arguments:
  1824. // [hRegion] -- region handle
  1825. // [mode] -- new mode
  1826. //
  1827. // History: Jun-16-95 BartoszM Created
  1828. //
  1829. //--------------------------------------------------------------------------
  1830. void CLargeTable::ChangeWatchMode ( HWATCHREGION hRegion, ULONG mode)
  1831. {
  1832. CLock lock( _mutex );
  1833. _watchList.ChangeMode (hRegion, mode);
  1834. }
  1835. //+-------------------------------------------------------------------------
  1836. //
  1837. // Member: CLargeTable::GetWatchRegionInfo
  1838. //
  1839. // Synopsis: Retrieves watch region information
  1840. //
  1841. // Arguments: [hRegion] -- region handle
  1842. // [pChapter] -- (out) chapter
  1843. // [pBookmark] -- (out) bookmark
  1844. // [pcRows] -- (out) size in rows
  1845. //
  1846. // History: Jun-16-95 BartoszM Created
  1847. //
  1848. //--------------------------------------------------------------------------
  1849. void CLargeTable::GetWatchRegionInfo ( HWATCHREGION hRegion,
  1850. CI_TBL_CHAPT* pChapter,
  1851. CI_TBL_BMK* pBookmark,
  1852. DBROWCOUNT * pcRows)
  1853. {
  1854. CLock lock( _mutex );
  1855. _watchList.GetInfo (hRegion, pChapter, pBookmark, (DBCOUNTITEM *)pcRows);
  1856. }
  1857. //+-------------------------------------------------------------------------
  1858. //
  1859. // Member: CLargeTable::DeleteWatchRegion
  1860. //
  1861. // Synopsis: Delete watch region
  1862. //
  1863. // Arguments: [hRegion] -- region handle
  1864. //
  1865. // History: Jun-16-95 BartoszM Created
  1866. //
  1867. //--------------------------------------------------------------------------
  1868. void CLargeTable::DeleteWatchRegion (HWATCHREGION hRegion)
  1869. {
  1870. CLock lock( _mutex );
  1871. _watchList.DeleteRegion (hRegion);
  1872. }
  1873. //+-------------------------------------------------------------------------
  1874. //
  1875. // Member: CLargeTable::ShrinkWatchRegion
  1876. //
  1877. // Synopsis: Shrinks watch region
  1878. //
  1879. // Arguments: [hRegion] -- region handle
  1880. // [chapter] -- new chapter
  1881. // [bookmark] -- new bookmark
  1882. // [cRows] -- new size in rows
  1883. //
  1884. // History: Jun-16-95 BartoszM Created
  1885. //
  1886. //--------------------------------------------------------------------------
  1887. void CLargeTable::ShrinkWatchRegion (HWATCHREGION hRegion,
  1888. CI_TBL_CHAPT chapter,
  1889. CI_TBL_BMK bookmark,
  1890. LONG cRows )
  1891. {
  1892. CLock lock( _mutex );
  1893. _watchList.ShrinkRegion (hRegion, chapter, bookmark, cRows);
  1894. #if CIDBG==1
  1895. CWatchRegion * pRegion = _watchList.GetRegion(hRegion);
  1896. _watchList.CheckRegionConsistency( pRegion );
  1897. #endif //CIDBG==1
  1898. }
  1899. //+-------------------------------------------------------------------------
  1900. //
  1901. // Member: CLargeTable::Refresh
  1902. //
  1903. // Synopsis: Stub implementation
  1904. //
  1905. // Arguments: [] --
  1906. //
  1907. // History: Apr-4-95 BartoszM Created
  1908. // Jul-27-95 BartoszM Implemented (with no change script)
  1909. //
  1910. //--------------------------------------------------------------------------
  1911. void CLargeTable::Refresh()
  1912. {
  1913. CDynStack<CTableBucket> xBktToConvert(0); // buckets to expand at our leisure
  1914. {
  1915. CLock lock (_mutex);
  1916. _LokCheckQueryStatus();
  1917. _bitClientNotified = 0;
  1918. _bitRefresh = 0;
  1919. // Update the bookmarks before refreshing windows.
  1920. // We need to know where they will be migrating
  1921. // in case rows were deleted.
  1922. // Remove watch regions from windows
  1923. for (CWatchIter iterWatch1 (_watchList);
  1924. !_watchList.AtEnd(iterWatch1);
  1925. _watchList.Advance(iterWatch1))
  1926. {
  1927. CWatchRegion* pRegion = iterWatch1.Get();
  1928. if (pRegion->IsInit())
  1929. {
  1930. CTableWindow * pWindow = (CTableWindow *) pRegion->Segment();
  1931. CI_TBL_BMK bookmark = _FindNearestDynamicBmk (
  1932. pWindow,
  1933. pRegion->Chapter(),
  1934. pRegion->Bookmark());
  1935. pRegion->Set( pRegion->Chapter(),
  1936. bookmark,
  1937. pRegion->RowCount() );
  1938. _watchList.ShrinkRegionToZero (pRegion->Handle());
  1939. }
  1940. }
  1941. // Recreate new watch regions in windows
  1942. // Find starting segments using bookmarks
  1943. for (CWatchIter iterWatch3 (_watchList);
  1944. !_watchList.AtEnd(iterWatch3);
  1945. _watchList.Advance(iterWatch3))
  1946. {
  1947. CWatchRegion* pRegion = iterWatch3.Get();
  1948. if ( pRegion->RowCount() > 0 )
  1949. {
  1950. Win4Assert( widInvalid != pRegion->Bookmark() );
  1951. CTableSegment* pSegment = _LokFindTableSegment(pRegion->Bookmark());
  1952. pRegion->SetSegment( pSegment );
  1953. if ( 0 != pSegment )
  1954. {
  1955. LokStretchWatchRegion (pRegion, xBktToConvert);
  1956. _watchList. BuildRegion (
  1957. pRegion->Handle(),
  1958. pSegment,
  1959. pRegion->Chapter(),
  1960. pRegion->Bookmark(),
  1961. pRegion->RowCount() );
  1962. }
  1963. #if CIDBG==1
  1964. _watchList.CheckRegionConsistency( pRegion );
  1965. #endif // CIDBG==1
  1966. }
  1967. }
  1968. //
  1969. // If there are any deferred rows that were not added during the
  1970. // normal "PutRow", we should expand them now.
  1971. //
  1972. if ( 0 != _pDeferredRows )
  1973. {
  1974. xBktToConvert.Push(_pDeferredRows);
  1975. _pDeferredRows = 0;
  1976. }
  1977. if (_bitChangeQuiesced)
  1978. LokCompleteAsyncNotification();
  1979. }
  1980. if ( xBktToConvert.Count() > 0 )
  1981. {
  1982. // Schedule buckets for asynchronous expansion
  1983. _NoLokBucketToWindows( xBktToConvert, widInvalid, TRUE, FALSE );
  1984. }
  1985. } //Refresh
  1986. void CLargeTable::LokStretchWatchRegion ( CWatchRegion* pRegion,
  1987. CDynStack<CTableBucket>& xBktToConvert)
  1988. {
  1989. CTableWindow* pFirstWindow = (CTableWindow*) pRegion->Segment();
  1990. Win4Assert (pFirstWindow->IsWindow());
  1991. //
  1992. // Compute the number of rows that can be retrieved from current window.
  1993. //
  1994. TBL_OFF dummy;
  1995. ULONG iRow;
  1996. pFirstWindow->FindBookMark(pRegion->Bookmark(), dummy, iRow );
  1997. ULONG cRowsToRetrieve = pRegion->RowCount();
  1998. ULONG cRowsFromCurrWindow = (ULONG)pFirstWindow->RowCount() - iRow;
  1999. if ( cRowsToRetrieve > cRowsFromCurrWindow )
  2000. {
  2001. //
  2002. // We still have more rows to be retrieved.
  2003. //
  2004. cRowsToRetrieve -= cRowsFromCurrWindow;
  2005. }
  2006. else
  2007. {
  2008. //
  2009. // This window can give us all the rows to be fetched.
  2010. //
  2011. return;
  2012. }
  2013. CDoubleTableSegIter iter (pRegion->Segment());
  2014. do
  2015. {
  2016. Win4Assert( !_segList.AtEnd(iter) );
  2017. _segList.Advance( iter );
  2018. if ( _segList.AtEnd(iter) )
  2019. {
  2020. break;
  2021. }
  2022. if ( iter.GetSegment()->IsBucket() )
  2023. {
  2024. CTableBucket * pBucket = _LokReplaceWithEmptyWindow(iter);
  2025. xBktToConvert.Push(pBucket);
  2026. }
  2027. else
  2028. {
  2029. CTableWindow * pWindow = iter.GetWindow();
  2030. cRowsFromCurrWindow = (ULONG)pWindow->RowCount();
  2031. if ( cRowsToRetrieve > cRowsFromCurrWindow )
  2032. {
  2033. cRowsToRetrieve -= cRowsFromCurrWindow;
  2034. }
  2035. else
  2036. {
  2037. cRowsToRetrieve = 0;
  2038. }
  2039. }
  2040. }
  2041. while ( cRowsToRetrieve > 0 );
  2042. }
  2043. //+---------------------------------------------------------------------------
  2044. //
  2045. // Member: CLargeTable::CancelAsyncNotification
  2046. //
  2047. // Synopsis: signals the notification event
  2048. //
  2049. // History: 10-24-94 dlee created
  2050. //
  2051. // Notes: can only be called from the destructor
  2052. //
  2053. //----------------------------------------------------------------------------
  2054. void CLargeTable::CancelAsyncNotification()
  2055. {
  2056. if ( 0 != _pRequestServer )
  2057. {
  2058. //_pRequestServer->CompleteNotification( 0 );
  2059. _pRequestServer = 0;
  2060. return;
  2061. }
  2062. if ( 0 != _hNotifyEvent )
  2063. SetEvent( _hNotifyEvent );
  2064. }
  2065. //+---------------------------------------------------------------------------
  2066. //
  2067. // Member: CLargeTable::LokCompleteAsyncNotification
  2068. //
  2069. // Synopsis: signals the notification event
  2070. //
  2071. // History: 10-24-94 dlee created
  2072. //
  2073. //----------------------------------------------------------------------------
  2074. void CLargeTable::LokCompleteAsyncNotification()
  2075. {
  2076. if (_bitClientNotified && !_bitChangeQuiesced)
  2077. return; // no need to keep pinging the client
  2078. Win4Assert( ! ( _pRequestServer && _hNotifyEvent ) );
  2079. if ( 0 != _pRequestServer )
  2080. {
  2081. if ( LokNeedToNotifyReset( _changeType ) )
  2082. {
  2083. if ( DBWATCHNOTIFY_ROWSCHANGED == _changeType )
  2084. _bitClientNotified = 1;
  2085. tbDebugOut (( DEB_NOTIFY, "complete in lcan: %d\n",
  2086. _changeType ));
  2087. _pRequestServer->CompleteNotification( _changeType );
  2088. _pRequestServer = 0;
  2089. }
  2090. return;
  2091. }
  2092. if ( 0 != _hNotifyEvent )
  2093. {
  2094. if ( LokNeedToNotifyReset( _changeType ) )
  2095. {
  2096. if ( DBWATCHNOTIFY_ROWSCHANGED == _changeType )
  2097. _bitClientNotified = 1;
  2098. SetEvent( _hNotifyEvent );
  2099. // event will be released / zeroed by notify thread.
  2100. }
  2101. }
  2102. } //LokCompleteAsyncNotification
  2103. //+---------------------------------------------------------------------------
  2104. //
  2105. // Function: _LokConvertToBucket
  2106. //
  2107. // Synopsis: Converts the given window into a bucket and replace the
  2108. // window with a bucket.
  2109. //
  2110. // Arguments: [window] - The window to be converted into a bucket.
  2111. //
  2112. // Returns: The bucket that was created.
  2113. //
  2114. // History: 3-24-95 srikants Created
  2115. //
  2116. //----------------------------------------------------------------------------
  2117. void CLargeTable::_LokConvertToBucket( CTableWindow ** ppWindow )
  2118. {
  2119. //
  2120. // Convert the window into a bucket.
  2121. //
  2122. CTableWindow * pWindow = *ppWindow;
  2123. Win4Assert( pWindow->IsWindow() );
  2124. CBucketizeWindows bucketize( *this, *pWindow );
  2125. tbDebugOut(( DEB_WINSPLIT, "Converting 0x%X window to buckets\n",
  2126. pWindow->GetSegId() ));
  2127. bucketize. LokCreateBuckets( *_pSortSet,
  2128. _keyCompare.GetReference(),
  2129. _MasterColumnSet );
  2130. CTableSegList & bktList = bucketize.GetBucketsList();
  2131. for ( CFwdTableSegIter iter2(bktList); !bktList.AtEnd(iter2);
  2132. bktList.Advance(iter2) )
  2133. {
  2134. //
  2135. // This information is needed for doing a wid->path translation
  2136. //
  2137. CTableBucket * pBucket = iter2.GetBucket();
  2138. pBucket->SetLargeTable(this);
  2139. }
  2140. if ( 0 != bktList.GetSegmentsCount() )
  2141. {
  2142. _segListMgr.Replace( pWindow, bktList );
  2143. }
  2144. else
  2145. {
  2146. _segListMgr.RemoveFromList( pWindow );
  2147. }
  2148. *ppWindow = 0;
  2149. delete pWindow;
  2150. } //_LokConvertToBucket
  2151. //+---------------------------------------------------------------------------
  2152. //
  2153. // Function: _LokConvertWindowsToBucket
  2154. //
  2155. // Synopsis: Uses heuristics to convert some of the windows into buckets
  2156. // to reduce memory usage.
  2157. //
  2158. // History: 3-24-95 srikants Created
  2159. //
  2160. // Notes: This function needs a lot of work. For now, it just tries
  2161. // to avoid converting windows that "were recently used". It
  2162. // also tries to alternate windows and buckets if possible.
  2163. //
  2164. // A heuristic used is NOT to convert the "last" window into a
  2165. // bucket. This is because in case of content queries, the
  2166. // results are probably coming sorted and we can use this fact.
  2167. // Since new rows always go to the end, we will win by creating
  2168. // well sorted buckets when windows are converted into buckets.
  2169. //
  2170. // Also, don't convert a window to a bucket if it is < 40%
  2171. // capacity.
  2172. //
  2173. //----------------------------------------------------------------------------
  2174. void CLargeTable::_LokConvertWindowsToBucket( WORKID widToPin )
  2175. {
  2176. if ( _segList.GetWindowsCount() < cMaxWindows )
  2177. return;
  2178. unsigned cWindows = 0;
  2179. BOOL fConvert = TRUE; // used to keep alternated segments as
  2180. // windows (approx)
  2181. CBackTableSegIter iter(_segList);
  2182. while( !_segList.AtEnd(iter) )
  2183. {
  2184. CTableSegment * pSegment = iter.GetSegment();
  2185. if ( pSegment->IsWindow() )
  2186. {
  2187. cWindows++;
  2188. CTableWindow * pWindow = iter.GetWindow();
  2189. ULONG cRows = (ULONG)pWindow->RowCount();
  2190. if ( fConvert &&
  2191. cRows >= cMinRowsToBucketize &&
  2192. !pWindow->IsWatched() &&
  2193. !_segListMgr.IsRecentlyUsed(pWindow) &&
  2194. !_segList.IsLast(iter) &&
  2195. !_segList.IsFirst(iter) &&
  2196. ( widInvalid == widToPin || !pWindow->IsRowInSegment( widToPin ) ) )
  2197. {
  2198. //
  2199. // The window may be deleted. So, we must backup the iterator
  2200. // before destroying the window.
  2201. //
  2202. Win4Assert( widInvalid == widToPin ||
  2203. !pWindow->IsRowInSegment( widToPin ) );
  2204. CTableWindow * pWindow = iter.GetWindow();
  2205. _segList.BackUp(iter);
  2206. _LokConvertToBucket( &pWindow );
  2207. //
  2208. // Don't convert the next segment into a bucket.
  2209. //
  2210. fConvert = FALSE;
  2211. }
  2212. else
  2213. {
  2214. _segList.BackUp(iter);
  2215. //
  2216. // We had a window which we didn't convert into a bucket.
  2217. // If the next one is a window, we can convert it.
  2218. //
  2219. fConvert = TRUE;
  2220. }
  2221. }
  2222. else
  2223. {
  2224. _segList.BackUp(iter);
  2225. }
  2226. }
  2227. Win4Assert( cWindows >= cMaxWindows );
  2228. } //_LokConvertWindowsToBucket
  2229. //+---------------------------------------------------------------------------
  2230. //
  2231. // Function: _LokReplaceWithEmptyWindow
  2232. //
  2233. // Synopsis: Replaces the given bucket with an empty window in preparation
  2234. // for bucket->window conversion.
  2235. //
  2236. // Arguments: [pBucket] - The bucket to replace
  2237. //
  2238. // History: 4-11-95 srikants Created
  2239. //
  2240. // Notes:
  2241. //
  2242. //----------------------------------------------------------------------------
  2243. CTableBucket *
  2244. CLargeTable::_LokReplaceWithEmptyWindow( CDoubleTableSegIter & iter )
  2245. {
  2246. Win4Assert( iter.GetSegment()->IsBucket() );
  2247. CTableBucket * pBucket = iter.GetBucket();
  2248. tbDebugOut(( DEB_WINSPLIT,
  2249. "Replacing Bucket 0x%X with Window\n", pBucket->GetSegId() ));
  2250. CTableWindow * pWindow = _CreateNewWindow( pBucket->GetSegId(),
  2251. pBucket->GetLowestKey(),
  2252. pBucket->GetHighestKey());
  2253. _segListMgr.Replace( iter, pWindow );
  2254. //
  2255. // Make the newly created window preferred place to put the new
  2256. // rows in.
  2257. //
  2258. _segListMgr.SetCachedPutRowSeg( pWindow );
  2259. return pBucket;
  2260. }
  2261. //+---------------------------------------------------------------------------
  2262. //
  2263. // Function: _NoLokBucketToWindows
  2264. //
  2265. // Synopsis:
  2266. //
  2267. // Arguments: [xBktToExpand] -
  2268. // [widToPin] -
  2269. // [isWatched] -
  2270. //
  2271. // Returns:
  2272. //
  2273. // History: 7-07-95 srikants Created
  2274. //
  2275. // Notes:
  2276. //
  2277. //----------------------------------------------------------------------------
  2278. void
  2279. CLargeTable::_NoLokBucketToWindows( XPtr<CTableBucket> & xBktToExpand,
  2280. WORKID widToPin,
  2281. BOOL isWatched,
  2282. BOOL fOptimizeBucketization )
  2283. {
  2284. CAsyncBucketExploder * pBktExploder =
  2285. new CAsyncBucketExploder( *this,
  2286. xBktToExpand,
  2287. widToPin,
  2288. !_fUniqueWorkid );
  2289. //
  2290. // DO NOT add any code here. pBucket now belongs to pBktExploder and we
  2291. // should acquire it from xBktToExpand before doing anything else.
  2292. //
  2293. Win4Assert( 0 == xBktToExpand.GetPointer() );
  2294. XInterface<CAsyncBucketExploder> xRef(pBktExploder);
  2295. //
  2296. // We don't have to do an AddRef on pBktExploder because it is refcounted
  2297. // in the constructor.
  2298. //
  2299. NTSTATUS status = STATUS_SUCCESS;
  2300. //
  2301. // Lock the table
  2302. // ============================================================
  2303. //
  2304. {
  2305. CLock lock(_mutex);
  2306. _LokCheckQueryStatus();
  2307. if ( 0 != _pQExecute )
  2308. {
  2309. pBktExploder->SetQuery( _pQExecute );
  2310. }
  2311. else
  2312. {
  2313. //
  2314. // The query is being destoryed.
  2315. //
  2316. THROW( CException( STATUS_TOO_LATE ) );
  2317. }
  2318. //
  2319. // Convert any excess windows to buckets.
  2320. //
  2321. if ( fOptimizeBucketization )
  2322. _LokConvertWindowsToBucket( widToPin );
  2323. //
  2324. // Add to the bigtable's list of exploding buckets.
  2325. //
  2326. _LokAddToExplodeList( pBktExploder );
  2327. pBktExploder->SetOnLTList();
  2328. }
  2329. //
  2330. // Release the table
  2331. // ============================================================
  2332. //
  2333. if ( isWatched )
  2334. {
  2335. //
  2336. // There can be a failure (like failing to create a worker thread)
  2337. // when we add to the work queue. So, we must be able to deal with
  2338. // it.
  2339. //
  2340. TRY
  2341. {
  2342. ciFAILTEST( STATUS_NO_MEMORY );
  2343. pBktExploder->AddToWorkQueue();
  2344. }
  2345. CATCH( CException, e )
  2346. {
  2347. tbDebugOut(( DEB_ERROR,
  2348. "CLargeTable::_NoLokBucketToWindow "
  2349. "AddToWorkQueue failed with error 0X%X\n",
  2350. e.GetErrorCode() ));
  2351. SetStatus( STAT_ERROR );
  2352. _RemoveFromExplodeList( pBktExploder );
  2353. pBktExploder->Abort();
  2354. RETHROW();
  2355. }
  2356. END_CATCH
  2357. xRef.Acquire();
  2358. pBktExploder->Release();
  2359. //
  2360. // Return to the caller from here and continue fetching the
  2361. // remaining rows.
  2362. //
  2363. }
  2364. else
  2365. {
  2366. //
  2367. // No need of using another thread. Just use the callers
  2368. // thread.
  2369. //
  2370. pBktExploder->DoIt( 0 );
  2371. status = pBktExploder->GetStatus();
  2372. xRef.Acquire();
  2373. pBktExploder->Release();
  2374. }
  2375. if ( STATUS_SUCCESS != status )
  2376. {
  2377. THROW( CException(status) );
  2378. }
  2379. if (!isWatched)
  2380. {
  2381. CLock lock(_mutex);
  2382. //
  2383. // Give a notification to "kick" the notification thread after the
  2384. // bucket->window conversion.
  2385. //
  2386. LokCompleteAsyncNotification();
  2387. }
  2388. }
  2389. //+---------------------------------------------------------------------------
  2390. //
  2391. // Function: _NoLokBucketToWindows
  2392. //
  2393. // Synopsis: Converts the given buckets into a windows.
  2394. //
  2395. // Arguments: [bucketRef] - Safe stack of buckets.
  2396. //
  2397. // History: 3-24-95 srikants Created
  2398. //
  2399. // Notes:
  2400. //
  2401. //----------------------------------------------------------------------------
  2402. void
  2403. CLargeTable::_NoLokBucketToWindows( CDynStack<CTableBucket> & xSegStack,
  2404. WORKID widToPin,
  2405. BOOL isWatched,
  2406. BOOL fOptimizeBucketization )
  2407. {
  2408. //
  2409. // The bucket that must be scheduled for expansion.
  2410. //
  2411. XPtr<CTableBucket> xBktToExpand;
  2412. do
  2413. {
  2414. //
  2415. //=============================================================
  2416. // Lock the table.
  2417. //
  2418. {
  2419. CLock lock(_mutex);
  2420. CTableBucket * pBucket = (CTableBucket *) xSegStack.Pop();
  2421. Win4Assert( 0 == xBktToExpand.GetPointer() );
  2422. xBktToExpand.Set( pBucket );
  2423. }
  2424. //
  2425. //=============================================================
  2426. // release the lock on table
  2427. //
  2428. //
  2429. // Convert this single bucket to a window.
  2430. //
  2431. _NoLokBucketToWindows( xBktToExpand, widToPin, isWatched, fOptimizeBucketization );
  2432. } while ( 0 != xSegStack.Count() );
  2433. } //_NoLokBucketToWindows
  2434. //+---------------------------------------------------------------------------
  2435. //
  2436. // Method: CLargeTable::GetRowsAt
  2437. //
  2438. // Synopsis: Fetch rows relative to a bookmark
  2439. //
  2440. // Arguments: [hRegion] - watch region handle
  2441. // [widStart] -
  2442. // [chapt] - Chapter from which to fetch rows (if chaptered)
  2443. // [iRowOffset] -
  2444. // [rOutColumns] -
  2445. // [rGetParams] -
  2446. // [rwidLastRowTransferred] -
  2447. //
  2448. // Returns:
  2449. //
  2450. // History: 3-31-95 srikants Created
  2451. // 6-28-95 BartoszM Added watch region support
  2452. //
  2453. // Notes:
  2454. //
  2455. //----------------------------------------------------------------------------
  2456. SCODE
  2457. CLargeTable::GetRowsAt(
  2458. HWATCHREGION hRegion,
  2459. WORKID widStart,
  2460. CI_TBL_CHAPT chapt,
  2461. DBROWOFFSET iRowOffset,
  2462. CTableColumnSet const & rOutColumns,
  2463. CGetRowsParams & rGetParams,
  2464. WORKID & rwidLastRowTransferred
  2465. )
  2466. {
  2467. //
  2468. // DO NOT OBTAIN LOCK HERE.
  2469. // If the row of interest is in an un-sorted bucket, it must be
  2470. // expanded into a window before we can determine the exact wid.
  2471. // For bucket->window expansion, we have to release the lock because
  2472. // the conversion (not now but later) will be done by a worker thread
  2473. // and we will deadlock.
  2474. //
  2475. tbDebugOut(( DEB_REGTRANS,
  2476. " =============== Entering GetRowsAt ========= \n\n " ));
  2477. if ( iRowOffset > 0 )
  2478. rwidLastRowTransferred = WORKID_TBLLAST;
  2479. else
  2480. rwidLastRowTransferred = WORKID_TBLFIRST;
  2481. BOOL fAsync = FALSE;
  2482. if ( WORKID_TBLBEFOREFIRST == widStart )
  2483. {
  2484. if (iRowOffset <= 0)
  2485. {
  2486. widStart = WORKID_TBLLAST;
  2487. }
  2488. else
  2489. {
  2490. widStart = WORKID_TBLFIRST;
  2491. iRowOffset--;
  2492. }
  2493. }
  2494. else if ( WORKID_TBLAFTERLAST == widStart )
  2495. {
  2496. widStart = WORKID_TBLLAST;
  2497. iRowOffset++;
  2498. }
  2499. tbDebugOut(( DEB_REGTRANS, " widstart 0x%x, offset %d\n", widStart, iRowOffset ));
  2500. ULONG cChaptRows = 0;
  2501. if ( IsCategorized() && DB_NULL_HCHAPTER != chapt )
  2502. {
  2503. CLock lock(_mutex);
  2504. cChaptRows = GetCategorizer()->GetRowCount( chapt );
  2505. if ( WORKID_TBLFIRST == widStart || WORKID_TBLLAST == widStart )
  2506. {
  2507. BOOL isLastBmk = WORKID_TBLLAST == widStart;
  2508. widStart = GetCategorizer()->GetFirstWorkid( chapt );
  2509. if ( isLastBmk )
  2510. {
  2511. iRowOffset += ( cChaptRows - 1 );
  2512. }
  2513. if ( ( isLastBmk ) &&
  2514. ( -1 == iRowOffset ) &&
  2515. ( 1 == cChaptRows ) )
  2516. {
  2517. iRowOffset = 0;
  2518. }
  2519. }
  2520. }
  2521. CTableRowLocator rowLocator( *this, widStart, (LONG) iRowOffset, chapt );
  2522. CTableRowGetter tableRowGetter( *this, rOutColumns, rGetParams, chapt, hRegion );
  2523. SCODE status = S_OK;
  2524. CWatchRegion* pWatchRegion = 0;
  2525. BOOL fBeyondTable = FALSE; // flag indicating if we had to go
  2526. // beyond the end of the table.
  2527. CDynStack<CTableBucket> xBktToConvert(0); // buckets to expand at our leisure
  2528. WORKID widToPin = widInvalid; // The workid we need to pin.
  2529. for (;;)
  2530. {
  2531. //
  2532. // Loop as long as there are buckets to be sychronously
  2533. // expanded.
  2534. //
  2535. XPtr<CTableBucket> xBktToExplode(0); // bucket to explode synchronously
  2536. // ============================================================
  2537. {
  2538. CLock lock(_mutex);
  2539. _LokCheckQueryStatus();
  2540. fAsync = _LokIsWatched() && (0 != hRegion);
  2541. if (!_watchList.IsEmpty())
  2542. {
  2543. _watchList.VerifyRegion (hRegion);
  2544. // valid region or null
  2545. pWatchRegion = _watchList.GetRegion(hRegion);
  2546. }
  2547. else if (hRegion != 0)
  2548. {
  2549. THROW (CException(E_INVALIDARG));
  2550. }
  2551. #if CIDBG==1
  2552. if ( 0 != pWatchRegion && pWatchRegion->IsInit() )
  2553. {
  2554. Win4Assert( pWatchRegion->Segment()->IsWindow() );
  2555. CTableWindow * pWindow = (CTableWindow *) pWatchRegion->Segment();
  2556. Win4Assert( pWindow->HasWatch( hRegion ) );
  2557. }
  2558. #endif // CIDBG==1
  2559. CRegionTransformer regionTransformer( pWatchRegion,
  2560. (LONG) iRowOffset,
  2561. rGetParams.RowsToTransfer(),
  2562. rGetParams.GetFwdFetch() );
  2563. CFwdTableSegIter iter( _segList );
  2564. //
  2565. // Locate the bookmark.
  2566. //
  2567. status = rowLocator.LokLocate( hRegion,
  2568. fAsync,
  2569. iter,
  2570. regionTransformer);
  2571. if ( S_OK != status )
  2572. {
  2573. return status;
  2574. }
  2575. // also: calculate the fetch coordinates
  2576. if ( !regionTransformer.Validate() )
  2577. THROW (CException(DB_E_NONCONTIGUOUSRANGE));
  2578. // The iterator is positioned at the fetch bookmark
  2579. // Move it to the actual fetch offset
  2580. rowLocator.LokRelocate ( fAsync,
  2581. iter,
  2582. regionTransformer,
  2583. xBktToExplode,
  2584. xBktToConvert );
  2585. if ( xBktToExplode.IsNull() )
  2586. {
  2587. //
  2588. // The first row to be fetched is in a window.
  2589. //
  2590. if ( 0 != rowLocator.GetBeyondTableCount() )
  2591. {
  2592. tbDebugOut(( DEB_REGTRANS,
  2593. " GetBeyondTableCount: %d\n",
  2594. rowLocator.GetBeyondTableCount() ));
  2595. //
  2596. // The requested offset is beyond the table. Must update
  2597. // the row retrieval count based on the residual row
  2598. // count.
  2599. //
  2600. regionTransformer.DecrementFetchCount( rowLocator,
  2601. iter,
  2602. _segList );
  2603. // to support watch regions, fetches beyond the
  2604. // end of rowset are expected
  2605. fBeyondTable = TRUE;
  2606. // don't read any rows that do fall in the table
  2607. // if notifications aren't enabled.
  2608. if ( 0 == _bitNotifyEnabled )
  2609. break;
  2610. }
  2611. Win4Assert( regionTransformer.GetFetchCount() >= 0 );
  2612. if ( regionTransformer.GetFetchCount() > 0 )
  2613. {
  2614. Win4Assert( iter.GetSegment()->IsWindow() );
  2615. if ( 0 != regionTransformer.Region() )
  2616. {
  2617. CDoubleTableSegIter fakeIter( iter );
  2618. //
  2619. // Simulate a fetch and collect all the buckets to be
  2620. // asynchronously expanded.
  2621. //
  2622. rowLocator.LokSimulateFetch( fakeIter,
  2623. regionTransformer,
  2624. xBktToConvert );
  2625. regionTransformer.Transform (_segList,
  2626. _watchList );
  2627. }
  2628. //
  2629. // We have located exactly where the starting workid is.
  2630. // Start fetching rows from here.
  2631. //
  2632. widStart = rowLocator.GetWorkIdFound();
  2633. tableRowGetter.SetRowsToTransfer( (ULONG) regionTransformer.GetFetchCount() );
  2634. //
  2635. // Real Work done here!
  2636. //
  2637. CTableWindow * pWindow = iter.GetWindow();
  2638. status = tableRowGetter.LokGetRowsAtSegment ( pWindow,
  2639. widStart,
  2640. fAsync,
  2641. xBktToExplode );
  2642. rwidLastRowTransferred = tableRowGetter.GetLastWorkId();
  2643. if ( !xBktToExplode.IsNull() )
  2644. {
  2645. Win4Assert( !fAsync );
  2646. //
  2647. // All the requested rows did not get filled in.
  2648. // We have hit a bucket which must be exploded.
  2649. // Snapshot the current position and offset (either
  2650. // +1 if forward fetch or -1 if backwards fetch) so
  2651. // that we can continue from the snapshotted position
  2652. // after the bucket has been exploded.
  2653. //
  2654. long iOffset;
  2655. if ( rGetParams.GetFwdFetch() )
  2656. iOffset = 1;
  2657. else
  2658. iOffset = -1;
  2659. rowLocator.SetLocateInfo( rwidLastRowTransferred, iOffset );
  2660. widToPin = rwidLastRowTransferred;
  2661. iRowOffset = iOffset;
  2662. }
  2663. } // regionTransformer.GetFetchCount() > 0
  2664. }
  2665. #if CIDBG==1
  2666. _watchList.CheckRegionConsistency( pWatchRegion );
  2667. #endif // CIDBG==1
  2668. }
  2669. // Lock released
  2670. // ============================================================
  2671. if ( 0 != xBktToExplode.GetPointer() )
  2672. {
  2673. // We need to synchronously convert bucket into windows.
  2674. // and then restart fetching from the top
  2675. if ( widInvalid == widToPin )
  2676. widToPin = widStart;
  2677. _NoLokBucketToWindows( xBktToExplode, widToPin, FALSE, FALSE );
  2678. }
  2679. else
  2680. {
  2681. break;
  2682. }
  2683. } // end of bucket expansion loop
  2684. if ( xBktToConvert.Count() > 0 )
  2685. {
  2686. // Schedule buckets for asynchronous expansion
  2687. _NoLokBucketToWindows ( xBktToConvert, widInvalid, TRUE, TRUE );
  2688. }
  2689. if (SUCCEEDED(status) && fBeyondTable)
  2690. {
  2691. if ( _bitNotifyEnabled )
  2692. status = DB_S_ENDOFROWSET;
  2693. else
  2694. status = DB_E_BADSTARTPOSITION;
  2695. }
  2696. else if ( ( IsCategorized() ) &&
  2697. ( DB_S_ENDOFROWSET == status ) &&
  2698. ( 0 == rGetParams.RowsTransferred() ) &&
  2699. ( 0 != cChaptRows ) )
  2700. {
  2701. status = DB_E_BADSTARTPOSITION;
  2702. }
  2703. //
  2704. // If the query timed out and we're fetching at the end of the
  2705. // rowset, give an appropriate status.
  2706. //
  2707. if ( ( DB_S_ENDOFROWSET == status ) &&
  2708. ( 0 != ( Status() & STAT_TIME_LIMIT_EXCEEDED ) ) )
  2709. {
  2710. status = DB_S_STOPLIMITREACHED;
  2711. }
  2712. return status;
  2713. } //GetRowsAt
  2714. //+---------------------------------------------------------------------------
  2715. //
  2716. // Function: GetRowsAtRatio
  2717. //
  2718. // Synopsis: An APPROXIMATE retrieval of rows. NOTE that this is not
  2719. // EXACT - use GetRowsAt to retrieve rows at exact position.
  2720. //
  2721. // Arguments: [hRegion] - watch region handle
  2722. // [ulNum] -
  2723. // [ulDenom] -
  2724. // [chapt] - Chapter of rows returned
  2725. // [rOutColumns] -
  2726. // [rGetParams] -
  2727. // [rwidLastRowTransferred] -
  2728. //
  2729. // Returns:
  2730. //
  2731. // History: 4-04-95 srikants Created
  2732. // 6-28-95 BartoszM Added watch region support
  2733. //
  2734. // Notes:
  2735. //
  2736. //----------------------------------------------------------------------------
  2737. SCODE
  2738. CLargeTable::GetRowsAtRatio(
  2739. HWATCHREGION hRegion,
  2740. ULONG ulNum,
  2741. ULONG ulDenom,
  2742. CI_TBL_CHAPT chapt,
  2743. CTableColumnSet const & rOutColumns,
  2744. CGetRowsParams & rGetParams,
  2745. WORKID & rwidLastRowTransferred
  2746. )
  2747. {
  2748. if ( 0 == ulDenom || ulNum > ulDenom )
  2749. {
  2750. QUIETTHROW( CException(DB_E_BADRATIO) );
  2751. }
  2752. BOOL fFarFromWindow = TRUE;
  2753. BOOL fAsync = FALSE;
  2754. SCODE scRet = S_OK;
  2755. ULONG cRowsFromFront = 0;
  2756. WORKID widAnchor = widInvalid; // initialize to an invalid value.
  2757. ULONG cPercentDiff = 0;
  2758. LONG offFetchStart = 0;
  2759. XPtr<CTableBucket> xBktToExplode(0);
  2760. //
  2761. // ==================================================================
  2762. // Obtain the lock
  2763. {
  2764. CLock lock(_mutex);
  2765. _LokCheckQueryStatus();
  2766. if ( IsCategorized() && DB_NULL_HCHAPTER != chapt )
  2767. {
  2768. ULONG cRows = GetCategorizer()->GetRowCount( chapt );
  2769. Win4Assert( 0 != cRows && "Chapter is empty" );
  2770. LONGLONG llcRowsFromFront = ( (LONGLONG) cRows) * ulNum ;
  2771. llcRowsFromFront /= ulDenom;
  2772. Win4Assert( llcRowsFromFront <= cRows && llcRowsFromFront >= 0 );
  2773. cRowsFromFront = lltoul( llcRowsFromFront ) ;
  2774. }
  2775. else
  2776. {
  2777. //
  2778. // Determine the approximate offset from the beginning of the table
  2779. //
  2780. const ULONG cTotalRows = (ULONG) RowCount();
  2781. if ( 0 == cTotalRows )
  2782. {
  2783. rwidLastRowTransferred = WORKID_TBLAFTERLAST;
  2784. return DB_S_ENDOFROWSET;
  2785. }
  2786. LONGLONG llcRowsFromFront = ( (LONGLONG) cTotalRows) * ulNum ;
  2787. llcRowsFromFront /= ulDenom;
  2788. Win4Assert( llcRowsFromFront <= cTotalRows && llcRowsFromFront >= 0 );
  2789. cRowsFromFront = lltoul( llcRowsFromFront ) ;
  2790. if ( cRowsFromFront == cTotalRows )
  2791. {
  2792. if ( rGetParams.GetFwdFetch() )
  2793. {
  2794. //
  2795. // The user is asking to retrieve past the end of table.
  2796. //
  2797. rwidLastRowTransferred = WORKID_TBLAFTERLAST;
  2798. return DB_S_ENDOFROWSET;
  2799. }
  2800. else
  2801. {
  2802. //
  2803. // Fetch rows starting with last row in rowset
  2804. //
  2805. rwidLastRowTransferred = WORKID_TBLAFTERLAST;
  2806. SCODE scRet = GetRowsAt( hRegion,
  2807. WORKID_TBLAFTERLAST,
  2808. chapt,
  2809. -1,
  2810. rOutColumns,
  2811. rGetParams,
  2812. rwidLastRowTransferred );
  2813. return scRet;
  2814. }
  2815. }
  2816. //
  2817. // Locate the closest window at this offset.
  2818. //
  2819. CLinearRange winRange; // will be initialized by _LokGetClosestWindow
  2820. CTableWindow * pWindow = _LokGetClosestWindow( cRowsFromFront, winRange );
  2821. if ( 0 == pWindow )
  2822. {
  2823. fFarFromWindow = TRUE;
  2824. }
  2825. else
  2826. {
  2827. //
  2828. // Offset of the first row from the beginning of the table.
  2829. //
  2830. offFetchStart = winRange.GetLow();
  2831. if ( winRange.InRange( cRowsFromFront ) &&
  2832. rGetParams.RowsToTransfer() <= winRange.GetRange() )
  2833. {
  2834. //
  2835. // This window has enough rows.
  2836. //
  2837. long cRowsOff; // offset within the window
  2838. if ( rGetParams.GetFwdFetch() )
  2839. {
  2840. if ( cRowsFromFront + rGetParams.RowsToTransfer() <=
  2841. winRange.GetHigh() )
  2842. {
  2843. //
  2844. // We hit the exact row that the client requested.
  2845. //
  2846. cRowsOff = cRowsFromFront - winRange.GetLow();
  2847. }
  2848. else
  2849. {
  2850. //
  2851. // Optimization : We cannot fill the output set with the rows
  2852. // from this window if we position exactly. So just use a
  2853. // little from front.
  2854. //
  2855. cRowsOff = winRange.GetRange() - rGetParams.RowsToTransfer();
  2856. }
  2857. }
  2858. else
  2859. {
  2860. if ( cRowsFromFront >= rGetParams.RowsToTransfer()
  2861. && cRowsFromFront - rGetParams.RowsToTransfer() >= winRange.GetLow() )
  2862. {
  2863. //
  2864. // We hit the exact row that the client requested
  2865. //
  2866. cRowsOff = cRowsFromFront - winRange.GetLow();
  2867. }
  2868. else
  2869. {
  2870. //
  2871. // Optimization : We cannot fill the output set with the rows
  2872. // from this window if we position exactly. So just use a
  2873. // little from back.
  2874. //
  2875. cRowsOff = rGetParams.RowsToTransfer() - 1;
  2876. }
  2877. }
  2878. //
  2879. // There was no wrap around.
  2880. //
  2881. Win4Assert( cRowsOff >= 0 && cRowsOff < (long)pWindow->RowCount());
  2882. offFetchStart += cRowsOff;
  2883. widAnchor = pWindow->GetBookMarkAt( cRowsOff );
  2884. }
  2885. else
  2886. {
  2887. widAnchor = WORKID_TBLFIRST;
  2888. }
  2889. const cMaxApproxPerCent = 10; // Let us say 10% maximum approximation
  2890. //
  2891. // Determine how accurate is the approximate position.
  2892. //
  2893. cPercentDiff = AbsDiff( offFetchStart, cRowsFromFront ) * 100;
  2894. cPercentDiff /= cTotalRows;
  2895. if ( cPercentDiff <= cMaxApproxPerCent )
  2896. {
  2897. Win4Assert( widInvalid != widAnchor );
  2898. fFarFromWindow = FALSE;
  2899. }
  2900. else
  2901. {
  2902. tbDebugOut(( DEB_WINSPLIT,
  2903. " Approximation is too off 0x%X - Requested - Arrived 0x%X\n",
  2904. cRowsFromFront, offFetchStart ));
  2905. }
  2906. }
  2907. fAsync = _LokIsWatched() && (0 != hRegion);
  2908. if ( 0 != hRegion )
  2909. {
  2910. // Nuke the region first
  2911. _watchList.ShrinkRegionToZero (hRegion);
  2912. }
  2913. //
  2914. // If we are either too far from a window or if the asynchronous
  2915. // mode is on, we have to use GetRowsAt()
  2916. //
  2917. if ( !fFarFromWindow && !fAsync )
  2918. {
  2919. tbDebugOut(( DEB_WINSPLIT,
  2920. "GetRowsAtRatio - Approximate. Requested 0x%X Actual 0x%X Percent 0x%X\n",
  2921. cRowsFromFront, offFetchStart, cPercentDiff ));
  2922. CTableRowGetter rowGetter( *this, rOutColumns, rGetParams, chapt, hRegion );
  2923. //
  2924. // Real work done here!
  2925. //
  2926. Win4Assert( widInvalid != widAnchor );
  2927. Win4Assert( 0 != pWindow->RowCount() );
  2928. #if CIDBG==1
  2929. TBL_OFF oRowdummy;
  2930. ULONG iRowdummy;
  2931. Win4Assert( pWindow->FindBookMark( widAnchor, oRowdummy, iRowdummy ) );
  2932. #endif // CIDBG==1
  2933. scRet = rowGetter.LokGetRowsAtSegment( pWindow,
  2934. widAnchor,
  2935. FALSE, // synchronous
  2936. xBktToExplode );
  2937. if ( !FAILED(scRet) )
  2938. {
  2939. rwidLastRowTransferred = rowGetter.GetLastWorkId();
  2940. Win4Assert( rwidLastRowTransferred != widInvalid &&
  2941. !IsSpecialWid( rwidLastRowTransferred ) );
  2942. }
  2943. if ( xBktToExplode.IsNull() )
  2944. {
  2945. return scRet;
  2946. }
  2947. //
  2948. // The bucket to explode is not null. We will synchronously
  2949. // explode it and continue fetching from there on.
  2950. //
  2951. Win4Assert( !FAILED(scRet) );
  2952. }
  2953. }
  2954. }
  2955. // ==================================================================
  2956. // Release table lock
  2957. //
  2958. // We are either :
  2959. // 1. Far from window or
  2960. // 2. Is being watched or
  2961. // 3. We have a bucket to explode synchronously.
  2962. //
  2963. if ( xBktToExplode.IsNull() )
  2964. {
  2965. //
  2966. // We are either far from a window or we have a watch region.
  2967. //
  2968. widAnchor = WORKID_TBLFIRST;
  2969. offFetchStart = cRowsFromFront;
  2970. }
  2971. else
  2972. {
  2973. widAnchor = rwidLastRowTransferred;
  2974. if ( rGetParams.GetFwdFetch() )
  2975. offFetchStart = 1;
  2976. else
  2977. offFetchStart = -1;
  2978. _NoLokBucketToWindows( xBktToExplode, widAnchor, FALSE, TRUE ); // synchronous
  2979. }
  2980. tbDebugOut(( DEB_WINSPLIT, "GetRowsAtRatio - going to GetRowsAt\n" ));
  2981. Win4Assert( widInvalid != widAnchor );
  2982. // The region is pre-shrunk to zero, so it's okay to start in a bucket.
  2983. scRet = GetRowsAt( hRegion,
  2984. widAnchor,
  2985. chapt,
  2986. offFetchStart,
  2987. rOutColumns,
  2988. rGetParams,
  2989. rwidLastRowTransferred );
  2990. if (DB_E_BADSTARTPOSITION == scRet)
  2991. scRet = DB_S_ENDOFROWSET;
  2992. return scRet;
  2993. } //GetRowsAtRatio
  2994. //+---------------------------------------------------------------------------
  2995. //
  2996. // Class: CClosestWindow
  2997. //
  2998. // Purpose: Determines the closest window for a given position in the
  2999. // table.
  3000. //
  3001. // History: 4-03-95 srikants Created
  3002. //
  3003. // Notes:
  3004. //
  3005. //----------------------------------------------------------------------------
  3006. class CClosestWindow
  3007. {
  3008. public:
  3009. CClosestWindow( ULONG targetOffset, CTableSegList & list )
  3010. : _targetOffset(targetOffset),
  3011. _list(list),
  3012. _iter(_list),
  3013. _currSegOffset(0),
  3014. _pBest(0), _bestRange(ULONG_MAX,ULONG_MAX),
  3015. _fDone(FALSE)
  3016. {
  3017. }
  3018. void ProcessCurrent();
  3019. BOOL IsDone() { return _fDone || _list.AtEnd(_iter); }
  3020. CTableWindow * GetClosest( CLinearRange & winRange )
  3021. {
  3022. if ( 0 != _pBest )
  3023. {
  3024. winRange.Set( _bestRange.GetLow(), _bestRange.GetHigh() );
  3025. }
  3026. return _pBest;
  3027. }
  3028. void Next()
  3029. {
  3030. Win4Assert( !_list.AtEnd(_iter) );
  3031. _list.Advance(_iter);
  3032. }
  3033. private:
  3034. ULONG _targetOffset; // Target offset to locate
  3035. CTableSegList & _list; // The list of segments
  3036. CFwdTableSegIter _iter; // Iterator over the list of segments
  3037. ULONG _currSegOffset; // Beginning offset of the current
  3038. // segment
  3039. CTableWindow * _pBest; // Best window so far
  3040. CLinearRange _bestRange; // Range of the best window
  3041. BOOL _fDone; // Flag set to TRUE if we have already
  3042. // found the best possible window.
  3043. };
  3044. //+---------------------------------------------------------------------------
  3045. //
  3046. // Function: ProcessCurrent
  3047. //
  3048. // Synopsis: Processes the current segment in the iterator and updates
  3049. // the "best" window if applicable.
  3050. //
  3051. // History: 4-03-95 srikants Created
  3052. //
  3053. // Notes:
  3054. //
  3055. //----------------------------------------------------------------------------
  3056. void CClosestWindow::ProcessCurrent( )
  3057. {
  3058. CTableSegment * pSegment = _iter.GetSegment();
  3059. Win4Assert( 0 != pSegment );
  3060. Win4Assert( !_fDone );
  3061. ULONG cRowsInSeg = (ULONG)pSegment->RowCount();
  3062. ULONG currEndSeg = _currSegOffset;
  3063. if ( 0 != cRowsInSeg )
  3064. {
  3065. currEndSeg += (cRowsInSeg-1);
  3066. }
  3067. if ( pSegment->IsWindow() && 0 != pSegment->RowCount() )
  3068. {
  3069. if ( _currSegOffset <= _targetOffset )
  3070. {
  3071. //
  3072. // We are still on the left hand side. So, this one MUST be
  3073. // closer than what we had before.
  3074. //
  3075. _pBest = (CTableWindow *) pSegment;
  3076. _bestRange.Set( _currSegOffset, currEndSeg );
  3077. _fDone = _bestRange.InRange( _targetOffset );
  3078. }
  3079. else
  3080. {
  3081. //
  3082. // We are on the right hand side of the target.
  3083. //
  3084. CTableWindow * pRight = (CTableWindow *) pSegment;
  3085. if ( 0 != _pBest )
  3086. {
  3087. if ( AbsDiff( _currSegOffset, _targetOffset ) <
  3088. AbsDiff( _bestRange.GetLow(), _targetOffset ) )
  3089. {
  3090. //
  3091. // We found a closer window on the right hand
  3092. // side.
  3093. //
  3094. _pBest = pRight;
  3095. _bestRange.Set( _currSegOffset, currEndSeg );
  3096. }
  3097. }
  3098. else
  3099. {
  3100. _pBest = pRight;
  3101. _bestRange.Set( _currSegOffset, currEndSeg );
  3102. }
  3103. _fDone = TRUE;
  3104. }
  3105. }
  3106. _currSegOffset += cRowsInSeg;
  3107. }
  3108. //+---------------------------------------------------------------------------
  3109. //
  3110. // Function: _LokGetClosestWindow
  3111. //
  3112. // Synopsis: Given a position from the beginning of the table, this
  3113. // method determines the closest window from that position.
  3114. //
  3115. // Arguments: [cRowsFromFront] - Number of rows from the beginning of
  3116. // the table.
  3117. // [winRange] - (output) Range of the window found in
  3118. // the table.
  3119. //
  3120. // Returns: Pointer to the window, if one is found that is closest to
  3121. // the position requested.
  3122. //
  3123. // History: 4-03-95 srikants Created
  3124. //
  3125. // Notes:
  3126. //
  3127. //----------------------------------------------------------------------------
  3128. CTableWindow * CLargeTable::_LokGetClosestWindow(
  3129. ULONG cRowsFromFront,
  3130. CLinearRange & winRange )
  3131. {
  3132. winRange.Set(0,0);
  3133. for ( CClosestWindow closest( cRowsFromFront, _segList );
  3134. !closest.IsDone();
  3135. closest.Next() )
  3136. {
  3137. closest.ProcessCurrent();
  3138. }
  3139. return closest.GetClosest( winRange );
  3140. }
  3141. //+---------------------------------------------------------------------------
  3142. //
  3143. // Function: _CreateNewWindow
  3144. //
  3145. // Synopsis: Creates a new window with the given segment id.
  3146. //
  3147. // Arguments: [segId] - Segment id of the window to create.
  3148. //
  3149. // Returns: Pointer to the newly created window.
  3150. //
  3151. // History: 4-19-95 srikants Created
  3152. //
  3153. // Notes: This is a helper function for "CTableRowPutter" class.
  3154. //
  3155. //----------------------------------------------------------------------------
  3156. CTableWindow * CLargeTable::_CreateNewWindow(
  3157. ULONG segId,
  3158. CTableRowKey &lowKey,
  3159. CTableRowKey &highKey)
  3160. {
  3161. XPtr<CTableWindow> xWindow( new CTableWindow( _pSortSet,
  3162. _keyCompare.GetReference(),
  3163. &_MasterColumnSet,
  3164. segId,
  3165. GetCategorizer(),
  3166. _sharedBuf,
  3167. *_pQExecute ) );
  3168. xWindow->GetLowestKey() = lowKey;
  3169. xWindow->GetHighestKey() = highKey;
  3170. return xWindow.Acquire();
  3171. } //_CreateNewWindow
  3172. //+---------------------------------------------------------------------------
  3173. //
  3174. // Function: SetQueryExecute
  3175. //
  3176. // Synopsis: Initializes the query executer object to be used during
  3177. // bucket->window conversion. The query object will be refcounted
  3178. // to co-ordinate destruction order.
  3179. //
  3180. // Arguments: [pQExecute] - Pointer to the query object.
  3181. //
  3182. // History: 4-25-95 srikants Created
  3183. //
  3184. // Notes: Must be called ONCE and only ONCE. Before ~CLargeTable is
  3185. // called, the ReleaseQueryExecute MUST be called.
  3186. //
  3187. //----------------------------------------------------------------------------
  3188. void CLargeTable::SetQueryExecute( CQAsyncExecute * pQExecute ) // virtual
  3189. {
  3190. CLock lock(_mutex);
  3191. Win4Assert( 0 == _pQExecute );
  3192. _pQExecute = pQExecute;
  3193. _pQExecute->AddRef();
  3194. }
  3195. //+---------------------------------------------------------------------------
  3196. //
  3197. // Function: ReleaseQueryExecute
  3198. //
  3199. // Synopsis: Releases the query object. After this, we cannot do any
  3200. // bucket->window conversions.
  3201. //
  3202. // History: 4-25-95 srikants Created
  3203. //
  3204. // Notes: MUST be called once before the destructor is called.
  3205. //
  3206. //----------------------------------------------------------------------------
  3207. void CLargeTable::ReleaseQueryExecute()
  3208. {
  3209. CLock lock(_mutex);
  3210. Win4Assert( 0 != _pQExecute );
  3211. _pQExecute->Release();
  3212. _pQExecute = 0;
  3213. //
  3214. // Abort all the bucket->window expansions that are in progress.
  3215. //
  3216. for ( CAsyncBucketExploder * pEntry = _explodeBktsList.RemoveLast();
  3217. 0 != pEntry;
  3218. pEntry = _explodeBktsList.RemoveLast() )
  3219. {
  3220. pEntry->Close();
  3221. pEntry->Abort();
  3222. pEntry->Release();
  3223. }
  3224. }
  3225. //+---------------------------------------------------------------------------
  3226. //
  3227. // Function: _LokAddToExplodeList
  3228. //
  3229. // Synopsis:
  3230. //
  3231. // Arguments: [pBktExploder] -
  3232. //
  3233. // Returns:
  3234. //
  3235. // History: 5-30-95 srikants Created
  3236. //
  3237. // Notes:
  3238. //
  3239. //----------------------------------------------------------------------------
  3240. void CLargeTable::_LokAddToExplodeList( CAsyncBucketExploder * pBktExploder )
  3241. {
  3242. Win4Assert( pBktExploder->IsSingle() );
  3243. pBktExploder->AddRef();
  3244. _explodeBktsList.Push( pBktExploder );
  3245. //
  3246. // Pin the workid to be in a window and prevent from being converted
  3247. // into a bucket.
  3248. //
  3249. _segListMgr.SetInUseByBuckets( pBktExploder->GetWorkIdToPin() );
  3250. } //_LokAddToExplodeList
  3251. //+---------------------------------------------------------------------------
  3252. //
  3253. // Function: _RemoveFromExplodeList
  3254. //
  3255. // Synopsis:
  3256. //
  3257. // Arguments: [pBktExploder] -
  3258. //
  3259. // Returns:
  3260. //
  3261. // History: 5-30-95 srikants Created
  3262. //
  3263. // Notes:
  3264. //
  3265. //----------------------------------------------------------------------------
  3266. void CLargeTable::_RemoveFromExplodeList( CAsyncBucketExploder * pBktExploder )
  3267. {
  3268. {
  3269. CLock lock(_mutex);
  3270. Win4Assert( 0 != pBktExploder );
  3271. _explodeBktsList.RemoveFromList( pBktExploder );
  3272. pBktExploder->Close();
  3273. _segListMgr.ClearInUseByBuckets( pBktExploder->GetWorkIdToPin() );
  3274. }
  3275. pBktExploder->Release();
  3276. }
  3277. //+---------------------------------------------------------------------------
  3278. //
  3279. // Function: QueryAbort
  3280. //
  3281. // Synopsis: Processes an abort and wakes up any waiters on the events
  3282. // in the largetable.
  3283. //
  3284. // History: 5-31-95 srikants Created
  3285. //
  3286. // Notes:
  3287. //
  3288. //----------------------------------------------------------------------------
  3289. void CLargeTable::QueryAbort()
  3290. {
  3291. CLock lock(_mutex);
  3292. Win4Assert( 0 == _pQExecute );
  3293. Win4Assert( _explodeBktsList.IsEmpty() );
  3294. _fAbort = TRUE;
  3295. } //QueryAbort
  3296. //+---------------------------------------------------------------------------
  3297. //
  3298. // Method: GetCurrentPosition, public
  3299. //
  3300. // Synopsis: Gets the current GetNextRows position for the table or
  3301. // chapter.
  3302. //
  3303. // Arguments: [chapt] - gets the current position for this chapter
  3304. //
  3305. // History: 6-30-95 dlee Created
  3306. //
  3307. //----------------------------------------------------------------------------
  3308. WORKID CLargeTable::GetCurrentPosition(
  3309. CI_TBL_CHAPT chapt)
  3310. {
  3311. if ( IsCategorized() && DB_NULL_HCHAPTER != chapt )
  3312. return GetCategorizer()->GetCurrentPositionThisLevel( chapt );
  3313. else
  3314. return _widCurrent;
  3315. } //GetCurrentPosition
  3316. //+---------------------------------------------------------------------------
  3317. //
  3318. // Method: SetCurrentPosition, public
  3319. //
  3320. // Synopsis: Sets the current GetNextRows position for the table or
  3321. // chapter.
  3322. //
  3323. // Arguments: [chapt] - sets the current position for this chapter
  3324. //
  3325. // History: 6-30-95 dlee Created
  3326. //
  3327. //----------------------------------------------------------------------------
  3328. WORKID CLargeTable::SetCurrentPosition(
  3329. CI_TBL_CHAPT chapt,
  3330. WORKID wid)
  3331. {
  3332. if ( IsCategorized() && DB_NULL_HCHAPTER != chapt )
  3333. return GetCategorizer()->SetCurrentPositionThisLevel( chapt, wid );
  3334. else
  3335. return ( _widCurrent = wid );
  3336. } //SetCurrentPosition
  3337. //+---------------------------------------------------------------------------
  3338. //
  3339. // Method: LokGetOneColumn, public
  3340. //
  3341. // Synopsis: Gets data for one column for the given workid.
  3342. //
  3343. // Arguments: [wid] - for which data is retrieved
  3344. // [rOutColumn] - column descritption for where data is written
  3345. // [pbOut] - where data is written
  3346. // [rVarAllocator] - allocator to use for variable-len data
  3347. //
  3348. // History: 8-22-95 dlee Created
  3349. //
  3350. //----------------------------------------------------------------------------
  3351. void CLargeTable::LokGetOneColumn(
  3352. WORKID wid,
  3353. CTableColumn const & rOutColumn,
  3354. BYTE * pbOut,
  3355. PVarAllocator & rVarAllocator )
  3356. {
  3357. CTableWindow *pWindow = (CTableWindow *) _LokFindTableSegment( wid );
  3358. pWindow->LokGetOneColumn( wid, rOutColumn, pbOut, rVarAllocator );
  3359. } //LokGetOneColumn
  3360. CI_TBL_BMK CLargeTable::_FindNearestDynamicBmk( CTableWindow * pWindow,
  3361. CI_TBL_CHAPT chapter,
  3362. CI_TBL_BMK bookmark )
  3363. {
  3364. // If the row corresponding to the bookmark exists in the
  3365. // dynamic state, return the original bookmark.
  3366. // If the row has been deleted from the dynamic state
  3367. // return the bookmark of the next available dynamic row.
  3368. // If you hit the end of the table, return the bookmark
  3369. // of the last row in the dynamic state of the table
  3370. CI_TBL_BMK bmkFound = bookmark;
  3371. if ( pWindow->FindNearestDynamicBmk( chapter, bmkFound ) )
  3372. {
  3373. return bmkFound;
  3374. }
  3375. //
  3376. // Find the closest bookmark to the right of this window.
  3377. //
  3378. CDoubleTableSegIter fwdIter( pWindow );
  3379. for ( _segList.Advance(fwdIter); !_segList.AtEnd(fwdIter);
  3380. _segList.Advance(fwdIter) )
  3381. {
  3382. if ( fwdIter.GetSegment()->IsWindow() )
  3383. {
  3384. CTableWindow * pWindow = fwdIter.GetWindow();
  3385. if ( pWindow->FindFirstNonDeleteDynamicBmk(bmkFound) )
  3386. return bmkFound;
  3387. }
  3388. }
  3389. //
  3390. // Couldn't find anything to the right of this window. Find to the
  3391. // left of the window.
  3392. //
  3393. CDoubleTableSegIter backIter( pWindow );
  3394. for ( _segList.BackUp(backIter); !_segList.AtEnd(backIter);
  3395. _segList.BackUp(backIter) )
  3396. {
  3397. if ( backIter.GetSegment()->IsWindow() )
  3398. {
  3399. CTableWindow * pWindow = backIter.GetWindow();
  3400. if ( pWindow->FindFirstNonDeleteDynamicBmk(bmkFound) )
  3401. return bmkFound;
  3402. }
  3403. }
  3404. //
  3405. // The whole table has been deleted. Just use the WORKID_TBLFIRST
  3406. //
  3407. return WORKID_TBLFIRST;
  3408. } //_FindNearestDynamicBmk
  3409. //+---------------------------------------------------------------------------
  3410. //
  3411. // Member: CTableSink::SetStatus, public
  3412. //
  3413. // Synopsis: Sets the query status.
  3414. //
  3415. // Arguments: [s] -- The new status
  3416. //
  3417. // History: 18-Oct-91 KyleP Created.
  3418. //
  3419. //----------------------------------------------------------------------------
  3420. void CTableSink::SetStatus(ULONG s, NTSTATUS sc)
  3421. {
  3422. _status = s;
  3423. if (sc != STATUS_SUCCESS)
  3424. {
  3425. Win4Assert( QUERY_FILL_STATUS(s) == STAT_ERROR &&
  3426. ! NT_SUCCESS(sc) );
  3427. _scStatus = sc;
  3428. tbDebugOut(( DEB_WARN,
  3429. "tablesink at 0x%x entering 0x%x ERROR state\n",
  3430. this, sc ));
  3431. }
  3432. else
  3433. {
  3434. Win4Assert( QUERY_FILL_STATUS(s) != STAT_ERROR );
  3435. }
  3436. }