/*
* 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.bufmgr.PageBufferManager;
import org.chaidb.db.log.LogManager;
import org.chaidb.db.log.LogRecord;
import org.chaidb.db.log.Lsn;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
/**
* Generic log record of all btree operations.
* Fields:
* fileName: String btree file name
* fileId: int sequent file number with the same filename
* pageNum: int page no in a file
*/
public abstract class BTreeLogRecord extends LogRecord {
private static final Logger logger = Logger.getLogger(BTreeInsertNodeLogRecord.class);
/**
* btree file name's hashcode
*/
protected int treeId;
/**
* page no in a file
*/
private int pageNum;
private static PageBufferManager bpm = PageBufferManager.getInstance();
static final byte TREEID_SIZE = 4; //4 bytes
static final byte PAGENUM_SIZE = 4; //4 bytes
//leon
boolean DEBUG = false;
//add on Dec 4th,2002. Hold locks in redo/undo acquired by BufferedPage.getPage
ArrayList locks;
private short btreeType;
private static final int BTREE_TYPE_SIZE = 1; //1 byte
public static final byte FLAG_DATA = 0;
public static final byte FLAG_BTREE = 1;
public static final byte FLAG_OVERFLOW = 4;
/**
* Default Constructor
*/
protected BTreeLogRecord() {
super();
}
/**
* Constructor
*
* @param treeId
* @param newPageNum
* @param newTxnId
*/
protected BTreeLogRecord(int treeId, int newPageNum, int newTxnId, short newBtreeType) {
super();
this.treeId = treeId;
pageNum = newPageNum;
btreeType = newBtreeType;
super.setTxnId(newTxnId);
}
protected boolean isBTreePage(int pageFlag) {
return (pageFlag == 1 || pageFlag == 2);
}
protected boolean isDataPage(int pageFlag) {
return (pageFlag == 0);
}
/**
* get pageNum of current BTreeLogRecord Object
*
* @return int pageNum
*/
public int getPageNum() {
return pageNum;
}
public short getBtreeType() {
return btreeType;
}
public int getTreeId() {
return treeId;
}
/**
* Get a page specified by this log record.
*
* @return page,if sucess. Otherwise, null.
*/
protected byte[] getPage(int newPageNum, boolean redo) throws ChaiDBException {
PageBufferManager bp = bpm;
byte[] page = bp.getPage(super.getTxnId(), treeId, btreeType, newPageNum, redo, locks);
return page;
}
/**
* Release a page got by the getPage
*/
protected void releasePage(int newPageNum, boolean newDirty) {
bpm.releasePage(treeId, newPageNum, newDirty);
}
/**
* user interface to add a log record and put it to buffer pool
*/
public Lsn log() throws ChaiDBException {
super.log();
Lsn newLsn = Db.getLogManager().put(this, LogManager.LOG_DATA);
return newLsn;
}
/**
* redo or undo the operation for recovery
*
* @param flag REDO or UNDO.
*/
public boolean recover(short flag) throws ChaiDBException {
boolean dirty = false;
byte[] page = null;
try {
/* must get the page while UNDO free (overflow) page */
if (type == LOG_BTREE_FREE_PAGE || type == LOG_BTREE_FREE_OVERFLOWPAGE || type == LOG_FREE_PAGE_WITH_DATA || flag == REDO) {
page = getPage(pageNum, true);
} else {
page = getPage(pageNum, false);
}
//page ==null when and only when flag==UNDO. Then we need do nothing
if (page == null && flag == UNDO) {
return true;
}
if (flag == REDO) {
dirty = doRedo(page);
} else {
dirty = doUndo(page);
}
/*if (DEBUG && (pageNum==0x1a66))
recordPage(prevLsn,pageNum,page);
*/
return true;
} catch (ChaiDBException e) {
logger.debug(e);
/* if BTree's filename's path doesn't exist, it'll produce
ChaiDBException */
return false;
} finally {
// releaseOnHoldLocks();
/* added freelist page needn't releasePage if it has been done indeed!
Otherwise, we must release it to free the location it occupies in pagepool.
*/
if (!(dirty && ((flag == UNDO && (type == LOG_BTREE_NEW_PAGE)) || (flag == REDO && (type == LOG_BTREE_FREE_PAGE)) || (flag == REDO && (type == LOG_BTREE_FREE_OVERFLOWPAGE)) || (flag == REDO && (type == LOG_FREE_PAGE_WITH_DATA))))) {
releasePage(pageNum, dirty);
}
}
}
// private void releaseOnHoldLocks() throws ChaiDBException {
// for (int i = 0; i < locks.size(); i++) {
// Lock lock = (Lock) locks.get(i);
//
// Db.getLockManager().put(lock);
// }
// locks.clear();
// }
protected void printPage(byte[] page, int pg, String fname) {
try {
BufferedWriter ow = new BufferedWriter(new PrintWriter(new FileOutputStream(fname, true)), page.length);
ow.write(" txnID=" + Integer.toHexString(txnId) + " type=" + types[type]);
ow.newLine();
String s = ByteTool.toHexString(0, page, 0, page.length);
ow.write(s, 0, s.length());
ow.newLine();
ow.flush();
ow.close();
} catch (Exception e) {
logger.debug(e);
}
}
void recordPage(Lsn lsn, int p, byte page[]) {
String dir = "D:\\working\\sampledb\\ChaiDB\\datalog\\page\\";
try {
BufferedWriter out = new BufferedWriter(new PrintWriter(new FileOutputStream(dir + Integer.toHexString(p), true)));
out.write("prevlsn=" + lsn.toHexString() + "\ttxn=" + Integer.toHexString(txnId) + "\t" + types[type] + "\n\r" + ByteTool.toHexString(2, page, 0, page.length) + "\n\r");
out.close();
} catch (Exception e) {
logger.debug(e);
}
}
/**
* 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 BTreeLogRecord Object */
int step = start + super.getRecordLength();
btreeType = (short) bArr[step];
step += BTREE_TYPE_SIZE;
treeId = ByteTool.bytesToInt(bArr, step, msbFirst);
step += TREEID_SIZE;
pageNum = ByteTool.bytesToInt(bArr, step, msbFirst);
locks = new ArrayList();
return true;
}
/**
* Redo.
*
* @return true if this page is dirty. Otherwise, false
*/
protected boolean doRedo(byte[] page) throws ChaiDBException {
return true;
}
;
/**
* Undo.
*
* @return true if this page is dirty. Otherwise, false
*/
protected boolean doUndo(byte[] page) throws ChaiDBException {
return true;
}
;
/**
* print data and help in debugging log files
*/
public void print() throws ChaiDBException {
super.print();
logger.debug("[btreeType]:" + btreeType);
logger.debug("treeId:" + treeId);
logger.debug("[pgno]:" + "0x" + Integer.toHexString(pageNum));
}
/**
* converts a log record instance into a byte array.
* The byte array has the following format:
* fileNameHashCode : 4 bytes
* fileId: 4 bytes.
* pageNum: 8 bytes.
*/
public void toBytes(byte[] byteArray, int start) throws ChaiDBException {
super.toBytes(byteArray, start);
int step = start + super.getRecordLength();
byteArray[step] = (byte) btreeType;
step += BTREE_TYPE_SIZE;
ByteTool.intToBytes(byteArray, step, treeId, msbFirst);
step += TREEID_SIZE;
ByteTool.intToBytes(byteArray, step, pageNum, msbFirst);
}
/**
* get current log record type total length
*
* @return int total lenth
*/
public int getRecordLength() {
return super.getRecordLength() + TREEID_SIZE + PAGENUM_SIZE + BTREE_TYPE_SIZE;
}
}