1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1999-2009 Oracle. All rights reserved. 5 * 6 * $Id$ 7 */ 8 9#include "db_config.h" 10 11#include "db_int.h" 12#include "dbinc/db_page.h" 13#include "dbinc/db_am.h" 14#include "dbinc/lock.h" 15#include "dbinc/log.h" 16#include "dbinc/mp.h" 17#include "dbinc/qam.h" 18#include "dbinc/txn.h" 19 20/* 21 * LSNs in queue data pages are advisory. They do not have to be accurate 22 * as all operations are idempotent on records. They should not be rolled 23 * forward during recovery as committed transaction may obscure updates from 24 * an incomplete transaction that updates the same page. The incomplete 25 * transaction may be completed during a later hot backup cycle. 26 */ 27 28/* Queue version of REC_DIRTY -- needs to probe the correct file. */ 29#define QAM_DIRTY(dbc, pgno, pagep) \ 30 if ((ret = __qam_dirty((dbc), \ 31 pgno, pagep, (dbc)->priority)) != 0) { \ 32 ret = __db_pgerr((dbc)->dbp, (pgno), ret); \ 33 goto out; \ 34 } 35 36/* 37 * __qam_incfirst_recover -- 38 * Recovery function for incfirst. 39 * 40 * PUBLIC: int __qam_incfirst_recover 41 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 42 */ 43int 44__qam_incfirst_recover(env, dbtp, lsnp, op, info) 45 ENV *env; 46 DBT *dbtp; 47 DB_LSN *lsnp; 48 db_recops op; 49 void *info; 50{ 51 __qam_incfirst_args *argp; 52 DB_THREAD_INFO *ip; 53 DB *file_dbp; 54 DBC *dbc; 55 DB_LOCK lock; 56 DB_LSN trunc_lsn; 57 DB_MPOOLFILE *mpf; 58 QMETA *meta; 59 QUEUE_CURSOR *cp; 60 db_pgno_t metapg; 61 u_int32_t rec_ext; 62 int exact, ret, t_ret; 63 64 COMPQUIET(meta, NULL); 65 66 ip = ((DB_TXNHEAD *)info)->thread_info; 67 LOCK_INIT(lock); 68 REC_PRINT(__qam_incfirst_print); 69 REC_INTRO(__qam_incfirst_read, ip, 1); 70 71 metapg = ((QUEUE *)file_dbp->q_internal)->q_meta; 72 73 if ((ret = __db_lget(dbc, 74 LCK_ROLLBACK, metapg, DB_LOCK_WRITE, 0, &lock)) != 0) 75 goto done; 76 if ((ret = __memp_fget(mpf, &metapg, ip, NULL, 77 0, &meta)) != 0) { 78 if (DB_REDO(op)) { 79 if ((ret = __memp_fget(mpf, &metapg, ip, NULL, 80 DB_MPOOL_CREATE, &meta)) != 0) { 81 (void)__LPUT(dbc, lock); 82 goto out; 83 } 84 meta->dbmeta.pgno = metapg; 85 meta->dbmeta.type = P_QAMMETA; 86 } else { 87 *lsnp = argp->prev_lsn; 88 ret = __LPUT(dbc, lock); 89 goto out; 90 } 91 } 92 93 /* 94 * Only move first_recno backwards so we pick up the aborted delete. 95 * When going forward we need to be careful since 96 * we may have bumped over a locked record. 97 */ 98 if (DB_UNDO(op)) { 99 if (QAM_BEFORE_FIRST(meta, argp->recno)) { 100 REC_DIRTY(mpf, ip, dbc->priority, &meta); 101 meta->first_recno = argp->recno; 102 } 103 104 trunc_lsn = ((DB_TXNHEAD *)info)->trunc_lsn; 105 /* if we are truncating, update the LSN */ 106 if (!IS_ZERO_LSN(trunc_lsn) && 107 LOG_COMPARE(&LSN(meta), &trunc_lsn) > 0) { 108 REC_DIRTY(mpf, ip, dbc->priority, &meta); 109 LSN(meta) = trunc_lsn; 110 } 111 } else { 112 if (LOG_COMPARE(&LSN(meta), lsnp) < 0) { 113 REC_DIRTY(mpf, ip, dbc->priority, &meta); 114 LSN(meta) = *lsnp; 115 } 116 if (meta->page_ext == 0) 117 rec_ext = 0; 118 else 119 rec_ext = meta->page_ext * meta->rec_page; 120 cp = (QUEUE_CURSOR *)dbc->internal; 121 if (meta->first_recno == RECNO_OOB) 122 meta->first_recno++; 123 while (meta->first_recno != meta->cur_recno && 124 !QAM_BEFORE_FIRST(meta, argp->recno + 1)) { 125 if ((ret = __qam_position(dbc, 126 &meta->first_recno, 0, &exact)) != 0) 127 goto err; 128 if (cp->page != NULL && (ret = __qam_fput(dbc, 129 cp->pgno, cp->page, dbc->priority)) != 0) 130 goto err; 131 132 if (exact == 1) 133 break; 134 if (cp->page != NULL && 135 rec_ext != 0 && meta->first_recno % rec_ext == 0) 136 if ((ret = 137 __qam_fremove(file_dbp, cp->pgno)) != 0) 138 goto err; 139 REC_DIRTY(mpf, ip, dbc->priority, &meta); 140 meta->first_recno++; 141 if (meta->first_recno == RECNO_OOB) 142 meta->first_recno++; 143 } 144 } 145 146 ret = __memp_fput(mpf, ip, meta, dbc->priority); 147 if ((t_ret = __LPUT(dbc, lock)) != 0 && ret == 0) 148 ret = t_ret; 149 if (ret != 0) 150 goto out; 151 152done: *lsnp = argp->prev_lsn; 153 ret = 0; 154 155 if (0) { 156err: (void)__memp_fput(mpf, ip, meta, dbc->priority); 157 (void)__LPUT(dbc, lock); 158 } 159 160out: REC_CLOSE; 161} 162 163/* 164 * __qam_mvptr_recover -- 165 * Recovery function for mvptr. 166 * 167 * PUBLIC: int __qam_mvptr_recover 168 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 169 */ 170int 171__qam_mvptr_recover(env, dbtp, lsnp, op, info) 172 ENV *env; 173 DBT *dbtp; 174 DB_LSN *lsnp; 175 db_recops op; 176 void *info; 177{ 178 __qam_mvptr_args *argp; 179 DB_THREAD_INFO *ip; 180 DB *file_dbp; 181 DBC *dbc; 182 DB_LSN trunc_lsn; 183 DB_LOCK lock; 184 DB_MPOOLFILE *mpf; 185 QMETA *meta; 186 QUEUE_CURSOR *cp; 187 db_pgno_t metapg; 188 int cmp_n, cmp_p, exact, ret; 189 190 ip = ((DB_TXNHEAD *)info)->thread_info; 191 REC_PRINT(__qam_mvptr_print); 192 REC_INTRO(__qam_mvptr_read, ip, 1); 193 194 metapg = ((QUEUE *)file_dbp->q_internal)->q_meta; 195 196 if ((ret = __db_lget(dbc, 197 LCK_ROLLBACK, metapg, DB_LOCK_WRITE, 0, &lock)) != 0) 198 goto done; 199 if ((ret = __memp_fget(mpf, &metapg, ip, NULL, 0, &meta)) != 0) { 200 if (DB_REDO(op)) { 201 if ((ret = __memp_fget(mpf, &metapg, ip, NULL, 202 DB_MPOOL_CREATE, &meta)) != 0) { 203 (void)__LPUT(dbc, lock); 204 goto out; 205 } 206 meta->dbmeta.pgno = metapg; 207 meta->dbmeta.type = P_QAMMETA; 208 } else { 209 *lsnp = argp->prev_lsn; 210 ret = __LPUT(dbc, lock); 211 goto out; 212 } 213 } 214 215 cmp_n = LOG_COMPARE(lsnp, &LSN(meta)); 216 cmp_p = LOG_COMPARE(&LSN(meta), &argp->metalsn); 217 218 /* 219 * Under normal circumstances, we never undo a movement of one of 220 * the pointers. Just move them along regardless of abort/commit. 221 * When going forward we need to verify that this is really where 222 * the pointer belongs. A transaction may roll back and reinsert 223 * a record that was missing at the time of this action. 224 * 225 * If we're undoing a truncate, we need to reset the pointers to 226 * their state before the truncate. 227 */ 228 if (DB_UNDO(op)) { 229 if ((argp->opcode & QAM_TRUNCATE) && cmp_n <= 0) { 230 REC_DIRTY(mpf, ip, dbc->priority, &meta); 231 meta->first_recno = argp->old_first; 232 meta->cur_recno = argp->old_cur; 233 LSN(meta) = argp->metalsn; 234 } 235 /* If the page lsn is beyond the truncate point, move it back */ 236 trunc_lsn = ((DB_TXNHEAD *)info)->trunc_lsn; 237 if (!IS_ZERO_LSN(trunc_lsn) && 238 LOG_COMPARE(&trunc_lsn, &LSN(meta)) < 0) { 239 REC_DIRTY(mpf, ip, dbc->priority, &meta); 240 LSN(meta) = argp->metalsn; 241 } 242 } else if (op == DB_TXN_APPLY || cmp_p == 0) { 243 REC_DIRTY(mpf, ip, dbc->priority, &meta); 244 cp = (QUEUE_CURSOR *)dbc->internal; 245 if ((argp->opcode & QAM_SETFIRST) && 246 meta->first_recno == argp->old_first) { 247 if (argp->old_first > argp->new_first) 248 meta->first_recno = argp->new_first; 249 else { 250 if ((ret = __qam_position(dbc, 251 &meta->first_recno, 0, &exact)) != 0) 252 goto err; 253 if (!exact) 254 meta->first_recno = argp->new_first; 255 if (cp->page != NULL && 256 (ret = __qam_fput(dbc, 257 cp->pgno, cp->page, dbc->priority)) != 0) 258 goto err; 259 } 260 } 261 262 if ((argp->opcode & QAM_SETCUR) && 263 meta->cur_recno == argp->old_cur) { 264 if (argp->old_cur < argp->new_cur) 265 meta->cur_recno = argp->new_cur; 266 else { 267 if ((ret = __qam_position(dbc, 268 &meta->cur_recno, 0, &exact)) != 0) 269 goto err; 270 if (!exact) 271 meta->cur_recno = argp->new_cur; 272 if (cp->page != NULL && 273 (ret = __qam_fput(dbc, 274 cp->pgno, cp->page, dbc->priority)) != 0) 275 goto err; 276 } 277 } 278 279 meta->dbmeta.lsn = *lsnp; 280 } 281 282 if ((ret = __memp_fput(mpf, ip, meta, dbc->priority)) != 0) 283 goto out; 284 285 if ((ret = __LPUT(dbc, lock)) != 0) 286 goto out; 287 288done: *lsnp = argp->prev_lsn; 289 ret = 0; 290 291 if (0) { 292err: (void)__memp_fput(mpf, ip, meta, dbc->priority); 293 (void)__LPUT(dbc, lock); 294 } 295 296out: REC_CLOSE; 297} 298 299/* 300 * __qam_del_recover -- 301 * Recovery function for del. 302 * Non-extent version or if there is no data (zero len). 303 * 304 * PUBLIC: int __qam_del_recover 305 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 306 */ 307int 308__qam_del_recover(env, dbtp, lsnp, op, info) 309 ENV *env; 310 DBT *dbtp; 311 DB_LSN *lsnp; 312 db_recops op; 313 void *info; 314{ 315 __qam_del_args *argp; 316 DB_THREAD_INFO *ip; 317 DB *file_dbp; 318 DBC *dbc; 319 DB_LOCK lock; 320 DB_MPOOLFILE *mpf; 321 QAMDATA *qp; 322 QMETA *meta; 323 QPAGE *pagep; 324 db_pgno_t metapg; 325 int cmp_n, ret, t_ret; 326 327 COMPQUIET(pagep, NULL); 328 LOCK_INIT(lock); 329 meta = NULL; 330 pagep = NULL; 331 332 ip = ((DB_TXNHEAD *)info)->thread_info; 333 REC_PRINT(__qam_del_print); 334 REC_INTRO(__qam_del_read, ip, 1); 335 336 /* Lock the meta page before latching the page. */ 337 if (DB_UNDO(op)) { 338 metapg = ((QUEUE *)file_dbp->q_internal)->q_meta; 339 if ((ret = __db_lget(dbc, 340 LCK_ROLLBACK, metapg, DB_LOCK_WRITE, 0, &lock)) != 0) 341 goto out; 342 if ((ret = __memp_fget(mpf, &metapg, ip, NULL, 343 DB_MPOOL_EDIT, &meta)) != 0) 344 goto err; 345 } 346 347 if ((ret = __qam_fget(dbc, &argp->pgno, DB_MPOOL_CREATE, &pagep)) != 0) 348 goto err; 349 350 if (pagep->pgno == PGNO_INVALID) { 351 QAM_DIRTY(dbc, argp->pgno, &pagep); 352 pagep->pgno = argp->pgno; 353 pagep->type = P_QAMDATA; 354 } 355 356 cmp_n = LOG_COMPARE(lsnp, &LSN(pagep)); 357 358 if (DB_UNDO(op)) { 359 /* make sure first is behind us */ 360 if (meta->first_recno == RECNO_OOB || 361 (QAM_BEFORE_FIRST(meta, argp->recno) && 362 (meta->first_recno <= meta->cur_recno || 363 meta->first_recno - 364 argp->recno < argp->recno - meta->cur_recno))) { 365 REC_DIRTY(mpf, ip, dbc->priority, &meta); 366 meta->first_recno = argp->recno; 367 } 368 369 /* Need to undo delete - mark the record as present */ 370 QAM_DIRTY(dbc, pagep->pgno, &pagep); 371 qp = QAM_GET_RECORD(file_dbp, pagep, argp->indx); 372 F_SET(qp, QAM_VALID); 373 374 /* 375 * Move the LSN back to this point; do not move it forward. 376 * If we're in an abort, because we don't hold a page lock, 377 * we could foul up a concurrent put. Having too late an 378 * LSN * is harmless in queue except when we're determining 379 * what we need to roll forward during recovery. [#2588] 380 */ 381 if (cmp_n <= 0 && op == DB_TXN_BACKWARD_ROLL) 382 LSN(pagep) = argp->lsn; 383 } else if (op == DB_TXN_APPLY || (cmp_n > 0 && DB_REDO(op))) { 384 /* Need to redo delete - clear the valid bit */ 385 QAM_DIRTY(dbc, pagep->pgno, &pagep); 386 qp = QAM_GET_RECORD(file_dbp, pagep, argp->indx); 387 F_CLR(qp, QAM_VALID); 388 /* 389 * We only move the LSN forward during replication. 390 * During recovery we could obscure an update from 391 * a partially completed transaction while processing 392 * a hot backup. [#13823] 393 */ 394 if (op == DB_TXN_APPLY) 395 LSN(pagep) = *lsnp; 396 } 397 398done: *lsnp = argp->prev_lsn; 399 ret = 0; 400 401err: if (pagep != NULL && (t_ret = 402 __qam_fput(dbc, argp->pgno, pagep, dbc->priority)) != 0 && ret == 0) 403 ret = t_ret; 404 if (meta != NULL && (t_ret = 405 __memp_fput(mpf, ip, meta, dbc->priority)) != 0 && ret == 0) 406 ret = t_ret; 407 if ((t_ret = __LPUT(dbc, lock)) != 0 && ret == 0) 408 ret = t_ret; 409out: REC_CLOSE; 410} 411 412/* 413 * __qam_delext_recover -- 414 * Recovery function for del in an extent based queue. 415 * 416 * PUBLIC: int __qam_delext_recover 417 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 418 */ 419int 420__qam_delext_recover(env, dbtp, lsnp, op, info) 421 ENV *env; 422 DBT *dbtp; 423 DB_LSN *lsnp; 424 db_recops op; 425 void *info; 426{ 427 __qam_delext_args *argp; 428 DB_THREAD_INFO *ip; 429 DB *file_dbp; 430 DBC *dbc; 431 DB_LOCK lock; 432 DB_MPOOLFILE *mpf; 433 QAMDATA *qp; 434 QMETA *meta; 435 QPAGE *pagep; 436 db_pgno_t metapg; 437 int cmp_n, ret, t_ret; 438 439 COMPQUIET(pagep, NULL); 440 LOCK_INIT(lock); 441 meta = NULL; 442 pagep = NULL; 443 444 ip = ((DB_TXNHEAD *)info)->thread_info; 445 REC_PRINT(__qam_delext_print); 446 REC_INTRO(__qam_delext_read, ip, 1); 447 448 if (DB_UNDO(op)) { 449 metapg = ((QUEUE *)file_dbp->q_internal)->q_meta; 450 if ((ret = __db_lget(dbc, 451 LCK_ROLLBACK, metapg, DB_LOCK_WRITE, 0, &lock)) != 0) 452 goto err; 453 if ((ret = __memp_fget(mpf, &metapg, ip, NULL, 454 DB_MPOOL_EDIT, &meta)) != 0) { 455 (void)__LPUT(dbc, lock); 456 goto err; 457 } 458 } 459 460 if ((ret = __qam_fget(dbc, &argp->pgno, 461 DB_REDO(op) ? 0 : DB_MPOOL_CREATE, &pagep)) != 0) { 462 /* 463 * If we are redoing a delete and the page is not there 464 * we are done. 465 */ 466 if (DB_REDO(op) && (ret == DB_PAGE_NOTFOUND || ret == ENOENT)) 467 goto done; 468 goto out; 469 } 470 471 if (pagep->pgno == PGNO_INVALID) { 472 QAM_DIRTY(dbc, argp->pgno, &pagep); 473 pagep->pgno = argp->pgno; 474 pagep->type = P_QAMDATA; 475 } 476 477 cmp_n = LOG_COMPARE(lsnp, &LSN(pagep)); 478 479 if (DB_UNDO(op)) { 480 /* make sure first is behind us */ 481 if (meta->first_recno == RECNO_OOB || 482 (QAM_BEFORE_FIRST(meta, argp->recno) && 483 (meta->first_recno <= meta->cur_recno || 484 meta->first_recno - 485 argp->recno < argp->recno - meta->cur_recno))) { 486 meta->first_recno = argp->recno; 487 } 488 489 QAM_DIRTY(dbc, pagep->pgno, &pagep); 490 if ((ret = __qam_pitem(dbc, pagep, 491 argp->indx, argp->recno, &argp->data)) != 0) 492 goto err; 493 494 /* 495 * Move the LSN back to this point; do not move it forward. 496 * If we're in an abort, because we don't hold a page lock, 497 * we could foul up a concurrent put. Having too late an 498 * LSN is harmless in queue except when we're determining 499 * what we need to roll forward during recovery. [#2588] 500 */ 501 if (cmp_n <= 0 && op == DB_TXN_BACKWARD_ROLL) 502 LSN(pagep) = argp->lsn; 503 } else if (op == DB_TXN_APPLY || (cmp_n > 0 && DB_REDO(op))) { 504 QAM_DIRTY(dbc, pagep->pgno, &pagep); 505 /* Need to redo delete - clear the valid bit */ 506 qp = QAM_GET_RECORD(file_dbp, pagep, argp->indx); 507 F_CLR(qp, QAM_VALID); 508 /* 509 * We only move the LSN forward during replication. 510 * During recovery we could obscure an update from 511 * a partially completed transaction while processing 512 * a hot backup. [#13823] 513 */ 514 if (op == DB_TXN_APPLY) 515 LSN(pagep) = *lsnp; 516 } 517 518done: *lsnp = argp->prev_lsn; 519 ret = 0; 520 521err: if (pagep != NULL && (t_ret = 522 __qam_fput(dbc, argp->pgno, pagep, dbc->priority)) != 0 && ret == 0) 523 ret = t_ret; 524 if (meta != NULL && (t_ret = 525 __memp_fput(mpf, ip, meta, dbc->priority)) != 0 && ret == 0) 526 ret = t_ret; 527 if ((t_ret = __LPUT(dbc, lock)) != 0 && ret == 0) 528 ret = t_ret; 529 530out: REC_CLOSE; 531} 532 533/* 534 * __qam_add_recover -- 535 * Recovery function for add. 536 * 537 * PUBLIC: int __qam_add_recover 538 * PUBLIC: __P((ENV *, DBT *, DB_LSN *, db_recops, void *)); 539 */ 540int 541__qam_add_recover(env, dbtp, lsnp, op, info) 542 ENV *env; 543 DBT *dbtp; 544 DB_LSN *lsnp; 545 db_recops op; 546 void *info; 547{ 548 __qam_add_args *argp; 549 DB_THREAD_INFO *ip; 550 DB *file_dbp; 551 DBC *dbc; 552 DB_MPOOLFILE *mpf; 553 QAMDATA *qp; 554 QMETA *meta; 555 QPAGE *pagep; 556 db_pgno_t metapg; 557 int cmp_n, ret; 558 559 COMPQUIET(pagep, NULL); 560 561 ip = ((DB_TXNHEAD *)info)->thread_info; 562 REC_PRINT(__qam_add_print); 563 REC_INTRO(__qam_add_read, ip, 1); 564 565 if ((ret = __qam_fget(dbc, &argp->pgno, 566 DB_UNDO(op) ? 0 : DB_MPOOL_CREATE, &pagep)) != 0) { 567 /* 568 * If we are undoing an append and the page is not there 569 * we are done. 570 */ 571 if (DB_UNDO(op) && (ret == DB_PAGE_NOTFOUND || ret == ENOENT)) 572 goto done; 573 goto out; 574 } 575 576 if (pagep->pgno == PGNO_INVALID) { 577 QAM_DIRTY(dbc, argp->pgno, &pagep); 578 pagep->pgno = argp->pgno; 579 pagep->type = P_QAMDATA; 580 } 581 582 cmp_n = LOG_COMPARE(lsnp, &LSN(pagep)); 583 584 if (DB_REDO(op)) { 585 /* Fix meta-data page. */ 586 metapg = ((QUEUE *)file_dbp->q_internal)->q_meta; 587 if ((ret = __memp_fget(mpf, &metapg, ip, NULL, 588 0, &meta)) != 0) 589 goto err; 590 if (QAM_BEFORE_FIRST(meta, argp->recno)) { 591 REC_DIRTY(mpf, ip, dbc->priority, &meta); 592 meta->first_recno = argp->recno; 593 } 594 if (argp->recno == meta->cur_recno || 595 QAM_AFTER_CURRENT(meta, argp->recno)) { 596 REC_DIRTY(mpf, ip, dbc->priority, &meta); 597 meta->cur_recno = argp->recno + 1; 598 } 599 if ((ret = __memp_fput(mpf, ip, meta, dbc->priority)) != 0) 600 goto err; 601 602 /* Now update the actual page if necessary. */ 603 if (op == DB_TXN_APPLY || cmp_n > 0) { 604 QAM_DIRTY(dbc, pagep->pgno, &pagep); 605 /* Need to redo add - put the record on page */ 606 if ((ret = __qam_pitem(dbc, 607 pagep, argp->indx, argp->recno, &argp->data)) != 0) 608 goto err; 609 /* 610 * We only move the LSN forward during replication. 611 * During recovery we could obscure an update from 612 * a partially completed transaction while processing 613 * a hot backup. [#13823] 614 */ 615 if (op == DB_TXN_APPLY) 616 LSN(pagep) = *lsnp; 617 } 618 } else if (DB_UNDO(op)) { 619 /* 620 * Need to undo add 621 * If this was an overwrite, put old record back. 622 * Otherwise just clear the valid bit 623 */ 624 if (argp->olddata.size != 0) { 625 QAM_DIRTY(dbc, pagep->pgno, &pagep); 626 if ((ret = __qam_pitem(dbc, pagep, 627 argp->indx, argp->recno, &argp->olddata)) != 0) 628 goto err; 629 630 if (!(argp->vflag & QAM_VALID)) { 631 qp = QAM_GET_RECORD( 632 file_dbp, pagep, argp->indx); 633 F_CLR(qp, QAM_VALID); 634 } 635 } else { 636 QAM_DIRTY(dbc, pagep->pgno, &pagep); 637 qp = QAM_GET_RECORD(file_dbp, pagep, argp->indx); 638 qp->flags = 0; 639 } 640 641 /* 642 * Move the LSN back to this point; do not move it forward. 643 * If we're in an abort, because we don't hold a page lock, 644 * we could foul up a concurrent put. Having too late an 645 * LSN is harmless in queue except when we're determining 646 * what we need to roll forward during recovery. [#2588] 647 */ 648 if (cmp_n <= 0 && op == DB_TXN_BACKWARD_ROLL) 649 LSN(pagep) = argp->lsn; 650 } 651 652 if ((ret = __qam_fput(dbc, argp->pgno, pagep, dbc->priority)) != 0) 653 goto out; 654 655done: *lsnp = argp->prev_lsn; 656 ret = 0; 657 658 if (0) { 659err: (void)__qam_fput(dbc, argp->pgno, pagep, dbc->priority); 660 } 661 662out: REC_CLOSE; 663} 664