//////////////////////////////////////////////////////////////////////////////// /// @brief V8-vocbase bridge /// /// @file /// /// DISCLAIMER /// /// Copyright 2014 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 /// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include #include "v8-vocbaseprivate.h" #include "Aql/Query.h" #include "Aql/QueryCache.h" #include "Aql/QueryList.h" #include "Aql/QueryRegistry.h" #include "Basics/conversions.h" #include "Basics/json-utilities.h" #include "Basics/MutexLocker.h" #include "Basics/ScopeGuard.h" #include "Basics/Utf8Helper.h" #include "Cluster/AgencyComm.h" #include "Cluster/ClusterComm.h" #include "Cluster/ClusterInfo.h" #include "Cluster/ClusterMethods.h" #include "Cluster/ServerState.h" #include "HttpServer/ApplicationEndpointServer.h" #include "RestServer/ConsoleThread.h" #include "RestServer/VocbaseContext.h" #include "Utils/transactions.h" #include "Utils/V8ResolverGuard.h" #include "V8/JSLoader.h" #include "V8/v8-conv.h" #include "V8/v8-utils.h" #include "V8/V8LineEditor.h" #include "V8Server/v8-collection.h" #include "V8Server/v8-replication.h" #include "V8Server/v8-statistics.h" #include "V8Server/v8-voccursor.h" #include "V8Server/v8-vocindex.h" #include "V8Server/v8-wrapshapedjson.h" #include "V8Server/V8Traverser.h" #include "VocBase/auth.h" #include "VocBase/KeyGenerator.h" #include "VocBase/VocShaper.h" #include "Wal/LogfileManager.h" #include #include #include #include using namespace std; using namespace triagens::basics; using namespace triagens::arango; using namespace triagens::rest; using namespace arangodb; // ----------------------------------------------------------------------------- // --SECTION-- forward declarations // ----------------------------------------------------------------------------- extern bool TRI_ENABLE_STATISTICS; // ----------------------------------------------------------------------------- // --SECTION-- private constants // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief wrapped class for TRI_vocbase_t /// /// Layout: /// - SLOT_CLASS_TYPE /// - SLOT_CLASS //////////////////////////////////////////////////////////////////////////////// int32_t const WRP_VOCBASE_TYPE = 1; //////////////////////////////////////////////////////////////////////////////// /// @brief wrapped class for TRI_vocbase_col_t /// /// Layout: /// - SLOT_CLASS_TYPE /// - SLOT_CLASS /// - SLOT_COLLECTION //////////////////////////////////////////////////////////////////////////////// int32_t const WRP_VOCBASE_COL_TYPE = 2; // ----------------------------------------------------------------------------- // --SECTION-- private constants // ----------------------------------------------------------------------------- struct CollectionDitchInfo { triagens::arango::DocumentDitch* ditch; TRI_transaction_collection_t* col; CollectionDitchInfo (triagens::arango::DocumentDitch* ditch, TRI_transaction_collection_t* col) : ditch(ditch), col(col) { } }; // ----------------------------------------------------------------------------- // --SECTION-- HELPER FUNCTIONS // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief wraps a C++ into a v8::Object //////////////////////////////////////////////////////////////////////////////// template static v8::Handle WrapClass (v8::Isolate *isolate, v8::Persistent& classTempl, int32_t type, T* y) { v8::EscapableHandleScope scope(isolate); auto localClassTemplate = v8::Local::New(isolate, classTempl); // create the new handle to return, and set its template type v8::Handle result = localClassTemplate->NewInstance(); if (result.IsEmpty()) { // error return scope.Escape(result); } // set the c++ pointer for unwrapping later result->SetInternalField(SLOT_CLASS_TYPE, v8::Integer::New(isolate, type)); result->SetInternalField(SLOT_CLASS, v8::External::New(isolate, y)); return scope.Escape(result); } // ----------------------------------------------------------------------------- // --SECTION-- javascript functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief executes a transaction //////////////////////////////////////////////////////////////////////////////// static void JS_Transaction (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() != 1 || ! args[0]->IsObject()) { TRI_V8_THROW_EXCEPTION_USAGE("TRANSACTION()"); } TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } // treat the argument as an object from now on v8::Handle object = v8::Handle::Cast(args[0]); // extract the properties from the object // "lockTimeout" double lockTimeout = (double) (TRI_TRANSACTION_DEFAULT_LOCK_TIMEOUT / 1000000ULL); if (object->Has(TRI_V8_ASCII_STRING("lockTimeout"))) { static const string timeoutError = " must be a valid numeric value"; if (! object->Get(TRI_V8_ASCII_STRING("lockTimeout"))->IsNumber()) { TRI_V8_THROW_EXCEPTION_PARAMETER(timeoutError); } lockTimeout = TRI_ObjectToDouble(object->Get(TRI_V8_ASCII_STRING("lockTimeout"))); if (lockTimeout < 0.0) { TRI_V8_THROW_EXCEPTION_PARAMETER(timeoutError); } } // "waitForSync" bool waitForSync = false; TRI_GET_GLOBALS(); TRI_GET_GLOBAL_STRING(WaitForSyncKey); if (object->Has(WaitForSyncKey)) { if (! object->Get(WaitForSyncKey)->IsBoolean() && ! object->Get(WaitForSyncKey)->IsBooleanObject()) { TRI_V8_THROW_EXCEPTION_PARAMETER(" must be a boolean value"); } waitForSync = TRI_ObjectToBoolean(WaitForSyncKey); } // "collections" static string const collectionError = "missing/invalid collections definition for transaction"; if (! object->Has(TRI_V8_ASCII_STRING("collections")) || ! object->Get(TRI_V8_ASCII_STRING("collections"))->IsObject()) { TRI_V8_THROW_EXCEPTION_PARAMETER(collectionError); } // extract collections v8::Handle collections = v8::Handle::Cast(object->Get(TRI_V8_ASCII_STRING("collections"))); if (collections.IsEmpty()) { TRI_V8_THROW_EXCEPTION_PARAMETER(collectionError); } bool isValid = true; vector readCollections; vector writeCollections; // collections.read if (collections->Has(TRI_V8_ASCII_STRING("read"))) { if (collections->Get(TRI_V8_ASCII_STRING("read"))->IsArray()) { v8::Handle names = v8::Handle::Cast(collections->Get(TRI_V8_ASCII_STRING("read"))); for (uint32_t i = 0 ; i < names->Length(); ++i) { v8::Handle collection = names->Get(i); if (! collection->IsString()) { isValid = false; break; } readCollections.emplace_back(TRI_ObjectToString(collection)); } } else if (collections->Get(TRI_V8_ASCII_STRING("read"))->IsString()) { readCollections.emplace_back(TRI_ObjectToString(collections->Get(TRI_V8_ASCII_STRING("read")))); } else { isValid = false; } } // collections.write if (collections->Has(TRI_V8_ASCII_STRING("write"))) { if (collections->Get(TRI_V8_ASCII_STRING("write"))->IsArray()) { v8::Handle names = v8::Handle::Cast(collections->Get(TRI_V8_ASCII_STRING("write"))); for (uint32_t i = 0 ; i < names->Length(); ++i) { v8::Handle collection = names->Get(i); if (! collection->IsString()) { isValid = false; break; } writeCollections.emplace_back(TRI_ObjectToString(collection)); } } else if (collections->Get(TRI_V8_ASCII_STRING("write"))->IsString()) { writeCollections.emplace_back(TRI_ObjectToString(collections->Get(TRI_V8_ASCII_STRING("write")))); } else { isValid = false; } } if (! isValid) { TRI_V8_THROW_EXCEPTION_PARAMETER(collectionError); } // extract the "action" property static const string actionErrorPrototype = "missing/invalid action definition for transaction"; string actionError = actionErrorPrototype; if (! object->Has(TRI_V8_ASCII_STRING("action"))) { TRI_V8_THROW_EXCEPTION_PARAMETER(actionError); } // function parameters v8::Handle params; if (object->Has(TRI_V8_ASCII_STRING("params"))) { params = v8::Handle::Cast(object->Get(TRI_V8_ASCII_STRING("params"))); } else { params = v8::Undefined(isolate); } if (params.IsEmpty()) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_INTERNAL); } bool embed = false; if (object->Has(TRI_V8_ASCII_STRING("embed"))) { v8::Handle v = v8::Handle::Cast(object->Get(TRI_V8_ASCII_STRING("embed"))); embed = TRI_ObjectToBoolean(v); } v8::Handle current = isolate->GetCurrentContext()->Global(); // callback function v8::Handle action; if (object->Get(TRI_V8_ASCII_STRING("action"))->IsFunction()) { action = v8::Handle::Cast(object->Get(TRI_V8_ASCII_STRING("action"))); } else if (object->Get(TRI_V8_ASCII_STRING("action"))->IsString()) { v8::TryCatch tryCatch; // get built-in Function constructor (see ECMA-262 5th edition 15.3.2) v8::Local ctor = v8::Local::Cast(current->Get(TRI_V8_ASCII_STRING("Function"))); // Invoke Function constructor to create function with the given body and no arguments string body = TRI_ObjectToString(object->Get(TRI_V8_ASCII_STRING("action"))->ToString()); body = "return (" + body + ")(params);"; v8::Handle args[2] = { TRI_V8_ASCII_STRING("params"), TRI_V8_STD_STRING(body) }; v8::Local function = ctor->NewInstance(2, args); action = v8::Local::Cast(function); if (tryCatch.HasCaught()) { actionError += " - "; actionError += *v8::String::Utf8Value(tryCatch.Message()->Get()); actionError += " - "; actionError += *v8::String::Utf8Value(tryCatch.StackTrace()); TRI_CreateErrorObject(isolate, TRI_ERROR_BAD_PARAMETER, actionError); tryCatch.ReThrow(); return; } } else { TRI_V8_THROW_EXCEPTION_PARAMETER(actionError); } if (action.IsEmpty()) { TRI_V8_THROW_EXCEPTION_PARAMETER(actionError); } // start actual transaction ExplicitTransaction trx(vocbase, readCollections, writeCollections, lockTimeout, waitForSync, embed); int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } v8::Handle result; try { v8::TryCatch tryCatch; v8::Handle arguments = params; result = action->Call(current, 1, &arguments); if (tryCatch.HasCaught()) { trx.abort(); if (tryCatch.CanContinue()) { tryCatch.ReThrow(); return; } else { v8g->_canceled = true; TRI_V8_RETURN(result); } } } catch (triagens::basics::Exception const& ex) { TRI_V8_THROW_EXCEPTION_MESSAGE(ex.code(), ex.what()); } catch (std::bad_alloc const&) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } catch (std::exception const& ex) { TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, ex.what()); } catch (...) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_INTERNAL); } res = trx.commit(); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief retrieves the configuration of the write-ahead log /// @startDocuBlock walPropertiesGet /// `internal.wal.properties()` /// /// Retrieves the configuration of the write-ahead log. The result is a JSON /// array with the following attributes: /// - *allowOversizeEntries*: whether or not operations that are bigger than a /// single logfile can be executed and stored /// - *logfileSize*: the size of each write-ahead logfile /// - *historicLogfiles*: the maximum number of historic logfiles to keep /// - *reserveLogfiles*: the maximum number of reserve logfiles that ArangoDB /// allocates in the background /// - *syncInterval*: the interval for automatic synchronization of not-yet /// synchronized write-ahead log data (in milliseconds) /// - *throttleWait*: the maximum wait time that operations will wait before /// they get aborted if case of write-throttling (in milliseconds) /// - *throttleWhenPending*: the number of unprocessed garbage-collection /// operations that, when reached, will activate write-throttling. A value of /// *0* means that write-throttling will not be triggered. /// /// @EXAMPLES /// /// @EXAMPLE_ARANGOSH_OUTPUT{WalPropertiesGet} /// require("internal").wal.properties(); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief configures the write-ahead log /// @startDocuBlock walPropertiesSet /// `internal.wal.properties(properties)` /// /// Configures the behavior of the write-ahead log. *properties* must be a JSON /// JSON object with the following attributes: /// - *allowOversizeEntries*: whether or not operations that are bigger than a /// single logfile can be executed and stored /// - *logfileSize*: the size of each write-ahead logfile /// - *historicLogfiles*: the maximum number of historic logfiles to keep /// - *reserveLogfiles*: the maximum number of reserve logfiles that ArangoDB /// allocates in the background /// - *throttleWait*: the maximum wait time that operations will wait before /// they get aborted if case of write-throttling (in milliseconds) /// - *throttleWhenPending*: the number of unprocessed garbage-collection /// operations that, when reached, will activate write-throttling. A value of /// *0* means that write-throttling will not be triggered. /// /// Specifying any of the above attributes is optional. Not specified attributes /// will be ignored and the configuration for them will not be modified. /// /// @EXAMPLES /// /// @EXAMPLE_ARANGOSH_OUTPUT{WalPropertiesSet} /// require("internal").wal.properties({ allowOverSizeEntries: true, logfileSize: 32 * 1024 * 1024 }); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// static void JS_PropertiesWal (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() > 1 || (args.Length() == 1 && ! args[0]->IsObject())) { TRI_V8_THROW_EXCEPTION_USAGE("properties()"); } auto l = triagens::wal::LogfileManager::instance(); if (args.Length() == 1) { // set the properties v8::Handle object = v8::Handle::Cast(args[0]); if (object->Has(TRI_V8_ASCII_STRING("allowOversizeEntries"))) { bool value = TRI_ObjectToBoolean(object->Get(TRI_V8_ASCII_STRING("allowOversizeEntries"))); l->allowOversizeEntries(value); } if (object->Has(TRI_V8_ASCII_STRING("logfileSize"))) { uint32_t value = static_cast(TRI_ObjectToUInt64(object->Get(TRI_V8_ASCII_STRING("logfileSize")), true)); l->filesize(value); } if (object->Has(TRI_V8_ASCII_STRING("historicLogfiles"))) { uint32_t value = static_cast(TRI_ObjectToUInt64(object->Get(TRI_V8_ASCII_STRING("historicLogfiles")), true)); l->historicLogfiles(value); } if (object->Has(TRI_V8_ASCII_STRING("reserveLogfiles"))) { uint32_t value = static_cast(TRI_ObjectToUInt64(object->Get(TRI_V8_ASCII_STRING("reserveLogfiles")), true)); l->reserveLogfiles(value); } if (object->Has(TRI_V8_ASCII_STRING("throttleWait"))) { uint64_t value = TRI_ObjectToUInt64(object->Get(TRI_V8_ASCII_STRING("throttleWait")), true); l->maxThrottleWait(value); } if (object->Has(TRI_V8_ASCII_STRING("throttleWhenPending"))) { uint64_t value = TRI_ObjectToUInt64(object->Get(TRI_V8_ASCII_STRING("throttleWhenPending")), true); l->throttleWhenPending(value); } } v8::Handle result = v8::Object::New(isolate); result->Set(TRI_V8_ASCII_STRING("allowOversizeEntries"), v8::Boolean::New(isolate, l->allowOversizeEntries())); result->Set(TRI_V8_ASCII_STRING("logfileSize"), v8::Number::New(isolate, l->filesize())); result->Set(TRI_V8_ASCII_STRING("historicLogfiles"), v8::Number::New(isolate, l->historicLogfiles())); result->Set(TRI_V8_ASCII_STRING("reserveLogfiles"), v8::Number::New(isolate, l->reserveLogfiles())); result->Set(TRI_V8_ASCII_STRING("syncInterval"), v8::Number::New(isolate, (double) l->syncInterval())); result->Set(TRI_V8_ASCII_STRING("throttleWait"), v8::Number::New(isolate, (double) l->maxThrottleWait())); result->Set(TRI_V8_ASCII_STRING("throttleWhenPending"), v8::Number::New(isolate, (double) l->throttleWhenPending())); TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief flushes the currently open WAL logfile /// @startDocuBlock walFlush /// `internal.wal.flush(waitForSync, waitForCollector)` /// /// Flushes the write-ahead log. By flushing the currently active write-ahead /// logfile, the data in it can be transferred to collection journals and /// datafiles. This is useful to ensure that all data for a collection is /// present in the collection journals and datafiles, for example, when dumping /// the data of a collection. /// /// The *waitForSync* option determines whether or not the operation should /// block until the not-yet synchronized data in the write-ahead log was /// synchronized to disk. /// /// The *waitForCollector* operation can be used to specify that the operation /// should block until the data in the flushed log has been collected by the /// write-ahead log garbage collector. Note that setting this option to *true* /// might block for a long time if there are long-running transactions and /// the write-ahead log garbage collector cannot finish garbage collection. /// /// @EXAMPLES /// /// @EXAMPLE_ARANGOSH_OUTPUT{WalFlush} /// require("internal").wal.flush(); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// static void JS_FlushWal (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); bool waitForSync = false; if (args.Length() > 0) { waitForSync = TRI_ObjectToBoolean(args[0]); } bool waitForCollector = false; if (args.Length() > 1) { waitForCollector = TRI_ObjectToBoolean(args[1]); } bool writeShutdownFile = false; if (args.Length() > 2) { writeShutdownFile = TRI_ObjectToBoolean(args[2]); } int res; if (ServerState::instance()->isCoordinator()) { res = flushWalOnAllDBServers( waitForSync, waitForCollector ); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } TRI_V8_RETURN_TRUE(); } res = triagens::wal::LogfileManager::instance()->flush(waitForSync, waitForCollector, writeShutdownFile); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } TRI_V8_RETURN_TRUE(); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief normalize UTF 16 strings //////////////////////////////////////////////////////////////////////////////// static void JS_NormalizeString (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("NORMALIZE_STRING()"); } TRI_normalize_V8_Obj(args, args[0]); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief enables or disables native backtrace //////////////////////////////////////////////////////////////////////////////// static void JS_EnableNativeBacktraces (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("ENABLE_NATIVE_BACKTRACES()"); } triagens::basics::Exception::SetVerbose(TRI_ObjectToBoolean(args[0])); TRI_V8_RETURN_UNDEFINED(); TRI_V8_TRY_CATCH_END } extern V8LineEditor* theConsole; //////////////////////////////////////////////////////////////////////////////// /// @brief starts a debugging console //////////////////////////////////////////////////////////////////////////////// static void JS_Debug (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::Local name(TRI_V8_ASCII_STRING("debug loop")); v8::Local debug(TRI_V8_ASCII_STRING("debug")); v8::Local callerScope; if (args.Length() >= 1) { TRI_AddGlobalVariableVocbase(isolate, isolate->GetCurrentContext(), debug, args[0]); } MUTEX_LOCKER(ConsoleThread::serverConsoleMutex); V8LineEditor* console = ConsoleThread::serverConsole; if (console != nullptr) { while (true) { bool eof; string input = console->prompt("debug> ", "debug", eof); if (eof) { break; } if (input.empty()) { continue; } console->addHistory(input); { v8::TryCatch tryCatch; v8::HandleScope scope(isolate); TRI_ExecuteJavaScriptString(isolate, isolate->GetCurrentContext(), TRI_V8_STRING(input.c_str()), name, true); if (tryCatch.HasCaught()) { std::cout << TRI_StringifyV8Exception(isolate, &tryCatch); } } } } TRI_V8_RETURN_UNDEFINED(); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief compare two UTF 16 strings //////////////////////////////////////////////////////////////////////////////// static void JS_CompareString (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() != 2) { TRI_V8_THROW_EXCEPTION_USAGE("COMPARE_STRING(, )"); } v8::String::Value left(args[0]); v8::String::Value right(args[1]); // .......................................................................... // Take note here: we are assuming that the ICU type UChar is two bytes. // There is no guarantee that this will be the case on all platforms and // compilers. // .......................................................................... int result = Utf8Helper::DefaultUtf8Helper.compareUtf16(*left, left.length(), *right, right.length()); TRI_V8_RETURN(v8::Integer::New(isolate, result)); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief get list of timezones //////////////////////////////////////////////////////////////////////////////// static void JS_GetIcuTimezones (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() != 0) { TRI_V8_THROW_EXCEPTION_USAGE("TIMEZONES()"); } v8::Handle result = v8::Array::New(isolate); UErrorCode status = U_ZERO_ERROR; StringEnumeration* timeZones = TimeZone::createEnumeration(); if (timeZones) { int32_t idsCount = timeZones->count(status); for (int32_t i = 0; i < idsCount && U_ZERO_ERROR == status; ++i) { int32_t resultLength; const char* str = timeZones->next(&resultLength, status); result->Set((uint32_t) i, TRI_V8_PAIR_STRING(str, resultLength)); } delete timeZones; } TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief get list of locales //////////////////////////////////////////////////////////////////////////////// static void JS_GetIcuLocales (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() != 0) { TRI_V8_THROW_EXCEPTION_USAGE("LOCALES()"); } v8::Handle result = v8::Array::New(isolate); int32_t count = 0; const Locale* locales = Locale::getAvailableLocales(count); if (locales) { for (int32_t i = 0; i < count; ++i) { const Locale* l = locales + i; const char* str = l->getBaseName(); result->Set((uint32_t) i, TRI_V8_STRING(str)); } } TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief format datetime //////////////////////////////////////////////////////////////////////////////// static void JS_FormatDatetime (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() < 2) { TRI_V8_THROW_EXCEPTION_USAGE("FORMAT_DATETIME(, , [, []])"); } int64_t datetime = TRI_ObjectToInt64(args[0]); v8::String::Value pattern(args[1]); TimeZone* tz = 0; if (args.Length() > 2) { v8::String::Value value(args[2]); // .......................................................................... // Take note here: we are assuming that the ICU type UChar is two bytes. // There is no guarantee that this will be the case on all platforms and // compilers. // .......................................................................... UnicodeString ts((const UChar *) *value, value.length()); tz = TimeZone::createTimeZone(ts); } else { tz = TimeZone::createDefault(); } Locale locale; if (args.Length() > 3) { string name = TRI_ObjectToString(args[3]); locale = Locale::createFromName(name.c_str()); } else { // use language of default collator string name = Utf8Helper::DefaultUtf8Helper.getCollatorLanguage(); locale = Locale::createFromName(name.c_str()); } UnicodeString formattedString; UErrorCode status = U_ZERO_ERROR; UnicodeString aPattern((const UChar *) *pattern, pattern.length()); DateFormatSymbols* ds = new DateFormatSymbols(locale, status); SimpleDateFormat* s = new SimpleDateFormat(aPattern, ds, status); s->setTimeZone(*tz); s->format((UDate) (datetime * 1000), formattedString); string resultString; formattedString.toUTF8String(resultString); delete s; delete tz; TRI_V8_RETURN_STD_STRING(resultString); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief parse datetime //////////////////////////////////////////////////////////////////////////////// static void JS_ParseDatetime (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() < 2) { TRI_V8_THROW_EXCEPTION_USAGE("PARSE_DATETIME(, , [, []])"); } v8::String::Value datetimeString(args[0]); v8::String::Value pattern(args[1]); TimeZone* tz = 0; if (args.Length() > 2) { v8::String::Value value(args[2]); // .......................................................................... // Take note here: we are assuming that the ICU type UChar is two bytes. // There is no guarantee that this will be the case on all platforms and // compilers. // .......................................................................... UnicodeString ts((const UChar *) *value, value.length()); tz = TimeZone::createTimeZone(ts); } else { tz = TimeZone::createDefault(); } Locale locale; if (args.Length() > 3) { string name = TRI_ObjectToString(args[3]); locale = Locale::createFromName(name.c_str()); } else { // use language of default collator string name = Utf8Helper::DefaultUtf8Helper.getCollatorLanguage(); locale = Locale::createFromName(name.c_str()); } UnicodeString formattedString((const UChar *) *datetimeString, datetimeString.length()); UErrorCode status = U_ZERO_ERROR; UnicodeString aPattern((const UChar *) *pattern, pattern.length()); DateFormatSymbols* ds = new DateFormatSymbols(locale, status); SimpleDateFormat* s = new SimpleDateFormat(aPattern, ds, status); s->setTimeZone(*tz); UDate udate = s->parse(formattedString, status); delete s; delete tz; TRI_V8_RETURN(v8::Number::New(isolate, udate / 1000)); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief reloads the authentication info, coordinator case //////////////////////////////////////////////////////////////////////////////// static bool ReloadAuthCoordinator (TRI_vocbase_t* vocbase) { TRI_json_t* json = nullptr; bool result; int res = usersOnCoordinator(string(vocbase->_name), json, 60.0); if (res == TRI_ERROR_NO_ERROR) { TRI_ASSERT(json != nullptr); result = TRI_PopulateAuthInfo(vocbase, json); } else { result = false; } if (json != nullptr) { TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); } return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief reloads the authentication info from collection _users //////////////////////////////////////////////////////////////////////////////// static void JS_ReloadAuth (const v8::FunctionCallbackInfo& 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 (args.Length() != 0) { TRI_V8_THROW_EXCEPTION_USAGE("RELOAD_AUTH()"); } bool result; if (ServerState::instance()->isCoordinator()) { result = ReloadAuthCoordinator(vocbase); } else { result = TRI_ReloadAuthInfo(vocbase); } if (result) { TRI_V8_RETURN_TRUE(); } TRI_V8_RETURN_FALSE(); TRI_V8_TRY_CATCH_END } // ----------------------------------------------------------------------------- // --SECTION-- AQL // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief parses an AQL query //////////////////////////////////////////////////////////////////////////////// static void JS_ParseAql (const v8::FunctionCallbackInfo& 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 (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("AQL_PARSE()"); } // get the query string if (! args[0]->IsString()) { TRI_V8_THROW_TYPE_ERROR("expecting string for "); } string const&& queryString = TRI_ObjectToString(args[0]); TRI_GET_GLOBALS(); triagens::aql::Query query(v8g->_applicationV8, true, vocbase, queryString.c_str(), queryString.size(), nullptr, nullptr, triagens::aql::PART_MAIN); auto parseResult = query.parse(); if (parseResult.code != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION_FULL(parseResult.code, parseResult.details); } v8::Handle result = v8::Object::New(isolate); result->Set(TRI_V8_ASCII_STRING("parsed"), v8::True(isolate)); { v8::Handle collections = v8::Array::New(isolate); result->Set(TRI_V8_ASCII_STRING("collections"), collections); uint32_t i = 0; for (auto it = parseResult.collectionNames.begin(); it != parseResult.collectionNames.end(); ++it) { collections->Set(i++, TRI_V8_STD_STRING((*it))); } } { v8::Handle bindVars = v8::Array::New(isolate); uint32_t i = 0; for (auto it = parseResult.bindParameters.begin(); it != parseResult.bindParameters.end(); ++it) { bindVars->Set(i++, TRI_V8_STD_STRING((*it))); } result->Set(TRI_V8_ASCII_STRING("parameters"), bindVars); } result->Set(TRI_V8_ASCII_STRING("ast"), TRI_ObjectJson(isolate, parseResult.json)); if (parseResult.warnings == nullptr) { result->Set(TRI_V8_ASCII_STRING("warnings"), v8::Array::New(isolate)); } else { result->Set(TRI_V8_ASCII_STRING("warnings"), TRI_ObjectJson(isolate, parseResult.warnings)); } TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief registers a warning for the currently running AQL query /// this function is called from aql.js //////////////////////////////////////////////////////////////////////////////// static void JS_WarningAql (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() != 2) { TRI_V8_THROW_EXCEPTION_USAGE("AQL_WARNING(, )"); } // get the query string if (! args[1]->IsString()) { TRI_V8_THROW_TYPE_ERROR("expecting string for "); } TRI_GET_GLOBALS(); if (v8g->_query != nullptr) { // only register the error if we have a query... // note: we may not have a query if the AQL functions are called without // a query, e.g. during tests int code = static_cast(TRI_ObjectToInt64(args[0])); std::string const&& message = TRI_ObjectToString(args[1]); auto query = static_cast(v8g->_query); query->registerWarning(code, message.c_str()); } TRI_V8_RETURN_UNDEFINED(); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief explains an AQL query //////////////////////////////////////////////////////////////////////////////// static void JS_ExplainAql (const v8::FunctionCallbackInfo& 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 (args.Length() < 1 || args.Length() > 3) { TRI_V8_THROW_EXCEPTION_USAGE("AQL_EXPLAIN(, , )"); } // get the query string if (! args[0]->IsString()) { TRI_V8_THROW_TYPE_ERROR("expecting string for "); } string const&& queryString = TRI_ObjectToString(args[0]); // bind parameters std::unique_ptr parameters; if (args.Length() > 1) { if (! args[1]->IsUndefined() && ! args[1]->IsNull() && ! args[1]->IsObject()) { TRI_V8_THROW_TYPE_ERROR("expecting object for "); } if (args[1]->IsObject()) { parameters.reset(TRI_ObjectToJson(isolate, args[1])); } } std::unique_ptr options; if (args.Length() > 2) { // handle options if (! args[2]->IsObject()) { TRI_V8_THROW_TYPE_ERROR("expecting object for "); } options.reset(TRI_ObjectToJson(isolate, args[2])); } // bind parameters will be freed by the query later TRI_GET_GLOBALS(); triagens::aql::Query query( v8g->_applicationV8, true, vocbase, queryString.c_str(), queryString.size(), parameters.release(), options.release(), triagens::aql::PART_MAIN ); auto queryResult = query.explain(); if (queryResult.code != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION_FULL(queryResult.code, queryResult.details); } v8::Handle result = v8::Object::New(isolate); if (queryResult.json != nullptr) { if (query.allPlans()) { result->Set(TRI_V8_ASCII_STRING("plans"), TRI_ObjectJson(isolate, queryResult.json)); } else { result->Set(TRI_V8_ASCII_STRING("plan"), TRI_ObjectJson(isolate, queryResult.json)); } if (queryResult.clusterplan != nullptr) { result->Set(TRI_V8_ASCII_STRING("clusterplans"), TRI_ObjectJson(isolate, queryResult.clusterplan)); } if (queryResult.warnings == nullptr) { result->Set(TRI_V8_ASCII_STRING("warnings"), v8::Array::New(isolate)); } else { result->Set(TRI_V8_ASCII_STRING("warnings"), TRI_ObjectJson(isolate, queryResult.warnings)); } if (queryResult.stats == nullptr) { result->Set(TRI_V8_STRING("stats"), v8::Object::New(isolate)); } else { result->Set(TRI_V8_STRING("stats"), TRI_ObjectJson(isolate, queryResult.stats)); } } TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief executes an AQL query //////////////////////////////////////////////////////////////////////////////// static void JS_ExecuteAqlJson (const v8::FunctionCallbackInfo& 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 (args.Length() < 1 || args.Length() > 2) { TRI_V8_THROW_EXCEPTION_USAGE("AQL_EXECUTEJSON(, )"); } if (! args[0]->IsObject()) { TRI_V8_THROW_TYPE_ERROR("expecting object for "); } std::unique_ptr queryjson(TRI_ObjectToJson(isolate, args[0])); std::unique_ptr options; if (args.Length() > 1) { // we have options! yikes! if (! args[1]->IsUndefined() && ! args[1]->IsObject()) { TRI_V8_THROW_TYPE_ERROR("expecting object for "); } options.reset(TRI_ObjectToJson(isolate, args[1])); } TRI_GET_GLOBALS(); triagens::aql::Query query( v8g->_applicationV8, true, vocbase, Json(TRI_UNKNOWN_MEM_ZONE, queryjson.release()), options.get(), triagens::aql::PART_MAIN ); options.release(); auto queryResult = query.execute(static_cast(v8g->_queryRegistry)); if (queryResult.code != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION_FULL(queryResult.code, queryResult.details); } // return the array value as it is. this is a performance optimisation v8::Handle result = v8::Object::New(isolate); if (queryResult.json != nullptr) { result->ForceSet(TRI_V8_ASCII_STRING("json"), TRI_ObjectJson(isolate, queryResult.json)); } if (queryResult.stats != nullptr) { result->ForceSet(TRI_V8_ASCII_STRING("stats"), TRI_ObjectJson(isolate, queryResult.stats)); } if (queryResult.profile != nullptr) { result->ForceSet(TRI_V8_ASCII_STRING("profile"), TRI_ObjectJson(isolate, queryResult.profile)); } if (queryResult.warnings == nullptr) { result->ForceSet(TRI_V8_ASCII_STRING("warnings"), v8::Array::New(isolate)); } else { result->ForceSet(TRI_V8_ASCII_STRING("warnings"), TRI_ObjectJson(isolate, queryResult.warnings)); } result->ForceSet(TRI_V8_ASCII_STRING("cached"), v8::Boolean::New(isolate, queryResult.cached)); TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief executes an AQL query //////////////////////////////////////////////////////////////////////////////// static void JS_ExecuteAql (const v8::FunctionCallbackInfo& 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 (args.Length() < 1 || args.Length() > 3) { TRI_V8_THROW_EXCEPTION_USAGE("AQL_EXECUTE(, , )"); } // get the query string if (! args[0]->IsString()) { TRI_V8_THROW_TYPE_ERROR("expecting string for "); } string const&& queryString = TRI_ObjectToString(args[0]); // bind parameters std::unique_ptr parameters; // options std::unique_ptr options; if (args.Length() > 1) { if (! args[1]->IsUndefined() && ! args[1]->IsNull() && ! args[1]->IsObject()) { TRI_V8_THROW_TYPE_ERROR("expecting object for "); } if (args[1]->IsObject()) { parameters.reset(TRI_ObjectToJson(isolate, args[1])); } } if (args.Length() > 2) { // we have options! yikes! if (! args[2]->IsObject()) { TRI_V8_THROW_TYPE_ERROR("expecting object for "); } options.reset(TRI_ObjectToJson(isolate, args[2])); } // bind parameters will be freed by the query later TRI_GET_GLOBALS(); triagens::aql::Query query( v8g->_applicationV8, true, vocbase, queryString.c_str(), queryString.size(), parameters.get(), options.get(), triagens::aql::PART_MAIN ); options.release(); parameters.release(); auto queryResult = query.executeV8(isolate, static_cast(v8g->_queryRegistry)); if (queryResult.code != TRI_ERROR_NO_ERROR) { if (queryResult.code == TRI_ERROR_REQUEST_CANCELED) { TRI_GET_GLOBALS(); v8g->_canceled = true; TRI_V8_THROW_EXCEPTION(TRI_ERROR_REQUEST_CANCELED); } TRI_V8_THROW_EXCEPTION_FULL(queryResult.code, queryResult.details); } // return the array value as it is. this is a performance optimisation v8::Handle result = v8::Object::New(isolate); result->ForceSet(TRI_V8_ASCII_STRING("json"), queryResult.result); if (queryResult.stats != nullptr) { result->ForceSet(TRI_V8_ASCII_STRING("stats"), TRI_ObjectJson(isolate, queryResult.stats)); } if (queryResult.profile != nullptr) { result->ForceSet(TRI_V8_ASCII_STRING("profile"), TRI_ObjectJson(isolate, queryResult.profile)); } if (queryResult.warnings == nullptr) { result->ForceSet(TRI_V8_ASCII_STRING("warnings"), v8::Array::New(isolate)); } else { result->ForceSet(TRI_V8_ASCII_STRING("warnings"), TRI_ObjectJson(isolate, queryResult.warnings)); } result->ForceSet(TRI_V8_ASCII_STRING("cached"), v8::Boolean::New(isolate, queryResult.cached)); TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief retrieve global query options or configure them //////////////////////////////////////////////////////////////////////////////// static void JS_QueriesPropertiesAql (const v8::FunctionCallbackInfo& 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); } auto queryList = static_cast(vocbase->_queries); TRI_ASSERT(queryList != nullptr); if (args.Length() > 1) { TRI_V8_THROW_EXCEPTION_USAGE("AQL_QUERIES_PROPERTIES()"); } if (args.Length() == 1) { // store options if (! args[0]->IsObject()) { TRI_V8_THROW_EXCEPTION_USAGE("AQL_QUERIES_PROPERTIES()"); } auto obj = args[0]->ToObject(); if (obj->Has(TRI_V8_ASCII_STRING("enabled"))) { queryList->enabled(TRI_ObjectToBoolean(obj->Get(TRI_V8_ASCII_STRING("enabled")))); } if (obj->Has(TRI_V8_ASCII_STRING("trackSlowQueries"))) { queryList->trackSlowQueries(TRI_ObjectToBoolean(obj->Get(TRI_V8_ASCII_STRING("trackSlowQueries")))); } if (obj->Has(TRI_V8_ASCII_STRING("maxSlowQueries"))) { queryList->maxSlowQueries(static_cast(TRI_ObjectToInt64(obj->Get(TRI_V8_ASCII_STRING("maxSlowQueries"))))); } if (obj->Has(TRI_V8_ASCII_STRING("slowQueryThreshold"))) { queryList->slowQueryThreshold(TRI_ObjectToDouble(obj->Get(TRI_V8_ASCII_STRING("slowQueryThreshold")))); } if (obj->Has(TRI_V8_ASCII_STRING("maxQueryStringLength"))) { queryList->maxQueryStringLength(static_cast(TRI_ObjectToInt64(obj->Get(TRI_V8_ASCII_STRING("maxQueryStringLength"))))); } // fall-through intentional } // return current settings auto result = v8::Object::New(isolate); result->Set(TRI_V8_ASCII_STRING("enabled"), v8::Boolean::New(isolate, queryList->enabled())); result->Set(TRI_V8_ASCII_STRING("trackSlowQueries"), v8::Boolean::New(isolate, queryList->trackSlowQueries())); result->Set(TRI_V8_ASCII_STRING("maxSlowQueries"), v8::Number::New(isolate, static_cast(queryList->maxSlowQueries()))); result->Set(TRI_V8_ASCII_STRING("slowQueryThreshold"), v8::Number::New(isolate, queryList->slowQueryThreshold())); result->Set(TRI_V8_ASCII_STRING("maxQueryStringLength"), v8::Number::New(isolate, static_cast(queryList->maxQueryStringLength()))); TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the list of currently running queries //////////////////////////////////////////////////////////////////////////////// static void JS_QueriesCurrentAql (const v8::FunctionCallbackInfo& 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 (args.Length() != 0) { TRI_V8_THROW_EXCEPTION_USAGE("AQL_QUERIES_CURRENT()"); } auto queryList = static_cast(vocbase->_queries); TRI_ASSERT(queryList != nullptr); try { auto const&& queries = queryList->listCurrent(); uint32_t i = 0; auto result = v8::Array::New(isolate, static_cast(queries.size())); for (auto it : queries) { auto const&& timeString = TRI_StringTimeStamp(it.started); v8::Handle obj = v8::Object::New(isolate); obj->Set(TRI_V8_ASCII_STRING("id"), V8TickId(isolate, it.id)); obj->Set(TRI_V8_ASCII_STRING("query"), TRI_V8_STD_STRING(it.queryString)); obj->Set(TRI_V8_ASCII_STRING("started"), TRI_V8_STD_STRING(timeString)); obj->Set(TRI_V8_ASCII_STRING("runTime"), v8::Number::New(isolate, it.runTime)); result->Set(i++, obj); } TRI_V8_RETURN(result); } catch (...) { TRI_V8_THROW_EXCEPTION_MEMORY(); } TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the list of slow running queries or clears the list //////////////////////////////////////////////////////////////////////////////// static void JS_QueriesSlowAql (const v8::FunctionCallbackInfo& 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); } auto queryList = static_cast(vocbase->_queries); TRI_ASSERT(queryList != nullptr); if (args.Length() == 1) { queryList->clearSlow(); TRI_V8_RETURN_TRUE(); } if (args.Length() != 0) { TRI_V8_THROW_EXCEPTION_USAGE("AQL_QUERIES_SLOW()"); } try { auto const&& queries = queryList->listSlow(); uint32_t i = 0; auto result = v8::Array::New(isolate, static_cast(queries.size())); for (auto it : queries) { auto const&& timeString = TRI_StringTimeStamp(it.started); v8::Handle obj = v8::Object::New(isolate); obj->Set(TRI_V8_ASCII_STRING("id"), V8TickId(isolate, it.id)); obj->Set(TRI_V8_ASCII_STRING("query"), TRI_V8_STD_STRING(it.queryString)); obj->Set(TRI_V8_ASCII_STRING("started"), TRI_V8_STD_STRING(timeString)); obj->Set(TRI_V8_ASCII_STRING("runTime"), v8::Number::New(isolate, it.runTime)); result->Set(i++, obj); } TRI_V8_RETURN(result); } catch (...) { TRI_V8_THROW_EXCEPTION_MEMORY(); } TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief kills an AQL query //////////////////////////////////////////////////////////////////////////////// static void JS_QueriesKillAql (const v8::FunctionCallbackInfo& 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 (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("AQL_QUERIES_KILL()"); } auto id = TRI_ObjectToUInt64(args[0], true); auto queryList = static_cast(vocbase->_queries); TRI_ASSERT(queryList != nullptr); auto res = queryList->kill(id); if (res == TRI_ERROR_NO_ERROR) { TRI_V8_RETURN_TRUE(); } TRI_V8_THROW_EXCEPTION(res); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief whether or not a query is killed //////////////////////////////////////////////////////////////////////////////// static void JS_QueryIsKilledAql (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); TRI_GET_GLOBALS(); if (v8g->_query != nullptr && static_cast(v8g->_query)->killed()) { TRI_V8_RETURN_TRUE(); } TRI_V8_RETURN_FALSE(); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief configures the AQL query cache //////////////////////////////////////////////////////////////////////////////// static void JS_QueryCachePropertiesAql (const v8::FunctionCallbackInfo& 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 (args.Length() > 1 || (args.Length() == 1 && ! args[0]->IsObject())) { TRI_V8_THROW_EXCEPTION_USAGE("AQL_QUERY_CACHE_PROPERTIES()"); } auto queryCache = triagens::aql::QueryCache::instance(); if (args.Length() == 1) { // called with options auto obj = args[0]->ToObject(); std::pair cacheProperties; // fetch current configuration queryCache->properties(cacheProperties); if (obj->Has(TRI_V8_ASCII_STRING("mode"))) { cacheProperties.first = TRI_ObjectToString(obj->Get(TRI_V8_ASCII_STRING("mode"))); } if (obj->Has(TRI_V8_ASCII_STRING("maxResults"))) { cacheProperties.second = static_cast(TRI_ObjectToInt64(obj->Get(TRI_V8_ASCII_STRING("maxResults")))); } // set mode and max elements queryCache->setProperties(cacheProperties); } auto properties = queryCache->properties(); TRI_V8_RETURN(TRI_ObjectJson(isolate, properties.json())); // fetch current configuration and return it TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief invalidates the AQL query cache //////////////////////////////////////////////////////////////////////////////// static void JS_QueryCacheInvalidateAql (const v8::FunctionCallbackInfo& 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 (args.Length() != 0) { TRI_V8_THROW_EXCEPTION_USAGE("AQL_QUERY_CACHE_INVALIDATE()"); } triagens::aql::QueryCache::instance()->invalidate(); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief throw collection not loaded //////////////////////////////////////////////////////////////////////////////// static void JS_ThrowCollectionNotLoaded (const v8::FunctionCallbackInfo& 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 (args.Length() == 0) { bool value = TRI_GetThrowCollectionNotLoadedVocBase(vocbase); TRI_V8_RETURN(v8::Boolean::New(isolate, value)); } else if (args.Length() == 1) { TRI_SetThrowCollectionNotLoadedVocBase(vocbase, TRI_ObjectToBoolean(args[0])); } else { TRI_V8_THROW_EXCEPTION_USAGE("THROW_COLLECTION_NOT_LOADED()"); } TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief Transforms VertexId to v8String //////////////////////////////////////////////////////////////////////////////// static v8::Local VertexIdToString (v8::Isolate* isolate, CollectionNameResolver const* resolver, VertexId const& id) { return TRI_V8_STD_STRING((resolver->getCollectionName(id.cid) + "/" + string(id.key))); } //////////////////////////////////////////////////////////////////////////////// /// @brief Transforms EdgeId to v8String //////////////////////////////////////////////////////////////////////////////// static v8::Local EdgeIdToString (v8::Isolate* isolate, CollectionNameResolver const* resolver, EdgeId const& id) { return TRI_V8_STD_STRING((resolver->getCollectionName(id.cid) + "/" + string(id.key))); } //////////////////////////////////////////////////////////////////////////////// /// @brief Transforms VertexId to v8 json //////////////////////////////////////////////////////////////////////////////// static v8::Handle VertexIdToData (v8::Isolate* isolate, CollectionNameResolver const* resolver, ExplicitTransaction* trx, unordered_map const& ditches, VertexId const& vertexId) { auto i = ditches.find(vertexId.cid); if (i == ditches.end()) { v8::EscapableHandleScope scope(isolate); return scope.Escape(v8::Null(isolate)); } TRI_doc_mptr_copy_t document; int res = trx->readSingle(i->second.col, &document, vertexId.key); if (res != TRI_ERROR_NO_ERROR) { v8::EscapableHandleScope scope(isolate); return scope.Escape(v8::Null(isolate)); } return TRI_WrapShapedJson(isolate, resolver, i->second.ditch, vertexId.cid, i->second.col->_collection->_collection, document.getDataPtr()); } //////////////////////////////////////////////////////////////////////////////// /// @brief Transforms EdgeId to v8 json //////////////////////////////////////////////////////////////////////////////// static v8::Handle EdgeIdToData (v8::Isolate* isolate, CollectionNameResolver const* resolver, ExplicitTransaction* trx, unordered_map const& ditches, EdgeId const& edgeId) { // EdgeId is a typedef of VertexId. return VertexIdToData(isolate, resolver, trx, ditches, edgeId); } //////////////////////////////////////////////////////////////////////////////// /// @brief Extracts all touched collections from ArangoDBPathFinder::Path //////////////////////////////////////////////////////////////////////////////// static void ExtractCidsFromPath (TRI_vocbase_t* vocbase, ArangoDBPathFinder::Path const& p, vector& result) { unordered_set found; uint32_t const vn = static_cast(p.vertices.size()); uint32_t const en = static_cast(p.edges.size()); for (uint32_t j = 0; j < vn; ++j) { TRI_voc_cid_t cid = p.vertices[j].cid; auto it = found.find(cid); if (it == found.end()) { // Not yet found. Insert it if it exists if (TRI_LookupCollectionByIdVocBase(vocbase, cid) != nullptr) { result.emplace_back(cid); found.insert(cid); } } } for (uint32_t j = 0; j < en; ++j) { TRI_voc_cid_t cid = p.edges[j].cid; auto it = found.find(cid); if (it == found.end()) { // Not yet found. Insert it if it exists if (TRI_LookupCollectionByIdVocBase(vocbase, cid) != nullptr) { result.emplace_back(cid); found.insert(cid); } } } } //////////////////////////////////////////////////////////////////////////////// /// @brief Extracts all touched collections from ArangoDBPathFinder::Path //////////////////////////////////////////////////////////////////////////////// static void ExtractCidsFromPath (TRI_vocbase_t* vocbase, ArangoDBConstDistancePathFinder::Path const& p, vector& result) { unordered_set found; uint32_t const vn = static_cast(p.vertices.size()); uint32_t const en = static_cast(p.edges.size()); for (uint32_t j = 0; j < vn; ++j) { TRI_voc_cid_t cid = p.vertices[j].cid; auto it = found.find(cid); if (it == found.end()) { // Not yet found. Insert it if it exists if (TRI_LookupCollectionByIdVocBase(vocbase, cid) != nullptr) { result.emplace_back(cid); found.insert(cid); } } } for (uint32_t j = 0; j < en; ++j) { TRI_voc_cid_t cid = p.edges[j].cid; auto it = found.find(cid); if (it == found.end()) { // Not yet found. Insert it if it exists if (TRI_LookupCollectionByIdVocBase(vocbase, cid) != nullptr) { result.emplace_back(cid); found.insert(cid); } } } } //////////////////////////////////////////////////////////////////////////////// /// @brief Request a ditch for the given collection //////////////////////////////////////////////////////////////////////////////// static void AddDitch (ExplicitTransaction* trx, TRI_voc_cid_t const& cid, unordered_map& ditches) { TRI_transaction_collection_t* col = trx->trxCollection(cid); auto ditch = trx->orderDitch(col); if (ditch == nullptr) { THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } ditches.emplace(cid, CollectionDitchInfo(ditch, col)); } //////////////////////////////////////////////////////////////////////////////// /// @brief Start a new transaction for the given collections and request /// all necessary ditches. /// The caller is responsible to finish and delete the transaction. /// If this function throws the transaction is non-existent. //////////////////////////////////////////////////////////////////////////////// static ExplicitTransaction* BeginTransaction (TRI_vocbase_t* vocbase, vector const& readCollections, vector const& writeCollections, CollectionNameResolver const* resolver, unordered_map& ditches) { // IHHF isCoordinator double lockTimeout = (double) (TRI_TRANSACTION_DEFAULT_LOCK_TIMEOUT / 1000000ULL); bool embed = true; bool waitForSync = false; // Start Transaction to collect all parts of the path std::unique_ptr trx(new ExplicitTransaction( vocbase, readCollections, writeCollections, lockTimeout, waitForSync, embed )); int res = trx->begin(); if (res != TRI_ERROR_NO_ERROR) { trx->finish(res); THROW_ARANGO_EXCEPTION(res); } // Get all ditches at once for (auto const& it : readCollections) { AddDitch(trx.get(), it, ditches); } for (auto const& it : writeCollections) { AddDitch(trx.get(), it, ditches); } return trx.release(); } //////////////////////////////////////////////////////////////////////////////// /// @brief Transforms an ArangoDBPathFinder::Path to v8 json values //////////////////////////////////////////////////////////////////////////////// static v8::Handle PathIdsToV8 (v8::Isolate* isolate, TRI_vocbase_t* vocbase, CollectionNameResolver const* resolver, ArangoDBPathFinder::Path const& p, unordered_map& ditches, bool& includeData) { v8::EscapableHandleScope scope(isolate); v8::Handle result = v8::Object::New(isolate); uint32_t const vn = static_cast(p.vertices.size()); v8::Handle vertices = v8::Array::New(isolate, static_cast(vn)); uint32_t const en = static_cast(p.edges.size()); v8::Handle edges = v8::Array::New(isolate, static_cast(en)); if (includeData) { vector readCollections; ExtractCidsFromPath(vocbase, p, readCollections); vector writeCollections; unordered_map ditches; std::unique_ptr trx; trx.reset(BeginTransaction(vocbase, readCollections, writeCollections, resolver, ditches)); for (uint32_t j = 0; j < vn; ++j) { vertices->Set(j, VertexIdToData(isolate, resolver, trx.get(), ditches, p.vertices[j])); } for (uint32_t j = 0; j < en; ++j) { edges->Set(j, EdgeIdToData(isolate, resolver, trx.get(), ditches, p.edges[j])); } trx->finish(TRI_ERROR_NO_ERROR); } else { for (uint32_t j = 0; j < vn; ++j) { vertices->Set(j, VertexIdToString(isolate, resolver, p.vertices[j])); } for (uint32_t j = 0; j < en; ++j) { edges->Set(j, EdgeIdToString(isolate, resolver, p.edges[j])); } } result->Set(TRI_V8_STRING("vertices"), vertices); result->Set(TRI_V8_STRING("edges"), edges); result->Set(TRI_V8_STRING("distance"), v8::Number::New(isolate, static_cast(p.weight))); return scope.Escape(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief Transforms an ConstDistanceFinder::Path to v8 json values //////////////////////////////////////////////////////////////////////////////// static v8::Handle PathIdsToV8 (v8::Isolate* isolate, TRI_vocbase_t* vocbase, CollectionNameResolver const* resolver, ArangoDBConstDistancePathFinder::Path const& p, unordered_map& ditches, bool& includeData) { v8::EscapableHandleScope scope(isolate); v8::Handle result = v8::Object::New(isolate); uint32_t const vn = static_cast(p.vertices.size()); v8::Handle vertices = v8::Array::New(isolate, static_cast(vn)); uint32_t const en = static_cast(p.edges.size()); v8::Handle edges = v8::Array::New(isolate, static_cast(en)); if (includeData) { vector readCollections; ExtractCidsFromPath(vocbase, p, readCollections); vector writeCollections; unordered_map ditches; std::unique_ptr trx; trx.reset(BeginTransaction(vocbase, readCollections, writeCollections, resolver, ditches)); for (uint32_t j = 0; j < vn; ++j) { vertices->Set(j, VertexIdToData(isolate, resolver, trx.get(), ditches, p.vertices[j])); } for (uint32_t j = 0; j < en; ++j) { edges->Set(j, EdgeIdToData(isolate, resolver, trx.get(), ditches, p.edges[j])); } trx->finish(TRI_ERROR_NO_ERROR); } else { for (uint32_t j = 0; j < vn; ++j) { vertices->Set(j, VertexIdToString(isolate, resolver, p.vertices[j])); } for (uint32_t j = 0; j < en; ++j) { edges->Set(j, EdgeIdToString(isolate, resolver, p.edges[j])); } } result->Set(TRI_V8_STRING("vertices"), vertices); result->Set(TRI_V8_STRING("edges"), edges); result->Set(TRI_V8_STRING("distance"), v8::Number::New(isolate, static_cast(p.weight))); return scope.Escape(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief Extract collection names from v8 array. //////////////////////////////////////////////////////////////////////////////// static void V8ArrayToStrings (const v8::Handle& parameter, unordered_set& result) { v8::Handle array = v8::Handle::Cast(parameter); uint32_t const n = array->Length(); for (uint32_t i = 0; i < n; ++i) { if (! array->Get(i)->IsString()) { // TODO Error Handling } else { result.emplace(TRI_ObjectToString(array->Get(i))); } } } //////////////////////////////////////////////////////////////////////////////// /// @brief Define edge weight by the number of hops. /// Respectively 1 for any edge. //////////////////////////////////////////////////////////////////////////////// class HopWeightCalculator { public: HopWeightCalculator() {}; //////////////////////////////////////////////////////////////////////////////// /// @brief Callable weight calculator for edge //////////////////////////////////////////////////////////////////////////////// double operator() (TRI_doc_mptr_copy_t& edge) { return 1; } }; //////////////////////////////////////////////////////////////////////////////// /// @brief Define edge weight by ony special attribute. /// Respectively 1 for any edge. //////////////////////////////////////////////////////////////////////////////// class AttributeWeightCalculator { TRI_shape_pid_t _shapePid; double _defaultWeight; VocShaper* _shaper; public: AttributeWeightCalculator (string const& keyWeight, double defaultWeight, VocShaper* shaper) : _defaultWeight(defaultWeight), _shaper(shaper) { _shapePid = _shaper->lookupAttributePathByName(keyWeight.c_str()); } //////////////////////////////////////////////////////////////////////////////// /// @brief Callable weight calculator for edge //////////////////////////////////////////////////////////////////////////////// double operator() (TRI_doc_mptr_copy_t const& edge) { if (_shapePid == 0) { return _defaultWeight; } TRI_shape_sid_t sid; TRI_EXTRACT_SHAPE_IDENTIFIER_MARKER(sid, edge.getDataPtr()); TRI_shape_access_t const* accessor = _shaper->findAccessor(sid, _shapePid); TRI_shaped_json_t shapedJson; TRI_EXTRACT_SHAPED_JSON_MARKER(shapedJson, edge.getDataPtr()); TRI_shaped_json_t resultJson; TRI_ExecuteShapeAccessor(accessor, &shapedJson, &resultJson); if (resultJson._sid != TRI_SHAPE_NUMBER) { return _defaultWeight; } std::unique_ptr json(TRI_JsonShapedJson(_shaper, &resultJson)); if (json == nullptr) { return _defaultWeight; } return json.get()->_value._number; } }; //////////////////////////////////////////////////////////////////////////////// /// @brief Helper to transform a vertex _id string to VertexId struct. //////////////////////////////////////////////////////////////////////////////// static VertexId IdStringToVertexId (CollectionNameResolver const* resolver, string const& vertex) { size_t split; char const* str = vertex.c_str(); if (! TRI_ValidateDocumentIdKeyGenerator(str, &split)) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD); } string const collectionName = vertex.substr(0, split); auto coli = resolver->getCollectionStruct(collectionName); if (coli == nullptr) { THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND); } return VertexId(coli->_cid, const_cast(str + split + 1)); } //////////////////////////////////////////////////////////////////////////////// /// @brief Executes a shortest Path Traversal //////////////////////////////////////////////////////////////////////////////// static void JS_QueryShortestPath (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() < 4 || args.Length() > 5) { TRI_V8_THROW_EXCEPTION_USAGE("CPP_SHORTEST_PATH(, , , , )"); } // get the vertex collections if (! args[0]->IsArray()) { TRI_V8_THROW_TYPE_ERROR("expecting array for "); } unordered_set vertexCollectionNames; V8ArrayToStrings(args[0], vertexCollectionNames); // get the edge collections if (! args[1]->IsArray()) { TRI_V8_THROW_TYPE_ERROR("expecting array for "); } unordered_set edgeCollectionNames; V8ArrayToStrings(args[1], edgeCollectionNames); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } if (! args[2]->IsString()) { TRI_V8_THROW_TYPE_ERROR("expecting id for "); } string const startVertex = TRI_ObjectToString(args[2]); if (! args[3]->IsString()) { TRI_V8_THROW_TYPE_ERROR("expecting id for "); } string const targetVertex = TRI_ObjectToString(args[3]); traverser::ShortestPathOptions opts; bool includeData = false; v8::Handle edgeExample; v8::Handle vertexExample; if (args.Length() == 5) { if (! args[4]->IsObject()) { TRI_V8_THROW_TYPE_ERROR("expecting json for "); } v8::Handle options = args[4]->ToObject(); // Parse direction v8::Local keyDirection = TRI_V8_ASCII_STRING("direction"); if (options->Has(keyDirection) ) { opts.direction = TRI_ObjectToString(options->Get(keyDirection)); if ( opts.direction != "outbound" && opts.direction != "inbound" && opts.direction != "any" ) { TRI_V8_THROW_TYPE_ERROR("expecting direction to be 'outbound', 'inbound' or 'any'"); } } // Parse Distance v8::Local keyWeight= TRI_V8_ASCII_STRING("weight"); v8::Local keyDefaultWeight= TRI_V8_ASCII_STRING("defaultWeight"); if (options->Has(keyWeight) && options->Has(keyDefaultWeight) ) { opts.useWeight = true; opts.weightAttribute = TRI_ObjectToString(options->Get(keyWeight)); opts.defaultWeight = TRI_ObjectToDouble(options->Get(keyDefaultWeight)); } // Parse includeData v8::Local keyIncludeData = TRI_V8_ASCII_STRING("includeData"); if (options->Has(keyIncludeData)) { includeData = TRI_ObjectToBoolean(options->Get(keyIncludeData)); } // Parse bidirectional v8::Local keyBidirectional = TRI_V8_ASCII_STRING("bidirectional"); if (options->Has(keyBidirectional)) { opts.bidirectional = TRI_ObjectToBoolean(options->Get(keyBidirectional)); } // Parse multiThreaded v8::Local keyMultiThreaded = TRI_V8_ASCII_STRING("multiThreaded"); if (options->Has(keyMultiThreaded)) { opts.multiThreaded = TRI_ObjectToBoolean(options->Get(keyMultiThreaded)); } // Parse filterEdges // note: only works with edge examples and not with user-defined AQL functions v8::Local keyFilterEdges = TRI_V8_ASCII_STRING("filterEdges"); if (options->Has(keyFilterEdges)) { opts.useEdgeFilter = true; edgeExample = v8::Handle::Cast(options->Get(keyFilterEdges)); } // Parse vertexFilter // note: only works with vertex examples and not with user-defined AQL functions v8::Local keyFilterVertices = TRI_V8_ASCII_STRING("filterVertices"); if (options->Has(keyFilterVertices)) { opts.useVertexFilter = true; vertexExample = v8::Handle::Cast(options->Get(keyFilterVertices)); } } vector readCollections; vector writeCollections; V8ResolverGuard resolverGuard(vocbase); int res = TRI_ERROR_NO_ERROR; CollectionNameResolver const* resolver = resolverGuard.getResolver(); for (auto const& it : edgeCollectionNames) { readCollections.emplace_back(resolver->getCollectionId(it)); } for (auto const& it : vertexCollectionNames) { readCollections.emplace_back(resolver->getCollectionId(it)); } // Start the transaction and order ditches unordered_map ditches; std::unique_ptr trx; try { trx.reset(BeginTransaction(vocbase, readCollections, writeCollections, resolver, ditches)); } catch (Exception& e) { TRI_V8_THROW_EXCEPTION(e.code()); } vector edgeCollectionInfos; vector vertexCollectionInfos; triagens::basics::ScopeGuard guard{ []() -> void { }, [&edgeCollectionInfos, &vertexCollectionInfos]() -> void { for (auto& p : edgeCollectionInfos) { delete p; } for (auto& p : vertexCollectionInfos) { delete p; } } }; if (opts.useWeight) { for (auto const& it : edgeCollectionNames) { auto cid = resolver->getCollectionId(it); auto colObj = ditches.find(cid)->second.col->_collection->_collection; edgeCollectionInfos.emplace_back(new EdgeCollectionInfo( cid, colObj, AttributeWeightCalculator( opts.weightAttribute, opts.defaultWeight, colObj->getShaper() ) )); } } else { for (auto const& it : edgeCollectionNames) { auto cid = resolver->getCollectionId(it); auto colObj = ditches.find(cid)->second.col->_collection->_collection; edgeCollectionInfos.emplace_back(new EdgeCollectionInfo( cid, colObj, HopWeightCalculator() )); } } for(auto const& it : vertexCollectionNames) { auto cid = resolver->getCollectionId(it); auto colObj = ditches.find(cid)->second.col; vertexCollectionInfos.emplace_back(new VertexCollectionInfo( cid, colObj )); } if (opts.useEdgeFilter) { string errorMessage; for (auto const& it: edgeCollectionInfos) { try { opts.addEdgeFilter(isolate, edgeExample, it->getShaper(), it->getCid(), errorMessage); } catch (Exception& e) { // ELEMENT not found is expected, if there is no shape of this type in this collection if (e.code() != TRI_RESULT_ELEMENT_NOT_FOUND) { TRI_V8_THROW_EXCEPTION(e.code()); } } } } if (opts.useVertexFilter) { string errorMessage; for (auto const& it: vertexCollectionInfos) { try { opts.addVertexFilter(isolate, vertexExample, trx.get(), it->getCollection(), it->getShaper(), it->getCid(), errorMessage); } catch (Exception& e) { // ELEMENT not found is expected, if there is no shape of this type in this collection if (e.code() != TRI_RESULT_ELEMENT_NOT_FOUND) { TRI_V8_THROW_EXCEPTION(e.code()); } } } } try { opts.start = IdStringToVertexId(resolver, startVertex); opts.end = IdStringToVertexId(resolver, targetVertex); } catch (Exception& e) { // Id string might have illegal collection name trx->finish(e.code()); TRI_V8_THROW_EXCEPTION(e.code()); } if (opts.useVertexFilter || opts.useEdgeFilter || opts.useWeight) { // Compute the path unique_ptr path; try { path = TRI_RunShortestPathSearch( edgeCollectionInfos, opts ); } catch (Exception& e) { trx->finish(e.code()); TRI_V8_THROW_EXCEPTION(e.code()); } // Lift the result to v8 if (path.get() == nullptr) { v8::EscapableHandleScope scope(isolate); trx->finish(res); TRI_V8_RETURN(scope.Escape(v8::Null(isolate))); } trx->finish(res); // must finish "old" transaction first before starting a new in PathIdsToV8 delete trx.release(); // Potential inconsistency here. Graph is outside a transaction in this very second. // Adding additional locks on vertex collections at this point to the transaction // would cause dead-locks. // Will be fixed automatically with new MVCC version. try { auto result = PathIdsToV8(isolate, vocbase, resolver, *path, ditches, includeData); TRI_V8_RETURN(result); } catch (Exception& e) { TRI_V8_THROW_EXCEPTION(e.code()); } } else { // No Data reading required for this path. Use shortcuts. // Compute the path unique_ptr path; try { path = TRI_RunSimpleShortestPathSearch( edgeCollectionInfos, opts ); } catch (Exception& e) { trx->finish(e.code()); TRI_V8_THROW_EXCEPTION(e.code()); } // Lift the result to v8 if (path.get() == nullptr) { v8::EscapableHandleScope scope(isolate); trx->finish(res); TRI_V8_RETURN(scope.Escape(v8::Null(isolate))); } trx->finish(res); // must finish "old" transaction first before starting a new in PathIdsToV8 delete trx.release(); // Potential inconsistency here. Graph is outside a transaction in this very second. // Adding additional locks on vertex collections at this point to the transaction // would cause dead-locks. // Will be fixed automatically with new MVCC version. try { auto result = PathIdsToV8(isolate, vocbase, resolver, *path, ditches, includeData); TRI_V8_RETURN(result); } catch (Exception& e) { TRI_V8_THROW_EXCEPTION(e.code()); } } TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief Transforms an vector to v8 json values //////////////////////////////////////////////////////////////////////////////// static v8::Handle VertexIdsToV8 (v8::Isolate* isolate, ExplicitTransaction* trx, CollectionNameResolver const* resolver, unordered_set& ids, unordered_map& ditches, bool includeData = false) { v8::EscapableHandleScope scope(isolate); uint32_t const vn = static_cast(ids.size()); v8::Handle vertices = v8::Array::New(isolate, static_cast(vn)); uint32_t j = 0; if (includeData) { for (auto& it: ids) { vertices->Set(j, VertexIdToData(isolate, resolver, trx, ditches, it)); ++j; } } else { for (auto& it: ids) { vertices->Set(j, VertexIdToString(isolate, resolver, it)); ++j; } } return scope.Escape(vertices); } //////////////////////////////////////////////////////////////////////////////// /// @brief Executes a Neighbors computation //////////////////////////////////////////////////////////////////////////////// static void JS_QueryNeighbors (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() < 3 || args.Length() > 4) { TRI_V8_THROW_EXCEPTION_USAGE("CPP_NEIGHBORS(, , , )"); } // get the vertex collections if (! args[0]->IsArray()) { TRI_V8_THROW_TYPE_ERROR("expecting array for "); } unordered_set vertexCollectionNames; V8ArrayToStrings(args[0], vertexCollectionNames); // get the edge collections if (! args[1]->IsArray()) { TRI_V8_THROW_TYPE_ERROR("expecting array for "); } unordered_set edgeCollectionNames; V8ArrayToStrings(args[1], edgeCollectionNames); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } vector startVertices; if (args[2]->IsString()) { startVertices.emplace_back(TRI_ObjectToString(args[2])); } else if (args[2]->IsArray()) { auto list = v8::Handle::Cast(args[2]); for (uint32_t i = 0; i < list->Length(); i++) { if (list->Get(i)->IsString()) { startVertices.emplace_back(TRI_ObjectToString(list->Get(i))); } else { TRI_V8_THROW_TYPE_ERROR("expecting array of IDs for "); } } } else { TRI_V8_THROW_TYPE_ERROR("expecting string ID for "); } traverser::NeighborsOptions opts; bool includeData = false; v8::Handle edgeExample; v8::Handle vertexExample; if (args.Length() == 4) { if (! args[3]->IsObject()) { TRI_V8_THROW_TYPE_ERROR("expecting json for "); } v8::Handle options = args[3]->ToObject(); // Parse direction v8::Local keyDirection = TRI_V8_ASCII_STRING("direction"); if (options->Has(keyDirection) ) { string dir = TRI_ObjectToString(options->Get(keyDirection)); if (dir == "outbound") { opts.direction = TRI_EDGE_OUT; } else if (dir == "inbound") { opts.direction = TRI_EDGE_IN; } else if (dir == "any") { opts.direction = TRI_EDGE_ANY; } else { TRI_V8_THROW_TYPE_ERROR("expecting direction to be 'outbound', 'inbound' or 'any'"); } } // Parse includeData v8::Local keyIncludeData = TRI_V8_ASCII_STRING("includeData"); if (options->Has(keyIncludeData)) { includeData = TRI_ObjectToBoolean(options->Get(keyIncludeData)); } // Parse filterEdges v8::Local keyFilterEdges = TRI_V8_ASCII_STRING("filterEdges"); if (options->Has(keyFilterEdges)) { opts.useEdgeFilter = true; edgeExample = options->Get(keyFilterEdges); } // Parse vertexFilter v8::Local keyFilterVertices = TRI_V8_ASCII_STRING("filterVertices"); if (options->Has(keyFilterVertices)) { opts.useVertexFilter = true; // note: only works with vertex examples and not with user-defined AQL functions vertexExample = v8::Handle::Cast(options->Get(keyFilterVertices)); } // Parse minDepth v8::Local keyMinDepth = TRI_V8_ASCII_STRING("minDepth"); if (options->Has(keyMinDepth)) { opts.minDepth = TRI_ObjectToUInt64(options->Get(keyMinDepth), false); } // Parse maxDepth v8::Local keyMaxDepth = TRI_V8_ASCII_STRING("maxDepth"); if (options->Has(keyMaxDepth)) { opts.maxDepth = TRI_ObjectToUInt64(options->Get(keyMaxDepth), false); } } vector readCollections; vector writeCollections; V8ResolverGuard resolverGuard(vocbase); int res = TRI_ERROR_NO_ERROR; CollectionNameResolver const* resolver = resolverGuard.getResolver(); for (auto const& it : edgeCollectionNames) { readCollections.emplace_back(resolver->getCollectionId(it)); } for (auto const& it : vertexCollectionNames) { readCollections.emplace_back(resolver->getCollectionId(it)); } unordered_map ditches; // Start the transaction std::unique_ptr trx; try { trx.reset(BeginTransaction(vocbase, readCollections, writeCollections, resolver, ditches)); } catch (Exception& e) { TRI_V8_THROW_EXCEPTION(e.code()); } vector edgeCollectionInfos; vector vertexCollectionInfos; triagens::basics::ScopeGuard guard{ []() -> void { }, [&edgeCollectionInfos, &vertexCollectionInfos]() -> void { for (auto& p : edgeCollectionInfos) { delete p; } for (auto& p : vertexCollectionInfos) { delete p; } } }; for (auto const& it : edgeCollectionNames) { auto cid = resolver->getCollectionId(it); auto colObj = ditches.find(cid)->second.col->_collection->_collection; edgeCollectionInfos.emplace_back(new EdgeCollectionInfo( cid, colObj, HopWeightCalculator() )); TRI_IF_FAILURE("EdgeCollectionDitchOOM") { THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG); } } for (auto it : vertexCollectionNames) { auto cid = resolver->getCollectionId(it); auto colObj = ditches.find(cid)->second.col; vertexCollectionInfos.emplace_back(new VertexCollectionInfo( cid, colObj )); // Explicitly allow all collections. opts.addCollectionRestriction(cid); TRI_IF_FAILURE("VertexCollectionDitchOOM") { THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG); } } unordered_set neighbors; if (opts.useEdgeFilter) { string errorMessage; for (auto const& it : edgeCollectionInfos) { try { opts.addEdgeFilter(isolate, edgeExample, it->getShaper(), it->getCid(), errorMessage); } catch (Exception& e) { // ELEMENT not found is expected, if there is no shape of this type in this collection if (e.code() != TRI_RESULT_ELEMENT_NOT_FOUND) { TRI_V8_THROW_EXCEPTION(e.code()); } } } } if (opts.useVertexFilter) { string errorMessage; for (auto const& it: vertexCollectionInfos) { try { opts.addVertexFilter(isolate, vertexExample, trx.get(), it->getCollection(), it->getShaper(), it->getCid(), errorMessage); } catch (Exception& e) { // ELEMENT not found is expected, if there is no shape of this type in this collection if (e.code() != TRI_RESULT_ELEMENT_NOT_FOUND) { TRI_V8_THROW_EXCEPTION(e.code()); } } } } for (auto const& startVertex : startVertices) { try { opts.start = IdStringToVertexId(resolver, startVertex); } catch (Exception& e) { // Id string might have illegal collection name trx->finish(e.code()); TRI_V8_THROW_EXCEPTION(e.code()); } try { TRI_RunNeighborsSearch( edgeCollectionInfos, opts, neighbors ); } catch (Exception& e) { trx->finish(e.code()); TRI_V8_THROW_EXCEPTION(e.code()); } } auto result = VertexIdsToV8(isolate, trx.get(), resolver, neighbors, ditches, includeData); trx->finish(res); TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } //////////////////////////////////////////////////////////////////////////////// /// @brief sleeps and checks for query abortion in between //////////////////////////////////////////////////////////////////////////////// static void JS_QuerySleepAql (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); // extract arguments if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("sleep()"); } TRI_GET_GLOBALS(); triagens::aql::Query* query = static_cast(v8g->_query); if (query == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_QUERY_NOT_FOUND); } double n = TRI_ObjectToDouble(args[0]); double const until = TRI_microtime() + n; // TODO: use select etc. to wait until point in time while (TRI_microtime() < until) { usleep(10000); if (query != nullptr) { if (query->killed()) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_QUERY_KILLED); } } } TRI_V8_RETURN_UNDEFINED(); TRI_V8_TRY_CATCH_END } // ----------------------------------------------------------------------------- // --SECTION-- TRI_VOCBASE_T FUNCTIONS // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief wraps a TRI_vocbase_t //////////////////////////////////////////////////////////////////////////////// static v8::Handle WrapVocBase (v8::Isolate *isolate, TRI_vocbase_t const* database) { TRI_GET_GLOBALS(); v8::Handle result = WrapClass(isolate, v8g->VocbaseTempl, WRP_VOCBASE_TYPE, const_cast(database)); return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief selects a collection from the vocbase /// @startDocuBlock collectionDatabaseCollectionName /// `db.collection-name` /// /// Returns the collection with the given *collection-name*. If no such /// collection exists, create a collection named *collection-name* with the /// default properties. /// /// @EXAMPLES /// /// @EXAMPLE_ARANGOSH_OUTPUT{collectionDatabaseCollectionName} /// ~ db._create("example"); /// db.example; /// ~ db._drop("example"); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// static void MapGetVocBase (v8::Local const name, v8::PropertyCallbackInfo const& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } // convert the JavaScript string to a string v8::String::Utf8Value s(name); char* key = *s; size_t keyLength = s.length(); if (keyLength > 2 && key[keyLength - 2] == '(') { keyLength -= 2; key[keyLength] = '\0'; } // empty or null if (key == nullptr || *key == '\0') { TRI_V8_RETURN(v8::Handle()); } if (strcmp(key, "hasOwnProperty") == 0 || // this prevents calling the property getter again (i.e. recursion!) strcmp(key, "toString") == 0 || strcmp(key, "toJSON") == 0) { TRI_V8_RETURN(v8::Handle()); } TRI_vocbase_col_t* collection = nullptr; // generate a name under which the cached property is stored string cacheKey(key, keyLength); cacheKey.push_back('*'); v8::Local cacheName = TRI_V8_STD_STRING(cacheKey); v8::Handle holder = args.Holder()->ToObject(); if (*key == '_') { // special treatment for all properties starting with _ v8::Local const l = TRI_V8_PAIR_STRING(key, (int) keyLength); if (holder->HasRealNamedProperty(l)) { // some internal function inside db TRI_V8_RETURN(v8::Handle()); } // something in the prototype chain? v8::Local v = holder->GetRealNamedPropertyInPrototypeChain(l); if (! v.IsEmpty()) { if (! v->IsExternal()) { // something but an external... this means we can directly return this TRI_V8_RETURN(v8::Handle()); } } } if (holder->HasRealNamedProperty(cacheName)) { v8::Handle value = holder->GetRealNamedProperty(cacheName)->ToObject(); collection = TRI_UnwrapClass(value, WRP_VOCBASE_COL_TYPE); // check if the collection is from the same database if (collection != nullptr && collection->_vocbase == vocbase) { TRI_READ_LOCK_STATUS_VOCBASE_COL(collection); TRI_vocbase_col_status_e status = collection->_status; TRI_voc_cid_t cid = collection->_cid; uint32_t internalVersion = collection->_internalVersion; TRI_READ_UNLOCK_STATUS_VOCBASE_COL(collection); // check if the collection is still alive if (status != TRI_VOC_COL_STATUS_DELETED && cid > 0 && collection->_isLocal) { TRI_GET_GLOBALS(); TRI_GET_GLOBAL_STRING(_IdKey); TRI_GET_GLOBAL_STRING(VersionKeyHidden); if (value->Has(_IdKey)) { auto cachedCid = static_cast(TRI_ObjectToUInt64(value->Get(_IdKey), true)); uint32_t cachedVersion = (uint32_t) TRI_ObjectToInt64(value->Get(VersionKeyHidden)); if (cachedCid == cid && cachedVersion == internalVersion) { // cache hit TRI_V8_RETURN(value); } // store the updated version number in the object for future comparisons value->ForceSet(VersionKeyHidden, v8::Number::New(isolate, (double) internalVersion), v8::DontEnum); // cid has changed (i.e. collection has been dropped and re-created) or version has changed } } } // cache miss holder->Delete(cacheName); } if (ServerState::instance()->isCoordinator()) { shared_ptr const& ci = ClusterInfo::instance()->getCollection(vocbase->_name, std::string(key)); if ((*ci).empty()) { collection = nullptr; } else { collection = CoordinatorCollection(vocbase, *ci); if (collection != nullptr && collection->_cid == 0) { FreeCoordinatorCollection(collection); TRI_V8_RETURN(v8::Handle()); } } } else { collection = TRI_LookupCollectionByNameVocBase(vocbase, key); } if (collection == nullptr) { if (*key == '_') { TRI_V8_RETURN(v8::Handle()); } TRI_V8_RETURN_UNDEFINED(); } v8::Handle result = WrapCollection(isolate, collection); if (result.IsEmpty()) { if (ServerState::instance()->isCoordinator()) { FreeCoordinatorCollection(collection); } TRI_V8_RETURN_UNDEFINED(); } // TODO: caching the result makes subsequent results much faster, but // prevents physical removal of the collection or database holder->ForceSet(cacheName, result, v8::DontEnum); TRI_V8_RETURN(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief return the server version string /// @startDocuBlock databaseVersion /// `db._version()` /// /// Returns the server version string. Note that this is not the version of the /// database. /// /// @EXAMPLES /// /// @EXAMPLE_ARANGOSH_OUTPUT{dbVersion} /// require("internal").db._version(); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// static void JS_VersionServer (const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); TRI_V8_RETURN(TRI_V8_ASCII_STRING(TRI_VERSION)); } //////////////////////////////////////////////////////////////////////////////// /// @brief return the path to database files /// @startDocuBlock databasePath /// `db._path()` /// /// Returns the filesystem path of the current database as a string. /// /// @EXAMPLES /// /// @EXAMPLE_ARANGOSH_OUTPUT{dbPath} /// require("internal").db._path(); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// static void JS_PathDatabase (const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } TRI_V8_RETURN_STRING(vocbase->_path); } //////////////////////////////////////////////////////////////////////////////// /// @brief return the database id /// @startDocuBlock databaseId /// `db._id()` /// /// Returns the id of the current database as a string. /// /// @EXAMPLES /// /// @EXAMPLE_ARANGOSH_OUTPUT{dbId} /// require("internal").db._id(); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// static void JS_IdDatabase (const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } TRI_V8_RETURN(V8TickId(isolate, vocbase->_id)); } //////////////////////////////////////////////////////////////////////////////// /// @brief return the database name /// @startDocuBlock databaseName /// `db._name()` /// /// Returns the name of the current database as a string. /// /// @EXAMPLES /// /// @EXAMPLE_ARANGOSH_OUTPUT{dbName} /// require("internal").db._name(); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// static void JS_NameDatabase (const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } TRI_V8_RETURN_STRING(vocbase->_name); } //////////////////////////////////////////////////////////////////////////////// /// @brief return the database type /// @startDocuBlock databaseIsSystem /// `db._isSystem()` /// /// Returns whether the currently used database is the *_system* database. /// The system database has some special privileges and properties, for example, /// database management operations such as create or drop can only be executed /// from within this database. Additionally, the *_system* database itself /// cannot be dropped. /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// static void JS_IsSystemDatabase (const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } TRI_V8_RETURN(v8::Boolean::New(isolate, TRI_IsSystemVocBase(vocbase))); } //////////////////////////////////////////////////////////////////////////////// /// @brief change the current database /// @startDocuBlock databaseUseDatabase /// `db._useDatabase(name)` /// /// Changes the current database to the database specified by *name*. Note /// that the database specified by *name* must already exist. /// /// Changing the database might be disallowed in some contexts, for example /// server-side actions (including Foxx). /// /// When performing this command from arangosh, the current credentials (username /// and password) will be re-used. These credentials might not be valid to /// connect to the database specified by *name*. Additionally, the database /// only be accessed from certain endpoints only. In this case, switching the /// database might not work, and the connection / session should be closed and /// restarted with different username and password credentials and/or /// endpoint data. /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// static void JS_UseDatabase (const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("db._useDatabase()"); } TRI_GET_GLOBALS(); if (! v8g->_allowUseDatabase) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_FORBIDDEN); } string&& name = TRI_ObjectToString(args[0]); TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_INTERNAL); } if (TRI_EqualString(name.c_str(), vocbase->_name)) { // same database. nothing to do TRI_V8_RETURN(WrapVocBase(isolate, vocbase)); } if (ServerState::instance()->isCoordinator()) { vocbase = TRI_UseCoordinatorDatabaseServer(static_cast(v8g->_server), name.c_str()); } else { // check if the other database exists, and increase its refcount vocbase = TRI_UseDatabaseServer(static_cast(v8g->_server), name.c_str()); } if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } // switch databases void* orig = v8g->_vocbase; TRI_ASSERT(orig != nullptr); v8g->_vocbase = vocbase; TRI_ASSERT(orig != vocbase); TRI_ReleaseDatabaseServer(static_cast(v8g->_server), static_cast(orig)); TRI_V8_RETURN(WrapVocBase(isolate, vocbase)); } //////////////////////////////////////////////////////////////////////////////// /// @brief return the list of all existing databases in a coordinator //////////////////////////////////////////////////////////////////////////////// static void ListDatabasesCoordinator (const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); // Arguments are already checked, there are 0 or 3. ClusterInfo* ci = ClusterInfo::instance(); if (args.Length() == 0) { vector list = ci->listDatabases(true); v8::Handle result = v8::Array::New(isolate); for (size_t i = 0; i < list.size(); ++i) { result->Set((uint32_t) i, TRI_V8_STD_STRING(list[i])); } TRI_V8_RETURN(result); } else { // We have to ask a DBServer, any will do: int tries = 0; vector DBServers; while (true) { DBServers = ci->getCurrentDBServers(); if (! DBServers.empty()) { ServerID sid = DBServers[0]; ClusterComm* cc = ClusterComm::instance(); ClusterCommResult* res; map headers; headers["Authentication"] = TRI_ObjectToString(args[2]); res = cc->syncRequest("", 0, "server:" + sid, triagens::rest::HttpRequest::HTTP_REQUEST_GET, "/_api/database/user", string(""), headers, 0.0); if (res->status == CL_COMM_SENT) { // We got an array back as JSON, let's parse it and build a v8 StringBuffer& body = res->result->getBody(); TRI_json_t* json = JsonHelper::fromString(body.c_str()); delete res; if (json != 0 && JsonHelper::isObject(json)) { TRI_json_t const* dotresult = JsonHelper::getObjectElement(json, "result"); if (dotresult != 0) { vector list = JsonHelper::stringArray(dotresult); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); v8::Handle result = v8::Array::New(isolate); for (size_t i = 0; i < list.size(); ++i) { result->Set((uint32_t) i, TRI_V8_STD_STRING(list[i])); } TRI_V8_RETURN(result); } TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); } } else { delete res; } } if (++tries >= 2) { break; } ci->loadCurrentDBServers(); // just in case some new have arrived } // Give up: TRI_V8_RETURN_UNDEFINED(); } } //////////////////////////////////////////////////////////////////////////////// /// @brief return the list of all existing databases /// @startDocuBlock databaseListDatabase /// `db._listDatabases()` /// /// Returns the list of all databases. This method can only be used from within /// the *_system* database. /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// static void JS_ListDatabases (const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); const uint32_t argc = args.Length(); if (argc > 1) { TRI_V8_THROW_EXCEPTION_USAGE("db._listDatabases()"); } TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } if (argc == 0 && ! TRI_IsSystemVocBase(vocbase)) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE); } // If we are a coordinator in a cluster, we have to behave differently: if (ServerState::instance()->isCoordinator()) { ListDatabasesCoordinator(args); return; } TRI_GET_GLOBALS(); std::vector names; int res; if (argc == 0) { // return all databases res = TRI_GetDatabaseNamesServer(static_cast(v8g->_server), names); } else { // return all databases for a specific user std::string&& username = TRI_ObjectToString(args[0]); res = TRI_GetUserDatabasesServer(static_cast(v8g->_server), username.c_str(), names); } if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } v8::Handle result = v8::Array::New(isolate); for (size_t i = 0; i < names.size(); ++i) { result->Set((uint32_t) i, TRI_V8_STD_STRING(names[i])); } TRI_V8_RETURN(result); } //////////////////////////////////////////////////////////////////////////////// /// @brief create a new database, case of a coordinator in a cluster //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief helper function for the agency /// /// `place` can be "/Target", "/Plan" or "/Current" and name is the database /// name. //////////////////////////////////////////////////////////////////////////////// static void CreateDatabaseCoordinator (const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); // First work with the arguments to create a JSON entry: string const name = TRI_ObjectToString(args[0]); if (! TRI_IsAllowedNameVocBase(false, name.c_str())) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NAME_INVALID); } TRI_json_t* json = TRI_CreateObjectJson(TRI_UNKNOWN_MEM_ZONE); if (nullptr == json) { TRI_V8_THROW_EXCEPTION_MEMORY(); } uint64_t const id = ClusterInfo::instance()->uniqid(); std::string const idString(StringUtils::itoa(id)); TRI_Insert3ObjectJson(TRI_UNKNOWN_MEM_ZONE, json, "id", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, idString.c_str(), idString.size())); std::string const valueString(TRI_ObjectToString(args[0])); TRI_Insert3ObjectJson(TRI_UNKNOWN_MEM_ZONE, json, "name", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, valueString.c_str(), valueString.size())); if (args.Length() > 1) { TRI_Insert3ObjectJson(TRI_UNKNOWN_MEM_ZONE, json, "options", TRI_ObjectToJson(isolate, args[1])); } std::string const serverId(ServerState::instance()->getId()); TRI_Insert3ObjectJson(TRI_UNKNOWN_MEM_ZONE, json, "coordinator", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, serverId.c_str(), serverId.size())); ClusterInfo* ci = ClusterInfo::instance(); string errorMsg; int res = ci->createDatabaseCoordinator( name, json, errorMsg, 120.0); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION_MESSAGE(res, errorMsg); } // database was created successfully in agency TRI_GET_GLOBALS(); // now wait for heartbeat thread to create the database object TRI_vocbase_t* vocbase = nullptr; int tries = 0; while (++tries <= 6000) { vocbase = TRI_UseByIdCoordinatorDatabaseServer(static_cast(v8g->_server), id); if (vocbase != nullptr) { break; } // sleep usleep(10000); } if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_INTERNAL); } // now run upgrade and copy users into context if (args.Length() >= 3 && args[2]->IsArray()) { v8::Handle users = v8::Object::New(isolate); users->Set(TRI_V8_ASCII_STRING("users"), args[2]); isolate->GetCurrentContext()->Global()->Set(TRI_V8_ASCII_STRING("UPGRADE_ARGS"), users); } else { isolate->GetCurrentContext()->Global()->Set(TRI_V8_ASCII_STRING("UPGRADE_ARGS"), v8::Object::New(isolate)); } // switch databases TRI_vocbase_t* orig = v8g->_vocbase; TRI_ASSERT(orig != nullptr); v8g->_vocbase = vocbase; // initalise database bool allowUseDatabase = v8g->_allowUseDatabase; v8g->_allowUseDatabase = true; v8g->_loader->executeGlobalScript(isolate, isolate->GetCurrentContext(), "server/bootstrap/coordinator-database.js"); v8g->_allowUseDatabase = allowUseDatabase; // and switch back v8g->_vocbase = orig; TRI_ReleaseVocBase(vocbase); TRI_V8_RETURN_TRUE(); } //////////////////////////////////////////////////////////////////////////////// /// @brief create a new database /// @startDocuBlock databaseCreateDatabase /// `db._createDatabase(name, options, users)` /// /// Creates a new database with the name specified by *name*. /// There are restrictions for database names /// (see [DatabaseNames](../NamingConventions/DatabaseNames.md)). /// /// Note that even if the database is created successfully, there will be no /// change into the current database to the new database. Changing the current /// database must explicitly be requested by using the /// *db._useDatabase* method. /// /// The *options* attribute currently has no meaning and is reserved for /// future use. /// /// The optional *users* attribute can be used to create initial users for /// the new database. If specified, it must be a list of user objects. Each user /// object can contain the following attributes: /// /// * *username*: the user name as a string. This attribute is mandatory. /// * *passwd*: the user password as a string. If not specified, then it defaults /// to the empty string. /// * *active*: a boolean flag indicating whether the user account should be /// active or not. The default value is *true*. /// * *extra*: an optional JSON object with extra user information. The data /// contained in *extra* will be stored for the user but not be interpreted /// further by ArangoDB. /// /// If no initial users are specified, a default user *root* will be created /// with an empty string password. This ensures that the new database will be /// accessible via HTTP after it is created. /// /// You can create users in a database if no initial user is specified. Switch /// into the new database (username and password must be identical to the current /// session) and add or modify users with the following commands. /// /// ```js /// require("org/arangodb/users").save(username, password, true); /// require("org/arangodb/users").update(username, password, true); /// require("org/arangodb/users").remove(username); /// ``` /// Alternatively, you can specify user data directly. For example: /// /// ```js /// db._createDatabase("newDB", [], [{ username: "newUser", passwd: "123456", active: true}]) /// ``` /// /// Those methods can only be used from within the *_system* database. /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// static void JS_CreateDatabase (const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); if (args.Length() < 1 || args.Length() > 3) { TRI_V8_THROW_EXCEPTION_USAGE("db._createDatabase(, , )"); } TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } if (TRI_GetOperationModeServer() == TRI_VOCBASE_MODE_NO_CREATE) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_READ_ONLY); } if (! TRI_IsSystemVocBase(vocbase)) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE); } if (ServerState::instance()->isCoordinator()) { CreateDatabaseCoordinator(args); return; } TRI_GET_GLOBALS(); TRI_voc_tick_t id = 0; // get database defaults from server TRI_vocbase_defaults_t defaults; TRI_GetDatabaseDefaultsServer(static_cast(v8g->_server), &defaults); v8::Local keyDefaultMaximalSize = TRI_V8_ASCII_STRING("defaultMaximalSize"); v8::Local keyDefaultWaitForSync = TRI_V8_ASCII_STRING("defaultWaitForSync"); v8::Local keyRequireAuthentication = TRI_V8_ASCII_STRING("requireAuthentication"); v8::Local keyRequireAuthenticationUnixSockets = TRI_V8_ASCII_STRING("requireAuthenticationUnixSockets"); v8::Local keyAuthenticateSystemOnly = TRI_V8_ASCII_STRING("authenticateSystemOnly"); v8::Local keyForceSyncProperties = TRI_V8_ASCII_STRING("forceSyncProperties"); // overwrite database defaults from args[2] if (args.Length() > 1 && args[1]->IsObject()) { v8::Handle options = args[1]->ToObject(); if (options->Has(keyDefaultMaximalSize)) { defaults.defaultMaximalSize = (TRI_voc_size_t) options->Get(keyDefaultMaximalSize)->IntegerValue(); } if (options->Has(keyDefaultWaitForSync)) { defaults.defaultWaitForSync = options->Get(keyDefaultWaitForSync)->BooleanValue(); } if (options->Has(keyRequireAuthentication)) { defaults.requireAuthentication = options->Get(keyRequireAuthentication)->BooleanValue(); } if (options->Has(keyRequireAuthenticationUnixSockets)) { defaults.requireAuthenticationUnixSockets = options->Get(keyRequireAuthenticationUnixSockets)->BooleanValue(); } if (options->Has(keyAuthenticateSystemOnly)) { defaults.authenticateSystemOnly = options->Get(keyAuthenticateSystemOnly)->BooleanValue(); } if (options->Has(keyForceSyncProperties)) { defaults.forceSyncProperties = options->Get(keyForceSyncProperties)->BooleanValue(); } TRI_GET_GLOBAL_STRING(IdKey); if (options->Has(IdKey)) { // only used for testing to create database with a specific id id = TRI_ObjectToUInt64(options->Get(IdKey), true); } } string const name = TRI_ObjectToString(args[0]); TRI_vocbase_t* database; int res = TRI_CreateDatabaseServer(static_cast(v8g->_server), id, name.c_str(), &defaults, &database, true); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } TRI_ASSERT(database != nullptr); // copy users into context if (args.Length() >= 3 && args[2]->IsArray()) { v8::Handle users = v8::Object::New(isolate); users->Set(TRI_V8_ASCII_STRING("users"), args[2]); isolate->GetCurrentContext()->Global()->Set(TRI_V8_ASCII_STRING("UPGRADE_ARGS"), users); } else { isolate->GetCurrentContext()->Global()->Set(TRI_V8_ASCII_STRING("UPGRADE_ARGS"), v8::Object::New(isolate)); } // switch databases TRI_vocbase_t* orig = v8g->_vocbase; TRI_ASSERT(orig != nullptr); v8g->_vocbase = database; // initalise database v8g->_loader->executeGlobalScript(isolate, isolate->GetCurrentContext(), "server/bootstrap/local-database.js"); // and switch back v8g->_vocbase = orig; // populate the authentication cache. otherwise no one can access the new database TRI_ReloadAuthInfo(database); // finally decrease the reference-counter TRI_ReleaseVocBase(database); TRI_V8_RETURN_TRUE(); } //////////////////////////////////////////////////////////////////////////////// /// @brief drop a database, case of a coordinator in a cluster //////////////////////////////////////////////////////////////////////////////// static void DropDatabaseCoordinator (const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); TRI_GET_GLOBALS(); // Arguments are already checked, there is exactly one argument string const name = TRI_ObjectToString(args[0]); TRI_vocbase_t* vocbase = TRI_UseCoordinatorDatabaseServer(static_cast(v8g->_server), name.c_str()); if (vocbase == nullptr) { // no such database TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } TRI_voc_tick_t const id = vocbase->_id; TRI_ReleaseVocBase(vocbase); ClusterInfo* ci = ClusterInfo::instance(); string errorMsg; // clear local sid cache for database triagens::arango::VocbaseContext::clearSid(name); int res = ci->dropDatabaseCoordinator(name, errorMsg, 120.0); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION_MESSAGE(res, errorMsg); } // now wait for heartbeat thread to drop the database object int tries = 0; while (++tries <= 6000) { TRI_vocbase_t* vocbase = TRI_UseByIdCoordinatorDatabaseServer(static_cast(v8g->_server), id); if (vocbase == nullptr) { // object has vanished break; } // sleep usleep(10000); } TRI_V8_RETURN_TRUE(); } //////////////////////////////////////////////////////////////////////////////// /// @brief drop an existing database /// @startDocuBlock databaseDropDatabase /// `db._dropDatabase(name)` /// /// Drops the database specified by *name*. The database specified by /// *name* must exist. /// /// **Note**: Dropping databases is only possible from within the *_system* /// database. The *_system* database itself cannot be dropped. /// /// Databases are dropped asynchronously, and will be physically removed if /// all clients have disconnected and references have been garbage-collected. /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// static void JS_DropDatabase (const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); if (args.Length() != 1) { TRI_V8_THROW_EXCEPTION_USAGE("db._dropDatabase()"); } TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } if (! TRI_IsSystemVocBase(vocbase)) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE); } // If we are a coordinator in a cluster, we have to behave differently: if (ServerState::instance()->isCoordinator()) { DropDatabaseCoordinator(args); return; } string const name = TRI_ObjectToString(args[0]); TRI_GET_GLOBALS(); int res = TRI_DropDatabaseServer(static_cast(v8g->_server), name.c_str(), true, true); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_THROW_EXCEPTION(res); } // clear local sid cache for the database triagens::arango::VocbaseContext::clearSid(name); // run the garbage collection in case the database held some objects which can now be freed TRI_RunGarbageCollectionV8(isolate, 0.25); TRI_V8ReloadRouting(isolate); TRI_V8_RETURN_TRUE(); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns a list of all endpoints /// /// @FUN{LIST_ENDPOINTS} //////////////////////////////////////////////////////////////////////////////// static void JS_ListEndpoints (const v8::FunctionCallbackInfo& args) { TRI_V8_TRY_CATCH_BEGIN(isolate); v8::HandleScope scope(isolate); if (args.Length() != 0) { TRI_V8_THROW_EXCEPTION_USAGE("db._listEndpoints()"); } TRI_GET_GLOBALS(); TRI_server_t* server = static_cast(v8g->_server); ApplicationEndpointServer* s = static_cast(server->_applicationEndpointServer); if (s == nullptr) { // not implemented in console mode TRI_V8_THROW_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED); } TRI_vocbase_t* vocbase = GetContextVocBase(isolate); if (vocbase == nullptr) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND); } if (! TRI_IsSystemVocBase(vocbase)) { TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_USE_SYSTEM_DATABASE); } auto const& endpoints = s->getEndpoints(); v8::Handle result = v8::Array::New(isolate); uint32_t j = 0; map >::const_iterator it; for (it = endpoints.begin(); it != endpoints.end(); ++it) { v8::Handle dbNames = v8::Array::New(isolate); for (uint32_t i = 0; i < (*it).second.size(); ++i) { dbNames->Set(i, TRI_V8_STD_STRING((*it).second.at(i))); } v8::Handle item = v8::Object::New(isolate); item->Set(TRI_V8_ASCII_STRING("endpoint"), TRI_V8_STD_STRING((*it).first)); item->Set(TRI_V8_ASCII_STRING("databases"), dbNames); result->Set(j++, item); } TRI_V8_RETURN(result); TRI_V8_TRY_CATCH_END } // ----------------------------------------------------------------------------- // --SECTION-- MODULE // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief parse vertex handle from a v8 value (string | object) //////////////////////////////////////////////////////////////////////////////// int TRI_ParseVertex (const v8::FunctionCallbackInfo& args, CollectionNameResolver const* resolver, TRI_voc_cid_t& cid, std::unique_ptr& key, v8::Handle const val) { v8::Isolate* isolate = args.GetIsolate(); v8::HandleScope scope(isolate); TRI_ASSERT(key.get() == nullptr); // reset everything string collectionName; TRI_voc_rid_t rid = 0; // try to extract the collection name, key, and revision from the object passed if (! ExtractDocumentHandle(isolate, val, collectionName, key, rid)) { return TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD; } // we have at least a key, we also might have a collection name TRI_ASSERT(key.get() != nullptr); if (collectionName.empty()) { // we do not know the collection return TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD; } if (ServerState::instance()->isDBServer()) { cid = resolver->getCollectionIdCluster(collectionName); } else { cid = resolver->getCollectionId(collectionName); } if (cid == 0) { return TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND; } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief return the private WRP_VOCBASE_COL_TYPE value //////////////////////////////////////////////////////////////////////////////// int32_t TRI_GetVocBaseColType () { return WRP_VOCBASE_COL_TYPE; } //////////////////////////////////////////////////////////////////////////////// /// @brief run version check //////////////////////////////////////////////////////////////////////////////// bool TRI_UpgradeDatabase (TRI_vocbase_t* vocbase, JSLoader* startupLoader, v8::Handle context) { TRI_ASSERT(startupLoader != nullptr); auto isolate = context->GetIsolate(); v8::HandleScope scope(isolate); TRI_GET_GLOBALS(); TRI_vocbase_t* orig = v8g->_vocbase; v8g->_vocbase = vocbase; v8::Handle result = startupLoader->executeGlobalScript(isolate, isolate->GetCurrentContext(), "server/upgrade-database.js"); bool ok = TRI_ObjectToBoolean(result); if (! ok) { vocbase->_state = (sig_atomic_t) TRI_VOCBASE_STATE_FAILED_VERSION; } v8g->_vocbase = orig; return ok; } //////////////////////////////////////////////////////////////////////////////// /// @brief run upgrade check //////////////////////////////////////////////////////////////////////////////// int TRI_CheckDatabaseVersion (TRI_vocbase_t* vocbase, JSLoader* startupLoader, v8::Handle context) { TRI_ASSERT(startupLoader != nullptr); auto isolate = context->GetIsolate(); v8::HandleScope scope(isolate); TRI_GET_GLOBALS(); TRI_vocbase_t* orig = v8g->_vocbase; v8g->_vocbase = vocbase; v8::Handle result = startupLoader->executeGlobalScript(isolate, isolate->GetCurrentContext(), "server/check-version.js"); int code = (int) TRI_ObjectToInt64(result); v8g->_vocbase = orig; return code; } //////////////////////////////////////////////////////////////////////////////// /// @brief reloads routing //////////////////////////////////////////////////////////////////////////////// void TRI_V8ReloadRouting (v8::Isolate *isolate) { TRI_ExecuteJavaScriptString(isolate, isolate->GetCurrentContext(), TRI_V8_ASCII_STRING("require('internal').executeGlobalContextFunction('reloadRouting')"), TRI_V8_ASCII_STRING("reload routing"), false); } //////////////////////////////////////////////////////////////////////////////// /// @brief creates a TRI_vocbase_t global context //////////////////////////////////////////////////////////////////////////////// void TRI_InitV8VocBridge (v8::Isolate* isolate, triagens::arango::ApplicationV8* applicationV8, v8::Handle context, triagens::aql::QueryRegistry* queryRegistry, TRI_server_t* server, TRI_vocbase_t* vocbase, JSLoader* loader, size_t threadNumber) { v8::HandleScope scope(isolate); // check the isolate TRI_v8_global_t* v8g = TRI_CreateV8Globals(isolate); TRI_ASSERT(v8g->_transactionContext == nullptr); v8g->_transactionContext = new V8TransactionContext(true); static_cast(v8g->_transactionContext)->makeGlobal(); // register the query registry v8g->_queryRegistry = queryRegistry; // register the server v8g->_server = server; // register the database v8g->_vocbase = vocbase; // register the startup loader v8g->_loader = loader; // register the context dealer v8g->_applicationV8 = applicationV8; v8::Handle ArangoNS; v8::Handle rt; v8::Handle ft; v8::Handle pt; // ............................................................................. // generate the TRI_vocbase_t template // ............................................................................. ft = v8::FunctionTemplate::New(isolate); ft->SetClassName(TRI_V8_ASCII_STRING("ArangoDatabase")); ArangoNS = ft->InstanceTemplate(); ArangoNS->SetInternalFieldCount(2); ArangoNS->SetNamedPropertyHandler(MapGetVocBase); // for any database function added here, be sure to add it to in function // JS_CompletionsVocbase, too for the auto-completion TRI_AddMethodVocbase(isolate, ArangoNS, TRI_V8_ASCII_STRING("_version"), JS_VersionServer); TRI_AddMethodVocbase(isolate, ArangoNS, TRI_V8_ASCII_STRING("_id"), JS_IdDatabase); TRI_AddMethodVocbase(isolate, ArangoNS, TRI_V8_ASCII_STRING("_isSystem"), JS_IsSystemDatabase); TRI_AddMethodVocbase(isolate, ArangoNS, TRI_V8_ASCII_STRING("_name"), JS_NameDatabase); TRI_AddMethodVocbase(isolate, ArangoNS, TRI_V8_ASCII_STRING("_path"), JS_PathDatabase); TRI_AddMethodVocbase(isolate, ArangoNS, TRI_V8_ASCII_STRING("_createDatabase"), JS_CreateDatabase); TRI_AddMethodVocbase(isolate, ArangoNS, TRI_V8_ASCII_STRING("_dropDatabase"), JS_DropDatabase); TRI_AddMethodVocbase(isolate, ArangoNS, TRI_V8_ASCII_STRING("_listDatabases"), JS_ListDatabases); TRI_AddMethodVocbase(isolate, ArangoNS, TRI_V8_ASCII_STRING("_useDatabase"), JS_UseDatabase); TRI_InitV8Statistics(isolate, context); TRI_InitV8indexArangoDB(isolate, ArangoNS); TRI_InitV8collection(context, server, vocbase, loader, threadNumber, v8g, isolate, ArangoNS); v8g->VocbaseTempl.Reset(isolate, ArangoNS); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("ArangoDatabase"), ft->GetFunction()); TRI_InitV8ShapedJson(isolate, context, threadNumber, v8g); TRI_InitV8cursor(context, v8g); // ............................................................................. // generate global functions // ............................................................................. // AQL functions. not intended to be used directly by end users TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_EXECUTE"), JS_ExecuteAql, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_EXECUTEJSON"), JS_ExecuteAqlJson, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_EXPLAIN"), JS_ExplainAql, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_PARSE"), JS_ParseAql, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_WARNING"), JS_WarningAql, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_QUERIES_PROPERTIES"), JS_QueriesPropertiesAql, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_QUERIES_CURRENT"), JS_QueriesCurrentAql, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_QUERIES_SLOW"), JS_QueriesSlowAql, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_QUERIES_KILL"), JS_QueriesKillAql, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_QUERY_SLEEP"), JS_QuerySleepAql, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_QUERY_IS_KILLED"), JS_QueryIsKilledAql, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_QUERY_CACHE_PROPERTIES"), JS_QueryCachePropertiesAql, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_QUERY_CACHE_INVALIDATE"), JS_QueryCacheInvalidateAql, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("THROW_COLLECTION_NOT_LOADED"), JS_ThrowCollectionNotLoaded, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("CPP_SHORTEST_PATH"), JS_QueryShortestPath, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("CPP_NEIGHBORS"), JS_QueryNeighbors, true); TRI_InitV8Replication(isolate, context, server, vocbase, loader, threadNumber, v8g); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("COMPARE_STRING"), JS_CompareString); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("NORMALIZE_STRING"), JS_NormalizeString); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("TIMEZONES"), JS_GetIcuTimezones); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("LOCALES"), JS_GetIcuLocales); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("FORMAT_DATETIME"), JS_FormatDatetime); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("PARSE_DATETIME"), JS_ParseDatetime); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("LIST_ENDPOINTS"), JS_ListEndpoints, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("RELOAD_AUTH"), JS_ReloadAuth, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("TRANSACTION"), JS_Transaction, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("WAL_FLUSH"), JS_FlushWal, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("WAL_PROPERTIES"), JS_PropertiesWal, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("ENABLE_NATIVE_BACKTRACES"), JS_EnableNativeBacktraces, true); TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("Debug"), JS_Debug, true); // ............................................................................. // create global variables // ............................................................................. v8::Handle v = WrapVocBase(isolate, vocbase); if (v.IsEmpty()) { // TODO: raise an error here LOG_ERROR("out of memory when initialising VocBase"); } else { TRI_AddGlobalVariableVocbase(isolate, context, TRI_V8_ASCII_STRING("db"), v); } // current thread number context->Global()->ForceSet(TRI_V8_ASCII_STRING("THREAD_NUMBER"), v8::Number::New(isolate, (double) threadNumber), v8::ReadOnly); // whether or not statistics are enabled context->Global()->ForceSet(TRI_V8_ASCII_STRING("ENABLE_STATISTICS"), v8::Boolean::New(isolate, TRI_ENABLE_STATISTICS), v8::ReadOnly); // a thread-global variable that will is supposed to contain the AQL module // do not remove this, otherwise AQL queries will break context->Global()->ForceSet(TRI_V8_ASCII_STRING("_AQL"), v8::Undefined(isolate), v8::DontEnum); } // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: