mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-27 20:03:03 +01:00
a7f18b2e31
to hold variables in CPU registers.
1111 lines
26 KiB
C++
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;
|
|
}
|