Package org.chaidb.db.index.btree

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

/*
* 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.KernelContext;
import org.chaidb.db.exception.ChaiDBException;
import org.chaidb.db.exception.EncodingException;
import org.chaidb.db.exception.ErrorCode;
import org.chaidb.db.helper.ByteTool;
import org.chaidb.db.index.IDBIndex;
import org.chaidb.db.index.ISingleKeyBTree;
import org.chaidb.db.index.Key;
import org.chaidb.db.index.btree.bufmgr.PageNumber;
import org.chaidb.db.lock.LockManager;
import org.chaidb.db.log.logrecord.BTreeFreeOverflowPageLogRecord;
import org.chaidb.db.log.logrecord.BTreeFreePageWithDataLogRecord;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;

// Todo:
// 1) cursor
// 2) hashing
// 3) writing

/**
* This class implements Berkeley DB database format.
* <p/>
* There are 3 classes:</P>
* <DL>
* <DT>BTree<DD> Maintains the actual file handle, and
* answers requests for pages.  Auxiliary classes are
* instantiated with a reference to this class so that they
* can access the file.
* <DT>BTreePage<DD> Most database
* manipulations are handled here. For example, to search, the
* top-level BTree object fetches the root page (always page 1)
* from the page file and asks it to
* search itself. It will go back to the page file to fetch additional
* pages as needed.
* <DT>BTreeNode<DD>A fairly simple class that encapsulates
* the key/data pairs stored in pages.
* </DD>
*/
public class BTree extends AbstractBTree implements ISingleKeyBTree {

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

    public static int DOCID_SIZE = 3;

    protected AbstractBTree GenerateClonedBTree() throws ChaiDBException {
        return new BTree();
    }

    protected void copyData(AbstractBTree newBTree, KernelContext kContext) throws ChaiDBException {
        try {
            this.acquire(kContext, LockManager.LOCK_READ);
            newBTree.acquire(kContext, LockManager.LOCK_WRITE);
            Enumeration keys = keys(kContext);
            if (keys == null) return;
            while (keys.hasMoreElements()) {
                Key key = (Key) keys.nextElement();
                newBTree.store(key, lookup(key, kContext), IDBIndex.STORE_REPLACE, kContext);
            }
        } finally {
            newBTree.release(kContext);
            this.release(kContext);
        }
    }


