Package org.chaidb.db.index.btree

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

/*
* 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.DbEnvironment;
import org.chaidb.db.KernelContext;
import org.chaidb.db.api.Converter;
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.BTreeFactory;
import org.chaidb.db.index.IDBIndex;
import org.chaidb.db.index.Key;
import org.chaidb.db.index.LockableBTree;
import org.chaidb.db.index.btree.bufmgr.PageBufferManager;
import org.chaidb.db.index.btree.bufmgr.PageNumber;
import org.chaidb.db.log.logrecord.BTreeSpecLogRecord;

import java.io.File;
import java.util.LinkedList;

/**
* Abstract class providing the interface for an IDB index.
*/
public abstract class AbstractBTree extends LockableBTree implements IBTree, IDBIndex {

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

    /**
     * btree spec
     */
    public BTreeSpec btreeSpec = new BTreeSpec();


    /**
     * The converter to be used by this Index implementation, In the
     * *  absence of the converter, all values are treated as byte arrays
     */
    protected Converter converter = null;
    /**
     * buffer manager
     */
    protected static PageBufferManager buffer = PageBufferManager.getInstance();
    /**
     * When btree init success and its metadata is ready, this flag is set to be true
     */
    protected boolean isReady = false;
    /**
     * name of this tree
     */
    private String BTreeName;


    public void setConverter(Converter converter) {
        this.converter = converter;
    }

    public Converter getConverter() {
        return converter;
    }

    public AbstractBTree() {
        btreeSpec.setBtree(this);
    }

    public void setKeySize(int keySize) {
//        this.getBTreeSpec().setModified(true);
        keySize = (keySize < MIN_KEY_SIZE) ? MIN_KEY_SIZE : keySize;
        keySize = (keySize > MAX_KEY_SIZE) ? MAX_KEY_SIZE : keySize;
        this.btreeSpec.setNodeSize(BTreeSpec.NODE_HEADER_SIZE + keySize);
    }

