Package org.chaidb.db.index.btree

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

/*
* 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.api.keys.IntKey;
import org.chaidb.db.api.keys.NodeId;
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.Key;
import org.chaidb.db.index.btree.bufmgr.Page;
import org.chaidb.db.index.btree.bufmgr.PageBufferManager;
import org.chaidb.db.index.btree.bufmgr.PageNumber;
import org.chaidb.db.lock.Lock;
import org.chaidb.db.lock.LockManager;
import org.chaidb.db.log.logrecord.*;

import java.util.ArrayList;

/**
* A single BTREE page read from the database.
* <p/>
* A page in a BTREE file has the following layout:</P>
* <PRE>
* +--------+----------+------------+---------------------------+
* | Header | Pointers | Free space | Nodes                     |
* +--------+----------+------------+---------------------------+
* </PRE>
* <p/>
* <P><B>Header:</B> A BTREE page has the following metadata:</P>
* <PRE>
* Size  Description
* 4     Number of this page
* 4     Page number of preceding page
* 4     Page number of following page
* 3     Doc id of this page
* 1     Key type of this page
* 4     Reserved 4 bytes
* 4     Flags:
* 0x01 - BTree internal page
* 0x02 - BTree leaf page
* 0x04 - Overflow page
* 0x08 - Recno internal page
* 0x10 - Recno leaf page
* 0x20 - Never delete this chain of pages
* 2     Lower bound of free space on page
* 2     Upper bound of free space on page
* </PRE>
* <p/>
* <p><b>Page number of preceding page</b>: For internal/leaf page, it is used
* to store parent page number. For overflow page, it is used to store original
* page number. The parent page number of root page is 0.
* <p/>
* <p><b>Page number of following page</b>: For leaf page, it is used to chain
* all leaf pages together. For internal page, it is used as the leftmost
* pointer to the pages less than key.
* <p/>
* <P><B>Pointers:</B> A series of two-byte offsets indicating
* the location of successive nodes.</P>
* <p/>
* <P><B>Nodes:</B> For internal pages, each node contains a key
* and the page containing elements greater than or equal to that
* key (but smaller than the next key). For leaf pages,
* each node has a key and a data item. Ordinarily, the key and/or data
* immediately follow the flags.  If the key or data is too big,
* it's stored on a separate 'overflow' page (or pageS, if it's really
* big!). In that case, the node contains a 4-byte page number
* and a 4-byte size instead of the corresponding key or data bytes.</P>
* <p/>
* <P>Overflow pages have the same header as normal pages, but
* simply hold raw key/data bytes.  They use the 'next page' value
* to chain successive pages of data together.</P>
*/
public class BTreePage {

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

    /**
     * reference to bufferedPage
     */
    PageBufferManager buffer;
    /**
     * reference to the raw page
     */
    public byte[] page; // get from BufferPage.lookup(pageNumber);
    /**
     * reference to the btree spec
     */
    BTreeSpec btreeSpec;

    /**
     * head information of the page
     */
    public PageNumber pageNumber;
    private PageNumber prevPage;
    public PageNumber nextPage;
    /**
     * only support leaf, internal, overflow currently
     */
    int flags;
    short lowerBound;
    short upperBound;

    /* key type of all keys in this page */ byte keyType;

    /**
     * file name of this page
     */
    //String BTreeName;

    int txnId;
    boolean needLog = false;

    /**
     * Used in getLeaf() to indicate our operation
     */
    public static final byte INSERT = 1;
    public static final byte DELETE = INSERT + 1;
    public static final byte SEARCH = DELETE + 1;

    //private static LockManager lm=Db.getLockManager();

    /** index of the node - sorted key */
    //private short[] pointers;

    /**
     * order of the page - need to be even
     */
    private int order;

    /** debug */

    /**
     * Default Constructor
     */
    public BTreePage(BTreeSpec btreeSpec, PageBufferManager buffer) {
        this.btreeSpec = btreeSpec;
        this.buffer = buffer;
        this.pageNumber = new PageNumber(-1, -1, -1);
        this.prevPage = new PageNumber(-1, -1, -1);
        this.nextPage = new PageNumber(-1, -1, -1);
        keyType = -1;
    }

    /**
     * Basically, this class just stores a reference to the
     * raw page bytes, and decodes them as necessary.
     */
    public BTreePage(int id, PageNumber pageNumber, BTreeSpec btreeSpec, PageBufferManager buffer) throws ChaiDBException {
        this(btreeSpec, buffer);
        pageNumber.setTreeId(id);
        nextPage.setTreeId(id);
        prevPage.setTreeId(id);
        this.pageNumber = new PageNumber(pageNumber);
        this.page = buffer.getPage(id, this.pageNumber);
        if (this.page != null) {
            // get variables from page
            if (Debug.DEBUG_CHECKPAGENUMBER) {
                if (!Debug.checkPageNumber(pageNumber, this.page)) {
                    logger.fatal(new Throwable("there is hole in GetPage!"));
                    new Throwable().printStackTrace(System.err);
                    buffer.dump();
                    Db.getLogManager().flush();
                    if (Debug.DEBUG_DUMP_MEMORY) buffer.dumpMemInfo(Debug.DUMP_ALL, "memoryinfo");
                    Debug.dumpPageToLog(page);
                    if (Debug.DEBUG_PAGEINFO_TRACE) Debug.pageHistory(this.pageNumber);

                    System.exit(-1);

                }
            }

            readMetaData();
        } else {
            if (pageNumber.getPageNumber() > BTreeSpec.INVALID_PAGENO) {
                if (Debug.DEBUG_PAGEINFO_TRACE) {
                    String details = "Invalid BTreePage " + "[" + Thread.currentThread().getName() + "] time=" + System.currentTimeMillis() + " file=" + btreeSpec.getBtreeName() + " pg=" + pageNumber.toHexString();
                    logger.error(details);
                    Db.getLogManager().flush();
                    buffer.dump();
                    if (Debug.DEBUG_PAGEINFO_TRACE) Debug.pageHistory(this.pageNumber);
                    System.exit(-1);
                }

                throw new ChaiDBException(ErrorCode.BTREE_INVALID_BTREEPAGE, "[" + Thread.currentThread().getName() + "] time=" + System.currentTimeMillis() + " file=" + btreeSpec.getBtreeName() + " pg=" + pageNumber.toHexString());
            }

        }

    }

    /**
     * Read the page header from byte[] page, and set the appropriate variables in
     * BTreePage class.
     */
    private void readMetaData() throws ChaiDBException {
        prevPage.setPageNumber(ByteTool.bytesToInt(page, BTreeSpec.OFF_PREVPAGE, btreeSpec.isMsbFirst()));
        nextPage.setPageNumber(ByteTool.bytesToInt(page, BTreeSpec.OFF_NEXTPAGE, btreeSpec.isMsbFirst()));

        flags = ByteTool.bytesToInt(page, BTreeSpec.OFF_FLAGS, btreeSpec.isMsbFirst());
        keyType = page[BTreeSpec.OFF_KEYTYPE];
        lowerBound = ByteTool.bytesToShort(page, BTreeSpec.OFF_LOWERBOUND, btreeSpec.isMsbFirst());
        upperBound = ByteTool.bytesToShort(page, BTreeSpec.OFF_UPPERBOUND, btreeSpec.isMsbFirst());

        /* since BTree can only have one order, using internal node order
           to hold leaf node too. In that case, overflow pages are used to hold
           data.
         */
        if (isLeaf()) {
            order = btreeSpec.getInternalOrder(); // btreeSpec.getLeafOrder();
        } else if (isBTree()) {
            order = btreeSpec.getInternalOrder();
        } else if (isOverflow()) {
            order = 0;
        } else {
            logger.error("Invalid BTreePage " + pageNumber.toHexString() + " of " + btreeSpec.btree.getBTreeName() + "\r\n page:" + ByteTool.toHexString(2, page, 0, page.length));
            if (Debug.DEBUG_PAGEINFO_TRACE) {
                Db.getLogManager().flush();
                buffer.dump();
                if (Debug.DEBUG_PAGEINFO_TRACE) Debug.pageHistory(this.pageNumber);
                if (Debug.DEBUG_DUMP_MEMORY) buffer.dumpMemInfo(Debug.DUMP_ALL, "memoryinfo");
                System.exit(-1);
            }

            throw new ChaiDBException(ErrorCode.BTREE_INVALID_BTREEPAGE, "Unknown BTreePage Type:" + pageNumber.toHexString() + " of " + btreeSpec.btree.getBTreeName());
        }
    }

    /**
     * When first create a new page, set lowerBound and upperBound to the right
     * number. BtreeSpec and BufferedPage are passed as parameters; pageNumber
     * is got from btreeSpec, which keeps the first available free page number.
     *
     * @param btreeSpec
     * @param buffer
     */
    public static BTreePage newPage(BTreeSpec btreeSpec, PageBufferManager buffer, KernelContext kContext) throws ChaiDBException {
        int txnId = kContext.getLocker();
        boolean needLog = kContext.getNeedLog();

        BTreePage newPage = new BTreePage(btreeSpec, buffer);
        Page freePageInfo = buffer.getFreePage(btreeSpec, false, kContext);

        newPage.setLogInfo(txnId, needLog);

        /* begin:added by marriane 2001-9-29 for logging newPage log record */
        if (needLog) {
            int newPageNo = freePageInfo.getPageNumber().getPageNumber();

            BTreeNewPageLogRecord logRec = new BTreeNewPageLogRecord(freePageInfo.getPageNumber().getTreeId(), newPageNo, txnId, 1, -1, btreeSpec.btree.getType());
            logRec.log();
        }
        /* end:added by marriane for logging newPage log record */

        newPage.page = freePageInfo.getPageData();
        newPage.setPageNumber(freePageInfo.getPageNumber());

        //the following code may generate log records.
        newPage.setLogInfo(txnId, false);
        newPage.setInternal();
        newPage.setNextPage(new PageNumber(BTreeSpec.INVALID_PAGENO));
        newPage.setPrevPage(new PageNumber(BTreeSpec.INVALID_PAGENO));
        // ### not write to page yet!
        newPage.setLowerBound((short) BTreeSpec.PAGE_HEADER_SIZE);
        newPage.setUpperBound((short) newPage.btreeSpec.getPageSize());
        newPage.setLogInfo(txnId, needLog);

        return newPage;

    }

