/*
 * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.index;

import org.h2.result.SearchRow;
import org.h2.store.Data;
import org.h2.store.Page;

A page that contains index data.
/** * A page that contains index data. */
public abstract class PageBtree extends Page {
This is a root page.
/** * This is a root page. */
static final int ROOT = 0;
Indicator that the row count is not known.
/** * Indicator that the row count is not known. */
static final int UNKNOWN_ROWCOUNT = -1;
The index.
/** * The index. */
protected final PageBtreeIndex index;
The page number of the parent.
/** * The page number of the parent. */
protected int parentPageId;
The data page.
/** * The data page. */
protected final Data data;
The row offsets.
/** * The row offsets. */
protected int[] offsets;
The number of entries.
/** * The number of entries. */
protected int entryCount;
The index data
/** * The index data */
protected SearchRow[] rows;
The start of the data area.
/** * The start of the data area. */
protected int start;
If only the position of the row is stored in the page
/** * If only the position of the row is stored in the page */
protected boolean onlyPosition;
Whether the data page is up-to-date.
/** * Whether the data page is up-to-date. */
protected boolean written;
The estimated memory used by this object.
/** * The estimated memory used by this object. */
private final int memoryEstimated; PageBtree(PageBtreeIndex 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.
Returns:the row count
/** * 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.
Params:
  • rowCount – the stored row count
/** * Set the stored row count. This will write the page. * * @param rowCount the stored row count */
abstract void setRowCountStored(int rowCount);
Find an entry.
Params:
  • compare – the row
  • bigger – if looking for a larger row
  • add – if the row should be added (check for duplicate keys)
  • compareKeys – compare the row keys as well
Returns:the index of the found row
/** * Find an entry. * * @param compare the row * @param bigger if looking for a larger row * @param add if the row should be added (check for duplicate keys) * @param compareKeys compare the row keys as well * @return the index of the found row */
int find(SearchRow compare, boolean bigger, boolean add, boolean compareKeys) { if (compare == null) { return 0; } int l = 0, r = entryCount; int comp = 1; while (l < r) { int i = (l + r) >>> 1; SearchRow row = getRow(i); comp = index.compareRows(row, compare); if (comp == 0) { if (add && index.indexType.isUnique()) { if (!index.mayHaveNullDuplicates(compare)) { throw index.getDuplicateKeyException(compare.toString()); } } if (compareKeys) { comp = index.compareKeys(row, compare); if (comp == 0) { return i; } } } if (comp > 0 || (!bigger && comp == 0)) { 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.
Params:
  • row – the row to add
Returns:the split point of this page, or -1 if no split is required
/** * 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 row to add * @return the split point of this page, or -1 if no split is required */
abstract int addRowTry(SearchRow row);
Find the first row.
Params:
  • cursor – the cursor
  • first – the row to find
  • bigger – if the row should be bigger
/** * Find the first row. * * @param cursor the cursor * @param first the row to find * @param bigger if the row should be bigger */
abstract void find(PageBtreeCursor cursor, SearchRow first, boolean bigger);
Find the last row.
Params:
  • cursor – the cursor
/** * Find the last row. * * @param cursor the cursor */
abstract void last(PageBtreeCursor cursor);
Get the row at this position.
Params:
  • at – the index
Returns:the row
/** * Get the row at this position. * * @param at the index * @return the row */
SearchRow getRow(int at) { SearchRow row = rows[at]; if (row == null) { row = index.readRow(data, offsets[at], onlyPosition, true); memoryChange(); rows[at] = row; } else if (!index.hasData(row)) { row = index.readRow(row.getKey()); memoryChange(); rows[at] = row; } return row; }
The memory usage of this page was changed. Propagate the change if needed.
/** * The memory usage of this page was changed. Propagate the change if * needed. */
protected void memoryChange() { // nothing to do }
Split the index page at the given point.
Params:
  • splitPoint – the index where to split
Returns:the new page that contains about half the entries
/** * 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 PageBtree split(int splitPoint);
Change the page id.
Params:
  • id – the new page id
/** * Change the page id. * * @param id the new page id */
void setPageId(int id) { changeCount = index.getPageStore().getChangeCount(); written = false; index.getPageStore().removeFromCache(getPos()); setPos(id); index.getPageStore().logUndo(this, null); remapChildren(); }
Get the first child leaf page of a page.
Returns:the page
/** * Get the first child leaf page of a page. * * @return the page */
abstract PageBtreeLeaf getFirstLeaf();
Get the first child leaf page of a page.
Returns:the page
/** * Get the first child leaf page of a page. * * @return the page */
abstract PageBtreeLeaf getLastLeaf();
Change the parent page id.
Params:
  • id – the new parent page id
/** * Change the parent page id. * * @param id the new parent page id */
void setParentPageId(int id) { index.getPageStore().logUndo(this, data); changeCount = index.getPageStore().getChangeCount(); written = false; parentPageId = id; }
Update the parent id of all children.
/** * Update the parent id of all children. */
abstract void remapChildren();
Remove a row.
Params:
  • row – the row to remove
Returns:null if the last row didn't change, the deleted row if the page is now empty, otherwise the new last row of this page
/** * Remove a row. * * @param row the row to remove * @return null if the last row didn't change, * the deleted row if the page is now empty, * otherwise the new last row of this page */
abstract SearchRow remove(SearchRow row);
Free this page and all child pages.
/** * Free this page and all child pages. */
abstract void freeRecursive();
Ensure all rows are read in memory.
/** * Ensure all rows are read in memory. */
protected void readAllRows() { for (int i = 0; i < entryCount; i++) { SearchRow row = rows[i]; if (row == null) { row = index.readRow(data, offsets[i], onlyPosition, false); rows[i] = row; } } }
Get the estimated memory size.
Returns:number of double words (4 bytes)
/** * Get the estimated memory size. * * @return number of double words (4 bytes) */
@Override 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; } @Override public boolean canRemove() { return changeCount < index.getPageStore().getChangeCount(); } }