/*
* 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.exception.ChaiDBException;
import org.chaidb.db.exception.ErrorCode;
import org.chaidb.db.helper.ByteTool;
import org.chaidb.db.index.btree.BTreeSpec;
public class BTreeInsertLeafNodeLogRecord extends BTreeLogRecord {
private static final Logger logger = Logger.getLogger(BTreeInsertLeafNodeLogRecord.class);
/* old upbound and lowbound on BTreePage */
private short oldLowBound; //2bytes
private short oldUpBound; //2bytes
/* new upbound and lowbound on BTreePage */
private short newLowBound; //2bytes
private short newUpBound; //2bytes
/* head offset location on BTreePage */
private short headOffset; //2bytes
/* insert node offset value on BTreePage */
private short nodeOffset; //2bytes
/* head move index byte array length on BTreePage */
private short headMoveIndexArrLen; //2bytes
/* head move index byte array on BTreePage */
private byte[] headMoveIndexArr; //headMoveIndexArrLen
private short compressedHeadMoveIndexArrLen;
private byte[] compressedHeadMoveIndexArr;
/* BTreeNode value length on BTreePage */
private short nodeValueLen; //2bytes
/* BTreeNode value on BTreePage */
private byte[] nodeValue; //nodeValueLen bytes
/* DataPage PageNumber */
private int dataPageNum;
/* old upbound on DataPage */
private short dataPageOldUpBound; //2bytes
/* old lowbound on DataPage */
private short dataPageNewUpBound; //2bytes
/* DataNode value length on DataPage */
private short dataNodeValueLen; //2bytes
/* DataNode value on DataPage */
private byte[] dataNodeValue; //dataNodeValueLen bytes
/* Node size */
private short nodeSize; //2bytes
private static final int OLD_LOWBOUND_SIZE = 2; //2 bytes
private static final int OLD_UPBOUND_SIZE = 2; //2 bytes
private static final int NEW_LOWBOUND_SIZE = 2; //2 bytes
private static final int NEW_UPBOUND_SIZE = 2; //2 bytes
private static final int HEAD_OFFSET_SIZE = 2; //2 bytes
private static final int NODE_OFFSET_SIZE = 2; //2 bytes
private static final int HEAD_MOVE_INDEX_LENGTH_SIZE = 2; //2 bytes
private static final int NODE_VALUE_LENGTH_SIZE = 2; //2 bytes
private static final int NODE_SIZE = 2; //2 bytes
/**
* default Constructor
*/
public BTreeInsertLeafNodeLogRecord() {
super();
super.setType(LOG_BTREE_INSERT_LEAF_NODE);
}
public BTreeInsertLeafNodeLogRecord(int treeId, int newPageNum, int newTxnId, short oldLowBound, short oldUpBound, short newLowBound, short newUpBound, short headOffset, short nodeOffset, byte[] headMoveOffsetArr, byte[] nodeValue, int dataPageNum, short dataPageOldUpBound, short dataPageNewUpBound, byte[] dataNodeValue, short nodeSize, short btreeType) {
super(treeId, newPageNum, newTxnId, btreeType);
super.setType(LOG_BTREE_INSERT_LEAF_NODE);
this.oldLowBound = oldLowBound;
this.oldUpBound = oldUpBound;
this.newLowBound = newLowBound;
this.newUpBound = newUpBound;
this.headOffset = headOffset;
this.nodeOffset = nodeOffset;
this.nodeSize = nodeSize;
this.headMoveIndexArr = headMoveOffsetArr;
if ((headMoveOffsetArr != null) && (headMoveOffsetArr.length > 0)) {
this.headMoveIndexArrLen = (short) headMoveIndexArr.length;
compressedHeadMoveIndexArr = offset2Location(headMoveIndexArr);
compressedHeadMoveIndexArrLen = (short) compressedHeadMoveIndexArr.length;
} else {
this.headMoveIndexArrLen = 0;
}
if (nodeValue != null) {
this.nodeValueLen = (short) nodeValue.length;
} else {
this.nodeValueLen = 0;
}
this.nodeValue = nodeValue;
this.dataPageNum = dataPageNum;
this.dataPageOldUpBound = dataPageOldUpBound;
this.dataPageNewUpBound = dataPageNewUpBound;
if (dataNodeValue != null) {
this.dataNodeValueLen = (short) dataNodeValue.length;
} else {
this.dataNodeValueLen = 0;
}
this.dataNodeValue = dataNodeValue;
}
public short getOldLowBound() {
return oldLowBound;
}
public short getOldUpBound() {
return oldUpBound;
}
public short getNewLowBound() {
return newLowBound;
}
public short getNewUpBound() {
return newUpBound;
}
public short getHeadOffset() {
return headOffset;
}
public short getNodeOffset() {
return nodeOffset;
}
public byte[] getHeadMoveIndexArr() {
if (headMoveIndexArr != null) {
return ByteTool.copyByteArray(headMoveIndexArr, 0, headMoveIndexArr.length);
}
return null;
}
public int getDataPageNum() {
return dataPageNum;
}
public short getDataPageOldUpBound() {
return dataPageOldUpBound;
}
public short getDataPageNewUpBound() {
return dataPageNewUpBound;
}
public byte[] getDataNodeValue() {
if (dataNodeValue != null) {
return ByteTool.copyByteArray(dataNodeValue, 0, dataNodeValue.length);
}
return null;
}
public byte[] getNodeValue() {
if (nodeValue != null) {
return ByteTool.copyByteArray(nodeValue, 0, nodeValue.length);
}
return null;
}
public short getNodeSize() {
return nodeSize;
}
public short getDataNodeValueLen() {
return dataNodeValueLen;
}
public short getNodeValueLen() {
return nodeValueLen;
}
/**
* return move index length
*
* @return short headMoveIndexArrLen
*/
public short getHeadMoveIndexArrLen() {
return headMoveIndexArrLen;
}
/**
* offset move array in BTreePage are transfered into location array
*
* @param moveOffsetArr
* @return byte[] moveLocArr
*/
private byte[] offset2Location(byte[] moveOffsetArr) {
int len = moveOffsetArr.length;
short pageSize = (short) BTreeSpec.PAGE_SIZE;
byte[] moveLocArr = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
short offset = ByteTool.bytesToShort(moveOffsetArr, i, msbFirst);
byte location = (byte) ((pageSize - offset) / nodeSize);
moveLocArr[i / 2] = location;
}
return moveLocArr;
}
/**
* location move array in BTreePage are transfered into offset array
*
* @param moveLocArr
* @return byte[] moveOffsetArr
*/
private byte[] location2Offset(byte[] moveLocArr) {
int len = moveLocArr.length;
short pageSize = (short) BTreeSpec.PAGE_SIZE;
byte[] moveOffsetArr = new byte[0];
for (int i = 0; i < len; i++) {
short location = ByteTool.unsignedByteToShort(moveLocArr[i]);
short offset = (short) (pageSize - location * nodeSize);
moveOffsetArr = ByteTool.append(moveOffsetArr, ByteTool.shortToBytes(offset));
}
return moveOffsetArr;
}
/**
* 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 BTreeMoveLogRecord Object */
int step = start + super.getRecordLength();
oldLowBound = ByteTool.bytesToShort(bArr, step, msbFirst);
step = step + OLD_LOWBOUND_SIZE;
oldUpBound = ByteTool.bytesToShort(bArr, step, msbFirst);
step = step + OLD_UPBOUND_SIZE;
newLowBound = ByteTool.bytesToShort(bArr, step, msbFirst);
step = step + NEW_LOWBOUND_SIZE;
newUpBound = ByteTool.bytesToShort(bArr, step, msbFirst);
step = step + NEW_UPBOUND_SIZE;
headOffset = ByteTool.bytesToShort(bArr, step, msbFirst);
step = step + HEAD_OFFSET_SIZE;
nodeOffset = ByteTool.bytesToShort(bArr, step, msbFirst);
step = step + NODE_OFFSET_SIZE;
nodeSize = ByteTool.bytesToShort(bArr, step, msbFirst);
step = step + NODE_SIZE;
compressedHeadMoveIndexArrLen = ByteTool.bytesToShort(bArr, step, msbFirst);
step = step + HEAD_MOVE_INDEX_LENGTH_SIZE;
if (compressedHeadMoveIndexArrLen > 0) {
compressedHeadMoveIndexArr = ByteTool.copyByteArray(bArr, step, compressedHeadMoveIndexArrLen);
headMoveIndexArr = location2Offset(compressedHeadMoveIndexArr);
headMoveIndexArrLen = (short) headMoveIndexArr.length;
step = step + compressedHeadMoveIndexArrLen;
}
nodeValueLen = ByteTool.bytesToShort(bArr, step, msbFirst);
step = step + NODE_VALUE_LENGTH_SIZE;
if (nodeValueLen > 0) {
nodeValue = ByteTool.copyByteArray(bArr, step, nodeValueLen);
}
step = step + nodeValueLen;
dataPageNum = ByteTool.bytesToInt(bArr, step, msbFirst);
step = step + BTreeLogRecord.PAGENUM_SIZE;
dataPageOldUpBound = ByteTool.bytesToShort(bArr, step, msbFirst);
step = step + OLD_UPBOUND_SIZE;
dataPageNewUpBound = ByteTool.bytesToShort(bArr, step, msbFirst);
step = step + NEW_UPBOUND_SIZE;
dataNodeValueLen = ByteTool.bytesToShort(bArr, step, msbFirst);
step = step + NODE_VALUE_LENGTH_SIZE;
if (dataNodeValueLen > 0) {
dataNodeValue = ByteTool.copyByteArray(bArr, step, dataNodeValueLen);
}
return true;
}
/**
* do redo function
*
* @param page
* @return boolean true|false
*/
protected boolean doRedo(byte[] page) throws ChaiDBException {
/* redo insert node operation */
try {
/* update lowBound and upBound value */
System.arraycopy(ByteTool.shortToBytes(newLowBound), 0, page, BTreeSpec.OFF_LOWERBOUND, NEW_LOWBOUND_SIZE);
System.arraycopy(ByteTool.shortToBytes(newUpBound), 0, page, BTreeSpec.OFF_UPPERBOUND, NEW_UPBOUND_SIZE);
/* add head offset */
System.arraycopy(ByteTool.shortToBytes(nodeOffset), 0, page, headOffset, NODE_OFFSET_SIZE);
/* move head index if have any to move */
if (headMoveIndexArrLen > 0) {
System.arraycopy(headMoveIndexArr, 0, page, headOffset + HEAD_OFFSET_SIZE, headMoveIndexArrLen);
}
/* add node value */
if (nodeValueLen > 0) {
System.arraycopy(nodeValue, 0, page, nodeOffset, nodeValueLen);
}
byte[] dataPage = getPage(dataPageNum, true);
System.arraycopy(ByteTool.shortToBytes(dataPageNewUpBound), 0, dataPage, BTreeSpec.OFF_UPPERBOUND, NEW_UPBOUND_SIZE);
if (dataNodeValueLen > 0) {
System.arraycopy(dataNodeValue, 0, dataPage, dataPageNewUpBound, dataNodeValueLen);
}
releasePage(dataPageNum, true);
} catch (Exception e) {
logger.debug(e);
throw new ChaiDBException(ErrorCode.LOG_REDO_FAILED, e.toString());
}
return true;
}
/**
* do undo function
*
* @param page
* @return boolean true|false
*/
protected boolean doUndo(byte[] page) throws ChaiDBException {
/* undo insert node operation */
try {
/* reset lowBouand and upBound value as old value */
System.arraycopy(ByteTool.shortToBytes(oldLowBound), 0, page, BTreeSpec.OFF_LOWERBOUND, OLD_LOWBOUND_SIZE);
System.arraycopy(ByteTool.shortToBytes(oldUpBound), 0, page, BTreeSpec.OFF_UPPERBOUND, OLD_UPBOUND_SIZE);
/* delete head offset and move later index to its location */
if (headMoveIndexArrLen > 0) {
System.arraycopy(headMoveIndexArr, 0, page, headOffset, headMoveIndexArrLen);
}
/* delete node value in nodeOffset location ,in fact nothing need to do
after upbound is reset old value
*/
byte[] dataPage = getPage(dataPageNum, true);
System.arraycopy(ByteTool.shortToBytes(dataPageOldUpBound), 0, dataPage, BTreeSpec.OFF_UPPERBOUND, OLD_UPBOUND_SIZE);
releasePage(dataPageNum, true);
/* delete dataNode value in dataNodeOffset location ,in fact
nothing need to do after upbound is reset old value
*/
} catch (Exception e) {
logger.debug(e);
throw new ChaiDBException(ErrorCode.LOG_UNDO_FAILED, e.toString());
}
return true;
}
/**
* print data and help in debugging log files
*/
public void print() throws ChaiDBException {
//logger.log(ServerLog.LOG_DEBUG,"begin: printing the information of BTreeInsertLeafNodeLogRecord object......");
super.print();
logger.debug("old LowBound UpBound: 0x" + Integer.toHexString(oldLowBound) + "_" + Integer.toHexString(oldUpBound));
logger.debug("new LowBound UpBound: 0x" + Integer.toHexString(newLowBound) + "_" + Integer.toHexString(newUpBound));
logger.debug("headOffset_nodeOffset: 0x" + Integer.toHexString(headOffset) + "_" + Integer.toHexString(nodeOffset));
logger.debug("move len:" + headMoveIndexArrLen);
if (headMoveIndexArrLen > 0) {
logger.debug("move index array:" + ByteTool.toHexString(1, headMoveIndexArr, 0, headMoveIndexArr.length));
}
logger.debug("node len:" + nodeValueLen);
if (nodeValueLen > 0) {
logger.debug("node value :" + ByteTool.toHexString(1, nodeValue, 0, nodeValue.length));
}
logger.debug("dataPage pageNumber: 0x" + Integer.toHexString(dataPageNum));
logger.debug("dataPage Old|New UpBound: 0x" + Integer.toHexString(dataPageOldUpBound) + "_" + Integer.toHexString(dataPageNewUpBound));
logger.debug("DataNode len:" + dataNodeValueLen);
if (dataNodeValueLen > 0) {
logger.debug("DataNode value :" + ByteTool.toHexString(1, dataNodeValue, 0, dataNodeValue.length));
}
//logger.log(ServerLog.LOG_DEBUG,"end: printing the information of BTreeInsertLeafNodeLogRecord object.");
}
/**
* converts a log record instance into a byte array.
* The byte array has the following format:
* --------------------------------------------------------------------
* | oldLowBound | oldUpBound | newLowBound | newUpBound |
* --------------------------------------------------------------------
* | headOffset | nodeOffset | btreePageFlag | headMoveIndexArrLen |
* --------------------------------------------------------------------
* | headMoveIndexArr | nodeValueLen | nodeValue | dataPageNum |
* --------------------------------------------------------------------
* | dataPageOldUpBound |dataPageNewUpBound | dataNodeValueLen |
* --------------------------------------------------------------------
* | nodeSize | dataNodeValue | |
* --------------------------------------------------------------------
* oldLowBund: 2bytes
* oldUpBound: 2bytes
* newLowBound: 2bytes
* newUpBound: 2bytes
* headOffset: 2bytes
* nodeOffset: 2bytes
* btreePageFlag: 1byte
* headMoveIndexArrLen: 2bytes
* headMoveIndexArr: headMoveIndexArrLen bytes
* nodeValueLen: 2bytes
* nodeValue: nodeValueLen bytes
* dataPageNum: 4bytes
* dataPageOldUpBund: 2bytes
* dataPageNewUpBound: 2bytes
* dataNodeValueLen: 2bytes
* nodeSize: 2bytes
* dataNodeValue: dataNodeValueLen bytes
*/
public void toBytes(byte[] byteArray, int start) throws ChaiDBException {
super.toBytes(byteArray, start);
int step = start + super.getRecordLength();
ByteTool.shortToBytes(byteArray, step, oldLowBound);
step += OLD_LOWBOUND_SIZE;
ByteTool.shortToBytes(byteArray, step, oldUpBound);
step += OLD_UPBOUND_SIZE;
ByteTool.shortToBytes(byteArray, step, newLowBound);
step += NEW_LOWBOUND_SIZE;
ByteTool.shortToBytes(byteArray, step, newUpBound);
step += NEW_UPBOUND_SIZE;
ByteTool.shortToBytes(byteArray, step, headOffset);
step += HEAD_OFFSET_SIZE;
ByteTool.shortToBytes(byteArray, step, nodeOffset);
step += NODE_OFFSET_SIZE;
ByteTool.shortToBytes(byteArray, step, nodeSize);
step += NODE_SIZE;
ByteTool.shortToBytes(byteArray, step, compressedHeadMoveIndexArrLen);
step += HEAD_MOVE_INDEX_LENGTH_SIZE;
if (compressedHeadMoveIndexArrLen > 0) {
System.arraycopy(compressedHeadMoveIndexArr, 0, byteArray, step, compressedHeadMoveIndexArrLen);
step += compressedHeadMoveIndexArrLen;
}
ByteTool.shortToBytes(byteArray, step, nodeValueLen);
step += NODE_VALUE_LENGTH_SIZE;
if (nodeValueLen > 0) {
System.arraycopy(nodeValue, 0, byteArray, step, nodeValueLen);
}
step += nodeValueLen;
ByteTool.intToBytes(byteArray, step, dataPageNum, msbFirst);
step += BTreeLogRecord.PAGENUM_SIZE;
ByteTool.shortToBytes(byteArray, step, dataPageOldUpBound);
step += OLD_UPBOUND_SIZE;
ByteTool.shortToBytes(byteArray, step, dataPageNewUpBound);
step += NEW_UPBOUND_SIZE;
ByteTool.shortToBytes(byteArray, step, dataNodeValueLen);
step += NODE_VALUE_LENGTH_SIZE;
if (dataNodeValueLen > 0) {
System.arraycopy(dataNodeValue, 0, byteArray, step, dataNodeValueLen);
}
}
/**
* get current log record type total length
*
* @return int total lenth
*/
public int getRecordLength() {
return super.getRecordLength() + OLD_LOWBOUND_SIZE + OLD_UPBOUND_SIZE * 2 + NEW_LOWBOUND_SIZE + NEW_UPBOUND_SIZE * 2 + HEAD_OFFSET_SIZE + NODE_OFFSET_SIZE + HEAD_MOVE_INDEX_LENGTH_SIZE + compressedHeadMoveIndexArrLen + NODE_VALUE_LENGTH_SIZE * 2 + NODE_SIZE + nodeValueLen + dataNodeValueLen + BTreeLogRecord.PAGENUM_SIZE;
}
}