Update from db-2.3.12.
[kopensolaris-gnu/glibc.git] / db2 / btree / bt_search.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997
5  *      Sleepycat Software.  All rights reserved.
6  */
7 /*
8  * Copyright (c) 1990, 1993, 1994, 1995, 1996
9  *      Keith Bostic.  All rights reserved.
10  */
11 /*
12  * Copyright (c) 1990, 1993, 1994, 1995
13  *      The Regents of the University of California.  All rights reserved.
14  *
15  * This code is derived from software contributed to Berkeley by
16  * Mike Olson.
17  *
18  * Redistribution and use in source and binary forms, with or without
19  * modification, are permitted provided that the following conditions
20  * are met:
21  * 1. Redistributions of source code must retain the above copyright
22  *    notice, this list of conditions and the following disclaimer.
23  * 2. Redistributions in binary form must reproduce the above copyright
24  *    notice, this list of conditions and the following disclaimer in the
25  *    documentation and/or other materials provided with the distribution.
26  * 3. All advertising materials mentioning features or use of this software
27  *    must display the following acknowledgement:
28  *      This product includes software developed by the University of
29  *      California, Berkeley and its contributors.
30  * 4. Neither the name of the University nor the names of its contributors
31  *    may be used to endorse or promote products derived from this software
32  *    without specific prior written permission.
33  *
34  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
35  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44  * SUCH DAMAGE.
45  */
46
47 #include "config.h"
48
49 #ifndef lint
50 static const char sccsid[] = "@(#)bt_search.c   10.8 (Sleepycat) 10/25/97";
51 #endif /* not lint */
52
53 #ifndef NO_SYSTEM_INCLUDES
54 #include <sys/types.h>
55
56 #include <errno.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #endif
61
62 #include "db_int.h"
63 #include "db_page.h"
64 #include "btree.h"
65
66 /*
67  * __bam_search --
68  *      Search a btree for a key.
69  *
70  * PUBLIC: int __bam_search __P((DB *,
71  * PUBLIC:     const DBT *, u_int, int, db_recno_t *, int *));
72  */
73 int
74 __bam_search(dbp, key, flags, stop, recnop, exactp)
75         DB *dbp;
76         const DBT *key;
77         u_int flags;
78         int stop, *exactp;
79         db_recno_t *recnop;
80 {
81         BTREE *t;
82         DB_LOCK lock;
83         EPG cur;
84         PAGE *h;
85         db_indx_t base, i, indx, lim;
86         db_pgno_t pg;
87         db_recno_t recno;
88         int cmp, jump, ret, stack;
89
90         t = dbp->internal;
91         recno = 0;
92
93         BT_STK_CLR(t);
94
95         /*
96          * There are several ways we search a btree tree.  The flags argument
97          * specifies if we're acquiring read or write locks, if we position
98          * to the first or last item in a set of duplicates, if we return
99          * deleted items, and if we are locking pairs of pages.  See btree.h
100          * for more details.  In addition, if we're doing record numbers, we
101          * have to lock the entire tree regardless.
102          *
103          * If write-locking pages, we need to know whether or not to acquire a
104          * write lock on a page before getting it.  This depends on how deep it
105          * is in tree, which we don't know until we acquire the root page.  So,
106          * if we need to lock the root page we may have to upgrade it later,
107          * because we won't get the correct lock initially.
108          *
109          * Retrieve the root page.
110          */
111         pg = PGNO_ROOT;
112         stack = F_ISSET(dbp, DB_BT_RECNUM) &&
113             (flags == S_INSERT || flags == S_DELETE);
114         if ((ret = __bam_lget(dbp,
115             0, pg, stack ? DB_LOCK_WRITE : DB_LOCK_READ, &lock)) != 0)
116                 return (ret);
117         if ((ret = __bam_pget(dbp, &h, &pg, 0)) != 0) {
118                 (void)__BT_LPUT(dbp, lock);
119                 return (ret);
120         }
121
122         /* Decide if we need to save this page; if we do, write lock it. */
123         if (!stack &&
124             ((LF_ISSET(S_PARENT) && (u_int8_t)(stop + 1) >= h->level) ||
125             (LF_ISSET(S_WRITE) && h->level == LEAFLEVEL))) {
126                 (void)memp_fput(dbp->mpf, h, 0);
127                 if ((ret = __bam_lget(dbp, 1, pg, DB_LOCK_WRITE, &lock)) != 0)
128                         return (ret);
129                 if ((ret = __bam_pget(dbp, &h, &pg, 0)) != 0) {
130                         (void)__BT_LPUT(dbp, lock);
131                         return (ret);
132                 }
133
134                 stack = 1;
135         }
136
137         for (;;) {
138                 /*
139                  * Do a binary search on the current page.  If we're searching
140                  * a leaf page, we have to manipulate the indices in groups of
141                  * two.  If we're searching an internal page, they're an index
142                  * per page item.  If we find an exact match on a leaf page,
143                  * we're done.
144                  */
145                 cur.page = h;
146                 jump = TYPE(h) == P_LBTREE ? P_INDX : O_INDX;
147                 for (base = 0,
148                     lim = NUM_ENT(h) / (db_indx_t)jump; lim != 0; lim >>= 1) {
149                         cur.indx = indx = base + ((lim >> 1) * jump);
150                         if ((cmp = __bam_cmp(dbp, key, &cur)) == 0) {
151                                 if (TYPE(h) == P_LBTREE)
152                                         goto match;
153                                 goto next;
154                         }
155                         if (cmp > 0) {
156                                 base = indx + jump;
157                                 --lim;
158                         }
159                 }
160
161                 /*
162                  * No match found.  Base is the smallest index greater than
163                  * key and may be zero or a last + O_INDX index.
164                  *
165                  * If it's a leaf page, return base as the "found" value.
166                  * Delete only deletes exact matches.
167                  */
168                 if (TYPE(h) == P_LBTREE) {
169                         *exactp = 0;
170
171                         if (LF_ISSET(S_EXACT))
172                                 goto notfound;
173
174                         BT_STK_ENTER(t, h, base, lock, ret);
175                         return (ret);
176                 }
177
178                 /*
179                  * If it's not a leaf page, record the internal page (which is
180                  * a parent page for the key).  Decrement the base by 1 if it's
181                  * non-zero so that if a split later occurs, the inserted page
182                  * will be to the right of the saved page.
183                  */
184                 indx = base > 0 ? base - O_INDX : base;
185
186                 /*
187                  * If we're trying to calculate the record number, sum up
188                  * all the record numbers on this page up to the indx point.
189                  */
190                 if (recnop != NULL)
191                         for (i = 0; i < indx; ++i)
192                                 recno += GET_BINTERNAL(h, i)->nrecs;
193
194 next:           pg = GET_BINTERNAL(h, indx)->pgno;
195                 if (stack) {
196                         /* Return if this is the lowest page wanted. */
197                         if (LF_ISSET(S_PARENT) && stop == h->level) {
198                                 BT_STK_ENTER(t, h, indx, lock, ret);
199                                 return (ret);
200                         }
201                         BT_STK_PUSH(t, h, indx, lock, ret);
202                         if (ret != 0)
203                                 goto err;
204
205                         if ((ret =
206                             __bam_lget(dbp, 0, pg, DB_LOCK_WRITE, &lock)) != 0)
207                                 goto err;
208                 } else {
209                         (void)memp_fput(dbp->mpf, h, 0);
210
211                         /*
212                          * Decide if we want to return a pointer to the next
213                          * page in the stack.  If we do, write lock it and
214                          * never unlock it.
215                          */
216                         if ((LF_ISSET(S_PARENT) &&
217                             (u_int8_t)(stop + 1) >= (u_int8_t)(h->level - 1)) ||
218                             (h->level - 1) == LEAFLEVEL)
219                                 stack = 1;
220
221                         if ((ret =
222                             __bam_lget(dbp, 1, pg, stack && LF_ISSET(S_WRITE) ?
223                             DB_LOCK_WRITE : DB_LOCK_READ, &lock)) != 0)
224                                 goto err;
225                 }
226                 if ((ret = __bam_pget(dbp, &h, &pg, 0)) != 0)
227                         goto err;
228         }
229
230         /* NOTREACHED */
231 match:  *exactp = 1;
232
233         /*
234          * If we're trying to calculate the record number, add in the
235          * offset on this page and correct for the fact that records
236          * in the tree are 0-based.
237          */
238         if (recnop != NULL)
239                 *recnop = recno + (indx / P_INDX) + 1;
240
241         /*
242          * If we got here, we know that we have a btree leaf page.
243          *
244          * If there are duplicates, go to the first/last one.
245          */
246         if (LF_ISSET(S_DUPLAST))
247                 while (indx < (db_indx_t)(NUM_ENT(h) - P_INDX) &&
248                     h->inp[indx] == h->inp[indx + P_INDX])
249                         indx += P_INDX;
250         else
251                 while (indx > 0 &&
252                     h->inp[indx] == h->inp[indx - P_INDX])
253                         indx -= P_INDX;
254
255         /*
256          * Now check if we are allowed to return deleted item; if not
257          * find/last the first non-deleted item.
258          */
259         if (LF_ISSET(S_DELNO)) {
260                 if (LF_ISSET(S_DUPLAST))
261                         while (B_DISSET(GET_BKEYDATA(h, indx + O_INDX)->type) &&
262                             indx > 0 &&
263                             h->inp[indx] == h->inp[indx - P_INDX])
264                                 indx -= P_INDX;
265                 else
266                         while (B_DISSET(GET_BKEYDATA(h, indx + O_INDX)->type) &&
267                             indx < (db_indx_t)(NUM_ENT(h) - P_INDX) &&
268                             h->inp[indx] == h->inp[indx + P_INDX])
269                                 indx += P_INDX;
270
271                 if (B_DISSET(GET_BKEYDATA(h, indx + O_INDX)->type))
272                         goto notfound;
273         }
274
275         BT_STK_ENTER(t, h, indx, lock, ret);
276         return (ret);
277
278 notfound:
279         (void)memp_fput(dbp->mpf, h, 0);
280         (void)__BT_LPUT(dbp, lock);
281         ret = DB_NOTFOUND;
282
283 err:    if (t->bt_csp > t->bt_sp) {
284                 BT_STK_POP(t);
285                 __bam_stkrel(dbp);
286         }
287         return (ret);
288 }
289
290 /*
291  * __bam_stkrel --
292  *      Release all pages currently held in the stack.
293  *
294  * PUBLIC: int __bam_stkrel __P((DB *));
295  */
296 int
297 __bam_stkrel(dbp)
298         DB *dbp;
299 {
300         BTREE *t;
301         EPG *epg;
302
303         t = dbp->internal;
304         for (epg = t->bt_sp; epg <= t->bt_csp; ++epg) {
305                 (void)memp_fput(dbp->mpf, epg->page, 0);
306                 (void)__BT_TLPUT(dbp, epg->lock);
307         }
308         return (0);
309 }
310
311 /*
312  * __bam_stkgrow --
313  *      Grow the stack.
314  *
315  * PUBLIC: int __bam_stkgrow __P((BTREE *));
316  */
317 int
318 __bam_stkgrow(t)
319         BTREE *t;
320 {
321         EPG *p;
322         size_t entries;
323
324         entries = t->bt_esp - t->bt_sp;
325
326         if ((p = (EPG *)__db_calloc(entries * 2, sizeof(EPG))) == NULL)
327                 return (ENOMEM);
328         memcpy(p, t->bt_sp, entries * sizeof(EPG));
329         if (t->bt_sp != t->bt_stack)
330                 FREE(t->bt_sp, entries * sizeof(EPG));
331         t->bt_sp = p;
332         t->bt_csp = p + entries;
333         t->bt_esp = p + entries * 2;
334         return (0);
335 }