42ef9cc30d18313819ad9996dd3e64519e3f9dad
[kopensolaris-gnu/glibc.git] / db2 / btree / bt_recno.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1997
5  *      Sleepycat Software.  All rights reserved.
6  */
7
8 #include "config.h"
9
10 #ifndef lint
11 static const char sccsid[] = "@(#)bt_recno.c    10.15 (Sleepycat) 9/3/97";
12 #endif /* not lint */
13
14 #ifndef NO_SYSTEM_INCLUDES
15 #include <sys/types.h>
16
17 #include <errno.h>
18 #include <limits.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #endif
23
24 #include "db_int.h"
25 #include "db_page.h"
26 #include "btree.h"
27
28 static int __ram_add __P((DB *, db_recno_t *, DBT *, int, int));
29 static int __ram_c_close __P((DBC *));
30 static int __ram_c_del __P((DBC *, int));
31 static int __ram_c_get __P((DBC *, DBT *, DBT *, int));
32 static int __ram_c_put __P((DBC *, DBT *, DBT *, int));
33 static int __ram_fmap __P((DB *, db_recno_t));
34 static int __ram_get __P((DB *, DB_TXN *, DBT *, DBT *, int));
35 static int __ram_put __P((DB *, DB_TXN *, DBT *, DBT *, int));
36 static int __ram_source __P((DB *, RECNO *, const char *));
37 static int __ram_sync __P((DB *, int));
38 static int __ram_update __P((DB *, db_recno_t, int));
39 static int __ram_vmap __P((DB *, db_recno_t));
40 static int __ram_writeback __P((DB *));
41
42 /*
43  * If we're renumbering records, then we have to detect in the cursor that a
44  * record was deleted, and adjust the cursor as necessary.  If not renumbering
45  * records, then we can detect this by looking at the actual record, so we
46  * ignore the cursor delete flag.
47  */
48 #define CD_SET(dbp, cp) {                                               \
49         if (F_ISSET(dbp, DB_RE_RENUMBER))                               \
50                 F_SET(cp, CR_DELETED);                                  \
51 }
52 #define CD_CLR(dbp, cp) {                                               \
53         if (F_ISSET(dbp, DB_RE_RENUMBER))                               \
54                 F_CLR(cp, CR_DELETED);                                  \
55 }
56 #define CD_ISSET(dbp, cp)                                               \
57         (F_ISSET(dbp, DB_RE_RENUMBER) && F_ISSET(cp, CR_DELETED))
58
59 /*
60  * __ram_open --
61  *      Recno open function.
62  *
63  * PUBLIC: int __ram_open __P((DB *, DBTYPE, DB_INFO *));
64  */
65 int
66 __ram_open(dbp, type, dbinfo)
67         DB *dbp;
68         DBTYPE type;
69         DB_INFO *dbinfo;
70 {
71         BTREE *t;
72         RECNO *rp;
73         int ret;
74
75         ret = 0;
76
77         /* Allocate and initialize the private RECNO structure. */
78         if ((rp = (RECNO *)calloc(1, sizeof(*rp))) == NULL)
79                 return (errno);
80
81         if (dbinfo != NULL) {
82                 /*
83                  * If the user specified a source tree, open it and map it in.
84                  *
85                  * !!!
86                  * We don't complain if the user specified transactions or
87                  * threads.  It's possible to make it work, but you'd better
88                  * know what you're doing!
89                  */
90                 if (dbinfo->re_source == NULL) {
91                         rp->re_fd = -1;
92                         F_SET(rp, RECNO_EOF);
93                 } else {
94                         if ((ret =
95                             __ram_source(dbp, rp, dbinfo->re_source)) != 0)
96                         goto err;
97                 }
98
99                 /* Copy delimiter, length and padding values. */
100                 rp->re_delim =
101                     F_ISSET(dbp, DB_RE_DELIMITER) ? dbinfo->re_delim : '\n';
102                 rp->re_pad = F_ISSET(dbp, DB_RE_PAD) ? dbinfo->re_pad : ' ';
103
104                 if (F_ISSET(dbp, DB_RE_FIXEDLEN)) {
105                         if ((rp->re_len = dbinfo->re_len) == 0) {
106                                 __db_err(dbp->dbenv,
107                                     "record length must be greater than 0");
108                                 ret = EINVAL;
109                                 goto err;
110                         }
111                 } else
112                         rp->re_len = 0;
113         } else {
114                 rp->re_delim = '\n';
115                 rp->re_pad = ' ';
116                 rp->re_fd = -1;
117                 F_SET(rp, RECNO_EOF);
118         }
119
120         /* Open the underlying btree. */
121         if ((ret = __bam_open(dbp, DB_RECNO, dbinfo)) != 0)
122                 goto err;
123
124         /* Set the routines necessary to make it look like a recno tree. */
125         dbp->cursor = __ram_cursor;
126         dbp->del = __ram_delete;
127         dbp->get = __ram_get;
128         dbp->put = __ram_put;
129         dbp->sync = __ram_sync;
130
131         /* Link in the private recno structure. */
132         ((BTREE *)dbp->internal)->bt_recno = rp;
133
134         /* If we're snapshotting an underlying source file, do it now. */
135         if (dbinfo != NULL && F_ISSET(dbinfo, DB_SNAPSHOT))
136                 if ((ret = __ram_snapshot(dbp)) != 0 && ret != DB_NOTFOUND)
137                         goto err;
138
139         return (0);
140
141 err:    /* If we mmap'd a source file, discard it. */
142         if (rp->re_smap != NULL)
143                 (void)__db_munmap(rp->re_smap, rp->re_msize);
144
145         /* If we opened a source file, discard it. */
146         if (rp->re_fd != -1)
147                 (void)__db_close(rp->re_fd);
148         if (rp->re_source != NULL)
149                 FREES(rp->re_source);
150
151         /* If we allocated room for key/data return, discard it. */
152         t = dbp->internal;
153         if (t->bt_rkey.data != NULL)
154                 free(t->bt_rkey.data);
155
156         FREE(rp, sizeof(*rp));
157
158         return (ret);
159 }
160
161 /*
162  * __ram_cursor --
163  *      Recno db->cursor function.
164  *
165  * PUBLIC: int __ram_cursor __P((DB *, DB_TXN *, DBC **));
166  */
167 int
168 __ram_cursor(dbp, txn, dbcp)
169         DB *dbp;
170         DB_TXN *txn;
171         DBC **dbcp;
172 {
173         RCURSOR *cp;
174         DBC *dbc;
175
176         DEBUG_LWRITE(dbp, txn, "ram_cursor", NULL, NULL, 0);
177
178         if ((dbc = (DBC *)calloc(1, sizeof(DBC))) == NULL)
179                 return (ENOMEM);
180         if ((cp = (RCURSOR *)calloc(1, sizeof(RCURSOR))) == NULL) {
181                 free(dbc);
182                 return (ENOMEM);
183         }
184
185         cp->dbc = dbc;
186         cp->recno = RECNO_OOB;
187
188         dbc->dbp = dbp;
189         dbc->txn = txn;
190         dbc->internal = cp;
191         dbc->c_close = __ram_c_close;
192         dbc->c_del = __ram_c_del;
193         dbc->c_get = __ram_c_get;
194         dbc->c_put = __ram_c_put;
195
196         /* All cursor structures hang off the main DB structure. */
197         DB_THREAD_LOCK(dbp);
198         TAILQ_INSERT_HEAD(&dbp->curs_queue, dbc, links);
199         DB_THREAD_UNLOCK(dbp);
200
201         *dbcp = dbc;
202         return (0);
203 }
204
205 /*
206  * __ram_get --
207  *      Recno db->get function.
208  */
209 static int
210 __ram_get(argdbp, txn, key, data, flags)
211         DB *argdbp;
212         DB_TXN *txn;
213         DBT *key, *data;
214         int flags;
215 {
216         BTREE *t;
217         DB *dbp;
218         PAGE *h;
219         db_indx_t indx;
220         db_recno_t recno;
221         int exact, ret, stack;
222
223         stack = 0;
224
225         DEBUG_LWRITE(argdbp, txn, "ram_get", key, NULL, flags);
226
227         /* Check for invalid flags. */
228         if ((ret = __db_getchk(argdbp, key, data, flags)) != 0)
229                 return (ret);
230
231         GETHANDLE(argdbp, txn, &dbp, ret);
232         t = dbp->internal;
233
234         /* Check the user's record number and fill in as necessary. */
235         if ((ret = __ram_getno(dbp, key, &recno, 0)) != 0)
236                 goto done;
237
238         /* Search the tree for the record. */
239         if ((ret = __bam_rsearch(dbp, &recno, S_FIND, 1, &exact)) != 0)
240                 goto done;
241         if (!exact)
242                 return (DB_NOTFOUND);
243         stack = 1;
244
245         h = t->bt_csp->page;
246         indx = t->bt_csp->indx;
247
248         /* If the record has already been deleted, we couldn't have found it. */
249         if (B_DISSET(GET_BKEYDATA(h, indx)->type)) {
250                 ret = DB_KEYEMPTY;
251                 goto done;
252         }
253
254         /* Return the data item. */
255         ret = __db_ret(dbp,
256             h, indx, data, &t->bt_rdata.data, &t->bt_rdata.ulen);
257         ++t->lstat.bt_get;
258
259 done:   /* Discard the stack. */
260         if (stack)
261                 __bam_stkrel(dbp);
262
263         PUTHANDLE(dbp);
264         return (ret);
265 }
266
267 /*
268  * __ram_put --
269  *      Recno db->put function.
270  */
271 static int
272 __ram_put(argdbp, txn, key, data, flags)
273         DB *argdbp;
274         DB_TXN *txn;
275         DBT *key, *data;
276         int flags;
277 {
278         BTREE *t;
279         DB *dbp;
280         db_recno_t recno;
281         int ret;
282
283         DEBUG_LWRITE(argdbp, txn, "ram_put", key, data, flags);
284
285         /* Check for invalid flags. */
286         if ((ret = __db_putchk(argdbp,
287             key, data, flags, F_ISSET(argdbp, DB_AM_RDONLY), 0)) != 0)
288                 return (ret);
289
290         GETHANDLE(argdbp, txn, &dbp, ret);
291
292         /*
293          * If we're appending to the tree, make sure we've read in all of
294          * the backing source file.  Otherwise, check the user's record
295          * number and fill in as necessary.
296          */
297         ret = LF_ISSET(DB_APPEND) ?
298             __ram_snapshot(dbp) : __ram_getno(dbp, key, &recno, 1);
299
300         /* Add the record. */
301         if (ret == 0)
302                 ret = __ram_add(dbp, &recno, data, flags, 0);
303
304         /* If we're appending to the tree, we have to return the record. */
305         if (ret == 0 && LF_ISSET(DB_APPEND)) {
306                 t = dbp->internal;
307                 ret = __db_retcopy(key, &recno, sizeof(recno),
308                     &t->bt_rkey.data, &t->bt_rkey.ulen, dbp->db_malloc);
309         }
310
311         PUTHANDLE(dbp);
312         return (ret);
313 }
314
315 /*
316  * __ram_sync --
317  *      Recno db->sync function.
318  */
319 static int
320 __ram_sync(argdbp, flags)
321         DB *argdbp;
322         int flags;
323 {
324         DB *dbp;
325         int ret;
326
327         DEBUG_LWRITE(argdbp, NULL, "ram_sync", NULL, NULL, flags);
328
329         /* Sync the underlying btree. */
330         if ((ret = __bam_sync(argdbp, flags)) != 0)
331                 return (ret);
332
333         /* Copy back the backing source file. */
334         GETHANDLE(argdbp, NULL, &dbp, ret);
335         ret = __ram_writeback(dbp);
336         PUTHANDLE(dbp);
337
338         return (ret);
339 }
340
341 /*
342  * __ram_close --
343  *      Recno db->close function.
344  *
345  * PUBLIC: int __ram_close __P((DB *));
346  */
347 int
348 __ram_close(argdbp)
349         DB *argdbp;
350 {
351         RECNO *rp;
352
353         DEBUG_LWRITE(argdbp, NULL, "ram_close", NULL, NULL, 0);
354
355         rp = ((BTREE *)argdbp->internal)->bt_recno;
356
357         /* Close any underlying mmap region. */
358         if (rp->re_smap != NULL)
359                 (void)__db_munmap(rp->re_smap, rp->re_msize);
360
361         /* Close any backing source file descriptor. */
362         if (rp->re_fd != -1)
363                 (void)__db_close(rp->re_fd);
364
365         /* Free any backing source file name. */
366         if (rp->re_source != NULL)
367                 FREES(rp->re_source);
368
369         /* Free allocated memory. */
370         FREE(rp, sizeof(RECNO));
371         ((BTREE *)argdbp->internal)->bt_recno = NULL;
372
373         /* Close the underlying btree. */
374         return (__bam_close(argdbp));
375 }
376
377 /*
378  * __ram_c_close --
379  *      Recno cursor->close function.
380  */
381 static int
382 __ram_c_close(dbc)
383         DBC *dbc;
384 {
385         DB *dbp;
386
387         DEBUG_LWRITE(dbc->dbp, dbc->txn, "ram_c_close", NULL, NULL, 0);
388
389         dbp = dbc->dbp;
390
391         /* Remove the cursor from the queue. */
392         DB_THREAD_LOCK(dbp);
393         TAILQ_REMOVE(&dbp->curs_queue, dbc, links);
394         DB_THREAD_UNLOCK(dbp);
395
396         /* Discard the structures. */
397         FREE(dbc->internal, sizeof(RCURSOR));
398         FREE(dbc, sizeof(DBC));
399
400         return (0);
401 }
402
403 /*
404  * __ram_c_del --
405  *      Recno cursor->c_del function.
406  */
407 static int
408 __ram_c_del(dbc, flags)
409         DBC *dbc;
410         int flags;
411 {
412         DBT key;
413         RCURSOR *cp;
414         int ret;
415
416         DEBUG_LWRITE(dbc->dbp, dbc->txn, "ram_c_del", NULL, NULL, flags);
417
418         cp = dbc->internal;
419
420         /* Check for invalid flags. */
421         if ((ret = __db_cdelchk(dbc->dbp, flags,
422             F_ISSET(dbc->dbp, DB_AM_RDONLY), cp->recno != RECNO_OOB)) != 0)
423                 return (ret);
424
425         /* If already deleted, return failure. */
426         if (CD_ISSET(dbc->dbp, cp))
427                 return (DB_KEYEMPTY);
428
429         /* Build a normal delete request. */
430         memset(&key, 0, sizeof(key));
431         key.data = &cp->recno;
432         key.size = sizeof(db_recno_t);
433         if ((ret = __ram_delete(dbc->dbp, dbc->txn, &key, 0)) == 0)
434                 CD_SET(dbc->dbp, cp);
435
436         return (ret);
437 }
438
439 /*
440  * __ram_c_get --
441  *      Recno cursor->c_get function.
442  */
443 static int
444 __ram_c_get(dbc, key, data, flags)
445         DBC *dbc;
446         DBT *key, *data;
447         int flags;
448 {
449         BTREE *t;
450         DB *dbp;
451         RCURSOR *cp, copy;
452         int ret;
453
454         DEBUG_LREAD(dbc->dbp, dbc->txn, "ram_c_get",
455             flags == DB_SET || flags == DB_SET_RANGE ? key : NULL,
456             NULL, flags);
457
458         cp = dbc->internal;
459         dbp = dbc->dbp;
460
461         /* Check for invalid flags. */
462         if ((ret = __db_cgetchk(dbc->dbp,
463             key, data, flags, cp->recno != RECNO_OOB)) != 0)
464                 return (ret);
465
466         GETHANDLE(dbc->dbp, dbc->txn, &dbp, ret);
467         t = dbp->internal;
468
469         /* Initialize the cursor for a new retrieval. */
470         copy = *cp;
471
472 retry:  /* Update the record number. */
473         switch (flags) {
474         case DB_CURRENT:
475                 if (CD_ISSET(dbp, cp)) {
476                         PUTHANDLE(dbp);
477                         return (DB_KEYEMPTY);
478                 }
479                 break;
480         case DB_NEXT:
481                 if (CD_ISSET(dbp, cp))
482                         break;
483                 if (cp->recno != RECNO_OOB) {
484                         ++cp->recno;
485                         break;
486                 }
487                 /* FALLTHROUGH */
488         case DB_FIRST:
489                 flags = DB_NEXT;
490                 cp->recno = 1;
491                 break;
492         case DB_PREV:
493                 if (cp->recno != RECNO_OOB) {
494                         if (cp->recno == 1)
495                                 return (DB_NOTFOUND);
496                         --cp->recno;
497                         break;
498                 }
499                 /* FALLTHROUGH */
500         case DB_LAST:
501                 flags = DB_PREV;
502                 if (((ret = __ram_snapshot(dbp)) != 0) && ret != DB_NOTFOUND)
503                         goto err;
504                 if ((ret = __bam_nrecs(dbp, &cp->recno)) != 0)
505                         goto err;
506                 if (cp->recno == 0)
507                         return (DB_NOTFOUND);
508                 break;
509         case DB_SET:
510         case DB_SET_RANGE:
511                 if ((ret = __ram_getno(dbp, key, &cp->recno, 0)) != 0)
512                         goto err;
513                 break;
514         }
515
516         /*
517          * Return the key if the user didn't give us one, and then pass it
518          * into __ram_get().
519          */
520         if (flags != DB_SET && flags != DB_SET_RANGE &&
521             (ret = __db_retcopy(key, &cp->recno, sizeof(cp->recno),
522             &t->bt_rkey.data, &t->bt_rkey.ulen, dbp->db_malloc)) != 0)
523                 return (ret);
524
525         /*
526          * The cursor was reset, so the delete adjustment is no
527          * longer necessary.
528          */
529         CD_CLR(dbp, cp);
530
531         /*
532          * Retrieve the record.
533          *
534          * Skip any keys that don't really exist.
535          */
536         if ((ret = __ram_get(dbp, dbc->txn, key, data, 0)) != 0)
537                 if (ret == DB_KEYEMPTY &&
538                     (flags == DB_NEXT || flags == DB_PREV))
539                         goto retry;
540
541 err:    if (ret != 0)
542                 *cp = copy;
543
544         PUTHANDLE(dbp);
545         return (ret);
546 }
547
548 /*
549  * __ram_c_put --
550  *      Recno cursor->c_put function.
551  */
552 static int
553 __ram_c_put(dbc, key, data, flags)
554         DBC *dbc;
555         DBT *key, *data;
556         int flags;
557 {
558         BTREE *t;
559         RCURSOR *cp, copy;
560         DB *dbp;
561         int exact, ret;
562         void *arg;
563
564         DEBUG_LWRITE(dbc->dbp, dbc->txn, "ram_c_put", NULL, data, flags);
565
566         cp = dbc->internal;
567
568         if ((ret = __db_cputchk(dbc->dbp, key, data, flags,
569             F_ISSET(dbc->dbp, DB_AM_RDONLY), cp->recno != RECNO_OOB)) != 0)
570                 return (ret);
571
572         GETHANDLE(dbc->dbp, dbc->txn, &dbp, ret);
573         t = dbp->internal;
574
575         /* Initialize the cursor for a new retrieval. */
576         copy = *cp;
577
578         /*
579          * To split, we need a valid key for the page.  Since it's a cursor,
580          * we have to build one.
581          *
582          * The split code discards all short-term locks and stack pages.
583          */
584         if (0) {
585 split:          arg = &cp->recno;
586                 if ((ret = __bam_split(dbp, arg)) != 0)
587                         goto err;
588         }
589
590         if ((ret = __bam_rsearch(dbp, &cp->recno, S_INSERT, 1, &exact)) != 0)
591                 goto err;
592         if (!exact) {
593                 ret = DB_NOTFOUND;
594                 goto err;
595         }
596         if ((ret = __bam_iitem(dbp, &t->bt_csp->page,
597             &t->bt_csp->indx, key, data, flags, 0)) == DB_NEEDSPLIT) {
598                 if ((ret = __bam_stkrel(dbp)) != 0)
599                         goto err;
600                 goto split;
601         }
602         if ((ret = __bam_stkrel(dbp)) != 0)
603                 goto err;
604
605         if (flags != DB_CURRENT) {
606                 /* Adjust the counts. */
607                 if ((ret = __bam_adjust(dbp, t, 1)) != 0)
608                         goto err;
609
610                 switch (flags) {
611                 case DB_AFTER:
612                         /* Adjust the cursors. */
613                         __ram_ca(dbp, cp->recno, CA_IAFTER);
614
615                         /* Set this cursor to reference the new record. */
616                         cp->recno = copy.recno + 1;
617                         break;
618                 case DB_BEFORE:
619                         /* Adjust the cursors. */
620                         __ram_ca(dbp, cp->recno, CA_IBEFORE);
621
622                         /* Set this cursor to reference the new record. */
623                         cp->recno = copy.recno;
624                         break;
625                 }
626
627         }
628
629         /*
630          * The cursor was reset, so the delete adjustment is no
631          * longer necessary.
632          */
633         CD_CLR(dbp, cp);
634
635 err:    if (ret != 0)
636                 *cp = copy;
637
638         PUTHANDLE(dbp);
639         return (ret);
640 }
641
642 /*
643  * __ram_ca --
644  *      Adjust cursors.
645  *
646  * PUBLIC: void __ram_ca __P((DB *, db_recno_t, ca_recno_arg));
647  */
648 void
649 __ram_ca(dbp, recno, op)
650         DB *dbp;
651         db_recno_t recno;
652         ca_recno_arg op;
653 {
654         DBC *dbc;
655         RCURSOR *cp;
656
657         /*
658          * Adjust the cursors.  See the comment in __bam_ca_delete().
659          */
660         DB_THREAD_LOCK(dbp);
661         for (dbc = TAILQ_FIRST(&dbp->curs_queue);
662             dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
663                 cp = (RCURSOR *)dbc->internal;
664                 switch (op) {
665                 case CA_DELETE:
666                         if (recno > cp->recno)
667                                 --cp->recno;
668                         break;
669                 case CA_IAFTER:
670                         if (recno > cp->recno)
671                                 ++cp->recno;
672                         break;
673                 case CA_IBEFORE:
674                         if (recno >= cp->recno)
675                                 ++cp->recno;
676                         break;
677                 }
678         }
679         DB_THREAD_UNLOCK(dbp);
680 }
681
682 #ifdef DEBUG
683 /*
684  * __ram_cprint --
685  *      Display the current recno cursor list.
686  */
687 int
688 __ram_cprint(dbp)
689         DB *dbp;
690 {
691         DBC *dbc;
692         RCURSOR *cp;
693
694         DB_THREAD_LOCK(dbp);
695         for (dbc = TAILQ_FIRST(&dbp->curs_queue);
696             dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
697                 cp = (RCURSOR *)dbc->internal;
698                 fprintf(stderr,
699                     "%#0x: recno: %lu\n", (u_int)cp, (u_long)cp->recno);
700         }
701         DB_THREAD_UNLOCK(dbp);
702         return (0);
703 }
704 #endif /* DEBUG */
705
706 /*
707  * __ram_getno --
708  *      Check the user's record number, and make sure we've seen it.
709  *
710  * PUBLIC: int __ram_getno __P((DB *, const DBT *, db_recno_t *, int));
711  */
712 int
713 __ram_getno(dbp, key, rep, can_create)
714         DB *dbp;
715         const DBT *key;
716         db_recno_t *rep;
717         int can_create;
718 {
719         db_recno_t recno;
720
721         /* Check the user's record number. */
722         if ((recno = *(db_recno_t *)key->data) == 0) {
723                 __db_err(dbp->dbenv, "illegal record number of 0");
724                 return (EINVAL);
725         }
726         if (rep != NULL)
727                 *rep = recno;
728
729         /*
730          * Btree can neither create records or read them in.  Recno can
731          * do both, see if we can find the record.
732          */
733         return (dbp->type == DB_RECNO ?
734             __ram_update(dbp, recno, can_create) : 0);
735 }
736
737 /*
738  * __ram_snapshot --
739  *      Read in any remaining records from the backing input file.
740  *
741  * PUBLIC: int __ram_snapshot __P((DB *));
742  */
743 int
744 __ram_snapshot(dbp)
745         DB *dbp;
746 {
747         return (__ram_update(dbp, DB_MAX_RECORDS, 0));
748 }
749
750 /*
751  * __ram_update --
752  *      Ensure the tree has records up to and including the specified one.
753  */
754 static int
755 __ram_update(dbp, recno, can_create)
756         DB *dbp;
757         db_recno_t recno;
758         int can_create;
759 {
760         BTREE *t;
761         RECNO *rp;
762         db_recno_t nrecs;
763         int ret;
764
765         t = dbp->internal;
766         rp = t->bt_recno;
767
768         /*
769          * If we can't create records and we've read the entire backing input
770          * file, we're done.
771          */
772         if (!can_create && F_ISSET(rp, RECNO_EOF))
773                 return (0);
774
775         /*
776          * If we haven't seen this record yet, try to get it from the original
777          * file.
778          */
779         if ((ret = __bam_nrecs(dbp, &nrecs)) != 0)
780                 return (ret);
781         if (!F_ISSET(rp, RECNO_EOF) && recno > nrecs) {
782                 if ((ret = rp->re_irec(dbp, recno)) != 0)
783                         return (ret);
784                 if ((ret = __bam_nrecs(dbp, &nrecs)) != 0)
785                         return (ret);
786         }
787
788         /*
789          * If we can create records, create empty ones up to the requested
790          * record.
791          */
792         if (!can_create || recno <= nrecs + 1)
793                 return (0);
794
795         t->bt_rdata.dlen = 0;
796         t->bt_rdata.doff = 0;
797         t->bt_rdata.flags = 0;
798         if (F_ISSET(dbp, DB_RE_FIXEDLEN)) {
799                 if (t->bt_rdata.ulen < rp->re_len) {
800                         t->bt_rdata.data = t->bt_rdata.data == NULL ?
801                             (void *)malloc(rp->re_len) :
802                             (void *)realloc(t->bt_rdata.data, rp->re_len);
803                         if (t->bt_rdata.data == NULL) {
804                                 t->bt_rdata.ulen = 0;
805                                 return (ENOMEM);
806                         }
807                         t->bt_rdata.ulen = rp->re_len;
808                 }
809                 t->bt_rdata.size = rp->re_len;
810                 memset(t->bt_rdata.data, rp->re_pad, rp->re_len);
811         } else
812                 t->bt_rdata.size = 0;
813
814         while (recno > ++nrecs)
815                 if ((ret = __ram_add(dbp,
816                     &nrecs, &t->bt_rdata, 0, BI_DELETED)) != 0)
817                         return (ret);
818         return (0);
819 }
820
821 /*
822  * __ram_source --
823  *      Load information about the backing file.
824  */
825 static int
826 __ram_source(dbp, rp, fname)
827         DB *dbp;
828         RECNO *rp;
829         const char *fname;
830 {
831         off_t size;
832         int oflags, ret;
833
834         if ((ret = __db_appname(dbp->dbenv,
835             DB_APP_DATA, NULL, fname, NULL, &rp->re_source)) != 0)
836                 return (ret);
837
838         oflags = F_ISSET(dbp, DB_AM_RDONLY) ? DB_RDONLY : 0;
839         if ((ret =
840             __db_fdopen(rp->re_source, oflags, oflags, 0, &rp->re_fd)) != 0) {
841                 __db_err(dbp->dbenv, "%s: %s", rp->re_source, strerror(ret));
842                 goto err;
843         }
844
845         /*
846          * XXX
847          * We'd like to test to see if the file is too big to mmap.  Since we
848          * don't know what size or type off_t's or size_t's are, or the largest
849          * unsigned integral type is, or what random insanity the local C
850          * compiler will perpetrate, doing the comparison in a portable way is
851          * flatly impossible.  Hope that mmap fails if the file is too large.
852          */
853         if ((ret =
854             __db_stat(dbp->dbenv, rp->re_source, rp->re_fd, &size, NULL)) != 0)
855                 goto err;
856         if (size == 0) {
857                 F_SET(rp, RECNO_EOF);
858                 return (0);
859         }
860
861         if ((ret = __db_mmap(rp->re_fd, (size_t)size, 1, 1, &rp->re_smap)) != 0)
862                 goto err;
863         rp->re_cmap = rp->re_smap;
864         rp->re_emap = (u_int8_t *)rp->re_smap + (rp->re_msize = size);
865         rp->re_irec = F_ISSET(dbp, DB_RE_FIXEDLEN) ?  __ram_fmap : __ram_vmap;
866         return (0);
867
868 err:    FREES(rp->re_source)
869         return (ret);
870 }
871
872 /*
873  * __ram_writeback --
874  *      Rewrite the backing file.
875  */
876 static int
877 __ram_writeback(dbp)
878         DB *dbp;
879 {
880         RECNO *rp;
881         DBT key, data;
882         db_recno_t keyno;
883         ssize_t nw;
884         int fd, ret, t_ret;
885         u_int8_t delim, *pad;
886
887         rp = ((BTREE *)dbp->internal)->bt_recno;
888
889         /* If the file wasn't modified, we're done. */
890         if (!F_ISSET(rp, RECNO_MODIFIED))
891                 return (0);
892
893         /* If there's no backing source file, we're done. */
894         if (rp->re_source == NULL) {
895                 F_CLR(rp, RECNO_MODIFIED);
896                 return (0);
897         }
898
899         /*
900          * Read any remaining records into the tree.
901          *
902          * XXX
903          * This is why we can't support transactions when applications specify
904          * backing (re_source) files.  At this point we have to read in the
905          * rest of the records from the file so that we can write all of the
906          * records back out again, which could modify a page for which we'd
907          * have to log changes and which we don't have locked.  This could be
908          * partially fixed by taking a snapshot of the entire file during the
909          * db_open(), or, since db_open() isn't transaction protected, as part
910          * of the first DB operation.  But, if a checkpoint occurs then, the
911          * part of the log holding the copy of the file could be discarded, and
912          * that would make it impossible to recover in the face of disaster.
913          * This could all probably be fixed, but it would require transaction
914          * protecting the backing source file, i.e. mpool would have to know
915          * about it, and we don't want to go there.
916          */
917         if ((ret = __ram_snapshot(dbp)) != 0 && ret != DB_NOTFOUND)
918                 return (ret);
919
920         /*
921          * !!!
922          * Close any underlying mmap region.  This is required for Windows NT
923          * (4.0, Service Pack 2) -- if the file is still mapped, the following
924          * open will fail.
925          */
926         if (rp->re_smap != NULL) {
927                 (void)__db_munmap(rp->re_smap, rp->re_msize);
928                 rp->re_smap = NULL;
929         }
930
931         /* Get rid of any backing file descriptor, just on GP's. */
932         if (rp->re_fd != -1) {
933                 (void)__db_close(rp->re_fd);
934                 rp->re_fd = -1;
935         }
936
937         /* Open the file, truncating it. */
938         if ((ret = __db_fdopen(rp->re_source,
939             DB_SEQUENTIAL | DB_TRUNCATE,
940             DB_SEQUENTIAL | DB_TRUNCATE, 0, &fd)) != 0) {
941                 __db_err(dbp->dbenv, "%s: %s", rp->re_source, strerror(ret));
942                 return (ret);
943         }
944
945         /*
946          * We step through the records, writing each one out.  Use the record
947          * number and the dbp->get() function, instead of a cursor, so we find
948          * and write out "deleted" or non-existent records.
949          */
950         memset(&key, 0, sizeof(key));
951         memset(&data, 0, sizeof(data));
952         key.size = sizeof(db_recno_t);
953         key.data = &keyno;
954
955         /*
956          * We'll need the delimiter if we're doing variable-length records,
957          * and the pad character if we're doing fixed-length records.
958          */
959         delim = rp->re_delim;
960         if (F_ISSET(dbp, DB_RE_FIXEDLEN)) {
961                 if ((pad = malloc(rp->re_len)) == NULL) {
962                         ret = ENOMEM;
963                         goto err;
964                 }
965                 memset(pad, rp->re_pad, rp->re_len);
966         } else
967                 pad = NULL;                     /* XXX: Shut the compiler up. */
968         for (keyno = 1;; ++keyno) {
969                 switch (ret = dbp->get(dbp, NULL, &key, &data, 0)) {
970                 case 0:
971                         if ((ret =
972                             __db_write(fd, data.data, data.size, &nw)) != 0)
973                                 goto err;
974                         if (nw != (ssize_t)data.size) {
975                                 ret = EIO;
976                                 goto err;
977                         }
978                         break;
979                 case DB_KEYEMPTY:
980                         if (F_ISSET(dbp, DB_RE_FIXEDLEN)) {
981                                 if ((ret =
982                                     __db_write(fd, pad, rp->re_len, &nw)) != 0)
983                                         goto err;
984                                 if (nw != (ssize_t)rp->re_len) {
985                                         ret = EIO;
986                                         goto err;
987                                 }
988                         }
989                         break;
990                 case DB_NOTFOUND:
991                         ret = 0;
992                         goto done;
993                 }
994                 if (!F_ISSET(dbp, DB_RE_FIXEDLEN)) {
995                         if ((ret = __db_write(fd, &delim, 1, &nw)) != 0)
996                                 goto err;
997                         if (nw != 1) {
998                                 ret = EIO;
999                                 goto err;
1000                         }
1001                 }
1002         }
1003
1004 err:
1005 done:   /* Close the file descriptor. */
1006         if ((t_ret = __db_close(fd)) != 0 || ret == 0)
1007                 ret = t_ret;
1008
1009         if (ret == 0)
1010                 F_CLR(rp, RECNO_MODIFIED);
1011         return (ret);
1012 }
1013
1014 /*
1015  * __ram_fmap --
1016  *      Get fixed length records from a file.
1017  */
1018 static int
1019 __ram_fmap(dbp, top)
1020         DB *dbp;
1021         db_recno_t top;
1022 {
1023         BTREE *t;
1024         DBT data;
1025         RECNO *rp;
1026         db_recno_t recno;
1027         u_int32_t len;
1028         u_int8_t *sp, *ep, *p;
1029         int ret;
1030
1031         if ((ret = __bam_nrecs(dbp, &recno)) != 0)
1032                 return (ret);
1033
1034         t = dbp->internal;
1035         rp = t->bt_recno;
1036         if (t->bt_rdata.ulen < rp->re_len) {
1037                 t->bt_rdata.data = t->bt_rdata.data == NULL ?
1038                     (void *)malloc(rp->re_len) :
1039                     (void *)realloc(t->bt_rdata.data, rp->re_len);
1040                 if (t->bt_rdata.data == NULL) {
1041                         t->bt_rdata.ulen = 0;
1042                         return (ENOMEM);
1043                 }
1044                 t->bt_rdata.ulen = rp->re_len;
1045         }
1046
1047         memset(&data, 0, sizeof(data));
1048         data.data = t->bt_rdata.data;
1049         data.size = rp->re_len;
1050
1051         sp = (u_int8_t *)rp->re_cmap;
1052         ep = (u_int8_t *)rp->re_emap;
1053         while (recno <= top) {
1054                 if (sp >= ep) {
1055                         F_SET(rp, RECNO_EOF);
1056                         return (DB_NOTFOUND);
1057                 }
1058                 len = rp->re_len;
1059                 for (p = t->bt_rdata.data;
1060                     sp < ep && len > 0; *p++ = *sp++, --len);
1061
1062                 /*
1063                  * Another process may have read some portion of the input
1064                  * file already, in which case we just want to discard the
1065                  * new record.
1066                  *
1067                  * XXX
1068                  * We should just do a seek, since the records are fixed
1069                  * length.
1070                  */
1071                 if (rp->re_last >= recno) {
1072                         if (len != 0)
1073                                 memset(p, rp->re_pad, len);
1074
1075                         ++recno;
1076                         if ((ret = __ram_add(dbp, &recno, &data, 0, 0)) != 0)
1077                                 return (ret);
1078                 }
1079                 ++rp->re_last;
1080         }
1081         rp->re_cmap = sp;
1082         return (0);
1083 }
1084
1085 /*
1086  * __ram_vmap --
1087  *      Get variable length records from a file.
1088  */
1089 static int
1090 __ram_vmap(dbp, top)
1091         DB *dbp;
1092         db_recno_t top;
1093 {
1094         BTREE *t;
1095         DBT data;
1096         RECNO *rp;
1097         db_recno_t recno;
1098         u_int8_t *sp, *ep;
1099         int delim, ret;
1100
1101         t = dbp->internal;
1102         rp = t->bt_recno;
1103
1104         if ((ret = __bam_nrecs(dbp, &recno)) != 0)
1105                 return (ret);
1106
1107         memset(&data, 0, sizeof(data));
1108
1109         delim = rp->re_delim;
1110
1111         sp = (u_int8_t *)rp->re_cmap;
1112         ep = (u_int8_t *)rp->re_emap;
1113         while (recno <= top) {
1114                 if (sp >= ep) {
1115                         F_SET(rp, RECNO_EOF);
1116                         return (DB_NOTFOUND);
1117                 }
1118                 for (data.data = sp; sp < ep && *sp != delim; ++sp);
1119
1120                 /*
1121                  * Another process may have read some portion of the input
1122                  * file already, in which case we just want to discard the
1123                  * new record.
1124                  */
1125                 if (rp->re_last >= recno) {
1126                         data.size = sp - (u_int8_t *)data.data;
1127                         ++recno;
1128                         if ((ret = __ram_add(dbp, &recno, &data, 0, 0)) != 0)
1129                                 return (ret);
1130                 }
1131                 ++rp->re_last;
1132                 ++sp;
1133         }
1134         rp->re_cmap = sp;
1135         return (0);
1136 }
1137
1138 /*
1139  * __ram_add --
1140  *      Add records into the tree.
1141  */
1142 static int
1143 __ram_add(dbp, recnop, data, flags, bi_flags)
1144         DB *dbp;
1145         db_recno_t *recnop;
1146         DBT *data;
1147         int flags, bi_flags;
1148 {
1149         BTREE *t;
1150         PAGE *h;
1151         db_indx_t indx;
1152         int exact, ret, stack;
1153
1154         t = dbp->internal;
1155
1156 retry:  /* Find the slot for insertion. */
1157         if ((ret = __bam_rsearch(dbp, recnop,
1158             S_INSERT | (LF_ISSET(DB_APPEND) ? S_APPEND : 0), 1, &exact)) != 0)
1159                 return (ret);
1160         h = t->bt_csp->page;
1161         indx = t->bt_csp->indx;
1162         stack = 1;
1163
1164         /*
1165          * The recno access method doesn't currently support duplicates, so
1166          * if an identical key is already in the tree we're either overwriting
1167          * it or an error is returned.
1168          */
1169         if (exact && LF_ISSET(DB_NOOVERWRITE)) {
1170                 ret = DB_KEYEXIST;
1171                 goto err;
1172         }
1173
1174         /*
1175          * Select the arguments for __bam_iitem() and do the insert.  If the
1176          * key is an exact match, or we're replacing the data item with a
1177          * new data item.  If the key isn't an exact match, we're inserting
1178          * a new key/data pair, before the search location.
1179          */
1180         if ((ret = __bam_iitem(dbp, &h, &indx, NULL,
1181             data, exact ? DB_CURRENT : DB_BEFORE, bi_flags)) == DB_NEEDSPLIT) {
1182                 (void)__bam_stkrel(dbp);
1183                 stack = 0;
1184                 if ((ret = __bam_split(dbp, recnop)) != 0)
1185                         goto err;
1186                 goto retry;
1187         }
1188
1189         if (!exact && ret == 0)
1190                 __bam_adjust(dbp, t, 1);
1191
1192 err:    if (stack)
1193                 __bam_stkrel(dbp);
1194         return (ret);
1195 }