diff --git a/arangod/RestHandler/RestVocbaseBaseHandler.cpp b/arangod/RestHandler/RestVocbaseBaseHandler.cpp index beb98a4732..1cbe3ae1a5 100644 --- a/arangod/RestHandler/RestVocbaseBaseHandler.cpp +++ b/arangod/RestHandler/RestVocbaseBaseHandler.cpp @@ -262,18 +262,13 @@ void RestVocbaseBaseHandler::generate20x( TRI_col_type_e type) { VPackSlice slice = result.slice(); - TRI_ASSERT(slice.isObject()); + TRI_ASSERT(slice.isObject() || slice.isArray()); _response->setContentType("application/json; charset=utf-8"); - _response->setHeader("etag", 4, "\"" + slice.get(TRI_VOC_ATTRIBUTE_REV).copyString() + "\""); - - std::string escapedHandle(DocumentHelper::assembleDocumentId( - collectionName, slice.get(TRI_VOC_ATTRIBUTE_KEY).copyString(), true)); - if (_request->compatibility() < 10400L) { - // pre-1.4 location header (e.g. /_api/document/xyz) - _response->setHeader("location", 8, - std::string(DOCUMENT_PATH + "/" + escapedHandle)); - } else { - // 1.4+ location header (e.g. /_db/_system/_api/document/xyz) + if (slice.isObject()) { + _response->setHeader("etag", 4, "\"" + slice.get(TRI_VOC_ATTRIBUTE_REV).copyString() + "\""); + // pre 1.4 location headers withdrawn for >= 3.0 + std::string escapedHandle(DocumentHelper::assembleDocumentId( + collectionName, slice.get(TRI_VOC_ATTRIBUTE_KEY).copyString(), true)); if (type == TRI_COL_TYPE_EDGE) { _response->setHeader("location", 8, std::string("/_db/" + _request->databaseName() + diff --git a/arangod/Utils/Transaction.cpp b/arangod/Utils/Transaction.cpp index 03ecb37e97..b1b7fd7426 100644 --- a/arangod/Utils/Transaction.cpp +++ b/arangod/Utils/Transaction.cpp @@ -737,40 +737,50 @@ OperationResult Transaction::insert(std::string const& collectionName, THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); } - if (value.isArray()) { - // multi-document variant is not yet implemented - THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED); - } - // Validate Edges if (isEdgeCollection(collectionName)) { // Check _from - size_t split; - VPackSlice from = value.get(TRI_VOC_ATTRIBUTE_FROM); - if (!from.isString()) { - THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE); - } - std::string docId = from.copyString(); - if (!TRI_ValidateDocumentIdKeyGenerator(docId.c_str(), &split)) { - THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE); - } - std::string cName = docId.substr(0, split); - if (TRI_COL_TYPE_UNKNOWN == resolver()->getCollectionType(cName)) { - THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND); - } + auto checkFrom = [&](VPackSlice const value) -> void { + size_t split; + VPackSlice from = value.get(TRI_VOC_ATTRIBUTE_FROM); + if (!from.isString()) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE); + } + std::string docId = from.copyString(); + if (!TRI_ValidateDocumentIdKeyGenerator(docId.c_str(), &split)) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE); + } + std::string cName = docId.substr(0, split); + if (TRI_COL_TYPE_UNKNOWN == resolver()->getCollectionType(cName)) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND); + } + }; // Check _to - VPackSlice to = value.get(TRI_VOC_ATTRIBUTE_TO); - if (!to.isString()) { - THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE); - } - docId = to.copyString(); - if (!TRI_ValidateDocumentIdKeyGenerator(docId.c_str(), &split)) { - THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE); - } - cName = docId.substr(0, split); - if (TRI_COL_TYPE_UNKNOWN == resolver()->getCollectionType(cName)) { - THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND); + auto checkTo = [&](VPackSlice const value) -> void { + size_t split; + VPackSlice to = value.get(TRI_VOC_ATTRIBUTE_TO); + if (!to.isString()) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE); + } + std::string docId = to.copyString(); + if (!TRI_ValidateDocumentIdKeyGenerator(docId.c_str(), &split)) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE); + } + std::string cName = docId.substr(0, split); + if (TRI_COL_TYPE_UNKNOWN == resolver()->getCollectionType(cName)) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND); + } + }; + + if (value.isArray()) { + for (auto s : VPackArrayIterator(value)) { + checkFrom(s); + checkTo(s); + } + } else { + checkFrom(value); + checkTo(value); } } @@ -792,6 +802,12 @@ OperationResult Transaction::insert(std::string const& collectionName, OperationResult Transaction::insertCoordinator(std::string const& collectionName, VPackSlice const& value, OperationOptions& options) { + + if (value.isArray()) { + // must provide a document object + THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); + } + std::map headers; arangodb::rest::HttpResponse::HttpResponseCode responseCode; std::map resultHeaders; @@ -847,70 +863,83 @@ OperationResult Transaction::insertLocal(std::string const& collectionName, return OperationResult(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND); } - // add missing attributes for document (_id, _rev, _key) - VPackBuilder merge; - merge.openObject(); - - // generate a new tick value - TRI_voc_tick_t const revisionId = TRI_NewTickServer(); - std::string keyString; - // TODO: clean this up TRI_document_collection_t* document = documentCollection(trxCollection(cid)); - auto key = value.get(TRI_VOC_ATTRIBUTE_KEY); - - if (key.isNone()) { - // "_key" attribute not present in object - keyString = document->_keyGenerator->generate(revisionId); - merge.add(TRI_VOC_ATTRIBUTE_KEY, VPackValue(keyString)); - } else if (!key.isString()) { - // "_key" present but wrong type - return OperationResult(TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD); - } else { - keyString = key.copyString(); - int res = document->_keyGenerator->validate(keyString, false); - - if (res != TRI_ERROR_NO_ERROR) { - // invalid key value - return OperationResult(res); - } - } - - // add _rev attribute - merge.add(TRI_VOC_ATTRIBUTE_REV, VPackValue(std::to_string(revisionId))); - - // add _id attribute - uint8_t* p = merge.add(TRI_VOC_ATTRIBUTE_ID, VPackValuePair(9ULL, VPackValueType::Custom)); - *p++ = 0xf3; // custom type for _id - MarkerHelper::storeNumber(p, cid, sizeof(uint64_t)); - - merge.close(); - - VPackBuilder toInsert = VPackCollection::merge(value, merge.slice(), false, false); - VPackSlice insertSlice = toInsert.slice(); - - if (orderDitch(trxCollection(cid)) == nullptr) { - return OperationResult(TRI_ERROR_OUT_OF_MEMORY); - } - - TRI_doc_mptr_t mptr; - int res = document->insert(this, &insertSlice, &mptr, options, !isLocked(document, TRI_TRANSACTION_WRITE)); - - if (res != TRI_ERROR_NO_ERROR) { - return OperationResult(res); - } - - if (options.silent) { - // no need to construct the result object - return OperationResult(TRI_ERROR_NO_ERROR); - } - - TRI_ASSERT(mptr.getDataPtr() != nullptr); - VPackBuilder resultBuilder; - buildDocumentIdentity(resultBuilder, cid, keyString, mptr._rid, ""); - return OperationResult(resultBuilder.steal(), nullptr, "", TRI_ERROR_NO_ERROR, + auto workForOneDocument = [&](VPackSlice const value) -> int { + // add missing attributes for document (_id, _rev, _key) + VPackBuilder merge; + merge.openObject(); + + // generate a new tick value + TRI_voc_tick_t const revisionId = TRI_NewTickServer(); + std::string keyString; + auto key = value.get(TRI_VOC_ATTRIBUTE_KEY); + + if (key.isNone()) { + // "_key" attribute not present in object + keyString = document->_keyGenerator->generate(revisionId); + merge.add(TRI_VOC_ATTRIBUTE_KEY, VPackValue(keyString)); + } else if (!key.isString()) { + // "_key" present but wrong type + return TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD; + } else { + keyString = key.copyString(); + int res = document->_keyGenerator->validate(keyString, false); + + if (res != TRI_ERROR_NO_ERROR) { + // invalid key value + return res; + } + } + + // add _rev attribute + merge.add(TRI_VOC_ATTRIBUTE_REV, VPackValue(std::to_string(revisionId))); + + // add _id attribute + uint8_t* p = merge.add(TRI_VOC_ATTRIBUTE_ID, VPackValuePair(9ULL, VPackValueType::Custom)); + *p++ = 0xf3; // custom type for _id + MarkerHelper::storeNumber(p, cid, sizeof(uint64_t)); + + merge.close(); + + VPackBuilder toInsert = VPackCollection::merge(value, merge.slice(), false, false); + VPackSlice insertSlice = toInsert.slice(); + + TRI_doc_mptr_t mptr; + int res = document->insert(this, &insertSlice, &mptr, options, + !isLocked(document, TRI_TRANSACTION_WRITE)); + + if (res != TRI_ERROR_NO_ERROR) { + return res; + } + + if (options.silent) { + // no need to construct the result object + return TRI_ERROR_NO_ERROR; + } + + TRI_ASSERT(mptr.getDataPtr() != nullptr); + + buildDocumentIdentity(resultBuilder, cid, keyString, mptr._rid, ""); + return TRI_ERROR_NO_ERROR; + }; + + int res; + if (value.isArray()) { + VPackArrayBuilder b(&resultBuilder); + for (auto const s : VPackArrayIterator(value)) { + res = workForOneDocument(s); + if (res != TRI_ERROR_NO_ERROR) { + break; + } + } + } else { + res = workForOneDocument(value); + } + + return OperationResult(resultBuilder.steal(), nullptr, "", res, options.waitForSync); } diff --git a/arangod/V8Server/v8-collection.cpp b/arangod/V8Server/v8-collection.cpp index 008171c4bb..78c8b10458 100644 --- a/arangod/V8Server/v8-collection.cpp +++ b/arangod/V8Server/v8-collection.cpp @@ -1886,7 +1886,7 @@ static void JS_InsertVocbaseVPack( options.waitForSync = ExtractWaitForSync(args, optsIdx + 1); } - if (!args[docIdx]->IsObject() || args[docIdx]->IsArray()) { + if (!args[docIdx]->IsObject()) { // invalid value type. must be a document TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); } @@ -1895,37 +1895,53 @@ static void JS_InsertVocbaseVPack( VPackOptions vpackOptions = VPackOptions::Defaults; vpackOptions.attributeExcludeHandler = basics::VelocyPackHelper::getExcludeHandler(); VPackBuilder builder(&vpackOptions); + v8::Handle payload = args[0]; - int res = TRI_V8ToVPack(isolate, builder, args[docIdx]->ToObject(), true); - - if (res != TRI_ERROR_NO_ERROR) { - TRI_V8_THROW_EXCEPTION(res); - } - - if (isEdgeCollection) { - // Just insert from and to. Check is done later. - std::string tmpId = ExtractIdString(isolate, args[0]); - if (tmpId.empty()) { - TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD); + auto doOneDocument = [&](v8::Handle obj) -> void { + int res = TRI_V8ToVPack(isolate, builder, obj, true); + + if (res != TRI_ERROR_NO_ERROR) { + TRI_V8_THROW_EXCEPTION(res); } - builder.add(TRI_VOC_ATTRIBUTE_FROM, VPackValue(tmpId)); - tmpId = ExtractIdString(isolate, args[1]); - if (tmpId.empty()) { - TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD); + if (isEdgeCollection) { + // Just insert from and to. Check is done later. + std::string tmpId = ExtractIdString(isolate, obj); + if (tmpId.empty()) { + TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD); + } + builder.add(TRI_VOC_ATTRIBUTE_FROM, VPackValue(tmpId)); + + tmpId = ExtractIdString(isolate, args[1]); + if (tmpId.empty()) { + TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD); + } + builder.add(TRI_VOC_ATTRIBUTE_TO, VPackValue(tmpId)); } - builder.add(TRI_VOC_ATTRIBUTE_TO, VPackValue(tmpId)); - } - builder.close(); + builder.close(); + }; + + if (payload->IsArray()) { + VPackArrayBuilder b(&builder); + v8::Handle array = v8::Handle::Cast(payload); + uint32_t const n = array->Length(); + for (uint32_t i = 0; i < n; ++i) { + doOneDocument(array->Get(i)); + } + } else { + doOneDocument(payload); + } // load collection auto transactionContext = std::make_shared(collection->_vocbase, true); SingleCollectionTransaction trx(transactionContext, collection->_cid, TRI_TRANSACTION_WRITE); - trx.addHint(TRI_TRANSACTION_HINT_SINGLE_OPERATION, false); + if (! payload->IsArray()) { + 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);