    /**
     * Read the metadata which are stored in the first page. Set parameters in
     * BTreeSpec and BufferedPage correspondingly.
     * <br>
     * The complete list of DB BTree metadata is show below:
     * <pre>
     *  Size  Description
     *  4     Magic Value: 0x053162 (used to determine endianness)
     *  4     Version of BTREE file
     *  4     Page Size
     *  4     Next available page
     *  4     BTree layer value. (only the highest byte is used now).
     *  4     Flags:
     *          0x0020 - Duplicate keys are not permitted
     *          0x0080 - R_RECNO: "record oriented tree" (??)
     *  4     Root Page Number
     *  4     node order - number of nodes in one page
     *  4     Latest data page number
     *  4     Number of free pages
     *  List of free page numbers
     * <p/>
     * </pre>
     * Flags is not used at the current version.
     * Assume there are no duplicate keys.
     */
    public static void readMetaData(IBTree bTree, byte[] metaData) throws ChaiDBException {
        int magic = ByteTool.bytesToInt(metaData, BTreeSpec.MAGIC_OFF, bTree.getBTreeSpec().getMsbFirst());
        if (magic != MAGIC_NUMBER) {
            bTree.getBTreeSpec().setMsbFirst(!bTree.getBTreeSpec().getMsbFirst());
            magic = ByteTool.bytesToInt(metaData, BTreeSpec.MAGIC_OFF, bTree.getBTreeSpec().getMsbFirst());
        }

        // Read version number and abort if not a DB file.
//        int treeid = ByteTool.bytesToInt(metaData, BTreeSpec.BTREEID_OFF, bTree.getBTreeSpec().getMsbFirst());
        if ((magic != MAGIC_NUMBER)) // Magic num and ver
        {
            // give all the magic information as details -ranjeet
            String details = "Not a DB file (magic: 0x" + Integer.toHexString(magic) + ").";

            throw new ChaiDBException(ErrorCode.BTREE_INVALID, details);
        }
        bTree.getBTreeSpec().getPageNumber().setPageNumber(ByteTool.bytesToInt(metaData, BTreeSpec.NEXT_AVAILABLE_PAGE_OFF, bTree.getBTreeSpec().getMsbFirst()));
        bTree.getBTreeSpec().setLayer(metaData[BTreeSpec.LAYER_OFF], null);

        if (bTree.getType() == IDBIndex.SUPER_BTREE) {
            ((SuperBTree) bTree).setKeyTypes(new int[bTree.getBTreeSpec().getLayers()]);
            //read key type
            for (int i = 0; i < bTree.getBTreeSpec().getLayers(); i++) {
                ((SuperBTree) bTree).getKeyTypes()[i] = metaData[BTreeSpec.LAYER_OFF + i + 1];
            }
        }
        bTree.getBTreeSpec().treeType = metaData[BTreeSpec.BTREE_TYPE_OFF];
        bTree.getBTreeSpec().setRootPageNumber(ByteTool.bytesToInt(metaData, BTreeSpec.ROOT_OFF, bTree.getBTreeSpec().getMsbFirst()));

        bTree.getBTreeSpec().setNodeSize(ByteTool.bytesToInt(metaData, BTreeSpec.NODE_SIZE_OFF, bTree.getBTreeSpec().getMsbFirst()));

        // set latest dataPage PageNumber in bufferedPage
        PageNumber ldpReservedPage = new PageNumber(ByteTool.bytesToInt(metaData, BTreeSpec.LATEST_DATA_PAGE_LIST_OFF, bTree.getBTreeSpec().getMsbFirst()));
        ldpReservedPage.setTreeId(bTree.getBtreeId());

        LinkedList reservedList = new LinkedList();

        if (ldpReservedPage.getPageNumber() > 0) {
            // (page size - page number - next page number) / 4
            int pageTotalSpace = (bTree.getBTreeSpec().getPageSize() - BTreeSpec.PAGENUMBER_BYTE_SIZE - BTreeSpec.PAGENUMBER_BYTE_SIZE) / BTreeSpec.PAGENUMBER_BYTE_SIZE;
            int nextLDPReservedPageNum = ldpReservedPage.getPageNumber();
            byte[] page = bTree.getBuffer().getPage(bTree.getBtreeId(), ldpReservedPage);
            if (page != null) {
                int ldpNumOffset = BTreeSpec.OFF_PAGENUMBER + BTreeSpec.PAGENUMBER_BYTE_SIZE;
                int ldpNum = ByteTool.bytesToInt(page, ldpNumOffset, bTree.getBTreeSpec().getMsbFirst());
                while (nextLDPReservedPageNum > 0) {
                    for (int i = 0; i < ldpNum; i++) {
                        PageNumber latestDataPage = new PageNumber(ByteTool.bytesToInt(page, ldpNumOffset + 4 + i * BTreeSpec.PAGENUMBER_BYTE_SIZE, bTree.getBTreeSpec().getMsbFirst()));
                        latestDataPage.setTreeId(bTree.getBtreeId());
                        bTree.getBuffer().addToLatestDataPageListWhenOpenBTree(bTree.getBtreeId(), latestDataPage);
                    }
                    if ((ldpNum < pageTotalSpace - 1) || (ldpNum == pageTotalSpace)) {
                        nextLDPReservedPageNum = -1;
                    } else {
                        nextLDPReservedPageNum = ByteTool.bytesToInt(page, bTree.getBTreeSpec()
                                .getPageSize() - 4, bTree.getBTreeSpec().getMsbFirst());
                    }
                    /*              this page can't be used because if btree is closed abnormally in the way
                                    metapage contains old info whereas other pages has been updated. Thus maybe
                                    this "ldpReservedPage" is reused and occurs in some BTree(Data)Node's links. Then
                                    btree reopen we read obsolete data from meta page and put this "ldpReservedPage" to
                                    free page list, which later may cause to "Used Freed page" exception. The same
                                    thing happens to pages storing free pages. We prefer to losing these "savePages".
                    */
                    bTree.getBuffer().releasePage(bTree.getBtreeId(), ldpReservedPage, false);
                    reservedList.add(ldpReservedPage);
                    if (nextLDPReservedPageNum > 0) {
                        ldpReservedPage = new PageNumber(nextLDPReservedPageNum);
                        ldpReservedPage.setTreeId(bTree.getBtreeId());
                        page = bTree.getBuffer().getPage(bTree.getBtreeId(), ldpReservedPage);
                        if (page == null) {
                            break;
                        }
                        ldpNum = ByteTool.bytesToInt(page, ldpNumOffset, bTree.getBTreeSpec().getMsbFirst());
                    }
                }
            }
        }

        // set free list in buffererdPage

        int freePageNums = ByteTool.bytesToInt(metaData, BTreeSpec.FREE_PAGE_LIST_OFF, bTree.getBTreeSpec().getMsbFirst());
        if (freePageNums == 0) {
            bTree.getBuffer().setReserveredList(bTree.getBtreeId(), reservedList);
            return;
        }

        logger.debug("freePageNums = " + freePageNums);
        int leftSpace = (bTree.getBTreeSpec().getPageSize() - BTreeSpec.FREE_PAGE_LIST_OFF - 4) / BTreeSpec.PAGENUMBER_BYTE_SIZE;
        int size = (freePageNums <= leftSpace) ? freePageNums : (leftSpace - 1);

        for (int i = 0; i < size; i++) {
            PageNumber freePage = new PageNumber(ByteTool.bytesToInt(metaData, BTreeSpec.FREE_PAGE_LIST_OFF + 4 + i * BTreeSpec.PAGENUMBER_BYTE_SIZE, bTree.getBTreeSpec().getMsbFirst()));
            freePage.setTreeId(bTree.getBtreeId());
            bTree.getBuffer().addToFreeListWhenOpenBTree(bTree.getBtreeId(), freePage);
        }

        if (freePageNums > leftSpace) {
            //(page size - free page number - next page number)/ 4
            int pageTotalSpace = (bTree.getBTreeSpec().getPageSize() - 4 - BTreeSpec.PAGENUMBER_BYTE_SIZE) / BTreeSpec.PAGENUMBER_BYTE_SIZE;

            int nextFPReservedPageNum = ByteTool.bytesToInt(metaData, BTreeSpec.FREE_PAGE_LIST_OFF + 4 + size * BTreeSpec.PAGENUMBER_BYTE_SIZE, bTree.getBTreeSpec().getMsbFirst());
            PageNumber fpReservedPage = new PageNumber(nextFPReservedPageNum);
            fpReservedPage.setTreeId(bTree.getBtreeId());
            byte[] page = bTree.getBuffer().getPage(bTree.getBtreeId(), fpReservedPage);
            if (page != null) {
                int freePageNumOffset = BTreeSpec.OFF_PAGENUMBER + BTreeSpec.PAGENUMBER_BYTE_SIZE;
                int freeNum = ByteTool.bytesToInt(page, freePageNumOffset, bTree.getBTreeSpec().getMsbFirst());
                while (nextFPReservedPageNum > 0) {
                    for (int i = 0; i < freeNum; i++) {
                        PageNumber freePage = new PageNumber(ByteTool.bytesToInt(page, freePageNumOffset + 4 + i * BTreeSpec.PAGENUMBER_BYTE_SIZE, bTree.getBTreeSpec().getMsbFirst()));
                        freePage.setTreeId(bTree.getBtreeId());
                        bTree.getBuffer().addToFreeListWhenOpenBTree(bTree.getBtreeId(), freePage);
                    }
                    if ((freeNum < pageTotalSpace - 1) || (freeNum == pageTotalSpace)) {
                        nextFPReservedPageNum = -1;
                    } else {
                        nextFPReservedPageNum = ByteTool.bytesToInt(page, bTree.getBTreeSpec().getPageSize() - BTreeSpec.PAGENUMBER_BYTE_SIZE, bTree.getBTreeSpec().getMsbFirst());
                    }
                    /* add the page which save freelist to freelist,becuase it's unuseful later */
                    fpReservedPage.setTreeId(bTree.getBtreeId());
                    reservedList.add(fpReservedPage);
                    bTree.getBuffer().releasePage(bTree.getBtreeId(), fpReservedPage, false);
                    if (nextFPReservedPageNum > 0) {
                        fpReservedPage = new PageNumber(nextFPReservedPageNum);
                        fpReservedPage.setTreeId(bTree.getBtreeId());
                        page = bTree.getBuffer().getPage(bTree.getBtreeId(), fpReservedPage);
                        if (page == null) {
                            break;
                        }
                        freeNum = ByteTool.bytesToInt(page, freePageNumOffset, bTree.getBTreeSpec().getMsbFirst());
                    }
                }
            }
        }

        bTree.getBuffer().setReserveredList(bTree.getBtreeId(), reservedList);
    }

