/*
* 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.index.btree.hbt;
import org.apache.log4j.Logger;
import org.chaidb.db.KernelContext;
import org.chaidb.db.api.DuplicatedKeyIterator;
import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.helper.ByteTool;
import org.chaidb.db.index.btree.AbstractBTree;
import org.chaidb.db.index.btree.BTreeSpec;
import org.chaidb.db.index.btree.DataNode;
import org.chaidb.db.index.btree.DataPage;
import org.chaidb.db.index.btree.bufmgr.PageBufferManager;
import org.chaidb.db.index.btree.bufmgr.PageNumber;
abstract class HyperBTreeIterator implements DuplicatedKeyIterator {
private static final Logger logger = Logger.getLogger(HyperBTreeIterator.class);
private static boolean useCachedIterator = getUseCache();
private static boolean getUseCache() {
boolean ret = true;
String prtStr = System.getProperty("chaidb.hbt.cachediterator");
if (prtStr != null && prtStr.equalsIgnoreCase("false")) {
ret = false;
}
return ret;
}
protected AbstractBTree btree;
protected KernelContext kContext;
protected boolean finished;
protected NodeInfo _nextNode;
protected NodeInfo _prevNode = new NodeInfo();
protected NodeInfo _curNode = new NodeInfo();
static final int DUP_DATA = 0x01;
static final int DUP_NEXT = 0x02;
static final int DUP_FINISHED = 0x04;
static final int DUP_FAILED = 0x80;
HyperBTreeIterator(AbstractBTree btree, KernelContext kContext) {
this.btree = btree;
this.kContext = kContext;
}
static HyperBTreeIterator createIterator(AbstractBTree btree, KernelContext kContext) {
if (useCachedIterator) {
return new HyperBTreeCachedIterator(btree, kContext);
} else {
return new HyperBTreeBasicIterator(btree, kContext);
}
}
void initIterator(NodePosition firstNode) {
_nextNode = new NodeInfo();
_nextNode.nodeNextPosition = firstNode;
}
/**
* To update a dup_next node to point to a new position
*
* @param nodePos the position of dup_next node
* @param nextNodePos the new position which dup_next will point to
*/
protected void updateDupNextNode(NodePosition nodePos, NodePosition nextNodePos) {
if (nodePos == null) return;
if (nextNodePos == null) {
nextNodePos = nodePos;
}
final PageBufferManager buffer = btree.getBuffer();
final BTreeSpec btreeSpec = btree.getBTreeSpec();
PageNumber dupPageNumber = new PageNumber(nodePos.getPageNo());
try {
DataPage curDataPage = new DataPage(btree.getBtreeId(), dupPageNumber, btreeSpec, buffer);
DataNode curDataNode = new DataNode(curDataPage, nodePos.getOffsetInPage());
curDataNode.setFlags(BTreeSpec.DATA_NODE_DUP_NEXT);
byte[] oldData = ByteTool.copyByteArray(curDataNode.getPage().getPage(), curDataNode.getNodeOffset(), (int) curDataNode.getNodeSpace());
byte[] data = new byte[8];
ByteTool.intToBytes(data, 0, nextNodePos.getOffsetInPage());
ByteTool.intToBytes(data, 4, nextNodePos.getPageNo());
curDataNode.storeNode(data, oldData, kContext);
buffer.releasePage(btree.getBtreeId(), nodePos.getPageNo(), true);
} catch (ChaiDBException e) {
logger.error(e);
buffer.releasePage(btree.getBtreeId(), nodePos.getPageNo(), false);
}
}
protected void removeOneNode(NodePosition prevNodeNextPos, NodeInfo curNode, NodePosition nextNodePos) {
if (prevNodeNextPos == null || prevNodeNextPos.equals(curNode.nodePosition)) {
updateDupNextNode(curNode.nodePosition, nextNodePos);
} else {
updateDupNextNode(prevNodeNextPos, nextNodePos);
}
curNode.nodeNextPosition = prevNodeNextPos;
}
/**
* Get a node located at node.nodePosition
*
* @return 0x80 - failed
* 0x1 - dupdata
* 0x2 - dupnext
* 0x4 - finished
* node.nodeNextPosition is updated to the next position after the node,
* if the node is dup_next, nodeNextPosition stored the pointer. if link
* is end, nodeNextPosition will be null.
*/
protected int getNode(DataPage dataPage, NodeInfo node) {
DataNode dupDataNode = new DataNode(dataPage, node.nodePosition.getOffsetInPage());
int nPageNo = node.nodePosition.getPageNo();
int nOffset = node.nodePosition.getOffsetInPage();
try {
byte[] data = dupDataNode.getData();
if (dupDataNode.isDupData()) {
//This node is available value
node.data = btree.decodeFromByteArray(data, null);
node.nodeNextPosition = new NodePosition(nPageNo, nOffset + dupDataNode.getNodeSpace());
return DUP_DATA;
} else if (dupDataNode.isDupNext()) {
node.data = null;
int nextOffset = ByteTool.bytesToInt(data, 0, btree.getBTreeSpec().isMsbFirst());
int nNextPageNumber = ByteTool.bytesToInt(data, 4, btree.getBTreeSpec().isMsbFirst());
if ((nextOffset == nOffset) && (nNextPageNumber == nPageNo)) {
node.nodeNextPosition = null;
return DUP_NEXT | DUP_FINISHED;
}
node.nodeNextPosition = new NodePosition(nNextPageNumber, nextOffset);
return DUP_NEXT;
} else {
node.data = btree.decodeFromByteArray(data, null);
node.nodeNextPosition = null;
return DUP_DATA | DUP_FINISHED;
}
} catch (ChaiDBException e) {
logger.error(e);
}
return DUP_FAILED;
}
public void remove() {
NodePosition prevPos = null;
NodePosition nextPos = null;
if (_prevNode != null) {
prevPos = _prevNode.nodeNextPosition;
}
if (_nextNode != null) {
nextPos = _nextNode.nodePosition;
}
removeOneNode(prevPos, _curNode, nextPos);
}
public void close() {
}
}