Package org.chaidb.db.index.btree

Source Code of org.chaidb.db.index.btree.DataNode

/*
* 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;

import org.apache.log4j.Logger;
import org.chaidb.db.Db;
import org.chaidb.db.KernelContext;
import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.exception.ErrorCode;
import org.chaidb.db.helper.ByteTool;
import org.chaidb.db.index.IDBIndex;
import org.chaidb.db.index.btree.bufmgr.PageNumber;
import org.chaidb.db.log.logrecord.BTreeAddRemoveLogRecord;
import org.chaidb.db.log.logrecord.BTreeReplLogRecord;

/**
* <P>Each data node has the following format:
* <pre>
* Size  Description
*  4    Key Size
*  4    Data Size
*  1    Flags: 0x01 for overflow data, 0x02 for overflow key
*  2    Space allocated for this node within the page
*  n    Key/Data
* </pre>
*/
public class DataNode {

    private static final Logger logger = Logger.getLogger(AbstractBTree.class);

    /**
     * Origin page of this node
     */
    private DataPage page;

    /**
     * Location of this node on that page
     */
    private int nodeOffset;

    /**
     * space allocated for this node within the page, two cases are:
     * 1. when overflow: this is DATA_NODE_HEADER_SIZE + 4
     * 2. if not overflow: this is DATA_NODE_HEADER_SIZE + keySize + dataSize
     */
    private short nodeSpace;


    /**
     * Length of data
     */
    private int dataSize;

    /**
     * 1 if data is on an overflow page, 2 for overflow key
     */
    private byte flags;

    boolean isOverflow() {
        return (flags & 0x01) != 0;
    }

    public int getNodeOffset() {
        return nodeOffset;
    }

    public byte getFlags() {
        return flags;
    }

    public boolean isDupData() {
        return ((flags & BTreeSpec.DATA_NODE_DUP) == BTreeSpec.DATA_NODE_DUP);
    }

    public boolean isDupNext() {
        return ((flags & BTreeSpec.DATA_NODE_DUP_NEXT) == BTreeSpec.DATA_NODE_DUP_NEXT);
    }

    int getBtreeId() {
        return page.btreeSpec.btree.getBtreeId();
    }

    int getNodeSize() {
        return dataSize + BTreeSpec.DATA_NODE_HEADER_SIZE;
    }

    int getDataSize() {
        return dataSize;
    }

    public DataPage getPage() {
        return page;
    }

    public void setFlags(byte flags) {
        this.flags = flags;
    }

    void setDataSize(int dataSize) {
        this.dataSize = dataSize;
    }


    void setNodeSpace(short nodeSpace) {
        this.nodeSpace = nodeSpace;
    }

    public short getNodeSpace() {
        return nodeSpace;
    }

    /**
     * create DataNode by Datapage and offset in the datapage,
     *
     * @param page   the page where this node stays
     * @param offset the offset of the node in the page
     */
    public DataNode(DataPage page, int offset) {
        this.page = page;
        byte[] rawPage = this.page.getPage();
        nodeOffset = offset;
        dataSize = ByteTool.bytesToInt(rawPage,
//            nodeOffset + BTreeSpec.DATA_NODE_OFF_PAGENUMBER, this.page.btreeSpec.msbFirst);
                nodeOffset + BTreeSpec.DATA_NODE_OFF_DATASIZE, this.page.btreeSpec.isMsbFirst());
        flags = rawPage[nodeOffset + BTreeSpec.DATA_NODE_OFF_FLAGS];
        nodeSpace = ByteTool.bytesToShort(rawPage, nodeOffset + BTreeSpec.DATA_NODE_OFF_ALLOCATED_SPACE, this.page.btreeSpec.isMsbFirst());
    }

    /**
     * Build a data node, get all parameters needed
     */
    public DataNode(DataPage page, int offset, byte[] data) {
        this(page, offset, data, (byte) 0); // normal node
    }

    /**
     * Build a data node, get all parameters needed
     */
    public DataNode(DataPage page, int offset, byte[] data, byte flags) {
        this.page = page;
        this.nodeOffset = offset;
        dataSize = data.length;
        this.flags = flags;
    }

    /**
     * return the node header in byte[]
     */
    public byte[] getHeader() {
        byte[] ret = new byte[BTreeSpec.DATA_NODE_HEADER_SIZE];
        System.arraycopy(ByteTool.intToBytes(dataSize), 0, ret, BTreeSpec.DATA_NODE_OFF_DATASIZE, 4);
//        ret,BTreeSpec.DATA_NODE_OFF_PAGENUMBER,4);
        ret[BTreeSpec.DATA_NODE_OFF_FLAGS] = flags;
        System.arraycopy(ByteTool.shortToBytes(nodeSpace), 0, ret, BTreeSpec.DATA_NODE_OFF_ALLOCATED_SPACE, 2);
        return ret;
    }


