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

Revision 19079, 15.4 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, 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. Neither the name of the University nor the names of its contributors
27 *    may be used to endorse or promote products derived from this software
28 *    without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
41 */
42
43#include "db_config.h"
44
45#ifndef lint
46static const char revid[] = "Id: bt_open.c,v 11.76 2002/09/04 19:06:42 margo Exp ";
47#endif /* not lint */
48
49#ifndef NO_SYSTEM_INCLUDES
50#include <sys/types.h>
51
52#include <limits.h>
53#include <string.h>
54#endif
55
56#include "db_int.h"
57#include "dbinc/crypto.h"
58#include "dbinc/db_page.h"
59#include "dbinc/db_swap.h"
60#include "dbinc/btree.h"
61#include "dbinc/db_shash.h"
62#include "dbinc/lock.h"
63#include "dbinc/log.h"
64#include "dbinc/fop.h"
65
66static void __bam_init_meta __P((DB *, BTMETA *, db_pgno_t, DB_LSN *));
67
68/*
69 * __bam_open --
70 *      Open a btree.
71 *
72 * PUBLIC: int __bam_open __P((DB *,
73 * PUBLIC:      DB_TXN *, const char *, db_pgno_t, u_int32_t));
74 */
75int
76__bam_open(dbp, txn, name, base_pgno, flags)
77        DB *dbp;
78        DB_TXN *txn;
79        const char *name;
80        db_pgno_t base_pgno;
81        u_int32_t flags;
82{
83        BTREE *t;
84
85        COMPQUIET(name, NULL);
86        t = dbp->bt_internal;
87
88        /* Initialize the remaining fields/methods of the DB. */
89        dbp->key_range = __bam_key_range;
90        dbp->stat = __bam_stat;
91
92        /*
93         * We don't permit the user to specify a prefix routine if they didn't
94         * also specify a comparison routine, they can't know enough about our
95         * comparison routine to get it right.
96         */
97        if (t->bt_compare == __bam_defcmp && t->bt_prefix != __bam_defpfx) {
98                __db_err(dbp->dbenv,
99"prefix comparison may not be specified for default comparison routine");
100                return (EINVAL);
101        }
102
103        /*
104         * Verify that the bt_minkey value specified won't cause the
105         * calculation of ovflsize to underflow [#2406] for this pagesize.
106         */
107        if (B_MINKEY_TO_OVFLSIZE(dbp, t->bt_minkey, dbp->pgsize) >
108            B_MINKEY_TO_OVFLSIZE(dbp, DEFMINKEYPAGE, dbp->pgsize)) {
109                __db_err(dbp->dbenv,
110                    "bt_minkey value of %lu too high for page size of %lu",
111                    (u_long)t->bt_minkey, (u_long)dbp->pgsize);
112                return (EINVAL);
113        }
114
115        /* Start up the tree. */
116        return (__bam_read_root(dbp, txn, base_pgno, flags));
117}
118
119/*
120 * __bam_metachk --
121 *
122 * PUBLIC: int __bam_metachk __P((DB *, const char *, BTMETA *));
123 */
124int
125__bam_metachk(dbp, name, btm)
126        DB *dbp;
127        const char *name;
128        BTMETA *btm;
129{
130        DB_ENV *dbenv;
131        u_int32_t vers;
132        int ret;
133
134        dbenv = dbp->dbenv;
135
136        /*
137         * At this point, all we know is that the magic number is for a Btree.
138         * Check the version, the database may be out of date.
139         */
140        vers = btm->dbmeta.version;
141        if (F_ISSET(dbp, DB_AM_SWAP))
142                M_32_SWAP(vers);
143        switch (vers) {
144        case 6:
145        case 7:
146                __db_err(dbenv,
147                    "%s: btree version %lu requires a version upgrade",
148                    name, (u_long)vers);
149                return (DB_OLD_VERSION);
150        case 8:
151        case 9:
152                break;
153        default:
154                __db_err(dbenv,
155                    "%s: unsupported btree version: %lu", name, (u_long)vers);
156                return (EINVAL);
157        }
158
159        /* Swap the page if we need to. */
160        if (F_ISSET(dbp, DB_AM_SWAP) && (ret = __bam_mswap((PAGE *)btm)) != 0)
161                return (ret);
162
163        /*
164         * Check application info against metadata info, and set info, flags,
165         * and type based on metadata info.
166         */
167        if ((ret =
168            __db_fchk(dbenv, "DB->open", btm->dbmeta.flags, BTM_MASK)) != 0)
169                return (ret);
170
171        if (F_ISSET(&btm->dbmeta, BTM_RECNO)) {
172                if (dbp->type == DB_BTREE)
173                        goto wrong_type;
174                dbp->type = DB_RECNO;
175                DB_ILLEGAL_METHOD(dbp, DB_OK_RECNO);
176        } else {
177                if (dbp->type == DB_RECNO)
178                        goto wrong_type;
179                dbp->type = DB_BTREE;
180                DB_ILLEGAL_METHOD(dbp, DB_OK_BTREE);
181        }
182
183        if (F_ISSET(&btm->dbmeta, BTM_DUP))
184                F_SET(dbp, DB_AM_DUP);
185        else
186                if (F_ISSET(dbp, DB_AM_DUP)) {
187                        __db_err(dbenv,
188                "%s: DB_DUP specified to open method but not set in database",
189                            name);
190                        return (EINVAL);
191                }
192
193        if (F_ISSET(&btm->dbmeta, BTM_RECNUM)) {
194                if (dbp->type != DB_BTREE)
195                        goto wrong_type;
196                F_SET(dbp, DB_AM_RECNUM);
197
198                if ((ret = __db_fcchk(dbenv,
199                    "DB->open", dbp->flags, DB_AM_DUP, DB_AM_RECNUM)) != 0)
200                        return (ret);
201        } else
202                if (F_ISSET(dbp, DB_AM_RECNUM)) {
203                        __db_err(dbenv,
204            "%s: DB_RECNUM specified to open method but not set in database",
205                            name);
206                        return (EINVAL);
207                }
208
209        if (F_ISSET(&btm->dbmeta, BTM_FIXEDLEN)) {
210                if (dbp->type != DB_RECNO)
211                        goto wrong_type;
212                F_SET(dbp, DB_AM_FIXEDLEN);
213        } else
214                if (F_ISSET(dbp, DB_AM_FIXEDLEN)) {
215                        __db_err(dbenv,
216        "%s: DB_FIXEDLEN specified to open method but not set in database",
217                            name);
218                        return (EINVAL);
219                }
220
221        if (F_ISSET(&btm->dbmeta, BTM_RENUMBER)) {
222                if (dbp->type != DB_RECNO)
223                        goto wrong_type;
224                F_SET(dbp, DB_AM_RENUMBER);
225        } else
226                if (F_ISSET(dbp, DB_AM_RENUMBER)) {
227                        __db_err(dbenv,
228            "%s: DB_RENUMBER specified to open method but not set in database",
229                            name);
230                        return (EINVAL);
231                }
232
233        if (F_ISSET(&btm->dbmeta, BTM_SUBDB))
234                F_SET(dbp, DB_AM_SUBDB);
235        else
236                if (F_ISSET(dbp, DB_AM_SUBDB)) {
237                        __db_err(dbenv,
238            "%s: multiple databases specified but not supported by file",
239                            name);
240                        return (EINVAL);
241                }
242
243        if (F_ISSET(&btm->dbmeta, BTM_DUPSORT)) {
244                if (dbp->dup_compare == NULL)
245                        dbp->dup_compare = __bam_defcmp;
246                F_SET(dbp, DB_AM_DUPSORT);
247        } else
248                if (dbp->dup_compare != NULL) {
249                        __db_err(dbenv,
250                "%s: duplicate sort specified but not supported in database",
251                            name);
252                        return (EINVAL);
253                }
254
255        /* Set the page size. */
256        dbp->pgsize = btm->dbmeta.pagesize;
257
258        /* Copy the file's ID. */
259        memcpy(dbp->fileid, btm->dbmeta.uid, DB_FILE_ID_LEN);
260
261        return (0);
262
263wrong_type:
264        if (dbp->type == DB_BTREE)
265                __db_err(dbenv,
266                    "open method type is Btree, database type is Recno");
267        else
268                __db_err(dbenv,
269                    "open method type is Recno, database type is Btree");
270        return (EINVAL);
271}
272
273/*
274 * __bam_read_root --
275 *      Read the root page and check a tree.
276 *
277 * PUBLIC: int __bam_read_root __P((DB *, DB_TXN *, db_pgno_t, u_int32_t));
278 */
279int
280__bam_read_root(dbp, txn, base_pgno, flags)
281        DB *dbp;
282        DB_TXN *txn;
283        db_pgno_t base_pgno;
284        u_int32_t flags;
285{
286        BTMETA *meta;
287        BTREE *t;
288        DBC *dbc;
289        DB_LOCK metalock;
290        DB_MPOOLFILE *mpf;
291        int ret, t_ret;
292
293        meta = NULL;
294        t = dbp->bt_internal;
295        LOCK_INIT(metalock);
296        mpf = dbp->mpf;
297        ret = 0;
298
299        /* Get a cursor.  */
300        if ((ret = dbp->cursor(dbp, txn, &dbc, 0)) != 0)
301                return (ret);
302
303        /* Get the metadata page. */
304        if ((ret =
305            __db_lget(dbc, 0, base_pgno, DB_LOCK_READ, 0, &metalock)) != 0)
306                goto err;
307        if ((ret = mpf->get(mpf, &base_pgno, 0, (PAGE **)&meta)) != 0)
308                goto err;
309
310        /*
311         * If the magic number is set, the tree has been created.  Correct
312         * any fields that may not be right.  Note, all of the local flags
313         * were set by DB->open.
314         *
315         * Otherwise, we'd better be in recovery or abort, in which case the
316         * metadata page will be created/initialized elsewhere.
317         */
318        DB_ASSERT(meta->dbmeta.magic != 0 ||
319            IS_RECOVERING(dbp->dbenv) || F_ISSET(dbp, DB_AM_RECOVER));
320
321        t->bt_maxkey = meta->maxkey;
322        t->bt_minkey = meta->minkey;
323        t->re_pad = meta->re_pad;
324        t->re_len = meta->re_len;
325
326        t->bt_meta = base_pgno;
327        t->bt_root = meta->root;
328
329        /*
330         * !!!
331         * If creating a subdatabase, we've already done an insert when
332         * we put the subdatabase's entry into the master database, so
333         * our last-page-inserted value is wrongly initialized for the
334         * master database, not the subdatabase we're creating.  I'm not
335         * sure where the *right* place to clear this value is, it's not
336         * intuitively obvious that it belongs here.
337         */
338        t->bt_lpgno = PGNO_INVALID;
339
340        /* We must initialize last_pgno, it could be stale. */
341        if (!LF_ISSET(DB_RDONLY) && dbp->meta_pgno == PGNO_BASE_MD) {
342                mpf->last_pgno(mpf, &meta->dbmeta.last_pgno);
343                ret = mpf->put(mpf, meta, DB_MPOOL_DIRTY);
344        } else
345                ret = mpf->put(mpf, meta, 0);
346        meta = NULL;
347
348err:    /* Put the metadata page back. */
349        if (meta != NULL && (t_ret = mpf->put(mpf, meta, 0)) != 0 && ret == 0)
350                ret = t_ret;
351        if ((t_ret = __LPUT(dbc, metalock)) != 0 && ret == 0)
352                ret = t_ret;
353
354        if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0)
355                ret = t_ret;
356        return (ret);
357}
358
359/*
360 * __bam_init_meta --
361 *
362 * Initialize a btree meta-data page.  The following fields may need
363 * to be updated later: last_pgno, root.
364 */
365static void
366__bam_init_meta(dbp, meta, pgno, lsnp)
367        DB *dbp;
368        BTMETA *meta;
369        db_pgno_t pgno;
370        DB_LSN *lsnp;
371{
372        BTREE *t;
373
374        memset(meta, 0, sizeof(BTMETA));
375        meta->dbmeta.lsn = *lsnp;
376        meta->dbmeta.pgno = pgno;
377        meta->dbmeta.magic = DB_BTREEMAGIC;
378        meta->dbmeta.version = DB_BTREEVERSION;
379        meta->dbmeta.pagesize = dbp->pgsize;
380        if (F_ISSET(dbp, DB_AM_CHKSUM))
381                FLD_SET(meta->dbmeta.metaflags, DBMETA_CHKSUM);
382        if (F_ISSET(dbp, DB_AM_ENCRYPT)) {
383                meta->dbmeta.encrypt_alg =
384                    ((DB_CIPHER *)dbp->dbenv->crypto_handle)->alg;
385                DB_ASSERT(meta->dbmeta.encrypt_alg != 0);
386                meta->crypto_magic = meta->dbmeta.magic;
387        }
388        meta->dbmeta.type = P_BTREEMETA;
389        meta->dbmeta.free = PGNO_INVALID;
390        meta->dbmeta.last_pgno = pgno;
391        if (F_ISSET(dbp, DB_AM_DUP))
392                F_SET(&meta->dbmeta, BTM_DUP);
393        if (F_ISSET(dbp, DB_AM_FIXEDLEN))
394                F_SET(&meta->dbmeta, BTM_FIXEDLEN);
395        if (F_ISSET(dbp, DB_AM_RECNUM))
396                F_SET(&meta->dbmeta, BTM_RECNUM);
397        if (F_ISSET(dbp, DB_AM_RENUMBER))
398                F_SET(&meta->dbmeta, BTM_RENUMBER);
399        if (F_ISSET(dbp, DB_AM_SUBDB))
400                F_SET(&meta->dbmeta, BTM_SUBDB);
401        if (dbp->dup_compare != NULL)
402                F_SET(&meta->dbmeta, BTM_DUPSORT);
403        if (dbp->type == DB_RECNO)
404                F_SET(&meta->dbmeta, BTM_RECNO);
405        memcpy(meta->dbmeta.uid, dbp->fileid, DB_FILE_ID_LEN);
406
407        t = dbp->bt_internal;
408        meta->maxkey = t->bt_maxkey;
409        meta->minkey = t->bt_minkey;
410        meta->re_len = t->re_len;
411        meta->re_pad = t->re_pad;
412}
413
414/*
415 * __bam_new_file --
416 * Create the necessary pages to begin a new database file.
417 *
418 * This code appears more complex than it is because of the two cases (named
419 * and unnamed).  The way to read the code is that for each page being created,
420 * there are three parts: 1) a "get page" chunk (which either uses malloc'd
421 * memory or calls mpf->get), 2) the initialization, and 3) the "put page"
422 * chunk which either does a fop write or an mpf->put.
423 *
424 * PUBLIC: int __bam_new_file __P((DB *, DB_TXN *, DB_FH *, const char *));
425 */
426int
427__bam_new_file(dbp, txn, fhp, name)
428        DB *dbp;
429        DB_TXN *txn;
430        DB_FH *fhp;
431        const char *name;
432{
433        BTMETA *meta;
434        DB_ENV *dbenv;
435        DB_LSN lsn;
436        DB_MPOOLFILE *mpf;
437        DB_PGINFO pginfo;
438        DBT pdbt;
439        PAGE *root;
440        db_pgno_t pgno;
441        int ret;
442        void *buf;
443
444        dbenv = dbp->dbenv;
445        mpf = dbp->mpf;
446        root = NULL;
447        meta = NULL;
448        memset(&pdbt, 0, sizeof(pdbt));
449
450        /* Build meta-data page. */
451
452        if (name == NULL) {
453                pgno = PGNO_BASE_MD;
454                ret = mpf->get(mpf, &pgno, DB_MPOOL_CREATE, &meta);
455        } else {
456                pginfo.db_pagesize = dbp->pgsize;
457                pginfo.flags =
458                    F_ISSET(dbp, (DB_AM_CHKSUM | DB_AM_ENCRYPT | DB_AM_SWAP));
459                pginfo.type = dbp->type;
460                pdbt.data = &pginfo;
461                pdbt.size = sizeof(pginfo);
462                ret = __os_calloc(dbp->dbenv, 1, dbp->pgsize, &buf);
463                meta = (BTMETA *)buf;
464        }
465        if (ret != 0)
466                return (ret);
467
468        LSN_NOT_LOGGED(lsn);
469        __bam_init_meta(dbp, meta, PGNO_BASE_MD, &lsn);
470        meta->root = 1;
471        meta->dbmeta.last_pgno = 1;
472
473        if (name == NULL)
474                ret = mpf->put(mpf, meta, DB_MPOOL_DIRTY);
475        else {
476                if ((ret = __db_pgout(dbenv, PGNO_BASE_MD, meta, &pdbt)) != 0)
477                        goto err;
478                ret = __fop_write(dbenv,
479                    txn, name, DB_APP_DATA, fhp, 0, buf, dbp->pgsize, 1);
480        }
481        if (ret != 0)
482                goto err;
483        meta = NULL;
484
485        /* Now build root page. */
486        if (name == NULL) {
487                pgno = 1;
488                if ((ret = mpf->get(mpf, &pgno, DB_MPOOL_CREATE, &root)) != 0)
489                        goto err;
490        } else {
491#ifdef DIAGNOSTIC
492                memset(buf, dbp->pgsize, 0);
493#endif
494                root = (PAGE *)buf;
495        }
496
497        P_INIT(root, dbp->pgsize, 1, PGNO_INVALID, PGNO_INVALID,
498            LEAFLEVEL, dbp->type == DB_RECNO ? P_LRECNO : P_LBTREE);
499        LSN_NOT_LOGGED(root->lsn);
500
501        if (name == NULL)
502                ret = mpf->put(mpf, root, DB_MPOOL_DIRTY);
503        else {
504                if ((ret = __db_pgout(dbenv, root->pgno, root, &pdbt)) != 0)
505                        goto err;
506                ret = __fop_write(dbenv, txn,
507                    name, DB_APP_DATA, fhp, dbp->pgsize, buf, dbp->pgsize, 1);
508        }
509        if (ret != 0)
510                goto err;
511        root = NULL;
512
513err:    if (name != NULL)
514                __os_free(dbenv, buf);
515        else {
516                if (meta != NULL)
517                        (void)mpf->put(mpf, meta, 0);
518                if (root != NULL)
519                        (void)mpf->put(mpf, root, 0);
520        }
521        return (ret);
522}
523
524/*
525 * __bam_new_subdb --
526 *      Create a metadata page and a root page for a new btree.
527 *
528 * PUBLIC: int __bam_new_subdb __P((DB *, DB *, DB_TXN *));
529 */
530int
531__bam_new_subdb(mdbp, dbp, txn)
532        DB *mdbp, *dbp;
533        DB_TXN *txn;
534{
535        BTMETA *meta;
536        DBC *dbc;
537        DB_ENV *dbenv;
538        DB_LOCK metalock;
539        DB_LSN lsn;
540        DB_MPOOLFILE *mpf;
541        PAGE *root;
542        int ret, t_ret;
543
544        dbenv = mdbp->dbenv;
545        mpf = mdbp->mpf;
546        dbc = NULL;
547        meta = NULL;
548        root = NULL;
549
550        if ((ret = mdbp->cursor(mdbp, txn,
551            &dbc, CDB_LOCKING(dbenv) ?  DB_WRITECURSOR : 0)) != 0)
552                return (ret);
553
554        /* Get, and optionally create the metadata page. */
555        if ((ret = __db_lget(dbc,
556            0, dbp->meta_pgno, DB_LOCK_WRITE, 0, &metalock)) != 0)
557                goto err;
558        if ((ret = mpf->get(mpf, &dbp->meta_pgno, DB_MPOOL_CREATE, &meta)) != 0)
559                goto err;
560
561        /* Build meta-data page. */
562        lsn = meta->dbmeta.lsn;
563        __bam_init_meta(dbp, meta, dbp->meta_pgno, &lsn);
564        if ((ret = __db_log_page(mdbp,
565            txn, &meta->dbmeta.lsn, dbp->meta_pgno, (PAGE *)meta)) != 0)
566                goto err;
567
568        /* Create and initialize a root page. */
569        if ((ret = __db_new(dbc,
570            dbp->type == DB_RECNO ? P_LRECNO : P_LBTREE, &root)) != 0)
571                goto err;
572        root->level = LEAFLEVEL;
573
574        if (DBENV_LOGGING(dbenv) &&
575            (ret = __bam_root_log(mdbp, txn, &meta->dbmeta.lsn, 0,
576            meta->dbmeta.pgno, root->pgno, &meta->dbmeta.lsn)) != 0)
577                goto err;
578
579        meta->root = root->pgno;
580        if ((ret =
581            __db_log_page(mdbp, txn, &root->lsn, root->pgno, root)) != 0)
582                goto err;
583
584        /* Release the metadata and root pages. */
585        if ((ret = mpf->put(mpf, meta, DB_MPOOL_DIRTY)) != 0)
586                goto err;
587        meta = NULL;
588        if ((ret = mpf->put(mpf, root, DB_MPOOL_DIRTY)) != 0)
589                goto err;
590        root = NULL;
591err:
592        if (meta != NULL)
593                if ((t_ret = mpf->put(mpf, meta, 0)) != 0 && ret == 0)
594                        ret = t_ret;
595        if (root != NULL)
596                if ((t_ret = mpf->put(mpf, root, 0)) != 0 && ret == 0)
597                        ret = t_ret;
598        if (LOCK_ISSET(metalock))
599                if ((t_ret = __LPUT(dbc, metalock)) != 0 && ret == 0)
600                        ret = t_ret;
601        if (dbc != NULL)
602                if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0)
603                        ret = t_ret;
604        return (ret);
605}
Note: See TracBrowser for help on using the repository browser.