/* * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Version 1.0, and under the Eclipse Public License, Version 1.0 * (http://h2database.com/html/license.html). * Initial Developer: H2 Group */ package org.h2.index; import org.h2.engine.Session; import org.h2.result.Row; import org.h2.store.Data; import org.h2.store.Page; /** * A page that contains data rows. */ abstract class PageData extends Page { /** * The position of the parent page id. */ static final int START_PARENT = 3; /** * This is a root page. */ static final int ROOT = 0; /** * Indicator that the row count is not known. */ static final int UNKNOWN_ROWCOUNT = -1; /** * The index. */ protected final PageDataIndex index; /** * The page number of the parent. */ protected int parentPageId; /** * The data page. */ protected final Data data; /** * The number of entries. */ protected int entryCount; /** * The row keys. */ protected long[] keys; /** * Whether the data page is up-to-date. */ protected boolean written; /** * The estimated heap memory used by this object, in number of double words * (4 bytes each). */ protected int memoryEstimated; PageData(PageDataIndex index, int pageId, Data data) { this.index = index; this.data = data; setPos(pageId); memoryEstimated = index.getMemoryPerPage(); } /** * Get the real row count. If required, this will read all child pages. * * @return the row count */ abstract int getRowCount(); /** * Set the stored row count. This will write the page. * * @param rowCount the stored row count */ abstract void setRowCountStored(int rowCount); /** * Find an entry by key. * * @param key the key (may not exist) * @return the matching or next index */ int find(long key) { int l = 0, r = entryCount; while (l < r) { int i = (l + r) >>> 1; long k = keys[i]; if (k == key) { return i; } else if (k > key) { r = i; } else { l = i + 1; } } return l; } /** * Add a row if possible. If it is possible this method returns -1, otherwise * the split point. It is always possible to add one row. * * @param row the now to add * @return the split point of this page, or -1 if no split is required */ abstract int addRowTry(Row row); /** * Get a cursor. * * @param session the session * @param minKey the smallest key * @param maxKey the largest key * @param multiVersion if the delta should be used * @return the cursor */ abstract Cursor find(Session session, long minKey, long maxKey, boolean multiVersion); /** * Get the key at this position. * * @param at the index * @return the key */ long getKey(int at) { return keys[at]; } /** * Split the index page at the given point. * * @param splitPoint the index where to split * @return the new page that contains about half the entries */ abstract PageData split(int splitPoint); /** * Change the page id. * * @param id the new page id */ void setPageId(int id) { int old = getPos(); index.getPageStore().removeRecord(getPos()); setPos(id); index.getPageStore().logUndo(this, null); remapChildren(old); } /** * Get the last key of a page. * * @return the last key */ abstract long getLastKey(); /** * Get the first child leaf page of a page. * * @return the page */ abstract PageDataLeaf getFirstLeaf(); /** * Change the parent page id. * * @param id the new parent page id */ void setParentPageId(int id) { index.getPageStore().logUndo(this, data); parentPageId = id; if (written) { changeCount = index.getPageStore().getChangeCount(); data.setInt(START_PARENT, parentPageId); } } /** * Update the parent id of all children. * * @param old the previous position */ abstract void remapChildren(int old); /** * Remove a row. * * @param key the key of the row to remove * @return true if this page is now empty */ abstract boolean remove(long key); /** * Free this page and all child pages. */ abstract void freeRecursive(); /** * Get the row for the given key. * * @param key the key * @return the row */ abstract Row getRowWithKey(long key); /** * Get the estimated heap memory size. * * @return number of double words (4 bytes each) */ public int getMemory() { // need to always return the same value for the same object (otherwise // the cache size would change after adding and then removing the same // page from the cache) but index.getMemoryPerPage() can adopt according // to how much memory a row needs on average return memoryEstimated; } int getParentPageId() { return parentPageId; } public boolean canRemove() { if (changeCount >= index.getPageStore().getChangeCount()) { return false; } return true; } }