Update from db-2.3.12.
[kopensolaris-gnu/glibc.git] / db2 / db / db_rec.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997
5  *      Sleepycat Software.  All rights reserved.
6  */
7
8 #include "config.h"
9
10 #ifndef lint
11 static const char sccsid[] = "@(#)db_rec.c      10.10 (Sleepycat) 11/2/97";
12 #endif /* not lint */
13
14 #ifndef NO_SYSTEM_INCLUDES
15 #include <sys/types.h>
16
17 #endif
18 #include <ctype.h>
19 #include <errno.h>
20 #include <stddef.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "db_int.h"
25 #include "shqueue.h"
26 #include "db_page.h"
27 #include "db_dispatch.h"
28 #include "log.h"
29 #include "hash.h"
30 #include "btree.h"
31
32 /*
33  * PUBLIC: int __db_addrem_recover
34  * PUBLIC:    __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
35  *
36  * This log message is generated whenever we add or remove a duplicate
37  * to/from a duplicate page.  On recover, we just do the opposite.
38  */
39 int
40 __db_addrem_recover(logp, dbtp, lsnp, redo, info)
41         DB_LOG *logp;
42         DBT *dbtp;
43         DB_LSN *lsnp;
44         int redo;
45         void *info;
46 {
47         __db_addrem_args *argp;
48         DB *file_dbp, *mdbp;
49         DB_MPOOLFILE *mpf;
50         PAGE *pagep;
51         int change, cmp_n, cmp_p, ret;
52
53         REC_PRINT(__db_addrem_print);
54         REC_INTRO(__db_addrem_read);
55
56         if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
57                 if (!redo) {
58                         /*
59                          * We are undoing and the page doesn't exist.  That
60                          * is equivalent to having a pagelsn of 0, so we
61                          * would not have to undo anything.  In this case,
62                          * don't bother creating a page.
63                          */
64                         *lsnp = argp->prev_lsn;
65                         ret = 0;
66                         goto out;
67                 } else
68                         if ((ret = memp_fget(mpf,
69                             &argp->pgno, DB_MPOOL_CREATE, &pagep)) != 0)
70                                 goto out;
71         }
72
73         cmp_n = log_compare(lsnp, &LSN(pagep));
74         cmp_p = log_compare(&LSN(pagep), &argp->pagelsn);
75         change = 0;
76         if ((cmp_p == 0 && redo && argp->opcode == DB_ADD_DUP) ||
77             (cmp_n == 0 && !redo && argp->opcode == DB_REM_DUP)) {
78
79                 /* Need to redo an add, or undo a delete. */
80                 if ((ret = __db_pitem(file_dbp, pagep, argp->indx, argp->nbytes,
81                     argp->hdr.size == 0 ? NULL : &argp->hdr,
82                     argp->dbt.size == 0 ? NULL : &argp->dbt)) != 0)
83                         goto out;
84
85                 change = DB_MPOOL_DIRTY;
86
87         } else if ((cmp_n == 0 && !redo && argp->opcode == DB_ADD_DUP) ||
88             (cmp_p == 0 && redo && argp->opcode == DB_REM_DUP)) {
89                 /* Need to undo an add, or redo a delete. */
90                 if ((ret = __db_ditem(file_dbp, pagep, argp->indx,
91                     argp->nbytes)) != 0)
92                         goto out;
93                 change = DB_MPOOL_DIRTY;
94         }
95
96         if (change)
97                 if (redo)
98                         LSN(pagep) = *lsnp;
99                 else
100                         LSN(pagep) = argp->pagelsn;
101
102         if ((ret = memp_fput(mpf, pagep, change)) == 0)
103                 *lsnp = argp->prev_lsn;
104
105 out:    REC_CLOSE;
106 }
107
108 /*
109  * PUBLIC: int __db_split_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
110  */
111 int
112 __db_split_recover(logp, dbtp, lsnp, redo, info)
113         DB_LOG *logp;
114         DBT *dbtp;
115         DB_LSN *lsnp;
116         int redo;
117         void *info;
118 {
119         __db_split_args *argp;
120         DB *file_dbp, *mdbp;
121         DB_MPOOLFILE *mpf;
122         PAGE *pagep;
123         int change, cmp_n, cmp_p, ret;
124
125         REC_PRINT(__db_split_print);
126         REC_INTRO(__db_split_read);
127
128         if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
129                 if (!redo) {
130                         /*
131                          * We are undoing and the page doesn't exist.  That
132                          * is equivalent to having a pagelsn of 0, so we
133                          * would not have to undo anything.  In this case,
134                          * don't bother creating a page.
135                          */
136                         *lsnp = argp->prev_lsn;
137                         ret = 0;
138                         goto out;
139                 } else
140                         if ((ret = memp_fget(mpf,
141                             &argp->pgno, DB_MPOOL_CREATE, &pagep)) != 0)
142                                 goto out;
143         }
144
145         /*
146          * There are two types of log messages here, one for the old page
147          * and one for the new pages created.  The original image in the
148          * SPLITOLD record is used for undo.  The image in the SPLITNEW
149          * is used for redo.  We should never have a case where there is
150          * a redo operation and the SPLITOLD record is on disk, but not
151          * the SPLITNEW record.  Therefore, we only redo NEW messages
152          * and only undo OLD messages.
153          */
154
155         change = 0;
156         cmp_n = log_compare(lsnp, &LSN(pagep));
157         cmp_p = log_compare(&LSN(pagep), &argp->pagelsn);
158         if (cmp_p == 0 && redo) {
159                 if (argp->opcode == DB_SPLITNEW) {
160                         /* Need to redo the split described. */
161                         memcpy(pagep,
162                             argp->pageimage.data, argp->pageimage.size);
163                 }
164                 LSN(pagep) = *lsnp;
165                 change = DB_MPOOL_DIRTY;
166         } else if (cmp_n == 0 && !redo) {
167                 if (argp->opcode == DB_SPLITOLD) {
168                         /* Put back the old image. */
169                         memcpy(pagep,
170                             argp->pageimage.data, argp->pageimage.size);
171                 }
172                 LSN(pagep) = argp->pagelsn;
173                 change = DB_MPOOL_DIRTY;
174         }
175         if ((ret = memp_fput(mpf, pagep, change)) == 0)
176                 *lsnp = argp->prev_lsn;
177
178 out:    REC_CLOSE;
179 }
180
181 /*
182  * PUBLIC: int __db_big_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
183  */
184 int
185 __db_big_recover(logp, dbtp, lsnp, redo, info)
186         DB_LOG *logp;
187         DBT *dbtp;
188         DB_LSN *lsnp;
189         int redo;
190         void *info;
191 {
192         __db_big_args *argp;
193         DB *file_dbp, *mdbp;
194         DB_MPOOLFILE *mpf;
195         PAGE *pagep;
196         int change, cmp_n, cmp_p, ret;
197
198         REC_PRINT(__db_big_print);
199         REC_INTRO(__db_big_read);
200
201         if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
202                 if (!redo) {
203                         /*
204                          * We are undoing and the page doesn't exist.  That
205                          * is equivalent to having a pagelsn of 0, so we
206                          * would not have to undo anything.  In this case,
207                          * don't bother creating a page.
208                          */
209                         ret = 0;
210                         goto ppage;
211                 } else
212                         if ((ret = memp_fget(mpf,
213                             &argp->pgno, DB_MPOOL_CREATE, &pagep)) != 0)
214                         goto out;
215         }
216
217         /*
218          * There are three pages we need to check.  The one on which we are
219          * adding data, the previous one whose next_pointer may have
220          * been updated, and the next one whose prev_pointer may have
221          * been updated.
222          */
223         cmp_n = log_compare(lsnp, &LSN(pagep));
224         cmp_p = log_compare(&LSN(pagep), &argp->pagelsn);
225         change = 0;
226         if ((cmp_p == 0 && redo && argp->opcode == DB_ADD_BIG) ||
227             (cmp_n == 0 && !redo && argp->opcode == DB_REM_BIG)) {
228                 /* We are either redo-ing an add, or undoing a delete. */
229                 P_INIT(pagep, file_dbp->pgsize, argp->pgno, argp->prev_pgno,
230                         argp->next_pgno, 0, P_OVERFLOW);
231                 OV_LEN(pagep) = argp->dbt.size;
232                 OV_REF(pagep) = 1;
233                 memcpy((u_int8_t *)pagep + P_OVERHEAD, argp->dbt.data,
234                     argp->dbt.size);
235                 PREV_PGNO(pagep) = argp->prev_pgno;
236                 change = DB_MPOOL_DIRTY;
237         } else if ((cmp_n == 0 && !redo && argp->opcode == DB_ADD_BIG) ||
238             (cmp_p == 0 && redo && argp->opcode == DB_REM_BIG)) {
239                 /*
240                  * We are either undo-ing an add or redo-ing a delete.
241                  * The page is about to be reclaimed in either case, so
242                  * there really isn't anything to do here.
243                  */
244                 change = DB_MPOOL_DIRTY;
245         }
246         if (change)
247                 LSN(pagep) = redo ? *lsnp : argp->pagelsn;
248
249         if ((ret = memp_fput(mpf, pagep, change)) != 0)
250                 goto out;
251
252         /* Now check the previous page. */
253 ppage:  if (argp->prev_pgno != PGNO_INVALID) {
254                 change = 0;
255                 if ((ret = memp_fget(mpf, &argp->prev_pgno, 0, &pagep)) != 0)
256                         if (!redo) {
257                                 /*
258                                  * We are undoing and the page doesn't exist.
259                                  * That is equivalent to having a pagelsn of 0,
260                                  * so we would not have to undo anything.  In
261                                  * this case, don't bother creating a page.
262                                  */
263                                 *lsnp = argp->prev_lsn;
264                                 ret = 0;
265                                 goto npage;
266                         } else
267                                 if ((ret = memp_fget(mpf, &argp->prev_pgno,
268                                     DB_MPOOL_CREATE, &pagep)) != 0)
269                                         goto out;
270
271                 cmp_n = log_compare(lsnp, &LSN(pagep));
272                 cmp_p = log_compare(&LSN(pagep), &argp->prevlsn);
273
274                 if ((cmp_p == 0 && redo && argp->opcode == DB_ADD_BIG) ||
275                     (cmp_n == 0 && !redo && argp->opcode == DB_REM_BIG)) {
276                         /* Redo add, undo delete. */
277                         NEXT_PGNO(pagep) = argp->pgno;
278                         change = DB_MPOOL_DIRTY;
279                 } else if ((cmp_n == 0 &&
280                     !redo && argp->opcode == DB_ADD_BIG) ||
281                     (cmp_p == 0 && redo && argp->opcode == DB_REM_BIG)) {
282                         /* Redo delete, undo add. */
283                         NEXT_PGNO(pagep) = argp->next_pgno;
284                         change = DB_MPOOL_DIRTY;
285                 }
286                 if (change)
287                         LSN(pagep) = redo ? *lsnp : argp->prevlsn;
288                 if ((ret = memp_fput(mpf, pagep, change)) != 0)
289                         goto out;
290         }
291
292         /* Now check the next page.  Can only be set on a delete. */
293 npage:  if (argp->next_pgno != PGNO_INVALID) {
294                 change = 0;
295                 if ((ret = memp_fget(mpf, &argp->next_pgno, 0, &pagep)) != 0)
296                         if (!redo) {
297                                 /*
298                                  * We are undoing and the page doesn't exist.
299                                  * That is equivalent to having a pagelsn of 0,
300                                  * so we would not have to undo anything.  In
301                                  * this case, don't bother creating a page.
302                                  */
303                                 *lsnp = argp->prev_lsn;
304                                 ret = 0;
305                                 goto out;
306                         } else
307                                 if ((ret = memp_fget(mpf, &argp->next_pgno,
308                                     DB_MPOOL_CREATE, &pagep)) != 0)
309                                         goto out;
310
311                 cmp_n = log_compare(lsnp, &LSN(pagep));
312                 cmp_p = log_compare(&LSN(pagep), &argp->nextlsn);
313                 if (cmp_p == 0 && redo) {
314                         PREV_PGNO(pagep) = PGNO_INVALID;
315                         change = DB_MPOOL_DIRTY;
316                 } else if (cmp_n == 0 && !redo) {
317                         PREV_PGNO(pagep) = argp->pgno;
318                         change = DB_MPOOL_DIRTY;
319                 }
320                 if (change)
321                         LSN(pagep) = redo ? *lsnp : argp->nextlsn;
322                 if ((ret = memp_fput(mpf, pagep, change)) != 0)
323                         goto out;
324         }
325
326         *lsnp = argp->prev_lsn;
327
328 out:    REC_CLOSE;
329 }
330
331 /*
332  * __db_ovref_recover --
333  *      Recovery function for __db_ovref().
334  *
335  * PUBLIC: int __db_ovref_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
336  */
337 int
338 __db_ovref_recover(logp, dbtp, lsnp, redo, info)
339         DB_LOG *logp;
340         DBT *dbtp;
341         DB_LSN *lsnp;
342         int redo;
343         void *info;
344 {
345         __db_ovref_args *argp;
346         DB *file_dbp, *mdbp;
347         DB_MPOOLFILE *mpf;
348         PAGE *pagep;
349         int modified, ret;
350
351         REC_PRINT(__db_ovref_print);
352         REC_INTRO(__db_ovref_read);
353
354         if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
355                 (void)__db_pgerr(file_dbp, argp->pgno);
356                 goto out;
357         }
358
359         modified = 0;
360         if (log_compare(&LSN(pagep), &argp->lsn) == 0 && redo) {
361                 /* Need to redo update described. */
362                 OV_REF(pagep) += argp->adjust;
363
364                 pagep->lsn = *lsnp;
365                 modified = 1;
366         } else if (log_compare(lsnp, &LSN(pagep)) == 0 && !redo) {
367                 /* Need to undo update described. */
368                 OV_REF(pagep) -= argp->adjust;
369
370                 pagep->lsn = argp->lsn;
371                 modified = 1;
372         }
373         if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) == 0)
374                 *lsnp = argp->prev_lsn;
375
376 out:    REC_CLOSE;
377 }
378
379 /*
380  * __db_relink_recover --
381  *      Recovery function for relink.
382  *
383  * PUBLIC: int __db_relink_recover
384  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
385  */
386 int
387 __db_relink_recover(logp, dbtp, lsnp, redo, info)
388         DB_LOG *logp;
389         DBT *dbtp;
390         DB_LSN *lsnp;
391         int redo;
392         void *info;
393 {
394         __db_relink_args *argp;
395         DB *file_dbp, *mdbp;
396         DB_MPOOLFILE *mpf;
397         PAGE *pagep;
398         int modified, ret;
399
400         REC_PRINT(__db_relink_print);
401         REC_INTRO(__db_relink_read);
402
403         /*
404          * There are three pages we need to check -- the page, and the
405          * previous and next pages, if they existed.
406          */
407         if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
408                 if (redo) {
409                         (void)__db_pgerr(file_dbp, argp->pgno);
410                         goto out;
411                 }
412                 goto next;
413         }
414         modified = 0;
415         if (log_compare(&LSN(pagep), &argp->lsn) == 0 && redo) {
416                 /* Redo the relink. */
417                 pagep->lsn = *lsnp;
418                 modified = 1;
419         } else if (log_compare(lsnp, &LSN(pagep)) == 0 && !redo) {
420                 /* Undo the relink. */
421                 pagep->next_pgno = argp->next;
422                 pagep->prev_pgno = argp->prev;
423
424                 pagep->lsn = argp->lsn;
425                 modified = 1;
426         }
427         if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0) {
428                 (void)__db_panic(file_dbp);
429                 goto out;
430         }
431
432 next:   if ((ret = memp_fget(mpf, &argp->next, 0, &pagep)) != 0) {
433                 if (redo) {
434                         (void)__db_pgerr(file_dbp, argp->next);
435                         goto out;
436                 }
437                 goto prev;
438         }
439         modified = 0;
440         if (log_compare(&LSN(pagep), &argp->lsn_next) == 0 && redo) {
441                 /* Redo the relink. */
442                 pagep->prev_pgno = argp->prev;
443
444                 pagep->lsn = *lsnp;
445                 modified = 1;
446         } else if (log_compare(lsnp, &LSN(pagep)) == 0 && !redo) {
447                 /* Undo the relink. */
448                 pagep->prev_pgno = argp->pgno;
449
450                 pagep->lsn = argp->lsn_next;
451                 modified = 1;
452         }
453         if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0) {
454                 (void)__db_panic(file_dbp);
455                 goto out;
456         }
457
458 prev:   if ((ret = memp_fget(mpf, &argp->prev, 0, &pagep)) != 0) {
459                 if (redo) {
460                         (void)__db_pgerr(file_dbp, argp->prev);
461                         goto out;
462                 }
463                 goto done;
464         }
465         modified = 0;
466         if (log_compare(&LSN(pagep), &argp->lsn_prev) == 0 && redo) {
467                 /* Redo the relink. */
468                 pagep->next_pgno = argp->next;
469
470                 pagep->lsn = *lsnp;
471                 modified = 1;
472         } else if (log_compare(lsnp, &LSN(pagep)) == 0 && !redo) {
473                 /* Undo the relink. */
474                 pagep->next_pgno = argp->pgno;
475
476                 pagep->lsn = argp->lsn_prev;
477                 modified = 1;
478         }
479         if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0) {
480                 (void) __db_panic(file_dbp);
481                 goto out;
482         }
483
484 done:   *lsnp = argp->prev_lsn;
485         ret = 0;
486
487 out:    REC_CLOSE;
488 }
489
490 /*
491  * PUBLIC: int __db_addpage_recover
492  * PUBLIC:    __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
493  */
494 int
495 __db_addpage_recover(logp, dbtp, lsnp, redo, info)
496         DB_LOG *logp;
497         DBT *dbtp;
498         DB_LSN *lsnp;
499         int redo;
500         void *info;
501 {
502         __db_addpage_args *argp;
503         DB *file_dbp, *mdbp;
504         DB_MPOOLFILE *mpf;
505         PAGE *pagep;
506         int change, cmp_n, cmp_p, ret;
507
508         REC_PRINT(__db_addpage_print);
509         REC_INTRO(__db_addpage_read);
510
511         /*
512          * We need to check two pages: the old one and the new one onto
513          * which we're going to add duplicates.  Do the old one first.
514          */
515         if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0)
516                 goto out;
517
518         change = 0;
519         cmp_n = log_compare(lsnp, &LSN(pagep));
520         cmp_p = log_compare(&LSN(pagep), &argp->lsn);
521         if (cmp_p == 0 && redo) {
522                 NEXT_PGNO(pagep) = argp->nextpgno;
523
524                 LSN(pagep) = *lsnp;
525                 change = DB_MPOOL_DIRTY;
526         } else if (cmp_n == 0 && !redo) {
527                 NEXT_PGNO(pagep) = PGNO_INVALID;
528
529                 LSN(pagep) = argp->lsn;
530                 change = DB_MPOOL_DIRTY;
531         }
532         if ((ret = memp_fput(mpf, pagep, change)) != 0)
533                 goto out;
534
535         if ((ret = memp_fget(mpf, &argp->nextpgno, 0, &pagep)) != 0)
536                 if (!redo) {
537                         /*
538                          * We are undoing and the page doesn't exist.  That
539                          * is equivalent to having a pagelsn of 0, so we
540                          * would not have to undo anything.  In this case,
541                          * don't bother creating a page.
542                          */
543                         ret = 0;
544                         goto out;
545                 } else
546                         if ((ret = memp_fget(mpf,
547                             &argp->nextpgno, DB_MPOOL_CREATE, &pagep)) != 0)
548                                 goto out;
549
550         change = 0;
551         cmp_n = log_compare(lsnp, &LSN(pagep));
552         cmp_p = log_compare(&LSN(pagep), &argp->nextlsn);
553         if (cmp_p == 0 && redo) {
554                 PREV_PGNO(pagep) = argp->pgno;
555
556                 LSN(pagep) = *lsnp;
557                 change = DB_MPOOL_DIRTY;
558         } else if (cmp_n == 0 && !redo) {
559                 PREV_PGNO(pagep) = PGNO_INVALID;
560
561                 LSN(pagep) = argp->nextlsn;
562                 change = DB_MPOOL_DIRTY;
563         }
564         ret = memp_fput(mpf, pagep, change);
565
566 out:    if (ret == 0)
567                 *lsnp = argp->prev_lsn;
568         REC_CLOSE;
569 }
570
571 /*
572  * __db_debug_recover --
573  *      Recovery function for debug.
574  *
575  * PUBLIC: int __db_debug_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
576  */
577 int
578 __db_debug_recover(logp, dbtp, lsnp, redo, info)
579         DB_LOG *logp;
580         DBT *dbtp;
581         DB_LSN *lsnp;
582         int redo;
583         void *info;
584 {
585         __db_debug_args *argp;
586         int ret;
587
588         REC_PRINT(__db_debug_print);
589         REC_NOOP_INTRO(__db_debug_read);
590
591         *lsnp = argp->prev_lsn;
592         ret = 0;
593
594         REC_NOOP_CLOSE;
595 }
596
597 /*
598  * __db_noop_recover --
599  *      Recovery function for noop.
600  *
601  * PUBLIC: int __db_noop_recover
602  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
603  */
604 int
605 __db_noop_recover(logp, dbtp, lsnp, redo, info)
606         DB_LOG *logp;
607         DBT *dbtp;
608         DB_LSN *lsnp;
609         int redo;
610         void *info;
611 {
612         __db_noop_args *argp;
613         int ret;
614
615         REC_PRINT(__db_noop_print);
616         REC_NOOP_INTRO(__db_noop_read);
617
618         *lsnp = argp->prev_lsn;
619         ret = 0;
620
621         REC_NOOP_CLOSE;
622 }