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.

2129 lines
66 KiB

  1. //
  2. // Microsoft Windows
  3. // Copyright (C) Microsoft Corporation, 1995 - 2000
  4. //
  5. // File: ScrlSort.cxx
  6. //
  7. // Contents: Sorted, fully scrollable, distributed rowset.
  8. //
  9. // Classes: CScrollableSorted
  10. //
  11. // History: 05-Jun-95 KyleP Created
  12. // 14-JAN-97 KrishnaN Brought it back to conform to 1.0 spec
  13. //
  14. // Notes: Some of the distributed versions of the Ole DB interfaces simply
  15. // call into the regular implementations. In such cases, we'll avoid
  16. // posting oledb errors because the underlying call had already done
  17. // that.
  18. //
  19. //----------------------------------------------------------------------------
  20. #include <pch.cxx>
  21. #pragma hdrstop
  22. #include "scrlsort.hxx"
  23. #include "disacc.hxx"
  24. #include "disbmk.hxx"
  25. // Rowset object Interfaces that support Ole DB error objects
  26. static const IID * apRowsetErrorIFs[] =
  27. {
  28. &IID_IAccessor,
  29. &IID_IColumnsInfo,
  30. &IID_IConvertType,
  31. &IID_IRowset,
  32. &IID_IRowsetInfo,
  33. &IID_IDBAsynchStatus,
  34. &IID_IRowsetWatchRegion,
  35. &IID_IRowsetAsynch,
  36. &IID_IRowsetQueryStatus,
  37. //&IID_IColumnsRowset,
  38. &IID_IConnectionPointContainer,
  39. &IID_IRowsetIdentity,
  40. &IID_IRowsetLocate,
  41. //&IID_IRowsetResynch,
  42. &IID_IRowsetScroll,
  43. //&IID_IRowsetUpdate,
  44. //&IID_ISupportErrorInfo
  45. };
  46. static const ULONG cRowsetErrorIFs = sizeof(apRowsetErrorIFs)/sizeof(apRowsetErrorIFs[0]);
  47. //
  48. // IUnknown methods.
  49. //
  50. //+-------------------------------------------------------------------------
  51. //
  52. // Method: CScrollableSorted::RealQueryInterface
  53. //
  54. // Synopsis: Rebind to other interface
  55. //
  56. // Arguments: [riid] -- IID of new interface
  57. // [ppiuk] -- New interface * returned here
  58. //
  59. // Returns: S_OK if bind succeeded, E_NOINTERFACE if bind failed
  60. //
  61. // History: 10-Apr-1995 KyleP Created
  62. //
  63. // Notes: ref count is incremented inside QueryInterface
  64. //
  65. //--------------------------------------------------------------------------
  66. SCODE CScrollableSorted::RealQueryInterface( REFIID riid, VOID **ppiuk )
  67. {
  68. SCODE sc = S_OK;
  69. *ppiuk = 0;
  70. // note -- IID_IUnknown covered in QueryInterface
  71. if ( riid == IID_IRowset )
  72. {
  73. *ppiuk = (void *)((IRowset *)this);
  74. }
  75. else if (IID_ISupportErrorInfo == riid)
  76. {
  77. *ppiuk = (void *) ((IUnknown *) (ISupportErrorInfo *) &_DBErrorObj);
  78. }
  79. else if ( riid == IID_IRowsetLocate )
  80. {
  81. *ppiuk = (void *)((IRowsetLocate *)this);
  82. }
  83. else if ( riid == IID_IRowsetScroll )
  84. {
  85. *ppiuk = (void *)((IRowsetScroll *)this);
  86. }
  87. else if ( riid == IID_IRowsetExactScroll )
  88. {
  89. *ppiuk = (void *)((IRowsetExactScroll *)this);
  90. }
  91. else if ( riid == IID_IColumnsInfo )
  92. {
  93. *ppiuk = (void *)((IColumnsInfo *)this);
  94. }
  95. else if ( riid == IID_IAccessor )
  96. {
  97. *ppiuk = (void *)((IAccessor *)this);
  98. }
  99. else if ( riid == IID_IRowsetIdentity )
  100. {
  101. *ppiuk = (void *)((IRowsetIdentity *)this);
  102. }
  103. else if ( riid == IID_IRowsetInfo )
  104. {
  105. *ppiuk = (void *)((IRowsetInfo *)this);
  106. }
  107. else if ( riid == IID_IRowsetAsynch )
  108. {
  109. sc = E_NOINTERFACE;
  110. //
  111. // Support IRowsetAsynch if any of the child rowsets do.
  112. //
  113. IRowsetAsynch * pra = 0;
  114. for (unsigned iChild=0; iChild < _rowset._cChild; iChild++ )
  115. {
  116. sc = Get(iChild)->QueryInterface (IID_IRowsetAsynch, (void**) &pra);
  117. if (SUCCEEDED(sc))
  118. {
  119. pra->Release();
  120. *ppiuk = (void *)((IRowsetAsynch *)this);
  121. break;
  122. }
  123. }
  124. }
  125. else if ( riid == IID_IRowsetWatchRegion )
  126. {
  127. *ppiuk = (void *) (IRowsetWatchRegion *) this;
  128. }
  129. else if ( riid == IID_IRowsetWatchAll )
  130. {
  131. *ppiuk = (void *) (IRowsetWatchAll *) this;
  132. }
  133. else if ( riid == IID_IDBAsynchStatus )
  134. {
  135. *ppiuk = (void *) (IDBAsynchStatus *) this;
  136. }
  137. else if ( riid == IID_IConnectionPointContainer )
  138. {
  139. sc = _rowset._SetupConnectionPointContainer( this, ppiuk );
  140. }
  141. else if ( riid == IID_IRowsetQueryStatus )
  142. {
  143. *ppiuk = (void *)((IRowsetQueryStatus *)this);
  144. }
  145. else
  146. {
  147. sc = E_NOINTERFACE;
  148. }
  149. return sc;
  150. }
  151. //+---------------------------------------------------------------------------
  152. //
  153. // Member: CScrollableSorted::GetData, public
  154. //
  155. // Synopsis: Fetch data for a row.
  156. //
  157. // Arguments: [hRow] -- Handle to row
  158. // [hAccessor] -- Accessor to use for fetch.
  159. // [pData] -- Data goes here.
  160. //
  161. // History: 03-Apr-95 KyleP Created.
  162. //
  163. // Notes: This method is virtually identical to the one in its base
  164. // class. The difference is that this class tracks HROWs
  165. // for all child cursors, to use as bookmark hints.
  166. //
  167. // Notes: Need to have Ole DB error handling here because an exception could
  168. // happen, resulting in a local error.
  169. //
  170. //----------------------------------------------------------------------------
  171. SCODE CScrollableSorted::GetData( HROW hRow,
  172. HACCESSOR hAccessor,
  173. void * pData )
  174. {
  175. _DBErrorObj.ClearErrorInfo();
  176. SCODE sc;
  177. TRY
  178. {
  179. unsigned iChild;
  180. HROW * ahrow = _rowset._RowManager.GetChildAndHROWs( hRow, iChild );
  181. CDistributedAccessor * pAcc = (CDistributedAccessor *)_rowset._aAccessors.Convert(hAccessor);
  182. sc = pAcc->GetData( iChild, ahrow, pData );
  183. }
  184. CATCH( CException, e )
  185. {
  186. vqDebugOut(( DEB_ERROR, "CScrollableSorted::GetData -- caught 0x%x\n", e.GetErrorCode() ));
  187. sc = e.GetErrorCode();
  188. }
  189. END_CATCH
  190. if (FAILED(sc))
  191. _DBErrorObj.PostHResult(sc, IID_IRowset);
  192. return sc;
  193. }
  194. //
  195. // IRowsetLocate methods
  196. //
  197. //+---------------------------------------------------------------------------
  198. //
  199. // Member: CScrollableSorted::Compare, public
  200. //
  201. // Synopsis: Compare bookmarks.
  202. //
  203. // Arguments: [hChapter] -- Chapter.
  204. // [cbBM1] -- Size of [pBM1].
  205. // [pBM1] -- First bookmark.
  206. // [cbBM2] -- Size of [pBM2].
  207. // [pBM2] -- Second bookmark.
  208. // [pdwComparison] -- Result of comparison returned here.
  209. //
  210. // History: 03-Apr-95 KyleP Created.
  211. //
  212. // Notes: Only equality test supported.
  213. //
  214. // Notes: Need to have Ole DB error handling here because an exception could
  215. // happen, resulting in a local error.
  216. //
  217. //----------------------------------------------------------------------------
  218. STDMETHODIMP CScrollableSorted::Compare( HCHAPTER hChapter,
  219. DBBKMARK cbBM1,
  220. const BYTE * pBM1,
  221. DBBKMARK cbBM2,
  222. const BYTE * pBM2,
  223. DBCOMPARE * pdwComparison )
  224. {
  225. _DBErrorObj.ClearErrorInfo();
  226. SCODE sc = S_OK;
  227. TRY
  228. {
  229. CDistributedBookmark bmk1( cbBM1,
  230. (BYTE const *)pBM1,
  231. _rowset._cbBookmark,
  232. _rowset._cChild );
  233. CDistributedBookmark bmk2( cbBM2,
  234. (BYTE const *)pBM2,
  235. _rowset._cbBookmark,
  236. _rowset._cChild );
  237. if ( bmk1.Index() != bmk2.Index() )
  238. *pdwComparison = DBCOMPARE_NE;
  239. else
  240. {
  241. sc = Get( bmk1.Index() )->Compare( hChapter,
  242. bmk1.GetSize(),
  243. bmk1.Get(),
  244. bmk2.GetSize(),
  245. bmk2.Get(),
  246. pdwComparison );
  247. if ( SUCCEEDED(sc) )
  248. {
  249. if ( *pdwComparison != DBCOMPARE_EQ &&
  250. *pdwComparison != DBCOMPARE_NOTCOMPARABLE )
  251. {
  252. *pdwComparison = DBCOMPARE_NE;
  253. }
  254. }
  255. }
  256. }
  257. CATCH( CException, e )
  258. {
  259. sc = e.GetErrorCode();
  260. vqDebugOut(( DEB_ERROR, "CScrollableSorted::Compare returned 0x%x\n", sc ));
  261. }
  262. END_CATCH
  263. if (FAILED(sc))
  264. _DBErrorObj.PostHResult(sc, IID_IRowsetLocate);
  265. return sc;
  266. }
  267. //+---------------------------------------------------------------------------
  268. //
  269. // Member: CScrollableSorted::GetRowsAt, public
  270. //
  271. // Synopsis: Fetch rows from specified starting location.
  272. //
  273. // Arguments: [hChapter] -- Chapter.
  274. // [cbBookmark] -- Size of [pBookmark]
  275. // [pBookmark] -- Bookmark of starting fetch position.
  276. // [lRowsOffset] -- Offset from bookmark to start fetch.
  277. // [cRows] -- Count of rows requested
  278. // [pcRowsObtained] -- Count of rows in [rrghRows] returned here.
  279. // [rrghRows] -- HROWs returned here.
  280. //
  281. // History: 03-Apr-95 KyleP Created.
  282. //
  283. // Notes: Backwards fetch not supported.
  284. //
  285. // Notes: Need to have Ole DB error handling here because an exception could
  286. // happen, resulting in a local error.
  287. //
  288. //----------------------------------------------------------------------------
  289. STDMETHODIMP CScrollableSorted::GetRowsAt( HWATCHREGION hRegion,
  290. HCHAPTER hChapter,
  291. DBBKMARK cbBookmark,
  292. const BYTE * pBookmark,
  293. DBROWOFFSET lRowsOffset,
  294. DBROWCOUNT cRows,
  295. DBCOUNTITEM * pcRowsObtained,
  296. HROW * rrghRows[])
  297. {
  298. _DBErrorObj.ClearErrorInfo();
  299. SCODE sc = S_OK;
  300. BOOL fAllocated = FALSE;
  301. ULONG cTotalRowsObtained = 0;
  302. Win4Assert( 0 == hChapter && "Chapter support not yet implemented" );
  303. if (0 == cRows) // nothing to fetch
  304. return S_OK;
  305. // Underlying routines are not checking for these errors, so have to
  306. if (0 == cbBookmark || 0 == pBookmark || 0 == pcRowsObtained || 0 == rrghRows )
  307. {
  308. vqDebugOut((DEB_IERROR, "CScrollableSorted::GetRowsAt: Invalid Argument(s)\n"));
  309. return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetLocate);
  310. }
  311. *pcRowsObtained = 0;
  312. TRY
  313. {
  314. fAllocated = SetupFetch( cRows, rrghRows );
  315. //
  316. // Seek to position. Special cases are beginning and end of rowset.
  317. //
  318. sc = Seek( cbBookmark, pBookmark, lRowsOffset );
  319. if ( SUCCEEDED( sc ) )
  320. {
  321. sc = StandardFetch( cRows, pcRowsObtained, *rrghRows );
  322. if ( SUCCEEDED( sc ) &&
  323. !_rowset._xChildNotify.IsNull() &&
  324. *pcRowsObtained != 0 )
  325. {
  326. _rowset._xChildNotify->OnRowChange( *pcRowsObtained,
  327. *rrghRows,
  328. DBREASON_ROW_ACTIVATE,
  329. DBEVENTPHASE_DIDEVENT,
  330. TRUE);
  331. }
  332. }
  333. else if ( DB_E_BADSTARTPOSITION == sc )
  334. {
  335. // According to OLE DB 2.0 spec we should return the following
  336. sc = DB_S_ENDOFROWSET;
  337. }
  338. }
  339. CATCH( CException, e )
  340. {
  341. sc = e.GetErrorCode();
  342. vqDebugOut(( DEB_ERROR, "Exception 0x%x calling IRowset::GetRowsAt\n", sc ));
  343. //
  344. // If we already have some rows, then we can't 'unfetch' them, so we have
  345. // to mask this error. Presumably it won't be transient and we'll get it
  346. // again later.
  347. //
  348. if ( *pcRowsObtained > 0 )
  349. {
  350. if ( FAILED(sc) )
  351. sc = DB_S_ROWLIMITEXCEEDED;
  352. }
  353. else if ( fAllocated )
  354. CoTaskMemFree( *rrghRows );
  355. }
  356. END_CATCH
  357. if (FAILED(sc))
  358. _DBErrorObj.PostHResult(sc, IID_IRowsetLocate);
  359. return( sc );
  360. }
  361. //+---------------------------------------------------------------------------
  362. //
  363. // Member: CScrollableSorted::GetRowsByBookmark, public
  364. //
  365. // Synopsis: Fetch rows at specified location(s).
  366. //
  367. // Arguments: [hChapter] -- Chapter.
  368. // [cRows] -- Number of input bookmarks.
  369. // [rgcbBookmarks] -- Count of element(s) in [ppBookmarks]
  370. // [ppBookmarks] -- Bookmarks.
  371. // [pcRowsObtained] -- Count of rows in [rrghRows] returned here.
  372. // [rghRows] -- HROWs returned here.
  373. // [rgRowStatus] -- Row fetch statuses returned here
  374. //
  375. // History: 03-Apr-95 KyleP Created.
  376. //
  377. // Notes: Need to have Ole DB error handling here because errors are being
  378. // translated.
  379. //
  380. //----------------------------------------------------------------------------
  381. STDMETHODIMP CScrollableSorted::GetRowsByBookmark( HCHAPTER hChapter,
  382. DBCOUNTITEM cRows,
  383. const DBBKMARK rgcbBookmarks [],
  384. const BYTE * ppBookmarks[],
  385. HROW rghRows[],
  386. DBROWSTATUS rgRowStatus[]
  387. )
  388. {
  389. _DBErrorObj.ClearErrorInfo();
  390. SCODE sc = S_OK;
  391. ULONG cRowsObtained = 0;
  392. Win4Assert( 0 == hChapter && "Chapter support not yet implemented" );
  393. if (0 == rgcbBookmarks || 0 == ppBookmarks || 0 == rghRows)
  394. {
  395. vqDebugOut((DEB_IERROR, "CScrollableSorted::GetRowsByBookmark: Invalid Argument(s)\n"));
  396. return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetLocate);
  397. }
  398. SetupFetch( cRows, &rghRows );
  399. //
  400. // Note: This code could be optimized to fetch more bookmarks at
  401. // once, but only by complicating the logic. Until we find
  402. // it's worth the pain, let's keep it simple!
  403. //
  404. unsigned cRowsProcessed;
  405. for ( cRowsProcessed = 0; cRowsProcessed < cRows; cRowsProcessed++ )
  406. {
  407. CDistributedBookmark bmk( rgcbBookmarks[cRowsProcessed],
  408. ppBookmarks[cRowsProcessed],
  409. _rowset._cbBookmark,
  410. _rowset._cChild );
  411. DBBKMARK cbBookmark = bmk.GetSize();
  412. BYTE const * pbBookmark = bmk.Get();
  413. sc = Get( bmk.Index() )->GetRowsByBookmark( hChapter,
  414. 1,
  415. &cbBookmark,
  416. (BYTE const **)&pbBookmark,
  417. &rghRows[cRowsProcessed],
  418. (0 == rgRowStatus) ? 0 : &rgRowStatus[cRowsProcessed]
  419. );
  420. if ( FAILED(sc) )
  421. {
  422. continue;
  423. }
  424. else
  425. {
  426. rghRows[cRowsProcessed] = _rowset._RowManager.Add( bmk.Index(), rghRows[cRowsProcessed] );
  427. cRowsObtained++;
  428. }
  429. }
  430. if (cRowsProcessed == cRowsObtained)
  431. sc = S_OK;
  432. else if (cRowsObtained > 0) // and not all rows were successfully processed
  433. sc = DB_S_ERRORSOCCURRED;
  434. else // no rows were successfully processed
  435. sc = DB_E_ERRORSOCCURRED;
  436. if ( SUCCEEDED( sc ) &&
  437. cRowsObtained > 0 &&
  438. !_rowset._xChildNotify.IsNull() )
  439. {
  440. _rowset._xChildNotify->OnRowChange( cRowsObtained,
  441. rghRows,
  442. DBREASON_ROW_ACTIVATE,
  443. DBEVENTPHASE_DIDEVENT,
  444. TRUE);
  445. }
  446. return ( S_OK == sc ?
  447. S_OK : _DBErrorObj.PostHResult(sc, IID_IRowsetLocate) );
  448. }
  449. //+---------------------------------------------------------------------------
  450. //
  451. // Member: CScrollableSorted::Hash, public
  452. //
  453. // Synopsis: Hash bookmark
  454. //
  455. // Arguments: [hChapter] -- Chapter.
  456. // [cBookmarks] -- Number of bookmarks.
  457. // [rgcbBookmarks] -- Size of bookmark(s)
  458. // [rgpBookmarks] -- Bookmark(s) to hash.
  459. // [rgHashedValues] -- Hash(s) returned here.
  460. // [rgRowStatus] -- Row fetch statuses returned here
  461. //
  462. // History: 03-Apr-95 KyleP Created.
  463. //
  464. // Notes: Need to have Ole DB error handling here because errors are being
  465. // translated.
  466. //
  467. //----------------------------------------------------------------------------
  468. STDMETHODIMP CScrollableSorted::Hash( HCHAPTER hChapter,
  469. DBBKMARK cBookmarks,
  470. const DBBKMARK rgcbBookmarks[],
  471. const BYTE * rgpBookmarks[],
  472. DBHASHVALUE rgHashedValues[],
  473. DBROWSTATUS rgRowStatus[] )
  474. {
  475. _DBErrorObj.ClearErrorInfo();
  476. SCODE sc = S_OK;
  477. ULONG cSuccessfulHashes = 0;
  478. Win4Assert( 0 == hChapter && "Chapter support not yet implemented" );
  479. Win4Assert( rgHashedValues != 0 );
  480. // We ignore error conditions returned on calls to individual bookmarks to be
  481. // able to process all the bookmarks. That means invalid arguments will never
  482. // be detected and reported without this explicit validation.
  483. if (0 == rgHashedValues || (cBookmarks && (0 == rgcbBookmarks || 0 == rgpBookmarks )))
  484. {
  485. vqDebugOut((DEB_IERROR, "CScrollableSorted::Hash: Invalid Argument(s)\n"));
  486. return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetLocate);
  487. }
  488. TRY
  489. {
  490. ULONG partition = 0xFFFFFFFF / _rowset._cChild;
  491. for ( ULONG i = 0; i < cBookmarks; i++ )
  492. {
  493. //
  494. // Special bookmarks hash 'as-is'.
  495. //
  496. if ( rgcbBookmarks[i] == 1 )
  497. {
  498. rgHashedValues[i] = (ULONG)*rgpBookmarks[i];
  499. continue;
  500. }
  501. // This throws, so we need the try/catch around it
  502. CDistributedBookmark bmk( rgcbBookmarks[i],
  503. rgpBookmarks[i],
  504. _rowset._cbBookmark,
  505. _rowset._cChild );
  506. BYTE const * pBmk = bmk.Get();
  507. DBBKMARK cbBmk = bmk.GetSize();
  508. DBHASHVALUE hash;
  509. ULONG cErrs = 0;
  510. DBROWSTATUS * pErr = 0;
  511. sc = Get(bmk.Index())->Hash( 0, 1, &cbBmk, &pBmk, &hash,
  512. (rgRowStatus == 0) ? 0 : &rgRowStatus[i]
  513. );
  514. if ( FAILED(sc) )
  515. {
  516. continue; // continue processing other bookmarks
  517. }
  518. rgHashedValues[i] = hash % partition + partition * bmk.Index();
  519. cSuccessfulHashes++;
  520. }
  521. }
  522. CATCH( CException, e )
  523. {
  524. sc = e.GetErrorCode();
  525. vqDebugOut(( DEB_ERROR, "CScrollableSorted::Hash caught exception 0x%x\n", sc ));
  526. }
  527. END_CATCH
  528. // if we see an error other than DB_E_ERRORSOCCURRED, pass it straight through
  529. if (FAILED(sc) && sc != DB_E_ERRORSOCCURRED)
  530. return _DBErrorObj.PostHResult(sc, IID_IRowsetLocate);
  531. if (cSuccessfulHashes == cBookmarks)
  532. return S_OK;
  533. if (cSuccessfulHashes > 0) // and not all bookmarks were successfully processed
  534. sc = DB_S_ERRORSOCCURRED;
  535. else // no hashes were successfully processed
  536. sc = DB_E_ERRORSOCCURRED;
  537. return _DBErrorObj.PostHResult(sc, IID_IRowsetLocate);
  538. }
  539. //
  540. // IRowsetScroll methods
  541. //
  542. //+---------------------------------------------------------------------------
  543. //
  544. // Member: CScrollableSorted::GetApproximatePosition, public
  545. //
  546. // Synopsis: Determine approximate position of bookmark.
  547. //
  548. // Arguments: [hChapter] -- Chapter.
  549. // [cbBookmark] -- Size of [pBookmark]
  550. // [pBookmark] -- Bookmark of starting fetch position.
  551. // [pulPosition] -- Approximate offset from beginning returned
  552. // here.
  553. // [pulRows] -- Approximate count of rows in table
  554. // returned here.
  555. //
  556. // History: 03-Apr-95 KyleP Created.
  557. //
  558. // Notes: Need to have Ole DB error handling here because an exception
  559. // could result in a local error.
  560. //
  561. //----------------------------------------------------------------------------
  562. STDMETHODIMP CScrollableSorted::GetApproximatePosition( HCHAPTER hChapter,
  563. DBBKMARK cbBookmark,
  564. const BYTE * pBookmark,
  565. DBCOUNTITEM * pulPosition,
  566. DBCOUNTITEM * pulRows )
  567. {
  568. _DBErrorObj.ClearErrorInfo();
  569. SCODE sc = S_OK;
  570. Win4Assert( 0 == hChapter && "Chapter support not yet implemented" );
  571. if (cbBookmark !=0 && 0 == pBookmark )
  572. {
  573. vqDebugOut((DEB_IERROR,
  574. "CScrollableSorted::GetApproximatePosition: Invalid Argument(s)\n"));
  575. return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetScroll);
  576. }
  577. TRY
  578. {
  579. CDistributedBookmark bmk( cbBookmark,
  580. (BYTE const *)pBookmark,
  581. _rowset._cbBookmark,
  582. _rowset._cChild );
  583. if ( 0 != pulPosition )
  584. *pulPosition = 0;
  585. if ( 0 != pulRows )
  586. *pulRows = 0;
  587. DBCOUNTITEM ulTotalRows = 0;
  588. DBCOUNTITEM ulValidRows = 0;
  589. unsigned cValidBmk = 0;
  590. for ( unsigned i = 0; i < _rowset._cChild; i++ )
  591. {
  592. DBCOUNTITEM ulPosition = 0;
  593. DBCOUNTITEM ulRows;
  594. BOOL fValid;
  595. DBBKMARK cb;
  596. if ( bmk.IsValid( i ) )
  597. {
  598. cValidBmk++;
  599. fValid = TRUE;
  600. cb = bmk.GetSize();
  601. }
  602. else
  603. {
  604. fValid = FALSE;
  605. cb = 0;
  606. }
  607. sc = Get(i)->GetApproximatePosition( hChapter,
  608. cb,
  609. bmk.Get(i),
  610. (0 == pulPosition) ? 0 : &ulPosition,
  611. &ulRows );
  612. if ( FAILED(sc) )
  613. {
  614. vqDebugOut(( DEB_ERROR,
  615. "CScrollableSorted: GetApproximatePosition(%u) returned 0x%x\n",
  616. i, sc ));
  617. break;
  618. }
  619. if ( 0 != pulPosition )
  620. {
  621. Win4Assert( ulPosition <= ulRows );
  622. *pulPosition += fValid ? ulPosition : ulRows; // If not valid bookmark,
  623. // assume its at end
  624. }
  625. ulTotalRows += ulRows;
  626. if ( fValid )
  627. ulValidRows += ulRows;
  628. }
  629. if ( pulPosition && cValidBmk > 1 )
  630. {
  631. *pulPosition -= ( cValidBmk - 1 );
  632. }
  633. //
  634. // Special cases (speced in doc)
  635. //
  636. if ( cbBookmark == 1 && *(BYTE *)pBookmark == DBBMK_FIRST && 0 != pulPosition )
  637. *pulPosition = 1;
  638. else if ( cbBookmark == 1 && *(BYTE *)pBookmark == DBBMK_LAST && 0 != pulPosition )
  639. *pulPosition = ulTotalRows;
  640. //else if ( 0 != pulPosition && cValidBmk < _rowset._cChild )
  641. //{
  642. // *pulPosition += *pulPosition * (ulTotalRows - ulValidRows) / ulValidRows;
  643. //}
  644. if ( 0 != pulRows )
  645. *pulRows = ulTotalRows;
  646. }
  647. CATCH( CException, e )
  648. {
  649. sc = e.GetErrorCode();
  650. vqDebugOut(( DEB_ERROR,
  651. "CScrollableSorted::GetApproximatePosition caught exception 0x%x\n", sc ));
  652. }
  653. END_CATCH
  654. if (FAILED(sc))
  655. _DBErrorObj.PostHResult(sc, IID_IRowsetScroll);
  656. return sc;
  657. }
  658. //+---------------------------------------------------------------------------
  659. //
  660. // Member: CScrollableSorted::GetExactPosition, public
  661. //
  662. // Synopsis: Returns the exact position of a bookmark
  663. //
  664. // Arguments: [hChapter] -- chapter
  665. // [cbBookmark] -- size of bookmark
  666. // [pBookmark] -- bookmark
  667. // [pulPosition] -- return approx row number of bookmark
  668. // [pulRows] -- returns approx # of rows in cursor or
  669. // 1 + approx rows if not at quiescence
  670. //
  671. // Returns: SCODE - the status of the operation.
  672. //
  673. // Notes: We don't distinguish between exact and approximate position.
  674. // IRowsetExactScroll is implemented only because ADO 1.5
  675. // started QI'ing for it.
  676. //
  677. //--------------------------------------------------------------------------
  678. STDMETHODIMP CScrollableSorted::GetExactPosition(
  679. HCHAPTER hChapter,
  680. DBBKMARK cbBookmark,
  681. const BYTE * pBookmark,
  682. DBCOUNTITEM * pulPosition,
  683. DBCOUNTITEM * pulRows) /*const*/
  684. {
  685. return GetApproximatePosition( hChapter,
  686. cbBookmark,
  687. pBookmark,
  688. pulPosition,
  689. pulRows );
  690. }
  691. //+---------------------------------------------------------------------------
  692. //
  693. // Member: CScrollableSorted::GetRowsAtRatio, public
  694. //
  695. // Synopsis: Fetch rows from approximate position.
  696. //
  697. // Arguments: [hRegion] -- Watch region
  698. // Arguments: [hChapter] -- Chapter.
  699. // [ulNumerator] -- Numerator of position.
  700. // [ulDenominator] -- Denominator of position.
  701. // [cRows] -- Count of rows requested
  702. // [pcRowsObtained] -- Count of rows in [rrghRows] returned here.
  703. // [rrghRows] -- HROWs returned here.
  704. //
  705. // History: 03-Apr-95 KyleP Created.
  706. //
  707. // Notes: Need to have Ole DB error handling here because an exception
  708. // could result in a local error.
  709. //
  710. //----------------------------------------------------------------------------
  711. STDMETHODIMP CScrollableSorted::GetRowsAtRatio( HWATCHREGION hRegion,
  712. HCHAPTER hChapter,
  713. DBCOUNTITEM ulNumerator,
  714. DBCOUNTITEM ulDenominator,
  715. DBROWCOUNT cRows,
  716. DBCOUNTITEM * pcRowsObtained,
  717. HROW ** rrghRows )
  718. {
  719. _DBErrorObj.ClearErrorInfo();
  720. SCODE sc = S_OK;
  721. BOOL fAllocated = FALSE;
  722. Win4Assert( 0 == hChapter && "Chapter support not yet implemented" );
  723. if (0 == pcRowsObtained || 0 == rrghRows )
  724. {
  725. vqDebugOut((DEB_IERROR, "CScrollableSorted::GetRowsAtRatio: Invalid Argument(s)"));
  726. return _DBErrorObj.PostHResult(E_INVALIDARG, IID_IRowsetScroll);
  727. }
  728. TRY
  729. {
  730. *pcRowsObtained = 0;
  731. fAllocated = SetupFetch( cRows, rrghRows );
  732. //
  733. // Seek to position. Special cases are beginning and end of rowset.
  734. //
  735. Seek( ulNumerator, ulDenominator );
  736. sc = StandardFetch( cRows, pcRowsObtained, *rrghRows );
  737. if ( SUCCEEDED( sc ) &&
  738. !_rowset._xChildNotify.IsNull() &&
  739. *pcRowsObtained != 0 )
  740. {
  741. _rowset._xChildNotify->OnRowChange( *pcRowsObtained,
  742. *rrghRows,
  743. DBREASON_ROW_ACTIVATE,
  744. DBEVENTPHASE_DIDEVENT,
  745. TRUE);
  746. }
  747. }
  748. CATCH( CException, e )
  749. {
  750. sc = e.GetErrorCode();
  751. vqDebugOut(( DEB_ERROR, "CScrollableSorted::GetRowsAtRatio: Exception 0x%x\n", sc ));
  752. //
  753. // If we already have some rows, then we can't 'unfetch' them, so we have
  754. // to mask this error. Presumably it won't be transient and we'll get it
  755. // again later.
  756. //
  757. if ( *pcRowsObtained > 0 )
  758. {
  759. if (FAILED(sc))
  760. sc = DB_S_ROWLIMITEXCEEDED;
  761. }
  762. else if ( fAllocated )
  763. {
  764. CoTaskMemFree( *rrghRows );
  765. *rrghRows = 0;
  766. }
  767. }
  768. END_CATCH
  769. if (FAILED(sc))
  770. _DBErrorObj.PostHResult(sc, IID_IRowsetScroll);
  771. return( sc );
  772. }
  773. //+---------------------------------------------------------------------------
  774. //
  775. // Member: CScrollableSorted::CScrollableSorted, public
  776. //
  777. // Synopsis: Initialize rowset.
  778. //
  779. // Arguments: [pUnkOuter] -- outer unknown
  780. // [ppMyUnk] -- OUT: on return, filled with pointer to my
  781. // non-delegating IUnknown
  782. // [aChild] -- Array of child cursors (rowsets).
  783. // [cChild] -- Count of elements in [aChild].
  784. // [Props] -- Rowset properties.
  785. // [cCol] -- Number of original columns.
  786. // [Sort] -- Sort specification.
  787. //
  788. // History: 03-Apr-95 KyleP Created.
  789. //
  790. //----------------------------------------------------------------------------
  791. CScrollableSorted::CScrollableSorted( IUnknown * pUnkOuter,
  792. IUnknown ** ppMyUnk,
  793. IRowsetScroll ** aChild,
  794. unsigned cChild,
  795. CMRowsetProps const & Props,
  796. unsigned cCol,
  797. CSort const & Sort,
  798. CAccessorBag & aAccessors ) :
  799. _rowset( 0, ppMyUnk,
  800. (IRowset **)aChild, cChild, Props, cCol + 1, // Add 1 col. for bookmark
  801. Sort, aAccessors ),
  802. _apPosCursor(cChild),
  803. _heap( cChild ),
  804. #pragma warning(disable : 4355) // 'this' in a constructor
  805. _impIUnknown(this),
  806. _DBErrorObj( * ((IUnknown *) (IRowset *) this), _mutex )
  807. #pragma warning(default : 4355) // 'this' in a constructor
  808. {
  809. unsigned iChild = 0;
  810. TRY
  811. {
  812. _DBErrorObj.SetInterfaceArray(cRowsetErrorIFs, apRowsetErrorIFs);
  813. _rowset._RowManager.TrackSiblings( cChild );
  814. if (pUnkOuter)
  815. _pControllingUnknown = pUnkOuter;
  816. else
  817. _pControllingUnknown = (IUnknown * )&_impIUnknown;
  818. //
  819. // Create accessors
  820. //
  821. for ( ; iChild < _rowset._cChild; iChild++ )
  822. {
  823. _apPosCursor[iChild] = new CMiniPositionableCache( iChild,
  824. Get(iChild),
  825. Sort.Count(),
  826. _rowset._bindSort.GetPointer(),
  827. _rowset._cbSort,
  828. _rowset._iColumnBookmark,
  829. _rowset._cbBookmark );
  830. }
  831. //
  832. // Initialize heap
  833. //
  834. _heap.Init( &_rowset._Comparator, GetCacheArray() );
  835. *ppMyUnk = ((IUnknown *)&_impIUnknown);
  836. (*ppMyUnk)->AddRef();
  837. }
  838. CATCH( CException, e )
  839. {
  840. long lChild = iChild;
  841. for ( lChild--; lChild >= 0; lChild-- )
  842. delete _apPosCursor[lChild];
  843. RETHROW();
  844. }
  845. END_CATCH
  846. END_CONSTRUCTION( CScrollableSorted );
  847. }
  848. //+---------------------------------------------------------------------------
  849. //
  850. // Member: CScrollableSorted::~CScrollableSorted, private
  851. //
  852. // Synopsis: Destructor.
  853. //
  854. // History: 03-Apr-95 KyleP Created.
  855. //
  856. //----------------------------------------------------------------------------
  857. CScrollableSorted::~CScrollableSorted()
  858. {
  859. unsigned ii;
  860. for ( ii = _rowset._cChild ; ii > 0; ii-- )
  861. {
  862. delete _apPosCursor[ii-1];
  863. }
  864. }
  865. //+---------------------------------------------------------------------------
  866. //
  867. // Member: CScrollableSorted::_GetMaxPrevRowChild, private
  868. //
  869. // Synopsis: From all the child cursors, return the index which has the
  870. // max. prev. row. If none of the cursor has a prev. row (they are
  871. // all at the top), the index returned is the total no. of cursors.
  872. //
  873. // Arguments: none
  874. //
  875. // History: 10-Sep-98 VikasMan Created.
  876. //
  877. //----------------------------------------------------------------------------
  878. unsigned CScrollableSorted::_GetMaxPrevRowChild()
  879. {
  880. unsigned iMaxRowChild = _rowset._cChild;
  881. unsigned iChild;
  882. BYTE * pMaxPrevData;
  883. BYTE * pPrevData;
  884. for ( iChild = 0; iChild < _rowset._cChild; iChild++ )
  885. {
  886. pMaxPrevData = _apPosCursor[iChild]->GetPrevData();
  887. if ( pMaxPrevData )
  888. {
  889. iMaxRowChild = iChild;
  890. break;
  891. }
  892. }
  893. for ( iChild++; iChild < _rowset._cChild; iChild++ )
  894. {
  895. pPrevData = _apPosCursor[iChild]->GetPrevData();
  896. if ( pPrevData )
  897. {
  898. if ( _rowset._Comparator.IsLT(
  899. pMaxPrevData,
  900. _apPosCursor[iMaxRowChild]->DataLength(),
  901. _apPosCursor[iMaxRowChild]->Index(),
  902. pPrevData,
  903. _apPosCursor[iChild]->DataLength(),
  904. _apPosCursor[iChild]->Index() ) )
  905. {
  906. iMaxRowChild = iChild;
  907. pMaxPrevData = pPrevData;
  908. }
  909. }
  910. }
  911. return iMaxRowChild;
  912. }
  913. //+---------------------------------------------------------------------------
  914. //
  915. // Member: CScrollableSorted::Seek, private
  916. //
  917. // Synopsis: Position heap to specified bookmark + offset.
  918. //
  919. // Arguments: [cbBookmark] -- Size of [pbBookmark].
  920. // [pbBookmark] -- Bookmark.
  921. // [lOffset] -- Offset from bookmark.
  922. //
  923. // History: 03-Apr-95 KyleP Created.
  924. //
  925. //----------------------------------------------------------------------------
  926. SCODE CScrollableSorted::Seek( DBBKMARK cbBookmark, BYTE const * pbBookmark, DBROWOFFSET lOffset )
  927. {
  928. unsigned cValid = _rowset._cChild;
  929. unsigned i;
  930. SCODE sc = S_OK;
  931. //
  932. // Special case: Beginning of table
  933. //
  934. if ( cbBookmark == 1 && *pbBookmark == DBBMK_FIRST )
  935. {
  936. //
  937. // Seek all cursors to beginning of table.
  938. //
  939. for ( i = 0; i < cValid; i++ )
  940. _apPosCursor[ i ]->Seek( cbBookmark, pbBookmark );
  941. }
  942. else if (cbBookmark == 1 && *pbBookmark == DBBMK_LAST)
  943. {
  944. //
  945. // First Seek all cursors to end of table.
  946. //
  947. for ( i = 0; i < cValid; i++ )
  948. _apPosCursor[ i ]->Seek( cbBookmark, pbBookmark );
  949. for ( i = 0; i < cValid; i++ )
  950. {
  951. if ( _apPosCursor[i]->IsAtEnd() )
  952. {
  953. _apPosCursor[i]->Seek(1); // pushes it beyond the end
  954. //
  955. // Move unseekable cursor to end of array.
  956. //
  957. cValid--;
  958. SwapCursor(i, cValid);
  959. i--;
  960. }
  961. }
  962. // Find the cursor with the maximum value
  963. unsigned iCurWithMaxVal = 0;
  964. for (i = 1; i < cValid; i++)
  965. {
  966. if ( _rowset._Comparator.IsLT( _apPosCursor[iCurWithMaxVal]-> GetData(),
  967. _apPosCursor[iCurWithMaxVal]->DataLength(),
  968. _apPosCursor[iCurWithMaxVal]->Index(),
  969. _apPosCursor[i]->GetData(),
  970. _apPosCursor[i]->DataLength(),
  971. _apPosCursor[i]->Index() )
  972. )
  973. iCurWithMaxVal = i;
  974. }
  975. // Now set all cursors except _apPosCursor[iCurWithMaxVal] to end of table
  976. for (i = 0; i < cValid; i++)
  977. if (i != iCurWithMaxVal)
  978. _apPosCursor[i]->Seek(1); // pushes it beyond the end
  979. }
  980. else
  981. {
  982. CDistributedBookmark bmk( cbBookmark,
  983. pbBookmark,
  984. _rowset._cbBookmark,
  985. _rowset._cChild );
  986. //
  987. // Seek target cursor. We have to do this one first.
  988. //
  989. for ( unsigned iTarget = 0; iTarget < _rowset._cChild; iTarget++ )
  990. {
  991. if ( _apPosCursor[iTarget]->Index() == (int)bmk.Index() )
  992. {
  993. _apPosCursor[ iTarget ]->Seek( bmk.GetSize(), bmk.Get() );
  994. break;
  995. }
  996. }
  997. //
  998. // Seek child cursor(s), other than target.
  999. //
  1000. for ( i = 0; i < cValid; i++ )
  1001. {
  1002. //
  1003. // Ignore target cursor
  1004. //
  1005. if ( i == iTarget )
  1006. continue;
  1007. //
  1008. // Seek to 'hint' position.
  1009. //
  1010. _apPosCursor[i]->FlushCache();
  1011. _apPosCursor[i]->SetCacheSize( 1 );
  1012. PMiniRowCache::ENext next;
  1013. if ( bmk.IsValid( _apPosCursor[i]->Index() ) )
  1014. next = _apPosCursor[i]->Seek( bmk.GetSize(), bmk.Get( _apPosCursor[i]->Index() ) );
  1015. else
  1016. {
  1017. BYTE bStart = DBBMK_FIRST;
  1018. next = _apPosCursor[i]->Seek( sizeof(bStart), &bStart );
  1019. }
  1020. //
  1021. // And adjust so that the cursor is positioned just after the target cursor.
  1022. //
  1023. if ( next != PMiniRowCache::Ok ||
  1024. AdjustPosition( i, iTarget ) != PMiniRowCache::Ok )
  1025. {
  1026. BYTE bEnd = DBBMK_LAST;
  1027. _apPosCursor[i]->Seek( sizeof(bEnd), &bEnd );
  1028. _apPosCursor[i]->Seek(1); // pushes it beyond the end
  1029. //
  1030. // Move unseekable cursor to end of array.
  1031. //
  1032. cValid--;
  1033. SwapCursor(i, cValid);
  1034. if ( cValid == iTarget )
  1035. {
  1036. iTarget = i;
  1037. }
  1038. i--;
  1039. }
  1040. }
  1041. }
  1042. for ( i = 0; i<cValid; i++ )
  1043. {
  1044. if ( _apPosCursor[i]->IsAtEnd() )
  1045. {
  1046. _apPosCursor[i]->Seek(1); // pushes it beyond the end
  1047. //
  1048. // Move unseekable cursor to end of array.
  1049. //
  1050. cValid--;
  1051. SwapCursor(i, cValid);
  1052. i--;
  1053. }
  1054. }
  1055. _heap.ReInit( cValid );
  1056. if ( lOffset < 0 )
  1057. {
  1058. cValid = _rowset._cChild;
  1059. // Load Previous rows from all valid
  1060. for ( i = 0; i < cValid; i++ )
  1061. {
  1062. _apPosCursor[i]->LoadPrevRowData();
  1063. }
  1064. unsigned iMaxRowChild;
  1065. BOOL fReInit = FALSE;
  1066. for( ;; )
  1067. {
  1068. // Find out which rowset has the max. prev. row
  1069. iMaxRowChild = _GetMaxPrevRowChild();
  1070. if ( iMaxRowChild >= _rowset._cChild )
  1071. {
  1072. break;
  1073. // no previous row
  1074. }
  1075. fReInit = TRUE;
  1076. // Move the rowset with the max. prev. row back
  1077. _apPosCursor[iMaxRowChild]->MovePrev();
  1078. if ( 0 == ++lOffset )
  1079. {
  1080. break;
  1081. }
  1082. // Reload prev. row for the rowset which we moved back
  1083. _apPosCursor[iMaxRowChild]->LoadPrevRowData();
  1084. }
  1085. if ( fReInit)
  1086. {
  1087. for ( i = 0; i<cValid; i++ )
  1088. {
  1089. if ( _apPosCursor[i]->IsAtEnd() )
  1090. {
  1091. _apPosCursor[i]->Seek(1); // pushes it beyond the end
  1092. //
  1093. // Move unseekable cursor to end of array.
  1094. //
  1095. cValid--;
  1096. SwapCursor(i, cValid);
  1097. i--;
  1098. }
  1099. }
  1100. _heap.ReInit( cValid );
  1101. }
  1102. if ( lOffset < 0 )
  1103. {
  1104. // NTRAID#DB-NTBUG9-84055-2000/07/31-dlee Failed distribued query row fetches don't restore previous seek position
  1105. // Do we need to reset the position of rowset back to
  1106. // where it was before call to Seek ?
  1107. sc = DB_E_BADSTARTPOSITION;
  1108. }
  1109. }
  1110. else
  1111. {
  1112. for ( ; lOffset > 0; lOffset-- )
  1113. {
  1114. //
  1115. // Release top HROW.
  1116. //
  1117. HROW hrow = _heap.Top()->GetHROW();
  1118. SCODE sc2 = Get( _heap.Top()->Index() )->ReleaseRows( 1, &hrow, 0, 0, 0 );
  1119. Win4Assert( SUCCEEDED(sc2) );
  1120. PMiniRowCache::ENext next = _heap.Next();
  1121. Win4Assert( PMiniRowCache::NotNow != next );
  1122. if ( PMiniRowCache::EndOfRows == next )
  1123. break;
  1124. }
  1125. }
  1126. return sc;
  1127. }
  1128. //+---------------------------------------------------------------------------
  1129. //
  1130. // Member: CScrollableSorted::AdjustPosition, private
  1131. //
  1132. // Synopsis: Adjust position of iChild-th cursor to just after the
  1133. // iTarget-th cursor.
  1134. //
  1135. // Arguments: [iChild] -- Index of child in _apPosCursor.
  1136. // [iTarget] -- Index of target in _apPosCursor.
  1137. //
  1138. // Returns: Seek result.
  1139. //
  1140. // History: 03-Apr-95 KyleP Created.
  1141. //
  1142. //----------------------------------------------------------------------------
  1143. PMiniRowCache::ENext CScrollableSorted::AdjustPosition( unsigned iChild, int iTarget )
  1144. {
  1145. vqDebugOut(( DEB_ITRACE, "Child: %d data = 0x%x, size = %u\n",
  1146. _apPosCursor[iChild]->Index(),
  1147. _apPosCursor[iChild]-> GetData(),
  1148. _apPosCursor[iChild]->DataLength() ));
  1149. vqDebugOut(( DEB_ITRACE, "Target: %d data = 0x%x, size = %u\n",
  1150. _apPosCursor[iTarget]->Index(),
  1151. _apPosCursor[iTarget]-> GetData(),
  1152. _apPosCursor[iTarget]->DataLength() ));
  1153. PMiniRowCache::ENext next; // Used to report error states.
  1154. int iJump; // Seek offset (positive or negative) from starting point.
  1155. int iNextInc; // Next seek increment.
  1156. int iDirection; // Direction of seek from initial position to target.
  1157. if ( _rowset._Comparator.IsLT( _apPosCursor[iTarget]-> GetData(),
  1158. _apPosCursor[iTarget]->DataLength(),
  1159. _apPosCursor[iTarget]->Index(),
  1160. _apPosCursor[iChild]->GetData(),
  1161. _apPosCursor[iChild]->DataLength(),
  1162. _apPosCursor[iChild]->Index() ) )
  1163. {
  1164. next = InitialSeek( iChild, iTarget, -1, iJump, iNextInc, iDirection );
  1165. //
  1166. // Running into end (actually beginning) here is ok.
  1167. //
  1168. if ( next == PMiniRowCache::EndOfRows )
  1169. return PMiniRowCache::Ok;
  1170. }
  1171. else
  1172. next = InitialSeek( iChild, iTarget, 1, iJump, iNextInc, iDirection );
  1173. if ( next != PMiniRowCache::Ok )
  1174. return next;
  1175. //
  1176. // At this point, iChild is at least 1 row < iTarget (or 1 row > iTarget).
  1177. //
  1178. vqDebugOut(( DEB_ITRACE, "Final positioning:\n" ));
  1179. while ( iNextInc > 0 )
  1180. {
  1181. iJump += (iNextInc * iDirection);
  1182. vqDebugOut(( DEB_ITRACE, "Backward %d\n", -iJump ));
  1183. PMiniRowCache::ENext next = _apPosCursor[iChild]->Seek( iJump );
  1184. iNextInc /= 2;
  1185. if ( _rowset._Comparator.IsLT( _apPosCursor[iTarget]-> GetData(),
  1186. _apPosCursor[iTarget]->DataLength(),
  1187. _apPosCursor[iTarget]->Index(),
  1188. _apPosCursor[iChild]->GetData(),
  1189. _apPosCursor[iChild]->DataLength(),
  1190. _apPosCursor[iChild]->Index() ) )
  1191. iDirection = -1;
  1192. else
  1193. iDirection = 1;
  1194. }
  1195. //
  1196. // Either the row we are on is correct, or off by one.
  1197. //
  1198. if ( _rowset._Comparator.IsLT( _apPosCursor[iChild]-> GetData(),
  1199. _apPosCursor[iChild]->DataLength(),
  1200. _apPosCursor[iChild]->Index(),
  1201. _apPosCursor[iTarget]->GetData(),
  1202. _apPosCursor[iTarget]->DataLength(),
  1203. _apPosCursor[iTarget]->Index() ) )
  1204. return _apPosCursor[iChild]->Seek( iJump + 1 );
  1205. else
  1206. return PMiniRowCache::Ok;
  1207. }
  1208. //+---------------------------------------------------------------------------
  1209. //
  1210. // Member: CScrollableSorted::InitialSeek, private
  1211. //
  1212. // Synopsis: Worker routine for AdjustPosition. Binary searches until
  1213. // iChild is at least one row on the opposite side of iTarget
  1214. // from where it started.
  1215. //
  1216. // Arguments: [iChild] -- Index of child in _apPosCursor.
  1217. // [iTarget] -- Index of target in _apPosCursor.
  1218. // [InitialDirection] -- Direction to start moving.
  1219. // [iJump] -- Offset from starting point returned
  1220. // here.
  1221. // [iNextInc] -- Size of last jump returned here.
  1222. // [iDirection] -- Direction of last jump returned here.
  1223. //
  1224. // Returns: Seek result.
  1225. //
  1226. // History: 03-Apr-95 KyleP Created.
  1227. //
  1228. //----------------------------------------------------------------------------
  1229. PMiniRowCache::ENext CScrollableSorted::InitialSeek( unsigned iChild,
  1230. int iTarget,
  1231. int InitialDirection,
  1232. int & iJump,
  1233. int & iNextInc,
  1234. int & iDirection )
  1235. {
  1236. //
  1237. // If the child starts out > target, then go backward until we pass
  1238. // the target, and then forward until *just* after target, otherwise
  1239. // do the opposite. The goal is to go a known distance past the target
  1240. // so we can seek back towards it logarithmically.
  1241. //
  1242. iJump = 1 * InitialDirection;
  1243. iNextInc = 1 * InitialDirection;
  1244. int LTa;
  1245. int LTb;
  1246. if ( InitialDirection == 1 )
  1247. {
  1248. LTa = iChild;
  1249. LTb = iTarget;
  1250. }
  1251. else
  1252. {
  1253. LTa = iTarget;
  1254. LTb = iChild;
  1255. }
  1256. vqDebugOut(( DEB_ITRACE, "Initial positioning:\n" ));
  1257. PMiniRowCache::ENext next = PMiniRowCache::EndOfRows;
  1258. while ( iNextInc != 0 )
  1259. {
  1260. do
  1261. {
  1262. vqDebugOut(( DEB_ITRACE, "%s %u\n", iJump < 0 ? "Backward" : "Forward",
  1263. iJump < 0 ? -iJump : iJump ));
  1264. next = _apPosCursor[iChild]->Seek( iJump );
  1265. if ( 0 == iNextInc )
  1266. {
  1267. Win4Assert( next != PMiniRowCache::EndOfRows );
  1268. next = PMiniRowCache::EndOfRows;
  1269. break;
  1270. }
  1271. switch ( next )
  1272. {
  1273. case PMiniRowCache::Ok:
  1274. iNextInc *= 2;
  1275. break;
  1276. case PMiniRowCache::EndOfRows:
  1277. iJump -= iNextInc;
  1278. iNextInc /= 2;
  1279. iJump += iNextInc;
  1280. break;
  1281. default:
  1282. return next;
  1283. }
  1284. } while ( next == PMiniRowCache::EndOfRows );
  1285. Win4Assert( iJump * InitialDirection >= 0 );
  1286. //if ( iJump * InitialDirection > 0 &&
  1287. if ( iJump != 0 &&
  1288. _rowset._Comparator.IsLT( _apPosCursor[LTa]-> GetData(),
  1289. _apPosCursor[LTa]->DataLength(),
  1290. _apPosCursor[LTa]->Index(),
  1291. _apPosCursor[LTb]->GetData(),
  1292. _apPosCursor[LTb]->DataLength(),
  1293. _apPosCursor[LTb]->Index() ) )
  1294. iJump += iNextInc;
  1295. else
  1296. break;
  1297. }
  1298. iNextInc = ((iJump*InitialDirection) / 2) + 1;
  1299. iDirection = -InitialDirection;
  1300. return next;
  1301. }
  1302. //+---------------------------------------------------------------------------
  1303. //
  1304. // Member: CScrollableSorted::Seek, private
  1305. //
  1306. // Synopsis: Position heap to approximate position.
  1307. //
  1308. // Arguments: [ulNumerator] -- Numerator.
  1309. // [ulDenominator] -- Denominator.
  1310. //
  1311. // History: 03-Apr-95 KyleP Created.
  1312. //
  1313. //----------------------------------------------------------------------------
  1314. void CScrollableSorted::Seek( DBCOUNTITEM ulNumerator, DBCOUNTITEM ulDenominator )
  1315. {
  1316. unsigned cValid = _rowset._cChild;
  1317. //
  1318. // Special case: Beginning of table
  1319. //
  1320. if ( 0 == ulNumerator )
  1321. {
  1322. //
  1323. // Seek all cursors to beginning of table.
  1324. //
  1325. BYTE bmkStart = DBBMK_FIRST;
  1326. for ( unsigned i = 0; i < _rowset._cChild; i++ )
  1327. _apPosCursor[ i ]->Seek( sizeof(bmkStart), &bmkStart );
  1328. _heap.ReInit( cValid );
  1329. }
  1330. //
  1331. // Special case: End of table
  1332. //
  1333. else if ( ulNumerator == ulDenominator )
  1334. {
  1335. //
  1336. // Seek all cursors to end of table.
  1337. //
  1338. BYTE bmkEnd = DBBMK_LAST;
  1339. for ( unsigned i = 0; i < _rowset._cChild; i++ )
  1340. _apPosCursor[ i ]->Seek( sizeof(bmkEnd), &bmkEnd );
  1341. _heap.ReInit( cValid );
  1342. }
  1343. //
  1344. // Normal case: Middle of table
  1345. //
  1346. else
  1347. {
  1348. //
  1349. // Seek all cursors to ratio.
  1350. //
  1351. // Get the total # of rows
  1352. DBCOUNTITEM ulRows = 0;
  1353. SCODE sc = GetApproximatePosition( NULL,
  1354. 0,
  1355. NULL,
  1356. NULL,
  1357. &ulRows );
  1358. if ( SUCCEEDED (sc ) && ulRows > 0 )
  1359. {
  1360. DBROWOFFSET lSeekPos = (( ulNumerator * ulRows ) / ulDenominator );
  1361. BYTE bmk;
  1362. if ( (lSeekPos * 100 / ulRows) > 50 )
  1363. {
  1364. // seek from bottom
  1365. bmk = DBBMK_LAST;
  1366. lSeekPos = lSeekPos - (LONG) ulRows + 1;
  1367. }
  1368. else
  1369. {
  1370. // seek from top
  1371. bmk = DBBMK_FIRST;
  1372. }
  1373. sc = Seek( sizeof(bmk), &bmk, lSeekPos );
  1374. }
  1375. #if 0
  1376. for ( unsigned i = 0; i < _rowset._cChild; i++ )
  1377. {
  1378. _apPosCursor[ i ]->FlushCache();
  1379. _apPosCursor[ i ]->SetCacheSize( 1 );
  1380. _apPosCursor[ i ]->Seek( ulNumerator, ulDenominator );
  1381. }
  1382. //
  1383. // Heapify, then pick the cursor ulNumerator / ulDenominator from
  1384. // top of the heap.
  1385. //
  1386. _heap.ReInit( _rowset._cChild );
  1387. _heap.NthToTop( _rowset._cChild * ulNumerator / ulDenominator );
  1388. unsigned iTarget = 0;
  1389. //
  1390. // Adjust position of all other cursors to follow target.
  1391. //
  1392. for ( i = 0; i < cValid; i++ )
  1393. {
  1394. //
  1395. // Ignore target cursor
  1396. //
  1397. if ( i == iTarget )
  1398. continue;
  1399. if ( AdjustPosition( i, iTarget ) != PMiniRowCache::Ok )
  1400. {
  1401. //
  1402. // Move unseekable cursor to end of array.
  1403. //
  1404. cValid--;
  1405. SwapCursor(i, cValid);
  1406. i--;
  1407. }
  1408. }
  1409. #endif
  1410. }
  1411. }
  1412. //+---------------------------------------------------------------------------
  1413. //
  1414. // Member: CScrollableSorted::SetupFetch, private
  1415. //
  1416. // Synopsis: Common operations before seek in Get* routines.
  1417. //
  1418. // Arguments: [cRows] -- Number of rows requested.
  1419. // [rrghRows] -- Rows returned here. May have to allocate.
  1420. //
  1421. // Returns: TRUE if *rrghRows was allocated.
  1422. //
  1423. // History: 03-Apr-95 KyleP Created.
  1424. //
  1425. //----------------------------------------------------------------------------
  1426. BOOL CScrollableSorted::SetupFetch( DBROWCOUNT cRows, HROW * rrghRows[] )
  1427. {
  1428. //
  1429. // We may have reached some temporary condition such as
  1430. // DB_S_ROWLIMITEXCEEDED on the last pass. Iterate until we
  1431. // have a valid heap.
  1432. //
  1433. PMiniRowCache::ENext next = _heap.Validate();
  1434. if ( next == PMiniRowCache::NotNow )
  1435. {
  1436. THROW( CException( DB_E_ROWLIMITEXCEEDED ) );
  1437. }
  1438. //
  1439. // We may have to allocate memory, if the caller didn't.
  1440. //
  1441. BOOL fAllocated = FALSE;
  1442. if ( 0 == *rrghRows )
  1443. {
  1444. *rrghRows = (HROW *)CoTaskMemAlloc( (ULONG) ( abs(((LONG) cRows)) * sizeof(HROW) ) );
  1445. fAllocated = TRUE;
  1446. }
  1447. if ( 0 == *rrghRows )
  1448. {
  1449. vqDebugOut(( DEB_ERROR, "CScrollableSorted::SetupFetch: Out of memory.\n" ));
  1450. THROW( CException( E_OUTOFMEMORY ) );
  1451. }
  1452. return fAllocated;
  1453. }
  1454. //+---------------------------------------------------------------------------
  1455. //
  1456. // Member: CScrollableSorted::SetupFetch, private
  1457. //
  1458. // Synopsis: Common operations before seek in Get* routines.
  1459. //
  1460. // Arguments: [cRows] -- Number of rows requested.
  1461. // [pcRowsObtained] -- Count actually fetched.
  1462. // [rghRows] -- Rows returned here. Already allocated
  1463. // if needed.
  1464. //
  1465. // Returns: Status code.
  1466. //
  1467. // History: 03-Apr-95 KyleP Created.
  1468. //
  1469. //----------------------------------------------------------------------------
  1470. SCODE CScrollableSorted::StandardFetch( DBROWCOUNT cRows,
  1471. DBCOUNTITEM * pcRowsObtained,
  1472. HROW rghRows[] )
  1473. {
  1474. SCODE sc = S_OK;
  1475. int iDir = 1;
  1476. if ( cRows < 0 )
  1477. {
  1478. cRows = -cRows;
  1479. iDir = -1;
  1480. }
  1481. unsigned ucRows = (unsigned) cRows;
  1482. //
  1483. // Adjust cache size if necessary.
  1484. //
  1485. _heap.AdjustCacheSize( ucRows );
  1486. //
  1487. // Fetch from top of heap.
  1488. //
  1489. while ( *pcRowsObtained < ucRows )
  1490. {
  1491. //
  1492. // We may be entirely out of rows.
  1493. //
  1494. if ( _heap.IsHeapEmpty() )
  1495. {
  1496. sc = DB_S_ENDOFROWSET;
  1497. break;
  1498. }
  1499. (rghRows)[*pcRowsObtained] =
  1500. _rowset._RowManager.Add( _heap.Top()->Index(),
  1501. _heap.TopHROWs() );
  1502. (*pcRowsObtained)++;
  1503. //
  1504. // Fetch the next row.
  1505. //
  1506. PMiniRowCache::ENext next = _heap.Next( iDir );
  1507. if ( CMiniRowCache::NotNow == next )
  1508. {
  1509. sc = DB_S_ROWLIMITEXCEEDED;
  1510. break;
  1511. }
  1512. }
  1513. return sc;
  1514. }
  1515. //+---------------------------------------------------------------------------
  1516. //
  1517. // Member: CScrollableSorted::RatioFinished, public
  1518. //
  1519. // Synopsis: Ratio finished for asynchronously populated rowsets.
  1520. //
  1521. // Arguments: [pulDenominator] -- Denominator returned here.
  1522. // [pulNumerator] -- Numerator returned here.
  1523. // [pcRows] -- Count of rows returned here
  1524. // [pfNewRows] -- TRUE if rows added since last call.
  1525. //
  1526. // History: 03-Apr-95 KyleP Created.
  1527. //
  1528. // Notes: Need to have Ole DB error handling here because an exception
  1529. // could result in a local error.
  1530. //
  1531. //----------------------------------------------------------------------------
  1532. STDMETHODIMP CScrollableSorted::RatioFinished( DBCOUNTITEM * pulDenominator,
  1533. DBCOUNTITEM * pulNumerator,
  1534. DBCOUNTITEM * pcRows,
  1535. BOOL * pfNewRows )
  1536. {
  1537. _DBErrorObj.ClearErrorInfo();
  1538. IRowsetAsynch * pra = 0;
  1539. SCODE scResult = S_OK;
  1540. *pulDenominator = 0;
  1541. *pulNumerator = 0;
  1542. *pcRows = 0;
  1543. *pfNewRows = FALSE;
  1544. for ( unsigned iChild = 0; iChild < _rowset._cChild; iChild++ )
  1545. {
  1546. DBCOUNTITEM ulDenom;
  1547. DBCOUNTITEM ulNum;
  1548. DBCOUNTITEM cRows;
  1549. BOOL fNew;
  1550. SCODE sc = Get(iChild)->QueryInterface( IID_IRowsetAsynch, (void **) &pra );
  1551. if ( SUCCEEDED(sc) )
  1552. {
  1553. sc = pra->RatioFinished( &ulDenom, &ulNum, &cRows, &fNew );
  1554. pra->Release();
  1555. }
  1556. if ( FAILED(sc) && E_NOTIMPL != sc && E_NOINTERFACE != sc )
  1557. {
  1558. vqDebugOut(( DEB_ERROR,
  1559. "IRowsetAsynch::RatioFinished(child %d) returned 0x%x\n",
  1560. iChild, sc ));
  1561. scResult = sc;
  1562. break;
  1563. }
  1564. if ( SUCCEEDED(sc) )
  1565. {
  1566. Win4Assert( *pulDenominator + ulDenom > *pulDenominator );
  1567. *pulDenominator += ulDenom;
  1568. *pulNumerator += ulNum;
  1569. *pcRows += cRows;
  1570. *pfNewRows = *pfNewRows || fNew;
  1571. }
  1572. }
  1573. if ( 0 == *pulDenominator )
  1574. scResult = E_NOTIMPL;
  1575. if (FAILED(scResult))
  1576. _DBErrorObj.PostHResult(scResult, IID_IRowsetAsynch);
  1577. return( scResult );
  1578. }
  1579. //+---------------------------------------------------------------------------
  1580. //
  1581. // Member: CScrollableSorted::Stop, public
  1582. //
  1583. // Synopsis: Stop population of asynchronously populated rowsets.
  1584. //
  1585. // Arguments: - None -
  1586. //
  1587. // History: 16 Jun 95 Alanw Created.
  1588. //
  1589. // Notes: Need to have Ole DB error handling here because errors are
  1590. // being translated.
  1591. //
  1592. //----------------------------------------------------------------------------
  1593. STDMETHODIMP CScrollableSorted::Stop( )
  1594. {
  1595. _DBErrorObj.ClearErrorInfo();
  1596. IRowsetAsynch * pra = 0;
  1597. SCODE scResult = S_OK;
  1598. for ( unsigned iChild = 0; iChild < _rowset._cChild; iChild++ )
  1599. {
  1600. SCODE sc = Get(iChild)->QueryInterface( IID_IRowsetAsynch,
  1601. (void **) &pra );
  1602. if ( SUCCEEDED(sc) )
  1603. {
  1604. sc = pra->Stop( );
  1605. pra->Release();
  1606. }
  1607. if ( FAILED(sc) && (S_OK == scResult ||
  1608. E_NOTIMPL != sc ||
  1609. E_NOINTERFACE != sc))
  1610. {
  1611. vqDebugOut(( DEB_ERROR,
  1612. "IRowsetAsynch::Stop (child %d) returned 0x%x\n",
  1613. iChild, sc ));
  1614. scResult = sc;
  1615. }
  1616. }
  1617. if (FAILED(scResult))
  1618. _DBErrorObj.PostHResult(scResult, IID_IRowsetAsynch);
  1619. return( scResult );
  1620. }
  1621. //
  1622. // IDbAsynchStatus methods
  1623. //
  1624. //+---------------------------------------------------------------------------
  1625. //
  1626. // Member: CScrollableSorted::Abort, public
  1627. //
  1628. // Synopsis: Cancels an asynchronously executing operation.
  1629. //
  1630. // Arguments: [hChapter] -- chapter which should restart
  1631. // [ulOperation] -- operation for which status is being requested
  1632. //
  1633. // Returns: SCODE error code
  1634. //
  1635. // History: 03 Sep 1998 VikasMan Created
  1636. //
  1637. //----------------------------------------------------------------------------
  1638. STDMETHODIMP CScrollableSorted::Abort(
  1639. HCHAPTER hChapter,
  1640. ULONG ulOperation )
  1641. {
  1642. _DBErrorObj.ClearErrorInfo();
  1643. SCODE scResult = S_OK;
  1644. XInterface<IDBAsynchStatus> xIDBAsynchStatus;
  1645. for ( unsigned iChild = 0; iChild < _rowset._cChild; iChild++ )
  1646. {
  1647. SCODE sc = Get(iChild)->QueryInterface( IID_IDBAsynchStatus,
  1648. xIDBAsynchStatus.GetQIPointer() );
  1649. if ( SUCCEEDED( sc ) )
  1650. {
  1651. sc = xIDBAsynchStatus->Abort( hChapter, ulOperation );
  1652. if ( S_OK == scResult )
  1653. {
  1654. scResult = sc;
  1655. }
  1656. }
  1657. xIDBAsynchStatus.Free();
  1658. }
  1659. return scResult;
  1660. }
  1661. //+---------------------------------------------------------------------------
  1662. //
  1663. // Member: CScrollableSorted::GetStatus, public
  1664. //
  1665. // Synopsis: Returns the status of an asynchronously executing operation.
  1666. //
  1667. // Arguments: [hChapter] -- chapter which should restart
  1668. // [ulOperation] -- operation for which status is being requested
  1669. //
  1670. // Returns: SCODE error code
  1671. //
  1672. // History: 03 Sep 1998 VikasMan Created
  1673. //
  1674. //----------------------------------------------------------------------------
  1675. STDMETHODIMP CScrollableSorted::GetStatus(
  1676. HCHAPTER hChapter,
  1677. DBASYNCHOP ulOperation,
  1678. DBCOUNTITEM * pulProgress,
  1679. DBCOUNTITEM * pulProgressMax,
  1680. DBASYNCHPHASE * pulAsynchPhase,
  1681. LPOLESTR * ppwszStatusText )
  1682. {
  1683. _DBErrorObj.ClearErrorInfo();
  1684. SCODE scResult = S_OK;
  1685. XInterface<IDBAsynchStatus> xIDBAsynchStatus;
  1686. if ( pulProgress )
  1687. *pulProgress = 0;
  1688. if ( pulProgressMax )
  1689. *pulProgressMax = 0;
  1690. if ( pulAsynchPhase )
  1691. *pulAsynchPhase = DBASYNCHPHASE_COMPLETE;
  1692. if ( ppwszStatusText )
  1693. *ppwszStatusText = 0;
  1694. XCoMem<OLECHAR> xStatusText;
  1695. double dRatio = 0;
  1696. for ( unsigned iChild = 0; iChild < _rowset._cChild; iChild++ )
  1697. {
  1698. SCODE sc = Get(iChild)->QueryInterface( IID_IDBAsynchStatus,
  1699. xIDBAsynchStatus.GetQIPointer() );
  1700. if ( SUCCEEDED( sc ) )
  1701. {
  1702. DBCOUNTITEM ulProgress, ulProgressMax;
  1703. DBASYNCHPHASE ulAsynchPhase;
  1704. scResult = xIDBAsynchStatus->GetStatus ( hChapter,
  1705. ulOperation,
  1706. &ulProgress,
  1707. &ulProgressMax,
  1708. &ulAsynchPhase,
  1709. 0 == iChild ?
  1710. ppwszStatusText : 0 );
  1711. if ( S_OK != scResult )
  1712. {
  1713. return scResult;
  1714. }
  1715. if ( 0 == iChild && ppwszStatusText )
  1716. {
  1717. xStatusText.Set( *ppwszStatusText );
  1718. }
  1719. if ( ulProgressMax )
  1720. {
  1721. dRatio += ( (double)ulProgress / (double)ulProgressMax );
  1722. }
  1723. if ( pulAsynchPhase && *pulAsynchPhase != DBASYNCHPHASE_POPULATION )
  1724. *pulAsynchPhase = ulAsynchPhase;
  1725. }
  1726. xIDBAsynchStatus.Free();
  1727. }
  1728. DWORD dwNum = 0;
  1729. DWORD dwDen = 0;
  1730. if ( dRatio )
  1731. {
  1732. Win4Assert( _rowset._cChild );
  1733. dRatio /= _rowset._cChild;
  1734. dwDen = 1;
  1735. while ( dRatio < 1.0 )
  1736. {
  1737. dRatio *= 10;
  1738. dwDen *= 10;
  1739. }
  1740. dwNum = (DWORD)dRatio;
  1741. }
  1742. if ( pulProgress )
  1743. *pulProgress = dwNum;
  1744. if ( pulProgressMax )
  1745. *pulProgressMax = dwDen;
  1746. if ( SUCCEEDED( scResult ) )
  1747. {
  1748. // Let memory pass thru to the client
  1749. xStatusText.Acquire();
  1750. }
  1751. return scResult;
  1752. }
  1753. //
  1754. // IRowsetWatchRegion methods
  1755. //
  1756. //+---------------------------------------------------------------------------
  1757. //
  1758. // Member: CScrollableSorted::Refresh, public
  1759. //
  1760. // Synopsis: Implementation of IRowsetWatchRegion::Refresh. Calls refresh on
  1761. // all the child rowsets
  1762. //
  1763. // Arguments: pChangesObtained
  1764. // prgChanges
  1765. //
  1766. // Returns: Always returns DB_S_TOOMAYCHANGES
  1767. //
  1768. // History: 03 Sep 1998 VikasMan Created
  1769. //
  1770. //----------------------------------------------------------------------------
  1771. STDMETHODIMP CScrollableSorted::Refresh(
  1772. DBCOUNTITEM* pChangesObtained,
  1773. DBROWWATCHCHANGE** prgChanges )
  1774. {
  1775. _DBErrorObj.ClearErrorInfo();
  1776. for ( unsigned iChild = 0; iChild < _rowset._cChild; iChild++ )
  1777. {
  1778. if ( _rowset._xArrChildRowsetWatchRegion[iChild].GetPointer() )
  1779. {
  1780. _rowset._xArrChildRowsetWatchRegion[iChild]->Refresh( pChangesObtained, prgChanges );
  1781. }
  1782. }
  1783. *pChangesObtained = 0;
  1784. return DB_S_TOOMANYCHANGES;
  1785. }