From ddfb5c965d7376d287010e09bf45825a2a784c54 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Wed, 24 Sep 2014 09:42:16 +0200 Subject: [PATCH] merged experimental features --- arangod/CMakeLists.txt | 15 +- arangod/Makefile.files | 15 +- arangod/V8Server/ApplicationV8.cpp | 2 + arangod/V8Server/v8-user-structures.cpp | 1716 +++++++++++++++++++++++ arangod/V8Server/v8-user-structures.h | 70 + arangod/VocBase/vocbase.cpp | 8 + arangod/VocBase/vocbase.h | 4 + lib/V8/v8-conv.cpp | 48 + lib/V8/v8-conv.h | 12 + 9 files changed, 1876 insertions(+), 14 deletions(-) create mode 100644 arangod/V8Server/v8-user-structures.cpp create mode 100644 arangod/V8Server/v8-user-structures.h diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index 86c5b80c35..02c16c8195 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -135,16 +135,17 @@ add_executable( V8Server/V8QueueJob.cpp V8Server/V8TimerTask.cpp V8Server/v8-actions.cpp - V8Server/v8-dispatcher.cpp - V8Server/v8-query.cpp - V8Server/v8-vocbase.cpp - V8Server/v8-wrapshapedjson.cpp - V8Server/v8-vocindex.cpp - V8Server/v8-voccursor.cpp - V8Server/v8-replication.cpp V8Server/v8-collection.cpp V8Server/v8-collection-util.cpp + V8Server/v8-dispatcher.cpp + V8Server/v8-query.cpp + V8Server/v8-replication.cpp + V8Server/v8-user-structures.cpp V8Server/v8-util.cpp + V8Server/v8-vocbase.cpp + V8Server/v8-voccursor.cpp + V8Server/v8-vocindex.cpp + V8Server/v8-wrapshapedjson.cpp VocBase/auth.cpp VocBase/barrier.cpp VocBase/cleanup.cpp diff --git a/arangod/Makefile.files b/arangod/Makefile.files index 13f14d85ce..8374ab5a3f 100644 --- a/arangod/Makefile.files +++ b/arangod/Makefile.files @@ -116,16 +116,17 @@ arangod_libarangod_a_SOURCES = \ arangod/V8Server/V8QueueJob.cpp \ arangod/V8Server/V8TimerTask.cpp \ arangod/V8Server/v8-actions.cpp \ - arangod/V8Server/v8-dispatcher.cpp \ - arangod/V8Server/v8-query.cpp \ - arangod/V8Server/v8-vocbase.cpp \ - arangod/V8Server/v8-wrapshapedjson.cpp \ - arangod/V8Server/v8-vocindex.cpp \ - arangod/V8Server/v8-voccursor.cpp \ - arangod/V8Server/v8-replication.cpp \ arangod/V8Server/v8-collection.cpp \ arangod/V8Server/v8-collection-util.cpp \ + arangod/V8Server/v8-dispatcher.cpp \ + arangod/V8Server/v8-query.cpp \ + arangod/V8Server/v8-replication.cpp \ + arangod/V8Server/v8-vocbase.cpp \ + arangod/V8Server/v8-vocindex.cpp \ + arangod/V8Server/v8-voccursor.cpp \ + arangod/V8Server/v8-user-structures.cpp \ arangod/V8Server/v8-util.cpp \ + arangod/V8Server/v8-wrapshapedjson.cpp \ arangod/VocBase/auth.cpp \ arangod/VocBase/barrier.cpp \ arangod/VocBase/cleanup.cpp \ diff --git a/arangod/V8Server/ApplicationV8.cpp b/arangod/V8Server/ApplicationV8.cpp index 862c68e78d..f4037340e8 100644 --- a/arangod/V8Server/ApplicationV8.cpp +++ b/arangod/V8Server/ApplicationV8.cpp @@ -52,6 +52,7 @@ #include "V8Server/v8-actions.h" #include "V8Server/v8-dispatcher.h" #include "V8Server/v8-query.h" +#include "V8Server/v8-user-structures.h" #include "V8Server/v8-vocbase.h" #include "VocBase/server.h" #include "Cluster/ServerState.h" @@ -1222,6 +1223,7 @@ bool ApplicationV8::prepareV8Instance (const string& name, size_t i, bool useAct TRI_InitV8VocBridge(context->_context, _server, _vocbase, &_startupLoader, i); TRI_InitV8Queries(context->_context); + TRI_InitV8UserStructures(context->_context); TRI_InitV8Cluster(context->_context); if (_dispatcher->dispatcher() != nullptr) { diff --git a/arangod/V8Server/v8-user-structures.cpp b/arangod/V8Server/v8-user-structures.cpp new file mode 100644 index 0000000000..cf1c0de14b --- /dev/null +++ b/arangod/V8Server/v8-user-structures.cpp @@ -0,0 +1,1716 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief V8 user data structures +/// +/// @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 Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany +/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +#include "v8-user-structures.h" +#include "Basics/ReadWriteLock.h" +#include "Basics/ReadLocker.h" +#include "Basics/WriteLocker.h" +#include "Basics/hashes.h" +#include "Basics/json.h" +#include "Basics/json-utilities.h" +#include "Basics/tri-strings.h" +#include "Utils/Exception.h" +#include "VocBase/vocbase.h" +#include "V8/v8-conv.h" +#include "V8/v8-utils.h" + +// ----------------------------------------------------------------------------- +// --SECTION-- struct KeySpaceElement +// ----------------------------------------------------------------------------- + +struct KeySpaceElement { + KeySpaceElement () = delete; + + KeySpaceElement (char const* k, + size_t length, + TRI_json_t* json) + : key(nullptr), + json(json) { + + key = TRI_DuplicateString2Z(TRI_UNKNOWN_MEM_ZONE, k, length); + if (key == nullptr) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); + } + } + + ~KeySpaceElement () { + if (key != nullptr) { + TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, key); + } + if (json != nullptr) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); + } + } + + void setValue (TRI_json_t* value) { + if (json != nullptr) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); + json = nullptr; + } + json = value; + } + + char* key; + TRI_json_t* json; +}; + +// ----------------------------------------------------------------------------- +// --SECTION-- class KeySpace +// ----------------------------------------------------------------------------- + +class KeySpace { + public: + KeySpace (uint32_t initialSize) + : _lock() { + + TRI_InitAssociativePointer(&_hash, + TRI_UNKNOWN_MEM_ZONE, + TRI_HashStringKeyAssociativePointer, + HashHash, + EqualHash, + nullptr); + + if (initialSize > 0) { + TRI_ReserveAssociativePointer(&_hash, initialSize); + } + } + + ~KeySpace () { + uint32_t const n = _hash._nrAlloc; + for (uint32_t i = 0; i < n; ++i) { + auto element = static_cast(_hash._table[i]); + + if (element != nullptr) { + delete element; + } + } + TRI_DestroyAssociativePointer(&_hash); + } + + static uint64_t HashHash (TRI_associative_pointer_t*, + void const* element) { + return TRI_FnvHashString(static_cast(element)->key); + } + + static bool EqualHash (TRI_associative_pointer_t*, + void const* key, + void const* element) { + return TRI_EqualString(static_cast(key), static_cast(element)->key); + } + + uint32_t keyspaceCount () { + READ_LOCKER(_lock); + return _hash._nrUsed; + } + + uint32_t keyspaceCount (std::string const& prefix) { + uint32_t count = 0; + READ_LOCKER(_lock); + + uint32_t const n = _hash._nrAlloc; + for (uint32_t i = 0; i < n; ++i) { + auto data = static_cast(_hash._table[i]); + + if (data != nullptr) { + if (TRI_IsPrefixString(data->key, prefix.c_str())) { + ++count; + } + } + } + + return count; + } + + v8::Handle keyspaceRemove () { + v8::HandleScope scope; + + WRITE_LOCKER(_lock); + + uint32_t const n = _hash._nrAlloc; + uint32_t deleted = 0; + + for (uint32_t i = 0; i < n; ++i) { + auto element = static_cast(_hash._table[i]); + + if (element != nullptr) { + delete element; + _hash._table[i] = nullptr; + ++deleted; + } + } + _hash._nrUsed = 0; + + return scope.Close(v8::Number::New(static_cast(deleted))); + } + + v8::Handle keyspaceRemove (std::string const& prefix) { + v8::HandleScope scope; + + WRITE_LOCKER(_lock); + + uint32_t const n = _hash._nrAlloc; + uint32_t i = 0; + uint32_t deleted = 0; + + while (i < n) { + auto element = static_cast(_hash._table[i]); + + if (element != nullptr) { + if (TRI_IsPrefixString(element->key, prefix.c_str())) { + if (TRI_RemoveKeyAssociativePointer(&_hash, element->key) != nullptr) { + delete element; + ++deleted; + continue; + } + } + } + ++i; + } + + return scope.Close(v8::Number::New(static_cast(deleted))); + } + + v8::Handle keyspaceKeys () { + v8::HandleScope scope; + + v8::Handle result; + { + READ_LOCKER(_lock); + + uint32_t const n = _hash._nrAlloc; + uint32_t count = 0; + result = v8::Array::New(static_cast(_hash._nrUsed)); + + for (uint32_t i = 0; i < n; ++i) { + auto element = static_cast(_hash._table[i]); + + if (element != nullptr) { + result->Set(count++, v8::String::New(element->key)); + } + } + } + + return scope.Close(result); + } + + v8::Handle keyspaceKeys (std::string const& prefix) { + v8::HandleScope scope; + + v8::Handle result; + { + READ_LOCKER(_lock); + + uint32_t const n = _hash._nrAlloc; + uint32_t count = 0; + result = v8::Array::New(); + + for (uint32_t i = 0; i < n; ++i) { + auto element = static_cast(_hash._table[i]); + + if (element != nullptr) { + if (TRI_IsPrefixString(element->key, prefix.c_str())) { + result->Set(count++, v8::String::New(element->key)); + } + } + } + } + + return scope.Close(result); + } + + v8::Handle keyspaceGet () { + v8::HandleScope scope; + + v8::Handle result = v8::Object::New(); + { + READ_LOCKER(_lock); + + uint32_t const n = _hash._nrAlloc; + + for (uint32_t i = 0; i < n; ++i) { + auto element = static_cast(_hash._table[i]); + + if (element != nullptr) { + result->Set(v8::String::New(element->key), TRI_ObjectJson(element->json)); + } + } + } + + return scope.Close(result); + } + + v8::Handle keyspaceGet (std::string const& prefix) { + v8::HandleScope scope; + + v8::Handle result = v8::Object::New(); + { + READ_LOCKER(_lock); + + uint32_t const n = _hash._nrAlloc; + + for (uint32_t i = 0; i < n; ++i) { + auto element = static_cast(_hash._table[i]); + + if (element != nullptr) { + if (TRI_IsPrefixString(element->key, prefix.c_str())) { + result->Set(v8::String::New(element->key), TRI_ObjectJson(element->json)); + } + } + } + } + + return scope.Close(result); + } + + + + bool keyCount (std::string const& key, + uint32_t& result) { + READ_LOCKER(_lock); + + auto found = static_cast(TRI_LookupByKeyAssociativePointer(&_hash, key.c_str())); + + if (found != nullptr) { + TRI_json_t const* value = found->json; + + if (TRI_IsListJson(value)) { + result = static_cast(TRI_LengthVector(&value->_value._objects)); + return true; + } + if (TRI_IsArrayJson(value)) { + result = static_cast(TRI_LengthVector(&value->_value._objects) / 2); + return true; + } + } + + result = 0; + return false; + } + + v8::Handle keyGet (std::string const& key) { + v8::HandleScope scope; + + v8::Handle result; + { + READ_LOCKER(_lock); + + auto found = static_cast(TRI_LookupByKeyAssociativePointer(&_hash, key.c_str())); + + if (found == nullptr) { + result = v8::Undefined(); + } + else { + result = TRI_ObjectJson(found->json); + } + } + + return scope.Close(result); + } + + bool keySet (std::string const& key, + v8::Handle const& value, + bool replace) { + auto element = new KeySpaceElement(key.c_str(), key.size(), TRI_ObjectToJson(value)); + + WRITE_LOCKER(_lock); + + auto found = static_cast(TRI_InsertKeyAssociativePointer(&_hash, element->key, element, replace)); + + if (found == nullptr) { + return true; + } + + if (replace) { + delete found; + return true; + } + + delete element; + return false; + } + + int keyCas (std::string const& key, + v8::Handle const& value, + v8::Handle const& compare, + bool& match) { + auto element = new KeySpaceElement(key.c_str(), key.size(), TRI_ObjectToJson(value)); + + WRITE_LOCKER(_lock); + + auto found = static_cast(TRI_InsertKeyAssociativePointer(&_hash, element->key, element, false)); + + if (found == nullptr) { + return TRI_ERROR_NO_ERROR; + } + + TRI_json_t* other = TRI_ObjectToJson(compare); + + if (other == nullptr) { + delete element; + return TRI_ERROR_OUT_OF_MEMORY; + } + + int res = TRI_CompareValuesJson(found->json, other); + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, other); + + if (res != 0) { + delete element; + match = false; + return TRI_ERROR_NO_ERROR; + } + + TRI_InsertKeyAssociativePointer(&_hash, element->key, element, true); + delete found; + match = true; + return TRI_ERROR_NO_ERROR; + } + + bool keyRemove (std::string const& key) { + WRITE_LOCKER(_lock); + + auto found = static_cast(TRI_RemoveKeyAssociativePointer(&_hash, key.c_str())); + + if (found != nullptr) { + delete found; + return true; + } + + return false; + } + + bool keyExists (std::string const& key) { + READ_LOCKER(_lock); + + return (TRI_LookupByKeyAssociativePointer(&_hash, key.c_str()) != nullptr); + } + + int keyIncr (std::string const& key, + double value, + double& result) { + WRITE_LOCKER(_lock); + + auto found = static_cast(TRI_LookupByKeyAssociativePointer(&_hash, key.c_str())); + + if (found == nullptr) { + auto element = new KeySpaceElement(key.c_str(), key.size(), TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, value)); + + if (TRI_InsertKeyAssociativePointer(&_hash, element->key, static_cast(element), false) != TRI_ERROR_NO_ERROR) { + delete element; + return TRI_ERROR_OUT_OF_MEMORY; + } + result = value; + } + else { + TRI_json_t* current = found->json; + + if (! TRI_IsNumberJson(current)) { + // TODO: change error code + return TRI_ERROR_ILLEGAL_NUMBER; + } + + result = current->_value._number += value; + } + + return TRI_ERROR_NO_ERROR; + } + + int keyPush (std::string const& key, + v8::Handle const& value) { + WRITE_LOCKER(_lock); + + auto found = static_cast(TRI_LookupByKeyAssociativePointer(&_hash, key.c_str())); + + if (found == nullptr) { + TRI_json_t* list = TRI_CreateList2Json(TRI_UNKNOWN_MEM_ZONE, 1); + + if (list == nullptr) { + return TRI_ERROR_OUT_OF_MEMORY; + } + + if (TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, list, TRI_ObjectToJson(value)) != TRI_ERROR_NO_ERROR) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, list); + return TRI_ERROR_OUT_OF_MEMORY; + } + + auto element = new KeySpaceElement(key.c_str(), key.size(), list); + + if (TRI_InsertKeyAssociativePointer(&_hash, element->key, static_cast(element), false) != TRI_ERROR_NO_ERROR) { + delete element; + return TRI_ERROR_OUT_OF_MEMORY; + } + } + else { + TRI_json_t* current = found->json; + + if (! TRI_IsListJson(current)) { + // TODO: change error code + return TRI_ERROR_INTERNAL; + } + + if (TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, current, TRI_ObjectToJson(value)) != TRI_ERROR_NO_ERROR) { + return TRI_ERROR_OUT_OF_MEMORY; + } + } + + return TRI_ERROR_NO_ERROR; + } + + v8::Handle keyPop (std::string const& key) { + v8::HandleScope scope; + + WRITE_LOCKER(_lock); + + auto found = static_cast(TRI_LookupByKeyAssociativePointer(&_hash, key.c_str())); + + if (found == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + TRI_json_t* current = found->json; + + if (! TRI_IsListJson(current)) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + size_t const n = TRI_LengthVector(¤t->_value._objects); + + if (n == 0) { + return scope.Close(v8::Undefined()); + } + + TRI_json_t* item = static_cast(TRI_AtVector(¤t->_value._objects, n - 1)); + // hack: decrease the vector size + --current->_value._objects._length; + + v8::Handle result = TRI_ObjectJson(item); + TRI_DestroyJson(TRI_UNKNOWN_MEM_ZONE, item); + + return scope.Close(result); + } + + v8::Handle keyTransfer (std::string const& keyFrom, + std::string const& keyTo) { + v8::HandleScope scope; + + WRITE_LOCKER(_lock); + + auto source = static_cast(TRI_LookupByKeyAssociativePointer(&_hash, keyFrom.c_str())); + + if (source == nullptr) { + return scope.Close(v8::Undefined()); + } + + TRI_json_t* current = source->json; + + if (! TRI_IsListJson(current)) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + size_t const n = TRI_LengthVector(&source->json->_value._objects); + + if (n == 0) { + return scope.Close(v8::Undefined()); + } + + TRI_json_t* sourceItem = static_cast(TRI_AtVector(&source->json->_value._objects, n - 1)); + + auto dest = static_cast(TRI_LookupByKeyAssociativePointer(&_hash, keyTo.c_str())); + + if (dest == nullptr) { + TRI_json_t* list = TRI_CreateList2Json(TRI_UNKNOWN_MEM_ZONE, 1); + + if (list == nullptr) { + TRI_V8_EXCEPTION_MEMORY(scope); + } + + TRI_PushBack2ListJson(list, sourceItem); + + try { + auto element = new KeySpaceElement(keyTo.c_str(), keyTo.size(), list); + TRI_InsertKeyAssociativePointer(&_hash, element->key, element, false); + // hack: decrease the vector size + --current->_value._objects._length; + + return TRI_ObjectJson(sourceItem); + } + catch (...) { + TRI_V8_EXCEPTION_MEMORY(scope); + } + } + + if (! TRI_IsListJson(dest->json)) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + TRI_PushBack2ListJson(dest->json, sourceItem); + + // hack: decrease the vector size + --current->_value._objects._length; + + return TRI_ObjectJson(sourceItem); + } + + v8::Handle keyKeys (std::string const& key) { + v8::HandleScope scope; + + v8::Handle result; + { + READ_LOCKER(_lock); + + auto found = static_cast(TRI_LookupByKeyAssociativePointer(&_hash, key.c_str())); + + if (found == nullptr) { + result = v8::Undefined(); + } + else { + result = TRI_KeysJson(found->json); + } + } + + return scope.Close(result); + } + + v8::Handle keyValues (std::string const& key) { + v8::HandleScope scope; + + v8::Handle result; + { + READ_LOCKER(_lock); + + auto found = static_cast(TRI_LookupByKeyAssociativePointer(&_hash, key.c_str())); + + if (found == nullptr) { + result = v8::Undefined(); + } + else { + result = TRI_ValuesJson(found->json); + } + } + + return scope.Close(result); + } + + v8::Handle keyAt (std::string const& key, + int64_t index) { + v8::HandleScope scope; + + v8::Handle result; + { + READ_LOCKER(_lock); + + auto found = static_cast(TRI_LookupByKeyAssociativePointer(&_hash, key.c_str())); + + if (found == nullptr) { + result = v8::Undefined(); + } + else { + if (! TRI_IsListJson(found->json)) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + size_t const n = found->json->_value._objects._length; + if (index < 0) { + index = static_cast(n) + index; + } + + if (index >= static_cast(n)) { + result = v8::Undefined(); + } + else { + auto item = static_cast(TRI_AtVector(&found->json->_value._objects, static_cast(index))); + result = TRI_ObjectJson(item); + } + } + } + + return scope.Close(result); + } + + char const* keyType (std::string const& key) { + READ_LOCKER(_lock); + + void* found = TRI_LookupByKeyAssociativePointer(&_hash, key.c_str()); + + if (found != nullptr) { + TRI_json_t const* value = static_cast(found)->json; + + switch (value->_type) { + case TRI_JSON_NULL: + return "null"; + case TRI_JSON_BOOLEAN: + return "boolean"; + case TRI_JSON_NUMBER: + return "number"; + case TRI_JSON_STRING: + case TRI_JSON_STRING_REFERENCE: + return "string"; + case TRI_JSON_LIST: + return "list"; + case TRI_JSON_ARRAY: + return "object"; + case TRI_JSON_UNUSED: + break; + } + } + + return "undefined"; + } + + v8::Handle keyMerge (std::string const& key, + v8::Handle const& value, + bool nullMeansRemove) { + v8::HandleScope scope; + + if (! value->IsObject() || value->IsArray()) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + WRITE_LOCKER(_lock); + + auto found = static_cast(TRI_LookupByKeyAssociativePointer(&_hash, key.c_str())); + + if (found == nullptr) { + auto element = new KeySpaceElement(key.c_str(), key.size(), TRI_ObjectToJson(value)); + TRI_InsertKeyAssociativePointer(&_hash, element->key, element, false); + + return scope.Close(value); + } + + if (! TRI_IsArrayJson(found->json)) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + TRI_json_t* other = TRI_ObjectToJson(value); + + if (other == nullptr) { + TRI_V8_EXCEPTION(scope, TRI_ERROR_OUT_OF_MEMORY); + } + + TRI_json_t* merged = TRI_MergeJson(TRI_UNKNOWN_MEM_ZONE, found->json, other, nullMeansRemove); + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, other); + + if (merged == nullptr) { + TRI_V8_EXCEPTION(scope, TRI_ERROR_OUT_OF_MEMORY); + } + + found->setValue(merged); + + return scope.Close(TRI_ObjectJson(merged)); + } + + private: + + triagens::basics::ReadWriteLock _lock; + TRI_associative_pointer_t _hash; + +}; + +// ----------------------------------------------------------------------------- +// --SECTION-- struct UserStructures +// ----------------------------------------------------------------------------- + +struct UserStructures { + struct { + triagens::basics::ReadWriteLock lock; + std::unordered_map data; + } + hashes; +}; + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief get the vocbase pointer from the current V8 context +//////////////////////////////////////////////////////////////////////////////// + +static inline TRI_vocbase_t* GetContextVocBase () { + TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + + TRI_ASSERT_EXPENSIVE(v8g->_vocbase != nullptr); + return static_cast(v8g->_vocbase); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief finds a hash array by name +/// note that at least the read-lock must be held to use this function +//////////////////////////////////////////////////////////////////////////////// + +static KeySpace* GetKeySpace (TRI_vocbase_t* vocbase, + std::string const& name) { + auto h = &(static_cast(vocbase->_userStructures)->hashes); + auto it = h->data.find(name); + + if (it != h->data.end()) { + return (*it).second; + } + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief creates a keyspace +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyspaceCreate (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 1 || ! argv[0]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEYSPACE_CREATE(, , )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + int64_t size = 0; + + if (argv.Length() > 1) { + size = TRI_ObjectToInt64(argv[1]); + if (size < 0 || size > static_cast(UINT32_MAX)) { + TRI_V8_EXCEPTION_PARAMETER(scope, "invalid value for "); + } + } + + bool ignoreExisting = false; + if (argv.Length() > 2) { + ignoreExisting = TRI_ObjectToBoolean(argv[2]); + } + + std::unique_ptr ptr(new KeySpace(static_cast(size))); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + bool result; + { + WRITE_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash != nullptr) { + if (ignoreExisting) { + result = true; + } + else { + // TODO: change error code + TRI_V8_EXCEPTION_MESSAGE(scope, TRI_ERROR_INTERNAL, "hash already exists"); + } + } + + try { + h->data.emplace(std::make_pair(name, ptr.release())); + } + catch (...) { + TRI_V8_EXCEPTION_MEMORY(scope); + } + } + + return scope.Close(v8::True()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief drops a keyspace +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyspaceDrop (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() != 1 || ! argv[0]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEYSPACE_DROP()"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + { + WRITE_LOCKER(h->lock); + + auto it = h->data.find(name); + + if (it == h->data.end()) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + h->data.erase(it); + delete (*it).second; + } + + return scope.Close(v8::True()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the number of items in the keyspace +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyspaceCount (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 1 || ! argv[0]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEYSPACE_COUNT(, )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + uint32_t count; + { + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + if (argv.Length() > 1) { + std::string const&& prefix = TRI_ObjectToString(argv[1]); + count = hash->keyspaceCount(prefix); + } + else { + count = hash->keyspaceCount(); + } + } + + return scope.Close(v8::Number::New(static_cast(count))); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns whether a keyspace exists +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyspaceExists (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() != 1 || ! argv[0]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEYSPACE_EXISTS()"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + + READ_LOCKER(h->lock); + auto hash = GetKeySpace(vocbase, name); + + return scope.Close(v8::Boolean::New(hash != nullptr)); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns all keys of the keyspace +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyspaceKeys (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 1 || ! argv[0]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEYSPACE_KEYS(, )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + if (argv.Length() > 1) { + std::string const&& prefix = TRI_ObjectToString(argv[1]); + return scope.Close(hash->keyspaceKeys(prefix)); + } + + return scope.Close(hash->keyspaceKeys()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns all data of the keyspace +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyspaceGet (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 1 || ! argv[0]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEYSPACE_GET(, )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + if (argv.Length() > 1) { + std::string const&& prefix = TRI_ObjectToString(argv[1]); + return scope.Close(hash->keyspaceGet(prefix)); + } + + return scope.Close(hash->keyspaceGet()); +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief removes all keys from the keyspace +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyspaceRemove (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 1 || ! argv[0]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEYSPACE_REMOVE(, )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + if (argv.Length() > 1) { + std::string const&& prefix = TRI_ObjectToString(argv[1]); + return scope.Close(hash->keyspaceRemove(prefix)); + } + + return scope.Close(hash->keyspaceRemove()); +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the value for a key in the keyspace +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyGet (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 2 || ! argv[0]->IsString() || ! argv[1]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_GET(, )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + std::string const&& key = TRI_ObjectToString(argv[1]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + v8::Handle result; + { + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + result = hash->keyGet(key); + } + + return scope.Close(result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set the value for a key in the keyspace +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeySet (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 3 || ! argv[0]->IsString() || ! argv[1]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_SET(, , , )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + std::string const&& key = TRI_ObjectToString(argv[1]); + bool replace = true; + + if (argv.Length() > 3) { + replace = TRI_ObjectToBoolean(argv[3]); + } + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + bool result; + { + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + result = hash->keySet(key, argv[2], replace); + } + + return scope.Close(result ? v8::True() : v8::False()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief conditionally set the value for a key in the keyspace +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeySetCas (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 4 || ! argv[0]->IsString() || ! argv[1]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_SET_CAS(, , , )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + std::string const&& key = TRI_ObjectToString(argv[1]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + int res; + bool match; + { + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + res = hash->keyCas(key, argv[2], argv[3], match); + } + + if (res != TRI_ERROR_NO_ERROR) { + TRI_V8_EXCEPTION(scope, res); + } + + return scope.Close(v8::Boolean::New(match)); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief remove the value for a key in the keyspace +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyRemove (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 2 || ! argv[0]->IsString() || ! argv[1]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_REMOVE(, )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + std::string const&& key = TRI_ObjectToString(argv[1]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + bool result; + { + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + result = hash->keyRemove(key); + } + + return v8::Boolean::New(result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief checks if a key exists in the keyspace +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyExists (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 2 || ! argv[0]->IsString() || ! argv[1]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_EXISTS(, )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + std::string const&& key = TRI_ObjectToString(argv[1]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + bool result; + { + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + result = hash->keyExists(key); + } + + return v8::Boolean::New(result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief increase or decrease the value for a key in a keyspace +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyIncr (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 2 || ! argv[0]->IsString() || ! argv[1]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_INCR(, , )"); + } + + if (argv.Length() >= 3 && ! argv[2]->IsNumber()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_INCR(, , )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + std::string const&& key = TRI_ObjectToString(argv[1]); + + double incr = 1.0; + + if (argv.Length() >= 3) { + incr = TRI_ObjectToDouble(argv[2]); + } + + double result; + auto h = &(static_cast(vocbase->_userStructures)->hashes); + { + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + int res = hash->keyIncr(key, incr, result); + + if (res != TRI_ERROR_NO_ERROR) { + TRI_V8_EXCEPTION(scope, res); + } + } + + return scope.Close(v8::Number::New(result)); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief merges an object into the object with the specified key +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyUpdate (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 3 || ! argv[0]->IsString() || ! argv[1]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_UPDATE(, , , )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + std::string const&& key = TRI_ObjectToString(argv[1]); + + bool nullMeansRemove = false; + if (argv.Length() > 3) { + nullMeansRemove = TRI_ObjectToBoolean(argv[3]); + } + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + return scope.Close(hash->keyMerge(key, argv[2], nullMeansRemove)); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns all keys of the key +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyKeys (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 2 || ! argv[0]->IsString() || ! argv[1]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_KEYS(, )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + std::string const&& key = TRI_ObjectToString(argv[1]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + return scope.Close(hash->keyKeys(key)); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns all value of the hash array +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyValues (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 2 || ! argv[0]->IsString() || ! argv[1]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_VALUES(, )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + std::string const&& key = TRI_ObjectToString(argv[1]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + return scope.Close(hash->keyValues(key)); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief right-pushes an element into a list value +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyPush (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 3 || ! argv[0]->IsString() || ! argv[1]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_PUSH(, , )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + std::string const&& key = TRI_ObjectToString(argv[1]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + int res = hash->keyPush(key, argv[2]); + + if (res != TRI_ERROR_NO_ERROR) { + TRI_V8_EXCEPTION(scope, res); + } + + return scope.Close(v8::True()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief pops an element from a list value +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyPop (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 2 || ! argv[0]->IsString() || ! argv[1]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_POP(, )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + std::string const&& key = TRI_ObjectToString(argv[1]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + return scope.Close(hash->keyPop(key)); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief transfer an element from a list value into another +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyTransfer (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 3 || ! argv[0]->IsString() || ! argv[1]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_TRANSFER(, , )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + std::string const&& keyFrom = TRI_ObjectToString(argv[1]); + std::string const&& keyTo = TRI_ObjectToString(argv[2]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + return scope.Close(hash->keyTransfer(keyFrom, keyTo)); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief get an element at a specific list position +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyAt (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 3 || ! argv[0]->IsString() || ! argv[1]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_AT(, , )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + std::string const&& key = TRI_ObjectToString(argv[1]); + int64_t offset = TRI_ObjectToInt64(argv[2]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + return scope.Close(hash->keyAt(key, offset)); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the type of the value for a key +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyType (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 2 || ! argv[0]->IsString() || ! argv[1]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_TYPE(, )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + std::string const&& key = TRI_ObjectToString(argv[1]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + char const* result; + { + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + result = hash->keyType(key); + } + + return scope.Close(v8::String::New(result)); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the number of items in a compound value +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_KeyCount (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 2 || ! argv[0]->IsString() || ! argv[1]->IsString()) { + TRI_V8_EXCEPTION_USAGE(scope, "KEY_COUNT(, )"); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (vocbase == nullptr) { + TRI_V8_EXCEPTION_INTERNAL(scope, "cannot extract vocbase"); + } + + std::string const&& name = TRI_ObjectToString(argv[0]); + std::string const&& key = TRI_ObjectToString(argv[1]); + + auto h = &(static_cast(vocbase->_userStructures)->hashes); + uint32_t result; + bool valid; + { + READ_LOCKER(h->lock); + + auto hash = GetKeySpace(vocbase, name); + + if (hash == nullptr) { + // TODO: change error code + TRI_V8_EXCEPTION(scope, TRI_ERROR_INTERNAL); + } + + valid = hash->keyCount(key, result); + } + + if (valid) { + return scope.Close(v8::Number::New(result)); + } + + return scope.Close(v8::Undefined()); +} + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief creates the user structures for a database +//////////////////////////////////////////////////////////////////////////////// + +void TRI_CreateUserStructuresVocBase (TRI_vocbase_t* vocbase) { + TRI_ASSERT(vocbase != nullptr); + TRI_ASSERT(vocbase->_userStructures == nullptr); + + vocbase->_userStructures = new UserStructures; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief drops the user structures for a database +//////////////////////////////////////////////////////////////////////////////// + +void TRI_FreeUserStructuresVocBase (TRI_vocbase_t* vocbase) { + if (vocbase->_userStructures != nullptr) { + delete static_cast(vocbase->_userStructures); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief creates the user structures functions +//////////////////////////////////////////////////////////////////////////////// + +void TRI_InitV8UserStructures (v8::Handle context) { + v8::HandleScope scope; + + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + TRI_v8_global_t* v8g = static_cast(isolate->GetData()); + + TRI_ASSERT(v8g != nullptr); + + TRI_AddGlobalFunctionVocbase(context, "KEYSPACE_CREATE", JS_KeyspaceCreate); + TRI_AddGlobalFunctionVocbase(context, "KEYSPACE_DROP", JS_KeyspaceDrop); + TRI_AddGlobalFunctionVocbase(context, "KEYSPACE_COUNT", JS_KeyspaceCount); + TRI_AddGlobalFunctionVocbase(context, "KEYSPACE_EXISTS", JS_KeyspaceExists); + TRI_AddGlobalFunctionVocbase(context, "KEYSPACE_KEYS", JS_KeyspaceKeys); + TRI_AddGlobalFunctionVocbase(context, "KEYSPACE_REMOVE", JS_KeyspaceRemove); + TRI_AddGlobalFunctionVocbase(context, "KEYSPACE_GET", JS_KeyspaceGet); + + TRI_AddGlobalFunctionVocbase(context, "KEY_SET", JS_KeySet); + TRI_AddGlobalFunctionVocbase(context, "KEY_SET_CAS", JS_KeySetCas); + TRI_AddGlobalFunctionVocbase(context, "KEY_GET", JS_KeyGet); + TRI_AddGlobalFunctionVocbase(context, "KEY_REMOVE", JS_KeyRemove); + TRI_AddGlobalFunctionVocbase(context, "KEY_EXISTS", JS_KeyExists); + TRI_AddGlobalFunctionVocbase(context, "KEY_TYPE", JS_KeyType); + + // numeric functions + TRI_AddGlobalFunctionVocbase(context, "KEY_INCR", JS_KeyIncr); + + // list / array functions + TRI_AddGlobalFunctionVocbase(context, "KEY_UPDATE", JS_KeyUpdate); + TRI_AddGlobalFunctionVocbase(context, "KEY_KEYS", JS_KeyKeys); + TRI_AddGlobalFunctionVocbase(context, "KEY_VALUES", JS_KeyValues); + TRI_AddGlobalFunctionVocbase(context, "KEY_COUNT", JS_KeyCount); + TRI_AddGlobalFunctionVocbase(context, "KEY_PUSH", JS_KeyPush); + TRI_AddGlobalFunctionVocbase(context, "KEY_POP", JS_KeyPop); + TRI_AddGlobalFunctionVocbase(context, "KEY_TRANSFER", JS_KeyTransfer); + TRI_AddGlobalFunctionVocbase(context, "KEY_AT", JS_KeyAt); +} + +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + +// Local Variables: +// mode: outline-minor +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" +// End: diff --git a/arangod/V8Server/v8-user-structures.h b/arangod/V8Server/v8-user-structures.h new file mode 100644 index 0000000000..36e42c7ebb --- /dev/null +++ b/arangod/V8Server/v8-user-structures.h @@ -0,0 +1,70 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief V8 user data structures +/// +/// @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 Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany +/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +#ifndef ARANGODB_V8SERVER_V8__USER_STRUCTURES_H +#define ARANGODB_V8SERVER_V8__USER_STRUCTURES_H 1 + +#include "Basics/Common.h" + +#include "V8/v8-globals.h" + +struct TRI_vocbase_s; + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief creates the user structures for a database +//////////////////////////////////////////////////////////////////////////////// + +void TRI_CreateUserStructuresVocBase (struct TRI_vocbase_s*); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys the user structures for a database +//////////////////////////////////////////////////////////////////////////////// + +void TRI_FreeUserStructuresVocBase (struct TRI_vocbase_s*); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief creates the user structures functions +//////////////////////////////////////////////////////////////////////////////// + +void TRI_InitV8UserStructures (v8::Handle); + +#endif + +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + +// Local Variables: +// mode: outline-minor +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" +// End: diff --git a/arangod/VocBase/vocbase.cpp b/arangod/VocBase/vocbase.cpp index 489363b031..da8cf0c4e3 100644 --- a/arangod/VocBase/vocbase.cpp +++ b/arangod/VocBase/vocbase.cpp @@ -57,6 +57,7 @@ #include "VocBase/server.h" #include "VocBase/transaction.h" #include "VocBase/vocbase-defaults.h" +#include "V8Server/v8-user-structures.h" #include "Wal/LogfileManager.h" #include "Ahuacatl/ahuacatl-functions.h" @@ -1342,6 +1343,7 @@ TRI_vocbase_t* TRI_CreateInitialVocBase (TRI_server_t* server, vocbase->_hasCompactor = false; vocbase->_isOwnAppsDirectory = true; vocbase->_replicationApplier = nullptr; + vocbase->_userStructures = nullptr; vocbase->_oldTransactions = nullptr; @@ -1403,6 +1405,8 @@ TRI_vocbase_t* TRI_CreateInitialVocBase (TRI_server_t* server, TRI_InitCondition(&vocbase->_compactorCondition); TRI_InitCondition(&vocbase->_cleanupCondition); + TRI_CreateUserStructuresVocBase(vocbase); + return vocbase; } @@ -1411,6 +1415,10 @@ TRI_vocbase_t* TRI_CreateInitialVocBase (TRI_server_t* server, //////////////////////////////////////////////////////////////////////////////// void TRI_DestroyInitialVocBase (TRI_vocbase_t* vocbase) { + if (vocbase->_userStructures != nullptr) { + TRI_FreeUserStructuresVocBase(vocbase); + } + // free replication if (vocbase->_replicationApplier != nullptr) { TRI_FreeReplicationApplier(vocbase->_replicationApplier); diff --git a/arangod/VocBase/vocbase.h b/arangod/VocBase/vocbase.h index f4cac3a430..4ee0d90e9b 100644 --- a/arangod/VocBase/vocbase.h +++ b/arangod/VocBase/vocbase.h @@ -31,6 +31,7 @@ #define ARANGODB_VOC_BASE_VOCBASE_H 1 #include "Basics/Common.h" +#include "Basics/ReadWriteLock.h" #include "Basics/associative.h" #include "Basics/locks.h" @@ -308,6 +309,9 @@ typedef struct TRI_vocbase_s { TRI_read_write_lock_t _inventoryLock; // object lock needed when replication is assessing the state of the vocbase + // structures for user-defined volatile data + void* _userStructures; + TRI_associative_pointer_t _authInfo; TRI_associative_pointer_t _authCache; TRI_read_write_lock_t _authInfoLock; diff --git a/lib/V8/v8-conv.cpp b/lib/V8/v8-conv.cpp index b61545901c..be4816ede9 100644 --- a/lib/V8/v8-conv.cpp +++ b/lib/V8/v8-conv.cpp @@ -1499,6 +1499,34 @@ static v8::Handle ObjectJsonList (TRI_json_t const* json) { return object; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief extracts keys or values from a TRI_json_t* array +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle ExtractArray (TRI_json_t const* json, + size_t offset) { + v8::HandleScope scope; + + if (json == nullptr || json->_type != TRI_JSON_ARRAY) { + return scope.Close(v8::Undefined()); + } + + size_t const n = TRI_LengthVector(&json->_value._objects); + + v8::Handle result = v8::Array::New(static_cast(n / 2)); + uint32_t count = 0; + + for (size_t i = offset; i < n; i += 2) { + TRI_json_t const* value = static_cast(TRI_AtVector(&json->_value._objects, i)); + + if (value != nullptr) { + result->Set(count++, TRI_ObjectJson(value)); + } + } + + return scope.Close(result); +} + // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- @@ -1527,6 +1555,26 @@ v8::Handle TRI_ArrayAssociativePointer (TRI_associative_pointer_t con return scope.Close(result); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the keys of a TRI_json_t* array into a V8 list +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle TRI_KeysJson (TRI_json_t const* json) { + v8::HandleScope scope; + + return scope.Close(ExtractArray(json, 0)); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the values of a TRI_json_t* array into a V8 list +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle TRI_ValuesJson (TRI_json_t const* json) { + v8::HandleScope scope; + + return scope.Close(ExtractArray(json, 1)); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief converts a TRI_json_t into a V8 object //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/V8/v8-conv.h b/lib/V8/v8-conv.h index 8af5b1123b..c9534d2a81 100644 --- a/lib/V8/v8-conv.h +++ b/lib/V8/v8-conv.h @@ -51,6 +51,18 @@ v8::Handle TRI_ArrayAssociativePointer (TRI_associative_pointer_t const*); +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the keys of a TRI_json_t* array into a V8 list +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle TRI_KeysJson (TRI_json_t const*); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the values of a TRI_json_t* array into a V8 list +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle TRI_ValuesJson (TRI_json_t const*); + //////////////////////////////////////////////////////////////////////////////// /// @brief converts a TRI_json_t into a V8 object ////////////////////////////////////////////////////////////////////////////////