DatabaseEntry foundData,
LockType lockType,
boolean first)
throws DatabaseException {
TreeWalkerStatsAccumulator treeStatsAccumulator =
getTreeStatsAccumulator();
boolean duplicateFetch = setTargetBin();
if (targetBin == null) {
return OperationStatus.NOTFOUND;
}
assert targetBin.isLatchOwner();
/*
* Check the deleted flag in the BIN and make sure this isn't an empty
* BIN. The BIN could be empty by virtue of the compressor running the
* size of this BIN to 0 but not having yet deleted it from the tree.
*
* The index may be negative if we're at an intermediate stage in an
* higher level operation, and we expect a higher level method to do a
* next or prev operation after this returns KEYEMPTY. [#11700]
*/
Node n = null;
if (targetIndex < 0 ||
targetIndex >= targetBin.getNEntries() ||
targetBin.isEntryKnownDeleted(targetIndex)) {
/* Node is no longer present. */
} else {
/*
* If we encounter a pendingDeleted entry, add it to the compressor
* queue.
*/
if (targetBin.isEntryPendingDeleted(targetIndex)) {
EnvironmentImpl envImpl = database.getDbEnvironment();
envImpl.addToCompressorQueue
(targetBin, new Key(targetBin.getKey(targetIndex)), false);
}
/* If fetchTarget returns null, a deleted LN was cleaned. */
try {
n = targetBin.fetchTarget(targetIndex);
} catch (DatabaseException DE) {
targetBin.releaseLatchIfOwner();
throw DE;
}
}
if (n == null) {
if (treeStatsAccumulator != null) {
treeStatsAccumulator.incrementDeletedLNCount();
}
targetBin.releaseLatchIfOwner();
return OperationStatus.KEYEMPTY;
}
/*
* Note that since we have the BIN/DBIN latched, we can safely check
* the node type. Any conversions from an LN to a dup tree must have
* the bin latched.
*/
addCursor(targetBin);
if (n.containsDuplicates()) {
assert !duplicateFetch;
/* Descend down duplicate tree, doing latch coupling. */
DIN duplicateRoot = (DIN) n;
duplicateRoot.latch();
targetBin.releaseLatch();
if (positionFirstOrLast(first, duplicateRoot)) {
try {
return fetchCurrent(foundKey, foundData, lockType, first);
} catch (DatabaseException DE) {
releaseBINs();
throw DE;
}
} else {
return OperationStatus.NOTFOUND;
}
}
LN ln = (LN) n;
assert TestHookExecute.doHookIfSet(testHook);
/*
* Lock the LN (will unlatch). For dirty-read, the data of the LN can
* be set to null at any time. Cache the data in a local variable so
* its state does not change before calling setDbt further below.
*/
LockResult lockResult = lockLN(ln, lockType);
ln = lockResult.getLN();
byte[] lnData = (ln != null) ? ln.getData() : null;
if (ln == null || lnData == null) {
if (treeStatsAccumulator != null) {
treeStatsAccumulator.incrementDeletedLNCount();
}
return OperationStatus.KEYEMPTY;
}
latchBINs();