    /**
     * Fetch data from a leaf page to a new copy; guaranteed to be data.
     *
     * @return A new copy of data in this node
     */
    // did NOT fix / unfix the current page ###
    public byte[] getData() throws ChaiDBException {
        byte[] data = new byte[dataSize];
        if (!isOverflow()) {
            System.arraycopy(page.getPage(), nodeOffset + BTreeSpec.DATA_NODE_HEADER_SIZE, data, 0, dataSize);
        } else {
            // handle overflow data
            PageNumber overflowPageNumber = new PageNumber(ByteTool.bytesToInt(page.getPage(), nodeOffset + BTreeSpec.DATA_NODE_HEADER_SIZE, page.btreeSpec.isMsbFirst()));
            DataPage overflowPage = new DataPage(page.btreeSpec.btree.getBtreeId(), overflowPageNumber, page.btreeSpec, page.buffer);

            //=======>>>> 1. try to find the first overflow page containing data
            int freeSpace = page.btreeSpec.getPageSize() - BTreeSpec.PAGE_HEADER_SIZE;
            int dataLeft = dataSize;
            int dataStartPos = 0;
            int dataLength = (dataLeft < freeSpace ? dataLeft : freeSpace);
            while (dataLeft > 0 && dataLength > 0) {
                System.arraycopy(overflowPage.getPage(), overflowPage.upperBound, data, dataStartPos, dataLength);
                // release / unfix the overflow page
                page.buffer.releasePage(overflowPage.pageNumber.getTreeId(), overflowPage.pageNumber, false);
                dataStartPos += dataLength;
                dataLeft -= dataLength;
                if (dataLeft > 0 && overflowPage.nextPage.getPageNumber() > 0) {
                    overflowPage = new DataPage(page.btreeSpec.btree.getBtreeId(), overflowPage.nextPage, page.btreeSpec, page.buffer);
                    dataLength = (dataLeft < freeSpace) ? dataLeft : freeSpace;
                } else if (dataLeft > 0 && overflowPage.nextPage.getPageNumber() <= 0) {
                    logger.error("dataSize=" + dataSize + " dataLeft" + dataLeft + " node offset=" + getNodeOffset() + " node pageNumber=" + page.pageNumber.toHexString() + " overflowpage=" + overflowPage.pageNumber.toHexString() + " of " + page.buffer.getBTreeName(overflowPage.pageNumber.getTreeId()));
                    /**
                     * Debug info for getXMLNode failed.
                     */


                    if (Debug.DEBUG_XMLNODEFAILED) {
                        new Throwable().printStackTrace(System.err);
                        Debug.dumpPageToLog(page.getPage());
                        Debug.dumpPageToLog(overflowPage.getPage());
                        page.buffer.closeAllBTrees();
                        page.buffer.dump();
                        Db.getLogManager().flush();
                        System.exit(-100);
                    }
                    // details -ranjeet
                    String details = "Non-match key length in the overflow page.";
                    throw new ChaiDBException(ErrorCode.KEY_LENGTH_NOT_MATCH, details);
                }
            }
        }
        return data;
    }


