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