package com.sleepycat.je.tree;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.cleaner.UtilizationTracker;
import com.sleepycat.je.config.EnvironmentParams;
import com.sleepycat.je.dbi.CursorImpl;
import com.sleepycat.je.dbi.DatabaseImpl;
import com.sleepycat.je.dbi.DbConfigManager;
import com.sleepycat.je.dbi.DbTree;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.dbi.INList;
import com.sleepycat.je.log.LogManager;
import com.sleepycat.je.log.LogReadable;
import com.sleepycat.je.log.LogUtils;
import com.sleepycat.je.log.LogWritable;
import com.sleepycat.je.recovery.RecoveryManager;
import com.sleepycat.je.txn.BasicLocker;
import com.sleepycat.je.txn.LockGrantType;
import com.sleepycat.je.txn.LockResult;
import com.sleepycat.je.txn.LockType;
import com.sleepycat.je.txn.Locker;
import com.sleepycat.je.txn.WriteLockInfo;
import com.sleepycat.je.utilint.DbLsn;
import com.sleepycat.je.utilint.TestHook;
import com.sleepycat.je.utilint.TestHookExecute;
import com.sleepycat.je.utilint.Tracer;
import de.ovgu.cide.jakutil.*;
/**
* Tree implements the JE B+Tree.
* A note on tree search patterns: There's a set of Tree.search* methods. Some
* clients of the tree use those search methods directly, whereas other clients
* of the tree tend to use methods built on top of search.
* The semantics of search* are they leave you pointing at a BIN or IN they
* don't tell you where the reference of interest is. they traverse a single
* tree, to jump into the duplicate tree, the caller has to take explicit
* action. The semantics of the get* methods are: they leave you pointing at a
* BIN or IN they return the index of the slot of interest they traverse down to
* whatever level is needed -- they'll take care of jumping into the duplicate
* tree. they are built on top of search* methods. For the future: Over time, we
* need to clarify which methods are to be used by clients of the tree.
* Preferably clients that call the tree use get*, although their are cases
* where they need visibility into the tree structure. For example, tee cursors
* use search* because they want to add themselves to BIN before jumping into
* the duplicate tree.
* Also, search* should return the location of the slot to save us a second
* binary search.
*/
public final class Tree implements LogWritable, LogReadable {
private static final String TRACE_ROOT_SPLIT="RootSplit:";
private static final String TRACE_DUP_ROOT_SPLIT="DupRootSplit:";
private static final String TRACE_MUTATE="Mut:";
private static final String TRACE_INSERT="Ins:";
private static final String TRACE_INSERT_DUPLICATE="InsD:";
private DatabaseImpl database;
private ChildReference root;
private int maxMainTreeEntriesPerNode;
private int maxDupTreeEntriesPerNode;
private boolean purgeRoot;
private TreeStats treeStats;
private ThreadLocal treeStatsAccumulatorTL=new ThreadLocal();
private static SplitRequiredException splitRequiredException=new SplitRequiredException();
/**
* Embodies an enum for the type of search being performed. NORMAL means do
* a regular search down the tree. LEFT/RIGHT means search down the
* left/right side to find the first/last node in the tree.
*/
public static class SearchType {
public static final SearchType NORMAL=new SearchType();
public static final SearchType LEFT=new SearchType();
public static final SearchType RIGHT=new SearchType();
private SearchType(){
}
}
private TestHook waitHook;
private TestHook searchHook;
private TestHook ckptHook;
/**
* Create a new tree.
*/
public Tree( DatabaseImpl database) throws DatabaseException {
init(database);
setDatabase(database);
}
/**
* Create a tree that's being read in from the log.
*/
public Tree() throws DatabaseException {
init(null);
maxMainTreeEntriesPerNode=0;
maxDupTreeEntriesPerNode=0;
}
/**
* constructor helper
*/
private void init( DatabaseImpl database){
treeStats=new TreeStats();
this.root=null;
this.database=database;
}
/**
* Set the database for this tree. Used by recovery when recreating an
* existing tree.
*/
public void setDatabase( DatabaseImpl database) throws DatabaseException {
this.database=database;
maxMainTreeEntriesPerNode=database.getNodeMaxEntries();
maxDupTreeEntriesPerNode=database.getNodeMaxDupTreeEntries();
DbConfigManager configManager=database.getDbEnvironment().getConfigManager();
purgeRoot=configManager.getBoolean(EnvironmentParams.COMPRESSOR_PURGE_ROOT);
}
/**
* @return the database for this Tree.
*/
public DatabaseImpl getDatabase(){
return database;
}
/**
* Set the root for the tree. Should only be called within the root latch.
*/
public void setRoot( ChildReference newRoot, boolean notLatched){
root=newRoot;
}
public ChildReference makeRootChildReference( Node target, byte[] key, long lsn){
return new RootChildReference(target,key,lsn);
}
private ChildReference makeRootChildReference(){
return new RootChildReference();
}
private class RootChildReference extends ChildReference {
private RootChildReference(){
super();
}
private RootChildReference( Node target, byte[] key, long lsn){
super(target,key,lsn);
}
private RootChildReference( Node target, byte[] key, long lsn, byte existingState){
super(target,key,lsn,existingState);
}
public Node fetchTarget( DatabaseImpl database, IN in) throws DatabaseException {
this.hook666();
return super.fetchTarget(database,in);
}
public void setTarget( Node target){
this.hook667();
super.setTarget(target);
}
public void clearTarget(){
this.hook668();
super.clearTarget();
}
public void setLsn( long lsn){
this.hook669();
super.setLsn(lsn);
}
protected void hook666() throws DatabaseException {
}
protected void hook667(){
}
protected void hook668(){
}
protected void hook669(){
}
}
/**
* Get LSN of the rootIN. Obtained without latching, should only be accessed
* while quiescent.
*/
public long getRootLsn(){
if (root == null) {
return DbLsn.NULL_LSN;
}
else {
return root.getLsn();
}
}
/**
* @return the TreeStats for this tree.
*/
TreeStats getTreeStats(){
return treeStats;
}
private TreeWalkerStatsAccumulator getTreeStatsAccumulator(){
if (EnvironmentImpl.getThreadLocalReferenceCount() > 0) {
return (TreeWalkerStatsAccumulator)treeStatsAccumulatorTL.get();
}
else {
return null;
}
}
public void setTreeStatsAccumulator( TreeWalkerStatsAccumulator tSA){
treeStatsAccumulatorTL.set(tSA);
}
public IN withRootLatchedExclusive( WithRootLatched wrl) throws DatabaseException {
try {
this.hook670(wrl);
throw ReturnHack.returnObject;
}
catch ( ReturnObject r) {
return (IN)r.value;
}
}
public IN withRootLatchedShared( WithRootLatched wrl) throws DatabaseException {
try {
this.hook671(wrl);
throw ReturnHack.returnObject;
}
catch ( ReturnObject r) {
return (IN)r.value;
}
}
/**
* Deletes a BIN specified by key from the tree. If the BIN resides in a
* subtree that can be pruned away, prune as much as possible, so we don't
* leave a branch that has no BINs.
* It's possible that the targeted BIN will now have entries, or will have
* resident cursors. Either will prevent deletion.
* @param idKey -
* the identifier key of the node to delete.
* @param trackeris used for tracking obsolete node info.
*/
public void delete( byte[] idKey, UtilizationTracker tracker) throws DatabaseException, NodeNotEmptyException, CursorsExistException {
try {
IN subtreeRootIN=null;
ArrayList nodeLadder=new ArrayList();
IN rootIN=null;
boolean rootNeedsUpdating=false;
this.hook672(idKey,tracker,subtreeRootIN,nodeLadder,rootIN,rootNeedsUpdating);
if (subtreeRootIN != null) {
EnvironmentImpl envImpl=database.getDbEnvironment();
if (rootNeedsUpdating) {
DbTree dbTree=envImpl.getDbMapTree();
dbTree.modifyDbRoot(database);
this.hook661();
}
INList inList=envImpl.getInMemoryINs();
accountForSubtreeRemoval(inList,subtreeRootIN,tracker);
}
}
catch ( ReturnVoid r) {
return;
}
}
/**
* This entire tree is empty, clear the root and log a new MapLN
* @return the rootIN that has been detached, or null if there hasn't been
* any removal.
*/
private IN logTreeRemoval( IN rootIN, UtilizationTracker tracker) throws DatabaseException {
IN detachedRootIN=null;
if ((rootIN.getNEntries() <= 1) && (rootIN.validateSubtreeBeforeDelete(0))) {
root=null;
EnvironmentImpl envImpl=database.getDbEnvironment();
LogManager logManager=envImpl.getLogManager();
logManager.log(new INDeleteInfo(rootIN.getNodeId(),rootIN.getIdentifierKey(),database.getId()));
detachedRootIN=rootIN;
}
return detachedRootIN;
}
/**
* Update nodes for a delete, going upwards. For example, suppose a node
* ladder holds: INa, INb, index for INb in INa INb, INc, index for INc in
* INb INc, BINd, index for BINd in INc
* When we enter this method, BINd has already been removed from INc. We
* need to - log INc - update INb, log INb - update INa, log INa
* @param nodeLadderList of SplitInfos describing each node pair on the downward
* path
* @param binRootparent of the dup tree, or null if this is not for dups.
* @param indexslot occupied by this din tree.
* @return whether the DB root needs updating.
*/
private boolean cascadeUpdates( ArrayList nodeLadder, BIN binRoot, int index) throws DatabaseException {
ListIterator iter=nodeLadder.listIterator(nodeLadder.size());
EnvironmentImpl envImpl=database.getDbEnvironment();
LogManager logManager=envImpl.getLogManager();
long newLsn=DbLsn.NULL_LSN;
SplitInfo info4=null;
while (iter.hasPrevious()) {
info4=(SplitInfo)iter.previous();
if (newLsn != DbLsn.NULL_LSN) {
info4.parent.updateEntry(info4.index,newLsn);
}
newLsn=info4.parent.log(logManager);
}
boolean rootNeedsUpdating=false;
if (info4 != null) {
if (info4.parent.isDbRoot()) {
this.hook673();
root.setLsn(newLsn);
rootNeedsUpdating=true;
}
else if ((binRoot != null) && info4.parent.isRoot()) {
binRoot.updateEntry(index,newLsn);
}
else {
assert false;
}
}
return rootNeedsUpdating;
}
/**
* Delete a subtree of a duplicate tree. Find the duplicate tree using
* mainKey in the top part of the tree and idKey in the duplicate tree.
* @param idKeythe identifier key to be used in the duplicate subtree to find
* the duplicate path.
* @param mainKeythe key to be used in the main tree to find the duplicate
* subtree.
* @param trackeris used for tracking obsolete node info.
* @return true if the delete succeeded, false if there were still cursors
* present on the leaf DBIN of the subtree that was located.
*/
public void deleteDup( byte[] idKey, byte[] mainKey, UtilizationTracker tracker) throws DatabaseException, NodeNotEmptyException, CursorsExistException {
IN in=search(mainKey,SearchType.NORMAL,-1,null,false);
IN deletedSubtreeRoot=null;
deletedSubtreeRoot=this.hook674(idKey,mainKey,in,deletedSubtreeRoot);
if (deletedSubtreeRoot != null) {
EnvironmentImpl envImpl=database.getDbEnvironment();
accountForSubtreeRemoval(envImpl.getInMemoryINs(),deletedSubtreeRoot,tracker);
}
}
/**
* We enter and leave this method with 'bin' latched.
* @return the root of the subtree we have deleted, so it can be properly
* accounted for. May be null if nothing was deleted.
*/
private IN deleteDupSubtree( byte[] idKey, BIN bin, int index) throws DatabaseException, NodeNotEmptyException, CursorsExistException {
EnvironmentImpl envImpl=database.getDbEnvironment();
boolean dupCountLNLocked=false;
DupCountLN dcl=null;
BasicLocker locker=new BasicLocker(envImpl);
DIN duplicateRoot=(DIN)bin.fetchTarget(index);
duplicateRoot.latch(false);
ArrayList nodeLadder=new ArrayList();
IN subtreeRootIN=null;
try {
ChildReference dclRef=duplicateRoot.getDupCountLNRef();
dcl=(DupCountLN)dclRef.fetchTarget(database,duplicateRoot);
LockResult lockResult=locker.nonBlockingLock(dcl.getNodeId(),LockType.READ,database);
if (lockResult.getLockGrant() == LockGrantType.DENIED) {
throw CursorsExistException.CURSORS_EXIST;
}
else {
dupCountLNLocked=true;
}
searchDeletableSubTree(duplicateRoot,idKey,nodeLadder);
LogManager logManager=envImpl.getLogManager();
if (nodeLadder.size() == 0) {
if (bin.nCursors() == 0) {
boolean deleteOk=bin.deleteEntry(index,true);
assert deleteOk;
logManager.log(new INDupDeleteInfo(duplicateRoot.getNodeId(),duplicateRoot.getMainTreeKey(),duplicateRoot.getDupTreeKey(),database.getId()));
subtreeRootIN=duplicateRoot;
this.hook754(bin);
}
else {
throw CursorsExistException.CURSORS_EXIST;
}
}
else {
SplitInfo detachPoint=(SplitInfo)nodeLadder.get(nodeLadder.size() - 1);
boolean deleteOk=detachPoint.parent.deleteEntry(detachPoint.index,true);
assert deleteOk;
cascadeUpdates(nodeLadder,bin,index);
subtreeRootIN=detachPoint.child;
}
}
finally {
this.hook676(nodeLadder);
if (dupCountLNLocked) {
locker.releaseLock(dcl.getNodeId());
}
this.hook675(duplicateRoot);
}
return subtreeRootIN;
}
/**
* Find the leftmost node (IN or BIN) in the tree. Do not descend into a
* duplicate tree if the leftmost entry of the first BIN refers to one.
* @return the leftmost node in the tree, null if the tree is empty. The
* returned node is latched and the caller must release it.
*/
public IN getFirstNode() throws DatabaseException {
return search(null,SearchType.LEFT,-1,null,true);
}
/**
* Find the rightmost node (IN or BIN) in the tree. Do not descend into a
* duplicate tree if the rightmost entry of the last BIN refers to one.
* @return the rightmost node in the tree, null if the tree is empty. The
* returned node is latched and the caller must release it.
*/
public IN getLastNode() throws DatabaseException {
return search(null,SearchType.RIGHT,-1,null,true);
}
/**
* Find the leftmost node (DBIN) in a duplicate tree.
* @return the leftmost node in the tree, null if the tree is empty. The
* returned node is latched and the caller must release it.
*/
public DBIN getFirstNode( DIN dupRoot) throws DatabaseException {
if (dupRoot == null) {
throw new IllegalArgumentException("getFirstNode passed null root");
}
this.hook677(dupRoot);
IN ret=searchSubTree(dupRoot,null,SearchType.LEFT,-1,null,true);
return (DBIN)ret;
}
/**
* Find the rightmost node (DBIN) in a duplicate tree.
* @return the rightmost node in the tree, null if the tree is empty. The
* returned node is latched and the caller must release it.
*/
public DBIN getLastNode( DIN dupRoot) throws DatabaseException {
if (dupRoot == null) {
throw new IllegalArgumentException("getLastNode passed null root");
}
this.hook678(dupRoot);
IN ret=searchSubTree(dupRoot,null,SearchType.RIGHT,-1,null,true);
return (DBIN)ret;
}
/**
* GetParentNode without optional tracking.
*/
public SearchResult getParentINForChildIN( IN child, boolean requireExactMatch, boolean updateGeneration) throws DatabaseException {
return getParentINForChildIN(child,requireExactMatch,updateGeneration,-1,null);
}
/**
* Return a reference to the parent or possible parent of the child. Used by
* objects that need to take a standalone node and find it in the tree, like
* the evictor, checkpointer, and recovery.
* @param childThe child node for which to find the parent. This node is
* latched by the caller and is released by this function before
* returning to the caller.
* @param requireExactMatchif true, we must find the exact parent, not a potential
* parent.
* @param updateGenerationif true, set the generation count during latching. Pass false
* when the LRU should not be impacted, such as during eviction
* and checkpointing.
* @param trackingListif not null, add the LSNs of the parents visited along the
* way, as a debug tracing mechanism. This is meant to stay in
* production, to add information to the log.
* @return a SearchResult object. If the parent has been found,
* result.foundExactMatch is true. If any parent, exact or potential
* has been found, result.parent refers to that node.
*/
public SearchResult getParentINForChildIN( IN child, boolean requireExactMatch, boolean updateGeneration, int targetLevel, List trackingList) throws DatabaseException {
if (child == null) {
throw new IllegalArgumentException("getParentNode passed null");
}
this.hook680(child);
byte[] mainTreeKey=child.getMainTreeKey();
byte[] dupTreeKey=child.getDupTreeKey();
boolean isRoot=child.isRoot();
this.hook679(child);
return getParentINForChildIN(child.getNodeId(),child.containsDuplicates(),isRoot,mainTreeKey,dupTreeKey,requireExactMatch,updateGeneration,targetLevel,trackingList,true);
}
/**
* Return a reference to the parent or possible parent of the child. Used by
* objects that need to take a node id and find it in the tree, like the
* evictor, checkpointer, and recovery.
* @param requireExactMatchif true, we must find the exact parent, not a potential
* parent.
* @param updateGenerationif true, set the generation count during latching. Pass false
* when the LRU should not be impacted, such as during eviction
* and checkpointing.
* @param trackingListif not null, add the LSNs of the parents visited along the
* way, as a debug tracing mechanism. This is meant to stay in
* production, to add information to the log.
* @param doFetchif false, stop the search if we run into a non-resident child.
* Used by the checkpointer to avoid conflicting with work done
* by the evictor.
* @param childThe child node for which to find the parent. This node is
* latched by the caller and is released by this function before
* returning to the caller.
* @return a SearchResult object. If the parent has been found,
* result.foundExactMatch is true. If any parent, exact or potential
* has been found, result.parent refers to that node.
*/
public SearchResult getParentINForChildIN( long targetNodeId, boolean targetContainsDuplicates, boolean targetIsRoot, byte[] targetMainTreeKey, byte[] targetDupTreeKey, boolean requireExactMatch, boolean updateGeneration, int targetLevel, List trackingList, boolean doFetch) throws DatabaseException {
IN rootIN=getRootIN(updateGeneration);
SearchResult result=new SearchResult();
if (rootIN != null) {
if (trackingList != null) {
trackingList.add(new TrackingInfo(root.getLsn(),rootIN.getNodeId()));
}
IN potentialParent=rootIN;
try {
while (result.keepSearching) {
assert TestHookExecute.doHookIfSet(searchHook);
potentialParent.findParent(SearchType.NORMAL,targetNodeId,targetContainsDuplicates,targetIsRoot,targetMainTreeKey,targetDupTreeKey,result,requireExactMatch,updateGeneration,targetLevel,trackingList,doFetch);
potentialParent=result.parent;
}
}
catch ( Exception e) {
this.hook681(potentialParent);
throw new DatabaseException(e);
}
}
return result;
}
/**
* Return a reference to the parent of this LN. This searches through the
* main and duplicate tree and allows splits. Set the tree location to the
* proper BIN parent whether or not the LN child is found. That's because if
* the LN is not found, recovery or abort will need to place it within the
* tree, and so we must point at the appropriate position.
* <p>
* When this method returns with location.bin non-null, the BIN is latched
* and must be unlatched by the caller. Note that location.bin may be
* non-null even if this method returns false.
* </p>
* @param locationa holder class to hold state about the location of our search.
* Sort of an internal cursor.
* @param mainKeykey to navigate through main key
* @param dupKeykey to navigate through duplicate tree. May be null, since
* deleted lns have no data.
* @param lnthe node instantiated from the log
* @param splitsAllowedtrue if this method is allowed to cause tree splits as a side
* effect. In practice, recovery can cause splits, but abort
* can't.
* @param searchDupTreetrue if a search through the dup tree looking for a match on
* the ln's node id should be made (only in the case where dupKey ==
* null). See SR 8984.
* @param updateGenerationif true, set the generation count during latching. Pass false
* when the LRU should not be impacted, such as during eviction
* and checkpointing.
* @return true if node found in tree. If false is returned and there is the
* possibility that we can insert the record into a plausible parent
* we must also set - location.bin (may be null if no possible
* parent found) - location.lnKey (don't need to set if no possible
* parent).
*/
public boolean getParentBINForChildLN( TreeLocation location, byte[] mainKey, byte[] dupKey, LN ln, boolean splitsAllowed, boolean findDeletedEntries, boolean searchDupTree, boolean updateGeneration) throws DatabaseException {
try {
IN searchResult=null;
try {
if (splitsAllowed) {
searchResult=searchSplitsAllowed(mainKey,-1,updateGeneration);
}
else {
searchResult=search(mainKey,SearchType.NORMAL,-1,null,updateGeneration);
}
location.bin=(BIN)searchResult;
}
catch ( Exception e) {
StringBuffer msg=new StringBuffer();
if (searchResult != null) {
this.hook682(searchResult);
msg.append("searchResult=" + searchResult.getClass() + " nodeId="+ searchResult.getNodeId()+ " nEntries="+ searchResult.getNEntries());
}
throw new DatabaseException(msg.toString(),e);
}
if (location.bin == null) {
return false;
}
boolean exactSearch=false;
boolean indicateIfExact=true;
if (!findDeletedEntries) {
exactSearch=true;
indicateIfExact=false;
}
location.index=location.bin.findEntry(mainKey,indicateIfExact,exactSearch);
boolean match=false;
if (findDeletedEntries) {
match=(location.index >= 0 && (location.index & IN.EXACT_MATCH) != 0);
location.index&=~IN.EXACT_MATCH;
}
else {
match=(location.index >= 0);
}
if (match) {
if (!location.bin.isEntryKnownDeleted(location.index)) {
if (database.getSortedDuplicates()) {
Node childNode=location.bin.fetchTarget(location.index);
this.hook683(location,mainKey,dupKey,ln,splitsAllowed,findDeletedEntries,searchDupTree,updateGeneration,exactSearch,indicateIfExact,childNode);
}
}
location.childLsn=location.bin.getLsn(location.index);
}
else {
location.lnKey=mainKey;
}
return match;
}
catch ( ReturnBoolean r) {
return r.value;
}
}
/**
* For SR [#8984]: our prospective child is a deleted LN, and we're facing a
* dup tree. Alas, the deleted LN has no data, and therefore nothing to
* guide the search in the dup tree. Instead, we search by node id. This is
* very expensive, but this situation is a very rare case.
*/
private boolean searchDupTreeByNodeId( TreeLocation location, Node childNode, LN ln, boolean searchDupTree, boolean updateGeneration) throws DatabaseException {
if (searchDupTree) {
BIN oldBIN=location.bin;
if (childNode.matchLNByNodeId(location,ln.getNodeId())) {
location.index&=~IN.EXACT_MATCH;
this.hook684(oldBIN);
location.bin.latch(updateGeneration);
return true;
}
else {
return false;
}
}
else {
return false;
}
}
/**
* @return true if childNode is the DIN parent of this DupCountLN
*/
private boolean searchDupTreeForDupCountLNParent( TreeLocation location, byte[] mainKey, Node childNode) throws DatabaseException {
location.lnKey=mainKey;
if (childNode instanceof DIN) {
DIN dupRoot=(DIN)childNode;
location.childLsn=dupRoot.getDupCountLNRef().getLsn();
return true;
}
else {
return false;
}
}
/**
* Search the dup tree for the DBIN parent of this ln.
*/
private boolean searchDupTreeForDBIN( TreeLocation location, byte[] dupKey, DIN dupRoot, LN ln, boolean findDeletedEntries, boolean indicateIfExact, boolean exactSearch, boolean splitsAllowed, boolean updateGeneration) throws DatabaseException {
try {
assert dupKey != null;
this.hook685(location,dupKey,dupRoot,ln,findDeletedEntries,indicateIfExact,exactSearch,splitsAllowed,updateGeneration);
throw ReturnHack.returnBoolean;
}
catch ( ReturnBoolean r) {
return r.value;
}
}
/**
* Return a reference to the adjacent BIN.
* @param binThe BIN to find the next BIN for. This BIN is latched.
* @param traverseWithinDupTreeif true, only search within the dup tree and return null when
* the traversal runs out of duplicates.
* @return The next BIN, or null if there are no more. The returned node is
* latched and the caller must release it. If null is returned, the
* argument BIN remains latched.
*/
public BIN getNextBin( BIN bin, boolean traverseWithinDupTree) throws DatabaseException {
return getNextBinInternal(traverseWithinDupTree,bin,true);
}
/**
* Return a reference to the previous BIN.
* @param binThe BIN to find the next BIN for. This BIN is latched.
* @param traverseWithinDupTreeif true, only search within the dup tree and return null when
* the traversal runs out of duplicates.
* @return The previous BIN, or null if there are no more. The returned node
* is latched and the caller must release it. If null is returned,
* the argument bin remains latched.
*/
public BIN getPrevBin( BIN bin, boolean traverseWithinDupTree) throws DatabaseException {
return getNextBinInternal(traverseWithinDupTree,bin,false);
}
/**
* Helper routine for above two routines to iterate through BIN's.
*/
private BIN getNextBinInternal( boolean traverseWithinDupTree, BIN bin, boolean forward) throws DatabaseException {
try {
byte[] idKey=null;
if (bin.getNEntries() == 0) {
idKey=bin.getIdentifierKey();
}
else if (forward) {
idKey=bin.getKey(bin.getNEntries() - 1);
}
else {
idKey=bin.getKey(0);
}
IN next=bin;
this.hook687();
IN parent=null;
IN nextIN=null;
this.hook686(traverseWithinDupTree,forward,idKey,next,parent,nextIN);
throw ReturnHack.returnObject;
}
catch ( ReturnObject r) {
return (BIN)r.value;
}
}
/**
* Split the root of the tree.
*/
private void splitRoot() throws DatabaseException {
EnvironmentImpl env=database.getDbEnvironment();
LogManager logManager=env.getLogManager();
INList inMemoryINs=env.getInMemoryINs();
IN curRoot=null;
curRoot=(IN)root.fetchTarget(database,null);
this.hook689(curRoot);
long curRootLsn=0;
long logLsn=0;
IN newRoot=null;
this.hook688(logManager,inMemoryINs,curRoot,curRootLsn,logLsn,newRoot);
treeStats.nRootSplits++;
this.hook662(curRoot,curRootLsn,logLsn,newRoot);
}
/**
* Search the tree, starting at the root. Depending on search type either
* search using key, or search all the way down the right or left sides.
* Stop the search either when the bottom of the tree is reached, or a node
* matching nid is found (see below) in which case that node's parent is
* returned.
* Preemptive splitting is not done during the search.
* @param key -
* the key to search for, or null if searchType is LEFT or RIGHT.
* @param searchType -
* The type of tree search to perform. NORMAL means we're
* searching for key in the tree. LEFT/RIGHT means we're
* descending down the left or right side, resp. DELETE means
* we're descending the tree and will return the lowest node in
* the path that has > 1 entries.
* @param nid -
* The nodeid to search for in the tree. If found, returns its
* parent. If the nodeid of the root is passed, null is returned.
* @param binBoundary -
* If non-null, information is returned about whether the BIN
* found is the first or last BIN in the database.
* @return - the Node that matches the criteria, if any. This is the node
* that is farthest down the tree with a match. Returns null if the
* root is null. Node is latched (unless it's null) and must be
* unlatched by the caller. Only IN's and BIN's are returned, not
* LN's. In a NORMAL search, It is the caller's responsibility to do
* the findEntry() call on the key and BIN to locate the entry that
* matches key. The return value node is latched upon return and it
* is the caller's responsibility to unlatch it.
*/
public IN search( byte[] key, SearchType searchType, long nid, BINBoundary binBoundary, boolean updateGeneration) throws DatabaseException {
IN rootIN=getRootIN(true);
if (rootIN != null) {
return searchSubTree(rootIN,key,searchType,nid,binBoundary,updateGeneration);
}
else {
return null;
}
}
/**
* Do a key based search, permitting pre-emptive splits. Returns the target
* node's parent.
*/
public IN searchSplitsAllowed( byte[] key, long nid, boolean updateGeneration) throws DatabaseException {
return new Tree_searchSplitsAllowed(this,key,nid,updateGeneration).execute();
}
/**
* Searches a portion of the tree starting at parent using key. If during
* the search a node matching a non-null nid argument is found, its parent
* is returned. If searchType is NORMAL, then key must be supplied to guide
* the search. If searchType is LEFT (or RIGHT), then the tree is searched
* down the left (or right) side to find the first (or last) leaf,
* respectively.
* <p>
* Enters with parent latched, assuming it's not null. Exits with the return
* value latched, assuming it's not null.
* <p>
* @param parent -
* the root of the subtree to start the search at. This node
* should be latched by the caller and will be unlatched prior to
* return.
* @param key -
* the key to search for, unless searchType is LEFT or RIGHT
* @param searchType -
* NORMAL means search using key and, optionally, nid. LEFT means
* find the first (leftmost) leaf RIGHT means find the last
* (rightmost) leaf
* @param nid -
* The nodeid to search for in the tree. If found, returns its
* parent. If the nodeid of the root is passed, null is returned.
* Pass -1 if no nodeid based search is desired.
* @return - the node matching the argument criteria, or null. The node is
* latched and must be unlatched by the caller. The parent argument
* and any other nodes that are latched during the search are
* unlatched prior to return.
*/
public IN searchSubTree( IN parent, byte[] key, SearchType searchType, long nid, BINBoundary binBoundary, boolean updateGeneration) throws DatabaseException {
if (parent == null) {
return null;
}
if ((searchType == SearchType.LEFT || searchType == SearchType.RIGHT) && key != null) {
throw new IllegalArgumentException("searchSubTree passed key and left/right search");
}
this.hook690(parent);
if (parent.getNodeId() == nid) {
this.hook691(parent);
return null;
}
if (binBoundary != null) {
binBoundary.isLastBin=true;
binBoundary.isFirstBin=true;
}
int index;
IN child=null;
TreeWalkerStatsAccumulator treeStatsAccumulator=getTreeStatsAccumulator();
try {
do {
if (treeStatsAccumulator != null) {
parent.accumulateStats(treeStatsAccumulator);
}
if (parent.getNEntries() == 0) {
return parent;
}
else if (searchType == SearchType.NORMAL) {
index=parent.findEntry(key,false,false);
}
else if (searchType == SearchType.LEFT) {
index=0;
}
else if (searchType == SearchType.RIGHT) {
index=parent.getNEntries() - 1;
}
else {
throw new IllegalArgumentException("Invalid value of searchType: " + searchType);
}
assert index >= 0;
if (binBoundary != null) {
if (index != parent.getNEntries() - 1) {
binBoundary.isLastBin=false;
}
if (index != 0) {
binBoundary.isFirstBin=false;
}
}
child=(IN)parent.fetchTarget(index);
child.latch(updateGeneration);
if (treeStatsAccumulator != null) {
child.accumulateStats(treeStatsAccumulator);
}
if (child.getNodeId() == nid) {
this.hook693(child);
return parent;
}
this.hook692(parent);
parent=child;
}
while (!(parent instanceof BIN));
return child;
}
catch ( Throwable t) {
this.hook694(parent,child);
if (t instanceof DatabaseException) {
throw (DatabaseException)t;
}
else {
throw new DatabaseException(t);
}
}
}
/**
* Search down the tree using a key, but instead of returning the BIN that
* houses that key, find the point where we can detach a deletable subtree.
* A deletable subtree is a branch where each IN has one child, and the
* bottom BIN has no entries and no resident cursors. That point can be
* found by saving a pointer to the lowest node in the path with more than
* one entry.
* INa / \ INb INc | | INd .. / \ INe .. | BINx (suspected of being empty)
* In this case, we'd like to prune off the subtree headed by INe. INd is
* the parent of this deletable subtree. As we descend, we must keep latches
* for all the nodes that will be logged. In this case, we will need to keep
* INa, INb and INd latched when we return from this method.
* The method returns a list of parent/child/index structures. In this
* example, the list will hold: INa/INb/index INb/INd/index INd/INe/index
* Every node is latched, and every node except for the bottom most child
* (INe) must be logged.
*/
public void searchDeletableSubTree( IN parent, byte[] key, ArrayList nodeLadder) throws DatabaseException, NodeNotEmptyException, CursorsExistException {
assert (parent != null);
assert (key != null);
this.hook695(parent);
int index;
IN child=null;
IN lowestMultipleEntryIN=null;
do {
if (parent.getNEntries() == 0) {
break;
}
if (parent.getNEntries() > 1) {
lowestMultipleEntryIN=parent;
}
index=parent.findEntry(key,false,false);
assert index >= 0;
child=(IN)parent.fetchTarget(index);
child.latch(false);
nodeLadder.add(new SplitInfo(parent,child,index));
parent=child;
}
while (!(parent instanceof BIN));
if ((child != null) && (child instanceof BIN)) {
if (child.getNEntries() != 0) {
throw NodeNotEmptyException.NODE_NOT_EMPTY;
}
if (((BIN)child).nCursors() > 0) {
throw CursorsExistException.CURSORS_EXIST;
}
}
if (lowestMultipleEntryIN != null) {
ListIterator iter=nodeLadder.listIterator(nodeLadder.size());
while (iter.hasPrevious()) {
SplitInfo info5=(SplitInfo)iter.previous();
if (info5.parent == lowestMultipleEntryIN) {
break;
}
else {
this.hook696(info5);
iter.remove();
}
}
}
else {
this.hook697(nodeLadder);
nodeLadder.clear();
}
}
/**
* Search the portion of the tree starting at the parent, permitting
* preemptive splits.
*/
private IN searchSubTreeSplitsAllowed( IN parent, byte[] key, long nid, boolean updateGeneration) throws DatabaseException, SplitRequiredException {
if (parent != null) {
while (true) {
try {
return searchSubTreeUntilSplit(parent,key,nid,updateGeneration);
}
catch ( SplitRequiredException e) {
if (waitHook != null) {
waitHook.doHook();
}
forceSplit(parent,key);
}
}
}
else {
return null;
}
}
/**
* Search the subtree, but throw an exception when we see a node that has to
* be split.
*/
private IN searchSubTreeUntilSplit( IN parent, byte[] key, long nid, boolean updateGeneration) throws DatabaseException, SplitRequiredException {
try {
if (parent == null) {
return null;
}
this.hook699(parent);
if (parent.getNodeId() == nid) {
this.hook700(parent);
return null;
}
int index=0;
IN child=null;
this.hook698(parent,key,nid,updateGeneration,index,child);
throw ReturnHack.returnObject;
}
catch ( ReturnObject r) {
return (IN)r.value;
}
}
/**
* Do pre-emptive splitting in the subtree topped by the "parent" node.
* Search down the tree until we get to the BIN level, and split any nodes
* that fit the splittable requirement.
* Note that more than one node in the path may be splittable. For example,
* a tree might have a level2 IN and a BIN that are both splittable, and
* would be encountered by the same insert operation.
*/
private void forceSplit( IN parent, byte[] key) throws DatabaseException, SplitRequiredException {
new Tree_forceSplit(this,parent,key).execute();
}
/**
* Helper to obtain the root IN with proper root latching. Optionally
* updates the generation of the root when latching it.
*/
public IN getRootIN( boolean updateGeneration) throws DatabaseException {
try {
this.hook702();
IN rootIN=null;
this.hook701(updateGeneration,rootIN);
throw ReturnHack.returnObject;
}
catch ( ReturnObject r) {
return (IN)r.value;
}
}
/**
* Inserts a new LN into the tree.
* @param lnThe LN to insert into the tree.
* @param keyKey value for the node
* @param allowDuplicateswhether to allow duplicates to be inserted
* @param cursorthe cursor to update to point to the newly inserted key/data
* pair, or null if no cursor should be updated.
* @return true if LN was inserted, false if it was a duplicate duplicate or
* if an attempt was made to insert a duplicate when allowDuplicates
* was false.
*/
public boolean insert( LN ln, byte[] key, boolean allowDuplicates, CursorImpl cursor, LockResult lnLock) throws DatabaseException {
try {
validateInsertArgs(allowDuplicates);
EnvironmentImpl env=database.getDbEnvironment();
LogManager logManager=env.getLogManager();
INList inMemoryINs=env.getInMemoryINs();
BIN bin=null;
this.hook703(ln,key,allowDuplicates,cursor,lnLock,env,logManager,inMemoryINs,bin);
throw ReturnHack.returnBoolean;
}
catch ( ReturnBoolean r) {
return r.value;
}
}
/**
* Attempts to insert a duplicate at the current cursor BIN position. If an
* existing dup tree exists, insert into it; otherwise, create a new dup
* tree and place the new LN and the existing LN into it. If the current BIN
* entry contains an LN, the caller guarantees that it is not deleted.
* @return true if duplicate inserted successfully, false if it was a
* duplicate duplicate, false if a there is an existing LN and
* allowDuplicates is false.
*/
private boolean insertDuplicate( byte[] key, BIN bin, LN newLN, LogManager logManager, INList inMemoryINs, CursorImpl cursor, LockResult lnLock, boolean allowDuplicates) throws DatabaseException {
try {
EnvironmentImpl env=database.getDbEnvironment();
int index=cursor.getIndex();
boolean successfulInsert=false;
DIN dupRoot=null;
Node n=bin.fetchTarget(index);
long binNid=bin.getNodeId();
if (n instanceof DIN) {
DBIN dupBin=null;
this.hook704(key,bin,newLN,cursor,lnLock,allowDuplicates,env,index,successfulInsert,dupRoot,n,binNid,dupBin);
}
else if (n instanceof LN) {
if (!allowDuplicates) {
return false;
}
try {
lnLock.setAbortLsn(DbLsn.NULL_LSN,true,true);
dupRoot=createDuplicateTree(key,logManager,inMemoryINs,newLN,cursor);
}
finally {
if (dupRoot != null) {
this.hook705(dupRoot);
successfulInsert=true;
}
else {
successfulInsert=false;
}
}
}
else {
throw new InconsistentNodeException("neither LN or DIN found in BIN");
}
return successfulInsert;
}
catch ( ReturnBoolean r) {
return r.value;
}
}
/**
* Check if the duplicate root needs to be split. The current duplicate root
* is latched. Exit with the new root (even if it's unchanged) latched and
* the old root (unless the root is unchanged) unlatched.
* @param binthe BIN containing the duplicate root.
* @param indexthe index of the duplicate root in bin.
* @return true if the duplicate root was split.
*/
private boolean maybeSplitDuplicateRoot( BIN bin, int index) throws DatabaseException {
DIN curRoot=(DIN)bin.fetchTarget(index);
if (curRoot.needsSplitting()) {
EnvironmentImpl env=database.getDbEnvironment();
LogManager logManager=env.getLogManager();
INList inMemoryINs=env.getInMemoryINs();
byte[] rootIdKey=curRoot.getKey(0);
DIN newRoot=new DIN(database,rootIdKey,maxDupTreeEntriesPerNode,curRoot.getDupKey(),curRoot.getDupCountLNRef(),curRoot.getLevel() + 1);
this.hook707(newRoot);
long curRootLsn=0;
long logLsn=0;
this.hook706(bin,index,curRoot,logManager,inMemoryINs,rootIdKey,newRoot,curRootLsn,logLsn);
this.hook663(curRoot,newRoot,curRootLsn,logLsn);
return true;
}
else {
return false;
}
}
/**
* Convert an existing BIN entry from a single (non-duplicate) LN to a new
* DIN/DupCountLN->DBIN->LN subtree.
* @param keythe key of the entry which will become the duplicate key for
* the duplicate subtree.
* @param logManagerthe logManager
* @param inMemoryINsthe in memory IN list
* @param newLNthe new record to be inserted
* @param cursorpoints to the target position for this new dup tree.
* @return the new duplicate subtree root (a DIN). It is latched when it is
* returned and the caller should unlatch it. If new entry to be
* inserted is a duplicate of the existing LN, null is returned.
*/
private DIN createDuplicateTree( byte[] key, LogManager logManager, INList inMemoryINs, LN newLN, CursorImpl cursor) throws DatabaseException {
EnvironmentImpl env=database.getDbEnvironment();
DIN dupRoot=null;
DBIN dupBin=null;
BIN bin=cursor.getBIN();
int index=cursor.getIndex();
LN existingLN=(LN)bin.fetchTarget(index);
boolean existingLNIsDeleted=bin.isEntryKnownDeleted(index) || existingLN.isDeleted();
assert existingLN != null;
byte[] existingKey=existingLN.getData();
byte[] newLNKey=newLN.getData();
boolean keysEqual=Key.compareKeys(newLNKey,existingKey,database.getDuplicateComparator()) == 0;
if (keysEqual) {
return null;
}
Locker locker=cursor.getLocker();
long nodeId=existingLN.getNodeId();
int startingCount=(locker.createdNode(nodeId) || existingLNIsDeleted || locker.getWriteLockInfo(nodeId).getAbortKnownDeleted()) ? 0 : 1;
DupCountLN dupCountLN=new DupCountLN(startingCount);
long firstDupCountLNLsn=dupCountLN.logProvisional(env,database.getId(),key,DbLsn.NULL_LSN);
dupRoot=new DIN(database,existingKey,maxDupTreeEntriesPerNode,key,new ChildReference(dupCountLN,key,firstDupCountLNLsn),2);
this.hook710(dupRoot);
dupRoot.setIsRoot(true);
dupBin=new DBIN(database,existingKey,maxDupTreeEntriesPerNode,key,1);
this.hook709(dupBin);
ChildReference newExistingLNRef=new ChildReference(existingLN,existingKey,bin.getLsn(index),bin.getState(index));
boolean insertOk=dupBin.insertEntry(newExistingLNRef);
assert insertOk;
this.hook708(key,logManager,inMemoryINs,newLN,cursor,env,dupRoot,dupBin,bin,index,existingLN,newLNKey,locker,dupCountLN,firstDupCountLNLsn);
return dupRoot;
}
/**
* Validate args passed to insert. Presently this just means making sure
* that if they say duplicates are allowed that the database supports
* duplicates.
*/
private void validateInsertArgs( boolean allowDuplicates) throws DatabaseException {
if (allowDuplicates && !database.getSortedDuplicates()) {
throw new DatabaseException("allowDuplicates passed to insert but database doesn't " + "have allow duplicates set.");
}
}
/**
* Find the BIN that is relevant to the insert. If the tree doesn't exist
* yet, then create the first IN and BIN.
* @return the BIN that was found or created and return it latched.
*/
private BIN findBinForInsert( byte[] key, LogManager logManager, INList inMemoryINs, CursorImpl cursor) throws DatabaseException {
BIN bin;
bin=cursor.latchBIN();
if (bin != null) {
if (!bin.needsSplitting() && bin.isKeyInBounds(key)) {
return bin;
}
else {
this.hook712(bin);
}
}
boolean rootLatchIsHeld=false;
bin=this.hook711(key,logManager,inMemoryINs,bin,rootLatchIsHeld);
if (ckptHook != null) {
ckptHook.doHook();
}
return bin;
}
private void accountForSubtreeRemoval( INList inList, IN subtreeRoot, UtilizationTracker tracker) throws DatabaseException {
this.hook713(inList,subtreeRoot,tracker);
this.hook665(subtreeRoot);
}
/**
* @see LogWritable#getLogSize
*/
public int getLogSize(){
int size=LogUtils.getBooleanLogSize();
if (root != null) {
size+=root.getLogSize();
}
return size;
}
/**
* @see LogWritable#writeToLog
*/
public void writeToLog( ByteBuffer logBuffer){
LogUtils.writeBoolean(logBuffer,(root != null));
if (root != null) {
root.writeToLog(logBuffer);
}
}
/**
* @see LogReadable#readFromLog
*/
public void readFromLog( ByteBuffer itemBuffer, byte entryTypeVersion){
boolean rootExists=LogUtils.readBoolean(itemBuffer);
if (rootExists) {
root=makeRootChildReference();
root.readFromLog(itemBuffer,entryTypeVersion);
}
}
/**
* @see LogReadable#dumpLog
*/
public void dumpLog( StringBuffer sb, boolean verbose){
sb.append("<root>");
if (root != null) {
root.dumpLog(sb,verbose);
}
sb.append("</root>");
}
/**
* @see LogReadable#isTransactional
*/
public boolean logEntryIsTransactional(){
return false;
}
/**
* @see LogReadable#getTransactionId
*/
public long getTransactionId(){
return 0;
}
/**
* rebuildINList is used by recovery to add all the resident nodes to the IN
* list.
*/
public void rebuildINList() throws DatabaseException {
INList inMemoryList=database.getDbEnvironment().getInMemoryINs();
if (root != null) {
this.hook714(inMemoryList);
}
}
public void dump() throws DatabaseException {
System.out.println(dumpString(0));
}
public String dumpString( int nSpaces) throws DatabaseException {
StringBuffer sb=new StringBuffer();
sb.append(TreeUtils.indent(nSpaces));
sb.append("<tree>");
sb.append('\n');
if (root != null) {
sb.append(DbLsn.dumpString(root.getLsn(),nSpaces));
sb.append('\n');
IN rootIN=(IN)root.getTarget();
if (rootIN == null) {
sb.append("<in/>");
}
else {
sb.append(rootIN.toString());
}
sb.append('\n');
}
sb.append(TreeUtils.indent(nSpaces));
sb.append("</tree>");
return sb.toString();
}
/**
* Unit test support to validate subtree pruning. Didn't want to make root
* access public.
*/
boolean validateDelete( int index) throws DatabaseException {
try {
this.hook715(index);
throw ReturnHack.returnBoolean;
}
catch ( ReturnBoolean r) {
return r.value;
}
}
/**
* Debugging check that all resident nodes are on the INList and no stray
* nodes are present in the unused portion of the IN arrays.
*/
public void validateINList( IN parent) throws DatabaseException {
if (parent == null) {
parent=(IN)root.getTarget();
}
if (parent != null) {
INList inList=database.getDbEnvironment().getInMemoryINs();
if (!inList.getINs().contains(parent)) {
throw new DatabaseException("IN " + parent.getNodeId() + " missing from INList");
}
for (int i=0; ; i+=1) {
try {
Node node=parent.getTarget(i);
if (i >= parent.getNEntries()) {
if (node != null) {
throw new DatabaseException("IN " + parent.getNodeId() + " has stray node "+ node.getNodeId()+ " at index "+ i);
}
byte[] key=parent.getKey(i);
if (key != null) {
throw new DatabaseException("IN " + parent.getNodeId() + " has stray key "+ key+ " at index "+ i);
}
}
if (node instanceof IN) {
validateINList((IN)node);
}
}
catch ( ArrayIndexOutOfBoundsException e) {
break;
}
}
}
}
public void setWaitHook( TestHook hook){
waitHook=hook;
}
public void setSearchHook( TestHook hook){
searchHook=hook;
}
public void setCkptHook( TestHook hook){
ckptHook=hook;
}
static private class SplitInfo {
IN parent;
IN child;
int index;
SplitInfo( IN parent, IN child, int index){
this.parent=parent;
this.child=child;
this.index=index;
}
}
@MethodObject static class Tree_searchSplitsAllowed {
Tree_searchSplitsAllowed( Tree _this, byte[] key, long nid, boolean updateGeneration){
this._this=_this;
this.key=key;
this.nid=nid;
this.updateGeneration=updateGeneration;
}
IN execute() throws DatabaseException {
insertTarget=null;
while (insertTarget == null) {
this.hook717();
rootIN=null;
this.hook716();
if (rootIN == null) {
break;
}
try {
insertTarget=_this.searchSubTreeSplitsAllowed(rootIN,key,nid,updateGeneration);
}
catch ( SplitRequiredException e) {
continue;
}
}
return insertTarget;
}
protected Tree _this;
protected byte[] key;
protected long nid;
protected boolean updateGeneration;
protected IN insertTarget;
protected boolean rootLatched;
protected boolean rootLatchedExclusive;
protected IN rootIN;
protected boolean b;
protected EnvironmentImpl env;
protected void hook716() throws DatabaseException {
while (true) {
if (_this.root != null) {
rootIN=(IN)_this.root.fetchTarget(_this.database,null);
if (rootIN.needsSplitting()) {
b=true;
this.hook721();
if (b) continue;
this.hook720();
env=_this.database.getDbEnvironment();
env.getDbMapTree().modifyDbRoot(_this.database);
this.hook719();
rootIN=(IN)_this.root.fetchTarget(_this.database,null);
}
this.hook718();
}
break;
}
}
protected void hook717() throws DatabaseException {
}
protected void hook718() throws DatabaseException {
}
protected void hook719() throws DatabaseException {
}
protected void hook720() throws DatabaseException {
}
protected void hook721() throws DatabaseException {
}
}
@MethodObject static class Tree_forceSplit {
Tree_forceSplit( Tree _this, IN parent, byte[] key){
this._this=_this;
this.parent=parent;
this.key=key;
}
void execute() throws DatabaseException, SplitRequiredException {
nodeLadder=new ArrayList();
allLeftSideDescent=true;
allRightSideDescent=true;
{
}
child=null;
origParent=parent;
iter=null;
this.hook722();
success=false;
try {
this.hook723();
if (origParent.needsSplitting() || !origParent.isRoot()) {
throw _this.splitRequiredException;
}
do {
if (parent.getNEntries() == 0) {
break;
}
else {
index=parent.findEntry(key,false,false);
if (index != 0) {
allLeftSideDescent=false;
}
if (index != (parent.getNEntries() - 1)) {
allRightSideDescent=false;
}
}
assert index >= 0;
child=(IN)parent.getTarget(index);
if (child == null) {
break;
}
else {
this.hook724();
nodeLadder.add(new SplitInfo(parent,child,index));
}
parent=child;
}
while (!(parent instanceof BIN));
startedSplits=false;
logManager=_this.database.getDbEnvironment().getLogManager();
iter=nodeLadder.listIterator(nodeLadder.size());
lastParentForSplit=-1;
while (iter.hasPrevious()) {
info1=(SplitInfo)iter.previous();
child=info1.child;
parent=info1.parent;
index=info1.index;
if (child.needsSplitting()) {
maxEntriesPerNode=(child.containsDuplicates() ? _this.maxDupTreeEntriesPerNode : _this.maxMainTreeEntriesPerNode);
if (allLeftSideDescent || allRightSideDescent) {
child.splitSpecial(parent,index,maxEntriesPerNode,key,allLeftSideDescent);
}
else {
child.split(parent,index,maxEntriesPerNode);
}
lastParentForSplit=parent.getNodeId();
startedSplits=true;
if (parent.isDbRoot()) {
this.hook726();
_this.root.setLsn(parent.getLastFullVersion());
parent.setDirty(true);
}
}
else {
if (startedSplits) {
newLsn=0;
if (lastParentForSplit == child.getNodeId()) {
newLsn=child.getLastFullVersion();
}
else {
newLsn=child.log(logManager);
}
parent.updateEntry(index,newLsn);
}
}
this.hook725();
child=null;
iter.remove();
}
success=true;
}
finally {
this.hook727();
}
}
protected Tree _this;
protected IN parent;
protected byte[] key;
protected ArrayList nodeLadder;
protected boolean allLeftSideDescent;
protected boolean allRightSideDescent;
protected int index;
protected IN child;
protected IN origParent;
protected ListIterator iter;
protected boolean isRootLatched;
protected boolean success;
protected boolean startedSplits;
protected LogManager logManager;
protected long lastParentForSplit;
protected SplitInfo info1;
protected int maxEntriesPerNode;
protected long newLsn;
protected SplitInfo info2;
protected void hook722() throws DatabaseException, SplitRequiredException {
}
protected void hook723() throws DatabaseException, SplitRequiredException {
}
protected void hook724() throws DatabaseException, SplitRequiredException {
}
protected void hook725() throws DatabaseException, SplitRequiredException {
}
protected void hook726() throws DatabaseException, SplitRequiredException {
}
protected void hook727() throws DatabaseException, SplitRequiredException {
}
}
protected void hook657( LN ln, EnvironmentImpl env, BIN bin, int index, long newLsn) throws DatabaseException {
}
protected void hook658( LN ln, EnvironmentImpl env, BIN bin, int index, long newLsn) throws DatabaseException {
}
protected void hook659( LN newLN, long binNid, DBIN dupBin, long newLsn) throws DatabaseException {
}
protected void hook660( LN newLN, long binNid, DBIN dupBin, long newLsn) throws DatabaseException {
}
protected void hook661() throws DatabaseException, NodeNotEmptyException, CursorsExistException {
}
protected void hook662( IN curRoot, long curRootLsn, long logLsn, IN newRoot) throws DatabaseException {
}
protected void hook663( DIN curRoot, DIN newRoot, long curRootLsn, long logLsn) throws DatabaseException {
}
protected void hook664( LN newLN, DIN dupRoot, DBIN dupBin, BIN bin, LN existingLN, DupCountLN dupCountLN, long dbinLsn, long dinLsn, long dupCountLsn, long newLsn) throws DatabaseException {
}
protected void hook665( IN subtreeRoot) throws DatabaseException {
}
protected void hook670( WithRootLatched wrl) throws DatabaseException {
this.hook728();
throw new ReturnObject(wrl.doWork(root));
}
protected void hook671( WithRootLatched wrl) throws DatabaseException {
this.hook729();
throw new ReturnObject(wrl.doWork(root));
}
protected void hook672( byte[] idKey, UtilizationTracker tracker, IN subtreeRootIN, ArrayList nodeLadder, IN rootIN, boolean rootNeedsUpdating) throws DatabaseException, NodeNotEmptyException, CursorsExistException {
if (root == null) {
throw new ReturnVoid();
}
rootIN=(IN)root.fetchTarget(database,null);
rootIN.latch(false);
searchDeletableSubTree(rootIN,idKey,nodeLadder);
if (nodeLadder.size() == 0) {
if (purgeRoot) {
subtreeRootIN=logTreeRemoval(rootIN,tracker);
if (subtreeRootIN != null) {
rootNeedsUpdating=true;
}
}
}
else {
SplitInfo detachPoint=(SplitInfo)nodeLadder.get(nodeLadder.size() - 1);
boolean deleteOk=detachPoint.parent.deleteEntry(detachPoint.index,true);
assert deleteOk;
rootNeedsUpdating=cascadeUpdates(nodeLadder,null,-1);
subtreeRootIN=detachPoint.child;
}
}
protected void hook673() throws DatabaseException {
}
protected IN hook674( byte[] idKey, byte[] mainKey, IN in, IN deletedSubtreeRoot) throws DatabaseException, NodeNotEmptyException, CursorsExistException {
this.hook730(in);
assert in instanceof BIN;
assert in.getNEntries() > 0;
int index=in.findEntry(mainKey,false,true);
if (index >= 0) {
deletedSubtreeRoot=deleteDupSubtree(idKey,(BIN)in,index);
}
return deletedSubtreeRoot;
}
protected void hook675( DIN duplicateRoot) throws DatabaseException, NodeNotEmptyException, CursorsExistException {
}
protected void hook676( ArrayList nodeLadder) throws DatabaseException, NodeNotEmptyException, CursorsExistException {
}
protected void hook677( DIN dupRoot) throws DatabaseException {
}
protected void hook678( DIN dupRoot) throws DatabaseException {
}
protected void hook679( IN child) throws DatabaseException {
}
protected void hook680( IN child) throws DatabaseException {
}
protected void hook681( IN potentialParent) throws DatabaseException {
}
protected void hook682( IN searchResult) throws DatabaseException {
}
protected void hook683( TreeLocation location, byte[] mainKey, byte[] dupKey, LN ln, boolean splitsAllowed, boolean findDeletedEntries, boolean searchDupTree, boolean updateGeneration, boolean exactSearch, boolean indicateIfExact, Node childNode) throws DatabaseException {
if (childNode == null) {
}
else if (ln.containsDuplicates()) {
throw new ReturnBoolean(searchDupTreeForDupCountLNParent(location,mainKey,childNode));
}
else {
if (childNode.containsDuplicates()) {
if (dupKey == null) {
throw new ReturnBoolean(searchDupTreeByNodeId(location,childNode,ln,searchDupTree,updateGeneration));
}
else {
throw new ReturnBoolean(searchDupTreeForDBIN(location,dupKey,(DIN)childNode,ln,findDeletedEntries,indicateIfExact,exactSearch,splitsAllowed,updateGeneration));
}
}
}
}
protected void hook684( BIN oldBIN) throws DatabaseException {
}
protected void hook685( TreeLocation location, byte[] dupKey, DIN dupRoot, LN ln, boolean findDeletedEntries, boolean indicateIfExact, boolean exactSearch, boolean splitsAllowed, boolean updateGeneration) throws DatabaseException {
if (maybeSplitDuplicateRoot(location.bin,location.index)) {
dupRoot=(DIN)location.bin.fetchTarget(location.index);
}
this.hook731(location);
location.lnKey=dupKey;
if (splitsAllowed) {
try {
location.bin=(BIN)searchSubTreeSplitsAllowed(dupRoot,location.lnKey,ln.getNodeId(),updateGeneration);
}
catch ( SplitRequiredException e) {
throw new DatabaseException(e);
}
}
else {
location.bin=(BIN)searchSubTree(dupRoot,location.lnKey,SearchType.NORMAL,ln.getNodeId(),null,updateGeneration);
}
location.index=location.bin.findEntry(location.lnKey,indicateIfExact,exactSearch);
boolean match;
if (findDeletedEntries) {
match=(location.index >= 0 && (location.index & IN.EXACT_MATCH) != 0);
location.index&=~IN.EXACT_MATCH;
}
else {
match=(location.index >= 0);
}
if (match) {
location.childLsn=location.bin.getLsn(location.index);
throw new ReturnBoolean(true);
}
else {
throw new ReturnBoolean(false);
}
}
protected void hook686( boolean traverseWithinDupTree, boolean forward, byte[] idKey, IN next, IN parent, IN nextIN) throws DatabaseException {
while (true) {
SearchResult result=null;
if (!traverseWithinDupTree) {
result=getParentINForChildIN(next,true,true);
if (result.exactParentFound) {
parent=result.parent;
}
else {
this.hook733();
throw new ReturnObject(null);
}
}
else {
if (next.isRoot()) {
this.hook734(next);
throw new ReturnObject(null);
}
else {
result=getParentINForChildIN(next,true,true);
if (result.exactParentFound) {
parent=result.parent;
}
else {
throw new ReturnObject(null);
}
}
}
this.hook732();
int index=parent.findEntry(idKey,false,false);
boolean moreEntriesThisBin=false;
if (forward) {
index++;
if (index < parent.getNEntries()) {
moreEntriesThisBin=true;
}
}
else {
if (index > 0) {
moreEntriesThisBin=true;
}
index--;
}
if (moreEntriesThisBin) {
nextIN=(IN)parent.fetchTarget(index);
this.hook735(nextIN);
if (nextIN instanceof BIN) {
this.hook736(parent);
TreeWalkerStatsAccumulator treeStatsAccumulator=getTreeStatsAccumulator();
if (treeStatsAccumulator != null) {
nextIN.accumulateStats(treeStatsAccumulator);
}
throw new ReturnObject((BIN)nextIN);
}
else {
IN ret=searchSubTree(nextIN,null,(forward ? SearchType.LEFT : SearchType.RIGHT),-1,null,true);
this.hook737(parent);
if (ret instanceof BIN) {
throw new ReturnObject((BIN)ret);
}
else {
throw new InconsistentNodeException("subtree did not have a BIN for leaf");
}
}
}
next=parent;
}
}
protected void hook687() throws DatabaseException {
}
protected void hook688( LogManager logManager, INList inMemoryINs, IN curRoot, long curRootLsn, long logLsn, IN newRoot) throws DatabaseException {
byte[] rootIdKey=curRoot.getKey(0);
newRoot=new IN(database,rootIdKey,maxMainTreeEntriesPerNode,curRoot.getLevel() + 1);
newRoot.setIsRoot(true);
curRoot.setIsRoot(false);
try {
curRootLsn=curRoot.logProvisional(logManager,newRoot);
boolean insertOk=newRoot.insertEntry(new ChildReference(curRoot,rootIdKey,curRootLsn));
assert insertOk;
logLsn=newRoot.log(logManager);
}
catch ( DatabaseException e) {
curRoot.setIsRoot(true);
throw e;
}
inMemoryINs.add(newRoot);
root.setTarget(newRoot);
root.setLsn(logLsn);
curRoot.split(newRoot,0,maxMainTreeEntriesPerNode);
root.setLsn(newRoot.getLastFullVersion());
}
protected void hook689( IN curRoot) throws DatabaseException {
}
protected void hook690( IN parent) throws DatabaseException {
}
protected void hook691( IN parent) throws DatabaseException {
}
protected void hook692( IN parent) throws DatabaseException, Throwable {
}
protected void hook693( IN child) throws DatabaseException, Throwable {
}
protected void hook694( IN parent, IN child) throws DatabaseException {
}
protected void hook695( IN parent) throws DatabaseException, NodeNotEmptyException, CursorsExistException {
}
protected void hook696( SplitInfo info5) throws DatabaseException, NodeNotEmptyException, CursorsExistException {
}
protected void hook697( ArrayList nodeLadder) throws DatabaseException, NodeNotEmptyException, CursorsExistException {
}
protected void hook698( IN parent, byte[] key, long nid, boolean updateGeneration, int index, IN child) throws DatabaseException, SplitRequiredException {
do {
if (parent.getNEntries() == 0) {
throw new ReturnObject(parent);
}
else {
index=parent.findEntry(key,false,false);
}
assert index >= 0;
child=(IN)parent.fetchTarget(index);
child.latch(updateGeneration);
if (child.needsSplitting()) {
this.hook739(parent,child);
throw splitRequiredException;
}
if (child.getNodeId() == nid) {
this.hook740(child);
throw new ReturnObject(parent);
}
this.hook738(parent);
parent=child;
}
while (!(parent instanceof BIN));
throw new ReturnObject(parent);
}
protected void hook699( IN parent) throws DatabaseException, SplitRequiredException {
}
protected void hook700( IN parent) throws DatabaseException, SplitRequiredException {
}
protected void hook701( boolean updateGeneration, IN rootIN) throws DatabaseException {
if (root != null) {
rootIN=(IN)root.fetchTarget(database,null);
rootIN.latch(updateGeneration);
}
throw new ReturnObject(rootIN);
}
protected void hook702() throws DatabaseException {
}
protected void hook703( LN ln, byte[] key, boolean allowDuplicates, CursorImpl cursor, LockResult lnLock, EnvironmentImpl env, LogManager logManager, INList inMemoryINs, BIN bin) throws DatabaseException {
bin=findBinForInsert(key,logManager,inMemoryINs,cursor);
this.hook741(bin);
ChildReference newLNRef=new ChildReference(ln,key,DbLsn.NULL_LSN);
cursor.setBIN(bin);
int index=bin.insertEntry1(newLNRef);
if ((index & IN.INSERT_SUCCESS) != 0) {
index&=~IN.INSERT_SUCCESS;
cursor.updateBin(bin,index);
long newLsn=DbLsn.NULL_LSN;
try {
newLsn=ln.log(env,database.getId(),key,DbLsn.NULL_LSN,cursor.getLocker());
}
finally {
if (newLsn == DbLsn.NULL_LSN) {
bin.setKnownDeleted(index);
}
}
lnLock.setAbortLsn(DbLsn.NULL_LSN,true,true);
bin.updateEntry(index,newLsn);
this.hook657(ln,env,bin,index,newLsn);
throw new ReturnBoolean(true);
}
else {
index&=~IN.EXACT_MATCH;
cursor.updateBin(bin,index);
LN currentLN=null;
boolean isDup=false;
Node n=bin.fetchTarget(index);
if (n == null || n instanceof LN) {
currentLN=(LN)n;
}
else {
isDup=true;
}
boolean isDeleted=false;
LockResult currentLock=null;
if (!isDup) {
if (currentLN == null) {
isDeleted=true;
}
else {
currentLock=cursor.lockLNDeletedAllowed(currentLN,LockType.WRITE);
currentLN=currentLock.getLN();
bin=cursor.getBIN();
index=cursor.getIndex();
if (cursor.getDupBIN() != null) {
cursor.clearDupBIN(true);
isDup=true;
}
else if (bin.isEntryKnownDeleted(index) || currentLN == null || currentLN.isDeleted()) {
isDeleted=true;
}
}
}
if (isDeleted) {
long abortLsn=bin.getLsn(index);
boolean abortKnownDeleted=true;
if (currentLN != null && currentLock.getLockGrant() == LockGrantType.EXISTING) {
long nodeId=currentLN.getNodeId();
Locker locker=cursor.getLocker();
WriteLockInfo info6=locker.getWriteLockInfo(nodeId);
abortLsn=info6.getAbortLsn();
abortKnownDeleted=info6.getAbortKnownDeleted();
}
lnLock.setAbortLsn(abortLsn,abortKnownDeleted);
long newLsn=ln.log(env,database.getId(),key,DbLsn.NULL_LSN,cursor.getLocker());
bin.updateEntry(index,ln,newLsn,key);
bin.clearKnownDeleted(index);
bin.clearPendingDeleted(index);
this.hook658(ln,env,bin,index,newLsn);
throw new ReturnBoolean(true);
}
else {
throw new ReturnBoolean(insertDuplicate(key,bin,ln,logManager,inMemoryINs,cursor,lnLock,allowDuplicates));
}
}
}
protected void hook704( byte[] key, BIN bin, LN newLN, CursorImpl cursor, LockResult lnLock, boolean allowDuplicates, EnvironmentImpl env, int index, boolean successfulInsert, DIN dupRoot, Node n, long binNid, DBIN dupBin) throws DatabaseException {
dupRoot=(DIN)n;
this.hook744(dupRoot);
LockResult dclLockResult=cursor.lockDupCountLN(dupRoot,LockType.WRITE);
bin=cursor.getBIN();
index=cursor.getIndex();
if (!allowDuplicates) {
DupCountLN dcl=(DupCountLN)dclLockResult.getLN();
if (dcl.getDupCount() > 0) {
throw new ReturnBoolean(false);
}
}
maybeSplitDuplicateRoot(bin,index);
dupRoot=(DIN)bin.fetchTarget(index);
byte[] newLNKey=newLN.getData();
long previousLsn=dupRoot.getLastFullVersion();
try {
dupBin=(DBIN)searchSubTreeSplitsAllowed(dupRoot,newLNKey,-1,true);
}
catch ( SplitRequiredException e) {
throw new DatabaseException(e);
}
long currentLsn=dupRoot.getLastFullVersion();
if (currentLsn != previousLsn) {
bin.updateEntry(index,currentLsn);
}
this.hook743(cursor);
bin=null;
dupRoot=null;
ChildReference newLNRef=new ChildReference(newLN,newLNKey,DbLsn.NULL_LSN);
int dupIndex=dupBin.insertEntry1(newLNRef);
if ((dupIndex & IN.INSERT_SUCCESS) != 0) {
dupIndex&=~IN.INSERT_SUCCESS;
cursor.updateDBin(dupBin,dupIndex);
long newLsn=DbLsn.NULL_LSN;
try {
newLsn=newLN.log(env,database.getId(),key,DbLsn.NULL_LSN,cursor.getLocker());
}
finally {
if (newLsn == DbLsn.NULL_LSN) {
dupBin.setKnownDeleted(dupIndex);
}
}
lnLock.setAbortLsn(DbLsn.NULL_LSN,true,true);
dupBin.setLsn(dupIndex,newLsn);
this.hook659(newLN,binNid,dupBin,newLsn);
successfulInsert=true;
}
else {
dupIndex&=~IN.EXACT_MATCH;
cursor.updateDBin(dupBin,dupIndex);
LN currentLN=(LN)dupBin.fetchTarget(dupIndex);
boolean isDeleted=false;
LockResult currentLock=null;
if (currentLN == null) {
isDeleted=true;
}
else {
currentLock=cursor.lockLNDeletedAllowed(currentLN,LockType.WRITE);
currentLN=currentLock.getLN();
dupBin=cursor.getDupBIN();
dupIndex=cursor.getDupIndex();
if (dupBin.isEntryKnownDeleted(dupIndex) || currentLN == null || currentLN.isDeleted()) {
isDeleted=true;
}
}
if (isDeleted) {
long abortLsn=dupBin.getLsn(dupIndex);
boolean abortKnownDeleted=true;
if (currentLN != null && currentLock.getLockGrant() == LockGrantType.EXISTING) {
long nodeId=currentLN.getNodeId();
Locker locker=cursor.getLocker();
WriteLockInfo info7=locker.getWriteLockInfo(nodeId);
abortLsn=info7.getAbortLsn();
abortKnownDeleted=info7.getAbortKnownDeleted();
}
lnLock.setAbortLsn(abortLsn,abortKnownDeleted);
long newLsn=newLN.log(env,database.getId(),key,DbLsn.NULL_LSN,cursor.getLocker());
dupBin.updateEntry(dupIndex,newLN,newLsn,newLNKey);
dupBin.clearKnownDeleted(dupIndex);
dupBin.clearPendingDeleted(dupIndex);
this.hook660(newLN,binNid,dupBin,newLsn);
successfulInsert=true;
}
else {
successfulInsert=false;
}
}
this.hook742(dupBin);
dupBin=null;
if (successfulInsert) {
this.hook746(cursor);
dupRoot=cursor.getLatchedDupRoot(false);
this.hook745(cursor);
dupRoot.incrementDuplicateCount(dclLockResult,key,cursor.getLocker(),true);
}
}
protected void hook705( DIN dupRoot) throws DatabaseException {
}
protected void hook706( BIN bin, int index, DIN curRoot, LogManager logManager, INList inMemoryINs, byte[] rootIdKey, DIN newRoot, long curRootLsn, long logLsn) throws DatabaseException {
newRoot.setIsRoot(true);
curRoot.setDupCountLN(null);
curRoot.setIsRoot(false);
try {
curRootLsn=curRoot.logProvisional(logManager,newRoot);
boolean insertOk=newRoot.insertEntry(new ChildReference(curRoot,rootIdKey,bin.getLsn(index)));
assert insertOk;
logLsn=newRoot.log(logManager);
}
catch ( DatabaseException e) {
curRoot.setIsRoot(true);
throw e;
}
inMemoryINs.add(newRoot);
bin.updateEntry(index,newRoot,logLsn);
curRoot.split(newRoot,0,maxDupTreeEntriesPerNode);
}
protected void hook707( DIN newRoot) throws DatabaseException {
}
protected void hook708( byte[] key, LogManager logManager, INList inMemoryINs, LN newLN, CursorImpl cursor, EnvironmentImpl env, DIN dupRoot, DBIN dupBin, BIN bin, int index, LN existingLN, byte[] newLNKey, Locker locker, DupCountLN dupCountLN, long firstDupCountLNLsn) throws DatabaseException {
long dbinLsn=dupBin.logProvisional(logManager,dupRoot);
inMemoryINs.add(dupBin);
dupRoot.setEntry(0,dupBin,dupBin.getKey(0),dbinLsn,dupBin.getState(0));
long dinLsn=dupRoot.log(logManager);
inMemoryINs.add(dupRoot);
LockResult lockResult=locker.lock(dupCountLN.getNodeId(),LockType.WRITE,false,database);
lockResult.setAbortLsn(firstDupCountLNLsn,false);
dupCountLN.setDupCount(2);
long dupCountLsn=dupCountLN.log(env,database.getId(),key,firstDupCountLNLsn,locker);
dupRoot.updateDupCountLNRef(dupCountLsn);
long newLsn=newLN.log(env,database.getId(),key,DbLsn.NULL_LSN,locker);
int dupIndex=dupBin.insertEntry1(new ChildReference(newLN,newLNKey,newLsn));
dupIndex&=~IN.INSERT_SUCCESS;
cursor.updateDBin(dupBin,dupIndex);
bin.adjustCursorsForMutation(index,dupBin,dupIndex ^ 1,cursor);
this.hook747(dupBin);
bin.updateEntry(index,dupRoot,dinLsn);
bin.setMigrate(index,false);
this.hook664(newLN,dupRoot,dupBin,bin,existingLN,dupCountLN,dbinLsn,dinLsn,dupCountLsn,newLsn);
}
protected void hook709( DBIN dupBin) throws DatabaseException {
}
protected void hook710( DIN dupRoot) throws DatabaseException {
}
protected BIN hook711( byte[] key, LogManager logManager, INList inMemoryINs, BIN bin, boolean rootLatchIsHeld) throws DatabaseException {
long logLsn;
while (true) {
rootLatchIsHeld=true;
this.hook748();
if (root == null) {
this.hook751();
if (root != null) {
this.hook752();
rootLatchIsHeld=false;
continue;
}
bin=new BIN(database,key,maxMainTreeEntriesPerNode,1);
this.hook750(bin);
logLsn=bin.logProvisional(logManager,null);
IN rootIN=new IN(database,key,maxMainTreeEntriesPerNode,2);
rootIN.setIsRoot(true);
boolean insertOk=rootIN.insertEntry(new ChildReference(bin,key,logLsn));
assert insertOk;
logLsn=rootIN.log(logManager);
rootIN.setDirty(true);
root=new ChildReference(rootIN,new byte[0],logLsn);
inMemoryINs.add(bin);
inMemoryINs.add(rootIN);
this.hook749();
rootLatchIsHeld=false;
break;
}
else {
this.hook753();
rootLatchIsHeld=false;
IN in=searchSplitsAllowed(key,-1,true);
if (in == null) {
continue;
}
else {
bin=(BIN)in;
break;
}
}
}
return bin;
}
protected void hook712( BIN bin) throws DatabaseException {
}
protected void hook713( INList inList, IN subtreeRoot, UtilizationTracker tracker) throws DatabaseException {
subtreeRoot.accountForSubtreeRemoval(inList,tracker);
}
protected void hook714( INList inMemoryList) throws DatabaseException {
Node rootIN=root.getTarget();
if (rootIN != null) {
rootIN.rebuildINList(inMemoryList);
}
}
protected void hook715( int index) throws DatabaseException {
IN rootIN=(IN)root.fetchTarget(database,null);
throw new ReturnBoolean(rootIN.validateSubtreeBeforeDelete(index));
}
protected void hook728() throws DatabaseException {
}
protected void hook729() throws DatabaseException {
}
protected void hook730( IN in) throws DatabaseException, NodeNotEmptyException, CursorsExistException {
}
protected void hook731( TreeLocation location) throws DatabaseException {
}
protected void hook732() throws DatabaseException {
}
protected void hook733() throws DatabaseException {
}
protected void hook734( IN next) throws DatabaseException {
}
protected void hook735( IN nextIN) throws DatabaseException {
}
protected void hook736( IN parent) throws DatabaseException {
}
protected void hook737( IN parent) throws DatabaseException {
}
protected void hook738( IN parent) throws DatabaseException, SplitRequiredException {
}
protected void hook739( IN parent, IN child) throws DatabaseException, SplitRequiredException {
}
protected void hook740( IN child) throws DatabaseException, SplitRequiredException {
}
protected void hook741( BIN bin) throws DatabaseException {
}
protected void hook742( DBIN dupBin) throws DatabaseException {
}
protected void hook743( CursorImpl cursor) throws DatabaseException {
}
protected void hook744( DIN dupRoot) throws DatabaseException {
}
protected void hook745( CursorImpl cursor) throws DatabaseException {
}
protected void hook746( CursorImpl cursor) throws DatabaseException {
}
protected void hook747( DBIN dupBin) throws DatabaseException {
}
protected void hook748() throws DatabaseException {
}
protected void hook749() throws DatabaseException {
}
protected void hook750( BIN bin) throws DatabaseException {
}
protected void hook751() throws DatabaseException {
}
protected void hook752() throws DatabaseException {
}
protected void hook753() throws DatabaseException {
}
protected void hook754( BIN bin) throws DatabaseException, NodeNotEmptyException, CursorsExistException {
}
}