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*51100Sbostic static char sccsid[] = "@(#)bt_seq.c 5.6 (Berkeley) 09/12/91"; 1346133Smao #endif /* LIBC_SCCS and not lint */ 1446133Smao 1546133Smao #include <sys/types.h> 1646561Sbostic #include <errno.h> 1746133Smao #include <db.h> 1850989Sbostic #include <stdio.h> 1946561Sbostic #include <stdlib.h> 2050989Sbostic #include <stddef.h> 2146133Smao #include "btree.h" 2246133Smao 2350989Sbostic static int bt_seqadv __P((BTREE *, EPG *, int)); 2450989Sbostic static int bt_seqset __P((BTREE *, EPG *, DBT *, int)); 2550989Sbostic 2646133Smao /* 2750989Sbostic * Sequential scan support. 2846133Smao * 2950989Sbostic * The tree can be scanned sequentially, starting from either end of the tree 3050989Sbostic * or from any specific key. A scan request before any scanning is done is 3150989Sbostic * initialized as starting from the least node. 3246133Smao * 3350989Sbostic * Each tree has an EPGNO which has the current position of the cursor. The 3450989Sbostic * cursor has to survive deletions/insertions in the tree without losing its 3550989Sbostic * position. This is done by noting deletions without doing them, and then 3650989Sbostic * doing them when the cursor moves (or the tree is closed). 3750989Sbostic */ 3850989Sbostic 3950989Sbostic /* 4050989Sbostic * __BT_SEQ -- Btree sequential scan interface. 4146133Smao * 4250989Sbostic * Parameters: 4350989Sbostic * dbp: pointer to access method 4450989Sbostic * key: key for positioning and return value 4550989Sbostic * data: data return value 4650989Sbostic * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV. 4746133Smao * 4850989Sbostic * Returns: 4950989Sbostic * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. 5046133Smao */ 5146133Smao int 5250989Sbostic __bt_seq(dbp, key, data, flags) 5350989Sbostic const DB *dbp; 5450989Sbostic DBT *key, *data; 5550989Sbostic u_int flags; 5646133Smao { 5750989Sbostic BTREE *t; 5850989Sbostic EPG e; 5950989Sbostic int status; 6046133Smao 6146133Smao /* 6250989Sbostic * If scan unitialized as yet, or starting at a specific record, set 6350989Sbostic * the scan to a specific key. Both bt_seqset and bt_seqadv pin the 6450989Sbostic * page the cursor references if they're successful. 6546133Smao */ 6650989Sbostic t = dbp->internal; 6750989Sbostic switch(flags) { 6850989Sbostic case R_NEXT: 69*51100Sbostic case R_PREV: 7050989Sbostic if (ISSET(t, BTF_SEQINIT)) { 7150989Sbostic status = bt_seqadv(t, &e, flags); 7250989Sbostic break; 7350989Sbostic } 7450989Sbostic /* FALLTHROUGH */ 7550989Sbostic case R_CURSOR: 7650989Sbostic case R_FIRST: 7750989Sbostic case R_LAST: 7850989Sbostic status = bt_seqset(t, &e, key, flags); 7950989Sbostic break; 8050989Sbostic default: 8150989Sbostic errno = EINVAL; 8250989Sbostic return (RET_ERROR); 8350989Sbostic } 8446133Smao 8550989Sbostic if (status == RET_SUCCESS) { 8650989Sbostic status = __bt_ret(t, &e, key, data); 8746133Smao 8850989Sbostic /* Update the actual cursor. */ 89*51100Sbostic t->bt_bcursor.pgno = e.page->pgno; 90*51100Sbostic t->bt_bcursor.index = e.index; 9150989Sbostic mpool_put(t->bt_mp, e.page, 0); 92*51100Sbostic SET(t, BTF_SEQINIT); 9350989Sbostic } 9450989Sbostic return (status); 9550989Sbostic } 9646133Smao 9750989Sbostic /* 9850989Sbostic * BT_SEQSET -- Set the sequential scan to a specific key. 9950989Sbostic * 10050989Sbostic * Parameters: 10150989Sbostic * t: tree 10250989Sbostic * ep: storage for returned key 10350989Sbostic * key: key for initial scan position 10450989Sbostic * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV 10550989Sbostic * 10650989Sbostic * Side effects: 10750989Sbostic * Pins the page the cursor references. 10850989Sbostic * 10950989Sbostic * Returns: 11050989Sbostic * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. 11150989Sbostic */ 11250989Sbostic static int 11350989Sbostic bt_seqset(t, ep, key, flags) 11450989Sbostic BTREE *t; 11550989Sbostic EPG *ep; 11650989Sbostic DBT *key; 11750989Sbostic int flags; 11850989Sbostic { 11950989Sbostic EPG *e; 12050989Sbostic PAGE *h; 12150989Sbostic pgno_t pg; 12250989Sbostic int exact; 12346133Smao 12450989Sbostic /* 12550989Sbostic * Delete any already deleted record that we've been saving because 12650989Sbostic * the cursor pointed to it. Since going to a specific key, should 12750989Sbostic * delete any logically deleted records so they aren't found. 12850989Sbostic */ 12950989Sbostic if (ISSET(t, BTF_DELCRSR) && __bt_crsrdel(t, &t->bt_bcursor)) 13050989Sbostic return (RET_ERROR); 13146133Smao 13250989Sbostic /* 133*51100Sbostic * Find the first, last or specific key in the tree and point the cursor 134*51100Sbostic * at it. The cursor may not be moved until a new key has been found. 13550989Sbostic */ 13650989Sbostic switch(flags) { 13750989Sbostic case R_CURSOR: /* Keyed scan. */ 138*51100Sbostic /* 139*51100Sbostic * Find the first instance of the key or the smallest key which 140*51100Sbostic * is greater than or equal to the specified key. If run out 141*51100Sbostic * of keys, return RET_SPECIAL. 142*51100Sbostic */ 14350989Sbostic if (key->data == NULL || key->size == 0) { 14450989Sbostic errno = EINVAL; 14546133Smao return (RET_ERROR); 14650989Sbostic } 14750989Sbostic e = __bt_first(t, key, &exact); /* Returns pinned page. */ 14850989Sbostic if (e == NULL) 14950989Sbostic return (RET_ERROR); 150*51100Sbostic /* 151*51100Sbostic * If at the end of a page, skip any empty pages and find the 152*51100Sbostic * next entry. 153*51100Sbostic */ 154*51100Sbostic if (e->index == NEXTINDEX(e->page)) { 155*51100Sbostic h = e->page; 156*51100Sbostic do { 157*51100Sbostic pg = h->nextpg; 158*51100Sbostic mpool_put(t->bt_mp, h, 0); 159*51100Sbostic if (pg == P_INVALID) 160*51100Sbostic return (RET_SPECIAL); 161*51100Sbostic if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) 162*51100Sbostic return (RET_ERROR); 163*51100Sbostic } while (NEXTINDEX(h) == 0); 164*51100Sbostic e->index = 0; 165*51100Sbostic e->page = h; 16650989Sbostic } 16750989Sbostic *ep = *e; 16850989Sbostic break; 16950989Sbostic case R_FIRST: /* First record. */ 17050989Sbostic case R_NEXT: 17150989Sbostic /* Walk down the left-hand side of the tree. */ 17250989Sbostic for (pg = P_ROOT;;) { 17350989Sbostic if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) 17450989Sbostic return (RET_ERROR); 17550989Sbostic if (h->flags & (P_BLEAF|P_RLEAF)) 17650989Sbostic break; 17750989Sbostic pg = GETBINTERNAL(h, 0)->pgno; 17850989Sbostic mpool_put(t->bt_mp, h, 0); 17950989Sbostic } 18046133Smao 18150989Sbostic /* Skip any empty pages. */ 18250989Sbostic while (NEXTINDEX(h) == 0 && h->nextpg != P_INVALID) { 18350989Sbostic pg = h->nextpg; 18450989Sbostic mpool_put(t->bt_mp, h, 0); 18550989Sbostic if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) 18650989Sbostic return (RET_ERROR); 18750989Sbostic } 18846133Smao 18950989Sbostic if (NEXTINDEX(h) == 0) { 19050989Sbostic mpool_put(t->bt_mp, h, 0); 19150989Sbostic return (RET_SPECIAL); 19250989Sbostic } 19346133Smao 19450989Sbostic ep->page = h; 19550989Sbostic ep->index = 0; 19650989Sbostic break; 19750989Sbostic case R_LAST: /* Last record. */ 19850989Sbostic case R_PREV: 19950989Sbostic /* Walk down the right-hand side of the tree. */ 20050989Sbostic for (pg = P_ROOT;;) { 20150989Sbostic if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) 20250989Sbostic return (RET_ERROR); 20350989Sbostic if (h->flags & (P_BLEAF|P_RLEAF)) 20450989Sbostic break; 20550989Sbostic pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno; 20650989Sbostic mpool_put(t->bt_mp, h, 0); 20750989Sbostic } 20846133Smao 20950989Sbostic /* Skip any empty pages. */ 21050989Sbostic while (NEXTINDEX(h) == 0 && h->prevpg != P_INVALID) { 21150989Sbostic pg = h->prevpg; 21250989Sbostic mpool_put(t->bt_mp, h, 0); 21350989Sbostic if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) 21450989Sbostic return (RET_ERROR); 21550989Sbostic } 21646133Smao 21750989Sbostic if (NEXTINDEX(h) == 0) { 21850989Sbostic mpool_put(t->bt_mp, h, 0); 21950989Sbostic return (RET_SPECIAL); 22046133Smao } 22150989Sbostic 22250989Sbostic ep->page = h; 22350989Sbostic ep->index = NEXTINDEX(h) - 1; 22450989Sbostic break; 22546133Smao } 22646133Smao return (RET_SUCCESS); 22746133Smao } 22846133Smao 22946133Smao /* 23050989Sbostic * BT_SEQADVANCE -- Advance the sequential scan. 23146133Smao * 23250989Sbostic * Parameters: 23350989Sbostic * t: tree 23450989Sbostic * flags: R_NEXT, R_PREV 23546133Smao * 23650989Sbostic * Side effects: 23750989Sbostic * Pins the page the new key/data record is on. 23846133Smao * 23950989Sbostic * Returns: 24050989Sbostic * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. 24146133Smao */ 24250989Sbostic static int 24350989Sbostic bt_seqadv(t, e, flags) 24450989Sbostic BTREE *t; 24550989Sbostic EPG *e; 24646133Smao int flags; 24746133Smao { 24850989Sbostic EPGNO *c, delc; 24950989Sbostic PAGE *h; 25046133Smao index_t index; 25150989Sbostic pgno_t pg; 25246133Smao 25350989Sbostic /* Save the current cursor if going to delete it. */ 25450989Sbostic c = &t->bt_bcursor; 25550989Sbostic if (ISSET(t, BTF_DELCRSR)) 25650989Sbostic delc = *c; 25746133Smao 25850989Sbostic if ((h = mpool_get(t->bt_mp, c->pgno, 0)) == NULL) 25946133Smao return (RET_ERROR); 26046133Smao 26150989Sbostic /* 26250989Sbostic * Find the next/previous record in the tree and point the cursor at it. 26350989Sbostic * The cursor may not be moved until a new key has been found. 26450989Sbostic */ 26550989Sbostic index = c->index; 26650989Sbostic switch(flags) { 26750989Sbostic case R_NEXT: /* Next record. */ 26850989Sbostic if (++index == NEXTINDEX(h)) { 26950989Sbostic do { 27050989Sbostic pg = h->nextpg; 27150989Sbostic mpool_put(t->bt_mp, h, 0); 27250989Sbostic if (pg == P_INVALID) 27346133Smao return (RET_SPECIAL); 27450989Sbostic if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) 27546133Smao return (RET_ERROR); 27650989Sbostic } while (NEXTINDEX(h) == 0); 27746133Smao index = 0; 27846133Smao } 27950989Sbostic break; 28050989Sbostic case R_PREV: /* Previous record. */ 28150989Sbostic if (index-- == 0) { 28246133Smao do { 28350989Sbostic pg = h->prevpg; 28450989Sbostic mpool_put(t->bt_mp, h, 0); 28550989Sbostic if (pg == P_INVALID) 28650989Sbostic return (RET_SPECIAL); 28750989Sbostic if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) 28846133Smao return (RET_ERROR); 28950989Sbostic } while (NEXTINDEX(h) == 0); 29050989Sbostic index = NEXTINDEX(h) - 1; 29150989Sbostic } 29250989Sbostic break; 29350989Sbostic } 29446133Smao 29550989Sbostic e->page = h; 29650989Sbostic e->index = index; 29746133Smao 29850989Sbostic /* 29950989Sbostic * Delete any already deleted record that we've been saving because the 30050989Sbostic * cursor pointed to it. This could cause the new index to be shifted 30150989Sbostic * down by one if the record we're deleting is on the same page and has 30250989Sbostic * a larger index. 30350989Sbostic */ 30450989Sbostic if (ISSET(t, BTF_DELCRSR)) { 30550989Sbostic UNSET(t, BTF_DELCRSR); /* Don't try twice. */ 30650989Sbostic if (c->pgno == delc.pgno && c->index > delc.index) 30750989Sbostic --c->index; 30850989Sbostic if (__bt_crsrdel(t, &delc)) 30950989Sbostic return (RET_ERROR); 31046133Smao } 31146133Smao return (RET_SUCCESS); 31246133Smao } 31350989Sbostic 31450989Sbostic /* 31550989Sbostic * __BT_CRSRDEL -- Delete the record referenced by the cursor. 31650989Sbostic * 31750989Sbostic * Parameters: 31850989Sbostic * t: tree 31950989Sbostic * 32050989Sbostic * Returns: 32150989Sbostic * RET_ERROR, RET_SUCCESS 32250989Sbostic */ 32350989Sbostic int 32450989Sbostic __bt_crsrdel(t, c) 32550989Sbostic BTREE *t; 32650989Sbostic EPGNO *c; 32750989Sbostic { 32850989Sbostic PAGE *h; 32950989Sbostic int status; 33050989Sbostic 33150989Sbostic UNSET(t, BTF_DELCRSR); /* Don't try twice. */ 33250989Sbostic if ((h = mpool_get(t->bt_mp, c->pgno, 0)) == NULL) 33350989Sbostic return (RET_ERROR); 33450989Sbostic status = __bt_dleaf(t, h, c->index); 33550989Sbostic mpool_put(t->bt_mp, h, MPOOL_DIRTY); 33650989Sbostic return (status); 33750989Sbostic } 338