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