xref: /csrg-svn/lib/libc/db/btree/bt_seq.c (revision 50989)
1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Mike Olson.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #if defined(LIBC_SCCS) && !defined(lint)
12 static char sccsid[] = "@(#)bt_seq.c	5.5 (Berkeley) 09/04/91";
13 #endif /* LIBC_SCCS and not lint */
14 
15 #include <sys/types.h>
16 #include <errno.h>
17 #include <db.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stddef.h>
21 #include "btree.h"
22 
23 static int	 bt_seqadv __P((BTREE *, EPG *, int));
24 static int	 bt_seqset __P((BTREE *, EPG *, DBT *, int));
25 
26 /*
27  * Sequential scan support.
28  *
29  * The tree can be scanned sequentially, starting from either end of the tree
30  * or from any specific key.  A scan request before any scanning is done is
31  * initialized as starting from the least node.
32  *
33  * Each tree has an EPGNO which has the current position of the cursor.  The
34  * cursor has to survive deletions/insertions in the tree without losing its
35  * position.  This is done by noting deletions without doing them, and then
36  * doing them when the cursor moves (or the tree is closed).
37  */
38 
39 /*
40  * __BT_SEQ -- Btree sequential scan interface.
41  *
42  * Parameters:
43  *	dbp:	pointer to access method
44  *	key:	key for positioning and return value
45  *	data:	data return value
46  *	flags:	R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV.
47  *
48  * Returns:
49  *	RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
50  */
51 int
52 __bt_seq(dbp, key, data, flags)
53 	const DB *dbp;
54 	DBT *key, *data;
55 	u_int flags;
56 {
57 	BTREE *t;
58 	EPG e;
59 	int status;
60 
61 	/*
62 	 * If scan unitialized as yet, or starting at a specific record, set
63 	 * the scan to a specific key.  Both bt_seqset and bt_seqadv pin the
64 	 * page the cursor references if they're successful.
65 	 */
66 	t = dbp->internal;
67 	switch(flags) {
68 	case R_NEXT:
69 		if (ISSET(t, BTF_SEQINIT)) {
70 			status = bt_seqadv(t, &e, flags);
71 			break;
72 		}
73 		/* FALLTHROUGH */
74 	case R_CURSOR:
75 	case R_FIRST:
76 		status = bt_seqset(t, &e, key, flags);
77 		SET(t, BTF_SEQINIT);
78 		break;
79 	case R_PREV:
80 		if (ISSET(t, BTF_SEQINIT)) {
81 			status = bt_seqadv(t, &e, flags);
82 			break;
83 		}
84 		/* FALLTHROUGH */
85 	case R_LAST:
86 		status = bt_seqset(t, &e, key, flags);
87 		SET(t, BTF_SEQINIT);
88 		break;
89 	default:
90 		errno = EINVAL;
91 		return (RET_ERROR);
92 	}
93 
94 	if (status == RET_SUCCESS) {
95 		status = __bt_ret(t, &e, key, data);
96 
97 		/* Update the actual cursor. */
98 		if (status == RET_SUCCESS) {
99 			t->bt_bcursor.pgno = e.page->pgno;
100 			t->bt_bcursor.index = e.index;
101 		}
102 		mpool_put(t->bt_mp, e.page, 0);
103 	}
104 	return (status);
105 }
106 
107 /*
108  * BT_SEQSET -- Set the sequential scan to a specific key.
109  *
110  * Parameters:
111  *	t:	tree
112  *	ep:	storage for returned key
113  *	key:	key for initial scan position
114  *	flags:	R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV
115  *
116  * Side effects:
117  *	Pins the page the cursor references.
118  *
119  * Returns:
120  *	RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
121  */
122 static int
123 bt_seqset(t, ep, key, flags)
124 	BTREE *t;
125 	EPG *ep;
126 	DBT *key;
127 	int flags;
128 {
129 	EPG *e;
130 	PAGE *h;
131 	pgno_t pg;
132 	int exact;
133 
134 	/*
135 	 * Delete any already deleted record that we've been saving because
136 	 * the cursor pointed to it.  Since going to a specific key, should
137 	 * delete any logically deleted records so they aren't found.
138 	 */
139 	if (ISSET(t, BTF_DELCRSR) && __bt_crsrdel(t, &t->bt_bcursor))
140 		return (RET_ERROR);
141 
142 	/*
143 	 * If R_CURSOR set, find the first instance of the key in the tree and
144 	 * point the cursor at it.  Otherwise, find the first or the last record
145 	 * in the tree and point the cursor at it.  The cursor may not be moved
146 	 * until a new key has been found.
147 	 */
148 	switch(flags) {
149 	case R_CURSOR:				/* Keyed scan. */
150 		if (key->data == NULL || key->size == 0) {
151 			errno = EINVAL;
152 			return (RET_ERROR);
153 		}
154 		e = __bt_first(t, key, &exact);	/* Returns pinned page. */
155 		if (e == NULL)
156 			return (RET_ERROR);
157 		if (!exact) {
158 			mpool_put(t->bt_mp, e->page, 0);
159 			return (RET_SPECIAL);
160 		}
161 		*ep = *e;
162 		break;
163 	case R_FIRST:				/* First record. */
164 	case R_NEXT:
165 		/* Walk down the left-hand side of the tree. */
166 		for (pg = P_ROOT;;) {
167 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
168 				return (RET_ERROR);
169 			if (h->flags & (P_BLEAF|P_RLEAF))
170 				break;
171 			pg = GETBINTERNAL(h, 0)->pgno;
172 			mpool_put(t->bt_mp, h, 0);
173 		}
174 
175 		/* Skip any empty pages. */
176 		while (NEXTINDEX(h) == 0 && h->nextpg != P_INVALID) {
177 			pg = h->nextpg;
178 			mpool_put(t->bt_mp, h, 0);
179 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
180 				return (RET_ERROR);
181 		}
182 
183 		if (NEXTINDEX(h) == 0) {
184 			mpool_put(t->bt_mp, h, 0);
185 			return (RET_SPECIAL);
186 		}
187 
188 		ep->page = h;
189 		ep->index = 0;
190 		break;
191 	case R_LAST:				/* Last record. */
192 	case R_PREV:
193 		/* Walk down the right-hand side of the tree. */
194 		for (pg = P_ROOT;;) {
195 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
196 				return (RET_ERROR);
197 			if (h->flags & (P_BLEAF|P_RLEAF))
198 				break;
199 			pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno;
200 			mpool_put(t->bt_mp, h, 0);
201 		}
202 
203 		/* Skip any empty pages. */
204 		while (NEXTINDEX(h) == 0 && h->prevpg != P_INVALID) {
205 			pg = h->prevpg;
206 			mpool_put(t->bt_mp, h, 0);
207 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
208 				return (RET_ERROR);
209 		}
210 
211 		if (NEXTINDEX(h) == 0) {
212 			mpool_put(t->bt_mp, h, 0);
213 			return (RET_SPECIAL);
214 		}
215 
216 		ep->page = h;
217 		ep->index = NEXTINDEX(h) - 1;
218 		break;
219 	}
220 	return (RET_SUCCESS);
221 }
222 
223 /*
224  * BT_SEQADVANCE -- Advance the sequential scan.
225  *
226  * Parameters:
227  *	t:	tree
228  *	flags:	R_NEXT, R_PREV
229  *
230  * Side effects:
231  *	Pins the page the new key/data record is on.
232  *
233  * Returns:
234  *	RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
235  */
236 static int
237 bt_seqadv(t, e, flags)
238 	BTREE *t;
239 	EPG *e;
240 	int flags;
241 {
242 	EPGNO *c, delc;
243 	PAGE *h;
244 	index_t index;
245 	pgno_t pg;
246 
247 	/* Save the current cursor if going to delete it. */
248 	c = &t->bt_bcursor;
249 	if (ISSET(t, BTF_DELCRSR))
250 		delc = *c;
251 
252 	if ((h = mpool_get(t->bt_mp, c->pgno, 0)) == NULL)
253 		return (RET_ERROR);
254 
255 	/*
256  	 * Find the next/previous record in the tree and point the cursor at it.
257 	 * The cursor may not be moved until a new key has been found.
258 	 */
259 	index = c->index;
260 	switch(flags) {
261 	case R_NEXT:			/* Next record. */
262 		if (++index == NEXTINDEX(h)) {
263 			do {
264 				pg = h->nextpg;
265 				mpool_put(t->bt_mp, h, 0);
266 				if (pg == P_INVALID)
267 					return (RET_SPECIAL);
268 				if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
269 					return (RET_ERROR);
270 			} while (NEXTINDEX(h) == 0);
271 			index = 0;
272 		}
273 		break;
274 	case R_PREV:			/* Previous record. */
275 		if (index-- == 0) {
276 			do {
277 				pg = h->prevpg;
278 				mpool_put(t->bt_mp, h, 0);
279 				if (pg == P_INVALID)
280 					return (RET_SPECIAL);
281 				if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
282 					return (RET_ERROR);
283 			} while (NEXTINDEX(h) == 0);
284 			index = NEXTINDEX(h) - 1;
285 		}
286 		break;
287 	}
288 
289 	e->page = h;
290 	e->index = index;
291 
292 	/*
293 	 * Delete any already deleted record that we've been saving because the
294 	 * cursor pointed to it.  This could cause the new index to be shifted
295 	 * down by one if the record we're deleting is on the same page and has
296 	 * a larger index.
297 	 */
298 	if (ISSET(t, BTF_DELCRSR)) {
299 		UNSET(t, BTF_DELCRSR);			/* Don't try twice. */
300 		if (c->pgno == delc.pgno && c->index > delc.index)
301 			--c->index;
302 		if (__bt_crsrdel(t, &delc))
303 			return (RET_ERROR);
304 	}
305 	return (RET_SUCCESS);
306 }
307 
308 /*
309  * __BT_CRSRDEL -- Delete the record referenced by the cursor.
310  *
311  * Parameters:
312  *	t:	tree
313  *
314  * Returns:
315  *	RET_ERROR, RET_SUCCESS
316  */
317 int
318 __bt_crsrdel(t, c)
319 	BTREE *t;
320 	EPGNO *c;
321 {
322 	PAGE *h;
323 	int status;
324 
325 	UNSET(t, BTF_DELCRSR);			/* Don't try twice. */
326 	if ((h = mpool_get(t->bt_mp, c->pgno, 0)) == NULL)
327 		return (RET_ERROR);
328 	status = __bt_dleaf(t, h, c->index);
329 	mpool_put(t->bt_mp, h, MPOOL_DIRTY);
330 	return (status);
331 }
332