/*
* Find the existing entry and get a reference to all BIN fields
* while latched.
*/
LN ln = (LN) targetBin.fetchTarget(targetIndex);
byte[] lnKey = targetBin.getKey(targetIndex);
Comparator userComparisonFcn = targetBin.getKeyComparator();
/* If fetchTarget returned null, a deleted LN was cleaned. */
if (targetBin.isEntryKnownDeleted(targetIndex) ||
ln == null) {
releaseBINs();
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) {
releaseBINs();
return OperationStatus.NOTFOUND;
}
/*
* 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 (database.getSortedDuplicates()) {
/* Check that data compares equal before replacing it. */
boolean keysEqual = false;
if (foundDataBytes != null) {
keysEqual = Key.compareKeys
(foundDataBytes, newData, userComparisonFcn) == 0;
}
if (!keysEqual) {
revertLock(ln, lockResult);
throw new DatabaseException
("Can't replace a duplicate with different data.");
}
}
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.
*/
long 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);
long newLsn = ln.modify(newData, database, newKey, oldLsn, locker);
long newLNSize = ln.getMemorySizeIncludedByParent();
/* Update the parent BIN. */
targetBin.updateEntry(targetIndex, newLsn, oldLNSize, newLNSize);
releaseBINs();