source: trunk/third/rpm/db/btree/bt_rsearch.c @ 19079

Revision 19079, 11.1 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19078, which included commits to RCS files with non-trunk default branches.
Line 
1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996-2002
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
13 *      The Regents of the University of California.  All rights reserved.
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 *    notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 *    notice, this list of conditions and the following disclaimer in the
22 *    documentation and/or other materials provided with the distribution.
23 * 3. Neither the name of the University nor the names of its contributors
24 *    may be used to endorse or promote products derived from this software
25 *    without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 */
39
40#include "db_config.h"
41
42#ifndef lint
43static const char revid[] = "Id: bt_rsearch.c,v 11.34 2002/07/03 19:03:50 bostic Exp ";
44#endif /* not lint */
45
46#ifndef NO_SYSTEM_INCLUDES
47#include <sys/types.h>
48#endif
49
50#include "db_int.h"
51#include "dbinc/db_page.h"
52#include "dbinc/btree.h"
53#include "dbinc/db_shash.h"
54#include "dbinc/lock.h"
55
56/*
57 * __bam_rsearch --
58 *      Search a btree for a record number.
59 *
60 * PUBLIC: int __bam_rsearch __P((DBC *, db_recno_t *, u_int32_t, int, int *));
61 */
62int
63__bam_rsearch(dbc, recnop, flags, stop, exactp)
64        DBC *dbc;
65        db_recno_t *recnop;
66        u_int32_t flags;
67        int stop, *exactp;
68{
69        BINTERNAL *bi;
70        BTREE_CURSOR *cp;
71        DB *dbp;
72        DB_LOCK lock;
73        DB_MPOOLFILE *mpf;
74        PAGE *h;
75        RINTERNAL *ri;
76        db_indx_t adjust, deloffset, indx, top;
77        db_lockmode_t lock_mode;
78        db_pgno_t pg;
79        db_recno_t recno, t_recno, total;
80        int ret, stack;
81
82        dbp = dbc->dbp;
83        mpf = dbp->mpf;
84        cp = (BTREE_CURSOR *)dbc->internal;
85
86        BT_STK_CLR(cp);
87
88        /*
89         * There are several ways we search a btree tree.  The flags argument
90         * specifies if we're acquiring read or write locks and if we are
91         * locking pairs of pages.  In addition, if we're adding or deleting
92         * an item, we have to lock the entire tree, regardless.  See btree.h
93         * for more details.
94         *
95         * If write-locking pages, we need to know whether or not to acquire a
96         * write lock on a page before getting it.  This depends on how deep it
97         * is in tree, which we don't know until we acquire the root page.  So,
98         * if we need to lock the root page we may have to upgrade it later,
99         * because we won't get the correct lock initially.
100         *
101         * Retrieve the root page.
102         */
103        pg = cp->root;
104        stack = LF_ISSET(S_STACK) ? 1 : 0;
105        lock_mode = stack ? DB_LOCK_WRITE : DB_LOCK_READ;
106        if ((ret = __db_lget(dbc, 0, pg, lock_mode, 0, &lock)) != 0)
107                return (ret);
108        if ((ret = mpf->get(mpf, &pg, 0, &h)) != 0) {
109                /* Did not read it, so we can release the lock */
110                (void)__LPUT(dbc, lock);
111                return (ret);
112        }
113
114        /*
115         * Decide if we need to save this page; if we do, write lock it.
116         * We deliberately don't lock-couple on this call.  If the tree
117         * is tiny, i.e., one page, and two threads are busily updating
118         * the root page, we're almost guaranteed deadlocks galore, as
119         * each one gets a read lock and then blocks the other's attempt
120         * for a write lock.
121         */
122        if (!stack &&
123            ((LF_ISSET(S_PARENT) && (u_int8_t)(stop + 1) >= h->level) ||
124            (LF_ISSET(S_WRITE) && h->level == LEAFLEVEL))) {
125                (void)mpf->put(mpf, h, 0);
126                (void)__LPUT(dbc, lock);
127                lock_mode = DB_LOCK_WRITE;
128                if ((ret = __db_lget(dbc, 0, pg, lock_mode, 0, &lock)) != 0)
129                        return (ret);
130                if ((ret = mpf->get(mpf, &pg, 0, &h)) != 0) {
131                        /* Did not read it, so we can release the lock */
132                        (void)__LPUT(dbc, lock);
133                        return (ret);
134                }
135                stack = 1;
136        }
137
138        /*
139         * If appending to the tree, set the record number now -- we have the
140         * root page locked.
141         *
142         * Delete only deletes exact matches, read only returns exact matches.
143         * Note, this is different from __bam_search(), which returns non-exact
144         * matches for read.
145         *
146         * The record may not exist.  We can only return the correct location
147         * for the record immediately after the last record in the tree, so do
148         * a fast check now.
149         */
150        total = RE_NREC(h);
151        if (LF_ISSET(S_APPEND)) {
152                *exactp = 0;
153                *recnop = recno = total + 1;
154        } else {
155                recno = *recnop;
156                if (recno <= total)
157                        *exactp = 1;
158                else {
159                        *exactp = 0;
160                        if (!LF_ISSET(S_PAST_EOF) || recno > total + 1) {
161                                /*
162                                 * Keep the page locked for serializability.
163                                 *
164                                 * XXX
165                                 * This leaves the root page locked, which will
166                                 * eliminate any concurrency.  A possible fix
167                                 * would be to lock the last leaf page instead.
168                                 */
169                                (void)mpf->put(mpf, h, 0);
170                                (void)__TLPUT(dbc, lock);
171                                return (DB_NOTFOUND);
172                        }
173                }
174        }
175
176        /*
177         * !!!
178         * Record numbers in the tree are 0-based, but the recno is
179         * 1-based.  All of the calculations below have to take this
180         * into account.
181         */
182        for (total = 0;;) {
183                switch (TYPE(h)) {
184                case P_LBTREE:
185                case P_LDUP:
186                        recno -= total;
187                        /*
188                         * There may be logically deleted records on the page.
189                         * If there are enough, the record may not exist.
190                         */
191                        if (TYPE(h) == P_LBTREE) {
192                                adjust = P_INDX;
193                                deloffset = O_INDX;
194                        } else {
195                                adjust = O_INDX;
196                                deloffset = 0;
197                        }
198                        for (t_recno = 0, indx = 0;; indx += adjust) {
199                                if (indx >= NUM_ENT(h)) {
200                                        *exactp = 0;
201                                        if (!LF_ISSET(S_PAST_EOF) ||
202                                            recno > t_recno + 1) {
203                                                ret = DB_NOTFOUND;
204                                                goto err;
205                                        }
206                                }
207                                if (!B_DISSET(GET_BKEYDATA(dbp, h,
208                                    indx + deloffset)->type) &&
209                                    ++t_recno == recno)
210                                        break;
211                        }
212
213                        /* Correct from 1-based to 0-based for a page offset. */
214                        BT_STK_ENTER(dbp->dbenv,
215                            cp, h, indx, lock, lock_mode, ret);
216                        if (ret != 0)
217                                goto err;
218                        return (0);
219                case P_IBTREE:
220                        for (indx = 0, top = NUM_ENT(h);;) {
221                                bi = GET_BINTERNAL(dbp, h, indx);
222                                if (++indx == top || total + bi->nrecs >= recno)
223                                        break;
224                                total += bi->nrecs;
225                        }
226                        pg = bi->pgno;
227                        break;
228                case P_LRECNO:
229                        recno -= total;
230
231                        /* Correct from 1-based to 0-based for a page offset. */
232                        --recno;
233                        BT_STK_ENTER(dbp->dbenv,
234                            cp, h, recno, lock, lock_mode, ret);
235                        if (ret != 0)
236                                goto err;
237                        return (0);
238                case P_IRECNO:
239                        for (indx = 0, top = NUM_ENT(h);;) {
240                                ri = GET_RINTERNAL(dbp, h, indx);
241                                if (++indx == top || total + ri->nrecs >= recno)
242                                        break;
243                                total += ri->nrecs;
244                        }
245                        pg = ri->pgno;
246                        break;
247                default:
248                        return (__db_pgfmt(dbp->dbenv, h->pgno));
249                }
250                --indx;
251
252                if (stack) {
253                        /* Return if this is the lowest page wanted. */
254                        if (LF_ISSET(S_PARENT) && stop == h->level) {
255                                BT_STK_ENTER(dbp->dbenv,
256                                    cp, h, indx, lock, lock_mode, ret);
257                                if (ret != 0)
258                                        goto err;
259                                return (0);
260                        }
261                        BT_STK_PUSH(dbp->dbenv,
262                            cp, h, indx, lock, lock_mode, ret);
263                        if (ret != 0)
264                                goto err;
265
266                        lock_mode = DB_LOCK_WRITE;
267                        if ((ret =
268                            __db_lget(dbc, 0, pg, lock_mode, 0, &lock)) != 0)
269                                goto err;
270                } else {
271                        /*
272                         * Decide if we want to return a pointer to the next
273                         * page in the stack.  If we do, write lock it and
274                         * never unlock it.
275                         */
276                        if ((LF_ISSET(S_PARENT) &&
277                            (u_int8_t)(stop + 1) >= (u_int8_t)(h->level - 1)) ||
278                            (h->level - 1) == LEAFLEVEL)
279                                stack = 1;
280
281                        (void)mpf->put(mpf, h, 0);
282
283                        lock_mode = stack &&
284                            LF_ISSET(S_WRITE) ? DB_LOCK_WRITE : DB_LOCK_READ;
285                        if ((ret = __db_lget(dbc,
286                            LCK_COUPLE_ALWAYS, pg, lock_mode, 0, &lock)) != 0) {
287                                /*
288                                 * If we fail, discard the lock we held.  This
289                                 * is OK because this only happens when we are
290                                 * descending the tree holding read-locks.
291                                 */
292                                __LPUT(dbc, lock);
293                                goto err;
294                        }
295                }
296
297                if ((ret = mpf->get(mpf, &pg, 0, &h)) != 0)
298                        goto err;
299        }
300        /* NOTREACHED */
301
302err:    BT_STK_POP(cp);
303        __bam_stkrel(dbc, 0);
304        return (ret);
305}
306
307/*
308 * __bam_adjust --
309 *      Adjust the tree after adding or deleting a record.
310 *
311 * PUBLIC: int __bam_adjust __P((DBC *, int32_t));
312 */
313int
314__bam_adjust(dbc, adjust)
315        DBC *dbc;
316        int32_t adjust;
317{
318        BTREE_CURSOR *cp;
319        DB *dbp;
320        DB_MPOOLFILE *mpf;
321        EPG *epg;
322        PAGE *h;
323        db_pgno_t root_pgno;
324        int ret;
325
326        dbp = dbc->dbp;
327        mpf = dbp->mpf;
328        cp = (BTREE_CURSOR *)dbc->internal;
329        root_pgno = cp->root;
330
331        /* Update the record counts for the tree. */
332        for (epg = cp->sp; epg <= cp->csp; ++epg) {
333                h = epg->page;
334                if (TYPE(h) == P_IBTREE || TYPE(h) == P_IRECNO) {
335                        if (DBC_LOGGING(dbc)) {
336                                if ((ret = __bam_cadjust_log(dbp, dbc->txn,
337                                    &LSN(h), 0, PGNO(h), &LSN(h),
338                                    (u_int32_t)epg->indx, adjust,
339                                    PGNO(h) == root_pgno ?
340                                    CAD_UPDATEROOT : 0)) != 0)
341                                        return (ret);
342                        } else
343                                LSN_NOT_LOGGED(LSN(h));
344
345                        if (TYPE(h) == P_IBTREE)
346                                GET_BINTERNAL(dbp, h, epg->indx)->nrecs +=
347                                    adjust;
348                        else
349                                GET_RINTERNAL(dbp, h, epg->indx)->nrecs +=
350                                    adjust;
351
352                        if (PGNO(h) == root_pgno)
353                                RE_NREC_ADJ(h, adjust);
354
355                        if ((ret = mpf->set(mpf, h, DB_MPOOL_DIRTY)) != 0)
356                                return (ret);
357                }
358        }
359        return (0);
360}
361
362/*
363 * __bam_nrecs --
364 *      Return the number of records in the tree.
365 *
366 * PUBLIC: int __bam_nrecs __P((DBC *, db_recno_t *));
367 */
368int
369__bam_nrecs(dbc, rep)
370        DBC *dbc;
371        db_recno_t *rep;
372{
373        DB *dbp;
374        DB_LOCK lock;
375        DB_MPOOLFILE *mpf;
376        PAGE *h;
377        db_pgno_t pgno;
378        int ret;
379
380        dbp = dbc->dbp;
381        mpf = dbp->mpf;
382
383        pgno = dbc->internal->root;
384        if ((ret = __db_lget(dbc, 0, pgno, DB_LOCK_READ, 0, &lock)) != 0)
385                return (ret);
386        if ((ret = mpf->get(mpf, &pgno, 0, &h)) != 0)
387                return (ret);
388
389        *rep = RE_NREC(h);
390
391        (void)mpf->put(mpf, h, 0);
392        (void)__TLPUT(dbc, lock);
393
394        return (0);
395}
396
397/*
398 * __bam_total --
399 *      Return the number of records below a page.
400 *
401 * PUBLIC: db_recno_t __bam_total __P((DB *, PAGE *));
402 */
403db_recno_t
404__bam_total(dbp, h)
405        DB *dbp;
406        PAGE *h;
407{
408        db_recno_t nrecs;
409        db_indx_t indx, top;
410
411        nrecs = 0;
412        top = NUM_ENT(h);
413
414        switch (TYPE(h)) {
415        case P_LBTREE:
416                /* Check for logically deleted records. */
417                for (indx = 0; indx < top; indx += P_INDX)
418                        if (!B_DISSET(
419                            GET_BKEYDATA(dbp, h, indx + O_INDX)->type))
420                                ++nrecs;
421                break;
422        case P_LDUP:
423                /* Check for logically deleted records. */
424                for (indx = 0; indx < top; indx += O_INDX)
425                        if (!B_DISSET(GET_BKEYDATA(dbp, h, indx)->type))
426                                ++nrecs;
427                break;
428        case P_IBTREE:
429                for (indx = 0; indx < top; indx += O_INDX)
430                        nrecs += GET_BINTERNAL(dbp, h, indx)->nrecs;
431                break;
432        case P_LRECNO:
433                nrecs = NUM_ENT(h);
434                break;
435        case P_IRECNO:
436                for (indx = 0; indx < top; indx += O_INDX)
437                        nrecs += GET_RINTERNAL(dbp, h, indx)->nrecs;
438                break;
439        }
440
441        return (nrecs);
442}
Note: See TracBrowser for help on using the repository browser.