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