bt_seq.c revision 1.2 1 /*-
2 * Copyright (c) 1990, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Mike Olson.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #if defined(LIBC_SCCS) && !defined(lint)
38 /*static char sccsid[] = "from: @(#)bt_seq.c 8.1 (Berkeley) 6/4/93";*/
39 static char rcsid[] = "$Id: bt_seq.c,v 1.2 1993/08/01 18:43:49 mycroft Exp $";
40 #endif /* LIBC_SCCS and not lint */
41
42 #include <sys/types.h>
43
44 #include <errno.h>
45 #include <stddef.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48
49 #include <db.h>
50 #include "btree.h"
51
52 static int bt_seqadv __P((BTREE *, EPG *, int));
53 static int bt_seqset __P((BTREE *, EPG *, DBT *, int));
54
55 /*
56 * Sequential scan support.
57 *
58 * The tree can be scanned sequentially, starting from either end of the tree
59 * or from any specific key. A scan request before any scanning is done is
60 * initialized as starting from the least node.
61 *
62 * Each tree has an EPGNO which has the current position of the cursor. The
63 * cursor has to survive deletions/insertions in the tree without losing its
64 * position. This is done by noting deletions without doing them, and then
65 * doing them when the cursor moves (or the tree is closed).
66 */
67
68 /*
69 * __BT_SEQ -- Btree sequential scan interface.
70 *
71 * Parameters:
72 * dbp: pointer to access method
73 * key: key for positioning and return value
74 * data: data return value
75 * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV.
76 *
77 * Returns:
78 * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
79 */
80 int
81 __bt_seq(dbp, key, data, flags)
82 const DB *dbp;
83 DBT *key, *data;
84 u_int flags;
85 {
86 BTREE *t;
87 EPG e;
88 int status;
89
90 /*
91 * If scan unitialized as yet, or starting at a specific record, set
92 * the scan to a specific key. Both bt_seqset and bt_seqadv pin the
93 * page the cursor references if they're successful.
94 */
95 t = dbp->internal;
96 switch(flags) {
97 case R_NEXT:
98 case R_PREV:
99 if (ISSET(t, B_SEQINIT)) {
100 status = bt_seqadv(t, &e, flags);
101 break;
102 }
103 /* FALLTHROUGH */
104 case R_CURSOR:
105 case R_FIRST:
106 case R_LAST:
107 status = bt_seqset(t, &e, key, flags);
108 break;
109 default:
110 errno = EINVAL;
111 return (RET_ERROR);
112 }
113
114 if (status == RET_SUCCESS) {
115 status = __bt_ret(t, &e, key, data);
116
117 /* Update the actual cursor. */
118 t->bt_bcursor.pgno = e.page->pgno;
119 t->bt_bcursor.index = e.index;
120 mpool_put(t->bt_mp, e.page, 0);
121 SET(t, B_SEQINIT);
122 }
123 return (status);
124 }
125
126 /*
127 * BT_SEQSET -- Set the sequential scan to a specific key.
128 *
129 * Parameters:
130 * t: tree
131 * ep: storage for returned key
132 * key: key for initial scan position
133 * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV
134 *
135 * Side effects:
136 * Pins the page the cursor references.
137 *
138 * Returns:
139 * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
140 */
141 static int
142 bt_seqset(t, ep, key, flags)
143 BTREE *t;
144 EPG *ep;
145 DBT *key;
146 int flags;
147 {
148 EPG *e;
149 PAGE *h;
150 pgno_t pg;
151 int exact;
152
153 /*
154 * Delete any already deleted record that we've been saving because
155 * the cursor pointed to it. Since going to a specific key, should
156 * delete any logically deleted records so they aren't found.
157 */
158 if (ISSET(t, B_DELCRSR) && __bt_crsrdel(t, &t->bt_bcursor))
159 return (RET_ERROR);
160
161 /*
162 * Find the first, last or specific key in the tree and point the cursor
163 * at it. The cursor may not be moved until a new key has been found.
164 */
165 switch(flags) {
166 case R_CURSOR: /* Keyed scan. */
167 /*
168 * Find the first instance of the key or the smallest key which
169 * is greater than or equal to the specified key. If run out
170 * of keys, return RET_SPECIAL.
171 */
172 if (key->data == NULL || key->size == 0) {
173 errno = EINVAL;
174 return (RET_ERROR);
175 }
176 e = __bt_first(t, key, &exact); /* Returns pinned page. */
177 if (e == NULL)
178 return (RET_ERROR);
179 /*
180 * If at the end of a page, skip any empty pages and find the
181 * next entry.
182 */
183 if (e->index == NEXTINDEX(e->page)) {
184 h = e->page;
185 do {
186 pg = h->nextpg;
187 mpool_put(t->bt_mp, h, 0);
188 if (pg == P_INVALID)
189 return (RET_SPECIAL);
190 if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
191 return (RET_ERROR);
192 } while (NEXTINDEX(h) == 0);
193 e->index = 0;
194 e->page = h;
195 }
196 *ep = *e;
197 break;
198 case R_FIRST: /* First record. */
199 case R_NEXT:
200 /* Walk down the left-hand side of the tree. */
201 for (pg = P_ROOT;;) {
202 if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
203 return (RET_ERROR);
204 if (h->flags & (P_BLEAF | P_RLEAF))
205 break;
206 pg = GETBINTERNAL(h, 0)->pgno;
207 mpool_put(t->bt_mp, h, 0);
208 }
209
210 /* Skip any empty pages. */
211 while (NEXTINDEX(h) == 0 && h->nextpg != P_INVALID) {
212 pg = h->nextpg;
213 mpool_put(t->bt_mp, h, 0);
214 if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
215 return (RET_ERROR);
216 }
217
218 if (NEXTINDEX(h) == 0) {
219 mpool_put(t->bt_mp, h, 0);
220 return (RET_SPECIAL);
221 }
222
223 ep->page = h;
224 ep->index = 0;
225 break;
226 case R_LAST: /* Last record. */
227 case R_PREV:
228 /* Walk down the right-hand side of the tree. */
229 for (pg = P_ROOT;;) {
230 if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
231 return (RET_ERROR);
232 if (h->flags & (P_BLEAF | P_RLEAF))
233 break;
234 pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno;
235 mpool_put(t->bt_mp, h, 0);
236 }
237
238 /* Skip any empty pages. */
239 while (NEXTINDEX(h) == 0 && h->prevpg != P_INVALID) {
240 pg = h->prevpg;
241 mpool_put(t->bt_mp, h, 0);
242 if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
243 return (RET_ERROR);
244 }
245
246 if (NEXTINDEX(h) == 0) {
247 mpool_put(t->bt_mp, h, 0);
248 return (RET_SPECIAL);
249 }
250
251 ep->page = h;
252 ep->index = NEXTINDEX(h) - 1;
253 break;
254 }
255 return (RET_SUCCESS);
256 }
257
258 /*
259 * BT_SEQADVANCE -- Advance the sequential scan.
260 *
261 * Parameters:
262 * t: tree
263 * flags: R_NEXT, R_PREV
264 *
265 * Side effects:
266 * Pins the page the new key/data record is on.
267 *
268 * Returns:
269 * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
270 */
271 static int
272 bt_seqadv(t, e, flags)
273 BTREE *t;
274 EPG *e;
275 int flags;
276 {
277 EPGNO *c, delc;
278 PAGE *h;
279 indx_t index;
280 pgno_t pg;
281
282 /* Save the current cursor if going to delete it. */
283 c = &t->bt_bcursor;
284 if (ISSET(t, B_DELCRSR))
285 delc = *c;
286
287 if ((h = mpool_get(t->bt_mp, c->pgno, 0)) == NULL)
288 return (RET_ERROR);
289
290 /*
291 * Find the next/previous record in the tree and point the cursor at it.
292 * The cursor may not be moved until a new key has been found.
293 */
294 index = c->index;
295 switch(flags) {
296 case R_NEXT: /* Next record. */
297 if (++index == NEXTINDEX(h)) {
298 do {
299 pg = h->nextpg;
300 mpool_put(t->bt_mp, h, 0);
301 if (pg == P_INVALID)
302 return (RET_SPECIAL);
303 if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
304 return (RET_ERROR);
305 } while (NEXTINDEX(h) == 0);
306 index = 0;
307 }
308 break;
309 case R_PREV: /* Previous record. */
310 if (index-- == 0) {
311 do {
312 pg = h->prevpg;
313 mpool_put(t->bt_mp, h, 0);
314 if (pg == P_INVALID)
315 return (RET_SPECIAL);
316 if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
317 return (RET_ERROR);
318 } while (NEXTINDEX(h) == 0);
319 index = NEXTINDEX(h) - 1;
320 }
321 break;
322 }
323
324 e->page = h;
325 e->index = index;
326
327 /*
328 * Delete any already deleted record that we've been saving because the
329 * cursor pointed to it. This could cause the new index to be shifted
330 * down by one if the record we're deleting is on the same page and has
331 * a larger index.
332 */
333 if (ISSET(t, B_DELCRSR)) {
334 CLR(t, B_DELCRSR); /* Don't try twice. */
335 if (c->pgno == delc.pgno && c->index > delc.index)
336 --c->index;
337 if (__bt_crsrdel(t, &delc))
338 return (RET_ERROR);
339 }
340 return (RET_SUCCESS);
341 }
342
343 /*
344 * __BT_CRSRDEL -- Delete the record referenced by the cursor.
345 *
346 * Parameters:
347 * t: tree
348 *
349 * Returns:
350 * RET_ERROR, RET_SUCCESS
351 */
352 int
353 __bt_crsrdel(t, c)
354 BTREE *t;
355 EPGNO *c;
356 {
357 PAGE *h;
358 int status;
359
360 CLR(t, B_DELCRSR); /* Don't try twice. */
361 if ((h = mpool_get(t->bt_mp, c->pgno, 0)) == NULL)
362 return (RET_ERROR);
363 status = __bt_dleaf(t, h, c->index);
364 mpool_put(t->bt_mp, h, MPOOL_DIRTY);
365 return (status);
366 }
367