    public byte[] encodeToByteArray(Object data) throws ChaiDBException {
        if (data == null) {
            return null;
        }
        try {
            if (getConverter() == null || (data instanceof byte[])) {
                return (byte[]) data;
            }
            return getConverter().encodeToByteArray(data);
        } catch (EncodingException ee) {
            logger.error(ee);
            // details -ranjeet
            String details = "Converter failed in encoding " + ee.toString() + ".";
            throw new ChaiDBException(ErrorCode.CONVERTER_ENCODING_ERROR, details);
        } catch (ClassCastException cce) {
            logger.error(cce);
            // details -ranjeet
            String details = "Value is not suitable type: " + cce.toString() + ".";
            throw new ChaiDBException(ErrorCode.CONVERTER_ENCODING_ERROR, details);
        }
    }

    public void doFinalJob() {
        if (PageBufferManager.PROTECT_PAGE) {
            getBuffer().releaseHoldedPage(getBtreeId());
        }
    }

    public void updateRootOnMetaPage(PageNumber root, KernelContext kContext) throws ChaiDBException {
        if (kContext != null && kContext.getNeedLog()) {
            BTreeSpecLogRecord logRec = new BTreeSpecLogRecord(id, getBTreeSpec().getRootPageNumber().getPageNumber(), root.getPageNumber(), BTreeSpecLogRecord.ROOT_PAGE_NUMBER_FLAG, kContext.getLocker(), getType());
            logRec.log();
        }
        getBTreeSpec().setRootPageNumber(root);
    }

