1
0
Fork 0

First go at babies variant of replace. RestHandler case missing.

This commit is contained in:
Max Neunhoeffer 2016-03-01 17:50:13 +01:00
parent 298c96f2cc
commit 2cfa417858
2 changed files with 169 additions and 97 deletions

View File

@ -990,9 +990,10 @@ OperationResult Transaction::replace(std::string const& collectionName,
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
}
if (oldValue.isArray() || newValue.isArray()) {
// multi-document variant is not yet implemented
THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED);
if ((!oldValue.isArray() ^ !newValue.isArray()) ||
(oldValue.isArray() && oldValue.length() != newValue.length())) {
// must provide two lists or two single documents
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
}
OperationOptions optionsCopy = options;
@ -1014,6 +1015,10 @@ OperationResult Transaction::replaceCoordinator(std::string const& collectionNam
VPackSlice const& oldValue,
VPackSlice const& newValue,
OperationOptions& options) {
if (oldValue.isArray()) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
}
auto headers = std::make_unique<std::map<std::string, std::string>>();
arangodb::rest::HttpResponse::HttpResponseCode responseCode;
std::map<std::string, std::string> resultHeaders;
@ -1078,77 +1083,100 @@ OperationResult Transaction::replaceLocal(std::string const& collectionName,
return OperationResult(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
}
// read key and expected revision
std::string const key(Transaction::extractKey(&oldValue));
TRI_voc_rid_t const expectedRevision = Transaction::extractRevisionId(&oldValue);
// 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 the (new) _rev attributes
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);
// Replace is 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);
}
res = document->replace(this, &oldValue, &sanitized, &mptr, &policy, options, !isLocked(document, TRI_TRANSACTION_WRITE));
VPackBuilder builder; // used repeatedly in closure
VPackBuilder resultBuilder; // building the complete result
if (res == TRI_ERROR_ARANGO_CONFLICT) {
// still return
VPackBuilder resultBuilder;
buildDocumentIdentity(resultBuilder, cid, key, mptr.revisionId(), "");
auto workForOneDocument = [&](VPackSlice const oldVal,
VPackSlice const newVal) -> int {
return OperationResult(resultBuilder.steal(), nullptr, "",
TRI_ERROR_ARANGO_CONFLICT,
options.waitForSync || document->_info.waitForSync());
} else if (res != TRI_ERROR_NO_ERROR) {
return OperationResult(res);
// read key and expected revision
std::string const key(Transaction::extractKey(&oldVal));
TRI_voc_rid_t const expectedRevision
= Transaction::extractRevisionId(&oldVal);
// generate a new tick value
TRI_voc_tick_t const revisionId = TRI_NewTickServer();
builder.clear();
{
VPackObjectBuilder b(&builder);
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 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);
res = document->replace(this, &oldValue, &sanitized, &mptr, &policy,
options, !isLocked(document, TRI_TRANSACTION_WRITE));
if (res == TRI_ERROR_ARANGO_CONFLICT) {
// still return
buildDocumentIdentity(resultBuilder, cid, key, mptr.revisionId(), "");
return TRI_ERROR_ARANGO_CONFLICT;
} else if (res != TRI_ERROR_NO_ERROR) {
return res;
}
TRI_ASSERT(mptr.getDataPtr() != nullptr);
if (!options.silent) {
buildDocumentIdentity(resultBuilder, cid, key, std::to_string(revisionId), std::to_string(actualRevision));
}
return TRI_ERROR_NO_ERROR;
};
res = TRI_ERROR_NO_ERROR;
if (oldValue.isArray()) {
// We already know that both oldValue and newValue are arrays and that
// they have equal length!
VPackArrayBuilder b(&resultBuilder);
VPackArrayIterator oIt(oldValue);
VPackArrayIterator nIt(newValue);
while (oIt.valid() && nIt.valid()) {
res = workForOneDocument(oIt.value(), nIt.value());
if (res != TRI_ERROR_NO_ERROR) {
break;
}
++oIt;
++nIt;
}
} else {
res = workForOneDocument(oldValue, newValue);
}
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,
return OperationResult(resultBuilder.steal(), nullptr, "", res,
options.waitForSync);
}

View File

