/*
* Copyright (C) 2006 http://www.chaidb.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package org.chaidb.db.log;
import org.apache.log4j.Logger;
import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.exception.ErrorCode;
import org.chaidb.db.helper.ByteTool;
import org.chaidb.db.transaction.Transaction;
import java.io.Serializable;
/**
* This class presents a log records
*/
public abstract class LogRecord implements Serializable {
private static final Logger logger = Logger.getLogger(LogRecord.class);
/* record header */
protected Hdr header;
/**
* type for this log record
*/
protected byte type;
/**
* current log records's transaction object
*/
protected int txnId;
/**
* Lsn of the previous log record of this txn
*/
protected Lsn prevLsn;
protected static boolean msbFirst = LogManager.msbFirst;
static int LOG_RECORD_TYPE_SIZE = 1; //1 bytes
static int LOG_RECORD_TXNID_SIZE = Transaction.TXN_ID_SIZE;//4 bytes
public static final short REDO = 1;
public static final short UNDO = 0;
/**
* Default Constructor
*/
protected LogRecord() {
header = new Hdr();
prevLsn = new Lsn();
}
public static final byte MIN_LOG_RECORD_TYPE = 1;
//===>>> Constant identifiers for log record types <<<<<=====
//public static final int LOG_REGISTER = 0;/* Identifier for LogRegisterLogRecord */
public static final byte LOG_TXN_REGOP = MIN_LOG_RECORD_TYPE;/* Identifier for TxnRegopLogRecord */
public static final byte LOG_TXN_CHILD = 2;/* Identifier for TxnChildLogRecord */
public static final byte LOG_TXN_CHECKPOINT = 3;/* Identifier for TxnCkpLogRecord */
public static final byte LOG_BTREE_REPLACE = 4;/* Identifier for BtreeReplLogRecord */
public static final byte LOG_BTREE_ADDREMOVE = 5;/* Identifier for BtreeAddRemoveLogRecord */
public static final byte LOG_BTREE_MOVE = 6;/* Identifier for BtreeMoveLogRecord */
public static final byte LOG_BTREE_NEW_PAGE = 7;/* Identifier for BtreeNewPageLogRecord */
public static final byte LOG_BTREE_FREE_PAGE = 8;/* Identifier for BtreeFreePageLogRecord */
public static final byte LOG_BTREE_SPLIT_PAGE = 9;/* Identifier for BtreeSplitPageLogRecord */
public static final byte LOG_BTREE_MERGE_PAGE = 10;/* Identifier for BtreeMergePageLogRecord */
public static final byte LOG_BTREE_FREE_OVERFLOWPAGE = 11;/* Identifier for BtreeFreeOverflowPageLogRecord */
public static final byte LOG_BTREE_SPEC = 12;/* Identifier for BtreeSpecLogRecord */
public static final byte LOG_BTREE_INSERT_NODE = 13;/* BTreeInsertNodeLogRecord */
public static final byte LOG_TXN_FUZZY_CHECKPOINT = 14;/* Identifier for TxnFuzzyCkpLogRecord */
public static final byte LOG_FILE_OPERATION = 15;/* Identifier for FileOperationLogRecord */
//add by leon
public static final byte LOG_BTREE_INSERT_LEAF_NODE = 16;/* BTreeInsertLeafNodeLogRecord */
//added by marriane 2003-11-10
public static final byte LOG_DELETE_FILES = 17; /* FilesDeleteLogRecord */
public static final byte LOG_FREE_PAGE_WITH_DATA = 18; /* BTreeFreePageWithDataLogRecord */
public static final byte MAX_LOG_RECORD_TYPE = LOG_FREE_PAGE_WITH_DATA;
protected String[] types = {"0:LOG_REGISTER", "1:LOG_TXN_REGOP", "2:LOG_TXN_CHILD", "3:LOG_TXN_CHECKPOINT", "4:LOG_BTREE_REPLACE", "5:LOG_BTREE_ADDREMOVE", "6:LOG_BTREE_MOVE", "7:LOG_BTREE_NEW_PAGE", "8:LOG_BTREE_FREE_PAGE", "9:LOG_BTREE_SPLIT_PAGE", "10:LOG_BTREE_MERGE_PAGE", "11:LOG_BTREE_FREE_OVERFLOWPAGE", "12:LOG_BTREE_SPEC", "13:LOG_BTREE_INSERT_NODE", "14:LOG_TXN_FUZZY_CHECKPOINT", "15:LOG_FILE_OPERATION", "16:LOG_BTREE_INSERT_LEAF_NODE", "17:LOG_DELETE_FILES", "18:BTreeFreePageWithDataLogRecord"};
/**
* set new header to current LogRecord
*
* @param newHeader
*/
public void setHeader(Hdr newHeader) {
this.header = newHeader;
}
/**
* set new Lsn to current LogRecord
*
* @param newLsn
*/
public void setPrevLsn(Lsn newLsn) {
this.prevLsn = newLsn;
}
/**
* set new type to current LogRecord
*
* @param newType
*/
public void setType(byte newType) {
this.type = newType;
}
/**
* set new transaction id to current LogRecord
*
* @param newTxnId
*/
public void setTxnId(int newTxnId) {
this.txnId = newTxnId;
}
/**
* get header of current LogRecord
*
* @return Hdr header
*/
public Hdr getHeader() {
return header;
}
/**
* get Lsn of current LogRecord
*
* @return Lsn lsn
*/
public Lsn getPrevLsn() {
return prevLsn;
}
/**
* get type of current LogRecord
*
* @return int type
*/
public byte getType() {
return type;
}
/**
* get current LogRecord's transaction object
*
* @return DbTxn txn
*/
public int getTxnId() {
return txnId;
}
/**
* converts first 28 byte array into LogRecord instance
*/
public boolean read(byte[] bArr, int start) throws ChaiDBException {
if (bArr == null) {
throw new ChaiDBException(ErrorCode.LOG_BYTE_ARRAY_IS_NULL);
}
/* get the values of Hdr object */
if (!header.read(bArr, start)) {
throw new ChaiDBException(ErrorCode.LOG_RECORD_HEADER_CANNOT_READ);
}
int logLen = header.getLength();
if (logLen == 0 || logLen > LogManagerImpl.READ_LOG_CHUNK_SIZE) {
throw new ChaiDBException(ErrorCode.INVALID_LOG_RECORD, "invalid log record length");
}
/* get the values of LogRecord object */
int hdrLen = Hdr.getHdrLength();
int step = start + hdrLen;
type = bArr[step];
step = step + LOG_RECORD_TYPE_SIZE;
txnId = ByteTool.bytesToInt(bArr, step, msbFirst);
step = step + LOG_RECORD_TXNID_SIZE;
prevLsn.read(bArr, step);
return true;
}
/**
* user interface to add a log record and put it to buffer pool
*/
protected Lsn log() throws ChaiDBException {
getHeader().setLength((short) getRecordLength());
return null;
}
/**
* redo or undo the operation for recovery
*
* @param flag REDO or UNDO.
*/
public abstract boolean recover(short flag) throws ChaiDBException;
/**
* helps in debugging log files
*/
public void print() throws ChaiDBException {
logger.debug("");
logger.debug("Hdr[ prev_Off:0x" + Integer.toHexString(this.getHeader().getPrevOffset()));
logger.debug(" len(Rec):" + this.getHeader().getLength() + " ]");
logger.debug("Rec[ type:" + types[type]);
logger.debug(" txnid:0x" + Integer.toHexString(this.getTxnId()));
logger.debug(" prev_Lsn:" + this.getPrevLsn().toHexString() + " ]");
}
/**
* converts a log record instance into a byte array.
* The byte array has the following format:
* -----------------------------------------------------------------
* | hdr | type | txn | prevLsn | detail Data of different type |
* -----------------------------------------------------------------
* hdr: 12 bytes,the byte array of the header,generated by Hdr.toBytes().
* type: 4 bytes.
* txnid: 4 bytes.
* prevLsn: 8 bytes,the byte array of the header of lsn,generated by Lsn.toBytes().
* Data: data byte array.
* <p/>
* Subclass LogRecord should know their own fields and can convert
* /restore them to/from byte array.
*/
public void toBytes(byte[] destArray, int start) throws ChaiDBException {
int step = start;
header.toBytes(destArray, step);
step += Hdr.getHdrLength();
destArray[step] = type;
step += LOG_RECORD_TYPE_SIZE;
ByteTool.intToBytes(destArray, step, txnId, msbFirst);
step += LOG_RECORD_TXNID_SIZE;
prevLsn.toBytes(destArray, step);
}
/**
* get LogRecord length
*
* @return int total length of LogRecord
*/
public int getRecordLength() {
return Hdr.getHdrLength() + LOG_RECORD_TYPE_SIZE + LOG_RECORD_TXNID_SIZE + Lsn.getLsnLength();
}
}