1*aae80e6bSchristos /* $NetBSD: bt_delete.c,v 1.19 2016/09/24 21:31:25 christos Exp $ */
2a954b078Scgd
39f0aa214Scgd /*-
4a6d14e36Scgd * Copyright (c) 1990, 1993, 1994
59f0aa214Scgd * The Regents of the University of California. All rights reserved.
69f0aa214Scgd *
79f0aa214Scgd * This code is derived from software contributed to Berkeley by
89f0aa214Scgd * Mike Olson.
99f0aa214Scgd *
109f0aa214Scgd * Redistribution and use in source and binary forms, with or without
119f0aa214Scgd * modification, are permitted provided that the following conditions
129f0aa214Scgd * are met:
139f0aa214Scgd * 1. Redistributions of source code must retain the above copyright
149f0aa214Scgd * notice, this list of conditions and the following disclaimer.
159f0aa214Scgd * 2. Redistributions in binary form must reproduce the above copyright
169f0aa214Scgd * notice, this list of conditions and the following disclaimer in the
179f0aa214Scgd * documentation and/or other materials provided with the distribution.
18eb7c1594Sagc * 3. Neither the name of the University nor the names of its contributors
199f0aa214Scgd * may be used to endorse or promote products derived from this software
209f0aa214Scgd * without specific prior written permission.
219f0aa214Scgd *
229f0aa214Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
239f0aa214Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
249f0aa214Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
259f0aa214Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
269f0aa214Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
279f0aa214Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
289f0aa214Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
299f0aa214Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
309f0aa214Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
319f0aa214Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
329f0aa214Scgd * SUCH DAMAGE.
339f0aa214Scgd */
349f0aa214Scgd
35d3595ddfSjoerg #if HAVE_NBTOOL_CONFIG_H
36d3595ddfSjoerg #include "nbtool_config.h"
37d3595ddfSjoerg #endif
38d3595ddfSjoerg
3900ae392dSchristos #include <sys/cdefs.h>
40*aae80e6bSchristos __RCSID("$NetBSD: bt_delete.c,v 1.19 2016/09/24 21:31:25 christos Exp $");
419f0aa214Scgd
4243fa6fe3Sjtc #include "namespace.h"
439f0aa214Scgd #include <sys/types.h>
449f0aa214Scgd
45cb9daf8fSchristos #include <assert.h>
469f0aa214Scgd #include <errno.h>
479f0aa214Scgd #include <stdio.h>
489f0aa214Scgd #include <string.h>
499f0aa214Scgd
509f0aa214Scgd #include <db.h>
519f0aa214Scgd #include "btree.h"
529f0aa214Scgd
53cb9daf8fSchristos static int __bt_bdelete(BTREE *, const DBT *);
54cb9daf8fSchristos static int __bt_curdel(BTREE *, const DBT *, PAGE *, u_int);
55cb9daf8fSchristos static int __bt_pdelete(BTREE *, PAGE *);
56cb9daf8fSchristos static int __bt_stkacq(BTREE *, PAGE **, CURSOR *);
579f0aa214Scgd
589f0aa214Scgd /*
5924420c01Scgd * __bt_delete
6024420c01Scgd * Delete the item(s) referenced by a key.
619f0aa214Scgd *
6224420c01Scgd * Return RET_SPECIAL if the key is not found.
639f0aa214Scgd */
649f0aa214Scgd int
__bt_delete(const DB * dbp,const DBT * key,u_int flags)65cb9daf8fSchristos __bt_delete(const DB *dbp, const DBT *key, u_int flags)
669f0aa214Scgd {
679f0aa214Scgd BTREE *t;
6824420c01Scgd CURSOR *c;
6924420c01Scgd PAGE *h;
709f0aa214Scgd int status;
719f0aa214Scgd
729f0aa214Scgd t = dbp->internal;
7345e27c80Scgd
7445e27c80Scgd /* Toss any page pinned across calls. */
7545e27c80Scgd if (t->bt_pinned != NULL) {
7645e27c80Scgd mpool_put(t->bt_mp, t->bt_pinned, 0);
7745e27c80Scgd t->bt_pinned = NULL;
7845e27c80Scgd }
7945e27c80Scgd
8024420c01Scgd /* Check for change to a read-only tree. */
8124420c01Scgd if (F_ISSET(t, B_RDONLY)) {
829f0aa214Scgd errno = EPERM;
839f0aa214Scgd return (RET_ERROR);
849f0aa214Scgd }
8545e27c80Scgd
869f0aa214Scgd switch (flags) {
879f0aa214Scgd case 0:
8824420c01Scgd status = __bt_bdelete(t, key);
899f0aa214Scgd break;
909f0aa214Scgd case R_CURSOR:
919f0aa214Scgd /*
9224420c01Scgd * If flags is R_CURSOR, delete the cursor. Must already
9324420c01Scgd * have started a scan and not have already deleted it.
949f0aa214Scgd */
9524420c01Scgd c = &t->bt_cursor;
9624420c01Scgd if (F_ISSET(c, CURS_INIT)) {
9724420c01Scgd if (F_ISSET(c, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE))
9824420c01Scgd return (RET_SPECIAL);
99*aae80e6bSchristos if ((h = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL)
10024420c01Scgd return (RET_ERROR);
10124420c01Scgd
10224420c01Scgd /*
10324420c01Scgd * If the page is about to be emptied, we'll need to
10424420c01Scgd * delete it, which means we have to acquire a stack.
10524420c01Scgd */
10624420c01Scgd if (NEXTINDEX(h) == 1)
10724420c01Scgd if (__bt_stkacq(t, &h, &t->bt_cursor))
10824420c01Scgd return (RET_ERROR);
10924420c01Scgd
11061238e71Schristos status = __bt_dleaf(t, NULL, h, (u_int)c->pg.index);
11124420c01Scgd
11224420c01Scgd if (NEXTINDEX(h) == 0 && status == RET_SUCCESS) {
11324420c01Scgd if (__bt_pdelete(t, h))
11424420c01Scgd return (RET_ERROR);
11524420c01Scgd } else
11661238e71Schristos mpool_put(t->bt_mp, h,
11761238e71Schristos (u_int)(status == RET_SUCCESS ?
11861238e71Schristos MPOOL_DIRTY : 0));
1199f0aa214Scgd break;
12024420c01Scgd }
12124420c01Scgd /* FALLTHROUGH */
1229f0aa214Scgd default:
12324420c01Scgd errno = EINVAL;
1249f0aa214Scgd return (RET_ERROR);
1259f0aa214Scgd }
1269f0aa214Scgd if (status == RET_SUCCESS)
12724420c01Scgd F_SET(t, B_MODIFIED);
1289f0aa214Scgd return (status);
1299f0aa214Scgd }
1309f0aa214Scgd
1319f0aa214Scgd /*
13224420c01Scgd * __bt_stkacq --
13324420c01Scgd * Acquire a stack so we can delete a cursor entry.
1349f0aa214Scgd *
1359f0aa214Scgd * Parameters:
13624420c01Scgd * t: tree
13724420c01Scgd * hp: pointer to current, pinned PAGE pointer
13824420c01Scgd * c: pointer to the cursor
13924420c01Scgd *
14024420c01Scgd * Returns:
14124420c01Scgd * 0 on success, 1 on failure
14224420c01Scgd */
14324420c01Scgd static int
__bt_stkacq(BTREE * t,PAGE ** hp,CURSOR * c)144cb9daf8fSchristos __bt_stkacq(BTREE *t, PAGE **hp, CURSOR *c)
14524420c01Scgd {
14624420c01Scgd BINTERNAL *bi;
14724420c01Scgd EPG *e;
14824420c01Scgd EPGNO *parent;
14924420c01Scgd PAGE *h;
15061238e71Schristos indx_t idx = 0; /* Pacify gcc */
15124420c01Scgd pgno_t pgno;
15224420c01Scgd recno_t nextpg, prevpg;
15324420c01Scgd int exact, level;
15424420c01Scgd
15524420c01Scgd /*
15624420c01Scgd * Find the first occurrence of the key in the tree. Toss the
15724420c01Scgd * currently locked page so we don't hit an already-locked page.
15824420c01Scgd */
15924420c01Scgd h = *hp;
16024420c01Scgd mpool_put(t->bt_mp, h, 0);
16124420c01Scgd if ((e = __bt_search(t, &c->key, &exact)) == NULL)
16224420c01Scgd return (1);
16324420c01Scgd h = e->page;
16424420c01Scgd
16524420c01Scgd /* See if we got it in one shot. */
16624420c01Scgd if (h->pgno == c->pg.pgno)
16724420c01Scgd goto ret;
16824420c01Scgd
16924420c01Scgd /*
17024420c01Scgd * Move right, looking for the page. At each move we have to move
17124420c01Scgd * up the stack until we don't have to move to the next page. If
17224420c01Scgd * we have to change pages at an internal level, we have to fix the
17324420c01Scgd * stack back up.
17424420c01Scgd */
17524420c01Scgd while (h->pgno != c->pg.pgno) {
17624420c01Scgd if ((nextpg = h->nextpg) == P_INVALID)
17724420c01Scgd break;
17824420c01Scgd mpool_put(t->bt_mp, h, 0);
17924420c01Scgd
18024420c01Scgd /* Move up the stack. */
18124420c01Scgd for (level = 0; (parent = BT_POP(t)) != NULL; ++level) {
18224420c01Scgd /* Get the parent page. */
183*aae80e6bSchristos if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL)
18424420c01Scgd return (1);
18524420c01Scgd
18624420c01Scgd /* Move to the next index. */
18724420c01Scgd if (parent->index != NEXTINDEX(h) - 1) {
18861238e71Schristos idx = parent->index + 1;
18961238e71Schristos BT_PUSH(t, h->pgno, idx);
19024420c01Scgd break;
19124420c01Scgd }
19224420c01Scgd mpool_put(t->bt_mp, h, 0);
19324420c01Scgd }
19424420c01Scgd
19524420c01Scgd /* Restore the stack. */
19624420c01Scgd while (level--) {
19724420c01Scgd /* Push the next level down onto the stack. */
19861238e71Schristos bi = GETBINTERNAL(h, idx);
19924420c01Scgd pgno = bi->pgno;
20024420c01Scgd BT_PUSH(t, pgno, 0);
20124420c01Scgd
20224420c01Scgd /* Lose the currently pinned page. */
20324420c01Scgd mpool_put(t->bt_mp, h, 0);
20424420c01Scgd
20524420c01Scgd /* Get the next level down. */
206*aae80e6bSchristos if ((h = mpool_get(t->bt_mp, pgno, 0)) == NULL)
20724420c01Scgd return (1);
20861238e71Schristos idx = 0;
20924420c01Scgd }
21024420c01Scgd mpool_put(t->bt_mp, h, 0);
211*aae80e6bSchristos if ((h = mpool_get(t->bt_mp, nextpg, 0)) == NULL)
21224420c01Scgd return (1);
21324420c01Scgd }
21424420c01Scgd
21524420c01Scgd if (h->pgno == c->pg.pgno)
21624420c01Scgd goto ret;
21724420c01Scgd
21824420c01Scgd /* Reacquire the original stack. */
21924420c01Scgd mpool_put(t->bt_mp, h, 0);
22024420c01Scgd if ((e = __bt_search(t, &c->key, &exact)) == NULL)
22124420c01Scgd return (1);
22224420c01Scgd h = e->page;
22324420c01Scgd
22424420c01Scgd /*
22524420c01Scgd * Move left, looking for the page. At each move we have to move
22624420c01Scgd * up the stack until we don't have to change pages to move to the
22724420c01Scgd * next page. If we have to change pages at an internal level, we
22824420c01Scgd * have to fix the stack back up.
22924420c01Scgd */
23024420c01Scgd while (h->pgno != c->pg.pgno) {
23124420c01Scgd if ((prevpg = h->prevpg) == P_INVALID)
23224420c01Scgd break;
23324420c01Scgd mpool_put(t->bt_mp, h, 0);
23424420c01Scgd
23524420c01Scgd /* Move up the stack. */
23624420c01Scgd for (level = 0; (parent = BT_POP(t)) != NULL; ++level) {
23724420c01Scgd /* Get the parent page. */
238*aae80e6bSchristos if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL)
23924420c01Scgd return (1);
24024420c01Scgd
24124420c01Scgd /* Move to the next index. */
24224420c01Scgd if (parent->index != 0) {
24361238e71Schristos idx = parent->index - 1;
24461238e71Schristos BT_PUSH(t, h->pgno, idx);
24524420c01Scgd break;
24624420c01Scgd }
24724420c01Scgd mpool_put(t->bt_mp, h, 0);
24824420c01Scgd }
24924420c01Scgd
25024420c01Scgd /* Restore the stack. */
25124420c01Scgd while (level--) {
25224420c01Scgd /* Push the next level down onto the stack. */
25361238e71Schristos bi = GETBINTERNAL(h, idx);
25424420c01Scgd pgno = bi->pgno;
25524420c01Scgd
25624420c01Scgd /* Lose the currently pinned page. */
25724420c01Scgd mpool_put(t->bt_mp, h, 0);
25824420c01Scgd
25924420c01Scgd /* Get the next level down. */
260*aae80e6bSchristos if ((h = mpool_get(t->bt_mp, pgno, 0)) == NULL)
26124420c01Scgd return (1);
26224420c01Scgd
26361238e71Schristos idx = NEXTINDEX(h) - 1;
26461238e71Schristos BT_PUSH(t, pgno, idx);
26524420c01Scgd }
26624420c01Scgd mpool_put(t->bt_mp, h, 0);
267*aae80e6bSchristos if ((h = mpool_get(t->bt_mp, prevpg, 0)) == NULL)
26824420c01Scgd return (1);
26924420c01Scgd }
27024420c01Scgd
27124420c01Scgd
27224420c01Scgd ret: mpool_put(t->bt_mp, h, 0);
273*aae80e6bSchristos return ((*hp = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL);
27424420c01Scgd }
27524420c01Scgd
27624420c01Scgd /*
27724420c01Scgd * __bt_bdelete --
27824420c01Scgd * Delete all key/data pairs matching the specified key.
27924420c01Scgd *
28024420c01Scgd * Parameters:
28124420c01Scgd * t: tree
2829f0aa214Scgd * key: key to delete
2839f0aa214Scgd *
2849f0aa214Scgd * Returns:
2859f0aa214Scgd * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found.
2869f0aa214Scgd */
2879f0aa214Scgd static int
__bt_bdelete(BTREE * t,const DBT * key)288cb9daf8fSchristos __bt_bdelete(BTREE *t, const DBT *key)
2899f0aa214Scgd {
29024420c01Scgd EPG *e;
2919f0aa214Scgd PAGE *h;
29224420c01Scgd int deleted, exact, redo;
29324420c01Scgd
29424420c01Scgd deleted = 0;
2959f0aa214Scgd
2969f0aa214Scgd /* Find any matching record; __bt_search pins the page. */
29724420c01Scgd loop: if ((e = __bt_search(t, key, &exact)) == NULL)
29824420c01Scgd return (deleted ? RET_SUCCESS : RET_ERROR);
2999f0aa214Scgd if (!exact) {
3009f0aa214Scgd mpool_put(t->bt_mp, e->page, 0);
3019f0aa214Scgd return (deleted ? RET_SUCCESS : RET_SPECIAL);
3029f0aa214Scgd }
3039f0aa214Scgd
3049f0aa214Scgd /*
30524420c01Scgd * Delete forward, then delete backward, from the found key. If
30624420c01Scgd * there are duplicates and we reach either side of the page, do
30724420c01Scgd * the key search again, so that we get them all.
30824420c01Scgd */
30924420c01Scgd redo = 0;
31024420c01Scgd h = e->page;
31124420c01Scgd do {
31261238e71Schristos if (__bt_dleaf(t, key, h, (u_int)e->index)) {
31324420c01Scgd mpool_put(t->bt_mp, h, 0);
31424420c01Scgd return (RET_ERROR);
31524420c01Scgd }
31624420c01Scgd if (F_ISSET(t, B_NODUPS)) {
31724420c01Scgd if (NEXTINDEX(h) == 0) {
31824420c01Scgd if (__bt_pdelete(t, h))
31924420c01Scgd return (RET_ERROR);
32024420c01Scgd } else
32124420c01Scgd mpool_put(t->bt_mp, h, MPOOL_DIRTY);
32224420c01Scgd return (RET_SUCCESS);
32324420c01Scgd }
32424420c01Scgd deleted = 1;
32524420c01Scgd } while (e->index < NEXTINDEX(h) && __bt_cmp(t, key, e) == 0);
32624420c01Scgd
32724420c01Scgd /* Check for right-hand edge of the page. */
32824420c01Scgd if (e->index == NEXTINDEX(h))
32924420c01Scgd redo = 1;
33024420c01Scgd
33124420c01Scgd /* Delete from the key to the beginning of the page. */
33224420c01Scgd while (e->index-- > 0) {
33324420c01Scgd if (__bt_cmp(t, key, e) != 0)
33424420c01Scgd break;
33561238e71Schristos if (__bt_dleaf(t, key, h, (u_int)e->index) == RET_ERROR) {
33624420c01Scgd mpool_put(t->bt_mp, h, 0);
33724420c01Scgd return (RET_ERROR);
33824420c01Scgd }
33924420c01Scgd if (e->index == 0)
34024420c01Scgd redo = 1;
34124420c01Scgd }
34224420c01Scgd
34324420c01Scgd /* Check for an empty page. */
34424420c01Scgd if (NEXTINDEX(h) == 0) {
34524420c01Scgd if (__bt_pdelete(t, h))
34624420c01Scgd return (RET_ERROR);
34724420c01Scgd goto loop;
34824420c01Scgd }
34924420c01Scgd
35024420c01Scgd /* Put the page. */
35124420c01Scgd mpool_put(t->bt_mp, h, MPOOL_DIRTY);
35224420c01Scgd
35324420c01Scgd if (redo)
35424420c01Scgd goto loop;
35524420c01Scgd return (RET_SUCCESS);
35624420c01Scgd }
35724420c01Scgd
35824420c01Scgd /*
35924420c01Scgd * __bt_pdelete --
36024420c01Scgd * Delete a single page from the tree.
3619f0aa214Scgd *
3629f0aa214Scgd * Parameters:
3639f0aa214Scgd * t: tree
36424420c01Scgd * h: leaf page
36524420c01Scgd *
36624420c01Scgd * Returns:
36724420c01Scgd * RET_SUCCESS, RET_ERROR.
36824420c01Scgd *
36924420c01Scgd * Side-effects:
37024420c01Scgd * mpool_put's the page
37124420c01Scgd */
37224420c01Scgd static int
__bt_pdelete(BTREE * t,PAGE * h)373cb9daf8fSchristos __bt_pdelete(BTREE *t, PAGE *h)
37424420c01Scgd {
37524420c01Scgd BINTERNAL *bi;
37624420c01Scgd PAGE *pg;
37724420c01Scgd EPGNO *parent;
37861238e71Schristos indx_t cnt, idx, *ip, offset;
37940b37a3bSjoerg uint32_t nksize;
38024420c01Scgd char *from;
38124420c01Scgd
38224420c01Scgd /*
38324420c01Scgd * Walk the parent page stack -- a LIFO stack of the pages that were
38424420c01Scgd * traversed when we searched for the page where the delete occurred.
38524420c01Scgd * Each stack entry is a page number and a page index offset. The
38624420c01Scgd * offset is for the page traversed on the search. We've just deleted
38724420c01Scgd * a page, so we have to delete the key from the parent page.
38824420c01Scgd *
38924420c01Scgd * If the delete from the parent page makes it empty, this process may
39024420c01Scgd * continue all the way up the tree. We stop if we reach the root page
39124420c01Scgd * (which is never deleted, it's just not worth the effort) or if the
39224420c01Scgd * delete does not empty the page.
39324420c01Scgd */
39424420c01Scgd while ((parent = BT_POP(t)) != NULL) {
39524420c01Scgd /* Get the parent page. */
396*aae80e6bSchristos if ((pg = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL)
39724420c01Scgd return (RET_ERROR);
39824420c01Scgd
39961238e71Schristos idx = parent->index;
40061238e71Schristos bi = GETBINTERNAL(pg, idx);
40124420c01Scgd
40224420c01Scgd /* Free any overflow pages. */
40324420c01Scgd if (bi->flags & P_BIGKEY &&
40424420c01Scgd __ovfl_delete(t, bi->bytes) == RET_ERROR) {
40524420c01Scgd mpool_put(t->bt_mp, pg, 0);
40624420c01Scgd return (RET_ERROR);
40724420c01Scgd }
40824420c01Scgd
40924420c01Scgd /*
41024420c01Scgd * Free the parent if it has only the one key and it's not the
41124420c01Scgd * root page. If it's the rootpage, turn it back into an empty
41224420c01Scgd * leaf page.
41324420c01Scgd */
414d4639c81Schristos if (NEXTINDEX(pg) == 1) {
41524420c01Scgd if (pg->pgno == P_ROOT) {
41624420c01Scgd pg->lower = BTDATAOFF;
41724420c01Scgd pg->upper = t->bt_psize;
41824420c01Scgd pg->flags = P_BLEAF;
41924420c01Scgd } else {
42024420c01Scgd if (__bt_relink(t, pg) || __bt_free(t, pg))
42124420c01Scgd return (RET_ERROR);
42224420c01Scgd continue;
42324420c01Scgd }
424d4639c81Schristos } else {
42524420c01Scgd /* Pack remaining key items at the end of the page. */
42624420c01Scgd nksize = NBINTERNAL(bi->ksize);
42761238e71Schristos from = (char *)(void *)pg + pg->upper;
42861238e71Schristos memmove(from + nksize, from,
42961238e71Schristos (size_t)((char *)(void *)bi - from));
43024420c01Scgd pg->upper += nksize;
43124420c01Scgd
43224420c01Scgd /* Adjust indices' offsets, shift the indices down. */
43361238e71Schristos offset = pg->linp[idx];
43461238e71Schristos for (cnt = idx, ip = &pg->linp[0]; cnt--; ++ip)
43524420c01Scgd if (ip[0] < offset)
43624420c01Scgd ip[0] += nksize;
43761238e71Schristos for (cnt = NEXTINDEX(pg) - idx; --cnt; ++ip)
43824420c01Scgd ip[0] = ip[1] < offset ? ip[1] + nksize : ip[1];
43924420c01Scgd pg->lower -= sizeof(indx_t);
44024420c01Scgd }
44124420c01Scgd
44224420c01Scgd mpool_put(t->bt_mp, pg, MPOOL_DIRTY);
44324420c01Scgd break;
44424420c01Scgd }
44524420c01Scgd
44624420c01Scgd /* Free the leaf page, as long as it wasn't the root. */
44724420c01Scgd if (h->pgno == P_ROOT) {
44824420c01Scgd mpool_put(t->bt_mp, h, MPOOL_DIRTY);
44924420c01Scgd return (RET_SUCCESS);
45024420c01Scgd }
45124420c01Scgd return (__bt_relink(t, h) || __bt_free(t, h));
45224420c01Scgd }
45324420c01Scgd
45424420c01Scgd /*
45524420c01Scgd * __bt_dleaf --
45624420c01Scgd * Delete a single record from a leaf page.
45724420c01Scgd *
45824420c01Scgd * Parameters:
45924420c01Scgd * t: tree
46024420c01Scgd * key: referenced key
46124420c01Scgd * h: page
46261238e71Schristos * idx: index on page to delete
4639f0aa214Scgd *
4649f0aa214Scgd * Returns:
4659f0aa214Scgd * RET_SUCCESS, RET_ERROR.
4669f0aa214Scgd */
4679f0aa214Scgd int
__bt_dleaf(BTREE * t,const DBT * key,PAGE * h,u_int idx)468cb9daf8fSchristos __bt_dleaf(BTREE *t, const DBT *key, PAGE *h, u_int idx)
4699f0aa214Scgd {
47024420c01Scgd BLEAF *bl;
47124420c01Scgd indx_t cnt, *ip, offset;
47240b37a3bSjoerg uint32_t nbytes;
4739f0aa214Scgd void *to;
47424420c01Scgd char *from;
4759f0aa214Scgd
47624420c01Scgd /* If this record is referenced by the cursor, delete the cursor. */
47724420c01Scgd if (F_ISSET(&t->bt_cursor, CURS_INIT) &&
47824420c01Scgd !F_ISSET(&t->bt_cursor, CURS_ACQUIRE) &&
47961238e71Schristos t->bt_cursor.pg.pgno == h->pgno && t->bt_cursor.pg.index == idx &&
48061238e71Schristos __bt_curdel(t, key, h, idx))
48124420c01Scgd return (RET_ERROR);
48224420c01Scgd
48324420c01Scgd /* If the entry uses overflow pages, make them available for reuse. */
48461238e71Schristos to = bl = GETBLEAF(h, idx);
4859f0aa214Scgd if (bl->flags & P_BIGKEY && __ovfl_delete(t, bl->bytes) == RET_ERROR)
4869f0aa214Scgd return (RET_ERROR);
4879f0aa214Scgd if (bl->flags & P_BIGDATA &&
4889f0aa214Scgd __ovfl_delete(t, bl->bytes + bl->ksize) == RET_ERROR)
4899f0aa214Scgd return (RET_ERROR);
4909f0aa214Scgd
49124420c01Scgd /* Pack the remaining key/data items at the end of the page. */
49224420c01Scgd nbytes = NBLEAF(bl);
49361238e71Schristos from = (char *)(void *)h + h->upper;
49461238e71Schristos memmove(from + nbytes, from, (size_t)((char *)(void *)to - from));
4959f0aa214Scgd h->upper += nbytes;
4969f0aa214Scgd
49724420c01Scgd /* Adjust the indices' offsets, shift the indices down. */
49861238e71Schristos offset = h->linp[idx];
49961238e71Schristos for (cnt = idx, ip = &h->linp[0]; cnt--; ++ip)
5009f0aa214Scgd if (ip[0] < offset)
5019f0aa214Scgd ip[0] += nbytes;
50261238e71Schristos for (cnt = NEXTINDEX(h) - idx; --cnt; ++ip)
5039f0aa214Scgd ip[0] = ip[1] < offset ? ip[1] + nbytes : ip[1];
5049f0aa214Scgd h->lower -= sizeof(indx_t);
50524420c01Scgd
50624420c01Scgd /* If the cursor is on this page, adjust it as necessary. */
50724420c01Scgd if (F_ISSET(&t->bt_cursor, CURS_INIT) &&
50824420c01Scgd !F_ISSET(&t->bt_cursor, CURS_ACQUIRE) &&
50961238e71Schristos t->bt_cursor.pg.pgno == h->pgno && t->bt_cursor.pg.index > idx)
51024420c01Scgd --t->bt_cursor.pg.index;
51124420c01Scgd
5129f0aa214Scgd return (RET_SUCCESS);
5139f0aa214Scgd }
51424420c01Scgd
51524420c01Scgd /*
51624420c01Scgd * __bt_curdel --
51724420c01Scgd * Delete the cursor.
51824420c01Scgd *
51924420c01Scgd * Parameters:
52024420c01Scgd * t: tree
52124420c01Scgd * key: referenced key (or NULL)
52224420c01Scgd * h: page
52361238e71Schristos * idx: index on page to delete
52424420c01Scgd *
52524420c01Scgd * Returns:
52624420c01Scgd * RET_SUCCESS, RET_ERROR.
52724420c01Scgd */
52824420c01Scgd static int
__bt_curdel(BTREE * t,const DBT * key,PAGE * h,u_int idx)529cb9daf8fSchristos __bt_curdel(BTREE *t, const DBT *key, PAGE *h, u_int idx)
53024420c01Scgd {
53124420c01Scgd CURSOR *c;
53224420c01Scgd EPG e;
53324420c01Scgd PAGE *pg;
53424420c01Scgd int curcopy, status;
53524420c01Scgd
53624420c01Scgd /*
53724420c01Scgd * If there are duplicates, move forward or backward to one.
53824420c01Scgd * Otherwise, copy the key into the cursor area.
53924420c01Scgd */
54024420c01Scgd c = &t->bt_cursor;
54124420c01Scgd F_CLR(c, CURS_AFTER | CURS_BEFORE | CURS_ACQUIRE);
54224420c01Scgd
54324420c01Scgd curcopy = 0;
54424420c01Scgd if (!F_ISSET(t, B_NODUPS)) {
54524420c01Scgd /*
54624420c01Scgd * We're going to have to do comparisons. If we weren't
54724420c01Scgd * provided a copy of the key, i.e. the user is deleting
54824420c01Scgd * the current cursor position, get one.
54924420c01Scgd */
55024420c01Scgd if (key == NULL) {
55124420c01Scgd e.page = h;
55261238e71Schristos e.index = idx;
55324420c01Scgd if ((status = __bt_ret(t, &e,
55424420c01Scgd &c->key, &c->key, NULL, NULL, 1)) != RET_SUCCESS)
55524420c01Scgd return (status);
55624420c01Scgd curcopy = 1;
55724420c01Scgd key = &c->key;
55824420c01Scgd }
55924420c01Scgd /* Check previous key, if not at the beginning of the page. */
56061238e71Schristos if (idx > 0) {
56124420c01Scgd e.page = h;
56261238e71Schristos e.index = idx - 1;
56324420c01Scgd if (__bt_cmp(t, key, &e) == 0) {
56424420c01Scgd F_SET(c, CURS_BEFORE);
56524420c01Scgd goto dup2;
56624420c01Scgd }
56724420c01Scgd }
56824420c01Scgd /* Check next key, if not at the end of the page. */
56979becf98Slukem if (idx < (unsigned)(NEXTINDEX(h) - 1)) {
57024420c01Scgd e.page = h;
57161238e71Schristos e.index = idx + 1;
57224420c01Scgd if (__bt_cmp(t, key, &e) == 0) {
57324420c01Scgd F_SET(c, CURS_AFTER);
57424420c01Scgd goto dup2;
57524420c01Scgd }
57624420c01Scgd }
57724420c01Scgd /* Check previous key if at the beginning of the page. */
57861238e71Schristos if (idx == 0 && h->prevpg != P_INVALID) {
579*aae80e6bSchristos if ((pg = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL)
58024420c01Scgd return (RET_ERROR);
58124420c01Scgd e.page = pg;
58224420c01Scgd e.index = NEXTINDEX(pg) - 1;
58324420c01Scgd if (__bt_cmp(t, key, &e) == 0) {
58424420c01Scgd F_SET(c, CURS_BEFORE);
58524420c01Scgd goto dup1;
58624420c01Scgd }
58724420c01Scgd mpool_put(t->bt_mp, pg, 0);
58824420c01Scgd }
58924420c01Scgd /* Check next key if at the end of the page. */
59079becf98Slukem if (idx == (unsigned)(NEXTINDEX(h) - 1) && h->nextpg != P_INVALID) {
591*aae80e6bSchristos if ((pg = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL)
59224420c01Scgd return (RET_ERROR);
59324420c01Scgd e.page = pg;
59424420c01Scgd e.index = 0;
59524420c01Scgd if (__bt_cmp(t, key, &e) == 0) {
59624420c01Scgd F_SET(c, CURS_AFTER);
59724420c01Scgd dup1: mpool_put(t->bt_mp, pg, 0);
59824420c01Scgd dup2: c->pg.pgno = e.page->pgno;
59924420c01Scgd c->pg.index = e.index;
60024420c01Scgd return (RET_SUCCESS);
60124420c01Scgd }
60224420c01Scgd mpool_put(t->bt_mp, pg, 0);
60324420c01Scgd }
60424420c01Scgd }
60524420c01Scgd e.page = h;
60661238e71Schristos e.index = idx;
60724420c01Scgd if (curcopy || (status =
60824420c01Scgd __bt_ret(t, &e, &c->key, &c->key, NULL, NULL, 1)) == RET_SUCCESS) {
60924420c01Scgd F_SET(c, CURS_ACQUIRE);
61024420c01Scgd return (RET_SUCCESS);
61124420c01Scgd }
61224420c01Scgd return (status);
61324420c01Scgd }
61424420c01Scgd
61524420c01Scgd /*
61624420c01Scgd * __bt_relink --
61724420c01Scgd * Link around a deleted page.
61824420c01Scgd *
61924420c01Scgd * Parameters:
62024420c01Scgd * t: tree
62124420c01Scgd * h: page to be deleted
62224420c01Scgd */
62386147b1cSchristos int
__bt_relink(BTREE * t,PAGE * h)624cb9daf8fSchristos __bt_relink(BTREE *t, PAGE *h)
62524420c01Scgd {
62624420c01Scgd PAGE *pg;
62724420c01Scgd
62824420c01Scgd if (h->nextpg != P_INVALID) {
629*aae80e6bSchristos if ((pg = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL)
63024420c01Scgd return (RET_ERROR);
63124420c01Scgd pg->prevpg = h->prevpg;
63224420c01Scgd mpool_put(t->bt_mp, pg, MPOOL_DIRTY);
63324420c01Scgd }
63424420c01Scgd if (h->prevpg != P_INVALID) {
635*aae80e6bSchristos if ((pg = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL)
63624420c01Scgd return (RET_ERROR);
63724420c01Scgd pg->nextpg = h->nextpg;
63824420c01Scgd mpool_put(t->bt_mp, pg, MPOOL_DIRTY);
63924420c01Scgd }
64024420c01Scgd return (0);
64124420c01Scgd }
642