]>
git.saurik.com Git - apple/libc.git/blob - db/btree/bt_delete.c
02715e9df1974e45943c05ba13f5585a0d59f064
2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 * Copyright (c) 1990, 1993, 1994
25 * The Regents of the University of California. All rights reserved.
27 * This code is derived from software contributed to Berkeley by
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 * must display the following acknowledgement:
40 * This product includes software developed by the University of
41 * California, Berkeley and its contributors.
42 * 4. Neither the name of the University nor the names of its contributors
43 * may be used to endorse or promote products derived from this software
44 * without specific prior written permission.
46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 #if defined(LIBC_SCCS) && !defined(lint)
60 static char sccsid
[] = "@(#)bt_delete.c 8.13 (Berkeley) 7/28/94";
61 #endif /* LIBC_SCCS and not lint */
62 #include <sys/cdefs.h>
64 #include <sys/types.h>
73 static int __bt_bdelete(BTREE
*, const DBT
*);
74 static int __bt_curdel(BTREE
*, const DBT
*, PAGE
*, u_int
);
75 static int __bt_pdelete(BTREE
*, PAGE
*);
76 static int __bt_relink(BTREE
*, PAGE
*);
77 static int __bt_stkacq(BTREE
*, PAGE
**, CURSOR
*);
81 * Delete the item(s) referenced by a key.
83 * Return RET_SPECIAL if the key is not found.
86 __bt_delete(dbp
, key
, flags
)
98 /* Toss any page pinned across calls. */
99 if (t
->bt_pinned
!= NULL
) {
100 mpool_put(t
->bt_mp
, t
->bt_pinned
, 0);
104 /* Check for change to a read-only tree. */
105 if (F_ISSET(t
, B_RDONLY
)) {
112 status
= __bt_bdelete(t
, key
);
116 * If flags is R_CURSOR, delete the cursor. Must already
117 * have started a scan and not have already deleted it.
120 if (F_ISSET(c
, CURS_INIT
)) {
121 if (F_ISSET(c
, CURS_ACQUIRE
| CURS_AFTER
| CURS_BEFORE
))
122 return (RET_SPECIAL
);
123 if ((h
= mpool_get(t
->bt_mp
, c
->pg
.pgno
, 0)) == NULL
)
127 * If the page is about to be emptied, we'll need to
128 * delete it, which means we have to acquire a stack.
130 if (NEXTINDEX(h
) == 1)
131 if (__bt_stkacq(t
, &h
, &t
->bt_cursor
))
134 status
= __bt_dleaf(t
, NULL
, h
, c
->pg
.index
);
136 if (NEXTINDEX(h
) == 0 && status
== RET_SUCCESS
) {
137 if (__bt_pdelete(t
, h
))
141 h
, status
== RET_SUCCESS
? MPOOL_DIRTY
: 0);
149 if (status
== RET_SUCCESS
)
150 F_SET(t
, B_MODIFIED
);
156 * Acquire a stack so we can delete a cursor entry.
160 * hp: pointer to current, pinned PAGE pointer
161 * c: pointer to the cursor
164 * 0 on success, 1 on failure
167 __bt_stkacq(t
, hp
, c
)
178 recno_t nextpg
, prevpg
;
182 * Find the first occurrence of the key in the tree. Toss the
183 * currently locked page so we don't hit an already-locked page.
186 mpool_put(t
->bt_mp
, h
, 0);
187 if ((e
= __bt_search(t
, &c
->key
, &exact
)) == NULL
)
191 /* See if we got it in one shot. */
192 if (h
->pgno
== c
->pg
.pgno
)
196 * Move right, looking for the page. At each move we have to move
197 * up the stack until we don't have to move to the next page. If
198 * we have to change pages at an internal level, we have to fix the
201 while (h
->pgno
!= c
->pg
.pgno
) {
202 if ((nextpg
= h
->nextpg
) == P_INVALID
)
204 mpool_put(t
->bt_mp
, h
, 0);
206 /* Move up the stack. */
207 for (level
= 0; (parent
= BT_POP(t
)) != NULL
; ++level
) {
208 /* Get the parent page. */
209 if ((h
= mpool_get(t
->bt_mp
, parent
->pgno
, 0)) == NULL
)
212 /* Move to the next index. */
213 if (parent
->index
!= NEXTINDEX(h
) - 1) {
214 index
= parent
->index
+ 1;
215 BT_PUSH(t
, h
->pgno
, index
);
218 mpool_put(t
->bt_mp
, h
, 0);
221 /* Restore the stack. */
223 /* Push the next level down onto the stack. */
224 bi
= GETBINTERNAL(h
, index
);
228 /* Lose the currently pinned page. */
229 mpool_put(t
->bt_mp
, h
, 0);
231 /* Get the next level down. */
232 if ((h
= mpool_get(t
->bt_mp
, pgno
, 0)) == NULL
)
236 mpool_put(t
->bt_mp
, h
, 0);
237 if ((h
= mpool_get(t
->bt_mp
, nextpg
, 0)) == NULL
)
241 if (h
->pgno
== c
->pg
.pgno
)
244 /* Reacquire the original stack. */
245 mpool_put(t
->bt_mp
, h
, 0);
246 if ((e
= __bt_search(t
, &c
->key
, &exact
)) == NULL
)
251 * Move left, looking for the page. At each move we have to move
252 * up the stack until we don't have to change pages to move to the
253 * next page. If we have to change pages at an internal level, we
254 * have to fix the stack back up.
256 while (h
->pgno
!= c
->pg
.pgno
) {
257 if ((prevpg
= h
->prevpg
) == P_INVALID
)
259 mpool_put(t
->bt_mp
, h
, 0);
261 /* Move up the stack. */
262 for (level
= 0; (parent
= BT_POP(t
)) != NULL
; ++level
) {
263 /* Get the parent page. */
264 if ((h
= mpool_get(t
->bt_mp
, parent
->pgno
, 0)) == NULL
)
267 /* Move to the next index. */
268 if (parent
->index
!= 0) {
269 index
= parent
->index
- 1;
270 BT_PUSH(t
, h
->pgno
, index
);
273 mpool_put(t
->bt_mp
, h
, 0);
276 /* Restore the stack. */
278 /* Push the next level down onto the stack. */
279 bi
= GETBINTERNAL(h
, index
);
282 /* Lose the currently pinned page. */
283 mpool_put(t
->bt_mp
, h
, 0);
285 /* Get the next level down. */
286 if ((h
= mpool_get(t
->bt_mp
, pgno
, 0)) == NULL
)
289 index
= NEXTINDEX(h
) - 1;
290 BT_PUSH(t
, pgno
, index
);
292 mpool_put(t
->bt_mp
, h
, 0);
293 if ((h
= mpool_get(t
->bt_mp
, prevpg
, 0)) == NULL
)
298 ret
: mpool_put(t
->bt_mp
, h
, 0);
299 return ((*hp
= mpool_get(t
->bt_mp
, c
->pg
.pgno
, 0)) == NULL
);
304 * Delete all key/data pairs matching the specified key.
311 * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found.
320 int deleted
, exact
, redo
;
324 /* Find any matching record; __bt_search pins the page. */
325 loop
: if ((e
= __bt_search(t
, key
, &exact
)) == NULL
)
326 return (deleted
? RET_SUCCESS
: RET_ERROR
);
328 mpool_put(t
->bt_mp
, e
->page
, 0);
329 return (deleted
? RET_SUCCESS
: RET_SPECIAL
);
333 * Delete forward, then delete backward, from the found key. If
334 * there are duplicates and we reach either side of the page, do
335 * the key search again, so that we get them all.
340 if (__bt_dleaf(t
, key
, h
, e
->index
)) {
341 mpool_put(t
->bt_mp
, h
, 0);
344 if (F_ISSET(t
, B_NODUPS
)) {
345 if (NEXTINDEX(h
) == 0) {
346 if (__bt_pdelete(t
, h
))
349 mpool_put(t
->bt_mp
, h
, MPOOL_DIRTY
);
350 return (RET_SUCCESS
);
353 } while (e
->index
< NEXTINDEX(h
) && __bt_cmp(t
, key
, e
) == 0);
355 /* Check for right-hand edge of the page. */
356 if (e
->index
== NEXTINDEX(h
))
359 /* Delete from the key to the beginning of the page. */
360 while (e
->index
-- > 0) {
361 if (__bt_cmp(t
, key
, e
) != 0)
363 if (__bt_dleaf(t
, key
, h
, e
->index
) == RET_ERROR
) {
364 mpool_put(t
->bt_mp
, h
, 0);
371 /* Check for an empty page. */
372 if (NEXTINDEX(h
) == 0) {
373 if (__bt_pdelete(t
, h
))
379 mpool_put(t
->bt_mp
, h
, MPOOL_DIRTY
);
383 return (RET_SUCCESS
);
388 * Delete a single page from the tree.
395 * RET_SUCCESS, RET_ERROR.
398 * mpool_put's the page
408 indx_t cnt
, index
, *ip
, offset
;
413 * Walk the parent page stack -- a LIFO stack of the pages that were
414 * traversed when we searched for the page where the delete occurred.
415 * Each stack entry is a page number and a page index offset. The
416 * offset is for the page traversed on the search. We've just deleted
417 * a page, so we have to delete the key from the parent page.
419 * If the delete from the parent page makes it empty, this process may
420 * continue all the way up the tree. We stop if we reach the root page
421 * (which is never deleted, it's just not worth the effort) or if the
422 * delete does not empty the page.
424 while ((parent
= BT_POP(t
)) != NULL
) {
425 /* Get the parent page. */
426 if ((pg
= mpool_get(t
->bt_mp
, parent
->pgno
, 0)) == NULL
)
429 index
= parent
->index
;
430 bi
= GETBINTERNAL(pg
, index
);
432 /* Free any overflow pages. */
433 if (bi
->flags
& P_BIGKEY
&&
434 __ovfl_delete(t
, bi
->bytes
) == RET_ERROR
) {
435 mpool_put(t
->bt_mp
, pg
, 0);
440 * Free the parent if it has only the one key and it's not the
441 * root page. If it's the rootpage, turn it back into an empty
444 if (NEXTINDEX(pg
) == 1)
445 if (pg
->pgno
== P_ROOT
) {
446 pg
->lower
= BTDATAOFF
;
447 pg
->upper
= t
->bt_psize
;
450 if (__bt_relink(t
, pg
) || __bt_free(t
, pg
))
455 /* Pack remaining key items at the end of the page. */
456 nksize
= NBINTERNAL(bi
->ksize
);
457 from
= (char *)pg
+ pg
->upper
;
458 memmove(from
+ nksize
, from
, (char *)bi
- from
);
461 /* Adjust indices' offsets, shift the indices down. */
462 offset
= pg
->linp
[index
];
463 for (cnt
= index
, ip
= &pg
->linp
[0]; cnt
--; ++ip
)
466 for (cnt
= NEXTINDEX(pg
) - index
; --cnt
; ++ip
)
467 ip
[0] = ip
[1] < offset
? ip
[1] + nksize
: ip
[1];
468 pg
->lower
-= sizeof(indx_t
);
471 mpool_put(t
->bt_mp
, pg
, MPOOL_DIRTY
);
475 /* Free the leaf page, as long as it wasn't the root. */
476 if (h
->pgno
== P_ROOT
) {
477 mpool_put(t
->bt_mp
, h
, MPOOL_DIRTY
);
478 return (RET_SUCCESS
);
480 return (__bt_relink(t
, h
) || __bt_free(t
, h
));
485 * Delete a single record from a leaf page.
489 * key: referenced key
491 * index: index on page to delete
494 * RET_SUCCESS, RET_ERROR.
497 __bt_dleaf(t
, key
, h
, index
)
504 indx_t cnt
, *ip
, offset
;
509 /* If this record is referenced by the cursor, delete the cursor. */
510 if (F_ISSET(&t
->bt_cursor
, CURS_INIT
) &&
511 !F_ISSET(&t
->bt_cursor
, CURS_ACQUIRE
) &&
512 t
->bt_cursor
.pg
.pgno
== h
->pgno
&& t
->bt_cursor
.pg
.index
== index
&&
513 __bt_curdel(t
, key
, h
, index
))
516 /* If the entry uses overflow pages, make them available for reuse. */
517 to
= bl
= GETBLEAF(h
, index
);
518 if (bl
->flags
& P_BIGKEY
&& __ovfl_delete(t
, bl
->bytes
) == RET_ERROR
)
520 if (bl
->flags
& P_BIGDATA
&&
521 __ovfl_delete(t
, bl
->bytes
+ bl
->ksize
) == RET_ERROR
)
524 /* Pack the remaining key/data items at the end of the page. */
526 from
= (char *)h
+ h
->upper
;
527 memmove(from
+ nbytes
, from
, (char *)to
- from
);
530 /* Adjust the indices' offsets, shift the indices down. */
531 offset
= h
->linp
[index
];
532 for (cnt
= index
, ip
= &h
->linp
[0]; cnt
--; ++ip
)
535 for (cnt
= NEXTINDEX(h
) - index
; --cnt
; ++ip
)
536 ip
[0] = ip
[1] < offset
? ip
[1] + nbytes
: ip
[1];
537 h
->lower
-= sizeof(indx_t
);
539 /* If the cursor is on this page, adjust it as necessary. */
540 if (F_ISSET(&t
->bt_cursor
, CURS_INIT
) &&
541 !F_ISSET(&t
->bt_cursor
, CURS_ACQUIRE
) &&
542 t
->bt_cursor
.pg
.pgno
== h
->pgno
&& t
->bt_cursor
.pg
.index
> index
)
543 --t
->bt_cursor
.pg
.index
;
545 return (RET_SUCCESS
);
554 * key: referenced key (or NULL)
556 * index: index on page to delete
559 * RET_SUCCESS, RET_ERROR.
562 __bt_curdel(t
, key
, h
, index
)
574 * If there are duplicates, move forward or backward to one.
575 * Otherwise, copy the key into the cursor area.
578 F_CLR(c
, CURS_AFTER
| CURS_BEFORE
| CURS_ACQUIRE
);
581 if (!F_ISSET(t
, B_NODUPS
)) {
583 * We're going to have to do comparisons. If we weren't
584 * provided a copy of the key, i.e. the user is deleting
585 * the current cursor position, get one.
590 if ((status
= __bt_ret(t
, &e
,
591 &c
->key
, &c
->key
, NULL
, NULL
, 1)) != RET_SUCCESS
)
596 /* Check previous key, if not at the beginning of the page. */
600 if (__bt_cmp(t
, key
, &e
) == 0) {
601 F_SET(c
, CURS_BEFORE
);
605 /* Check next key, if not at the end of the page. */
606 if (index
< NEXTINDEX(h
) - 1) {
609 if (__bt_cmp(t
, key
, &e
) == 0) {
610 F_SET(c
, CURS_AFTER
);
614 /* Check previous key if at the beginning of the page. */
615 if (index
== 0 && h
->prevpg
!= P_INVALID
) {
616 if ((pg
= mpool_get(t
->bt_mp
, h
->prevpg
, 0)) == NULL
)
619 e
.index
= NEXTINDEX(pg
) - 1;
620 if (__bt_cmp(t
, key
, &e
) == 0) {
621 F_SET(c
, CURS_BEFORE
);
624 mpool_put(t
->bt_mp
, pg
, 0);
626 /* Check next key if at the end of the page. */
627 if (index
== NEXTINDEX(h
) - 1 && h
->nextpg
!= P_INVALID
) {
628 if ((pg
= mpool_get(t
->bt_mp
, h
->nextpg
, 0)) == NULL
)
632 if (__bt_cmp(t
, key
, &e
) == 0) {
633 F_SET(c
, CURS_AFTER
);
634 dup1
: mpool_put(t
->bt_mp
, pg
, 0);
635 dup2
: c
->pg
.pgno
= e
.page
->pgno
;
636 c
->pg
.index
= e
.index
;
637 return (RET_SUCCESS
);
639 mpool_put(t
->bt_mp
, pg
, 0);
644 if (curcopy
|| (status
=
645 __bt_ret(t
, &e
, &c
->key
, &c
->key
, NULL
, NULL
, 1)) == RET_SUCCESS
) {
646 F_SET(c
, CURS_ACQUIRE
);
647 return (RET_SUCCESS
);
654 * Link around a deleted page.
658 * h: page to be deleted
667 if (h
->nextpg
!= P_INVALID
) {
668 if ((pg
= mpool_get(t
->bt_mp
, h
->nextpg
, 0)) == NULL
)
670 pg
->prevpg
= h
->prevpg
;
671 mpool_put(t
->bt_mp
, pg
, MPOOL_DIRTY
);
673 if (h
->prevpg
!= P_INVALID
) {
674 if ((pg
= mpool_get(t
->bt_mp
, h
->prevpg
, 0)) == NULL
)
676 pg
->nextpg
= h
->nextpg
;
677 mpool_put(t
->bt_mp
, pg
, MPOOL_DIRTY
);