    /**
     * First time set up. guaranteed to be data; need to know whether overflow
     * or not ahead.
     */
    // did NOT unfix / fix the current page ###
    public void storeNode(byte[] data, byte[] oldData, KernelContext kContext) throws ChaiDBException {
        int txnId = kContext.getLocker();
        boolean needLog = kContext.getNeedLog();

        if (!isOverflow()) {
            if (needLog) {
                int newPageNo = page.pageNumber.getPageNumber();
                byte[] newData = ByteTool.append(this.getHeader(), data);
                if (oldData != null && (oldData != newData)) {
                    /* replace old data with new data*/
                    BTreeReplLogRecord logRec = new BTreeReplLogRecord(page.getPageNumber().getTreeId(), newPageNo, txnId, nodeOffset, oldData, newData, page.btreeSpec.btree.getType());
                    logRec.log();
                } else {
                    if (page.btreeSpec.btree.getType() == IDBIndex.HYPER_BTREE) {
                        BTreeAddRemoveLogRecord logRec = new BTreeAddRemoveLogRecord(page.getPageNumber().getTreeId(), newPageNo, txnId, BTreeAddRemoveLogRecord.ADD_FLAG, nodeOffset, newData, page.btreeSpec.btree.getType());
                        logRec.log();
                    }
                }
            }
            // add the new node
            System.arraycopy(this.getHeader(), 0, page.getPage(), nodeOffset, BTreeSpec.DATA_NODE_HEADER_SIZE);
            System.arraycopy(data, 0, page.getPage(), nodeOffset + BTreeSpec.DATA_NODE_HEADER_SIZE, dataSize);
        } else {

            //=====>>> Build overflow pages
            // make it large enough
            int freeSpace = page.btreeSpec.getPageSize() - BTreeSpec.PAGE_HEADER_SIZE;
            int overflows = data.length / freeSpace + (data.length % freeSpace > 0 ? 1 : 0);
            int dataStartPos = 0;
            int dataLeft = data.length;
            int requireSpace;
            int left;
            int dataLength = 0;
            DataPage last = page;
            PageNumber firstOverflowPageNo = null;
            for (int i = 0; i < overflows; i++) {
                left = dataLeft;
                if (left <= 0) break;
                requireSpace = (left > freeSpace) ? freeSpace : left;
                dataLength = (dataLeft >= freeSpace) ? freeSpace : dataLeft;
                DataPage overflowPage = DataPage.newPage(page.btreeSpec, page.buffer, true, kContext, 0);
                overflowPage.setLogInfo(txnId, false);
                if (needLog) {
                    int pgno = overflowPage.getPageNumber().getPageNumber();
                    byte[] oldV = ByteTool.copyByteArray(overflowPage.getPage(), 0, BTreeSpec.PAGE_HEADER_SIZE);

                    byte[] newV = new byte[oldV.length];
                    System.arraycopy(oldV, 0, newV, 0, oldV.length);
                    System.arraycopy(ByteTool.intToBytes(4), 0, newV, BTreeSpec.OFF_FLAGS, 4);
                    System.arraycopy(ByteTool.shortToBytes((short) (overflowPage.upperBound - requireSpace)), 0, newV, BTreeSpec.OFF_UPPERBOUND, 2);
                    System.arraycopy(ByteTool.intToBytes(last.pageNumber.getPageNumber(), overflowPage.btreeSpec.getMsbFirst()), 0, newV, BTreeSpec.OFF_PREVPAGE, 4);
                    if (newV != oldV) {
                        BTreeReplLogRecord lr = new BTreeReplLogRecord(overflowPage.getPageNumber().getTreeId(), pgno, txnId, 0, oldV, newV, overflowPage.btreeSpec.btree.getType());
                        lr.log();
                    }
                }

                overflowPage.setUpperBound((short) (overflowPage.upperBound - requireSpace));
                overflowPage.setPrevPage(last.pageNumber);
                overflowPage.setLogInfo(txnId, needLog);

                if (i == 0) firstOverflowPageNo = overflowPage.pageNumber;
                else last.setNextPage(overflowPage.pageNumber);

                //add the new node
                if (needLog) {
                    int newPageNo = overflowPage.getPageNumber().getPageNumber();
                    byte[] addData = ByteTool.subByteArray(data, dataStartPos, dataLength);
                    BTreeAddRemoveLogRecord logRec = new BTreeAddRemoveLogRecord(overflowPage.getPageNumber().getTreeId(), newPageNo, txnId, BTreeAddRemoveLogRecord.ADD_FLAG, overflowPage.upperBound, addData, overflowPage.btreeSpec.btree.getType());
                    logRec.log();
                }
                // only data left
                System.arraycopy(data, dataStartPos, overflowPage.page, overflowPage.upperBound, dataLength);
                dataStartPos += dataLength;
                dataLeft -= dataLength;

                //Dont release the owner nonoverflow page of these overflow pages.
                if (i > 0) page.buffer.releasePage(last.pageNumber.getTreeId(), last.pageNumber, true);
                last = overflowPage;
            }
            //unfix the last overflow page
            page.buffer.releasePage(last.pageNumber.getTreeId(), last.pageNumber, true);
            setOverflowPage(firstOverflowPageNo, oldData);

        }
    }

    private void setOverflowPage(PageNumber pn, byte[] oldData) throws ChaiDBException {
        if (page.needLog) {
            int newPageNo = page.getPageNumber().getPageNumber();
            byte[] newData = ByteTool.append(this.getHeader(), ByteTool.intToBytes(pn.getPageNumber()));
            if (oldData != null && (newData != oldData)) {
                BTreeReplLogRecord logRec = new BTreeReplLogRecord(page.getPageNumber().getTreeId(), newPageNo, page.txnId, nodeOffset, oldData, newData, page.btreeSpec.btree.getType());
                logRec.log();
            } else {
                BTreeAddRemoveLogRecord logRec = new BTreeAddRemoveLogRecord(page.getPageNumber().getTreeId(), newPageNo, page.txnId, BTreeAddRemoveLogRecord.ADD_FLAG, nodeOffset, newData, page.btreeSpec.btree.getType());
                logRec.log();
            }
        }
        // add the new node to the current page
        System.arraycopy(this.getHeader(), 0, page.getPage(), nodeOffset, BTreeSpec.DATA_NODE_HEADER_SIZE);
        // last pageNumber is the one we need here!!
        System.arraycopy(ByteTool.intToBytes(pn.getPageNumber()), 0, page.getPage(), nodeOffset + BTreeSpec.DATA_NODE_HEADER_SIZE, 4);
    }

    /**
     * Debug method
     */
    public String toString() {
        StringBuffer str = new StringBuffer("Node: " + nodeOffset + "{\n");
        str.append(this.page.pageNumber.toHexString());
        str.append(" dataSize: " + dataSize);
        str.append("}\n");
        return str.toString();
    }
}
TOP

Related Classes of org.chaidb.db.index.btree.DataNode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.