    public Object decodeFromByteArray(byte[] bytes, Key key) throws ChaiDBException {
        if (bytes == null) return null;

        if (getConverter() == null) {
            return bytes;
        } else {
            try {
                /* Modified by ben zhang at aug,12,2002 pending issue*/
                return getConverter().decodeFromByteArray(key, bytes);
            } catch (EncodingException ee) {
                logger.error(ee);
                // details -ranjeet
                String details = "Converter failed to decode : " + ee.toString() + ".";
                throw new ChaiDBException(ErrorCode.CONVERTER_DECODING_ERROR, details);
            }
        }
    }

    /**
     * Find the left most leaf of a BTree. Here this btree is a logicall one instead of
     * a physical one (a disk file), represented by root.
     * Before go out, remember the last lock on leaf page in KernelContext.locklist.
     *
     * @param root
     * @return
     * @throws ChaiDBException
     */
    public BTreePage findLeftMostLeaf(PageNumber root) throws ChaiDBException {
        BTreePage page = new BTreePage(getBtreeId(), root, getBTreeSpec(), getBuffer());
        while (true) {
            if (page.getPage() == null || page.isLeaf()) {
                return page;
            }
            getBuffer().releasePage(getBtreeId(), page.pageNumber, false);
            page = new BTreePage(getBtreeId(), page.nextPage, getBTreeSpec(), getBuffer());
        }
    }

    private BTreePage findRightMostLeaf(PageNumber root) throws ChaiDBException {
        BTreePage page = new BTreePage(getBtreeId(), root, getBTreeSpec(), getBuffer());
        while (true) {
            if (page.getPage() == null || page.isLeaf()) {
                return page;
            }
            PageNumber pg = (page.getNode(page.getCurrNodeNumbers() - 1)).getPageNumber();
            getBuffer().releasePage(getBtreeId(), page.pageNumber, false);
            page = new BTreePage(getBtreeId(), pg, getBTreeSpec(), getBuffer());
        }
    }