    /**
     * returns the values associated with an index
     *
     * @return A list of values associated with the given key,
     *         if not found, return null
     * @throws ChaiDBException Exception while looking up the key
     */
    public Object lookup(Key key, KernelContext kContext) throws ChaiDBException {
        kContext.checkLock(getBTreeName());
        if (key == null) return null;
        byte[] bytes = null;
        for (int i = 0; i < 3; i++) {
            try {
                PageNumber root = getTopRoot();
                bytes = lookup(key, root, kContext);
            } catch (ChaiDBException e) {
                if (i == 2) {
                    throw e;
                } else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException ie) {
                        ;
                    }
                    continue;

                }

            } finally {
                doFinalJob();
            }
            break;
        }//end for
        return decodeFromByteArray(bytes, key);
    }

    //Before going out, release all BTree level locks
    protected byte[] lookup(Key key, PageNumber root, KernelContext kContext) throws ChaiDBException {
        BTreePage rootPage = new BTreePage(id, root, btreeSpec, getBuffer());
        if (rootPage.getPage() == null) {
            return null;
        }

        BTreeNode n = rootPage.search(key, kContext);
        // unfix root and leaf page.
        getBuffer().releasePage(id, rootPage.pageNumber, false);

        if (n == null) {
            return null;
        }

        byte[] value = n.getData(kContext);

        //if found,release it.
        getBuffer().releasePage(id, n.getBTreePage().pageNumber, false);
        return value;
    }


    public ArrayList rangeLookup(Key minKey, Key maxKey, boolean includeMinKey, boolean includeMaxKey, KernelContext kContext) throws ChaiDBException {
        kContext.checkLock(getBTreeName());
        ArrayList values = new ArrayList();
        for (int i = 0; i < 3; i++) {
            try {
                // if (islockTree) lm.getLock(MR1WLock.READ);
                PageNumber root = getTopRoot();
                rangeLookup(minKey, maxKey, includeMinKey, includeMaxKey, root, values, kContext);
            } catch (ChaiDBException e) {
                if (i == 2) {
                    throw e;
                } else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException ie) {
                        ;
                    }
                    continue;

                }
            } finally {
                doFinalJob();
            }
            break;
        }//end for


        try {
            if (converter != null) {
                ArrayList newList = new ArrayList(values.size());
                for (int i = 0; i < values.size(); i++) {
                    Object obj = values.get(i);
                    newList.add(converter.decodeFromByteArray(minKey, (byte[]) obj));
                }
                return newList;
            } else return values;

        } catch (EncodingException ee) {
            logger.debug(ee);
            String details = "Converter failed to decode : " + ee.toString() + ".";
            throw new ChaiDBException(ErrorCode.CONVERTER_DECODING_ERROR, details);
        }
    }

    //here convert is not performed
    protected void rangeLookup(Key minKey, Key maxKey, boolean includeMinKey, boolean includeMaxKey, PageNumber root, ArrayList values, KernelContext kContext) throws ChaiDBException {

        if (!checkKeyPair(minKey, maxKey)) return;

        BTreePage page;

        if (minKey == null) {
            page = findLeftMostLeaf(root);
        } else {
            page = new BTreePage(id, root, btreeSpec, getBuffer());
            if (page.getPage() != null) {
                page = page.getLeaf(minKey, kContext, BTreePage.SEARCH);
                getBuffer().releasePage(id, root, false);
            }
        }
        if (page.getPage() != null) {
            page.search(minKey, maxKey, includeMinKey, includeMaxKey, kContext, values);
            // unfix root and leaf page.
            getBuffer().releasePage(id, page.pageNumber, false);
        }

    }


    /**
     * returns either enumeration or set of keys.
     * If DBM and BTREE, return enumeration;
     * if HashMap or TreeMap, return Set.
     *
     * @return Enumeration of keys
     */
    public Enumeration keys(KernelContext kContext) {
        BTreeEnumerator btreeEnum = null;
        for (int i = 0; i < 3; i++) {
            try {
                kContext.checkLock(getBTreeName());
                btreeEnum = new BTreeEnumerator(this, btreeSpec, getBuffer(), kContext);
            } catch (ChaiDBException e) {
                if (i == 2) {
                    logger.debug(e);
                    return null;
                } else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException ie) {
                        ;
                    }
                    continue;


                }
            }
            break;
        }//end for
        return btreeEnum;
    }


    /**
     * returns the index type of this index
     *
     * @return A short integer representing the index type
     */
    public short getType() {
        return IDBIndex.BTREE;
    }

    /**
     * @param root
     * @param dataPageMixed ONLY ture, for non-id2node.idb, if data pages dont belongs to one doc.
     * @param kContext
     * @throws ChaiDBException
     */
    protected void delSubTreeCore(PageNumber root, boolean dataPageMixed, KernelContext kContext) throws ChaiDBException {

        BTreePage rootPage = new BTreePage(id, root, btreeSpec, getBuffer());

        if (rootPage.getPage() != null) {
            ArrayList pageList = new ArrayList(rootPage.getCurrNodeNumbers() + 1);
            Hashtable dataPages = new Hashtable();
            BTreePage tmpPage = rootPage;
            do {
                freePages(tmpPage, pageList, dataPageMixed, dataPages, kContext);
                if (!pageList.isEmpty()) {
                    tmpPage = new BTreePage(id, (PageNumber) pageList.remove(0), btreeSpec, getBuffer());

                } else break;
            } while (true);

            if (dataPageMixed) return;

            //for id2node, datapages are not freed yet.

            Enumeration pagenumbers = dataPages.keys();
            PageNumber ppn = null;
            while (pagenumbers.hasMoreElements()) {
                ppn = (PageNumber) pagenumbers.nextElement();
                DataPage dPage = new DataPage(id, ppn, btreeSpec, getBuffer());

                if (Debug.DEBUG_TRAVERAL) {
                    logger.fatal("saved data page is " + ppn.toHexString() + " ");
                }
                freeAPage(kContext, dPage);
            }
            if (Debug.DEBUG_TRAVERAL) {
                logger.fatal("end loopup");
            }


        }
    }


    /**
     * Get all used information of passed page, and then free it.
     *
     * @param page
     * @param pageList
     * @param dataPages
     * @param kContext
     * @throws ChaiDBException
     */
    private void freePages(BTreePage page, ArrayList pageList, boolean dataPageMixed, Hashtable dataPages, KernelContext kContext) throws ChaiDBException {

        if (!page.isLeaf() && page.nextPage.getPageInFile() > BTreeSpec.INVALID_PAGENO) pageList.add(page.nextPage);


        for (int i = 0; i < page.getCurrNodeNumbers(); i++) {
            BTreeNode node = page.getNode(i);
            PageNumber p = node.getPageNumber();
            if (!page.isLeaf()) //add btree pages
                pageList.add(p);
            else {
                if (!dataPageMixed && !dataPages.containsKey(p)) {
                    PageNumber tmpPage = new PageNumber(p);
                    dataPages.put(tmpPage, tmpPage); //add data pages
                    if (Debug.DEBUG_TRAVERAL) {
                        logger.fatal("parent page is " + page.pageNumber.toHexString() + " ");
                        logger.fatal("data page is " + tmpPage.toHexString() + " ");
                    }

                }

                freeDataNode(p, node.getDataNodeOffset(), dataPageMixed, kContext);


            }

            if (node.isOverflowKey()) {
                p = new PageNumber(ByteTool.bytesToInt(page.getPage(), node.getNodeOffset() + BTreeSpec.NODE_HEADER_SIZE, btreeSpec.isMsbFirst()));
                p.setTreeId(id);

                freeOverFlowPages(p, kContext);
            }

        }
        //All info is got and then free this page
        freeAPage(kContext, page);
    }

    private void freeDataNode(PageNumber pNumber, int offset, boolean dataPageMixed, KernelContext kContext) throws ChaiDBException {

        DataPage dPage = new DataPage(id, pNumber, btreeSpec, getBuffer());
        if (dataPageMixed) {
            dPage.deleteNode(kContext, offset);
            //if this datapage is empty and isn't LDP, free it. Otherwise, just release it later
            if (dPage.getCurrNodeNumbers() == 0) {
                freeAPage(kContext, dPage);
                return;
            }

        } else {
            DataNode node = new DataNode(dPage, offset);
            if (node.isOverflow()) {
                PageNumber p = new PageNumber(ByteTool.bytesToInt(dPage.getPage(), node.getNodeOffset() + BTreeSpec.DATA_NODE_HEADER_SIZE, btreeSpec.isMsbFirst()));
                p.setTreeId(id);

                freeOverFlowPages(p, kContext);
            }
        }

        getBuffer().releasePage(id, pNumber, false);
    }


    //free overflowpage
    private void freeOverFlowPages(PageNumber overflowPageNumber, KernelContext kContext) throws ChaiDBException {
        while (overflowPageNumber.getPageNumber() > 0) {
            BTreePage overflowPage = new BTreePage(id, overflowPageNumber, btreeSpec, getBuffer());
            overflowPageNumber = overflowPage.nextPage;
            freeAPage(kContext, overflowPage);
        }
    }

    //In fact the two following method is same except for parameter BTreePage or DataPage.
    //This is becasue there is no a superclass aggreate both.
    private void freeAPage(KernelContext kContext, BTreePage recycledPage) throws ChaiDBException {
        int txnId = kContext.getLocker();
        boolean needLog = kContext.getNeedLog();

        // put into freeList
        /******************* Add by Leon, Sep 29 *****************/
        if (needLog) {
            int pgno = recycledPage.pageNumber.getPageNumber();
            short upBound = recycledPage.upperBound;
            //overflowpage: record the header.
            short lowBound = recycledPage.isOverflow() ? (short) BTreeSpec.PAGE_HEADER_SIZE : recycledPage.lowerBound;
            BTreeFreeOverflowPageLogRecord lr = new BTreeFreeOverflowPageLogRecord(id, pgno, txnId, ByteTool.copyByteArray(recycledPage.page, 0, lowBound), ByteTool.copyByteArray(recycledPage.page, upBound, BTreeSpec.PAGE_SIZE - upBound), btreeSpec.btree.getType());
            lr.log();
        }
        /***************************/
        getBuffer().addToFreeList(id, recycledPage.pageNumber, needLog ? new Integer(txnId) : null);
    }

    private void freeAPage(KernelContext kContext, DataPage recycledPage) throws ChaiDBException {
        int txnId = kContext.getLocker();
        boolean needLog = kContext.getNeedLog();

        // put into freeList
        /******************* Add by Leon, Sep 29 *****************/
        if (needLog) {
            int pgno = recycledPage.pageNumber.getPageNumber();
            short upBound = recycledPage.upperBound;

            BTreeFreePageWithDataLogRecord lr = new BTreeFreePageWithDataLogRecord(id, pgno, txnId, recycledPage.flags, recycledPage.docid, ByteTool.copyByteArray(recycledPage.page, 0, BTreeSpec.PAGE_HEADER_SIZE), ByteTool.copyByteArray(recycledPage.page, upBound, BTreeSpec.PAGE_SIZE - upBound), btreeSpec.btree.getType());
            lr.log();
        }
        /***************************/

        Integer iTxnID = new Integer(txnId);
        if (getBuffer().isLatestDataPage(recycledPage.docid, recycledPage.pageNumber, iTxnID))
            getBuffer().removeLatestDataPage(recycledPage.docid, id, recycledPage.pageNumber, iTxnID);


        getBuffer().addToFreeList(id, recycledPage.pageNumber, needLog ? iTxnID : null);
    }
}



TOP

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

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.