Update from db-2.3.12.
[kopensolaris-gnu/glibc.git] / db2 / common / db_apprec.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 copyright[] =
12 "@(#) Copyright (c) 1997\n\
13         Sleepycat Software Inc.  All rights reserved.\n";
14 static const char sccsid[] = "@(#)db_apprec.c   10.18 (Sleepycat) 9/30/97";
15 #endif
16
17 #ifndef NO_SYSTEM_INCLUDES
18 #include <sys/types.h>
19
20 #include <time.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #endif
24
25 #include "db_int.h"
26 #include "shqueue.h"
27 #include "db_page.h"
28 #include "db_dispatch.h"
29 #include "db_am.h"
30 #include "log.h"
31 #include "txn.h"
32 #include "common_ext.h"
33
34 /*
35  * __db_apprec --
36  *      Perform recovery.
37  *
38  * PUBLIC: int __db_apprec __P((DB_ENV *, int));
39  */
40 int
41 __db_apprec(dbenv, flags)
42         DB_ENV *dbenv;
43         int flags;
44 {
45         DBT data;
46         DB_LOG *lp;
47         DB_LSN ckp_lsn, first_lsn, lsn, tmp_lsn;
48         time_t now;
49         int first_flag, is_thread, ret;
50         void *txninfo;
51
52         lp = dbenv->lg_info;
53
54         /* Initialize the transaction list. */
55         if ((ret = __db_txnlist_init(&txninfo)) != 0)
56                 return (ret);
57
58         /*
59          * Save the state of the thread flag -- we don't need it on at the
60          * moment because we're single-threaded until recovery is complete.
61          */
62         is_thread = F_ISSET(lp, DB_AM_THREAD);
63         F_CLR(lp, DB_AM_THREAD);
64
65         /*
66          * Read forward through the log, opening the appropriate files so that
67          * we can call recovery routines.  In general, we start at the last
68          * checkpoint prior to the last checkpointed LSN.  For catastrophic
69          * recovery, we begin at the first LSN that appears in any log file
70          * (log_get figures this out for us when we pass it the DB_FIRST flag).
71          */
72         if (LF_ISSET(DB_RECOVER_FATAL))
73                 first_flag = DB_FIRST;
74         else {
75                 if ((ret = __log_findckp(lp, &lsn)) == DB_NOTFOUND) {
76                         F_SET(lp, is_thread);
77                         return (0);
78                 }
79                 first_flag = DB_SET;
80         }
81
82         /* If we're a threaded application, we have to allocate space. */
83         memset(&data, 0, sizeof(data));
84         if ((ret = log_get(lp, &lsn, &data, first_flag)) != 0) {
85                 __db_err(dbenv, "Failure: unable to get log record");
86                 if (first_flag == DB_SET)
87                         __db_err(dbenv, "Retrieving LSN %lu %lu",
88                             (u_long)lsn.file, (u_long)lsn.offset);
89                 else
90                         __db_err(dbenv, "Retrieving first LSN");
91                 goto err;
92         }
93
94         first_lsn = lsn;
95         for (;;) {
96                 ret = __db_dispatch(lp, &data, &lsn, TXN_OPENFILES, txninfo);
97                 if (ret != 0 && ret != DB_TXN_CKP)
98                         goto msgerr;
99                 if ((ret =
100                     log_get(dbenv->lg_info, &lsn, &data, DB_NEXT)) != 0) {
101                         if (ret != DB_NOTFOUND)
102                                 goto err;
103                         break;
104                 }
105         }
106
107         /*
108          * Initialize the ckp_lsn to 0,0.  If we never find a valid
109          * checkpoint in the log, then leaving ckp_lsn at 0,0 is correct.
110          */
111         ZERO_LSN(ckp_lsn);
112         for (ret = log_get(lp, &lsn, &data, DB_LAST);
113             ret == 0 && log_compare(&lsn, &first_lsn) > 0;
114             ret = log_get(lp,&lsn, &data, DB_PREV)) {
115                 tmp_lsn = lsn;
116                 ret = __db_dispatch(lp,
117                     &data, &lsn, TXN_BACKWARD_ROLL, txninfo);
118                 if (ret == DB_TXN_CKP) {
119                         if (IS_ZERO_LSN(ckp_lsn))
120                                 ckp_lsn = tmp_lsn;
121                         ret = 0;
122                 } else if (ret != 0)
123                         goto msgerr;
124         }
125         if (ret != 0 && ret != DB_NOTFOUND)
126                 goto err;
127
128         for (ret = log_get(lp, &lsn, &data, DB_NEXT);
129             ret == 0; ret = log_get(lp, &lsn, &data, DB_NEXT)) {
130                 ret = __db_dispatch(lp, &data, &lsn, TXN_FORWARD_ROLL, txninfo);
131                 if (ret == DB_TXN_CKP)
132                         ret = 0;
133                 else if (ret != 0)
134                         goto msgerr;
135         }
136         if (ret != DB_NOTFOUND)
137                 goto err;
138
139         /* Now close all the db files that are open. */
140         __log_close_files(lp);
141
142         /*
143          * Now set the maximum transaction id, set the last checkpoint lsn,
144          * and the current time.  Then take a checkpoint.
145          */
146         (void)time(&now);
147         dbenv->tx_info->region->last_txnid = ((__db_txnhead *)txninfo)->maxid;
148         dbenv->tx_info->region->last_ckp = ckp_lsn;
149         dbenv->tx_info->region->time_ckp = (u_int32_t)now;
150         if ((ret = txn_checkpoint(dbenv->tx_info, 0, 0)) != 0)
151                 goto err;
152
153         if (dbenv->db_verbose) {
154                 __db_err(lp->dbenv, "Recovery complete at %s", ctime(&now));
155                 __db_err(lp->dbenv, "%s %lu %s [%lu][%lu]",
156                     "Maximum transaction id",
157                     (u_long)dbenv->tx_info->region->last_txnid,
158                     "Recovery checkpoint",
159                     (u_long)dbenv->tx_info->region->last_ckp.file,
160                     (u_long)dbenv->tx_info->region->last_ckp.offset);
161         }
162
163         F_SET(lp, is_thread);
164         return (0);
165
166 msgerr: __db_err(dbenv, "Recovery function for LSN %lu %lu failed",
167             (u_long)lsn.file, (u_long)lsn.offset);
168
169 err:    F_SET(lp, is_thread);
170         return (ret);
171 }