DatabaseEntry foundData,
LockType lockType,
boolean first)
throws DatabaseException {
TreeWalkerStatsAccumulator treeStatsAccumulator =
getTreeStatsAccumulator();
boolean duplicateFetch = setTargetBin();
if (targetBin == null) {
return OperationStatus.NOTFOUND;
}
assert targetBin.isLatchOwnerForWrite();
/*
* 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]
*/
if (targetIndex < 0 ||
targetIndex >= targetBin.getNEntries() ||
targetBin.isEntryKnownDeleted(targetIndex)) {
/* Node is no longer present. */
if (treeStatsAccumulator != null) {
treeStatsAccumulator.incrementDeletedLNCount();
}
targetBin.releaseLatch();
return OperationStatus.KEYEMPTY;
}
/*
* If we encounter a pendingDeleted entry, add it to the compressor
* queue.
*/
if (targetBin.isEntryPendingDeleted(targetIndex)) {
EnvironmentImpl envImpl = databaseImpl.getDbEnvironment();
envImpl.addToCompressorQueue
(targetBin, new Key(targetBin.getKey(targetIndex)), false);
}
/*
* We don't need to fetch the LN if all these conditions are true:
* 1. No locking is needed.
* 2. The user has not requested that we return the data -- only the
* key may be returned, and it is available in the BIN.
* 3. We don't need to fetch the node in order to determine whether to
* descend into the dup tree.
*/
if (lockType == LockType.NONE &&
(foundData == null ||
(foundData.getPartial() &&
foundData.getPartialLength() == 0)) &&
(duplicateFetch ||
!databaseImpl.getSortedDuplicates())) {
if (targetBin.isEntryPendingDeleted(targetIndex)) {
if (treeStatsAccumulator != null) {
treeStatsAccumulator.incrementDeletedLNCount();
}
return OperationStatus.KEYEMPTY;
}
if (foundKey != null) {
setDbt(foundKey,
duplicateFetch ?
dupKey :
targetBin.getKey(targetIndex));
}
return OperationStatus.SUCCESS;
}
/* If fetchTarget returns null, a deleted LN was cleaned. */
Node n;
try {
n = targetBin.fetchTarget(targetIndex);
} catch (DatabaseException DE) {
targetBin.releaseLatch();
throw DE;
}
if (n == null) {
if (treeStatsAccumulator != null) {
treeStatsAccumulator.incrementDeletedLNCount();
}
targetBin.releaseLatch();
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(cacheMode);
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. 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);
try {
ln = lockResult.getLN();
byte[] lnData = (ln != null) ? ln.getData() : null;
if (ln == null || lnData == null) {
if (treeStatsAccumulator != null) {
treeStatsAccumulator.incrementDeletedLNCount();
}
return OperationStatus.KEYEMPTY;
}
/*