/*
* 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.logrecord;
import org.apache.log4j.Logger;
import org.chaidb.db.Db;
import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.helper.ByteTool;
import org.chaidb.db.index.btree.BTree;
import org.chaidb.db.index.btree.bufmgr.PageBufferManager;
import org.chaidb.db.log.LogManager;
import org.chaidb.db.log.LogRecord;
import org.chaidb.db.log.Lsn;
/**
* BTREE-spec: used to log the modification of BTreeSpec.
* <p/>
* pgno: the page modified.
* lsn: the page's original lsn.
* orig: the original data.
* new: the replacement data.
* duplicate: the prefix of the replacement that matches the original.
*/
public class BTreeSpecLogRecord extends LogRecord {
private static PageBufferManager bpm = PageBufferManager.getInstance();
private static final Logger logger = Logger.getLogger(BTreeSpecLogRecord.class);
private int treeId;
private int oldPageNum;
private int newPageNum;
private byte flag; //replace data flag of BTreeSpec
private byte[] docIdArr = null; //added by marriane 2002-1-31 for one doc one btree
// private byte[] bUdiKey = null; //added by marriane 2002-1-31 for one doc one btree
private int udiKeyType = 0;
// private Key udiKey = null;
/* Appended by Arefool at 2003/3/25 for support Hyper Btree*/
private short btreeType;
// private static final int UDI_KEY_LEN_SIZE = 4;
private static final int FLAG_SIZE = 1; //1 bytes
private static final int BTREE_TYPE_SIZE = 1; //1 byte
//public static final byte RECORD_COUNT_FLAG = 1 ;
public static final byte PAGE_NUMBER_FLAG = 1;
public static final byte ROOT_PAGE_NUMBER_FLAG = 2;
public static final byte LATEST_DATA_PAGE_NUMBER_FLAG = 3;
public static final byte DOC_ROOT_PAGE_NUMBER_FLAG = 4;
/* Appended by ben at Aug, 06, 2002 for supporting flexible node size */
public static final byte NODE_SIZE_FLAG = 5;
public static final byte LAYER_FLAG = 6;
/* Appended by ben zhang at aug, 06, 2002 for supporting key type. */
public static final byte KEY_TYPE_FLAG = 7;
/**
* Default Constructor
*/
public BTreeSpecLogRecord() {
setType(LOG_BTREE_SPEC);
}
/**
* Constructor
*
* @param treeId
* @param oldPageNum
* @param newPageNum
* @param flag
* @param txnId
*/
public BTreeSpecLogRecord(int treeId, int oldPageNum, int newPageNum, byte flag, int txnId, short btreeType) {
super();
super.setTxnId(txnId);
super.setType(LOG_BTREE_SPEC);
this.treeId = treeId;
this.oldPageNum = oldPageNum;
this.newPageNum = newPageNum;
this.flag = flag;
this.docIdArr = null;
// this.udiKey = null;
this.btreeType = btreeType;
}
/**
* Constructor
*
* @param treeId
* @param oldPageNum
* @param newPageNum
* @param flag
* @param txnId
* @param docid
*/
public BTreeSpecLogRecord(int treeId, int oldPageNum, int newPageNum, byte flag, int txnId, int docid, short btreeType) {
super();
super.setTxnId(txnId);
super.setType(LOG_BTREE_SPEC);
this.treeId = treeId;
this.oldPageNum = oldPageNum;
this.newPageNum = newPageNum;
this.flag = flag;
byte[] docId = ByteTool.intToBytes(docid);
docId = ByteTool.subByteArray(docId, docId.length - BTree.DOCID_SIZE, BTree.DOCID_SIZE);
this.docIdArr = docId;
this.btreeType = btreeType;
}
/**
* get flag of current BTreeSpecLogRecord Object
*
* @return int flag
*/
public byte getFlag() {
return flag;
}
/**
* get docIdArray
* if flag<4,return null
*/
public byte[] getDocIdArray() {
if (docIdArr != null) return ByteTool.copyByteArray(docIdArr, 0, docIdArr.length);
else return null;
}
public short getBtreeType() {
return btreeType;
}
public int getTreeId() {
return treeId;
}
public int getOldPageNum() {
return oldPageNum;
}
public int getNewPageNum() {
return newPageNum;
}
public int getUdiKeyType() {
return udiKeyType;
}
/**
* user interface to add a log record and put it to buffer pool
*/
public Lsn log() throws ChaiDBException {
super.log();
LogManager logMgr = Db.getLogManager();
Lsn newLsn = logMgr.put(this, LogManager.LOG_DATA);
return newLsn;
}
/**
* converts a byte array into a log record instance
*/
public boolean read(byte[] bArr, int start) throws ChaiDBException {
/* construct a new LogRecord instance */
super.read(bArr, start);
/* get the values of BTreeSpecLogRecord Object */
int step = start + super.getRecordLength();
treeId = ByteTool.bytesToInt(bArr, step, msbFirst);
step += BTreeLogRecord.TREEID_SIZE;
oldPageNum = ByteTool.bytesToInt(bArr, step, msbFirst);
step += BTreeLogRecord.PAGENUM_SIZE;
newPageNum = ByteTool.bytesToInt(bArr, step, msbFirst);
step += BTreeLogRecord.PAGENUM_SIZE;
flag = bArr[step];
step += FLAG_SIZE;
btreeType = (short) bArr[step];
step += BTREE_TYPE_SIZE;
if (flag == DOC_ROOT_PAGE_NUMBER_FLAG || flag == LATEST_DATA_PAGE_NUMBER_FLAG) {
docIdArr = new byte[BTree.DOCID_SIZE];
System.arraycopy(bArr, step, docIdArr, 0, BTree.DOCID_SIZE);
}
return true;
}
/**
* redo
*
* @return boolean true|false
*/
protected boolean doRedo() throws ChaiDBException {
PageBufferManager bp = bpm;
return bp.updateBTreeSpec(treeId, newPageNum, flag, new Integer(super.getTxnId()), docIdArr, this.btreeType);
}
/**
* undo
*
* @return boolean true|false
*/
protected boolean doUndo() throws ChaiDBException {
/* needn't do undo while flag is PAGE_NUMBER_FLAG or LATEST_DATA_PAGE_NUMBER_FLAG,
because the first free page number or latest datapage number still is
the value of replaceData while the page is added to freelist */
if (flag == PAGE_NUMBER_FLAG || flag == LATEST_DATA_PAGE_NUMBER_FLAG || flag == LAYER_FLAG || //by leon
flag == KEY_TYPE_FLAG || //by ben zhang
/* needn't do undo while flag is NODE_SIZE_FLAG, because
it is not meaning. ?!*/
flag == NODE_SIZE_FLAG) //appended by ben at Aug, 06, 2002
return false;
PageBufferManager bp = bpm;
return bp.updateBTreeSpec(treeId, oldPageNum, flag, new Integer(super.getTxnId()), docIdArr, this.btreeType);
}
/**
* redo or undo the operation for recovery
*
* @param flag REDO or UNDO.
* @return
* @throws ChaiDBException
*/
public boolean recover(short flag) throws ChaiDBException {
/* To see updates have been written to disk already.
* if REDO and lsn>pageLsn or UNDO and lsn<pageLsn, the operation has
* not been updated to disk and redo/undo is necessary.
*/
if (flag == REDO) {
return doRedo();
} else if (flag == UNDO) {
return doUndo();
}
return true;
}
/**
* print data and help in debugging log files
*/
public void print() throws ChaiDBException {
//logger.debug("begin: printing the information of BTreeSpecLogRecord object......");
super.print();
logger.debug("treeId:" + treeId);
/* Modified by ben at Aug, 06, 2002 */
if (flag != 5 && flag != 6 && flag != 7) {
logger.debug("oldPageNum:" + oldPageNum);
logger.debug("newPageNum:" + newPageNum);
} else if (flag == 5) {
logger.debug("old node size:" + oldPageNum);
logger.debug("new node size:" + newPageNum);
} else if (flag == 6) {
logger.debug("old layer size:" + oldPageNum);
logger.debug("new layer size:" + newPageNum);
} else if (flag == 7) {
logger.debug("old key size:" + oldPageNum);
logger.debug("new key size:" + newPageNum);
}
if (flag == 1) {
logger.debug("flag:1 VALID_FREE_PAGE_NUM");
} else if (flag == 2) {
logger.debug("flag:2 ROOT_PAGE_NUM");
} else if (flag == 3) {
logger.debug("flag:3 LATEST_DATAPAGE_NUM");
} else if (flag == 4) {
logger.debug("flag:4 DOC_ROOT_PAGE_NUM");
} else if (flag == 5) {/* APPENDED by ben at Aug, 06, 2002 for supporting flexible node size. */
logger.debug("flag:5 NODE_SIZE");
} else if (flag == 6) {
logger.debug("flag:6 LAYER");
} else if (flag == 7) {
logger.debug("flag:7 KEY_SIZE");
} else if (flag == 8) {
logger.debug("flag:8 UDI_KEY_PAGE_NUMBER");
}
if (flag == LATEST_DATA_PAGE_NUMBER_FLAG) {
byte[] tmp = new byte[4];
System.arraycopy(docIdArr, 0, tmp, tmp.length - BTree.DOCID_SIZE, BTree.DOCID_SIZE);
logger.debug("docid=" + ByteTool.bytesToInt(tmp, 0, LogRecord.msbFirst));
}
//logger.debug("end: printing the information of BTreeSpecLogRecord object.");
}
/**
* converts a log record instance into a byte array.
* The byte array has the following format:
* -------------------------------------------------------------------------------
* | hdr | type | txn | prevLsn | BTreeLogRecord length | offset | prefix | suffix|
* -------------------------------------------------------------------------------
* | orgSize | orgData | replSize | replData | flag
* -------------------------------------------------
* hdr: 8 bytes,the byte array of the header,generated by Hdr.toBytes().
* type: 1 bytes.
* txnId: 4 bytes.
* prevLsn: 8 bytes,the byte array of the header of lsn,generated by Lsn.toBytes().
* <p/>
* fileNameLen 2 bytes
* fileName fileNameLen bytes
* oldPageNum 4 bytes
* newPageNum 4 bytes
* flag: 1 byte
* docIdArr: 3 bytes if flag is more than 3.
*/
public void toBytes(byte[] byteArray, int start) throws ChaiDBException {
super.toBytes(byteArray, start);
int step = start + super.getRecordLength();
ByteTool.intToBytes(byteArray, step, treeId, msbFirst);
step += BTreeLogRecord.TREEID_SIZE;
ByteTool.intToBytes(byteArray, step, oldPageNum, msbFirst);
step += BTreeLogRecord.PAGENUM_SIZE;
ByteTool.intToBytes(byteArray, step, newPageNum, msbFirst);
step += BTreeLogRecord.PAGENUM_SIZE;
byteArray[step] = flag;
step += FLAG_SIZE;
byteArray[step] = (byte) btreeType;
step += BTREE_TYPE_SIZE;
if (flag == DOC_ROOT_PAGE_NUMBER_FLAG || flag == LATEST_DATA_PAGE_NUMBER_FLAG) {
System.arraycopy(docIdArr, 0, byteArray, step, BTree.DOCID_SIZE);
step += BTree.DOCID_SIZE;
}
}
/**
* get current log record type total length
*
* @return int total lenth
*/
public int getRecordLength() {
int len = super.getRecordLength() + BTreeLogRecord.TREEID_SIZE + BTreeLogRecord.PAGENUM_SIZE * 2 + FLAG_SIZE + BTREE_TYPE_SIZE;
if (flag == DOC_ROOT_PAGE_NUMBER_FLAG || flag == LATEST_DATA_PAGE_NUMBER_FLAG) {
len += BTree.DOCID_SIZE;
}
return len;
}
}