    public Key getMinKey() throws ChaiDBException {
        PageNumber root = getBTreeSpec().getRootPageNumber();
        BTreePage page = findLeftMostLeaf(root);
        Key key = (page.getNode(0)).getKey();
        return key;
    }

    /**
     * @return null if BTree is empty
     * @throws ChaiDBException
     */
    public Key getMaxKey() throws ChaiDBException {
        PageNumber root = getBTreeSpec().getRootPageNumber();
        BTreePage page = findRightMostLeaf(root);
        Key key = (page.getNode(page.getCurrNodeNumbers() - 1).getKey());
        return key;
    }

    public PageBufferManager getBuffer() {
        return buffer;
    }

    public boolean isReady() {
        return isReady;
    }

    public void setReady(boolean ready) {
        this.isReady = ready;
    }

    public String getBTreeName() {
        return BTreeName;
    }

    public void setBTreeName(String BTreeName) {
        this.BTreeName = BTreeName;
    }

    public int getBtreeId() {
        return id;
    }

    public void setBtreeId(int treeid) {
        id = treeid;
        //update treeid of rootpage and first available page in btreeSpec
        btreeSpec.setTreeId(id);
    }

    public BTreeSpec getBTreeSpec() {
        return btreeSpec;
    }

    public void setBTreeSpec(BTreeSpec s) {
        btreeSpec = s;
        btreeSpec.setBtree(this);
        btreeSpec.setTreeId(this.getBtreeId());
    }

    /**
     * closes the index and backs up the data
     *
     * @throws org.chaidb.db.exception.ChaiDBException
     *          Exception while closing the index
     */
    public void close() throws ChaiDBException {
        buffer.close(id);
    }

    /**
     * performs a backup of the data in the index
     *
     * @throws org.chaidb.db.exception.ChaiDBException
     *          Exception while backing up the index
     */
    public void backup() throws ChaiDBException {
        flush();
    }

    /**
     * flushes some information in memory to persistent storage
     *
     * @throws org.chaidb.db.exception.ChaiDBException
     *          Exception while writing to persistent storage
     */
    public void flush() throws ChaiDBException {
        buffer.dump(id);
    }

    /**
     * Opens the index
     *
     * @param dataSource The source of data for the index. The type of the
     *                   *    argument depends on the index type. In most cases this is either
     *                   *    a java.io.File object or a String representing the name of the
     *                   *    file containing the index data.
     * @throws org.chaidb.db.exception.ChaiDBException
     *          Exception while opening/loading index
     */
    public void open(String dataSource, KernelContext kContext) throws ChaiDBException {
        open(new File(dataSource), kContext);
    }

    /**
     * Opens the index
     *
     * @param dataSource The source of data for the index. The type of the
     *                   *    argument depends on the index type. In most cases this is either
     *                   *    a java.io.File object or a String representing the name of the
     *                   *    file containing the index data.
     * @throws org.chaidb.db.exception.ChaiDBException
     *          Exception while opening/loading index
     */
    public void open(File dataSource, KernelContext kContext) throws ChaiDBException {
        if (dataSource == null) {
            throw new ChaiDBException(ErrorCode.BTREE_INVALID, "Data source is empty when openning btree");
        }
        isReady = false;
        getBuffer().open(this, dataSource);
        prepareBTree(dataSource, kContext, this);

    }

    public void openForRecovery(String dataSource, int treeId) throws ChaiDBException {
        if (dataSource == null) {
            throw new ChaiDBException(ErrorCode.BTREE_INVALID, "Data source is empty when openning btree");
        }
        isReady = false;
        File dsFile = new File(dataSource);
        getBuffer().open(this, dsFile, treeId);
        prepareBTree(dsFile, null, this);

    }


