import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Queue;
import java.util.RandomAccess;
+import java.util.Set;
import static com.healthmarketscience.jackcess.impl.IndexData.*;
+import com.healthmarketscience.jackcess.impl.IndexData.DataPage;
import org.apache.commons.lang3.builder.ToStringBuilder;
/**
/** max number of pages to cache (unless a write operation is in
progress) */
private static final int MAX_CACHE_SIZE = 25;
-
+
/** the index whose pages this cache is managing */
private final IndexData _indexData;
/** the root page for the index */
/** the currently modified index pages */
private final List<CacheDataPage> _modifiedPages =
new ArrayList<CacheDataPage>();
-
+
public IndexPageCache(IndexData indexData) {
_indexData = indexData;
}
public IndexData getIndexData() {
return _indexData;
}
-
+
public PageChannel getPageChannel() {
return getIndexData().getPageChannel();
}
-
+
/**
* Sets the root page for this index, must be called before normal usage.
*
// root page has no parent
_rootPage.initParentPage(INVALID_INDEX_PAGE_NUMBER, false);
}
-
+
/**
* Writes any outstanding changes for this index to the file.
*/
}
}
}
-
+
/**
* Prepares any non-empty modified pages for writing as the second pass
* during a {@link #write} call. Updates entry prefixes, promotes/demotes
if(dpMain.hasChildTail()) {
if(size == 1) {
demoteTail(cacheDataPage);
- }
+ }
} else {
if(size > 1) {
promoteTail(cacheDataPage);
}
}
}
-
+
// look for pages with more entries than can fit on a page
if(cacheDataPage.getTotalEntrySize() > maxPageEntrySize) {
}
}
}
-
+
} while(splitPages);
}
DataPageMain main = getDataPage(pageNumber);
return((main != null) ? new CacheDataPage(main) : null);
}
-
+
/**
* Returns a DataPageMain for the given page number, may be {@code null} if
* the given page number is invalid. Loads the given page if necessary.
getIndexData().writeDataPage(cacheDataPage);
// lastly, mark the page as no longer modified
- cacheDataPage._extra._modified = false;
+ cacheDataPage._extra._modified = false;
}
-
+
/**
* Deletes the given index page from the file (clears the page).
*/
// discard from our cache
_dataPages.remove(cacheDataPage._main._pageNumber);
-
+
// lastly, mark the page as no longer modified
- cacheDataPage._extra._modified = false;
+ cacheDataPage._extra._modified = false;
}
-
+
/**
* Reads the given index page from the file.
*/
// associate the extra info with the main data page
dataPage.setExtra(extra);
-
+
return cacheDataPage;
- }
+ }
/**
* Removes the entry with the given index from the given page.
{
updateEntry(cacheDataPage, entryIdx, newEntry, UpdateType.ADD);
}
-
+
/**
* Updates the entries on the given page according to the given updateType.
*
CacheDataPage parentDataPage = (!dpMain.isRoot() ?
new CacheDataPage(dpMain.getParentPage()) :
null);
-
+
Entry oldLastEntry = dpExtra._entryView.getLast();
Entry oldEntry = null;
int entrySizeDiff = 0;
}
boolean updateLast = (oldLastEntry != dpExtra._entryView.getLast());
-
+
// child tail entry updates do not modify the page
if(!updateLast || !dpMain.hasChildTail()) {
dpExtra._totalEntrySize += entrySizeDiff;
return oldEntry;
}
- // determine if we need to update our parent page
+ // determine if we need to update our parent page
if(!updateLast || dpMain.isRoot()) {
// no parent
return oldEntry;
"Empty page but size is not 0? " + dpExtra._totalEntrySize + ", " +
cacheDataPage));
}
-
+
if(dpMain.isRoot()) {
// clear out this page (we don't actually remove it)
dpExtra._entryPrefix = EMPTY_PREFIX;
Integer prevPageNumber = dpMain._prevPageNumber;
Integer nextPageNumber = dpMain._nextPageNumber;
-
+
DataPageMain prevMain = dpMain.getPrevPage();
if(prevMain != null) {
setModified(new CacheDataPage(prevMain));
updateParentEntry(parentDataPage, childDataPage, null,
childExtra._entryView.getLast(), UpdateType.ADD);
}
-
+
/**
* Replaces the entry for the given child page in the given parent page.
*
updateParentEntry(parentDataPage, childDataPage, oldEntry,
childExtra._entryView.getLast(), UpdateType.REPLACE);
}
-
+
/**
* Updates the entry for the given child page in the given parent page
* according to the given updateType.
boolean expectFound = true;
int idx = 0;
-
+
switch(upType) {
case ADD:
expectFound = false;
case REMOVE:
idx = parentExtra._entryView.find(oldEntry);
break;
-
+
default:
throw new RuntimeException(withErrorContext(
"unknown update type " + upType));
}
-
+
if(idx < 0) {
if(expectFound) {
throw new IllegalStateException(withErrorContext(
private void updateParentTail(CacheDataPage parentDataPage,
CacheDataPage childDataPage,
UpdateType upType)
- throws IOException
{
DataPageMain parentMain = parentDataPage._main;
parentMain._childTailPageNumber = newChildTailPageNumber;
}
}
-
+
/**
* Verifies that the given entry type (node/leaf) is valid for the given
* page (node/leaf).
DataPageExtra origExtra = origDataPage._extra;
setModified(origDataPage);
-
+
int numEntries = origExtra._entries.size();
if(numEntries < 2) {
throw new IllegalStateException(withErrorContext(
"Cannot split page with less than 2 entries " + origDataPage));
}
-
+
if(origMain.isRoot()) {
// we can't split the root page directly, so we need to put another page
// between the root page and its sub-pages, and then split that page.
// start mucking with our entries because our parent may use our entries.
DataPageMain parentMain = origMain.getParentPage();
CacheDataPage parentDataPage = new CacheDataPage(parentMain);
-
+
// note, there are many, many ways this could be improved/tweaked. for
// now, we just want it to be functional...
// so, we will naively move half the entries from one page to a new page.
parentMain._pageNumber, origMain._leaf);
DataPageMain newMain = newDataPage._main;
DataPageExtra newExtra = newDataPage._extra;
-
+
List<Entry> headEntries =
origExtra._entries.subList(0, ((numEntries + 1) / 2));
// insert this new page between the old page and any previous page
addToPeersBefore(newDataPage, origDataPage);
-
+
if(!newMain._leaf) {
// reparent the children pages of the new page
reparentChildren(newDataPage);
* split.
*
* @param rootDataPage the root data page
- *
+ *
* @return the newly created page nested under the root page
*/
private CacheDataPage nestRootDataPage(CacheDataPage rootDataPage)
throw new IllegalArgumentException(withErrorContext(
"should be called with root, duh"));
}
-
+
CacheDataPage newDataPage =
allocateNewCacheDataPage(rootMain._pageNumber, rootMain._leaf);
DataPageMain newMain = newDataPage._main;
// we need to re-parent all the child pages
reparentChildren(newDataPage);
}
-
+
// clear the root page
rootMain._leaf = false;
rootMain._childTailPageNumber = INVALID_INDEX_PAGE_NUMBER;
return newDataPage;
}
-
+
/**
* Allocates a new index page with the given parent page and type.
*
newMain._nextPageNumber = origMain._pageNumber;
newMain._prevPageNumber = origMain._prevPageNumber;
origMain._prevPageNumber = newMain._pageNumber;
-
+
if(prevMain != null) {
setModified(new CacheDataPage(prevMain));
prevMain._nextPageNumber = newMain._pageNumber;
nextMain._prevPageNumber = INVALID_INDEX_PAGE_NUMBER;
dpMain._nextPageNumber = INVALID_INDEX_PAGE_NUMBER;
}
-
+
/**
* Sets the parent info for the children of the given page to the given
* page.
* @param cacheDataPage the page whose children need to be updated
*/
private void reparentChildren(CacheDataPage cacheDataPage)
- throws IOException
{
DataPageMain dpMain = cacheDataPage._main;
DataPageExtra dpExtra = cacheDataPage._extra;
DataPageExtra dpExtra = cacheDataPage._extra;
setModified(cacheDataPage);
-
+
DataPageMain tailMain = dpMain.getChildTailPage();
CacheDataPage tailDataPage = new CacheDataPage(tailMain);
Entry tailEntry = dpExtra._entryView.demoteTail();
dpExtra._totalEntrySize += tailEntry.size();
dpExtra._entryPrefix = EMPTY_PREFIX;
-
+
tailMain.setParentPage(dpMain._pageNumber, false);
}
-
+
/**
* Makes the last normal entry of the given page the tail entry on that
* page, done when there are multiple entries on a page and no tail entry.
DataPageExtra dpExtra = cacheDataPage._extra;
setModified(cacheDataPage);
-
+
DataPageMain lastMain = dpMain.getChildPage(dpExtra._entryView.getLast());
CacheDataPage lastDataPage = new CacheDataPage(lastMain);
lastMain.setParentPage(dpMain._pageNumber, true);
}
-
+
/**
* Finds the index page on which the given entry does or should reside.
*
// nowhere to go from here
return new CacheDataPage(curPage);
}
-
+
DataPageExtra extra = curPage.getExtra();
// need to descend
{
byte[] b1 = e1.getEntryBytes();
byte[] b2 = e2.getEntryBytes();
-
+
int maxLen = b1.length;
byte[] prefix = b1;
if(b1.length > b2.length) {
maxLen = b2.length;
prefix = b2;
}
-
+
int len = 0;
while((len < maxLen) && (b1[len] == b2[len])) {
++len;
}
-
+
if(len < prefix.length) {
if(len == 0) {
return EMPTY_PREFIX;
}
-
+
// need new prefix
prefix = ByteUtil.copyOf(prefix, len);
}
/**
* Used by unit tests to validate the internal status of the index.
*/
- void validate() throws IOException {
- // copy the values as the validation methods might trigger map updates
- for(DataPageMain dpMain : new ArrayList<DataPageMain>(_dataPages.values())) {
- DataPageExtra dpExtra = dpMain.getExtra();
- validateEntries(dpExtra);
- validateChildren(dpMain, dpExtra);
- validatePeers(dpMain);
- }
- }
-
- /**
- * Validates the entries for an index page
- *
- * @param dpExtra the entries to validate
- */
- private void validateEntries(DataPageExtra dpExtra) throws IOException {
- int entrySize = 0;
- Entry prevEntry = FIRST_ENTRY;
- for(Entry e : dpExtra._entries) {
- entrySize += e.size();
- if(prevEntry.compareTo(e) >= 0) {
- throw new IOException(withErrorContext(
- "Unexpected order in index entries, " + prevEntry + " >= " + e));
- }
- prevEntry = e;
- }
- if(entrySize != dpExtra._totalEntrySize) {
- throw new IllegalStateException(withErrorContext(
- "Expected size " + entrySize +
- " but was " + dpExtra._totalEntrySize));
- }
- }
-
- /**
- * Validates the children for an index page
- *
- * @param dpMain the index page
- * @param dpExtra the child entries to validate
- */
- private void validateChildren(DataPageMain dpMain,
- DataPageExtra dpExtra) throws IOException {
- int childTailPageNumber = dpMain._childTailPageNumber;
- if(dpMain._leaf) {
- if(childTailPageNumber != INVALID_INDEX_PAGE_NUMBER) {
- throw new IllegalStateException(withErrorContext(
- "Leaf page has tail " + dpMain));
- }
- return;
- }
- if((dpExtra._entryView.size() == 1) && dpMain.hasChildTail()) {
- throw new IllegalStateException(withErrorContext(
- "Single child is tail " + dpMain));
- }
- for(Entry e : dpExtra._entryView) {
- validateEntryForPage(dpMain, e);
- Integer subPageNumber = e.getSubPageNumber();
- DataPageMain childMain = _dataPages.get(subPageNumber);
- if(childMain != null) {
- if(childMain._parentPageNumber != null) {
- if(childMain._parentPageNumber != dpMain._pageNumber) {
- throw new IllegalStateException(withErrorContext(
- "Child's parent is incorrect " + childMain));
- }
- boolean expectTail = (subPageNumber == childTailPageNumber);
- if(expectTail != childMain._tail) {
- throw new IllegalStateException(withErrorContext(
- "Child tail status incorrect " + childMain));
- }
- }
- Entry lastEntry = childMain.getExtra()._entryView.getLast();
- if(e.compareTo(lastEntry) != 0) {
- throw new IllegalStateException(withErrorContext(
- "Invalid entry " + e + " but child is " + lastEntry));
- }
- }
- }
- }
-
- /**
- * Validates the peer pages for an index page.
- *
- * @param dpMain the index page
- */
- private void validatePeers(DataPageMain dpMain) throws IOException {
- DataPageMain prevMain = _dataPages.get(dpMain._prevPageNumber);
- if(prevMain != null) {
- if(prevMain._nextPageNumber != dpMain._pageNumber) {
- throw new IllegalStateException(withErrorContext(
- "Prev page " + prevMain + " does not ref " + dpMain));
- }
- validatePeerStatus(dpMain, prevMain);
- }
-
- DataPageMain nextMain = _dataPages.get(dpMain._nextPageNumber);
- if(nextMain != null) {
- if(nextMain._prevPageNumber != dpMain._pageNumber) {
- throw new IllegalStateException(withErrorContext(
- "Next page " + nextMain + " does not ref " + dpMain));
- }
- validatePeerStatus(dpMain, nextMain);
- }
+ void validate(boolean forceLoad) throws IOException {
+ new Validator(forceLoad).validate();
}
- /**
- * Validates the given peer page against the given index page
- *
- * @param dpMain the index page
- * @param peerMain the peer index page
- */
- private void validatePeerStatus(DataPageMain dpMain, DataPageMain peerMain)
- throws IOException
- {
- if(dpMain._leaf != peerMain._leaf) {
- throw new IllegalStateException(withErrorContext(
- "Mismatched peer status " + dpMain._leaf + " " + peerMain._leaf));
- }
- if(!dpMain._leaf) {
- if((dpMain._parentPageNumber != null) &&
- (peerMain._parentPageNumber != null) &&
- ((int)dpMain._parentPageNumber != (int)peerMain._parentPageNumber)) {
- throw new IllegalStateException(withErrorContext(
- "Mismatched node parents " + dpMain._parentPageNumber + " " +
- peerMain._parentPageNumber));
- }
- }
- }
-
/**
* Collects all the cache pages in the cache.
*
iter.remove();
if(_dataPages.size() <= MAX_CACHE_SIZE) {
break;
- }
+ }
}
}
}
-
+
@Override
public String toString() {
ToStringBuilder sb = CustomToStringStyle.builder(this);
if(_rootPage == null) {
sb.append("pages", "(uninitialized)");
- } else {
+ } else {
sb.append("pages", collectPages(new ArrayList<Object>(), _rootPage));
}
return sb.toString();
private String withErrorContext(String msg) {
return _indexData.withErrorContext(msg);
}
-
+
/**
* Keeps track of the main info for an index page.
public IndexPageCache getCache() {
return IndexPageCache.this;
}
-
+
public boolean isRoot() {
return(this == _rootPage);
}
-
+
public boolean isTail() throws IOException
{
resolveParent();
public boolean isChildTailPageNumber(int pageNumber) {
return(_childTailPageNumber == pageNumber);
}
-
+
public DataPageMain getParentPage() throws IOException
{
resolveParent();
setParentPage(parentPageNumber, isTail);
}
}
-
+
public void setParentPage(Integer parentPageNumber, boolean isTail) {
_parentPageNumber = parentPageNumber;
_tail = isTail;
{
return IndexPageCache.this.getDataPage(_prevPageNumber);
}
-
+
public DataPageMain getNextPage() throws IOException
{
return IndexPageCache.this.getDataPage(_nextPageNumber);
}
-
+
public DataPageMain getChildPage(Entry e) throws IOException
{
Integer childPageNumber = e.getSubPageNumber();
return getChildPage(childPageNumber,
isChildTailPageNumber(childPageNumber));
}
-
+
public DataPageMain getChildTailPage() throws IOException
{
return getChildPage(_childTailPageNumber, true);
}
return child;
}
-
+
public DataPageExtra getExtra() throws IOException
{
DataPageExtra extra = _extra.get();
extra = readDataPage(_pageNumber)._extra;
setExtra(extra);
}
-
+
return extra;
}
-
+
public void setExtra(DataPageExtra extra) throws IOException
{
extra.setEntryView(this);
public void setEntryView(DataPageMain main) throws IOException {
_entryView = new EntryListView(main, this);
}
-
+
public void updateEntryPrefix() {
if(_entryPrefix.length == 0) {
// prefix is only related to *real* entries, tail not included
_entries.get(_entries.size() - 1));
}
}
-
+
@Override
public String toString() {
return CustomToStringStyle.builder("DPExtra")
private CacheDataPage(DataPageMain dataPage) throws IOException {
this(dataPage, dataPage.getExtra());
}
-
+
private CacheDataPage(DataPageMain dataPage, DataPageExtra extra) {
_main = dataPage;
_extra = extra;
public int getPageNumber() {
return _main._pageNumber;
}
-
+
@Override
public boolean isLeaf() {
return _main._leaf;
_main._childTailPageNumber = pageNumber;
}
-
+
@Override
public int getTotalEntrySize() {
return _extra._totalEntrySize;
public void addEntry(int idx, Entry entry) throws IOException {
_main.getCache().addEntry(this, idx, entry);
}
-
+
@Override
public Entry removeEntry(int idx) throws IOException {
return _main.getCache().removeEntry(this, idx);
}
-
+
}
/**
private List<Entry> getEntries() {
return _extra._entries;
}
-
+
@Override
public int size() {
int size = getEntries().size();
setChildTailEntry(newEntry) :
getEntries().set(idx, newEntry));
}
-
+
@Override
public void add(int idx, Entry newEntry) {
// note, we will never add to the "tail" entry, that will always be
// handled through promoteTail
getEntries().add(idx, newEntry);
}
-
+
@Override
public Entry remove(int idx) {
return (isCurrentChildTailIndex(idx) ?
setChildTailEntry(null) :
getEntries().remove(idx));
}
-
+
public Entry setChildTailEntry(Entry newEntry) {
Entry old = _childTailEntry;
_childTailEntry = newEntry;
return old;
}
-
+
private boolean hasChildTail() {
return(_childTailEntry != null);
}
-
+
private boolean isCurrentChildTailIndex(int idx) {
return(idx == getEntries().size());
}
getEntries().add(tail);
return tail;
}
-
+
public Entry promoteTail() {
Entry last = getEntries().remove(getEntries().size() - 1);
_childTailEntry = last;
return last;
}
-
+
public int find(Entry e) {
return Collections.binarySearch(this, e);
}
}
+ /**
+ * Utility class for running index validation.
+ */
+ private final class Validator {
+ private final boolean _forceLoad;
+ private final Map<Integer,DataPageMain> _knownPages = new HashMap<>();
+ private final Queue<DataPageMain> _pendingPages = new LinkedList<>();
+
+ private Validator(boolean forceLoad) {
+ _forceLoad = forceLoad;
+ _knownPages.putAll(_dataPages);
+ _pendingPages.addAll(_knownPages.values());
+ }
+
+ void validate() throws IOException {
+ DataPageMain dpMain = null;
+ while((dpMain = _pendingPages.poll()) != null) {
+ DataPageExtra dpExtra = dpMain.getExtra();
+ validateEntries(dpExtra);
+ validateChildren(dpMain, dpExtra);
+ validatePeers(dpMain);
+ }
+ }
+
+ /**
+ * Validates the entries for an index page
+ *
+ * @param dpExtra the entries to validate
+ */
+ private void validateEntries(DataPageExtra dpExtra) throws IOException {
+ int entrySize = 0;
+ Entry prevEntry = FIRST_ENTRY;
+ for(Entry e : dpExtra._entries) {
+ entrySize += e.size();
+ if(prevEntry.compareTo(e) >= 0) {
+ throw new IOException(withErrorContext(
+ "Unexpected order in index entries, " + prevEntry +
+ " >= " + e));
+ }
+ prevEntry = e;
+ }
+ if(entrySize != dpExtra._totalEntrySize) {
+ throw new IllegalStateException(withErrorContext(
+ "Expected size " + entrySize +
+ " but was " + dpExtra._totalEntrySize));
+ }
+ }
+
+ /**
+ * Validates the children for an index page
+ *
+ * @param dpMain the index page
+ * @param dpExtra the child entries to validate
+ */
+ private void validateChildren(DataPageMain dpMain,
+ DataPageExtra dpExtra) throws IOException {
+ int childTailPageNumber = dpMain._childTailPageNumber;
+ if(dpMain._leaf) {
+ if(childTailPageNumber != INVALID_INDEX_PAGE_NUMBER) {
+ throw new IllegalStateException(
+ withErrorContext("Leaf page has tail " + dpMain));
+ }
+ return;
+ }
+ if((dpExtra._entryView.size() == 1) && dpMain.hasChildTail()) {
+ throw new IllegalStateException(
+ withErrorContext("Single child is tail " + dpMain));
+ }
+ Integer prevPageNumber = null;
+ Integer nextPageNumber = null;
+ for(Entry e : dpExtra._entryView) {
+ validateEntryForPage(dpMain, e);
+ Integer subPageNumber = e.getSubPageNumber();
+ DataPageMain childMain = getPageForValidate(subPageNumber);
+ if(childMain != null) {
+ if((prevPageNumber != null) &&
+ ((int)childMain._prevPageNumber != prevPageNumber)) {
+ throw new IllegalStateException(withErrorContext(
+ "Child's prevPageNumber is not the previous child for " +
+ childMain + " " + dpExtra._entryView + " " +
+ prevPageNumber));
+ }
+ if((nextPageNumber != null) &&
+ (childMain._pageNumber != nextPageNumber)) {
+ throw new IllegalStateException(withErrorContext(
+ "Child's pageNumber is not the expected next child for " +
+ childMain));
+ }
+ if(childMain._parentPageNumber != null) {
+ if(childMain._parentPageNumber != dpMain._pageNumber) {
+ throw new IllegalStateException(
+ withErrorContext("Child's parent is incorrect " + childMain));
+ }
+ boolean expectTail = (subPageNumber == childTailPageNumber);
+ if(expectTail != childMain._tail) {
+ throw new IllegalStateException(withErrorContext(
+ "Child tail status incorrect " + childMain));
+ }
+ }
+ Entry lastEntry = childMain.getExtra()._entryView.getLast();
+ if(e.compareTo(lastEntry) != 0) {
+ throw new IllegalStateException(withErrorContext(
+ "Invalid entry " + e + " but child is " + lastEntry));
+ }
+ nextPageNumber = childMain._nextPageNumber;
+ prevPageNumber = childMain._pageNumber;
+ } else {
+ // if we aren't force loading, we may have gaps in the children so we
+ // can't validate these for the current child
+ nextPageNumber = null;
+ prevPageNumber = null;
+ }
+ }
+ }
+
+ /**
+ * Validates the peer pages for an index page.
+ *
+ * @param dpMain the index page
+ */
+ private void validatePeers(DataPageMain dpMain)
+ throws IOException {
+
+ DataPageMain prevMain = getPageForValidate(dpMain._prevPageNumber);
+ if(prevMain != null) {
+ if(prevMain._nextPageNumber != dpMain._pageNumber) {
+ throw new IllegalStateException(withErrorContext(
+ "Prev page " + prevMain + " does not ref " + dpMain));
+ }
+ validatePeerStatus(dpMain, prevMain);
+ }
+
+ DataPageMain nextMain =
+ getPageForValidate(dpMain._nextPageNumber);
+ if(nextMain != null) {
+ if(nextMain._prevPageNumber != dpMain._pageNumber) {
+ throw new IllegalStateException(withErrorContext(
+ "Next page " + nextMain + " does not ref " + dpMain));
+ }
+ validatePeerStatus(dpMain, nextMain);
+ }
+ }
+
+ /**
+ * Validates the given peer page against the given index page
+ *
+ * @param dpMain the index page
+ * @param peerMain the peer index page
+ */
+ private void validatePeerStatus(DataPageMain dpMain, DataPageMain peerMain)
+ {
+ if(dpMain._leaf != peerMain._leaf) {
+ throw new IllegalStateException(withErrorContext(
+ "Mismatched peer status " + dpMain._leaf + " " +
+ peerMain._leaf));
+ }
+ if(!dpMain._leaf) {
+ if((dpMain._parentPageNumber != null) &&
+ (peerMain._parentPageNumber != null) &&
+ ((int)dpMain._parentPageNumber != (int)peerMain._parentPageNumber)) {
+ throw new IllegalStateException(withErrorContext(
+ "Mismatched node parents " + dpMain._parentPageNumber + " " +
+ peerMain._parentPageNumber));
+ }
+ }
+ }
+
+ private DataPageMain getPageForValidate(
+ Integer pageNumber) throws IOException {
+ DataPageMain dpMain = _knownPages.get(pageNumber);
+ if((dpMain == null) && _forceLoad &&
+ (pageNumber != INVALID_INDEX_PAGE_NUMBER)) {
+ dpMain = getDataPage(pageNumber);
+ if(dpMain != null) {
+ _knownPages.put(pageNumber, dpMain);
+ _pendingPages.add(dpMain);
+ }
+ }
+ return dpMain;
+ }
+ }
+
}