146135Smao /*-
261194Sbostic * Copyright (c) 1990, 1993
361194Sbostic * The Regents of the University of California. All rights reserved.
446135Smao *
546135Smao * This code is derived from software contributed to Berkeley by
646135Smao * Mike Olson.
746135Smao *
846135Smao * %sccs.include.redist.c%
946135Smao */
1046135Smao
1146135Smao #if defined(LIBC_SCCS) && !defined(lint)
12*64457Sbostic static char sccsid[] = "@(#)bt_get.c 8.2 (Berkeley) 09/07/93";
1346135Smao #endif /* LIBC_SCCS and not lint */
1446135Smao
1550989Sbostic #include <sys/types.h>
1656733Sbostic
1750989Sbostic #include <errno.h>
1856733Sbostic #include <stddef.h>
1950989Sbostic #include <stdio.h>
2056733Sbostic
2157932Sbostic #include <db.h>
2246135Smao #include "btree.h"
2346135Smao
2446135Smao /*
2550989Sbostic * __BT_GET -- Get a record from the btree.
2646135Smao *
2750989Sbostic * Parameters:
2850989Sbostic * dbp: pointer to access method
2950989Sbostic * key: key to find
3050989Sbostic * data: data to return
3150989Sbostic * flag: currently unused
3246135Smao *
3350989Sbostic * Returns:
3450989Sbostic * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found.
3546135Smao */
3646135Smao int
__bt_get(dbp,key,data,flags)3750989Sbostic __bt_get(dbp, key, data, flags)
3850989Sbostic const DB *dbp;
3951103Sbostic const DBT *key;
4051103Sbostic DBT *data;
4150989Sbostic u_int flags;
4246135Smao {
4350989Sbostic BTREE *t;
4450989Sbostic EPG *e;
4550989Sbostic int exact, status;
4646135Smao
47*64457Sbostic t = dbp->internal;
48*64457Sbostic
49*64457Sbostic /* Toss any page pinned across calls. */
50*64457Sbostic if (t->bt_pinned != NULL) {
51*64457Sbostic mpool_put(t->bt_mp, t->bt_pinned, 0);
52*64457Sbostic t->bt_pinned = NULL;
53*64457Sbostic }
54*64457Sbostic
55*64457Sbostic /* Get currently doesn't take any flags. */
5650989Sbostic if (flags) {
5750989Sbostic errno = EINVAL;
5850989Sbostic return (RET_ERROR);
5946135Smao }
60*64457Sbostic
6150989Sbostic if ((e = __bt_search(t, key, &exact)) == NULL)
6250989Sbostic return (RET_ERROR);
6350989Sbostic if (!exact) {
6450989Sbostic mpool_put(t->bt_mp, e->page, 0);
6550989Sbostic return (RET_SPECIAL);
6650989Sbostic }
6746135Smao
6850989Sbostic /*
6950989Sbostic * A special case is if we found the record but it's flagged for
7050989Sbostic * deletion. In this case, we want to find another record with the
7150989Sbostic * same key, if it exists. Rather than look around the tree we call
7251103Sbostic * __bt_first and have it redo the search, as __bt_first will not
7350989Sbostic * return keys marked for deletion. Slow, but should never happen.
7450989Sbostic */
7560042Sbostic if (ISSET(t, B_DELCRSR) && e->page->pgno == t->bt_bcursor.pgno &&
7650989Sbostic e->index == t->bt_bcursor.index) {
7750989Sbostic mpool_put(t->bt_mp, e->page, 0);
7850989Sbostic if ((e = __bt_first(t, key, &exact)) == NULL)
7946135Smao return (RET_ERROR);
8050989Sbostic if (!exact)
8150989Sbostic return (RET_SPECIAL);
8246135Smao }
8346135Smao
8451103Sbostic status = __bt_ret(t, e, NULL, data);
85*64457Sbostic /*
86*64457Sbostic * If the user is doing concurrent access, we copied the
87*64457Sbostic * key/data, toss the page.
88*64457Sbostic */
89*64457Sbostic if (ISSET(t, B_DB_LOCK))
90*64457Sbostic mpool_put(t->bt_mp, e->page, 0);
91*64457Sbostic else
92*64457Sbostic t->bt_pinned = e->page;
9350989Sbostic return (status);
9446135Smao }
9546135Smao
9646135Smao /*
9751103Sbostic * __BT_FIRST -- Find the first entry.
9846135Smao *
9950989Sbostic * Parameters:
10050989Sbostic * t: the tree
10150989Sbostic * key: the key
10246135Smao *
10350989Sbostic * Returns:
10451103Sbostic * The first entry in the tree greater than or equal to key.
10546135Smao */
10650989Sbostic EPG *
__bt_first(t,key,exactp)10750989Sbostic __bt_first(t, key, exactp)
10850989Sbostic BTREE *t;
10951103Sbostic const DBT *key;
11050989Sbostic int *exactp;
11146135Smao {
11250989Sbostic register PAGE *h;
11350989Sbostic register EPG *e;
11450989Sbostic EPG save;
11550989Sbostic pgno_t cpgno, pg;
11657989Sbostic indx_t cindex;
11750989Sbostic int found;
11846135Smao
11950989Sbostic /*
12050989Sbostic * Find any matching record; __bt_search pins the page. Only exact
12151103Sbostic * matches are tricky, otherwise just return the location of the key
12251103Sbostic * if it were to be inserted into the tree.
12350989Sbostic */
12450989Sbostic if ((e = __bt_search(t, key, exactp)) == NULL)
12550989Sbostic return (NULL);
12651103Sbostic if (!*exactp)
12750989Sbostic return (e);
12846135Smao
12960042Sbostic if (ISSET(t, B_DELCRSR)) {
13050989Sbostic cpgno = t->bt_bcursor.pgno;
13150989Sbostic cindex = t->bt_bcursor.index;
13256733Sbostic } else {
13350989Sbostic cpgno = P_INVALID;
13456733Sbostic cindex = 0; /* GCC thinks it's uninitialized. */
13556733Sbostic }
13646135Smao
13750989Sbostic /*
13850989Sbostic * Walk backwards, skipping empty pages, as long as the entry matches
13950989Sbostic * and there are keys left in the tree. Save a copy of each match in
14050989Sbostic * case we go too far. A special case is that we don't return a match
14150989Sbostic * on records that the cursor references that have already been flagged
14250989Sbostic * for deletion.
14350989Sbostic */
14450989Sbostic save = *e;
14550989Sbostic h = e->page;
14650989Sbostic found = 0;
14750989Sbostic do {
14850989Sbostic if (cpgno != h->pgno || cindex != e->index) {
14950989Sbostic if (save.page->pgno != e->page->pgno) {
15050989Sbostic mpool_put(t->bt_mp, save.page, 0);
15150989Sbostic save = *e;
15250989Sbostic } else
15350989Sbostic save.index = e->index;
15450989Sbostic found = 1;
15546135Smao }
15646135Smao /*
15750989Sbostic * Make a special effort not to unpin the page the last (or
15850989Sbostic * original) match was on, but also make sure it's unpinned
15950989Sbostic * if an error occurs.
16046135Smao */
16157002Sbostic while (e->index == 0) {
16257002Sbostic if (h->prevpg == P_INVALID)
16357002Sbostic goto done1;
16457002Sbostic if (h->pgno != save.page->pgno)
16557002Sbostic mpool_put(t->bt_mp, h, 0);
16657002Sbostic if ((h = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL) {
16757002Sbostic if (h->pgno == save.page->pgno)
16857002Sbostic mpool_put(t->bt_mp, save.page, 0);
16957002Sbostic return (NULL);
17057002Sbostic }
17157002Sbostic e->page = h;
17257002Sbostic e->index = NEXTINDEX(h);
17357002Sbostic }
17450989Sbostic --e->index;
17550989Sbostic } while (__bt_cmp(t, key, e) == 0);
17646135Smao
17750989Sbostic /*
17850989Sbostic * Reach here with the last page that was looked at pinned, which may
17950989Sbostic * or may not be the same as the last (or original) match page. If
18050989Sbostic * it's not useful, release it.
18150989Sbostic */
18250989Sbostic done1: if (h->pgno != save.page->pgno)
18350989Sbostic mpool_put(t->bt_mp, h, 0);
18446135Smao
18546135Smao /*
18650989Sbostic * If still haven't found a record, the only possibility left is the
18750989Sbostic * next one. Move forward one slot, skipping empty pages and check.
18846135Smao */
18950989Sbostic if (!found) {
19050989Sbostic h = save.page;
19150989Sbostic if (++save.index == NEXTINDEX(h)) {
19250989Sbostic do {
19350989Sbostic pg = h->nextpg;
19450989Sbostic mpool_put(t->bt_mp, h, 0);
19550989Sbostic if (pg == P_INVALID) {
19650989Sbostic *exactp = 0;
19750989Sbostic return (e);
19846135Smao }
19950989Sbostic if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
20050989Sbostic return (NULL);
20150989Sbostic } while ((save.index = NEXTINDEX(h)) == 0);
20250989Sbostic save.page = h;
20346135Smao }
20450989Sbostic if (__bt_cmp(t, key, &save) != 0) {
20550989Sbostic *exactp = 0;
20650989Sbostic return (e);
20746135Smao }
20846135Smao }
20950989Sbostic *e = save;
21050989Sbostic *exactp = 1;
21150989Sbostic return (e);
21246135Smao }
213