146133Smao /*- 246133Smao * Copyright (c) 1990 The Regents of the University of California. 346133Smao * All rights reserved. 446133Smao * 546133Smao * This code is derived from software contributed to Berkeley by 646133Smao * Mike Olson. 746133Smao * 846133Smao * %sccs.include.redist.c% 946133Smao */ 1046133Smao 1146133Smao #if defined(LIBC_SCCS) && !defined(lint) 12*57989Sbostic static char sccsid[] = "@(#)bt_seq.c 5.9 (Berkeley) 02/14/93"; 1346133Smao #endif /* LIBC_SCCS and not lint */ 1446133Smao 1546133Smao #include <sys/types.h> 1656743Sbostic 1746561Sbostic #include <errno.h> 1856743Sbostic #include <stddef.h> 1950989Sbostic #include <stdio.h> 2046561Sbostic #include <stdlib.h> 2156743Sbostic 2257932Sbostic #include <db.h> 2346133Smao #include "btree.h" 2446133Smao 2550989Sbostic static int bt_seqadv __P((BTREE *, EPG *, int)); 2650989Sbostic static int bt_seqset __P((BTREE *, EPG *, DBT *, int)); 2750989Sbostic 2846133Smao /* 2950989Sbostic * Sequential scan support. 3046133Smao * 3150989Sbostic * The tree can be scanned sequentially, starting from either end of the tree 3250989Sbostic * or from any specific key. A scan request before any scanning is done is 3350989Sbostic * initialized as starting from the least node. 3446133Smao * 3550989Sbostic * Each tree has an EPGNO which has the current position of the cursor. The 3650989Sbostic * cursor has to survive deletions/insertions in the tree without losing its 3750989Sbostic * position. This is done by noting deletions without doing them, and then 3850989Sbostic * doing them when the cursor moves (or the tree is closed). 3950989Sbostic */ 4050989Sbostic 4150989Sbostic /* 4250989Sbostic * __BT_SEQ -- Btree sequential scan interface. 4346133Smao * 4450989Sbostic * Parameters: 4550989Sbostic * dbp: pointer to access method 4650989Sbostic * key: key for positioning and return value 4750989Sbostic * data: data return value 4850989Sbostic * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV. 4946133Smao * 5050989Sbostic * Returns: 5150989Sbostic * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. 5246133Smao */ 5346133Smao int 5450989Sbostic __bt_seq(dbp, key, data, flags) 5550989Sbostic const DB *dbp; 5650989Sbostic DBT *key, *data; 5750989Sbostic u_int flags; 5846133Smao { 5950989Sbostic BTREE *t; 6050989Sbostic EPG e; 6150989Sbostic int status; 6246133Smao 6346133Smao /* 6450989Sbostic * If scan unitialized as yet, or starting at a specific record, set 6550989Sbostic * the scan to a specific key. Both bt_seqset and bt_seqadv pin the 6650989Sbostic * page the cursor references if they're successful. 6746133Smao */ 6850989Sbostic t = dbp->internal; 6950989Sbostic switch(flags) { 7050989Sbostic case R_NEXT: 7151100Sbostic case R_PREV: 7250989Sbostic if (ISSET(t, BTF_SEQINIT)) { 7350989Sbostic status = bt_seqadv(t, &e, flags); 7450989Sbostic break; 7550989Sbostic } 7650989Sbostic /* FALLTHROUGH */ 7750989Sbostic case R_CURSOR: 7850989Sbostic case R_FIRST: 7950989Sbostic case R_LAST: 8050989Sbostic status = bt_seqset(t, &e, key, flags); 8150989Sbostic break; 8250989Sbostic default: 8350989Sbostic errno = EINVAL; 8450989Sbostic return (RET_ERROR); 8550989Sbostic } 8646133Smao 8750989Sbostic if (status == RET_SUCCESS) { 8850989Sbostic status = __bt_ret(t, &e, key, data); 8946133Smao 9050989Sbostic /* Update the actual cursor. */ 9151100Sbostic t->bt_bcursor.pgno = e.page->pgno; 9251100Sbostic t->bt_bcursor.index = e.index; 9350989Sbostic mpool_put(t->bt_mp, e.page, 0); 9451100Sbostic SET(t, BTF_SEQINIT); 9550989Sbostic } 9650989Sbostic return (status); 9750989Sbostic } 9846133Smao 9950989Sbostic /* 10050989Sbostic * BT_SEQSET -- Set the sequential scan to a specific key. 10150989Sbostic * 10250989Sbostic * Parameters: 10350989Sbostic * t: tree 10450989Sbostic * ep: storage for returned key 10550989Sbostic * key: key for initial scan position 10650989Sbostic * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV 10750989Sbostic * 10850989Sbostic * Side effects: 10950989Sbostic * Pins the page the cursor references. 11050989Sbostic * 11150989Sbostic * Returns: 11250989Sbostic * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. 11350989Sbostic */ 11450989Sbostic static int 11550989Sbostic bt_seqset(t, ep, key, flags) 11650989Sbostic BTREE *t; 11750989Sbostic EPG *ep; 11850989Sbostic DBT *key; 11950989Sbostic int flags; 12050989Sbostic { 12150989Sbostic EPG *e; 12250989Sbostic PAGE *h; 12350989Sbostic pgno_t pg; 12450989Sbostic int exact; 12546133Smao 12650989Sbostic /* 12750989Sbostic * Delete any already deleted record that we've been saving because 12850989Sbostic * the cursor pointed to it. Since going to a specific key, should 12950989Sbostic * delete any logically deleted records so they aren't found. 13050989Sbostic */ 13150989Sbostic if (ISSET(t, BTF_DELCRSR) && __bt_crsrdel(t, &t->bt_bcursor)) 13250989Sbostic return (RET_ERROR); 13346133Smao 13450989Sbostic /* 13551100Sbostic * Find the first, last or specific key in the tree and point the cursor 13651100Sbostic * at it. The cursor may not be moved until a new key has been found. 13750989Sbostic */ 13850989Sbostic switch(flags) { 13950989Sbostic case R_CURSOR: /* Keyed scan. */ 14051100Sbostic /* 14151100Sbostic * Find the first instance of the key or the smallest key which 14251100Sbostic * is greater than or equal to the specified key. If run out 14351100Sbostic * of keys, return RET_SPECIAL. 14451100Sbostic */ 14550989Sbostic if (key->data == NULL || key->size == 0) { 14650989Sbostic errno = EINVAL; 14746133Smao return (RET_ERROR); 14850989Sbostic } 14950989Sbostic e = __bt_first(t, key, &exact); /* Returns pinned page. */ 15050989Sbostic if (e == NULL) 15150989Sbostic return (RET_ERROR); 15251100Sbostic /* 15351100Sbostic * If at the end of a page, skip any empty pages and find the 15451100Sbostic * next entry. 15551100Sbostic */ 15651100Sbostic if (e->index == NEXTINDEX(e->page)) { 15751100Sbostic h = e->page; 15851100Sbostic do { 15951100Sbostic pg = h->nextpg; 16051100Sbostic mpool_put(t->bt_mp, h, 0); 16151100Sbostic if (pg == P_INVALID) 16251100Sbostic return (RET_SPECIAL); 16351100Sbostic if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) 16451100Sbostic return (RET_ERROR); 16551100Sbostic } while (NEXTINDEX(h) == 0); 16651100Sbostic e->index = 0; 16751100Sbostic e->page = h; 16850989Sbostic } 16950989Sbostic *ep = *e; 17050989Sbostic break; 17150989Sbostic case R_FIRST: /* First record. */ 17250989Sbostic case R_NEXT: 17350989Sbostic /* Walk down the left-hand side of the tree. */ 17450989Sbostic for (pg = P_ROOT;;) { 17550989Sbostic if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) 17650989Sbostic return (RET_ERROR); 17756743Sbostic if (h->flags & (P_BLEAF | P_RLEAF)) 17850989Sbostic break; 17950989Sbostic pg = GETBINTERNAL(h, 0)->pgno; 18050989Sbostic mpool_put(t->bt_mp, h, 0); 18150989Sbostic } 18246133Smao 18350989Sbostic /* Skip any empty pages. */ 18450989Sbostic while (NEXTINDEX(h) == 0 && h->nextpg != P_INVALID) { 18550989Sbostic pg = h->nextpg; 18650989Sbostic mpool_put(t->bt_mp, h, 0); 18750989Sbostic if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) 18850989Sbostic return (RET_ERROR); 18950989Sbostic } 19046133Smao 19150989Sbostic if (NEXTINDEX(h) == 0) { 19250989Sbostic mpool_put(t->bt_mp, h, 0); 19350989Sbostic return (RET_SPECIAL); 19450989Sbostic } 19546133Smao 19650989Sbostic ep->page = h; 19750989Sbostic ep->index = 0; 19850989Sbostic break; 19950989Sbostic case R_LAST: /* Last record. */ 20050989Sbostic case R_PREV: 20150989Sbostic /* Walk down the right-hand side of the tree. */ 20250989Sbostic for (pg = P_ROOT;;) { 20350989Sbostic if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) 20450989Sbostic return (RET_ERROR); 20556743Sbostic if (h->flags & (P_BLEAF | P_RLEAF)) 20650989Sbostic break; 20750989Sbostic pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno; 20850989Sbostic mpool_put(t->bt_mp, h, 0); 20950989Sbostic } 21046133Smao 21150989Sbostic /* Skip any empty pages. */ 21250989Sbostic while (NEXTINDEX(h) == 0 && h->prevpg != P_INVALID) { 21350989Sbostic pg = h->prevpg; 21450989Sbostic mpool_put(t->bt_mp, h, 0); 21550989Sbostic if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) 21650989Sbostic return (RET_ERROR); 21750989Sbostic } 21846133Smao 21950989Sbostic if (NEXTINDEX(h) == 0) { 22050989Sbostic mpool_put(t->bt_mp, h, 0); 22150989Sbostic return (RET_SPECIAL); 22246133Smao } 22350989Sbostic 22450989Sbostic ep->page = h; 22550989Sbostic ep->index = NEXTINDEX(h) - 1; 22650989Sbostic break; 22746133Smao } 22846133Smao return (RET_SUCCESS); 22946133Smao } 23046133Smao 23146133Smao /* 23250989Sbostic * BT_SEQADVANCE -- Advance the sequential scan. 23346133Smao * 23450989Sbostic * Parameters: 23550989Sbostic * t: tree 23650989Sbostic * flags: R_NEXT, R_PREV 23746133Smao * 23850989Sbostic * Side effects: 23950989Sbostic * Pins the page the new key/data record is on. 24046133Smao * 24150989Sbostic * Returns: 24250989Sbostic * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. 24346133Smao */ 24450989Sbostic static int 24550989Sbostic bt_seqadv(t, e, flags) 24650989Sbostic BTREE *t; 24750989Sbostic EPG *e; 24846133Smao int flags; 24946133Smao { 25050989Sbostic EPGNO *c, delc; 25150989Sbostic PAGE *h; 252*57989Sbostic indx_t index; 25350989Sbostic pgno_t pg; 25446133Smao 25550989Sbostic /* Save the current cursor if going to delete it. */ 25650989Sbostic c = &t->bt_bcursor; 25750989Sbostic if (ISSET(t, BTF_DELCRSR)) 25850989Sbostic delc = *c; 25946133Smao 26050989Sbostic if ((h = mpool_get(t->bt_mp, c->pgno, 0)) == NULL) 26146133Smao return (RET_ERROR); 26246133Smao 26350989Sbostic /* 26450989Sbostic * Find the next/previous record in the tree and point the cursor at it. 26550989Sbostic * The cursor may not be moved until a new key has been found. 26650989Sbostic */ 26750989Sbostic index = c->index; 26850989Sbostic switch(flags) { 26950989Sbostic case R_NEXT: /* Next record. */ 27050989Sbostic if (++index == NEXTINDEX(h)) { 27150989Sbostic do { 27250989Sbostic pg = h->nextpg; 27350989Sbostic mpool_put(t->bt_mp, h, 0); 27450989Sbostic if (pg == P_INVALID) 27546133Smao return (RET_SPECIAL); 27650989Sbostic if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) 27746133Smao return (RET_ERROR); 27850989Sbostic } while (NEXTINDEX(h) == 0); 27946133Smao index = 0; 28046133Smao } 28150989Sbostic break; 28250989Sbostic case R_PREV: /* Previous record. */ 28350989Sbostic if (index-- == 0) { 28446133Smao do { 28550989Sbostic pg = h->prevpg; 28650989Sbostic mpool_put(t->bt_mp, h, 0); 28750989Sbostic if (pg == P_INVALID) 28850989Sbostic return (RET_SPECIAL); 28950989Sbostic if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) 29046133Smao return (RET_ERROR); 29150989Sbostic } while (NEXTINDEX(h) == 0); 29250989Sbostic index = NEXTINDEX(h) - 1; 29350989Sbostic } 29450989Sbostic break; 29550989Sbostic } 29646133Smao 29750989Sbostic e->page = h; 29850989Sbostic e->index = index; 29946133Smao 30050989Sbostic /* 30150989Sbostic * Delete any already deleted record that we've been saving because the 30250989Sbostic * cursor pointed to it. This could cause the new index to be shifted 30350989Sbostic * down by one if the record we're deleting is on the same page and has 30450989Sbostic * a larger index. 30550989Sbostic */ 30650989Sbostic if (ISSET(t, BTF_DELCRSR)) { 30756743Sbostic CLR(t, BTF_DELCRSR); /* Don't try twice. */ 30850989Sbostic if (c->pgno == delc.pgno && c->index > delc.index) 30950989Sbostic --c->index; 31050989Sbostic if (__bt_crsrdel(t, &delc)) 31150989Sbostic return (RET_ERROR); 31246133Smao } 31346133Smao return (RET_SUCCESS); 31446133Smao } 31550989Sbostic 31650989Sbostic /* 31750989Sbostic * __BT_CRSRDEL -- Delete the record referenced by the cursor. 31850989Sbostic * 31950989Sbostic * Parameters: 32050989Sbostic * t: tree 32150989Sbostic * 32250989Sbostic * Returns: 32350989Sbostic * RET_ERROR, RET_SUCCESS 32450989Sbostic */ 32550989Sbostic int 32650989Sbostic __bt_crsrdel(t, c) 32750989Sbostic BTREE *t; 32850989Sbostic EPGNO *c; 32950989Sbostic { 33050989Sbostic PAGE *h; 33150989Sbostic int status; 33250989Sbostic 33356743Sbostic CLR(t, BTF_DELCRSR); /* Don't try twice. */ 33450989Sbostic if ((h = mpool_get(t->bt_mp, c->pgno, 0)) == NULL) 33550989Sbostic return (RET_ERROR); 33650989Sbostic status = __bt_dleaf(t, h, c->index); 33750989Sbostic mpool_put(t->bt_mp, h, MPOOL_DIRTY); 33850989Sbostic return (status); 33950989Sbostic } 340