/*-1* SPDX-License-Identifier: BSD-3-Clause2*3* Copyright (c) 1990, 1993, 19944* The Regents of the University of California. All rights reserved.5*6* This code is derived from software contributed to Berkeley by7* Mike Olson.8*9* Redistribution and use in source and binary forms, with or without10* modification, are permitted provided that the following conditions11* are met:12* 1. Redistributions of source code must retain the above copyright13* notice, this list of conditions and the following disclaimer.14* 2. Redistributions in binary form must reproduce the above copyright15* notice, this list of conditions and the following disclaimer in the16* documentation and/or other materials provided with the distribution.17* 3. Neither the name of the University nor the names of its contributors18* may be used to endorse or promote products derived from this software19* without specific prior written permission.20*21* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND22* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE23* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE24* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE25* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL26* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS27* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)28* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT29* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY30* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF31* SUCH DAMAGE.32*/3334#include <sys/param.h>3536#include <limits.h>37#include <stdio.h>38#include <stdlib.h>39#include <string.h>4041#include <db.h>42#include "btree.h"4344static int bt_broot(BTREE *, PAGE *, PAGE *, PAGE *);45static PAGE *bt_page(BTREE *, PAGE *, PAGE **, PAGE **, indx_t *, size_t);46static int bt_preserve(BTREE *, pgno_t);47static PAGE *bt_psplit(BTREE *, PAGE *, PAGE *, PAGE *, indx_t *, size_t);48static PAGE *bt_root(BTREE *, PAGE *, PAGE **, PAGE **, indx_t *, size_t);49static int bt_rroot(BTREE *, PAGE *, PAGE *, PAGE *);50static recno_t rec_total(PAGE *);5152#ifdef STATISTICS53u_long bt_rootsplit, bt_split, bt_sortsplit, bt_pfxsaved;54#endif5556/*57* __BT_SPLIT -- Split the tree.58*59* Parameters:60* t: tree61* sp: page to split62* key: key to insert63* data: data to insert64* flags: BIGKEY/BIGDATA flags65* ilen: insert length66* skip: index to leave open67*68* Returns:69* RET_ERROR, RET_SUCCESS70*/71int72__bt_split(BTREE *t, PAGE *sp, const DBT *key, const DBT *data, int flags,73size_t ilen, u_int32_t argskip)74{75BINTERNAL *bi;76BLEAF *bl, *tbl;77DBT a, b;78EPGNO *parent;79PAGE *h, *l, *r, *lchild, *rchild;80indx_t nxtindex;81u_int16_t skip;82u_int32_t n, nbytes, nksize;83int parentsplit;84char *dest;8586/*87* Split the page into two pages, l and r. The split routines return88* a pointer to the page into which the key should be inserted and with89* skip set to the offset which should be used. Additionally, l and r90* are pinned.91*/92skip = argskip;93h = sp->pgno == P_ROOT ?94bt_root(t, sp, &l, &r, &skip, ilen) :95bt_page(t, sp, &l, &r, &skip, ilen);96if (h == NULL)97return (RET_ERROR);9899/*100* Insert the new key/data pair into the leaf page. (Key inserts101* always cause a leaf page to split first.)102*/103h->linp[skip] = h->upper -= ilen;104dest = (char *)h + h->upper;105if (F_ISSET(t, R_RECNO))106WR_RLEAF(dest, data, flags)107else108WR_BLEAF(dest, key, data, flags)109110/* If the root page was split, make it look right. */111if (sp->pgno == P_ROOT &&112(F_ISSET(t, R_RECNO) ?113bt_rroot(t, sp, l, r) : bt_broot(t, sp, l, r)) == RET_ERROR)114goto err2;115116/*117* Now we walk the parent page stack -- a LIFO stack of the pages that118* were traversed when we searched for the page that split. Each stack119* entry is a page number and a page index offset. The offset is for120* the page traversed on the search. We've just split a page, so we121* have to insert a new key into the parent page.122*123* If the insert into the parent page causes it to split, may have to124* continue splitting all the way up the tree. We stop if the root125* splits or the page inserted into didn't have to split to hold the126* new key. Some algorithms replace the key for the old page as well127* as the new page. We don't, as there's no reason to believe that the128* first key on the old page is any better than the key we have, and,129* in the case of a key being placed at index 0 causing the split, the130* key is unavailable.131*132* There are a maximum of 5 pages pinned at any time. We keep the left133* and right pages pinned while working on the parent. The 5 are the134* two children, left parent and right parent (when the parent splits)135* and the root page or the overflow key page when calling bt_preserve.136* This code must make sure that all pins are released other than the137* root page or overflow page which is unlocked elsewhere.138*/139while ((parent = BT_POP(t)) != NULL) {140lchild = l;141rchild = r;142143/* Get the parent page. */144if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL)145goto err2;146147/*148* The new key goes ONE AFTER the index, because the split149* was to the right.150*/151skip = parent->index + 1;152153/*154* Calculate the space needed on the parent page.155*156* Prefix trees: space hack when inserting into BINTERNAL157* pages. Retain only what's needed to distinguish between158* the new entry and the LAST entry on the page to its left.159* If the keys compare equal, retain the entire key. Note,160* we don't touch overflow keys, and the entire key must be161* retained for the next-to-left most key on the leftmost162* page of each level, or the search will fail. Applicable163* ONLY to internal pages that have leaf pages as children.164* Further reduction of the key between pairs of internal165* pages loses too much information.166*/167switch (rchild->flags & P_TYPE) {168case P_BINTERNAL:169bi = GETBINTERNAL(rchild, 0);170nbytes = NBINTERNAL(bi->ksize);171break;172case P_BLEAF:173bl = GETBLEAF(rchild, 0);174nbytes = NBINTERNAL(bl->ksize);175if (t->bt_pfx && !(bl->flags & P_BIGKEY) &&176(h->prevpg != P_INVALID || skip > 1)) {177tbl = GETBLEAF(lchild, NEXTINDEX(lchild) - 1);178a.size = tbl->ksize;179a.data = tbl->bytes;180b.size = bl->ksize;181b.data = bl->bytes;182nksize = t->bt_pfx(&a, &b);183n = NBINTERNAL(nksize);184if (n < nbytes) {185#ifdef STATISTICS186bt_pfxsaved += nbytes - n;187#endif188nbytes = n;189} else190nksize = 0;191} else192nksize = 0;193break;194case P_RINTERNAL:195case P_RLEAF:196nbytes = NRINTERNAL;197break;198default:199abort();200}201202/* Split the parent page if necessary or shift the indices. */203if ((u_int32_t)(h->upper - h->lower) < nbytes + sizeof(indx_t)) {204sp = h;205h = h->pgno == P_ROOT ?206bt_root(t, h, &l, &r, &skip, nbytes) :207bt_page(t, h, &l, &r, &skip, nbytes);208if (h == NULL)209goto err1;210parentsplit = 1;211} else {212if (skip < (nxtindex = NEXTINDEX(h)))213memmove(h->linp + skip + 1, h->linp + skip,214(nxtindex - skip) * sizeof(indx_t));215h->lower += sizeof(indx_t);216parentsplit = 0;217}218219/* Insert the key into the parent page. */220switch (rchild->flags & P_TYPE) {221case P_BINTERNAL:222h->linp[skip] = h->upper -= nbytes;223dest = (char *)h + h->linp[skip];224memmove(dest, bi, nbytes);225((BINTERNAL *)dest)->pgno = rchild->pgno;226break;227case P_BLEAF:228h->linp[skip] = h->upper -= nbytes;229dest = (char *)h + h->linp[skip];230WR_BINTERNAL(dest, nksize ? nksize : bl->ksize,231rchild->pgno, bl->flags & P_BIGKEY);232memmove(dest, bl->bytes, nksize ? nksize : bl->ksize);233if (bl->flags & P_BIGKEY) {234pgno_t pgno;235memcpy(&pgno, bl->bytes, sizeof(pgno));236if (bt_preserve(t, pgno) == RET_ERROR)237goto err1;238}239break;240case P_RINTERNAL:241/*242* Update the left page count. If split243* added at index 0, fix the correct page.244*/245if (skip > 0)246dest = (char *)h + h->linp[skip - 1];247else248dest = (char *)l + l->linp[NEXTINDEX(l) - 1];249((RINTERNAL *)dest)->nrecs = rec_total(lchild);250((RINTERNAL *)dest)->pgno = lchild->pgno;251252/* Update the right page count. */253h->linp[skip] = h->upper -= nbytes;254dest = (char *)h + h->linp[skip];255((RINTERNAL *)dest)->nrecs = rec_total(rchild);256((RINTERNAL *)dest)->pgno = rchild->pgno;257break;258case P_RLEAF:259/*260* Update the left page count. If split261* added at index 0, fix the correct page.262*/263if (skip > 0)264dest = (char *)h + h->linp[skip - 1];265else266dest = (char *)l + l->linp[NEXTINDEX(l) - 1];267((RINTERNAL *)dest)->nrecs = NEXTINDEX(lchild);268((RINTERNAL *)dest)->pgno = lchild->pgno;269270/* Update the right page count. */271h->linp[skip] = h->upper -= nbytes;272dest = (char *)h + h->linp[skip];273((RINTERNAL *)dest)->nrecs = NEXTINDEX(rchild);274((RINTERNAL *)dest)->pgno = rchild->pgno;275break;276default:277abort();278}279280/* Unpin the held pages. */281if (!parentsplit) {282mpool_put(t->bt_mp, h, MPOOL_DIRTY);283break;284}285286/* If the root page was split, make it look right. */287if (sp->pgno == P_ROOT &&288(F_ISSET(t, R_RECNO) ?289bt_rroot(t, sp, l, r) : bt_broot(t, sp, l, r)) == RET_ERROR)290goto err1;291292mpool_put(t->bt_mp, lchild, MPOOL_DIRTY);293mpool_put(t->bt_mp, rchild, MPOOL_DIRTY);294}295296/* Unpin the held pages. */297mpool_put(t->bt_mp, l, MPOOL_DIRTY);298mpool_put(t->bt_mp, r, MPOOL_DIRTY);299300/* Clear any pages left on the stack. */301return (RET_SUCCESS);302303/*304* If something fails in the above loop we were already walking back305* up the tree and the tree is now inconsistent. Nothing much we can306* do about it but release any memory we're holding.307*/308err1: mpool_put(t->bt_mp, lchild, MPOOL_DIRTY);309mpool_put(t->bt_mp, rchild, MPOOL_DIRTY);310311err2: mpool_put(t->bt_mp, l, 0);312mpool_put(t->bt_mp, r, 0);313__dbpanic(t->bt_dbp);314return (RET_ERROR);315}316317/*318* BT_PAGE -- Split a non-root page of a btree.319*320* Parameters:321* t: tree322* h: root page323* lp: pointer to left page pointer324* rp: pointer to right page pointer325* skip: pointer to index to leave open326* ilen: insert length327*328* Returns:329* Pointer to page in which to insert or NULL on error.330*/331static PAGE *332bt_page(BTREE *t, PAGE *h, PAGE **lp, PAGE **rp, indx_t *skip, size_t ilen)333{334PAGE *l, *r, *tp;335pgno_t npg;336337#ifdef STATISTICS338++bt_split;339#endif340/* Put the new right page for the split into place. */341if ((r = __bt_new(t, &npg)) == NULL)342return (NULL);343r->pgno = npg;344r->lower = BTDATAOFF;345r->upper = t->bt_psize;346r->nextpg = h->nextpg;347r->prevpg = h->pgno;348r->flags = h->flags & P_TYPE;349350/*351* If we're splitting the last page on a level because we're appending352* a key to it (skip is NEXTINDEX()), it's likely that the data is353* sorted. Adding an empty page on the side of the level is less work354* and can push the fill factor much higher than normal. If we're355* wrong it's no big deal, we'll just do the split the right way next356* time. It may look like it's equally easy to do a similar hack for357* reverse sorted data, that is, split the tree left, but it's not.358* Don't even try.359*/360if (h->nextpg == P_INVALID && *skip == NEXTINDEX(h)) {361#ifdef STATISTICS362++bt_sortsplit;363#endif364h->nextpg = r->pgno;365r->lower = BTDATAOFF + sizeof(indx_t);366*skip = 0;367*lp = h;368*rp = r;369return (r);370}371372/* Put the new left page for the split into place. */373if ((l = (PAGE *)calloc(1, t->bt_psize)) == NULL) {374mpool_put(t->bt_mp, r, 0);375return (NULL);376}377l->pgno = h->pgno;378l->nextpg = r->pgno;379l->prevpg = h->prevpg;380l->lower = BTDATAOFF;381l->upper = t->bt_psize;382l->flags = h->flags & P_TYPE;383384/* Fix up the previous pointer of the page after the split page. */385if (h->nextpg != P_INVALID) {386if ((tp = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL) {387free(l);388/* XXX mpool_free(t->bt_mp, r->pgno); */389return (NULL);390}391tp->prevpg = r->pgno;392mpool_put(t->bt_mp, tp, MPOOL_DIRTY);393}394395/*396* Split right. The key/data pairs aren't sorted in the btree page so397* it's simpler to copy the data from the split page onto two new pages398* instead of copying half the data to the right page and compacting399* the left page in place. Since the left page can't change, we have400* to swap the original and the allocated left page after the split.401*/402tp = bt_psplit(t, h, l, r, skip, ilen);403404/* Move the new left page onto the old left page. */405memmove(h, l, t->bt_psize);406if (tp == l)407tp = h;408free(l);409410*lp = h;411*rp = r;412return (tp);413}414415/*416* BT_ROOT -- Split the root page of a btree.417*418* Parameters:419* t: tree420* h: root page421* lp: pointer to left page pointer422* rp: pointer to right page pointer423* skip: pointer to index to leave open424* ilen: insert length425*426* Returns:427* Pointer to page in which to insert or NULL on error.428*/429static PAGE *430bt_root(BTREE *t, PAGE *h, PAGE **lp, PAGE **rp, indx_t *skip, size_t ilen)431{432PAGE *l, *r, *tp;433pgno_t lnpg, rnpg;434435#ifdef STATISTICS436++bt_split;437++bt_rootsplit;438#endif439/* Put the new left and right pages for the split into place. */440if ((l = __bt_new(t, &lnpg)) == NULL ||441(r = __bt_new(t, &rnpg)) == NULL)442return (NULL);443l->pgno = lnpg;444r->pgno = rnpg;445l->nextpg = r->pgno;446r->prevpg = l->pgno;447l->prevpg = r->nextpg = P_INVALID;448l->lower = r->lower = BTDATAOFF;449l->upper = r->upper = t->bt_psize;450l->flags = r->flags = h->flags & P_TYPE;451452/* Split the root page. */453tp = bt_psplit(t, h, l, r, skip, ilen);454455*lp = l;456*rp = r;457return (tp);458}459460/*461* BT_RROOT -- Fix up the recno root page after it has been split.462*463* Parameters:464* t: tree465* h: root page466* l: left page467* r: right page468*469* Returns:470* RET_ERROR, RET_SUCCESS471*/472static int473bt_rroot(BTREE *t, PAGE *h, PAGE *l, PAGE *r)474{475char *dest;476477/* Insert the left and right keys, set the header information. */478h->linp[0] = h->upper = t->bt_psize - NRINTERNAL;479dest = (char *)h + h->upper;480WR_RINTERNAL(dest,481l->flags & P_RLEAF ? NEXTINDEX(l) : rec_total(l), l->pgno);482483__PAST_END(h->linp, 1) = h->upper -= NRINTERNAL;484dest = (char *)h + h->upper;485WR_RINTERNAL(dest,486r->flags & P_RLEAF ? NEXTINDEX(r) : rec_total(r), r->pgno);487488h->lower = BTDATAOFF + 2 * sizeof(indx_t);489490/* Unpin the root page, set to recno internal page. */491h->flags &= ~P_TYPE;492h->flags |= P_RINTERNAL;493mpool_put(t->bt_mp, h, MPOOL_DIRTY);494495return (RET_SUCCESS);496}497498/*499* BT_BROOT -- Fix up the btree root page after it has been split.500*501* Parameters:502* t: tree503* h: root page504* l: left page505* r: right page506*507* Returns:508* RET_ERROR, RET_SUCCESS509*/510static int511bt_broot(BTREE *t, PAGE *h, PAGE *l, PAGE *r)512{513BINTERNAL *bi;514BLEAF *bl;515u_int32_t nbytes;516char *dest;517518/*519* If the root page was a leaf page, change it into an internal page.520* We copy the key we split on (but not the key's data, in the case of521* a leaf page) to the new root page.522*523* The btree comparison code guarantees that the left-most key on any524* level of the tree is never used, so it doesn't need to be filled in.525*/526nbytes = NBINTERNAL(0);527h->linp[0] = h->upper = t->bt_psize - nbytes;528dest = (char *)h + h->upper;529WR_BINTERNAL(dest, 0, l->pgno, 0);530531switch (h->flags & P_TYPE) {532case P_BLEAF:533bl = GETBLEAF(r, 0);534nbytes = NBINTERNAL(bl->ksize);535__PAST_END(h->linp, 1) = h->upper -= nbytes;536dest = (char *)h + h->upper;537WR_BINTERNAL(dest, bl->ksize, r->pgno, 0);538memmove(dest, bl->bytes, bl->ksize);539540/*541* If the key is on an overflow page, mark the overflow chain542* so it isn't deleted when the leaf copy of the key is deleted.543*/544if (bl->flags & P_BIGKEY) {545pgno_t pgno;546memcpy(&pgno, bl->bytes, sizeof(pgno));547if (bt_preserve(t, pgno) == RET_ERROR)548return (RET_ERROR);549}550break;551case P_BINTERNAL:552bi = GETBINTERNAL(r, 0);553nbytes = NBINTERNAL(bi->ksize);554__PAST_END(h->linp, 1) = h->upper -= nbytes;555dest = (char *)h + h->upper;556memmove(dest, bi, nbytes);557((BINTERNAL *)dest)->pgno = r->pgno;558break;559default:560abort();561}562563/* There are two keys on the page. */564h->lower = BTDATAOFF + 2 * sizeof(indx_t);565566/* Unpin the root page, set to btree internal page. */567h->flags &= ~P_TYPE;568h->flags |= P_BINTERNAL;569mpool_put(t->bt_mp, h, MPOOL_DIRTY);570571return (RET_SUCCESS);572}573574/*575* BT_PSPLIT -- Do the real work of splitting the page.576*577* Parameters:578* t: tree579* h: page to be split580* l: page to put lower half of data581* r: page to put upper half of data582* pskip: pointer to index to leave open583* ilen: insert length584*585* Returns:586* Pointer to page in which to insert.587*/588static PAGE *589bt_psplit(BTREE *t, PAGE *h, PAGE *l, PAGE *r, indx_t *pskip, size_t ilen)590{591BINTERNAL *bi;592BLEAF *bl;593CURSOR *c;594RLEAF *rl;595PAGE *rval;596void *src;597indx_t full, half, nxt, off, skip, top, used;598u_int32_t nbytes;599int bigkeycnt, isbigkey;600601/*602* Split the data to the left and right pages. Leave the skip index603* open. Additionally, make some effort not to split on an overflow604* key. This makes internal page processing faster and can save605* space as overflow keys used by internal pages are never deleted.606*/607bigkeycnt = 0;608skip = *pskip;609full = t->bt_psize - BTDATAOFF;610half = full / 2;611used = 0;612for (nxt = off = 0, top = NEXTINDEX(h); nxt < top; ++off) {613if (skip == off) {614nbytes = ilen;615isbigkey = 0; /* XXX: not really known. */616} else617switch (h->flags & P_TYPE) {618case P_BINTERNAL:619src = bi = GETBINTERNAL(h, nxt);620nbytes = NBINTERNAL(bi->ksize);621isbigkey = bi->flags & P_BIGKEY;622break;623case P_BLEAF:624src = bl = GETBLEAF(h, nxt);625nbytes = NBLEAF(bl);626isbigkey = bl->flags & P_BIGKEY;627break;628case P_RINTERNAL:629src = GETRINTERNAL(h, nxt);630nbytes = NRINTERNAL;631isbigkey = 0;632break;633case P_RLEAF:634src = rl = GETRLEAF(h, nxt);635nbytes = NRLEAF(rl);636isbigkey = 0;637break;638default:639abort();640}641642/*643* If the key/data pairs are substantial fractions of the max644* possible size for the page, it's possible to get situations645* where we decide to try and copy too much onto the left page.646* Make sure that doesn't happen.647*/648if ((skip <= off && used + nbytes + sizeof(indx_t) >= full) ||649nxt == top - 1) {650--off;651break;652}653654/* Copy the key/data pair, if not the skipped index. */655if (skip != off) {656++nxt;657658l->linp[off] = l->upper -= nbytes;659memmove((char *)l + l->upper, src, nbytes);660}661662used += nbytes + sizeof(indx_t);663if (used >= half) {664if (!isbigkey || bigkeycnt == 3)665break;666else667++bigkeycnt;668}669}670671/*672* Off is the last offset that's valid for the left page.673* Nxt is the first offset to be placed on the right page.674*/675l->lower += (off + 1) * sizeof(indx_t);676677/*678* If splitting the page that the cursor was on, the cursor has to be679* adjusted to point to the same record as before the split. If the680* cursor is at or past the skipped slot, the cursor is incremented by681* one. If the cursor is on the right page, it is decremented by the682* number of records split to the left page.683*/684c = &t->bt_cursor;685if (F_ISSET(c, CURS_INIT) && c->pg.pgno == h->pgno) {686if (c->pg.index >= skip)687++c->pg.index;688if (c->pg.index < nxt) /* Left page. */689c->pg.pgno = l->pgno;690else { /* Right page. */691c->pg.pgno = r->pgno;692c->pg.index -= nxt;693}694}695696/*697* If the skipped index was on the left page, just return that page.698* Otherwise, adjust the skip index to reflect the new position on699* the right page.700*/701if (skip <= off) {702skip = MAX_PAGE_OFFSET;703rval = l;704} else {705rval = r;706*pskip -= nxt;707}708709for (off = 0; nxt < top; ++off) {710if (skip == nxt) {711++off;712skip = MAX_PAGE_OFFSET;713}714switch (h->flags & P_TYPE) {715case P_BINTERNAL:716src = bi = GETBINTERNAL(h, nxt);717nbytes = NBINTERNAL(bi->ksize);718break;719case P_BLEAF:720src = bl = GETBLEAF(h, nxt);721nbytes = NBLEAF(bl);722break;723case P_RINTERNAL:724src = GETRINTERNAL(h, nxt);725nbytes = NRINTERNAL;726break;727case P_RLEAF:728src = rl = GETRLEAF(h, nxt);729nbytes = NRLEAF(rl);730break;731default:732abort();733}734++nxt;735r->linp[off] = r->upper -= nbytes;736memmove((char *)r + r->upper, src, nbytes);737}738r->lower += off * sizeof(indx_t);739740/* If the key is being appended to the page, adjust the index. */741if (skip == top)742r->lower += sizeof(indx_t);743744return (rval);745}746747/*748* BT_PRESERVE -- Mark a chain of pages as used by an internal node.749*750* Chains of indirect blocks pointed to by leaf nodes get reclaimed when the751* record that references them gets deleted. Chains pointed to by internal752* pages never get deleted. This routine marks a chain as pointed to by an753* internal page.754*755* Parameters:756* t: tree757* pg: page number of first page in the chain.758*759* Returns:760* RET_SUCCESS, RET_ERROR.761*/762static int763bt_preserve(BTREE *t, pgno_t pg)764{765PAGE *h;766767if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)768return (RET_ERROR);769h->flags |= P_PRESERVE;770mpool_put(t->bt_mp, h, MPOOL_DIRTY);771return (RET_SUCCESS);772}773774/*775* REC_TOTAL -- Return the number of recno entries below a page.776*777* Parameters:778* h: page779*780* Returns:781* The number of recno entries below a page.782*783* XXX784* These values could be set by the bt_psplit routine. The problem is that the785* entry has to be popped off of the stack etc. or the values have to be passed786* all the way back to bt_split/bt_rroot and it's not very clean.787*/788static recno_t789rec_total(PAGE *h)790{791recno_t recs;792indx_t nxt, top;793794for (recs = 0, nxt = 0, top = NEXTINDEX(h); nxt < top; ++nxt)795recs += GETRINTERNAL(h, nxt)->nrecs;796return (recs);797}798799800