    protected void prepareBTree(File dataSource, KernelContext kContext, IBTree tree) throws ChaiDBException {

        //assign btreename. This must be before getPage(), because it may be used in that method.
        setBTreeName(dataSource.getAbsolutePath());

        byte[] metaPage = getBuffer().getPage(getBtreeId(), new PageNumber(getBtreeId(), 0, 0));
        if (metaPage != null) {
            readMetaData(this, metaPage);

            // unfix metaPage
            getBuffer().releasePage(getBtreeId(), new PageNumber(getBtreeId(), 0, 0), false);
        } else {
            this.btreeSpec.changeNodeSize(btreeSpec.getNodeSize(), kContext);
            getBuffer().writeMetaPage(getBtreeId(), true);
        }

        //Here set BTree ready flag be true. From now, fuzzy checkpoint daemon
        //can write this btree's meta data to disk.
        isReady = true;
    }

    /**
     * Clone the object to another object.
     *
     * @param dir      the new directory of clonee. Must be different from the old one's
     * @param kContext
     * @throws org.chaidb.db.exception.ChaiDBException
     *
     */
    public IDBIndex clone(String dir, KernelContext kContext) throws ChaiDBException {
        File f = new File(dir);
        if (!f.isDirectory()) {
            throw new ChaiDBException(ErrorCode.BTREE_INVALID_CLONE_PATH, dir + " is not a path.");
        }
        AbstractBTree newBTree = GenerateClonedBTree();
        newBTree.btreeSpec.setNodeSize(btreeSpec.getNodeSize());
        newBTree.open(dir + File.separator + new File(getBTreeName()).getName(), kContext);
        newBTree.setConverter(getConverter());
        copyData(newBTree, kContext);
        return newBTree;
    }

    protected abstract AbstractBTree GenerateClonedBTree() throws ChaiDBException;

    protected abstract void copyData(AbstractBTree newBTree, KernelContext kContext) throws ChaiDBException;

    /**
     * returns a clean instance of that database, after reorganization.
     *
     * @return A clean instance of that database, after reorganization
     */
    public IDBIndex reorganize(boolean trace) throws ChaiDBException {
        return this;
    }

    /**
     * Mapping between file name and btree type
     * btree --> b_
     * id2node btree --> i_
     * super btree --> s_
     * hyper btree --> h_
     * path btree --> p_
     *
     * @param btreeName
     * @return
     */

    private static short getBTreeTypeFromBTreeName(String btreeName) {
        short btreeType = 0;
        int beginIndex = btreeName.lastIndexOf(File.separator);
        String fileName = btreeName.substring(beginIndex + File.separator.length());
        String sBtreeType = fileName.substring(0, fileName.indexOf(IDBIndex.PREFIX_SYMBOL));
        try {
            btreeType = (short) Integer.parseInt(sBtreeType);
        } catch (Exception e) {
            btreeType = IDBIndex.BTREE;
        }
        return btreeType;
    }

    public static void clone(String oldBTreeFileName, String dir, KernelContext kContext) throws ChaiDBException {
        if (oldBTreeFileName == null || oldBTreeFileName.length() == 0) {
            throw new ChaiDBException(ErrorCode.RUNTIME_ERROR_BASE, "oldBTreeFileName is null");
        }
        short btreeType = getBTreeTypeFromBTreeName(oldBTreeFileName);

        IDBIndex oldBTree = BTreeFactory.createBTree(btreeType);
        oldBTree.open(oldBTreeFileName, kContext);
        IDBIndex clonedBTree = oldBTree.clone(dir, kContext);
        oldBTree.close();
        clonedBTree.close();
    }

    protected PageNumber store(Key key, byte[] value, short mode, PageNumber root, KernelContext kContext) throws ChaiDBException {

        PageNumber rootPgNumber;
        this.getBTreeSpec().setModified(true);

        //lock root page
        BTreePage rootPage = new BTreePage(id, root, btreeSpec, buffer);

        if (rootPage.getPage() == null) {
            rootPage = BTreePage.newPage(btreeSpec, buffer, kContext);
            rootPage.setLeaf();
            rootPage.setPrevPage(new PageNumber(id, 0, 0)); // root
            // insert
            rootPage.insert(key, value, mode, kContext);

            buffer.releasePage(id, rootPage.pageNumber, true);
            rootPgNumber = rootPage.pageNumber;

        } else {
            //unlock meta page. Here because we need not write metapage, release it really!
            BTreePage leafPage = rootPage.getLeaf(key, kContext, BTreePage.INSERT); // search candidate leaf
            // unfix rootPage
            buffer.releasePage(id, rootPage.pageNumber, false);

            rootPgNumber = leafPage.insert(key, value, mode, kContext);

            // unfix leafPage
            buffer.releasePage(id, leafPage.pageNumber, false);
        }

        //if STORE_FLUSH flag is set, directly flush data to disk
        if ((mode & STORE_FLUSH) != 0) {
            flush();
        }

        return rootPgNumber;
    }

