bt_get.c revision 1573
1/*-
2 * Copyright (c) 1990, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Mike Olson.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#if defined(LIBC_SCCS) && !defined(lint)
38static char sccsid[] = "@(#)bt_get.c	8.2 (Berkeley) 9/7/93";
39#endif /* LIBC_SCCS and not lint */
40
41#include <sys/types.h>
42
43#include <errno.h>
44#include <stddef.h>
45#include <stdio.h>
46
47#include <db.h>
48#include "btree.h"
49
50/*
51 * __BT_GET -- Get a record from the btree.
52 *
53 * Parameters:
54 *	dbp:	pointer to access method
55 *	key:	key to find
56 *	data:	data to return
57 *	flag:	currently unused
58 *
59 * Returns:
60 *	RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found.
61 */
62int
63__bt_get(dbp, key, data, flags)
64	const DB *dbp;
65	const DBT *key;
66	DBT *data;
67	u_int flags;
68{
69	BTREE *t;
70	EPG *e;
71	int exact, status;
72
73	t = dbp->internal;
74
75	/* Toss any page pinned across calls. */
76	if (t->bt_pinned != NULL) {
77		mpool_put(t->bt_mp, t->bt_pinned, 0);
78		t->bt_pinned = NULL;
79	}
80
81	/* Get currently doesn't take any flags. */
82	if (flags) {
83		errno = EINVAL;
84		return (RET_ERROR);
85	}
86
87	if ((e = __bt_search(t, key, &exact)) == NULL)
88		return (RET_ERROR);
89	if (!exact) {
90		mpool_put(t->bt_mp, e->page, 0);
91		return (RET_SPECIAL);
92	}
93
94	/*
95	 * A special case is if we found the record but it's flagged for
96	 * deletion.  In this case, we want to find another record with the
97	 * same key, if it exists.  Rather than look around the tree we call
98	 * __bt_first and have it redo the search, as __bt_first will not
99	 * return keys marked for deletion.  Slow, but should never happen.
100	 */
101	if (ISSET(t, B_DELCRSR) && e->page->pgno == t->bt_bcursor.pgno &&
102	    e->index == t->bt_bcursor.index) {
103		mpool_put(t->bt_mp, e->page, 0);
104		if ((e = __bt_first(t, key, &exact)) == NULL)
105			return (RET_ERROR);
106		if (!exact)
107			return (RET_SPECIAL);
108	}
109
110	status = __bt_ret(t, e, NULL, data);
111	/*
112	 * If the user is doing concurrent access, we copied the
113	 * key/data, toss the page.
114	 */
115	if (ISSET(t, B_DB_LOCK))
116		mpool_put(t->bt_mp, e->page, 0);
117	else
118		t->bt_pinned = e->page;
119	return (status);
120}
121
122/*
123 * __BT_FIRST -- Find the first entry.
124 *
125 * Parameters:
126 *	t:	the tree
127 *	key:	the key
128 *
129 * Returns:
130 *	The first entry in the tree greater than or equal to key.
131 */
132EPG *
133__bt_first(t, key, exactp)
134	BTREE *t;
135	const DBT *key;
136	int *exactp;
137{
138	register PAGE *h;
139	register EPG *e;
140	EPG save;
141	pgno_t cpgno, pg;
142	indx_t cindex;
143	int found;
144
145	/*
146	 * Find any matching record; __bt_search pins the page.  Only exact
147	 * matches are tricky, otherwise just return the location of the key
148	 * if it were to be inserted into the tree.
149	 */
150	if ((e = __bt_search(t, key, exactp)) == NULL)
151		return (NULL);
152	if (!*exactp)
153		return (e);
154
155	if (ISSET(t, B_DELCRSR)) {
156		cpgno = t->bt_bcursor.pgno;
157		cindex = t->bt_bcursor.index;
158	} else {
159		cpgno = P_INVALID;
160		cindex = 0;		/* GCC thinks it's uninitialized. */
161	}
162
163	/*
164	 * Walk backwards, skipping empty pages, as long as the entry matches
165	 * and there are keys left in the tree.  Save a copy of each match in
166	 * case we go too far.  A special case is that we don't return a match
167	 * on records that the cursor references that have already been flagged
168	 * for deletion.
169	 */
170	save = *e;
171	h = e->page;
172	found = 0;
173	do {
174		if (cpgno != h->pgno || cindex != e->index) {
175			if (save.page->pgno != e->page->pgno) {
176				mpool_put(t->bt_mp, save.page, 0);
177				save = *e;
178			} else
179				save.index = e->index;
180			found = 1;
181		}
182		/*
183		 * Make a special effort not to unpin the page the last (or
184		 * original) match was on, but also make sure it's unpinned
185		 * if an error occurs.
186		 */
187		while (e->index == 0) {
188			if (h->prevpg == P_INVALID)
189				goto done1;
190			if (h->pgno != save.page->pgno)
191				mpool_put(t->bt_mp, h, 0);
192			if ((h = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL) {
193				if (h->pgno == save.page->pgno)
194					mpool_put(t->bt_mp, save.page, 0);
195				return (NULL);
196			}
197			e->page = h;
198			e->index = NEXTINDEX(h);
199		}
200		--e->index;
201	} while (__bt_cmp(t, key, e) == 0);
202
203	/*
204	 * Reach here with the last page that was looked at pinned, which may
205	 * or may not be the same as the last (or original) match page.  If
206	 * it's not useful, release it.
207	 */
208done1:	if (h->pgno != save.page->pgno)
209		mpool_put(t->bt_mp, h, 0);
210
211	/*
212	 * If still haven't found a record, the only possibility left is the
213	 * next one.  Move forward one slot, skipping empty pages and check.
214	 */
215	if (!found) {
216		h = save.page;
217		if (++save.index == NEXTINDEX(h)) {
218			do {
219				pg = h->nextpg;
220				mpool_put(t->bt_mp, h, 0);
221				if (pg == P_INVALID) {
222					*exactp = 0;
223					return (e);
224				}
225				if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
226					return (NULL);
227			} while ((save.index = NEXTINDEX(h)) == 0);
228			save.page = h;
229		}
230		if (__bt_cmp(t, key, &save) != 0) {
231			*exactp = 0;
232			return (e);
233		}
234	}
235	*e = save;
236	*exactp = 1;
237	return (e);
238}
239