8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 20:03:03 +01:00
firebird-mirror/src/jrd/rec.cpp
2003-02-10 13:28:35 +00:00

1111 lines
26 KiB
C++

/*
* PROGRAM: JRD Access Method
* MODULE: rec.c
* DESCRIPTION:
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#include "firebird.h"
#include <string.h>
#include "../jrd/gds.h"
#include "../jrd/jrd.h"
#include "../jrd/ods.h"
#include "../jrd/btr.h"
#include "../jrd/jrn.h"
#include "../jrd/sbm.h"
#include "../jrd/lck.h"
#include "../jrd/cch.h"
#include "../wal/wal.h"
#include "../jrd/rse.h"
#include "../jrd/all_proto.h"
#include "../jrd/cch_proto.h"
#include "../jrd/dpm_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/misc_proto.h"
#include "../jrd/mov_proto.h"
#include "../jrd/pag_proto.h"
#include "../jrd/rec_proto.h"
#include "../jrd/sbm_proto.h"
#include "../jrd/thd_proto.h"
#include "../wal/walr_proto.h"
#define MOVE_BYTE(x_from, x_to) *x_to++ = *x_from++
#define HIGH_WATER(x) ((SSHORT) sizeof (dpg) + (SSHORT) sizeof (dpg::dpg_repeat) * (x - 1))
static void apply_data(DPG, JRND *);
static void apply_header(HDR, JRND *);
static void apply_ids(PPG, JRND *);
static void apply_index(BTR, JRND *);
static void apply_log(LIP, JRND *);
static void apply_pip(PIP, JRND *);
static void apply_pointer(PPG, JRND *);
static void apply_root(IRT, JRND *);
static void apply_transaction(TIP, JRND *);
static void disable(void);
static JRNP *next_clump(JRND *, JRNP *);
static void process_page(TDBB, JRND *, SLONG, SLONG, PAG, SBM *, USHORT);
static void quad_move(UCHAR *, UCHAR *);
static void rec_process_record(TDBB, JRNH *, USHORT, ULONG,
ULONG, PAG, SBM *, USHORT);
static void scan_and_apply_logs(TEXT *, TEXT *, CP *, SBM *, USHORT, SLONG *,
SLONG, PAG);
void REC_recover(
TEXT * dbname,
TEXT * walname,
CP * cp1, SBM * sbm_rec, USHORT activate_shadow)
{
/**************************************
*
* R E C _ r e c o v e r
*
**************************************
*
* Functional description
* Do short term recovery.
* Fixup log header at end.
*
**************************************/
gds__log("Short term recovery for database \"%s\". Start log file \"%s\"",
dbname, walname);
scan_and_apply_logs(dbname, walname, cp1, sbm_rec,
activate_shadow, (SLONG *) NULL, -1, (PAG) NULL);
REC_update_next_transid();
/* Should do the equivalent of 2 control point flush to write
* all pages to disk, so that the effects of recovery are not
* lost.
* The control points should not be done for 2 cases -
* - if shadows are present, doing a control point will "recover" the
* database. So a crash before the shadows are recovered will leave
* the shadows out of sync.
* - if there is no shared cache, there will be no one to complete
* the control point.
* The control points are called in CCH_recover_shadow () after the
* shadows are recovered.
*/
}
void REC_recover_page(
TEXT * dbname,
TEXT * walname, CP * cp1, SLONG page_no, PAG page)
{
/**************************************
*
* R E C _ r e c o v e r _ p a g e
*
**************************************
*
* Functional description
* recover a single page from a point in time to end of log.
*
**************************************/
SLONG date[2];
/* get current time stamp */
MOV_time_stamp(reinterpret_cast < ISC_TIMESTAMP * >(date));
scan_and_apply_logs(dbname, walname, cp1, (SBM *) NULL,
0, date, page_no, page);
}
void REC_update_next_transid(void)
{
/**************************************
*
* R E C _ u p d a t e _ n e x t _ t r a n s i d
*
**************************************
*
* Functional description
* Used to set the next transaction id from the
* bumped transaction id.
*
**************************************/
WIN window;
HDR hdr;
TDBB tdbb;
tdbb = GET_THREAD_DATA;
window.win_page = HEADER_PAGE;
window.win_flags = 0;
hdr = (HDR) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
CCH_MARK_MUST_WRITE(tdbb, &window);
hdr->hdr_next_transaction = hdr->hdr_bumped_transaction;
CCH_RELEASE(tdbb, &window);
}
static void apply_data(DPG page, JRND * record)
{
/**************************************
*
* a p p l y _ d a t a
*
**************************************
*
* Functional description
* Apply incremental changes to a data page.
*
**************************************/
DBB dbb;
JRNP temp, *clump;
SSHORT space, l, top, used;
UCHAR *p, *q;
dpg::dpg_repeat * index, *end;
TDBB tdbb;
tdbb = GET_THREAD_DATA;
dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
/* Process clumps */
for (clump = NULL; (clump = next_clump(record, clump));) {
MOVE_FAST((SCHAR *) clump, (SCHAR *) & temp, JRNP_SIZE);
if (temp.jrnp_type != JRNP_DATA_SEGMENT)
BUGCHECK(270); /* msg 270 error in recovery! wrong data page record */
/* Handle segment deletion */
if (!temp.jrnp_length) {
index = page->dpg_rpt + temp.jrnp_index;
index->dpg_offset = 0;
index->dpg_length = 0;
}
/* Re-compute page high water mark */
index = page->dpg_rpt;
end = index + page->dpg_count;
page->dpg_count = 0;
space = dbb->dbb_page_size;
for (l = 1, used = 0; index < end; index++, l++)
if (index->dpg_length) {
page->dpg_count = l;
space = MIN((int) space, (int) index->dpg_offset);
used += ROUNDUP(index->dpg_length, ODS_ALIGNMENT);
}
/* delete record */
if (!temp.jrnp_length)
continue;
/* Handle segment addition */
index = page->dpg_rpt + temp.jrnp_index;
q = clump->jrnp_data;
l = temp.jrnp_length;
/* If space exists, just all record */
if (((SCHAR *) index < (SCHAR *) end)
&& ((USHORT) l <= index->dpg_length)) {
index->dpg_length = l;
p = (UCHAR *) page + index->dpg_offset;
do
*p++ = *q++;
while (--l);
continue;
}
page->dpg_count =
MAX((int) page->dpg_count, (int) (temp.jrnp_index + 1));
top = HIGH_WATER(page->dpg_count);
l = ROUNDUP(l, ODS_ALIGNMENT);
space -= l;
/* Compress the page and make space */
if (space < top) {
index->dpg_length = 0;
space = DPM_compress(tdbb, page);
space -= l;
if (space < top)
BUGCHECK(271); /* msg 271 error in recovery! no space on data page */
}
if ((space + l) > dbb->dbb_page_size)
BUGCHECK(269); /* msg 269 error in recovery! database corrupted */
index->dpg_offset = space;
index->dpg_length = temp.jrnp_length;
p = (UCHAR *) page + space;
do
*p++ = *q++;
while (--l);
}
}
static void apply_header(HDR page, JRND * record)
{
/**************************************
*
* a p p l y _ h e a d e r
*
**************************************
*
* Functional description
* Apply changes to database header page
*
**************************************/
JRNDH temp, *clump;
JRNDA temp1;
for (clump = NULL;
(clump =
(JRNDH *) next_clump(record, reinterpret_cast < jrnp * >(clump)));) {
MOVE_FAST((SCHAR *) clump, (SCHAR *) & temp, JRNDH_SIZE);
if (temp.jrndh_type == JRNP_DB_HEADER) {
page->hdr_bumped_transaction = temp.jrndh_nti;
page->hdr_oldest_transaction = temp.jrndh_oit;
page->hdr_oldest_active = temp.jrndh_oat;
}
else {
MOVE_FAST((SCHAR *) clump, (SCHAR *) & temp1, JRNDA_SIZE);
switch (temp1.jrnda_type) {
case JRNP_DB_ATTACHMENT:
page->hdr_attachment_id = temp1.jrnda_data;
break;
case JRNP_DB_HDR_PAGES:
page->hdr_PAGES = temp1.jrnda_data;
break;
case JRNP_DB_HDR_FLAGS:
page->hdr_flags = (USHORT) temp1.jrnda_data;
break;
case JRNP_DB_HDR_SDW_COUNT:
page->hdr_shadow_count = temp1.jrnda_data;
break;
default:
BUGCHECK(272); /* msg 272 error in recovery! wrong header page record */
}
}
}
}
static void apply_ids(PPG page, JRND * record)
{
/**************************************
*
* a p p l y _ i d s
*
**************************************
*
* Functional description
* Apply changes to generator page (gen ids) page
*
**************************************/
JRNG temp, *clump;
SLONG *ptr;
for (clump = NULL;
(clump =
(JRNG *) next_clump(record, reinterpret_cast < jrnp * >(clump)));) {
MOVE_FAST((SCHAR *) clump, (SCHAR *) & temp, JRNG_SIZE);
if (temp.jrng_type != JRNP_GENERATOR)
BUGCHECK(273); /* msg 273 error in recovery! wrong generator page record */
ptr = page->ppg_page + temp.jrng_offset;
*ptr = temp.jrng_genval;
}
}
static void apply_index(BTR page, JRND * record)
{
/**************************************
*
* a p p l y _ i n d e x
*
**************************************
*
* Functional description
* Apply changes to b-tree pages
*
**************************************/
JRNB temp, *clump;
UCHAR *p, *q;
SLONG l;
SLONG delta;
BTN node, next;
for (clump = NULL;
(clump =
(JRNB *) next_clump(record, reinterpret_cast < jrnp * >(clump)));) {
MOVE_FAST((SCHAR *) clump, (SCHAR *) & temp, JRNB_SIZE);
switch (temp.jrnb_type) {
case JRNP_BTREE_NODE:
/*
* slide down upper part by delta
* add node and increment btr_length
*/
delta = temp.jrnb_delta;
p = (UCHAR *) page + page->btr_length;
q = p + delta;
if ( (l = page->btr_length - temp.jrnb_offset) )
do
*--q = *--p;
while (--l);
/* move in node , then BTN */
p = (UCHAR *) page + temp.jrnb_offset;
q = clump->jrnb_data;
if ( (l = temp.jrnb_length) )
MOVE_FAST(q, p, l);
page->btr_length += delta;
page->btr_prefix_total = temp.jrnb_prefix_total;
break;
case JRNP_BTREE_SEGMENT:
/* apply change directly */
p = (UCHAR *) page;
q = (UCHAR *) clump->jrnb_data;
if ( (l = temp.jrnb_length) )
MOVE_FAST(q, p, l);
break;
case JRNP_BTREE_DELETE:
/* delete a node entry */
node = (BTN) ((UCHAR *) page + temp.jrnb_offset);
next = (BTN) (BTN_DATA(node) + BTN_LENGTH(node));
QUAD_MOVE(BTN_NUMBER(next), BTN_NUMBER(node));
p = BTN_DATA(node);
q = BTN_DATA(next);
l = BTN_LENGTH(next);
if (BTN_PREFIX(node) < BTN_PREFIX(next)) {
BTN_LENGTH(node) = BTN_LENGTH(next) + BTN_PREFIX(next)
- BTN_PREFIX(node);
p += BTN_PREFIX(next) - BTN_PREFIX(node);
}
else {
BTN_LENGTH(node) = l;
BTN_PREFIX(node) = BTN_PREFIX(next);
}
if (l)
do
*p++ = *q++;
while (--l);
/* Compute length of rest of bucket and move it down. */
if ( (l = page->btr_length - (q - (UCHAR *) page)) )
do
*p++ = *q++;
while (--l);
page->btr_length = p - (UCHAR *) page;
page->btr_prefix_total = temp.jrnb_prefix_total;
/* Error Check */
if ((BTN_PREFIX(node) != temp.jrnb_delta) ||
(page->btr_length != temp.jrnb_length)) BUGCHECK(274); /* msg 274 error in recovery! wrong b-tree page record */
break;
default:
BUGCHECK(274); /* msg 274 error in recovery! wrong b-tree page record */
}
}
}
static void apply_log(LIP page, JRND * record)
{
/**************************************
*
* a p p l y _ l o g
*
**************************************
*
* Functional description
* Apply changes to database log page (= 2)
*
**************************************/
JRNL temp, *clump;
for (clump = NULL;
(clump =
(JRNL *) next_clump(record, reinterpret_cast < jrnp * >(clump)));) {
MOVE_FAST((SCHAR *) clump, (SCHAR *) & temp, JRNL_SIZE);
page->log_flags = temp.jrnl_flags;
page->log_mod_tid = temp.jrnl_tid;
page->log_mod_tip = temp.jrnl_tip;
}
page->log_flags |= log_rec_in_progress;
}
static void apply_pip(PIP page, JRND * record)
{
/**************************************
*
* a p p l y _ p i p
*
**************************************
*
* Functional description
* Apply changes to page inventory page.
*
**************************************/
JRNA temp, *clump;
UCHAR bit;
USHORT byte;
for (clump = NULL;
(clump =
(JRNA *) next_clump(record, reinterpret_cast < jrnp * >(clump)));) {
MOVE_FAST((SCHAR *) clump, (SCHAR *) & temp, sizeof(struct jrna));
if (temp.jrna_type != JRNP_PIP)
BUGCHECK(275); /* msg 275 error in recovery! wrong page inventory page record */
byte = temp.jrna_slot >> 3;
bit = 1 << (temp.jrna_slot & 7);
if (temp.jrna_allocate)
page->pip_bits[byte] &= ~bit;
else {
page->pip_bits[byte] |= bit;
page->pip_min = MIN(page->pip_min, temp.jrna_slot);
}
}
}
static void apply_pointer(PPG page, JRND * record)
{
/**************************************
*
* a p p l y _ p o i n t e r
*
**************************************
*
* Functional description
* Apply incremental changes to a pointer page.
*
**************************************/
JRNP temp, *clump;
SLONG longword;
for (clump = NULL; (clump = next_clump(record, clump));) {
MOVE_FAST((SCHAR *) clump, (SCHAR *) & temp, JRNP_SIZE);
if (temp.jrnp_type != JRNP_POINTER_SLOT)
BUGCHECK(276); /* msg 276 error in recovery! wrong pointer page record */
if (temp.jrnp_length) {
MOVE_FAST(clump->jrnp_data, &longword, sizeof(SLONG));
page->ppg_page[temp.jrnp_index] = longword;
page->ppg_count = MAX(page->ppg_count, temp.jrnp_index + 1);
page->ppg_min_space = MIN(page->ppg_min_space, temp.jrnp_index);
page->ppg_max_space = MAX(page->ppg_min_space, temp.jrnp_index);
}
else
page->ppg_page[temp.jrnp_index] = 0;
}
}
static void apply_root(IRT page, JRND * record)
{
/**************************************
*
* a p p l y _ r o o t
*
**************************************
*
* Functional description
* Apply changes to index root page
*
**************************************/
JRNRP temp, *clump;
for (clump = NULL;
(clump =
(JRNRP *) next_clump(record, reinterpret_cast < jrnp * >(clump)));) {
MOVE_FAST((SCHAR *) clump, (SCHAR *) & temp, JRNRP_SIZE);
if (temp.jrnrp_type != JRNP_ROOT_PAGE)
BUGCHECK(277); /* msg 277 error in recovery! wrong index root page record */
page->irt_rpt[temp.jrnrp_id].irt_root = temp.jrnrp_page;
}
}
static void apply_transaction(TIP page, JRND * record)
{
/**************************************
*
* a p p l y _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Apply incremental changes to transaction inventory page
*
**************************************/
JRNI *clump, *clump_end;
JRNI temp;
JRND rec;
MOVE_FAST((SCHAR *) record, (SCHAR *) & rec, JRND_SIZE);
clump = (JRNI *) record->jrnd_data;
clump_end = (JRNI *) (record->jrnd_data + rec.jrnd_length);
/* Process clumps */
for (; clump < clump_end; clump++) {
MOVE_FAST((SCHAR *) clump, (SCHAR *) & temp, JRNI_SIZE);
if (temp.jrni_type == JRNP_TRANSACTION)
page->tip_transactions[temp.jrni_position] = temp.jrni_states;
else if (temp.jrni_type == JRNP_NEXT_TIP)
page->tip_next = temp.jrni_transaction;
else
BUGCHECK(278); /* msg 278 error in recovery! wrong transaction page record */
}
}
static void disable(void)
{
/**************************************
*
* d i s a b l e
*
**************************************
*
* Functional description
* We encountered a database disable record.
* Clean up database header page.
*
**************************************/
/*
* Delete journal related entries
*/
PAG_delete_clump_entry(HEADER_PAGE, HDR_journal_server);
PAG_delete_clump_entry(HEADER_PAGE, HDR_backup_info);
}
static JRNP *next_clump(JRND * record, JRNP * prior)
{
/**************************************
*
* n e x t _ c l u m p
*
**************************************
*
* Functional description
* Given a prior clump, compute the address of the next
* clump on a data page. If the prior clump is null,
* compute the address of the first clump. If we run
* off the record, return NULL.
*
**************************************/
JRNB temp1;
USHORT offset, l;
JRNP temp;
/* If the prior pointer is null, just return the data area */
if (!prior)
return (JRNP *) record->jrnd_data;
/* Compute the offset and length of prior clump */
offset = (SCHAR *) prior - (SCHAR *) record;
MOVE_FAST((SCHAR *) prior, (SCHAR *) & temp, JRNP_SIZE);
switch (temp.jrnp_type) {
case JRNP_DATA_SEGMENT:
case JRNP_POINTER_SLOT:
l = JRNP_SIZE + temp.jrnp_length;
break;
case JRNP_BTREE_SEGMENT:
case JRNP_BTREE_NODE:
MOVE_FAST((SCHAR *) prior, (SCHAR *) & temp1, JRNB_SIZE);
l = JRNB_SIZE + temp1.jrnb_length;
break;
/*
* currently DELETE node contains some debug info in jrnb_length
* but data field is not used.
*/
case JRNP_BTREE_DELETE:
l = JRNB_SIZE;
break;
case JRNP_PIP:
l = sizeof(struct jrna);
break;
case JRNP_DB_HEADER:
l = JRNDH_SIZE;
break;
case JRNP_LOG_PAGE:
l = JRNL_SIZE;
break;
case JRNP_DB_ATTACHMENT:
case JRNP_DB_HDR_PAGES:
case JRNP_DB_HDR_FLAGS:
case JRNP_DB_HDR_SDW_COUNT:
l = JRNDA_SIZE;
break;
case JRNP_GENERATOR:
l = JRNG_SIZE;
break;
case JRNP_ROOT_PAGE:
l = JRNRP_SIZE;
break;
default:
BUGCHECK(269); /* msg 269 error in recovery! database corrupted */
}
offset += l;
if (offset & 1)
++offset;
if (offset >= (JRND_SIZE + record->jrnd_length))
return 0;
return (JRNP *) ((SCHAR *) record + offset);
}
static void process_page(
TDBB tdbb,
JRND * record,
SLONG seqno,
SLONG offset,
PAG rec_page, SBM * sbm_rec, USHORT activate_shadow)
{
/**************************************
*
* p r o c e s s _ p a g e
*
**************************************
*
* Functional description
* Process a WAL record associated with a database page
* If rec_page is set, a corrupt page is being recovered.
* This page may have been read with a read lock, so do not
* mark it or read it again.
*
**************************************/
DBB dbb;
PAG page;
JRNP *clump;
UCHAR *p, *q;
WIN window;
LIP logp;
JRND rec;
HDR hdr;
USHORT hdr_end;
SET_TDBB(tdbb);
dbb = tdbb->tdbb_database;
window.win_flags = 0;
clump = (JRNP *) record->jrnd_data;
q = (UCHAR *) clump;
/* If record is a full replacement page, handle it */
MOVE_FAST((SCHAR *) record, (SCHAR *) & rec, JRND_SIZE);
/* handle zero length data records. This is an offshoot of PAG_release_page
* where the free page is left in the shared cache with some possible
* journal records in it.
*/
if (rec.jrnd_length == 0)
return;
/* set the page number in the sparse bit map during short term recovery
* but not during single page recovery.
*/
if (!rec_page)
SBM_set(tdbb, sbm_rec, rec.jrnd_page);
if (rec_page)
page = rec_page;
else {
window.win_page = rec.jrnd_page;
/* If this is a newly allocated page, read will fail at EOF */
if ((rec.jrnd_header.jrnh_prev_seqno == 0)
&& (clump->jrnp_type <= pag_max))
page = CCH_fake(tdbb, &window, 1);
else
page = CCH_FETCH(tdbb, &window, LCK_write, pag_undefined);
}
/* If there is no need to apply record, just return */
if ((page->pag_seqno > seqno) ||
((page->pag_seqno == seqno) && (page->pag_offset >= offset))) {
if (!rec_page)
CCH_RELEASE(tdbb, &window);
return;
}
/*
* Error check to see if records are being skipped
* Do not check for error if it is a full page, or clump type
* is JRNP_BTREE_SEGMENT and the previous seqno & offset are 0
*/
if (!((rec.jrnd_header.jrnh_prev_seqno == 0) &&
(rec.jrnd_header.jrnh_prev_offset == 0) &&
((clump->jrnp_type <= pag_max) ||
(clump->jrnp_type == JRNP_BTREE_SEGMENT)))) {
if ((page->pag_seqno != rec.jrnd_header.jrnh_prev_seqno) ||
(page->pag_offset != rec.jrnd_header.jrnh_prev_offset)) {
if (!rec_page)
CCH_RELEASE(tdbb, &window);
BUGCHECK(279); /* msg 279 error in recovery! out of sequence log record encountered */
}
}
/* changes need to be applied */
if (!rec_page)
CCH_MARK(tdbb, &window);
if (clump->jrnp_type <= pag_max) {
p = (UCHAR *) page;
/*
* if header page is being replaced, and we are activating shadow
* copy only relevent part. i.e. non clumplet part.
*/
if ((activate_shadow) && (rec.jrnd_page == HEADER_PAGE)) {
hdr = (HDR) page;
hdr_end = hdr->hdr_end;
MOVE_FAST(q, p, HDR_SIZE);
hdr->hdr_end = hdr_end;
hdr->hdr_flags |= hdr_active_shadow;
hdr->hdr_next_page = 0;
}
else
MOVE_FAST(q, p, dbb->dbb_page_size);
page->pag_seqno = seqno;
page->pag_offset = offset;
/* If LOG_page is being replaced, set the flag log_rec_in_progress */
if (rec.jrnd_page == LOG_PAGE) {
logp = (LIP) page;
logp->log_flags |= log_rec_in_progress;
}
if (!rec_page)
CCH_RELEASE(tdbb, &window);
return;
}
switch (page->pag_type) {
case pag_pointer:
apply_pointer(reinterpret_cast < PPG > (page), record);
break;
case pag_data:
apply_data(reinterpret_cast < dpg * >(page), record);
break;
case pag_transactions:
apply_transaction(reinterpret_cast < tip * >(page), record);
break;
case pag_pages:
apply_pip(reinterpret_cast < pip * >(page), record);
break;
case pag_header:
apply_header(reinterpret_cast < HDR > (page), record);
break;
case pag_root:
apply_root(reinterpret_cast < IRT > (page), record);
break;
case pag_index:
apply_index(reinterpret_cast < btr * >(page), record);
break;
case pag_ids:
apply_ids(reinterpret_cast < PPG > (page), record);
break;
case pag_log:
apply_log(reinterpret_cast < LIP > (page), record);
break;
default:
BUGCHECK(280); /* msg 280 error in recovery! unknown page type */
}
page->pag_seqno = seqno;
page->pag_offset = offset;
if (!rec_page)
CCH_RELEASE(tdbb, &window);
}
static void quad_move(UCHAR * a, UCHAR * b)
{
/**************************************
*
* q u a d _ m o v e
*
**************************************
*
* Functional description
* Move an unaligned longword ( 4 bytes).
*
**************************************/
MOVE_BYTE(a, b);
MOVE_BYTE(a, b);
MOVE_BYTE(a, b);
MOVE_BYTE(a, b);
}
static void rec_process_record(
TDBB tdbb,
JRNH * record,
USHORT length,
ULONG seqno,
ULONG offset,
PAG page, SBM * sbm_rec, USHORT activate_shadow)
{
/**************************************
*
* r e c _ p r o c e s s _ r e c o r d
*
**************************************
*
* Functional description
* Process a single WAL record.
*
**************************************/
SET_TDBB(tdbb);
/* Handle particular record type */
switch (record->jrnh_type) {
case JRN_PAGE:
process_page(tdbb, reinterpret_cast < JRND * >(record), seqno, offset,
page, sbm_rec, activate_shadow);
break;
case JRN_DISABLE:
disable();
break;
case JRN_NEW_FILE:
/*
* The whole page is journalled if it is the primary page
* The page is already written out, so NO
* problem!! (Or should PAG_add_file () be called?)
*/
break;
case JRN_COMMIT:
/*
* the commit is journalled in the tip pages
*/
break;
case JRN_ENABLE:
case JRN_SYNC:
case JRN_ONLINE_DMP_FILE:
case JRN_START_ONLINE_DMP:
case JRN_END_ONLINE_DMP:
case JRN_CNTRL_PT:
case JRN_WAL_NAME:
/*
* No action needed
*/
break;
default:
BUGCHECK(281); /* msg 281 error in recovery! unknown record type */
}
}
static void scan_and_apply_logs(
TEXT * dbname,
TEXT * start_logname,
CP * cp1,
SBM * sbm_rec,
USHORT activate_shadow, SLONG * timestamp, SLONG page_no, PAG page)
{
/**************************************
*
* s c a n _ a n d _ a p p l y _ l o g
*
**************************************
*
* Functional description
* Do short-term or page recovery.
* If a page is passed, recover only that page.
* Fixup log header at end in case of short-term recovery.
*
**************************************/
TDBB tdbb;
DBB dbb;
SLONG ret_val;
SSHORT len;
ULONG seqno;
ULONG offset;
WALR WALRS_handle;
UCHAR *wal_buff;
STR string;
JRND rec;
#ifdef DEV_BUILD
JRND rec1;
SLONG checksum;
#endif
tdbb = GET_THREAD_DATA;
dbb = tdbb->tdbb_database;
/* Open wal files */
WALRS_handle = 0;
ret_val =
WALR_open(tdbb->tdbb_status_vector,
reinterpret_cast < walrs ** >(&WALRS_handle), dbname, 1,
&start_logname, &cp1->cp_p_offset, cp1->cp_offset,
timestamp, FALSE);
if (ret_val == -1)
return; /* reached EOF */
else if (ret_val != FB_SUCCESS)
ERR_punt();
else if (!WALRS_handle)
ERR_post(gds_wal_failure, 0);
string = FB_NEW_RPT(*dbb->dbb_permanent, MAX_WALBUFLEN) str();
wal_buff = (UCHAR *) string->str_data;
while (TRUE) {
ret_val =
WALR_get(tdbb->tdbb_status_vector,
reinterpret_cast < walrs * >(WALRS_handle), wal_buff,
reinterpret_cast < USHORT * >(&len),
reinterpret_cast < long *>(&seqno),
reinterpret_cast < long *>(&offset));
if (ret_val == -1) /* done processing */
break;
else if (ret_val != FB_SUCCESS) {
WALR_close(tdbb->tdbb_status_vector,
reinterpret_cast < walrs ** >(&WALRS_handle));
delete string;
ERR_punt();
}
#ifdef DEV_BUILD
/* take care of word alignment problems */
MOVE_FAST((SCHAR *) wal_buff, (SCHAR *) & rec1, JRND_SIZE);
checksum = rec1.jrnd_header.jrnh_series;
rec1.jrnd_header.jrnh_series = 0;
MOVE_FAST((SCHAR *) & rec1, (SCHAR *) wal_buff, JRND_SIZE);
if (checksum != MISC_checksum_log_rec(wal_buff, len, 0, 0))
BUGCHECK(283);
rec1.jrnd_header.jrnh_series = checksum;
MOVE_FAST((SCHAR *) & rec1, (SCHAR *) wal_buff, JRND_SIZE);
#endif
if (page) {
/* Recovery is needed for one page only */
MOVE_FAST((SCHAR *) wal_buff, (SCHAR *) & rec, JRND_SIZE);
if (rec.jrnd_page != page_no)
continue;
}
rec_process_record(tdbb, (JRNH *) wal_buff, len, seqno, offset, page,
sbm_rec, activate_shadow);
}
if (!page) /* i.e. regular short-term recovery */
WALR_fixup_log_header(tdbb->tdbb_status_vector,
reinterpret_cast < walrs * >(WALRS_handle));
WALR_close(tdbb->tdbb_status_vector,
reinterpret_cast < walrs ** >(&WALRS_handle));
delete string;
}