rec_put.c revision 1.5
1/*	$OpenBSD: rec_put.c,v 1.5 1999/02/15 05:11:25 millert Exp $	*/
2
3/*-
4 * Copyright (c) 1990, 1993, 1994
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by the University of
18 *	California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#if defined(LIBC_SCCS) && !defined(lint)
37#if 0
38static char sccsid[] = "@(#)rec_put.c	8.7 (Berkeley) 8/18/94";
39#else
40static char rcsid[] = "$OpenBSD: rec_put.c,v 1.5 1999/02/15 05:11:25 millert Exp $";
41#endif
42#endif /* LIBC_SCCS and not lint */
43
44#include <sys/types.h>
45
46#include <errno.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50
51#include <db.h>
52#include "recno.h"
53
54/*
55 * __REC_PUT -- Add a recno item to the tree.
56 *
57 * Parameters:
58 *	dbp:	pointer to access method
59 *	key:	key
60 *	data:	data
61 *	flag:	R_CURSOR, R_IAFTER, R_IBEFORE, R_NOOVERWRITE
62 *
63 * Returns:
64 *	RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key is
65 *	already in the tree and R_NOOVERWRITE specified.
66 */
67int
68__rec_put(dbp, key, data, flags)
69	const DB *dbp;
70	DBT *key;
71	const DBT *data;
72	u_int flags;
73{
74	BTREE *t;
75	DBT fdata, tdata;
76	recno_t nrec;
77	int status;
78	void *tp;
79
80	t = dbp->internal;
81
82	/* Toss any page pinned across calls. */
83	if (t->bt_pinned != NULL) {
84		mpool_put(t->bt_mp, t->bt_pinned, 0);
85		t->bt_pinned = NULL;
86	}
87
88	/*
89	 * If using fixed-length records, and the record is long, return
90	 * EINVAL.  If it's short, pad it out.  Use the record data return
91	 * memory, it's only short-term.
92	 */
93	if (F_ISSET(t, R_FIXLEN) && data->size != t->bt_reclen) {
94		if (data->size > t->bt_reclen)
95			goto einval;
96
97		if (t->bt_rdata.size < t->bt_reclen) {
98			tp = t->bt_rdata.data == NULL ?
99			    malloc(t->bt_reclen) :
100			    realloc(t->bt_rdata.data, t->bt_reclen);
101			if (tp == NULL)
102				return (RET_ERROR);
103			t->bt_rdata.data = tp;
104			t->bt_rdata.size = t->bt_reclen;
105		}
106		memmove(t->bt_rdata.data, data->data, data->size);
107		memset((char *)t->bt_rdata.data + data->size,
108		    t->bt_bval, t->bt_reclen - data->size);
109		fdata.data = t->bt_rdata.data;
110		fdata.size = t->bt_reclen;
111	} else {
112		fdata.data = data->data;
113		fdata.size = data->size;
114	}
115
116	switch (flags) {
117	case R_CURSOR:
118		if (!F_ISSET(&t->bt_cursor, CURS_INIT))
119			goto einval;
120		nrec = t->bt_cursor.rcursor;
121		break;
122	case R_SETCURSOR:
123		if ((nrec = *(recno_t *)key->data) == 0)
124			goto einval;
125		break;
126	case R_IAFTER:
127		if ((nrec = *(recno_t *)key->data) == 0) {
128			nrec = 1;
129			flags = R_IBEFORE;
130		}
131		break;
132	case 0:
133	case R_IBEFORE:
134		if ((nrec = *(recno_t *)key->data) == 0)
135			goto einval;
136		break;
137	case R_NOOVERWRITE:
138		if ((nrec = *(recno_t *)key->data) == 0)
139			goto einval;
140		if (nrec <= t->bt_nrecs)
141			return (RET_SPECIAL);
142		break;
143	default:
144einval:		errno = EINVAL;
145		return (RET_ERROR);
146	}
147
148	/*
149	 * Make sure that records up to and including the put record are
150	 * already in the database.  If skipping records, create empty ones.
151	 */
152	if (nrec > t->bt_nrecs) {
153		if (!F_ISSET(t, R_EOF | R_INMEM) &&
154		    t->bt_irec(t, nrec) == RET_ERROR)
155			return (RET_ERROR);
156		if (nrec > t->bt_nrecs + 1) {
157			if (F_ISSET(t, R_FIXLEN)) {
158				if ((tdata.data =
159				    (void *)malloc(t->bt_reclen)) == NULL)
160					return (RET_ERROR);
161				tdata.size = t->bt_reclen;
162				memset(tdata.data, t->bt_bval, tdata.size);
163			} else {
164				tdata.data = NULL;
165				tdata.size = 0;
166			}
167			while (nrec > t->bt_nrecs + 1)
168				if (__rec_iput(t,
169				    t->bt_nrecs, &tdata, 0) != RET_SUCCESS)
170					return (RET_ERROR);
171			if (F_ISSET(t, R_FIXLEN))
172				free(tdata.data);
173		}
174	}
175
176	if ((status = __rec_iput(t, nrec - 1, &fdata, flags)) != RET_SUCCESS)
177		return (status);
178
179	if (flags == R_SETCURSOR)
180		t->bt_cursor.rcursor = nrec;
181
182	F_SET(t, R_MODIFIED);
183	return (__rec_ret(t, NULL, nrec, key, NULL));
184}
185
186/*
187 * __REC_IPUT -- Add a recno item to the tree.
188 *
189 * Parameters:
190 *	t:	tree
191 *	nrec:	record number
192 *	data:	data
193 *
194 * Returns:
195 *	RET_ERROR, RET_SUCCESS
196 */
197int
198__rec_iput(t, nrec, data, flags)
199	BTREE *t;
200	recno_t nrec;
201	const DBT *data;
202	u_int flags;
203{
204	DBT tdata;
205	EPG *e;
206	PAGE *h;
207	indx_t index, nxtindex;
208	pgno_t pg;
209	u_int32_t nbytes;
210	int dflags, status;
211	char *dest, db[NOVFLSIZE];
212
213	/*
214	 * If the data won't fit on a page, store it on indirect pages.
215	 *
216	 * XXX
217	 * If the insert fails later on, these pages aren't recovered.
218	 */
219	if (data->size > t->bt_ovflsize) {
220		if (__ovfl_put(t, data, &pg) == RET_ERROR)
221			return (RET_ERROR);
222		tdata.data = db;
223		tdata.size = NOVFLSIZE;
224		*(pgno_t *)db = pg;
225		*(u_int32_t *)(db + sizeof(pgno_t)) = data->size;
226		dflags = P_BIGDATA;
227		data = &tdata;
228	} else
229		dflags = 0;
230
231	/* __rec_search pins the returned page. */
232	if ((e = __rec_search(t, nrec,
233	    nrec > t->bt_nrecs || flags == R_IAFTER || flags == R_IBEFORE ?
234	    SINSERT : SEARCH)) == NULL)
235		return (RET_ERROR);
236
237	h = e->page;
238	index = e->index;
239
240	/*
241	 * Add the specified key/data pair to the tree.  The R_IAFTER and
242	 * R_IBEFORE flags insert the key after/before the specified key.
243	 *
244	 * Pages are split as required.
245	 */
246	switch (flags) {
247	case R_IAFTER:
248		++index;
249		break;
250	case R_IBEFORE:
251		break;
252	default:
253		if (nrec < t->bt_nrecs &&
254		    __rec_dleaf(t, h, index) == RET_ERROR) {
255			mpool_put(t->bt_mp, h, 0);
256			return (RET_ERROR);
257		}
258		break;
259	}
260
261	/*
262	 * If not enough room, split the page.  The split code will insert
263	 * the key and data and unpin the current page.  If inserting into
264	 * the offset array, shift the pointers up.
265	 */
266	nbytes = NRLEAFDBT(data->size);
267	if (h->upper - h->lower < nbytes + sizeof(indx_t)) {
268		status = __bt_split(t, h, NULL, data, dflags, nbytes, index);
269		if (status == RET_SUCCESS)
270			++t->bt_nrecs;
271		return (status);
272	}
273
274	if (index < (nxtindex = NEXTINDEX(h)))
275		memmove(h->linp + index + 1, h->linp + index,
276		    (nxtindex - index) * sizeof(indx_t));
277	h->lower += sizeof(indx_t);
278
279	h->linp[index] = h->upper -= nbytes;
280	dest = (char *)h + h->upper;
281	WR_RLEAF(dest, data, dflags);
282
283	++t->bt_nrecs;
284	F_SET(t, B_MODIFIED);
285	mpool_put(t->bt_mp, h, MPOOL_DIRTY);
286
287	return (RET_SUCCESS);
288}
289