boolean isDup = setTargetBin();
/* Variables for tracing. */
final long oldLsn;
final long newLsn;
LN ln;
try {
/*
* Find the existing entry and get a reference to all BIN fields
* while latched.
*/
ln = (LN) targetBin.fetchTarget(targetIndex);
byte[] lnKey = targetBin.getKey(targetIndex);
/* If fetchTarget returned null, a deleted LN was cleaned. */
if (targetBin.isEntryKnownDeleted(targetIndex) ||
ln == null) {
return OperationStatus.NOTFOUND;
}
/* Get a write lock. */
LockResult lockResult = lockLN(ln, LockType.WRITE);
ln = lockResult.getLN();
/* Check LN deleted status under the protection of a write lock. */
if (ln == null) {
return OperationStatus.NOTFOUND;
}
if (checkNodeId != Node.NULL_NODE_ID &&
checkNodeId != ln.getNodeId()) {
EnvironmentFailureException.unexpectedState
("Overwrite node ID expected = " + checkNodeId +
" but in Btree = " + ln.getNodeId());
}
/*
* If cursor points at a dup, then we can only replace the entry
* with a new entry that is "equal" to the old one. Since a user
* defined comparison function may actually compare equal for two
* byte sequences that are actually different we still have to do
* the replace. Arguably we could skip the replacement if there is
* no user defined comparison function and the new data is the
* same.
*/
byte[] foundDataBytes;
byte[] foundKeyBytes;
isDup = setTargetBin();
if (isDup) {
foundDataBytes = lnKey;
foundKeyBytes = targetBin.getDupKey();
} else {
foundDataBytes = ln.getData();
foundKeyBytes = lnKey;
}
byte[] newData;
/* Resolve partial puts. */
if (data.getPartial()) {
int dlen = data.getPartialLength();
int doff = data.getPartialOffset();
int origlen = (foundDataBytes != null) ?
foundDataBytes.length : 0;
int oldlen = (doff + dlen > origlen) ? doff + dlen : origlen;
int len = oldlen - dlen + data.getSize();
if (len == 0) {
newData = LogUtils.ZERO_LENGTH_BYTE_ARRAY;
} else {
newData = new byte[len];
}
int pos = 0;
/*
* Keep 0..doff of the old data (truncating if doff > length).
*/
int slicelen = (doff < origlen) ? doff : origlen;
if (slicelen > 0) {
System.arraycopy(foundDataBytes, 0, newData,
pos, slicelen);
}
pos += doff;
/* Copy in the new data. */
slicelen = data.getSize();
System.arraycopy(data.getData(), data.getOffset(),
newData, pos, slicelen);
pos += slicelen;
/* Append the rest of the old data (if any). */
slicelen = origlen - (doff + dlen);
if (slicelen > 0) {
System.arraycopy(foundDataBytes, doff + dlen, newData, pos,
slicelen);
}
} else {
int len = data.getSize();
if (len == 0) {
newData = LogUtils.ZERO_LENGTH_BYTE_ARRAY;
} else {
newData = new byte[len];
}
System.arraycopy(data.getData(), data.getOffset(),
newData, 0, len);
}
if (databaseImpl.getSortedDuplicates()) {
/* Check that data compares equal before replacing it. */
boolean keysEqual = false;
if (foundDataBytes != null) {
keysEqual = Key.compareKeys
(foundDataBytes, newData,
databaseImpl.getDuplicateComparator()) == 0;
}
if (!keysEqual) {
revertLock(ln, lockResult);
throw new DuplicateDataException
("Can't replace a duplicate with data that is " +
"unequal according to the duplicate comparator.");
}
}
if (foundData != null) {
setDbt(foundData, foundDataBytes);
}
if (foundKey != null) {
setDbt(foundKey, foundKeyBytes);
}
/*
* Between the release of the BIN latch and acquiring the write
* lock any number of operations may have executed which would
* result in a new abort LSN for this record. Therefore, wait until
* now to get the abort LSN.
*/
oldLsn = targetBin.getLsn(targetIndex);
lockResult.setAbortLsn
(oldLsn, targetBin.isEntryKnownDeleted(targetIndex));
/*
* The modify has to be inside the latch so that the BIN is updated
* inside the latch.
*/
long oldLNSize = ln.getMemorySizeIncludedByParent();
byte[] newKey = (isDup ? targetBin.getDupKey() : lnKey);
newLsn = ln.modify(newData, databaseImpl, newKey, oldLsn, locker,
repContext);
/* Return a copy of resulting data, if requested. [#16932] */
if (returnNewData != null) {
returnNewData.setData(ln.copyData());
}
/*
* Update the parent BIN. Update the data-as-key, if changed, for
* a DBIN. [#15704]