mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-27 20:03:03 +01:00
1653 lines
38 KiB
C++
1653 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/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/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
|
|
|
|
|
|
extern "C" {
|
|
|
|
#ifdef NOT_USED_OR_REPLACED
|
|
static void build_wal_param(UCHAR *, LGFILE **, SLONG, LGFILE *, SLONG *);
|
|
#endif
|
|
static void delete_log_files(TEXT *, SLONG, LLS);
|
|
#ifdef NOT_USED_OR_REPLACED
|
|
static BOOLEAN get_walinfo(TEXT *);
|
|
#endif
|
|
static void initialize_wal(TDBB, TEXT *, WIN *, LIP, SSHORT, USHORT, SBM *);
|
|
#ifdef NOT_USED_OR_REPLACED
|
|
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 *);
|
|
#endif
|
|
|
|
#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(
|
|
ISC_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;
|
|
ISC_STATUS_ARRAY status_vector;
|
|
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(log_info_page) - sizeof(pag);
|
|
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(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,
|
|
ISC_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);
|
|
}
|
|
|
|
#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;
|
|
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 * >(¶m1))) {
|
|
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 * >(¶m2))) {
|
|
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 * >(¶m2))) {
|
|
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 * >(¶m1))) {
|
|
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(
|
|
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;
|
|
ISC_STATUS_ARRAY local_status;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
#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
|
|
*
|
|
**************************************/
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
#ifdef NOT_USED_OR_REPLACED
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
} // extern "C"
|