/*-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/types.h>3536#include <errno.h>37#include <stdio.h>38#include <stdlib.h>39#include <string.h>4041#include <db.h>42#include "btree.h"4344static EPG *bt_fast(BTREE *, const DBT *, const DBT *, int *);4546/*47* __BT_PUT -- Add a btree item to the tree.48*49* Parameters:50* dbp: pointer to access method51* key: key52* data: data53* flag: R_NOOVERWRITE, R_SETCURSOR, R_CURSOR54*55* Returns:56* RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key is already in the57* tree and R_NOOVERWRITE specified.58*/59int60__bt_put(const DB *dbp, DBT *key, const DBT *data, u_int flags)61{62BTREE *t;63DBT tkey, tdata;64EPG *e;65PAGE *h;66indx_t idx, nxtindex;67pgno_t pg;68u_int32_t nbytes, tmp;69int dflags, exact, status;70char *dest, db[NOVFLSIZE], kb[NOVFLSIZE];7172t = dbp->internal;7374/* Toss any page pinned across calls. */75if (t->bt_pinned != NULL) {76mpool_put(t->bt_mp, t->bt_pinned, 0);77t->bt_pinned = NULL;78}7980/* Check for change to a read-only tree. */81if (F_ISSET(t, B_RDONLY)) {82errno = EPERM;83return (RET_ERROR);84}8586switch (flags) {87case 0:88case R_NOOVERWRITE:89case R_SETCURSOR:90break;91case R_CURSOR:92/*93* If flags is R_CURSOR, put the cursor. Must already94* have started a scan and not have already deleted it.95*/96if (F_ISSET(&t->bt_cursor, CURS_INIT) &&97!F_ISSET(&t->bt_cursor,98CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE))99break;100/* FALLTHROUGH */101default:102errno = EINVAL;103return (RET_ERROR);104}105106/*107* If the key/data pair won't fit on a page, store it on overflow108* pages. Only put the key on the overflow page if the pair are109* still too big after moving the data to an overflow page.110*111* XXX112* If the insert fails later on, the overflow pages aren't recovered.113*/114dflags = 0;115if (key->size + data->size > t->bt_ovflsize) {116if (key->size > t->bt_ovflsize) {117storekey: if (__ovfl_put(t, key, &pg) == RET_ERROR)118return (RET_ERROR);119tkey.data = kb;120tkey.size = NOVFLSIZE;121memmove(kb, &pg, sizeof(pgno_t));122tmp = key->size;123memmove(kb + sizeof(pgno_t),124&tmp, sizeof(u_int32_t));125dflags |= P_BIGKEY;126key = &tkey;127}128if (key->size + data->size > t->bt_ovflsize) {129if (__ovfl_put(t, data, &pg) == RET_ERROR)130return (RET_ERROR);131tdata.data = db;132tdata.size = NOVFLSIZE;133memmove(db, &pg, sizeof(pgno_t));134tmp = data->size;135memmove(db + sizeof(pgno_t),136&tmp, sizeof(u_int32_t));137dflags |= P_BIGDATA;138data = &tdata;139}140if (key->size + data->size > t->bt_ovflsize)141goto storekey;142}143144/* Replace the cursor. */145if (flags == R_CURSOR) {146if ((h = mpool_get(t->bt_mp, t->bt_cursor.pg.pgno, 0)) == NULL)147return (RET_ERROR);148idx = t->bt_cursor.pg.index;149goto delete;150}151152/*153* Find the key to delete, or, the location at which to insert.154* Bt_fast and __bt_search both pin the returned page.155*/156if (t->bt_order == NOT || (e = bt_fast(t, key, data, &exact)) == NULL)157if ((e = __bt_search(t, key, &exact)) == NULL)158return (RET_ERROR);159h = e->page;160idx = e->index;161162/*163* Add the key/data pair to the tree. If an identical key is already164* in the tree, and R_NOOVERWRITE is set, an error is returned. If165* R_NOOVERWRITE is not set, the key is either added (if duplicates are166* permitted) or an error is returned.167*/168switch (flags) {169case R_NOOVERWRITE:170if (!exact)171break;172mpool_put(t->bt_mp, h, 0);173return (RET_SPECIAL);174default:175if (!exact || !F_ISSET(t, B_NODUPS))176break;177/*178* !!!179* Note, the delete may empty the page, so we need to put a180* new entry into the page immediately.181*/182delete: if (__bt_dleaf(t, key, h, idx) == RET_ERROR) {183mpool_put(t->bt_mp, h, 0);184return (RET_ERROR);185}186break;187}188189/*190* If not enough room, or the user has put a ceiling on the number of191* keys permitted in the page, split the page. The split code will192* insert the key and data and unpin the current page. If inserting193* into the offset array, shift the pointers up.194*/195nbytes = NBLEAFDBT(key->size, data->size);196if ((u_int32_t)(h->upper - h->lower) < nbytes + sizeof(indx_t)) {197if ((status = __bt_split(t, h, key,198data, dflags, nbytes, idx)) != RET_SUCCESS)199return (status);200goto success;201}202203if (idx < (nxtindex = NEXTINDEX(h)))204memmove(h->linp + idx + 1, h->linp + idx,205(nxtindex - idx) * sizeof(indx_t));206h->lower += sizeof(indx_t);207208h->linp[idx] = h->upper -= nbytes;209dest = (char *)h + h->upper;210WR_BLEAF(dest, key, data, dflags);211212/* If the cursor is on this page, adjust it as necessary. */213if (F_ISSET(&t->bt_cursor, CURS_INIT) &&214!F_ISSET(&t->bt_cursor, CURS_ACQUIRE) &&215t->bt_cursor.pg.pgno == h->pgno && t->bt_cursor.pg.index >= idx)216++t->bt_cursor.pg.index;217218if (t->bt_order == NOT) {219if (h->nextpg == P_INVALID) {220if (idx == NEXTINDEX(h) - 1) {221t->bt_order = FORWARD;222t->bt_last.index = idx;223t->bt_last.pgno = h->pgno;224}225} else if (h->prevpg == P_INVALID) {226if (idx == 0) {227t->bt_order = BACK;228t->bt_last.index = 0;229t->bt_last.pgno = h->pgno;230}231}232}233234mpool_put(t->bt_mp, h, MPOOL_DIRTY);235236success:237if (flags == R_SETCURSOR)238__bt_setcur(t, e->page->pgno, e->index);239240F_SET(t, B_MODIFIED);241return (RET_SUCCESS);242}243244#ifdef STATISTICS245u_long bt_cache_hit, bt_cache_miss;246#endif247248/*249* BT_FAST -- Do a quick check for sorted data.250*251* Parameters:252* t: tree253* key: key to insert254*255* Returns:256* EPG for new record or NULL if not found.257*/258static EPG *259bt_fast(BTREE *t, const DBT *key, const DBT *data, int *exactp)260{261PAGE *h;262u_int32_t nbytes;263int cmp;264265if ((h = mpool_get(t->bt_mp, t->bt_last.pgno, 0)) == NULL) {266t->bt_order = NOT;267return (NULL);268}269t->bt_cur.page = h;270t->bt_cur.index = t->bt_last.index;271272/*273* If won't fit in this page or have too many keys in this page,274* have to search to get split stack.275*/276nbytes = NBLEAFDBT(key->size, data->size);277if ((u_int32_t)(h->upper - h->lower) < nbytes + sizeof(indx_t))278goto miss;279280if (t->bt_order == FORWARD) {281if (t->bt_cur.page->nextpg != P_INVALID)282goto miss;283if (t->bt_cur.index != NEXTINDEX(h) - 1)284goto miss;285if ((cmp = __bt_cmp(t, key, &t->bt_cur)) < 0)286goto miss;287t->bt_last.index = cmp ? ++t->bt_cur.index : t->bt_cur.index;288} else {289if (t->bt_cur.page->prevpg != P_INVALID)290goto miss;291if (t->bt_cur.index != 0)292goto miss;293if ((cmp = __bt_cmp(t, key, &t->bt_cur)) > 0)294goto miss;295t->bt_last.index = 0;296}297*exactp = cmp == 0;298#ifdef STATISTICS299++bt_cache_hit;300#endif301return (&t->bt_cur);302303miss:304#ifdef STATISTICS305++bt_cache_miss;306#endif307t->bt_order = NOT;308mpool_put(t->bt_mp, h, 0);309return (NULL);310}311312313