hash_dup.c
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:21k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /*-
  2.  * See the file LICENSE for redistribution information.
  3.  *
  4.  * Copyright (c) 1996, 1997, 1998, 1999, 2000
  5.  * Sleepycat Software.  All rights reserved.
  6.  */
  7. /*
  8.  * Copyright (c) 1990, 1993, 1994
  9.  * The Regents of the University of California.  All rights reserved.
  10.  *
  11.  * This code is derived from software contributed to Berkeley by
  12.  * Margo Seltzer.
  13.  *
  14.  * Redistribution and use in source and binary forms, with or without
  15.  * modification, are permitted provided that the following conditions
  16.  * are met:
  17.  * 1. Redistributions of source code must retain the above copyright
  18.  *    notice, this list of conditions and the following disclaimer.
  19.  * 2. Redistributions in binary form must reproduce the above copyright
  20.  *    notice, this list of conditions and the following disclaimer in the
  21.  *    documentation and/or other materials provided with the distribution.
  22.  * 3. Neither the name of the University nor the names of its contributors
  23.  *    may be used to endorse or promote products derived from this software
  24.  *    without specific prior written permission.
  25.  *
  26.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  27.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  30.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  31.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  32.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  33.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  34.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  35.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  36.  * SUCH DAMAGE.
  37.  */
  38. #include "db_config.h"
  39. #ifndef lint
  40. static const char revid[] = "$Id: hash_dup.c,v 11.49 2000/12/21 21:54:35 margo Exp $";
  41. #endif /* not lint */
  42. /*
  43.  * PACKAGE:  hashing
  44.  *
  45.  * DESCRIPTION:
  46.  *      Manipulation of duplicates for the hash package.
  47.  *
  48.  * ROUTINES:
  49.  *
  50.  * External
  51.  *      __add_dup
  52.  * Internal
  53.  */
  54. #ifndef NO_SYSTEM_INCLUDES
  55. #include <sys/types.h>
  56. #include <string.h>
  57. #endif
  58. #include "db_int.h"
  59. #include "db_page.h"
  60. #include "hash.h"
  61. #include "btree.h"
  62. #include "txn.h"
  63. static int __ham_check_move __P((DBC *, u_int32_t));
  64. static int __ham_dcursor __P((DBC *, db_pgno_t, u_int32_t));
  65. /*
  66.  * Called from hash_access to add a duplicate key. nval is the new
  67.  * value that we want to add.  The flags correspond to the flag values
  68.  * to cursor_put indicating where to add the new element.
  69.  * There are 4 cases.
  70.  * Case 1: The existing duplicate set already resides on a separate page.
  71.  *    We return and let the common code handle this.
  72.  * Case 2: The element is small enough to just be added to the existing set.
  73.  * Case 3: The element is large enough to be a big item, so we're going to
  74.  *    have to push the set onto a new page.
  75.  * Case 4: The element is large enough to push the duplicate set onto a
  76.  *    separate page.
  77.  *
  78.  * PUBLIC: int __ham_add_dup __P((DBC *, DBT *, u_int32_t, db_pgno_t *));
  79.  */
  80. int
  81. __ham_add_dup(dbc, nval, flags, pgnop)
  82. DBC *dbc;
  83. DBT *nval;
  84. u_int32_t flags;
  85. db_pgno_t *pgnop;
  86. {
  87. DB *dbp;
  88. HASH_CURSOR *hcp;
  89. DBT pval, tmp_val;
  90. u_int32_t add_bytes, new_size;
  91. int cmp, ret;
  92. u_int8_t *hk;
  93. dbp = dbc->dbp;
  94. hcp = (HASH_CURSOR *)dbc->internal;
  95. DB_ASSERT(flags != DB_CURRENT);
  96. add_bytes = nval->size +
  97.     (F_ISSET(nval, DB_DBT_PARTIAL) ? nval->doff : 0);
  98. add_bytes = DUP_SIZE(add_bytes);
  99. if ((ret = __ham_check_move(dbc, add_bytes)) != 0)
  100. return (ret);
  101. /*
  102.  * Check if resulting duplicate set is going to need to go
  103.  * onto a separate duplicate page.  If so, convert the
  104.  * duplicate set and add the new one.  After conversion,
  105.  * hcp->dndx is the first free ndx or the index of the
  106.  * current pointer into the duplicate set.
  107.  */
  108. hk = H_PAIRDATA(hcp->page, hcp->indx);
  109. /* Add the len bytes to the current singleton. */
  110. if (HPAGE_PTYPE(hk) != H_DUPLICATE)
  111. add_bytes += DUP_SIZE(0);
  112. new_size =
  113.     LEN_HKEYDATA(hcp->page, dbp->pgsize, H_DATAINDEX(hcp->indx)) +
  114.     add_bytes;
  115. /*
  116.  * We convert to off-page duplicates if the item is a big item,
  117.  * the addition of the new item will make the set large, or
  118.  * if there isn't enough room on this page to add the next item.
  119.  */
  120. if (HPAGE_PTYPE(hk) != H_OFFDUP &&
  121.     (HPAGE_PTYPE(hk) == H_OFFPAGE || ISBIG(hcp, new_size) ||
  122.     add_bytes > P_FREESPACE(hcp->page))) {
  123. if ((ret = __ham_dup_convert(dbc)) != 0)
  124. return (ret);
  125. return (hcp->opd->c_am_put(hcp->opd,
  126.     NULL, nval, flags, NULL));
  127. }
  128. /* There are two separate cases here: on page and off page. */
  129. if (HPAGE_PTYPE(hk) != H_OFFDUP) {
  130. if (HPAGE_PTYPE(hk) != H_DUPLICATE) {
  131. pval.flags = 0;
  132. pval.data = HKEYDATA_DATA(hk);
  133. pval.size = LEN_HDATA(hcp->page, dbp->pgsize,
  134.     hcp->indx);
  135. if ((ret = __ham_make_dup(dbp->dbenv,
  136.     &pval, &tmp_val, &dbc->rdata.data,
  137.     &dbc->rdata.ulen)) != 0 || (ret =
  138.     __ham_replpair(dbc, &tmp_val, 1)) != 0)
  139. return (ret);
  140. hk = H_PAIRDATA(hcp->page, hcp->indx);
  141. HPAGE_PTYPE(hk) = H_DUPLICATE;
  142. /*
  143.  * Update the cursor position since we now are in
  144.  * duplicates.
  145.  */
  146. F_SET(hcp, H_ISDUP);
  147. hcp->dup_off = 0;
  148. hcp->dup_len = pval.size;
  149. hcp->dup_tlen = DUP_SIZE(hcp->dup_len);
  150. }
  151. /* Now make the new entry a duplicate. */
  152. if ((ret = __ham_make_dup(dbp->dbenv, nval,
  153.     &tmp_val, &dbc->rdata.data, &dbc->rdata.ulen)) != 0)
  154. return (ret);
  155. tmp_val.dlen = 0;
  156. switch (flags) { /* On page. */
  157. case DB_KEYFIRST:
  158. case DB_KEYLAST:
  159. case DB_NODUPDATA:
  160. if (dbp->dup_compare != NULL) {
  161. __ham_dsearch(dbc, nval, &tmp_val.doff, &cmp);
  162. /* dup dups are not supported w/ sorted dups */
  163. if (cmp == 0)
  164. return (__db_duperr(dbp, flags));
  165. } else {
  166. hcp->dup_tlen = LEN_HDATA(hcp->page,
  167.     dbp->pgsize, hcp->indx);
  168. hcp->dup_len = nval->size;
  169. F_SET(hcp, H_ISDUP);
  170. if (flags == DB_KEYFIRST)
  171. hcp->dup_off = tmp_val.doff = 0;
  172. else
  173. hcp->dup_off =
  174.     tmp_val.doff = hcp->dup_tlen;
  175. }
  176. break;
  177. case DB_BEFORE:
  178. tmp_val.doff = hcp->dup_off;
  179. break;
  180. case DB_AFTER:
  181. tmp_val.doff = hcp->dup_off + DUP_SIZE(hcp->dup_len);
  182. break;
  183. }
  184. /* Add the duplicate. */
  185. ret = __ham_replpair(dbc, &tmp_val, 0);
  186. if (ret == 0)
  187. ret = memp_fset(dbp->mpf, hcp->page, DB_MPOOL_DIRTY);
  188. if (ret != 0)
  189. return (ret);
  190. /* Now, update the cursor if necessary. */
  191. switch (flags) {
  192. case DB_AFTER:
  193. hcp->dup_off += DUP_SIZE(hcp->dup_len);
  194. hcp->dup_len = nval->size;
  195. hcp->dup_tlen += DUP_SIZE(nval->size);
  196. break;
  197. case DB_KEYFIRST:
  198. case DB_KEYLAST:
  199. case DB_BEFORE:
  200. hcp->dup_tlen += DUP_SIZE(nval->size);
  201. hcp->dup_len = nval->size;
  202. break;
  203. }
  204. ret = __ham_c_update(dbc, tmp_val.size, 1, 1);
  205. return (ret);
  206. }
  207. /*
  208.  * If we get here, then we're on duplicate pages; set pgnop and
  209.  * return so the common code can handle it.
  210.  */
  211. memcpy(pgnop,
  212.     HOFFDUP_PGNO(H_PAIRDATA(hcp->page, hcp->indx)), sizeof(db_pgno_t));
  213. return (ret);
  214. }
  215. /*
  216.  * Convert an on-page set of duplicates to an offpage set of duplicates.
  217.  *
  218.  * PUBLIC: int __ham_dup_convert __P((DBC *));
  219.  */
  220. int
  221. __ham_dup_convert(dbc)
  222. DBC *dbc;
  223. {
  224. DB *dbp;
  225. DBC **hcs;
  226. DB_LSN lsn;
  227. PAGE *dp;
  228. HASH_CURSOR *hcp;
  229. BOVERFLOW bo;
  230. DBT dbt;
  231. HOFFPAGE ho;
  232. db_indx_t i, len, off;
  233. int c, ret, t_ret;
  234. u_int8_t *p, *pend;
  235. dbp = dbc->dbp;
  236. hcp = (HASH_CURSOR *)dbc->internal;
  237. /*
  238.  * Create a new page for the duplicates.
  239.  */
  240. if ((ret = __db_new(dbc,
  241.     dbp->dup_compare == NULL ? P_LRECNO : P_LDUP, &dp)) != 0)
  242. return (ret);
  243. P_INIT(dp, dbp->pgsize,
  244.     dp->pgno, PGNO_INVALID, PGNO_INVALID, LEAFLEVEL, TYPE(dp));
  245. /*
  246.  * Get the list of cursors that may need to be updated.
  247.  */
  248. if ((ret = __ham_get_clist(dbp,
  249.     PGNO(hcp->page), (u_int32_t)hcp->indx, &hcs)) != 0)
  250. return (ret);
  251. /*
  252.  * Now put the duplicates onto the new page.
  253.  */
  254. dbt.flags = 0;
  255. switch (HPAGE_PTYPE(H_PAIRDATA(hcp->page, hcp->indx))) {
  256. case H_KEYDATA:
  257. /* Simple case, one key on page; move it to dup page. */
  258. dbt.size = LEN_HDATA(hcp->page, dbp->pgsize, hcp->indx);
  259. dbt.data = HKEYDATA_DATA(H_PAIRDATA(hcp->page, hcp->indx));
  260. ret = __db_pitem(dbc,
  261.     dp, 0, BKEYDATA_SIZE(dbt.size), NULL, &dbt);
  262. goto finish;
  263. case H_OFFPAGE:
  264. /* Simple case, one key on page; move it to dup page. */
  265. memcpy(&ho,
  266.     P_ENTRY(hcp->page, H_DATAINDEX(hcp->indx)), HOFFPAGE_SIZE);
  267. UMRW_SET(bo.unused1);
  268. B_TSET(bo.type, ho.type, 0);
  269. UMRW_SET(bo.unused2);
  270. bo.pgno = ho.pgno;
  271. bo.tlen = ho.tlen;
  272. dbt.size = BOVERFLOW_SIZE;
  273. dbt.data = &bo;
  274. ret = __db_pitem(dbc, dp, 0, dbt.size, &dbt, NULL);
  275. finish: if (ret == 0) {
  276. memp_fset(dbp->mpf, dp, DB_MPOOL_DIRTY);
  277. /*
  278.  * Update any other cursors
  279.  */
  280. if (hcs != NULL && DB_LOGGING(dbc)
  281.      && IS_SUBTRANSACTION(dbc->txn)) {
  282. if ((ret = __ham_chgpg_log(dbp->dbenv,
  283.     dbc->txn, &lsn, 0, dbp->log_fileid,
  284.     DB_HAM_DUP, PGNO(hcp->page),
  285.     PGNO(dp), hcp->indx, 0)) != 0)
  286. break;
  287. }
  288. for (c = 0; hcs != NULL && hcs[c] != NULL; c++)
  289. if ((ret = __ham_dcursor(hcs[c],
  290.     PGNO(dp), 0)) != 0)
  291. break;
  292. }
  293. break;
  294. case H_DUPLICATE:
  295. p = HKEYDATA_DATA(H_PAIRDATA(hcp->page, hcp->indx));
  296. pend = p +
  297.     LEN_HDATA(hcp->page, dbp->pgsize, hcp->indx);
  298. /*
  299.  * We need to maintain the duplicate cursor position.
  300.  * Keep track of where we are in the duplicate set via
  301.  * the offset, and when it matches the one in the cursor,
  302.  * set the off-page duplicate cursor index to the current
  303.  * index.
  304.  */
  305. for (off = 0, i = 0; p < pend; i++) {
  306. memcpy(&len, p, sizeof(db_indx_t));
  307. dbt.size = len;
  308. p += sizeof(db_indx_t);
  309. dbt.data = p;
  310. p += len + sizeof(db_indx_t);
  311. if ((ret = __db_pitem(dbc, dp,
  312.     i, BKEYDATA_SIZE(dbt.size), NULL, &dbt)) != 0)
  313. break;
  314. /*
  315.  * Update any other cursors
  316.  */
  317. for (c = 0; hcs != NULL && hcs[c] != NULL; c++)
  318. if (((HASH_CURSOR *)(hcs[c]->internal))->dup_off
  319.     == off && (ret = __ham_dcursor(hcs[c],
  320.     PGNO(dp), i)) != 0)
  321. goto out;
  322. off += len + 2 * sizeof(db_indx_t);
  323. }
  324. out: break;
  325. default:
  326. ret = __db_pgfmt(dbp, (u_long)hcp->pgno);
  327. break;
  328. }
  329. if (ret == 0) {
  330. /*
  331.  * Now attach this to the source page in place of
  332.  * the old duplicate item.
  333.  */
  334. __ham_move_offpage(dbc, hcp->page,
  335.     (u_int32_t)H_DATAINDEX(hcp->indx), PGNO(dp));
  336. ret = memp_fset(dbp->mpf, hcp->page, DB_MPOOL_DIRTY);
  337. if ((t_ret = memp_fput(dbp->mpf, dp, DB_MPOOL_DIRTY)) != 0)
  338. ret = t_ret;
  339. hcp->dup_tlen = hcp->dup_off = hcp->dup_len = 0;
  340. } else
  341. (void)__db_free(dbc, dp);
  342. if (hcs != NULL)
  343. __os_free(hcs, 0);
  344. return (ret);
  345. }
  346. /*
  347.  * __ham_make_dup
  348.  *
  349.  * Take a regular dbt and make it into a duplicate item with all the partial
  350.  * information set appropriately. If the incoming dbt is a partial, assume
  351.  * we are creating a new entry and make sure that we do any initial padding.
  352.  *
  353.  * PUBLIC: int __ham_make_dup __P((DB_ENV *,
  354.  * PUBLIC:     const DBT *, DBT *d, void **, u_int32_t *));
  355.  */
  356. int
  357. __ham_make_dup(dbenv, notdup, duplicate, bufp, sizep)
  358. DB_ENV *dbenv;
  359. const DBT *notdup;
  360. DBT *duplicate;
  361. void **bufp;
  362. u_int32_t *sizep;
  363. {
  364. db_indx_t tsize, item_size;
  365. int ret;
  366. u_int8_t *p;
  367. item_size = (db_indx_t)notdup->size;
  368. if (F_ISSET(notdup, DB_DBT_PARTIAL))
  369. item_size += notdup->doff;
  370. tsize = DUP_SIZE(item_size);
  371. if ((ret = __ham_init_dbt(dbenv, duplicate, tsize, bufp, sizep)) != 0)
  372. return (ret);
  373. duplicate->dlen = 0;
  374. duplicate->flags = notdup->flags;
  375. F_SET(duplicate, DB_DBT_PARTIAL);
  376. p = duplicate->data;
  377. memcpy(p, &item_size, sizeof(db_indx_t));
  378. p += sizeof(db_indx_t);
  379. if (F_ISSET(notdup, DB_DBT_PARTIAL)) {
  380. memset(p, 0, notdup->doff);
  381. p += notdup->doff;
  382. }
  383. memcpy(p, notdup->data, notdup->size);
  384. p += notdup->size;
  385. memcpy(p, &item_size, sizeof(db_indx_t));
  386. duplicate->doff = 0;
  387. duplicate->dlen = notdup->size;
  388. return (0);
  389. }
  390. /*
  391.  * __ham_check_move --
  392.  *
  393.  * Check if we can do whatever we need to on this page.  If not,
  394.  * then we'll have to move the current element to a new page.
  395.  */
  396. static int
  397. __ham_check_move(dbc, add_len)
  398. DBC *dbc;
  399. u_int32_t add_len;
  400. {
  401. DB *dbp;
  402. HASH_CURSOR *hcp;
  403. DBT k, d;
  404. DB_LSN new_lsn;
  405. PAGE *next_pagep;
  406. db_pgno_t next_pgno;
  407. u_int32_t new_datalen, old_len, rectype;
  408. u_int8_t *hk;
  409. int ret;
  410. dbp = dbc->dbp;
  411. hcp = (HASH_CURSOR *)dbc->internal;
  412. hk = H_PAIRDATA(hcp->page, hcp->indx);
  413. /*
  414.  * If the item is already off page duplicates or an offpage item,
  415.  * then we know we can do whatever we need to do in-place
  416.  */
  417. if (HPAGE_PTYPE(hk) == H_OFFDUP || HPAGE_PTYPE(hk) == H_OFFPAGE)
  418. return (0);
  419. old_len = LEN_HITEM(hcp->page, dbp->pgsize, H_DATAINDEX(hcp->indx));
  420. new_datalen = old_len - HKEYDATA_SIZE(0) + add_len;
  421. if (HPAGE_PTYPE(hk) != H_DUPLICATE)
  422. new_datalen += DUP_SIZE(0);
  423. /*
  424.  * We need to add a new page under two conditions:
  425.  * 1. The addition makes the total data length cross the BIG
  426.  *    threshold and the OFFDUP structure won't fit on this page.
  427.  * 2. The addition does not make the total data cross the
  428.  *    threshold, but the new data won't fit on the page.
  429.  * If neither of these is true, then we can return.
  430.  */
  431. if (ISBIG(hcp, new_datalen) && (old_len > HOFFDUP_SIZE ||
  432.     HOFFDUP_SIZE - old_len <= P_FREESPACE(hcp->page)))
  433. return (0);
  434. if (!ISBIG(hcp, new_datalen) && add_len <= P_FREESPACE(hcp->page))
  435. return (0);
  436. /*
  437.  * If we get here, then we need to move the item to a new page.
  438.  * Check if there are more pages in the chain.  We now need to
  439.  * update new_datalen to include the size of both the key and
  440.  * the data that we need to move.
  441.  */
  442. new_datalen = ISBIG(hcp, new_datalen) ?
  443.     HOFFDUP_SIZE : HKEYDATA_SIZE(new_datalen);
  444. new_datalen += LEN_HITEM(hcp->page, dbp->pgsize, H_KEYINDEX(hcp->indx));
  445. next_pagep = NULL;
  446. for (next_pgno = NEXT_PGNO(hcp->page); next_pgno != PGNO_INVALID;
  447.     next_pgno = NEXT_PGNO(next_pagep)) {
  448. if (next_pagep != NULL &&
  449.     (ret = memp_fput(dbp->mpf, next_pagep, 0)) != 0)
  450. return (ret);
  451. if ((ret = memp_fget(dbp->mpf,
  452.     &next_pgno, DB_MPOOL_CREATE, &next_pagep)) != 0)
  453. return (ret);
  454. if (P_FREESPACE(next_pagep) >= new_datalen)
  455. break;
  456. }
  457. /* No more pages, add one. */
  458. if (next_pagep == NULL && (ret = __ham_add_ovflpage(dbc,
  459.     hcp->page, 0, &next_pagep)) != 0)
  460. return (ret);
  461. /* Add new page at the end of the chain. */
  462. if (P_FREESPACE(next_pagep) < new_datalen && (ret =
  463.     __ham_add_ovflpage(dbc, next_pagep, 1, &next_pagep)) != 0) {
  464. (void)memp_fput(dbp->mpf, next_pagep, 0);
  465. return (ret);
  466. }
  467. /* Copy the item to the new page. */
  468. if (DB_LOGGING(dbc)) {
  469. rectype = PUTPAIR;
  470. k.flags = 0;
  471. d.flags = 0;
  472. if (HPAGE_PTYPE(
  473.     H_PAIRKEY(hcp->page, hcp->indx)) == H_OFFPAGE) {
  474. rectype |= PAIR_KEYMASK;
  475. k.data = H_PAIRKEY(hcp->page, hcp->indx);
  476. k.size = HOFFPAGE_SIZE;
  477. } else {
  478. k.data =
  479.     HKEYDATA_DATA(H_PAIRKEY(hcp->page, hcp->indx));
  480. k.size = LEN_HKEY(hcp->page, dbp->pgsize, hcp->indx);
  481. }
  482. if (HPAGE_PTYPE(hk) == H_OFFPAGE) {
  483. rectype |= PAIR_DATAMASK;
  484. d.data = H_PAIRDATA(hcp->page, hcp->indx);
  485. d.size = HOFFPAGE_SIZE;
  486. } else {
  487. if (HPAGE_PTYPE(H_PAIRDATA(hcp->page, hcp->indx))
  488.     == H_DUPLICATE)
  489. rectype |= PAIR_DUPMASK;
  490. d.data =
  491.     HKEYDATA_DATA(H_PAIRDATA(hcp->page, hcp->indx));
  492. d.size = LEN_HDATA(hcp->page, dbp->pgsize, hcp->indx);
  493. }
  494. if ((ret = __ham_insdel_log(dbp->dbenv,
  495.     dbc->txn, &new_lsn, 0, rectype,
  496.     dbp->log_fileid, PGNO(next_pagep),
  497.     (u_int32_t)NUM_ENT(next_pagep), &LSN(next_pagep),
  498.     &k, &d)) != 0) {
  499. (void)memp_fput(dbp->mpf, next_pagep, 0);
  500. return (ret);
  501. }
  502. /* Move lsn onto page. */
  503. LSN(next_pagep) = new_lsn; /* Structure assignment. */
  504. }
  505. __ham_copy_item(dbp->pgsize,
  506.     hcp->page, H_KEYINDEX(hcp->indx), next_pagep);
  507. __ham_copy_item(dbp->pgsize,
  508.     hcp->page, H_DATAINDEX(hcp->indx), next_pagep);
  509. /*
  510.  * We've just manually inserted a key and set of data onto
  511.  * next_pagep;  however, it's possible that our caller will
  512.  * return without further modifying the new page, for instance
  513.  * if DB_NODUPDATA is set and our new item is a duplicate duplicate.
  514.  * Thus, to be on the safe side, we need to mark the page dirty
  515.  * here. [#2996]
  516.  *
  517.  * Note that __ham_del_pair should dirty the page we're moving
  518.  * the items from, so we need only dirty the new page ourselves.
  519.  */
  520. if ((ret = memp_fset(dbp->mpf, next_pagep, DB_MPOOL_DIRTY)) != 0)
  521. goto out;
  522. /* Update all cursors that used to point to this item. */
  523. if ((ret = __ham_c_chgpg(dbc, PGNO(hcp->page), H_KEYINDEX(hcp->indx),
  524.     PGNO(next_pagep), NUM_ENT(next_pagep) - 2)) != 0)
  525. goto out;
  526. /* Now delete the pair from the current page. */
  527. ret = __ham_del_pair(dbc, 0);
  528. /*
  529.  * __ham_del_pair decremented nelem.  This is incorrect;  we
  530.  * manually copied the element elsewhere, so the total number
  531.  * of elements hasn't changed.  Increment it again.
  532.  */
  533. if (!STD_LOCKING(dbc))
  534. hcp->hdr->nelem++;
  535. out:
  536. (void)memp_fput(dbp->mpf, hcp->page, DB_MPOOL_DIRTY);
  537. hcp->page = next_pagep;
  538. hcp->pgno = PGNO(hcp->page);
  539. hcp->indx = NUM_ENT(hcp->page) - 2;
  540. F_SET(hcp, H_EXPAND);
  541. F_CLR(hcp, H_DELETED);
  542. return (ret);
  543. }
  544. /*
  545.  * __ham_move_offpage --
  546.  * Replace an onpage set of duplicates with the OFFDUP structure
  547.  * that references the duplicate page.
  548.  *
  549.  * XXX
  550.  * This is really just a special case of __onpage_replace; we should
  551.  * probably combine them.
  552.  *
  553.  * PUBLIC: void __ham_move_offpage __P((DBC *, PAGE *, u_int32_t, db_pgno_t));
  554.  */
  555. void
  556. __ham_move_offpage(dbc, pagep, ndx, pgno)
  557. DBC *dbc;
  558. PAGE *pagep;
  559. u_int32_t ndx;
  560. db_pgno_t pgno;
  561. {
  562. DB *dbp;
  563. HASH_CURSOR *hcp;
  564. DBT new_dbt;
  565. DBT old_dbt;
  566. HOFFDUP od;
  567. db_indx_t i;
  568. int32_t shrink;
  569. u_int8_t *src;
  570. dbp = dbc->dbp;
  571. hcp = (HASH_CURSOR *)dbc->internal;
  572. od.type = H_OFFDUP;
  573. UMRW_SET(od.unused[0]);
  574. UMRW_SET(od.unused[1]);
  575. UMRW_SET(od.unused[2]);
  576. od.pgno = pgno;
  577. if (DB_LOGGING(dbc)) {
  578. new_dbt.data = &od;
  579. new_dbt.size = HOFFDUP_SIZE;
  580. old_dbt.data = P_ENTRY(pagep, ndx);
  581. old_dbt.size = LEN_HITEM(pagep, dbp->pgsize, ndx);
  582. (void)__ham_replace_log(dbp->dbenv,
  583.     dbc->txn, &LSN(pagep), 0, dbp->log_fileid,
  584.     PGNO(pagep), (u_int32_t)ndx, &LSN(pagep), -1,
  585.     &old_dbt, &new_dbt, 0);
  586. }
  587. shrink = LEN_HITEM(pagep, dbp->pgsize, ndx) - HOFFDUP_SIZE;
  588. if (shrink != 0) {
  589. /* Copy data. */
  590. src = (u_int8_t *)(pagep) + HOFFSET(pagep);
  591. memmove(src + shrink, src, pagep->inp[ndx] - HOFFSET(pagep));
  592. HOFFSET(pagep) += shrink;
  593. /* Update index table. */
  594. for (i = ndx; i < NUM_ENT(pagep); i++)
  595. pagep->inp[i] += shrink;
  596. }
  597. /* Now copy the offdup entry onto the page. */
  598. memcpy(P_ENTRY(pagep, ndx), &od, HOFFDUP_SIZE);
  599. }
  600. /*
  601.  * __ham_dsearch:
  602.  * Locate a particular duplicate in a duplicate set.  Make sure that
  603.  * we exit with the cursor set appropriately.
  604.  *
  605.  * PUBLIC: void __ham_dsearch __P((DBC *, DBT *, u_int32_t *, int *));
  606.  */
  607. void
  608. __ham_dsearch(dbc, dbt, offp, cmpp)
  609. DBC *dbc;
  610. DBT *dbt;
  611. u_int32_t *offp;
  612. int *cmpp;
  613. {
  614. DB *dbp;
  615. HASH_CURSOR *hcp;
  616. DBT cur;
  617. db_indx_t i, len;
  618. int (*func) __P((DB *, const DBT *, const DBT *));
  619. u_int8_t *data;
  620. dbp = dbc->dbp;
  621. hcp = (HASH_CURSOR *)dbc->internal;
  622. if (dbp->dup_compare == NULL)
  623. func = __bam_defcmp;
  624. else
  625. func = dbp->dup_compare;
  626. i = F_ISSET(hcp, H_CONTINUE) ? hcp->dup_off: 0;
  627. data = HKEYDATA_DATA(H_PAIRDATA(hcp->page, hcp->indx)) + i;
  628. hcp->dup_tlen = LEN_HDATA(hcp->page, dbp->pgsize, hcp->indx);
  629. while (i < hcp->dup_tlen) {
  630. memcpy(&len, data, sizeof(db_indx_t));
  631. data += sizeof(db_indx_t);
  632. cur.data = data;
  633. cur.size = (u_int32_t)len;
  634. *cmpp = func(dbp, dbt, &cur);
  635. if (*cmpp == 0 || (*cmpp < 0 && dbp->dup_compare != NULL))
  636. break;
  637. i += len + 2 * sizeof(db_indx_t);
  638. data += len + sizeof(db_indx_t);
  639. }
  640. *offp = i;
  641. hcp->dup_off = i;
  642. hcp->dup_len = len;
  643. F_SET(hcp, H_ISDUP);
  644. }
  645. #ifdef DEBUG
  646. /*
  647.  * __ham_cprint --
  648.  * Display the current cursor list.
  649.  *
  650.  * PUBLIC: int __ham_cprint __P((DB *));
  651.  */
  652. int
  653. __ham_cprint(dbp)
  654. DB *dbp;
  655. {
  656. HASH_CURSOR *cp;
  657. DBC *dbc;
  658. MUTEX_THREAD_LOCK(dbp->dbenv, dbp->mutexp);
  659. for (dbc = TAILQ_FIRST(&dbp->active_queue);
  660.     dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
  661. cp = (HASH_CURSOR *)dbc->internal;
  662. fprintf(stderr, "%#0lx->%#0lx: page: %lu index: %lu",
  663.     P_TO_ULONG(dbc), P_TO_ULONG(cp), (u_long)cp->pgno,
  664.     (u_long)cp->indx);
  665. if (F_ISSET(cp, H_DELETED))
  666. fprintf(stderr, " (deleted)");
  667. fprintf(stderr, "n");
  668. }
  669. MUTEX_THREAD_UNLOCK(dbp->dbenv, dbp->mutexp);
  670. return (0);
  671. }
  672. #endif /* DEBUG */
  673. /*
  674.  * __ham_dcursor --
  675.  *
  676.  * Create an off page duplicate cursor for this cursor.
  677.  */
  678. static int
  679. __ham_dcursor(dbc, pgno, indx)
  680. DBC *dbc;
  681. db_pgno_t pgno;
  682. u_int32_t indx;
  683. {
  684. DB *dbp;
  685. DBC *dbc_nopd;
  686. HASH_CURSOR *hcp;
  687. BTREE_CURSOR *dcp;
  688. int ret;
  689. dbp = dbc->dbp;
  690. if ((ret = __db_c_newopd(dbc, pgno, &dbc_nopd)) != 0)
  691. return (ret);
  692. dcp = (BTREE_CURSOR *)dbc_nopd->internal;
  693. dcp->pgno = pgno;
  694. dcp->indx = indx;
  695. if (dbp->dup_compare == NULL) {
  696. /*
  697.  * Converting to off-page Recno trees is tricky.  The
  698.  * record number for the cursor is the index + 1 (to
  699.  * convert to 1-based record numbers).
  700.  */
  701. dcp->recno = indx + 1;
  702. }
  703. /*
  704.  * Transfer the deleted flag from the top-level cursor to the
  705.  * created one.
  706.  */
  707. hcp = (HASH_CURSOR *)dbc->internal;
  708. if (F_ISSET(hcp, H_DELETED)) {
  709. F_SET(dcp, C_DELETED);
  710. F_CLR(hcp, H_DELETED);
  711. }
  712. /* Stack the cursors and reset the initial cursor's index. */
  713. hcp->opd = dbc_nopd;
  714. return (0);
  715. }