@ -379,14 +379,28 @@ static void ReplaceVocbaseCol(bool useCollection,
if (argLength < 2) {
TRI_V8_THROW_EXCEPTION_USAGE(
"replace(<document>, <data>, {overwrite: booleanValue, waitForSync: "
"replace(<document(s)>, <data>, {overwrite: booleanValue, waitForSync: "
"booleanValue})");
}
// we're only accepting "real" object documents
if (!args[1]->IsObject() || args[1]->IsArray()) {
// we're only accepting "real" object documents or arrays of such, if this
// is the collection method
if (!args[1]->IsObject()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
}
if (args[1]->IsArray() && !useCollection) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
}
if (args[0]->IsArray() ^ args[1]->IsArray()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
}
if (args[0]->IsArray()) { // then both are arrays, check equal length
auto a = v8::Local<v8::Array>::Cast(args[0]);
auto b = v8::Local<v8::Array>::Cast(args[1]);
if (a->Length() != b->Length()) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
}
}
if (args.Length() > 2) {
if (args[2]->IsObject()) {
@ -435,54 +449,84 @@ static void ReplaceVocbaseCol(bool useCollection,
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
}
if (!args[1]->IsObject() || args[1]->IsArray()) {
// we're only accepting "real" object documents
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID);
}
VPackBuilder builder;
std::string collectionName;
auto transactionContext = std::make_shared<V8TransactionContext>(vocbase, true);
int res = ParseDocumentOrDocumentHandle(
isolate, vocbase, transactionContext->getResolver(), col, collectionName, builder,
!overwrite, args[0]);
VPackBuilder builder; // to build the search value
std::string collectionName;
if (!useCollection) { // the db._replace case
int res = ParseDocumentOrDocumentHandle(
isolate, vocbase, transactionContext->getResolver(), col,
collectionName, builder, !overwrite, args[0]);
if (res != TRI_ERROR_NO_ERROR) {
TRI_V8_THROW_EXCEPTION(res);
}
}
// In case of useCollection we already have a collection object and
// do not need to free it later.
LocalCollectionGuard g(useCollection ? nullptr
: const_cast<TRI_vocbase_col_t*>(col));
if (res != TRI_ERROR_NO_ERROR) {
TRI_V8_THROW_EXCEPTION(res);
TRI_ASSERT(col != nullptr);
TRI_ASSERT(!collectionName.empty());
// In either case we now have a collection object col
SingleCollectionTransaction trx(transactionContext, collectionName,
TRI_TRANSACTION_WRITE);
if (!useCollection || !args[0]->IsArray()) {
trx.addHint(TRI_TRANSACTION_HINT_SINGLE_OPERATION, false);
}
TRI_ASSERT(col != nullptr);
TRI_ASSERT(!collectionName.empty());
VPackSlice search = builder.slice();
TRI_ASSERT(search.isObject());
SingleCollectionTransaction trx(transactionContext, collectionName, TRI_TRANSACTION_WRITE);
trx.addHint(TRI_TRANSACTION_HINT_SINGLE_OPERATION, false);
res = trx.begin();
int res = trx.begin();
if (res != TRI_ERROR_NO_ERROR) {
TRI_V8_THROW_EXCEPTION(res);
}
VPackBuilder updateBuilder;
res = TRI_V8ToVPack(isolate, updateBuilder, args[1], false);
if (res != TRI_ERROR_NO_ERROR) {
TRI_V8_THROW_EXCEPTION(res);
auto workOnOneSearchVal = [&](v8::Local<v8::Value> const searchVal) {
std::string collName;
if (!ExtractDocumentHandle(isolate, searchVal, collName, builder, false)) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD);
}
};
auto workOnOneDocument = [&](v8::Local<v8::Value> const newVal) {
auto savePos = updateBuilder.buffer()->size();
int res = TRI_V8ToVPack(isolate, updateBuilder, newVal, false);
if (res != TRI_ERROR_NO_ERROR) {
TRI_V8_THROW_EXCEPTION(res);
}
if (savePos == updateBuilder.buffer()->size()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_errno(), "<data> is no valid JSON");
}
};
// In the !useCollection case the search value is already prepared, we
// only have to set up the single replacement document:
if (!useCollection) {
workOnOneDocument(args[1]);
} else if (args[0]->IsArray()) {
// we deal with the single document case:
workOnOneSearchVal(args[0]);
workOnOneDocument(args[1]);
} else { // finally, the array case, note that we already know that the two
// arrays have equal length!
VPackArrayBuilder b1(&builder);
VPackArrayBuilder b2(&updateBuilder);
auto searchVals = v8::Local<v8::Array>::Cast(args[0]);
auto documents = v8::Local<v8::Array>::Cast(args[1]);
for (uint32_t i = 0; i < searchVals->Length(); i++) {
workOnOneSearchVal(searchVals->Get(i));
workOnOneDocument(documents->Get(i));
}
}
VPackSlice update = updateBuilder.slice();
if (update.isNone()) {
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_errno(), "<data> is no valid JSON");
}
VPackSlice const search = builder.slice();
VPackSlice const update = updateBuilder.slice();
OperationResult opResult = trx.replace(collectionName, search, update, options);