900b0ed579586c1bab512d365bc5d36608a129cd
[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.8 (Sleepycat) 8/22/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_ioff().
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(lsnp, &argp->lsn) == 0 && redo) {
361                 /* Need to redo update described. */
362                 ++OV_REF(pagep);
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);
369
370                 pagep->lsn = argp->lsn;
371                 modified = 1;
372         }
373         ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0);
374
375         *lsnp = argp->prev_lsn;
376
377 out:    REC_CLOSE;
378 }
379
380 /*
381  * __db_relink_recover --
382  *      Recovery function for relink.
383  *
384  * PUBLIC: int __db_relink_recover
385  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
386  */
387 int
388 __db_relink_recover(logp, dbtp, lsnp, redo, info)
389         DB_LOG *logp;
390         DBT *dbtp;
391         DB_LSN *lsnp;
392         int redo;
393         void *info;
394 {
395         __db_relink_args *argp;
396         DB *file_dbp, *mdbp;
397         DB_MPOOLFILE *mpf;
398         PAGE *pagep;
399         int modified, ret;
400
401         REC_PRINT(__db_relink_print);
402         REC_INTRO(__db_relink_read);
403
404         /*
405          * There are three pages we need to check -- the page, and the
406          * previous and next pages, if they existed.
407          */
408         if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
409                 if (redo) {
410                         (void)__db_pgerr(file_dbp, argp->pgno);
411                         goto out;
412                 }
413                 goto next;
414         }
415         modified = 0;
416         if (log_compare(lsnp, &argp->lsn) == 0 && redo) {
417                 /* Redo the relink. */
418                 pagep->lsn = *lsnp;
419                 modified = 1;
420         } else if (log_compare(lsnp, &LSN(pagep)) == 0 && !redo) {
421                 /* Undo the relink. */
422                 pagep->next_pgno = argp->next;
423                 pagep->prev_pgno = argp->prev;
424
425                 pagep->lsn = argp->lsn;
426                 modified = 1;
427         }
428         if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0) {
429                 (void)__db_panic(file_dbp);
430                 goto out;
431         }
432
433 next:   if ((ret = memp_fget(mpf, &argp->next, 0, &pagep)) != 0) {
434                 if (redo) {
435                         (void)__db_pgerr(file_dbp, argp->next);
436                         goto out;
437                 }
438                 goto prev;
439         }
440         modified = 0;
441         if (log_compare(lsnp, &argp->lsn_next) == 0 && redo) {
442                 /* Redo the relink. */
443                 pagep->prev_pgno = argp->prev;
444
445                 pagep->lsn = *lsnp;
446                 modified = 1;
447         } else if (log_compare(lsnp, &LSN(pagep)) == 0 && !redo) {
448                 /* Undo the relink. */
449                 pagep->prev_pgno = argp->pgno;
450
451                 pagep->lsn = argp->lsn_next;
452                 modified = 1;
453         }
454         if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0) {
455                 (void)__db_panic(file_dbp);
456                 goto out;
457         }
458
459 prev:   if ((ret = memp_fget(mpf, &argp->prev, 0, &pagep)) != 0) {
460                 if (redo) {
461                         (void)__db_pgerr(file_dbp, argp->prev);
462                         goto out;
463                 }
464                 goto done;
465         }
466         modified = 0;
467         if (log_compare(lsnp, &argp->lsn_prev) == 0 && redo) {
468                 /* Redo the relink. */
469                 pagep->next_pgno = argp->next;
470
471                 pagep->lsn = *lsnp;
472                 modified = 1;
473         } else if (log_compare(lsnp, &LSN(pagep)) == 0 && !redo) {
474                 /* Undo the relink. */
475                 pagep->next_pgno = argp->pgno;
476
477                 pagep->lsn = argp->lsn_prev;
478                 modified = 1;
479         }
480         if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0) {
481                 (void) __db_panic(file_dbp);
482                 goto out;
483         }
484
485 done:   *lsnp = argp->prev_lsn;
486         ret = 0;
487
488 out:    REC_CLOSE;
489 }
490
491 /*
492  * PUBLIC: int __db_addpage_recover
493  * PUBLIC:    __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
494  */
495 int
496 __db_addpage_recover(logp, dbtp, lsnp, redo, info)
497         DB_LOG *logp;
498         DBT *dbtp;
499         DB_LSN *lsnp;
500         int redo;
501         void *info;
502 {
503         __db_addpage_args *argp;
504         DB *file_dbp, *mdbp;
505         DB_MPOOLFILE *mpf;
506         PAGE *pagep;
507         int change, cmp_n, cmp_p, ret;
508
509         REC_PRINT(__db_addpage_print);
510         REC_INTRO(__db_addpage_read);
511
512         /*
513          * We need to check two pages: the old one and the new one onto
514          * which we're going to add duplicates.  Do the old one first.
515          */
516         if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0)
517                 goto out;
518
519         change = 0;
520         cmp_n = log_compare(lsnp, &LSN(pagep));
521         cmp_p = log_compare(&LSN(pagep), &argp->lsn);
522         if (cmp_p == 0 && redo) {
523                 NEXT_PGNO(pagep) = argp->nextpgno;
524
525                 LSN(pagep) = *lsnp;
526                 change = DB_MPOOL_DIRTY;
527         } else if (cmp_n == 0 && !redo) {
528                 NEXT_PGNO(pagep) = PGNO_INVALID;
529
530                 LSN(pagep) = argp->lsn;
531                 change = DB_MPOOL_DIRTY;
532         }
533         if ((ret = memp_fput(mpf, pagep, change)) != 0)
534                 goto out;
535
536         if ((ret = memp_fget(mpf, &argp->nextpgno, 0, &pagep)) != 0)
537                 if (!redo) {
538                         /*
539                          * We are undoing and the page doesn't exist.  That
540                          * is equivalent to having a pagelsn of 0, so we
541                          * would not have to undo anything.  In this case,
542                          * don't bother creating a page.
543                          */
544                         ret = 0;
545                         goto out;
546                 } else
547                         if ((ret = memp_fget(mpf,
548                             &argp->nextpgno, DB_MPOOL_CREATE, &pagep)) != 0)
549                                 goto out;
550
551         change = 0;
552         cmp_n = log_compare(lsnp, &LSN(pagep));
553         cmp_p = log_compare(&LSN(pagep), &argp->nextlsn);
554         if (cmp_p == 0 && redo) {
555                 PREV_PGNO(pagep) = argp->pgno;
556
557                 LSN(pagep) = *lsnp;
558                 change = DB_MPOOL_DIRTY;
559         } else if (cmp_n == 0 && !redo) {
560                 PREV_PGNO(pagep) = PGNO_INVALID;
561
562                 LSN(pagep) = argp->nextlsn;
563                 change = DB_MPOOL_DIRTY;
564         }
565         ret = memp_fput(mpf, pagep, change);
566
567 out:    if (ret == 0)
568                 *lsnp = argp->prev_lsn;
569         REC_CLOSE;
570 }
571
572 /*
573  * __db_debug_recover --
574  *      Recovery function for debug.
575  *
576  * PUBLIC: int __db_debug_recover __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
577  */
578 int
579 __db_debug_recover(logp, dbtp, lsnp, redo, info)
580         DB_LOG *logp;
581         DBT *dbtp;
582         DB_LSN *lsnp;
583         int redo;
584         void *info;
585 {
586         __db_debug_args *argp;
587         int ret;
588
589         REC_PRINT(__db_debug_print);
590         REC_NOOP_INTRO(__db_debug_read);
591
592         *lsnp = argp->prev_lsn;
593         ret = 0;
594
595         REC_NOOP_CLOSE;
596 }
597
598 /*
599  * __db_noop_recover --
600  *      Recovery function for noop.
601  *
602  * PUBLIC: int __db_noop_recover
603  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
604  */
605 int
606 __db_noop_recover(logp, dbtp, lsnp, redo, info)
607         DB_LOG *logp;
608         DBT *dbtp;
609         DB_LSN *lsnp;
610         int redo;
611         void *info;
612 {
613         __db_noop_args *argp;
614         int ret;
615
616         REC_PRINT(__db_noop_print);
617         REC_NOOP_INTRO(__db_noop_read);
618
619         *lsnp = argp->prev_lsn;
620         ret = 0;
621
622         REC_NOOP_CLOSE;
623 }