    /**
     * stores an entry in the index in the given mode
     *
     * @param key      The key
     * @param data     The data associated with the key
     * @param mode     The mode in which to store, allowed values are
     *                 *    IDBIndex.STORE_INSERT and IDBIndex.STORE_REPLACE
     * @param kContext
     * @throws org.chaidb.db.exception.ChaiDBException
     *          Exception while adding data to the
     */
    public void store(Key key, Object data, short mode, KernelContext kContext) throws ChaiDBException {
        if (DbEnvironment.READ_ONLY) throw new ChaiDBException(ErrorCode.DATABASE_IS_READONLY);

        kContext.checkLock(getBTreeName());
        byte[] value = encodeToByteArray(data);
        if (value == null) return;

        this.getBTreeSpec().setModified(true);
        try {
            PageNumber root = getTopRoot();
            root = store(key, value, mode, root, kContext);
            if (root == null) {
                return;
            }
            // save new root page number
            updateRootOnMetaPage(root, kContext);
        } finally {
            doFinalJob();
        }
    }

    /**
     * Get the top layer root page number.
     *
     * @return top layer root page number if there is one. Otherwise, INVAILD_PAGENUMBER.
     */
    protected PageNumber getTopRoot() throws ChaiDBException {
        /* First lock metapage so that we can read correct rootPageNumber.*/
        return btreeSpec.getRootPageNumber();
    }

    protected OperResult delete(Key key, PageNumber root, KernelContext kContext) throws ChaiDBException {

        BTreePage rootPage = new BTreePage(id, root, btreeSpec, getBuffer());
        if (rootPage.getPage() == null) {
            return new OperResult(false, null);
        }
        BTreePage leafPage = rootPage.getLeaf(key, kContext, BTreePage.DELETE); // search candidate leaf
        // unfix root page
        getBuffer().releasePage(id, rootPage.pageNumber, false);

        OperResult result = leafPage.delete(key, kContext);
        getBuffer().releasePage(id, leafPage.pageNumber, true);

        return result;
    }

    /**
     * deletes the entry associated with the given key
     *
     * @return A boolean value, <b>true</b> if the deletion was successful
     * @throws org.chaidb.db.exception.ChaiDBException
     *          exception while deleting the entry associated
     *          *    with the given key
     */
    public boolean delete(Key key, KernelContext kContext) throws ChaiDBException {
        if (DbEnvironment.READ_ONLY) throw new ChaiDBException(ErrorCode.DATABASE_IS_READONLY);

        kContext.checkLock(getBTreeName());
        this.getBTreeSpec().setModified(true);
        try {
            PageNumber root = getTopRoot();
            OperResult result = delete(key, root, kContext);
            if (result.newRoot != null) {
                //save new root
                updateRootOnMetaPage(result.newRoot, kContext);
            }
            return result.success;
        } catch (ChaiDBException e) {
            throw new ChaiDBException(ErrorCode.HYPERBTREE_HAS_DELETED);

        } finally {
            doFinalJob();
        }
    }

    protected static boolean checkKeyPair(Key minKey, Key maxKey) throws ChaiDBException {
        if (minKey == null && maxKey == null) {
            throw new ChaiDBException(ErrorCode.BTREE_KEY_IS_NOT_ENOUGH, "Both keys can't be null at the same time when do range lookup.");
        }

        if (minKey != null && maxKey != null && minKey.compareTo(maxKey) > 0) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * returns the index type of this index
     *
     * @return A short integer representing the index type
     */
    public abstract short getType();
}
TOP

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

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.