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