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.

1060 lines
26 KiB

  1. /*****************************************************************************
  2. * *
  3. * BTINSERT.C *
  4. * *
  5. * Copyright (C) Microsoft Corporation 1989 - 1994. *
  6. * All Rights reserved. *
  7. * *
  8. ******************************************************************************
  9. * *
  10. * Module Intent *
  11. * *
  12. * Btree insertion functions and helpers. *
  13. * *
  14. ******************************************************************************
  15. * *
  16. * Current Owner: BinhN *
  17. * *
  18. *****************************************************************************/
  19. /*****************************************************************************
  20. *
  21. * Revision History: Created 04/20/89 by JohnSc
  22. *
  23. * 08/21/90 JohnSc autodocified
  24. * 04-Feb-1991 JohnSc set ghCache to NULL after freeing it
  25. * 3/05/97 erinfox Change errors to HRESULTS
  26. *****************************************************************************/
  27. static char s_aszModule[]= __FILE__; /* For error report */
  28. #include <mvopsys.h>
  29. #include <orkin.h>
  30. #include <iterror.h>
  31. #include <misc.h>
  32. #include <wrapstor.h>
  33. #include <_mvutil.h>
  34. /*************************************************************************
  35. *
  36. * INTERNAL GLOBAL FUNCTIONS
  37. *
  38. * All of them should be declared far, unless they are known to be called
  39. * in the same segment. They should be prototyped in some include file
  40. *
  41. *************************************************************************/
  42. PUBLIC BK PASCAL FAR BkAlloc(QBTHR, LPVOID);
  43. PUBLIC HRESULT PASCAL FAR RcInsertInternal(BK, KEY, SHORT, QBTHR);
  44. PUBLIC HRESULT PASCAL FAR RcSplitLeaf(QCB, QCB, QBTHR);
  45. PUBLIC void PASCAL FAR SplitInternal(QCB, QCB, QBTHR, QW);
  46. /*************************************************************************
  47. *
  48. * API FUNCTIONS
  49. * Those functions should be exported in a .DEF file
  50. *************************************************************************/
  51. PUBLIC HRESULT PASCAL FAR EXPORT_API RcUpdateHbt(HBT, KEY, QV);
  52. PUBLIC HRESULT PASCAL FAR EXPORT_API RcInsertHbt(HBT , KEY, QV);
  53. /***************************************************************************
  54. *
  55. * @doc INTERNAL
  56. *
  57. * @func BK PASCAL FAR | BkAlloc |
  58. * Make up a new BK.
  59. *
  60. * @parm QBTHR | qbthr |
  61. * Pointer to B-tree strucuture.
  62. * qbthr->bkFree - head of free list, unless it's bkNil.
  63. * qbthr->bkEOF - use this if bkFree == bkNil (then ++)
  64. *
  65. * @rdesc a valid BK or bkNil if file is hosed
  66. * args OUT: qbthr->bkFree or qbthr->bkEOF will be different
  67. *
  68. * @comm Side Effects: btree file may grow
  69. * Method: Use the head of the free list. If the free list is empty,
  70. * there are no holes in the file and we carve a new one.
  71. *
  72. ***************************************************************************/
  73. PUBLIC BK PASCAL FAR BkAlloc(QBTHR qbthr, PHRESULT phr)
  74. {
  75. BK bk;
  76. if (qbthr->bth.bkFree == bkNil)
  77. bk = (qbthr->bth.bkEOF++);
  78. else
  79. {
  80. FILEOFFSET foSeek;
  81. bk = qbthr->bth.bkFree;
  82. foSeek=FoFromBk(bk,qbthr);
  83. if (!FoEquals(FoSeekHf(qbthr->hf, foSeek, wFSSeekSet, phr),foSeek ))
  84. return bkNil;
  85. if (LcbReadHf(qbthr->hf, &(qbthr->bth.bkFree), (LONG)sizeof(BK),
  86. phr) != (LONG)sizeof(BK))
  87. return bkNil;
  88. }
  89. return bk;
  90. }
  91. /***************************************************************************
  92. *
  93. * @doc INTERNAL
  94. *
  95. * @func HRESULT PASCAL FAR | RcSplitLeaf |
  96. * Split a leaf node when a new key won't fit into it.
  97. *
  98. * @parm QCB | qcbOld |
  99. * the leaf to be split
  100. *
  101. * @parm QCB | qcbNew |
  102. * a leaf buffer to get half the contents of qcbOld;
  103. * qcbNew->bk must be set
  104. *
  105. * @parm QBTHR | qbthr |
  106. * Pointer to B-tree structure
  107. *
  108. * @rdesc S_OK, E_OUTOFMEMORY
  109. * args OUT: qcbOld - cbSlack, cKeys, bkPrev, bkNext updated
  110. * qcbNew - about half of the old contents of qcbOld
  111. * get put here. cbSlack, cKeys set.
  112. * qbthr - qbthr->bkFirst and bkLast can be changed
  113. * globals OUT: rcBtreeError
  114. *
  115. * @comm ompressed keys not implemented
  116. * For fixed length keys and records, could just split at
  117. * middle key rather than scanning from the beginning.
  118. *
  119. * The new block is always after the old block. This is
  120. * why we don't have to adjust pointers to the old block
  121. * (i.e. qbthr->bth.bkFirst).
  122. *
  123. ***************************************************************************/
  124. PUBLIC HRESULT PASCAL FAR RcSplitLeaf(QCB qcbOld, QCB qcbNew, QBTHR qbthr)
  125. {
  126. SHORT iOK, iNext, iHalf, cbKey, cbRec, cKeys;
  127. QB q;
  128. HANDLE gh;
  129. QCB qcb;
  130. HRESULT rc;
  131. SHORT cbCopyToNew;
  132. assert(qcbOld->bFlags & fCacheValid);
  133. iOK = iNext = 0;
  134. q = qcbOld->db.rgbBlock + 2 * sizeof(BK);
  135. iHalf = (qbthr->bth.cbBlock / 2) - sizeof(BK);
  136. for (cKeys = qcbOld->db.cKeys; ;)
  137. {
  138. assert(cKeys > 0);
  139. cbKey = CbSizeKey((KEY)q, qbthr, TRUE);
  140. cbRec = CbSizeRec(q + cbKey, qbthr);
  141. iNext = iOK + cbKey + cbRec;
  142. if (iNext > iHalf) break;
  143. q += cbKey + cbRec;
  144. iOK = iNext;
  145. cKeys--;
  146. }
  147. // >>>> if compressed, expand first key here
  148. // Note that the total block size includes the disk block struct.
  149. // The new slack in the old block should equal the number of bytes
  150. // copied to the new block. The amount being copied was previously too large
  151. // by 4 bytes.
  152. cbCopyToNew = qbthr->bth.cbBlock - sizeof(DISK_BLOCK) + 1 - (iOK + 2 * sizeof(BK));
  153. QVCOPY(qcbNew->db.rgbBlock + 2 * sizeof(BK),
  154. qcbOld->db.rgbBlock + 2 * sizeof(BK) + iOK, (LONG)cbCopyToNew);
  155. qcbNew->db.cKeys = cKeys;
  156. qcbOld->db.cKeys -= cKeys;
  157. qcbNew->db.cbSlack = qcbOld->db.cbSlack + iOK;
  158. qcbOld->db.cbSlack = cbCopyToNew;
  159. qcbOld->bFlags |= fCacheDirty | fCacheValid;
  160. qcbNew->bFlags = fCacheDirty | fCacheValid;
  161. SetBkPrev(qcbNew, qcbOld->bk);
  162. SetBkNext(qcbNew, BkNext(qcbOld));
  163. SetBkNext(qcbOld, qcbNew->bk);
  164. if (BkNext(qcbNew) == bkNil)
  165. qbthr->bth.bkLast = qcbNew->bk;
  166. else
  167. {
  168. /* set new->next->prev = new; */
  169. if ((gh = _GLOBALALLOC(GMEM_ZEROINIT| GMEM_SHARE| GMEM_MOVEABLE,
  170. (LONG)CbCacheBlock(qbthr))) == NULL)
  171. return (E_OUTOFMEMORY);
  172. qcb = _GLOBALLOCK(gh);
  173. qcb->bk = BkNext(qcbNew);
  174. if ((rc = FReadBlock(qcb, qbthr)) != S_OK)
  175. {
  176. _GLOBALUNLOCK(gh);
  177. _GLOBALFREE(gh);
  178. return rc;
  179. }
  180. SetBkPrev(qcb, qcbNew->bk);
  181. if ((rc = RcWriteBlock(qcb, qbthr)) != S_OK)
  182. {
  183. _GLOBALUNLOCK(gh);
  184. _GLOBALFREE(gh);
  185. return rc;
  186. }
  187. _GLOBALUNLOCK(gh);
  188. _GLOBALFREE(gh);
  189. }
  190. return (S_OK);
  191. }
  192. /***************************************************************************
  193. *
  194. * @doc INTERNAL
  195. *
  196. * @func void PASCAL FAR | SplitInternal |
  197. *
  198. * Split an internal node node when a new key won't fit into it.
  199. * Old node gets BKs and KEYs up to the first key that won't
  200. * fit in half the block size. (Leave that key there with iKey
  201. * pointing at it). The new block gets the BKs and KEYs after
  202. * that key.
  203. *
  204. * @parm QCB | qcbOld |
  205. * the block to split
  206. *
  207. * @parm QCB | qcbNew |
  208. * pointer to a qcb
  209. *
  210. * @parm QBTHR | qbthr |
  211. * Pointer to B-tree structure
  212. *
  213. * @rdesc qcbNew - keys and records copied to this buffer.
  214. * cbSlack, cKeys set.
  215. * qcbOld - cbSlack and cKeys updated.
  216. * qi - index into qcbOld->db.rgbBlock of discriminating key
  217. *
  218. * @comm compressed keys not implemented
  219. * *qi is index of a key that is not valid for qcbOld. This
  220. * key gets copied into the parent node.
  221. *
  222. ***************************************************************************/
  223. PUBLIC void PASCAL FAR SplitInternal(QCB qcbOld, QCB qcbNew, QBTHR qbthr, QW qi)
  224. {
  225. SHORT iOK, iNext, iHalf, cb, cKeys, cbTotal;
  226. QB q;
  227. assert(qcbOld->bFlags & fCacheValid);
  228. iOK = iNext = sizeof(BK);
  229. q = qcbOld->db.rgbBlock + sizeof(BK);
  230. iHalf = qbthr->bth.cbBlock / 2;
  231. for (cKeys = qcbOld->db.cKeys; ; cKeys--)
  232. {
  233. assert(cKeys > 0);
  234. cb = CbSizeKey((KEY)q, qbthr, TRUE) + sizeof(BK);
  235. iNext = iOK + cb;
  236. if (iNext > iHalf) break;
  237. q += cb;
  238. iOK = iNext;
  239. }
  240. // have to expand first key if compressed
  241. cbTotal = qbthr->bth.cbBlock - sizeof(DISK_BLOCK) + 1;
  242. QVCOPY(qcbNew->db.rgbBlock,
  243. qcbOld->db.rgbBlock + iNext - sizeof(BK),
  244. (LONG)cbTotal - qcbOld->db.cbSlack - iNext + sizeof(BK));
  245. *qi = iOK;
  246. qcbNew->db.cKeys = cKeys - 1;
  247. qcbOld->db.cKeys -= cKeys;
  248. qcbNew->db.cbSlack = qcbOld->db.cbSlack + iNext - sizeof(BK);
  249. qcbOld->db.cbSlack = cbTotal - iOK;
  250. qcbOld->bFlags |= fCacheDirty | fCacheValid;
  251. qcbNew->bFlags = fCacheDirty | fCacheValid;
  252. }
  253. /***************************************************************************
  254. *
  255. * @doc INTERNAL
  256. *
  257. * @func HRESULT PASCAL FAR | RcInsertInternal |
  258. * Insert a bk and key into an internal block.
  259. * state IN: We've just done a lookup, so all ancestors are cached.
  260. * Cache is locked.
  261. *
  262. *
  263. * @parm BK | bk | BK to insert
  264. *
  265. * @parm KEY | key | least key in bk
  266. *
  267. * @parm SHORT | wLevel |
  268. * level of the block we're inserting
  269. *
  270. * @parm QNTHR | qbthr |
  271. * btree header
  272. *
  273. *
  274. * @rdesc S_OK, E_OUTOFMEMORY
  275. * args OUT: qbthr->cLevels - incremented if root is split
  276. * qbthr->ghCache, qbthr->qCache - may change if root is
  277. * split and cache therefore grows
  278. * state OUT: Cache locked, all ancestors cached.
  279. *
  280. * @comm
  281. * Status: compressed keys unimplemented
  282. * Method: Works recursively. Splits root if need be.
  283. * Side Effects: Cache could be different after this call than it
  284. * was before.
  285. * Pointers or handles to it from before this call could be
  286. * invalid. Use qbthr->ghCache or qbthr->qCache to be safe.
  287. *
  288. ***************************************************************************/
  289. PUBLIC HRESULT PASCAL FAR RcInsertInternal(BK bk, KEY key, SHORT wLevel, QBTHR qbthr)
  290. {
  291. QCB qcb, qcbNew, qcbRoot;
  292. WORD iKey;
  293. SHORT cLevels, cbKey, cbCBlock = CbCacheBlock(qbthr);
  294. QB qb;
  295. HANDLE gh, ghOldCache;
  296. KEY keyNew;
  297. BK bkRoot;
  298. HRESULT rc = S_OK;
  299. UINT_PTR iKeySav = 0;
  300. ERRB errb;
  301. cbKey = CbSizeKey(key, qbthr, TRUE);
  302. if (wLevel == 0)
  303. {
  304. /* inserting another block at root level */
  305. // allocate new root bk;
  306. bkRoot = BkAlloc(qbthr, &errb);
  307. if (bkRoot == bkNil)
  308. {
  309. return errb;
  310. }
  311. // grow cache by one cache block;
  312. qbthr->bth.cLevels++;
  313. gh = _GLOBALALLOC(GMEM_ZEROINIT| GMEM_SHARE| GMEM_MOVEABLE,
  314. (LONG)cbCBlock * qbthr->bth.cLevels);
  315. if (gh == NULL)
  316. return (E_OUTOFMEMORY);
  317. qb = _GLOBALLOCK(gh);
  318. QVCOPY(qb + cbCBlock, qbthr->qCache,
  319. (LONG)cbCBlock * (qbthr->bth.cLevels - 1));
  320. /* Since key points into the cache if this is a recursive */
  321. /* call, we can't free the old cache until a bit later. */
  322. ghOldCache = qbthr->ghCache;
  323. qbthr->ghCache = gh;
  324. qbthr->qCache = qb;
  325. // put old root bk, key, bk into new root block;
  326. qcbRoot = (QCB)qbthr->qCache;
  327. qcbRoot->bk = bkRoot;
  328. qcbRoot->bFlags = fCacheDirty | fCacheValid;
  329. qcbRoot->db.cbSlack = qbthr->bth.cbBlock - sizeof(DISK_BLOCK) + 1
  330. - (2 * sizeof(BK) + cbKey);
  331. qcbRoot->db.cKeys = 1;
  332. *(BK FAR *)(qcbRoot->db.rgbBlock) = qbthr->bth.bkRoot;
  333. QVCOPY(qcbRoot->db.rgbBlock + sizeof(BK), (QB)key, (LONG)cbKey);
  334. /* OK, now we're done with key, so we can safely free the */
  335. /* old cache. */
  336. _GLOBALUNLOCK(ghOldCache);
  337. _GLOBALFREE(ghOldCache);
  338. *(BK FAR *)(qcbRoot->db.rgbBlock + sizeof(BK) + cbKey) = bk;
  339. qbthr->bth.bkRoot = bkRoot;
  340. return S_OK;
  341. }
  342. qcb = QCacheBlock(qbthr, wLevel - 1);
  343. if ((SHORT)(cbKey + sizeof(BK)) >= qcb->db.cbSlack) {
  344. // new key and BK won't fit in block
  345. // split the block;
  346. if ((gh = _GLOBALALLOC(GMEM_ZEROINIT| GMEM_SHARE| GMEM_MOVEABLE,
  347. (LONG)CbCacheBlock(qbthr))) == NULL)
  348. return (E_OUTOFMEMORY);
  349. qcbNew = _GLOBALLOCK(gh);
  350. if ((qcbNew->bk = BkAlloc(qbthr, &errb)) == bkNil) {
  351. _GLOBALUNLOCK(gh);
  352. _GLOBALFREE(gh);
  353. return errb;
  354. }
  355. SplitInternal(qcb, qcbNew, qbthr, &iKey);
  356. keyNew = (KEY)qcb->db.rgbBlock + iKey;
  357. cLevels = qbthr->bth.cLevels;
  358. if (wLevel < cLevels - 1)
  359. {
  360. /* This is a recursive call (the arg bk doesn't refer to a leaf.)
  361. ** This means that the arg key points into the cache, so it will
  362. ** be invalid if the root is split.
  363. ** Verify with some asserts that key points into the cache.
  364. */
  365. assert((QB)key > qbthr->qCache + CbCacheBlock(qbthr));
  366. assert((QB)key < qbthr->qCache + (wLevel + 1) * CbCacheBlock(qbthr));
  367. /* Save the offset of key into the cache block. Recall that key
  368. ** is the first invalid key in an internal node that has just
  369. ** been split. It points into the part that is still in the cache.
  370. */
  371. iKeySav = (QB)key - (qbthr->qCache + wLevel * CbCacheBlock(qbthr));
  372. }
  373. if ((rc = RcInsertInternal(qcbNew->bk, (KEY)qcb->db.rgbBlock + iKey,
  374. (SHORT)(wLevel - 1), qbthr)) != S_OK)
  375. {
  376. _GLOBALUNLOCK(gh);
  377. _GLOBALFREE(gh);
  378. return rc;
  379. }
  380. /* RcInsertInternal() can change cache and qbthr->bth.cLevels */
  381. if (cLevels != qbthr->bth.cLevels)
  382. {
  383. assert(cLevels + 1 == qbthr->bth.cLevels);
  384. wLevel++;
  385. qcb = QCacheBlock(qbthr, wLevel - 1);
  386. keyNew = (KEY)qcb->db.rgbBlock + iKey;
  387. /* Also restore the arg "key" if it pointed into the cache.
  388. */
  389. if (iKeySav)
  390. {
  391. key = (KEY)(qbthr->qCache + wLevel * CbCacheBlock(qbthr)
  392. + iKeySav);
  393. }
  394. }
  395. /* find out which block to put new key and bk in, and cache it */
  396. if (WCmpKey(key, keyNew, qbthr) < 0)
  397. {
  398. if ((rc = RcWriteBlock(qcbNew, qbthr)) != S_OK)
  399. {
  400. _GLOBALUNLOCK(gh);
  401. _GLOBALFREE(gh);
  402. return rc;
  403. }
  404. }
  405. else
  406. {
  407. // write old block and cache the new one
  408. if ((rc = RcWriteBlock(qcb, qbthr)) != S_OK)
  409. {
  410. _GLOBALUNLOCK(gh);
  411. _GLOBALFREE(gh);
  412. return rc;
  413. }
  414. QVCOPY(qcb, qcbNew, (LONG)CbCacheBlock(qbthr));
  415. }
  416. _GLOBALUNLOCK(gh);
  417. _GLOBALFREE(gh);
  418. }
  419. // slide stuff over and insert the new key, bk
  420. /* get pos */
  421. if (qbthr->BkScanInternal(qcb->bk, key, (SHORT)(wLevel - 1), qbthr,
  422. &iKey, &errb) == bkNil)
  423. {
  424. return errb;
  425. }
  426. assert(iKey + cbKey + sizeof(BK) <
  427. qbthr->bth.cbBlock - sizeof(DISK_BLOCK) + 1);
  428. qb = (QB)(qcb->db.rgbBlock) + iKey;
  429. QVCOPY(qb + cbKey + sizeof(BK), qb,
  430. (LONG)qbthr->bth.cbBlock - iKey - qcb->db.cbSlack
  431. - sizeof(DISK_BLOCK) + 1);
  432. QVCOPY(qb, (QB)key, (LONG)cbKey);
  433. *(BK FAR *)(qb + cbKey) = bk;
  434. qcb->db.cKeys++;
  435. qcb->db.cbSlack -= (cbKey + sizeof(BK));
  436. qcb->bFlags |= fCacheDirty;
  437. return (S_OK);
  438. }
  439. /***************************************************************************
  440. *
  441. * @doc PUBLIC API
  442. *
  443. * @func HRESULT PASCAL FAR | RcInsertHbt |
  444. * Insert a key and record into a btree
  445. *
  446. * @parm HBT | hbt |
  447. * btree handle
  448. *
  449. * @parm KEY | key |
  450. * key to insert
  451. *
  452. * @parm QV | qvRec |
  453. * record associated with key to insert
  454. *
  455. * @rdesc S_OK, E_DUPLICATE (duplicate key)
  456. *
  457. * @comm
  458. * state IN: cache unlocked
  459. * state OUT: cache unlocked, all ancestor blocks cached
  460. * Notes: compressed keys unimplemented
  461. *
  462. ***************************************************************************/
  463. PUBLIC HRESULT PASCAL FAR EXPORT_API RcInsertHbt(HBT hbt, KEY key, QV qvRec)
  464. {
  465. QBTHR qbthr;
  466. HF hf;
  467. HRESULT rc;
  468. SHORT cbAdd, cbKey, cbRec;
  469. QCB qcbLeaf, qcbNew, qcb;
  470. HANDLE gh;
  471. KEY keyNew;
  472. QB qb;
  473. BTPOS btpos;
  474. ERRB errb;
  475. if ((qbthr = _GLOBALLOCK(hbt)) == NULL)
  476. return(E_INVALIDARG);
  477. hf = qbthr->hf;
  478. if ((rc = RcLookupByKeyAux(hbt, key, &btpos, NULL, TRUE)) == S_OK)
  479. {
  480. rc = E_DUPLICATE;
  481. exit0:
  482. _GLOBALUNLOCK (hbt);
  483. return rc;
  484. }
  485. /*
  486. After lookup, all nodes on path from root to correct leaf are
  487. guaranteed to be cached, with iKey valid.
  488. */
  489. if (rc != E_NOTEXIST)
  490. goto exit0;
  491. rc = S_OK;
  492. if (qbthr->bth.cLevels == 0)
  493. {
  494. // need to build a valid root block
  495. if ((qbthr->ghCache = _GLOBALALLOC(GMEM_ZEROINIT| GMEM_SHARE| GMEM_MOVEABLE,
  496. (LONG)CbCacheBlock(qbthr))) == NULL)
  497. {
  498. rc = E_OUTOFMEMORY;
  499. goto exit0;
  500. }
  501. qbthr->qCache = _GLOBALLOCK(qbthr->ghCache);
  502. qcb = (QCB)qbthr->qCache;
  503. qbthr->bth.cLevels = 1;
  504. qbthr->bth.bkFirst = qbthr->bth.bkLast = qbthr->bth.bkRoot =
  505. qcb->bk = BkAlloc(qbthr, &errb);
  506. if (qcb->bk == bkNil)
  507. {
  508. exit01:
  509. _GLOBALUNLOCK(qbthr->ghCache);
  510. _GLOBALFREE(qbthr->ghCache);
  511. qbthr->ghCache = NULL;
  512. rc = errb;
  513. goto exit0;
  514. }
  515. qcb->bFlags = fCacheDirty | fCacheValid;
  516. qcb->db.cbSlack = qbthr->bth.cbBlock - sizeof(DISK_BLOCK) + 1
  517. - 2 * sizeof(BK);
  518. qcb->db.cKeys = 0;
  519. SetBkPrev(qcb, bkNil);
  520. SetBkNext(qcb, bkNil);
  521. btpos.iKey = 2 * sizeof(BK);
  522. }
  523. else
  524. qbthr->qCache = _GLOBALLOCK(qbthr->ghCache);
  525. cbKey = CbSizeKey(key, qbthr, FALSE);
  526. cbRec = CbSizeRec(qvRec, qbthr);
  527. cbAdd = cbKey + cbRec;
  528. /* check to see if key and rec can fit harmoniously in a block */
  529. if (cbAdd > qbthr->bth.cbBlock / 2)
  530. {
  531. rc = E_FAIL;
  532. goto exit01;
  533. }
  534. qcbLeaf = QCacheBlock(qbthr, qbthr->bth.cLevels - 1);
  535. if (cbAdd > qcbLeaf->db.cbSlack)
  536. {
  537. /* new key and rec don't fit in leaf: split the block */
  538. /* create new leaf block */
  539. if ((gh = _GLOBALALLOC(GMEM_ZEROINIT| GMEM_SHARE| GMEM_MOVEABLE,
  540. (LONG)CbCacheBlock(qbthr))) == NULL)
  541. {
  542. rc = E_OUTOFMEMORY;
  543. goto exit01;
  544. }
  545. qcbNew = _GLOBALLOCK(gh);
  546. if ((qcbNew->bk = BkAlloc(qbthr, &errb)) == bkNil)
  547. {
  548. rc = errb;
  549. exit02:
  550. _GLOBALUNLOCK(gh);
  551. _GLOBALFREE(gh);
  552. goto exit01;
  553. }
  554. if ((rc = RcSplitLeaf(qcbLeaf, qcbNew, qbthr)) != S_OK)
  555. goto exit02;
  556. keyNew = (KEY)qcbNew->db.rgbBlock + 2 * sizeof(BK);
  557. /* insert new leaf into parent block */
  558. if ((rc = RcInsertInternal(qcbNew->bk,
  559. keyNew, (SHORT)(qbthr->bth.cLevels - 1), qbthr)) != S_OK)
  560. {
  561. goto exit02;
  562. }
  563. // InsertInternal can invalidate cache block pointers..
  564. qcbLeaf = QCacheBlock(qbthr, qbthr->bth.cLevels - 1);
  565. /* find out which leaf to put new key and rec in and cache it */
  566. if (WCmpKey(key, keyNew, qbthr) >= 0)
  567. {
  568. /* key goes in new block. Write out old one and cache the new one */
  569. if ((rc = RcWriteBlock(qcbLeaf, qbthr)) != S_OK)
  570. goto exit02;
  571. QVCOPY(qcbLeaf, qcbNew, (LONG)CbCacheBlock(qbthr));
  572. /* get pos */
  573. if ((rc = qbthr->RcScanLeaf(qcbLeaf->bk, key,
  574. (SHORT)(qbthr->bth.cLevels - 1),
  575. qbthr, NULL, &btpos)) != E_NOTEXIST)
  576. {
  577. if (rc == S_OK)
  578. rc = E_FAIL;
  579. goto exit02;
  580. }
  581. }
  582. else
  583. {
  584. /* key goes in old block. Write out the new one */
  585. if ((rc = RcWriteBlock(qcbNew, qbthr)) != S_OK)
  586. {
  587. goto exit02;
  588. }
  589. }
  590. _GLOBALUNLOCK(gh);
  591. _GLOBALFREE(gh);
  592. }
  593. /* insert new key and rec into the leaf block */
  594. assert(btpos.iKey + cbAdd <= (SHORT)(qbthr->bth.cbBlock -
  595. sizeof(DISK_BLOCK) + 1));
  596. qb = (QB)(qcbLeaf->db.rgbBlock) + btpos.iKey;
  597. QVCOPY(qb + cbAdd, qb, (LONG)qbthr->bth.cbBlock - btpos.iKey -
  598. qcbLeaf->db.cbSlack - sizeof(DISK_BLOCK) + 1);
  599. QVCOPY(qb, (QV)key, (LONG)cbKey);
  600. QVCOPY(qb + cbKey, qvRec, (LONG)cbRec);
  601. qcbLeaf->db.cKeys ++;
  602. qcbLeaf->db.cbSlack -= cbAdd;
  603. qcbLeaf->bFlags |= fCacheDirty;
  604. qbthr->bth.lcEntries++;
  605. qbthr->bth.bFlags |= fFSDirty;
  606. _GLOBALUNLOCK(qbthr->ghCache);
  607. _GLOBALUNLOCK(hbt);
  608. return S_OK;
  609. }
  610. PUBLIC HRESULT PASCAL FAR EXPORT_API RcInsertMacBrsHbt(HBT hbt, KEY key, QV qvRec)
  611. {
  612. QBTHR qbthr;
  613. HF hf;
  614. HRESULT rc;
  615. SHORT cbAdd, cbKey, cbRec;
  616. QCB qcbLeaf, qcbNew, qcb;
  617. HANDLE gh;
  618. KEY keyNew;
  619. QB qb;
  620. BTPOS btpos;
  621. ERRB errb;
  622. DWORD tmp;
  623. if ((qbthr = _GLOBALLOCK(hbt)) == NULL)
  624. return(E_INVALIDARG);
  625. hf = qbthr->hf;
  626. if ((rc = RcLookupByKeyAux(hbt, key, &btpos, NULL, TRUE)) == S_OK)
  627. {
  628. rc = E_DUPLICATE;
  629. exit0:
  630. _GLOBALUNLOCK (hbt);
  631. return rc;
  632. }
  633. /*
  634. After lookup, all nodes on path from root to correct leaf are
  635. guaranteed to be cached, with iKey valid.
  636. */
  637. if (rc != E_NOTEXIST)
  638. goto exit0;
  639. rc = S_OK;
  640. if (qbthr->bth.cLevels == 0)
  641. {
  642. // need to build a valid root block
  643. if ((qbthr->ghCache = _GLOBALALLOC(GMEM_ZEROINIT| GMEM_SHARE| GMEM_MOVEABLE,
  644. (LONG)CbCacheBlock(qbthr))) == NULL)
  645. {
  646. rc = E_OUTOFMEMORY;
  647. goto exit0;
  648. }
  649. qbthr->qCache = _GLOBALLOCK(qbthr->ghCache);
  650. qcb = (QCB)qbthr->qCache;
  651. qbthr->bth.cLevels = 1;
  652. qbthr->bth.bkFirst = qbthr->bth.bkLast = qbthr->bth.bkRoot =
  653. qcb->bk = BkAlloc(qbthr, &errb);
  654. if (qcb->bk == bkNil)
  655. {
  656. exit01:
  657. _GLOBALUNLOCK(qbthr->ghCache);
  658. _GLOBALFREE(qbthr->ghCache);
  659. qbthr->ghCache = NULL;
  660. rc = errb;
  661. goto exit0;
  662. }
  663. qcb->bFlags = fCacheDirty | fCacheValid;
  664. qcb->db.cbSlack = qbthr->bth.cbBlock - sizeof(DISK_BLOCK) + 1
  665. - 2 * sizeof(BK);
  666. qcb->db.cKeys = 0;
  667. SetBkPrev(qcb, bkNil);
  668. SetBkNext(qcb, bkNil);
  669. btpos.iKey = 2 * sizeof(BK);
  670. }
  671. else
  672. qbthr->qCache = _GLOBALLOCK(qbthr->ghCache);
  673. cbKey = CbSizeKey(key, qbthr, FALSE);
  674. cbRec = CbSizeRec(qvRec, qbthr);
  675. cbAdd = cbKey + cbRec;
  676. /* check to see if key and rec can fit harmoniously in a block */
  677. if (cbAdd > qbthr->bth.cbBlock / 2)
  678. {
  679. rc = E_FAIL;
  680. goto exit01;
  681. }
  682. qcbLeaf = QCacheBlock(qbthr, qbthr->bth.cLevels - 1);
  683. if (cbAdd > qcbLeaf->db.cbSlack)
  684. {
  685. /* new key and rec don't fit in leaf: split the block */
  686. /* create new leaf block */
  687. if ((gh = _GLOBALALLOC(GMEM_ZEROINIT| GMEM_SHARE| GMEM_MOVEABLE,
  688. (LONG)CbCacheBlock(qbthr))) == NULL)
  689. {
  690. rc = E_OUTOFMEMORY;
  691. goto exit01;
  692. }
  693. qcbNew = _GLOBALLOCK(gh);
  694. if ((qcbNew->bk = BkAlloc(qbthr, &errb)) == bkNil)
  695. {
  696. rc = errb;
  697. exit02:
  698. _GLOBALUNLOCK(gh);
  699. _GLOBALFREE(gh);
  700. goto exit01;
  701. }
  702. if ((rc = RcSplitLeaf(qcbLeaf, qcbNew, qbthr)) != S_OK)
  703. goto exit02;
  704. keyNew = (KEY)qcbNew->db.rgbBlock + 2 * sizeof(BK);
  705. /* insert new leaf into parent block */
  706. if ((rc = RcInsertInternal(qcbNew->bk,
  707. keyNew, (SHORT)(qbthr->bth.cLevels - 1), qbthr)) != S_OK)
  708. {
  709. goto exit02;
  710. }
  711. // InsertInternal can invalidate cache block pointers..
  712. qcbLeaf = QCacheBlock(qbthr, qbthr->bth.cLevels - 1);
  713. /* find out which leaf to put new key and rec in and cache it */
  714. if (WCmpKey(key, keyNew, qbthr) >= 0)
  715. {
  716. /* key goes in new block. Write out old one and cache the new one */
  717. if ((rc = RcWriteBlock(qcbLeaf, qbthr)) != S_OK)
  718. goto exit02;
  719. QVCOPY(qcbLeaf, qcbNew, (LONG)CbCacheBlock(qbthr));
  720. /* get pos */
  721. if ((rc = qbthr->RcScanLeaf(qcbLeaf->bk, key,
  722. (SHORT)(qbthr->bth.cLevels - 1),
  723. qbthr, NULL, &btpos)) != E_NOTEXIST)
  724. {
  725. if (rc == S_OK)
  726. rc = E_FAIL;
  727. goto exit02;
  728. }
  729. }
  730. else
  731. {
  732. /* key goes in old block. Write out the new one */
  733. if ((rc = RcWriteBlock(qcbNew, qbthr)) != S_OK)
  734. {
  735. goto exit02;
  736. }
  737. }
  738. _GLOBALUNLOCK(gh);
  739. _GLOBALFREE(gh);
  740. }
  741. /* insert new key and rec into the leaf block */
  742. assert(btpos.iKey + cbAdd <= (SHORT)(qbthr->bth.cbBlock -
  743. sizeof(DISK_BLOCK) + 1));
  744. qb = (QB)(qcbLeaf->db.rgbBlock) + btpos.iKey;
  745. QVCOPY(qb + cbAdd, qb, (LONG)qbthr->bth.cbBlock - btpos.iKey -
  746. qcbLeaf->db.cbSlack - sizeof(DISK_BLOCK) + 1);
  747. tmp = GETLONG((QV)key);
  748. QVCOPY(qb, (QV)&tmp, (LONG)cbKey);
  749. QVCOPY(qb + cbKey, qvRec, (LONG)cbRec);
  750. qcbLeaf->db.cKeys ++;
  751. qcbLeaf->db.cbSlack -= cbAdd;
  752. qcbLeaf->bFlags |= fCacheDirty;
  753. qbthr->bth.lcEntries++;
  754. qbthr->bth.bFlags |= fFSDirty;
  755. _GLOBALUNLOCK(qbthr->ghCache);
  756. _GLOBALUNLOCK(hbt);
  757. return S_OK;
  758. }
  759. /***************************************************************************
  760. *
  761. * @doc PUBLIC API
  762. *
  763. * @func HRESULT PASCAL FAR | RcUpdateHbt |
  764. * Update the record for an existing key. If the key wasn't
  765. * there already, it will not be inserted.
  766. *
  767. * @parm HBT | hbt |
  768. * Handle to B-tree structure
  769. *
  770. * @parm KEY | key |
  771. * key that already exists in btree
  772. *
  773. * @parm QV | qvRec |
  774. * new record
  775. *
  776. * @rdesc E_INVALIDARG, S_OK; ERR_NOTEXIST
  777. * args OUT: hbt - if key was in btree, it now has a new record.
  778. *
  779. * @comm
  780. * Method: If the records are the same size, copy the new over
  781. * the old. Otherwise, delete the old key/rec and insert the new.
  782. *
  783. ***************************************************************************/
  784. PUBLIC HRESULT PASCAL FAR EXPORT_API RcUpdateHbt(HBT hbt, KEY key, QV qvRec)
  785. {
  786. HRESULT rc;
  787. QBTHR qbthr;
  788. QB qb;
  789. QCB qcb;
  790. BTPOS btpos;
  791. WORD wSizeNew,wSizeOld;
  792. SHORT iSizeKey;
  793. if ((qbthr = _GLOBALLOCK(hbt)) == NULL)
  794. return E_INVALIDARG;
  795. if ((rc = RcLookupByKey(hbt, key, &btpos, NULL)) != S_OK)
  796. {
  797. _GLOBALUNLOCK(hbt);
  798. return rc;
  799. }
  800. qbthr->qCache = _GLOBALLOCK(qbthr->ghCache);
  801. if (qbthr->bth.cLevels <= 0 || qbthr->qCache == NULL)
  802. return (E_ASSERT);
  803. qcb = QCacheBlock(qbthr, qbthr->bth.cLevels - 1);
  804. qb = qcb->db.rgbBlock + btpos.iKey;
  805. qb += (iSizeKey=CbSizeKey((KEY)qb, qbthr, FALSE));
  806. if ((wSizeNew=CbSizeRec(qvRec, qbthr)) != (wSizeOld=CbSizeRec(qb, qbthr)))
  807. {
  808. // Today's the day we do something clever:
  809. if ((wSizeNew<wSizeOld) || ((wSizeNew>wSizeOld) && (qcb->db.cbSlack>=wSizeNew-wSizeOld)))
  810. {
  811. WORD wBytesAfterBlock = (WORD) (qbthr->bth.cbBlock-sizeof(qcb->db)+1 -qcb->db.cbSlack -btpos.iKey -wSizeOld -iSizeKey);
  812. QB qb1, qb2;
  813. qb1=qb+max(wSizeNew,wSizeOld)+wBytesAfterBlock;
  814. qb2=(QB)(&qcb->db)+qbthr->bth.cbBlock;
  815. assert(qb1<=qb2);
  816. if (wBytesAfterBlock)
  817. QVCOPY(qb+wSizeNew,qb+wSizeOld,(LONG)wBytesAfterBlock);
  818. QVCOPY(qb, qvRec, (LONG)wSizeNew);
  819. qcb->bFlags |= fCacheDirty;
  820. qbthr->bth.bFlags |= fFSDirty;
  821. qcb->db.cbSlack=qcb->db.cbSlack+wSizeOld-wSizeNew;
  822. _GLOBALUNLOCK(qbthr->ghCache);
  823. _GLOBALUNLOCK(hbt);
  824. }
  825. else
  826. {
  827. _GLOBALUNLOCK(qbthr->ghCache);
  828. _GLOBALUNLOCK(hbt);
  829. rc = RcDeleteHbt(hbt, key);
  830. if (rc == S_OK)
  831. {
  832. rc = RcInsertHbt(hbt, key, qvRec);
  833. }
  834. }
  835. }
  836. else
  837. {
  838. QVCOPY(qb, qvRec, (LONG)wSizeNew);
  839. qcb->bFlags |= fCacheDirty;
  840. qbthr->bth.bFlags |= fFSDirty;
  841. _GLOBALUNLOCK(qbthr->ghCache);
  842. _GLOBALUNLOCK(hbt);
  843. }
  844. return rc;
  845. }
  846. /* EOF */