/* * Copyright 2004-2014 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. */ public abstract class PageBtree extends Page { /** * 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 PageBtreeIndex index; /** * The page number of the parent. */ protected int parentPageId; /** * The data page. */ protected final Data data; /** * The row offsets. */ protected int[] offsets; /** * The number of entries. */ protected int entryCount; //对于PageBtreeNode就是分隔点(rows)的个数(也等于子节点个数-1),对于PageBtreeLeaf就代表行数 /** * The index data */ protected SearchRow[] rows; /** * The start of the data area. */ protected int start; /** * If only the position of the row is stored in the page */ protected boolean onlyPosition; /** * Whether the data page is up-to-date. */ protected boolean written; /** * 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(); //最开始时为0 } /** * 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. * * @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 */ //折半查找 //返回的下标会用于org.h2.store.Page的insert方法中,使得insert到rows时就按照建立索引时指定的排序方式排好了, //比如CREATE index IF NOT EXISTS idx_name ON IndexTestTable(name desc),因为为name指定了desc,所以rows数组是降序的 //此方法的返回值是: 0<=x<=entryCount int find(SearchRow compare, boolean bigger, boolean add, boolean compareKeys) { //只有addRow和remove时compareKeys为true,find时compareKeys为false if (compare == null) { return 0; } int l = 0, r = entryCount; int comp = 1; while (l < r) { int i = (l + r) >>> 1; //除以2,从中间元素开始 SearchRow row = getRow(i); //如果索引字段是降序的,比如CREATE index IF NOT EXISTS idx_name ON IndexTestTable(name desc) //那么此时会按降序比较,如果row<compare,那么得到的结果是row>compare,此时rows数组中的元素是降序排列的。 comp = index.compareRows(row, compare); if (comp == 0) { //增加新记录时,如果是唯一索引,当两条记录相等时, //再根据不同数据库的兼容模式来判断记录中的null字段情况 //1. uniqueIndexSingleNull 返回false, 抛出DuplicateKeyException //2. uniqueIndexSingleNullExceptAllColumnsAreNull // 如果要比较的记录包含非null字段,返回false, 抛出DuplicateKeyException,否则允许通过 //3. 其他情况: 如果要比较的记录含null字段,返回true,否则返回false, 抛出DuplicateKeyException if (add && index.indexType.isUnique()) { if (!index.containsNullAndAllowMultipleNull(compare)) { throw index.getDuplicateKeyException(compare.toString()); } } if (compareKeys) { comp = index.compareKeys(row, compare); if (comp == 0) { return i; } } } //第i个元素大于要比较的元素时,往左移动, //或者相等时,如果bigger为false,说明要找更小的,往左移动 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. * * @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. * * @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. * * @param cursor the cursor */ abstract void last(PageBtreeCursor cursor); /** * 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. */ protected void memoryChange() { // nothing to do } /** * 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. * * @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. * * @return the page */ abstract PageBtreeLeaf getFirstLeaf(); /** * Get the first child leaf page of a page. * * @return the page */ abstract PageBtreeLeaf getLastLeaf(); /** * 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. */ abstract void remapChildren(); /** * 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. */ abstract void freeRecursive(); /** * 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. * * @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() { if (changeCount >= index.getPageStore().getChangeCount()) { return false; } return true; } // 我加上的 public String tree() { return tree(""); } // 我加上的 public abstract String tree(String p); }