//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany /// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Dr. Frank Celler //////////////////////////////////////////////////////////////////////////////// #include "v8-collection.h" #include "ApplicationFeatures/ApplicationServer.h" #include "Aql/Query.h" #include "Basics/conversions.h" #include "Basics/ReadLocker.h" #include "Basics/Result.h" #include "Basics/ScopeGuard.h" #include "Basics/StaticStrings.h" #include "Basics/StringBuffer.h" #include "Basics/Utf8Helper.h" #include "Basics/VelocyPackHelper.h" #include "Basics/WriteLocker.h" #include "Cluster/ClusterInfo.h" #include "Cluster/FollowerInfo.h" #include "Cluster/ClusterMethods.h" #include "RestServer/DatabaseFeature.h" #include "StorageEngine/EngineSelectorFeature.h" #include "StorageEngine/StorageEngine.h" #include "Utils/OperationOptions.h" #include "Utils/OperationResult.h" #include "Utils/SingleCollectionTransaction.h" #include "Transaction/Hints.h" #include "Transaction/V8Context.h" #include "V8/v8-conv.h" #include "V8/v8-utils.h" #include "V8/v8-vpack.h" #include "V8Server/v8-externals.h" #include "V8Server/v8-vocbase.h" #include "V8Server/v8-vocbaseprivate.h" #include "V8Server/v8-vocindex.h" #include "VocBase/KeyGenerator.h" #include "VocBase/LogicalCollection.h" #include "VocBase/PhysicalCollection.h" #include "VocBase/modes.h" #include "Pregel/Conductor.h" #include "Pregel/PregelFeature.h" #include "Pregel/AggregatorHandler.h" #include "Pregel/Worker.h" #include #include #include #include using namespace arangodb; using namespace arangodb::basics; using namespace arangodb::rest; struct LocalCollectionGuard { explicit LocalCollectionGuard(LogicalCollection* collection) : _collection(collection) {} ~LocalCollectionGuard() { if (_collection != nullptr && !_collection->isLocal()) { delete _collection; } } LogicalCollection* _collection; }; //////////////////////////////////////////////////////////////////////////////// /// @brief extract the forceSync flag from the arguments /// must specify the argument index starting from 1 //////////////////////////////////////////////////////////////////////////////// static inline bool ExtractWaitForSync( v8::FunctionCallbackInfo const& args, int index) { TRI_ASSERT(index > 0); return (args.Length() >= index && TRI_ObjectToBoolean(args[index - 1])); } //////////////////////////////////////////////////////////////////////////////// /// @brief extracts a string value referencing a documents _id /// If value is a string it is simply returned. /// If value is an object and has a string _id attribute, this is /// returned /// Otherwise the empty string is returned //////////////////////////////////////////////////////////////////////////////// static std::string ExtractIdString(v8::Isolate* isolate, v8::Handle const val) { if (val->IsString()) { return TRI_ObjectToString(val); } if (val->IsObject()) { TRI_GET_GLOBALS(); v8::Handle obj = val->ToObject(); TRI_GET_GLOBAL_STRING(_IdKey); if (obj->HasRealNamedProperty(_IdKey)) { v8::Handle idVal = obj->Get(_IdKey); if (idVal->IsString()) { return TRI_ObjectToString(idVal); } } } std::string empty; return empty; } //////////////////////////////////////////////////////////////////////////////// /// @brief parse document or document handle from a v8 value (string | object) //////////////////////////////////////////////////////////////////////////////// static int ParseDocumentOrDocumentHandle(v8::Isolate* isolate, TRI_vocbase_t* vocbase, CollectionNameResolver const* resolver, LogicalCollection const*& collection, std::string& collectionName, VPackBuilder& builder, bool includeRev, v8::Handle const val) { v8::HandleScope scope(isolate); // try to extract the collection name, key, and revision from the object // passed if (!ExtractDocumentHandle(isolate, val, collectionName, builder, includeRev)) { return TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD; } if (collectionName.empty()) { // only a document key without collection name was passed if (collection == nullptr) { // we do not know the collection return TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD; } // we use the current collection's name collectionName = resolver->getCollectionNameCluster(collection->cid()); } else { // we read a collection name from the document id // check cross-collection requests if (collection != nullptr) { if (!EqualCollection(resolver, collectionName, collection)) { return TRI_ERROR_ARANGO_CROSS_COLLECTION_REQUEST; } } } TRI_ASSERT(!collectionName.empty()); if (collection == nullptr) { // no collection object was passed, now check the user-supplied collection // name if (ServerState::instance()->isCoordinator()) { ClusterInfo* ci = ClusterInfo::instance(); try { std::shared_ptr col = ci->getCollection(vocbase->name(), collectionName); auto colCopy = col->clone(); collection = colCopy.release(); } catch (...) { return TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND; } } else { collection = resolver->getCollectionStruct(collectionName); } if (collection == nullptr) { // collection not found return TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND; } } TRI_ASSERT(collection != nullptr); return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief V8ToVPack without _key and _rev, builder must be open with an /// object and is left open at the end //////////////////////////////////////////////////////////////////////////////// static int V8ToVPackNoKeyRevId (v8::Isolate* isolate, VPackBuilder& builder, v8::Local const obj) { TRI_ASSERT(obj->IsObject() && !obj->IsArray()); auto o = v8::Local::Cast(obj); v8::Handle names = o->GetOwnPropertyNames(); uint32_t const n = names->Length(); for (uint32_t i = 0; i < n; ++i) { v8::Handle key = names->Get(i); TRI_Utf8ValueNFC str(TRI_UNKNOWN_MEM_ZONE, key); if (*str == nullptr) { return TRI_ERROR_OUT_OF_MEMORY; } if (strcmp(*str, "_key") != 0 && strcmp(*str, "_rev") != 0 && strcmp(*str, "_id") != 0) { builder.add(VPackValue(*str)); int res = TRI_V8ToVPack(isolate, builder, o->Get(key), false); if (res != TRI_ERROR_NO_ERROR) { return res; } } } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief get all cluster collections //////////////////////////////////////////////////////////////////////////////// static std::vector GetCollectionsCluster( TRI_vocbase_t* vocbase) { std::vector result; std::vector> const collections = ClusterInfo::instance()->getCollections(vocbase->name()); for (auto& collection : collections) { std::unique_ptr c(collection->clone()); result.emplace_back(c.get()); c.release(); } return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief get all cluster collection names //////////////////////////////////////////////////////////////////////////////// static std::vector GetCollectionNamesCluster( TRI_vocbase_t* vocbase) { std::vector result; std::vector> const collections = ClusterInfo::instance()->getCollections(vocbase->name()); for (auto& collection : collections) { std::string const& name = collection->name(); result.emplace_back(name); } return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up a document and returns whether it exists //////////////////////////////////////////////////////////////////////////////// static void ExistsVocbaseVPack( bool useCollection, v8::FunctionCallbackInfo const& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); // first and only argument should be a document idenfifier if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("exists( or )"); } TRI_vocbase_t* vocbase; LogicalCollection const* col = nullptr; if (useCollection) { // called as db.collection.exists() col = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (col == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } vocbase = col->vocbase(); } else { // called as db._exists() vocbase = GetContextVocBase(isolate); } if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } auto transactionContext = std::make_shared(vocbase, true); VPackBuilder builder; std::string collectionName; int res; { VPackObjectBuilder guard(&builder); res = ParseDocumentOrDocumentHandle( isolate, vocbase, transactionContext->getResolver(), col, collectionName, builder, true, args[0]); } LocalCollectionGuard g( useCollection ? nullptr : const_cast(col)); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } TRI_ASSERT(col != nullptr); TRI_ASSERT(!collectionName.empty()); VPackSlice search = builder.slice(); TRI_ASSERT(search.isObject()); SingleCollectionTransaction trx(transactionContext, collectionName, AccessMode::Type::READ); trx.addHint(transaction::Hints::Hint::SINGLE_OPERATION); res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } OperationOptions options; options.silent = false; options.ignoreRevs = false; OperationResult opResult = trx.document(collectionName, search, options); res = trx.finish(opResult.code); if (!opResult.successful()) { if (opResult.code == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) { TRI_V8_RETURN_FALSE(); } TRI_V8_THROW_EXCEPTION(opResult.code); } if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } v8::Handle result = TRI_VPackToV8(isolate, opResult.slice(), transactionContext->getVPackOptions()); TRI_V8_RETURN(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up (a) document(s) and returns it/them, collection method //////////////////////////////////////////////////////////////////////////////// static void DocumentVocbaseCol( v8::FunctionCallbackInfo const& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); // first and only argument should be a document handle or key or an object if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("document( or or or )"); } OperationOptions options; options.ignoreRevs = false; // Find collection and vocbase std::string collectionName; arangodb::LogicalCollection const* col = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (col == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } TRI_vocbase_t* vocbase = col->vocbase(); collectionName = col->name(); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } VPackBuilder searchBuilder; auto workOnOneDocument = [&](v8::Local const searchValue, bool isBabies) { std::string collName; if (!ExtractDocumentHandle(isolate, searchValue, collName, searchBuilder, true)) { if (!isBabies) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD); } } if (!collName.empty() && collName != collectionName) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_CROSS_COLLECTION_REQUEST); } }; if (!args[0]->IsArray()) { VPackObjectBuilder guard(&searchBuilder); workOnOneDocument(args[0], false); } else { VPackArrayBuilder guard(&searchBuilder); auto searchVals = v8::Local::Cast(args[0]); for (uint32_t i = 0; i < searchVals->Length(); ++i) { VPackObjectBuilder guard(&searchBuilder); workOnOneDocument(searchVals->Get(i), true); } } VPackSlice search = searchBuilder.slice(); auto transactionContext = std::make_shared(vocbase, true); SingleCollectionTransaction trx(transactionContext, collectionName, AccessMode::Type::READ); if (!args[0]->IsArray()) { trx.addHint(transaction::Hints::Hint::SINGLE_OPERATION); } int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } OperationResult opResult = trx.document(collectionName, search, options); res = trx.finish(opResult.code); if (!opResult.successful()) { TRI_V8_THROW_EXCEPTION(opResult.code); } if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } v8::Handle result = TRI_VPackToV8(isolate, opResult.slice(), transactionContext->getVPackOptions()); TRI_V8_RETURN(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up a document and returns it, database method //////////////////////////////////////////////////////////////////////////////// static void DocumentVocbase( v8::FunctionCallbackInfo const& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); // first and only argument should be a document idenfifier if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("document()"); } TRI_vocbase_t* vocbase; LogicalCollection const* col = nullptr; vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } auto transactionContext = std::make_shared(vocbase, true); VPackBuilder builder; std::string collectionName; { VPackObjectBuilder guard(&builder); int res = ParseDocumentOrDocumentHandle( isolate, vocbase, transactionContext->getResolver(), col, collectionName, builder, true, args[0]); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } } LocalCollectionGuard g(const_cast(col)); TRI_ASSERT(col != nullptr); TRI_ASSERT(!collectionName.empty()); VPackSlice search = builder.slice(); TRI_ASSERT(search.isObject()); OperationOptions options; options.ignoreRevs = false; SingleCollectionTransaction trx(transactionContext, collectionName, AccessMode::Type::READ); trx.addHint(transaction::Hints::Hint::SINGLE_OPERATION); int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } OperationResult opResult = trx.document(collectionName, search, options); res = trx.finish(opResult.code); if (!opResult.successful()) { TRI_V8_THROW_EXCEPTION(opResult.code); } if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } v8::Handle result = TRI_VPackToV8(isolate, opResult.slice(), transactionContext->getVPackOptions()); TRI_V8_RETURN(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief deletes (a) document(s), collection method //////////////////////////////////////////////////////////////////////////////// static void RemoveVocbaseCol(v8::FunctionCallbackInfo const& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); OperationOptions options; options.ignoreRevs = false; // check the arguments uint32_t const argLength = args.Length(); TRI_GET_GLOBALS(); if (argLength < 1 || argLength > 3) { TRI_V8_THROW_EXCEPTION_USAGE("remove(, " "{overwrite: booleanValue, waitForSync: booleanValue, returnOld: " "booleanValue, silent:booleanValue})"); } if (argLength > 1) { if (args[1]->IsObject()) { v8::Handle optionsObject = args[1].As(); TRI_GET_GLOBAL_STRING(OverwriteKey); if (optionsObject->Has(OverwriteKey)) { options.ignoreRevs = TRI_ObjectToBoolean(optionsObject->Get(OverwriteKey)); } TRI_GET_GLOBAL_STRING(WaitForSyncKey); if (optionsObject->Has(WaitForSyncKey)) { options.waitForSync = TRI_ObjectToBoolean(optionsObject->Get(WaitForSyncKey)); } TRI_GET_GLOBAL_STRING(ReturnOldKey); if (optionsObject->Has(ReturnOldKey)) { options.returnOld = TRI_ObjectToBoolean(optionsObject->Get(ReturnOldKey)); } TRI_GET_GLOBAL_STRING(SilentKey); if (optionsObject->Has(SilentKey)) { options.silent = TRI_ObjectToBoolean(optionsObject->Get(SilentKey)); } } else { // old variant remove(, , ) options.ignoreRevs = TRI_ObjectToBoolean(args[1]); if (argLength > 2) { options.waitForSync = TRI_ObjectToBoolean(args[2]); } } } // Find collection and vocbase std::string collectionName; arangodb::LogicalCollection const* col = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (col == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } TRI_vocbase_t* vocbase = col->vocbase(); collectionName = col->name(); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } auto transactionContext = std::make_shared(vocbase, true); SingleCollectionTransaction trx(transactionContext, collectionName, AccessMode::Type::WRITE); if (!args[0]->IsArray()) { trx.addHint(transaction::Hints::Hint::SINGLE_OPERATION); } int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } VPackBuilder searchBuilder; auto workOnOneDocument = [&](v8::Local const searchValue, bool isBabies) { std::string collName; if (!ExtractDocumentHandle(isolate, searchValue, collName, searchBuilder, true)) { if (!isBabies) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD); } return; } if (!collName.empty() && collName != collectionName) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_CROSS_COLLECTION_REQUEST); } }; if (!args[0]->IsArray()) { VPackObjectBuilder guard(&searchBuilder); workOnOneDocument(args[0], false); } else { VPackArrayBuilder guard(&searchBuilder); auto searchVals = v8::Local::Cast(args[0]); for (uint32_t i = 0; i < searchVals->Length(); ++i) { VPackObjectBuilder guard(&searchBuilder); workOnOneDocument(searchVals->Get(i), true); } } VPackSlice toRemove = searchBuilder.slice(); OperationResult result = trx.remove(collectionName, toRemove, options); res = trx.finish(result.code); if (!result.successful()) { TRI_V8_THROW_EXCEPTION(result.code); } if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } if (options.silent) { // no return value TRI_V8_RETURN_TRUE(); } v8::Handle finalResult = TRI_VPackToV8(isolate, result.slice(), transactionContext->getVPackOptions()); TRI_V8_RETURN(finalResult); } //////////////////////////////////////////////////////////////////////////////// /// @brief deletes a document, database method //////////////////////////////////////////////////////////////////////////////// static void RemoveVocbase(v8::FunctionCallbackInfo const& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); OperationOptions options; options.ignoreRevs = false; // check the arguments uint32_t const argLength = args.Length(); TRI_GET_GLOBALS(); if (argLength < 1 || argLength > 3) { TRI_V8_THROW_EXCEPTION_USAGE("remove(, )"); } if (argLength > 1) { if (args[1]->IsObject()) { v8::Handle optionsObject = args[1].As(); TRI_GET_GLOBAL_STRING(OverwriteKey); if (optionsObject->Has(OverwriteKey)) { options.ignoreRevs = TRI_ObjectToBoolean(optionsObject->Get(OverwriteKey)); } TRI_GET_GLOBAL_STRING(WaitForSyncKey); if (optionsObject->Has(WaitForSyncKey)) { options.waitForSync = TRI_ObjectToBoolean(optionsObject->Get(WaitForSyncKey)); } TRI_GET_GLOBAL_STRING(ReturnOldKey); if (optionsObject->Has(ReturnOldKey)) { options.returnOld = TRI_ObjectToBoolean(optionsObject->Get(ReturnOldKey)); } TRI_GET_GLOBAL_STRING(SilentKey); if (optionsObject->Has(SilentKey)) { options.silent = TRI_ObjectToBoolean(optionsObject->Get(SilentKey)); } } else { // old variant replace(, , , // ) options.ignoreRevs = TRI_ObjectToBoolean(args[1]); if (argLength > 2) { options.waitForSync = TRI_ObjectToBoolean(args[2]); } } } TRI_vocbase_t* vocbase; LogicalCollection const* col = nullptr; vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } auto transactionContext = std::make_shared(vocbase, true); VPackBuilder builder; std::string collectionName; { VPackObjectBuilder guard(&builder); int res = ParseDocumentOrDocumentHandle( isolate, vocbase, transactionContext->getResolver(), col, collectionName, builder, !options.ignoreRevs, args[0]); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } } LocalCollectionGuard g(const_cast(col)); TRI_ASSERT(col != nullptr); TRI_ASSERT(!collectionName.empty()); VPackSlice toRemove = builder.slice(); TRI_ASSERT(toRemove.isObject()); SingleCollectionTransaction trx(transactionContext, collectionName, AccessMode::Type::WRITE); trx.addHint(transaction::Hints::Hint::SINGLE_OPERATION); int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } OperationResult result = trx.remove(collectionName, toRemove, options); res = trx.finish(result.code); if (!result.successful()) { TRI_V8_THROW_EXCEPTION(result.code); } if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } if (options.silent) { // no return value TRI_V8_RETURN_TRUE(); } v8::Handle finalResult = TRI_VPackToV8(isolate, result.slice(), transactionContext->getVPackOptions()); TRI_V8_RETURN(finalResult); } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock documentsCollectionName //////////////////////////////////////////////////////////////////////////////// static void JS_DocumentVocbaseCol( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); DocumentVocbaseCol(args); TRI_V8_TRY_CATCH_END } #ifndef USE_ENTERPRISE //////////////////////////////////////////////////////////////////////////////// /// @brief unloads a collection, case of a coordinator in a cluster //////////////////////////////////////////////////////////////////////////////// static int ULVocbaseColCoordinator(std::string const& databaseName, std::string const& collectionCID, TRI_vocbase_col_status_e status) { return ClusterInfo::instance()->setCollectionStatusCoordinator( databaseName, collectionCID, status); } //////////////////////////////////////////////////////////////////////////////// /// @brief drops a collection, case of a coordinator in a cluster //////////////////////////////////////////////////////////////////////////////// static void DropVocbaseColCoordinator( v8::FunctionCallbackInfo const& args, arangodb::LogicalCollection* collection) { // cppcheck-suppress * v8::Isolate* isolate = args.GetIsolate(); if (collection->isSystem()) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_FORBIDDEN); } std::string const databaseName(collection->dbName()); std::string const cid = collection->cid_as_string(); ClusterInfo* ci = ClusterInfo::instance(); std::string errorMsg; int res = ci->dropCollectionCoordinator(databaseName, cid, errorMsg, 120.0); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION_MESSAGE(res, errorMsg); } collection->setStatus(TRI_VOC_COL_STATUS_DELETED); TRI_V8_RETURN_UNDEFINED(); } #endif //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock collectionDrop //////////////////////////////////////////////////////////////////////////////// static void JS_DropVocbaseCol(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); arangodb::LogicalCollection* collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } PREVENT_EMBEDDED_TRANSACTION(); // If we are a coordinator in a cluster, we have to behave differently: if (ServerState::instance()->isCoordinator()) { #ifdef USE_ENTERPRISE DropVocbaseColCoordinatorEnterprise(args, collection); #else DropVocbaseColCoordinator(args, collection); #endif return; } bool allowDropSystem = false; if (args.Length() > 0) { // options if (args[0]->IsObject()) { TRI_GET_GLOBALS(); v8::Handle optionsObject = args[0].As(); TRI_GET_GLOBAL_STRING(IsSystemKey); if (optionsObject->Has(IsSystemKey)) { allowDropSystem = TRI_ObjectToBoolean(optionsObject->Get(IsSystemKey)); } } else { allowDropSystem = TRI_ObjectToBoolean(args[0]); } } int res = collection->vocbase()->dropCollection(collection, allowDropSystem); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION_MESSAGE(res, "cannot drop collection"); } TRI_V8_RETURN_UNDEFINED(); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock documentsCollectionExists //////////////////////////////////////////////////////////////////////////////// static void JS_ExistsVocbaseVPack( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); return ExistsVocbaseVPack(true, args); // cppcheck-suppress style TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock collectionFigures //////////////////////////////////////////////////////////////////////////////// static void JS_FiguresVocbaseCol( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); arangodb::LogicalCollection* collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } SingleCollectionTransaction trx(transaction::V8Context::Create(collection->vocbase(), true), collection->cid(), AccessMode::Type::READ); int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } std::shared_ptr builder = collection->figures(); trx.finish(TRI_ERROR_NO_ERROR); v8::Handle result = TRI_VPackToV8(isolate, builder->slice()); TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock leaderResign //////////////////////////////////////////////////////////////////////////////// static void JS_LeaderResign(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } if (ServerState::instance()->isDBServer()) { arangodb::LogicalCollection const* v8Collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (v8Collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } TRI_vocbase_t* vocbase = v8Collection->vocbase(); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } std::string collectionName = v8Collection->name(); auto collection = vocbase->lookupCollection(collectionName); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND); } // do not reset followers at this time...we are still the only source of truth // to trust... //trx.documentCollection()->followers()->clear(); collection->followers()->setLeader(false); } TRI_V8_RETURN_UNDEFINED(); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock assumeLeadership //////////////////////////////////////////////////////////////////////////////// static void JS_AssumeLeadership(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } if (ServerState::instance()->isDBServer()) { arangodb::LogicalCollection const* v8Collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (v8Collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } TRI_vocbase_t* vocbase = v8Collection->vocbase(); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } std::string collectionName = v8Collection->name(); auto collection = vocbase->lookupCollection(collectionName); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND); } collection->followers()->clear(); collection->followers()->setLeader(true); } TRI_V8_RETURN_UNDEFINED(); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock isLeader //////////////////////////////////////////////////////////////////////////////// static void JS_IsLeader(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } bool b = false; if (ServerState::instance()->isDBServer()) { arangodb::LogicalCollection const* collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } TRI_vocbase_t* vocbase = collection->vocbase(); std::string collectionName = collection->name(); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } auto realCollection = vocbase->lookupCollection(collectionName); if (realCollection == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND); } b = realCollection->followers()->isLeader(); } if (b) { TRI_V8_RETURN_TRUE(); } else { TRI_V8_RETURN_FALSE(); } TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock getFollowers //////////////////////////////////////////////////////////////////////////////// static void JS_GetFollowers(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } v8::Handle list = v8::Array::New(isolate); if (ServerState::instance()->isDBServer()) { arangodb::LogicalCollection const* v8Collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (v8Collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } TRI_vocbase_t* vocbase = v8Collection->vocbase(); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } std::string collectionName = v8Collection->name(); auto collection = vocbase->lookupCollection(collectionName); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND); } std::unique_ptr const& followerInfo = collection->followers(); std::shared_ptr const> followers = followerInfo->get(); uint32_t i = 0; for (auto const& n : *followers) { list->Set(i++, TRI_V8_STD_STRING(n)); } } TRI_V8_RETURN(list); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock collectionLoad //////////////////////////////////////////////////////////////////////////////// static void JS_LoadVocbaseCol(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); arangodb::LogicalCollection const* collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } if (ServerState::instance()->isCoordinator()) { int res = #ifdef USE_ENTERPRISE ULVocbaseColCoordinatorEnterprise( collection->dbName(), collection->cid_as_string(), TRI_VOC_COL_STATUS_LOADED); #else ULVocbaseColCoordinator( collection->dbName(), collection->cid_as_string(), TRI_VOC_COL_STATUS_LOADED); #endif if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } TRI_V8_RETURN_UNDEFINED(); } SingleCollectionTransaction trx( transaction::V8Context::Create(collection->vocbase(), true), collection->cid(), AccessMode::Type::READ); int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } res = trx.finish(res); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } TRI_V8_RETURN_UNDEFINED(); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the name of a collection //////////////////////////////////////////////////////////////////////////////// static void JS_NameVocbaseCol(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); arangodb::LogicalCollection const* collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } std::string const collectionName(collection->name()); if (collectionName.empty()) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND); } v8::Handle result = TRI_V8_STD_STRING(collectionName); TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the path of a collection //////////////////////////////////////////////////////////////////////////////// static void JS_PathVocbaseCol(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); arangodb::LogicalCollection const* collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } std::string const path(collection->getPhysical()->path()); v8::Handle result = TRI_V8_STD_STRING(path); TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief return the collection's cluster plan id //////////////////////////////////////////////////////////////////////////////// static void JS_PlanIdVocbaseCol( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); arangodb::LogicalCollection const* collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } if (ServerState::instance()->isCoordinator()) { TRI_V8_RETURN(TRI_V8UInt64String(isolate, collection->cid())); } TRI_V8_RETURN(TRI_V8UInt64String(isolate, collection->planId())); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock collectionProperties //////////////////////////////////////////////////////////////////////////////// static void JS_PropertiesVocbaseCol( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); arangodb::LogicalCollection* collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } if (ServerState::instance()->isCoordinator()) { std::string const databaseName(collection->dbName()); std::shared_ptr info = ClusterInfo::instance()->getCollection( databaseName, collection->cid_as_string()); if (0 < args.Length()) { v8::Handle par = args[0]; if (par->IsObject()) { VPackBuilder builder; { int res = TRI_V8ToVPack(isolate, builder, args[0], false); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } } VPackSlice const slice = builder.slice(); arangodb::Result res = info->updateProperties(slice, false); if (!res.ok()) { TRI_V8_THROW_EXCEPTION_MESSAGE(res.errorNumber(), res.errorMessage()); } } } auto c = ClusterInfo::instance()->getCollection( databaseName, StringUtils::itoa(collection->cid())); std::unordered_set const ignoreKeys{ "allowUserKeys", "cid", "count", "deleted", "id", "indexes", "name", "path", "planId", "shards", "status", "type", "version"}; VPackBuilder vpackProperties = c->toVelocyPackIgnore(ignoreKeys, true); // return the current parameter set v8::Handle result = TRI_VPackToV8(isolate, vpackProperties.slice())->ToObject(); TRI_V8_RETURN(result); } bool const isModification = (args.Length() != 0); SingleCollectionTransaction trx( transaction::V8Context::Create(collection->vocbase(), true), collection->cid(), isModification ? AccessMode::Type::WRITE : AccessMode::Type::READ); if (!isModification) { trx.addHint(transaction::Hints::Hint::NO_USAGE_LOCK); } int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } // check if we want to change some parameters if (isModification) { v8::Handle par = args[0]; if (par->IsObject()) { VPackBuilder builder; int res = TRI_V8ToVPack(isolate, builder, args[0], false); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } VPackSlice const slice = builder.slice(); // try to write new parameter to file bool doSync = application_features::ApplicationServer::getFeature("Database")->forceSyncProperties(); arangodb::Result updateRes = collection->updateProperties(slice, doSync); if (!updateRes.ok()) { TRI_V8_THROW_EXCEPTION_MESSAGE(updateRes.errorNumber(), updateRes.errorMessage()); } auto physical = collection->getPhysical(); TRI_ASSERT(physical != nullptr); arangodb::Result res2 = physical->persistProperties(); // TODO Review // TODO API compatibility, for now we ignore if persisting fails... } } std::unordered_set const ignoreKeys{ "allowUserKeys", "cid", "count", "deleted", "id", "indexes", "name", "path", "planId", "shards", "status", "type", "version", /* These are only relevant for cluster */ "distributeShardsLike", "isSmart", "numberOfShards", "replicationFactor", "shardKeys"}; VPackBuilder vpackProperties = collection->toVelocyPackIgnore(ignoreKeys, true); // return the current parameter set v8::Handle result = TRI_VPackToV8(isolate, vpackProperties.slice())->ToObject(); trx.finish(res); TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } static void JS_RemoveVocbaseCol( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); return RemoveVocbaseCol(args); // cppcheck-suppress style TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief helper function to rename collections in _graphs as well //////////////////////////////////////////////////////////////////////////////// static int RenameGraphCollections(v8::Isolate* isolate, std::string const& oldName, std::string const& newName) { v8::HandleScope scope(isolate); StringBuffer buffer(TRI_UNKNOWN_MEM_ZONE); buffer.appendText("require('@arangodb/general-graph')._renameCollection("); buffer.appendJsonEncoded(oldName.c_str(), oldName.size()); buffer.appendChar(','); buffer.appendJsonEncoded(newName.c_str(), newName.size()); buffer.appendText(");"); TRI_ExecuteJavaScriptString( isolate, isolate->GetCurrentContext(), TRI_V8_ASCII_PAIR_STRING(buffer.c_str(), buffer.length()), TRI_V8_ASCII_STRING("collection rename"), false); return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock collectionRename //////////////////////////////////////////////////////////////////////////////// static void JS_RenameVocbaseCol( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() < 1) { TRI_V8_THROW_EXCEPTION_USAGE("rename()"); } if (ServerState::instance()->isCoordinator()) { // renaming a collection in a cluster is unsupported TRI_V8_THROW_EXCEPTION(TRI_ERROR_CLUSTER_UNSUPPORTED); } std::string const name = TRI_ObjectToString(args[0]); // second parameter "override" is to override renaming restrictions, e.g. // renaming from a system collection name to a non-system collection name and // vice versa. this parameter is not publicly exposed but used internally bool doOverride = false; if (args.Length() > 1) { doOverride = TRI_ObjectToBoolean(args[1]); } if (name.empty()) { TRI_V8_THROW_EXCEPTION_PARAMETER(" must be non-empty"); } arangodb::LogicalCollection* collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } PREVENT_EMBEDDED_TRANSACTION(); if (ServerState::instance()->isCoordinator()) { // renaming a collection in a cluster is unsupported TRI_V8_THROW_EXCEPTION(TRI_ERROR_CLUSTER_UNSUPPORTED); } std::string const oldName(collection->name()); int res = collection->vocbase()->renameCollection(collection, name, doOverride); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION_MESSAGE(res, "cannot rename collection"); } // rename collection inside _graphs as well RenameGraphCollections(isolate, oldName, name); TRI_V8_RETURN_UNDEFINED(); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief option parsing for replace and update methods //////////////////////////////////////////////////////////////////////////////// static void parseReplaceAndUpdateOptions( v8::Isolate* isolate, v8::FunctionCallbackInfo const& args, OperationOptions& options, TRI_voc_document_operation_e operation) { TRI_GET_GLOBALS(); TRI_ASSERT(args.Length() > 2); if (args[2]->IsObject()) { v8::Handle optionsObject = args[2].As(); TRI_GET_GLOBAL_STRING(OverwriteKey); if (optionsObject->Has(OverwriteKey)) { options.ignoreRevs = TRI_ObjectToBoolean(optionsObject->Get(OverwriteKey)); } TRI_GET_GLOBAL_STRING(WaitForSyncKey); if (optionsObject->Has(WaitForSyncKey)) { options.waitForSync = TRI_ObjectToBoolean(optionsObject->Get(WaitForSyncKey)); } TRI_GET_GLOBAL_STRING(SilentKey); if (optionsObject->Has(SilentKey)) { options.silent = TRI_ObjectToBoolean(optionsObject->Get(SilentKey)); } TRI_GET_GLOBAL_STRING(ReturnNewKey); if (optionsObject->Has(ReturnNewKey)) { options.returnNew = TRI_ObjectToBoolean(optionsObject->Get(ReturnNewKey)); } TRI_GET_GLOBAL_STRING(ReturnOldKey); if (optionsObject->Has(ReturnOldKey)) { options.returnOld = TRI_ObjectToBoolean(optionsObject->Get(ReturnOldKey)); } TRI_GET_GLOBAL_STRING(IsRestoreKey); if (optionsObject->Has(IsRestoreKey)) { options.isRestore = TRI_ObjectToBoolean(optionsObject->Get(IsRestoreKey)); } if (operation == TRI_VOC_DOCUMENT_OPERATION_UPDATE) { // intentionally not called for TRI_VOC_DOCUMENT_OPERATION_REPLACE TRI_GET_GLOBAL_STRING(KeepNullKey); if (optionsObject->Has(KeepNullKey)) { options.keepNull = TRI_ObjectToBoolean(optionsObject->Get(KeepNullKey)); } TRI_GET_GLOBAL_STRING(MergeObjectsKey); if (optionsObject->Has(MergeObjectsKey)) { options.mergeObjects = TRI_ObjectToBoolean(optionsObject->Get(MergeObjectsKey)); } } } else { // old variants // replace(, , , ) // and // update(, , , , options.ignoreRevs = TRI_ObjectToBoolean(args[2]); if (args.Length() > 3) { if (operation == TRI_VOC_DOCUMENT_OPERATION_REPLACE) { options.waitForSync = TRI_ObjectToBoolean(args[3]); } else { // UPDATE options.keepNull = TRI_ObjectToBoolean(args[3]); if (args.Length() > 4) { options.waitForSync = TRI_ObjectToBoolean(args[4]); } } } } } //////////////////////////////////////////////////////////////////////////////// /// @brief ModifyVocbaseCol //////////////////////////////////////////////////////////////////////////////// static void ModifyVocbaseCol(TRI_voc_document_operation_e operation, v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); // check the arguments uint32_t const argLength = args.Length(); if (argLength < 2 || argLength > (operation == TRI_VOC_DOCUMENT_OPERATION_REPLACE ? 4UL : 5UL)) { if (operation == TRI_VOC_DOCUMENT_OPERATION_REPLACE) { TRI_V8_THROW_EXCEPTION_USAGE( "replace(, , {overwrite: booleanValue," " waitForSync: booleanValue, returnNew: booleanValue," " returnOld: booleanValue, silent: booleanValue})"); } else { // UPDATE TRI_V8_THROW_EXCEPTION_USAGE( "update(, , {overwrite: booleanValue, keepNull: " "booleanValue, mergeObjects: booleanValue, waitForSync: " "booleanValue, returnNew: booleanValue, returnOld: booleanValue," " silent: booleanValue})"); } } // we're only accepting "real" object documents or arrays of such if (!args[1]->IsObject()) { 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::Cast(args[0]); auto b = v8::Local::Cast(args[1]); if (a->Length() != b->Length()) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); } } OperationOptions options; options.ignoreRevs = false; if (args.Length() > 2) { parseReplaceAndUpdateOptions(isolate, args, options, operation); } if (options.isRestore) { options.ignoreRevs = true; } // Find collection and vocbase arangodb::LogicalCollection const* col = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (col == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } TRI_vocbase_t* vocbase = col->vocbase(); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } std::string collectionName = col->name(); VPackBuilder updateBuilder; auto workOnOneSearchVal = [&](v8::Local const searchVal, bool isBabies) { std::string collName; if (!ExtractDocumentHandle(isolate, searchVal, collName, updateBuilder, !options.isRestore)) { // If this is no restore, then we must extract the _rev from the // search value. If options.isRestore is set, the _rev value must // be taken from the new value, see below in workOnOneDocument! if (!isBabies) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD); } else { return; } } if (!collName.empty() && collName != collectionName) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_CROSS_COLLECTION_REQUEST); } }; auto workOnOneDocument = [&](v8::Local const newVal) { if (!newVal->IsObject() || newVal->IsArray()) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); } int res = V8ToVPackNoKeyRevId(isolate, updateBuilder, newVal); if (res != TRI_ERROR_NO_ERROR) { THROW_ARANGO_EXCEPTION(res); } if (options.isRestore) { // In this case we have to extract the _rev entry from newVal: TRI_GET_GLOBALS(); v8::Handle obj = newVal->ToObject(); TRI_GET_GLOBAL_STRING(_RevKey); if (!obj->HasRealNamedProperty(_RevKey)) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_REV_BAD); } v8::Handle revVal = obj->Get(_RevKey); if (!revVal->IsString()) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_REV_BAD); } v8::String::Utf8Value str(revVal); if (*str == nullptr) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_REV_BAD); } updateBuilder.add(StaticStrings::RevString, VPackValue(*str)); } }; if (!args[0]->IsArray()) { // we deal with the single document case: VPackObjectBuilder guard(&updateBuilder); workOnOneDocument(args[1]); workOnOneSearchVal(args[0], false); } else { // finally, the array case, note that we already know that the two // arrays have equal length! TRI_ASSERT(args[0]->IsArray() && args[1]->IsArray()); VPackArrayBuilder guard(&updateBuilder); auto searchVals = v8::Local::Cast(args[0]); auto documents = v8::Local::Cast(args[1]); for (uint32_t i = 0; i < searchVals->Length(); ++i) { v8::Local const newVal = documents->Get(i); if (!newVal->IsObject() || newVal->IsArray()) { // We insert a non-object that should fail later. updateBuilder.add(VPackValue(VPackValueType::Null)); continue; } VPackObjectBuilder guard(&updateBuilder); workOnOneDocument(newVal); workOnOneSearchVal(searchVals->Get(i), true); } } VPackSlice const update = updateBuilder.slice(); auto transactionContext = std::make_shared(vocbase, true); // Now start the transaction: SingleCollectionTransaction trx(transactionContext, collectionName, AccessMode::Type::WRITE); if (!args[0]->IsArray()) { trx.addHint(transaction::Hints::Hint::SINGLE_OPERATION); } int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } OperationResult opResult; if (operation == TRI_VOC_DOCUMENT_OPERATION_REPLACE) { opResult = trx.replace(collectionName, update, options); } else { opResult = trx.update(collectionName, update, options); } res = trx.finish(opResult.code); if (!opResult.successful()) { TRI_V8_THROW_EXCEPTION(opResult.code); } if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } if (options.silent) { // no return value TRI_V8_RETURN_TRUE(); } VPackSlice resultSlice = opResult.slice(); TRI_V8_RETURN(TRI_VPackToV8(isolate, resultSlice, transactionContext->getVPackOptions())); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock documentsCollectionReplace /// Replace a document, collection method //////////////////////////////////////////////////////////////////////////////// static void JS_ReplaceVocbaseCol( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); ModifyVocbaseCol(TRI_VOC_DOCUMENT_OPERATION_REPLACE, args); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock documentsCollectionUpdate //////////////////////////////////////////////////////////////////////////////// static void JS_UpdateVocbaseCol( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); ModifyVocbaseCol(TRI_VOC_DOCUMENT_OPERATION_UPDATE, args); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief ModifyVocbase, database method, only single documents //////////////////////////////////////////////////////////////////////////////// static void ModifyVocbase(TRI_voc_document_operation_e operation, v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); // check the arguments uint32_t const argLength = args.Length(); if (argLength < 2 || argLength > (operation == TRI_VOC_DOCUMENT_OPERATION_REPLACE ? 4UL : 5UL)) { if (operation == TRI_VOC_DOCUMENT_OPERATION_REPLACE) { TRI_V8_THROW_EXCEPTION_USAGE( "_replace(, , {overwrite: booleanValue, waitForSync: " "booleanValue, returnNew: booleanValue, returnOld: booleanValue," " silent: booleanValue})"); } else { TRI_V8_THROW_EXCEPTION_USAGE( "_update(, , {overwrite: booleanValue, keepNull: " "booleanValue, mergeObjects: booleanValue, waitForSync: " "booleanValue, returnNew: booleanValue, returnOld: booleanValue," " silent: booleanValue})"); } } // we're only accepting "real" object documents if (!args[1]->IsObject() || args[1]->IsArray()) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); } OperationOptions options; options.ignoreRevs = false; if (args.Length() > 2) { parseReplaceAndUpdateOptions(isolate, args, options, operation); } LogicalCollection const* col = nullptr; std::string collectionName; TRI_vocbase_t* vocbase = GetContextVocBase(isolate); auto transactionContext = std::make_shared(vocbase, true); VPackBuilder updateBuilder; { VPackObjectBuilder guard(&updateBuilder); int res = V8ToVPackNoKeyRevId(isolate, updateBuilder, args[1]); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } res = ParseDocumentOrDocumentHandle( isolate, vocbase, transactionContext->getResolver(), col, collectionName, updateBuilder, !options.ignoreRevs, args[0]); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } } // We need to free the collection object in the end LocalCollectionGuard g(const_cast(col)); SingleCollectionTransaction trx(transactionContext, collectionName, AccessMode::Type::WRITE); trx.addHint(transaction::Hints::Hint::SINGLE_OPERATION); int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } VPackSlice const update = updateBuilder.slice(); OperationResult opResult; if (operation == TRI_VOC_DOCUMENT_OPERATION_REPLACE) { opResult = trx.replace(collectionName, update, options); } else { opResult = trx.update(collectionName, update, options); } res = trx.finish(opResult.code); if (!opResult.successful()) { TRI_V8_THROW_EXCEPTION(opResult.code); } if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } if (options.silent) { // no return value TRI_V8_RETURN_TRUE(); } VPackSlice resultSlice = opResult.slice(); TRI_V8_RETURN(TRI_VPackToV8(isolate, resultSlice, transactionContext->getVPackOptions())); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock documentsDocumentReplace //////////////////////////////////////////////////////////////////////////////// static void JS_ReplaceVocbase(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); ModifyVocbase(TRI_VOC_DOCUMENT_OPERATION_REPLACE, args); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock documentsDocumentUpdate //////////////////////////////////////////////////////////////////////////////// static void JS_UpdateVocbase(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); ModifyVocbase(TRI_VOC_DOCUMENT_OPERATION_UPDATE, args); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief Pregel Stuff //////////////////////////////////////////////////////////////////////////////// static void JS_PregelStart(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); ServerState *ss = ServerState::instance(); if (ss->isRunningInCluster() && !ss->isCoordinator()) { TRI_V8_THROW_EXCEPTION_USAGE("Only call on coordinator or in single server mode"); } // check the arguments uint32_t const argLength = args.Length(); if (argLength < 3 || !args[0]->IsString()) { // TODO extend this for named graphs, use the Graph class TRI_V8_THROW_EXCEPTION_USAGE( "_pregelStart(, ," "[, {maxGSS:100, ...}]"); } auto parse = [](v8::Local const& value, std::vector &out) { v8::Handle array = v8::Handle::Cast(value); uint32_t const n = array->Length(); for (uint32_t i = 0; i < n; ++i) { v8::Handle obj = array->Get(i); if (obj->IsString()) { out.push_back(TRI_ObjectToString(obj)); } } }; std::string algorithm = TRI_ObjectToString(args[0]); std::vector paramVertices, paramEdges; if (args[1]->IsArray()) { parse(args[1], paramVertices); } else if (args[1]->IsString()) { paramVertices.push_back(TRI_ObjectToString(args[1])); } else { TRI_V8_THROW_EXCEPTION_USAGE("Specify an array of vertex collections (or a string)"); } if (paramVertices.size() == 0) { TRI_V8_THROW_EXCEPTION_USAGE("Specify at least one vertex collection"); } if (args[2]->IsArray()) { parse(args[2], paramEdges); } else if (args[2]->IsString()) { paramEdges.push_back(TRI_ObjectToString(args[2])); } else { TRI_V8_THROW_EXCEPTION_USAGE("Specify an array of edge collections (or a string)"); } if (paramEdges.size() == 0) { TRI_V8_THROW_EXCEPTION_USAGE("Specify at least one edge collection"); } VPackBuilder paramBuilder; if (argLength >= 4 && args[3]->IsObject()) { int res = TRI_V8ToVPack(isolate, paramBuilder, args[3], false); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } } TRI_vocbase_t* vocbase = GetContextVocBase(isolate); for (std::string const& name : paramVertices) { if (ss->isCoordinator()) { try { auto coll = ClusterInfo::instance()->getCollection(vocbase->name(), name); if (coll->isSystem()) { TRI_V8_THROW_EXCEPTION_USAGE( "Cannot use pregel on system collection"); } //if (!coll->usesDefaultShardKeys()) { // TRI_V8_THROW_EXCEPTION_USAGE( // "Vertex collection needs to be shared after '_key'"); //} if (coll->deleted()) { TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND, name); } } catch (...) { TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND, name); } } else if (ss->getRole() == ServerState::ROLE_SINGLE) { LogicalCollection *coll = vocbase->lookupCollection(name); if (coll == nullptr || coll->deleted()) { TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND, name); } } else { THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); } } std::vector edgeColls; // load edge collection for (std::string const& name : paramEdges) { if (ss->isCoordinator()) { try { auto coll = ClusterInfo::instance()->getCollection(vocbase->name(), name); if (coll->isSystem()) { TRI_V8_THROW_EXCEPTION_USAGE( "Cannot use pregel on system collection"); } if (!coll->isSmart()) { std::vector eKeys = coll->shardKeys(); if ( eKeys.size() != 1 || eKeys[0] != "vertex") { TRI_V8_THROW_EXCEPTION_USAGE( "Edge collection needs to be sharded after 'vertex', or use " "smart graphs"); } } if (coll->deleted()) { TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND, name); } // smart edge collections contain multiple actual collections std::vector actual = coll->realNamesForRead(); edgeColls.insert(edgeColls.end(), actual.begin(), actual.end()); } catch (...) { TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND, name); } } else if (ss->getRole() == ServerState::ROLE_SINGLE) { LogicalCollection *coll = vocbase->lookupCollection(name); if (coll == nullptr || coll->deleted()) { TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND, name); } std::vector actual = coll->realNamesForRead(); edgeColls.insert(edgeColls.end(), actual.begin(), actual.end()); } else { THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); } } uint64_t en = pregel::PregelFeature::instance()->createExecutionNumber(); auto c = std::make_unique(en, vocbase, paramVertices, edgeColls, algorithm, paramBuilder.slice()); pregel::PregelFeature::instance()->addConductor(c.get(), en); c->start(); c.release(); TRI_V8_RETURN(v8::Number::New(isolate, en)); TRI_V8_TRY_CATCH_END } static void JS_PregelStatus(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); // check the arguments uint32_t const argLength = args.Length(); if (argLength != 1 || (!args[0]->IsNumber() && !args[0]->IsString())) { // TODO extend this for named graphs, use the Graph class TRI_V8_THROW_EXCEPTION_USAGE("_pregelStatus(]"); } uint64_t executionNum = TRI_ObjectToUInt64(args[0], true); pregel::Conductor *c = pregel::PregelFeature::instance()->conductor(executionNum); if (!c) { TRI_V8_THROW_EXCEPTION_USAGE("Execution number is invalid"); } VPackBuilder result; result.openObject(); result.add("state", VPackValue(pregel::ExecutionStateNames[c->getState()])); result.add("gss", VPackValue(c->globalSuperstep())); result.add("totalRuntime", VPackValue(c->totalRuntimeSecs())); c->aggregators()->serializeValues(result); c->workerStats().serializeValues(result); result.close(); TRI_V8_RETURN(TRI_VPackToV8(isolate, result.slice())); TRI_V8_TRY_CATCH_END } static void JS_PregelCancel(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); // check the arguments uint32_t const argLength = args.Length(); if (argLength != 1 || !(args[0]->IsNumber() || args[0]->IsString())) { // TODO extend this for named graphs, use the Graph class TRI_V8_THROW_EXCEPTION_USAGE("_pregelStatus(]"); } uint64_t executionNum = TRI_ObjectToUInt64(args[0], true); pregel::Conductor *c = pregel::PregelFeature::instance()->conductor(executionNum); if (!c) { TRI_V8_THROW_EXCEPTION_USAGE("Execution number is invalid"); } c->cancel(); pregel::PregelFeature::instance()->cleanupConductor(executionNum); TRI_V8_RETURN_UNDEFINED(); TRI_V8_TRY_CATCH_END } static void JS_PregelAQLResult(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); // check the arguments uint32_t const argLength = args.Length(); if (argLength != 1 || !(args[0]->IsNumber() || args[0]->IsString())) { // TODO extend this for named graphs, use the Graph class TRI_V8_THROW_EXCEPTION_USAGE("_pregelStatus(]"); } uint64_t executionNum = TRI_ObjectToUInt64(args[0], true); if (ServerState::instance()->isCoordinator()) { pregel::Conductor *c = pregel::PregelFeature::instance()->conductor(executionNum); if (!c) { TRI_V8_THROW_EXCEPTION_USAGE("Execution number is invalid"); } VPackBuilder docs = c->collectAQLResults(); TRI_ASSERT(docs.slice().isArray()); VPackOptions resultOptions = VPackOptions::Defaults; auto documents = TRI_VPackToV8(isolate, docs.slice(), &resultOptions); TRI_V8_RETURN(documents); } else { TRI_V8_THROW_EXCEPTION_USAGE("Only valid on the conductor"); } TRI_V8_RETURN_UNDEFINED(); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief fetch the revision for a local collection //////////////////////////////////////////////////////////////////////////////// static int GetRevision(arangodb::LogicalCollection* collection, TRI_voc_rid_t& rid) { TRI_ASSERT(collection != nullptr); SingleCollectionTransaction trx( transaction::V8Context::Create(collection->vocbase(), true), collection->cid(), AccessMode::Type::READ); int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { return res; } // READ-LOCK start trx.lockRead(); rid = collection->revision(); trx.finish(res); // READ-LOCK end return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief fetch the revision for a sharded collection //////////////////////////////////////////////////////////////////////////////// static int GetRevisionCoordinator(arangodb::LogicalCollection* collection, TRI_voc_rid_t& rid) { TRI_ASSERT(collection != nullptr); std::string const databaseName(collection->dbName()); std::string const cid = collection->cid_as_string(); return revisionOnCoordinator(databaseName, cid, rid); } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock collectionRevision //////////////////////////////////////////////////////////////////////////////// static void JS_RevisionVocbaseCol( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); arangodb::LogicalCollection* collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } TRI_voc_rid_t revisionId; int res; if (ServerState::instance()->isCoordinator()) { res = GetRevisionCoordinator(collection, revisionId); } else { res = GetRevision(collection, revisionId); } if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } std::string ridString = TRI_RidToString(revisionId); TRI_V8_RETURN(TRI_V8_STD_STRING(ridString)); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief retrieves a collection from a V8 argument //////////////////////////////////////////////////////////////////////////////// static arangodb::LogicalCollection* GetCollectionFromArgument( TRI_vocbase_t* vocbase, v8::Handle const val) { // number if (val->IsNumber() || val->IsNumberObject()) { uint64_t cid = TRI_ObjectToUInt64(val, true); return vocbase->lookupCollection(cid); } return vocbase->lookupCollection(TRI_ObjectToString(val)); } //////////////////////////////////////////////////////////////////////////////// /// @brief __save(collection, document). This method is used internally and not /// part of the public API //////////////////////////////////////////////////////////////////////////////// static void JS_SaveVocbase(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } if (vocbase->isDropped()) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } // expecting two arguments if (args.Length() != 2) { TRI_V8_THROW_EXCEPTION_USAGE("__save(, )"); } v8::Handle val = args[0]; if (!val->IsString()) { // invalid value type. Collection Name must be a string TRI_V8_THROW_EXCEPTION_PARAMETER(" must be a string"); } std::string const collectionName = TRI_ObjectToString(val); // We cannot give any options here. Use default. OperationOptions options; VPackBuilder builder; v8::Handle doc = args[1]; if (!doc->IsObject()) { // invalid value type. must be a document TRI_V8_THROW_EXCEPTION_PARAMETER(" must be a document"); } int res = TRI_V8ToVPack(isolate, builder, doc, false); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } // load collection auto transactionContext(transaction::V8Context::Create(vocbase, true)); SingleCollectionTransaction trx(transactionContext, collectionName, AccessMode::Type::WRITE); trx.addHint(transaction::Hints::Hint::SINGLE_OPERATION); res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } OperationResult result = trx.insert(collectionName, builder.slice(), options); res = trx.finish(result.code); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } VPackSlice resultSlice = result.slice(); TRI_V8_RETURN(TRI_VPackToV8(isolate, resultSlice, transactionContext->getVPackOptions())); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief inserts a document, using VPack //////////////////////////////////////////////////////////////////////////////// static void JS_InsertVocbaseCol( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); auto collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } bool const isEdgeCollection = ((TRI_col_type_e)collection->type() == TRI_COL_TYPE_EDGE); uint32_t const argLength = args.Length(); // Position of and // They differ for edge (old signature) and document. uint32_t docIdx = 0; uint32_t optsIdx = 1; TRI_GET_GLOBALS(); if (argLength < 1) { TRI_V8_THROW_EXCEPTION_USAGE("insert(, [, ])"); } bool oldEdgeSignature = false; if (isEdgeCollection && argLength >= 3) { oldEdgeSignature = true; if (argLength > 4) { TRI_V8_THROW_EXCEPTION_USAGE( "insert(, , [, ])"); } docIdx = 2; optsIdx = 3; if (args[2]->IsArray()) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); } } else { if (argLength < 1 || argLength > 2) { TRI_V8_THROW_EXCEPTION_USAGE("insert( [, ])"); } } OperationOptions options; if (argLength > optsIdx && args[optsIdx]->IsObject()) { v8::Handle optionsObject = args[optsIdx].As(); TRI_GET_GLOBAL_STRING(WaitForSyncKey); if (optionsObject->Has(WaitForSyncKey)) { options.waitForSync = TRI_ObjectToBoolean(optionsObject->Get(WaitForSyncKey)); } TRI_GET_GLOBAL_STRING(SilentKey); if (optionsObject->Has(SilentKey)) { options.silent = TRI_ObjectToBoolean(optionsObject->Get(SilentKey)); } TRI_GET_GLOBAL_STRING(ReturnNewKey); if (optionsObject->Has(ReturnNewKey)) { options.returnNew = TRI_ObjectToBoolean(optionsObject->Get(ReturnNewKey)); } TRI_GET_GLOBAL_STRING(IsRestoreKey); if (optionsObject->Has(IsRestoreKey)) { options.isRestore = TRI_ObjectToBoolean(optionsObject->Get(IsRestoreKey)); } } else { options.waitForSync = ExtractWaitForSync(args, optsIdx + 1); } if (!args[docIdx]->IsObject()) { // invalid value type. must be a document TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID); } // copy default options (and set exclude handler in copy) VPackOptions vpackOptions = VPackOptions::Defaults; vpackOptions.attributeExcludeHandler = basics::VelocyPackHelper::getExcludeHandler(); VPackBuilder builder(&vpackOptions); auto doOneDocument = [&](v8::Handle obj) -> void { int res = TRI_V8ToVPack(isolate, builder, obj, true); if (res != TRI_ERROR_NO_ERROR) { THROW_ARANGO_EXCEPTION(res); } if (isEdgeCollection && oldEdgeSignature) { // Just insert from and to. Check is done later. std::string tmpId(ExtractIdString(isolate, args[0])); if (tmpId.empty()) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD); } builder.add(StaticStrings::FromString, VPackValue(tmpId)); tmpId = ExtractIdString(isolate, args[1]); if (tmpId.empty()) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD); } builder.add(StaticStrings::ToString, VPackValue(tmpId)); } builder.close(); }; v8::Handle payload = args[docIdx]; bool payloadIsArray; if (payload->IsArray()) { payloadIsArray = true; 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 { payloadIsArray = false; doOneDocument(payload); } // load collection auto transactionContext = std::make_shared(collection->vocbase(), true); SingleCollectionTransaction trx(transactionContext, collection->cid(), AccessMode::Type::WRITE); if (!payloadIsArray) { trx.addHint(transaction::Hints::Hint::SINGLE_OPERATION); } int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } OperationResult result = trx.insert(collection->name(), builder.slice(), options); res = trx.finish(result.code); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } if (options.silent) { // no return value TRI_V8_RETURN_TRUE(); } VPackSlice resultSlice = result.slice(); auto v8Result = TRI_VPackToV8(isolate, resultSlice, transactionContext->getVPackOptions()); TRI_V8_RETURN(v8Result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the status of a collection //////////////////////////////////////////////////////////////////////////////// static void JS_StatusVocbaseCol( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); arangodb::LogicalCollection* collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } if (ServerState::instance()->isCoordinator()) { std::string const databaseName(collection->dbName()); try { std::shared_ptr const ci = ClusterInfo::instance()->getCollection(databaseName, collection->cid_as_string()); TRI_V8_RETURN(v8::Number::New(isolate, (int)ci->getStatusLocked())); } catch (...) { TRI_V8_RETURN(v8::Number::New(isolate, (int)TRI_VOC_COL_STATUS_DELETED)); } } // fallthru intentional TRI_vocbase_col_status_e status = collection->getStatusLocked(); TRI_V8_RETURN(v8::Number::New(isolate, (int)status)); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief truncates a collection //////////////////////////////////////////////////////////////////////////////// static void JS_TruncateVocbaseCol( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); OperationOptions opOptions; opOptions.waitForSync = ExtractWaitForSync(args, 1); arangodb::LogicalCollection* collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } SingleCollectionTransaction trx( transaction::V8Context::Create(collection->vocbase(), true), collection->cid(), AccessMode::Type::WRITE); int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } OperationResult result = trx.truncate(collection->name(), opOptions); res = trx.finish(result.code); if (result.failed()) { TRI_V8_THROW_EXCEPTION(result.code); } if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } TRI_V8_RETURN_UNDEFINED(); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock collectionType //////////////////////////////////////////////////////////////////////////////// static void JS_TypeVocbaseCol(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); arangodb::LogicalCollection* collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } if (ServerState::instance()->isCoordinator()) { std::string const databaseName = collection->dbName(); try { std::shared_ptr const ci = ClusterInfo::instance()->getCollection(databaseName, collection->cid_as_string()); TRI_V8_RETURN(v8::Number::New(isolate, (int)ci->type())); } catch (...) { TRI_V8_RETURN(v8::Number::New(isolate, (int)collection->type())); } } // fallthru intentional TRI_col_type_e type = collection->type(); TRI_V8_RETURN(v8::Number::New(isolate, (int)type)); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock collectionUnload //////////////////////////////////////////////////////////////////////////////// static void JS_UnloadVocbaseCol( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); arangodb::LogicalCollection* collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } int res; if (ServerState::instance()->isCoordinator()) { res = #ifdef USE_ENTERPRISE ULVocbaseColCoordinatorEnterprise( collection->dbName(), collection->cid_as_string(), TRI_VOC_COL_STATUS_UNLOADED); #else ULVocbaseColCoordinator( collection->dbName(), collection->cid_as_string(), TRI_VOC_COL_STATUS_UNLOADED); #endif } else { res = collection->vocbase()->unloadCollection(collection, false); } if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } TRI_V8_RETURN_UNDEFINED(); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the version of a collection //////////////////////////////////////////////////////////////////////////////// static void JS_VersionVocbaseCol( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); arangodb::LogicalCollection* collection = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (collection == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } TRI_V8_RETURN(v8::Number::New(isolate, collection->version())); TRI_V8_TRY_CATCH_END } static void JS_ChangeOperationModeVocbase( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); TRI_GET_GLOBALS(); bool allowModeChange = false; TRI_GET_GLOBAL(_currentRequest, v8::Value); if (_currentRequest.IsEmpty() || _currentRequest->IsUndefined()) { // console mode allowModeChange = true; } else if (_currentRequest->IsObject()) { v8::Handle obj = v8::Handle::Cast(_currentRequest); TRI_GET_GLOBAL_STRING(PortTypeKey); if (obj->Has(PortTypeKey)) { std::string const portType = TRI_ObjectToString(obj->Get(PortTypeKey)); if (portType == "unix") { allowModeChange = true; } } } if (!allowModeChange) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_FORBIDDEN); } // expecting one argument if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE( "_changeMode(), with modes: 'Normal', 'NoCreate'"); } std::string const newModeStr = TRI_ObjectToString(args[0]); TRI_vocbase_operationmode_e newMode = TRI_VOCBASE_MODE_NORMAL; if (newModeStr == "NoCreate") { newMode = TRI_VOCBASE_MODE_NO_CREATE; } else if (newModeStr != "Normal") { TRI_V8_THROW_EXCEPTION_USAGE( "illegal mode, allowed modes are: 'Normal' and 'NoCreate'"); } TRI_ChangeOperationModeServer(newMode); TRI_V8_RETURN_TRUE(); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock collectionDatabaseName //////////////////////////////////////////////////////////////////////////////// static void JS_CollectionVocbase( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } if (vocbase->isDropped()) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } // expecting one argument if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("_collection(|)"); } v8::Handle val = args[0]; arangodb::LogicalCollection const* collection = nullptr; if (ServerState::instance()->isCoordinator()) { std::string const name = TRI_ObjectToString(val); try { std::shared_ptr const ci = ClusterInfo::instance()->getCollection(vocbase->name(), name); auto colCopy = ci->clone(); collection = colCopy.release(); } catch (...) { // not found TRI_V8_RETURN_NULL(); } } else { collection = GetCollectionFromArgument(vocbase, val); } if (collection == nullptr) { TRI_V8_RETURN_NULL(); } v8::Handle result = WrapCollection(isolate, collection); if (result.IsEmpty()) { TRI_V8_THROW_EXCEPTION_MEMORY(); } TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock collectionDatabaseNameAll //////////////////////////////////////////////////////////////////////////////// static void JS_CollectionsVocbase( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } std::vector colls; // if we are a coordinator, we need to fetch the collection info from the // agency if (ServerState::instance()->isCoordinator()) { colls = GetCollectionsCluster(vocbase); } else { colls = vocbase->collections(false); } std::sort(colls.begin(), colls.end(), [](LogicalCollection* lhs, LogicalCollection* rhs) -> bool { return StringUtils::tolower(lhs->name()) < StringUtils::tolower(rhs->name()); }); bool error = false; // already create an array of the correct size v8::Handle result = v8::Array::New(isolate); size_t const n = colls.size(); for (size_t i = 0; i < n; ++i) { auto collection = colls[i]; v8::Handle c = WrapCollection(isolate, collection); if (c.IsEmpty()) { error = true; break; } result->Set(static_cast(i), c); } if (error) { TRI_V8_THROW_EXCEPTION_MEMORY(); } TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief returns all collection names //////////////////////////////////////////////////////////////////////////////// static void JS_CompletionsVocbase( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_RETURN(v8::Array::New(isolate)); } std::vector names; if (ServerState::instance()->isCoordinator()) { if (ClusterInfo::instance()->doesDatabaseExist(vocbase->name())) { names = GetCollectionNamesCluster(vocbase); } } else { names = vocbase->collectionNames(); } uint32_t j = 0; v8::Handle result = v8::Array::New(isolate); // add collection names for (auto& name : names) { result->Set(j++, TRI_V8_STD_STRING(name)); } // add function names. these are hard coded result->Set(j++, TRI_V8_ASCII_STRING("_changeMode()")); result->Set(j++, TRI_V8_ASCII_STRING("_collection()")); result->Set(j++, TRI_V8_ASCII_STRING("_collections()")); result->Set(j++, TRI_V8_ASCII_STRING("_create()")); result->Set(j++, TRI_V8_ASCII_STRING("_createDatabase()")); result->Set(j++, TRI_V8_ASCII_STRING("_createDocumentCollection()")); result->Set(j++, TRI_V8_ASCII_STRING("_createEdgeCollection()")); result->Set(j++, TRI_V8_ASCII_STRING("_createView()")); result->Set(j++, TRI_V8_ASCII_STRING("_createStatement()")); result->Set(j++, TRI_V8_ASCII_STRING("_document()")); result->Set(j++, TRI_V8_ASCII_STRING("_drop()")); result->Set(j++, TRI_V8_ASCII_STRING("_dropDatabase()")); result->Set(j++, TRI_V8_ASCII_STRING("_dropView()")); result->Set(j++, TRI_V8_ASCII_STRING("_executeTransaction()")); result->Set(j++, TRI_V8_ASCII_STRING("_exists()")); result->Set(j++, TRI_V8_ASCII_STRING("_id")); result->Set(j++, TRI_V8_ASCII_STRING("_isSystem()")); result->Set(j++, TRI_V8_ASCII_STRING("_databases()")); result->Set(j++, TRI_V8_ASCII_STRING("_engine()")); result->Set(j++, TRI_V8_ASCII_STRING("_name()")); result->Set(j++, TRI_V8_ASCII_STRING("_path()")); result->Set(j++, TRI_V8_ASCII_STRING("_pregelStart()")); result->Set(j++, TRI_V8_ASCII_STRING("_pregelStatus()")); result->Set(j++, TRI_V8_ASCII_STRING("_pregelStop()")); result->Set(j++, TRI_V8_ASCII_STRING("_query()")); result->Set(j++, TRI_V8_ASCII_STRING("_remove()")); result->Set(j++, TRI_V8_ASCII_STRING("_replace()")); result->Set(j++, TRI_V8_ASCII_STRING("_update()")); result->Set(j++, TRI_V8_ASCII_STRING("_useDatabase()")); result->Set(j++, TRI_V8_ASCII_STRING("_version()")); result->Set(j++, TRI_V8_ASCII_STRING("_view()")); result->Set(j++, TRI_V8_ASCII_STRING("_views()")); TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock documentsDocumentRemove //////////////////////////////////////////////////////////////////////////////// static void JS_RemoveVocbase(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); return RemoveVocbase(args); // cppcheck-suppress style TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock documentsDocumentName //////////////////////////////////////////////////////////////////////////////// static void JS_DocumentVocbase( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); DocumentVocbase(args); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock documentsDocumentExists //////////////////////////////////////////////////////////////////////////////// static void JS_ExistsVocbase(v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); return ExistsVocbaseVPack(false, args); // cppcheck-suppress style TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock collectionCount //////////////////////////////////////////////////////////////////////////////// static void JS_CountVocbaseCol( v8::FunctionCallbackInfo const& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); arangodb::LogicalCollection const* col = TRI_UnwrapClass(args.Holder(), WRP_VOCBASE_COL_TYPE); if (col == nullptr) { TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } if (args.Length() > 1) { TRI_V8_THROW_EXCEPTION_USAGE("count()"); } bool details = false; if (args.Length() == 1 && ServerState::instance()->isCoordinator()) { details = TRI_ObjectToBoolean(args[0]); } TRI_vocbase_t* vocbase = col->vocbase(); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } std::string collectionName(col->name()); SingleCollectionTransaction trx(transaction::V8Context::Create(vocbase, true), collectionName, AccessMode::Type::READ); int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } OperationResult opResult = trx.count(collectionName, !details); res = trx.finish(opResult.code); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } VPackSlice s = opResult.slice(); if (details) { TRI_ASSERT(s.isObject()); v8::Handle result = TRI_VPackToV8(isolate, s); TRI_V8_RETURN(result); } else { TRI_ASSERT(s.isNumber()); TRI_V8_RETURN(v8::Number::New(isolate, static_cast(s.getNumber()))); } TRI_V8_TRY_CATCH_END } // ............................................................................. // generate the arangodb::LogicalCollection template // ............................................................................. void TRI_InitV8Collections(v8::Handle context, TRI_vocbase_t* vocbase, TRI_v8_global_t* v8g, v8::Isolate* isolate, v8::Handle ArangoDBNS) { TRI_AddMethodVocbase(isolate, ArangoDBNS, TRI_V8_ASCII_STRING("_changeMode"), JS_ChangeOperationModeVocbase); TRI_AddMethodVocbase(isolate, ArangoDBNS, TRI_V8_ASCII_STRING("_collection"), JS_CollectionVocbase); TRI_AddMethodVocbase(isolate, ArangoDBNS, TRI_V8_ASCII_STRING("_collections"), JS_CollectionsVocbase); TRI_AddMethodVocbase(isolate, ArangoDBNS, TRI_V8_ASCII_STRING("_COMPLETIONS"), JS_CompletionsVocbase, true); TRI_AddMethodVocbase(isolate, ArangoDBNS, TRI_V8_ASCII_STRING("_document"), JS_DocumentVocbase); TRI_AddMethodVocbase(isolate, ArangoDBNS, TRI_V8_ASCII_STRING("_exists"), JS_ExistsVocbase); TRI_AddMethodVocbase(isolate, ArangoDBNS, TRI_V8_ASCII_STRING("_remove"), JS_RemoveVocbase); TRI_AddMethodVocbase(isolate, ArangoDBNS, TRI_V8_ASCII_STRING("_replace"), JS_ReplaceVocbase); TRI_AddMethodVocbase(isolate, ArangoDBNS, TRI_V8_ASCII_STRING("_update"), JS_UpdateVocbase); TRI_AddMethodVocbase(isolate, ArangoDBNS, TRI_V8_ASCII_STRING("_pregelStart"), JS_PregelStart); TRI_AddMethodVocbase(isolate, ArangoDBNS, TRI_V8_ASCII_STRING("_pregelStatus"), JS_PregelStatus); TRI_AddMethodVocbase(isolate, ArangoDBNS, TRI_V8_ASCII_STRING("_pregelCancel"), JS_PregelCancel); TRI_AddMethodVocbase(isolate, ArangoDBNS, TRI_V8_ASCII_STRING("_pregelAqlResult"), JS_PregelAQLResult); // an internal API used for storing a document without wrapping a V8 // collection object TRI_AddMethodVocbase(isolate, ArangoDBNS, TRI_V8_ASCII_STRING("__save"), JS_SaveVocbase, true); v8::Handle rt; v8::Handle ft; ft = v8::FunctionTemplate::New(isolate); ft->SetClassName(TRI_V8_ASCII_STRING("ArangoCollection")); rt = ft->InstanceTemplate(); rt->SetInternalFieldCount(3); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("count"), JS_CountVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("document"), JS_DocumentVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("drop"), JS_DropVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("exists"), JS_ExistsVocbaseVPack); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("figures"), JS_FiguresVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("insert"), JS_InsertVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("leaderResign"), JS_LeaderResign, true); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("assumeLeadership"), JS_AssumeLeadership, true); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("isLeader"), JS_IsLeader, true); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("getFollowers"), JS_GetFollowers, true); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("load"), JS_LoadVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("name"), JS_NameVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("path"), JS_PathVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("planId"), JS_PlanIdVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("properties"), JS_PropertiesVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("remove"), JS_RemoveVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("revision"), JS_RevisionVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("rename"), JS_RenameVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("replace"), JS_ReplaceVocbaseCol); TRI_AddMethodVocbase( isolate, rt, TRI_V8_ASCII_STRING("save"), JS_InsertVocbaseCol); // note: save is now an alias for insert TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("status"), JS_StatusVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("TRUNCATE"), JS_TruncateVocbaseCol, true); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("type"), JS_TypeVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("unload"), JS_UnloadVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("update"), JS_UpdateVocbaseCol); TRI_AddMethodVocbase(isolate, rt, TRI_V8_ASCII_STRING("version"), JS_VersionVocbaseCol); TRI_InitV8IndexCollection(isolate, rt); v8g->VocbaseColTempl.Reset(isolate, rt); TRI_AddGlobalFunctionVocbase(isolate, TRI_V8_ASCII_STRING("ArangoCollection"), ft->GetFunction()); }