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.

3084 lines
96 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1994 - 2000.
  5. //
  6. // File: tblwindo.cxx
  7. //
  8. // Contents:
  9. //
  10. // Classes: CTableWindow - a window over a segment of a table
  11. // XCompressFreeVariant - container for variant which must be freed
  12. //
  13. // Functions:
  14. //
  15. // Notes:
  16. //
  17. // History: 25 Jan 1994 AlanW Created
  18. // 20 Jun 1995 BartoszM Added watch regions
  19. //
  20. //--------------------------------------------------------------------------
  21. #include "pch.cxx"
  22. #pragma hdrstop
  23. #include <query.hxx> // For CGetRowsParams
  24. #include <tbrowkey.hxx>
  25. #include <singlcur.hxx>
  26. #include "tblwindo.hxx"
  27. #include "colcompr.hxx"
  28. #include "tabledbg.hxx"
  29. //+-------------------------------------------------------------------------
  30. //
  31. // Member: CTableWindow::CTableWindow, public
  32. //
  33. // Synopsis: Constructor for a window. Allocates and fills
  34. // in the column description. Computes size of
  35. // per-row data and assigns column offsets.
  36. // Allocates initial table row data and variable
  37. // data. Allocates initial row index.
  38. //
  39. // Arguments: [pMasterColumns] -- a pointer to the master column set
  40. //
  41. // Notes: PERFFIX - vikasman: To save space, we can set up all columns
  42. // which are not part of the sort set and which are not special
  43. // columns as deferred columns.
  44. //
  45. //--------------------------------------------------------------------------
  46. CTableWindow::CTableWindow(
  47. CSortSet const * pSortSet,
  48. CTableKeyCompare & comparator,
  49. CColumnMasterSet * pMasterColumns,
  50. ULONG segId,
  51. CCategorize *pCategorize,
  52. CSharedBuffer & sharedBuf,
  53. CQAsyncExecute & QExecute
  54. ) : CTableSegment( CTableSegment::eWindow, segId,
  55. *pSortSet, comparator ),
  56. _pSortSet( pSortSet ),
  57. _Columns( pMasterColumns->Size() ),
  58. _sharedBuf( sharedBuf ),
  59. _BookMarkMap(_visibleRowIndex),
  60. _cPendingDeletes(0),
  61. _cRowsHardDeleted(0),
  62. _cRowsAllocated(0),
  63. _fSplitInProgress(FALSE),
  64. _cbHeapUsed ( 0 ),
  65. _pPathStore(0),
  66. _dynRowIndex(),
  67. _QueryExecute(QExecute),
  68. _fCanPartialDefer(QExecute.CanPartialDefer())
  69. {
  70. tbDebugOut(( DEB_ITRACE, "_CanPartialDefer = %d\n", _fCanPartialDefer ));
  71. SetCategorizer(pCategorize);
  72. int cCol = pMasterColumns->Size();
  73. CTableRowAlloc RowMap(0);
  74. unsigned maxAlignment = 0;
  75. _iOffsetWid = ULONG_MAX;
  76. _iOffsetRowStatus = ULONG_MAX;
  77. _iOffsetChapter = ULONG_MAX;
  78. _iOffsetRank = ULONG_MAX;
  79. _iOffsetHitCount = ULONG_MAX;
  80. for (int iCol=0; iCol < cCol; iCol++) {
  81. CColumnMasterDesc& MasterCol = pMasterColumns->Get(iCol);
  82. if (MasterCol.IsCompressedCol() &&
  83. MasterCol.GetCompressMasterId() != 0)
  84. {
  85. //
  86. // Global shared compression; make sure the referenced
  87. // column is in the table column set.
  88. //
  89. BOOL fFound = FALSE;
  90. CTableColumn* pColumn =
  91. _Columns.Find(MasterCol.GetCompressMasterId(), fFound);
  92. if (fFound != TRUE) {
  93. CColumnMasterDesc* pMasterComprCol =
  94. pMasterColumns->Find(MasterCol.GetCompressMasterId());
  95. Win4Assert(pMasterComprCol != 0);
  96. _AddColumnDesc(*pMasterComprCol, RowMap, maxAlignment);
  97. }
  98. }
  99. else if ( pidName == MasterCol.PropId )
  100. {
  101. //
  102. // We must always add the pidPath before pidName if it is
  103. // present in the MasterColumnSet. This is because we
  104. // do shared compression for path and name.
  105. //
  106. BOOL fPathPresent = FALSE;
  107. CTableColumn * pWindowPathCol = _Columns.Find( pidPath ,
  108. fPathPresent );
  109. if ( !fPathPresent )
  110. {
  111. //
  112. // There is no column for pidPath in the window before
  113. // this column. Check if there is pidPath in the master
  114. // column set and if so, add pidPath to the window column
  115. // set before pidName.
  116. //
  117. CColumnMasterDesc * pMasterPathCol =
  118. pMasterColumns->Find( pidPath );
  119. if ( 0 != pMasterPathCol )
  120. {
  121. _AddColumnDesc( *pMasterPathCol, RowMap, maxAlignment );
  122. }
  123. }
  124. }
  125. //
  126. // See if the column is already there (added for a shared compr)
  127. //
  128. BOOL fFound = FALSE;
  129. CTableColumn* pColumn =
  130. _Columns.Find(MasterCol.PropId, fFound);
  131. if (fFound)
  132. continue;
  133. _AddColumnDesc(MasterCol, RowMap, maxAlignment);
  134. }
  135. _FinishInit( RowMap, maxAlignment );
  136. END_CONSTRUCTION(CTableWindow);
  137. }
  138. //+---------------------------------------------------------------------------
  139. //
  140. // Function: CTableWindow ~ctor
  141. //
  142. // Synopsis: Constructor used for creating a new table window with the
  143. // same structure as an existing table window.
  144. //
  145. // Arguments: [src] - Source window which should be used as a basis for
  146. // the ROW FORMAT only; not the data.
  147. //
  148. // History: 2-07-95 srikants Created
  149. //
  150. // Notes: This is used during window splitting to create new windows
  151. // from an existing window.
  152. //
  153. //----------------------------------------------------------------------------
  154. CTableWindow::CTableWindow(
  155. CTableWindow & src,
  156. ULONG segId
  157. ) : CTableSegment( src, segId ),
  158. _Columns( src._Columns.Count() ),
  159. _sharedBuf( src._GetSharedBuf() ),
  160. _BookMarkMap(_visibleRowIndex),
  161. _pSortSet( src._pSortSet ),
  162. _cPendingDeletes(0),
  163. _cRowsHardDeleted(0),
  164. _cRowsAllocated(0),
  165. _fSplitInProgress(FALSE),
  166. _cbHeapUsed ( 0 ),
  167. _pPathStore(0),
  168. _dynRowIndex(),
  169. _QueryExecute(src._QueryExecute),
  170. _fCanPartialDefer(src._fCanPartialDefer)
  171. {
  172. SetCategorizer(src.GetCategorizer());
  173. int cCol = src._Columns.Count();
  174. CTableRowAlloc RowMap(0);
  175. unsigned maxAlignment = 0;
  176. _iOffsetWid = ULONG_MAX;
  177. _iOffsetRowStatus = ULONG_MAX;
  178. _iOffsetChapter = ULONG_MAX;
  179. _iOffsetRank = ULONG_MAX;
  180. _iOffsetHitCount = ULONG_MAX;
  181. //
  182. // PERFFIX: if we don't do window local compression, then we
  183. // can just copy the column format bit-by-bit and not worry
  184. // about looking at each column individually. For now, I will leave
  185. // the loop in place.
  186. //
  187. for (int iCol=0; iCol < cCol; iCol++)
  188. {
  189. CTableColumn& tableCol = *src._Columns.Get(iCol);
  190. if ( tableCol.IsCompressedCol() &&
  191. 0 != tableCol.GetCompressMasterId() )
  192. {
  193. //
  194. // Global shared compression; make sure the referenced
  195. // column is in the table column set.
  196. //
  197. BOOL fAlreadyPresent = FALSE;
  198. CTableColumn* pColumn =
  199. _Columns.Find(tableCol.GetCompressMasterId(), fAlreadyPresent);
  200. Win4Assert( fAlreadyPresent );
  201. if (!fAlreadyPresent)
  202. {
  203. CTableColumn* pTableComprCol =
  204. src._Columns.Find( tableCol.GetCompressMasterId(),
  205. fAlreadyPresent );
  206. Win4Assert(0 != pTableComprCol);
  207. _AddColumnDesc(*pTableComprCol, RowMap, maxAlignment);
  208. }
  209. }
  210. //
  211. // See if the column is already there (added for a shared compr)
  212. //
  213. BOOL fFound = FALSE;
  214. CTableColumn* pColumn =
  215. _Columns.Find(tableCol.GetPropId(), fFound);
  216. if (!fFound)
  217. {
  218. _AddColumnDesc(tableCol, RowMap, maxAlignment);
  219. }
  220. }
  221. _FinishInit( RowMap, maxAlignment );
  222. // tbDebugOut(( DEB_WINSPLIT, "NewWindow-C with id 0x%X\n", segId ));
  223. END_CONSTRUCTION(CTableWindow);
  224. }
  225. //+---------------------------------------------------------------------------
  226. //
  227. // Function: _FinishInit
  228. //
  229. // Synopsis: Completes the initialization of the constructor.
  230. //
  231. // Arguments: [RowMap] - RowMap containing the allocation details for
  232. // the column
  233. // [maxAlignment] - Maximum alignment requirement
  234. //
  235. // History: 2-07-95 srikants Split from the ~ctor to move the common
  236. // code to a function.
  237. //
  238. // Notes:
  239. //
  240. //----------------------------------------------------------------------------
  241. void CTableWindow::_FinishInit( CTableRowAlloc & RowMap,
  242. unsigned & maxAlignment )
  243. {
  244. Win4Assert(_iOffsetWid != ULONG_MAX); // workid must be there
  245. Win4Assert(_iOffsetRowStatus != ULONG_MAX); // row status must be there
  246. _cbRowSize = RowMap.GetRowWidth();
  247. //
  248. // Be sure the alignment holds from row to row too.
  249. //
  250. if (maxAlignment &&
  251. ALIGN(_cbRowSize, maxAlignment) != _cbRowSize) {
  252. RowMap.ReserveRowSpace(
  253. _cbRowSize,
  254. ALIGN(_cbRowSize, maxAlignment) - _cbRowSize
  255. );
  256. _cbRowSize = RowMap.GetRowWidth();
  257. Win4Assert(ALIGN(_cbRowSize, maxAlignment) == _cbRowSize);
  258. }
  259. Win4Assert(_cbRowSize >= sizeof (ULONG) && _cbRowSize < MAX_ROW_SIZE);
  260. Win4Assert(TBL_PAGE_ALLOC_MIN > _cbRowSize);
  261. _DataAllocator.SetRowSize(_cbRowSize);
  262. tbDebugOut(( DEB_ITRACE,
  263. "New CTableWindow %08x, Columns: %d\tRowSize: %d\n",
  264. this, _Columns.Count(), _cbRowSize ));
  265. _InitSortComparators();
  266. }
  267. //+-------------------------------------------------------------------------
  268. //
  269. // Member: CTableWindow::~CTableWindow, public
  270. //
  271. // Synopsis: Destructor for a window. Deallocates any memory,
  272. // and releases resources.
  273. //
  274. // Notes: All work is done by sub-object destructors
  275. //
  276. //--------------------------------------------------------------------------
  277. CTableWindow::~CTableWindow(void)
  278. {
  279. tbDebugOut(( DEB_WINSPLIT, "Destroying Window 0x%X\n", GetSegId() ));
  280. // Win4Assert(_cbHeapUsed == 0);
  281. }
  282. //+-------------------------------------------------------------------------
  283. //
  284. // Member: CTableWindow::_AddColumnDesc, private
  285. //
  286. // Synopsis: Add a column description to a table
  287. //
  288. // Arguments: [MasterCol] - a reference to a master column description
  289. // for the column to be added
  290. // [RowMap] - a reference to a row allocation map for
  291. // allocating row data
  292. // [maxAlignment] - maximumn alignment constraint for the
  293. // allocated row
  294. //
  295. // Returns: Nothing
  296. //
  297. // Effects: Space is allocated in the RowMap, maxAlignment is
  298. // adjusted.
  299. //
  300. // Notes:
  301. //
  302. //--------------------------------------------------------------------------
  303. void
  304. CTableWindow::_AddColumnDesc(
  305. CColumnMasterDesc& MasterCol,
  306. CTableRowAlloc& RowMap,
  307. unsigned& maxAlignment
  308. )
  309. {
  310. VARTYPE vt = MasterCol.DataType;
  311. if (vt == VT_NULL)
  312. vt = VT_VARIANT;
  313. //
  314. // The data type VT_EMPTY is used for columns like bookmark which
  315. // may occur in the master column set, but which has no data
  316. // stored in the table. Don't even bother adding it to the table
  317. // column set.
  318. //
  319. if (vt == VT_EMPTY)
  320. return;
  321. XPtr<CTableColumn> xTableCol( new CTableColumn( MasterCol.PropId, vt ) );
  322. _cbHeapUsed += sizeof CTableColumn;
  323. // If we can make this partial deferred, do so and get out
  324. if ( _fCanPartialDefer && _CanPartialDeferCol( xTableCol ) )
  325. {
  326. tbDebugOut(( DEB_ITRACE,
  327. "Setting col as partial deferred propid = %x\n",
  328. xTableCol->GetPropId()));
  329. xTableCol->SetPartialDeferred( TRUE );
  330. _Columns.Add( xTableCol.GetPointer(), _Columns.Count() );
  331. xTableCol.Acquire();
  332. return;
  333. }
  334. USHORT cbData, cbAlignment, rgfFlags;
  335. CTableVariant::VartypeInfo(vt, cbData, cbAlignment, rgfFlags);
  336. Win4Assert(cbData != 0);
  337. if (cbData == 0)
  338. {
  339. tbDebugOut(( DEB_WARN,
  340. "Unknown variant type %4x for propid %d\n",
  341. vt, MasterCol.PropId ));
  342. }
  343. if (MasterCol.IsCompressedCol())
  344. {
  345. //
  346. // Global compression; save a pointer to the
  347. // compressor.
  348. //
  349. xTableCol->SetGlobalCompressor( MasterCol.GetCompressor(),
  350. MasterCol.GetCompressMasterId() );
  351. if (xTableCol->GetCompressMasterId() != 0)
  352. {
  353. //
  354. // Look up the masterID, point the data offset and width
  355. // to it. Shadow the existing column.
  356. //
  357. BOOL fFound = FALSE;
  358. CTableColumn* pColumn =
  359. _Columns.Find(MasterCol.GetCompressMasterId(), fFound);
  360. Win4Assert(fFound == TRUE);
  361. xTableCol->SetValueField( vt,
  362. pColumn->GetValueOffset(),
  363. pColumn->GetValueSize() );
  364. cbData = 0; // indicate no new data needed
  365. Win4Assert( pColumn->IsStatusStored() );
  366. xTableCol->SetStatusField( pColumn->GetStatusOffset(), sizeof (BYTE) );
  367. }
  368. else
  369. {
  370. cbData = cbAlignment = sizeof (ULONG);
  371. }
  372. }
  373. if (cbAlignment)
  374. {
  375. if (cbAlignment > maxAlignment)
  376. maxAlignment = cbAlignment;
  377. }
  378. else
  379. {
  380. cbAlignment = 1;
  381. }
  382. if (cbData != 0)
  383. {
  384. xTableCol->SetValueField( vt,
  385. RowMap.AllocOffset( cbData, cbAlignment, TRUE ),
  386. cbData);
  387. // Always add a status byte -- even storage props may not be present
  388. // for summary catalogs. For others, need to know if the value is
  389. // deferred.
  390. xTableCol->SetStatusField( RowMap.AllocOffset( sizeof (BYTE),
  391. sizeof (BYTE),
  392. TRUE ),
  393. sizeof (BYTE));
  394. }
  395. if (xTableCol->PropId == pidWorkId)
  396. {
  397. _iOffsetWid = xTableCol->GetValueOffset();
  398. xTableCol->MarkAsSpecial();
  399. }
  400. else if (xTableCol->PropId == pidRank)
  401. {
  402. _iOffsetRank = xTableCol->GetValueOffset();
  403. }
  404. else if (xTableCol->PropId == pidHitCount)
  405. {
  406. _iOffsetHitCount = xTableCol->GetValueOffset();
  407. }
  408. else if (xTableCol->PropId == pidRowStatus)
  409. {
  410. _iOffsetRowStatus = xTableCol->GetValueOffset();
  411. xTableCol->MarkAsSpecial();
  412. }
  413. else if (xTableCol->PropId == pidChapter)
  414. {
  415. _iOffsetChapter = xTableCol->GetValueOffset();
  416. xTableCol->MarkAsSpecial();
  417. }
  418. else if (xTableCol->PropId == pidSelf)
  419. {
  420. xTableCol->MarkAsSpecial();
  421. }
  422. else if ( xTableCol->IsCompressedCol() && 0 != xTableCol->GetCompressMasterId() )
  423. {
  424. xTableCol->MarkAsSpecial();
  425. }
  426. #if CIDBG==1
  427. if ( xTableCol->IsValueStored() )
  428. Win4Assert( xTableCol->IsStatusStored() );
  429. #endif
  430. _Columns.Add( xTableCol.GetPointer(), _Columns.Count() );
  431. xTableCol.Acquire();
  432. } //_AddColumnDesc
  433. //+---------------------------------------------------------------------------
  434. //
  435. // Function: CTableWindow::_AddColumnDesc, private
  436. //
  437. // Synopsis: Add a column description to the table based on another
  438. // column description.
  439. //
  440. // Arguments: [srcTableCol] - The source upon which the new column table
  441. // description is based
  442. // [RowMap] - a reference to a row allocation map for
  443. // allocating row data
  444. // [maxAlignment] - maximumn alignment constraint for the
  445. // allocated row.
  446. //
  447. // History: 2-07-95 srikants Created
  448. // 11-Nov-99 KLam size check only valid for non-compressed
  449. // columns
  450. //
  451. // Notes:
  452. //
  453. //----------------------------------------------------------------------------
  454. void
  455. CTableWindow::_AddColumnDesc(
  456. CTableColumn& srcTableCol,
  457. CTableRowAlloc& RowMap,
  458. unsigned& maxAlignment
  459. )
  460. {
  461. XPtr<CTableColumn> xTableCol( new CTableColumn(srcTableCol) );
  462. _cbHeapUsed += sizeof CTableColumn;
  463. // If partial deferred, just add and return
  464. if ( xTableCol->IsPartialDeferred() )
  465. {
  466. _Columns.Add( xTableCol.GetPointer(), _Columns.Count() );
  467. xTableCol.Acquire();
  468. return;
  469. }
  470. VARTYPE vt = srcTableCol.GetStoredType();
  471. USHORT cbData, cbAlignment, rgfFlags;
  472. CTableVariant::VartypeInfo(vt, cbData, cbAlignment, rgfFlags);
  473. // Globally compressed columns either have a size of 0 or the size of the key
  474. // in the column
  475. Win4Assert( cbData != 0
  476. || ( srcTableCol.IsGlobalCompressedCol()
  477. && srcTableCol.GetCompressMasterId() ) );
  478. Win4Assert( cbData == srcTableCol.GetValueSize()
  479. || srcTableCol.IsGlobalCompressedCol() );
  480. if ( 0 == cbData )
  481. {
  482. tbDebugOut(( DEB_WARN,
  483. "Unknown variant type %4x for propid %d\n",
  484. vt, srcTableCol.GetPropId() ));
  485. }
  486. //
  487. // Store strings by reference
  488. //
  489. if ( (rgfFlags & CTableVariant::ByRef) &&
  490. !(rgfFlags & CTableVariant::StoreDirect))
  491. {
  492. Win4Assert( 0 == ( vt & VT_BYREF ) );
  493. }
  494. if ( srcTableCol.IsCompressedCol() )
  495. {
  496. if ( srcTableCol.IsGlobalCompressedCol() )
  497. {
  498. if ( 0 != srcTableCol.GetCompressMasterId() )
  499. {
  500. cbData = 0; // indicate no new data needed
  501. }
  502. else
  503. {
  504. // This size is refering to the size of the compression key
  505. cbData = cbAlignment = sizeof (ULONG);
  506. }
  507. }
  508. else
  509. {
  510. //
  511. // There is local compression in the source table.
  512. //
  513. if ( (pidName == srcTableCol.PropId) || (pidPath == srcTableCol.PropId ) )
  514. {
  515. }
  516. else
  517. {
  518. //
  519. // How did the local compression got set when it
  520. // is not yet implemented.
  521. //
  522. Win4Assert( !"TableWindow - Local Compression - NYI " );
  523. }
  524. }
  525. }
  526. if (cbAlignment)
  527. {
  528. if (cbAlignment > maxAlignment)
  529. maxAlignment = cbAlignment;
  530. }
  531. else
  532. {
  533. cbAlignment = 1;
  534. }
  535. //
  536. // This SetValueField call is not necessary for setting the
  537. // values in the column, since those have already been copied
  538. // from the source column, but this has the important side-effect
  539. // of updating the RowMap (which will presumably give back the
  540. // same values as the original allocation).
  541. //
  542. if (cbData != 0)
  543. {
  544. xTableCol->SetValueField( vt,
  545. RowMap.AllocOffset( cbData, cbAlignment, TRUE ),
  546. cbData);
  547. Win4Assert( xTableCol->GetValueOffset() == srcTableCol.GetValueOffset() );
  548. // Always add a status byte -- even storage props may not be present
  549. // for summary catalogs. For others, need to know if the value is
  550. // deferred.
  551. xTableCol->SetStatusField( RowMap.AllocOffset( sizeof (BYTE),
  552. sizeof (BYTE),
  553. TRUE ),
  554. sizeof (BYTE));
  555. }
  556. if (xTableCol->PropId == pidWorkId)
  557. {
  558. _iOffsetWid = xTableCol->GetValueOffset();
  559. xTableCol->MarkAsSpecial();
  560. }
  561. else if (xTableCol->PropId == pidRank)
  562. {
  563. _iOffsetRank = xTableCol->GetValueOffset();
  564. }
  565. else if (xTableCol->PropId == pidHitCount)
  566. {
  567. _iOffsetHitCount = xTableCol->GetValueOffset();
  568. }
  569. else if (xTableCol->PropId == pidRowStatus)
  570. {
  571. _iOffsetRowStatus = xTableCol->GetValueOffset();
  572. xTableCol->MarkAsSpecial();
  573. }
  574. else if (xTableCol->PropId == pidChapter)
  575. {
  576. _iOffsetChapter = xTableCol->GetValueOffset();
  577. xTableCol->MarkAsSpecial();
  578. }
  579. else if (xTableCol->PropId == pidSelf)
  580. {
  581. xTableCol->MarkAsSpecial();
  582. }
  583. else if ( xTableCol->IsCompressedCol() && 0 != xTableCol->GetCompressMasterId() )
  584. {
  585. xTableCol->MarkAsSpecial();
  586. }
  587. #if CIDBG==1
  588. if ( xTableCol->IsValueStored() )
  589. Win4Assert( xTableCol->IsStatusStored() );
  590. #endif
  591. _Columns.Add(xTableCol.GetPointer(), _Columns.Count());
  592. xTableCol.Acquire();
  593. }
  594. //+---------------------------------------------------------------------------
  595. //
  596. // Member: CTableWindow::_PopulateRow, private
  597. //
  598. // Synopsis: Extracts row data from an object cursor into row storage
  599. //
  600. // Arguments: [obj] -- source of data
  601. // [pThisRow] -- where to put the data
  602. // [wid] -- workid of the row
  603. //
  604. // Returns: Nothing
  605. //
  606. //--------------------------------------------------------------------------
  607. void CTableWindow::_PopulateRow(
  608. CRetriever & obj,
  609. BYTE * pThisRow,
  610. WORKID wid )
  611. {
  612. Win4Assert(!(wid & 0x80000000)); // Bad work ID?
  613. _SetRowWorkid(pThisRow, wid);
  614. //
  615. // Temporary buffer for column data. If it doesn't fit in this
  616. // buffer, defer the load. The buffer is owned by CLargeTable
  617. // and is only accessed under the lock taken in CLargeTable::PutRow
  618. //
  619. XUseSharedBuffer xSharedBuf(_sharedBuf);
  620. XArray<BYTE> xBuf;
  621. CTableVariant* pvarnt = (CTableVariant *) xSharedBuf.LokGetBuffer();
  622. unsigned cbBuf = xSharedBuf.LokGetSize();
  623. for (unsigned i=0; i < _Columns.Count(); i++)
  624. {
  625. CTableColumn* pColumn = _Columns[ i ];
  626. // Ignore PartailDeferred columns here. Retrieve data only when
  627. // requested by the client
  628. if ( pColumn->IsPartialDeferred() )
  629. {
  630. continue;
  631. }
  632. Win4Assert( pColumn->IsStatusStored() );
  633. // Some columns have special processing and don't need to go through
  634. // this loop.
  635. if ( pColumn->IsSpecial() )
  636. {
  637. pColumn->SetStatus( pThisRow, CTableColumn::StoreStatusOK );
  638. continue;
  639. }
  640. ULONG cbLength = cbBuf;
  641. VARTYPE vt = pColumn->GetStoredType();
  642. // it'd be a "special" column handled above if it were VT_EMPTY
  643. Win4Assert( VT_EMPTY != vt );
  644. GetValueResult eGvr = obj.GetPropertyValue( pColumn->PropId,
  645. pvarnt,
  646. &cbLength );
  647. CTableColumn::StoreStatus stat = CTableColumn::StoreStatusOK;
  648. switch ( eGvr )
  649. {
  650. case GVRSuccess:
  651. break;
  652. case GVRNotAvailable:
  653. pvarnt->vt = VT_EMPTY;
  654. stat = CTableColumn::StoreStatusNull;
  655. break;
  656. case GVRNotEnoughSpace:
  657. {
  658. tbDebugOut(( DEB_ITRACE,
  659. "variant data too large for propid %d\n",
  660. pColumn->PropId ));
  661. stat = CTableColumn::StoreStatusDeferred;
  662. if ( pColumn->IsLengthStored() )
  663. * (ULONG *) (pThisRow + pColumn->GetLengthOffset()) = cbLength;
  664. }
  665. break;
  666. case GVRSharingViolation:
  667. pvarnt->vt = VT_EMPTY;
  668. stat = CTableColumn::StoreStatusNull;
  669. break;
  670. default:
  671. //
  672. // There was an error while retrieving the column from the
  673. // retriever.
  674. //
  675. THROW( CException(CRetriever::NtStatusFromGVR(eGvr)) );
  676. break;
  677. }
  678. BYTE* pRowColDataBuf = pThisRow + pColumn->GetValueOffset();
  679. ULONG cbRowColDataBuf = pColumn->GetValueSize();
  680. RtlZeroMemory(pRowColDataBuf, cbRowColDataBuf);
  681. Win4Assert( pColumn->IsStatusStored() );
  682. if ( ( CTableColumn::StoreStatusOK == stat ) &&
  683. ( VT_EMPTY == pvarnt->vt ) )
  684. pColumn->SetStatus( pThisRow, CTableColumn::StoreStatusNull );
  685. else
  686. pColumn->SetStatus( pThisRow, stat );
  687. if ( CTableColumn::StoreStatusOK == stat )
  688. {
  689. if ( pColumn->IsLengthStored() )
  690. * (ULONG *) (pThisRow + pColumn->GetLengthOffset()) = cbLength;
  691. //
  692. // Store the property value in the table. The main cases
  693. // handled below are:
  694. //
  695. // 1. The column is compressed. In this case, the column
  696. // compressor will handle it.
  697. // 2. The column is stored as a variant. Just store the
  698. // value, as long as it's not too big.
  699. // 3. The column is stored as a data value, and the property
  700. // value is of the same type. Just store the data value.
  701. // 4. The column is stored as a data value, and the property
  702. // value is of a different type. In this case, attempt to
  703. // convert the value to another type and store it. Otherwise
  704. // fail.
  705. // BROKENCODE - failure here could be the wrong thing to do. Other
  706. // things that could be done include converting the column
  707. // (might be the correct thing to do if it's a range error
  708. // on a column which has been range compressed), or storing
  709. // the value as an exception (might be the correct thing to
  710. // do when it is a column which has been type compressed).
  711. //
  712. if (pColumn->IsCompressedCol())
  713. {
  714. pColumn->GetCompressor()->AddData(pvarnt,
  715. cbRowColDataBuf?
  716. (ULONG *)pRowColDataBuf: 0,
  717. eGvr);
  718. }
  719. else
  720. {
  721. DBLENGTH ulTemp;
  722. pvarnt->CopyOrCoerce( pRowColDataBuf,
  723. cbRowColDataBuf,
  724. vt,
  725. ulTemp,
  726. _DataAllocator );
  727. }
  728. }
  729. }
  730. } //_PopulateRow
  731. //+-------------------------------------------------------------------------
  732. //
  733. // Member: CTableWindow::PutRow, public
  734. //
  735. // Synopsis: Add a row to a large table
  736. //
  737. // Arguments: [obj] -- a pointer to an accessor which can
  738. // return object data
  739. //
  740. // Returns: FALSE -- Don't need progress indication
  741. //
  742. // Returns: Nothing
  743. //
  744. //--------------------------------------------------------------------------
  745. BOOL CTableWindow::PutRow( CRetriever& obj, CTableRowKey & currKey )
  746. {
  747. WORKID wid = obj.WorkId();
  748. //
  749. // NOTE: Even if in the initial fill, first check to see if the row
  750. // is already in the table. We may see the same work ID
  751. // multiple times due to links.
  752. //
  753. TBL_OFF obRow;
  754. ULONG iRowOrdinal;
  755. BOOL fExisting = _FindWorkId(wid, obRow, iRowOrdinal);
  756. // Win4Assert( !fExisting &&
  757. // "Modifications must be treated as deletions followed by additions" );
  758. if ( fExisting )
  759. {
  760. BYTE * pbOldRow = _DataAllocator.FixedPointer( obRow );
  761. if ( TBL_DATA_PENDING_DELETE != _RowStatus(pbOldRow) )
  762. {
  763. tbDebugOut(( DEB_WARN, "Not adding a linked object with wid 0x%X \n", wid ));
  764. return FALSE;
  765. }
  766. else
  767. {
  768. //
  769. // There can be a pending delete only if there is a watch
  770. // region.
  771. //
  772. Win4Assert( IsWatched() );
  773. }
  774. }
  775. BYTE * pThisRow = _RowAlloc();
  776. Win4Assert(pThisRow != NULL);
  777. _PopulateRow( obj, pThisRow, wid );
  778. TBL_OFF oTableRow = _DataAllocator.FixedOffset(pThisRow);
  779. CRowIndex & rowIndex = _GetInvisibleRowIndex();
  780. ULONG iRowPos = rowIndex.AddRow(oTableRow);
  781. tbDebugOut(( DEB_BOOKMARK,
  782. "CTableWindow::PutRow Add new row Wid 0x%X -- Segment 0x%X -- oTable 0x%X \n",
  783. wid, GetSegId(), oTableRow ));
  784. _SetRowStatus(pThisRow,TBL_DATA_ROWADDED);
  785. if ( !IsWatched() )
  786. {
  787. //
  788. // There are no notifications enabled for this window. So, we
  789. // should add the entry to the book mark mapping.
  790. //
  791. _BookMarkMap.AddReplaceBookMark( wid, oTableRow );
  792. }
  793. else
  794. {
  795. _xDeltaBookMarkMap->AddReplaceBookMark( wid, oTableRow );
  796. }
  797. if ( IsCategorized() )
  798. {
  799. CCategParams params = { wid, widInvalid, widInvalid,
  800. chaptInvalid, chaptInvalid,
  801. 0, 0 };
  802. if ( 0 != iRowPos )
  803. {
  804. BYTE * pb = (BYTE *) _DataAllocator.FixedPointer(
  805. rowIndex.GetRow( iRowPos - 1) );
  806. params.widPrev = RowWorkid( pb );
  807. params.catPrev = _RowChapter( pb );
  808. }
  809. if ( (iRowPos + 1) != rowIndex.RowCount() )
  810. {
  811. BYTE * pb = (BYTE *) _DataAllocator.FixedPointer(
  812. rowIndex.GetRow( iRowPos + 1) );
  813. params.widNext = RowWorkid( pb );
  814. params.catNext = _RowChapter( pb );
  815. }
  816. if ( ( chaptInvalid == params.catPrev ) ||
  817. ( params.catNext != params.catPrev ) )
  818. {
  819. // new row is not sandwiched between rows in same category,
  820. // so come up with positive column # where the new row
  821. // differs from its neighbors.
  822. if ( widInvalid != params.widPrev )
  823. params.icmpPrev = _CompareRows( rowIndex.GetRow( iRowPos ),
  824. rowIndex.GetRow( iRowPos - 1 ));
  825. if ( widInvalid != params.widNext )
  826. params.icmpNext = _CompareRows( rowIndex.GetRow( iRowPos + 1),
  827. rowIndex.GetRow( iRowPos ));
  828. }
  829. _SetChapter( pThisRow, LokCategorize( params ) );
  830. }
  831. // Update Low and High Keys, if necessary, based on the rowpos of the new row
  832. BOOL fReady = FALSE;
  833. if ( 0 == iRowPos )
  834. {
  835. // need to update lowkey
  836. currKey.MakeReady();
  837. fReady = TRUE;
  838. _lowKey = currKey;
  839. }
  840. if ( RowCount() - 1 == iRowPos )
  841. {
  842. // need to update highKey
  843. if ( !fReady )
  844. currKey.MakeReady();
  845. _highKey = currKey;
  846. }
  847. return FALSE; // no need for progress calculation
  848. }
  849. //+-------------------------------------------------------------------------
  850. //
  851. // Member: CTableWindow::_FindWorkId, public
  852. //
  853. // Synopsis: Find the row associated with a work ID
  854. //
  855. // Arguments: [wid] -- work ID to be found
  856. // [obRow] - set to the row offset from the base of the
  857. // row data if the workid was found
  858. // [riRowOrdinal] - set to the ordinal row number if the
  859. // workid was found (the row index ordinal)
  860. //
  861. // Returns: BOOL - TRUE if wid is represented in this window, FALSE
  862. // otherwise
  863. //
  864. // Notes:
  865. //
  866. //--------------------------------------------------------------------------
  867. BOOL CTableWindow::_FindWorkId(
  868. WORKID wid,
  869. TBL_OFF & obRow,
  870. ULONG & riRowOrdinal
  871. )
  872. {
  873. //
  874. // A linear search is made in the dynamic row index based on whether
  875. // an entry exists in the bookmark mapping or not. This will eliminate
  876. // searches in cases where the wid does not exist in the window.
  877. //
  878. BOOL fFound = FALSE;
  879. if ( !IsWatched() )
  880. {
  881. //
  882. // The notifications are not enabled. The "visible" bookmark mapping
  883. // is uptodate and has all the entries.
  884. //
  885. fFound = _BookMarkMap.FindBookMark( wid, obRow, riRowOrdinal );
  886. }
  887. else
  888. {
  889. BOOL fLookInDynRowIndex = FALSE;
  890. //
  891. // Since notifications are enabled, we must first look for the wid
  892. // in the "delta" bookmark mapping and then in the "visible" bookmark
  893. // mapping. If a "valid" bookmark mapping exists, then we must locate the
  894. // row ordinal in the "dynamic" row index.
  895. //
  896. if ( _xDeltaBookMarkMap->FindBookMark(wid, obRow) )
  897. {
  898. fLookInDynRowIndex = TRUE;
  899. }
  900. else if ( _BookMarkMap.FindBookMark(wid, obRow) )
  901. {
  902. //
  903. // If it is in the static book mark mapping, then only the
  904. // rows with the TBL_DATA_OKAY are valid for further look up.
  905. //
  906. BYTE * pbRow = (BYTE *) _DataAllocator.FixedPointer(obRow);
  907. fLookInDynRowIndex = TBL_DATA_OKAY == _RowStatus(pbRow);
  908. }
  909. if (fLookInDynRowIndex)
  910. fFound = _dynRowIndex.FindRow(obRow, riRowOrdinal);
  911. }
  912. #if 0
  913. //
  914. // This is an expensive test. Re-add it if there are bugs
  915. //
  916. if ( !fFound )
  917. {
  918. CRowIndex & srchRowIndex = _GetInvisibleRowIndex();
  919. for (unsigned i = 0; i < srchRowIndex.RowCount(); i++)
  920. {
  921. BYTE * pbRow = (BYTE *) _DataAllocator.FixedPointer(srchRowIndex.GetRow(i));
  922. Win4Assert(0 != pbRow);
  923. Win4Assert( _RowStatus(pbRow) != TBL_DATA_PENDING_DELETE &&
  924. _RowStatus(pbRow) != TBL_DATA_SOFT_DELETE );
  925. if (RowWorkid(pbRow) == wid)
  926. {
  927. Win4Assert( !"Must not be found" );
  928. obRow = srchRowIndex.GetRow(i);
  929. riRowOrdinal = i;
  930. return TRUE;
  931. }
  932. }
  933. }
  934. #endif
  935. return fFound;
  936. }
  937. //+---------------------------------------------------------------------------
  938. //
  939. // Function: CTableWindow::FindBookMark, private
  940. //
  941. // Synopsis: Locates the requested bookmark using the bookmark mapping.
  942. //
  943. // Arguments: [wid] -- WorkId to locate
  944. // [obRow] -- set to the row offset from the base of the
  945. // row data if the workid was found
  946. // [riRowOrdinal] -- set to the ordinal row number if the
  947. // workid was found (the row index ordinal)
  948. //
  949. // History: 11-30-94 srikants Created
  950. //
  951. // Notes: This method differs from the _FindWorkId in that this uses
  952. // the book mark mapping and _FindWorkId does not. When
  953. // notifications are enabled for this window, we add rows to the
  954. // "dynamic" row index but do not add the corresponding entry
  955. // to the book mark mapping. In that case, we cannot use the
  956. // BookMarkMap to locate the work id.
  957. //
  958. //----------------------------------------------------------------------------
  959. BOOL CTableWindow::FindBookMark(
  960. WORKID wid,
  961. TBL_OFF & obRow,
  962. ULONG & riRowOrdinal
  963. )
  964. {
  965. BOOL fFound = FALSE;
  966. if ( _visibleRowIndex.RowCount() > 0 )
  967. {
  968. if ( WORKID_TBLFIRST == wid )
  969. {
  970. riRowOrdinal = 0;
  971. obRow = _visibleRowIndex.GetRow(riRowOrdinal);
  972. fFound = TRUE;
  973. }
  974. else if ( WORKID_TBLLAST == wid )
  975. {
  976. riRowOrdinal = _visibleRowIndex.RowCount()-1;
  977. obRow = _visibleRowIndex.GetRow(riRowOrdinal);
  978. fFound = TRUE;
  979. }
  980. else
  981. {
  982. fFound = _BookMarkMap.FindBookMark( wid, obRow, riRowOrdinal );
  983. }
  984. }
  985. return fFound;
  986. }
  987. //+-------------------------------------------------------------------------
  988. //
  989. // Member: CTableWindow::IsRowInSegment, inline
  990. //
  991. // Synopsis: Determine if a row for some work ID exists in the table.
  992. //
  993. // Arguments: [wid] -- work ID to be found
  994. //
  995. // Returns: BOOL - TRUE if wid is represented in this window, FALSE
  996. // otherwise
  997. //
  998. // Notes:
  999. //
  1000. //--------------------------------------------------------------------------
  1001. BOOL CTableWindow::IsRowInSegment(WORKID wid)
  1002. {
  1003. TBL_OFF iRowOffset;
  1004. ULONG iRow;
  1005. return _FindWorkId(wid, iRowOffset, iRow);
  1006. }
  1007. //+-------------------------------------------------------------------------
  1008. //
  1009. // Member: CTableWindow::GetRows
  1010. //
  1011. // Synopsis: Return a set of row data to the caller
  1012. //
  1013. // Arguments: [hRegion] - region to be expanded
  1014. // [widStart] - WORKID identifying first row to be
  1015. // transferred. If WORKID_TBLBEFOREFIRST or
  1016. // WORKID_TBLFIRST is used, the transfer will
  1017. // start at the first row in the segment.
  1018. // [chapt] - Chapter from which to fetch rows (if chaptered)
  1019. // [rOutColumns] - a CTableColumnSet that describes the
  1020. // output format of the data table.
  1021. // [rGetParams] - an CGetRowsParams structure which
  1022. // describes how many rows are to be fetched and
  1023. // other parameters of the operation.
  1024. // [rwidLastRowTransferred] - on return, the work ID of
  1025. // the next row to be transferred from this table.
  1026. // Can be used as widStart on next call.
  1027. //
  1028. // Returns: SCODE - status of the operation. DB_S_ENDOFROWSET if
  1029. // widStart is WORKID_TBLAFTERLAST at start of
  1030. // transfer, or if rwidLastRowTransferred is
  1031. // the last row in the table segment at the end
  1032. // of the transfer, and not all requested rows were
  1033. // transferred.
  1034. // DB_S_BLOCKLIMITEDROWS if insufficient space is
  1035. // available in the pVarAllocator.
  1036. //
  1037. // Notes: Extends the watch region (if handle non-zero)
  1038. //
  1039. //--------------------------------------------------------------------------
  1040. SCODE CTableWindow::GetRows(
  1041. HWATCHREGION hRegion,
  1042. WORKID widStart,
  1043. CI_TBL_CHAPT chapt,
  1044. ULONG & cRowsToGet,
  1045. CTableColumnSet const & rOutColumns,
  1046. CGetRowsParams & rGetParams,
  1047. WORKID& rwidLastRowTransferred
  1048. ) {
  1049. tbDebugOut(( DEB_BOOKMARK, "CTableWindow::GetRows called with starting wid 0x%X\n", widStart ));
  1050. if (widStart == WORKID_TBLAFTERLAST)
  1051. {
  1052. rwidLastRowTransferred = WORKID_TBLAFTERLAST;
  1053. tbDebugOut(( DEB_BOOKMARK, "CTableWindow::GetRows returning with 0x%X\n",
  1054. DB_S_ENDOFROWSET ));
  1055. return DB_S_ENDOFROWSET;
  1056. }
  1057. if (widStart == widInvalid)
  1058. {
  1059. tbDebugOut(( DEB_BOOKMARK, "CTableWindow::GetRows returning with 0x%X\n", E_FAIL ));
  1060. return E_FAIL; // maybe the wid got deleted???
  1061. }
  1062. ULONG iRowIndex = 0; // Starting row as an index in the row index
  1063. CRowIndex * pRowIndex = _BookMarkMap.GetRowIndex();
  1064. Win4Assert( pRowIndex == &_visibleRowIndex );
  1065. if ( widStart == WORKID_TBLLAST )
  1066. iRowIndex = pRowIndex->RowCount() - 1;
  1067. if (widStart != WORKID_TBLFIRST
  1068. && widStart != WORKID_TBLBEFOREFIRST
  1069. && widStart != WORKID_TBLLAST )
  1070. {
  1071. TBL_OFF iRowOffset = 0; // Starting row as an offset into row data
  1072. BOOL fFound = FindBookMark(widStart, iRowOffset, iRowIndex);
  1073. if (!fFound)
  1074. return E_FAIL;
  1075. }
  1076. else if ( IsCategorized() && DB_NULL_HCHAPTER != chapt )
  1077. {
  1078. // turn special wids (first/last) into real wids in the chapter
  1079. if ( !rGetParams.GetFwdFetch() )
  1080. Win4Assert( !"Backwards fetch not yet implemented for categorized rowsets" );
  1081. TBL_OFF iRowOffset = 0;
  1082. if ( !_FindWorkId( GetCategorizer()->GetFirstWorkid( chapt ),
  1083. iRowOffset,
  1084. iRowIndex ) )
  1085. {
  1086. // must be a continuation of getrows from a previous window
  1087. iRowIndex = 0;
  1088. }
  1089. }
  1090. SCODE scResult = S_OK;
  1091. //
  1092. // iRowIndex is a ulong, and so after row 0 is fetched (in
  1093. // backwards fetch) we set fBwdRowsExhausted to true, instead
  1094. // decrementing 0, which is the value of iRowIndex.
  1095. // fBwdRowsExhausted is initialy true if there are 0 rows in this
  1096. // segment.
  1097. //
  1098. BOOL fBwdRowsExhausted = pRowIndex->RowCount() == 0;
  1099. //
  1100. // This flag is set to true, if rOutColumns contains at least one
  1101. // partially deferred column
  1102. //
  1103. BOOL fPartialDeferredCols = FALSE;
  1104. Win4Assert( 0 != pRowIndex );
  1105. TRY
  1106. {
  1107. const COUNT_CACHED_TABLE_COLS = 10; // Size of cached column array
  1108. CTableColumn *apTableCol[COUNT_CACHED_TABLE_COLS];
  1109. CTableColumn **pTableCols = apTableCol;
  1110. XArray<CTableColumn *> xaTableCol;
  1111. if ( rOutColumns.Count() > COUNT_CACHED_TABLE_COLS )
  1112. {
  1113. xaTableCol.Init( rOutColumns.Count() );
  1114. pTableCols = xaTableCol.GetPointer();
  1115. }
  1116. for (unsigned i=0; i < rOutColumns.Count(); i++)
  1117. {
  1118. CTableColumn const & outColumn = *rOutColumns[ i ];
  1119. BOOL fFound = FALSE;
  1120. pTableCols[i] = _Columns.Find( outColumn.PropId, fFound );
  1121. Win4Assert(fFound == TRUE);
  1122. fPartialDeferredCols = ( fPartialDeferredCols ||
  1123. pTableCols[i]->IsPartialDeferred() );
  1124. }
  1125. //
  1126. // FRowsRemaining tests whether there are rows remaining.
  1127. // The test differs for forward and backwards fetch.
  1128. //
  1129. BOOL fRowsRemaining;
  1130. if ( rGetParams.GetFwdFetch() )
  1131. fRowsRemaining = iRowIndex < pRowIndex->RowCount();
  1132. else
  1133. fRowsRemaining = !fBwdRowsExhausted;
  1134. //
  1135. // Set up the cursor in case we have partial deferred column(s)
  1136. //
  1137. BOOL fAbort = FALSE;
  1138. if ( fPartialDeferredCols && _xCursor.IsNull() )
  1139. {
  1140. _xCursor.Set( _QueryExecute.GetSingletonCursor( fAbort ) );
  1141. Win4Assert( _xCursor.GetPointer() );
  1142. }
  1143. while ( 0 != cRowsToGet && fRowsRemaining )
  1144. {
  1145. BYTE *pRow = (BYTE *)
  1146. _DataAllocator.FixedPointer(pRowIndex->GetRow(iRowIndex));
  1147. // If we've moved on to another chapter, stop retrieving rows
  1148. if ( IsCategorized() &&
  1149. DB_NULL_HCHAPTER != chapt &&
  1150. (_RowChapter(pRow) != chapt) )
  1151. {
  1152. scResult = DB_S_ENDOFROWSET;
  1153. break;
  1154. }
  1155. BYTE* pbOutRow = (BYTE *)rGetParams.GetRowBuffer();
  1156. //
  1157. // Update RowId in xCursor if we have partial deferred column(s)
  1158. //
  1159. if ( fPartialDeferredCols )
  1160. {
  1161. _xCursor->SetCurrentWorkId( RowWorkid( pRow ) );
  1162. Win4Assert( _xCursor->WorkId() != widInvalid );
  1163. }
  1164. _CopyColumnData( pRow,
  1165. pbOutRow,
  1166. pTableCols,
  1167. rOutColumns,
  1168. rGetParams.GetVarAllocator(),
  1169. _xCursor.GetPointer() );
  1170. //
  1171. // We've transferred a row. Update pointers and counters.
  1172. //
  1173. rwidLastRowTransferred = RowWorkid((BYTE *)_DataAllocator.
  1174. FixedPointer(pRowIndex->GetRow(iRowIndex)));
  1175. if ( rGetParams.GetFwdFetch() )
  1176. iRowIndex++;
  1177. else
  1178. {
  1179. if ( iRowIndex == 0 )
  1180. fBwdRowsExhausted = TRUE;
  1181. else
  1182. iRowIndex--;
  1183. }
  1184. rGetParams.IncrementRowCount();
  1185. cRowsToGet--;
  1186. if ( rGetParams.GetFwdFetch() )
  1187. fRowsRemaining = iRowIndex < pRowIndex->RowCount();
  1188. else
  1189. fRowsRemaining = !fBwdRowsExhausted;
  1190. }
  1191. }
  1192. CATCH( CException, e )
  1193. {
  1194. scResult = e.GetErrorCode();
  1195. if ( STATUS_BUFFER_TOO_SMALL == scResult &&
  1196. rGetParams.RowsTransferred() > 0 )
  1197. scResult = DB_S_BLOCKLIMITEDROWS;
  1198. }
  1199. END_CATCH
  1200. if (! _xCursor.IsNull() )
  1201. _xCursor->Quiesce( );
  1202. if (iRowIndex >= pRowIndex->RowCount() || fBwdRowsExhausted )
  1203. {
  1204. Win4Assert(scResult != DB_S_BLOCKLIMITEDROWS);
  1205. tbDebugOut(( DEB_BOOKMARK, "CTableWindow::GetRows End of table window\n" ));
  1206. return DB_S_ENDOFROWSET;
  1207. }
  1208. else
  1209. {
  1210. tbDebugOut(( DEB_BOOKMARK, "CTableWindow::GetRows next wid 0x%X\n", rwidLastRowTransferred ));
  1211. return scResult;
  1212. }
  1213. } //GetRows
  1214. //+---------------------------------------------------------------------------
  1215. //
  1216. // Member: CTableWindow::_CopyColumnData, private
  1217. //
  1218. // Synopsis: Goes thru the list of output columns and transfers
  1219. // data to pbOutRow for them. The input data either comes
  1220. // from pbSrcRow or in case of partially deferred column
  1221. // we retrive it from the CRetriever object
  1222. //
  1223. // Arguments: [pSrcRow] -- the src row
  1224. // [pThisRow] -- where to put the data
  1225. // [pTableCols] -- table(input) column set corr. to output columns
  1226. // [rOutColumns] -- output column set
  1227. // [rDstPool] -- destination variable data allocator
  1228. // [obj] -- source of data
  1229. //
  1230. // Returns: Nothing
  1231. //
  1232. //--------------------------------------------------------------------------
  1233. void CTableWindow::_CopyColumnData(
  1234. BYTE* pbSrcRow,
  1235. BYTE* pbOutRow,
  1236. CTableColumn **pTableCols,
  1237. CTableColumnSet const& rOutColumns,
  1238. PVarAllocator& rDstPool,
  1239. CRetriever* obj /* = NULL */ )
  1240. {
  1241. //
  1242. // Temporary buffer for column data. If it doesn't fit in this
  1243. // buffer, defer the load. The buffer is owned by CLargeTable.
  1244. // and is only accessed under the lock taken in CLargeTable::GetRowsAt
  1245. //
  1246. XUseSharedBuffer xSharedBuf(_sharedBuf);
  1247. CTableVariant* pvarnt = (CTableVariant *) xSharedBuf.LokGetBuffer();
  1248. unsigned cbBuf = xSharedBuf.LokGetSize();
  1249. for (unsigned i=0; i < rOutColumns.Count(); i++)
  1250. {
  1251. CTableColumn * pColumn = pTableCols[i];
  1252. Win4Assert(pColumn);
  1253. CTableColumn* pOutColumn = rOutColumns[ i ];
  1254. Win4Assert(pOutColumn);
  1255. if ( !pColumn->IsPartialDeferred() )
  1256. {
  1257. pColumn->CopyColumnData( pbOutRow,
  1258. *pOutColumn,
  1259. rDstPool,
  1260. pbSrcRow,
  1261. _DataAllocator );
  1262. continue;
  1263. }
  1264. ULONG cbLength = cbBuf;
  1265. VARTYPE vt = pOutColumn->GetStoredType();
  1266. Win4Assert( obj );
  1267. GetValueResult eGvr = obj->GetPropertyValue( pColumn->PropId,
  1268. pvarnt,
  1269. &cbLength );
  1270. CTableColumn::StoreStatus stat = CTableColumn::StoreStatusOK;
  1271. switch ( eGvr )
  1272. {
  1273. case GVRSuccess:
  1274. break;
  1275. case GVRNotAvailable:
  1276. pvarnt->vt = VT_EMPTY;
  1277. stat = CTableColumn::StoreStatusNull;
  1278. break;
  1279. case GVRNotEnoughSpace:
  1280. {
  1281. tbDebugOut(( DEB_ITRACE,
  1282. "variant data too large for propid %d\n",
  1283. pColumn->PropId ));
  1284. stat = CTableColumn::StoreStatusDeferred;
  1285. if ( pOutColumn->IsLengthStored() )
  1286. * (ULONG *) (pbOutRow + pOutColumn->GetLengthOffset()) = cbLength;
  1287. }
  1288. break;
  1289. case GVRSharingViolation:
  1290. pvarnt->vt = VT_EMPTY;
  1291. stat = CTableColumn::StoreStatusNull;
  1292. break;
  1293. default:
  1294. //
  1295. // There was an error while retrieving the column from the
  1296. // retriever.
  1297. THROW( CException(CRetriever::NtStatusFromGVR(eGvr)) );
  1298. break;
  1299. }
  1300. BYTE* pRowColDataBuf = pbOutRow + pOutColumn->GetValueOffset();
  1301. ULONG cbRowColDataBuf = pOutColumn->GetValueSize();
  1302. RtlZeroMemory(pRowColDataBuf, cbRowColDataBuf);
  1303. Win4Assert( pOutColumn->IsStatusStored() );
  1304. if ( ( CTableColumn::StoreStatusOK == stat ) &&
  1305. ( VT_EMPTY == pvarnt->vt ) )
  1306. pOutColumn->SetStatus( pbOutRow, CTableColumn::StoreStatusNull );
  1307. else
  1308. pOutColumn->SetStatus( pbOutRow, stat );
  1309. if ( CTableColumn::StoreStatusOK == stat )
  1310. {
  1311. if ( pOutColumn->IsLengthStored() )
  1312. * (ULONG *) (pbOutRow + pOutColumn->GetLengthOffset()) = cbLength;
  1313. if (pOutColumn->IsCompressedCol())
  1314. {
  1315. pOutColumn->GetCompressor()->AddData(pvarnt,
  1316. cbRowColDataBuf?
  1317. (ULONG *)pRowColDataBuf: 0,
  1318. eGvr);
  1319. }
  1320. else
  1321. {
  1322. DBLENGTH ulTemp;
  1323. pvarnt->CopyOrCoerce( pRowColDataBuf,
  1324. cbRowColDataBuf,
  1325. vt,
  1326. ulTemp,
  1327. rDstPool );
  1328. }
  1329. }
  1330. }
  1331. }
  1332. //+-------------------------------------------------------------------------
  1333. //
  1334. // Member: CTableWindow::RemoveRow, public
  1335. //
  1336. // Synopsis: Removes a row from the table, if it exists.
  1337. //
  1338. // Arguments: [varUnique] -- Variant that uniquely identifies the row.
  1339. // [widNext] -- Returns workid of row immediately after
  1340. // deleted row, used for categorization.
  1341. // [chapt] -- Returns chapter of deleted row.
  1342. //
  1343. // Returns: TRUE if the row existed or FALSE otherwise
  1344. //
  1345. // History: 24 Oct 1994 dlee Created
  1346. //
  1347. // BROKENCODE/NEWFEATURE: update min/max wid? This code won't be called...
  1348. //
  1349. //--------------------------------------------------------------------------
  1350. BOOL CTableWindow::RemoveRow(
  1351. PROPVARIANT const & varUnique,
  1352. WORKID & widNext,
  1353. CI_TBL_CHAPT & chapt )
  1354. {
  1355. Win4Assert(varUnique.vt == VT_I4);
  1356. WORKID wid = (WORKID) varUnique.lVal;
  1357. TBL_OFF obRow;
  1358. ULONG iRowOrdinal;
  1359. BOOL fExisting = _FindWorkId(wid, obRow, iRowOrdinal);
  1360. if (fExisting)
  1361. {
  1362. BOOL fFirstRow = (0 == iRowOrdinal);
  1363. BOOL fLastRow = (RowCount() - 1 == iRowOrdinal);
  1364. CRowIndex & srchRowIndex = _GetInvisibleRowIndex();
  1365. BYTE * pRow = (BYTE *) _DataAllocator.FixedPointer(obRow);
  1366. if ( IsCategorized() )
  1367. {
  1368. chapt = _RowChapter( pRow );
  1369. if ( iRowOrdinal < ( srchRowIndex.RowCount() - 1 ) )
  1370. widNext = RowWorkid( _DataAllocator.FixedPointer(
  1371. srchRowIndex.GetRow(iRowOrdinal + 1)));
  1372. else
  1373. widNext = widInvalid;
  1374. }
  1375. const ULONG rowStatus = _RowStatus(pRow);
  1376. if ( TBL_DATA_ROWADDED == rowStatus || !IsWatched() )
  1377. {
  1378. //
  1379. // This row has only been added but not notified to the
  1380. // user. So, we can go ahead and do a hard delete.
  1381. //
  1382. BOOL fFound = FALSE;
  1383. if ( IsWatched() )
  1384. {
  1385. fFound = _xDeltaBookMarkMap->DeleteBookMark( wid );
  1386. }
  1387. else
  1388. {
  1389. Win4Assert( _xDeltaBookMarkMap.IsNull() ||
  1390. !_xDeltaBookMarkMap->IsBookMarkPresent(wid) );
  1391. Win4Assert( 0 == _dynRowIndex.RowCount() );
  1392. fFound = _BookMarkMap.DeleteBookMark( wid );
  1393. }
  1394. Win4Assert( fFound && "BookMark to be deleted not found" );
  1395. Win4Assert( obRow == srchRowIndex.GetRow(iRowOrdinal) );
  1396. srchRowIndex.DeleteRow(iRowOrdinal);
  1397. _cRowsHardDeleted++;
  1398. _SetRowStatus(pRow,TBL_DATA_HARD_DELETE);
  1399. tbDebugOut(( DEB_WATCH,
  1400. "Hard Deleting Workid 0x%X oTable %#p oIndex 0x%X\n",
  1401. wid, obRow, iRowOrdinal ));
  1402. // Update low and high keys
  1403. if ( fFirstRow && fLastRow )
  1404. {
  1405. // this means now RowCount == 0
  1406. // we should probably delete this segment
  1407. }
  1408. else if ( fFirstRow )
  1409. {
  1410. // the first row got deleted, therefore update lowKey
  1411. GetSortKey( 0, _lowKey );
  1412. }
  1413. else if ( fLastRow )
  1414. {
  1415. // the last row got deleted, therefore update highKey
  1416. GetSortKey( (ULONG) ( RowCount() - 1 ), _highKey );
  1417. }
  1418. }
  1419. else
  1420. {
  1421. //
  1422. // The existence of this row is known to the user. It is left in
  1423. // a pending delete state until a refresh is done.
  1424. // Updation of _highKey and _lowKey is also done at that time
  1425. Win4Assert( _BookMarkMap.IsBookMarkPresent(wid) );
  1426. Win4Assert( _xDeltaBookMarkMap.IsNull() ||
  1427. !_xDeltaBookMarkMap->IsBookMarkPresent(wid) );
  1428. _cPendingDeletes++;
  1429. _SetRowStatus(pRow,TBL_DATA_PENDING_DELETE);
  1430. tbDebugOut(( DEB_WATCH,
  1431. "Soft Deleting Workid 0x%X oTable %#p oIndex 0x%X\n",
  1432. wid, obRow, iRowOrdinal ));
  1433. }
  1434. }
  1435. return fExisting;
  1436. }
  1437. //+---------------------------------------------------------------------------
  1438. //
  1439. // Member: CTableWindow::IsPendingDelete
  1440. //
  1441. // Synopsis:
  1442. //
  1443. // Arguments: [wid] -
  1444. //
  1445. // Returns:
  1446. //
  1447. // Modifies:
  1448. //
  1449. // History: 8-01-95 srikants Created
  1450. //
  1451. // Notes:
  1452. //
  1453. //----------------------------------------------------------------------------
  1454. BOOL CTableWindow::IsPendingDelete( WORKID wid )
  1455. {
  1456. TBL_OFF iRowOffset;
  1457. BOOL fFound = _BookMarkMap.FindBookMark( wid, iRowOffset );
  1458. if ( fFound )
  1459. {
  1460. BYTE * pbRow = (BYTE *) _DataAllocator.FixedPointer(iRowOffset);
  1461. return TBL_DATA_PENDING_DELETE == _RowStatus(pbRow) ;
  1462. }
  1463. return FALSE;
  1464. }
  1465. //+-------------------------------------------------------------------------
  1466. //
  1467. // Member: CTableWindow::CompareRange, public
  1468. //
  1469. // Synopsis: Checks to see how a given potential row object would fit
  1470. // in this window based on the current sort criteria.
  1471. //
  1472. // Arguments: [obj] -- accessor to the potential row object
  1473. //
  1474. // Returns: -1 if < min item
  1475. // 0 if (<= max and >= min) or no items in window
  1476. // 1 if > max item
  1477. //
  1478. // History: 2 Sep 1994 dlee Created
  1479. //
  1480. //--------------------------------------------------------------------------
  1481. void
  1482. CTableWindow::CompareRange( CRetriever &obj, CCompareResult & rslt )
  1483. {
  1484. //
  1485. // If there is a comparator and any rows for comparison
  1486. //
  1487. CRowIndex & srchRowIndex = _GetInvisibleRowIndex();
  1488. if ((0 != _RowCompare.GetPointer()) &&
  1489. (0 != srchRowIndex.RowCount()))
  1490. {
  1491. int iComp = _RowCompare->CompareObject(obj,srchRowIndex.GetRow(0));
  1492. if (iComp > 0)
  1493. {
  1494. //
  1495. // Compare to the maximum row
  1496. // If > min and < max, it belongs in the range
  1497. //
  1498. iComp = _RowCompare->CompareObject(obj,
  1499. srchRowIndex.GetRow(srchRowIndex.RowCount()-1));
  1500. if (iComp < 0)
  1501. iComp = 0;
  1502. }
  1503. rslt.Set( iComp );
  1504. }
  1505. else
  1506. {
  1507. rslt.Set( CCompareResult::eUnknown );
  1508. }
  1509. } //CompareRange
  1510. //+-------------------------------------------------------------------------
  1511. //
  1512. // Member: CTableWindow::_InitSortComparators, private
  1513. //
  1514. // Synopsis: Establishes the sort order used by the table.
  1515. //
  1516. // History: 19 Jan 1995 dlee Created from old SetSortOrderCode
  1517. //
  1518. //--------------------------------------------------------------------------
  1519. void CTableWindow::_InitSortComparators()
  1520. {
  1521. Win4Assert( 0 == _RowCompare.GetPointer() &&
  1522. 0 == _QuickRowCompare.GetPointer() );
  1523. Win4Assert( 0 != _pSortSet->Count() );
  1524. // Special-case quick comparators for one or two comparisons
  1525. // of basic types.
  1526. if (_pSortSet->Count() == 1)
  1527. {
  1528. SSortKey Key = _pSortSet->Get(0);
  1529. BOOL fFound = FALSE;
  1530. CTableColumn* pColumn = _Columns.Find(Key.pidColumn,fFound);
  1531. Win4Assert(fFound);
  1532. //
  1533. // If inline, not compressed, and not a vector, it is a
  1534. // candidate for a faster comparison.
  1535. //
  1536. // pidWorkid can be compressed and stored in the row for
  1537. // the downlevel case.
  1538. if ((pColumn->PropId == pidWorkId ||
  1539. ! pColumn->IsCompressedCol()) &&
  1540. (0 == (pColumn->GetStoredType() & VT_VECTOR)))
  1541. {
  1542. if (pColumn->GetStoredType() == VT_I8 ||
  1543. pColumn->GetStoredType() == VT_FILETIME)
  1544. _QuickRowCompare.Set( new TRowCompare<LONGLONG>
  1545. (_DataAllocator,
  1546. pColumn->GetValueOffset(),
  1547. Key.dwOrder ));
  1548. else if (pColumn->GetStoredType() == VT_I4)
  1549. _QuickRowCompare.Set( new TRowCompare<LONG>
  1550. (_DataAllocator,
  1551. pColumn->GetValueOffset(),
  1552. Key.dwOrder ));
  1553. }
  1554. }
  1555. else if (_pSortSet->Count() == 2)
  1556. {
  1557. SSortKey Key1 = _pSortSet->Get(0);
  1558. SSortKey Key2 = _pSortSet->Get(1);
  1559. BOOL fFound = FALSE;
  1560. CTableColumn* pColumn1 = _Columns.Find(Key1.pidColumn,fFound);
  1561. Win4Assert(fFound);
  1562. CTableColumn* pColumn2 = _Columns.Find(Key2.pidColumn,fFound);
  1563. Win4Assert(fFound);
  1564. //
  1565. // If inline, not compressed, and not a vector, it is a
  1566. // candidate for a faster comparison.
  1567. //
  1568. // pidWorkid can be compressed and stored in the row for
  1569. // the downlevel case.
  1570. if ((! pColumn1->IsCompressedCol()) &&
  1571. (0 == (pColumn1->GetStoredType() & VT_VECTOR)) &&
  1572. (! pColumn2->IsCompressedCol()
  1573. || pColumn2->PropId == pidWorkId
  1574. ) &&
  1575. (0 == (pColumn2->GetStoredType() & VT_VECTOR)))
  1576. {
  1577. if (((pColumn1->GetStoredType() == VT_I8) ||
  1578. (pColumn1->GetStoredType() == VT_FILETIME)) &&
  1579. (pColumn2->GetStoredType() == VT_I4))
  1580. _QuickRowCompare.Set( new TRowCompareTwo<LONGLONG,LONG>
  1581. (_DataAllocator,
  1582. pColumn1->GetValueOffset(),
  1583. Key1.dwOrder,
  1584. pColumn2->GetValueOffset(),
  1585. Key2.dwOrder ));
  1586. else if ((pColumn1->GetStoredType() == VT_I4) &&
  1587. (pColumn2->GetStoredType() == VT_I4))
  1588. _QuickRowCompare.Set( new TRowCompareTwo<LONG,LONG>
  1589. (_DataAllocator,
  1590. pColumn1->GetValueOffset(),
  1591. Key1.dwOrder,
  1592. pColumn2->GetValueOffset(),
  1593. Key2.dwOrder ));
  1594. else if ((pColumn1->GetStoredType() == VT_UI4) &&
  1595. (pColumn2->GetStoredType() == VT_I4))
  1596. _QuickRowCompare.Set( new TRowCompareTwo<ULONG,LONG>
  1597. (_DataAllocator,
  1598. pColumn1->GetValueOffset(),
  1599. Key1.dwOrder,
  1600. pColumn2->GetValueOffset(),
  1601. Key2.dwOrder ));
  1602. else if ((pColumn1->GetStoredType() == VT_LPWSTR) &&
  1603. (pColumn2->GetStoredType() == VT_I4))
  1604. _QuickRowCompare.Set( new TRowCompareStringPlus<LONG>
  1605. (_DataAllocator,
  1606. pColumn1->GetValueOffset(),
  1607. Key1.dwOrder,
  1608. pColumn2->GetValueOffset(),
  1609. Key2.dwOrder ));
  1610. }
  1611. }
  1612. //
  1613. // Make the default comparator
  1614. //
  1615. _RowCompare.Set( new CRowCompareVariant(*this) );
  1616. //
  1617. // Use a faster comparator if available, or just use the default
  1618. //
  1619. _dynRowIndex.SetComparator((0 != _QuickRowCompare.GetPointer()) ?
  1620. _QuickRowCompare.GetPointer() : _RowCompare.GetPointer());
  1621. _visibleRowIndex.SetComparator((0 != _QuickRowCompare.GetPointer()) ?
  1622. _QuickRowCompare.GetPointer() : _RowCompare.GetPointer());
  1623. } //_InitSortComparators
  1624. //+-------------------------------------------------------------------------
  1625. //
  1626. // Member: CTableWindow::SortOrder, private
  1627. //
  1628. // Synopsis: Only here because of class hierarchy artifact.
  1629. //
  1630. // Returns: CSortSet & -- sort set from parent object.
  1631. //
  1632. // History: 24 Oct 1994 dlee Created
  1633. //
  1634. //--------------------------------------------------------------------------
  1635. CSortSet const & CTableWindow::SortOrder()
  1636. {
  1637. return * _pSortSet;
  1638. } //SortOrder
  1639. //+---------------------------------------------------------------------------
  1640. //
  1641. // Member: CTableWindow::_ReconcileRowIndexes
  1642. //
  1643. // Synopsis: Reconciles the source and destination row indexes. At the end
  1644. // of it both the source and destination will be identical. Also,
  1645. // all the deleted rows would have been removed from the row
  1646. // indexes.
  1647. //
  1648. // Arguments: [src] -
  1649. // [dst] -
  1650. //
  1651. // History: 7-31-95 srikants Created
  1652. //
  1653. // Notes:
  1654. //
  1655. //----------------------------------------------------------------------------
  1656. void CTableWindow::_ReconcileRowIndexes( CRowIndex & dst , CRowIndex & src )
  1657. {
  1658. ULONG cRowsInSrc = src.RowCount();
  1659. dst.ResizeAndInit( cRowsInSrc );
  1660. for ( unsigned i = 0; i < cRowsInSrc ; i++ )
  1661. {
  1662. TBL_OFF oRow = src.GetRow(i);
  1663. BYTE * pbRow = _GetRow( oRow );
  1664. const ULONG rowStatus = _RowStatus( pbRow );
  1665. //
  1666. // By the time reconcile is called, all pending deletes must be
  1667. // converted to hard deletes.
  1668. //
  1669. Win4Assert( TBL_DATA_PENDING_DELETE != rowStatus );
  1670. if ( TBL_DATA_OKAY == rowStatus )
  1671. {
  1672. dst.AppendRow( oRow );
  1673. }
  1674. else
  1675. {
  1676. tbDebugOut(( DEB_BOOKMARK,
  1677. "Not Copying Row 0x%X because of status 0x%X\n",
  1678. oRow, rowStatus ));
  1679. }
  1680. }
  1681. src.SyncUp( dst );
  1682. // Update Low and High Keys
  1683. if ( dst.RowCount() )
  1684. {
  1685. GetSortKey( 0, _lowKey );
  1686. GetSortKey( dst.RowCount() - 1, _highKey );
  1687. }
  1688. }
  1689. //+---------------------------------------------------------------------------
  1690. //
  1691. // Member: CTableWindow::_CleanupAndReconcileRowIndexes
  1692. //
  1693. // Synopsis:
  1694. //
  1695. // Arguments: [fCreatingWatch] -
  1696. // [pScript] -
  1697. //
  1698. // Returns:
  1699. //
  1700. // Modifies:
  1701. //
  1702. // History: 7-27-95 srikants Created
  1703. //
  1704. // Notes:
  1705. //
  1706. //----------------------------------------------------------------------------
  1707. void CTableWindow::_CleanupAndReconcileRowIndexes( BOOL fCreatingWatch )
  1708. {
  1709. Win4Assert( !_fSplitInProgress );
  1710. //
  1711. // First see how many rows have notification information
  1712. //
  1713. ULONG cAllocatedRows = _AllocatedRowCount();
  1714. BYTE *pbRow = (BYTE *) _DataAllocator.FirstRow();
  1715. ULONG cAdd = 0, cDelete = 0;
  1716. for (unsigned i = 0; i < cAllocatedRows; i++, pbRow += _cbRowSize)
  1717. {
  1718. switch (_RowStatus(pbRow))
  1719. {
  1720. case TBL_DATA_ROWADDED :
  1721. cAdd++;
  1722. break;
  1723. case TBL_DATA_PENDING_DELETE :
  1724. cDelete++;
  1725. break;
  1726. }
  1727. }
  1728. //
  1729. // Record the notification info
  1730. //
  1731. ULONG cChanges = cAdd + cDelete;
  1732. ULONG iChange = 0;
  1733. for (i = 0, pbRow = (BYTE *) _DataAllocator.FirstRow();
  1734. iChange < cChanges && i < cAllocatedRows;
  1735. i++, pbRow += _cbRowSize)
  1736. {
  1737. ULONG currStatus = _RowStatus(pbRow);
  1738. switch (currStatus)
  1739. {
  1740. case TBL_DATA_ROWADDED :
  1741. //
  1742. // Update the book mark mapping to reflect the new row if
  1743. // notifications were enabled before this.
  1744. //
  1745. if ( !fCreatingWatch )
  1746. {
  1747. _BookMarkMap.AddReplaceBookMark( RowWorkid(pbRow),
  1748. _DataAllocator.FixedOffset(pbRow) );
  1749. }
  1750. _SetRowStatus(pbRow, TBL_DATA_OKAY);
  1751. tbDebugOut(( DEB_WATCH,
  1752. "Notify:Add Wid 0x%X TblRow0x%X\n",
  1753. RowWorkid(pbRow), _DataAllocator.FixedOffset(pbRow) ));
  1754. iChange++;
  1755. break;
  1756. case TBL_DATA_PENDING_DELETE :
  1757. Win4Assert( !fCreatingWatch );
  1758. //
  1759. // Mark the row so that it is considered a hard delete and
  1760. // also remove it from the bookmark mapping.
  1761. //
  1762. _SetRowStatus(pbRow,TBL_DATA_HARD_DELETE);
  1763. _BookMarkMap.DeleteBookMark( RowWorkid(pbRow) );
  1764. _cPendingDeletes--;
  1765. _cRowsHardDeleted++;
  1766. tbDebugOut(( DEB_WATCH,
  1767. "Notify:Delete Wid 0x%X TblRow0x%X\n",
  1768. RowWorkid(pbRow), _DataAllocator.FixedOffset(pbRow) ));
  1769. iChange++;
  1770. break;
  1771. }
  1772. #if DBG==1
  1773. //
  1774. // This is a very expensive test. Turn it off once code stabilizes.
  1775. //
  1776. {
  1777. //
  1778. // If any row in the tablewindow has a stats of TBL_DATA_OKAY, it
  1779. // must have an entry in the bookmark map now.
  1780. //
  1781. TBL_OFF oTableRow;
  1782. ULONG status = _RowStatus(pbRow);
  1783. if ( TBL_DATA_OKAY == status )
  1784. {
  1785. BOOL fFound = _BookMarkMap.FindBookMark( RowWorkid(pbRow),
  1786. oTableRow );
  1787. if ( !fFound )
  1788. {
  1789. _BookMarkMap.FindBookMark( RowWorkid(pbRow),
  1790. oTableRow );
  1791. Win4Assert( !"Not Found in BookMarkMap" );
  1792. }
  1793. TBL_OFF tbRow = _DataAllocator.FixedOffset(pbRow);
  1794. if ( tbRow != oTableRow )
  1795. {
  1796. Win4Assert( !"Wrong Data in BookMarkMap" );
  1797. }
  1798. }
  1799. }
  1800. #endif // DBG
  1801. }
  1802. //
  1803. // We should have processed all the changes.
  1804. //
  1805. Win4Assert( iChange == cChanges );
  1806. Win4Assert( 0 == _cPendingDeletes );
  1807. //
  1808. // Synchronize the static and dynamic row indexes.
  1809. //
  1810. if ( fCreatingWatch )
  1811. {
  1812. //
  1813. // Watch is being created for the first time. Selectively copy
  1814. // only the non-soft deleted rows from the visible row index to
  1815. // the dynamic row index.
  1816. //
  1817. _ReconcileRowIndexes( _dynRowIndex , _visibleRowIndex );
  1818. }
  1819. else
  1820. {
  1821. //
  1822. // Reconcile the dynamic row index to the visible row index and
  1823. // then get rid of the soft deletion entries from the dynamic
  1824. // row index.
  1825. //
  1826. _ReconcileRowIndexes( _visibleRowIndex , _dynRowIndex );
  1827. }
  1828. _xDeltaBookMarkMap->DeleteAllEntries();
  1829. #if DBG==1
  1830. //
  1831. // This is a very expensive test. Take it out once code stabilizes.
  1832. //
  1833. for ( unsigned j = 0; j < _visibleRowIndex.RowCount(); j++ )
  1834. {
  1835. TBL_OFF oTableRow = _visibleRowIndex.GetRow(j);
  1836. BYTE * pbRow = (BYTE *) _DataAllocator.FixedPointer(oTableRow);
  1837. WORKID wid = RowWorkid(pbRow);
  1838. TBL_OFF bmTableRow;
  1839. ULONG bmIndex;
  1840. BOOL fFound = _BookMarkMap.FindBookMark( wid, bmTableRow, bmIndex );
  1841. if ( !fFound )
  1842. {
  1843. _BookMarkMap.FindBookMark( RowWorkid(pbRow), bmTableRow, bmIndex );
  1844. tbDebugOut(( DEB_ERROR,
  1845. "Workid 0x%X Not Found In BookMarkMap\n", wid ));
  1846. Win4Assert( !"Verification:Not Found in BookMarkMap" );
  1847. }
  1848. if ( bmTableRow != oTableRow || bmIndex != j )
  1849. {
  1850. tbDebugOut(( DEB_ERROR,
  1851. "Mismatch in BookMarkMap oRow=%p:iRowIndex=%x ",
  1852. bmTableRow, bmIndex ));
  1853. tbDebugOut(( DEB_ERROR|DEB_NOCOMPNAME,
  1854. "and RowIndex oRow=%p:iRowIndex=%x\n",
  1855. oTableRow, j ));
  1856. Win4Assert( !"Verification:Wrong Data in BookMarkMap" );
  1857. }
  1858. }
  1859. #endif // DBG==1
  1860. }
  1861. //+---------------------------------------------------------------------------
  1862. //
  1863. // Member: CTableWindow::_StartStaticDynamicSplit
  1864. //
  1865. // Synopsis:
  1866. //
  1867. // Returns:
  1868. //
  1869. // Modifies:
  1870. //
  1871. // History: 7-27-95 srikants Created
  1872. //
  1873. // Notes:
  1874. //
  1875. //----------------------------------------------------------------------------
  1876. void CTableWindow::_StartStaticDynamicSplit()
  1877. {
  1878. _CleanupAndReconcileRowIndexes(TRUE);
  1879. }
  1880. //+---------------------------------------------------------------------------
  1881. //
  1882. // Member: CTableWindow::_EndStaticDynamicSplit
  1883. //
  1884. // Synopsis:
  1885. //
  1886. // Returns:
  1887. //
  1888. // Modifies:
  1889. //
  1890. // History: 7-27-95 srikants Created
  1891. //
  1892. // Notes:
  1893. //
  1894. //----------------------------------------------------------------------------
  1895. void CTableWindow::_EndStaticDynamicSplit()
  1896. {
  1897. _CleanupAndReconcileRowIndexes(FALSE);
  1898. _dynRowIndex.ClearAll();
  1899. }
  1900. //+---------------------------------------------------------------------------
  1901. //
  1902. // Member: CTableWindow::_Refresh
  1903. //
  1904. // Synopsis:
  1905. //
  1906. // Returns:
  1907. //
  1908. // Modifies:
  1909. //
  1910. // History: 7-27-95 srikants Created
  1911. //
  1912. // Notes:
  1913. //
  1914. //----------------------------------------------------------------------------
  1915. void CTableWindow::_Refresh()
  1916. {
  1917. _CleanupAndReconcileRowIndexes(FALSE);
  1918. }
  1919. //+---------------------------------------------------------------------------
  1920. //
  1921. // Method: CTableWindow::_CopyRow
  1922. //
  1923. // Synopsis: Given a source window and a row offset in the source window,
  1924. // this method makes a copy of the source row and adds it to the
  1925. // current window.
  1926. //
  1927. // Arguments: [srcWindow] - Reference to the source window.
  1928. // [oSrcTableRow] - Offset in the source window.
  1929. //
  1930. // Returns: The offset of the row added in this window.
  1931. //
  1932. // History: 2-07-95 srikants Created
  1933. //
  1934. // Notes: There are four kinds of variant data in the rows:
  1935. //
  1936. // 1. In place data (eg. I1, I2, I4, I8, etc )
  1937. // 2. Pointer to global compression data.
  1938. // 3. Pointer to window specific compression data.
  1939. // 4. Pointer to window specific non-compressed data.
  1940. //
  1941. // Data fields of types 1 and 2 can be copied directly from the
  1942. // source row but types 3 and 4 have to be extracted from the
  1943. // source row and then copied.
  1944. //
  1945. // An underlying assumption is that rows in the source and
  1946. // target windows have the same layout.
  1947. //
  1948. //----------------------------------------------------------------------------
  1949. TBL_OFF CTableWindow::_CopyRow( CTableWindow & srcWindow, TBL_OFF oSrcTableRow )
  1950. {
  1951. BYTE * pSrcRow = srcWindow._GetRow( oSrcTableRow );
  1952. WORKID wid = srcWindow.RowWorkid( pSrcRow );
  1953. BYTE * pThisRow = _RowAlloc();
  1954. Win4Assert( _cbRowSize == srcWindow._cbRowSize &&
  1955. _Columns.Count() == srcWindow._Columns.Count());
  1956. //
  1957. // First copy the entire row and then fix up the variable length
  1958. // variant parts.
  1959. //
  1960. RtlCopyMemory( pThisRow, pSrcRow, _cbRowSize );
  1961. TBL_OFF oTableRow = _DataAllocator.FixedOffset(pThisRow);
  1962. //
  1963. // For each column, determine its type. If it is an in-place value or a
  1964. // globally compressed value, just ignore it as we have already copied the
  1965. // data. O/W, extract the variant and copy its contents.
  1966. //
  1967. for ( unsigned i=0; i < _Columns.Count(); i++)
  1968. {
  1969. CTableColumn * pDstColumn = _Columns.Get(i);
  1970. CTableColumn * pSrcColumn = srcWindow._Columns.Get(i);
  1971. Win4Assert( 0 != pDstColumn && 0 != pSrcColumn );
  1972. if ( pSrcColumn->IsPartialDeferred() )
  1973. {
  1974. // No Data to copy here
  1975. continue;
  1976. }
  1977. Win4Assert(
  1978. pDstColumn->GetStoredType() == pSrcColumn->GetStoredType() &&
  1979. pDstColumn->GetValueOffset() == pSrcColumn->GetValueOffset() &&
  1980. pDstColumn->GetStatusOffset() == pSrcColumn->GetStatusOffset() &&
  1981. pDstColumn->GetLengthOffset() == pSrcColumn->GetLengthOffset()
  1982. );
  1983. VARTYPE vtSrc = pSrcColumn->GetStoredType();
  1984. Win4Assert( pDstColumn->GetStoredType() == vtSrc );
  1985. USHORT cbData, cbAlignment, rgfFlags;
  1986. CTableVariant::VartypeInfo(vtSrc, cbData, cbAlignment, rgfFlags);
  1987. if ( CTableVariant::TableIsStoredInline( rgfFlags, vtSrc ) ||
  1988. pSrcColumn->IsGlobalCompressedCol() ||
  1989. ! pSrcColumn->IsValueStored())
  1990. {
  1991. //
  1992. // This data is either stored in-line or is a globally compressed
  1993. // data. We can skip and go the next field.
  1994. //
  1995. continue;
  1996. }
  1997. DBSTATUS CopyStatus = pSrcColumn->CopyColumnData(
  1998. pThisRow,
  1999. *pDstColumn,
  2000. _DataAllocator,
  2001. pSrcRow,
  2002. srcWindow._DataAllocator);
  2003. Win4Assert(DBSTATUS_S_OK == CopyStatus ||
  2004. DBSTATUS_S_ISNULL == CopyStatus);
  2005. } // for loop
  2006. return oTableRow;
  2007. }
  2008. //+---------------------------------------------------------------------------
  2009. //
  2010. // Function: _PutRowToVisibleRowIndex
  2011. //
  2012. // Synopsis: Copies the given row to the table and adds an entry to the
  2013. // visible row index.
  2014. //
  2015. // Arguments: [srcWindow] - Reference to the source window.
  2016. // [oSrcRow] - Row Offset in the source window to be copied.
  2017. //
  2018. // History: 1-24-95 srikants Created
  2019. //
  2020. // Notes:
  2021. //
  2022. //----------------------------------------------------------------------------
  2023. void CTableWindow::_PutRowToVisibleRowIndex( CTableWindow & srcWindow,
  2024. TBL_OFF oSrcRow )
  2025. {
  2026. BYTE * pbRow = srcWindow._GetRow( oSrcRow );
  2027. //
  2028. // Add the rowdata and make an entry in the visible row index
  2029. // for this.
  2030. //
  2031. WORKID wid = RowWorkid( pbRow );
  2032. Win4Assert( !_BookMarkMap.IsBookMarkPresent(wid) );
  2033. TBL_OFF oTableRow = _CopyRow( srcWindow, oSrcRow );
  2034. _visibleRowIndex.AddRow(oTableRow);
  2035. _BookMarkMap.AddBookMark( wid, oTableRow );
  2036. const ULONG status = _RowStatus(pbRow);
  2037. Win4Assert( TBL_DATA_HARD_DELETE != status );
  2038. Win4Assert( TBL_DATA_OKAY == status ||
  2039. TBL_DATA_PENDING_DELETE == status ||
  2040. TBL_DATA_ROWADDED == status );
  2041. if ( TBL_DATA_PENDING_DELETE == status )
  2042. {
  2043. _cPendingDeletes++;
  2044. }
  2045. }
  2046. //+---------------------------------------------------------------------------
  2047. //
  2048. // Function: _PutRowToDynRowIndex
  2049. //
  2050. // Synopsis: Adds the given row to the dynamic row index. In addition, if
  2051. // the row is a "new" row and is not present in the table
  2052. // already, it will be added to the table also.
  2053. //
  2054. // Arguments: [srcWindow] - Source window
  2055. // [oSrcRow] - Offset of the row in the source window.
  2056. //
  2057. // History: 1-24-95 srikants Created
  2058. //
  2059. // Notes:
  2060. //
  2061. //----------------------------------------------------------------------------
  2062. void CTableWindow::_PutRowToDynRowIndex( CTableWindow & srcWindow,
  2063. TBL_OFF oSrcRow )
  2064. {
  2065. BYTE * pbRow = srcWindow._GetRow( oSrcRow );
  2066. WORKID wid = srcWindow.RowWorkid( pbRow );
  2067. TBL_OFF oTableRow;
  2068. const ULONG rowStatus = srcWindow._RowStatus(pbRow);
  2069. Win4Assert( TBL_DATA_HARD_DELETE != rowStatus );
  2070. if ( TBL_DATA_OKAY == rowStatus || TBL_DATA_PENDING_DELETE == rowStatus )
  2071. {
  2072. //
  2073. // Since the row status is OKAY or pending delete, it MUST have
  2074. // already been added via the _visibleRowIndex.
  2075. //
  2076. BOOL fFound = _BookMarkMap.FindBookMark( wid, oTableRow );
  2077. Win4Assert( fFound );
  2078. }
  2079. else
  2080. {
  2081. //
  2082. // This row has not been added already via the visible row
  2083. // index. We should add it to the table.
  2084. //
  2085. Win4Assert( TBL_DATA_ROWADDED == rowStatus );
  2086. oTableRow = _CopyRow( srcWindow, oSrcRow );
  2087. Win4Assert( _xDeltaBookMarkMap.IsNull() ||
  2088. !_xDeltaBookMarkMap->IsBookMarkPresent(wid) );
  2089. _xDeltaBookMarkMap->AddBookMark( wid, oTableRow );
  2090. }
  2091. _dynRowIndex.AddRow( oTableRow );
  2092. }
  2093. //+---------------------------------------------------------------------------
  2094. //
  2095. // Function: IsEmptyForQuery
  2096. //
  2097. // Synopsis: Checks if the window is empty from query's perspective.
  2098. //
  2099. // Returns: TRUE if there are no entries in the "invisible" row index.
  2100. // FALSE o/w
  2101. //
  2102. // History: 2-07-95 srikants Created
  2103. //
  2104. // Notes:
  2105. //
  2106. //----------------------------------------------------------------------------
  2107. BOOL CTableWindow::IsEmptyForQuery()
  2108. {
  2109. return _GetInvisibleRowIndex().RowCount() == 0;
  2110. }
  2111. //+---------------------------------------------------------------------------
  2112. //
  2113. // Function: IsGettingFull
  2114. //
  2115. // Synopsis: Checks if the current window is getting full.
  2116. //
  2117. // Returns: TRUE if it is getting full.
  2118. // FALSE o/w
  2119. //
  2120. // History: 2-07-95 srikants Created
  2121. //
  2122. // Notes:
  2123. //
  2124. //----------------------------------------------------------------------------
  2125. BOOL CTableWindow::IsGettingFull()
  2126. {
  2127. //
  2128. // PERFFIX - need a better heuristic based on memory usage.
  2129. //
  2130. return _GetInvisibleRowIndex().RowCount() >= CTableSink::cWindowRowLimit;
  2131. }
  2132. unsigned CTableWindow::PercentFull()
  2133. {
  2134. return (_GetInvisibleRowIndex().RowCount() * 100) / CTableSink::cWindowRowLimit;
  2135. }
  2136. //+---------------------------------------------------------------------------
  2137. //
  2138. // Function: GetSortKey
  2139. //
  2140. // Synopsis: Fills the requested window row as a "bktRow" by extracting
  2141. // only the "sort columns".
  2142. //
  2143. // Arguments: [iRow] - Index of the requested window row.
  2144. // [pvtOut] - Variants of the sort set.
  2145. // [bktRow] - (Output) will have the row in a "bucket row"
  2146. // form.
  2147. //
  2148. // History: 2-16-95 srikants Created
  2149. //
  2150. // Notes:
  2151. //
  2152. //----------------------------------------------------------------------------
  2153. void CTableWindow::GetSortKey( ULONG iRow, CTableRowKey & sortKey )
  2154. {
  2155. Win4Assert( 0 != _RowCompare.GetPointer() &&
  2156. iRow < _GetInvisibleRowIndex().RowCount() );
  2157. TBL_OFF oTableRow = _GetInvisibleRowIndex().GetRow(iRow);
  2158. Win4Assert( 0 != _pSortSet );
  2159. BYTE * pRow = _GetRow( oTableRow );
  2160. for ( unsigned i = 0; i < _pSortSet->Count(); i++ )
  2161. {
  2162. const SSortKey & key = _pSortSet->Get(i);
  2163. PROPID pidColumn = key.pidColumn;
  2164. BOOL fFoundCol = FALSE;
  2165. CTableColumn * pColumn = _Columns.Find(pidColumn, fFoundCol);
  2166. Win4Assert(fFoundCol == TRUE);
  2167. //
  2168. // Create a variant out of the data in the column and copy its contents
  2169. // to the output row.
  2170. //
  2171. CTableVariant varnt;
  2172. CTableVariant* pvarnt;
  2173. XCompressFreeVariant xpvarnt;
  2174. pvarnt = &varnt;
  2175. if ( pColumn->CreateVariant( *pvarnt, pRow, _DataAllocator ) )
  2176. {
  2177. //
  2178. // Variant needs to be freed by the column compressor.
  2179. //
  2180. xpvarnt.Set(pColumn->GetCompressor(), pvarnt);
  2181. }
  2182. //
  2183. // Initialize the column in the bucket row with the data in the
  2184. // variant just extracted.
  2185. //
  2186. sortKey.Init( i, *pvarnt );
  2187. }
  2188. }
  2189. //+-------------------------------------------------------------------------
  2190. //
  2191. // Member: CTableWindow::FindRegion, private
  2192. //
  2193. // Synopsis: Delete watch region
  2194. //
  2195. // Returns: The number of rows deleted from watch
  2196. //
  2197. // History: 22-Jun-95 BartoszM Created
  2198. //
  2199. //--------------------------------------------------------------------------
  2200. unsigned CTableWindow::FindRegion (HWATCHREGION hRegion) const
  2201. {
  2202. for (unsigned i = 0; i < _aWindowWatch.Count(); i++)
  2203. {
  2204. if (_aWindowWatch.Get(i)._hRegion == hRegion)
  2205. break;
  2206. }
  2207. return i;
  2208. }
  2209. //+-------------------------------------------------------------------------
  2210. //
  2211. // Member: CTableWindow::AddWatch
  2212. //
  2213. // Synopsis: Add watch region starting at offset
  2214. //
  2215. // Returns: The number of rows added to the watch
  2216. //
  2217. // History: 26-Jul-95 BartoszM Created
  2218. //
  2219. // Notes: If this is the last segment, add all cRows
  2220. // otherwise don't overshoot the end of window
  2221. //--------------------------------------------------------------------------
  2222. long CTableWindow::AddWatch ( HWATCHREGION hRegion,
  2223. long iStart,
  2224. LONG cRows,
  2225. BOOL isLast)
  2226. {
  2227. Win4Assert( cRows > 0 );
  2228. long cRowsHere = cRows;
  2229. if (!isLast && cRows > (long)_visibleRowIndex.RowCount() - iStart)
  2230. {
  2231. cRowsHere = (long)_visibleRowIndex.RowCount() - iStart;
  2232. }
  2233. Win4Assert( cRowsHere >= 0 );
  2234. CWindowWatch watch;
  2235. watch._hRegion = hRegion;
  2236. watch._iRowStart = iStart;
  2237. watch._cRowsHere = cRowsHere;
  2238. watch._cRowsLeft = cRows;
  2239. _AddWatchItem (watch);
  2240. return cRowsHere;
  2241. }
  2242. //+-------------------------------------------------------------------------
  2243. //
  2244. // Member: CTableWindow::DeleteWatch
  2245. //
  2246. // Synopsis: Delete watch region
  2247. //
  2248. // Returns: The number of rows deleted from watch
  2249. //
  2250. // History: 20-Jun-95 BartoszM Created
  2251. //
  2252. //--------------------------------------------------------------------------
  2253. long CTableWindow::DeleteWatch (HWATCHREGION hRegion)
  2254. {
  2255. unsigned i = FindRegion(hRegion);
  2256. Win4Assert (i < _aWindowWatch.Count());
  2257. long count = _aWindowWatch.Get(i)._cRowsHere;
  2258. _RemoveWatchItem (i);
  2259. return count;
  2260. }
  2261. //+-------------------------------------------------------------------------
  2262. //
  2263. // Member: CTableWindow::ModifyWatch
  2264. //
  2265. // Synopsis: Modify watch region
  2266. //
  2267. // Returns: The number of rows in the modified watch
  2268. //
  2269. // History: 20-Jun-95 BartoszM Created
  2270. //
  2271. //--------------------------------------------------------------------------
  2272. long CTableWindow::ModifyWatch (HWATCHREGION hRegion, long iStart, long cRows, BOOL isLast)
  2273. {
  2274. Win4Assert( cRows > 0 );
  2275. Win4Assert( 0 == iStart || iStart < (long) _visibleRowIndex.RowCount() );
  2276. unsigned i = FindRegion(hRegion);
  2277. Win4Assert (i < _aWindowWatch.Count());
  2278. CWindowWatch& watch = _aWindowWatch.Get(i);
  2279. long cRowsHere = cRows;
  2280. if ( !isLast && cRows > (long)_visibleRowIndex.RowCount() - iStart )
  2281. {
  2282. cRowsHere = (long)_visibleRowIndex.RowCount() - iStart;
  2283. }
  2284. Win4Assert( cRowsHere >= 0 );
  2285. watch._iRowStart = iStart;
  2286. watch._cRowsHere = cRowsHere;
  2287. watch._cRowsLeft = cRows;
  2288. return cRowsHere;
  2289. }
  2290. //+-------------------------------------------------------------------------
  2291. //
  2292. // Member: CTableWindow::ShrinkWatch
  2293. //
  2294. // Synopsis: Shrink watch region so that it starts with the
  2295. // bookmark and extends no farther than cRows
  2296. //
  2297. // Returns: The number of rows left in the region
  2298. //
  2299. // History: 21-Jun-95 BartoszM Created
  2300. //
  2301. //--------------------------------------------------------------------------
  2302. long CTableWindow::ShrinkWatch (HWATCHREGION hRegion,
  2303. CI_TBL_BMK bookmark,
  2304. LONG cRows)
  2305. {
  2306. Win4Assert( cRows > 0 );
  2307. // find the bookmark
  2308. TBL_OFF off;
  2309. ULONG uiStart;
  2310. if ( !FindBookMark( bookmark, off, uiStart))
  2311. {
  2312. Win4Assert (!"CTableWindow::ShrinkWatch, bookmark not found" );
  2313. }
  2314. long iStart = (long)uiStart;
  2315. unsigned i = FindRegion(hRegion);
  2316. Win4Assert (i < _aWindowWatch.Count());
  2317. CWindowWatch& watch = _aWindowWatch.Get(i);
  2318. // Assert that bookmark is within the watch region
  2319. Win4Assert (watch._iRowStart <= iStart);
  2320. Win4Assert (watch._iRowStart + watch._cRowsHere > iStart);
  2321. long cRowsLeftHere = watch._cRowsHere - (iStart - watch._iRowStart);
  2322. long cRowsLeft = watch._cRowsLeft - (iStart - watch._iRowStart);
  2323. Win4Assert( cRowsLeftHere > 0 );
  2324. Win4Assert( cRowsLeft > 0 );
  2325. watch._iRowStart = iStart;
  2326. watch._cRowsHere = min (cRowsLeftHere, cRows);
  2327. watch._cRowsLeft = min (cRowsLeft, cRows);
  2328. return watch._cRowsHere;
  2329. }
  2330. //+-------------------------------------------------------------------------
  2331. //
  2332. // Member: CTableWindow::ShrinkWatch
  2333. //
  2334. // Synopsis: Shrink watch region so that it extends no farther than cRows
  2335. // The assumption is that the beginning of the region
  2336. // is at the beginning of the window.
  2337. //
  2338. // Returns: The number of rows left in the region
  2339. //
  2340. // History: 21-Jun-95 BartoszM Created
  2341. //
  2342. //--------------------------------------------------------------------------
  2343. long CTableWindow::ShrinkWatch (HWATCHREGION hRegion, LONG cRows)
  2344. {
  2345. unsigned i = FindRegion(hRegion);
  2346. Win4Assert (i < _aWindowWatch.Count());
  2347. Win4Assert (_aWindowWatch.Get(i)._iRowStart == 0);
  2348. CWindowWatch& watch = _aWindowWatch.Get(i);
  2349. Win4Assert( cRows > 0 );
  2350. Win4Assert( watch._cRowsHere >= 0 );
  2351. Win4Assert( watch._cRowsLeft > 0 );
  2352. watch._cRowsHere = min (watch._cRowsHere, cRows);
  2353. watch._cRowsLeft = min (watch._cRowsLeft, cRows);
  2354. return watch._cRowsHere;
  2355. }
  2356. //+-------------------------------------------------------------------------
  2357. //
  2358. // Member: CTableWindow::IsWatched
  2359. //
  2360. // Synopsis: Returns TRUE if the window has the watch region hRegion
  2361. // and the bookmark is within this region
  2362. //
  2363. // History: 21-Jun-95 BartoszM Created
  2364. //
  2365. //--------------------------------------------------------------------------
  2366. BOOL CTableWindow::IsWatched (HWATCHREGION hRegion, CI_TBL_BMK bookmark)
  2367. {
  2368. unsigned i = FindRegion(hRegion);
  2369. if (i == _aWindowWatch.Count())
  2370. return FALSE;
  2371. // find the bookmark
  2372. TBL_OFF off;
  2373. ULONG iBmk;
  2374. if ( !FindBookMark( bookmark, off, iBmk))
  2375. {
  2376. return FALSE;
  2377. }
  2378. long iWatchStart = _aWindowWatch.Get(i)._iRowStart;
  2379. long iWatchEnd = iWatchStart + _aWindowWatch.Get(i)._cRowsHere;
  2380. return iWatchStart <= (long)iBmk && (long)iBmk < iWatchEnd;
  2381. }
  2382. //+-------------------------------------------------------------------------
  2383. //
  2384. // Member: CTableWindow::HasWatch
  2385. //
  2386. // Synopsis: Returns TRUE if the window has the watch region hRegion
  2387. //
  2388. // History: 21-Jun-95 BartoszM Created
  2389. //
  2390. //--------------------------------------------------------------------------
  2391. BOOL CTableWindow::HasWatch (HWATCHREGION hRegion)
  2392. {
  2393. unsigned i = FindRegion(hRegion);
  2394. return i < _aWindowWatch.Count();
  2395. }
  2396. //+-------------------------------------------------------------------------
  2397. //
  2398. // Member: CTableWindow::RowsWatched
  2399. //
  2400. // Synopsis: Returns the number of rows in the watch region hRegion
  2401. //
  2402. // History: 22-Jun-95 BartoszM Created
  2403. //
  2404. //--------------------------------------------------------------------------
  2405. long CTableWindow::RowsWatched (HWATCHREGION hRegion)
  2406. {
  2407. unsigned i = FindRegion(hRegion);
  2408. if ( i < _aWindowWatch.Count() )
  2409. return _aWindowWatch.Get(i)._cRowsHere;
  2410. return 0;
  2411. }
  2412. //+-------------------------------------------------------------------------
  2413. //
  2414. // Member: CTableWindow::_AddWatchItem
  2415. //
  2416. // Synopsis: Adds a new watch item to list and
  2417. // changes the state of the window if necessary
  2418. //
  2419. // History: 26-Jul-95 BartoszM Created
  2420. //
  2421. //--------------------------------------------------------------------------
  2422. void CTableWindow::_AddWatchItem (CWindowWatch& watch)
  2423. {
  2424. _aWindowWatch.Add (watch, _aWindowWatch.Count());
  2425. if ( _aWindowWatch.Count() == 1 && !_fSplitInProgress )
  2426. {
  2427. _StartStaticDynamicSplit();
  2428. }
  2429. }
  2430. //+-------------------------------------------------------------------------
  2431. //
  2432. // Member: CTableWindow::_RemoveWatcheItem
  2433. //
  2434. // Synopsis: Removes a watch item from the list and
  2435. // changes the state of the window if necessary
  2436. //
  2437. // History: 26-Jul-95 BartoszM Created
  2438. //
  2439. //--------------------------------------------------------------------------
  2440. void CTableWindow::_RemoveWatchItem (unsigned i)
  2441. {
  2442. _aWindowWatch.Remove(i);
  2443. if (_aWindowWatch.Count() == 0)
  2444. {
  2445. _EndStaticDynamicSplit();
  2446. }
  2447. }
  2448. BOOL CTableWindow::FindNearestDynamicBmk( CI_TBL_CHAPT chapter,
  2449. CI_TBL_BMK & bookMark )
  2450. {
  2451. // If the row corresponding to the bookmark exists in the
  2452. // dynamic state, return the original bookmark.
  2453. // If the row has been deleted from the dynamic state
  2454. // return the bookmark of the next available dynamic row.
  2455. // If you hit the end of the table, return the bookmark
  2456. // of the last row in the dynamic state of the table
  2457. //
  2458. // NEWFEATURE - does not understand categorization .
  2459. //
  2460. Win4Assert( IsWatched() );
  2461. if ( WORKID_TBLFIRST == bookMark || WORKID_TBLLAST == bookMark )
  2462. {
  2463. return TRUE;
  2464. }
  2465. TBL_OFF iRowOffset;
  2466. if ( _xDeltaBookMarkMap->FindBookMark(bookMark, iRowOffset) )
  2467. {
  2468. //
  2469. // The row is present in the delta bookmark map. Just return
  2470. // it.
  2471. //
  2472. return TRUE;
  2473. }
  2474. //
  2475. // Locate the closest row in the dynamic bookmark mapping
  2476. // for the row.
  2477. //
  2478. BOOL fFound = _BookMarkMap.FindBookMark(bookMark, iRowOffset);
  2479. Win4Assert( fFound );
  2480. //
  2481. // Locate the row offset in the dynamic row index and find the
  2482. // closest row which is not in a pending delete state.
  2483. //
  2484. ULONG iRowOrdinal;
  2485. fFound = _dynRowIndex.FindRow(iRowOffset,iRowOrdinal);
  2486. Win4Assert( fFound );
  2487. for ( ULONG i = iRowOrdinal; i < _dynRowIndex.RowCount(); i++ )
  2488. {
  2489. BYTE * pbRow = (BYTE *) _DataAllocator.FixedPointer(
  2490. _dynRowIndex.GetRow(i) );
  2491. ULONG status = _RowStatus(pbRow);
  2492. if ( TBL_DATA_PENDING_DELETE != status )
  2493. {
  2494. Win4Assert( TBL_DATA_HARD_DELETE != status );
  2495. bookMark = RowWorkid(pbRow);
  2496. return TRUE;
  2497. }
  2498. }
  2499. //
  2500. // Try to the left of the bookmark.
  2501. //
  2502. for ( long j = (long) iRowOrdinal-1; j >=0; j-- )
  2503. {
  2504. BYTE * pbRow = (BYTE *) _DataAllocator.FixedPointer(
  2505. _dynRowIndex.GetRow( (ULONG) j) );
  2506. ULONG status = _RowStatus(pbRow);
  2507. if ( TBL_DATA_PENDING_DELETE != status )
  2508. {
  2509. Win4Assert( TBL_DATA_HARD_DELETE != status );
  2510. bookMark = RowWorkid(pbRow);
  2511. return TRUE;
  2512. }
  2513. }
  2514. return FALSE;
  2515. }
  2516. BOOL CTableWindow::FindFirstNonDeleteDynamicBmk( CI_TBL_BMK & bmk )
  2517. {
  2518. CRowIndex & rowIndex = _GetInvisibleRowIndex();
  2519. for ( unsigned i = 0; i < rowIndex.RowCount(); i++ )
  2520. {
  2521. BYTE * pbRow = (BYTE *)
  2522. _DataAllocator.FixedPointer( rowIndex.GetRow(i) );
  2523. if ( TBL_DATA_PENDING_DELETE != _RowStatus(pbRow) )
  2524. {
  2525. Win4Assert( TBL_DATA_HARD_DELETE != _RowStatus(pbRow) );
  2526. bmk = RowWorkid(pbRow);
  2527. return TRUE;
  2528. }
  2529. }
  2530. return FALSE;
  2531. }
  2532. BOOL CTableWindow::FindLastNonDeleteDynamicBmk( CI_TBL_BMK & bmk )
  2533. {
  2534. CRowIndex & rowIndex = _GetInvisibleRowIndex();
  2535. for ( long i = (long) rowIndex.RowCount()-1; i >= 0; i-- )
  2536. {
  2537. BYTE * pbRow = (BYTE *)
  2538. _DataAllocator.FixedPointer( rowIndex.GetRow( (ULONG) i) );
  2539. if ( TBL_DATA_PENDING_DELETE != _RowStatus(pbRow) )
  2540. {
  2541. Win4Assert( TBL_DATA_HARD_DELETE != _RowStatus(pbRow) );
  2542. bmk = RowWorkid(pbRow);
  2543. return TRUE;
  2544. }
  2545. }
  2546. return FALSE;
  2547. }
  2548. //+-------------------------------------------------------------------------
  2549. //
  2550. // Member: CTableWindow::LokGetOneColumn
  2551. //
  2552. // Synopsis: Fills a buffer with a particular column from a row
  2553. //
  2554. // Arguments: [wid] -- workid of the row to be queried
  2555. // [rOutColumn] -- layout of the output data
  2556. // [pbOut] -- where to write the column data
  2557. // [rVarAlloc] -- variable data allocator to use
  2558. //
  2559. // History: 22-Aug-95 dlee Created
  2560. //
  2561. //--------------------------------------------------------------------------
  2562. void CTableWindow::LokGetOneColumn(
  2563. WORKID wid,
  2564. CTableColumn const & rOutColumn,
  2565. BYTE * pbOut,
  2566. PVarAllocator & rVarAllocator )
  2567. {
  2568. TBL_OFF oRow;
  2569. ULONG iRow;
  2570. BOOL fFound = FindBookMark( wid, oRow, iRow );
  2571. Win4Assert( fFound && "LokGetOneColumn could not find bmk" );
  2572. BYTE *pbRow = (BYTE *) _DataAllocator.FixedPointer( oRow );
  2573. CTableColumn * pColumn = _Columns.Find( rOutColumn.PropId );
  2574. pColumn->CopyColumnData( pbOut,
  2575. rOutColumn,
  2576. rVarAllocator,
  2577. pbRow,
  2578. _DataAllocator );
  2579. } //LokGetOneColumn
  2580. //+-------------------------------------------------------------------------
  2581. //
  2582. // Member: CTableWindow::IsFirstRowFirstOfCategory
  2583. //
  2584. // Synopsis: Returns TRUE if the first row in the window is also the
  2585. // first of a category. Needed for window selection in PutRow
  2586. //
  2587. // History: 6-Nov-95 dlee Created
  2588. //
  2589. //--------------------------------------------------------------------------
  2590. BOOL CTableWindow::IsFirstRowFirstOfCategory()
  2591. {
  2592. CRowIndex & ri = _GetInvisibleRowIndex();
  2593. if ( 0 != ri.RowCount() )
  2594. {
  2595. BYTE *pbRow = _GetRowFromIndex( 0, ri );
  2596. ULONG chapt = _RowChapter( pbRow );
  2597. WORKID wid = RowWorkid( pbRow );
  2598. CCategorize & Cat = * GetCategorizer();
  2599. return ( Cat.GetFirstWorkid( chapt ) == wid );
  2600. }
  2601. return FALSE;
  2602. } //IsFirstRowFirstOfCategory