bt_recno.c

Go to the documentation of this file.
00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1997, 1998, 1999, 2000
00005  *      Sleepycat Software.  All rights reserved.
00006  */
00007 
00008 #include "config.h"
00009 
00010 #ifndef lint
00011 static const char revid[] = "$Id: bt__recno_8c-source.html,v 1.1 2008/06/08 10:13:41 sebdiaz Exp $";
00012 #endif /* not lint */
00013 
00014 #ifndef NO_SYSTEM_INCLUDES
00015 #include <sys/types.h>
00016 
00017 #include <errno.h>
00018 #include <limits.h>
00019 #include <string.h>
00020 #endif
00021 
00022 #include "db_int.h"
00023 #include "db_page.h"
00024 #include "btree.h"
00025 #include "db_ext.h"
00026 #include "db_shash.h"
00027 #include "lock.h"
00028 #include "lock_ext.h"
00029 #include "qam.h"
00030 
00031 static int  __ram_add __P((DBC *, db_recno_t *, DBT *, u_int32_t, u_int32_t));
00032 static void __ram_ca __P((DBC *, db_recno_t, ca_recno_arg));
00033 static int  __ram_delete __P((DB *, DB_TXN *, DBT *, u_int32_t));
00034 static int  __ram_fmap __P((DBC *, db_recno_t));
00035 static int  __ram_put __P((DB *, DB_TXN *, DBT *, DBT *, u_int32_t));
00036 static int  __ram_source __P((DB *));
00037 static int  __ram_update __P((DBC *, db_recno_t, int));
00038 static int  __ram_vmap __P((DBC *, db_recno_t));
00039 
00040 /*
00041  * In recno, there are two meanings to the on-page "deleted" flag.  If we're
00042  * re-numbering records, it means the record was implicitly created.  We skip
00043  * over implicitly created records if doing a cursor "next" or "prev", and
00044  * return DB_KEYEMPTY if they're explicitly requested..  If not re-numbering
00045  * records, it means that the record was implicitly created, or was deleted.
00046  * We skip over implicitly created or deleted records if doing a cursor "next"
00047  * or "prev", and return DB_KEYEMPTY if they're explicitly requested.
00048  *
00049  * If we're re-numbering records, then we have to detect in the cursor that
00050  * a record was deleted, and adjust the cursor as necessary on the next get.
00051  * If we're not re-numbering records, then we can detect that a record has
00052  * been deleted by looking at the actual on-page record, so we completely
00053  * ignore the cursor's delete flag.  This is different from the B+tree code.
00054  * It also maintains whether the cursor references a deleted record in the
00055  * cursor, and it doesn't always check the on-page value.
00056  */
00057 #define CD_SET(dbp, cp) {                                               \
00058         if (F_ISSET(cp, C_RENUMBER))                                    \
00059                 F_SET(cp, C_DELETED);                                   \
00060 }
00061 #define CD_CLR(dbp, cp) {                                               \
00062         if (F_ISSET(cp, C_RENUMBER))                                    \
00063                 F_CLR(cp, C_DELETED);                                   \
00064 }
00065 #define CD_ISSET(dbp, cp)                                               \
00066         (F_ISSET(cp, C_RENUMBER) && F_ISSET(cp, C_DELETED))
00067 
00068 /*
00069  * CDB___ram_open --
00070  *      Recno open function.
00071  *
00072  * PUBLIC: int CDB___ram_open __P((DB *, const char *, db_pgno_t, u_int32_t));
00073  */
00074 int
00075 CDB___ram_open(dbp, name, base_pgno, flags)
00076         DB *dbp;
00077         const char *name;
00078         db_pgno_t base_pgno;
00079         u_int32_t flags;
00080 {
00081         BTREE *t;
00082         DBC *dbc;
00083         int ret, t_ret;
00084 
00085         t = dbp->bt_internal;
00086 
00087         /* Initialize the remaining fields/methods of the DB. */
00088         dbp->del = __ram_delete;
00089         dbp->put = __ram_put;
00090         dbp->stat = CDB___bam_stat;
00091 
00092         /* Start up the tree. */
00093         if ((ret = CDB___bam_read_root(dbp, name, base_pgno, flags)) != 0)
00094                 goto err;
00095 
00096         /*
00097          * If the user specified a source tree, open it and map it in.
00098          *
00099          * !!!
00100          * We don't complain if the user specified transactions or threads.
00101          * It's possible to make it work, but you'd better know what you're
00102          * doing!
00103          */
00104         if (t->re_source != NULL && (ret = __ram_source(dbp)) != 0)
00105                 goto err;
00106 
00107         /* If we're snapshotting an underlying source file, do it now. */
00108         if (F_ISSET(dbp, DB_RE_SNAPSHOT)) {
00109                 /* Allocate a cursor. */
00110                 if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0)
00111                         goto err;
00112 
00113                 /* Do the snapshot. */
00114                 if ((ret = __ram_update(dbc,
00115                     DB_MAX_RECORDS, 0)) != 0 && ret == DB_NOTFOUND)
00116                         ret = 0;
00117 
00118                 /* Discard the cursor. */
00119                 if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0)
00120                         ret = t_ret;
00121 
00122                 if (ret != 0)
00123                         goto err;
00124         }
00125 
00126         return (0);
00127 
00128 err:    /* If we mmap'd a source file, discard it. */
00129         if (t->re_smap != NULL)
00130                 (void)CDB___os_unmapfile(dbp->dbenv, t->re_smap, t->re_msize);
00131 
00132         /* If we opened a source file, discard it. */
00133         if (F_ISSET(&t->re_fh, DB_FH_VALID))
00134                 (void)CDB___os_closehandle(&t->re_fh);
00135         if (t->re_source != NULL)
00136                 CDB___os_freestr(t->re_source);
00137 
00138         return (ret);
00139 }
00140 
00141 /*
00142  * __ram_delete --
00143  *      Recno db->del function.
00144  */
00145 static int
00146 __ram_delete(dbp, txn, key, flags)
00147         DB *dbp;
00148         DB_TXN *txn;
00149         DBT *key;
00150         u_int32_t flags;
00151 {
00152         BTREE_CURSOR *cp;
00153         DBC *dbc;
00154         db_recno_t recno;
00155         int ret, t_ret;
00156 
00157         PANIC_CHECK(dbp->dbenv);
00158 
00159         /* Check for invalid flags. */
00160         if ((ret = CDB___db_delchk(dbp,
00161             key, flags, F_ISSET(dbp, DB_AM_RDONLY))) != 0)
00162                 return (ret);
00163 
00164         /* Acquire a cursor. */
00165         if ((ret = dbp->cursor(dbp, txn, &dbc, DB_WRITELOCK)) != 0)
00166                 return (ret);
00167 
00168         DEBUG_LWRITE(dbc, txn, "ram_delete", key, NULL, flags);
00169 
00170         /* Check the user's record number and fill in as necessary. */
00171         if ((ret = CDB___ram_getno(dbc, key, &recno, 0)) != 0)
00172                 goto err;
00173 
00174         /* Do the delete. */
00175         cp = (BTREE_CURSOR *)dbc->internal;
00176         cp->recno = recno;
00177 
00178         ret = CDB___ram_c_del(dbc);
00179 
00180         /* Release the cursor. */
00181 err:    if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0)
00182                 ret = t_ret;
00183 
00184         return (ret);
00185 }
00186 
00187 /*
00188  * __ram_put --
00189  *      Recno db->put function.
00190  */
00191 static int
00192 __ram_put(dbp, txn, key, data, flags)
00193         DB *dbp;
00194         DB_TXN *txn;
00195         DBT *key, *data;
00196         u_int32_t flags;
00197 {
00198         DBC *dbc;
00199         db_recno_t recno;
00200         int ret, t_ret;
00201 
00202         PANIC_CHECK(dbp->dbenv);
00203 
00204         /* Check for invalid flags. */
00205         if ((ret = CDB___db_putchk(dbp,
00206             key, data, flags, F_ISSET(dbp, DB_AM_RDONLY), 0)) != 0)
00207                 return (ret);
00208 
00209         /* Allocate a cursor. */
00210         if ((ret = dbp->cursor(dbp, txn, &dbc, DB_WRITELOCK)) != 0)
00211                 return (ret);
00212 
00213         DEBUG_LWRITE(dbc, txn, "ram_put", key, data, flags);
00214 
00215         /*
00216          * If we're appending to the tree, make sure we've read in all of
00217          * the backing source file.  Otherwise, check the user's record
00218          * number and fill in as necessary.
00219          */
00220         if (flags == DB_APPEND) {
00221                 if ((ret = __ram_update(
00222                     dbc, DB_MAX_RECORDS, 0)) != 0 && ret == DB_NOTFOUND)
00223                         ret = 0;
00224         } else
00225                 ret = CDB___ram_getno(dbc, key, &recno, 1);
00226 
00227         /* Add the record. */
00228         if (ret == 0)
00229                 ret = __ram_add(dbc, &recno, data, flags, 0);
00230 
00231         /* Discard the cursor. */
00232         if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0)
00233                 ret = t_ret;
00234 
00235         /* Return the record number if we're appending to the tree. */
00236         if (ret == 0 && flags == DB_APPEND)
00237                 ret = CDB___db_retcopy(dbp, key, &recno, sizeof(recno),
00238                     &dbc->rkey.data, &dbc->rkey.ulen);
00239 
00240         return (ret);
00241 }
00242 
00243 /*
00244  * CDB___ram_c_del --
00245  *      Recno cursor->c_del function.
00246  *
00247  * PUBLIC: int CDB___ram_c_del __P((DBC *));
00248  */
00249 int
00250 CDB___ram_c_del(dbc)
00251         DBC *dbc;
00252 {
00253         BKEYDATA bk;
00254         BTREE *t;
00255         BTREE_CURSOR *cp;
00256         DB *dbp;
00257         DBT hdr, data;
00258         EPG *epg;
00259         int exact, ret, stack;
00260 
00261         dbp = dbc->dbp;
00262         cp = (BTREE_CURSOR *)dbc->internal;
00263         t = dbp->bt_internal;
00264         stack = 0;
00265 
00266         /*
00267          * The semantics of cursors during delete are as follows: if record
00268          * numbers are mutable (C_RENUMBER is set), deleting a record causes
00269          * the cursor to automatically point to the immediately following
00270          * record.  In this case it is possible to use a single cursor for
00271          * repeated delete operations, without intervening operations.
00272          *
00273          * If record numbers are not mutable, then records are replaced with
00274          * a marker containing a delete flag.  If the record referenced by
00275          * this cursor has already been deleted, we will detect that as part
00276          * of the delete operation, and fail.
00277          */
00278 
00279         /* Search the tree for the key; delete only deletes exact matches. */
00280         if ((ret = CDB___bam_rsearch(dbc, &cp->recno, S_DELETE, 1, &exact)) != 0)
00281                 goto err;
00282         if (!exact) {
00283                 ret = DB_NOTFOUND;
00284                 goto err;
00285         }
00286         stack = 1;
00287         cp->page = cp->csp->page;
00288         cp->pgno = cp->csp->page->pgno;
00289         cp->indx = cp->csp->indx;
00290 
00291         /*
00292          * If re-numbering records, the on-page deleted flag can only mean
00293          * that this record was implicitly created.  Applications aren't
00294          * permitted to delete records they never created, return an error.
00295          *
00296          * If not re-numbering records, the on-page deleted flag means that
00297          * this record was implicitly created, or, was deleted at some time.
00298          * The former is an error because applications aren't permitted to
00299          * delete records they never created, the latter is an error because
00300          * if the record was "deleted", we could never have found it.
00301          */
00302         if (B_DISSET(GET_BKEYDATA(cp->page, cp->indx)->type)) {
00303                 ret = DB_KEYEMPTY;
00304                 goto err;
00305         }
00306 
00307         if (F_ISSET(cp, C_RENUMBER)) {
00308                 /* Delete the item, adjust the counts, adjust the cursors. */
00309                 if ((ret = CDB___bam_ditem(dbc, cp->page, cp->indx)) != 0)
00310                         goto err;
00311                 CDB___bam_adjust(dbc, -1);
00312                 __ram_ca(dbc, cp->recno, CA_DELETE);
00313 
00314                 /*
00315                  * If the page is empty, delete it.
00316                  *
00317                  * We never delete a root page.  First, root pages of primary
00318                  * databases never go away, recno or otherwise.  However, if
00319                  * it's the root page of an off-page duplicates database, then
00320                  * it can be deleted.   We don't delete it here because we have
00321                  * no way of telling the primary database page holder (e.g.,
00322                  * the hash access method) that its page element should cleaned
00323                  * up because the underlying tree is gone.  So, we keep the page
00324                  * around until the last cursor referencing the empty tree is
00325                  * are closed, and then clean it up.
00326                  */
00327                 if (NUM_ENT(cp->page) == 0 && PGNO(cp->page) != cp->root) {
00328                         /*
00329                          * We already have a locked stack of pages.  However,
00330                          * there are likely entries in the stack that aren't
00331                          * going to be emptied by removing the single reference
00332                          * to the emptied page (or one of its parents).
00333                          */
00334                         for (epg = cp->sp; epg <= cp->csp; ++epg)
00335                                 if (NUM_ENT(epg->page) <= 1)
00336                                         break;
00337 
00338                         /*
00339                          * We want to delete a single item out of the last page
00340                          * that we're not deleting, back up to that page.
00341                          */
00342                         ret = CDB___bam_dpages(dbc, --epg);
00343 
00344                         /*
00345                          * Regardless of the return from CDB___bam_dpages, it will
00346                          * discard our stack and pinned page.
00347                          */
00348                         stack = 0;
00349                         cp->page = NULL;
00350                 }
00351         } else {
00352                 /* Use a delete/put pair to replace the record with a marker. */
00353                 if ((ret = CDB___bam_ditem(dbc, cp->page, cp->indx)) != 0)
00354                         goto err;
00355 
00356                 B_TSET(bk.type, B_KEYDATA, 1);
00357                 bk.len = 0;
00358                 memset(&hdr, 0, sizeof(hdr));
00359                 hdr.data = &bk;
00360                 hdr.size = SSZA(BKEYDATA, data);
00361                 memset(&data, 0, sizeof(data));
00362                 data.data = (void *)"";
00363                 data.size = 0;
00364                 if ((ret = CDB___db_pitem(dbc,
00365                     cp->page, cp->indx, BKEYDATA_SIZE(0), &hdr, &data)) != 0)
00366                         goto err;
00367         }
00368 
00369         F_SET(t, RECNO_MODIFIED);
00370 
00371 err:    if (stack)
00372                 CDB___bam_stkrel(dbc, STK_CLRDBC);
00373 
00374         return (ret);
00375 }
00376 
00377 /*
00378  * CDB___ram_c_get --
00379  *      Recno cursor->c_get function.
00380  *
00381  * PUBLIC: int CDB___ram_c_get
00382  * PUBLIC:     __P((DBC *, DBT *, DBT *, u_int32_t, db_pgno_t *));
00383  */
00384 int
00385 CDB___ram_c_get(dbc, key, data, flags, pgnop)
00386         DBC *dbc;
00387         DBT *key, *data;
00388         u_int32_t flags;
00389         db_pgno_t *pgnop;
00390 {
00391         BTREE_CURSOR *cp;
00392         DB *dbp;
00393         int cmp, exact, ret;
00394 
00395         COMPQUIET(pgnop, NULL);
00396 
00397         dbp = dbc->dbp;
00398         cp = (BTREE_CURSOR *)dbc->internal;
00399 
00400 retry:  switch (flags) {
00401         case DB_CURRENT:
00402                 /*
00403                  * If we're in a normal Recno tree with mutable record numbers,
00404                  * we need make no correction if we just deleted a record, we
00405                  * will return the record following the deleted one by virtue
00406                  * of renumbering the tree.
00407                  *
00408                  * If we're in an off-page duplicate Recno tree, we are using
00409                  * mutable records, but we don't implicitly move the cursor if
00410                  * we delete one.
00411                  */
00412                 if (F_ISSET(dbc, DBC_OPD) && F_ISSET(cp, C_DELETED))
00413                         return (DB_KEYEMPTY);
00414                 break;
00415         case DB_NEXT_DUP:
00416                 /*
00417                  * If we're not in an off-page dup set, we know there's no
00418                  * next duplicate since recnos don't have them.  If we
00419                  * are in an off-page dup set, the next item assuredly is
00420                  * a dup, so we set flags to DB_NEXT and keep going.
00421                  */
00422                 if (!F_ISSET(dbc, DBC_OPD))
00423                         return (DB_NOTFOUND);
00424                 /* FALLTHROUGH */
00425         case DB_NEXT_NODUP:
00426                 /*
00427                  * Recno databases don't have duplicates, set flags to DB_NEXT
00428                  * and keep going.
00429                  */
00430                 /* FALLTHROUGH */
00431         case DB_NEXT:
00432                 flags = DB_NEXT;
00433                 /*
00434                  * If record numbers are mutable: if we just deleted a record,
00435                  * we have to avoid incrementing the record number so that we
00436                  * return the right record by virtue of renumbering the tree.
00437                  */
00438                 if (CD_ISSET(dbp, cp))
00439                         break;
00440 
00441                 if (cp->recno != RECNO_OOB) {
00442                         ++cp->recno;
00443                         break;
00444                 }
00445                 /* FALLTHROUGH */
00446         case DB_FIRST:
00447                 flags = DB_NEXT;
00448                 cp->recno = 1;
00449                 break;
00450         case DB_PREV_NODUP:
00451                 /*
00452                  * Recno databases don't have duplicates, set flags to DB_PREV
00453                  * and keep going.
00454                  */
00455                 /* FALLTHROUGH */
00456         case DB_PREV:
00457                 flags = DB_PREV;
00458                 if (cp->recno != RECNO_OOB) {
00459                         if (cp->recno == 1) {
00460                                 ret = DB_NOTFOUND;
00461                                 goto err;
00462                         }
00463                         --cp->recno;
00464                         break;
00465                 }
00466                 /* FALLTHROUGH */
00467         case DB_LAST:
00468                 flags = DB_PREV;
00469                 if (((ret = __ram_update(dbc,
00470                     DB_MAX_RECORDS, 0)) != 0) && ret != DB_NOTFOUND)
00471                         goto err;
00472                 if ((ret = CDB___bam_nrecs(dbc, &cp->recno)) != 0)
00473                         goto err;
00474                 if (cp->recno == 0) {
00475                         ret = DB_NOTFOUND;
00476                         goto err;
00477                 }
00478                 break;
00479         case DB_GET_BOTHC:
00480                 /*
00481                  * If we're doing a join and these are offpage dups,
00482                  * we want to keep searching forward from after the
00483                  * current cursor position.  Increment the recno by 1,
00484                  * then proceed as for a DB_SET.
00485                  *
00486                  * Otherwise, we know there are no additional matching
00487                  * data, as recnos don't have dups.  return DB_NOTFOUND.
00488                  */
00489                 if (F_ISSET(dbc, DBC_OPD)) {
00490                         cp->recno++;
00491                         break;
00492                 } else
00493                         ret = DB_NOTFOUND;
00494                         goto err;
00495                 /* NOTREACHED */
00496         case DB_GET_BOTH:
00497                 /*
00498                  * If we're searching a set of off-page dups, we start
00499                  * a new linear search from the first record.  Otherwise,
00500                  * we compare the single data item associated with the
00501                  * requested record for a match.
00502                  */
00503                 if (F_ISSET(dbc, DBC_OPD)) {
00504                         cp->recno = 1;
00505                         break;
00506                 }
00507                 /* FALLTHROUGH */
00508         case DB_SET:
00509         case DB_SET_RANGE:
00510                 if ((ret = CDB___ram_getno(dbc, key, &cp->recno, 0)) != 0)
00511                         goto err;
00512                 break;
00513         default:
00514                 ret = CDB___db_unknown_flag(dbp->dbenv, "CDB___ram_c_get", flags);
00515                 goto err;
00516         }
00517 
00518         /*
00519          * For DB_PREV, DB_LAST, DB_SET and DB_SET_RANGE, we have already
00520          * called __ram_update() to make sure sufficient records have been
00521          * read from the backing source file.  Do it now for DB_CURRENT (if
00522          * the current record was deleted we may need more records from the
00523          * backing file for a DB_CURRENT operation), DB_FIRST and DB_NEXT.
00524          */
00525         if ((flags == DB_NEXT || flags == DB_CURRENT) && ((ret =
00526             __ram_update(dbc, cp->recno, 0)) != 0) && ret != DB_NOTFOUND)
00527                 goto err;
00528 
00529         for (;; ++cp->recno) {
00530                 /* Search the tree for the record. */
00531                 if ((ret = CDB___bam_rsearch(dbc, &cp->recno,
00532                     F_ISSET(dbc, DBC_RMW) ? S_FIND_WR : S_FIND,
00533                     1, &exact)) != 0)
00534                         goto err;
00535                 if (!exact) {
00536                         ret = DB_NOTFOUND;
00537                         goto err;
00538                 }
00539 
00540                 /*
00541                  * Copy the page into the cursor, discarding any lock we
00542                  * are currently holding.
00543                  */
00544                 cp->page = cp->csp->page;
00545                 cp->pgno = cp->csp->page->pgno;
00546                 cp->indx = cp->csp->indx;
00547                 (void)__TLPUT(dbc, cp->lock);
00548                 cp->lock = cp->csp->lock;
00549                 cp->lock_mode = cp->csp->lock_mode;
00550 
00551                 /*
00552                  * If re-numbering records, the on-page deleted flag means this
00553                  * record was implicitly created.  If not re-numbering records,
00554                  * the on-page deleted flag means this record was implicitly
00555                  * created, or, it was deleted at some time.  Regardless, we
00556                  * skip such records if doing cursor next/prev operations or
00557                  * walking through off-page duplicates, and fail if they were
00558                  * requested explicitly by the application.
00559                  */
00560                 if (B_DISSET(GET_BKEYDATA(cp->page, cp->indx)->type))
00561                         switch (flags) {
00562                         case DB_NEXT:
00563                         case DB_PREV:
00564                                 (void)CDB___bam_stkrel(dbc, STK_CLRDBC);
00565                                 goto retry;
00566                         case DB_GET_BOTH:
00567                                 (void)CDB___bam_stkrel(dbc, STK_CLRDBC);
00568                                 continue;
00569                         default:
00570                                 ret = DB_KEYEMPTY;
00571                                 goto err;
00572                         }
00573 
00574                 if (flags == DB_GET_BOTH || flags == DB_GET_BOTHC) {
00575                         if ((ret = CDB___bam_cmp(dbp, data,
00576                             cp->page, cp->indx, CDB___bam_defcmp, &cmp)) != 0)
00577                                 return (ret);
00578                         if (cmp == 0)
00579                                 break;
00580                         if (!F_ISSET(dbc, DBC_OPD)) {
00581                                 ret = DB_NOTFOUND;
00582                                 goto err;
00583                         }
00584                         (void)CDB___bam_stkrel(dbc, STK_CLRDBC);
00585                 } else
00586                         break;
00587         }
00588 
00589         /* Return the key if the user didn't give us one. */
00590         if (!F_ISSET(dbc, DBC_OPD)) {
00591                 if (flags != DB_SET && flags != DB_SET_RANGE)
00592                         ret = CDB___db_retcopy(dbp,
00593                              key, &cp->recno, sizeof(cp->recno),
00594                              &dbc->rkey.data, &dbc->rkey.ulen);
00595                 F_SET(key, DB_DBT_ISSET);
00596         }
00597 
00598         /* The cursor was reset, no further delete adjustment is necessary. */
00599 err:    CD_CLR(dbp, cp);
00600 
00601         return (ret);
00602 }
00603 
00604 /*
00605  * CDB___ram_c_put --
00606  *      Recno cursor->c_put function.
00607  *
00608  * PUBLIC: int CDB___ram_c_put __P((DBC *, DBT *, DBT *, u_int32_t, db_pgno_t *));
00609  */
00610 int
00611 CDB___ram_c_put(dbc, key, data, flags, pgnop)
00612         DBC *dbc;
00613         DBT *key, *data;
00614         u_int32_t flags;
00615         db_pgno_t *pgnop;
00616 {
00617         BTREE_CURSOR *cp;
00618         DB *dbp;
00619         int exact, ret, t_ret;
00620         void *arg;
00621 
00622         COMPQUIET(pgnop, NULL);
00623 
00624         dbp = dbc->dbp;
00625         cp = (BTREE_CURSOR *)dbc->internal;
00626 
00627         /*
00628          * DB_KEYFIRST and DB_KEYLAST will only be set if we're dealing with
00629          * an off-page duplicate tree, they can't be specified at user level.
00630          * Translate them into something else.
00631          */
00632         switch (flags) {
00633         case DB_KEYFIRST:
00634                 cp->recno = 1;
00635                 flags = DB_BEFORE;
00636                 break;
00637         case DB_KEYLAST:
00638                 return (__ram_add(dbc, &cp->recno, data, DB_APPEND, 0));
00639         }
00640 
00641 split:  if ((ret = CDB___bam_rsearch(dbc, &cp->recno, S_INSERT, 1, &exact)) != 0)
00642                 goto err;
00643         if (!exact) {
00644                 ret = DB_NOTFOUND;
00645                 goto err;
00646         }
00647         cp->page = cp->csp->page;
00648         cp->pgno = cp->csp->page->pgno;
00649         cp->indx = cp->csp->indx;
00650 
00651         ret = CDB___bam_iitem(dbc, key, data, flags, 0);
00652         t_ret = CDB___bam_stkrel(dbc, STK_CLRDBC);
00653 
00654         if (t_ret != 0 && (ret == 0 || ret == DB_NEEDSPLIT))
00655                 ret = t_ret;
00656         else if (ret == DB_NEEDSPLIT) {
00657                 arg = &cp->recno;
00658                 if ((ret = CDB___bam_split(dbc, arg)) != 0)
00659                         goto err;
00660                 goto split;
00661         }
00662         if (ret != 0)
00663                 goto err;
00664 
00665         switch (flags) {                        /* Adjust the cursors. */
00666         case DB_AFTER:
00667                 __ram_ca(dbc, cp->recno, CA_IAFTER);
00668                 ++cp->recno;
00669                 break;
00670         case DB_BEFORE:
00671                 __ram_ca(dbc, cp->recno, CA_IBEFORE);
00672                 --cp->recno;
00673                 break;
00674         }
00675 
00676         /* Return the key if we've created a new record. */
00677         if (!F_ISSET(dbc, DBC_OPD) && (flags == DB_AFTER || flags == DB_BEFORE))
00678                 ret = CDB___db_retcopy(dbp, key, &cp->recno,
00679                     sizeof(cp->recno), &dbc->rkey.data, &dbc->rkey.ulen);
00680 
00681         /* The cursor was reset, no further delete adjustment is necessary. */
00682 err:    CD_CLR(dbp, cp);
00683 
00684         return (ret);
00685 }
00686 
00687 /*
00688  * __ram_ca --
00689  *      Adjust cursors.
00690  */
00691 static void
00692 __ram_ca(dbc_arg, recno, op)
00693         DBC *dbc_arg;
00694         db_recno_t recno;
00695         ca_recno_arg op;
00696 {
00697         BTREE_CURSOR *cp;
00698         DB *dbp;
00699         DBC *dbc;
00700         db_recno_t nrecs;
00701 
00702         dbp = dbc_arg->dbp;
00703 
00704         /*
00705          * Adjust the cursors.  See the comment in CDB___bam_ca_delete().
00706          */
00707         MUTEX_THREAD_LOCK(dbp->mutexp);
00708         for (dbc = TAILQ_FIRST(&dbp->active_queue);
00709             dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
00710                 cp = (BTREE_CURSOR *)dbc->internal;
00711                 if (dbc_arg->internal->root != cp->root)
00712                         continue;
00713 
00714                 switch (op) {
00715                 case CA_DELETE:
00716                         if (recno < cp->recno)
00717                                 --cp->recno;
00718                         if (recno == cp->recno) {
00719                                 /*
00720                                  * If we're in an off-page duplicate Recno tree,
00721                                  * we are using mutable records, but we don't
00722                                  * implicitly move the cursor if we delete one.
00723                                  */
00724                                 if (!F_ISSET(dbc, DBC_OPD) &&
00725                                     CDB___bam_nrecs(dbc, &nrecs) == 0 &&
00726                                     recno > nrecs)
00727                                         --cp->recno;
00728                                 else
00729                                         CD_SET(dbp, cp);
00730                         }
00731                         break;
00732                 case CA_IAFTER:
00733                         if (recno < cp->recno)
00734                                 ++cp->recno;
00735                         break;
00736                 case CA_IBEFORE:
00737                         if (recno <= cp->recno)
00738                                 ++cp->recno;
00739                         break;
00740                 }
00741         }
00742         MUTEX_THREAD_UNLOCK(dbp->mutexp);
00743 }
00744 
00745 /*
00746  * CDB___ram_getno --
00747  *      Check the user's record number, and make sure we've seen it.
00748  *
00749  * PUBLIC: int CDB___ram_getno __P((DBC *, const DBT *, db_recno_t *, int));
00750  */
00751 int
00752 CDB___ram_getno(dbc, key, rep, can_create)
00753         DBC *dbc;
00754         const DBT *key;
00755         db_recno_t *rep;
00756         int can_create;
00757 {
00758         DB *dbp;
00759         db_recno_t recno;
00760 
00761         dbp = dbc->dbp;
00762 
00763         /* Check the user's record number. */
00764         if ((recno = *(db_recno_t *)key->data) == 0) {
00765                 CDB___db_err(dbp->dbenv, "illegal record number of 0");
00766                 return (EINVAL);
00767         }
00768         if (rep != NULL)
00769                 *rep = recno;
00770 
00771         /*
00772          * Btree can neither create records nor read them in.  Recno can
00773          * do both, see if we can find the record.
00774          */
00775         return (dbc->dbtype == DB_RECNO ?
00776             __ram_update(dbc, recno, can_create) : 0);
00777 }
00778 
00779 /*
00780  * __ram_update --
00781  *      Ensure the tree has records up to and including the specified one.
00782  */
00783 static int
00784 __ram_update(dbc, recno, can_create)
00785         DBC *dbc;
00786         db_recno_t recno;
00787         int can_create;
00788 {
00789         BTREE *t;
00790         BTREE_CURSOR *cp;
00791         DB *dbp;
00792         db_recno_t nrecs;
00793         int ret;
00794 
00795         dbp = dbc->dbp;
00796         cp = (BTREE_CURSOR *)dbc->internal;
00797         t = dbp->bt_internal;
00798 
00799         /*
00800          * If we can't create records and we've read the entire backing input
00801          * file, we're done.
00802          */
00803         if (!can_create && !F_ISSET(t, RECNO_READFILE))
00804                 return (0);
00805 
00806         /*
00807          * If we haven't seen this record yet, try to get it from the original
00808          * file.
00809          */
00810         if ((ret = CDB___bam_nrecs(dbc, &nrecs)) != 0)
00811                 return (ret);
00812         if (F_ISSET(t, RECNO_READFILE) && recno > nrecs) {
00813                 if ((ret = t->re_irec(dbc, recno)) != 0)
00814                         return (ret);
00815                 if ((ret = CDB___bam_nrecs(dbc, &nrecs)) != 0)
00816                         return (ret);
00817         }
00818 
00819         /*
00820          * If we can create records, create empty ones up to the requested
00821          * record.
00822          */
00823         if (!can_create || recno <= nrecs + 1)
00824                 return (0);
00825 
00826         dbc->rdata.dlen = 0;
00827         dbc->rdata.doff = 0;
00828         dbc->rdata.flags = 0;
00829         if (F_ISSET(dbp, DB_RE_FIXEDLEN)) {
00830                 if (dbc->rdata.ulen < t->re_len) {
00831                         if ((ret = CDB___os_realloc(dbp->dbenv,
00832                             t->re_len, NULL, &dbc->rdata.data)) != 0) {
00833                                 dbc->rdata.ulen = 0;
00834                                 dbc->rdata.data = NULL;
00835                                 return (ret);
00836                         }
00837                         dbc->rdata.ulen = t->re_len;
00838                 }
00839                 dbc->rdata.size = t->re_len;
00840                 memset(dbc->rdata.data, t->re_pad, t->re_len);
00841         } else
00842                 dbc->rdata.size = 0;
00843 
00844         while (recno > ++nrecs)
00845                 if ((ret = __ram_add(dbc,
00846                     &nrecs, &dbc->rdata, 0, BI_DELETED)) != 0)
00847                         return (ret);
00848         return (0);
00849 }
00850 
00851 /*
00852  * __ram_source --
00853  *      Load information about the backing file.
00854  */
00855 static int
00856 __ram_source(dbp)
00857         DB *dbp;
00858 {
00859         BTREE *t;
00860         size_t size;
00861         u_int32_t bytes, mbytes;
00862         char *source;
00863         int ret;
00864 
00865         t = dbp->bt_internal;
00866         source = t->re_source;
00867 
00868         /*
00869          * !!!
00870          * The caller has full responsibility for cleaning up on error --
00871          * (it has to anyway, in case it fails after this routine succeeds).
00872          */
00873         ret = CDB___db_appname(dbp->dbenv,
00874             DB_APP_DATA, NULL, source, 0, NULL, &t->re_source);
00875 
00876         CDB___os_freestr(source);
00877 
00878         if (ret != 0)
00879                 return (ret);
00880 
00881         /*
00882          * !!!
00883          * It's possible that the backing source file is read-only.  We don't
00884          * much care other than we'll complain if there are any modifications
00885          * when it comes time to write the database back to the source.
00886          */
00887         ret = CDB___os_open(dbp->dbenv, t->re_source,
00888             F_ISSET(dbp, DB_AM_RDONLY) ? DB_OSO_RDONLY : 0, 0, &t->re_fh);
00889         if (ret != 0 && !F_ISSET(dbp, DB_AM_RDONLY))
00890                 ret = CDB___os_open(dbp->dbenv,
00891                     t->re_source, DB_OSO_RDONLY, 0, &t->re_fh);
00892         if (ret != 0) {
00893                 CDB___db_err(dbp->dbenv, "%s: %s", t->re_source, CDB_db_strerror(ret));
00894                 return (ret);
00895         }
00896 
00897         /*
00898          * !!!
00899          * We'd like to test to see if the file is too big to mmap.  Since we
00900          * don't know what size or type off_t's or size_t's are, or the largest
00901          * unsigned integral type is, or what random insanity the local C
00902          * compiler will perpetrate, doing the comparison in a portable way is
00903          * flatly impossible.  Hope that mmap fails if the file is too large.
00904          */
00905         if ((ret = CDB___os_ioinfo(dbp->dbenv, t->re_source,
00906             &t->re_fh, &mbytes, &bytes, NULL)) != 0) {
00907                 CDB___db_err(dbp->dbenv, "%s: %s", t->re_source, CDB_db_strerror(ret));
00908                 return (ret);
00909         }
00910         if (mbytes == 0 && bytes == 0)
00911                 return (0);
00912 
00913         size = mbytes * MEGABYTE + bytes;
00914         if ((ret = CDB___os_mapfile(dbp->dbenv, t->re_source,
00915             &t->re_fh, (size_t)size, 1, &t->re_smap)) != 0)
00916                 return (ret);
00917         t->re_cmap = t->re_smap;
00918         t->re_emap = (u_int8_t *)t->re_smap + (t->re_msize = size);
00919         t->re_irec = F_ISSET(dbp, DB_RE_FIXEDLEN) ?  __ram_fmap : __ram_vmap;
00920         F_SET(t, RECNO_READFILE);
00921         return (0);
00922 }
00923 
00924 /*
00925  * CDB___ram_writeback --
00926  *      Rewrite the backing file.
00927  *
00928  * PUBLIC: int CDB___ram_writeback __P((DB *));
00929  */
00930 int
00931 CDB___ram_writeback(dbp)
00932         DB *dbp;
00933 {
00934         BTREE *t;
00935         DB_ENV *dbenv;
00936         DB_FH fh;
00937         DBC *dbc;
00938         DBT key, data;
00939         db_recno_t keyno;
00940         size_t nw;
00941         int ret, t_ret;
00942         u_int8_t delim, *pad;
00943 
00944         t = dbp->bt_internal;
00945         dbenv = dbp->dbenv;
00946 
00947         /* If the file wasn't modified, we're done. */
00948         if (!F_ISSET(t, RECNO_MODIFIED))
00949                 return (0);
00950 
00951         /* If there's no backing source file, we're done. */
00952         if (t->re_source == NULL) {
00953                 F_CLR(t, RECNO_MODIFIED);
00954                 return (0);
00955         }
00956 
00957         /* Allocate a cursor. */
00958         if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0)
00959                 return (ret);
00960 
00961         /*
00962          * Read any remaining records into the tree.
00963          *
00964          * !!!
00965          * This is why we can't support transactions when applications specify
00966          * backing (re_source) files.  At this point we have to read in the
00967          * rest of the records from the file so that we can write all of the
00968          * records back out again, which could modify a page for which we'd
00969          * have to log changes and which we don't have locked.  This could be
00970          * partially fixed by taking a snapshot of the entire file during the
00971          * DB->open as DB->open is transaction protected.  But, if a checkpoint
00972          * occurs then, the part of the log holding the copy of the file could
00973          * be discarded, and that would make it impossible to recover in the
00974          * face of disaster.  This could all probably be fixed, but it would
00975          * require transaction protecting the backing source file, i.e. mpool
00976          * would have to know about it, and we don't want to go there.
00977          */
00978         if ((ret =
00979             __ram_update(dbc, DB_MAX_RECORDS, 0)) != 0 && ret != DB_NOTFOUND)
00980                 return (ret);
00981 
00982         /*
00983          * !!!
00984          * Close any underlying mmap region.  This is required for Windows NT
00985          * (4.0, Service Pack 2) -- if the file is still mapped, the following
00986          * open will fail.
00987          */
00988         if (t->re_smap != NULL) {
00989                 (void)CDB___os_unmapfile(dbenv, t->re_smap, t->re_msize);
00990                 t->re_smap = NULL;
00991         }
00992 
00993         /* Get rid of any backing file descriptor, just on GP's. */
00994         if (F_ISSET(&t->re_fh, DB_FH_VALID))
00995                 (void)CDB___os_closehandle(&t->re_fh);
00996 
00997         /* Open the file, truncating it. */
00998         if ((ret = CDB___os_open(dbenv,
00999             t->re_source, DB_OSO_SEQ | DB_OSO_TRUNC, 0, &fh)) != 0) {
01000                 CDB___db_err(dbenv, "%s: %s", t->re_source, CDB_db_strerror(ret));
01001                 goto err;
01002         }
01003 
01004         /*
01005          * We step through the records, writing each one out.  Use the record
01006          * number and the dbp->get() function, instead of a cursor, so we find
01007          * and write out "deleted" or non-existent records.
01008          */
01009         memset(&key, 0, sizeof(key));
01010         memset(&data, 0, sizeof(data));
01011         key.size = sizeof(db_recno_t);
01012         key.data = &keyno;
01013 
01014         /*
01015          * We'll need the delimiter if we're doing variable-length records,
01016          * and the pad character if we're doing fixed-length records.
01017          */
01018         delim = t->re_delim;
01019         if (F_ISSET(dbp, DB_RE_FIXEDLEN)) {
01020                 if ((ret = CDB___os_malloc(dbenv, t->re_len, NULL, &pad)) != 0)
01021                         goto err;
01022                 memset(pad, t->re_pad, t->re_len);
01023         } else
01024                 COMPQUIET(pad, NULL);
01025         for (keyno = 1;; ++keyno) {
01026                 switch (ret = dbp->get(dbp, NULL, &key, &data, 0)) {
01027                 case 0:
01028                         if ((ret = CDB___os_write(dbenv,
01029                             &fh, data.data, data.size, &nw)) != 0)
01030                                 goto err;
01031                         if (nw != (size_t)data.size) {
01032                                 ret = EIO;
01033                                 goto write_err;
01034                         }
01035                         break;
01036                 case DB_KEYEMPTY:
01037                         if (F_ISSET(dbp, DB_RE_FIXEDLEN)) {
01038                                 if ((ret = CDB___os_write(dbenv,
01039                                     &fh, pad, t->re_len, &nw)) != 0)
01040                                         goto write_err;
01041                                 if (nw != (size_t)t->re_len) {
01042                                         ret = EIO;
01043                                         goto write_err;
01044                                 }
01045                         }
01046                         break;
01047                 case DB_NOTFOUND:
01048                         ret = 0;
01049                         goto done;
01050                 }
01051                 if (!F_ISSET(dbp, DB_RE_FIXEDLEN)) {
01052                         if ((ret = CDB___os_write(dbenv, &fh, &delim, 1, &nw)) != 0)
01053                                 goto write_err;
01054                         if (nw != 1) {
01055                                 ret = EIO;
01056 write_err:                      CDB___db_err(dbp->dbenv,
01057                                     "Write failed to backing file");
01058                                 goto err;
01059                         }
01060                 }
01061         }
01062 
01063 err:
01064 done:   /* Close the file descriptor. */
01065         if (F_ISSET(&fh, DB_FH_VALID) &&
01066             (t_ret = CDB___os_closehandle(&fh)) != 0 && ret == 0)
01067                 ret = t_ret;
01068 
01069         /* Discard the cursor. */
01070         if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0)
01071                 ret = t_ret;
01072 
01073         if (ret == 0)
01074                 F_CLR(t, RECNO_MODIFIED);
01075 
01076         return (ret);
01077 }
01078 
01079 /*
01080  * __ram_fmap --
01081  *      Get fixed length records from a file.
01082  */
01083 static int
01084 __ram_fmap(dbc, top)
01085         DBC *dbc;
01086         db_recno_t top;
01087 {
01088         BTREE *t;
01089         BTREE_CURSOR *cp;
01090         DB *dbp;
01091         DBT data;
01092         db_recno_t recno;
01093         u_int32_t len;
01094         u_int8_t *sp, *ep, *p;
01095         int is_modified, ret;
01096 
01097         dbp = dbc->dbp;
01098         cp = (BTREE_CURSOR *)dbc->internal;
01099         t = dbp->bt_internal;
01100 
01101         if ((ret = CDB___bam_nrecs(dbc, &recno)) != 0)
01102                 return (ret);
01103 
01104         if (dbc->rdata.ulen < t->re_len) {
01105                 if ((ret = CDB___os_realloc(dbp->dbenv,
01106                     t->re_len, NULL, &dbc->rdata.data)) != 0) {
01107                         dbc->rdata.ulen = 0;
01108                         dbc->rdata.data = NULL;
01109                         return (ret);
01110                 }
01111                 dbc->rdata.ulen = t->re_len;
01112         }
01113 
01114         is_modified = F_ISSET(t, RECNO_MODIFIED);
01115 
01116         memset(&data, 0, sizeof(data));
01117         data.data = dbc->rdata.data;
01118         data.size = t->re_len;
01119 
01120         sp = (u_int8_t *)t->re_cmap;
01121         ep = (u_int8_t *)t->re_emap;
01122         while (recno < top) {
01123                 if (sp >= ep) {
01124                         F_CLR(t, RECNO_READFILE);
01125                         ret = DB_NOTFOUND;
01126                         goto err;
01127                 }
01128                 len = t->re_len;
01129                 for (p = dbc->rdata.data;
01130                     sp < ep && len > 0; *p++ = *sp++, --len)
01131                         ;
01132 
01133                 /*
01134                  * Another process may have read this record from the input
01135                  * file and stored it into the database already, in which
01136                  * case we don't need to repeat that operation.  We detect
01137                  * this by checking if the last record we've read is greater
01138                  * or equal to the number of records in the database.
01139                  *
01140                  * XXX
01141                  * We should just do a seek since the records are fixed length.
01142                  */
01143                 if (t->re_last >= recno) {
01144                         if (len != 0)
01145                                 memset(p, t->re_pad, len);
01146 
01147                         ++recno;
01148                         if ((ret = __ram_add(dbc, &recno, &data, 0, 0)) != 0)
01149                                 goto err;
01150                 }
01151                 ++t->re_last;
01152         }
01153         t->re_cmap = sp;
01154 
01155 err:    if (!is_modified)
01156                 F_CLR(t, RECNO_MODIFIED);
01157 
01158         return (0);
01159 }
01160 
01161 /*
01162  * __ram_vmap --
01163  *      Get variable length records from a file.
01164  */
01165 static int
01166 __ram_vmap(dbc, top)
01167         DBC *dbc;
01168         db_recno_t top;
01169 {
01170         BTREE *t;
01171         DBT data;
01172         db_recno_t recno;
01173         u_int8_t *sp, *ep;
01174         int delim, is_modified, ret;
01175 
01176         t = dbc->dbp->bt_internal;
01177 
01178         if ((ret = CDB___bam_nrecs(dbc, &recno)) != 0)
01179                 return (ret);
01180 
01181         delim = t->re_delim;
01182         is_modified = F_ISSET(t, RECNO_MODIFIED);
01183 
01184         memset(&data, 0, sizeof(data));
01185 
01186         sp = (u_int8_t *)t->re_cmap;
01187         ep = (u_int8_t *)t->re_emap;
01188         while (recno < top) {
01189                 if (sp >= ep) {
01190                         F_CLR(t, RECNO_READFILE);
01191                         ret = DB_NOTFOUND;
01192                         goto err;
01193                 }
01194                 for (data.data = sp; sp < ep && *sp != delim; ++sp)
01195                         ;
01196 
01197                 /*
01198                  * Another process may have read this record from the input
01199                  * file and stored it into the database already, in which
01200                  * case we don't need to repeat that operation.  We detect
01201                  * this by checking if the last record we've read is greater
01202                  * or equal to the number of records in the database.
01203                  */
01204                 if (t->re_last >= recno) {
01205                         data.size = sp - (u_int8_t *)data.data;
01206                         ++recno;
01207                         if ((ret = __ram_add(dbc, &recno, &data, 0, 0)) != 0)
01208                                 goto err;
01209                 }
01210                 ++t->re_last;
01211                 ++sp;
01212         }
01213         t->re_cmap = sp;
01214 
01215 err:    if (!is_modified)
01216                 F_CLR(t, RECNO_MODIFIED);
01217 
01218         return (ret);
01219 }
01220 
01221 /*
01222  * __ram_add --
01223  *      Add records into the tree.
01224  */
01225 static int
01226 __ram_add(dbc, recnop, data, flags, bi_flags)
01227         DBC *dbc;
01228         db_recno_t *recnop;
01229         DBT *data;
01230         u_int32_t flags, bi_flags;
01231 {
01232         BKEYDATA *bk;
01233         BTREE_CURSOR *cp;
01234         int exact, ret, stack;
01235 
01236         cp = (BTREE_CURSOR *)dbc->internal;
01237 
01238 retry:  /* Find the slot for insertion. */
01239         if ((ret = CDB___bam_rsearch(dbc, recnop,
01240             S_INSERT | (flags == DB_APPEND ? S_APPEND : 0), 1, &exact)) != 0)
01241                 return (ret);
01242         stack = 1;
01243         cp->page = cp->csp->page;
01244         cp->pgno = cp->csp->page->pgno;
01245         cp->indx = cp->csp->indx;
01246 
01247         /*
01248          * If re-numbering records, the on-page deleted flag means this record
01249          * was implicitly created.  If not re-numbering records, the on-page
01250          * deleted flag means this record was implicitly created, or, it was
01251          * deleted at some time.
01252          *
01253          * If DB_NOOVERWRITE is set and the item already exists in the tree,
01254          * return an error unless the item was either marked for deletion or
01255          * only implicitly created.
01256          */
01257         if (exact) {
01258                 bk = GET_BKEYDATA(cp->page, cp->indx);
01259                 if (!B_DISSET(bk->type) && flags == DB_NOOVERWRITE) {
01260                         ret = DB_KEYEXIST;
01261                         goto err;
01262                 }
01263         }
01264 
01265         /*
01266          * Select the arguments for CDB___bam_iitem() and do the insert.  If the
01267          * key is an exact match, or we're replacing the data item with a
01268          * new data item, replace the current item.  If the key isn't an exact
01269          * match, we're inserting a new key/data pair, before the search
01270          * location.
01271          */
01272         switch (ret = CDB___bam_iitem(dbc,
01273             NULL, data, exact ? DB_CURRENT : DB_BEFORE, bi_flags)) {
01274         case 0:
01275                 /*
01276                  * Don't adjust anything.
01277                  *
01278                  * If we inserted a record, no cursors need adjusting because
01279                  * the only new record it's possible to insert is at the very
01280                  * end of the tree.  The necessary adjustments to the internal
01281                  * page counts were made by CDB___bam_iitem().
01282                  *
01283                  * If we overwrote a record, no cursors need adjusting because
01284                  * future DBcursor->get calls will simply return the underlying
01285                  * record (there's no adjustment made for the DB_CURRENT flag
01286                  * when a cursor get operation immediately follows a cursor
01287                  * delete operation, and the normal adjustment for the DB_NEXT
01288                  * flag is still correct).
01289                  */
01290                 break;
01291         case DB_NEEDSPLIT:
01292                 /* Discard the stack of pages and split the page. */
01293                 (void)CDB___bam_stkrel(dbc, STK_CLRDBC);
01294                 stack = 0;
01295 
01296                 if ((ret = CDB___bam_split(dbc, recnop)) != 0)
01297                         goto err;
01298 
01299                 goto retry;
01300                 /* NOTREACHED */
01301         default:
01302                 goto err;
01303         }
01304 
01305 err:    if (stack)
01306                 CDB___bam_stkrel(dbc, STK_CLRDBC);
01307 
01308         return (ret);
01309 }

Generated on Sun Jun 8 10:56:35 2008 for GNUmifluz by  doxygen 1.5.5