xref: /csrg-svn/lib/libc/db/btree/bt_seq.c (revision 51100)
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*51100Sbostic static char sccsid[] = "@(#)bt_seq.c	5.6 (Berkeley) 09/12/91";
1346133Smao #endif /* LIBC_SCCS and not lint */
1446133Smao 
1546133Smao #include <sys/types.h>
1646561Sbostic #include <errno.h>
1746133Smao #include <db.h>
1850989Sbostic #include <stdio.h>
1946561Sbostic #include <stdlib.h>
2050989Sbostic #include <stddef.h>
2146133Smao #include "btree.h"
2246133Smao 
2350989Sbostic static int	 bt_seqadv __P((BTREE *, EPG *, int));
2450989Sbostic static int	 bt_seqset __P((BTREE *, EPG *, DBT *, int));
2550989Sbostic 
2646133Smao /*
2750989Sbostic  * Sequential scan support.
2846133Smao  *
2950989Sbostic  * The tree can be scanned sequentially, starting from either end of the tree
3050989Sbostic  * or from any specific key.  A scan request before any scanning is done is
3150989Sbostic  * initialized as starting from the least node.
3246133Smao  *
3350989Sbostic  * Each tree has an EPGNO which has the current position of the cursor.  The
3450989Sbostic  * cursor has to survive deletions/insertions in the tree without losing its
3550989Sbostic  * position.  This is done by noting deletions without doing them, and then
3650989Sbostic  * doing them when the cursor moves (or the tree is closed).
3750989Sbostic  */
3850989Sbostic 
3950989Sbostic /*
4050989Sbostic  * __BT_SEQ -- Btree sequential scan interface.
4146133Smao  *
4250989Sbostic  * Parameters:
4350989Sbostic  *	dbp:	pointer to access method
4450989Sbostic  *	key:	key for positioning and return value
4550989Sbostic  *	data:	data return value
4650989Sbostic  *	flags:	R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV.
4746133Smao  *
4850989Sbostic  * Returns:
4950989Sbostic  *	RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
5046133Smao  */
5146133Smao int
5250989Sbostic __bt_seq(dbp, key, data, flags)
5350989Sbostic 	const DB *dbp;
5450989Sbostic 	DBT *key, *data;
5550989Sbostic 	u_int flags;
5646133Smao {
5750989Sbostic 	BTREE *t;
5850989Sbostic 	EPG e;
5950989Sbostic 	int status;
6046133Smao 
6146133Smao 	/*
6250989Sbostic 	 * If scan unitialized as yet, or starting at a specific record, set
6350989Sbostic 	 * the scan to a specific key.  Both bt_seqset and bt_seqadv pin the
6450989Sbostic 	 * page the cursor references if they're successful.
6546133Smao 	 */
6650989Sbostic 	t = dbp->internal;
6750989Sbostic 	switch(flags) {
6850989Sbostic 	case R_NEXT:
69*51100Sbostic 	case R_PREV:
7050989Sbostic 		if (ISSET(t, BTF_SEQINIT)) {
7150989Sbostic 			status = bt_seqadv(t, &e, flags);
7250989Sbostic 			break;
7350989Sbostic 		}
7450989Sbostic 		/* FALLTHROUGH */
7550989Sbostic 	case R_CURSOR:
7650989Sbostic 	case R_FIRST:
7750989Sbostic 	case R_LAST:
7850989Sbostic 		status = bt_seqset(t, &e, key, flags);
7950989Sbostic 		break;
8050989Sbostic 	default:
8150989Sbostic 		errno = EINVAL;
8250989Sbostic 		return (RET_ERROR);
8350989Sbostic 	}
8446133Smao 
8550989Sbostic 	if (status == RET_SUCCESS) {
8650989Sbostic 		status = __bt_ret(t, &e, key, data);
8746133Smao 
8850989Sbostic 		/* Update the actual cursor. */
89*51100Sbostic 		t->bt_bcursor.pgno = e.page->pgno;
90*51100Sbostic 		t->bt_bcursor.index = e.index;
9150989Sbostic 		mpool_put(t->bt_mp, e.page, 0);
92*51100Sbostic 		SET(t, BTF_SEQINIT);
9350989Sbostic 	}
9450989Sbostic 	return (status);
9550989Sbostic }
9646133Smao 
9750989Sbostic /*
9850989Sbostic  * BT_SEQSET -- Set the sequential scan to a specific key.
9950989Sbostic  *
10050989Sbostic  * Parameters:
10150989Sbostic  *	t:	tree
10250989Sbostic  *	ep:	storage for returned key
10350989Sbostic  *	key:	key for initial scan position
10450989Sbostic  *	flags:	R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV
10550989Sbostic  *
10650989Sbostic  * Side effects:
10750989Sbostic  *	Pins the page the cursor references.
10850989Sbostic  *
10950989Sbostic  * Returns:
11050989Sbostic  *	RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
11150989Sbostic  */
11250989Sbostic static int
11350989Sbostic bt_seqset(t, ep, key, flags)
11450989Sbostic 	BTREE *t;
11550989Sbostic 	EPG *ep;
11650989Sbostic 	DBT *key;
11750989Sbostic 	int flags;
11850989Sbostic {
11950989Sbostic 	EPG *e;
12050989Sbostic 	PAGE *h;
12150989Sbostic 	pgno_t pg;
12250989Sbostic 	int exact;
12346133Smao 
12450989Sbostic 	/*
12550989Sbostic 	 * Delete any already deleted record that we've been saving because
12650989Sbostic 	 * the cursor pointed to it.  Since going to a specific key, should
12750989Sbostic 	 * delete any logically deleted records so they aren't found.
12850989Sbostic 	 */
12950989Sbostic 	if (ISSET(t, BTF_DELCRSR) && __bt_crsrdel(t, &t->bt_bcursor))
13050989Sbostic 		return (RET_ERROR);
13146133Smao 
13250989Sbostic 	/*
133*51100Sbostic 	 * Find the first, last or specific key in the tree and point the cursor
134*51100Sbostic 	 * at it.  The cursor may not be moved until a new key has been found.
13550989Sbostic 	 */
13650989Sbostic 	switch(flags) {
13750989Sbostic 	case R_CURSOR:				/* Keyed scan. */
138*51100Sbostic 		/*
139*51100Sbostic 		 * Find the first instance of the key or the smallest key which
140*51100Sbostic 		 * is greater than or equal to the specified key.  If run out
141*51100Sbostic 		 * of keys, return RET_SPECIAL.
142*51100Sbostic 		 */
14350989Sbostic 		if (key->data == NULL || key->size == 0) {
14450989Sbostic 			errno = EINVAL;
14546133Smao 			return (RET_ERROR);
14650989Sbostic 		}
14750989Sbostic 		e = __bt_first(t, key, &exact);	/* Returns pinned page. */
14850989Sbostic 		if (e == NULL)
14950989Sbostic 			return (RET_ERROR);
150*51100Sbostic 		/*
151*51100Sbostic 		 * If at the end of a page, skip any empty pages and find the
152*51100Sbostic 		 * next entry.
153*51100Sbostic 		 */
154*51100Sbostic 		if (e->index == NEXTINDEX(e->page)) {
155*51100Sbostic 			h = e->page;
156*51100Sbostic 			do {
157*51100Sbostic 				pg = h->nextpg;
158*51100Sbostic 				mpool_put(t->bt_mp, h, 0);
159*51100Sbostic 				if (pg == P_INVALID)
160*51100Sbostic 					return (RET_SPECIAL);
161*51100Sbostic 				if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
162*51100Sbostic 					return (RET_ERROR);
163*51100Sbostic 			} while (NEXTINDEX(h) == 0);
164*51100Sbostic 			e->index = 0;
165*51100Sbostic 			e->page = h;
16650989Sbostic 		}
16750989Sbostic 		*ep = *e;
16850989Sbostic 		break;
16950989Sbostic 	case R_FIRST:				/* First record. */
17050989Sbostic 	case R_NEXT:
17150989Sbostic 		/* Walk down the left-hand side of the tree. */
17250989Sbostic 		for (pg = P_ROOT;;) {
17350989Sbostic 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
17450989Sbostic 				return (RET_ERROR);
17550989Sbostic 			if (h->flags & (P_BLEAF|P_RLEAF))
17650989Sbostic 				break;
17750989Sbostic 			pg = GETBINTERNAL(h, 0)->pgno;
17850989Sbostic 			mpool_put(t->bt_mp, h, 0);
17950989Sbostic 		}
18046133Smao 
18150989Sbostic 		/* Skip any empty pages. */
18250989Sbostic 		while (NEXTINDEX(h) == 0 && h->nextpg != P_INVALID) {
18350989Sbostic 			pg = h->nextpg;
18450989Sbostic 			mpool_put(t->bt_mp, h, 0);
18550989Sbostic 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
18650989Sbostic 				return (RET_ERROR);
18750989Sbostic 		}
18846133Smao 
18950989Sbostic 		if (NEXTINDEX(h) == 0) {
19050989Sbostic 			mpool_put(t->bt_mp, h, 0);
19150989Sbostic 			return (RET_SPECIAL);
19250989Sbostic 		}
19346133Smao 
19450989Sbostic 		ep->page = h;
19550989Sbostic 		ep->index = 0;
19650989Sbostic 		break;
19750989Sbostic 	case R_LAST:				/* Last record. */
19850989Sbostic 	case R_PREV:
19950989Sbostic 		/* Walk down the right-hand side of the tree. */
20050989Sbostic 		for (pg = P_ROOT;;) {
20150989Sbostic 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
20250989Sbostic 				return (RET_ERROR);
20350989Sbostic 			if (h->flags & (P_BLEAF|P_RLEAF))
20450989Sbostic 				break;
20550989Sbostic 			pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno;
20650989Sbostic 			mpool_put(t->bt_mp, h, 0);
20750989Sbostic 		}
20846133Smao 
20950989Sbostic 		/* Skip any empty pages. */
21050989Sbostic 		while (NEXTINDEX(h) == 0 && h->prevpg != P_INVALID) {
21150989Sbostic 			pg = h->prevpg;
21250989Sbostic 			mpool_put(t->bt_mp, h, 0);
21350989Sbostic 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
21450989Sbostic 				return (RET_ERROR);
21550989Sbostic 		}
21646133Smao 
21750989Sbostic 		if (NEXTINDEX(h) == 0) {
21850989Sbostic 			mpool_put(t->bt_mp, h, 0);
21950989Sbostic 			return (RET_SPECIAL);
22046133Smao 		}
22150989Sbostic 
22250989Sbostic 		ep->page = h;
22350989Sbostic 		ep->index = NEXTINDEX(h) - 1;
22450989Sbostic 		break;
22546133Smao 	}
22646133Smao 	return (RET_SUCCESS);
22746133Smao }
22846133Smao 
22946133Smao /*
23050989Sbostic  * BT_SEQADVANCE -- Advance the sequential scan.
23146133Smao  *
23250989Sbostic  * Parameters:
23350989Sbostic  *	t:	tree
23450989Sbostic  *	flags:	R_NEXT, R_PREV
23546133Smao  *
23650989Sbostic  * Side effects:
23750989Sbostic  *	Pins the page the new key/data record is on.
23846133Smao  *
23950989Sbostic  * Returns:
24050989Sbostic  *	RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
24146133Smao  */
24250989Sbostic static int
24350989Sbostic bt_seqadv(t, e, flags)
24450989Sbostic 	BTREE *t;
24550989Sbostic 	EPG *e;
24646133Smao 	int flags;
24746133Smao {
24850989Sbostic 	EPGNO *c, delc;
24950989Sbostic 	PAGE *h;
25046133Smao 	index_t index;
25150989Sbostic 	pgno_t pg;
25246133Smao 
25350989Sbostic 	/* Save the current cursor if going to delete it. */
25450989Sbostic 	c = &t->bt_bcursor;
25550989Sbostic 	if (ISSET(t, BTF_DELCRSR))
25650989Sbostic 		delc = *c;
25746133Smao 
25850989Sbostic 	if ((h = mpool_get(t->bt_mp, c->pgno, 0)) == NULL)
25946133Smao 		return (RET_ERROR);
26046133Smao 
26150989Sbostic 	/*
26250989Sbostic  	 * Find the next/previous record in the tree and point the cursor at it.
26350989Sbostic 	 * The cursor may not be moved until a new key has been found.
26450989Sbostic 	 */
26550989Sbostic 	index = c->index;
26650989Sbostic 	switch(flags) {
26750989Sbostic 	case R_NEXT:			/* Next record. */
26850989Sbostic 		if (++index == NEXTINDEX(h)) {
26950989Sbostic 			do {
27050989Sbostic 				pg = h->nextpg;
27150989Sbostic 				mpool_put(t->bt_mp, h, 0);
27250989Sbostic 				if (pg == P_INVALID)
27346133Smao 					return (RET_SPECIAL);
27450989Sbostic 				if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
27546133Smao 					return (RET_ERROR);
27650989Sbostic 			} while (NEXTINDEX(h) == 0);
27746133Smao 			index = 0;
27846133Smao 		}
27950989Sbostic 		break;
28050989Sbostic 	case R_PREV:			/* Previous record. */
28150989Sbostic 		if (index-- == 0) {
28246133Smao 			do {
28350989Sbostic 				pg = h->prevpg;
28450989Sbostic 				mpool_put(t->bt_mp, h, 0);
28550989Sbostic 				if (pg == P_INVALID)
28650989Sbostic 					return (RET_SPECIAL);
28750989Sbostic 				if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
28846133Smao 					return (RET_ERROR);
28950989Sbostic 			} while (NEXTINDEX(h) == 0);
29050989Sbostic 			index = NEXTINDEX(h) - 1;
29150989Sbostic 		}
29250989Sbostic 		break;
29350989Sbostic 	}
29446133Smao 
29550989Sbostic 	e->page = h;
29650989Sbostic 	e->index = index;
29746133Smao 
29850989Sbostic 	/*
29950989Sbostic 	 * Delete any already deleted record that we've been saving because the
30050989Sbostic 	 * cursor pointed to it.  This could cause the new index to be shifted
30150989Sbostic 	 * down by one if the record we're deleting is on the same page and has
30250989Sbostic 	 * a larger index.
30350989Sbostic 	 */
30450989Sbostic 	if (ISSET(t, BTF_DELCRSR)) {
30550989Sbostic 		UNSET(t, BTF_DELCRSR);			/* Don't try twice. */
30650989Sbostic 		if (c->pgno == delc.pgno && c->index > delc.index)
30750989Sbostic 			--c->index;
30850989Sbostic 		if (__bt_crsrdel(t, &delc))
30950989Sbostic 			return (RET_ERROR);
31046133Smao 	}
31146133Smao 	return (RET_SUCCESS);
31246133Smao }
31350989Sbostic 
31450989Sbostic /*
31550989Sbostic  * __BT_CRSRDEL -- Delete the record referenced by the cursor.
31650989Sbostic  *
31750989Sbostic  * Parameters:
31850989Sbostic  *	t:	tree
31950989Sbostic  *
32050989Sbostic  * Returns:
32150989Sbostic  *	RET_ERROR, RET_SUCCESS
32250989Sbostic  */
32350989Sbostic int
32450989Sbostic __bt_crsrdel(t, c)
32550989Sbostic 	BTREE *t;
32650989Sbostic 	EPGNO *c;
32750989Sbostic {
32850989Sbostic 	PAGE *h;
32950989Sbostic 	int status;
33050989Sbostic 
33150989Sbostic 	UNSET(t, BTF_DELCRSR);			/* Don't try twice. */
33250989Sbostic 	if ((h = mpool_get(t->bt_mp, c->pgno, 0)) == NULL)
33350989Sbostic 		return (RET_ERROR);
33450989Sbostic 	status = __bt_dleaf(t, h, c->index);
33550989Sbostic 	mpool_put(t->bt_mp, h, MPOOL_DIRTY);
33650989Sbostic 	return (status);
33750989Sbostic }
338