xref: /csrg-svn/lib/libc/db/btree/bt_seq.c (revision 57989)
146133Smao /*-
246133Smao  * Copyright (c) 1990 The Regents of the University of California.
346133Smao  * All rights reserved.
446133Smao  *
546133Smao  * This code is derived from software contributed to Berkeley by
646133Smao  * Mike Olson.
746133Smao  *
846133Smao  * %sccs.include.redist.c%
946133Smao  */
1046133Smao 
1146133Smao #if defined(LIBC_SCCS) && !defined(lint)
12*57989Sbostic static char sccsid[] = "@(#)bt_seq.c	5.9 (Berkeley) 02/14/93";
1346133Smao #endif /* LIBC_SCCS and not lint */
1446133Smao 
1546133Smao #include <sys/types.h>
1656743Sbostic 
1746561Sbostic #include <errno.h>
1856743Sbostic #include <stddef.h>
1950989Sbostic #include <stdio.h>
2046561Sbostic #include <stdlib.h>
2156743Sbostic 
2257932Sbostic #include <db.h>
2346133Smao #include "btree.h"
2446133Smao 
2550989Sbostic static int	 bt_seqadv __P((BTREE *, EPG *, int));
2650989Sbostic static int	 bt_seqset __P((BTREE *, EPG *, DBT *, int));
2750989Sbostic 
2846133Smao /*
2950989Sbostic  * Sequential scan support.
3046133Smao  *
3150989Sbostic  * The tree can be scanned sequentially, starting from either end of the tree
3250989Sbostic  * or from any specific key.  A scan request before any scanning is done is
3350989Sbostic  * initialized as starting from the least node.
3446133Smao  *
3550989Sbostic  * Each tree has an EPGNO which has the current position of the cursor.  The
3650989Sbostic  * cursor has to survive deletions/insertions in the tree without losing its
3750989Sbostic  * position.  This is done by noting deletions without doing them, and then
3850989Sbostic  * doing them when the cursor moves (or the tree is closed).
3950989Sbostic  */
4050989Sbostic 
4150989Sbostic /*
4250989Sbostic  * __BT_SEQ -- Btree sequential scan interface.
4346133Smao  *
4450989Sbostic  * Parameters:
4550989Sbostic  *	dbp:	pointer to access method
4650989Sbostic  *	key:	key for positioning and return value
4750989Sbostic  *	data:	data return value
4850989Sbostic  *	flags:	R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV.
4946133Smao  *
5050989Sbostic  * Returns:
5150989Sbostic  *	RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
5246133Smao  */
5346133Smao int
5450989Sbostic __bt_seq(dbp, key, data, flags)
5550989Sbostic 	const DB *dbp;
5650989Sbostic 	DBT *key, *data;
5750989Sbostic 	u_int flags;
5846133Smao {
5950989Sbostic 	BTREE *t;
6050989Sbostic 	EPG e;
6150989Sbostic 	int status;
6246133Smao 
6346133Smao 	/*
6450989Sbostic 	 * If scan unitialized as yet, or starting at a specific record, set
6550989Sbostic 	 * the scan to a specific key.  Both bt_seqset and bt_seqadv pin the
6650989Sbostic 	 * page the cursor references if they're successful.
6746133Smao 	 */
6850989Sbostic 	t = dbp->internal;
6950989Sbostic 	switch(flags) {
7050989Sbostic 	case R_NEXT:
7151100Sbostic 	case R_PREV:
7250989Sbostic 		if (ISSET(t, BTF_SEQINIT)) {
7350989Sbostic 			status = bt_seqadv(t, &e, flags);
7450989Sbostic 			break;
7550989Sbostic 		}
7650989Sbostic 		/* FALLTHROUGH */
7750989Sbostic 	case R_CURSOR:
7850989Sbostic 	case R_FIRST:
7950989Sbostic 	case R_LAST:
8050989Sbostic 		status = bt_seqset(t, &e, key, flags);
8150989Sbostic 		break;
8250989Sbostic 	default:
8350989Sbostic 		errno = EINVAL;
8450989Sbostic 		return (RET_ERROR);
8550989Sbostic 	}
8646133Smao 
8750989Sbostic 	if (status == RET_SUCCESS) {
8850989Sbostic 		status = __bt_ret(t, &e, key, data);
8946133Smao 
9050989Sbostic 		/* Update the actual cursor. */
9151100Sbostic 		t->bt_bcursor.pgno = e.page->pgno;
9251100Sbostic 		t->bt_bcursor.index = e.index;
9350989Sbostic 		mpool_put(t->bt_mp, e.page, 0);
9451100Sbostic 		SET(t, BTF_SEQINIT);
9550989Sbostic 	}
9650989Sbostic 	return (status);
9750989Sbostic }
9846133Smao 
9950989Sbostic /*
10050989Sbostic  * BT_SEQSET -- Set the sequential scan to a specific key.
10150989Sbostic  *
10250989Sbostic  * Parameters:
10350989Sbostic  *	t:	tree
10450989Sbostic  *	ep:	storage for returned key
10550989Sbostic  *	key:	key for initial scan position
10650989Sbostic  *	flags:	R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV
10750989Sbostic  *
10850989Sbostic  * Side effects:
10950989Sbostic  *	Pins the page the cursor references.
11050989Sbostic  *
11150989Sbostic  * Returns:
11250989Sbostic  *	RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
11350989Sbostic  */
11450989Sbostic static int
11550989Sbostic bt_seqset(t, ep, key, flags)
11650989Sbostic 	BTREE *t;
11750989Sbostic 	EPG *ep;
11850989Sbostic 	DBT *key;
11950989Sbostic 	int flags;
12050989Sbostic {
12150989Sbostic 	EPG *e;
12250989Sbostic 	PAGE *h;
12350989Sbostic 	pgno_t pg;
12450989Sbostic 	int exact;
12546133Smao 
12650989Sbostic 	/*
12750989Sbostic 	 * Delete any already deleted record that we've been saving because
12850989Sbostic 	 * the cursor pointed to it.  Since going to a specific key, should
12950989Sbostic 	 * delete any logically deleted records so they aren't found.
13050989Sbostic 	 */
13150989Sbostic 	if (ISSET(t, BTF_DELCRSR) && __bt_crsrdel(t, &t->bt_bcursor))
13250989Sbostic 		return (RET_ERROR);
13346133Smao 
13450989Sbostic 	/*
13551100Sbostic 	 * Find the first, last or specific key in the tree and point the cursor
13651100Sbostic 	 * at it.  The cursor may not be moved until a new key has been found.
13750989Sbostic 	 */
13850989Sbostic 	switch(flags) {
13950989Sbostic 	case R_CURSOR:				/* Keyed scan. */
14051100Sbostic 		/*
14151100Sbostic 		 * Find the first instance of the key or the smallest key which
14251100Sbostic 		 * is greater than or equal to the specified key.  If run out
14351100Sbostic 		 * of keys, return RET_SPECIAL.
14451100Sbostic 		 */
14550989Sbostic 		if (key->data == NULL || key->size == 0) {
14650989Sbostic 			errno = EINVAL;
14746133Smao 			return (RET_ERROR);
14850989Sbostic 		}
14950989Sbostic 		e = __bt_first(t, key, &exact);	/* Returns pinned page. */
15050989Sbostic 		if (e == NULL)
15150989Sbostic 			return (RET_ERROR);
15251100Sbostic 		/*
15351100Sbostic 		 * If at the end of a page, skip any empty pages and find the
15451100Sbostic 		 * next entry.
15551100Sbostic 		 */
15651100Sbostic 		if (e->index == NEXTINDEX(e->page)) {
15751100Sbostic 			h = e->page;
15851100Sbostic 			do {
15951100Sbostic 				pg = h->nextpg;
16051100Sbostic 				mpool_put(t->bt_mp, h, 0);
16151100Sbostic 				if (pg == P_INVALID)
16251100Sbostic 					return (RET_SPECIAL);
16351100Sbostic 				if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
16451100Sbostic 					return (RET_ERROR);
16551100Sbostic 			} while (NEXTINDEX(h) == 0);
16651100Sbostic 			e->index = 0;
16751100Sbostic 			e->page = h;
16850989Sbostic 		}
16950989Sbostic 		*ep = *e;
17050989Sbostic 		break;
17150989Sbostic 	case R_FIRST:				/* First record. */
17250989Sbostic 	case R_NEXT:
17350989Sbostic 		/* Walk down the left-hand side of the tree. */
17450989Sbostic 		for (pg = P_ROOT;;) {
17550989Sbostic 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
17650989Sbostic 				return (RET_ERROR);
17756743Sbostic 			if (h->flags & (P_BLEAF | P_RLEAF))
17850989Sbostic 				break;
17950989Sbostic 			pg = GETBINTERNAL(h, 0)->pgno;
18050989Sbostic 			mpool_put(t->bt_mp, h, 0);
18150989Sbostic 		}
18246133Smao 
18350989Sbostic 		/* Skip any empty pages. */
18450989Sbostic 		while (NEXTINDEX(h) == 0 && h->nextpg != P_INVALID) {
18550989Sbostic 			pg = h->nextpg;
18650989Sbostic 			mpool_put(t->bt_mp, h, 0);
18750989Sbostic 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
18850989Sbostic 				return (RET_ERROR);
18950989Sbostic 		}
19046133Smao 
19150989Sbostic 		if (NEXTINDEX(h) == 0) {
19250989Sbostic 			mpool_put(t->bt_mp, h, 0);
19350989Sbostic 			return (RET_SPECIAL);
19450989Sbostic 		}
19546133Smao 
19650989Sbostic 		ep->page = h;
19750989Sbostic 		ep->index = 0;
19850989Sbostic 		break;
19950989Sbostic 	case R_LAST:				/* Last record. */
20050989Sbostic 	case R_PREV:
20150989Sbostic 		/* Walk down the right-hand side of the tree. */
20250989Sbostic 		for (pg = P_ROOT;;) {
20350989Sbostic 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
20450989Sbostic 				return (RET_ERROR);
20556743Sbostic 			if (h->flags & (P_BLEAF | P_RLEAF))
20650989Sbostic 				break;
20750989Sbostic 			pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno;
20850989Sbostic 			mpool_put(t->bt_mp, h, 0);
20950989Sbostic 		}
21046133Smao 
21150989Sbostic 		/* Skip any empty pages. */
21250989Sbostic 		while (NEXTINDEX(h) == 0 && h->prevpg != P_INVALID) {
21350989Sbostic 			pg = h->prevpg;
21450989Sbostic 			mpool_put(t->bt_mp, h, 0);
21550989Sbostic 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
21650989Sbostic 				return (RET_ERROR);
21750989Sbostic 		}
21846133Smao 
21950989Sbostic 		if (NEXTINDEX(h) == 0) {
22050989Sbostic 			mpool_put(t->bt_mp, h, 0);
22150989Sbostic 			return (RET_SPECIAL);
22246133Smao 		}
22350989Sbostic 
22450989Sbostic 		ep->page = h;
22550989Sbostic 		ep->index = NEXTINDEX(h) - 1;
22650989Sbostic 		break;
22746133Smao 	}
22846133Smao 	return (RET_SUCCESS);
22946133Smao }
23046133Smao 
23146133Smao /*
23250989Sbostic  * BT_SEQADVANCE -- Advance the sequential scan.
23346133Smao  *
23450989Sbostic  * Parameters:
23550989Sbostic  *	t:	tree
23650989Sbostic  *	flags:	R_NEXT, R_PREV
23746133Smao  *
23850989Sbostic  * Side effects:
23950989Sbostic  *	Pins the page the new key/data record is on.
24046133Smao  *
24150989Sbostic  * Returns:
24250989Sbostic  *	RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
24346133Smao  */
24450989Sbostic static int
24550989Sbostic bt_seqadv(t, e, flags)
24650989Sbostic 	BTREE *t;
24750989Sbostic 	EPG *e;
24846133Smao 	int flags;
24946133Smao {
25050989Sbostic 	EPGNO *c, delc;
25150989Sbostic 	PAGE *h;
252*57989Sbostic 	indx_t index;
25350989Sbostic 	pgno_t pg;
25446133Smao 
25550989Sbostic 	/* Save the current cursor if going to delete it. */
25650989Sbostic 	c = &t->bt_bcursor;
25750989Sbostic 	if (ISSET(t, BTF_DELCRSR))
25850989Sbostic 		delc = *c;
25946133Smao 
26050989Sbostic 	if ((h = mpool_get(t->bt_mp, c->pgno, 0)) == NULL)
26146133Smao 		return (RET_ERROR);
26246133Smao 
26350989Sbostic 	/*
26450989Sbostic  	 * Find the next/previous record in the tree and point the cursor at it.
26550989Sbostic 	 * The cursor may not be moved until a new key has been found.
26650989Sbostic 	 */
26750989Sbostic 	index = c->index;
26850989Sbostic 	switch(flags) {
26950989Sbostic 	case R_NEXT:			/* Next record. */
27050989Sbostic 		if (++index == NEXTINDEX(h)) {
27150989Sbostic 			do {
27250989Sbostic 				pg = h->nextpg;
27350989Sbostic 				mpool_put(t->bt_mp, h, 0);
27450989Sbostic 				if (pg == P_INVALID)
27546133Smao 					return (RET_SPECIAL);
27650989Sbostic 				if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
27746133Smao 					return (RET_ERROR);
27850989Sbostic 			} while (NEXTINDEX(h) == 0);
27946133Smao 			index = 0;
28046133Smao 		}
28150989Sbostic 		break;
28250989Sbostic 	case R_PREV:			/* Previous record. */
28350989Sbostic 		if (index-- == 0) {
28446133Smao 			do {
28550989Sbostic 				pg = h->prevpg;
28650989Sbostic 				mpool_put(t->bt_mp, h, 0);
28750989Sbostic 				if (pg == P_INVALID)
28850989Sbostic 					return (RET_SPECIAL);
28950989Sbostic 				if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
29046133Smao 					return (RET_ERROR);
29150989Sbostic 			} while (NEXTINDEX(h) == 0);
29250989Sbostic 			index = NEXTINDEX(h) - 1;
29350989Sbostic 		}
29450989Sbostic 		break;
29550989Sbostic 	}
29646133Smao 
29750989Sbostic 	e->page = h;
29850989Sbostic 	e->index = index;
29946133Smao 
30050989Sbostic 	/*
30150989Sbostic 	 * Delete any already deleted record that we've been saving because the
30250989Sbostic 	 * cursor pointed to it.  This could cause the new index to be shifted
30350989Sbostic 	 * down by one if the record we're deleting is on the same page and has
30450989Sbostic 	 * a larger index.
30550989Sbostic 	 */
30650989Sbostic 	if (ISSET(t, BTF_DELCRSR)) {
30756743Sbostic 		CLR(t, BTF_DELCRSR);			/* Don't try twice. */
30850989Sbostic 		if (c->pgno == delc.pgno && c->index > delc.index)
30950989Sbostic 			--c->index;
31050989Sbostic 		if (__bt_crsrdel(t, &delc))
31150989Sbostic 			return (RET_ERROR);
31246133Smao 	}
31346133Smao 	return (RET_SUCCESS);
31446133Smao }
31550989Sbostic 
31650989Sbostic /*
31750989Sbostic  * __BT_CRSRDEL -- Delete the record referenced by the cursor.
31850989Sbostic  *
31950989Sbostic  * Parameters:
32050989Sbostic  *	t:	tree
32150989Sbostic  *
32250989Sbostic  * Returns:
32350989Sbostic  *	RET_ERROR, RET_SUCCESS
32450989Sbostic  */
32550989Sbostic int
32650989Sbostic __bt_crsrdel(t, c)
32750989Sbostic 	BTREE *t;
32850989Sbostic 	EPGNO *c;
32950989Sbostic {
33050989Sbostic 	PAGE *h;
33150989Sbostic 	int status;
33250989Sbostic 
33356743Sbostic 	CLR(t, BTF_DELCRSR);			/* Don't try twice. */
33450989Sbostic 	if ((h = mpool_get(t->bt_mp, c->pgno, 0)) == NULL)
33550989Sbostic 		return (RET_ERROR);
33650989Sbostic 	status = __bt_dleaf(t, h, c->index);
33750989Sbostic 	mpool_put(t->bt_mp, h, MPOOL_DIRTY);
33850989Sbostic 	return (status);
33950989Sbostic }
340