Source code of Windows XP (NT5)
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.

1531 lines
42 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1994 - 1999.
  5. //
  6. // File: rowbuf.cxx
  7. //
  8. // Contents: Declaration of the row buffer classes, used for HROW
  9. // buffering at the interface level.
  10. //
  11. // Classes: CRowBuffer
  12. // CRowBufferSet
  13. // CDeferredValue
  14. //
  15. // History: 22 Nov 1994 AlanW Created
  16. //
  17. //--------------------------------------------------------------------------
  18. #include "pch.cxx"
  19. #pragma hdrstop
  20. #include <hraccess.hxx>
  21. #include "tabledbg.hxx"
  22. //+-------------------------------------------------------------------------
  23. //
  24. // Member: CRowBufferSet constructor, public
  25. //
  26. // Synopsis: Create a row buffer set
  27. //
  28. // Arguments: [fSequential] - if TRUE, sequential rowset; obRowId
  29. // is not required.
  30. // [obRowRefcount] - offset in row data reserved for a
  31. // USHORT reference count.
  32. // [obRowId] - offset in row data of a ULONG row
  33. // identifier field, used for bookmarks and HROW
  34. // identity tests.
  35. // [obChaptRefcount] - offset in row data reserved for a
  36. // USHORT reference count for chapters.
  37. // [obChaptId] - offset in row data of a ULONG chapter
  38. // identifier field. 0xFFFFFFFF if the rowset is not
  39. // chaptered.
  40. //
  41. // Returns: Nothing
  42. //
  43. //--------------------------------------------------------------------------
  44. CRowBufferSet::CRowBufferSet(
  45. BOOL fSequential,
  46. ULONG obRowRefcount,
  47. ULONG obRowId,
  48. ULONG obChaptRefcount,
  49. ULONG obChaptId
  50. ) :
  51. _mutex( ),
  52. _fSequential( fSequential ),
  53. _obRowRefcount( obRowRefcount ),
  54. _obRowId( obRowId ),
  55. _obChaptRefcount( obChaptRefcount ),
  56. _obChaptId( obChaptId ),
  57. _cRowBufs( 0 ),
  58. _iBufHint( 0 ),
  59. _iRowHint( 0 )
  60. #ifdef _WIN64
  61. , _ArrayAlloc (FALSE, FALSE, sizeof (void *), 0)
  62. #endif
  63. #if CIDBG
  64. , _cHintHits( 0 ),
  65. _cHintMisses( 0 )
  66. #endif
  67. {
  68. // Lots of code in this file assumes the refcount is at offset 0, and
  69. // occupies a USHORT
  70. Win4Assert( _obRowRefcount == 0 &&
  71. sizeof (CRBRefCount) == sizeof (USHORT));
  72. Win4Assert( _obRowId != 0xFFFFFFFF || fSequential );
  73. }
  74. //+-------------------------------------------------------------------------
  75. //
  76. // Member: CRowBufferSet::~CRowBufferSet, public
  77. //
  78. // Synopsis: Destroy a row buffer set
  79. //
  80. // Returns: - nothing -
  81. //
  82. //--------------------------------------------------------------------------
  83. CRowBufferSet::~CRowBufferSet( )
  84. {
  85. CLock lock(_mutex);
  86. #if CIDBG
  87. tbDebugOut(( DEB_ROWBUF,
  88. " hint hits / misses: %d %d\n",
  89. _cHintHits,
  90. _cHintMisses ));
  91. #endif
  92. for (unsigned i = 0; i < Size(); i++) {
  93. if (Get(i) != 0)
  94. {
  95. tbDebugOut(( DEB_WARN,
  96. "CRowBufferSet::~CRowBufferSet, unreleased row buffer %x\n",
  97. Get(i) ));
  98. delete Acquire(i);
  99. }
  100. }
  101. }
  102. //+-------------------------------------------------------------------------
  103. //
  104. // Member: CRowBufferSet::_FindRowBuffer, private
  105. //
  106. // Synopsis: Find a row buffer given an HROW
  107. //
  108. // Arguments: [hRow] - the row to be looked up
  109. // [riBuf] - index to the buffer in the buffer set
  110. // [riRow] - index to the row in the buffer
  111. //
  112. // Returns: CRowBuffer* - pointer to the row buffer if found, 0 otherwise
  113. //
  114. // Notes:
  115. //
  116. //--------------------------------------------------------------------------
  117. CRowBuffer* CRowBufferSet::_FindRowBuffer(
  118. HROW hRow,
  119. unsigned & riBuf,
  120. unsigned & riRow )
  121. {
  122. CRowBuffer* pBuffer = 0;
  123. if (IsHrowRowId())
  124. {
  125. // Check the hints. This assumes that most hrow lookups
  126. // will be either the same as the last requested hrow, one after the
  127. // last requested hrow, or one before the last requested row
  128. if ( _iBufHint < Size() && 0 != ( pBuffer = Get(_iBufHint) ) )
  129. {
  130. if ( pBuffer->IsRowOkAndHRow( _iRowHint, hRow ) )
  131. {
  132. #if CIDBG
  133. _cHintHits++;
  134. #endif
  135. riBuf = _iBufHint;
  136. riRow = _iRowHint;
  137. return pBuffer;
  138. }
  139. else if ( pBuffer->IsRowOkAndHRow( _iRowHint + 1, hRow ) )
  140. {
  141. #if CIDBG
  142. _cHintHits++;
  143. #endif
  144. _iRowHint++;
  145. riBuf = _iBufHint;
  146. riRow = _iRowHint;
  147. return pBuffer;
  148. }
  149. else if ( ( _iRowHint > 0 ) &&
  150. ( pBuffer->IsRowOkAndHRow( _iRowHint - 1, hRow ) ) )
  151. {
  152. #if CIDBG
  153. _cHintHits++;
  154. #endif
  155. _iRowHint--;
  156. riBuf = _iBufHint;
  157. riRow = _iRowHint;
  158. return pBuffer;
  159. }
  160. }
  161. // Lookup HROW by row id via a linear search.
  162. for (riBuf = 0; riBuf < Size(); riBuf++)
  163. {
  164. pBuffer = Get(riBuf);
  165. if ( ( 0 != pBuffer ) &&
  166. ( pBuffer->FindHRow( riRow, hRow ) ) )
  167. {
  168. #if CIDBG
  169. _cHintMisses++;
  170. #endif
  171. _iBufHint = riBuf;
  172. _iRowHint = riRow;
  173. return pBuffer;
  174. }
  175. }
  176. pBuffer = 0; // Buffer not found
  177. }
  178. else
  179. {
  180. //
  181. // Row handle contains the buffer and row indices. Just unpack
  182. // them.
  183. //
  184. riBuf = ((ULONG) hRow >> 20 & 0xFFF) - 1; // get buffer index
  185. riRow = ((ULONG) hRow & 0xFFFFF) - 1; // get row index
  186. pBuffer = Get(riBuf);
  187. }
  188. return pBuffer;
  189. }
  190. //+-------------------------------------------------------------------------
  191. //
  192. // Member: CRowBufferSet::_FindRowBufferByChapter, private
  193. //
  194. // Synopsis: Find a row buffer given an HCHAPTER
  195. //
  196. // Arguments: [hChapter] - the chapter to be looked up
  197. // [riBuf] - index to the buffer in the buffer set
  198. // [riRow] - index to the row in the buffer
  199. //
  200. // Returns: CRowBuffer* - pointer to the row buffer if found, 0 otherwise
  201. //
  202. // Notes:
  203. //
  204. //--------------------------------------------------------------------------
  205. CRowBuffer* CRowBufferSet::_FindRowBufferByChapter(
  206. HCHAPTER hChapter,
  207. unsigned & riBuf,
  208. unsigned & riRow )
  209. {
  210. CRowBuffer* pBuffer = 0;
  211. Win4Assert( IsHrowRowId() && _obChaptId != 0xFFFFFFFF );
  212. Win4Assert( DB_NULL_HCHAPTER != hChapter );
  213. // Check the hints. This assumes that most HCHAPTER lookups
  214. // will be either the same as the last requested HROW, one after the
  215. // last requested HROW, or one before the last requested HROW.
  216. if ( _iBufHint < Size() && 0 != ( pBuffer = Get(_iBufHint) ) )
  217. {
  218. if ( pBuffer->IsRowOkAndHChapt( _iRowHint, hChapter ) )
  219. {
  220. #if CIDBG
  221. _cHintHits++;
  222. #endif
  223. riBuf = _iBufHint;
  224. riRow = _iRowHint;
  225. return pBuffer;
  226. }
  227. else if ( pBuffer->IsRowOkAndHChapt( _iRowHint + 1, hChapter ) )
  228. {
  229. #if CIDBG
  230. _cHintHits++;
  231. #endif
  232. _iRowHint++;
  233. riBuf = _iBufHint;
  234. riRow = _iRowHint;
  235. return pBuffer;
  236. }
  237. else if ( ( _iRowHint > 0 ) &&
  238. ( pBuffer->IsRowOkAndHChapt( _iRowHint - 1, hChapter ) ) )
  239. {
  240. #if CIDBG
  241. _cHintHits++;
  242. #endif
  243. _iRowHint--;
  244. riBuf = _iBufHint;
  245. riRow = _iRowHint;
  246. return pBuffer;
  247. }
  248. }
  249. // Lookup HCHAPTER via a linear search.
  250. for (riBuf = 0; riBuf < Size(); riBuf++)
  251. {
  252. pBuffer = Get(riBuf);
  253. if ( ( 0 != pBuffer ) &&
  254. ( pBuffer->FindHChapter( riRow, hChapter ) ) )
  255. {
  256. #if CIDBG
  257. _cHintMisses++;
  258. #endif
  259. // NOTE: row hint not updated for this chapter lookup
  260. //_iBufHint = riBuf;
  261. //_iRowHint = riRow;
  262. return pBuffer;
  263. }
  264. }
  265. return 0; // Buffer not found
  266. }
  267. //+-------------------------------------------------------------------------
  268. //
  269. // Member: CRowBufferSet::Add, public
  270. //
  271. // Synopsis: Add a row buffer to the set
  272. //
  273. // Arguments: [pBuf] - a smart pointer to the buffer to be added.
  274. // [fPossibleDuplicateHRows] - TRUE if some of the hrows may be
  275. // duplicated in this buffer
  276. // [pahRows] - optional pointer to array of row handles to
  277. // be returned.
  278. //
  279. // Returns: Nothing, thows on error.
  280. //
  281. // Notes: Acquires the row buffer if successful
  282. //
  283. //--------------------------------------------------------------------------
  284. VOID CRowBufferSet::Add(
  285. XPtr<CRowBuffer> & pBuf,
  286. BOOL fPossibleDuplicateHRows,
  287. HROW * pahRows
  288. ) {
  289. CLock lock(_mutex);
  290. tbDebugOut(( DEB_ROWBUF,
  291. "CRowBufferSet::Add - new row buffer = %x\n",
  292. pBuf.GetPointer() ));
  293. unsigned iRowBuf;
  294. if ( _cRowBufs == Size() )
  295. iRowBuf = Size(); // There is no free element
  296. else
  297. {
  298. for (iRowBuf = 0; iRowBuf < Size(); iRowBuf++)
  299. {
  300. if (Get(iRowBuf) == 0)
  301. break; // found a free array element.
  302. }
  303. }
  304. #if CIDBG
  305. if ( iRowBuf == Size() )
  306. {
  307. tbDebugOut(( DEB_ROWBUF,
  308. "CRowBufferSet::Add, growing row buffer array, new entry = %d\n",
  309. iRowBuf ));
  310. }
  311. #endif // CIDBG
  312. pBuf->SetRowIdOffset( _obRowId );
  313. if ( IsChaptered() )
  314. pBuf->SetChapterVars( _obChaptId, _obChaptRefcount );
  315. //
  316. // Refcount the rows in the buffer. If there is no row identifier
  317. // in the buffer, generate the HROW from a combination of the
  318. // buffer number and the row index within the buffer.
  319. // Otherwise, dereference any other occurance of the row, and
  320. // collapse the references to the newly fetched row. Generate
  321. // the HROW from the row identifier.
  322. //
  323. ULONG hRowGen = (iRowBuf+1) << 20;
  324. for (unsigned iRow = 0; iRow < pBuf->GetRowCount(); iRow++)
  325. {
  326. if (IsHrowRowId())
  327. {
  328. HROW hRowId = pBuf->GetRowId(iRow);
  329. CRBRefCount RowRefCount(0);
  330. CRBRefCount ChapterRefCount(0);
  331. if ( _cRowBufs != 0 )
  332. {
  333. unsigned iOldBuf = 0, iOldRow = 0;
  334. CRowBuffer * pOldRowBuf = _FindRowBuffer( hRowId,
  335. iOldBuf,
  336. iOldRow );
  337. if (pOldRowBuf)
  338. {
  339. CRBRefCount OldRowRefCount;
  340. OldRowRefCount = pOldRowBuf->DereferenceRow( iOldRow );
  341. RowRefCount.AddRefs( OldRowRefCount );
  342. if ( IsChaptered() )
  343. {
  344. CRBRefCount & OldChaptRefCount =
  345. pOldRowBuf->_GetChaptRefCount(iOldRow);
  346. ChapterRefCount.AddRefs( OldChaptRefCount );
  347. }
  348. if (pOldRowBuf->RefCount() <= 0)
  349. {
  350. //
  351. // Deleted the last reference to the buffer. Now free
  352. // it and its location in the array.
  353. //
  354. pOldRowBuf = Acquire( iOldBuf );
  355. delete pOldRowBuf;
  356. _cRowBufs--;
  357. }
  358. }
  359. }
  360. //
  361. // Search for the row handle in the portion of the buffer
  362. // already processed. This should only occur for rows which
  363. // were fetched with GetRowsByBookmark.
  364. //
  365. // Chapter refcounts doen't need to be updated in this loop
  366. // because the duplicate rows don't add to their ref. counts.
  367. //
  368. if ( fPossibleDuplicateHRows )
  369. {
  370. for (unsigned iOldRow = 0; iOldRow < iRow; iOldRow++)
  371. {
  372. if ( pBuf->IsRowHRow( iOldRow, hRowId ) )
  373. {
  374. tbDebugOut(( DEB_ROWBUF,
  375. "CRowBufferSet::Add - duplicate row: %x\n",
  376. hRowId ));
  377. CRBRefCount OldRowRefCount;
  378. OldRowRefCount = pBuf->DereferenceRow( iOldRow );
  379. RowRefCount.AddRefs( OldRowRefCount );
  380. break;
  381. }
  382. }
  383. }
  384. pBuf->InitRowRefcount(iRow, RowRefCount);
  385. if (IsChaptered())
  386. pBuf->_GetChaptRefCount(iRow).SetRefCount( ChapterRefCount );
  387. if (pahRows)
  388. {
  389. *pahRows++ = hRowId;
  390. }
  391. }
  392. else
  393. {
  394. CRBRefCount RowRefCount(0);
  395. pBuf->InitRowRefcount(iRow, RowRefCount);
  396. if (pahRows)
  397. {
  398. *pahRows++ = (HROW) (++hRowGen);
  399. }
  400. }
  401. }
  402. CRowBufferArray::Add(pBuf.GetPointer(), iRowBuf);
  403. pBuf.Acquire();
  404. _cRowBufs++; // A row buffer has been added
  405. return;
  406. }
  407. //+-------------------------------------------------------------------------
  408. //
  409. // Member: CRowBufferSet::Lookup, public
  410. //
  411. // Synopsis: Lookup a row by its HROW. Return data about it.
  412. //
  413. // Arguments: [hRow] - handle of row to be looked up
  414. // [ppColumns] - on return, a description of the row columns
  415. // [ppbRowData] - on return, points to row data
  416. //
  417. // Returns: Reference to the row buffer in which row was found.
  418. //
  419. // Notes: THROWs on errors.
  420. // The row buffer set is locked only while doing the
  421. // lookup. According to the spec, it is the responsibility
  422. // of the consumer to ensure that only one thread will be
  423. // using any one HROW at any one time.
  424. //
  425. //--------------------------------------------------------------------------
  426. CRowBuffer & CRowBufferSet::Lookup(
  427. HROW hRow,
  428. CTableColumnSet ** ppColumns,
  429. void ** ppbRowData )
  430. {
  431. CLock lock(_mutex);
  432. unsigned iBuffer, iRow;
  433. CRowBuffer* pRowBuf = _FindRowBuffer(hRow, iBuffer, iRow);
  434. if (pRowBuf == 0)
  435. QUIETTHROW( CException(DB_E_BADROWHANDLE) );
  436. SCODE sc = pRowBuf->Lookup(iRow, ppColumns, ppbRowData );
  437. if (FAILED(sc))
  438. THROW( CException(sc) );
  439. return *pRowBuf;
  440. }
  441. //+-------------------------------------------------------------------------
  442. //
  443. // Member: CRowBufferSet::_LokAddRefRow, private
  444. //
  445. // Synopsis: Reference an individual HROW.
  446. //
  447. // Arguments: [hRow] - the handle of the row to be ref. counted
  448. // [rRefCount] - reference to location where remaining ref.
  449. // count will be stored.
  450. // [rRowStatus] - reference to DBROWSTATUS where status will be
  451. // stored.
  452. //
  453. // Returns: Nothing
  454. //
  455. // History: 21 Nov 1995 Alanw Created
  456. //
  457. //--------------------------------------------------------------------------
  458. void CRowBufferSet::_LokAddRefRow(
  459. HROW hRow,
  460. ULONG & rRefCount,
  461. DBROWSTATUS & rRowStatus
  462. ) {
  463. unsigned iBuffer, iRow;
  464. rRowStatus = DBROWSTATUS_S_OK;
  465. rRefCount = 0;
  466. CRowBuffer* pRowBuf = _FindRowBuffer(hRow, iBuffer, iRow);
  467. if (pRowBuf == 0)
  468. {
  469. rRowStatus = DBROWSTATUS_E_INVALID;
  470. return;
  471. }
  472. pRowBuf->AddRefRow( hRow, iRow, rRefCount, rRowStatus );
  473. Win4Assert (pRowBuf->RefCount() > 0);
  474. return;
  475. }
  476. //+-------------------------------------------------------------------------
  477. //
  478. // Member: CRowBufferSet::AddRefRows, public
  479. //
  480. // Synopsis: De-reference an array of HROWs.
  481. //
  482. // Arguments: [cRows] - the number of rows to be ref. counted
  483. // [rghRows] - the handle of the row to be ref. counted
  484. // [rgRefCounts] - optional array where remaining row ref.
  485. // counts will be stored.
  486. // [rgRowStatus] -- optional array for status of each row
  487. //
  488. // Returns: Nothing - throws on error
  489. //
  490. // History: 21 Nov 1995 Alanw Created
  491. //
  492. //--------------------------------------------------------------------------
  493. void CRowBufferSet::AddRefRows(
  494. DBCOUNTITEM cRows,
  495. const HROW rghRows [],
  496. DBREFCOUNT rgRefCounts[],
  497. DBROWSTATUS rgRowStatus[]
  498. ) {
  499. CLock lock(_mutex);
  500. ULONG cError = 0;
  501. if (rghRows == 0 && cRows != 0)
  502. THROW( CException( E_INVALIDARG ));
  503. for (unsigned i=0; i<cRows; i++)
  504. {
  505. TRY
  506. {
  507. ULONG ulRefCount;
  508. DBROWSTATUS RowStatus;
  509. _LokAddRefRow(rghRows[i], ulRefCount, RowStatus);
  510. if (rgRefCounts)
  511. rgRefCounts[i] = ulRefCount;
  512. if (rgRowStatus)
  513. rgRowStatus[i] = RowStatus;
  514. if (DBROWSTATUS_S_OK != RowStatus)
  515. cError++;
  516. }
  517. CATCH( CException, e )
  518. {
  519. if (DB_E_BADROWHANDLE == e.GetErrorCode())
  520. {
  521. if (rgRowStatus)
  522. rgRowStatus[i] = DBROWSTATUS_E_INVALID;
  523. if (rgRefCounts)
  524. rgRefCounts[i] = 0;
  525. cError++;
  526. }
  527. else
  528. {
  529. RETHROW();
  530. }
  531. }
  532. END_CATCH;
  533. }
  534. if (cError)
  535. THROW( CException( (cError==cRows) ? DB_E_ERRORSOCCURRED :
  536. DB_S_ERRORSOCCURRED ));
  537. return;
  538. }
  539. //+-------------------------------------------------------------------------
  540. //
  541. // Member: CRowBufferSet::_LokReleaseRow, private
  542. //
  543. // Synopsis: De-reference an individual HROW.
  544. //
  545. // Arguments: [hRow] - the handle of the row to be released
  546. // [rRefCount] - reference to location where remaining ref.
  547. // count will be stored.
  548. // [rRowStatus] - reference to DBROWSTATUS where status will be
  549. // stored.
  550. //
  551. // Returns: Nothing
  552. //
  553. //--------------------------------------------------------------------------
  554. void CRowBufferSet::_LokReleaseRow(
  555. HROW hRow,
  556. ULONG & rRefCount,
  557. DBROWSTATUS & rRowStatus
  558. ) {
  559. unsigned iBuffer, iRow;
  560. CRowBuffer* pRowBuf = _FindRowBuffer(hRow, iBuffer, iRow);
  561. rRowStatus = DBROWSTATUS_S_OK;
  562. rRefCount = 0;
  563. if (pRowBuf == 0)
  564. {
  565. rRowStatus = DBROWSTATUS_E_INVALID;
  566. return;
  567. }
  568. BOOL fRemoveCopies =
  569. pRowBuf->ReleaseRow( hRow, iRow, rRefCount, rRowStatus );
  570. if (pRowBuf->RefCount() <= 0)
  571. {
  572. //
  573. // Deleted the last reference to the buffer. Now free it
  574. // and its location in the array.
  575. //
  576. Win4Assert( pRowBuf == Get( iBuffer ) );
  577. pRowBuf = Acquire( iBuffer );
  578. Win4Assert( 0 == Get( iBuffer ) );
  579. delete pRowBuf;
  580. _cRowBufs--; // A row buffer has been removed
  581. }
  582. if (fRemoveCopies)
  583. {
  584. //
  585. // The last reference to a row which also exists in other row
  586. // buffers was released. Finally get rid of the row in the
  587. // other buffer(s).
  588. //
  589. Win4Assert(IsHrowRowId());
  590. // Lookup HROW by row id via a linear search.
  591. for (unsigned iBuf = Size(); iBuf > 0; iBuf--)
  592. {
  593. unsigned iRow;
  594. CRowBuffer* pBuffer = Get(iBuf-1);
  595. if ( ( 0 != pBuffer ) &&
  596. ( pBuffer->FindHRow( iRow, hRow, TRUE ) ) )
  597. {
  598. ULONG cRefs;
  599. DBROWSTATUS RowStat;
  600. pBuffer->ReleaseRow( hRow, iRow, cRefs, RowStat );
  601. if (pBuffer->RefCount() <= 0)
  602. {
  603. //
  604. // Deleted the last reference to the buffer. Now free it
  605. // and its location in the array.
  606. //
  607. pBuffer = Acquire( iBuf-1);
  608. delete pBuffer;
  609. _cRowBufs--; // A row buffer has been removed
  610. //
  611. // Start scanning buffers again
  612. //
  613. iBuf = Size() + 1;
  614. }
  615. }
  616. }
  617. }
  618. return;
  619. }
  620. //+-------------------------------------------------------------------------
  621. //
  622. // Member: CRowBufferSet::ReleaseRows, public
  623. //
  624. // Synopsis: De-reference an array of HROWs.
  625. //
  626. // Arguments: [cRows] - the number of rows to be released
  627. // [rghRows] - the handle of the row to be released
  628. // [rgRefCounts] - optional array where remaining row ref.
  629. // counts will be stored.
  630. // [rgRowStatus] -- optional array for status of each row
  631. //
  632. // Returns: SCODE - result status, usually one of S_OK,
  633. // DB_S_ERRORSOCCURRED, or DB_E_ERRORSOCCURRED
  634. //
  635. // Notes:
  636. //
  637. //--------------------------------------------------------------------------
  638. SCODE CRowBufferSet::ReleaseRows(
  639. DBCOUNTITEM cRows,
  640. const HROW rghRows [],
  641. DBREFCOUNT rgRefCounts[],
  642. DBROWSTATUS rgRowStatus[]
  643. ) {
  644. CLock lock(_mutex);
  645. ULONG cError = 0;
  646. if (rghRows == 0 && cRows != 0)
  647. THROW( CException( E_INVALIDARG ));
  648. for (unsigned i=0; i<cRows; i++)
  649. {
  650. TRY
  651. {
  652. ULONG ulRefCount;
  653. DBROWSTATUS RowStatus;
  654. _LokReleaseRow(rghRows[i], ulRefCount, RowStatus);
  655. if (rgRefCounts)
  656. rgRefCounts[i] = ulRefCount;
  657. if (rgRowStatus)
  658. rgRowStatus[i] = RowStatus;
  659. if (DBROWSTATUS_S_OK != RowStatus)
  660. cError++;
  661. }
  662. CATCH( CException, e )
  663. {
  664. if (DB_E_BADROWHANDLE == e.GetErrorCode())
  665. {
  666. if (rgRowStatus)
  667. rgRowStatus[i] = DBROWSTATUS_E_INVALID;
  668. if (rgRefCounts)
  669. rgRefCounts[i] = 0;
  670. cError++;
  671. }
  672. else
  673. {
  674. RETHROW();
  675. }
  676. }
  677. END_CATCH;
  678. }
  679. SCODE scResult = cError ?
  680. ( (cError==cRows) ? DB_E_ERRORSOCCURRED :
  681. DB_S_ERRORSOCCURRED ) :
  682. S_OK;
  683. return scResult;
  684. }
  685. //+-------------------------------------------------------------------------
  686. //
  687. // Member: CRowBufferSet::CheckAllHrowsReleased, public
  688. //
  689. // Synopsis: Check that there are no outstanding HROWs. Used by
  690. // the sequential rowset to check its strict sequential
  691. // semantics.
  692. //
  693. // Arguments: - none -
  694. //
  695. // Returns: Nothing
  696. //
  697. // Notes: THROWs DB_E_ROWSNOTRELEASED if any row buffers are
  698. // still held.
  699. //
  700. //--------------------------------------------------------------------------
  701. VOID CRowBufferSet::CheckAllHrowsReleased( )
  702. {
  703. CLock lock(_mutex);
  704. if ( _cRowBufs != 0 )
  705. {
  706. tbDebugOut(( DEB_WARN,
  707. "CRowBufferSet::CheckAllHrowsReleased, unreleased row buffer(s)\n" ));
  708. QUIETTHROW( CException(DB_E_ROWSNOTRELEASED) );
  709. }
  710. }
  711. //+-------------------------------------------------------------------------
  712. //
  713. // Member: CRowBufferSet::AddRefChapter, private
  714. //
  715. // Synopsis: Reference an individual HCHAPTER.
  716. //
  717. // Arguments: [hChapter] - the handle of the Chapter to be ref. counted
  718. // [pcRefCount] - optional pointer where remaining ref.
  719. // count will be stored.
  720. //
  721. // Returns: Nothing - throws on error
  722. //
  723. // History: 16 Mar 1999 Alanw Created
  724. //
  725. //--------------------------------------------------------------------------
  726. void CRowBufferSet::AddRefChapter(
  727. HCHAPTER hChapter,
  728. ULONG * pcRefCount
  729. ) {
  730. CLock lock(_mutex);
  731. Win4Assert( _obChaptRefcount != 0xFFFFFFFF );
  732. unsigned iBuffer, iRow;
  733. CRowBuffer* pRowBuf = _FindRowBufferByChapter(hChapter, iBuffer, iRow);
  734. if (pRowBuf == 0)
  735. {
  736. THROW(CException( DB_E_BADCHAPTER ));
  737. return;
  738. }
  739. ULONG cRefCount = 0;
  740. pRowBuf->AddRefChapter( iRow, cRefCount );
  741. if ( 0 != pcRefCount )
  742. *pcRefCount = cRefCount;
  743. return;
  744. }
  745. //+-------------------------------------------------------------------------
  746. //
  747. // Member: CRowBufferSet::ReleaseChapter, private
  748. //
  749. // Synopsis: Release an individual HCHAPTER.
  750. //
  751. // Arguments: [hChapter] - the handle of the Chapter to be released
  752. // [pcRefCount] - optional pointer where remaining ref.
  753. // count will be stored.
  754. //
  755. // Returns: Nothing - throws on error
  756. //
  757. // History: 16 Mar 1999 Alanw Created
  758. //
  759. //--------------------------------------------------------------------------
  760. void CRowBufferSet::ReleaseChapter(
  761. HCHAPTER hChapter,
  762. ULONG * pcRefCount
  763. ) {
  764. CLock lock(_mutex);
  765. Win4Assert( _obChaptRefcount != 0xFFFFFFFF );
  766. unsigned iBuffer, iRow;
  767. CRowBuffer* pRowBuf = _FindRowBufferByChapter(hChapter, iBuffer, iRow);
  768. if (pRowBuf == 0)
  769. {
  770. THROW(CException( DB_E_BADCHAPTER ));
  771. return;
  772. }
  773. ULONG cRefCount = 0;
  774. pRowBuf->ReleaseChapter( iRow, cRefCount );
  775. if ( 0 != pcRefCount )
  776. *pcRefCount = cRefCount;
  777. return;
  778. }
  779. //+-------------------------------------------------------------------------
  780. //
  781. // Member: CRowBuffer constructor, public
  782. //
  783. // Synopsis: Create a row buffer
  784. //
  785. // Arguments: [rColumns] - a description of the row columns
  786. // [cbRowWidth] - row data length per row
  787. // [cRows] - number of rows represented in the row data
  788. // [rAlloc] - allocator xptr to be acquired
  789. //
  790. // Returns: Nothing
  791. //
  792. //--------------------------------------------------------------------------
  793. CRowBuffer::CRowBuffer(
  794. CTableColumnSet& rColumns,
  795. ULONG cbRowWidth,
  796. ULONG cRows,
  797. XPtr<CFixedVarAllocator> & rAlloc
  798. ) :
  799. _cRows( cRows ),
  800. _cReferences( 0 ),
  801. _cbRowWidth( cbRowWidth ),
  802. _Columns( rColumns ),
  803. _fQuickPROPID(TRUE),
  804. _pbRowData( rAlloc->FirstRow() ),
  805. _obRowId( 0 ),
  806. _Alloc( rAlloc.Acquire() ),
  807. _aDeferredValues( 0 )
  808. {
  809. //
  810. // OPTIMIZATION - See if we can support a quick lookup of PROPIDs.
  811. //
  812. for ( unsigned i = 0; i < _Columns.Count(); i++ )
  813. {
  814. CTableColumn * pCol = _Columns.Get(i);
  815. if ( pCol && i != pCol->GetPropId()-1 )
  816. {
  817. _fQuickPROPID = FALSE;
  818. break;
  819. }
  820. }
  821. }
  822. //+-------------------------------------------------------------------------
  823. //
  824. // Member: CRowBuffer destructor, public
  825. //
  826. // Synopsis: Destroy a row buffer
  827. //
  828. //+-------------------------------------------------------------------------
  829. CRowBuffer::~CRowBuffer( )
  830. {
  831. }
  832. //+-------------------------------------------------------------------------
  833. //
  834. // Member: CRowBuffer::_IndexRow, private
  835. //
  836. // Synopsis: Find a row in a row buffer given its index
  837. //
  838. // Arguments: [iRow] - the index of the row to be looked up
  839. // [fVerifyRefcnt] - if TRUE, the row's refcount will be
  840. // checked for non-zero.
  841. //
  842. // Returns: BYTE* - the address of the row's data
  843. //
  844. // Notes: Throws DB_E_BADROWHANDLE if the row index is out of
  845. // range, or if the row is not referenced.
  846. //
  847. //--------------------------------------------------------------------------
  848. BYTE* CRowBuffer::_IndexRow(
  849. unsigned iRow,
  850. int fVerifyRefcnt ) const
  851. {
  852. if (iRow >= _cRows)
  853. QUIETTHROW( CException( DB_E_BADROWHANDLE ) );
  854. BYTE* pbRow = _pbRowData + (iRow * _cbRowWidth);
  855. // Is the row still referenced?
  856. if ( fVerifyRefcnt &&
  857. ((CRBRefCount *) (pbRow))->GetRefCount() == 0)
  858. QUIETTHROW( CException( DB_E_BADROWHANDLE ) );
  859. return pbRow;
  860. }
  861. //+-------------------------------------------------------------------------
  862. //
  863. // Member: CRowBuffer::_GetRowRefCount, private
  864. //
  865. // Synopsis: Return a reference to the refcount of a row.
  866. //
  867. // Arguments: [iRow] - the index of the row to be looked up
  868. //
  869. // Returns: CRBRefCount& - the address of the row's refcount
  870. //
  871. // Notes: Throws DB_E_BADROWHANDLE if the row index is out of
  872. // range.
  873. //
  874. //--------------------------------------------------------------------------
  875. inline CRBRefCount & CRowBuffer::_GetRowRefCount( unsigned iRow ) const
  876. {
  877. CRBRefCount * pbRowRefCount = (CRBRefCount *)_IndexRow( iRow, FALSE );
  878. return *pbRowRefCount;
  879. }
  880. //+-------------------------------------------------------------------------
  881. //
  882. // Member: CRowBuffer::_GetChaptRefCount, private
  883. //
  884. // Synopsis: Return a reference to the refcount of a row.
  885. //
  886. // Arguments: [iRow] - the index of the row to be looked up
  887. //
  888. // Returns: CRBRefCount& - the address of the row's refcount
  889. //
  890. // Notes: Throws DB_E_BADROWHANDLE if the row index is out of
  891. // range.
  892. //
  893. //--------------------------------------------------------------------------
  894. inline CRBRefCount & CRowBuffer::_GetChaptRefCount( unsigned iRow ) const
  895. {
  896. Win4Assert( _obChaptRefcount != 0xFFFFFFFF );
  897. CRBRefCount * pbRowRefCount =
  898. (CRBRefCount *) (_IndexRow( iRow, FALSE ) + _obChaptRefcount);
  899. return *pbRowRefCount;
  900. }
  901. //+-------------------------------------------------------------------------
  902. //
  903. // Member: CRowBuffer::GetRowId, public
  904. //
  905. // Synopsis: Lookup a row's ID.
  906. //
  907. // Arguments: [iRow] - index of row to be looked up
  908. //
  909. // Returns: HROW - the row's Row ID.
  910. //
  911. // Notes: _IndexRow is called without ref. count verification
  912. // for the case of CRowBufferSet::Add where new buffer
  913. // has its ref. counts initialized.
  914. //
  915. //--------------------------------------------------------------------------
  916. inline HROW CRowBuffer::GetRowId( unsigned iRow ) const
  917. {
  918. Win4Assert( _obRowId <= _cbRowWidth - sizeof (ULONG) );
  919. HROW* phRowId = (HROW *) ( _IndexRow(iRow, FALSE) + _obRowId );
  920. return *(HROW UNALIGNED *) phRowId;
  921. }
  922. //+-------------------------------------------------------------------------
  923. //
  924. // Member: CRowBuffer::Lookup, public
  925. //
  926. // Synopsis: Lookup a row by its HROW. Return data about it.
  927. //
  928. // Arguments: [iRow] - index of row to be looked up
  929. // [ppColumns] - on return, a description of the row columns
  930. // [ppbRowData] - on return, points to row data
  931. // [fValidate] - whether IndexRow should validate the refcount
  932. //
  933. // Returns: SCODE - status of lookup, DB_E_BADROWHANDLE for
  934. // an HROW that could not be found in the buffer
  935. //
  936. // Notes:
  937. //
  938. //--------------------------------------------------------------------------
  939. SCODE CRowBuffer::Lookup(
  940. unsigned iRow,
  941. CTableColumnSet ** ppColumns,
  942. void ** ppbRowData,
  943. BOOL fValidate ) const
  944. {
  945. *ppbRowData = _IndexRow( iRow, fValidate );
  946. *ppColumns = &_Columns;
  947. return S_OK;
  948. }
  949. //+-------------------------------------------------------------------------
  950. //
  951. // Member: CRowBuffer::AddRefRow, public
  952. //
  953. // Synopsis: Reference an HROW.
  954. //
  955. // Arguments: [hRow] - hrow of row to be ref. counted
  956. // [iRow] - the index of the row to be ref. counted
  957. // [rcRef] - on return, remaining ref. count
  958. // of the row.
  959. // [rRowStatus] - reference to DBROWSTATUS where status will be
  960. // stored.
  961. //
  962. // Returns: Nothing - throws DB_E_BADROWHANDLE if row couldn't be
  963. // found.
  964. //
  965. // History: 21 Nov 1995 Alanw Created
  966. //
  967. //--------------------------------------------------------------------------
  968. void CRowBuffer::AddRefRow(
  969. HROW hRow,
  970. unsigned iRow,
  971. ULONG & rcRef,
  972. DBROWSTATUS & rRowStatus
  973. )
  974. {
  975. CRBRefCount & rRefCount = _GetRowRefCount(iRow);
  976. if (rRefCount.GetRefCount() == 0)
  977. {
  978. rRowStatus = DBROWSTATUS_E_INVALID;
  979. rcRef = 0;
  980. }
  981. else
  982. {
  983. rRefCount.IncRefCount();
  984. rcRef = rRefCount.GetRefCount();
  985. }
  986. return;
  987. }
  988. //+-------------------------------------------------------------------------
  989. //
  990. // Member: CRowBuffer::ReleaseRow, public
  991. //
  992. // Synopsis: De-reference an HROW.
  993. //
  994. // Arguments: [hRow] - hrow of row to be released
  995. // [iRow] - the index of the row to be released
  996. // [rcRef] - on return, remaining ref. count
  997. // of the row.
  998. // [rRowStatus] - reference to DBROWSTATUS where status will be
  999. // stored.
  1000. //
  1001. // Returns: BOOL - if TRUE on return, there are copies of the HROW
  1002. // with byref data. Delete those as well
  1003. // since this is the last reference to the HROW.
  1004. //
  1005. // Notes:
  1006. //
  1007. // History: 20 Feb 1995 Alanw Added individual row refcounts
  1008. //
  1009. //--------------------------------------------------------------------------
  1010. BOOL CRowBuffer::ReleaseRow(
  1011. HROW hRow,
  1012. unsigned iRow,
  1013. ULONG & rcRef,
  1014. DBROWSTATUS & rRowStatus
  1015. )
  1016. {
  1017. BOOL fRemoveCopies = FALSE;
  1018. CRBRefCount & rRefCount = _GetRowRefCount(iRow);
  1019. if ( rRefCount.GetRefCount() == 0 &&
  1020. ! rRefCount.HasByrefData() )
  1021. {
  1022. rRowStatus = DBROWSTATUS_E_INVALID;
  1023. rcRef = 0;
  1024. }
  1025. else
  1026. {
  1027. //
  1028. // This might be a zero ref-count row with the ByrefData bit set.
  1029. //
  1030. if (rRefCount.GetRefCount() > 0)
  1031. {
  1032. rRefCount.DecRefCount();
  1033. }
  1034. if (rRefCount.GetRefCount() == 0)
  1035. {
  1036. if (rRefCount.HasByrefCopy())
  1037. fRemoveCopies = TRUE;
  1038. // Free any deferred values hanging around for this row.
  1039. // Better now than at row buffer destruction time.
  1040. for ( unsigned x = 0; x < _aDeferredValues.Count(); x++ )
  1041. {
  1042. if ( hRow == _aDeferredValues[ x ].GetHRow() )
  1043. {
  1044. Win4Assert(rRefCount.HasByrefData());
  1045. _aDeferredValues[ x ].Release();
  1046. }
  1047. }
  1048. _cReferences--;
  1049. CRBRefCount ZeroRefCount(0);
  1050. rRefCount = ZeroRefCount;
  1051. }
  1052. rcRef = rRefCount.GetRefCount();
  1053. }
  1054. return fRemoveCopies;
  1055. }
  1056. //+-------------------------------------------------------------------------
  1057. //
  1058. // Member: CRowBuffer::ReferenceChapter, public
  1059. //
  1060. // Synopsis: Reference a chapter handle. Used by accessors.
  1061. //
  1062. // Arguments: [pbRow] - pointer to the row within the row buffer
  1063. //
  1064. // Returns: Nothing
  1065. //
  1066. // History: 17 Mar 1999 Alanw Created
  1067. //
  1068. //--------------------------------------------------------------------------
  1069. void CRowBuffer::ReferenceChapter(
  1070. BYTE * pbRow
  1071. )
  1072. {
  1073. Win4Assert( _obChaptRefcount != 0xFFFFFFFF );
  1074. CRBRefCount & rRefCount = *(CRBRefCount *) (pbRow + _obChaptRefcount);
  1075. Win4Assert( ! rRefCount.HasByrefCopy() &&
  1076. ! rRefCount.HasByrefData() );
  1077. rRefCount.IncRefCount();
  1078. return;
  1079. }
  1080. //+-------------------------------------------------------------------------
  1081. //
  1082. // Member: CRowBuffer::AddRefChapter, public
  1083. //
  1084. // Synopsis: Reference a chapter handle
  1085. //
  1086. // Arguments: [iRow] - the index of the row with chapter to be released
  1087. // [rcRef] - on return, remaining ref. count of the chapter.
  1088. //
  1089. // Returns: Nothing
  1090. //
  1091. // History: 17 Mar 1999 Alanw Created
  1092. //
  1093. //--------------------------------------------------------------------------
  1094. void CRowBuffer::AddRefChapter(
  1095. unsigned iRow,
  1096. ULONG & rcRef
  1097. )
  1098. {
  1099. CRBRefCount & rRefCount = _GetChaptRefCount(iRow);
  1100. Win4Assert( rRefCount.GetRefCount() > 0 &&
  1101. ! rRefCount.HasByrefCopy() &&
  1102. ! rRefCount.HasByrefData() );
  1103. if (rRefCount.GetRefCount() == 0)
  1104. {
  1105. rcRef = 0;
  1106. }
  1107. else
  1108. {
  1109. rRefCount.IncRefCount();
  1110. rcRef = rRefCount.GetRefCount();
  1111. }
  1112. return;
  1113. }
  1114. //+-------------------------------------------------------------------------
  1115. //
  1116. // Member: CRowBuffer::ReleaseChapter, public
  1117. //
  1118. // Synopsis: De-reference a chapter handle
  1119. //
  1120. // Arguments: [iRow] - the index of the row with chapter to be released
  1121. // [rcRef] - on return, remaining ref. count of the chapter.
  1122. //
  1123. // Returns: Nothing
  1124. //
  1125. // History: 17 Mar 1999 Alanw Created
  1126. //
  1127. //--------------------------------------------------------------------------
  1128. void CRowBuffer::ReleaseChapter(
  1129. unsigned iRow,
  1130. ULONG & rcRef
  1131. )
  1132. {
  1133. CRBRefCount & rRefCount = _GetChaptRefCount(iRow);
  1134. if ( rRefCount.GetRefCount() == 0 )
  1135. {
  1136. rcRef = 0;
  1137. THROW( CException( DB_E_BADCHAPTER ) );
  1138. }
  1139. else
  1140. {
  1141. rRefCount.DecRefCount();
  1142. rcRef = rRefCount.GetRefCount();
  1143. }
  1144. return;
  1145. }
  1146. //+-------------------------------------------------------------------------
  1147. //
  1148. // Member: CRowBuffer::InitRowRefcount, public
  1149. //
  1150. // Synopsis: Set initial reference count on an HROW.
  1151. //
  1152. // Arguments: [iRow] - the index of the row within the buffer
  1153. // [OtherRefs] - reference count transferred from
  1154. // another row.
  1155. //
  1156. // Returns: Nothing
  1157. //
  1158. // Notes:
  1159. //
  1160. // History: 20 Feb 1995 Alanw Created
  1161. //
  1162. //--------------------------------------------------------------------------
  1163. void CRowBuffer::InitRowRefcount(
  1164. unsigned iRow,
  1165. CRBRefCount & OtherRefs
  1166. )
  1167. {
  1168. CRBRefCount RefCount( OtherRefs.GetRefCount() );
  1169. RefCount.IncRefCount();
  1170. if ( OtherRefs.HasByrefData() || OtherRefs.HasByrefCopy() )
  1171. RefCount.SetByrefCopy();
  1172. CRBRefCount & rRef = _GetRowRefCount(iRow);
  1173. rRef = RefCount;
  1174. _cReferences++;
  1175. return;
  1176. }
  1177. //+-------------------------------------------------------------------------
  1178. //
  1179. // Member: CRowBuffer::DereferenceRow, public
  1180. //
  1181. // Synopsis: Remove all references from a row
  1182. //
  1183. // Arguments: [iRow] - the index of the row within the buffer
  1184. //
  1185. // Returns: CRBRefCount - reference count of row
  1186. //
  1187. // Notes: If the client had retrieved a pointer into the rowbuffer,
  1188. // the row stays around with a zero ref. count and will
  1189. // be finally dereferenced when all references to all copies
  1190. // of the row are released.
  1191. //
  1192. // History: 22 Mar 1995 Alanw Created
  1193. //
  1194. //--------------------------------------------------------------------------
  1195. CRBRefCount CRowBuffer::DereferenceRow(
  1196. unsigned iRow
  1197. )
  1198. {
  1199. CRBRefCount & rRef = _GetRowRefCount(iRow);
  1200. CRBRefCount OldRef = rRef;
  1201. CRBRefCount NewRef(0);
  1202. Win4Assert(OldRef.GetRefCount() != 0 && _cReferences > 0);
  1203. if ( OldRef.HasByrefData() )
  1204. NewRef.SetByrefData();
  1205. else
  1206. _cReferences--;
  1207. rRef = NewRef;
  1208. return OldRef;
  1209. }
  1210. //+-------------------------------------------------------------------------
  1211. //
  1212. // Member: CRowBuffer::FindHRow, public
  1213. //
  1214. // Synopsis: Looks for an hrow in the row buffer and returns its index
  1215. //
  1216. // Arguments: [riRow] - returns the index of hRow's row if found
  1217. // [hRow] - HROW to be found
  1218. // [fFindByrefData] - if TRUE, zero ref. rows with byref data
  1219. // are found.
  1220. //
  1221. // Returns: BOOL - TRUE if found, FALSE otherwise
  1222. //
  1223. // History: 16 Aug 1995 dlee Created
  1224. //
  1225. //--------------------------------------------------------------------------
  1226. BOOL CRowBuffer::FindHRow(
  1227. unsigned & riRow,
  1228. HROW hRow,
  1229. BOOL fFindByrefData ) const
  1230. {
  1231. BYTE* pbRow = _pbRowData;
  1232. for ( unsigned iRow = 0;
  1233. iRow < GetRowCount();
  1234. iRow++, pbRow += _cbRowWidth )
  1235. {
  1236. // refcount is the first USHORT in each row
  1237. CRBRefCount * pRefCount = (CRBRefCount *) pbRow;
  1238. //
  1239. // HROW == 64 bits on Sundown, but we know HROWs are just
  1240. // workids that fit in a ULONG.
  1241. //
  1242. if ( (ULONG) hRow == ( * (ULONG *) ( pbRow + _obRowId ) ) )
  1243. {
  1244. if ( ( 0 != pRefCount->GetRefCount() ) ||
  1245. (fFindByrefData && pRefCount->HasByrefData()) )
  1246. {
  1247. riRow = iRow;
  1248. return TRUE;
  1249. }
  1250. }
  1251. }
  1252. return FALSE;
  1253. }
  1254. //+-------------------------------------------------------------------------
  1255. //
  1256. // Member: CRowBuffer::FindHChapter, public
  1257. //
  1258. // Synopsis: Looks for an HCHAPTER in the row buffer and returns its index
  1259. //
  1260. // Arguments: [riRow] - returns the index of hChapter's row if found
  1261. // [hChapter] - HCHAPTER to be found
  1262. //
  1263. // Returns: BOOL - TRUE if found, FALSE otherwise
  1264. //
  1265. // History: 17 Mar 1999 AlanW Created
  1266. // 10 Nov 1999 KLam Changed HCHAPTER cast to CI_TBL_CHAPT
  1267. //
  1268. //--------------------------------------------------------------------------
  1269. BOOL CRowBuffer::FindHChapter( unsigned & riRow, HCHAPTER hChapter ) const
  1270. {
  1271. BYTE* pbRow = _pbRowData;
  1272. Win4Assert( IsChaptered() );
  1273. for ( unsigned iRow = 0;
  1274. iRow < GetRowCount();
  1275. iRow++, pbRow += _cbRowWidth )
  1276. {
  1277. // refcount is the first USHORT in each row
  1278. CRBRefCount * pRefCount = (CRBRefCount *) pbRow;
  1279. if ( hChapter == ( * (CI_TBL_CHAPT *) ( pbRow + _obChaptId ) ) )
  1280. {
  1281. if ( ( 0 != pRefCount->GetRefCount() )
  1282. // || (fFindByrefData && pRefCount->HasByrefData())
  1283. )
  1284. {
  1285. riRow = iRow;
  1286. return TRUE;
  1287. }
  1288. }
  1289. }
  1290. return FALSE;
  1291. }
  1292. //+-------------------------------------------------------------------------
  1293. //
  1294. // Member: CDeferredValue::Release, public
  1295. //
  1296. // Synopsis: Frees a deferred value
  1297. //
  1298. // History: 4 Aug 1995 dlee Created
  1299. //
  1300. //--------------------------------------------------------------------------
  1301. void CDeferredValue::Release()
  1302. {
  1303. if ( 0 != _hrow )
  1304. {
  1305. PropVariantClear( &_var );
  1306. _hrow = 0;
  1307. }
  1308. } //Release