8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 20:43:03 +01:00
firebird-mirror/src/jrd/ail.cpp

1647 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/pio.h"
#include "../jrd/dsc.h"
#include "../wal/wal.h"
#include "gen/codes.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/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
extern "C" {
static void build_wal_param(UCHAR *, LGFILE **, SLONG, LGFILE *, SLONG *);
static void delete_log_files(TEXT *, SLONG, LLS);
static BOOLEAN get_walinfo(TEXT *);
static void initialize_wal(TDBB, TEXT *, WIN *, LIP, SSHORT, USHORT, SBM *);
static void process_log_updater(LIP);
static void process_recovery(TDBB, TEXT *, WIN *, LIP *,
SSHORT, USHORT, SBM *);
static void set_first_user(LGFILE **, LIP, TEXT *);
#define MOVE_BYTE(x_from,x_to) *x_to++ = *x_from++;
void AIL_add_log(void)
{
/**************************************
*
* A I L _ a d d _ l o g
*
**************************************
*
* Functional description
* WAL is being enabled.
*
**************************************/
DBB dbb;
WIN window;
LIP logp;
SCHAR db_name[MAXPATHLEN];
SBM sbm_rec;
TDBB tdbb;
tdbb = GET_THREAD_DATA;
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);
window.win_page = LOG_PAGE;
window.win_flags = 0;
logp = (LIP) 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;
AIL_init(db_name, 0, &window, 0, &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(
STATUS * status_vector,
DBB dbb,
SLONG seq, 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.
*
**************************************/
WIN window;
PAG log_page;
TDBB 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);
window.win_page = LOG_PAGE;
window.win_flags = 0;
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;
DBB dbb;
LTJC commit_rec;
SLONG seqno, offset;
tdbb = GET_THREAD_DATA;
dbb = tdbb->tdbb_database;
if (!dbb->dbb_wal)
return;
/* Prepare WAL message */
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;
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(void)
{
/**************************************
*
* A I L _ d i s a b l e
*
**************************************
*
* Functional description
* Disable journalling, if enabled.
* Delete journal related entries from header page.
*
**************************************/
TDBB tdbb;
DBB dbb;
LTJC record;
ULONG seqno;
ULONG offset;
TEXT journal_dir[MAXPATHLEN];
UCHAR data[MAXPATHLEN];
USHORT jd_len, d_len;
SLONG ret_val;
tdbb = GET_THREAD_DATA;
dbb = tdbb->tdbb_database;
PAG_get_clump(HEADER_PAGE, HDR_backup_info, &d_len, data);
PAG_get_clump(HEADER_PAGE, HDR_journal_server, &jd_len,
reinterpret_cast < UCHAR * >(journal_dir));
journal_dir[jd_len] = 0;
if (!jd_len)
ERR_post(gds_no_jrn, 0);
if (!dbb->dbb_wal)
ERR_post(gds_no_wal, 0);
if ((ret_val = JRN_init(tdbb->tdbb_status_vector, &dbb->dbb_journal,
dbb->dbb_page_size,
reinterpret_cast < UCHAR * >(journal_dir), jd_len,
data, d_len)) != 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);
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;
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) {
if ((ret_val = JRN_disable(tdbb->tdbb_status_vector, dbb->dbb_journal,
reinterpret_cast < jrnh * >(&record), data,
d_len)) != FB_SUCCESS)
AIL_process_jrn_error(ret_val);
}
}
void AIL_drop_log(void)
{
/**************************************
*
* 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;
DBB dbb;
WIN window;
LIP logp;
LLS stack = NULL;
TEXT latest_logname[MAXPATHLEN], journal_dir[MAXPATHLEN];
SLONG latest_log_p_offset;
USHORT jd_len;
tdbb = GET_THREAD_DATA;
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 */
if (PAG_get_clump
(HEADER_PAGE, HDR_journal_server, &jd_len,
reinterpret_cast < UCHAR * >(journal_dir))) {
AIL_disable();
}
/* Now get a list of all the log files to be deleted. */
WAL_status(tdbb->tdbb_status_vector, dbb->dbb_wal, NULL,
latest_logname, &latest_log_p_offset, NULL,
NULL, NULL, NULL, NULL);
AIL_get_file_list(&stack);
/* Shutdown the WAL subsystem cleanly */
WAL_set_cleanup_flag(dbb->dbb_wal);
CCH_do_log_shutdown(tdbb, 1);
/* 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 */
window.win_page = LOG_PAGE;
window.win_flags = 0;
logp = (LIP) 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(void)
{
/**************************************
*
* 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;
WIN window;
LIP logp;
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 */
window.win_page = LOG_PAGE;
window.win_flags = 0;
logp = (LIP) 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(
TEXT * journal_name,
USHORT j_length, 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;
DBB dbb;
LTJC jrecord;
ULONG seqno;
ULONG offset;
LGFILE *log_files[MAX_LOG_FILES];
LGFILE *log_ovflow;
SLONG ret_val;
ULONG number, i;
USHORT jd_len;
TEXT journal_dir[MAXPATHLEN];
tdbb = GET_THREAD_DATA;
dbb = tdbb->tdbb_database;
/* Can enable journal only if wal sub system is in use */
if (!dbb->dbb_wal)
ERR_post(gds_no_wal_no_jrn, 0);
/* check if journal is already enabled */
if (PAG_get_clump
(HEADER_PAGE, HDR_journal_server, &jd_len,
reinterpret_cast <
UCHAR * >(journal_dir))) ERR_post(gds_jrn_present, 0);
/*
* check if we are trying to enable long term journaling without
* specifying archive and we are using round robin.
*/
if (!archive) {
MET_get_walinfo(tdbb, log_files, &number, &log_ovflow);
for (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(gds_no_archive, 0);
}
}
/* Put a enable record in the WAL and get the current seqno/offset pair */
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;
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;
if ((ret_val = JRN_enable(tdbb->tdbb_status_vector, &dbb->dbb_journal,
journal_name, j_length, data, d_len,
&jrecord)) != 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 < char *>(data)) != FB_SUCCESS)
ERR_punt();
/* Add journal entries to header page */
PAG_add_clump(HEADER_PAGE, HDR_journal_server, j_length,
reinterpret_cast < 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 < long *>(&seqno),
reinterpret_cast < long *>(&offset), FALSE);
}
void AIL_fini(void)
{
/**************************************
*
* A I L _ f i n i
*
**************************************
*
* Functional description
* Sign off with WAL substem and journal server (if needed).
*
**************************************/
TDBB tdbb;
DBB dbb;
SLONG ret_val;
tdbb = GET_THREAD_DATA;
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;
if ((ret_val = JRN_fini(tdbb->tdbb_status_vector, &dbb->dbb_journal)) !=
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;
WALS WAL_segment;
STATUS status_vector[ISC_STATUS_LENGTH];
SCHAR *curr_name;
SLONG curr_log_partition_offset;
SCHAR *prev_name;
SLONG prev_log_partition_offset;
SLONG log_flags;
SCHAR log_name1[MAXPATHLEN];
SCHAR log_name2[MAXPATHLEN];
SCHAR *temp_name;
SLONG log_seqno;
SLONG log_length;
STR fname;
SCHAR *temp_fname;
SSHORT count;
SSHORT fname_term_length;
LOGF *logf;
dbb = GET_DBB;
if (!dbb->dbb_wal)
return;
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.
*/
if ((count = WAL_segment->wals_max_logfiles) > 0) {
while (--count >= 0) {
logf = LOGF_INFO(count);
if (logf->logf_flags & LOGF_RAW)
continue;
temp_fname = LOGF_NAME(logf);
fname_term_length = strlen(temp_fname) + 1;
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 */
curr_name = log_name1;
prev_name = log_name2;
strcpy(curr_name, WAL_segment->wals_logname);
curr_log_partition_offset = WAL_segment->wals_log_partition_offset;
WALC_release(dbb->dbb_wal);
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)) {
fname_term_length = strlen(curr_name) + 1;
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);
}
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;
temp_name = prev_name;
prev_name = curr_name;
curr_name = temp_name;
curr_log_partition_offset = prev_log_partition_offset;
}
}
void AIL_init(
TEXT * filename,
SSHORT file_len,
WIN * in_win, USHORT 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.
**************************************/
TDBB tdbb;
DBB dbb;
WIN *win, window;
LIP logp;
TEXT dbname[MAXPATHLEN];
/* null out the sparse bit map */
*sbm_rec = (SBM) 0;
tdbb = GET_THREAD_DATA;
dbb = tdbb->tdbb_database;
/* Get WAL file name */
dbb->dbb_wal = 0;
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.
*/
if (in_win) {
win = in_win;
win->win_flags = 0;
logp = (LIP) win->win_buffer;
}
else {
window.win_page = LOG_PAGE;
window.win_flags = 0;
logp = (LIP) 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(LIP 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.
**************************************/
UCHAR *p;
SLONG len;
/* First zero out the whole structure except for the page header portion */
len = sizeof(struct log_info_page) - sizeof(struct pag);
p = (UCHAR *) logp + sizeof(struct 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(void)
{
/**************************************
*
* 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.
**************************************/
WIN window;
JRNDH journal;
TDBB tdbb = GET_THREAD_DATA;
DBB dbb = tdbb->tdbb_database;
window.win_page = HEADER_PAGE;
window.win_flags = 0;
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;
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,
STATUS * status,
JRNH * header,
USHORT h_length,
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,
(UCHAR *) header,
h_length,
data,
d_length,
reinterpret_cast < long *>(seqno),
reinterpret_cast < long *>(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.
**************************************/
TDBB tdbb;
DBB dbb;
WIN window;
LIP logp;
CP cp1;
UCHAR *p;
SCHAR rwal[MAXPATHLEN];
SLONG seqno, offset;
tdbb = GET_THREAD_DATA;
dbb = tdbb->tdbb_database;
/* Flush the records on WAL buffer before trying to recover the
* page
*/
WAL_flush(tdbb->tdbb_status_vector, dbb->dbb_wal, &seqno, &offset, FALSE);
window.win_page = LOG_PAGE;
window.win_flags = 0;
logp = (LIP) 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;
}
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;
for (p = logp->log_data; (*p != LOG_ctrl_file1); p += 2 + p[1]);
MOVE_FAST((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.
*
**************************************/
WIN window;
TDBB tdbb;
DBB dbb;
tdbb = GET_THREAD_DATA;
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;
window.win_page = LOG_PAGE;
window.win_flags = 0;
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, SSHORT force_archive)
{
/**************************************
*
* A I L _ s h u t d o w n
*
**************************************
*
* Functional description
* Shutdown wal subsystem after a flush.
*
**************************************/
TDBB tdbb;
DBB dbb;
tdbb = GET_THREAD_DATA;
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(
TEXT * walname,
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.
*
**************************************/
LIP logp;
WIN window;
UCHAR *p1, *p2, *p3, *q, *p;
USHORT len;
TDBB tdbb;
tdbb = GET_THREAD_DATA;
window.win_page = LOG_PAGE;
window.win_flags = 0;
logp = (LIP) CCH_FETCH(tdbb, &window, LCK_write, pag_log);
CCH_MARK_MUST_WRITE(tdbb, &window);
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++;
q = reinterpret_cast < UCHAR * >(walname);
if ( (len = w_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 < UCHAR * >(walname);
if ( (len = w_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);
}
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;
TEXT journal_dir[MAXPATHLEN];
USHORT jd_len, d_len;
UCHAR data[MAXPATHLEN], *p;
/* Get journal information, if any */
PAG_get_clump(HEADER_PAGE, HDR_backup_info, &d_len, data);
PAG_get_clump(HEADER_PAGE, HDR_journal_server, &jd_len,
reinterpret_cast < UCHAR * >(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;
}
static void delete_log_files(
TEXT * latest_logname,
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).
*
**************************************/
TDBB tdbb;
DBB dbb;
STR fname;
STATUS local_status[ISC_STATUS_LENGTH];
tdbb = GET_THREAD_DATA;
dbb = tdbb->tdbb_database;
while (stack) {
fname = (STR) LLS_POP(&stack);
if (unlink(reinterpret_cast < const char *>(fname->str_data))) {
IBERR_build_status(local_status, isc_io_error,
gds_arg_string, "unlink",
gds_arg_string, fname->str_data,
isc_arg_gds, isc_io_delete_err, 0);
gds__log_status(dbb->dbb_file->fil_string, local_status);
}
}
}
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
*
**************************************/
USHORT len;
WIN window;
LIP logp;
TDBB tdbb;
tdbb = GET_THREAD_DATA;
window.win_page = LOG_PAGE;
window.win_flags = 0;
logp = (LIP) CCH_FETCH(tdbb, &window, LCK_read, pag_log);
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;
}
static void initialize_wal(
TDBB tdbb,
TEXT * dbname,
WIN * window,
LIP logp,
SSHORT release,
USHORT activate_shadow,
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;
}
}
static void process_log_updater(LIP 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.
*
**************************************/
SSHORT state;
TDBB tdbb;
tdbb = GET_THREAD_DATA;
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,
TEXT * dbname,
WIN * window,
LIP * logpp,
SSHORT release,
USHORT 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.
*
**************************************/
TEXT root_db[MAXPATHLEN], rwal[MAXPATHLEN];
CP cp1;
LIP logp;
UCHAR *p;
WIN win;
HDR hdr;
SET_TDBB(tdbb);
logp = *logpp;
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;
for (p = logp->log_data; (*p != LOG_ctrl_file1); p += 2 + p[1]);
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_page = HEADER_PAGE;
win.win_flags = 0;
hdr = (HDR) CCH_FETCH(tdbb, &win, LCK_read, pag_header);
for (p = hdr->hdr_data; (*p != HDR_root_file_name); p += 2 + p[1]);
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 = (LIP) 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, LIP 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.
*
**************************************/
SSHORT len;
UCHAR *p;
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);
len = strlen(walname);
logp->log_file.cp_fn_length = len;
for (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;
}
} // extern "C"