    void setKeyType(byte keyType) throws ChaiDBException {
        //keyType is only permitted to set one time.
        if (this.keyType > -1) return;

        /***************** Add by leon,2001-9-27 14:56 ********************/
        if (needLog) {
            byte[] oldV = {this.keyType};
            byte[] newV = {keyType};
            int pgno = pageNumber.getPageNumber();
            BTreeReplLogRecord lr = new BTreeReplLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeSpec.OFF_KEYTYPE, oldV, newV, btreeSpec.btree.getType());

            lr.log();
        }
        /*******************************************************************/
        this.keyType = keyType;
        page[BTreeSpec.OFF_KEYTYPE] = keyType;
    }

    byte getKeyType() {
        return keyType;
    }

    public byte[] getPage() {
        return page;
    }

    public void release(boolean dirty) {
        buffer.releasePage(pageNumber.getTreeId(), pageNumber, dirty);
    }

    public BTreeSpec getBtreeSpec() {
        return btreeSpec;
    }

    public PageBufferManager getBuffer() {
        return buffer;
    }

    public PageNumber getPageNumber() {
        return pageNumber;
    }

    void setLowerBound(short lowerBound) {
        this.lowerBound = lowerBound;
        System.arraycopy(ByteTool.shortToBytes(this.lowerBound), 0, this.page, BTreeSpec.OFF_LOWERBOUND, 2);
    }

    void setUpperBound(short upperBound) {
        this.upperBound = upperBound;
        System.arraycopy(ByteTool.shortToBytes(this.upperBound), 0, this.page, BTreeSpec.OFF_UPPERBOUND, 2);
    }

    void setPageNumber(PageNumber pageNumber) {
        this.pageNumber.setPageNumber(pageNumber);
        System.arraycopy(ByteTool.intToBytes(this.pageNumber.getPageNumber()), 0, this.page, BTreeSpec.OFF_PAGENUMBER, 4);
    }

    void setPrevPage(PageNumber prevPage) throws ChaiDBException {
        int oldV = this.prevPage.getPageNumber();
        int newV = prevPage.getPageNumber();
        /***************** Add by leon,2001-9-27 14:56 ********************/
        if (needLog && (oldV != newV)) {
            int pgno = pageNumber.getPageNumber();

            BTreeReplLogRecord lr = new BTreeReplLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeSpec.OFF_PREVPAGE, ByteTool.intToBytes(oldV, btreeSpec.isMsbFirst()), ByteTool.intToBytes(newV, btreeSpec.isMsbFirst()), btreeSpec.btree.getType());

            lr.log();
        }
        /******************************************************************/
        this.prevPage.setPageNumber(prevPage);
        try {
            System.arraycopy(ByteTool.intToBytes(this.prevPage.getPageNumber()), 0, this.page, BTreeSpec.OFF_PREVPAGE, 4);
        } catch (Exception e) {
            logger.error("Victim=" + pageNumber.toHexString() + ",prev=" + prevPage.toHexString());
            buffer.dump(this.pageNumber.getTreeId());
            logger.error(e);
            //Thread.dumpStack(); System.exit(1);
        }
    }

    void setNextPage(PageNumber nextPage) throws ChaiDBException {
        int oldV = this.nextPage.getPageNumber();
        int newV = nextPage.getPageNumber();
        /***************** Add by leon,2001-9-27 14:56 ********************/
        if (needLog && (oldV != newV)) {
            int pgno = pageNumber.getPageNumber();

            BTreeReplLogRecord lr = new BTreeReplLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeSpec.OFF_NEXTPAGE, ByteTool.intToBytes(oldV, btreeSpec.isMsbFirst()), ByteTool.intToBytes(newV, btreeSpec.isMsbFirst()), btreeSpec.btree.getType());

            lr.log();
        }
        /******************************************************************/
        this.nextPage.setPageNumber(nextPage);
        System.arraycopy(ByteTool.intToBytes(this.nextPage.getPageNumber()), 0, this.page, BTreeSpec.OFF_NEXTPAGE, 4);
    }

    void setFlags(int flags) throws ChaiDBException {
        int oldV = this.flags;
        int newV = flags;
        /***************** Add by leon,2001-9-27 14:56 ********************/
        if (needLog && (oldV != newV)) {
            int pgno = pageNumber.getPageNumber();
            BTreeReplLogRecord lr = new BTreeReplLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeSpec.OFF_FLAGS, ByteTool.intToBytes(oldV, btreeSpec.isMsbFirst()), ByteTool.intToBytes(newV, btreeSpec.isMsbFirst()), btreeSpec.btree.getType());

            lr.log();
        }
        /******************************************************************/
        this.flags = flags;
        System.arraycopy(ByteTool.intToBytes(this.flags), 0, this.page, BTreeSpec.OFF_FLAGS, 4);
    }

    void setOverflow() throws ChaiDBException {
        setFlags(4);
        order = 0;
    }

    void setLeaf() throws ChaiDBException {
        setFlags(2);
        order = btreeSpec.getLeafOrder();
    }

    void setInternal() throws ChaiDBException {
        setFlags(1);
        order = btreeSpec.getInternalOrder();
    }

    public final boolean isBTree() {
        return (flags & 0x03) != 0;
    }

    public final boolean isLeaf() {
        return (flags & 0x02) != 0;
    }

    public final boolean isOverflow() {
        return (flags & 0x04) != 0;
    }

    /**
     * Get the number of nodes in a page
     */
    public int getCurrNodeNumbers() {
        return (lowerBound - BTreeSpec.PAGE_HEADER_SIZE) / 2;
    }

    /**
     * insert a node in a page sorted by the key; guaranteed the node has
     * enough space. If key already exist, and mode == STORE_REPLACE, the
     * data is replaced by the new data; otherwise, do not touch anything.
     *
     * @param key
     * @param data Can be the value or the pageNumber
     */
    private void insertNode(Key key, byte[] data, short mode, KernelContext kContext) throws ChaiDBException {
        int txnId = kContext.getLocker();
        boolean needLog = kContext.getNeedLog();

        try {
            // fix the current page
            page = buffer.getPage(pageNumber.getTreeId(), pageNumber);

            if (page == null)
                throw new ChaiDBException(ErrorCode.BTREE_INVALID_BTREEPAGE, "Page is null: " + pageNumber.toHexString() + " of " + btreeSpec.getBtreeName());
            if (Debug.DEBUG_CHECKPAGENUMBER) {
                if (!Debug.checkPageNumber(pageNumber, this.page)) {
                    logger.fatal(new Throwable("there is hole in GetPage!"));
                    this.buffer.dump();
                    Db.getLogManager().flush();
                    Debug.dumpPageToLog(page);
                    if (Debug.DEBUG_PAGEINFO_TRACE) Debug.pageHistory(this.pageNumber);

                    System.exit(-1);

                }
            }


            setKeyType((byte) key.getKeyType());

            //====>>> 1. calculate the place to insert the node
            // sort insert
            KeyIndex keyIndex = sortKey(key);
            boolean keyExist = keyIndex.keyExist;
            int nextIndex = keyIndex.keyIndex;
            int currNode = getCurrNodeNumbers();
            // check mode to replace existing key's value or just return
            if (keyExist && mode == IDBIndex.STORE_INSERT) {
                // untouch the node, may throw exception later &&&
                // unfix this page
                buffer.releasePage(pageNumber.getTreeId(), pageNumber, false);
                // details -ranjeet
                String details = "The key value is " + new String(key.toBytes()) + ".";
                /* Modified by ben at aug, 12, 2002. pending issue */
                throw new ChaiDBException(ErrorCode.ENTRY_ALREADY_EXIST, details);
            }
            /* Modified by ben zhang at aug, 12, 2002 */
            int newNodeSize = BTreeSpec.DATA_NODE_HEADER_SIZE + data.length;

            if (isLeaf()) {
                //=====>>> If is a leaf page
                //=====>>> 2. insert / replace key/data pair
//                BTreeNode leafNode;
                DataPage dataPage = null;
                if (keyExist) {
                    insertNodeWhenKeyExistedAndIsLeaf(nextIndex, kContext, key, data, mode);
                } else { //Key not exist
                    insertNodeWhenKeyNotExistedAndIsLeaf(key, needLog, txnId, kContext, dataPage, newNodeSize, data, keyExist, nextIndex, currNode, mode);
                }
            } else if (isBTree()) {
                //=====>>> If is an internal page
                if (data.length != 4) {
                    throw new ChaiDBException(ErrorCode.BTREE_INT_PAGE_NUMBER);
                }

                //=====>>> 3a. if node already exists, simply change the pageNumber and return
                if (keyExist) {
                    insertNodeWhenKeyExistedAndIsBTree(nextIndex, needLog, data, txnId);
                    return;
                }

                //======>>> 3b. insert key/data pair
                insertNodeWhenKeyNotExistedAndIsBTree(key, needLog, data, txnId, nextIndex, currNode, kContext);

            }
            // unfix this page
            buffer.releasePage(pageNumber.getTreeId(), pageNumber, true);
        } finally {
            //TODO move release lock here?
        }
    }

    private void insertNodeWhenKeyNotExistedAndIsBTree(Key key, boolean needLog, byte[] data, int txnId, int nextIndex, int currNode, KernelContext kContext) throws ChaiDBException {
        /*Modified by ben zhang at aug, 12, 2002 */
        int nodeSize = BTreeSpec.NODE_HEADER_SIZE + key.size();

        /* begin : added by marriane 2001-12-28 for bind all insert node log records*/
        if (needLog) {
            byte[] newNode = null;
//                    if (nodeSize <= btreeSpec.getInternalNodeSize() &&
//                        !keyExist) {
            // @@@ Modified by Arefool for the key must not exist here.
            if (nodeSize <= btreeSpec.getInternalNodeSize()) {
                byte[] headArr = new byte[BTreeSpec.NODE_HEADER_SIZE];
                /*Modified by ben zhang at Aug, 12, 2002 */
                System.arraycopy(ByteTool.intToBytes(key.size()), 0, headArr, BTreeSpec.NODE_OFF_KEYSIZE, 4);
                System.arraycopy(data, 0, headArr, BTreeSpec.NODE_OFF_PAGENUMBER, 4);
                headArr[BTreeSpec.NODE_OFF_FLAGS] = (nodeSize <= btreeSpec.getInternalNodeSize()) ? (byte) 0 : (byte) 2;
                newNode = headArr;
                /*Modified by ben zhang at aug, 12, 2002 */
                newNode = ByteTool.append(newNode, key.toBytes());
            }
            BTreeInsertNodeLogRecord lr = new BTreeInsertNodeLogRecord(pageNumber.getTreeId(), pageNumber.getPageNumber(), txnId, ByteTool.bytesToShort(ByteTool.copyByteArray(page, BTreeSpec.OFF_LOWERBOUND, 2), 0, btreeSpec.isMsbFirst()), ByteTool.bytesToShort(ByteTool.copyByteArray(page, BTreeSpec.OFF_UPPERBOUND, 2), 0, btreeSpec.isMsbFirst()), (short) (lowerBound + 2), (short) (upperBound - btreeSpec.getInternalNodeSize()), (short) (BTreeSpec.PAGE_HEADER_SIZE + nextIndex * 2),
                    //headOffset
                    (short) (upperBound - btreeSpec.getInternalNodeSize()),
                    //nodeOffset
                    (nextIndex < currNode) ? ByteTool.copyByteArray(page, BTreeSpec.PAGE_HEADER_SIZE + nextIndex * 2, (currNode - nextIndex) * 2) : null,
                    //move index
                    newNode, true, (short) btreeSpec.getNodeSize(), btreeSpec.btree.getType());
            lr.log();
        }
        /* end : added by marriane 2001-12-28 for bind all insert node log records*/

        setUpperBound((short) (upperBound - btreeSpec.getInternalNodeSize()));
        setLowerBound((short) (lowerBound + 2));

        if (nextIndex < currNode) {
            System.arraycopy(page, (BTreeSpec.PAGE_HEADER_SIZE + nextIndex * 2), page, (BTreeSpec.PAGE_HEADER_SIZE + (nextIndex + 1) * 2), (currNode - nextIndex) * 2);
        }
        System.arraycopy(ByteTool.shortToBytes(upperBound), 0, page, (BTreeSpec.PAGE_HEADER_SIZE + nextIndex * 2), 2);
        //logger.debug(" internalpage:" + pageNumber +" lb:" + lowerBound + " ub:" + upperBound);
        /*Modified by ben zhang at aug, 12, 2002 */
        PageNumber pageNum = new PageNumber(ByteTool.bytesToInt(data, 0, btreeSpec.isMsbFirst()));
        pageNum.setTreeId(btreeSpec.btree.getBtreeId());
        BTreeNode internalNode = BTreeNode.createNewBTreeNode(this, upperBound, key.toBytes(), pageNum);

        // size in byte of the node needed
        if (nodeSize <= btreeSpec.getInternalNodeSize()) {
            internalNode.setFlags((byte) 0);
        } else {
            internalNode.setFlags((byte) 2);
        }
        /*Modified by ben zhang at aug, 12, 2002 */
        internalNode.setInternalNode(key.toBytes(), kContext);
    }

    private void insertNodeWhenKeyExistedAndIsBTree(int nextIndex, boolean needLog, byte[] data, int txnId) throws ChaiDBException {
        // replace the pageNumber
        int offset = BTreeSpec.PAGE_HEADER_SIZE + 2 * nextIndex;
        // Convert the two-byte pointer to an short
        short nodeOffset = ByteTool.bytesToShort(page, offset, btreeSpec.isMsbFirst());

        /************** Add by leon,2001-9-27 14:56 ***********/
        if (needLog) {
            int pgno = pageNumber.getPageNumber();
            byte[] oldV = ByteTool.copyByteArray(page, (int) nodeOffset + BTreeSpec.NODE_OFF_PAGENUMBER, 4);
            byte[] newV = data;
            if (oldV != newV) {
                BTreeReplLogRecord lr = new BTreeReplLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeSpec.OFF_PREVPAGE, oldV, newV, btreeSpec.btree.getType());

                lr.log();
            }
        }
        /******************************************************/
        System.arraycopy(data, 0, page, (int) nodeOffset + BTreeSpec.NODE_OFF_PAGENUMBER, 4);

        // unfix this page
        buffer.releasePage(pageNumber.getPageNumber(), pageNumber, true);
    }

    private void insertNodeWhenKeyNotExistedAndIsLeaf(Key key, boolean needLog, int txnId, KernelContext kContext, DataPage dataPage, int newNodeSize, byte[] data, boolean keyExist, int nextIndex, int currNode, short mode) throws ChaiDBException {
        BTreeNode leafNode;
        //For normal Btree, docid is always be 0;
        int docid = 0;
        if (btreeSpec.btree.getType() == IDBIndex
                .ID2NODE_BTREE) docid = ((NodeId) key).getDocId();

        // Get the latest data page within which we store data node
        boolean pageFixed = false;

        //add by stanley
        boolean hasPageForOverflow = false;
        PageNumber PgnForOverflow = null;
        DataPage DataPageForOverflow = null;
        Lock lockforOverflow = null;

        PageNumber latestDataPageNumber = buffer.getLatestDataPage(docid, pageNumber.getTreeId(), needLog ? new Integer(txnId) : null);

        Lock lock = null;
        boolean isValidPage;
        while (latestDataPageNumber != null && latestDataPageNumber.getPageNumber() > 0) {

            lock = BTreeLock.acquire(kContext, LockManager.LOCK_WAITING, latestDataPageNumber, LockManager.LOCK_WRITE);

            //Usually the page we get should not stay at freelist. But there is one exception:
            //With txn, each txn get LDP from GLDP list. Thus assuming
            //(1) txn1 get a LDP (pageNumber) from GLDP list and remove it from the list and
            //paused due to OS scheduler
            //(2) At this moment, txn2 deletes all nodes in this page
            //, finds that it is not a LDP by isLatestDataPage() (checking a page in its own LDP
            //list and GLDP list), then frees this page.
            //(3) Later txn1 resumes getting the LDP page with the pagenumber it got at (1), then
            // finds it has been freed. The following exception occurs!!!!!!!!
            //Hereby, when we get a LDP, we must catch this exception and get a new LDP. See codes

            try {
                isValidPage = true;
                dataPage = new DataPage(btreeSpec.btree.getBtreeId(), latestDataPageNumber, btreeSpec, buffer);
            } catch (ChaiDBException e) {
                if (e.getErrorCode() != ErrorCode.BTREE_USE_FREEPAGE) {
                    logger.error(e);
                    throw e;
                }
                isValidPage = false;
            }
            /* we abide by the following algorithm to get a data page
             * 1.if  the latest data page is big enough to hold all the new datanode then use it. else go to 2
             * 2.if a new data page is big enough to hold all the new datanode then use it. else go to 3
             * 3.if the latest data page is big enough to hold BTreeSpec.DATA_NODE_HEADER_SIZE + 4 then use it. else go to 2
             * 4.we new a data page and use it.
            */
            if (isValidPage && !dataPage.isOverflow()) {

                if (dataPage.getFreeSpace() >= newNodeSize) {

                    if (!(btreeSpec.btree.getType() == IDBIndex.ID2NODE_BTREE)) {
                        pageFixed = true;
                        break;
                    } else { // if this tree is special, this data page must
                        // belong to this doc if there are still data in
                        // this datapage
                        int docID = ((NodeId) key).getDocId();
                        if (dataPage.getCurrNodeNumbers() == 0) {
                            dataPage.setDocID(docID);
                            pageFixed = true;
                            break;
                        } else {
                            if (dataPage.isOfDoc(docID)) {
                                pageFixed = true;
                                break;
                            } else {
                                // release the data page

                                buffer.releasePage(latestDataPageNumber
                                        .getTreeId(), latestDataPageNumber, false);
                                // because we did NOT update this page, so we
                                // release it at once
                                BTreeLock.release(kContext, pageNumber
                                        .getTreeId(), lock);

                                latestDataPageNumber = buffer
                                        .getANewLatestDataPage(docid, pageNumber.getTreeId(), needLog ? new Integer(txnId) : null);
                            }
                        }
                    }

                } else if (!hasPageForOverflow && dataPage.getFreeSpace() >= BTreeSpec.DATA_NODE_HEADER_SIZE + 4) {

                    if (!(btreeSpec.btree.getType() == IDBIndex.ID2NODE_BTREE)) {
                        hasPageForOverflow = true;
                        PgnForOverflow = latestDataPageNumber;
                        DataPageForOverflow = dataPage;
                        lockforOverflow = lock;
                    } else { // if this tree is special, this data page must
                        // belong to this doc if there are still data in
                        // this datapage
                        int docID = ((NodeId) key).getDocId();
                        if (dataPage.getCurrNodeNumbers() == 0) {
                            dataPage.setDocID(docID);
                            hasPageForOverflow = true;
                            PgnForOverflow = latestDataPageNumber;
                            DataPageForOverflow = dataPage;
                            lockforOverflow = lock;
                        } else {
                            if (dataPage.isOfDoc(docID)) {
                                hasPageForOverflow = true;
                                PgnForOverflow = latestDataPageNumber;
                                DataPageForOverflow = dataPage;
                                lockforOverflow = lock;
                            } else {
                                // release the data page

                                buffer.releasePage(latestDataPageNumber
                                        .getTreeId(), latestDataPageNumber, false);
                                // because we did NOT update this page, so we
                                // release it at once
                                BTreeLock.release(kContext, pageNumber
                                        .getTreeId(), lock);
                            }
                        }
                    }
                    latestDataPageNumber = buffer.getANewLatestDataPage(docid, pageNumber.getTreeId(), needLog ? new Integer(txnId) : null);
                } else {
                    //The datapage is not suitable then release it
                    buffer.releasePage(latestDataPageNumber.getTreeId(), latestDataPageNumber, false);
                    //because we did NOT update this page, so we release it at once
                    BTreeLock.release(kContext, pageNumber.getTreeId(), lock);

                    latestDataPageNumber = buffer.getANewLatestDataPage(docid, pageNumber.getTreeId(), needLog ? new Integer(txnId) : null);
                }

            } else {
                //The datapage is not suitable then release it
                buffer.releasePage(latestDataPageNumber.getTreeId(), latestDataPageNumber, false);
                //because we did NOT update this page, so we release it at once
                BTreeLock.release(kContext, pageNumber.getTreeId(), lock);

                latestDataPageNumber = buffer.getANewLatestDataPage(docid, pageNumber.getTreeId(), needLog ? new Integer(txnId) : null);
            }
        }
        // size in byte of the node needed
        if (!pageFixed) {
            if (!hasPageForOverflow) {
                dataPage = DataPage.newPage(btreeSpec, buffer, false, kContext, docid);
                if (btreeSpec.btree.getType() == IDBIndex.ID2NODE_BTREE) dataPage.setDocID(docid);
            } else {
                if (newNodeSize <= BTreeSpec.PAGE_SIZE - BTreeSpec.PAGE_HEADER_SIZE) {
                    dataPage = DataPage.newPage(btreeSpec, buffer, false, kContext, docid);
                    if (btreeSpec.btree.getType() == IDBIndex.ID2NODE_BTREE) dataPage.setDocID(docid);

                    buffer.releasePage(PgnForOverflow.getTreeId(), PgnForOverflow, false);
                    // because we did NOT update this page, so we release it
                    // at once
                    BTreeLock.release(kContext, pageNumber.getTreeId(), lockforOverflow);

                } else {
                    dataPage = DataPageForOverflow;
                    latestDataPageNumber = PgnForOverflow;
                    lock = lockforOverflow;
                }
            }
            // lock=returnLock[0];
        } else {
            if (hasPageForOverflow) {
                buffer.releasePage(PgnForOverflow.getTreeId(), PgnForOverflow, false);
                //because we did NOT update this page, so we release it at once
                BTreeLock.release(kContext, pageNumber.getTreeId(), lockforOverflow);
            }
        }

        /*Modified by ben zhang at Aug, 12, 2002 */
        int nodeSize = BTreeSpec.NODE_HEADER_SIZE + key.size();

        /* begin : added by marriane 2001-12-28 for bind all insert
         * node log records
         */
        if (needLog) {
            bindAllInsertNodeLogRecords(dataPage, data, nodeSize, keyExist, key, txnId, nextIndex, currNode);
        }
        /* end : added by marriane 2001-12-28 for bind all insert
         * node log records
         */

        setUpperBound((short) (upperBound - btreeSpec.getLeafNodeSize()));
        setLowerBound((short) (lowerBound + 2));

        if (nextIndex < currNode) {
            System.arraycopy(page, (BTreeSpec.PAGE_HEADER_SIZE + nextIndex * 2), page, (BTreeSpec.PAGE_HEADER_SIZE + (nextIndex + 1) * 2), (currNode - nextIndex) * 2);
        }
        System.arraycopy(ByteTool.shortToBytes(upperBound), 0, page, (BTreeSpec.PAGE_HEADER_SIZE + nextIndex * 2), 2);
        // insert data node in data page
        /* Modified by ben zhang at Aug, 12, 2002 Pending issue
         * whether key is necessary
         */
        // @@@ Modified by Kurt

        byte[] newPageOff = null;
        short newOffset = 0;
        if (btreeSpec.btree.getType() == IDBIndex.HYPER_BTREE) {
            newPageOff = insertDupNode(dataPage, key, data, mode, kContext, -1);
        } else {
            newOffset = dataPage.insertNode(key.toBytes(), data, mode, kContext, -1, (byte) 0);
        }
//
//                short dataNodeOff=dataPage.insertNode(key,data,mode,kContext,-1);
        // @@@ Modified by Kurt

        // unfix the page
        buffer.releasePage(dataPage.pageNumber.getTreeId(), dataPage.pageNumber, true);

        // create a new BTree leaf node
        /*Modified by ben zhang at aug, 12, 2002 */
        leafNode = BTreeNode.createNewBTreeNode(this, upperBound, key.toBytes(), dataPage.getPageNumber());
        if (nodeSize <= btreeSpec.getLeafNodeSize()) {
            // if the page has enough space to hold the new node
            leafNode.setFlags((byte) 0);
        } else {
            // The leaf page doesn't have enough space to hold data
            leafNode.setFlags((byte) 3);
        }
        short dataNodeOff = 0;
        // @@@ Modified by Kurt
        if (btreeSpec.btree.getType() == IDBIndex.HYPER_BTREE) {
            if (newPageOff.length >= 4) {
                dataNodeOff = (short) ByteTool.bytesToInt(newPageOff, 0, btreeSpec.isMsbFirst());
                leafNode.setDataNodeOffset(dataNodeOff);
            }
            if (newPageOff.length == 8) {
                int newPageNum = ByteTool.bytesToInt(newPageOff, 4, btreeSpec.isMsbFirst());
                leafNode.setPageNumber(newPageNum);
            }
        } else {
            leafNode.setDataNodeOffset(newOffset);
        }
        // @@@ Modified by Kurt

        // We only need to store key in the leaf page now
        /*Modified by ben zhang at aug, 12, 2002 */
        leafNode.setInternalNode(key.toBytes(), kContext);
        //reput it to LDPL if this datapage is allocated this time
        if (!pageFixed) {
            if (dataPage.isOverflow()) {
                String details = Debug.getDebugInfo() + " want to put overflowpage[" + dataPage.pageNumber + "] to LDPL.";
                throw new ChaiDBException(ErrorCode.BTREE_DEBUG, details);
            }
            buffer.putLatestDataPage(docid, dataPage.pageNumber, needLog ? new Integer(txnId) : null);
        } else {
            /* if the following statement is not within "else",
             * the below line must be uncommented
             */
            if (needLog) BTreeLock.change(kContext, lock, latestDataPageNumber.getTreeId(), LockManager.LOCK_READ);
            else BTreeLock.release(kContext, pageNumber.getTreeId(), lock);

        }

    }

    private void bindAllInsertNodeLogRecords(DataPage dataPage, byte[] data, int nodeSize, boolean keyExist, Key key, int txnId, int nextIndex, int currNode) throws ChaiDBException {
        byte[] newNode = null;
        byte[] newDataNode = null;
        int dataPageOldUpBound = dataPage.upperBound;
        int dataPageNewUpBound = 0;
        int newDataNodeSize = BTreeSpec.DATA_NODE_HEADER_SIZE + data.length;
        byte dataNodeFlag = 0;
        short dataNodeSpace = 0;
        if (dataPageOldUpBound - BTreeSpec.PAGE_HEADER_SIZE >= newDataNodeSize) {
            dataPageNewUpBound = dataPageOldUpBound - newDataNodeSize;
            dataNodeSpace = (short) newDataNodeSize;
        } else {
            dataNodeFlag = (byte) 1;
            dataNodeSpace = (short) (BTreeSpec.DATA_NODE_HEADER_SIZE + 4);
            dataPageNewUpBound = dataPageOldUpBound - BTreeSpec
                    .DATA_NODE_HEADER_SIZE - 4;
        }
        if (nodeSize <= btreeSpec.getInternalNodeSize() && !keyExist) {
            byte[] headArr = new byte[BTreeSpec.NODE_HEADER_SIZE];
            /*Modified by ben zhang at aug, 12, 2002 */
            System.arraycopy(ByteTool.intToBytes(key.size(), btreeSpec.isMsbFirst()), 0, headArr, BTreeSpec.NODE_OFF_KEYSIZE, 4);
            System.arraycopy(ByteTool.intToBytes(dataPage.getPageNumber().getPageNumber(), btreeSpec.isMsbFirst()), 0, headArr, BTreeSpec.NODE_OFF_PAGENUMBER, 4);
            headArr[BTreeSpec.NODE_OFF_FLAGS] = (nodeSize <= btreeSpec.getLeafNodeSize()) ? (byte) 0 : (byte) 3;
            System.arraycopy(ByteTool.shortToBytes((short) dataPageNewUpBound),
                    //dataNodeOffset
                    0, headArr, BTreeSpec.NODE_OFF_DATANODEOFFSET, 2);
            newNode = headArr;
            /* Modified by ben zhang at aug, 12, 2002*/
            newNode = ByteTool.append(newNode, key.toBytes());
        }

        if ((dataPageOldUpBound - BTreeSpec.PAGE_HEADER_SIZE >= newDataNodeSize) && !keyExist) {

            byte[] dataNodeHeadArr = new byte[BTreeSpec.DATA_NODE_HEADER_SIZE];
            System.arraycopy(ByteTool.intToBytes(data.length), 0, dataNodeHeadArr, BTreeSpec.DATA_NODE_OFF_DATASIZE,
                    //@@@ Modified by Kurt
//                                                BTreeSpec.DATA_NODE_OFF_PAGENUMBER,
                    4);
            dataNodeHeadArr[BTreeSpec.DATA_NODE_OFF_FLAGS] = dataNodeFlag;

            System.arraycopy(ByteTool.shortToBytes(dataNodeSpace), 0, dataNodeHeadArr, BTreeSpec.DATA_NODE_OFF_ALLOCATED_SPACE, 2);

            newDataNode = dataNodeHeadArr;
            newDataNode = ByteTool.append(newDataNode, data);
        }

        ///debug
        BTreeInsertLeafNodeLogRecord lr = new BTreeInsertLeafNodeLogRecord(pageNumber.getTreeId(), pageNumber.getPageNumber(), txnId, lowerBound, upperBound, (short) (lowerBound + 2), (short) (upperBound - btreeSpec.getLeafNodeSize()), (short) (BTreeSpec.PAGE_HEADER_SIZE + nextIndex * 2),
                //headOffset
                (short) (upperBound - btreeSpec.getLeafNodeSize()),
                //nodeOffset
                (nextIndex < currNode) ? ByteTool.copyByteArray(page, BTreeSpec.PAGE_HEADER_SIZE + nextIndex * 2, (currNode - nextIndex) * 2) : null,
                //move index
                newNode, dataPage.getPageNumber().getPageNumber(), (short) dataPageOldUpBound, (short) dataPageNewUpBound, newDataNode, (short) btreeSpec.getNodeSize(), btreeSpec.btree.getType());
        lr.log();
    }

    private void insertNodeWhenKeyExistedAndIsLeaf(int nextIndex, KernelContext kContext, Key key, byte[] data, short mode) throws ChaiDBException {
        BTreeNode leafNode;
        DataPage dataPage;
        // get the existing node
        leafNode = getNode(nextIndex);
        // insert data node in data page
        PageNumber dataPageNumber = new PageNumber(leafNode.getPageNumber());

        dataPage = new DataPage(btreeSpec.btree.getBtreeId(), dataPageNumber, btreeSpec, buffer);
        //logger.debug(""+ dataPageNumber );
        /* Modified by ben zhang at Aug, 12, 2002 Pending issue whether key is necessary */
//                    dataPage.insertNode(key.toBytes(), data, mode, kContext,
//                                        leafNode.getDataNodeOffset());
        /* Modified by Kurt Sung at Feb.24, 2003 to support duplicated key */
        if (btreeSpec.btree.getType() == IDBIndex.HYPER_BTREE) {
            insertDuplicatedNode(dataPage, key, data, mode, kContext, leafNode);
        } else {
            dataPage.insertNode(key.toBytes(), data, mode, kContext, leafNode.getDataNodeOffset(), (byte) 0);
        }

        // unfix this data page
        buffer.releasePage(dataPageNumber.getTreeId(), dataPageNumber, true);
    }

    private void insertDuplicatedNode(DataPage dataPage, Key key, byte[] data, short mode, KernelContext kContext, BTreeNode leafNode) throws ChaiDBException {
        byte[] newPageOff = null;
//        short newOffset = 0;
        newPageOff = insertDupNode(dataPage, key, data, mode, kContext, leafNode.getDataNodeOffset());
        short dataNodeOff = 0;
//                        if (btreeSpec.btree.isSpecial) {
        if (newPageOff != null) {
            if (newPageOff.length >= 4) {
                dataNodeOff = (short) ByteTool.bytesToInt(newPageOff, 0, btreeSpec.isMsbFirst());
                leafNode.setDataNodeOffset(dataNodeOff);
            }
            if (newPageOff.length == 8) {
                int newPageNum = ByteTool.bytesToInt(newPageOff, 4, btreeSpec.isMsbFirst());
                leafNode.setPageNumber(newPageNum);
            }
        }
//                        } else {
//                            leafNode.setDataNodeOffset(newOffset);
//                        }
        leafNode.setInternalNode(key.toBytes(), kContext);
    }

    /**
     * Inner utility class used for sortKey return result
     */
    class KeyIndex {
        boolean keyExist;
        int keyIndex;

        KeyIndex(boolean keyExist, int keyIndex) {
            this.keyExist = keyExist;
            this.keyIndex = keyIndex;
        }
    }

    /**
     * If key found in the page, set keyExist to true; return the right place
     * to insert the key, or the index of the key if exists.
     *
     * @return the index to put the key, and whether the key already exists
     * @throws ChaiDBException thrown out by getNode(int)
     */
    private KeyIndex sortKey(Key key) throws ChaiDBException {
        // binary search
        int cmp;
        int currNode = getCurrNodeNumbers();
        if (currNode <= 0) return new KeyIndex(false, 0);
        int first = 0;
        int last = currNode - 1;
        int currIn = 0;
        while (last > first) {
            currIn = (first + last) / 2;
            /* Modified by ben zhang at Aug, 12, 2002 */
            Key tmpKey = getNode(currIn).getKey();
            cmp = tmpKey.compareTo(key);
            if (cmp == 0) return new KeyIndex(true, currIn);
            else if (cmp > 0) // a[currIn] > key
                last = currIn - 1;
            else { // a[currIn] < key
                /*Modified by ben zhang at Aug, 12, 2002 */
                tmpKey = getNode(currIn + 1).getKey();
                int tmp = tmpKey.compareTo(key);
                if (tmp > 0) return new KeyIndex(false, currIn + 1);
                else if (tmp == 0) return new KeyIndex(true, currIn + 1);
                else // a[currIn+1] < key
                    first = currIn + 1;
            }
        }
        /*Modified by ben zhang at Aug, 12, 2002*/
        Key tmpKey1 = getNode(first).getKey();
        cmp = tmpKey1.compareTo(key);
        if (cmp > 0) return new KeyIndex(false, first);
        else if (cmp == 0) return new KeyIndex(true, first);
        else return new KeyIndex(false, first + 1);
    }

    /**
     * Insert a node - key/data pair
     * recursive method, data can be pageNumber byte[4]
     *
     * @param key
     * @param data can be value or pageNumber - byte[4]
     * @param mode STORE_INSERT, or STORE_REPLACE
     * @return null if root doesn't change. Otherwise, new root.
     */
    public PageNumber insert(Key key, byte[] data, short mode, KernelContext kContext) throws ChaiDBException {
        int txnId = kContext.getLocker();
        boolean needLog = kContext.getNeedLog();

        try {
            /********* add by leon *********/
            setLogInfo(txnId, needLog);
            /*****************************/

            // fix the current page
            page = buffer.getPage(pageNumber.getTreeId(), pageNumber);

            if (page == null)
                throw new ChaiDBException(ErrorCode.BTREE_INVALID_BTREEPAGE, "Page is null: " + pageNumber.toHexString() + " of " + btreeSpec.getBtreeName());
            if (Debug.DEBUG_CHECKPAGENUMBER) {
                if (!Debug.checkPageNumber(pageNumber, this.page)) {
                    logger.fatal(new Throwable("there is hole in GetPage!"));
                    this.buffer.dump();
                    Db.getLogManager().flush();
                    Debug.dumpPageToLog(page);
                    if (Debug.DEBUG_PAGEINFO_TRACE) Debug.pageHistory(this.pageNumber);

                    System.exit(-1);

                }
            }

            int currNode = getCurrNodeNumbers();
            if (currNode < order) {
                insertWhenCurNodeLessThanOrder(key, data, mode, kContext, txnId, needLog);
                return null;
            }

            //@@@ new comment: insert when cur node larger than order
            // if key exist
            KeyIndex keyIndex = sortKey(key);
            if (keyIndex.keyExist) {
                if (isLeaf()) {
                    insertNode(key, data, mode, kContext);
                    buffer.releasePage(pageNumber.getTreeId(), pageNumber, true);
                    /*XZ*/
                    return null;
                } else if (isBTree()) {
                    shouldNotHappen(key, keyIndex);
                }
            }

            // split the page
            //====>>> 1. create new page
            BTreePage newPage = newPage(btreeSpec, buffer, kContext);
            //set keyType of new page be one of this page
            newPage.setKeyType(getKeyType());

            //!!!!
            //for sibling, we add it in the locklist so that we
            //can release outside.
            //locks.add(lock);

            Key newKey = null;
            //====>>> 2a. if this is leaf page
            if (isLeaf()) {
                newKey = insertWhenIsLeaf(newPage, txnId, needLog, keyIndex, key, currNode, data, mode, kContext);

                //=====>>> 2b. if this is internal page
            } else if (isBTree()) {
                newKey = insertWhenIsBTree(newPage, txnId, needLog, keyIndex, key, currNode, data, mode, kContext);

            }
            //====>>> 3a. if this is not root page
            if (this.prevPage.getPageNumber() != 0) {
                PageNumber newRoot = insertWhenNotRootPage(newPage, txnId, needLog, newKey, mode, kContext);

                return newRoot;
                //====>>> 3b. if this is root page
            } else {
                BTreePage rootPage = insertWhenIsRootPage(kContext, txnId, needLog, newKey, newPage, mode);
                return rootPage.pageNumber;
            }
        } finally {
            /**todo release lock */
        }
    }

    private void shouldNotHappen(Key key, KeyIndex keyIndex) throws ChaiDBException {
        // should not happen
        // details -ranjeet
        String details = "Key cannot be inserted or replaced at an internal page.";
        String kk = new String(key.toBytes()); //Modified by ben at Aug,12,2002. Pending, I don't know whether correct.
        logger.error("key=" + kk + ", KeyIndex=" + keyIndex
                .keyIndex + ",pg=" + pageNumber.toHexString());
        //Debug.pageHistory(Integer.toHexString(pageNumber.getPageNumber()));
        Debug.flushPages();
        if (Debug.DEBUG_THREAD) Debug.flushThreads();
        throw new ChaiDBException(ErrorCode.ENTRY_ALREADY_EXIST, details);
    }

    private BTreePage insertWhenIsRootPage(KernelContext kContext, int txnId, boolean needLog, Key newKey, BTreePage newPage, short mode) throws ChaiDBException {
        BTreePage rootPage = newPage(btreeSpec, buffer, kContext);

        /********* add by leon *******
         * Here a little trick is played to reduce the log record number.
         * First we turn off the needLog switch to avoiding log individually.
         * After done, we can log in batch.
         */
        rootPage.setLogInfo(txnId, false);
        if (needLog) {
            int pgno = rootPage.getPageNumber().getPageNumber();
            byte[] oldV = ByteTool.copyByteArray(rootPage.page, 0, BTreeSpec.PAGE_HEADER_SIZE);
            byte[] newV = ByteTool.copyByteArray(rootPage.page, 0, BTreeSpec.PAGE_HEADER_SIZE);
            System.arraycopy(ByteTool.intToBytes(1), 0, newV, BTreeSpec.OFF_FLAGS, 4);
            System.arraycopy(ByteTool.intToBytes(0), 0, newV, BTreeSpec.OFF_PREVPAGE, 4);
            System.arraycopy(ByteTool.intToBytes(pageNumber.getPageNumber()), 0, newV, BTreeSpec.OFF_NEXTPAGE, 4);
            BTreeReplLogRecord lr = new BTreeReplLogRecord(pageNumber.getTreeId(), pgno, txnId, 0, oldV, newV, btreeSpec.btree.getType());
            lr.log();
        }
        rootPage.setInternal();
        rootPage.setPrevPage(new PageNumber(0)); // root
        rootPage.setNextPage(this.pageNumber); // leftmost
        rootPage.setLogInfo(txnId, needLog);

        rootPage.insertNode(newKey, ByteTool.intToBytes(newPage.pageNumber.getPageNumber()), mode, kContext);
        // set parent to the new rootPage

        this.setPrevPage(rootPage.pageNumber);

        /********* add by leon *******/
        newPage.setLogInfo(txnId, needLog);
        /***************************/
        newPage.setPrevPage(rootPage.pageNumber);

        // unfix this page,new Page and root page
        buffer.releasePage(this.pageNumber.getTreeId(), this.pageNumber, true);
        buffer.releasePage(newPage.pageNumber.getTreeId(), newPage.pageNumber, true);
        buffer.releasePage(rootPage.pageNumber.getTreeId(), rootPage.pageNumber, true);
        return rootPage;
    }

    private PageNumber insertWhenNotRootPage(BTreePage newPage, int txnId, boolean needLog, Key newKey, short mode, KernelContext kContext) throws ChaiDBException {
        BTreePage parent = new BTreePage(btreeSpec.btree.getBtreeId(), this.prevPage, btreeSpec, buffer);
        // set new page's parent to be this page's parent
        /********* add by leon *******/
        newPage.setLogInfo(txnId, needLog);
        /***************************/
        newPage.setPrevPage(this.prevPage);

        // unfix and unlock this page and new Page
        buffer.releasePage(this.pageNumber.getTreeId(), this.pageNumber, true);
        buffer.releasePage(newPage.pageNumber.getTreeId(), newPage.pageNumber, true);

        PageNumber newRoot = parent.insert(newKey, ByteTool.intToBytes(newPage.pageNumber.getPageNumber()), mode, kContext);
        //unfix parent
        buffer.releasePage(btreeSpec.btree.getBtreeId(), parent.pageNumber, true);
        return newRoot;
    }

    private Key insertWhenIsBTree(BTreePage newPage, int txnId, boolean needLog, KeyIndex keyIndex, Key key, int currNode, byte[] data, short mode, KernelContext kContext) throws ChaiDBException {
        Key newKey;
        PageNumber tmpPgNo;
        newPage.setLogInfo(txnId, needLog);
        newPage.setInternal();
        int indexOfInsert = keyIndex.keyIndex;
        // index of V', Tn/2T of keys in (key1, key2, keyn-1, key) >= V'
        int indexOfVictim = order / 2;
        // m is the index of the lowest key that >= V'
        int m = (indexOfInsert < indexOfVictim) ? (indexOfVictim - 1) : indexOfVictim;
        // V'
        /* Modified by ben zhang at Aug, 12, 2002 */
        Key tmpKey = getNode(m).getKey();
        newKey = (indexOfInsert == indexOfVictim) ? key : tmpKey;
        if (Debug.DEBUG_BTREEPAGE)
            logger.debug("m= " + m + " V'= " + new String(newKey.toBytes())); //Modified by ben zhang at Aug, 12, 2002 . Pending issue
        // insert datam, keym+1,...keyn-1 to new page
        // copy to new page
        int startCopyPoint = (indexOfInsert == indexOfVictim) ? m : m + 1;

        /************* The below is recorded in BTreeSplitLogRecord ******/
        /***************** Add by leon,2001-9-27 14:56 ********************/
        if (needLog) {
            int pgno = pageNumber.getPageNumber();
            int newpgno = newPage.getPageNumber().getPageNumber();

            BTreeSplitLogRecord lr = new BTreeSplitLogRecord(pageNumber.getTreeId(), pgno, txnId, (short) m, (short) startCopyPoint, newpgno, page, (short) btreeSpec.getNodeSize(), btreeSpec.btree.getType());
            lr.log();
        }
        /******************************************************************/

        for (int j = startCopyPoint; j < currNode; j++) {
            newPage.upperBound -= btreeSpec.getLeafNodeSize();
            BTreeNode node = getNode(j);
            // set child page's parent to new page
            BTreePage childPage = new BTreePage(btreeSpec.btree.getBtreeId(), node.getPageNumber(), btreeSpec, buffer);

            /********* add by leon *******/
            childPage.setLogInfo(txnId, needLog);
            /***************************/
            childPage.setPrevPage(newPage.pageNumber);

            // unfix and unlock child page
            buffer.releasePage(childPage.pageNumber.getTreeId(), childPage.pageNumber, true);
            System.arraycopy(page, node.getNodeOffset(), newPage.page, newPage.upperBound, btreeSpec.getLeafNodeSize());
            System.arraycopy(ByteTool.shortToBytes(newPage.upperBound), 0, newPage.page, newPage.lowerBound, 2);
            newPage.lowerBound += 2;
        }
        newPage.setLowerBound(newPage.lowerBound);
        newPage.setUpperBound(newPage.upperBound);

        if (indexOfInsert != indexOfVictim) {
            // leftmost is datam
            PageNumber leftmostPageNumber = getNode(m).getPageNumber();

            /********* add by leon *******/
            newPage.setLogInfo(txnId, needLog);
            /***************************/
            newPage.setNextPage(leftmostPageNumber);

            // set child page's parent to newPage
            BTreePage childPage = new BTreePage(btreeSpec.btree.getBtreeId(), leftmostPageNumber, btreeSpec, buffer);

            /********* add by leon *******/
            childPage.setLogInfo(txnId, needLog);
            /***************************/
            childPage.setPrevPage(newPage.pageNumber);

            // unfix child page
            buffer.releasePage(childPage.pageNumber.getTreeId(), childPage.pageNumber, true);
        } else {
            // leftmost is the new data
            if (data.length != 4) throw new ChaiDBException(ErrorCode.BTREE_INT_PAGE_NUMBER);
            PageNumber leftmostPageNumber = new PageNumber(ByteTool.bytesToInt(data, 0, btreeSpec.isMsbFirst()));
            leftmostPageNumber.setTreeId(btreeSpec.btree.getBtreeId());

            /********* add by leon *******/
            newPage.setLogInfo(txnId, needLog);
            /***************************/
            newPage.setNextPage(leftmostPageNumber);

            // set child page's parent to newPage
            BTreePage childPage = new BTreePage(leftmostPageNumber.getTreeId(), leftmostPageNumber, btreeSpec, buffer);

            /********* add by leon *******/
            childPage.setLogInfo(txnId, needLog);
            /***************************/
            childPage.setPrevPage(newPage.pageNumber);
            // unfix child page
            buffer.releasePage(childPage.pageNumber.getTreeId(), childPage.pageNumber, true);
        }
        // delete keym,...keyn-1 from the current page
        // remove from the current page
        byte[] tmp = new byte[m * btreeSpec.getLeafNodeSize()];
        byte[] ptr = new byte[m * 2];
        int k = 0;
        int p = 2 * (m - 1);
        short newUpperBound = (short) (btreeSpec.getPageSize() - btreeSpec.getLeafNodeSize() * m);
        for (int j = m - 1; j >= 0; j--) {
            System.arraycopy(page, getNodeOffset(j), tmp, k, btreeSpec.getLeafNodeSize());
            System.arraycopy(ByteTool.shortToBytes((short) (newUpperBound + k)), 0, ptr, p, 2);
            k += btreeSpec.getLeafNodeSize();
            p -= 2;
        }
        this.setUpperBound(newUpperBound);
        System.arraycopy(tmp, 0, page, this.upperBound, m * btreeSpec.getLeafNodeSize());
        System.arraycopy(ptr, 0, page, BTreeSpec.PAGE_HEADER_SIZE, 2 * m);
        this.setLowerBound((short) (BTreeSpec.PAGE_HEADER_SIZE + 2 * m));
        // insert the new node
        /*Modified by Ben Zhang at Aug, 12, 2002*/
        int cmp = key.compareTo(newKey);
        if (cmp < 0) {
            this.insertNode(key, data, mode, kContext);

            tmpPgNo = new PageNumber(ByteTool.bytesToInt(data, 0, btreeSpec.isMsbFirst()));
            tmpPgNo.setTreeId(btreeSpec.btree.getBtreeId());

            // change childPage's parent
            BTreePage childPage = new BTreePage(btreeSpec.btree.getBtreeId(), new PageNumber(ByteTool.bytesToInt(data, 0, btreeSpec.isMsbFirst())), btreeSpec, buffer);

            /********* add by leon *******/
            childPage.setLogInfo(txnId, needLog);
            /***************************/
            childPage.setPrevPage(this.pageNumber);

            // unfix childPage
            buffer.releasePage(childPage.pageNumber.getTreeId(), childPage.pageNumber, true);
        } else if (cmp > 0) {
            newPage.insertNode(key, data, mode, kContext);

            tmpPgNo = new PageNumber(ByteTool.bytesToInt(data, 0, btreeSpec.isMsbFirst()));
            tmpPgNo.setTreeId(btreeSpec.btree.getBtreeId());
            // change childPage's parent
            BTreePage childPage = new BTreePage(btreeSpec.btree.getBtreeId(), tmpPgNo, btreeSpec, buffer);

            /********* add by leon *******/
            childPage.setLogInfo(txnId, needLog);
            /***************************/
            childPage.setPrevPage(newPage.pageNumber);

            // unfix childPage
            buffer.releasePage(childPage.pageNumber.getTreeId(), childPage.pageNumber, true);
        }
        return newKey;
    }

    private Key insertWhenIsLeaf(BTreePage newPage, int txnId, boolean needLog, KeyIndex keyIndex, Key key, int currNode, byte[] data, short mode, KernelContext kContext) throws ChaiDBException {
        Key newKey;
        newPage.setLogInfo(txnId, needLog);
        newPage.setLeaf();
        int indexOfInsert = keyIndex.keyIndex;
        // index of V', Tn/2T of keys in (key1, key2,...keyn-1, key) < V'
        int indexOfVictim = order / 2 + 1;
        // m is the index of the lowest key that >= V'
        int m = (indexOfInsert < indexOfVictim) ? (indexOfVictim - 1) : indexOfVictim;

        /************* The below is recorded in BTreeSplitLogRecord ******/
        /***************** Add by leon,2001-9-27 14:56 ********************/
        if (needLog) {
            int pgno = pageNumber.getPageNumber();
            int newpgno = newPage.getPageNumber().getPageNumber();

            BTreeSplitLogRecord lr = new BTreeSplitLogRecord(pageNumber.getTreeId(), pgno, txnId, (short) m, (short) m, newpgno, page, (short) btreeSpec.getNodeSize(), btreeSpec.btree.getType());
            lr.log();
        }
        /******************************************************************/

        // V'
        /*Modified by Ben Zhang at Aug, 12, 2002*/
        newKey = (indexOfInsert == indexOfVictim) ? key : getNode(m).getKey();
        // move keym,...keyn-1 to new page
        // copy to new page
        for (int j = m; j < currNode; j++) {
            newPage.upperBound -= btreeSpec.getLeafNodeSize();
            System.arraycopy(page, getNodeOffset(j), newPage.page, newPage.upperBound, btreeSpec.getLeafNodeSize());
            System.arraycopy(ByteTool.shortToBytes(newPage.upperBound), 0, newPage.page, newPage.lowerBound, 2);
            newPage.lowerBound += 2;
        }
        newPage.setLowerBound(newPage.lowerBound);
        newPage.setUpperBound(newPage.upperBound);
        if (Debug.DEBUG_BTREEPAGE)
            logger.debug("m= " + m + " V'= " + new String(newKey.toBytes())); //Modified by ben at Aug, 12, 2002 Pending
        // remove from the current page
        if (m < order) {
            byte[] tmp = new byte[m * btreeSpec.getLeafNodeSize()];
            byte[] ptr = new byte[m * 2];
            int k = 0;
            int p = 2 * (m - 1);
            short newUpperBound = (short) (btreeSpec.getPageSize() - m * btreeSpec.getLeafNodeSize());
            for (int j = m - 1; j >= 0; j--) {
                System.arraycopy(page, getNodeOffset(j), tmp, k, btreeSpec.getLeafNodeSize());
                System.arraycopy(ByteTool.shortToBytes((short) (newUpperBound + k)), 0, ptr, p, 2);
                k += btreeSpec.getLeafNodeSize();
                p -= 2;
            }
            this.setUpperBound(newUpperBound);
            System.arraycopy(tmp, 0, page, this.upperBound, m * btreeSpec.getLeafNodeSize());
            System.arraycopy(ptr, 0, page, BTreeSpec.PAGE_HEADER_SIZE, 2 * m);
            this.setLowerBound((short) (BTreeSpec.PAGE_HEADER_SIZE + 2 * m));
        }

        /************* The above is recorded in BTreeSplitLogRecord ******/

        // insert the new node
        //Modified by ben zhang at Aug, 12, 2002
        if (key.compareTo(newKey) < 0) this.insertNode(key, data, mode, kContext);
        else newPage.insertNode(key, data, mode, kContext);
        // link leaf pages together

        /********* add by leon *******/
        newPage.setLogInfo(txnId, needLog);
        /***************************/
        newPage.setNextPage(this.nextPage);

        this.setNextPage(newPage.pageNumber);
        return newKey;
    }

    private void insertWhenCurNodeLessThanOrder(Key key, byte[] data, short mode, KernelContext kContext, int txnId, boolean needLog) throws ChaiDBException {
        PageNumber tmpPgNo;
        // the page has enough space to store the new node
        insertNode(key, data, mode, kContext);

        if (isBTree() && !isLeaf()) {
            tmpPgNo = new PageNumber(ByteTool.bytesToInt(data, 0, btreeSpec.isMsbFirst()));
            tmpPgNo.setTreeId(btreeSpec.btree.getBtreeId());
            //Because once the upper page is locked, Others later coming
            //must be blocked at upper page. And the former who have
            //already entered, will NOT change children's previous page.
            //For BTree page, ONLY at SPLIT or MERGE condition, one's
            //previous page can be changed. So, because there is no
            //conflict update between me(only chagne previous page) and
            //the former owners(NEVER change previous page) of children,
            //we can modify them parallely.
            // The same strategy is applied to later codes.

            // set childPage's parent
            BTreePage childPage = new BTreePage(btreeSpec.btree.getBtreeId(), tmpPgNo, btreeSpec, buffer);

            /********* add by leon *******/
            childPage.setLogInfo(txnId, needLog);
            /***************************/
            childPage.setPrevPage(this.pageNumber);

            // unfix and unlock childPage
            buffer.releasePage(childPage.pageNumber.getTreeId(), childPage.pageNumber, true);
        }
        buffer.releasePage(pageNumber.getTreeId(), pageNumber, true);
        /*XZ*/
    }

    public BTreePage lockAPage(KernelContext kContext, PageNumber pageNumber, byte oper) throws ChaiDBException {

        BTreePage page = null;
        page = new BTreePage(btreeSpec.btree.getBtreeId(), pageNumber, btreeSpec, buffer);

        return page;
    }

    /**
     * @param key  specified key we search
     * @param oper indicate getLeaf is for insert or delete operation
     * @return leaf to insert/delete the key
     */
    // The final return leaf page is fixed already. ###
    // Before this returns, it has been locked already. (modifed by leon)
    public BTreePage getLeaf(Key key, KernelContext kContext, byte oper) throws ChaiDBException {
        BTreePage returnPage;

        try {
            // fix the page first
            page = buffer.getPage(pageNumber.getTreeId(), pageNumber);

            if (page == null)
                throw new ChaiDBException(ErrorCode.BTREE_INVALID_BTREEPAGE, "Page is null: " + pageNumber.toHexString() + " of " + btreeSpec.getBtreeName());
            if (Debug.DEBUG_CHECKPAGENUMBER) {
                if (!Debug.checkPageNumber(pageNumber, this.page)) {
                    logger.fatal(new Throwable("there is hole in GetPage!"));
                    this.buffer.dump();
                    Db.getLogManager().flush();
                    Debug.dumpPageToLog(page);
                    if (Debug.DEBUG_PAGEINFO_TRACE) Debug.pageHistory(this.pageNumber);

                    System.exit(-1);

                }
            }

            if (isLeaf()) {
                return this;
            }
            int nodeNumbers = getCurrNodeNumbers();
            if (nodeNumbers <= 0 || nodeNumbers > btreeSpec.getNodeOrder()) {
                String logMessage = new StringBuffer().append("Page ").append(pageNumber).append(" has invalid node number: ").append(nodeNumbers).toString();
                logger.error(logMessage);
                throw new ChaiDBException(ErrorCode.BTREE_INVALID_BTREEPAGE, logMessage);
            }

            int nodeIndexFirst = 0;
            int nodeIndexLast = nodeNumbers - 1;
            int nodeIndex = 0;

            // Binary search within page nodes
            while (nodeIndexLast > nodeIndexFirst) {
                nodeIndex = (nodeIndexFirst + nodeIndexLast) / 2;
                BTreeNode node = getNode(nodeIndex);
                /*Modified by ben zhang at aug, 12, 2002 */
                Key tmpKey = node.getKey();
                int cmp = tmpKey.compareTo(key);
                if (cmp > 0) {
                    nodeIndexLast = nodeIndex - 1;
                } else if (cmp < 0) {
                    nodeIndexFirst = nodeIndex;
                    if (nodeIndexLast - nodeIndexFirst == 1) {
                        BTreeNode node2 = getNode(nodeIndexLast);
                        /*Modified by ben zhang at aug, 12, 2002 */
                        tmpKey = node2.getKey();
                        int cmpResult = tmpKey.compareTo(key);
                        if (cmpResult > 0) {
                            //lock it with shared lock.

                            //update the lowest predecessor must be locked
                            BTreePage nPage = lockAPage(kContext, node.getPageNumber(), oper);

                            // unfix current page
                            buffer.releasePage(pageNumber.getTreeId(), pageNumber, false);
                            returnPage = nPage.getLeaf(key, kContext, oper);
                            buffer.releasePage(nPage.pageNumber.getTreeId(), nPage.pageNumber, false);
                            return returnPage;
                        } else {
                            //lock it with shared lock.

                            BTreePage page = lockAPage(kContext, node2.getPageNumber(), oper);
                            // unfix current page
                            buffer.releasePage(pageNumber.getTreeId(), pageNumber, false);
                            returnPage = page.getLeaf(key, kContext, oper);
                            buffer.releasePage(page.pageNumber.getTreeId(), page.pageNumber, false);
                            return returnPage;
                        }
                    }
                } else { // Exact match
                    //lock it with shared lock.
                    BTreePage page = lockAPage(kContext, node.getPageNumber(), oper);
                    // unfix current page
                    buffer.releasePage(pageNumber.getTreeId(), pageNumber, false);
                    returnPage = page.getLeaf(key, kContext, oper);
                    buffer.releasePage(page.pageNumber.getTreeId(), page.pageNumber, false);
                    return returnPage;
                }
            } // end of binary search

            if (nodeIndexFirst - nodeIndexLast == 1) {
                // go to leftmost node
                if (nextPage.getPageNumber() > 0) {
                    //lock it with shared lock.

                    BTreePage leftPage = lockAPage(kContext, nextPage, oper);
                    // unfix current page
                    buffer.releasePage(pageNumber.getTreeId(), pageNumber, false);
                    returnPage = leftPage.getLeaf(key, kContext, oper);
                    buffer.releasePage(leftPage.pageNumber.getTreeId(), leftPage.pageNumber, false);
                    return returnPage;
                } else {
                    logger.error("Page " + pageNumber.toHexString() + " " + buffer.getBTreeName(pageNumber.getTreeId()) + " missing leftmost page " + Debug.getDebugInfo());
                    //Debug.pageHistory(Integer.toHexString(pageNumber.getPageNumber()));
                    Debug.flushPages();
                    if (Debug.DEBUG_THREAD) Debug.flushThreads();
                    // details -ranjeet
                    String details = "The page number is " + pageNumber.toHexString() + ".";
                    throw new ChaiDBException(ErrorCode.BTREE_LEFTMOST_PAGE_MISSING, details);
                }
            }
            // now binary search has identified a single node
            BTreeNode node = getNode(nodeIndexFirst);
            /* Modified by ben zhang at aug, 12, 2002 */
            Key tmpKey1 = node.getKey();
            int cmp = key.compareTo(tmpKey1);
            if (cmp < 0) {
                // go to leftmost pointer
                if (nextPage.getPageNumber() > 0) {
                    //lock it with shared lock.

                    BTreePage leftPage = lockAPage(kContext, nextPage, oper);
                    // unfix current page
                    buffer.releasePage(pageNumber.getTreeId(), pageNumber, false);
                    returnPage = leftPage.getLeaf(key, kContext, oper);
                    buffer.releasePage(leftPage.pageNumber.getTreeId(), leftPage.pageNumber, false);
                    return returnPage;
                } else {
                    logger.error("Page " + pageNumber + " missing leftmost page " + Debug.getDebugInfo());
                    //Debug.pageHistory(Integer.toHexString(pageNumber.getPageNumber()));
                    Debug.flushPages();
                    if (Debug.DEBUG_THREAD) Debug.flushThreads();

                    //Thread.dumpStack(); System.exit(1);
                    // details -ranjeet
                    String details = "The page number is " + pageNumber.getPageNumber() + ".";
                    throw new ChaiDBException(ErrorCode.BTREE_LEFTMOST_PAGE_MISSING, details);
                }
            }

            BTreePage nPage = lockAPage(kContext, node.getPageNumber(), oper);
            // unfix current page
            buffer.releasePage(pageNumber.getTreeId(), pageNumber, false);
            returnPage = nPage.getLeaf(key, kContext, oper);
            buffer.releasePage(nPage.pageNumber.getTreeId(), nPage.pageNumber, false);
            return returnPage;
        } catch (ChaiDBException e) {
            Debug.flushPages();
            if (Debug.DEBUG_THREAD) Debug.flushThreads();
            logger.error(e);
            throw e;
        }
    }

    /**
     * <P>Locate the indicated key and return a page/offset pair.
     * Note that this is implicitly recursive; to search for a key,
     * typically have to fetch a new page and invoke this same
     * method on that page.</P>
     * <p/>
     * <P>The search is a simple binary search.  If this is an
     * internal page, locate the key preceding (or equal to)
     * the search key, then fetch the corresponding page and recurse.
     * If this is a leaf page, look for an exact match.</P>
     */
    // The page contains the key is fixed if specified node is found. Otherwise, it's not fixed!!
    // because if not found, we didn't know the leaf page outside this method. And if found, we
    // will read its data so that the page node belongs to should NOT be kicked out of buffer!!!###
    //For search(query) lock mode is LockManager.WIATTING,because this does NOT cause to deadlock
    public BTreeNode search(Key key, KernelContext kContext) throws ChaiDBException {
        BTreeNode returnNode;

        try {
            // fix the page
            page = buffer.getPage(pageNumber.getTreeId(), pageNumber);
            if (page == null)
                throw new ChaiDBException(ErrorCode.BTREE_INVALID_BTREEPAGE, "Page is null: " + pageNumber.toHexString() + " of " + btreeSpec.getBtreeName());
            if (Debug.DEBUG_CHECKPAGENUMBER) {
                if (!Debug.checkPageNumber(pageNumber, this.page)) {
                    logger.fatal(new Throwable("there is hole in GetPage!"));
                    this.buffer.dump();
                    Db.getLogManager().flush();
                    Debug.dumpPageToLog(page);
                    if (Debug.DEBUG_PAGEINFO_TRACE) Debug.pageHistory(this.pageNumber);

                    System.exit(-1);

                }
            }

            // Binary search within page
            int nodeIndexFirst = 0;
            int nodeIndexLast = getCurrNodeNumbers() - 1;
            while (nodeIndexLast > nodeIndexFirst) {
                int nodeIndex = (nodeIndexFirst + nodeIndexLast) / 2;
                BTreeNode node = getNode(nodeIndex);
                /* Modified by ben zhang at aug, 12, 2002 */
                Key tmpKey = node.getKey();
                int cmp = tmpKey.compareTo(key);
                if (cmp > 0) {
                    nodeIndexLast = nodeIndex - 1;
                } else if (cmp < 0) {
                    nodeIndexFirst = nodeIndex;
                    if (isLeaf()) nodeIndexFirst++;
                    else if (nodeIndexLast - nodeIndexFirst == 1) {
                        BTreeNode node2 = getNode(nodeIndexLast);
                        /*Modified by ben zhang at aug, 12, 2002 */
                        tmpKey = node2.getKey();
                        int cmpResult = tmpKey.compareTo(key);
                        if (cmpResult > 0) {
                            BTreePage page = this.lockAPage(kContext, node.getPageNumber(), SEARCH);
                            // unfix the current page
                            buffer.releasePage(pageNumber.getTreeId(), pageNumber, false);

                            returnNode = page.search(key, kContext);
                            // unfix the current page
                            buffer.releasePage(btreeSpec.btree.getBtreeId(), page.pageNumber, false);
                            return returnNode;
                        } else {
                            BTreePage page = this.lockAPage(kContext, node2.getPageNumber(), SEARCH);
                            // unfix the current page
                            buffer.releasePage(pageNumber.getTreeId(), pageNumber, false);

                            returnNode = page.search(key, kContext);
                            // unfix the current page
                            buffer.releasePage(btreeSpec.btree.getBtreeId(), page.pageNumber, false);
                            return returnNode;
                        }
                    }
                } else { // Exact match
                    if (isLeaf()) {
                        return node;
                    }
                    BTreePage page = this.lockAPage(kContext, node.getPageNumber(), SEARCH);
                    // unfix the current page
                    buffer.releasePage(pageNumber.getTreeId(), pageNumber, false);

                    returnNode = page.search(key, kContext);
                    // unfix the new allocated page
                    buffer.releasePage(btreeSpec.btree.getBtreeId(), page.pageNumber, false);
                    return returnNode;
                }
            } // end of binary search

            if (!isLeaf() && nodeIndexFirst - nodeIndexLast == 1) {
                // go to leftmost node
                if (nextPage.getPageNumber() > 0) {
                    BTreePage page = this.lockAPage(kContext, nextPage, SEARCH);
                    // unfix current page
                    buffer.releasePage(pageNumber.getTreeId(), pageNumber, false);

                    returnNode = page.search(key, kContext);
                    // unfix the new allocated page
                    buffer.releasePage(btreeSpec.btree.getBtreeId(), page.pageNumber, false);
                    return returnNode;
                } else {
                    logger.error("Page " + pageNumber.toHexString() + " " + buffer.getBTreeName(pageNumber.getTreeId()) + " missing leftmost page " + Debug.getDebugInfo());
                    //Debug.pageHistory(Integer.toHexString(pageNumber.getPageNumber()));
                    Debug.flushPages();
                    if (Debug.DEBUG_THREAD) Debug.flushThreads();
                    // details -ranjeet
                    String details = "The page number is " + pageNumber.toHexString() + ".";
                    throw new ChaiDBException(ErrorCode.BTREE_LEFTMOST_PAGE_MISSING, details);
                }
            }

            // now binary search has identified a single node
            // node.setTreeId(btreeSpec.btree.getBtreeId());
            // Leaf node? need an exact match to avoid failure
            if (isLeaf()) {
                BTreeNode node = getNode(nodeIndexFirst);
                //buffer.releasePage(pageNumber.getTreeId() ,pageNumber, false);
                /*Modified by ben zhang at aug, 12, 2002 */
                if (node != null && node.getKey().compareTo(key) == 0) return node;
                else {
                    //release it for outside this method, this page can't be found any longer.
                    buffer.releasePage(pageNumber.getTreeId(), pageNumber, false);
                    return null;
                }
            } else {
                BTreeNode node = getNode(nodeIndexFirst);
                /*Modified by ben zhang at aug, 12, 2002 */
                Key tmpKey = node.getKey();
                int cmp = key.compareTo(tmpKey);
                if (cmp < 0) {
                    // go to leftmost pointer
                    if (nextPage.getPageNumber() > 0) {
                        BTreePage page = this.lockAPage(kContext, nextPage, SEARCH);
                        // unfix current page
                        buffer.releasePage(pageNumber.getTreeId(), pageNumber, false);

                        returnNode = page.search(key, kContext);
                        // unfix the new allocated page
                        buffer.releasePage(btreeSpec.btree.getBtreeId(), page.pageNumber, false);
                        return returnNode;
                    } else {
                        logger.error("Page " + pageNumber + " missing leftmost page " + Debug.getDebugInfo());
                        //Debug.pageHistory(Integer.toHexString(pageNumber.getPageNumber()));
                        Debug.flushPages();
                        if (Debug.DEBUG_THREAD) Debug.flushThreads();
                        // details -ranjeet
                        String details = "The page number is " + pageNumber.getPageNumber() + ".";
                        throw new ChaiDBException(ErrorCode.BTREE_LEFTMOST_PAGE_MISSING, details);
                    }
                }

                BTreePage page = this.lockAPage(kContext, node.getPageNumber(), SEARCH);
                // unfix the current page
                buffer.releasePage(pageNumber.getTreeId(), pageNumber, false);

                returnNode = page.search(key, kContext);
                // unfix the new allocated page
                buffer.releasePage(btreeSpec.btree.getBtreeId(), page.pageNumber, false);
                return returnNode;
            }
        } catch (Exception e) {
            logger.error(e);
            if (e instanceof ChaiDBException) throw (ChaiDBException) e;
            else {
                return null;
            }
        }
    }

    /**
     * search BTreeNode list which key is in the range of bottomKey and topKey
     *
     * @param minKey
     * @param maxKey
     * @param kContext
     * @param totalNodeIdList total nodeId list,every item is 8bytes nodeId
     */
    public void search(Key minKey, Key maxKey, boolean includeMinKey, boolean includeMaxKey, KernelContext kContext, ArrayList totalNodeIdList) throws ChaiDBException {

        BTreePage tempPage = this;
        boolean cont = true;
        while (cont) {
            cont = tempPage.getNodeIdList(minKey, maxKey, includeMinKey, includeMaxKey, kContext, totalNodeIdList);

            buffer.releasePage(btreeSpec.btree.getBtreeId(), tempPage.pageNumber, false);

            //if this page is the last one.
            cont &= (tempPage.nextPage.getPageInFile() != BTreeSpec
                    .INVALID_PAGENO);

            if (cont) tempPage = lockAPage(kContext, tempPage.nextPage, SEARCH);

        }

    }

    public void searchKeys(Key minKey, Key maxKey, boolean includeMinKey, boolean includeMaxKey, KernelContext kContext, ArrayList totalNodeIdList) throws ChaiDBException {

        BTreePage tempPage = this;
        boolean cont = true;
        while (cont) {
            cont = tempPage.getKeysList(minKey, maxKey, includeMinKey, includeMaxKey, totalNodeIdList);

            buffer.releasePage(btreeSpec.btree.getBtreeId(), tempPage.pageNumber, false);

            //if this page is the last one.
            cont &= (tempPage.nextPage.getPageInFile() != BTreeSpec
                    .INVALID_PAGENO);

            if (cont) tempPage = lockAPage(kContext, tempPage.nextPage, SEARCH);

        }

    }


    /**
     * get nodeId list which key is between minKey and maxKey
     *
     * @param minKey
     * @param maxKey
     * @param kContext
     * @return true if all keys in this page is not larger than maxKey.
     */
    private boolean getNodeIdList(Key minKey, Key maxKey, boolean includeMinKey, boolean includeMaxKey, KernelContext kContext, ArrayList valueList) throws ChaiDBException {

        BTreeNode btreeNode;
        int nodeNumOnPage = getCurrNodeNumbers();
        Key key;

        int i = 0;
        for (; i < nodeNumOnPage; i++) {
            btreeNode = getNode(i);
            /*Modified by ben zhang at Aug, 12, 2002. */
            key = btreeNode.getKey();
            if (minKey == null || (includeMinKey ? key.compareTo(minKey) >= 0 : key.compareTo(minKey) > 0)) {
                if (maxKey == null || (includeMaxKey ? key.compareTo(maxKey) <= 0 : key.compareTo(maxKey) < 0))
                    valueList.add(btreeNode.getData(kContext));
                else break;
            }
        }
        return i == nodeNumOnPage;
    }

    /**
     * get matched keys list
     *
     * @param minKey
     * @param maxKey
     * @param includeMinKey
     * @param includeMaxKey
     * @param keyList
     * @return
     * @throws ChaiDBException
     */
    private boolean getKeysList(Key minKey, Key maxKey, boolean includeMinKey, boolean includeMaxKey, ArrayList keyList) throws ChaiDBException {

        BTreeNode btreeNode;
        int nodeNumOnPage = getCurrNodeNumbers();
        Key key;

        int i = 0;
        for (; i < nodeNumOnPage; i++) {
            btreeNode = getNode(i);
            /*Modified by ben zhang at Aug, 12, 2002. */
            key = btreeNode.getKey();
            if (minKey == null || (includeMinKey ? key.compareTo(minKey) >= 0 : key.compareTo(minKey) > 0)) {
                if (maxKey == null || (includeMaxKey ? key.compareTo(maxKey) <= 0 : key.compareTo(maxKey) < 0))
                    keyList.add(key);
                else break;
            }
        }
        return i == nodeNumOnPage;
    }


    /**
     * @param nodeIndex
     * @return an object representing the indicated node on this page
     */
    // did NOT unfix the page ###
    public BTreeNode getNode(int nodeIndex) throws ChaiDBException {
        if (nodeIndex >= getCurrNodeNumbers())
            throw new ChaiDBException(ErrorCode.BTREE_INVALID_BTREEPAGE, "nodeIndex is out of bound:" + nodeIndex + " max available:" + getCurrNodeNumbers());
        // Build and return a node object
        BTreeNode node = new BTreeNode(this, getNodeOffset(nodeIndex));

        return node;
    }

    /**
     * @param nodeIndex
     * @return nodeOffSet
     */
    // did NOT unfix the page ###
    private int getNodeOffset(int nodeIndex) throws ChaiDBException {
        if (isOverflow()) { // Overflow pages don't have nodes...
            // details -ranjeet
            String details = "Request for offset on an overflow page." + " nodeIndex=" + nodeIndex + " page=" + pageNumber.toHexString() + " of " + btreeSpec.getBtreeName() + ByteTool.toHexString(2, page, 0, page.length);
            if (Debug.DEBUG_OVERFLOW_PAGE) {
                logger.fatal(details);
                buffer.dump();
                Db.getLogManager().flush();
                if (Debug.DEBUG_DUMP_MEMORY) buffer.dumpMemInfo(Debug.DUMP_ALL, "memory");
                if (Debug.DEBUG_PAGEINFO_TRACE) Debug.pageHistory(this.pageNumber);
                System.exit(-1);
            }

            throw new ChaiDBException(ErrorCode.ENTRY_NOEXIST, details);
        }
        if (nodeIndex >= getCurrNodeNumbers()) {
            // details -ranjeet
            String details = "Request for node that is not present." + " nodeIndex=" + nodeIndex + " page=" + pageNumber.toHexString() + " of " + btreeSpec.getBtreeName() + ByteTool.toHexString(2, page, 0, page.length);

            logger.error(details);
            Debug.pageHistory(pageNumber);

            throw new ChaiDBException(ErrorCode.ENTRY_NOEXIST, details);
        }
        // fix the page first
        //page = buffer.getPage(pageNumber.getTreeId() ,pageNumber);
        // 20-byte header + 2 bytes per pointer
        int offset = BTreeSpec.PAGE_HEADER_SIZE + 2 * nodeIndex;
        // Convert the two-byte pointer to an short
        short ptr = ByteTool.bytesToShort(page, offset, btreeSpec.isMsbFirst());
        if (ptr > page.length) {
            throw new ChaiDBException(ErrorCode.BTREE_INVALID_BTREEPAGE, "offset of btree node is out of bound:" + ptr);
        }
        return (int) ptr;
    }

    /**
     * @param key
     * @return true if the key found; otherwise return false
     */
    private boolean deleteNode(Key key, KernelContext kContext) throws ChaiDBException {
        int txnId = kContext.getLocker();
        boolean needLog = kContext.getNeedLog();

        try {
            // fix the page
            page = buffer.getPage(pageNumber.getTreeId(), pageNumber);
            if (page == null)
                throw new ChaiDBException(ErrorCode.BTREE_INVALID_BTREEPAGE, "Page is null: " + pageNumber.toHexString() + " of " + btreeSpec.getBtreeName());
            if (Debug.DEBUG_CHECKPAGENUMBER) {
                if (!Debug.checkPageNumber(pageNumber, this.page)) {
                    logger.fatal(new Throwable("there is hole in GetPage!"));
                    this.buffer.dump();
                    Db.getLogManager().flush();
                    Debug.dumpPageToLog(page);
                    if (Debug.DEBUG_PAGEINFO_TRACE) Debug.pageHistory(this.pageNumber);
                    System.exit(-1);

                }
            }

            int currNode = getCurrNodeNumbers();
            int upperBoundNodeIndex = -1;
            // right now use simple lineal search/insert
            // may change to binary search/insert later ###
            for (int i = 0; i < currNode; i++) {
                BTreeNode node = getNode(i);
                if (node.getNodeOffset() == upperBound) upperBoundNodeIndex = i;
                /*Modified by ben zhang at aug, 12, 2002 */
                Key tmpKey = node.getKey();
                if (tmpKey.compareTo(key) == 0) {

                    PageNumber overflowPageNumber = null;
                    if (node.isOverflowKey()) {
                        //remember the overflowpage before overriding this node
                        overflowPageNumber = new PageNumber(ByteTool.bytesToInt(page, node.getNodeOffset() + BTreeSpec
                                .NODE_HEADER_SIZE, btreeSpec.isMsbFirst()));
                        overflowPageNumber.setTreeId(btreeSpec.btree.getBtreeId());
                    }

                    // 1. move the upperBound node to occupy the space
                    int nodeSize = btreeSpec.getInternalNodeSize();
                    if (needLog) {
                        int pgno = pageNumber.getPageNumber();
                        /* logging remove data log record */
                        BTreeAddRemoveLogRecord logRec = new BTreeAddRemoveLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeAddRemoveLogRecord.REMOVE_FLAG, node.getNodeOffset(), ByteTool.copyByteArray(page, node.getNodeOffset(), nodeSize), btreeSpec.btree.getType());
                        logRec.log();
                    }
                    if (node.getNodeOffset() != upperBound) {
                        /******************* Add by Leon, Sep 29 *****************/
                        if (needLog) {
                            int pgno = pageNumber.getPageNumber();
                            byte[] data = ByteTool.copyByteArray(page, upperBound, nodeSize);
                            BTreeMoveLogRecord lr = new BTreeMoveLogRecord(pageNumber.getTreeId(), pgno, txnId, upperBound, node.getNodeOffset(), data.length, data, btreeSpec.btree.getType());
                            lr.log();
                        }
                        /***************************/
                        System.arraycopy(page, upperBound, page, node.getNodeOffset(), nodeSize);
                        // find the key has upperBound
                        if (upperBoundNodeIndex < 0) {
                            // some better algorithm later ###
                            for (int j = i + 1; j < currNode; j++) {
                                if (getNodeOffset(j) == upperBound) {
                                    upperBoundNodeIndex = j;
                                    break;
                                }
                            }
                        }
                        if (upperBoundNodeIndex >= 0) {
                            /******************* Add by Leon, Sep 29 *****************/
                            if (needLog) {
                                int pgno = pageNumber.getPageNumber();
                                byte[] oldV = ByteTool.copyByteArray(page, BTreeSpec.PAGE_HEADER_SIZE + 2 * upperBoundNodeIndex, 2);
                                byte[] newV = ByteTool.shortToBytes((short) node.getNodeOffset());
                                BTreeReplLogRecord lr = new BTreeReplLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeSpec.PAGE_HEADER_SIZE + 2 * upperBoundNodeIndex, oldV, newV, btreeSpec.btree.getType());
                                lr.log();
                            }
                            /***************************/
                            System.arraycopy(ByteTool.shortToBytes((short) node.getNodeOffset()), 0, page, BTreeSpec.PAGE_HEADER_SIZE + 2 * upperBoundNodeIndex, 2);

                        }
                    }

                    // 2. rest clean up, upperBound, lowerBound
                    /******************* Add by Leon, Sep 29 *****************/
                    if (needLog) {
                        int pgno = pageNumber.getPageNumber();
                        byte[] oldV = ByteTool.copyByteArray(page, BTreeSpec.OFF_LOWERBOUND, 4);
                        byte[] newV = ByteTool.shortToBytes((short) (lowerBound - 2));
                        newV = ByteTool.append(newV, ByteTool.shortToBytes((short) (upperBound + nodeSize)));
                        BTreeReplLogRecord lr = new BTreeReplLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeSpec.OFF_LOWERBOUND, oldV, newV, btreeSpec.btree.getType());
                        lr.log();
                    }
                    /***************************/
                    this.setLowerBound((short) (lowerBound - 2));
                    this.setUpperBound((short) (upperBound + nodeSize));

                    /******************* Add by Leon, Sep 29 *****************/
                    if (needLog) {
                        int pgno = pageNumber.getPageNumber();
                        /* logging remove data log record */
                        BTreeAddRemoveLogRecord logRec = new BTreeAddRemoveLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeAddRemoveLogRecord.REMOVE_FLAG, BTreeSpec.PAGE_HEADER_SIZE + i * 2, ByteTool.copyByteArray(page, BTreeSpec.PAGE_HEADER_SIZE + i * 2, 2), btreeSpec.btree.getType());
                        logRec.log();
                    }
                    if (needLog && currNode - 1 > i) {
                        int pgno = pageNumber.getPageNumber();
                        int srcOff = BTreeSpec.PAGE_HEADER_SIZE + (i + 1) * 2;
                        int desOff = srcOff - 2;
                        byte[] data = ByteTool.copyByteArray(page, srcOff, (currNode - 1 - i) * 2);

                        BTreeMoveLogRecord lr = new BTreeMoveLogRecord(pageNumber.getTreeId(), pgno, txnId, srcOff, desOff, data.length, data, btreeSpec.btree.getType());
                        lr.log();
                    }
                    /***************************/
                    System.arraycopy(page, BTreeSpec.PAGE_HEADER_SIZE + (i + 1) * 2, page, BTreeSpec.PAGE_HEADER_SIZE + i * 2, (currNode - 1 - i) * 2);

                    // 3. free the overflow pages
                    if (node.isOverflowKey()) {

                        BTreePage overflowPage = new BTreePage(btreeSpec.btree.getBtreeId(), overflowPageNumber, btreeSpec, buffer);
                        // put into freeList
                        /******************* Add by Leon, Sep 29 *****************/
                        if (needLog) {
                            int pgno = overflowPage.pageNumber.getPageNumber();
                            short upBound = overflowPage.upperBound;
                            short lowBound = (short) BTreeSpec.PAGE_HEADER_SIZE;
                            BTreeFreeOverflowPageLogRecord lr = new BTreeFreeOverflowPageLogRecord(pageNumber.getTreeId(), pgno, txnId, ByteTool.copyByteArray(overflowPage.page, 0, lowBound), ByteTool.copyByteArray(overflowPage.page, upBound, BTreeSpec.PAGE_SIZE - upBound), btreeSpec.btree.getType());
                            lr.log();
                        }
                        /***************************/
                        buffer.addToFreeList(overflowPage.pageNumber.getTreeId(), overflowPage.pageNumber, needLog ? new Integer(txnId) : null);

                        while (overflowPage.nextPage.getPageNumber() > 0) {
                            overflowPage = new BTreePage(btreeSpec.btree.getBtreeId(), overflowPage.nextPage, btreeSpec, buffer);
                            // put into freeList
                            /******************* Add by Leon, Sep 29 *****************/
                            if (needLog) {
                                int pgno = overflowPage.pageNumber.getPageNumber();
                                short upBound = overflowPage.upperBound;
                                short lowBound = (short) BTreeSpec.PAGE_HEADER_SIZE;
                                BTreeFreeOverflowPageLogRecord lr = new BTreeFreeOverflowPageLogRecord(pageNumber.getTreeId(), pgno, txnId, ByteTool.copyByteArray(overflowPage.page, 0, lowBound), ByteTool.copyByteArray(overflowPage.page, upBound, BTreeSpec.PAGE_SIZE - upBound), btreeSpec.btree.getType());

                                lr.log();
                            }
                            /***************************/
                            buffer.addToFreeList(overflowPage.pageNumber.getTreeId(), overflowPage.pageNumber, needLog ? new Integer(txnId) : null);
                        }
                    }

                    // 4. delete assoicated data node if this is a leaf page **XZ**
                    if (isLeaf()) {
                        DataPage dataPage = new DataPage(btreeSpec.btree.getBtreeId(), node.getPageNumber(), btreeSpec, buffer);
                        dataPage.deleteNode(kContext, node.getDataNodeOffset());

                        int docid = (btreeSpec.btree.getType() == IDBIndex
                                .ID2NODE_BTREE) ? ((NodeId) key).getDocId() : 0;

                        /*  If this data page has no node
                         (1) it is not the latest data page, we can free it so that it will enter the
                            free page list.
                         (2) it is the latest data page, just release it so that it is still able to
                            be used by others.
                         From the two above conditions, it will be either in free list or be latest
                         data page, thus, page lost will never happen.
                         */
                        if (dataPage.getCurrNodeNumbers() > 0 || buffer.isLatestDataPage(docid, dataPage.pageNumber, needLog ? new Integer(txnId) : null))
                            buffer.releasePage(node.getPageNumber().getTreeId(), node.getPageNumber(), true);
                        else {
                            /******************* Add by Leon, Sep 30 *****************/
                            if (needLog) {
                                int pgno = dataPage.pageNumber.getPageNumber();
                                int prevPg = dataPage.prevPage.getPageNumber();
                                int nextPg = dataPage.nextPage.getPageNumber();

                                BTreeFreePageLogRecord lr = new BTreeFreePageLogRecord(pageNumber.getTreeId(), pgno, txnId, dataPage.flags, prevPg, nextPg, (byte) -1, dataPage.docid, btreeSpec.btree.getType());
                                lr.log();
                            }
                            /***************************/
                            buffer.addToFreeList(node.getPageNumber().getTreeId(), node.getPageNumber(), needLog ? new Integer(txnId) : null);
                        }

                    }

                    // unfix the page
                    buffer.releasePage(pageNumber.getTreeId(), pageNumber, true);
                    return true;
                }
            }
            //unfix the page
            buffer.releasePage(pageNumber.getTreeId(), pageNumber, false);
            return false;
        } finally {
            /**todo release lock */
        }

    }

    /**
     * delete the entry with key
     * recursive method
     *
     * @return
     */
    //This method will not change the page's fixed count.
    public OperResult delete(Key key, KernelContext kContext) throws ChaiDBException {
        int txnId = kContext.getLocker();
        boolean needLog = kContext.getNeedLog();
        PageNumber tmpPgNo = null;
        OperResult result;

        try {
            /********* add by leon *********/
            setLogInfo(txnId, needLog);
            /*****************************/

            // delete node from page
            if (!deleteNode(key, kContext)) {
                return new OperResult(false, null);
            }

            // fix the current page
            page = buffer.getPage(pageNumber.getTreeId(), pageNumber);
            if (page == null)
                throw new ChaiDBException(ErrorCode.BTREE_INVALID_BTREEPAGE, "Page is null: " + pageNumber.toHexString() + " of " + btreeSpec.getBtreeName());
            if (Debug.DEBUG_CHECKPAGENUMBER) {
                if (!Debug.checkPageNumber(pageNumber, this.page)) {
                    logger.fatal(new Throwable("there is hole in GetPage!"));
                    this.buffer.dump();
                    Db.getLogManager().flush();
                    Debug.dumpPageToLog(page);
                    if (Debug.DEBUG_PAGEINFO_TRACE) Debug.pageHistory(this.pageNumber);
                    System.exit(-1);

                }
            }

            int currNode = getCurrNodeNumbers();
            // if page is root
            if (this.prevPage.getPageNumber() == 0 && currNode == 0) {
                //has only one child remaining, which should be the leftmost
                if (this.nextPage.getPageNumber() > 0) {
                    //Write lock child page
                    BTreePage child = new BTreePage(btreeSpec.btree.getBtreeId(), this.nextPage, btreeSpec, buffer);

                    // set the child to be root
                    /********* add by leon *******/
                    child.setLogInfo(txnId, needLog);
                    /***************************/
                    child.setPrevPage(new PageNumber(0));

                    result = new OperResult(true, child.pageNumber);
                    // unfix child
                    buffer.releasePage(child.pageNumber.getTreeId(), child.pageNumber, true);

                } else {
                    PageNumber p = new PageNumber(BTreeSpec.INVALID_PAGENO);
                    p.setTreeId(pageNumber.getTreeId());
                    result = new OperResult(true, p);
                }
                //free this root
                /******************* Add by Leon, Sep 30 *****************/
                if (needLog) {
                    int pgno = pageNumber.getPageNumber();
                    int prevPg = prevPage.getPageNumber();
                    int nextPg = nextPage.getPageNumber();

                    BTreeFreePageLogRecord lr = new BTreeFreePageLogRecord(pageNumber.getTreeId(), pgno, txnId, flags, prevPg, nextPg, keyType, -1, btreeSpec.btree.getType());
                    lr.log();
                }
                /***************************/
                buffer.addToFreeList(this.pageNumber.getTreeId(), this.pageNumber, needLog ? new Integer(txnId) : null); // delete the current page L

                // if the page has too few nodes and this is not the root page
            } else if (this.prevPage.getPageNumber() != 0 && currNode < order / 2) {
                BTreePage parent = new BTreePage(btreeSpec.btree.getBtreeId(), this.prevPage, btreeSpec, buffer);
                int nodeSize = btreeSpec.getInternalNodeSize();
                Key newKey = null; // V'
                BTreePage newPage = null; // L'

                // if this is the leftmost child in parent page
                // set L' to be the right sibling of the page
                if (parent.nextPage.getPageNumber() == this.pageNumber.getPageNumber()) {
                    // get L' and V'
                    BTreeNode newNode = parent.getNode(0);
                    /* Modified by ben zhang at Aug, 12, 2002*/
                    newKey = newNode.getKey();
                    newPage = new BTreePage(btreeSpec.btree.getBtreeId(), newNode.getPageNumber(), btreeSpec, buffer);
                    verifyLR(parent.pageNumber, pageNumber, newPage.pageNumber, true);

                    // if nodes in L and L' can fit in one page
                    int size = order;
                    if (!isLeaf() && isBTree()) size = order - 1;
                    if (currNode + newPage.getCurrNodeNumbers() <= size) {
                        if (isLeaf()) {
                            // append all nodes in L' to L
                            /********* add by leon *******/
                            if (needLog) {
                                int pgno = pageNumber.getPageNumber();
                                int siblingpgno = newPage.getPageNumber().getPageNumber();
                                BTreeMergeLogRecord lr = new BTreeMergeLogRecord(pageNumber.getTreeId(), pgno, txnId, false, siblingpgno, page, newPage.getPage(), (short) btreeSpec.getNodeSize(), btreeSpec.btree.getType());
                                lr.log();
                            }
                            /***************************/

                            for (int j = 0; j < newPage.getCurrNodeNumbers(); j++) {
                                upperBound -= nodeSize;
                                System.arraycopy(newPage.page, newPage.getNodeOffset(j), page, upperBound, nodeSize);
                                System.arraycopy(ByteTool.shortToBytes(upperBound), 0, page, lowerBound, 2);
                                lowerBound += 2;

                            }
                            setLowerBound(lowerBound);
                            setUpperBound(upperBound);

                            // set L.nextPage = L'.nextPage
                            setNextPage(newPage.nextPage);
                            // unfix this page L
                            buffer.releasePage(pageNumber.getTreeId(), pageNumber, true);
                        } else if (isBTree()) {
                            // append V' and leftmost pointer in L' to L
                            insertNode(newKey, ByteTool.intToBytes(newPage.nextPage.getPageNumber()), IDBIndex.STORE_REPLACE, kContext);
                            currNode = this.getCurrNodeNumbers();

                            // set newpage's child page's parent to this page
                            BTreePage childPage = new BTreePage(btreeSpec.btree.getBtreeId(), newPage.nextPage, btreeSpec, buffer);

                            /********* add by leon *******/
                            childPage.setLogInfo(txnId, needLog);
                            /***************************/
                            childPage.setPrevPage(pageNumber);
                            // unfix child page
                            buffer.releasePage(childPage.pageNumber.getTreeId(), childPage.pageNumber, true);
                            // fix this page

                            /********* add by leon *******/
                            if (needLog) {
                                int pgno = pageNumber.getPageNumber();
                                int siblingpgno = newPage.getPageNumber().getPageNumber();

                                BTreeMergeLogRecord lr = new BTreeMergeLogRecord(pageNumber.getTreeId(), pgno, txnId, false, siblingpgno, page, newPage.getPage(), (short) btreeSpec.getNodeSize(), btreeSpec.btree.getType());
                                lr.log();
                            }
                            /***************************/

                            // append all nodes in L' to L
                            for (int j = 0; j < newPage.getCurrNodeNumbers(); j++) {
                                upperBound -= nodeSize;
                                BTreeNode node = newPage.getNode(j);
                                // set newpage's child page's parent to this page
                                childPage = new BTreePage(btreeSpec.btree.getBtreeId(), node.getPageNumber(), btreeSpec, buffer);
                                /********* add by leon *******/
                                childPage.setLogInfo(txnId, needLog);
                                /***************************/
                                childPage.setPrevPage(pageNumber);
                                // unfix child page
                                buffer.releasePage(childPage.pageNumber.getTreeId(), childPage.pageNumber, true);
                                System.arraycopy(newPage.page, node.getNodeOffset(), page, upperBound, nodeSize);
                                System.arraycopy(ByteTool.shortToBytes(upperBound), 0, page, lowerBound, 2);
                                lowerBound += 2;
                            }
                            setLowerBound(lowerBound);
                            setUpperBound(upperBound);
                            // unfix this page
                            buffer.releasePage(pageNumber.getTreeId(), pageNumber, true);
                        }
                        if (Debug.DEBUG_BTREEPAGE) {
                            logger.debug("L: " + this.toString());
                            logger.debug("L': " + newPage.toString());
                        }
                        // delete L'
                        /******************* Add by Leon, Sep 30 *****************/
                        if (needLog) {
                            int pgno = newPage.pageNumber.getPageNumber();
                            int prevPg = newPage.prevPage.getPageNumber();
                            int nextPg = newPage.nextPage.getPageNumber();

                            BTreeFreePageLogRecord lr = new BTreeFreePageLogRecord(pageNumber.getTreeId(), pgno, txnId, newPage.flags, prevPg, nextPg, newPage.keyType, -1, btreeSpec.btree.getType());
                            lr.log();
                        }
                        /***************************/
                        buffer.addToFreeList(btreeSpec.btree.getBtreeId(), newPage.pageNumber, needLog ? new Integer(txnId) : null);

                        // recursive delete
                        result = parent.delete(newKey, kContext);

                    } else {

                        // redistribution: borrow one node from L' and insert to L rightmost
                        if (isLeaf()) {
                            BTreeNode firstNode = newPage.getNode(0);
                            int firstNodeOffset = firstNode.getNodeOffset();
                            // insert the new node to L rightmost
                            /***************** Add by leon,2001-9-27 14:56 ********************/
                            if (needLog) {
                                int pgno = pageNumber.getPageNumber();
                                byte[] oldV = ByteTool.shortToBytes(lowerBound);
                                oldV = ByteTool.append(oldV, ByteTool.shortToBytes(upperBound));
                                byte[] newV = ByteTool.shortToBytes((short) (lowerBound + 2));
                                newV = ByteTool.append(newV, ByteTool.shortToBytes((short) (upperBound - nodeSize)));
                                if (oldV != newV) {
                                    BTreeReplLogRecord lr = new BTreeReplLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeSpec.OFF_LOWERBOUND, oldV, newV, btreeSpec.btree.getType());
                                    lr.log();
                                }
                            }
                            /******************************************************************/
                            short oldLB = lowerBound;
                            setUpperBound((short) (upperBound - nodeSize));
                            setLowerBound((short) (lowerBound + 2));

                            /***************** Add by leon,2001-9-27 14:56 ********************/
                            if (needLog) {
                                int pgno = pageNumber.getPageNumber();
                                byte[] node = ByteTool.copyByteArray(newPage.page, firstNodeOffset, nodeSize);

                                BTreeAddRemoveLogRecord lr = new BTreeAddRemoveLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeAddRemoveLogRecord.ADD_FLAG, upperBound, node, btreeSpec.btree.getType());
                                lr.log();
                            }
                            /******************************************************************/
                            System.arraycopy(newPage.page, firstNodeOffset, page, upperBound, nodeSize);

                            /***************** Add by leon,2001-9-27 14:56 ********************/
                            if (needLog) {
                                int pgno = pageNumber.getPageNumber();
                                BTreeAddRemoveLogRecord lr = new BTreeAddRemoveLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeAddRemoveLogRecord.ADD_FLAG, oldLB, ByteTool.shortToBytes(upperBound), btreeSpec.btree.getType());
                                lr.log();
                            }
                            /******************************************************************/
                            System.arraycopy(ByteTool.shortToBytes(upperBound), 0, page, oldLB, 2);

                            // remove the first node from L'

                            /******************* Add by Leon, Sep 29 *****************/
                            int pgno = newPage.pageNumber.getPageNumber();
                            if (needLog) {
                                //remember the dead node
                                byte[] data = ByteTool.copyByteArray(newPage.page, firstNodeOffset, nodeSize);
                                BTreeAddRemoveLogRecord lr = new BTreeAddRemoveLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeAddRemoveLogRecord.REMOVE_FLAG, firstNodeOffset, data, btreeSpec.btree.getType());
                                lr.log();

                                //here we dont remember the offset of this node, for later move offset talbe will
                                //log it, which is different from borrowing the last node from left sibling.

                            }

                            if (firstNodeOffset != newPage.upperBound) {
                                if (needLog) {
                                    byte[] data = ByteTool.copyByteArray(newPage.page, newPage.upperBound, nodeSize);

                                    BTreeMoveLogRecord lr1 = new BTreeMoveLogRecord(pageNumber.getTreeId(), pgno, txnId, newPage.upperBound, firstNodeOffset, data.length, data, btreeSpec.btree.getType());
                                    lr1.log();
                                }
                                /***************************/
                                System.arraycopy(newPage.page, newPage.upperBound, newPage.page, firstNodeOffset, nodeSize);

                                // find the key has upperBound
                                int upperBoundNodeIndex = -1;
                                // some better algorithm later ###
                                for (int j = 0; j < newPage.getCurrNodeNumbers(); j++) {
                                    if (newPage.getNodeOffset(j) == newPage
                                            .upperBound) {
                                        upperBoundNodeIndex = j;
                                        break;
                                    }
                                }
                                if (upperBoundNodeIndex >= 0) {
                                    /***************** Add by leon,2001-9-27 14:56 ********************/
                                    if (needLog) {
                                        byte[] oldV = ByteTool.copyByteArray(newPage.page, BTreeSpec
                                                .PAGE_HEADER_SIZE + 2 * upperBoundNodeIndex, 2);
                                        byte[] newV = ByteTool.shortToBytes((short) firstNodeOffset);
                                        if (oldV != newV) {
                                            BTreeReplLogRecord lr = new BTreeReplLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeSpec
                                                    .PAGE_HEADER_SIZE + 2 * upperBoundNodeIndex, oldV, newV, btreeSpec.btree.getType());
                                            lr.log();
                                        }
                                    }
                                    /******************************************************************/
                                    System.arraycopy(ByteTool.shortToBytes((short) firstNodeOffset), 0, newPage.page, BTreeSpec
                                            .PAGE_HEADER_SIZE + 2 * upperBoundNodeIndex, 2);

                                }
                            }
                            // rest clean up, upperBound, lowerBound
                            oldLB = newPage.lowerBound;
                            /***************** Add by leon,2001-9-27 14:56 ********************/

                            if (needLog) {
                                byte[] oldV = ByteTool.shortToBytes(newPage.lowerBound);
                                oldV = ByteTool.append(oldV, ByteTool.shortToBytes(newPage.upperBound));
                                byte[] newV = ByteTool.shortToBytes((short) (newPage.lowerBound - 2));
                                newV = ByteTool.append(newV, ByteTool.shortToBytes((short) (newPage
                                        .upperBound + nodeSize)));
                                BTreeReplLogRecord lr = new BTreeReplLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeSpec.OFF_LOWERBOUND, oldV, newV, btreeSpec.btree.getType());
                                lr.log();
                            }
                            /******************************************************************/
                            newPage.setUpperBound((short) (newPage.upperBound + nodeSize));
                            newPage.setLowerBound((short) (newPage.lowerBound - 2));
                            // move all pointers one slot up

                            /******************* Add by Leon, Sep 29 *****************/
                            if (needLog) {
                                byte[] data = ByteTool.copyByteArray(newPage.page, BTreeSpec.PAGE_HEADER_SIZE, 2);

                                BTreeAddRemoveLogRecord lr = new BTreeAddRemoveLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeAddRemoveLogRecord.REMOVE_FLAG, BTreeSpec.PAGE_HEADER_SIZE, data, btreeSpec.btree.getType());

                                lr.log();

                                data = ByteTool.copyByteArray(newPage.page, BTreeSpec.PAGE_HEADER_SIZE + 2, oldLB - BTreeSpec.PAGE_HEADER_SIZE - 2);

                                BTreeMoveLogRecord lr1 = new BTreeMoveLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeSpec.PAGE_HEADER_SIZE + 2, BTreeSpec.PAGE_HEADER_SIZE, data.length, data, btreeSpec.btree.getType());
                                lr1.log();
                            }
                            /***************************/
                            System.arraycopy(newPage.page, BTreeSpec.PAGE_HEADER_SIZE + 2, newPage.page, BTreeSpec.PAGE_HEADER_SIZE, oldLB - BTreeSpec.PAGE_HEADER_SIZE - 2);

                            // replace V' in parent by firstNode's key
                            BTreeNode newFirstNode = newPage.getNode(0);
                            newNode.internalReplaceKey(newFirstNode.getKey().toBytes(), kContext);
                        } else if (isBTree()) {
                            PageNumber leftmostPageNumber = new PageNumber(newPage.nextPage);
                            BTreeNode firstNode = newPage.getNode(0);
                            PageNumber firstNodePageNumber = firstNode.getPageNumber();
                            /*Modified by ben zhang at Aug, 12, 2002 */
                            Key firstNodeKey = firstNode.getKey();
                            // remove the first node from L', already unfix page

                            /********* add by leon *******/
                            newPage.setLogInfo(txnId, needLog);
                            /***************************/
                            newPage.setNextPage(firstNodePageNumber);

                            newPage.deleteNode(firstNodeKey, kContext);
                            // insert V' and leftmostPageNumber to the rightmost of L
                            // already unfix page
                            insertNode(newKey, ByteTool.intToBytes(leftmostPageNumber.getPageNumber()), IDBIndex.STORE_REPLACE, kContext);

                            // set newpage's child page's parent to this page
                            BTreePage childPage = new BTreePage(btreeSpec.btree.getBtreeId(), leftmostPageNumber, btreeSpec, buffer);

                            /********* add by leon *******/
                            childPage.setLogInfo(txnId, needLog);
                            /***************************/
                            childPage.setPrevPage(pageNumber);

                            // unfix child page
                            buffer.releasePage(childPage.pageNumber.getTreeId(), childPage.pageNumber, true);
                            // replace V' in parent by firstNode's key
                            newNode.internalReplaceKey(firstNodeKey.toBytes(), kContext);

                        }

                        // unfix and unlock this page and its sibling
                        buffer.releasePage(pageNumber.getTreeId(), pageNumber, true);
                        buffer.releasePage(newPage.pageNumber.getTreeId(), newPage.pageNumber, true);
                        result = new OperResult(true, null);
                    }

                    // otherwise let L' be the left sibling (predecessor) of this page L
                } else {
                    // get L' and V'
                    int prevIndex = parent.nextPage.getPageNumber();
                    BTreeNode newNode = null;
//                    int newNodeLocation = 0;

                    for (int i = 0; i < parent.getCurrNodeNumbers(); i++) {
                        BTreeNode node = parent.getNode(i);
                        if (node.getPageNumber().getPageNumber() == this.pageNumber.getPageNumber()) {
                            /* Modified by ben at Aug, 12, 2002 */
                            newKey = node.getKey();
                            newNode = node;
//                            newNodeLocation = i;
                            break;
                        }
                        prevIndex = node.getPageNumber().getPageNumber();
                    }

                    tmpPgNo = new PageNumber(prevIndex);
                    tmpPgNo.setTreeId(btreeSpec.btree.getBtreeId());
                    newPage = new BTreePage(btreeSpec.btree.getBtreeId(), tmpPgNo, btreeSpec, buffer);

                    verifyLR(parent.pageNumber, newPage.pageNumber, pageNumber, false);

                    if (Debug.DEBUG_BTREEPAGE)
                        logger.debug("L'= " + newPage.pageNumber.getPageNumber() + " V'=" + new String(newKey.toBytes())); //Modified by ben at Aug, 12, 2002. Pending issue

                    // if nodes in L and L' can fit in one page
                    int size = order;
                    if (!isLeaf() && isBTree()) size = order - 1;
                    if (currNode + newPage.getCurrNodeNumbers() <= size) {
                        if (isLeaf()) {
                            /********* add by leon *******/
                            if (needLog) {
                                int pgno = pageNumber.getPageNumber();
                                int siblingpgno = newPage.getPageNumber().getPageNumber();

                                BTreeMergeLogRecord lr = new BTreeMergeLogRecord(pageNumber.getTreeId(), pgno, txnId, true, siblingpgno, page, newPage.getPage(), (short) btreeSpec.getNodeSize(), btreeSpec.btree.getType());
                                lr.log();
                            }
                            /***************************/
                            // append all nodes in L to L'
                            for (int j = 0; j < currNode; j++) {
                                newPage.upperBound -= nodeSize;
                                System.arraycopy(page, getNodeOffset(j), newPage.page, newPage.upperBound, nodeSize);
                                System.arraycopy(ByteTool.shortToBytes(newPage.upperBound), 0, newPage.page, newPage.lowerBound, 2);
                                newPage.lowerBound += 2;

                            }
                            newPage.setLowerBound(newPage.lowerBound);
                            newPage.setUpperBound(newPage.upperBound);
                            // set L'.nextPage = L.nextPage

                            /********* add by leon *******/
                            newPage.setLogInfo(txnId, needLog);
                            /***************************/
                            newPage.setNextPage(this.nextPage);

                            // unfix new page
                            buffer.releasePage(newPage.pageNumber.getTreeId(), newPage.pageNumber, true);
                        } else if (isBTree()) {
                            // append V' and leftmost pointer in L to L'
                            newPage.insertNode(newKey, ByteTool.intToBytes(this.nextPage.getPageNumber()), IDBIndex.STORE_REPLACE, kContext);

                            // set child page's parent to newPage
                            BTreePage childPage = new BTreePage(btreeSpec.btree.getBtreeId(), this.nextPage, btreeSpec, buffer);
                            /********* add by leon *******/
                            childPage.setLogInfo(txnId, needLog);
                            /***************************/
                            childPage.setPrevPage(newPage.pageNumber);
                            // unfix child page
                            buffer.releasePage(childPage.pageNumber.getTreeId(), childPage.pageNumber, true);
                            // fix new page
                            //newPage.page = buffer.getPage(btreeSpec.btree.getBtreeId(),newPage.pageNumber);

                            /********* add by leon *******/
                            if (needLog) {
                                int pgno = pageNumber.getPageNumber();
                                int siblingpgno = newPage.getPageNumber().getPageNumber();

                                BTreeMergeLogRecord lr = new BTreeMergeLogRecord(pageNumber.getTreeId(), pgno, txnId, true, siblingpgno, page, newPage.getPage(), (short) btreeSpec.getNodeSize(), btreeSpec.btree.getType());
                                lr.log();
                            }
                            /***************************/
                            // append all nodes in L to L'
                            for (int j = 0; j < currNode; j++) {
                                newPage.upperBound -= nodeSize;
                                BTreeNode node = getNode(j);
                                // set child page's parent to new page
                                childPage = new BTreePage(btreeSpec.btree.getBtreeId(), node.getPageNumber(), btreeSpec, buffer);

                                /********* add by leon *******/
                                childPage.setLogInfo(txnId, needLog);
                                /***************************/
                                childPage.setPrevPage(newPage.pageNumber);
                                // unfix child page
                                buffer.releasePage(childPage.pageNumber.getTreeId(), childPage.pageNumber, true);
                                System.arraycopy(page, getNodeOffset(j), newPage.page, newPage.upperBound, nodeSize);
                                System.arraycopy(ByteTool.shortToBytes(newPage.upperBound), 0, newPage.page, newPage.lowerBound, 2);
                                newPage.lowerBound += 2;

                            }
                            newPage.setLowerBound(newPage.lowerBound);
                            newPage.setUpperBound(newPage.upperBound);
                            // unfix new page
                            buffer.releasePage(newPage.pageNumber.getTreeId(), newPage.pageNumber, true);
                        }
                        if (Debug.DEBUG_BTREEPAGE) {
                            logger.debug("L: " + this.toString());
                            logger.debug("L': " + newPage.toString());
                        }

                        // delete L
                        /******************* Add by Leon, Sep 30 *****************/
                        if (needLog) {
                            int pgno = pageNumber.getPageNumber();
                            int prevPg = prevPage.getPageNumber();
                            int nextPg = nextPage.getPageNumber();

                            BTreeFreePageLogRecord lr = new BTreeFreePageLogRecord(pageNumber.getTreeId(), pgno, txnId, flags, prevPg, nextPg, keyType, -1, btreeSpec.btree.getType());
                            lr.log();
                        }
                        /***************************/
                        buffer.addToFreeList(btreeSpec.btree.getBtreeId(), this.pageNumber, needLog ? new Integer(txnId) : null);

                        // delete node from parent
                        result = parent.delete(newKey, kContext);

                    } else {

                        // redistribution: borrow and node from L' and insert to L leftmost
                        if (isLeaf()) {
                            BTreeNode lastNode = newPage.getNode(newPage.getCurrNodeNumbers() - 1);
                            byte[] lastNodeKey = lastNode.getKey().toBytes();
                            int lastNodeOffset = lastNode.getNodeOffset();
                            // insert to the new node to L leftmost
                            /***************** Add by leon,2001-9-27 14:56 ********************/

                            if (needLog) {
                                int pgno = pageNumber.getPageNumber();
                                byte[] oldV = ByteTool.shortToBytes(lowerBound);
                                oldV = ByteTool.append(oldV, ByteTool.shortToBytes(upperBound));
                                byte[] newV = ByteTool.shortToBytes((short) (lowerBound + 2));
                                newV = ByteTool.append(newV, ByteTool.shortToBytes((short) (upperBound - nodeSize)));
                                BTreeReplLogRecord lr = new BTreeReplLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeSpec.OFF_LOWERBOUND, oldV, newV, btreeSpec.btree.getType());
                                lr.log();
                            }
                            /******************************************************************/
                            setLowerBound((short) (lowerBound + 2));
                            setUpperBound((short) (upperBound - nodeSize));
                            /***************** Add by leon,2001-9-27 14:56 ********************/
                            if (needLog) {
                                int pgno = pageNumber.getPageNumber();
                                byte[] node = ByteTool.copyByteArray(newPage.page, lastNodeOffset, nodeSize);

                                BTreeAddRemoveLogRecord lr = new BTreeAddRemoveLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeAddRemoveLogRecord.ADD_FLAG, upperBound, node, btreeSpec.btree.getType());
                                lr.log();
                            }
                            /******************************************************************/
                            System.arraycopy(newPage.page, lastNodeOffset, page, upperBound, nodeSize);

                            /******************* Add by Leon, Sep 29 *****************/

                            if (needLog) {
                                int pgno = pageNumber.getPageNumber();
                                byte[] oldV = ByteTool.copyByteArray(page, BTreeSpec.PAGE_HEADER_SIZE, 2 * currNode);
                                byte[] newV = ByteTool.shortToBytes(upperBound);
                                newV = ByteTool.append(newV, oldV);
                                BTreeReplLogRecord lr = new BTreeReplLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeSpec.PAGE_HEADER_SIZE, oldV, newV, btreeSpec.btree.getType());
                                lr.log();
                            }
                            /***************************/
                            System.arraycopy(page, BTreeSpec.PAGE_HEADER_SIZE, page, BTreeSpec.PAGE_HEADER_SIZE + 2, 2 * currNode);
                            System.arraycopy(ByteTool.shortToBytes(upperBound), 0, page, BTreeSpec.PAGE_HEADER_SIZE, 2);

                            // remove the last node from L'

                            /******************* Add by Leon, Sep 29 *****************/
                            int pgno = newPage.pageNumber.getPageNumber();
                            if (needLog) {
                                //remember the dead node.
                                byte[] data = ByteTool.copyByteArray(newPage.page, lastNodeOffset, nodeSize);
                                BTreeAddRemoveLogRecord lr = new BTreeAddRemoveLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeAddRemoveLogRecord.REMOVE_FLAG, lastNodeOffset, data, btreeSpec.btree.getType());
                                lr.log();

                                data = ByteTool.copyByteArray(newPage.page, newPage.lowerBound - 2, 2);
                                lr = new BTreeAddRemoveLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeAddRemoveLogRecord.REMOVE_FLAG, newPage.lowerBound - 2, data, btreeSpec.btree.getType());
                                lr.log();

                            }

                            if (lastNodeOffset != newPage.upperBound) {
                                if (needLog) {
                                    byte[] data = ByteTool.copyByteArray(newPage.page, newPage.upperBound, nodeSize);

                                    BTreeMoveLogRecord lr1 = new BTreeMoveLogRecord(pageNumber.getTreeId(), pgno, txnId, newPage.upperBound, lastNodeOffset, data.length, data, btreeSpec.btree.getType());
                                    lr1.log();
                                }
                                /***************************/
                                System.arraycopy(newPage.page, newPage.upperBound, newPage.page, lastNodeOffset, nodeSize);

                                // find the key has upperBound
                                int upperBoundNodeIndex = -1;
                                // some better algorithm later ###
                                for (int j = 0; j < newPage.getCurrNodeNumbers(); j++) {
                                    if (newPage.getNodeOffset(j) == newPage
                                            .upperBound) {
                                        upperBoundNodeIndex = j;
                                        break;
                                    }
                                }
                                if (upperBoundNodeIndex >= 0) {
                                    /****** Add by leon,2001-9-27 14:56 ******/

                                    if (needLog) {
                                        byte[] oldV = ByteTool.copyByteArray(newPage.page, BTreeSpec
                                                .PAGE_HEADER_SIZE + 2 * upperBoundNodeIndex, 2);
                                        byte[] newV = ByteTool.shortToBytes((short) lastNodeOffset);
                                        BTreeReplLogRecord lr = new BTreeReplLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeSpec
                                                .PAGE_HEADER_SIZE + 2 * upperBoundNodeIndex, oldV, newV, btreeSpec.btree.getType());
                                        lr.log();
                                    }
                                    /******************************************************************/
                                    System.arraycopy(ByteTool.shortToBytes((short) lastNodeOffset), 0, newPage.page, BTreeSpec
                                            .PAGE_HEADER_SIZE + 2 * upperBoundNodeIndex, 2);

                                }
                            }
                            // rest clean up, upperBound, lowerBound

                            /******************* Add by Leon, Sep 29 *****************/

                            if (needLog) {
                                byte[] oldV = ByteTool.copyByteArray(newPage.page, BTreeSpec.OFF_LOWERBOUND, 4);
                                byte[] newV = ByteTool.shortToBytes((short) (newPage.lowerBound - 2));
                                newV = ByteTool.append(newV, ByteTool.shortToBytes((short) (newPage
                                        .upperBound + nodeSize)));
                                BTreeReplLogRecord lr = new BTreeReplLogRecord(pageNumber.getTreeId(), pgno, txnId, BTreeSpec.OFF_LOWERBOUND, oldV, newV, btreeSpec.btree.getType());
                                lr.log();
                            }
                            /***************************/
                            newPage.setUpperBound((short) (newPage.upperBound + nodeSize));
                            newPage.setLowerBound((short) (newPage.lowerBound - 2));

                            if (newPage.getCurrNodeNumbers() == 0) {
                                /******************* Add by Leon, Sep 30 *****************/
                                if (needLog) {
                                    int prevPg = newPage.prevPage.getPageNumber();
                                    int nextPg = newPage.nextPage.getPageNumber();

                                    BTreeFreePageLogRecord lr = new BTreeFreePageLogRecord(pageNumber.getTreeId(), pgno, txnId, newPage.flags, prevPg, nextPg, newPage.keyType, -1, btreeSpec.btree.getType());
                                    lr.log();
                                }
                                /***************************/
                                buffer.addToFreeList(btreeSpec.btree.getBtreeId(), newPage.pageNumber, needLog ? new Integer(txnId) : null);
                            }

                            // replace V' in parent by lastNode's key
                            newNode.internalReplaceKey(lastNodeKey, kContext);

                        } else if (isBTree()) {
                            BTreeNode lastNode = newPage.getNode(newPage.getCurrNodeNumbers() - 1);
                            PageNumber lastNodePageNumber = lastNode.getPageNumber();
                            /* Modified by ben zhang at Aug, 12, 2002 */
                            Key lastNodeKey = lastNode.getKey();
                            // set child page's parent to this page
                            BTreePage childPage = new BTreePage(btreeSpec.btree.getBtreeId(), lastNodePageNumber, btreeSpec, buffer);

                            /********* add by leon *******/
                            childPage.setLogInfo(txnId, needLog);
                            /***************************/
                            childPage.setPrevPage(pageNumber);
                            // unfix child page
                            buffer.releasePage(childPage.pageNumber.getTreeId(), childPage.pageNumber, true);
                            PageNumber oldNextPage = new PageNumber(nextPage);
                            setNextPage(lastNodePageNumber);
                            // insert lastNodePageNumber and V' to L; already unfix page
                            insertNode(newKey, ByteTool.intToBytes(oldNextPage.getPageNumber()), IDBIndex.STORE_REPLACE, kContext);
                            // remove the last node from L'; already unfix page
                            newPage.deleteNode(lastNodeKey, kContext);
                            // replace V' in parent by lastNode's key
                            newNode.internalReplaceKey(lastNodeKey.toBytes(), kContext);

                        }

                        // unfix this page and new page
                        buffer.releasePage(pageNumber.getTreeId(), pageNumber, true);
                        buffer.releasePage(newPage.pageNumber.getTreeId(), newPage.pageNumber, true);

                        result = new OperResult(true, null);
                    }
                }
                // release parent page
                buffer.releasePage(parent.pageNumber.getTreeId(), parent.pageNumber, true);

            } else {
                //if this page needs not borrow node from its sibling or merge
                //then release and unlock it
                buffer.releasePage(this.pageNumber.getTreeId(), this.pageNumber, true);
                result = new OperResult(true, null);
            }
        } finally {
        }

        return result;
    }

    /***************************** Add by Leon ********************/
    /**
     * Set the txnId and needLog flag of this page
     */
    public void setLogInfo(int txnId, boolean needLog) {
        this.txnId = txnId;
        this.needLog = needLog;
    }

    private void verifyLR(PageNumber parent, PageNumber left, PageNumber right, boolean iAmLeft) {
        if (left.equ(right)) {
            logger.debug(Debug.getDebugInfo() + " Parent:" + parent.toHexString() + ",Left:" + left.toHexString() + ",Right:" + right.toHexString() + ". I am " + (iAmLeft ? "left" : "right"));
            Debug.pageHistory(parent);
            Debug.pageHistory(left);
            //Debug.pageHistory(Integer.toHexString(right.getPageNumber()));
            Thread.dumpStack();
            if (Debug.DEBUG_THREAD) Debug.flushThreads();
        }
    }

    /**
     * Insert data with duplicated key support.
     *
     * @param dataPage
     * @param key
     * @param data
     * @param mode
     * @param kContext
     * @param nodeOffset
     * @return new PageNumber and Offset
     * @throws ChaiDBException
     */
    private byte[] insertDupNode(DataPage dataPage, Key key, byte[] data, short mode, KernelContext kContext, int nodeOffset) throws ChaiDBException {
        byte[] ret = null;
        try {
            //Debug
//            buffer.dumpMemInfo(PageBufferManager.DUMP_ALL, "insertDupNode_begin");
            int newNodeSize = BTreeSpec.DATA_NODE_HEADER_SIZE + data.length;
            int docId = 0;
            DataNode dataNode = null;
            boolean KeyExist = false;
            if (nodeOffset > 0) {
                // key exist
                dataNode = dataPage.getNode(nodeOffset);
                if (ByteTool.compare(data, 0, dataNode.getData(), 0, data.length) == 0) {
                    return ret; //return null if data exist
                }
                KeyExist = true;
            } else {
                nodeOffset = dataPage.upperBound;
            }
            // if has enough space, and lastest node was at top, just insert the node into the datapage
            if (nodeOffset == dataPage.upperBound && dataPage.getFreeSpace() > newNodeSize) {
                int newOffset = dataPage.insertNode(key.toBytes(), data, mode, kContext, -1, KeyExist ? BTreeSpec.DATA_NODE_DUP : 0);
                ret = new byte[4];
                ByteTool.intToBytes(ret, 0, newOffset);
                //Debug
//                buffer.dumpMemInfo(PageBufferManager.DUMP_ALL, "insertDupNode_enoughspace");
                return ret;
            }
            // if has no enough space, need to new a data page and insert node to the btree page
            /*******************
             * If the lastest node is not at the top, the size of new node need include linked node
             * there are two ways to insert new node.
             *
             * 1. insert the node in current page, it may cause frag in page, but has less
             *    inflation ratio.
             * 2. new a datapage, insert the node in the new page, in this case, the new page will
             *    be a private page of the key. This will improve performance if there are lots of
             *    values in a key
             *
             **********************/
            else {
                DataPage newDataPage = DataPage.newPage(btreeSpec, buffer, false, kContext, docId);
                ret = new byte[8];
                if (KeyExist) {
                    ByteTool.intToBytes(ret, 0, nodeOffset);
                    ByteTool.intToBytes(ret, 4, dataPage.getPageNumber().getPageNumber());
                    newDataPage.insertNode(key.toBytes(), ret, mode, kContext, -1, BTreeSpec.DATA_NODE_DUP_NEXT);
                }
                int newOffset = newDataPage.insertNode(key.toBytes(), data, mode, kContext, -1, KeyExist ? BTreeSpec.DATA_NODE_DUP : 0);
                ByteTool.intToBytes(ret, 0, newOffset);
                ByteTool.intToBytes(ret, 4, newDataPage.getPageNumber().getPageNumber());
                buffer.releasePage(newDataPage.pageNumber.getTreeId(), newDataPage.pageNumber, true);
                return ret;
            }
        } catch (Exception e) {
            logger.error(e);
            String errorMsg = "Exception in insertDupNode: Key is " + ((IntKey) key).getInt();
            throw new ChaiDBException(ErrorCode.BTREE_ERROR_BASE, errorMsg);
        } finally {
            //Debug
//            buffer.dumpMemInfo(PageBufferManager.DUMP_ALL, "insertDupNode_finally");
        }
    }
}
TOP

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

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.