8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-29 06:43:03 +01:00
firebird-mirror/src/jrd/ail.cpp
2003-12-22 10:00:59 +00:00

1567 lines
38 KiB
C++

/*
* PROGRAM: JRD Access Method
* MODULE: ail.cpp
* 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): ______________________________________.
*
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
*
* 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define
*
*/
#include "firebird.h"
#include <string.h>
#include "../jrd/jrd.h"
#include "../jrd/ods.h"
#include "../jrd/jrn.h"
#include "../jrd/tra.h"
#include "../jrd/pag.h"
#include "../jrd/os/pio.h"
#include "../jrd/dsc.h"
#include "../wal/wal.h"
#include "gen/iberror.h"
#include "../jrd/flags.h"
#include "../jrd/sbm.h"
#include "../jrd/sdw.h"
#include "../jrd/lls.h"
#include "../jrd/rse.h"
#include "../jrd/ail.h"
#include "../jrd/all_proto.h"
#include "../jrd/cch_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/iberr_proto.h"
#include "../jrd/jrn_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/misc_proto.h"
#include "../jrd/mov_proto.h"
#include "../jrd/pag_proto.h"
#include "../jrd/os/pio_proto.h"
#include "../jrd/rec_proto.h"
#include "../jrd/sbm_proto.h"
#include "../jrd/sch_proto.h"
#include "../jrd/thd_proto.h"
#include "../jrd/tra_proto.h"
#include "../jrd/isc_f_proto.h"
#include "../wal/wal_proto.h"
#include "../wal/walc_proto.h"
#include "../wal/walf_proto.h"
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef NOT_USED_OR_REPLACED
static void build_wal_param(UCHAR *, LGFILE **, SLONG, LGFILE *, SLONG *);
#endif
static void delete_log_files(const TEXT*, SLONG, lls*);
#ifdef NOT_USED_OR_REPLACED
static BOOLEAN get_walinfo(TEXT *);
#endif
static void initialize_wal(TDBB, const TEXT*, WIN*, log_info_page*, SSHORT, bool, SBM*);
#ifdef NOT_USED_OR_REPLACED
static void process_log_updater(log_info_page*);
static void process_recovery(TDBB, const TEXT*, WIN*, log_info_page**,
SSHORT, bool, SBM*);
static void set_first_user(LGFILE **, log_info_page*, TEXT *);
#endif
#define MOVE_BYTE(x_from,x_to) *x_to++ = *x_from++;
void AIL_add_log()
{
/**************************************
*
* A I L _ a d d _ l o g
*
**************************************
*
* Functional description
* WAL is being enabled.
*
**************************************/
SCHAR db_name[MAXPATHLEN];
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
/*
* remove the rollover flag in the log page. Update the current file
* and control points to point to new file!
*/
CCH_flush(tdbb, (USHORT) FLUSH_ALL, 0);
WIN window(LOG_PAGE);
log_info_page* logp = (log_info_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_log);
CCH_MARK_MUST_WRITE(tdbb, &window);
if (logp->log_flags & log_add) {
CCH_exclusive(tdbb, LCK_EX, LCK_WAIT);
strcpy(db_name, dbb->dbb_file->fil_string);
logp->log_flags &= ~log_no_ail;
logp->log_mod_tid = 0;
logp->log_mod_tip = 0;
sbm* sbm_rec;
AIL_init(db_name, 0, &window, false, &sbm_rec);
/* With WAL, database write should be async */
PAG_set_force_write(dbb, FALSE);
CCH_release_exclusive(tdbb);
}
CCH_RELEASE(tdbb, &window);
}
void AIL_checkpoint_finish(
ISC_STATUS* status_vector,
DBB dbb,
SLONG seq, const TEXT* walname, SLONG p_off, SLONG off)
{
/**************************************
*
* A I L _ c h e c k p o i n t _ f i n i s h
*
**************************************
*
* Functional description
* Finish a database checkpoint by flushing the OS cache
* and updating the control point on the database log page.
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
PIO_flush(dbb->dbb_file);
if (dbb->dbb_shadow) {
PIO_flush(dbb->dbb_shadow->sdw_file);
}
AIL_upd_cntrl_pt(walname, (USHORT) strlen(walname), seq, off, p_off);
WIN window(LOG_PAGE);
pag* log_page = CCH_FETCH(tdbb, &window, LCK_write, pag_log);
CCH_MARK(tdbb, &window);
log_page->pag_checksum = CCH_checksum(window.win_bdb);
PIO_write(dbb->dbb_file, window.win_bdb, window.win_buffer,
status_vector);
CCH_write_all_shadows(tdbb, 0, window.win_bdb, status_vector, 1, false);
PIO_flush(dbb->dbb_file);
if (dbb->dbb_shadow) {
PIO_flush(dbb->dbb_shadow->sdw_file);
}
CCH_RELEASE(tdbb, &window);
/* Inform WAL writer that older log files can be reused */
WAL_checkpoint_recorded(status_vector, dbb->dbb_wal);
/* Make sure there is a journal entry of next transaction ID */
AIL_journal_tid();
}
void AIL_commit(SLONG number)
{
/**************************************
*
* A I L _ c o m m i t
*
**************************************
*
* Functional description
* Write a commit record to WAL and participate in group commit.
* A 0 length will be written out to flush the WAL.
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
if (!dbb->dbb_wal)
return;
/* Prepare WAL message */
LTJC commit_rec;
MOVE_CLEAR((SLONG*)&commit_rec, LTJC_SIZE);
commit_rec.ltjc_header.jrnh_type = JRN_COMMIT;
commit_rec.ltjc_header.jrnh_length = LTJC_SIZE;
commit_rec.ltjc_header.jrnh_version = JOURNAL_VERSION;
/* Write message to WAL */
THREAD_EXIT;
SLONG seqno, offset;
if (WAL_commit
(tdbb->tdbb_status_vector, dbb->dbb_wal, (UCHAR *) & commit_rec,
/* LTJC_SIZE */ 0, &seqno, &offset) != FB_SUCCESS)
{
THREAD_ENTER;
ERR_punt();
}
THREAD_ENTER;
}
void AIL_disable()
{
/**************************************
*
* A I L _ d i s a b l e
*
**************************************
*
* Functional description
* Disable journalling, if enabled.
* Delete journal related entries from header page.
*
**************************************/
UCHAR data[MAXPATHLEN];
USHORT jd_len, d_len;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
UCHAR journal_dir[MAXPATHLEN];
PAG_get_clump(HEADER_PAGE, HDR_backup_info, &d_len, data);
PAG_get_clump(HEADER_PAGE, HDR_journal_server, &jd_len, journal_dir);
journal_dir[jd_len] = 0;
if (!jd_len)
ERR_post(isc_no_jrn, 0);
if (!dbb->dbb_wal)
ERR_post(isc_no_wal, 0);
const SLONG ret_val = JRN_init(tdbb->tdbb_status_vector, &dbb->dbb_journal,
dbb->dbb_page_size,
journal_dir, jd_len,
data, d_len);
if (ret_val != FB_SUCCESS) {
AIL_process_jrn_error(ret_val);
}
PAG_delete_clump_entry(HEADER_PAGE, HDR_journal_server);
PAG_delete_clump_entry(HEADER_PAGE, HDR_backup_info);
LTJC record;
record.ltjc_header.jrnh_type = JRN_DISABLE;
record.ltjc_page_size = 0;
record.ltjc_length = d_len;
record.ltjc_seqno = 0;
record.ltjc_offset = 0;
tdbb->tdbb_status_vector[1] = 0;
ULONG seqno;
ULONG offset;
AIL_put(dbb, tdbb->tdbb_status_vector,
reinterpret_cast<jrnh*>(&record), LTJC_SIZE, data, d_len,
(ULONG) 0, (ULONG) 0, &seqno, &offset);
if (tdbb->tdbb_status_vector[1]) {
ERR_punt();
}
record.ltjc_seqno = seqno;
record.ltjc_offset = offset;
/*
* Inform WAL manager about disable journal.
* The WAL manager will roll over to a new file.
*/
if (WAL_journal_disable(tdbb->tdbb_status_vector, dbb->dbb_wal) != FB_SUCCESS)
{
ERR_punt();
}
if (dbb->dbb_journal) {
const SLONG ret_val2 = JRN_disable(tdbb->tdbb_status_vector, dbb->dbb_journal,
reinterpret_cast<jrnh*>(&record), data, d_len);
if (ret_val2 != FB_SUCCESS)
AIL_process_jrn_error(ret_val);
}
}
void AIL_drop_log()
{
/**************************************
*
* A I L _ d r o p _ l o g
*
**************************************
*
* Functional description
* Do things related to dropping WAL protocol.
* Flush cache, disable journaling, initialize log page,
* delete log files etc.
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
if (!dbb->dbb_wal)
return;
/* Get a protected write access to the database; only other
process (thread) running during this step could be
that of the cache manager. */
CCH_exclusive(tdbb, LCK_PW, LCK_WAIT);
/* Flush the cache out so that the database on disk becomes
consistent. WAL is not going to help any more ! */
CCH_flush(tdbb, (USHORT) FLUSH_ALL, 0);
/* journal has to be disabled before dropping WAL */
UCHAR journal_dir[MAXPATHLEN];
USHORT jd_len;
if (PAG_get_clump(HEADER_PAGE, HDR_journal_server, &jd_len, journal_dir)) {
AIL_disable();
}
/* Now get a list of all the log files to be deleted. */
TEXT latest_logname[MAXPATHLEN];
SLONG latest_log_p_offset;
WAL_status(tdbb->tdbb_status_vector, dbb->dbb_wal, NULL,
latest_logname, &latest_log_p_offset, NULL,
NULL, NULL, NULL, NULL);
lls* stack = NULL;
AIL_get_file_list(&stack);
/* Shutdown the WAL subsystem cleanly */
WAL_set_cleanup_flag(dbb->dbb_wal);
CCH_do_log_shutdown(tdbb, true);
/* Turn on sync writes if WAL protocol is dropped */
PAG_set_force_write(dbb, TRUE);
/* Now get rid of all the log files */
delete_log_files(latest_logname, latest_log_p_offset, stack);
/* With WAL, transids are bumped up to optimize logging. So
bring the next transaction id to the last bumped up value. */
REC_update_next_transid();
/* Fetch the log page to reset WAL info on it */
WIN window(LOG_PAGE);
log_info_page* logp = (log_info_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_log);
CCH_MARK_MUST_WRITE(tdbb, &window);
AIL_init_log_page(logp, logp->log_file.cp_seqno);
CCH_RELEASE(tdbb, &window);
CCH_release_exclusive(tdbb);
}
void AIL_drop_log_force()
{
/**************************************
*
* A I L _ d r o p _ l o g _ f o r c e
*
**************************************
*
* Functional description
* Initialize log page to drop WAL protocol forcefully.
* May be log files are not available for recovery or they
* are corrupted.
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
/* Get a protected write access to the database; only other
process (thread) running during this step could be
that of the cache manager. */
CCH_exclusive(tdbb, LCK_PW, LCK_WAIT);
/* Fetch the log page to reset WAL info on it */
WIN window(LOG_PAGE);
log_info_page* logp = (log_info_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_log);
if (logp->log_flags & log_no_ail) { /* WAL already disabled */
CCH_RELEASE(tdbb, &window);
CCH_release_exclusive(tdbb);
return;
}
/* With WAL, transids are bumped up to optimize logging. So
bring the next transaction id to the last bumped up value. */
REC_update_next_transid();
CCH_MARK_MUST_WRITE(tdbb, &window);
AIL_init_log_page(logp, logp->log_file.cp_seqno);
CCH_RELEASE(tdbb, &window);
CCH_release_exclusive(tdbb);
}
void AIL_enable(
const TEXT* journal_name,
USHORT j_length, const UCHAR* data, USHORT d_len, SSHORT archive)
{
/**************************************
*
* A I L _ e n a b l e
*
**************************************
*
* Functional description
* Enable journalling for the database.
* Add entries to header page
* RETURNS
* journal descriptor in ret_journal.
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
/* Can enable journal only if wal sub system is in use */
if (!dbb->dbb_wal)
ERR_post(isc_no_wal_no_jrn, 0);
/* check if journal is already enabled */
UCHAR journal_dir[MAXPATHLEN];
USHORT jd_len;
if (PAG_get_clump(HEADER_PAGE, HDR_journal_server, &jd_len, journal_dir)) {
ERR_post(isc_jrn_present, 0);
}
/*
* check if we are trying to enable long term journaling without
* specifying archive and we are using round robin.
*/
if (!archive) {
LGFILE* log_files[MAX_LOG_FILES];
LGFILE* log_ovflow;
ULONG number;
MET_get_walinfo(tdbb, log_files, &number, &log_ovflow);
for (ULONG i = 0; i < number; i++) {
if (!(log_files[i]->lg_flags & LOG_serial)) {
for (i = 0; i < number; i++)
delete log_files[i];
ERR_post(isc_no_archive, 0);
}
}
}
/* Put a enable record in the WAL and get the current seqno/offset pair */
LTJC jrecord;
jrecord.ltjc_header.jrnh_type = JRN_ENABLE;
jrecord.ltjc_page_size = dbb->dbb_page_size;
jrecord.ltjc_length = d_len;
jrecord.ltjc_seqno = 0;
jrecord.ltjc_offset = 0;
tdbb->tdbb_status_vector[1] = 0;
ULONG seqno;
ULONG offset;
AIL_put(dbb, tdbb->tdbb_status_vector,
reinterpret_cast<jrnh*>(&jrecord), LTJC_SIZE, data, d_len,
(ULONG) 0, (ULONG) 0, &seqno, &offset);
if (tdbb->tdbb_status_vector[1])
ERR_punt();
/* Inform the journal server about enable */
jrecord.ltjc_seqno = seqno;
jrecord.ltjc_offset = offset;
const SLONG ret_val = JRN_enable(tdbb->tdbb_status_vector, &dbb->dbb_journal,
journal_name, j_length, data, d_len,
&jrecord);
if (ret_val != FB_SUCCESS) {
AIL_process_jrn_error(ret_val);
}
/* Inform wal subsystem about enable */
if (WAL_journal_enable(tdbb->tdbb_status_vector, dbb->dbb_wal,
journal_name, d_len,
reinterpret_cast<const char*>(data)) != FB_SUCCESS)
{
ERR_punt();
}
/* Add journal entries to header page */
PAG_add_clump(HEADER_PAGE, HDR_journal_server, j_length,
reinterpret_cast<const UCHAR*>(journal_name), CLUMP_ADD, 0);
PAG_add_clump(HEADER_PAGE, HDR_backup_info, d_len, data, CLUMP_ADD, 1);
WAL_flush(tdbb->tdbb_status_vector, dbb->dbb_wal,
reinterpret_cast<SLONG*>(&seqno),
reinterpret_cast<SLONG*>(&offset), false);
}
void AIL_fini()
{
/**************************************
*
* A I L _ f i n i
*
**************************************
*
* Functional description
* Sign off with WAL substem and journal server (if needed).
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
if (!dbb->dbb_wal)
return;
WAL_fini(tdbb->tdbb_status_vector, &dbb->dbb_wal);
dbb->dbb_wal = 0;
if (!dbb->dbb_journal)
return;
const SLONG ret_val = JRN_fini(tdbb->tdbb_status_vector, &dbb->dbb_journal);
if (ret_val != FB_SUCCESS)
{
AIL_process_jrn_error(ret_val);
}
dbb->dbb_journal = 0;
}
void AIL_get_file_list(LLS * stack)
{
/**************************************
*
* A I L _ g e t _ f i l e _ l i s t
*
**************************************
*
* Functional description
* Get list of WAL files to drop
*
**************************************/
DBB dbb = GET_DBB;
if (!dbb->dbb_wal)
return;
WALS WAL_segment;
WALC_acquire(dbb->dbb_wal, &WAL_segment);
/*
** Do we have round robin files defined.
** If yes then put them on stack of files
** to be deleted.
*/
SSHORT count = WAL_segment->wals_max_logfiles;
if (count > 0) {
while (--count >= 0) {
LOGF* logf = LOGF_INFO(count);
if (logf->logf_flags & LOGF_RAW)
continue;
const char* temp_fname = LOGF_NAME(logf);
const SSHORT fname_term_length = strlen(temp_fname) + 1;
str* fname = FB_NEW_RPT(*dbb->dbb_permanent, fname_term_length) str();
MOVE_FAST(temp_fname, (SCHAR*)fname->str_data, fname_term_length);
LLS_PUSH(fname, stack);
}
}
/*
** Now check for overflow files or serial files.
** Start from the current logfile and retrieve existing
** log file names which need to be deleted.
*/
/* WAL_segment->wals_logname is the current log file */
SCHAR log_name1[MAXPATHLEN];
SCHAR log_name2[MAXPATHLEN] = "";
SCHAR* curr_name = log_name1;
SCHAR* prev_name = log_name2;
strcpy(curr_name, WAL_segment->wals_logname);
SLONG curr_log_partition_offset = WAL_segment->wals_log_partition_offset;
WALC_release(dbb->dbb_wal);
SLONG log_flags;
SLONG log_seqno;
SLONG log_length;
ISC_STATUS_ARRAY status_vector;
if (WALF_get_log_info(status_vector, dbb->dbb_file->fil_string, curr_name,
curr_log_partition_offset, &log_seqno, &log_length,
&log_flags) != FB_SUCCESS)
{
gds__free((SLONG *) log_name1);
gds__free((SLONG *) log_name2);
return;
}
while (true) {
if (!(log_flags & WALFH_RAW)) {
const SSHORT fname_term_length = strlen(curr_name) + 1;
str* fname = FB_NEW_RPT(*dbb->dbb_permanent, fname_term_length) str();
MOVE_FAST(curr_name, (SCHAR*)fname->str_data, fname_term_length);
LLS_PUSH(fname, stack);
}
SLONG prev_log_partition_offset;
if (WALF_get_next_log_info(status_vector, dbb->dbb_file->fil_string,
curr_name, curr_log_partition_offset,
prev_name, &prev_log_partition_offset,
&log_seqno, &log_length, &log_flags,
-1) != FB_SUCCESS)
{
break;
}
char* temp_name = prev_name;
prev_name = curr_name;
curr_name = temp_name;
curr_log_partition_offset = prev_log_partition_offset;
}
}
void AIL_init(
const TEXT* filename,
SSHORT file_len,
WIN* in_win, bool activate_shadow, SBM* sbm_rec)
{
/**************************************
*
* A I L _ i n i t
*
**************************************
*
* Functional description
* Init WAL handle at database attachment time.
* If this is the first attachment, read logfile information and
* start WAL subsystem.
* If WAL has never been used for the database, do some set up work.
* Perform short term recovery if required.
**************************************/
// null out the sparse bit map
*sbm_rec = (SBM) 0;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
/* Get WAL file name */
dbb->dbb_wal = 0;
TEXT dbname[MAXPATHLEN];
if (file_len) {
MOVE_FAST(filename, dbname, file_len);
dbname[file_len] = 0;
}
else
strcpy(dbname, filename);
/*
* The first user has to initialize the WAL subsystem after doing
* some checks.
*/
WIN window(LOG_PAGE);
WIN* win;
log_info_page* logp;
if (in_win) {
win = in_win;
win->win_flags = 0;
logp = (log_info_page*) win->win_buffer;
}
else {
logp = (log_info_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_log);
win = &window;
}
if (CCH_exclusive(tdbb, LCK_EX, LCK_NO_WAIT)) {
initialize_wal(tdbb, dbname, win, logp, 1, activate_shadow, sbm_rec);
if (!in_win)
CCH_RELEASE(tdbb, win);
return;
}
/*
* Handle the case where the process doing short term recovery aborted.
* Anyone getting the log page in exclusive access will restart recovery.
* During recovery, we have to ensure that the log_rec_in_progress is not
* reset.
*/
if ((logp->log_flags & log_recover)
&& (logp->log_flags & log_rec_in_progress)) {
initialize_wal(tdbb, dbname, win, logp, 0, activate_shadow, sbm_rec);
if (!in_win)
CCH_RELEASE(tdbb, win);
return;
}
if (logp->log_flags & log_no_ail) {
if (!in_win)
CCH_RELEASE(tdbb, win);
return;
}
if (!in_win)
CCH_RELEASE(tdbb, win);
dbb->dbb_wal = 0;
/* If not the first user, call WAL_attach () */
if (WAL_attach(tdbb->tdbb_status_vector, &dbb->dbb_wal, dbname) !=
FB_SUCCESS) ERR_punt();
}
void AIL_init_log_page(log_info_page* logp, SLONG seqno)
{
/**************************************
*
* A I L _ i n i t _ l o g _ p a g e
*
**************************************
*
* Functional description
* (Re-)initialize the passed log page.
* Set all parameters to 0 except the log file
* sequence number which is set to the passed value.
**************************************/
/* First zero out the whole structure except for the page header portion */
SLONG len = sizeof(log_info_page) - sizeof(pag);
UCHAR* p = (UCHAR *) logp + sizeof(pag);
do {
*p++ = 0;
} while (--len);
/* Now set individual fields as appropriate */
logp->log_flags = log_no_ail;
logp->log_file.cp_seqno = seqno;
p = logp->log_data;
/* Set control point 1 file name */
*p++ = LOG_ctrl_file1;
*p++ = CTRL_FILE_LEN;
len = CTRL_FILE_LEN;
do {
*p++ = 0;
} while (--len);
/* Set control point 2 file name */
*p++ = LOG_ctrl_file2;
*p++ = CTRL_FILE_LEN;
len = CTRL_FILE_LEN;
do {
*p++ = 0;
} while (--len);
/* Set current log file */
*p++ = LOG_logfile;
*p++ = CTRL_FILE_LEN;
len = CTRL_FILE_LEN;
do {
*p++ = 0;
} while (--len);
*p = LOG_end;
logp->log_end = (USHORT) (p - (UCHAR *) logp);
}
void AIL_journal_tid()
{
/**************************************
*
* A I L _ j o u r n a l _ t i d
*
**************************************
*
* Functional description
* Journal the next transaction id on the header page.
* This will be used at WAL_init and control point time.
**************************************/
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
WIN window(HEADER_PAGE);
HDR hdr = (HDR) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
CCH_MARK_MUST_WRITE(tdbb, &window);
SLONG fake_tid = hdr->hdr_next_transaction + MOD_START_TRAN;
SLONG seqno = hdr->hdr_next_transaction / dbb->dbb_pcontrol->pgc_tpt;
if (seqno != (SLONG)((fake_tid + 1) / dbb->dbb_pcontrol->pgc_tpt))
{
fake_tid = ((seqno + 1) * dbb->dbb_pcontrol->pgc_tpt) - 1;
}
hdr->hdr_bumped_transaction = fake_tid;
JRNDH journal;
journal.jrndh_type = JRNP_DB_HEADER;
journal.jrndh_nti = fake_tid;
journal.jrndh_oit = hdr->hdr_oldest_transaction;
journal.jrndh_oat = hdr->hdr_oldest_active;
CCH_journal_record(tdbb, &window, reinterpret_cast < UCHAR * >(&journal),
JRNDH_SIZE, 0, 0);
CCH_RELEASE(tdbb, &window);
SLONG offset;
WAL_flush(tdbb->tdbb_status_vector, dbb->dbb_wal, &seqno, &offset, false);
}
void AIL_process_jrn_error(SLONG ret_val)
{
/**************************************
*
* A I L _ p r o c e s s _ j r n _ e r r o r
*
**************************************
*
* Functional description
* Handle error from a journal call.
*
**************************************/
if (ret_val == FB_FAILURE)
ERR_punt();
else if (ret_val < 0)
BUGCHECK(-1 * ret_val);
else
IBERROR(ret_val);
}
void AIL_put(
DBB dbb,
ISC_STATUS * status,
JRNH* header,
USHORT h_length,
const UCHAR* data,
USHORT d_length,
ULONG prev_seqno, ULONG prev_offset, ULONG* seqno, ULONG* offset)
{
/**************************************
*
* A I L _ p u t
*
**************************************
*
* Functional description
* Write a record to WAL.
* Returns seqno & offset pair.
*
* NOTE: AIL_put () can be called from signal handlers to write
* pages. So ERR_punt () cannot be called from within the routine
* Check status [1] on return.
**************************************/
if (!dbb->dbb_wal) {
*seqno = prev_seqno;
*offset = prev_offset;
return;
}
switch (header->jrnh_type) {
case JRN_CNTRL_PT:
case JRN_START_ONLINE_DMP:
case JRN_END_ONLINE_DMP:
case JRN_ONLINE_DMP_FILE:
case JRN_WAL_NAME:
MOV_time_stamp(reinterpret_cast <
ISC_TIMESTAMP * >(((LTJW *) header)->ltjw_date));
break;
}
/* Prepare WAL message */
header->jrnh_handle = 0;
header->jrnh_length = h_length + d_length;
header->jrnh_series = 0;
header->jrnh_version = JOURNAL_VERSION;
header->jrnh_prev_seqno = prev_seqno;
header->jrnh_prev_offset = prev_offset;
#ifdef DEV_BUILD
/* Checksum the log record after zeroing out the field */
header->jrnh_series =
MISC_checksum_log_rec(reinterpret_cast<UCHAR*>(header), h_length,
data, d_length);
#endif /* DEV_BUILD */
/* Write message to WAL */
THREAD_EXIT;
WAL_put(status,
dbb->dbb_wal,
reinterpret_cast<UCHAR*>(header),
h_length,
data,
d_length,
reinterpret_cast<SLONG*>(seqno),
reinterpret_cast<SLONG*>(offset));
THREAD_ENTER;
}
void AIL_recover_page(SLONG page_no, pag* page)
{
/**************************************
*
* A I L _ r e c o v e r _ p a g e
*
**************************************
*
* Functional description
* recovers a single page from the second last control point.
* assumes that the 'corrupt' page is not in the cache.
**************************************/
SCHAR rwal[MAXPATHLEN];
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
/* Flush the records on WAL buffer before trying to recover the
* page
*/
SLONG seqno, offset;
WAL_flush(tdbb->tdbb_status_vector, dbb->dbb_wal, &seqno, &offset, false);
WIN window(LOG_PAGE);
log_info_page* logp = (log_info_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_log);
/* If there is no log, just return */
if (logp->log_flags & log_no_ail) {
CCH_RELEASE(tdbb, &window);
return;
}
CP cp1;
cp1.cp_seqno = logp->log_cp_1.cp_seqno;
cp1.cp_offset = logp->log_cp_1.cp_offset;
cp1.cp_p_offset = logp->log_cp_1.cp_p_offset;
const UCHAR* p = logp->log_data;
while (*p != LOG_ctrl_file1) {
p += 2 + p[1];
}
MOVE_FAST((const SCHAR*)(p + 2), rwal, logp->log_cp_1.cp_fn_length);
rwal[logp->log_cp_1.cp_fn_length] = 0;
CCH_RELEASE(tdbb, &window);
REC_recover_page(dbb->dbb_file->fil_string, rwal, &cp1, page_no, page);
}
void AIL_set_log_options(
SLONG chkpt_len,
SSHORT num_bufs, USHORT bufsize, SLONG grp_cmt_wait)
{
/**************************************
*
* A I L _ s e t _ l o g _ o p t i o n s
*
**************************************
*
* Functional description
* Set any log options if specified.
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
/* why fetch log page if you don't need it */
if (!(chkpt_len || num_bufs || bufsize || (grp_cmt_wait >= 0)))
return;
WIN window(LOG_PAGE);
CCH_FETCH(tdbb, &window, LCK_write, pag_log);
if (chkpt_len) {
PAG_add_clump(LOG_PAGE, LOG_chkpt_len, sizeof(SLONG),
(UCHAR *) & chkpt_len, CLUMP_REPLACE, 0);
if (dbb->dbb_wal) {
WAL_set_checkpoint_length(tdbb->tdbb_status_vector, dbb->dbb_wal,
chkpt_len);
}
}
if (num_bufs) {
PAG_add_clump(LOG_PAGE, LOG_num_bufs, sizeof(SSHORT),
(UCHAR *) & num_bufs, CLUMP_REPLACE, 0);
}
if (bufsize) {
PAG_add_clump(LOG_PAGE, LOG_bufsize, sizeof(USHORT),
(UCHAR *) & bufsize, CLUMP_REPLACE, 0);
}
if (grp_cmt_wait >= 0) {
PAG_add_clump(LOG_PAGE, LOG_grp_cmt_wait, sizeof(SLONG),
(UCHAR *) & grp_cmt_wait, CLUMP_REPLACE, 0);
if (dbb->dbb_wal) {
WAL_set_grpc_wait_time(tdbb->tdbb_status_vector, dbb->dbb_wal,
grp_cmt_wait);
}
}
CCH_RELEASE(tdbb, &window);
}
void AIL_shutdown(
SCHAR* walname,
SLONG* seqno,
SLONG* offset, SLONG* p_offset, bool force_archive)
{
/**************************************
*
* A I L _ s h u t d o w n
*
**************************************
*
* Functional description
* Shutdown wal subsystem after a flush.
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
WAL_flush(tdbb->tdbb_status_vector, dbb->dbb_wal, seqno, offset, false);
WAL_shutdown(tdbb->tdbb_status_vector, dbb->dbb_wal, seqno, walname,
p_offset, offset, force_archive);
AIL_fini();
}
void AIL_upd_cntrl_pt(
const TEXT* walname,
const USHORT w_len, ULONG seqno, ULONG offset, ULONG p_offset)
{
/***********************************************
*
* A I L _ u p d _ c n t r l _ p t
*
***********************************************
*
* Functional description
* Updates the log page control point.
* Makes the 1st control point the second control point
* Updates the second control point with the new values.
*
* Assumption:: All control point entries are in one page
* :: All control point entries are contiguous.
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
WIN window(LOG_PAGE);
log_info_page* logp =
(log_info_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_log);
CCH_MARK_MUST_WRITE(tdbb, &window);
UCHAR *p1, *p2, *p3, *p;
p1 = p2 = p3 = 0;
for (p = logp->log_data; (*p != LOG_end); p += 2 + p[1]) {
if (*p == LOG_ctrl_file1)
p1 = p;
if (*p == LOG_ctrl_file2)
p2 = p;
if (*p == LOG_logfile)
p3 = p;
if (p1 && p2 && p3)
break;
}
if (*p == LOG_end)
BUGCHECK(269);
/* update the second last entry */
p = p2;
*p = LOG_ctrl_file1;
logp->log_cp_1.cp_seqno = logp->log_cp_2.cp_seqno;
logp->log_cp_1.cp_offset = logp->log_cp_2.cp_offset;
logp->log_cp_1.cp_p_offset = logp->log_cp_2.cp_p_offset;
logp->log_cp_1.cp_fn_length = logp->log_cp_2.cp_fn_length;
/* add the new entries */
p = p1;
*p++ = LOG_ctrl_file2;
p++;
const UCHAR* q = reinterpret_cast<const UCHAR*>(walname);
USHORT len = w_len;
if (len)
do {
*p++ = *q++;
} while (--len);
logp->log_cp_2.cp_seqno = seqno;
logp->log_cp_2.cp_offset = offset;
logp->log_cp_2.cp_p_offset = p_offset;
logp->log_cp_2.cp_fn_length = w_len;
/* update the current file also */
p = p3 + 2;
q = reinterpret_cast<const UCHAR*>(walname);
len = w_len;
if (len) {
do {
*p++ = *q++;
} while (--len);
}
logp->log_file.cp_seqno = seqno;
logp->log_file.cp_offset = offset;
logp->log_file.cp_p_offset = p_offset;
logp->log_file.cp_fn_length = w_len;
CCH_RELEASE(tdbb, &window);
}
#ifdef NOT_USED_OR_REPLACED
static void build_wal_param(
UCHAR * wpb,
LGFILE ** log_files,
SLONG number,
LGFILE * log_ovflow, SLONG * wpb_len)
{
/**************************************
*
* b u i l d _ w a l _ p a r a m
*
**************************************
*
* Functional description
* Build the wal parameter block and return length
*
**************************************/
ULONG param1;
USHORT param2;
USHORT plen;
USHORT jd_len, d_len;
UCHAR data[MAXPATHLEN], *p;
/* Get journal information, if any */
UCHAR journal_dir[MAXPATHLEN];
PAG_get_clump(HEADER_PAGE, HDR_backup_info, &d_len, data);
PAG_get_clump(HEADER_PAGE, HDR_journal_server, &jd_len, journal_dir);
journal_dir[jd_len] = 0;
/* Build the wal param block */
p = wpb;
if (jd_len) {
p += MISC_build_parameters_block(p,
PARAM_BYTE(WAL_PARAM_JRN_DIRNAME),
PARAM_STRING(journal_dir),
PARAM_BYTE(WAL_PARAM_JRN_DATA),
PARAM_NBYTES(d_len, data),
(SCHAR) 0);
}
if (log_files[0]->lg_flags & LOG_serial) {
p += MISC_build_parameters_block(p,
PARAM_BYTE(WAL_PARAM_SERIAL_LOG),
PARAM_POINTER(log_files[0]),
(SCHAR) 0);
}
else {
p += MISC_build_parameters_block(p,
PARAM_BYTE(WAL_PARAM_RR_LOGS_COUNT),
PARAM_SHORT(number),
PARAM_BYTE(WAL_PARAM_RR_LOGS),
PARAM_POINTER(log_files), (SCHAR) 0);
}
if (log_ovflow)
p += MISC_build_parameters_block(p,
PARAM_BYTE(WAL_PARAM_OVFLOW_LOG),
PARAM_POINTER(log_ovflow),
(SCHAR) 0);
if (PAG_get_clump
(LOG_PAGE, LOG_chkpt_len, &plen,
reinterpret_cast < UCHAR * >(&param1))) {
p +=
MISC_build_parameters_block(p, PARAM_BYTE(WAL_PARAM_CKPT_INTRVL),
PARAM_LONG(param1), (SCHAR) 0);
}
if (PAG_get_clump
(LOG_PAGE, LOG_num_bufs, &plen,
reinterpret_cast < UCHAR * >(&param2))) {
p +=
MISC_build_parameters_block(p, PARAM_BYTE(WAL_PARAM_BUF_COUNT),
PARAM_SHORT(param2), (SCHAR) 0);
}
if (PAG_get_clump
(LOG_PAGE, LOG_bufsize, &plen,
reinterpret_cast < UCHAR * >(&param2))) {
p +=
MISC_build_parameters_block(p, PARAM_BYTE(WAL_PARAM_BUF_LEN),
PARAM_SHORT(param2), (SCHAR) 0);
}
if (PAG_get_clump
(LOG_PAGE, LOG_grp_cmt_wait, &plen,
reinterpret_cast < UCHAR * >(&param1))) {
p +=
MISC_build_parameters_block(p,
PARAM_BYTE(WAL_PARAM_GRPC_WAIT_USECS),
PARAM_LONG(param1), (SCHAR) 0);
}
p += MISC_build_parameters_block(p, PARAM_BYTE(WAL_PARAM_END), (SCHAR) 0);
*wpb_len = p - wpb;
}
#endif
static void delete_log_files(
const TEXT* latest_logname, // unused
SLONG latest_log_p_offset,
lls* stack)
{
/**************************************
*
* d e l e t e _ l o g _ f i l e s
*
**************************************
*
* Functional description
* Delete the log files listed in the stack. First give
* some time to the journal server, if configured, to archive
* the last log file(s).
*
**************************************/
ISC_STATUS_ARRAY local_status;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
while (stack) {
const str* fname = (STR) LLS_POP(&stack);
if (unlink(reinterpret_cast<const char*>(fname->str_data))) {
IBERR_build_status(local_status, isc_io_error,
isc_arg_string, "unlink",
isc_arg_string, fname->str_data,
isc_arg_gds, isc_io_delete_err, 0);
gds__log_status(dbb->dbb_file->fil_string, local_status);
}
}
}
#ifdef NOT_USED_OR_REPLACED
static BOOLEAN get_walinfo(TEXT * walname)
{
/**************************************
*
* g e t _ w a l i n f o
*
**************************************
*
* Functional description
* Get wal name and wal number from header page.
* Build the log name the way subsystem understands it.
* RETURNS
* TRUE if WAL exists
* FALSE otherwise
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
WIN window(LOG_PAGE);
log_info_page* logp =
(log_info_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_log);
USHORT len;
if (!PAG_get_clump
(LOG_PAGE, LOG_logfile, &len,
reinterpret_cast < UCHAR * >(walname))) {
CCH_RELEASE(tdbb, &window);
return FALSE;
}
len = logp->log_file.cp_fn_length;
walname[len] = 0;
CCH_RELEASE(tdbb, &window);
return TRUE;
}
#endif
static void initialize_wal(
TDBB tdbb,
const TEXT* dbname, // unused
WIN* window,
log_info_page* logp,
SSHORT release,
bool activate_shadow, // unused
SBM* sbm_rec)
{
/**************************************
*
* i n i t i a l i z e _ w a l
*
**************************************
*
* Functional description
* Initialize wal subsystem.
* If release is true, release log page before recovery.
* logp can be modified inside this procedure, so do not use it
* after it is called. The page is released.
*
**************************************/
SET_TDBB(tdbb);
/* Get WAL file name */
tdbb->tdbb_database->dbb_wal = 0;
/* If there is no log, just return */
if ((logp->log_flags & log_no_ail) && (!(logp->log_flags & log_add))) {
/* if partial recovery is in progress, reset bit */
if (logp->log_flags & log_partial_rebuild) {
CCH_MARK_MUST_WRITE(tdbb, window);
logp->log_flags &= ~log_partial_rebuild;
}
return;
}
}
#ifdef NOT_USED_OR_REPLACED
static void process_log_updater(log_info_page* logp)
{
/**************************************
*
* p r o c e s s _ l o g _ u p d a t e r
*
**************************************
*
* Functional description
* Check if the process which updated the log page committed.
* Fixup the log_flags appropriately.
*
**************************************/
TDBB tdbb = GET_THREAD_DATA;
const SSHORT state = TRA_get_state(tdbb, logp->log_mod_tid);
logp->log_mod_tid = 0;
logp->log_mod_tip = 0;
if (state == tra_committed) {
if (logp->log_flags & log_delete) {
logp->log_flags &= ~log_delete;
logp->log_flags |= log_no_ail;
}
if (logp->log_flags & log_add) {
logp->log_flags &= ~log_no_ail;
}
}
else {
logp->log_flags &= ~(log_add | log_delete);
}
}
static void process_recovery(
TDBB tdbb,
const TEXT* dbname,
WIN* window,
log_info_page** logpp,
SSHORT release,
bool activate_shadow, SBM* sbm_rec)
{
/**************************************
*
* p r o c e s s _ r e c o v e r y
*
**************************************
*
* Functional description
* Do short term recovery.
* If release flag is set, release LOG_PAGE before recovery and
* fetch after recovery. Holding the page will act as a lock
* to prevent multiple users from running recovery. The first
* user will run in exclusive mode.
*
**************************************/
SET_TDBB(tdbb);
log_info_page* logp = *logpp;
CP cp1;
cp1.cp_seqno = logp->log_cp_1.cp_seqno;
cp1.cp_offset = logp->log_cp_1.cp_offset;
cp1.cp_p_offset = logp->log_cp_1.cp_p_offset;
UCHAR* p;
for (p = logp->log_data; (*p != LOG_ctrl_file1); p += 2 + p[1]);
TEXT rwal[MAXPATHLEN];
MOVE_FAST((SCHAR*)(p + 2), rwal, logp->log_cp_1.cp_fn_length);
rwal[logp->log_cp_1.cp_fn_length] = 0;
if (release) {
/* Page is already marked must_write */
logp->log_flags |= log_rec_in_progress;
CCH_RELEASE(tdbb, window);
}
if (activate_shadow) {
/* If activating shadow, recovery needs root file name */
WIN win(HEADER_PAGE);
HRD hdr = (HDR) CCH_FETCH(tdbb, &win, LCK_read, pag_header);
for (p = hdr->hdr_data; (*p != HDR_root_file_name); p += 2 + p[1]);
TEXT root_db[MAXPATHLEN];
MOVE_FAST((SCHAR*)(p + 2), root_db, p[1]);
root_db[p[1]] = 0;
CCH_RELEASE(tdbb, &win);
REC_recover(root_db, rwal, &cp1, sbm_rec, activate_shadow);
}
else
REC_recover(dbname, rwal, &cp1, sbm_rec, activate_shadow);
if (release) {
logp = (log_info_page*) CCH_FETCH(tdbb, window, LCK_write, pag_log);
CCH_MARK_MUST_WRITE(tdbb, window);
}
/* Since a recovery updates the LOG_PAGE, make sure it will be
* written to the shadows also
*/
SBM_set(tdbb, sbm_rec, LOG_PAGE);
logp->log_flags &= ~log_rec_in_progress;
/* If a shadow is being activated, roll over log file */
if (activate_shadow)
logp->log_flags |= log_add;
*logpp = logp;
}
static void set_first_user(LGFILE ** log_files, log_info_page* logp, TEXT * walname)
{
/**************************************
*
* s e t _ f i r s t _ u s e r
*
**************************************
*
* Functional description
* Initialize log page when using the WAL for the first time.
*
**************************************/
logp->log_flags &= ~log_add;
logp->log_flags &= ~log_no_ail;
logp->log_file.cp_seqno++;
MOV_time_stamp(reinterpret_cast <
ISC_TIMESTAMP * >(logp->log_creation_date));
if (log_files[0]->lg_flags & LOG_serial) {
WALC_build_logname(walname, log_files[0]->lg_name,
logp->log_file.cp_seqno);
}
else
strcpy(walname, log_files[0]->lg_name);
const SSHORT len = strlen(walname);
logp->log_file.cp_fn_length = len;
for (UCHAR* p = logp->log_data; (*p != LOG_end); p += 2 + p[1]) {
switch (*p) {
case LOG_ctrl_file1:
MOVE_FAST(walname, (SCHAR*)(p + 2), len);
break;
case LOG_ctrl_file2:
MOVE_FAST(walname, (SCHAR*)(p + 2), len);
break;
case LOG_logfile:
MOVE_FAST(walname, (SCHAR*)(p + 2), len);
break;
}
}
logp->log_cp_1.cp_fn_length = len;
logp->log_cp_1.cp_seqno = logp->log_file.cp_seqno;
logp->log_cp_1.cp_offset = 0;
logp->log_cp_1.cp_p_offset = 0;
logp->log_cp_2.cp_fn_length = len;
logp->log_cp_2.cp_seqno = logp->log_file.cp_seqno;
logp->log_cp_2.cp_offset = 0;
logp->log_cp_2.cp_p_offset = 0;
}
#endif