Update from db 2.7.5.
[kopensolaris-gnu/glibc.git] / db2 / btree / bt_rec.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997, 1998
5  *      Sleepycat Software.  All rights reserved.
6  */
7
8 #include "config.h"
9
10 #ifndef lint
11 static const char sccsid[] = "@(#)bt_rec.c      10.28 (Sleepycat) 9/27/98";
12 #endif /* not lint */
13
14 #ifndef NO_SYSTEM_INCLUDES
15 #include <sys/types.h>
16
17 #include <errno.h>
18 #include <string.h>
19 #endif
20
21 #include "db_int.h"
22 #include "db_page.h"
23 #include "shqueue.h"
24 #include "hash.h"
25 #include "btree.h"
26 #include "log.h"
27 #include "common_ext.h"
28
29 /*
30  * __bam_pg_alloc_recover --
31  *      Recovery function for pg_alloc.
32  *
33  * PUBLIC: int __bam_pg_alloc_recover
34  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
35  */
36 int
37 __bam_pg_alloc_recover(logp, dbtp, lsnp, redo, info)
38         DB_LOG *logp;
39         DBT *dbtp;
40         DB_LSN *lsnp;
41         int redo;
42         void *info;
43 {
44         __bam_pg_alloc_args *argp;
45         BTMETA *meta;
46         DB_MPOOLFILE *mpf;
47         PAGE *pagep;
48         DB *file_dbp;
49         DBC *dbc;
50         db_pgno_t pgno;
51         int cmp_n, cmp_p, modified, ret;
52
53         REC_PRINT(__bam_pg_alloc_print);
54         REC_INTRO(__bam_pg_alloc_read);
55
56         /*
57          * Fix up the allocated page.  If we're redoing the operation, we have
58          * to get the page (creating it if it doesn't exist), and update its
59          * LSN.  If we're undoing the operation, we have to reset the page's
60          * LSN and put it on the free list.
61          *
62          * Fix up the metadata page.  If we're redoing the operation, we have
63          * to get the metadata page and update its LSN and its free pointer.
64          * If we're undoing the operation and the page was ever created, we put
65          * it on the freelist.
66          */
67         pgno = PGNO_METADATA;
68         if ((ret = memp_fget(mpf, &pgno, 0, &meta)) != 0) {
69                 /* The metadata page must always exist. */
70                 (void)__db_pgerr(file_dbp, pgno);
71                 goto out;
72         }
73         if ((ret = memp_fget(mpf, &argp->pgno, DB_MPOOL_CREATE, &pagep)) != 0) {
74                 /*
75                  * We specify creation and check for it later, because this
76                  * operation was supposed to create the page, and even in
77                  * the undo case it's going to get linked onto the freelist
78                  * which we're also fixing up.
79                  */
80                 (void)__db_pgerr(file_dbp, argp->pgno);
81                 (void)memp_fput(mpf, meta, 0);
82                 goto out;
83         }
84
85         /* Fix up the allocated page. */
86         modified = 0;
87         cmp_n = log_compare(lsnp, &LSN(pagep));
88         cmp_p = log_compare(&LSN(pagep), &argp->page_lsn);
89         if (cmp_p == 0 && redo) {
90                 /* Need to redo update described. */
91                 P_INIT(pagep, file_dbp->pgsize,
92                     argp->pgno, PGNO_INVALID, PGNO_INVALID, 0, argp->ptype);
93
94                 pagep->lsn = *lsnp;
95                 modified = 1;
96         } else if (cmp_n == 0 && !redo) {
97                 /* Need to undo update described. */
98                 P_INIT(pagep, file_dbp->pgsize,
99                     argp->pgno, PGNO_INVALID, meta->free, 0, P_INVALID);
100
101                 pagep->lsn = argp->page_lsn;
102                 modified = 1;
103         }
104         if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0) {
105                 (void)memp_fput(mpf, meta, 0);
106                 goto out;
107         }
108
109         /* Fix up the metadata page. */
110         modified = 0;
111         cmp_n = log_compare(lsnp, &LSN(meta));
112         cmp_p = log_compare(&LSN(meta), &argp->meta_lsn);
113         if (cmp_p == 0 && redo) {
114                 /* Need to redo update described. */
115                 meta->lsn = *lsnp;
116                 meta->free = argp->next;
117                 modified = 1;
118         } else if (cmp_n == 0 && !redo) {
119                 /* Need to undo update described. */
120                 meta->lsn = argp->meta_lsn;
121                 meta->free = argp->pgno;
122                 modified = 1;
123         }
124         if ((ret = memp_fput(mpf, meta, modified ? DB_MPOOL_DIRTY : 0)) != 0)
125                 goto out;
126
127 done:   *lsnp = argp->prev_lsn;
128         ret = 0;
129
130 out:    REC_CLOSE;
131 }
132
133 /*
134  * __bam_pg_free_recover --
135  *      Recovery function for pg_free.
136  *
137  * PUBLIC: int __bam_pg_free_recover
138  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
139  */
140 int
141 __bam_pg_free_recover(logp, dbtp, lsnp, redo, info)
142         DB_LOG *logp;
143         DBT *dbtp;
144         DB_LSN *lsnp;
145         int redo;
146         void *info;
147 {
148         __bam_pg_free_args *argp;
149         BTMETA *meta;
150         DB *file_dbp;
151         DBC *dbc;
152         DB_MPOOLFILE *mpf;
153         PAGE *pagep;
154         db_pgno_t pgno;
155         int cmp_n, cmp_p, modified, ret;
156
157         REC_PRINT(__bam_pg_free_print);
158         REC_INTRO(__bam_pg_free_read);
159
160         /*
161          * Fix up the freed page.  If we're redoing the operation we get the
162          * page and explicitly discard its contents, then update its LSN.  If
163          * we're undoing the operation, we get the page and restore its header.
164          */
165         if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
166                 /*
167                  * We don't automatically create the page.  The only way the
168                  * page might not exist is if the alloc never happened, and
169                  * the only way the alloc might never have happened is if we
170                  * are undoing, in which case there's no reason to create the
171                  * page.
172                  */
173                 if (!redo)
174                         goto done;
175                 (void)__db_pgerr(file_dbp, argp->pgno);
176                 goto out;
177         }
178         modified = 0;
179         cmp_n = log_compare(lsnp, &LSN(pagep));
180         cmp_p = log_compare(&LSN(pagep), &LSN(argp->header.data));
181         if (cmp_p == 0 && redo) {
182                 /* Need to redo update described. */
183                 P_INIT(pagep, file_dbp->pgsize,
184                     pagep->pgno, PGNO_INVALID, argp->next, 0, P_INVALID);
185                 pagep->lsn = *lsnp;
186
187                 modified = 1;
188         } else if (cmp_n == 0 && !redo) {
189                 /* Need to undo update described. */
190                 memcpy(pagep, argp->header.data, argp->header.size);
191
192                 modified = 1;
193         }
194         if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
195                 goto out;
196
197         /*
198          * Fix up the metadata page.  If we're redoing or undoing the operation
199          * we get the page and update its LSN and free pointer.
200          */
201         pgno = PGNO_METADATA;
202         if ((ret = memp_fget(mpf, &pgno, 0, &meta)) != 0) {
203                 /* The metadata page must always exist. */
204                 (void)__db_pgerr(file_dbp, pgno);
205                 goto out;
206         }
207
208         modified = 0;
209         cmp_n = log_compare(lsnp, &LSN(meta));
210         cmp_p = log_compare(&LSN(meta), &argp->meta_lsn);
211         if (cmp_p == 0 && redo) {
212                 /* Need to redo update described. */
213                 meta->free = argp->pgno;
214
215                 meta->lsn = *lsnp;
216                 modified = 1;
217         } else if (cmp_n == 0 && !redo) {
218                 /* Need to undo update described. */
219                 meta->free = argp->next;
220
221                 meta->lsn = argp->meta_lsn;
222                 modified = 1;
223         }
224         if ((ret = memp_fput(mpf, meta, modified ? DB_MPOOL_DIRTY : 0)) != 0)
225                 goto out;
226
227 done:   *lsnp = argp->prev_lsn;
228         ret = 0;
229
230 out:    REC_CLOSE;
231 }
232
233 /*
234  * __bam_split_recover --
235  *      Recovery function for split.
236  *
237  * PUBLIC: int __bam_split_recover
238  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
239  */
240 int
241 __bam_split_recover(logp, dbtp, lsnp, redo, info)
242         DB_LOG *logp;
243         DBT *dbtp;
244         DB_LSN *lsnp;
245         int redo;
246         void *info;
247 {
248         __bam_split_args *argp;
249         DB *file_dbp;
250         DBC *dbc;
251         DB_MPOOLFILE *mpf;
252         PAGE *_lp, *lp, *np, *pp, *_rp, *rp, *sp;
253         db_pgno_t pgno;
254         int l_update, p_update, r_update, ret, rootsplit, t_ret;
255
256         REC_PRINT(__bam_split_print);
257
258         mpf = NULL;
259         _lp = lp = np = pp = _rp = rp = NULL;
260
261         REC_INTRO(__bam_split_read);
262
263         /*
264          * There are two kinds of splits that we have to recover from.  The
265          * first is a root-page split, where the root page is split from a
266          * leaf page into an internal page and two new leaf pages are created.
267          * The second is where a page is split into two pages, and a new key
268          * is inserted into the parent page.
269          */
270         sp = argp->pg.data;
271         pgno = PGNO(sp);
272         rootsplit = pgno == PGNO_ROOT;
273         if (memp_fget(mpf, &argp->left, 0, &lp) != 0)
274                 lp = NULL;
275         if (memp_fget(mpf, &argp->right, 0, &rp) != 0)
276                 rp = NULL;
277
278         if (redo) {
279                 l_update = r_update = p_update = 0;
280                 /*
281                  * Decide if we need to resplit the page.
282                  *
283                  * If this is a root split, then the root has to exist, it's
284                  * the page we're splitting and it gets modified.  If this is
285                  * not a root split, then the left page has to exist, for the
286                  * same reason.
287                  */
288                 if (rootsplit) {
289                         if ((ret = memp_fget(mpf, &pgno, 0, &pp)) != 0) {
290                                 (void)__db_pgerr(file_dbp, pgno);
291                                 pp = NULL;
292                                 goto out;
293                         }
294                         p_update =
295                             log_compare(&LSN(pp), &LSN(argp->pg.data)) == 0;
296                 } else
297                         if (lp == NULL) {
298                                 (void)__db_pgerr(file_dbp, argp->left);
299                                 goto out;
300                         }
301                 if (lp == NULL || log_compare(&LSN(lp), &argp->llsn) == 0)
302                         l_update = 1;
303                 if (rp == NULL || log_compare(&LSN(rp), &argp->rlsn) == 0)
304                         r_update = 1;
305                 if (!p_update && !l_update && !r_update)
306                         goto done;
307
308                 /* Allocate and initialize new left/right child pages. */
309                 if ((ret = __os_malloc(file_dbp->pgsize, NULL, &_lp)) != 0 ||
310                     (ret = __os_malloc(file_dbp->pgsize, NULL, &_rp)) != 0)
311                         goto out;
312                 if (rootsplit) {
313                         P_INIT(_lp, file_dbp->pgsize, argp->left,
314                             PGNO_INVALID,
315                             ISINTERNAL(sp) ? PGNO_INVALID : argp->right,
316                             LEVEL(sp), TYPE(sp));
317                         P_INIT(_rp, file_dbp->pgsize, argp->right,
318                             ISINTERNAL(sp) ?  PGNO_INVALID : argp->left,
319                             PGNO_INVALID, LEVEL(sp), TYPE(sp));
320                 } else {
321                         P_INIT(_lp, file_dbp->pgsize, PGNO(sp),
322                             ISINTERNAL(sp) ? PGNO_INVALID : PREV_PGNO(sp),
323                             ISINTERNAL(sp) ? PGNO_INVALID : argp->right,
324                             LEVEL(sp), TYPE(sp));
325                         P_INIT(_rp, file_dbp->pgsize, argp->right,
326                             ISINTERNAL(sp) ? PGNO_INVALID : sp->pgno,
327                             ISINTERNAL(sp) ? PGNO_INVALID : NEXT_PGNO(sp),
328                             LEVEL(sp), TYPE(sp));
329                 }
330
331                 /* Split the page. */
332                 if ((ret = __bam_copy(file_dbp, sp, _lp, 0, argp->indx)) != 0 ||
333                     (ret = __bam_copy(file_dbp, sp, _rp, argp->indx,
334                     NUM_ENT(sp))) != 0)
335                         goto out;
336
337                 /* If the left child is wrong, update it. */
338                 if (lp == NULL && (ret =
339                     memp_fget(mpf, &argp->left, DB_MPOOL_CREATE, &lp)) != 0) {
340                         (void)__db_pgerr(file_dbp, argp->left);
341                         lp = NULL;
342                         goto out;
343                 }
344                 if (l_update) {
345                         memcpy(lp, _lp, file_dbp->pgsize);
346                         lp->lsn = *lsnp;
347                         if ((ret = memp_fput(mpf, lp, DB_MPOOL_DIRTY)) != 0)
348                                 goto out;
349                         lp = NULL;
350                 }
351
352                 /* If the right child is wrong, update it. */
353                 if (rp == NULL && (ret = memp_fget(mpf,
354                     &argp->right, DB_MPOOL_CREATE, &rp)) != 0) {
355                         (void)__db_pgerr(file_dbp, argp->right);
356                         rp = NULL;
357                         goto out;
358                 }
359                 if (r_update) {
360                         memcpy(rp, _rp, file_dbp->pgsize);
361                         rp->lsn = *lsnp;
362                         if ((ret = memp_fput(mpf, rp, DB_MPOOL_DIRTY)) != 0)
363                                 goto out;
364                         rp = NULL;
365                 }
366
367                 /*
368                  * If the parent page is wrong, update it.  This is of interest
369                  * only if it was a root split, since root splits create parent
370                  * pages.  All other splits modify a parent page, but those are
371                  * separately logged and recovered.
372                  */
373                 if (rootsplit && p_update) {
374                         if (file_dbp->type == DB_BTREE)
375                                 P_INIT(pp, file_dbp->pgsize,
376                                     PGNO_ROOT, PGNO_INVALID, PGNO_INVALID,
377                                     _lp->level + 1, P_IBTREE);
378                         else
379                                 P_INIT(pp, file_dbp->pgsize,
380                                     PGNO_ROOT, PGNO_INVALID, PGNO_INVALID,
381                                     _lp->level + 1, P_IRECNO);
382                         RE_NREC_SET(pp,
383                             file_dbp->type == DB_RECNO ||
384                             F_ISSET(file_dbp, DB_BT_RECNUM) ?
385                             __bam_total(_lp) + __bam_total(_rp) : 0);
386                         pp->lsn = *lsnp;
387                         if ((ret = memp_fput(mpf, pp, DB_MPOOL_DIRTY)) != 0)
388                                 goto out;
389                         pp = NULL;
390                 }
391
392                 /*
393                  * Finally, redo the next-page link if necessary.  This is of
394                  * interest only if it wasn't a root split -- inserting a new
395                  * page in the tree requires that any following page have its
396                  * previous-page pointer updated to our new page.  The next
397                  * page must exist because we're redoing the operation.
398                  */
399                 if (!rootsplit && !IS_ZERO_LSN(argp->nlsn)) {
400                         if ((ret = memp_fget(mpf, &argp->npgno, 0, &np)) != 0) {
401                                 (void)__db_pgerr(file_dbp, argp->npgno);
402                                 np = NULL;
403                                 goto out;
404                         }
405                         if (log_compare(&LSN(np), &argp->nlsn) == 0) {
406                                 PREV_PGNO(np) = argp->right;
407                                 np->lsn = *lsnp;
408                                 if ((ret =
409                                     memp_fput(mpf, np, DB_MPOOL_DIRTY)) != 0)
410                                         goto out;
411                                 np = NULL;
412                         }
413                 }
414         } else {
415                 /*
416                  * If the split page is wrong, replace its contents with the
417                  * logged page contents.  If the page doesn't exist, it means
418                  * that the create of the page never happened, nor did any of
419                  * the adds onto the page that caused the split, and there's
420                  * really no undo-ing to be done.
421                  */
422                 if ((ret = memp_fget(mpf, &pgno, 0, &pp)) != 0) {
423                         pp = NULL;
424                         goto lrundo;
425                 }
426                 if (log_compare(lsnp, &LSN(pp)) == 0) {
427                         memcpy(pp, argp->pg.data, argp->pg.size);
428                         if ((ret = memp_fput(mpf, pp, DB_MPOOL_DIRTY)) != 0)
429                                 goto out;
430                         pp = NULL;
431                 }
432
433                 /*
434                  * If it's a root split and the left child ever existed, update
435                  * its LSN.  (If it's not a root split, we've updated the left
436                  * page already -- it's the same as the split page.) If the
437                  * right child ever existed, root split or not, update its LSN.
438                  * The undo of the page allocation(s) will restore them to the
439                  * free list.
440                  */
441 lrundo:         if ((rootsplit && lp != NULL) || rp != NULL) {
442                         if (rootsplit && lp != NULL &&
443                             log_compare(lsnp, &LSN(lp)) == 0) {
444                                 lp->lsn = argp->llsn;
445                                 if ((ret =
446                                     memp_fput(mpf, lp, DB_MPOOL_DIRTY)) != 0)
447                                         goto out;
448                                 lp = NULL;
449                         }
450                         if (rp != NULL &&
451                             log_compare(lsnp, &LSN(rp)) == 0) {
452                                 rp->lsn = argp->rlsn;
453                                 if ((ret =
454                                     memp_fput(mpf, rp, DB_MPOOL_DIRTY)) != 0)
455                                         goto out;
456                                 rp = NULL;
457                         }
458                 }
459
460                 /*
461                  * Finally, undo the next-page link if necessary.  This is of
462                  * interest only if it wasn't a root split -- inserting a new
463                  * page in the tree requires that any following page have its
464                  * previous-page pointer updated to our new page.  Since it's
465                  * possible that the next-page never existed, we ignore it as
466                  * if there's nothing to undo.
467                  */
468                 if (!rootsplit && !IS_ZERO_LSN(argp->nlsn)) {
469                         if ((ret = memp_fget(mpf, &argp->npgno, 0, &np)) != 0) {
470                                 np = NULL;
471                                 goto done;
472                         }
473                         if (log_compare(lsnp, &LSN(np)) == 0) {
474                                 PREV_PGNO(np) = argp->left;
475                                 np->lsn = argp->nlsn;
476                                 if (memp_fput(mpf, np, DB_MPOOL_DIRTY))
477                                         goto out;
478                                 np = NULL;
479                         }
480                 }
481         }
482
483 done:   *lsnp = argp->prev_lsn;
484         ret = 0;
485
486 out:    /* Free any pages that weren't dirtied. */
487         if (pp != NULL && (t_ret = memp_fput(mpf, pp, 0)) != 0 && ret == 0)
488                 ret = t_ret;
489         if (lp != NULL && (t_ret = memp_fput(mpf, lp, 0)) != 0 && ret == 0)
490                 ret = t_ret;
491         if (np != NULL && (t_ret = memp_fput(mpf, np, 0)) != 0 && ret == 0)
492                 ret = t_ret;
493         if (rp != NULL && (t_ret = memp_fput(mpf, rp, 0)) != 0 && ret == 0)
494                 ret = t_ret;
495
496         /* Free any allocated space. */
497         if (_lp != NULL)
498                 __os_free(_lp, file_dbp->pgsize);
499         if (_rp != NULL)
500                 __os_free(_rp, file_dbp->pgsize);
501
502         REC_CLOSE;
503 }
504
505 /*
506  * __bam_rsplit_recover --
507  *      Recovery function for a reverse split.
508  *
509  * PUBLIC: int __bam_rsplit_recover
510  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
511  */
512 int
513 __bam_rsplit_recover(logp, dbtp, lsnp, redo, info)
514         DB_LOG *logp;
515         DBT *dbtp;
516         DB_LSN *lsnp;
517         int redo;
518         void *info;
519 {
520         __bam_rsplit_args *argp;
521         DB *file_dbp;
522         DBC *dbc;
523         DB_MPOOLFILE *mpf;
524         PAGE *pagep;
525         db_pgno_t pgno;
526         int cmp_n, cmp_p, modified, ret;
527
528         REC_PRINT(__bam_rsplit_print);
529         REC_INTRO(__bam_rsplit_read);
530
531         /* Fix the root page. */
532         pgno = PGNO_ROOT;
533         if ((ret = memp_fget(mpf, &pgno, 0, &pagep)) != 0) {
534                 /* The root page must always exist. */
535                 __db_pgerr(file_dbp, pgno);
536                 goto out;
537         }
538         modified = 0;
539         cmp_n = log_compare(lsnp, &LSN(pagep));
540         cmp_p = log_compare(&LSN(pagep), &argp->rootlsn);
541         if (cmp_p == 0 && redo) {
542                 /* Need to redo update described. */
543                 memcpy(pagep, argp->pgdbt.data, argp->pgdbt.size);
544                 pagep->pgno = PGNO_ROOT;
545                 pagep->lsn = *lsnp;
546                 modified = 1;
547         } else if (cmp_n == 0 && !redo) {
548                 /* Need to undo update described. */
549                 P_INIT(pagep, file_dbp->pgsize, PGNO_ROOT,
550                     argp->nrec, PGNO_INVALID, pagep->level + 1,
551                     file_dbp->type == DB_BTREE ? P_IBTREE : P_IRECNO);
552                 if ((ret = __db_pitem(dbc, pagep, 0,
553                     argp->rootent.size, &argp->rootent, NULL)) != 0)
554                         goto out;
555                 pagep->lsn = argp->rootlsn;
556                 modified = 1;
557         }
558         if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
559                 goto out;
560
561         /*
562          * Fix the page copied over the root page.  It's possible that the
563          * page never made it to disk, so if we're undo-ing and the page
564          * doesn't exist, it's okay and there's nothing further to do.
565          */
566         if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
567                 if (!redo)
568                         goto done;
569                 (void)__db_pgerr(file_dbp, argp->pgno);
570                 goto out;
571         }
572         modified = 0;
573         cmp_n = log_compare(lsnp, &LSN(pagep));
574         cmp_p = log_compare(&LSN(pagep), &LSN(argp->pgdbt.data));
575         if (cmp_p == 0 && redo) {
576                 /* Need to redo update described. */
577                 pagep->lsn = *lsnp;
578                 modified = 1;
579         } else if (cmp_n == 0 && !redo) {
580                 /* Need to undo update described. */
581                 memcpy(pagep, argp->pgdbt.data, argp->pgdbt.size);
582                 modified = 1;
583         }
584         if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
585                 goto out;
586
587 done:   *lsnp = argp->prev_lsn;
588         ret = 0;
589
590 out:    REC_CLOSE;
591 }
592
593 /*
594  * __bam_adj_recover --
595  *      Recovery function for adj.
596  *
597  * PUBLIC: int __bam_adj_recover
598  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
599  */
600 int
601 __bam_adj_recover(logp, dbtp, lsnp, redo, info)
602         DB_LOG *logp;
603         DBT *dbtp;
604         DB_LSN *lsnp;
605         int redo;
606         void *info;
607 {
608         __bam_adj_args *argp;
609         DB *file_dbp;
610         DBC *dbc;
611         DB_MPOOLFILE *mpf;
612         PAGE *pagep;
613         int cmp_n, cmp_p, modified, ret;
614
615         REC_PRINT(__bam_adj_print);
616         REC_INTRO(__bam_adj_read);
617
618         /* Get the page; if it never existed and we're undoing, we're done. */
619         if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
620                 if (!redo)
621                         goto done;
622                 (void)__db_pgerr(file_dbp, argp->pgno);
623                 goto out;
624         }
625
626         modified = 0;
627         cmp_n = log_compare(lsnp, &LSN(pagep));
628         cmp_p = log_compare(&LSN(pagep), &argp->lsn);
629         if (cmp_p == 0 && redo) {
630                 /* Need to redo update described. */
631                 if ((ret = __bam_adjindx(dbc,
632                     pagep, argp->indx, argp->indx_copy, argp->is_insert)) != 0)
633                         goto err;
634
635                 LSN(pagep) = *lsnp;
636                 modified = 1;
637         } else if (cmp_n == 0 && !redo) {
638                 /* Need to undo update described. */
639                 if ((ret = __bam_adjindx(dbc,
640                     pagep, argp->indx, argp->indx_copy, !argp->is_insert)) != 0)
641                         goto err;
642
643                 LSN(pagep) = argp->lsn;
644                 modified = 1;
645         }
646         if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
647                 goto out;
648
649 done:   *lsnp = argp->prev_lsn;
650         ret = 0;
651
652         if (0) {
653 err:            (void)memp_fput(mpf, pagep, 0);
654         }
655 out:    REC_CLOSE;
656 }
657
658 /*
659  * __bam_cadjust_recover --
660  *      Recovery function for the adjust of a count change in an internal
661  *      page.
662  *
663  * PUBLIC: int __bam_cadjust_recover
664  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
665  */
666 int
667 __bam_cadjust_recover(logp, dbtp, lsnp, redo, info)
668         DB_LOG *logp;
669         DBT *dbtp;
670         DB_LSN *lsnp;
671         int redo;
672         void *info;
673 {
674         __bam_cadjust_args *argp;
675         DB *file_dbp;
676         DBC *dbc;
677         DB_MPOOLFILE *mpf;
678         PAGE *pagep;
679         int cmp_n, cmp_p, modified, ret;
680
681         REC_PRINT(__bam_cadjust_print);
682         REC_INTRO(__bam_cadjust_read);
683
684         /* Get the page; if it never existed and we're undoing, we're done. */
685         if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
686                 if (!redo)
687                         goto done;
688                 (void)__db_pgerr(file_dbp, argp->pgno);
689                 goto out;
690         }
691
692         modified = 0;
693         cmp_n = log_compare(lsnp, &LSN(pagep));
694         cmp_p = log_compare(&LSN(pagep), &argp->lsn);
695         if (cmp_p == 0 && redo) {
696                 /* Need to redo update described. */
697                 if (file_dbp->type == DB_BTREE &&
698                     F_ISSET(file_dbp, DB_BT_RECNUM)) {
699                         GET_BINTERNAL(pagep, argp->indx)->nrecs += argp->adjust;
700                         if (argp->total && PGNO(pagep) == PGNO_ROOT)
701                                 RE_NREC_ADJ(pagep, argp->adjust);
702                 }
703                 if (file_dbp->type == DB_RECNO) {
704                         GET_RINTERNAL(pagep, argp->indx)->nrecs += argp->adjust;
705                         if (argp->total && PGNO(pagep) == PGNO_ROOT)
706                                 RE_NREC_ADJ(pagep, argp->adjust);
707                 }
708
709                 LSN(pagep) = *lsnp;
710                 modified = 1;
711         } else if (cmp_n == 0 && !redo) {
712                 /* Need to undo update described. */
713                 if (file_dbp->type == DB_BTREE &&
714                     F_ISSET(file_dbp, DB_BT_RECNUM)) {
715                         GET_BINTERNAL(pagep, argp->indx)->nrecs -= argp->adjust;
716                         if (argp->total && PGNO(pagep) == PGNO_ROOT)
717                                 RE_NREC_ADJ(pagep, argp->adjust);
718                 }
719                 if (file_dbp->type == DB_RECNO) {
720                         GET_RINTERNAL(pagep, argp->indx)->nrecs -= argp->adjust;
721                         if (argp->total && PGNO(pagep) == PGNO_ROOT)
722                                 RE_NREC_ADJ(pagep, -(argp->adjust));
723                 }
724                 LSN(pagep) = argp->lsn;
725                 modified = 1;
726         }
727         if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
728                 goto out;
729
730 done:   *lsnp = argp->prev_lsn;
731         ret = 0;
732
733 out:    REC_CLOSE;
734 }
735
736 /*
737  * __bam_cdel_recover --
738  *      Recovery function for the intent-to-delete of a cursor record.
739  *
740  * PUBLIC: int __bam_cdel_recover
741  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
742  */
743 int
744 __bam_cdel_recover(logp, dbtp, lsnp, redo, info)
745         DB_LOG *logp;
746         DBT *dbtp;
747         DB_LSN *lsnp;
748         int redo;
749         void *info;
750 {
751         __bam_cdel_args *argp;
752         DB *file_dbp;
753         DBC *dbc;
754         DB_MPOOLFILE *mpf;
755         PAGE *pagep;
756         int cmp_n, cmp_p, modified, ret;
757
758         REC_PRINT(__bam_cdel_print);
759         REC_INTRO(__bam_cdel_read);
760
761         /* Get the page; if it never existed and we're undoing, we're done. */
762         if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
763                 if (!redo)
764                         goto done;
765                 (void)__db_pgerr(file_dbp, argp->pgno);
766                 goto out;
767         }
768
769         modified = 0;
770         cmp_n = log_compare(lsnp, &LSN(pagep));
771         cmp_p = log_compare(&LSN(pagep), &argp->lsn);
772         if (cmp_p == 0 && redo) {
773                 /* Need to redo update described. */
774                 if (pagep->type == P_DUPLICATE)
775                         B_DSET(GET_BKEYDATA(pagep, argp->indx)->type);
776                 else
777                         B_DSET(GET_BKEYDATA(pagep, argp->indx + O_INDX)->type);
778
779                 LSN(pagep) = *lsnp;
780                 modified = 1;
781         } else if (cmp_n == 0 && !redo) {
782                 /* Need to undo update described. */
783                 if (pagep->type == P_DUPLICATE)
784                         B_DCLR(GET_BKEYDATA(pagep, argp->indx)->type);
785                 else
786                         B_DCLR(GET_BKEYDATA(pagep, argp->indx + O_INDX)->type);
787
788                 LSN(pagep) = argp->lsn;
789                 modified = 1;
790         }
791         if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
792                 goto out;
793
794 done:   *lsnp = argp->prev_lsn;
795         ret = 0;
796
797 out:    REC_CLOSE;
798 }
799
800 /*
801  * __bam_repl_recover --
802  *      Recovery function for page item replacement.
803  *
804  * PUBLIC: int __bam_repl_recover
805  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
806  */
807 int
808 __bam_repl_recover(logp, dbtp, lsnp, redo, info)
809         DB_LOG *logp;
810         DBT *dbtp;
811         DB_LSN *lsnp;
812         int redo;
813         void *info;
814 {
815         __bam_repl_args *argp;
816         BKEYDATA *bk;
817         DB *file_dbp;
818         DBC *dbc;
819         DBT dbt;
820         DB_MPOOLFILE *mpf;
821         PAGE *pagep;
822         int cmp_n, cmp_p, modified, ret;
823         u_int8_t *p;
824
825         REC_PRINT(__bam_repl_print);
826         REC_INTRO(__bam_repl_read);
827
828         /* Get the page; if it never existed and we're undoing, we're done. */
829         if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
830                 if (!redo)
831                         goto done;
832                 (void)__db_pgerr(file_dbp, argp->pgno);
833                 goto out;
834         }
835         bk = GET_BKEYDATA(pagep, argp->indx);
836
837         modified = 0;
838         cmp_n = log_compare(lsnp, &LSN(pagep));
839         cmp_p = log_compare(&LSN(pagep), &argp->lsn);
840         if (cmp_p == 0 && redo) {
841                 /*
842                  * Need to redo update described.
843                  *
844                  * Re-build the replacement item.
845                  */
846                 memset(&dbt, 0, sizeof(dbt));
847                 dbt.size = argp->prefix + argp->suffix + argp->repl.size;
848                 if ((ret = __os_malloc(dbt.size, NULL, &dbt.data)) != 0)
849                         goto err;
850                 p = dbt.data;
851                 memcpy(p, bk->data, argp->prefix);
852                 p += argp->prefix;
853                 memcpy(p, argp->repl.data, argp->repl.size);
854                 p += argp->repl.size;
855                 memcpy(p, bk->data + (bk->len - argp->suffix), argp->suffix);
856
857                 ret = __bam_ritem(dbc, pagep, argp->indx, &dbt);
858                 __os_free(dbt.data, dbt.size);
859                 if (ret != 0)
860                         goto err;
861
862                 LSN(pagep) = *lsnp;
863                 modified = 1;
864         } else if (cmp_n == 0 && !redo) {
865                 /*
866                  * Need to undo update described.
867                  *
868                  * Re-build the original item.
869                  */
870                 memset(&dbt, 0, sizeof(dbt));
871                 dbt.size = argp->prefix + argp->suffix + argp->orig.size;
872                 if ((ret = __os_malloc(dbt.size, NULL, &dbt.data)) != 0)
873                         goto err;
874                 p = dbt.data;
875                 memcpy(p, bk->data, argp->prefix);
876                 p += argp->prefix;
877                 memcpy(p, argp->orig.data, argp->orig.size);
878                 p += argp->orig.size;
879                 memcpy(p, bk->data + (bk->len - argp->suffix), argp->suffix);
880
881                 ret = __bam_ritem(dbc, pagep, argp->indx, &dbt);
882                 __os_free(dbt.data, dbt.size);
883                 if (ret != 0)
884                         goto err;
885
886                 /* Reset the deleted flag, if necessary. */
887                 if (argp->isdeleted)
888                         B_DSET(GET_BKEYDATA(pagep, argp->indx)->type);
889
890                 LSN(pagep) = argp->lsn;
891                 modified = 1;
892         }
893         if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
894                 goto out;
895
896 done:   *lsnp = argp->prev_lsn;
897         ret = 0;
898
899         if (0) {
900 err:            (void)memp_fput(mpf, pagep, 0);
901         }
902 out:    REC_CLOSE;
903 }