add packaging
[platform/upstream/db4.git] / txn / txn_rec.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996-2009 Oracle.  All rights reserved.
5  */
6 /*
7  * Copyright (c) 1996
8  *      The President and Fellows of Harvard University.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * $Id$
35  */
36
37 #include "db_config.h"
38
39 #include "db_int.h"
40 #include "dbinc/db_page.h"
41 #include "dbinc/lock.h"
42 #include "dbinc/txn.h"
43 #include "dbinc/db_am.h"
44
45 /*
46  * PUBLIC: int __txn_regop_recover
47  * PUBLIC:    __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
48  *
49  * These records are only ever written for commits.  Normally, we redo any
50  * committed transaction, however if we are doing recovery to a timestamp, then
51  * we may treat transactions that committed after the timestamp as aborted.
52  */
53 int
54 __txn_regop_recover(env, dbtp, lsnp, op, info)
55         ENV *env;
56         DBT *dbtp;
57         DB_LSN *lsnp;
58         db_recops op;
59         void *info;
60 {
61         __txn_regop_args *argp;
62         DB_TXNHEAD *headp;
63         int ret;
64         u_int32_t status;
65
66 #ifdef DEBUG_RECOVER
67         (void)__txn_regop_print(env, dbtp, lsnp, op, info);
68 #endif
69
70         if ((ret = __txn_regop_read(env, dbtp->data, &argp)) != 0)
71                 return (ret);
72
73         headp = info;
74         /*
75          * We are only ever called during FORWARD_ROLL or BACKWARD_ROLL.
76          * We check for the former explicitly and the last two clauses
77          * apply to the BACKWARD_ROLL case.
78          */
79
80         if (op == DB_TXN_FORWARD_ROLL) {
81                 /*
82                  * If this was a 2-phase-commit transaction, then it
83                  * might already have been removed from the list, and
84                  * that's OK.  Ignore the return code from remove.
85                  */
86                 if ((ret = __db_txnlist_remove(env,
87                     info, argp->txnp->txnid)) != DB_NOTFOUND && ret != 0)
88                         goto err;
89         } else if ((env->dbenv->tx_timestamp != 0 &&
90             argp->timestamp > (int32_t)env->dbenv->tx_timestamp) ||
91             (!IS_ZERO_LSN(headp->trunc_lsn) &&
92             LOG_COMPARE(&headp->trunc_lsn, lsnp) < 0)) {
93                 /*
94                  * We failed either the timestamp check or the trunc_lsn check,
95                  * so we treat this as an abort even if it was a commit record.
96                  */
97                 if ((ret = __db_txnlist_update(env, info,
98                     argp->txnp->txnid, TXN_ABORT, NULL, &status, 1)) != 0)
99                         goto err;
100                 else if (status != TXN_IGNORE && status != TXN_OK)
101                         goto err;
102         } else {
103                 /* This is a normal commit; mark it appropriately. */
104                 if ((ret = __db_txnlist_update(env,
105                     info, argp->txnp->txnid, argp->opcode, lsnp,
106                     &status, 0)) == DB_NOTFOUND) {
107                         if ((ret = __db_txnlist_add(env,
108                             info, argp->txnp->txnid,
109                             argp->opcode == TXN_ABORT ?
110                             TXN_IGNORE : argp->opcode, lsnp)) != 0)
111                                 goto err;
112                 } else if (ret != 0 ||
113                     (status != TXN_IGNORE && status != TXN_OK))
114                         goto err;
115         }
116
117         if (ret == 0)
118                 *lsnp = argp->prev_lsn;
119
120         if (0) {
121 err:            __db_errx(env,
122                     "txnid %lx commit record found, already on commit list",
123                     (u_long)argp->txnp->txnid);
124                 ret = EINVAL;
125         }
126         __os_free(env, argp);
127
128         return (ret);
129 }
130
131 /*
132  * PUBLIC: int __txn_prepare_recover
133  * PUBLIC:    __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
134  *
135  * These records are only ever written for prepares.
136  */
137 int
138 __txn_prepare_recover(env, dbtp, lsnp, op, info)
139         ENV *env;
140         DBT *dbtp;
141         DB_LSN *lsnp;
142         db_recops op;
143         void *info;
144 {
145         __txn_prepare_args *argp;
146         DBT *lock_dbt;
147         DB_TXNHEAD *headp;
148         DB_LOCKTAB *lt;
149         u_int32_t status;
150         int ret;
151
152 #ifdef DEBUG_RECOVER
153         (void)__txn_prepare_print(env, dbtp, lsnp, op, info);
154 #endif
155
156         if ((ret = __txn_prepare_read(env, dbtp->data, &argp)) != 0)
157                 return (ret);
158
159         if (argp->opcode != TXN_PREPARE && argp->opcode != TXN_ABORT) {
160                 ret = EINVAL;
161                 goto err;
162         }
163         headp = info;
164
165         /*
166          * The return value here is either a DB_NOTFOUND or it is
167          * the transaction status from the list.  It is not a normal
168          * error return, so we must make sure that in each of the
169          * cases below, we overwrite the ret value so we return
170          * appropriately.
171          */
172         ret = __db_txnlist_find(env, info, argp->txnp->txnid, &status);
173
174         /*
175          * If we are rolling forward, then an aborted prepare
176          * indicates that this may be the last record we'll see for
177          * this transaction ID, so we should remove it from the list.
178          */
179
180         if (op == DB_TXN_FORWARD_ROLL) {
181                 if ((ret = __db_txnlist_remove(env,
182                     info, argp->txnp->txnid)) != 0)
183                         goto txn_err;
184         } else if (op == DB_TXN_BACKWARD_ROLL && status == TXN_PREPARE) {
185                 /*
186                  * On the backward pass, we have four possibilities:
187                  * 1. The transaction is already committed, no-op.
188                  * 2. The transaction is already aborted, no-op.
189                  * 3. The prepare failed and was aborted, mark as abort.
190                  * 4. The transaction is neither committed nor aborted.
191                  *       Treat this like a commit and roll forward so that
192                  *       the transaction can be resurrected in the region.
193                  * We handle cases 3 and 4 here; cases 1 and 2
194                  * are the final clause below.
195                  */
196                 if (argp->opcode == TXN_ABORT) {
197                         if ((ret = __db_txnlist_update(env,
198                              info, argp->txnp->txnid,
199                              TXN_ABORT, NULL, &status, 0)) != 0 &&
200                              status != TXN_PREPARE)
201                                 goto txn_err;
202                         ret = 0;
203                 }
204                 /*
205                  * This is prepared, but not yet committed transaction.  We
206                  * need to add it to the transaction list, so that it gets
207                  * rolled forward. We also have to add it to the region's
208                  * internal state so it can be properly aborted or committed
209                  * after recovery (see txn_recover).
210                  */
211                 else if ((ret = __db_txnlist_remove(env,
212                     info, argp->txnp->txnid)) != 0) {
213 txn_err:                __db_errx(env,
214                             "transaction not in list %lx",
215                             (u_long)argp->txnp->txnid);
216                         ret = DB_NOTFOUND;
217                 } else if (IS_ZERO_LSN(headp->trunc_lsn) ||
218                     LOG_COMPARE(&headp->trunc_lsn, lsnp) >= 0) {
219                         if ((ret = __db_txnlist_add(env,
220                            info, argp->txnp->txnid, TXN_COMMIT, lsnp)) == 0) {
221                                 /* Re-acquire the locks for this transaction. */
222                                 lock_dbt = &argp->locks;
223                                 if (LOCKING_ON(env)) {
224                                         lt = env->lk_handle;
225                                         if ((ret = __lock_getlocker(lt,
226                                                 argp->txnp->txnid, 1,
227                                                 &argp->txnp->locker)) != 0)
228                                                 goto err;
229                                         if ((ret = __lock_get_list(env,
230                                             argp->txnp->locker, 0,
231                                             DB_LOCK_WRITE, lock_dbt)) != 0)
232                                                 goto err;
233                                 }
234
235                                 ret = __txn_restore_txn(env, lsnp, argp);
236                         }
237                 }
238         } else
239                 ret = 0;
240
241         if (ret == 0)
242                 *lsnp = argp->prev_lsn;
243
244 err:    __os_free(env, argp);
245
246         return (ret);
247 }
248
249 /*
250  * PUBLIC: int __txn_ckp_recover
251  * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
252  */
253 int
254 __txn_ckp_recover(env, dbtp, lsnp, op, info)
255         ENV *env;
256         DBT *dbtp;
257         DB_LSN *lsnp;
258         db_recops op;
259         void *info;
260 {
261         __txn_ckp_args *argp;
262         int ret;
263
264 #ifdef DEBUG_RECOVER
265         __txn_ckp_print(env, dbtp, lsnp, op, info);
266 #endif
267         if ((ret = __txn_ckp_read(env, dbtp->data, &argp)) != 0)
268                 return (ret);
269
270         if (op == DB_TXN_BACKWARD_ROLL)
271                 __db_txnlist_ckp(env, info, lsnp);
272
273         *lsnp = argp->last_ckp;
274         __os_free(env, argp);
275         return (DB_TXN_CKP);
276 }
277
278 /*
279  * __txn_child_recover
280  *      Recover a commit record for a child transaction.
281  *
282  * PUBLIC: int __txn_child_recover
283  * PUBLIC:    __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
284  */
285 int
286 __txn_child_recover(env, dbtp, lsnp, op, info)
287         ENV *env;
288         DBT *dbtp;
289         DB_LSN *lsnp;
290         db_recops op;
291         void *info;
292 {
293         __txn_child_args *argp;
294         u_int32_t c_stat, p_stat, tmpstat;
295         int ret, t_ret;
296
297 #ifdef DEBUG_RECOVER
298         (void)__txn_child_print(env, dbtp, lsnp, op, info);
299 #endif
300         if ((ret = __txn_child_read(env, dbtp->data, &argp)) != 0)
301                 return (ret);
302
303         /*
304          * This is a record in a PARENT's log trail indicating that a
305          * child committed.  If we are aborting, return the childs last
306          * record's LSN.  If we are in recovery, then if the
307          * parent is committing, we set ourselves up to commit, else
308          * we do nothing.
309          */
310         if (op == DB_TXN_ABORT) {
311                 *lsnp = argp->c_lsn;
312                 ret = __db_txnlist_lsnadd(env, info, &argp->prev_lsn);
313                 goto out;
314         } else if (op == DB_TXN_BACKWARD_ROLL) {
315                 /* Child might exist -- look for it. */
316                 ret = __db_txnlist_find(env, info, argp->child, &c_stat);
317                 t_ret =
318                     __db_txnlist_find(env, info, argp->txnp->txnid, &p_stat);
319                 if (ret != 0 && ret != DB_NOTFOUND)
320                         goto out;
321                 if (t_ret != 0 && t_ret != DB_NOTFOUND) {
322                         ret = t_ret;
323                         goto out;
324                 }
325                 /*
326                  * If the parent is in state COMMIT or IGNORE, then we apply
327                  * that to the child, else we need to abort the child.
328                  */
329
330                 if (ret == DB_NOTFOUND  ||
331                     c_stat == TXN_OK || c_stat == TXN_COMMIT) {
332                         if (t_ret == DB_NOTFOUND ||
333                              (p_stat != TXN_COMMIT  && p_stat != TXN_IGNORE))
334                                 c_stat = TXN_ABORT;
335                         else
336                                 c_stat = p_stat;
337
338                         if (ret == DB_NOTFOUND)
339                                 ret = __db_txnlist_add(env,
340                                      info, argp->child, c_stat, NULL);
341                         else
342                                 ret = __db_txnlist_update(env, info,
343                                      argp->child, c_stat, NULL, &tmpstat, 0);
344                 } else if (c_stat == TXN_EXPECTED) {
345                         /*
346                          * The open after this create succeeded.  If the
347                          * parent succeeded, we don't want to redo; if the
348                          * parent aborted, we do want to undo.
349                          */
350                         switch (p_stat) {
351                         case TXN_COMMIT:
352                         case TXN_IGNORE:
353                                 c_stat = TXN_IGNORE;
354                                 break;
355                         default:
356                                 c_stat = TXN_ABORT;
357                         }
358                         ret = __db_txnlist_update(env,
359                             info, argp->child, c_stat, NULL, &tmpstat, 0);
360                 } else if (c_stat == TXN_UNEXPECTED) {
361                         /*
362                          * The open after this create failed.  If the parent
363                          * is rolling forward, we need to roll forward.  If
364                          * the parent failed, then we do not want to abort
365                          * (because the file may not be the one in which we
366                          * are interested).
367                          */
368                         ret = __db_txnlist_update(env, info, argp->child,
369                             p_stat == TXN_COMMIT ? TXN_COMMIT : TXN_IGNORE,
370                             NULL, &tmpstat, 0);
371                 }
372         } else if (op == DB_TXN_OPENFILES) {
373                 /*
374                  * If we have a partial subtransaction, then the whole
375                  * transaction should be ignored.
376                  */
377                 if ((ret = __db_txnlist_find(env,
378                     info, argp->child, &c_stat)) == DB_NOTFOUND)
379                         ret = __db_txnlist_update(env, info,
380                              argp->txnp->txnid, TXN_IGNORE,
381                              NULL, &p_stat, 1);
382         } else if (DB_REDO(op)) {
383                 /* Forward Roll */
384                 if ((ret =
385                     __db_txnlist_remove(env, info, argp->child)) != 0)
386                         __db_errx(env,
387                             "Transaction not in list %x", argp->child);
388         }
389
390         if (ret == 0)
391                 *lsnp = argp->prev_lsn;
392
393 out:    __os_free(env, argp);
394
395         return (ret);
396 }
397
398 /*
399  * __txn_restore_txn --
400  *      Using only during XA recovery.  If we find any transactions that are
401  * prepared, but not yet committed, then we need to restore the transaction's
402  * state into the shared region, because the TM is going to issue an abort
403  * or commit and we need to respond correctly.
404  *
405  * lsnp is the LSN of the returned LSN
406  * argp is the prepare record (in an appropriate structure)
407  *
408  * PUBLIC: int __txn_restore_txn __P((ENV *, DB_LSN *, __txn_prepare_args *));
409  */
410 int
411 __txn_restore_txn(env, lsnp, argp)
412         ENV *env;
413         DB_LSN *lsnp;
414         __txn_prepare_args *argp;
415 {
416         DB_TXNMGR *mgr;
417         DB_TXNREGION *region;
418         TXN_DETAIL *td;
419         int ret;
420
421         if (argp->gid.size == 0)
422                 return (0);
423
424         mgr = env->tx_handle;
425         region = mgr->reginfo.primary;
426         TXN_SYSTEM_LOCK(env);
427
428         /* Allocate a new transaction detail structure. */
429         if ((ret = __env_alloc(&mgr->reginfo, sizeof(TXN_DETAIL), &td)) != 0) {
430                 TXN_SYSTEM_UNLOCK(env);
431                 return (ret);
432         }
433
434         /* Place transaction on active transaction list. */
435         SH_TAILQ_INSERT_HEAD(&region->active_txn, td, links, __txn_detail);
436
437         td->txnid = argp->txnp->txnid;
438         __os_id(env->dbenv, &td->pid, &td->tid);
439         td->last_lsn = *lsnp;
440         td->begin_lsn = argp->begin_lsn;
441         td->parent = INVALID_ROFF;
442         td->name = INVALID_ROFF;
443         SH_TAILQ_INIT(&td->kids);
444         MAX_LSN(td->read_lsn);
445         MAX_LSN(td->visible_lsn);
446         td->mvcc_ref = 0;
447         td->mvcc_mtx = MUTEX_INVALID;
448         td->status = TXN_PREPARED;
449         td->flags = TXN_DTL_RESTORED;
450         memcpy(td->gid, argp->gid.data, argp->gid.size);
451         td->nlog_dbs = 0;
452         td->nlog_slots = TXN_NSLOTS;
453         td->log_dbs = R_OFFSET(&mgr->reginfo, td->slots);
454
455         region->stat.st_nrestores++;
456 #ifdef HAVE_STATISTICS
457         region->stat.st_nactive++;
458         if (region->stat.st_nactive > region->stat.st_maxnactive)
459                 region->stat.st_maxnactive = region->stat.st_nactive;
460 #endif
461         TXN_SYSTEM_UNLOCK(env);
462         return (0);
463 }
464
465 /*
466  * __txn_recycle_recover --
467  *      Recovery function for recycle.
468  *
469  * PUBLIC: int __txn_recycle_recover
470  * PUBLIC:   __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
471  */
472 int
473 __txn_recycle_recover(env, dbtp, lsnp, op, info)
474         ENV *env;
475         DBT *dbtp;
476         DB_LSN *lsnp;
477         db_recops op;
478         void *info;
479 {
480         __txn_recycle_args *argp;
481         int ret;
482
483 #ifdef DEBUG_RECOVER
484         (void)__txn_child_print(env, dbtp, lsnp, op, info);
485 #endif
486         if ((ret = __txn_recycle_read(env, dbtp->data, &argp)) != 0)
487                 return (ret);
488
489         COMPQUIET(lsnp, NULL);
490
491         if ((ret = __db_txnlist_gen(env, info,
492             DB_UNDO(op) ? -1 : 1, argp->min, argp->max)) != 0)
493                 return (ret);
494
495         __os_free(env, argp);
496
497         return (0);
498 }
499
500 /*
501  * PUBLIC: int __txn_regop_42_recover
502  * PUBLIC:    __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
503  *
504  * These records are only ever written for commits.  Normally, we redo any
505  * committed transaction, however if we are doing recovery to a timestamp, then
506  * we may treat transactions that committed after the timestamp as aborted.
507  */
508 int
509 __txn_regop_42_recover(env, dbtp, lsnp, op, info)
510         ENV *env;
511         DBT *dbtp;
512         DB_LSN *lsnp;
513         db_recops op;
514         void *info;
515 {
516         __txn_regop_42_args *argp;
517         DB_TXNHEAD *headp;
518         u_int32_t status;
519         int ret;
520
521 #ifdef DEBUG_RECOVER
522         (void)__txn_regop_42_print(env, dbtp, lsnp, op, info);
523 #endif
524
525         if ((ret = __txn_regop_42_read(env, dbtp->data, &argp)) != 0)
526                 return (ret);
527
528         headp = info;
529         /*
530          * We are only ever called during FORWARD_ROLL or BACKWARD_ROLL.
531          * We check for the former explicitly and the last two clauses
532          * apply to the BACKWARD_ROLL case.
533          */
534
535         if (op == DB_TXN_FORWARD_ROLL) {
536                 /*
537                  * If this was a 2-phase-commit transaction, then it
538                  * might already have been removed from the list, and
539                  * that's OK.  Ignore the return code from remove.
540                  */
541                 if ((ret = __db_txnlist_remove(env,
542                     info, argp->txnp->txnid)) != DB_NOTFOUND && ret != 0)
543                         goto err;
544         } else if ((env->dbenv->tx_timestamp != 0 &&
545             argp->timestamp > (int32_t)env->dbenv->tx_timestamp) ||
546             (!IS_ZERO_LSN(headp->trunc_lsn) &&
547             LOG_COMPARE(&headp->trunc_lsn, lsnp) < 0)) {
548                 /*
549                  * We failed either the timestamp check or the trunc_lsn check,
550                  * so we treat this as an abort even if it was a commit record.
551                  */
552                 if ((ret = __db_txnlist_update(env, info,
553                     argp->txnp->txnid, TXN_ABORT, NULL, &status, 1)) != 0)
554                         goto err;
555                 else if (status != TXN_IGNORE && status != TXN_OK)
556                         goto err;
557         } else {
558                 /* This is a normal commit; mark it appropriately. */
559                 if ((ret = __db_txnlist_update(env,
560                     info, argp->txnp->txnid, argp->opcode, lsnp,
561                     &status, 0)) == DB_NOTFOUND) {
562                         if ((ret = __db_txnlist_add(env,
563                             info, argp->txnp->txnid,
564                             argp->opcode == TXN_ABORT ?
565                             TXN_IGNORE : argp->opcode, lsnp)) != 0)
566                                 goto err;
567                 } else if (ret != 0 ||
568                     (status != TXN_IGNORE && status != TXN_OK))
569                         goto err;
570         }
571
572         if (ret == 0)
573                 *lsnp = argp->prev_lsn;
574
575         if (0) {
576 err:            __db_errx(env,
577                     "txnid %lx commit record found, already on commit list",
578                     (u_long)argp->txnp->txnid);
579                 ret = EINVAL;
580         }
581         __os_free(env, argp);
582
583         return (ret);
584 }
585
586 /*
587  * PUBLIC: int __txn_ckp_42_recover
588  * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
589  */
590 int
591 __txn_ckp_42_recover(env, dbtp, lsnp, op, info)
592         ENV *env;
593         DBT *dbtp;
594         DB_LSN *lsnp;
595         db_recops op;
596         void *info;
597 {
598         __txn_ckp_42_args *argp;
599         int ret;
600
601 #ifdef DEBUG_RECOVER
602         __txn_ckp_42_print(env, dbtp, lsnp, op, info);
603 #endif
604         if ((ret = __txn_ckp_42_read(env, dbtp->data, &argp)) != 0)
605                 return (ret);
606
607         if (op == DB_TXN_BACKWARD_ROLL)
608                 __db_txnlist_ckp(env, info, lsnp);
609
610         *lsnp = argp->last_ckp;
611         __os_free(env, argp);
612         return (DB_TXN_CKP);
613 }