386BSD 0.0 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Tue, 7 May 1991 17:00:43 +0000 (09:00 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Tue, 7 May 1991 17:00:43 +0000 (09:00 -0800)
Work on file usr/src/lib/libc/db/btree/btree.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.0/src

usr/src/lib/libc/db/btree/btree.c [new file with mode: 0644]

diff --git a/usr/src/lib/libc/db/btree/btree.c b/usr/src/lib/libc/db/btree/btree.c
new file mode 100644 (file)
index 0000000..d47699a
--- /dev/null
@@ -0,0 +1,752 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)btree.c    5.9 (Berkeley) 5/7/91";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ *  btree.c -- implementation of btree access method for 4.4BSD.
+ *
+ *     The design here is based on that of the btree access method used
+ *     in the Postgres database system at UC Berkeley.  The implementation
+ *     is wholly independent of the Postgres code.
+ *
+ *     This implementation supports btrees on disk (supply a filename) or
+ *     in memory (don't).  Public interfaces defined here are:
+ *
+ *             btree_open()    -- wrapper; returns a filled DB struct for
+ *                                a btree.
+ *
+ *             bt_open()       -- open a new or existing btree.
+ *             bt_get()        -- fetch data from a tree by key.
+ *             bt_seq()        -- do a sequential scan on a tree.
+ *             bt_put()        -- add data to a tree by key.
+ *             bt_delete()     -- remove data from a tree by key.
+ *             bt_close()      -- close a btree.
+ *             bt_sync()       -- force btree pages to disk (disk trees only).
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <db.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "btree.h"
+
+BTREEINFO _DefaultBTInfo = {
+       0,      /* flags */
+       0,      /* cachesize */
+       0,      /* psize */
+       strcmp, /* compare */
+       0
+};
+
+/*
+ *  BTREE_OPEN -- Wrapper routine to open a btree.
+ *
+ *     Creates and fills a DB struct, and calls the routine that actually
+ *     opens the btree.
+ *
+ *     Parameters:
+ *             f:  filename to open
+ *             flags:  flag bits passed to open
+ *             mode:  permission bits, used if O_CREAT specified
+ *             b:  BTREEINFO pointer
+ *
+ *     Returns:
+ *             Filled-in DBT on success; NULL on failure, with errno
+ *             set as appropriate.
+ *
+ *     Side Effects:
+ *             Allocates memory for the DB struct.
+ */
+
+DB *
+btree_open(f, flags, mode, b)
+       const char *f;
+       int flags;
+       int mode;
+       const BTREEINFO *b;
+{
+       DB *db;
+       BTREE t;
+
+       if ((db = (DB *) malloc((unsigned) sizeof(DB))) == (DB *) NULL)
+               return ((DB *) NULL);
+
+       if ((t = bt_open(f, flags, mode, b)) == (BTREE) NULL) {
+               (void) free ((char *) db);
+               return ((DB *) NULL);
+       }
+
+       db->internal = (char *) t;
+       db->close = bt_close;
+       db->del = bt_delete;
+       db->get = bt_get;
+       db->put = bt_put;
+       db->seq = bt_seq;
+       db->sync = bt_sync;
+       db->type = DB_BTREE;
+
+       return (db);
+}
+
+/*
+ *  BT_OPEN -- Open a btree.
+ *
+ *     This routine creates the correct kind (disk or in-memory) of
+ *     btree and initializes it as required.
+ *
+ *     Parameters:
+ *             f -- name of btree (NULL for in-memory btrees)
+ *             flags -- flags passed to open()
+ *             mode -- mode passed to open ()
+ *             b -- BTREEINFO structure, describing btree
+ *
+ *     Returns:
+ *             (Opaque) pointer to the btree.  On failure, returns NULL
+ *             with errno set as appropriate.
+ *
+ *     Side Effects:
+ *             Allocates memory, opens files.
+ */
+
+BTREE
+bt_open(f, flags, mode, b)
+       char *f;
+       int flags;
+       int mode;
+       BTREEINFO *b;
+{
+       BTREE_P t;
+       HTABLE ht;
+       int nbytes;
+       int fd;
+       CURSOR *c;
+       BTMETA m;
+       struct stat buf;
+
+       /* use the default info if none was provided */
+       if (b == (BTREEINFO *) NULL)
+               b = &_DefaultBTInfo;
+
+       if ((t = (BTREE_P) malloc((unsigned) sizeof *t)) == (BTREE_P) NULL)
+               return ((BTREE) NULL);
+
+       if (b->compare)
+               t->bt_compare = b->compare;
+       else
+               t->bt_compare = strcmp;
+
+       t->bt_fname = f;
+       t->bt_curpage = (BTHEADER *) NULL;
+       t->bt_free = P_NONE;
+       c = &(t->bt_cursor);
+       c->c_pgno = P_NONE;
+       c->c_index = 0;
+       c->c_flags = (u_char) NULL;
+       c->c_key = (char *) NULL;
+       t->bt_stack = (BTSTACK *) NULL;
+       t->bt_flags = 0;
+
+       /*
+        *  If no file name was supplied, this is an in-memory btree.
+        *  Otherwise, it's a disk-based btree.
+        */
+       if (f == (char *) NULL) {
+               /* in memory */
+               if ((t->bt_psize = b->psize) < MINPSIZE) {
+                       if (t->bt_psize != 0) {
+                               (void) free ((char *) t);
+                               errno = EINVAL;
+                               return ((BTREE) NULL);
+                       }
+                       t->bt_psize = getpagesize();
+               }
+
+               nbytes = HTSIZE * sizeof(HTBUCKET *);
+               if ((ht = (HTABLE) malloc((unsigned) nbytes))
+                   == (HTABLE) NULL) {
+                       (void) free((char *) t);
+                       return ((BTREE) NULL);
+               }
+               (void) bzero((char *) ht, nbytes);
+               t->bt_s.bt_ht = ht;
+               t->bt_npages = 0;
+               t->bt_lorder = BYTE_ORDER;
+               if (!(b->flags & R_DUP))
+                       t->bt_flags |= BTF_NODUPS;
+       } else {
+               /* on disk */
+               if ((fd = open(f, O_RDONLY, 0)) < 0) {
+                       /* doesn't exist yet, be sure page is big enough */
+                       if ((t->bt_psize = b->psize) < sizeof(BTHEADER)
+                           && b->psize != 0) {
+                               (void) free((char *) t);
+                               errno = EINVAL;
+                               return ((BTREE) NULL);
+                       }
+                       if (b->lorder == 0)
+                               b->lorder = BYTE_ORDER;
+
+                       if (b->lorder != BIG_ENDIAN
+                           && b->lorder != LITTLE_ENDIAN) {
+                               (void) free((char *) t);
+                               errno = EINVAL;
+                               return ((BTREE) NULL);
+                       }
+                       t->bt_lorder = b->lorder;
+                       if (!(b->flags & R_DUP))
+                               t->bt_flags |= BTF_NODUPS;
+               } else {
+                       /* exists, get page size from file */
+                       if (read(fd, &m, sizeof(m)) < sizeof(m)) {
+                               (void) close(fd);
+                               (void) free((char *) t);
+                               errno = EINVAL;
+                               return ((BTREE) NULL);
+                       }
+
+                       /* lorder always stored in host-independent format */
+                       NTOHL(m.m_lorder);
+                       if (m.m_lorder != BIG_ENDIAN
+                           && m.m_lorder != LITTLE_ENDIAN) {
+                               (void) free((char *) t);
+                               errno = EINVAL;
+                               return ((BTREE) NULL);
+                       }
+                       t->bt_lorder = m.m_lorder;
+
+                       if (t->bt_lorder != BYTE_ORDER) {
+                               BLSWAP(m.m_magic);
+                               BLSWAP(m.m_version);
+                               BLSWAP(m.m_psize);
+                               BLSWAP(m.m_free);
+                               BLSWAP(m.m_flags);
+                       }
+
+                       if (m.m_magic != BTREEMAGIC
+                           || m.m_version != BTREEVERSION
+                           || m.m_psize < MINPSIZE) {
+                               (void) close(fd);
+                               (void) free((char *) t);
+#ifndef EFTYPE
+#define EFTYPE -100
+#endif
+                               errno = EFTYPE;
+                               return ((BTREE) NULL);
+                       }
+                       t->bt_psize = m.m_psize;
+                       t->bt_free = m.m_free;
+                       t->bt_flags |= (m.m_flags & BTF_NODUPS) | BTF_METAOK;
+                       (void) close(fd);
+               }
+
+               /* now open the file the way the user wanted it */
+               if ((t->bt_s.bt_d.d_fd = open(f, flags, mode)) < 0) {
+                       (void) free ((char *) t);
+                       return ((BTREE) NULL);
+               }
+
+               /* access method files are always close-on-exec */
+               if (fcntl(t->bt_s.bt_d.d_fd, F_SETFL, 1) == -1) {
+                       (void) close(t->bt_s.bt_d.d_fd);
+                       (void) free ((char *) t);
+                       return ((BTREE) NULL);
+               }
+
+               /* get number of pages, page size if necessary */
+               (void) fstat(t->bt_s.bt_d.d_fd, &buf);
+               if (t->bt_psize == 0)
+                       t->bt_psize = buf.st_blksize;
+               t->bt_npages = (pgno_t) (buf.st_size / t->bt_psize);
+
+               /* page zero is metadata, doesn't count */
+               if (t->bt_npages > 0)
+                       --(t->bt_npages);
+
+               if (b->cachesize == 0)
+                       b->cachesize = DEFCACHE;
+
+               /* get an lru buffer cache, if the user asked for one */
+               if ((b->cachesize / t->bt_psize) > 0) {
+                       BTDISK *d = &(t->bt_s.bt_d);
+
+                       d->d_cache = lruinit(d->d_fd,
+                                            (int) (b->cachesize / t->bt_psize),
+                                            (int) t->bt_psize,
+                                            (char *) t->bt_lorder,
+                                            _bt_pgin, _bt_pgout);
+
+                       if (d->d_cache == (char *) NULL) {
+                               (void) free((char *) t);
+                               return ((BTREE) NULL);
+                       }
+               }
+       }
+
+       /* remember if tree was opened for write */
+       if (((flags & O_WRONLY) == O_WRONLY)
+           || ((flags & O_RDWR) == O_RDWR))
+               t->bt_flags |= BTF_ISWRITE;
+
+       return ((BTREE) t);
+}
+
+/*
+ *  BT_GET -- Get an entry from a btree.
+ *
+ *     Does a key lookup in the tree to find the specified key, and returns
+ *     the key/data pair found.
+ *
+ *     Parameters:
+ *             tree -- btree in which to do lookup
+ *             key -- key to find
+ *             data -- pointer to DBT in which to return data
+ *             flag -- ignored
+ *
+ *     Returns:
+ *             RET_SUCCESS, RET_ERROR, or RET_SPECIAL if the key is not
+ *             found.  If key is not found, nothing is stored in the
+ *             return DBT 'data'.
+ *
+ *     Side Effects:
+ *             None.
+ *
+ *     Warnings:
+ *             Return data is statically allocated, and will be overwritten
+ *             at the next call.
+ */
+
+int
+bt_get(dbp, key, data, flag)
+       DB *dbp; 
+       DBT *key, *data;
+       u_long flag;
+{
+       BTREE_P t = (BTREE_P) (dbp->internal);
+       BTHEADER *h;
+       DATUM *d;
+       BTITEM *item;
+
+#ifdef lint
+       flag = flag;
+#endif /* lint */
+
+       /* lookup */
+       item = _bt_search(t, key);
+
+       /* clear parent pointer stack */
+       while (_bt_pop(t) != P_NONE)
+               continue;
+
+       if (item == (BTITEM *) NULL)
+               return (RET_ERROR);
+
+       h = (BTHEADER *) t->bt_curpage;
+       data->size = 0;
+       data->data = (u_char *) NULL;
+
+       /* match? */
+       if (VALIDITEM(t, item)
+           && _bt_cmp(t, key->data, item->bti_index) == 0) {
+               d = (DATUM *) GETDATUM(h, item->bti_index);
+               return (_bt_buildret(t, d, data, key));
+       }
+
+       /* nope */
+       return (RET_SPECIAL);
+}
+
+/*
+ *  BT_PUT -- Add an entry to a btree.
+ *
+ *     The specified (key, data) pair is added to the tree.  If the tree
+ *     was created for unique keys only, then duplicates will not be
+ *     entered.  If the requested key exists in the tree, it will be over-
+ *     written unless the flags parameter is R_NOOVERWRITE, in which case
+ *     the update will not be done.  If duplicate keys are permitted in the
+ *     tree, duplicates will be inserted and will not overwrite existing
+ *     keys.  Nodes are split as required.
+ *
+ *     Parameters:
+ *             tree -- btree in which to put the new entry
+ *             key -- key component to add
+ *             data -- data corresponding to key
+ *             flag -- R_NOOVERWRITE or zero.
+ *
+ *     Returns:
+ *             RET_SUCCESS, RET_ERROR, or RET_SPECIAL if the
+ *             NOOVERWRITE flag was set and the specified key exists
+ *             in the database.
+ *
+ *     Side Effects:
+ *             None.
+ */
+
+int
+bt_put(dbp, key, data, flag)
+       DB *dbp;
+       DBT *key, *data;
+       u_long flag;
+{
+       BTREE_P t;
+       BTITEM *item;
+
+       t = (BTREE_P)dbp->internal;
+
+       /* look for this key in the tree */
+       item = _bt_search(t, key);
+
+       /*
+        *  If this tree was originally created without R_DUP, then duplicate
+        *  keys are not allowed.  We need to check this at insertion time.
+        */
+
+       if (VALIDITEM(t, item) && _bt_cmp(t, key->data, item->bti_index) == 0) {
+               if ((t->bt_flags & BTF_NODUPS) && flag == R_NOOVERWRITE) {
+                       if (_bt_delone(t, item->bti_index) == RET_ERROR) {
+                               while (_bt_pop(t) != P_NONE)
+                                       continue;
+                               return (RET_ERROR);
+                       }
+               }
+       }
+
+       return (_bt_insert(t, item, key, data, flag));
+}
+
+/*
+ *  BT_DELETE -- delete a key from the tree.
+ *
+ *     Deletes all keys (and their associated data items) matching the
+ *     supplied key from the tree.  If the flags entry is R_CURSOR, then
+ *     the current item in the active scan is deleted.
+ *
+ *     Parameters:
+ *             tree -- btree from which to delete key
+ *             key -- key to delete
+ *             flags -- R_CURSOR or zero
+ *
+ *     Returns:
+ *             RET_SUCCESS, RET_ERROR, or RET_SPECIAL if the specified
+ *             key was not in the tree.
+ *
+ *     Side Effects:
+ *             None.
+ */
+
+int
+bt_delete(dbp, key, flags)
+       DB *dbp;
+       DBT *key;
+       u_long flags;
+{
+       BTREE_P t;
+       BTHEADER *h;
+       BTITEM *item;
+       int ndel = 0;
+
+       t = (BTREE_P)dbp->internal;
+
+       if (flags == R_CURSOR)
+               return (_bt_crsrdel(t));
+
+       /* find the first matching key in the tree */
+       item = _bt_first(t, key);
+       h = t->bt_curpage;
+
+       /* don't need the descent stack for deletes */
+       while (_bt_pop(t) != P_NONE)
+               continue;
+
+       /* delete all matching keys */
+       for (;;) {
+               while (VALIDITEM(t, item)
+                      && (_bt_cmp(t, key->data, item->bti_index) == 0)) {
+                       if (_bt_delone(t, item->bti_index) == RET_ERROR)
+                               return (RET_ERROR);
+                       ndel++;
+               }
+
+               if (VALIDITEM(t, item) || h->h_nextpg == P_NONE)
+                       break;
+
+               /* next page, if necessary */
+               do {
+                       if (_bt_getpage(t, h->h_nextpg) == RET_ERROR)
+                               return (RET_ERROR);
+                       h = t->bt_curpage;
+               } while (NEXTINDEX(h) == 0 && h->h_nextpg != P_NONE);
+
+               item->bti_pgno = h->h_pgno;
+               item->bti_index = 0;
+
+               if (!VALIDITEM(t, item)
+                   || _bt_cmp(t, key->data, item->bti_index) != 0)
+                       break;
+       }
+
+       /* flush changes to disk */
+       if (ISDISK(t)) {
+               if (h->h_flags & F_DIRTY) {
+                       if (_bt_write(t, t->bt_curpage, NORELEASE) == RET_ERROR)
+                               return (RET_ERROR);
+               }
+       }
+
+       if (ndel == 0)
+               return (RET_SPECIAL);
+
+       return (RET_SUCCESS);
+}
+
+/*
+ *  BT_SYNC -- sync the btree to disk.
+ *
+ *     Parameters:
+ *             tree -- btree to sync.
+ *
+ *     Returns:
+ *             RET_SUCCESS, RET_ERROR.
+ */
+
+bt_sync(dbp)
+       DB *dbp;
+{
+       BTREE_P t;
+       BTHEADER *h;
+       pgno_t pgno;
+
+       t = (BTREE_P)dbp->internal;
+
+       /* if this is an in-memory btree, syncing is a no-op */
+       if (!ISDISK(t))
+               return (RET_SUCCESS);
+
+       h = (BTHEADER *) t->bt_curpage;
+       h->h_flags &= ~F_DIRTY;
+
+       if (ISCACHE(t)) {
+               pgno = t->bt_curpage->h_pgno;
+               if (_bt_write(t, h, RELEASE) == RET_ERROR)
+                       return(RET_ERROR);
+               if (lrusync(t->bt_s.bt_d.d_cache) < RET_ERROR)
+                       return (RET_ERROR);
+               if (_bt_getpage(t, pgno) == RET_ERROR)
+                       return (RET_ERROR);
+       } else {
+               if (_bt_write(t, h, NORELEASE) == RET_ERROR)
+                       return (RET_ERROR);
+       }
+
+       return (fsync(t->bt_s.bt_d.d_fd));
+}
+
+/*
+ *  BT_SEQ -- Sequential scan interface.
+ *
+ *     This routine supports sequential scans on the btree.  If called with
+ *     flags set to R_CURSOR, or if no seq scan has been initialized in the
+ *     current tree, we initialize the scan.  Otherwise, we advance the scan
+ *     and return the next item.
+ *
+ *     Scans can be either keyed or non-keyed.  Keyed scans basically have
+ *     a starting point somewhere in the middle of the tree.  Non-keyed
+ *     scans start at an endpoint.  Also, scans can be backward (descending
+ *     order), forward (ascending order), or no movement (keep returning
+ *     the same item).
+ *
+ *     Flags is checked every time we enter the routine, so the user can
+ *     change directions on an active scan if desired.  The key argument
+ *     is examined only when we initialize the scan, in order to position
+ *     it properly.
+ *
+ *     Items are returned via the key and data arguments passed in.
+ *
+ *     Parameters:
+ *             tree -- btree in which to do scan
+ *             key -- key, used to position scan on initialization, and
+ *                    used to return key components to the user.
+ *             data -- used to return data components to the user.
+ *             flags -- specify R_CURSOR, R_FIRST, R_LAST, R_NEXT, or
+ *                      R_PREV.
+ *
+ *     Returns:
+ *             RET_SUCCESS, RET_ERROR, or RET_SPECIAL if no more data
+ *             exists in the tree in the specified direction.
+ *
+ *     Side Effects:
+ *             Changes the btree's notion of the current position in the
+ *             scan.
+ *
+ *     Warnings:
+ *             The key and data items returned are static and will be
+ *             overwritten by the next bt_get or bt_seq.
+ */
+
+int
+bt_seq(dbp, key, data, flags)
+       DB *dbp;
+       DBT *key, *data;
+       u_long flags;
+{
+       BTREE_P t;
+       BTHEADER *h;
+       DATUM *d;
+       int status;
+
+       t = (BTREE_P)dbp->internal;
+
+       /* do we need to initialize the scan? */
+       if (flags == R_CURSOR || flags == R_LAST || flags == R_FIRST
+           || !(t->bt_flags & BTF_SEQINIT)) {
+
+               /* initialize it */
+               status = _bt_seqinit(t, key, flags);
+       } else {
+               /* just advance the current scan pointer */
+               status = _bt_seqadvance(t, flags);
+       }
+
+       key->size = data->size = 0;
+       key->data = data->data = (u_char *) NULL;
+
+       h = t->bt_curpage;
+
+       /* is there a valid item at the current scan location? */
+       if (status == RET_SPECIAL) {
+               if (flags == R_NEXT) {
+                       if (t->bt_cursor.c_index >= NEXTINDEX(h)) {
+                               if (NEXTINDEX(h) > 0)
+                                       t->bt_cursor.c_index = NEXTINDEX(h) - 1;
+                               else
+                                       t->bt_cursor.c_index = 0;
+                       }
+               } else {
+                       t->bt_cursor.c_index = 0;
+               }
+               return (RET_SPECIAL);
+       } else if (status == RET_ERROR)
+               return (RET_ERROR);
+
+       /* okay, return the data */
+       d = (DATUM *) GETDATUM(h, t->bt_cursor.c_index);
+
+       return (_bt_buildret(t, d, data, key));
+}
+
+/*
+ *  BT_CLOSE -- Close a btree
+ *
+ *     Parameters:
+ *             tree -- tree to close
+ *
+ *     Returns:
+ *             RET_SUCCESS, RET_ERROR.
+ *
+ *     Side Effects:
+ *             Frees space occupied by the tree.
+ */
+
+int
+bt_close(dbp)
+       DB *dbp;
+{
+       struct HTBUCKET *b, *sb;
+       BTREE_P t;
+       BTHEADER *h;
+       HTABLE ht;
+       int fd, i;
+       char *cache;
+
+       t = (BTREE_P)dbp->internal;
+
+       if (t->bt_cursor.c_key != (char *) NULL)
+               (void) free(t->bt_cursor.c_key);
+
+       if (!ISDISK(t)) {
+               /* in-memory tree, release hash table memory */
+               ht = t->bt_s.bt_ht;
+               for (i = 0; i < HTSIZE; i++) {
+                       if ((b = ht[i]) == (struct HTBUCKET *) NULL)
+                               break;
+                       do {
+                               sb = b;
+                               (void) free((char *) b->ht_page);
+                               b = b->ht_next;
+                               (void) free((char *) sb);
+                       } while (b != (struct HTBUCKET *) NULL);
+               }
+               (void) free ((char *) ht);
+               (void) free ((char *) t);
+               return (RET_SUCCESS);
+       }
+
+       if ((t->bt_flags & BTF_ISWRITE) && !(t->bt_flags & BTF_METAOK)) {
+               if (_bt_wrtmeta(t) == RET_ERROR)
+                       return (RET_ERROR);
+       }
+
+       if (t->bt_curpage != (BTHEADER *) NULL) {
+               h = t->bt_curpage;
+               if (h->h_flags & F_DIRTY) {
+                       if (_bt_write(t, h, RELEASE) == RET_ERROR)
+                               return (RET_ERROR);
+               } else {
+                       if (_bt_release(t, h) == RET_ERROR)
+                               return (RET_ERROR);
+               }
+
+               /* flush and free the cache, if there is one */
+               if (ISCACHE(t)) {
+                       cache = t->bt_s.bt_d.d_cache;
+                       if (lrusync(cache) == RET_ERROR)
+                               return (RET_ERROR);
+                       lrufree(cache);
+               }
+               (void) free ((char *) h);
+       }
+
+       fd = t->bt_s.bt_d.d_fd;
+       (void) free ((char *) t);
+       return (close(fd));
+}