mirror of https://gitee.com/bigwinds/arangodb
Finish revolution of document API, RestHandler still missing.
This commit is contained in:
parent
2654e654db
commit
e4ce808349
|
@ -848,7 +848,8 @@ OperationResult Transaction::update(std::string const& collectionName,
|
|||
return updateCoordinator(collectionName, newValue, optionsCopy);
|
||||
}
|
||||
|
||||
return updateLocal(collectionName, newValue, optionsCopy);
|
||||
return modifyLocal(collectionName, newValue, optionsCopy,
|
||||
TRI_VOC_DOCUMENT_OPERATION_UPDATE);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -909,105 +910,6 @@ OperationResult Transaction::updateCoordinator(std::string const& collectionName
|
|||
return OperationResult(res);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief update one or multiple documents in a collection, local
|
||||
/// the single-document variant of this operation will either succeed or,
|
||||
/// if it fails, clean up after itself
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
OperationResult Transaction::updateLocal(std::string const& collectionName,
|
||||
VPackSlice const newValue,
|
||||
OperationOptions& options) {
|
||||
TRI_voc_cid_t cid = resolver()->getCollectionIdLocal(collectionName);
|
||||
|
||||
if (cid == 0) {
|
||||
return OperationResult(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
// read expected revision
|
||||
std::string const key(Transaction::extractKey(newValue));
|
||||
TRI_voc_rid_t const expectedRevision
|
||||
= options.ignoreRevs ? 0 : Transaction::extractRevisionId(newValue);
|
||||
|
||||
// generate a new tick value
|
||||
TRI_voc_tick_t const revisionId = TRI_NewTickServer();
|
||||
// TODO: clean this up
|
||||
TRI_document_collection_t* document = documentCollection(trxCollection(cid));
|
||||
|
||||
VPackBuilder builder;
|
||||
builder.openObject();
|
||||
|
||||
VPackObjectIterator it(newValue);
|
||||
while (it.valid()) {
|
||||
// let all but the system attributes pass
|
||||
std::string k = it.key().copyString();
|
||||
if (k[0] != '_' ||
|
||||
(k != TRI_VOC_ATTRIBUTE_KEY &&
|
||||
k != TRI_VOC_ATTRIBUTE_ID &&
|
||||
k != TRI_VOC_ATTRIBUTE_REV &&
|
||||
k != TRI_VOC_ATTRIBUTE_FROM &&
|
||||
k != TRI_VOC_ATTRIBUTE_TO)) {
|
||||
builder.add(k, it.value());
|
||||
}
|
||||
it.next();
|
||||
}
|
||||
// finally add (new) _rev attribute
|
||||
builder.add(TRI_VOC_ATTRIBUTE_REV, VPackValue(std::to_string(revisionId)));
|
||||
builder.close();
|
||||
|
||||
VPackSlice sanitized = builder.slice();
|
||||
|
||||
if (orderDitch(trxCollection(cid)) == nullptr) {
|
||||
return OperationResult(TRI_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
TRI_doc_mptr_t mptr;
|
||||
TRI_voc_rid_t actualRevision = 0;
|
||||
TRI_doc_update_policy_t policy(expectedRevision == 0 ? TRI_DOC_UPDATE_LAST_WRITE : TRI_DOC_UPDATE_ERROR, expectedRevision, &actualRevision);
|
||||
|
||||
int res = lock(trxCollection(cid), TRI_TRANSACTION_WRITE);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return OperationResult(res);
|
||||
}
|
||||
|
||||
VPackBuilder oldBuilder;
|
||||
{ VPackObjectBuilder guard(&oldBuilder);
|
||||
oldBuilder.add(TRI_VOC_ATTRIBUTE_KEY, VPackValue(key));
|
||||
if (expectedRevision != 0) {
|
||||
oldBuilder.add(TRI_VOC_ATTRIBUTE_REV,
|
||||
VPackValue(std::to_string(expectedRevision)));
|
||||
}
|
||||
}
|
||||
VPackSlice oldValue = oldBuilder.slice();
|
||||
|
||||
res = document->update(this, &oldValue, &sanitized, &mptr, &policy, options, !isLocked(document, TRI_TRANSACTION_WRITE));
|
||||
|
||||
if (res == TRI_ERROR_ARANGO_CONFLICT) {
|
||||
// still return
|
||||
VPackBuilder resultBuilder;
|
||||
buildDocumentIdentity(resultBuilder, cid, key, mptr.revisionId(), "");
|
||||
|
||||
return OperationResult(resultBuilder.steal(), nullptr, "",
|
||||
TRI_ERROR_ARANGO_CONFLICT,
|
||||
options.waitForSync || document->_info.waitForSync());
|
||||
} else if (res != TRI_ERROR_NO_ERROR) {
|
||||
return OperationResult(res);
|
||||
}
|
||||
|
||||
TRI_ASSERT(mptr.getDataPtr() != nullptr);
|
||||
|
||||
if (options.silent) {
|
||||
return OperationResult(TRI_ERROR_NO_ERROR);
|
||||
}
|
||||
|
||||
VPackBuilder resultBuilder;
|
||||
buildDocumentIdentity(resultBuilder, cid, key, std::to_string(revisionId), std::to_string(actualRevision));
|
||||
|
||||
return OperationResult(resultBuilder.steal(), nullptr, "", TRI_ERROR_NO_ERROR,
|
||||
options.waitForSync);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief replace one or multiple documents in a collection
|
||||
/// the single-document variant of this operation will either succeed or,
|
||||
|
@ -1030,7 +932,8 @@ OperationResult Transaction::replace(std::string const& collectionName,
|
|||
return replaceCoordinator(collectionName, newValue, optionsCopy);
|
||||
}
|
||||
|
||||
return replaceLocal(collectionName, newValue, optionsCopy);
|
||||
return modifyLocal(collectionName, newValue, optionsCopy,
|
||||
TRI_VOC_DOCUMENT_OPERATION_REPLACE);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1101,9 +1004,12 @@ OperationResult Transaction::replaceCoordinator(std::string const& collectionNam
|
|||
/// if it fails, clean up after itself
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
OperationResult Transaction::replaceLocal(std::string const& collectionName,
|
||||
VPackSlice const newValue,
|
||||
OperationOptions& options) {
|
||||
OperationResult Transaction::modifyLocal(
|
||||
std::string const& collectionName,
|
||||
VPackSlice const newValue,
|
||||
OperationOptions& options,
|
||||
TRI_voc_document_operation_e operation) {
|
||||
|
||||
TRI_voc_cid_t cid = resolver()->getCollectionIdLocal(collectionName);
|
||||
|
||||
if (cid == 0) {
|
||||
|
@ -1113,73 +1019,31 @@ OperationResult Transaction::replaceLocal(std::string const& collectionName,
|
|||
// TODO: clean this up
|
||||
TRI_document_collection_t* document = documentCollection(trxCollection(cid));
|
||||
|
||||
// Replace is a read and a write, let's get the write lock already
|
||||
// Update/replace are a read and a write, let's get the write lock already
|
||||
// for the read operation:
|
||||
int res = lock(trxCollection(cid), TRI_TRANSACTION_WRITE);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return OperationResult(res);
|
||||
}
|
||||
|
||||
VPackBuilder builder; // used repeatedly in closure
|
||||
VPackBuilder resultBuilder; // building the complete result
|
||||
|
||||
auto workForOneDocument = [&](VPackSlice const newVal) -> int {
|
||||
|
||||
// read key and expected revision
|
||||
std::string const key(Transaction::extractKey(newVal));
|
||||
TRI_voc_rid_t const expectedRevision
|
||||
= options.ignoreRevs ? 0 : Transaction::extractRevisionId(newVal);
|
||||
|
||||
// generate a new tick value
|
||||
TRI_voc_tick_t const revisionId = TRI_NewTickServer();
|
||||
|
||||
builder.clear();
|
||||
{
|
||||
VPackObjectBuilder b(&builder);
|
||||
VPackObjectIterator it(newVal);
|
||||
while (it.valid()) {
|
||||
// let all but the system attributes pass
|
||||
std::string k = it.key().copyString();
|
||||
if (k[0] != '_' ||
|
||||
(k != TRI_VOC_ATTRIBUTE_KEY &&
|
||||
k != TRI_VOC_ATTRIBUTE_ID &&
|
||||
k != TRI_VOC_ATTRIBUTE_REV &&
|
||||
k != TRI_VOC_ATTRIBUTE_FROM &&
|
||||
k != TRI_VOC_ATTRIBUTE_TO)) {
|
||||
builder.add(k, it.value());
|
||||
}
|
||||
it.next();
|
||||
}
|
||||
// finally add the (new) _rev attributes
|
||||
builder.add(TRI_VOC_ATTRIBUTE_REV, VPackValue(std::to_string(revisionId)));
|
||||
}
|
||||
|
||||
VPackSlice sanitized = builder.slice();
|
||||
|
||||
TRI_doc_mptr_t mptr;
|
||||
TRI_voc_rid_t actualRevision = 0;
|
||||
TRI_doc_update_policy_t policy(expectedRevision == 0 ?
|
||||
TRI_DOC_UPDATE_LAST_WRITE :
|
||||
TRI_DOC_UPDATE_ERROR,
|
||||
expectedRevision, &actualRevision);
|
||||
|
||||
VPackBuilder oldBuilder;
|
||||
{ VPackObjectBuilder guard(&oldBuilder);
|
||||
oldBuilder.add(TRI_VOC_ATTRIBUTE_KEY, VPackValue(key));
|
||||
if (expectedRevision != 0) {
|
||||
oldBuilder.add(TRI_VOC_ATTRIBUTE_REV,
|
||||
VPackValue(std::to_string(expectedRevision)));
|
||||
}
|
||||
if (operation == TRI_VOC_DOCUMENT_OPERATION_REPLACE) {
|
||||
res = document->replace(this, newVal, &mptr, options,
|
||||
!isLocked(document, TRI_TRANSACTION_WRITE), actualRevision);
|
||||
} else {
|
||||
res = document->update(this, newVal, &mptr, options,
|
||||
!isLocked(document, TRI_TRANSACTION_WRITE), actualRevision);
|
||||
}
|
||||
VPackSlice oldVal = oldBuilder.slice();
|
||||
|
||||
res = document->replace(this, &oldVal, &sanitized, &mptr, &policy,
|
||||
options, !isLocked(document, TRI_TRANSACTION_WRITE));
|
||||
|
||||
std::string key = newVal.get(TRI_VOC_ATTRIBUTE_KEY).copyString();
|
||||
if (res == TRI_ERROR_ARANGO_CONFLICT) {
|
||||
// still return
|
||||
buildDocumentIdentity(resultBuilder, cid, key, mptr.revisionId(), "");
|
||||
buildDocumentIdentity(resultBuilder, cid, key, actualRevision, "");
|
||||
return TRI_ERROR_ARANGO_CONFLICT;
|
||||
} else if (res != TRI_ERROR_NO_ERROR) {
|
||||
return res;
|
||||
|
@ -1188,7 +1052,8 @@ OperationResult Transaction::replaceLocal(std::string const& collectionName,
|
|||
TRI_ASSERT(mptr.getDataPtr() != nullptr);
|
||||
|
||||
if (!options.silent) {
|
||||
buildDocumentIdentity(resultBuilder, cid, key, std::to_string(revisionId), std::to_string(actualRevision));
|
||||
buildDocumentIdentity(resultBuilder, cid, key,
|
||||
std::to_string(mptr.revisionId()), std::to_string(actualRevision));
|
||||
}
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
};
|
||||
|
|
|
@ -699,17 +699,14 @@ class Transaction {
|
|||
VPackSlice const newValue,
|
||||
OperationOptions& options);
|
||||
|
||||
OperationResult updateLocal(std::string const& collectionName,
|
||||
VPackSlice const newValue,
|
||||
OperationOptions& options);
|
||||
|
||||
OperationResult replaceCoordinator(std::string const& collectionName,
|
||||
VPackSlice const newValue,
|
||||
OperationOptions& options);
|
||||
|
||||
OperationResult replaceLocal(std::string const& collectionName,
|
||||
VPackSlice const newValue,
|
||||
OperationOptions& options);
|
||||
OperationResult modifyLocal(std::string const& collectionName,
|
||||
VPackSlice const newValue,
|
||||
OperationOptions& options,
|
||||
TRI_voc_document_operation_e operation);
|
||||
|
||||
OperationResult removeCoordinator(std::string const& collectionName,
|
||||
VPackSlice const& value,
|
||||
|
|
|
@ -71,12 +71,6 @@ struct LocalCollectionGuard {
|
|||
TRI_vocbase_col_t* _collection;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief mode to distinguish replace and update, which share code
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum ModifyMode { REPLACE, UPDATE };
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief extract the forceSync flag from the arguments
|
||||
/// must specify the argument index starting from 1
|
||||
|
@ -1365,7 +1359,7 @@ static void parseReplaceAndUpdateOptions(
|
|||
v8::Isolate* isolate,
|
||||
v8::FunctionCallbackInfo<v8::Value> const& args,
|
||||
OperationOptions& options,
|
||||
ModifyMode mode) {
|
||||
TRI_voc_document_operation_e operation) {
|
||||
|
||||
TRI_GET_GLOBALS();
|
||||
TRI_ASSERT(args.Length() > 2);
|
||||
|
@ -1384,7 +1378,7 @@ static void parseReplaceAndUpdateOptions(
|
|||
if (optionsObject->Has(SilentKey)) {
|
||||
options.silent = TRI_ObjectToBoolean(optionsObject->Get(SilentKey));
|
||||
}
|
||||
if (mode == UPDATE) {
|
||||
if (operation == TRI_VOC_DOCUMENT_OPERATION_UPDATE) {
|
||||
TRI_GET_GLOBAL_STRING(KeepNullKey);
|
||||
if (optionsObject->Has(KeepNullKey)) {
|
||||
options.keepNull = TRI_ObjectToBoolean(optionsObject->Get(KeepNullKey));
|
||||
|
@ -1402,7 +1396,7 @@ static void parseReplaceAndUpdateOptions(
|
|||
// update(<document>, <data>, <overwrite>, <keepNull>, <waitForSync>
|
||||
options.ignoreRevs = TRI_ObjectToBoolean(args[2]);
|
||||
if (args.Length() > 3) {
|
||||
if (mode == REPLACE) {
|
||||
if (operation == TRI_VOC_DOCUMENT_OPERATION_REPLACE) {
|
||||
options.waitForSync = TRI_ObjectToBoolean(args[3]);
|
||||
} else { // UPDATE
|
||||
options.keepNull = TRI_ObjectToBoolean(args[3]);
|
||||
|
@ -1418,7 +1412,7 @@ static void parseReplaceAndUpdateOptions(
|
|||
/// @brief ModifyVocbaseCol
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void ModifyVocbaseCol(ModifyMode mode,
|
||||
static void ModifyVocbaseCol(TRI_voc_document_operation_e operation,
|
||||
v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
|
@ -1426,8 +1420,9 @@ static void ModifyVocbaseCol(ModifyMode mode,
|
|||
// check the arguments
|
||||
uint32_t const argLength = args.Length();
|
||||
|
||||
if (argLength < 2 || argLength > (mode == REPLACE) ? 3 : 5) {
|
||||
if (mode == REPLACE) {
|
||||
if (argLength < 2 ||
|
||||
argLength > (operation == TRI_VOC_DOCUMENT_OPERATION_REPLACE) ? 3 : 5) {
|
||||
if (operation == TRI_VOC_DOCUMENT_OPERATION_REPLACE) {
|
||||
TRI_V8_THROW_EXCEPTION_USAGE(
|
||||
"replace(<document(s)>, <data>, {overwrite: booleanValue,"
|
||||
" waitForSync: booleanValue})");
|
||||
|
@ -1456,7 +1451,7 @@ static void ModifyVocbaseCol(ModifyMode mode,
|
|||
|
||||
OperationOptions options;
|
||||
options.ignoreRevs = false;
|
||||
parseReplaceAndUpdateOptions(isolate, args, options, mode);
|
||||
parseReplaceAndUpdateOptions(isolate, args, options, operation);
|
||||
|
||||
// Find collection and vocbase
|
||||
std::string collectionName;
|
||||
|
@ -1523,7 +1518,7 @@ static void ModifyVocbaseCol(ModifyMode mode,
|
|||
VPackSlice const update = updateBuilder.slice();
|
||||
|
||||
OperationResult opResult;
|
||||
if (mode == REPLACE) {
|
||||
if (operation == TRI_VOC_DOCUMENT_OPERATION_REPLACE) {
|
||||
opResult = trx.replace(collectionName, update, options);
|
||||
} else {
|
||||
opResult = trx.update(collectionName, update, options);
|
||||
|
@ -1556,7 +1551,7 @@ static void ModifyVocbaseCol(ModifyMode mode,
|
|||
static void JS_ReplaceVocbaseCol(
|
||||
v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||
ModifyVocbaseCol(REPLACE, args);
|
||||
ModifyVocbaseCol(TRI_VOC_DOCUMENT_OPERATION_REPLACE, args);
|
||||
TRI_V8_TRY_CATCH_END
|
||||
}
|
||||
|
||||
|
@ -1567,7 +1562,7 @@ static void JS_ReplaceVocbaseCol(
|
|||
static void JS_UpdateVocbaseCol(
|
||||
v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||
return ModifyVocbaseCol(UPDATE, args);
|
||||
ModifyVocbaseCol(TRI_VOC_DOCUMENT_OPERATION_UPDATE, args);
|
||||
TRI_V8_TRY_CATCH_END
|
||||
}
|
||||
|
||||
|
@ -1575,7 +1570,7 @@ static void JS_UpdateVocbaseCol(
|
|||
/// @brief ModifyVocbase, database method, only single documents
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void ModifyVocbase(ModifyMode mode,
|
||||
static void ModifyVocbase(TRI_voc_document_operation_e operation,
|
||||
v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
|
@ -1583,8 +1578,9 @@ static void ModifyVocbase(ModifyMode mode,
|
|||
// check the arguments
|
||||
uint32_t const argLength = args.Length();
|
||||
|
||||
if (argLength < 2 || argLength > (mode == REPLACE) ? 3 : 5) {
|
||||
if (mode == REPLACE) {
|
||||
if (argLength < 2 ||
|
||||
argLength > (operation == TRI_VOC_DOCUMENT_OPERATION_REPLACE) ? 3 : 5) {
|
||||
if (operation == TRI_VOC_DOCUMENT_OPERATION_REPLACE) {
|
||||
TRI_V8_THROW_EXCEPTION_USAGE(
|
||||
"_replace(<document>, <data>, {overwrite: booleanValue, waitForSync: "
|
||||
"booleanValue})");
|
||||
|
@ -1604,7 +1600,7 @@ static void ModifyVocbase(ModifyMode mode,
|
|||
OperationOptions options;
|
||||
options.ignoreRevs = false;
|
||||
if (args.Length() > 2) {
|
||||
parseReplaceAndUpdateOptions(isolate, args, options, mode);
|
||||
parseReplaceAndUpdateOptions(isolate, args, options, operation);
|
||||
}
|
||||
|
||||
TRI_vocbase_col_t const* col = nullptr;
|
||||
|
@ -1645,7 +1641,7 @@ static void ModifyVocbase(ModifyMode mode,
|
|||
VPackSlice const update = updateBuilder.slice();
|
||||
|
||||
OperationResult opResult;
|
||||
if (mode == REPLACE) {
|
||||
if (operation == TRI_VOC_DOCUMENT_OPERATION_REPLACE) {
|
||||
opResult = trx.replace(collectionName, update, options);
|
||||
} else {
|
||||
opResult = trx.update(collectionName, update, options);
|
||||
|
@ -1677,7 +1673,7 @@ static void ModifyVocbase(ModifyMode mode,
|
|||
|
||||
static void JS_ReplaceVocbase(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||
ModifyVocbase(REPLACE, args);
|
||||
ModifyVocbase(TRI_VOC_DOCUMENT_OPERATION_REPLACE, args);
|
||||
TRI_V8_TRY_CATCH_END
|
||||
}
|
||||
|
||||
|
@ -1687,7 +1683,7 @@ static void JS_ReplaceVocbase(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|||
|
||||
static void JS_UpdateVocbase(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||
ModifyVocbase(UPDATE, args);
|
||||
ModifyVocbase(TRI_VOC_DOCUMENT_OPERATION_UPDATE, args);
|
||||
TRI_V8_TRY_CATCH_END
|
||||
}
|
||||
|
||||
|
|
|
@ -3889,17 +3889,17 @@ int TRI_document_collection_t::insert(Transaction* trx, VPackSlice const* slice,
|
|||
/// @brief updates a document or edge in a collection
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int TRI_document_collection_t::update(Transaction* trx, VPackSlice const* slice,
|
||||
VPackSlice const* newSlice,
|
||||
int TRI_document_collection_t::update(Transaction* trx,
|
||||
VPackSlice const newSlice,
|
||||
TRI_doc_mptr_t* mptr,
|
||||
TRI_doc_update_policy_t const* policy,
|
||||
OperationOptions& options,
|
||||
bool lock) {
|
||||
bool lock,
|
||||
TRI_voc_rid_t& prevRev) {
|
||||
// initialize the result
|
||||
TRI_ASSERT(mptr != nullptr);
|
||||
mptr->setDataPtr(nullptr);
|
||||
|
||||
TRI_voc_rid_t revisionId = Transaction::extractRevisionId(*newSlice);
|
||||
TRI_voc_rid_t revisionId = TRI_NewTickServer();
|
||||
|
||||
TRI_voc_tick_t markerTick = 0;
|
||||
int res;
|
||||
|
@ -3910,8 +3910,9 @@ int TRI_document_collection_t::update(Transaction* trx, VPackSlice const* slice,
|
|||
|
||||
// get the header pointer of the previous revision
|
||||
TRI_doc_mptr_t* oldHeader;
|
||||
res = lookupDocument(trx, slice, policy, oldHeader);
|
||||
|
||||
VPackSlice key = newSlice.get(TRI_VOC_ATTRIBUTE_KEY);
|
||||
TRI_ASSERT(!key.isNone());
|
||||
res = lookupDocument(trx, key, oldHeader);
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return res;
|
||||
}
|
||||
|
@ -3926,8 +3927,21 @@ int TRI_document_collection_t::update(Transaction* trx, VPackSlice const* slice,
|
|||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||
}
|
||||
|
||||
prevRev = oldHeader->revisionId();
|
||||
|
||||
// Check old revision:
|
||||
if (!options.ignoreRevs) {
|
||||
VPackSlice expectedRevSlice = newSlice.get(TRI_VOC_ATTRIBUTE_REV);
|
||||
int res = checkRevision(trx, expectedRevSlice, prevRev);
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// merge old and new values
|
||||
VPackBuilder builder = mergeObjects(trx, false, VPackSlice(oldHeader->vpack()), *newSlice, options.mergeObjects, !options.keepNull);
|
||||
VPackBuilder builder = mergeObjectsForUpdate(
|
||||
trx, VPackSlice(oldHeader->vpack()), newSlice,
|
||||
std::to_string(revisionId), options.mergeObjects, options.keepNull);
|
||||
|
||||
// create marker
|
||||
std::unique_ptr<arangodb::wal::Marker> marker;
|
||||
|
@ -3935,7 +3949,9 @@ int TRI_document_collection_t::update(Transaction* trx, VPackSlice const* slice,
|
|||
marker.reset(createVPackInsertMarker(trx, builder.slice()));
|
||||
}
|
||||
|
||||
auto actualMarker = (options.recoveryMarker == nullptr ? marker.get() : options.recoveryMarker);
|
||||
auto actualMarker = (options.recoveryMarker == nullptr
|
||||
? marker.get()
|
||||
: options.recoveryMarker);
|
||||
bool const freeMarker = (options.recoveryMarker == nullptr);
|
||||
|
||||
arangodb::wal::DocumentOperation operation(
|
||||
|
@ -3975,17 +3991,17 @@ int TRI_document_collection_t::update(Transaction* trx, VPackSlice const* slice,
|
|||
/// @brief replaces a document or edge in a collection
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int TRI_document_collection_t::replace(Transaction* trx, VPackSlice const* slice,
|
||||
VPackSlice const* newSlice,
|
||||
int TRI_document_collection_t::replace(Transaction* trx,
|
||||
VPackSlice const newSlice,
|
||||
TRI_doc_mptr_t* mptr,
|
||||
TRI_doc_update_policy_t const* policy,
|
||||
OperationOptions& options,
|
||||
bool lock) {
|
||||
bool lock,
|
||||
TRI_voc_rid_t& prevRev) {
|
||||
// initialize the result
|
||||
TRI_ASSERT(mptr != nullptr);
|
||||
mptr->setDataPtr(nullptr);
|
||||
|
||||
TRI_voc_rid_t revisionId = Transaction::extractRevisionId(*newSlice);
|
||||
TRI_voc_rid_t revisionId = TRI_NewTickServer();
|
||||
|
||||
TRI_voc_tick_t markerTick = 0;
|
||||
int res;
|
||||
|
@ -3996,8 +4012,9 @@ int TRI_document_collection_t::replace(Transaction* trx, VPackSlice const* slice
|
|||
|
||||
// get the header pointer of the previous revision
|
||||
TRI_doc_mptr_t* oldHeader;
|
||||
res = lookupDocument(trx, slice, policy, oldHeader);
|
||||
|
||||
VPackSlice key = newSlice.get(TRI_VOC_ATTRIBUTE_KEY);
|
||||
TRI_ASSERT(!key.isNone());
|
||||
res = lookupDocument(trx, key, oldHeader);
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return res;
|
||||
}
|
||||
|
@ -4012,8 +4029,21 @@ int TRI_document_collection_t::replace(Transaction* trx, VPackSlice const* slice
|
|||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||
}
|
||||
|
||||
prevRev = oldHeader->revisionId();
|
||||
|
||||
// Check old revision:
|
||||
if (!options.ignoreRevs) {
|
||||
VPackSlice expectedRevSlice = newSlice.get(TRI_VOC_ATTRIBUTE_REV);
|
||||
int res = checkRevision(trx, expectedRevSlice, prevRev);
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// merge old and new values
|
||||
VPackBuilder builder = mergeObjects(trx, true, VPackSlice(oldHeader->vpack()), *newSlice, false, true);
|
||||
VPackBuilder builder = newObjectForReplace(
|
||||
trx, VPackSlice(oldHeader->vpack()),
|
||||
newSlice, std::to_string(revisionId));
|
||||
|
||||
// create marker
|
||||
std::unique_ptr<arangodb::wal::Marker> marker;
|
||||
|
@ -4025,7 +4055,7 @@ int TRI_document_collection_t::replace(Transaction* trx, VPackSlice const* slice
|
|||
bool const freeMarker = (options.recoveryMarker == nullptr);
|
||||
|
||||
arangodb::wal::DocumentOperation operation(
|
||||
trx, actualMarker, freeMarker, this, TRI_VOC_DOCUMENT_OPERATION_UPDATE);
|
||||
trx, actualMarker, freeMarker, this, TRI_VOC_DOCUMENT_OPERATION_REPLACE);
|
||||
|
||||
marker.release();
|
||||
|
||||
|
@ -4175,7 +4205,8 @@ int TRI_document_collection_t::rollbackOperation(arangodb::Transaction* trx,
|
|||
_numberDocuments--;
|
||||
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
} else if (type == TRI_VOC_DOCUMENT_OPERATION_UPDATE) {
|
||||
} else if (type == TRI_VOC_DOCUMENT_OPERATION_UPDATE ||
|
||||
type == TRI_VOC_DOCUMENT_OPERATION_REPLACE) {
|
||||
// copy the existing header's state
|
||||
TRI_doc_mptr_t copy = *header;
|
||||
|
||||
|
@ -4231,6 +4262,7 @@ arangodb::wal::Marker* TRI_document_collection_t::createVPackRemoveMarker(
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief looks up a document by key, low level worker
|
||||
/// the caller must make sure the read lock on the collection is held
|
||||
/// the slice contains _key and possibly _rev
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int TRI_document_collection_t::lookupDocument(
|
||||
|
@ -4262,6 +4294,54 @@ int TRI_document_collection_t::lookupDocument(
|
|||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief looks up a document by key, low level worker
|
||||
/// the caller must make sure the read lock on the collection is held
|
||||
/// the key must be a string slice, no revision check is performed
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int TRI_document_collection_t::lookupDocument(
|
||||
arangodb::Transaction* trx, VPackSlice const key,
|
||||
TRI_doc_mptr_t*& header) {
|
||||
|
||||
if (!key.isString()) {
|
||||
return TRI_ERROR_INTERNAL;
|
||||
}
|
||||
VPackBuilder searchValue;
|
||||
searchValue.openArray();
|
||||
searchValue.openObject();
|
||||
searchValue.add(TRI_SLICE_KEY_EQUAL, key);
|
||||
searchValue.close();
|
||||
searchValue.close();
|
||||
|
||||
header = primaryIndex()->lookup(trx, searchValue.slice());
|
||||
|
||||
if (header == nullptr) {
|
||||
return TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND;
|
||||
}
|
||||
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief checks the revision of a document
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int TRI_document_collection_t::checkRevision(Transaction* trx,
|
||||
VPackSlice const expected,
|
||||
TRI_voc_rid_t found) {
|
||||
TRI_voc_rid_t expectedRev = 0;
|
||||
if (expected.isString()) {
|
||||
expectedRev = arangodb::basics::VelocyPackHelper::stringUInt64(expected);
|
||||
} else if (expected.isNumber()) {
|
||||
expectedRev = expected.getNumber<TRI_voc_rid_t>();
|
||||
}
|
||||
if (expectedRev != 0 && found != expectedRev) {
|
||||
return TRI_ERROR_ARANGO_CONFLICT;
|
||||
}
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief updates an existing document, low level worker
|
||||
/// the caller must make sure the write lock on the collection is held
|
||||
|
@ -4491,18 +4571,18 @@ int TRI_document_collection_t::deleteSecondaryIndexes(
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief merge two object slices
|
||||
/// @brief new object for replace, oldValue must have _key and _id correctly
|
||||
/// set.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
VPackBuilder TRI_document_collection_t::mergeObjects(arangodb::Transaction* trx,
|
||||
bool isReplace,
|
||||
VPackSlice const& oldValue,
|
||||
VPackSlice const& newValue,
|
||||
bool mergeObjects, bool keepNull) {
|
||||
if (isReplace) {
|
||||
// replace
|
||||
VPackBuilder builder;
|
||||
builder.openObject();
|
||||
VPackBuilder TRI_document_collection_t::newObjectForReplace(
|
||||
Transaction* trx,
|
||||
VPackSlice const oldValue,
|
||||
VPackSlice const newValue,
|
||||
std::string const& rev) {
|
||||
// replace
|
||||
VPackBuilder builder;
|
||||
{ VPackObjectBuilder guard(&builder);
|
||||
|
||||
VPackObjectIterator it(newValue);
|
||||
while (it.valid()) {
|
||||
|
@ -4510,33 +4590,101 @@ VPackBuilder TRI_document_collection_t::mergeObjects(arangodb::Transaction* trx,
|
|||
if (key[0] != '_' ||
|
||||
(key != TRI_VOC_ATTRIBUTE_ID &&
|
||||
key != TRI_VOC_ATTRIBUTE_KEY &&
|
||||
key != TRI_VOC_ATTRIBUTE_REV &&
|
||||
key != TRI_VOC_ATTRIBUTE_FROM &&
|
||||
key != TRI_VOC_ATTRIBUTE_TO)) {
|
||||
key != TRI_VOC_ATTRIBUTE_REV)) {
|
||||
builder.add(key, it.value());
|
||||
}
|
||||
it.next();
|
||||
}
|
||||
VPackSlice s = oldValue.get(TRI_VOC_ATTRIBUTE_ID);
|
||||
TRI_ASSERT(!s.isNone());
|
||||
builder.add(TRI_VOC_ATTRIBUTE_ID, s);
|
||||
s = oldValue.get(TRI_VOC_ATTRIBUTE_KEY);
|
||||
TRI_ASSERT(!s.isNone());
|
||||
builder.add(TRI_VOC_ATTRIBUTE_KEY, s);
|
||||
builder.add(TRI_VOC_ATTRIBUTE_REV, VPackValue(rev));
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
VPackObjectIterator it2(oldValue);
|
||||
while (it2.valid()) {
|
||||
std::string key(it2.key().copyString());
|
||||
if (key[0] == '_' &&
|
||||
(key == TRI_VOC_ATTRIBUTE_ID ||
|
||||
key == TRI_VOC_ATTRIBUTE_KEY ||
|
||||
key == TRI_VOC_ATTRIBUTE_REV ||
|
||||
key == TRI_VOC_ATTRIBUTE_FROM ||
|
||||
key == TRI_VOC_ATTRIBUTE_TO)) {
|
||||
builder.add(key, it2.value());
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief merge two objects for update, oldValue must have correctly set
|
||||
/// _key and _id attributes
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
VPackBuilder TRI_document_collection_t::mergeObjectsForUpdate(
|
||||
arangodb::Transaction* trx,
|
||||
VPackSlice const oldValue,
|
||||
VPackSlice const newValue,
|
||||
std::string const& rev,
|
||||
bool mergeObjects, bool keepNull) {
|
||||
|
||||
VPackBuilder b;
|
||||
{ VPackObjectBuilder guard(&b);
|
||||
|
||||
// Find the attributes in the newValue object:
|
||||
std::unordered_map<std::string, VPackSlice> newValues;
|
||||
{ VPackObjectIterator it(newValue);
|
||||
while (it.valid()) {
|
||||
std::string key = it.key().copyString();
|
||||
if (key != TRI_VOC_ATTRIBUTE_KEY &&
|
||||
key != TRI_VOC_ATTRIBUTE_ID &&
|
||||
key != TRI_VOC_ATTRIBUTE_REV) {
|
||||
newValues.emplace(it.key().copyString(), it.value());
|
||||
it.next();
|
||||
}
|
||||
}
|
||||
it2.next();
|
||||
}
|
||||
|
||||
builder.close();
|
||||
return builder;
|
||||
}
|
||||
{ VPackObjectIterator it(oldValue);
|
||||
while (it.valid()) {
|
||||
auto key = it.key().copyString();
|
||||
if (key == TRI_VOC_ATTRIBUTE_REV) {
|
||||
it.next();
|
||||
continue;
|
||||
}
|
||||
auto found = newValues.find(key);
|
||||
|
||||
// update
|
||||
return VPackCollection::merge(oldValue, newValue, mergeObjects, keepNull);
|
||||
if (found == newValues.end()) {
|
||||
// use old value
|
||||
b.add(key, it.value());
|
||||
} else if (mergeObjects && it.value().isObject() &&
|
||||
(*found).second.isObject()) {
|
||||
// merge both values
|
||||
auto& value = (*found).second;
|
||||
if (keepNull || (!value.isNone() && !value.isNull())) {
|
||||
VPackBuilder sub = VPackCollection::merge(it.value(), value,
|
||||
true, !keepNull);
|
||||
b.add(key, sub.slice());
|
||||
}
|
||||
// clear the value in the map so its not added again
|
||||
(*found).second = VPackSlice();
|
||||
} else {
|
||||
// use new value
|
||||
auto& value = (*found).second;
|
||||
if (keepNull || (!value.isNone() && !value.isNull())) {
|
||||
b.add(key, value);
|
||||
}
|
||||
// clear the value in the map so its not added again
|
||||
(*found).second = VPackSlice();
|
||||
}
|
||||
it.next();
|
||||
}
|
||||
}
|
||||
|
||||
// add remaining values that were only in new object
|
||||
for (auto& it : newValues) {
|
||||
auto& s = it.second;
|
||||
if (s.isNone()) {
|
||||
continue;
|
||||
}
|
||||
if (!keepNull && s.isNull()) {
|
||||
continue;
|
||||
}
|
||||
b.add(std::move(it.first), s);
|
||||
}
|
||||
|
||||
// Finally, add the new revision:
|
||||
b.add(TRI_VOC_ATTRIBUTE_REV, VPackValue(rev));
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
|
|
|
@ -308,12 +308,12 @@ struct TRI_document_collection_t : public TRI_collection_t {
|
|||
TRI_doc_mptr_t*, bool);
|
||||
int insert(arangodb::Transaction*, arangodb::velocypack::Slice const*,
|
||||
TRI_doc_mptr_t*, arangodb::OperationOptions&, bool);
|
||||
int update(arangodb::Transaction*, arangodb::velocypack::Slice const*,
|
||||
arangodb::velocypack::Slice const*, TRI_doc_mptr_t*,
|
||||
TRI_doc_update_policy_t const*, arangodb::OperationOptions&, bool);
|
||||
int replace(arangodb::Transaction*, arangodb::velocypack::Slice const*,
|
||||
arangodb::velocypack::Slice const*, TRI_doc_mptr_t*,
|
||||
TRI_doc_update_policy_t const*, arangodb::OperationOptions&, bool);
|
||||
int update(arangodb::Transaction*, arangodb::velocypack::Slice const,
|
||||
TRI_doc_mptr_t*, arangodb::OperationOptions&, bool,
|
||||
TRI_voc_rid_t&);
|
||||
int replace(arangodb::Transaction*, arangodb::velocypack::Slice const,
|
||||
TRI_doc_mptr_t*, arangodb::OperationOptions&, bool,
|
||||
TRI_voc_rid_t&);
|
||||
int remove(arangodb::Transaction*, arangodb::velocypack::Slice const*,
|
||||
TRI_doc_update_policy_t const*, arangodb::OperationOptions&, bool);
|
||||
|
||||
|
@ -329,6 +329,10 @@ struct TRI_document_collection_t : public TRI_collection_t {
|
|||
arangodb::Transaction*, arangodb::velocypack::Slice const*);
|
||||
int lookupDocument(arangodb::Transaction*, arangodb::velocypack::Slice const*,
|
||||
TRI_doc_update_policy_t const*, TRI_doc_mptr_t*&);
|
||||
int lookupDocument(arangodb::Transaction*, arangodb::velocypack::Slice const,
|
||||
TRI_doc_mptr_t*&);
|
||||
int checkRevision(arangodb::Transaction*, arangodb::velocypack::Slice const,
|
||||
TRI_voc_rid_t);
|
||||
int updateDocument(arangodb::Transaction*, TRI_voc_rid_t, TRI_doc_mptr_t*,
|
||||
arangodb::wal::DocumentOperation&,
|
||||
TRI_doc_mptr_t*, bool&);
|
||||
|
@ -345,14 +349,25 @@ struct TRI_document_collection_t : public TRI_collection_t {
|
|||
bool);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief merge two object slices
|
||||
/// @brief new object for Replace
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
arangodb::velocypack::Builder mergeObjects(arangodb::Transaction* trx,
|
||||
bool isReplace,
|
||||
arangodb::velocypack::Slice const& oldValue,
|
||||
arangodb::velocypack::Slice const& newValue,
|
||||
bool mergeObjects, bool keepNull);
|
||||
arangodb::velocypack::Builder newObjectForReplace(
|
||||
arangodb::Transaction* trx,
|
||||
arangodb::velocypack::Slice const oldValue,
|
||||
arangodb::velocypack::Slice const newValue,
|
||||
std::string const& rev);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief merge two objects for update
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
arangodb::velocypack::Builder mergeObjectsForUpdate(
|
||||
arangodb::Transaction* trx,
|
||||
arangodb::velocypack::Slice const oldValue,
|
||||
arangodb::velocypack::Slice const newValue,
|
||||
std::string const& rev,
|
||||
bool mergeObjects, bool keepNull);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -122,6 +122,7 @@ enum TRI_voc_document_operation_e : uint8_t {
|
|||
TRI_VOC_DOCUMENT_OPERATION_UNKNOWN = 0,
|
||||
TRI_VOC_DOCUMENT_OPERATION_INSERT,
|
||||
TRI_VOC_DOCUMENT_OPERATION_UPDATE,
|
||||
TRI_VOC_DOCUMENT_OPERATION_REPLACE,
|
||||
TRI_VOC_DOCUMENT_OPERATION_REMOVE
|
||||
};
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ struct DocumentOperation {
|
|||
|
||||
void init() {
|
||||
if (type == TRI_VOC_DOCUMENT_OPERATION_UPDATE ||
|
||||
type == TRI_VOC_DOCUMENT_OPERATION_REPLACE ||
|
||||
type == TRI_VOC_DOCUMENT_OPERATION_REMOVE) {
|
||||
// copy the old header into a safe area
|
||||
TRI_ASSERT(header != nullptr);
|
||||
|
@ -86,7 +87,8 @@ struct DocumentOperation {
|
|||
TRI_ASSERT(header != nullptr);
|
||||
TRI_ASSERT(status == StatusType::INDEXED);
|
||||
|
||||
if (type == TRI_VOC_DOCUMENT_OPERATION_UPDATE) {
|
||||
if (type == TRI_VOC_DOCUMENT_OPERATION_UPDATE ||
|
||||
type == TRI_VOC_DOCUMENT_OPERATION_REPLACE) {
|
||||
// move header to the end of the list
|
||||
document->_masterPointers.moveBack(header, &oldHeader); // PROTECTED by trx
|
||||
}
|
||||
|
@ -108,7 +110,8 @@ struct DocumentOperation {
|
|||
|
||||
if (type == TRI_VOC_DOCUMENT_OPERATION_INSERT) {
|
||||
document->_masterPointers.release(header, true); // PROTECTED by trx
|
||||
} else if (type == TRI_VOC_DOCUMENT_OPERATION_UPDATE) {
|
||||
} else if (type == TRI_VOC_DOCUMENT_OPERATION_UPDATE ||
|
||||
type == TRI_VOC_DOCUMENT_OPERATION_REPLACE) {
|
||||
if (status != StatusType::CREATED && status != StatusType::INDEXED) {
|
||||
document->_masterPointers.move(header, &oldHeader); // PROTECTED by trx in trxCollection
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue