blob: 6d50461ffdb5491013c5b59d8d45982ad3f8ab1c [file] [log] [blame]
/*
* Copyright 2009-2010 by The Regents of the University of California
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may obtain a copy of the License from
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package edu.uci.ics.hyracks.storage.am.btree.frames;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import edu.uci.ics.hyracks.api.dataflow.value.ISerializerDeserializer;
import edu.uci.ics.hyracks.api.exceptions.HyracksDataException;
import edu.uci.ics.hyracks.dataflow.common.data.accessors.ITupleReference;
import edu.uci.ics.hyracks.storage.am.btree.api.IBTreeFrame;
import edu.uci.ics.hyracks.storage.am.btree.api.IBTreeInteriorFrame;
import edu.uci.ics.hyracks.storage.am.btree.api.IBTreeTupleReference;
import edu.uci.ics.hyracks.storage.am.btree.api.IBTreeTupleWriter;
import edu.uci.ics.hyracks.storage.am.btree.impls.BTreeException;
import edu.uci.ics.hyracks.storage.am.btree.impls.FindSlotMode;
import edu.uci.ics.hyracks.storage.am.btree.impls.MultiComparator;
import edu.uci.ics.hyracks.storage.am.btree.impls.RangePredicate;
import edu.uci.ics.hyracks.storage.am.btree.impls.SlotOffTupleOff;
import edu.uci.ics.hyracks.storage.am.btree.impls.SpaceStatus;
import edu.uci.ics.hyracks.storage.am.btree.impls.SplitKey;
public class NSMInteriorFrame extends NSMFrame implements IBTreeInteriorFrame {
private static final int rightLeafOff = smFlagOff + 1;
private static final int childPtrSize = 4;
//private SimpleTupleReference cmpFrameTuple = new SimpleTupleReference();
private IBTreeTupleReference cmpFrameTuple;
public NSMInteriorFrame(IBTreeTupleWriter tupleWriter) {
super(tupleWriter);
cmpFrameTuple = tupleWriter.createTupleReference();
}
private int getLeftChildPageOff(ITupleReference tuple, MultiComparator cmp) {
return tuple.getFieldStart(cmp.getKeyFieldCount()-1) + tuple.getFieldLength(cmp.getKeyFieldCount()-1);
}
@Override
public void initBuffer(byte level) {
super.initBuffer(level);
buf.putInt(rightLeafOff, -1);
}
@Override
public SpaceStatus hasSpaceInsert(ITupleReference tuple, MultiComparator cmp) {
int bytesRequired = tupleWriter.bytesRequired(tuple) + 8; // for the two childpointers
if(bytesRequired + slotManager.getSlotSize() <= buf.capacity() - buf.getInt(freeSpaceOff) - (buf.getInt(tupleCountOff) * slotManager.getSlotSize()) ) return SpaceStatus.SUFFICIENT_CONTIGUOUS_SPACE;
else if(bytesRequired + slotManager.getSlotSize() <= buf.getInt(totalFreeSpaceOff)) return SpaceStatus.SUFFICIENT_SPACE;
else return SpaceStatus.INSUFFICIENT_SPACE;
}
@Override
public void insert(ITupleReference tuple, MultiComparator cmp) throws Exception {
frameTuple.setFieldCount(cmp.getKeyFieldCount());
int tupleIndex = slotManager.findTupleIndex(tuple, frameTuple, cmp, FindSlotMode.FSM_INCLUSIVE);
int slotOff = slotManager.getSlotOff(tupleIndex);
boolean isDuplicate = true;
if(tupleIndex < 0) isDuplicate = false; // greater than all existing keys
else {
frameTuple.resetByOffset(buf, slotManager.getTupleOff(slotOff));
if(cmp.compare(tuple, frameTuple) != 0) isDuplicate = false;
}
if(isDuplicate) {
throw new BTreeException("Trying to insert duplicate value into interior node.");
}
else {
slotOff = slotManager.insertSlot(tupleIndex, buf.getInt(freeSpaceOff));
int freeSpace = buf.getInt(freeSpaceOff);
int bytesWritten = tupleWriter.writeTupleFields(tuple, 0, cmp.getKeyFieldCount(), buf, freeSpace);
System.arraycopy(tuple.getFieldData(cmp.getKeyFieldCount()-1), getLeftChildPageOff(tuple, cmp), buf.array(), freeSpace + bytesWritten, childPtrSize);
int tupleSize = bytesWritten + childPtrSize;
buf.putInt(tupleCountOff, buf.getInt(tupleCountOff) + 1);
buf.putInt(freeSpaceOff, buf.getInt(freeSpaceOff) + tupleSize);
buf.putInt(totalFreeSpaceOff, buf.getInt(totalFreeSpaceOff) - tupleSize - slotManager.getSlotSize());
// did insert into the rightmost slot?
if(slotOff == slotManager.getSlotEndOff()) {
System.arraycopy(tuple.getFieldData(cmp.getKeyFieldCount()-1), getLeftChildPageOff(tuple, cmp) + childPtrSize, buf.array(), rightLeafOff, childPtrSize);
}
else {
// if slotOff has a right (slot-)neighbor then update its child pointer
// the only time when this is NOT the case, is when this is the first tuple
// (or when the splitkey goes into the rightmost slot but that case was handled in the if above)
if(buf.getInt(tupleCountOff) > 1) {
int rightNeighborOff = slotOff - slotManager.getSlotSize();
frameTuple.resetByOffset(buf, slotManager.getTupleOff(rightNeighborOff));
System.arraycopy(tuple.getFieldData(0), getLeftChildPageOff(tuple, cmp) + childPtrSize, buf.array(), getLeftChildPageOff(frameTuple, cmp), childPtrSize);
}
}
}
}
@Override
public void insertSorted(ITupleReference tuple, MultiComparator cmp) throws Exception {
int freeSpace = buf.getInt(freeSpaceOff);
slotManager.insertSlot(-1, freeSpace);
int bytesWritten = tupleWriter.writeTupleFields(tuple, 0, cmp.getKeyFieldCount(), buf, freeSpace);
System.arraycopy(tuple.getFieldData(cmp.getKeyFieldCount()-1), getLeftChildPageOff(tuple, cmp), buf.array(), freeSpace + bytesWritten, childPtrSize);
int tupleSize = bytesWritten + childPtrSize;
buf.putInt(tupleCountOff, buf.getInt(tupleCountOff) + 1);
buf.putInt(freeSpaceOff, buf.getInt(freeSpaceOff) + tupleSize);
buf.putInt(totalFreeSpaceOff, buf.getInt(totalFreeSpaceOff) - tupleSize - slotManager.getSlotSize());
System.arraycopy(tuple.getFieldData(0), getLeftChildPageOff(tuple, cmp) + childPtrSize, buf.array(), rightLeafOff, childPtrSize);
}
@Override
public int split(IBTreeFrame rightFrame, ITupleReference tuple, MultiComparator cmp, SplitKey splitKey) throws Exception {
// before doing anything check if key already exists
frameTuple.setFieldCount(cmp.getKeyFieldCount());
int tupleIndex = slotManager.findTupleIndex(tuple, frameTuple, cmp, FindSlotMode.FSM_EXACT);
int slotOff = slotManager.getSlotOff(tupleIndex);
if(tupleIndex >= 0) {
frameTuple.resetByOffset(buf, slotManager.getTupleOff(slotOff));
if(cmp.compare(tuple, frameTuple) == 0) {
throw new BTreeException("Inserting duplicate key in interior node during split");
}
}
ByteBuffer right = rightFrame.getBuffer();
int tupleCount = buf.getInt(tupleCountOff);
int tuplesToLeft = (tupleCount / 2) + (tupleCount % 2);
IBTreeFrame targetFrame = null;
frameTuple.resetByOffset(buf, getTupleOffset(tuplesToLeft-1));
if(cmp.compare(tuple, frameTuple) <= 0) {
targetFrame = this;
}
else {
targetFrame = rightFrame;
}
int tuplesToRight = tupleCount - tuplesToLeft;
// copy entire page
System.arraycopy(buf.array(), 0, right.array(), 0, buf.capacity());
// on right page we need to copy rightmost slots to left
int src = rightFrame.getSlotManager().getSlotEndOff();
int dest = rightFrame.getSlotManager().getSlotEndOff() + tuplesToLeft * rightFrame.getSlotManager().getSlotSize();
int length = rightFrame.getSlotManager().getSlotSize() * tuplesToRight;
System.arraycopy(right.array(), src, right.array(), dest, length);
right.putInt(tupleCountOff, tuplesToRight);
// on left page, remove highest key and make its childpointer the rightmost childpointer
buf.putInt(tupleCountOff, tuplesToLeft);
// copy data to be inserted, we need this because creating the splitkey will overwrite the data param (data points to same memory as splitKey.getData())
SplitKey savedSplitKey = splitKey.duplicate(tupleWriter.createTupleReference());
// set split key to be highest value in left page
int tupleOff = slotManager.getTupleOff(slotManager.getSlotEndOff());
frameTuple.resetByOffset(buf, tupleOff);
int splitKeySize = tupleWriter.bytesRequired(frameTuple, 0, cmp.getKeyFieldCount());
splitKey.initData(splitKeySize);
tupleWriter.writeTupleFields(frameTuple, 0, cmp.getKeyFieldCount(), splitKey.getBuffer(), 0);
splitKey.getTuple().resetByOffset(splitKey.getBuffer(), 0);
int deleteTupleOff = slotManager.getTupleOff(slotManager.getSlotEndOff());
frameTuple.resetByOffset(buf, deleteTupleOff);
buf.putInt(rightLeafOff, buf.getInt(getLeftChildPageOff(frameTuple, cmp)));
buf.putInt(tupleCountOff, tuplesToLeft - 1);
// compact both pages
rightFrame.compact(cmp);
compact(cmp);
// insert key
targetFrame.insert(savedSplitKey.getTuple(), cmp);
return 0;
}
@Override
public void compact(MultiComparator cmp) {
resetSpaceParams();
frameTuple.setFieldCount(cmp.getKeyFieldCount());
int tupleCount = buf.getInt(tupleCountOff);
int freeSpace = buf.getInt(freeSpaceOff);
ArrayList<SlotOffTupleOff> sortedTupleOffs = new ArrayList<SlotOffTupleOff>();
sortedTupleOffs.ensureCapacity(tupleCount);
for(int i = 0; i < tupleCount; i++) {
int slotOff = slotManager.getSlotOff(i);
int tupleOff = slotManager.getTupleOff(slotOff);
sortedTupleOffs.add(new SlotOffTupleOff(i, slotOff, tupleOff));
}
Collections.sort(sortedTupleOffs);
for(int i = 0; i < sortedTupleOffs.size(); i++) {
int tupleOff = sortedTupleOffs.get(i).tupleOff;
frameTuple.resetByOffset(buf, tupleOff);
int tupleEndOff = frameTuple.getFieldStart(frameTuple.getFieldCount()-1) + frameTuple.getFieldLength(frameTuple.getFieldCount()-1);
int tupleLength = tupleEndOff - tupleOff + childPtrSize;
System.arraycopy(buf.array(), tupleOff, buf.array(), freeSpace, tupleLength);
slotManager.setSlot(sortedTupleOffs.get(i).slotOff, freeSpace);
freeSpace += tupleLength;
}
buf.putInt(freeSpaceOff, freeSpace);
buf.putInt(totalFreeSpaceOff, buf.capacity() - freeSpace - tupleCount * slotManager.getSlotSize());
}
@Override
public int getChildPageId(RangePredicate pred, MultiComparator srcCmp) {
// check for trivial case where there is only a child pointer (and no key)
if(buf.getInt(tupleCountOff) == 0) {
return buf.getInt(rightLeafOff);
}
cmpFrameTuple.setFieldCount(srcCmp.getKeyFieldCount());
frameTuple.setFieldCount(srcCmp.getKeyFieldCount());
// check for trivial cases where no low key or high key exists (e.g. during an index scan)
ITupleReference tuple = null;
FindSlotMode fsm = null;
if(pred.isForward()) {
tuple = pred.getLowKey();
if(tuple == null) {
return getLeftmostChildPageId(srcCmp);
}
if(pred.isLowKeyInclusive()) fsm = FindSlotMode.FSM_INCLUSIVE;
else fsm = FindSlotMode.FSM_EXCLUSIVE;
}
else {
tuple = pred.getHighKey();
if(tuple == null) {
return getRightmostChildPageId(srcCmp);
}
fsm = FindSlotMode.FSM_INCLUSIVE;
}
MultiComparator targetCmp = pred.getComparator();
int tupleIndex = slotManager.findTupleIndex(tuple, frameTuple, targetCmp, fsm);
int slotOff = slotManager.getSlotOff(tupleIndex);
if(tupleIndex < 0) {
return buf.getInt(rightLeafOff);
}
else {
int origTupleOff = slotManager.getTupleOff(slotOff);
cmpFrameTuple.resetByOffset(buf, origTupleOff);
int cmpTupleOff = origTupleOff;
if(pred.isForward()) {
int maxSlotOff = buf.capacity();
slotOff += slotManager.getSlotSize();
while(slotOff < maxSlotOff) {
cmpTupleOff = slotManager.getTupleOff(slotOff);
frameTuple.resetByOffset(buf, cmpTupleOff);
if(targetCmp.compare(cmpFrameTuple, frameTuple) != 0) break;
slotOff += slotManager.getSlotSize();
}
slotOff -= slotManager.getSlotSize();
}
else {
int minSlotOff = slotManager.getSlotEndOff() - slotManager.getSlotSize();
slotOff -= slotManager.getSlotSize();
while(slotOff > minSlotOff) {
cmpTupleOff = slotManager.getTupleOff(slotOff);
frameTuple.resetByOffset(buf, cmpTupleOff);
if(targetCmp.compare(cmpFrameTuple, frameTuple) != 0) break;
slotOff -= slotManager.getSlotSize();
}
slotOff += slotManager.getSlotSize();
}
frameTuple.resetByOffset(buf, slotManager.getTupleOff(slotOff));
int childPageOff = getLeftChildPageOff(frameTuple, srcCmp);
return buf.getInt(childPageOff);
}
}
@Override
public void delete(ITupleReference tuple, MultiComparator cmp, boolean exactDelete) throws Exception {
frameTuple.setFieldCount(cmp.getKeyFieldCount());
int tupleIndex = slotManager.findTupleIndex(tuple, frameTuple, cmp, FindSlotMode.FSM_INCLUSIVE);
int slotOff = slotManager.getSlotOff(tupleIndex);
int tupleOff;
int keySize;
if(tupleIndex < 0) {
tupleOff = slotManager.getTupleOff(slotManager.getSlotEndOff());
frameTuple.resetByOffset(buf, tupleOff);
keySize = tupleWriter.bytesRequired(frameTuple, 0, cmp.getKeyFieldCount());
// copy new rightmost pointer
System.arraycopy(buf.array(), tupleOff + keySize, buf.array(), rightLeafOff, childPtrSize);
}
else {
tupleOff = slotManager.getTupleOff(slotOff);
frameTuple.resetByOffset(buf, tupleOff);
keySize = tupleWriter.bytesRequired(frameTuple, 0, cmp.getKeyFieldCount());
// perform deletion (we just do a memcpy to overwrite the slot)
int slotStartOff = slotManager.getSlotEndOff();
int length = slotOff - slotStartOff;
System.arraycopy(buf.array(), slotStartOff, buf.array(), slotStartOff + slotManager.getSlotSize(), length);
}
// maintain space information
buf.putInt(tupleCountOff, buf.getInt(tupleCountOff) - 1);
buf.putInt(totalFreeSpaceOff, buf.getInt(totalFreeSpaceOff) + keySize + childPtrSize + slotManager.getSlotSize());
}
@Override
protected void resetSpaceParams() {
buf.putInt(freeSpaceOff, rightLeafOff + childPtrSize);
buf.putInt(totalFreeSpaceOff, buf.capacity() - (rightLeafOff + childPtrSize));
}
@Override
public int getLeftmostChildPageId(MultiComparator cmp) {
int tupleOff = slotManager.getTupleOff(slotManager.getSlotStartOff());
frameTuple.setFieldCount(cmp.getKeyFieldCount());
frameTuple.resetByOffset(buf, tupleOff);
int childPageOff = getLeftChildPageOff(frameTuple, cmp);
return buf.getInt(childPageOff);
}
@Override
public int getRightmostChildPageId(MultiComparator cmp) {
return buf.getInt(rightLeafOff);
}
@Override
public void setRightmostChildPageId(int pageId) {
buf.putInt(rightLeafOff, pageId);
}
// for debugging
public ArrayList<Integer> getChildren(MultiComparator cmp) {
ArrayList<Integer> ret = new ArrayList<Integer>();
frameTuple.setFieldCount(cmp.getKeyFieldCount());
int tupleCount = buf.getInt(tupleCountOff);
for(int i = 0; i < tupleCount; i++) {
int tupleOff = slotManager.getTupleOff(slotManager.getSlotOff(i));
frameTuple.resetByOffset(buf, tupleOff);
int intVal = getInt(buf.array(), frameTuple.getFieldStart(frameTuple.getFieldCount()-1) + frameTuple.getFieldLength(frameTuple.getFieldCount()-1));
ret.add(intVal);
}
if(!isLeaf()) {
int rightLeaf = buf.getInt(rightLeafOff);
if(rightLeaf > 0) ret.add(buf.getInt(rightLeafOff));
}
return ret;
}
@Override
public void deleteGreatest(MultiComparator cmp) {
int slotOff = slotManager.getSlotEndOff();
int tupleOff = slotManager.getTupleOff(slotOff);
frameTuple.setFieldCount(cmp.getKeyFieldCount());
frameTuple.resetByOffset(buf, tupleOff);
int keySize = tupleWriter.bytesRequired(frameTuple, 0, cmp.getKeyFieldCount());
System.arraycopy(buf.array(), tupleOff + keySize, buf.array(), rightLeafOff, childPtrSize);
// maintain space information
buf.putInt(tupleCountOff, buf.getInt(tupleCountOff) - 1);
buf.putInt(totalFreeSpaceOff, buf.getInt(totalFreeSpaceOff) + keySize + childPtrSize + slotManager.getSlotSize());
int freeSpace = buf.getInt(freeSpaceOff);
if(freeSpace == tupleOff + keySize + childPtrSize) {
buf.putInt(freeSpace, freeSpace - (keySize + childPtrSize));
}
}
private int getInt(byte[] bytes, int offset) {
return ((bytes[offset] & 0xff) << 24) + ((bytes[offset + 1] & 0xff) << 16) + ((bytes[offset + 2] & 0xff) << 8) + ((bytes[offset + 3] & 0xff) << 0);
}
@Override
public String printKeys(MultiComparator cmp, ISerializerDeserializer[] fields) throws HyracksDataException {
StringBuilder strBuilder = new StringBuilder();
int tupleCount = buf.getInt(tupleCountOff);
frameTuple.setFieldCount(cmp.getKeyFieldCount());
for(int i = 0; i < tupleCount; i++) {
frameTuple.resetByTupleIndex(this, i);
for(int j = 0; j < cmp.getKeyFieldCount(); j++) {
ByteArrayInputStream inStream = new ByteArrayInputStream(frameTuple.getFieldData(j), frameTuple.getFieldStart(j), frameTuple.getFieldLength(j));
DataInput dataIn = new DataInputStream(inStream);
Object o = fields[j].deserialize(dataIn);
strBuilder.append(o.toString() + " ");
}
strBuilder.append(" | ");
}
strBuilder.append("\n");
return strBuilder.toString();
}
}