Put put = new Put(recordId.toBytes());
Set<BlobReference> referencedBlobs = new HashSet<BlobReference>();
Set<BlobReference> unReferencedBlobs = new HashSet<BlobReference>();
RecordEvent recordEvent = new RecordEvent();
recordEvent.setType(Type.UPDATE);
recordEvent.setTableName(getTableName());
recordEvent.setVersionUpdated(version);
for (RecordUpdateHook hook : updateHooks) {
hook.beforeUpdate(record, originalRecord, this, fieldTypes, recordEvent);
}
Set<Scope> changedScopes = calculateUpdateFields(record, fields, record.getMetadataMap(), originalFields,
originalRecord.getMetadataMap(), originalNextFields, version, put,
recordEvent, referencedBlobs, unReferencedBlobs, true, fieldTypes);
for (BlobReference referencedBlob : referencedBlobs) {
referencedBlob.setRecordId(recordId);
}
for (BlobReference unReferencedBlob : unReferencedBlobs) {
unReferencedBlob.setRecordId(recordId);
}
if (!changedScopes.isEmpty()) {
// Check the conditions after establishing that the record really needs updating, this makes the
// conditional update operation idempotent.
Record conditionsRecord = MutationConditionVerifier.checkConditions(originalRecord, conditions, this,
record);
if (conditionsRecord != null) {
return conditionsRecord;
}
// Update the record types
// If no record type is specified explicitly, use the current one of the non-versioned scope
QName recordTypeName = record.getRecordTypeName() != null ? record.getRecordTypeName() :
originalRecord.getRecordTypeName();
Long recordTypeVersion;
if (latestRecordType) {
recordTypeVersion = null;
} else if (record.getRecordTypeName() == null) {
recordTypeVersion = originalRecord.getRecordTypeVersion();
} else {
recordTypeVersion = record.getRecordTypeVersion();
}
RecordType recordType = typeManager.getRecordTypeByName(recordTypeName, recordTypeVersion);
// Update the mutable record type in the record object
Scope mutableScope = Scope.VERSIONED_MUTABLE;
newRecord.setRecordType(mutableScope, recordType.getName(), recordType.getVersion());
// If the record type changed, update it on the record table
QName originalMutableScopeRecordTypeName = originalRecord.getRecordTypeName(mutableScope);
if (originalMutableScopeRecordTypeName == null) { // There was no initial mutable record type yet
put.add(RecordCf.DATA.bytes, RECORD_TYPE_ID_QUALIFIERS.get(mutableScope), version,
recordType.getId().getBytes());
put.add(RecordCf.DATA.bytes, RECORD_TYPE_VERSION_QUALIFIERS.get(mutableScope), version,
Bytes.toBytes(recordType.getVersion()));
} else {
RecordType originalMutableScopeRecordType = typeManager
.getRecordTypeByName(originalMutableScopeRecordTypeName,
originalRecord.getRecordTypeVersion(mutableScope));
if (!recordType.getId().equals(originalMutableScopeRecordType.getId())) {
// If the next record version had the same record type name, copy the original value to that one
if (originalNextRecord != null && originalMutableScopeRecordType.getName()
.equals(originalNextRecord.getRecordTypeName(mutableScope))) {
put.add(RecordCf.DATA.bytes, RECORD_TYPE_ID_QUALIFIERS.get(mutableScope), version + 1,
originalMutableScopeRecordType.getId().getBytes());
}
put.add(RecordCf.DATA.bytes, RECORD_TYPE_ID_QUALIFIERS.get(mutableScope), version,
recordType.getId().getBytes());
}
if (!recordType.getVersion().equals(originalMutableScopeRecordType.getVersion())) {
// If the next record version had the same record type version, copy the original value to that one
if (originalNextRecord != null && originalMutableScopeRecordType.getVersion()
.equals(originalNextRecord.getRecordTypeVersion(mutableScope))) {
put.add(RecordCf.DATA.bytes, RECORD_TYPE_ID_QUALIFIERS.get(mutableScope), version + 1,
Bytes.toBytes(originalMutableScopeRecordType.getVersion()));
}
put.add(RecordCf.DATA.bytes, RECORD_TYPE_VERSION_QUALIFIERS.get(mutableScope), version,
Bytes.toBytes(recordType.getVersion()));
}
}
// Validate if the new values for the record are valid wrt the recordType (e.g. mandatory fields)
validateRecord(newRecord, originalRecord, recordType, fieldTypes);
// Reserve blobs so no other records can use them
reserveBlobs(record.getId(), referencedBlobs);
put.add(RecordCf.DATA.bytes, RecordColumn.PAYLOAD.bytes, 1L, recordEvent.toJsonBytes());
put.add(RecordCf.DATA.bytes, RecordColumn.OCC.bytes, 1L, nextOcc(oldOccBytes));
boolean occSuccess = recordTable.checkAndPut(put.getRow(), RecordCf.DATA.bytes, RecordColumn.OCC.bytes,
oldOccBytes, put);
if (!occSuccess) {
throw new ConcurrentRecordUpdateException(recordId);