xref: /csrg-svn/lib/libc/db/btree/bt_put.c (revision 56995)
146131Smao /*-
246131Smao  * Copyright (c) 1990 The Regents of the University of California.
346131Smao  * All rights reserved.
446131Smao  *
546131Smao  * This code is derived from software contributed to Berkeley by
646131Smao  * Mike Olson.
746131Smao  *
846131Smao  * %sccs.include.redist.c%
946131Smao  */
1046131Smao 
1146131Smao #if defined(LIBC_SCCS) && !defined(lint)
12*56995Sbostic static char sccsid[] = "@(#)bt_put.c	5.9 (Berkeley) 12/04/92";
1346131Smao #endif /* LIBC_SCCS and not lint */
1446131Smao 
1546131Smao #include <sys/types.h>
1656751Sbostic 
1756751Sbostic #include <db.h>
1850989Sbostic #include <errno.h>
1950989Sbostic #include <stdio.h>
2046561Sbostic #include <stdlib.h>
2146561Sbostic #include <string.h>
2256751Sbostic 
2346131Smao #include "btree.h"
2446131Smao 
2550989Sbostic static EPG *bt_fast __P((BTREE *, const DBT *, const DBT *, int *));
2650989Sbostic 
2746131Smao /*
2850989Sbostic  * __BT_PUT -- Add a btree item to the tree.
2946131Smao  *
3050989Sbostic  * Parameters:
3150989Sbostic  *	dbp:	pointer to access method
3250989Sbostic  *	key:	key
3350989Sbostic  *	data:	data
3450989Sbostic  *	flag:	R_NOOVERWRITE
3546131Smao  *
3650989Sbostic  * Returns:
3750989Sbostic  *	RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key is already in the
3850989Sbostic  *	tree and R_NOOVERWRITE specified.
3946131Smao  */
4046131Smao int
4150989Sbostic __bt_put(dbp, key, data, flags)
4250989Sbostic 	const DB *dbp;
4356751Sbostic 	DBT *key;
4456751Sbostic 	const DBT *data;
4550989Sbostic 	u_int flags;
4646131Smao {
4750989Sbostic 	BTREE *t;
4850989Sbostic 	DBT tkey, tdata;
4950989Sbostic 	EPG *e;
5050989Sbostic 	PAGE *h;
5150989Sbostic 	index_t index, nxtindex;
5250989Sbostic 	pgno_t pg;
5350989Sbostic 	size_t nbytes;
5451099Sbostic 	int dflags, exact, status;
5550989Sbostic 	char *dest, db[NOVFLSIZE], kb[NOVFLSIZE];
5646131Smao 
5756751Sbostic 	t = dbp->internal;
5856751Sbostic 
5951099Sbostic 	switch (flags) {
6051099Sbostic 	case R_CURSOR:
61*56995Sbostic 		if (!ISSET(t, BTF_SEQINIT))
62*56995Sbostic 			goto einval;
63*56995Sbostic 		if (ISSET(t, BTF_DELCRSR))
64*56995Sbostic 			goto einval;
6551099Sbostic 		break;
6651099Sbostic 	case 0:
6751099Sbostic 	case R_NOOVERWRITE:
6851099Sbostic 		break;
6951099Sbostic 	default:
70*56995Sbostic einval:		errno = EINVAL;
7146131Smao 		return (RET_ERROR);
7246131Smao 	}
7351099Sbostic 
7450989Sbostic 	if (ISSET(t, BTF_RDONLY)) {
7550989Sbostic 		errno = EPERM;
7650989Sbostic 		return (RET_ERROR);
7746131Smao 	}
7851099Sbostic 
7950989Sbostic 	/*
8050989Sbostic 	 * If the key/data won't fit on a page, store it on indirect pages.
8151099Sbostic 	 * Only store the key on the overflow page if it's too big after the
8251099Sbostic 	 * data is on an overflow page.
8350989Sbostic 	 *
8450989Sbostic 	 * XXX
8550989Sbostic 	 * If the insert fails later on, these pages aren't recovered.
8650989Sbostic 	 */
8750989Sbostic 	dflags = 0;
8851099Sbostic 	if (key->size + data->size > t->bt_ovflsize) {
8951099Sbostic 		if (key->size > t->bt_ovflsize) {
9051099Sbostic storekey:		if (__ovfl_put(t, key, &pg) == RET_ERROR)
9151099Sbostic 				return (RET_ERROR);
9251099Sbostic 			tkey.data = kb;
9351099Sbostic 			tkey.size = NOVFLSIZE;
9456558Sbostic 			bcopy(&pg, kb, sizeof(pgno_t));
9556558Sbostic 			bcopy(&key->size, kb + sizeof(pgno_t), sizeof(size_t));
9651099Sbostic 			dflags |= P_BIGKEY;
9751099Sbostic 			key = &tkey;
9851099Sbostic 		}
9951099Sbostic 		if (key->size + data->size > t->bt_ovflsize) {
10051099Sbostic 			if (__ovfl_put(t, data, &pg) == RET_ERROR)
10151099Sbostic 				return (RET_ERROR);
10251099Sbostic 			tdata.data = db;
10351099Sbostic 			tdata.size = NOVFLSIZE;
10456558Sbostic 			bcopy(&pg, db, sizeof(pgno_t));
10556558Sbostic 			bcopy(&data->size, db + sizeof(pgno_t), sizeof(size_t));
10651099Sbostic 			dflags |= P_BIGDATA;
10751099Sbostic 			data = &tdata;
10851099Sbostic 		}
10951099Sbostic 		if (key->size + data->size > t->bt_ovflsize)
11051099Sbostic 			goto storekey;
11146131Smao 	}
11251099Sbostic 
11351099Sbostic 	/* Replace the cursor. */
11451099Sbostic 	if (flags == R_CURSOR) {
11551099Sbostic 		if ((h = mpool_get(t->bt_mp, t->bt_bcursor.pgno, 0)) == NULL)
11650989Sbostic 			return (RET_ERROR);
11751099Sbostic 		index = t->bt_bcursor.index;
11851099Sbostic 		goto delete;
11950989Sbostic 	}
12046131Smao 
12151099Sbostic 	/*
12251099Sbostic 	 * Find the key to delete, or, the location at which to insert.  Bt_fast
12351099Sbostic 	 * and __bt_search pin the returned page.
12451099Sbostic 	 */
12550989Sbostic 	if (t->bt_order == NOT || (e = bt_fast(t, key, data, &exact)) == NULL)
12650989Sbostic 		if ((e = __bt_search(t, key, &exact)) == NULL)
12750989Sbostic 			return (RET_ERROR);
12850989Sbostic 	h = e->page;
12950989Sbostic 	index = e->index;
13046131Smao 
13150989Sbostic 	/*
13250989Sbostic 	 * Add the specified key/data pair to the tree.  If an identical key
13350989Sbostic 	 * is already in the tree, and R_NOOVERWRITE is set, an error is
13450989Sbostic 	 * returned.  If R_NOOVERWRITE is not set, the key is either added (if
13550989Sbostic 	 * duplicates are permitted) or an error is returned.
13650989Sbostic 	 *
13750989Sbostic 	 * Pages are split as required.
13850989Sbostic 	 */
13950989Sbostic 	switch (flags) {
14050989Sbostic 	case R_NOOVERWRITE:
14150989Sbostic 		if (!exact)
14250989Sbostic 			break;
14350989Sbostic 		/*
14450989Sbostic 		 * One special case is if the cursor references the record and
14551099Sbostic 		 * it's been flagged for deletion.  Then, we delete the record,
14651099Sbostic 		 * leaving the cursor there -- this means that the inserted
14751099Sbostic 		 * record will not be seen in a cursor scan.
14850989Sbostic 		 */
14950989Sbostic 		if (ISSET(t, BTF_DELCRSR) && t->bt_bcursor.pgno == h->pgno &&
15050989Sbostic 		    t->bt_bcursor.index == index) {
15156751Sbostic 			CLR(t, BTF_DELCRSR);
15250989Sbostic 			goto delete;
15346131Smao 		}
15450989Sbostic 		BT_CLR(t);
15550989Sbostic 		mpool_put(t->bt_mp, h, 0);
15650989Sbostic 		return (RET_SPECIAL);
15750989Sbostic 	default:
15856751Sbostic 		if (!exact || !ISSET(t, BTF_NODUPS))
15950989Sbostic 			break;
16050989Sbostic delete:		if (__bt_dleaf(t, h, index) == RET_ERROR) {
16150989Sbostic 			BT_CLR(t);
16250989Sbostic 			mpool_put(t->bt_mp, h, 0);
16346131Smao 			return (RET_ERROR);
16446131Smao 		}
16550989Sbostic 		break;
16646131Smao 	}
16746131Smao 
16850989Sbostic 	/*
16950989Sbostic 	 * If not enough room, or the user has put a ceiling on the number of
17050989Sbostic 	 * keys permitted in the page, split the page.  The split code will
17150989Sbostic 	 * insert the key and data and unpin the current page.  If inserting
17250989Sbostic 	 * into the offset array, shift the pointers up.
17350989Sbostic 	 */
17450989Sbostic 	nbytes = NBLEAFDBT(key->size, data->size);
17551099Sbostic 	if (h->upper - h->lower < nbytes + sizeof(index_t)) {
17656751Sbostic 		if ((status = __bt_split(t, h, key,
17756751Sbostic 		    data, dflags, nbytes, index)) != RET_SUCCESS)
17856751Sbostic 			return (status);
17956751Sbostic 		goto success;
18051099Sbostic 	}
18146131Smao 
18250989Sbostic 	if (index < (nxtindex = NEXTINDEX(h)))
18350989Sbostic 		bcopy(h->linp + index, h->linp + index + 1,
18450989Sbostic 		    (nxtindex - index) * sizeof(index_t));
18550989Sbostic 	h->lower += sizeof(index_t);
18646131Smao 
18750989Sbostic 	h->linp[index] = h->upper -= nbytes;
18850989Sbostic 	dest = (char *)h + h->upper;
18950989Sbostic 	WR_BLEAF(dest, key, data, dflags);
19046131Smao 
19150989Sbostic 	if (t->bt_order == NOT)
19250989Sbostic 		if (h->nextpg == P_INVALID) {
19350989Sbostic 			if (index == NEXTINDEX(h) - 1) {
19450989Sbostic 				t->bt_order = FORWARD;
19550989Sbostic 				t->bt_last.index = index;
19650989Sbostic 				t->bt_last.pgno = h->pgno;
19750989Sbostic 			}
19850989Sbostic 		} else if (h->prevpg == P_INVALID) {
19950989Sbostic 			if (index == 0) {
20050989Sbostic 				t->bt_order = BACK;
20150989Sbostic 				t->bt_last.index = 0;
20250989Sbostic 				t->bt_last.pgno = h->pgno;
20350989Sbostic 			}
20446131Smao 		}
20546131Smao 
20651099Sbostic 	mpool_put(t->bt_mp, h, MPOOL_DIRTY);
20750989Sbostic 	BT_CLR(t);
20856751Sbostic 
20956751Sbostic success:
21056751Sbostic 	if (flags == R_SETCURSOR) {
21156751Sbostic 		t->bt_bcursor.pgno = e->page->pgno;
21256751Sbostic 		t->bt_bcursor.index = e->index;
21356751Sbostic 	}
21450989Sbostic 	SET(t, BTF_MODIFIED);
21550989Sbostic 	return (RET_SUCCESS);
21646131Smao }
21746131Smao 
21850989Sbostic #ifdef STATISTICS
21950989Sbostic u_long bt_cache_hit, bt_cache_miss;
22050989Sbostic #endif
22150989Sbostic 
22246131Smao /*
22350989Sbostic  * BT_FAST -- Do a quick check for sorted data.
22446131Smao  *
22550989Sbostic  * Parameters:
22650989Sbostic  *	t:	tree
22750989Sbostic  *	key:	key to insert
22846131Smao  *
22950989Sbostic  * Returns:
23050989Sbostic  * 	EPG for new record or NULL if not found.
23146131Smao  */
23250989Sbostic static EPG *
23350989Sbostic bt_fast(t, key, data, exactp)
23450989Sbostic 	BTREE *t;
23550989Sbostic 	const DBT *key, *data;
23650989Sbostic 	int *exactp;
23746131Smao {
23850989Sbostic 	EPG e;
23950989Sbostic 	PAGE *h;
24050989Sbostic 	size_t nbytes;
24150989Sbostic 	int cmp;
24246131Smao 
24350989Sbostic 	if ((h = mpool_get(t->bt_mp, t->bt_last.pgno, 0)) == NULL) {
24450989Sbostic 		t->bt_order = NOT;
24550989Sbostic 		return (NULL);
24650989Sbostic 	}
24750989Sbostic 	e.page = h;
24850989Sbostic 	e.index = t->bt_last.index;
24946131Smao 
25046131Smao 	/*
25150989Sbostic 	 * If won't fit in this page or have too many keys in this page, have
25250989Sbostic 	 * to search to get split stack.
25346131Smao 	 */
25451099Sbostic 	nbytes = NBLEAFDBT(key->size, data->size);
25551099Sbostic 	if (h->upper - h->lower < nbytes + sizeof(index_t))
25650989Sbostic 		goto miss;
25746131Smao 
25850989Sbostic 	if (t->bt_order == FORWARD) {
25950989Sbostic 		if (e.page->nextpg != P_INVALID)
26050989Sbostic 			goto miss;
26150989Sbostic 		if (e.index != NEXTINDEX(h) - 1)
26250989Sbostic 			goto miss;
26350989Sbostic 		if ((cmp = __bt_cmp(t, key, &e)) < 0)
26450989Sbostic 			goto miss;
26551880Sbostic 		t->bt_last.index = cmp ? ++e.index : e.index;
26646131Smao 	} else {
26750989Sbostic 		if (e.page->prevpg != P_INVALID)
26850989Sbostic 			goto miss;
26950989Sbostic 		if (e.index != 0)
27050989Sbostic 			goto miss;
27150989Sbostic 		if ((cmp = __bt_cmp(t, key, &e)) > 0)
27250989Sbostic 			goto miss;
27350989Sbostic 		t->bt_last.index = 0;
27446131Smao 	}
27550989Sbostic 	*exactp = cmp == 0;
27650989Sbostic #ifdef STATISTICS
27750989Sbostic 	++bt_cache_hit;
27850989Sbostic #endif
27950989Sbostic 	return (&e);
28046131Smao 
28151099Sbostic miss:
28250989Sbostic #ifdef STATISTICS
28350989Sbostic 	++bt_cache_miss;
28450989Sbostic #endif
28550989Sbostic 	t->bt_order = NOT;
28650989Sbostic 	mpool_put(t->bt_mp, h, 0);
28750989Sbostic 	return (NULL);
28846131Smao }
289