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