diff --git a/CHANGELOG b/CHANGELOG index 4b34fff879..ae009594ab 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ v2.8.0 (XXXX-XX-XX) ------------------- +* web interface: aql editor now supports bind parameters + * added startup option `--server.hide-product-header` to make the server not send the HTTP response header `"Server: ArangoDB"` in its HTTP responses. By default, the option is turned off so the header is still sent as usual. @@ -101,6 +103,9 @@ v2.8.0 (XXXX-XX-XX) v2.7.2 (XXXX-XX-XX) ------------------- +* fixed undefined behavior when accessing the top level of a document with the `[*]` + operator + * fixed potentially invalid pointer access in shaper when the currently accessed document got re-located by the WAL collector at the very same time diff --git a/arangod/Aql/AstNode.h b/arangod/Aql/AstNode.h index 55bf103f84..b9aabadc49 100644 --- a/arangod/Aql/AstNode.h +++ b/arangod/Aql/AstNode.h @@ -34,7 +34,6 @@ #include "Basics/AttributeNameParser.h" #include "Basics/Exceptions.h" #include "Basics/json.h" -#include "Basics/vector.h" #include "Basics/JsonHelper.h" #include diff --git a/arangod/Aql/Expression.cpp b/arangod/Aql/Expression.cpp index 9665183861..9e8764295b 100644 --- a/arangod/Aql/Expression.cpp +++ b/arangod/Aql/Expression.cpp @@ -529,7 +529,7 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node, case NODE_TYPE_EXPANSION: return executeSimpleExpressionExpansion(node, trx, argv, startPos, vars, regs); case NODE_TYPE_ITERATOR: - return executeSimpleExpressionIterator(node, trx, argv, startPos, vars, regs); + return executeSimpleExpressionIterator(node, collection, trx, argv, startPos, vars, regs); case NODE_TYPE_OPERATOR_BINARY_PLUS: case NODE_TYPE_OPERATOR_BINARY_MINUS: case NODE_TYPE_OPERATOR_BINARY_TIMES: @@ -1290,6 +1290,7 @@ AqlValue Expression::executeSimpleExpressionExpansion (AstNode const* node, //////////////////////////////////////////////////////////////////////////////// AqlValue Expression::executeSimpleExpressionIterator (AstNode const* node, + TRI_document_collection_t const** collection, triagens::arango::AqlTransaction* trx, AqlItemBlock const* argv, size_t startPos, @@ -1298,9 +1299,8 @@ AqlValue Expression::executeSimpleExpressionIterator (AstNode const* node, TRI_ASSERT(node != nullptr); TRI_ASSERT(node->numMembers() == 2); - // intentionally do not stringify node 0 - TRI_document_collection_t const* myCollection = nullptr; - return executeSimpleExpression(node->getMember(1), &myCollection, trx, argv, startPos, vars, regs, true); + *collection = nullptr; + return executeSimpleExpression(node->getMember(1), collection, trx, argv, startPos, vars, regs, true); } //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/Expression.h b/arangod/Aql/Expression.h index c76fe8cced..21f0de7c6f 100644 --- a/arangod/Aql/Expression.h +++ b/arangod/Aql/Expression.h @@ -485,6 +485,7 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// AqlValue executeSimpleExpressionIterator (AstNode const*, + TRI_document_collection_t const**, triagens::arango::AqlTransaction*, AqlItemBlock const*, size_t, diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index 7b6c5bd109..f121a66fd3 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -241,6 +241,7 @@ add_executable( VocBase/datafile.cpp VocBase/Ditch.cpp VocBase/document-collection.cpp + VocBase/DocumentAccessor.cpp VocBase/ExampleMatcher.cpp VocBase/edge-collection.cpp VocBase/headers.cpp diff --git a/arangod/Cluster/AgencyComm.cpp b/arangod/Cluster/AgencyComm.cpp index 7b8a8ec44e..c2c44c9feb 100644 --- a/arangod/Cluster/AgencyComm.cpp +++ b/arangod/Cluster/AgencyComm.cpp @@ -383,10 +383,10 @@ std::list AgencyComm::_globalEndpoints; //////////////////////////////////////////////////////////////////////////////// AgencyConnectionOptions AgencyComm::_globalConnectionOptions = { - 15.0, // connectTimeout - 9.0, // requestTimeout - 5.0, // lockTimeout - 3 // numRetries + 15.0, // connectTimeout + 120.0, // requestTimeout + 120.0, // lockTimeout + 10 // numRetries }; // ----------------------------------------------------------------------------- diff --git a/arangod/Cluster/ClusterInfo.cpp b/arangod/Cluster/ClusterInfo.cpp index 8e15041255..d03f0b29d0 100644 --- a/arangod/Cluster/ClusterInfo.cpp +++ b/arangod/Cluster/ClusterInfo.cpp @@ -34,7 +34,6 @@ #include "Basics/json.h" #include "Basics/logging.h" #include "Basics/tri-strings.h" -#include "Basics/vector.h" #include "Basics/json-utilities.h" #include "Basics/JsonHelper.h" #include "Basics/MutexLocker.h" @@ -403,8 +402,8 @@ bool ClusterInfo::doesDatabaseExist (DatabaseID const& databaseID, /// @brief get list of databases in the cluster //////////////////////////////////////////////////////////////////////////////// -vector ClusterInfo::listDatabases (bool reload) { - vector result; +std::vector ClusterInfo::listDatabases (bool reload) { + std::vector result; if (reload || ! _plannedDatabasesProt.isValid || @@ -712,18 +711,18 @@ void ClusterInfo::loadPlannedCollections (bool acquireLock) { (*it).second._json = nullptr; shared_ptr collectionData (new CollectionInfo(json)); - vector* shardKeys = new vector; + std::vector* shardKeys = new std::vector; *shardKeys = collectionData->shardKeys(); newShardKeys.insert( - make_pair(collection, shared_ptr > (shardKeys))); + make_pair(collection, shared_ptr > (shardKeys))); map shardIDs = collectionData->shardIds(); - vector* shards = new vector; + std::vector* shards = new std::vector; map::iterator it3; for (it3 = shardIDs.begin(); it3 != shardIDs.end(); ++it3) { shards->push_back(it3->first); } newShards.emplace( - std::make_pair(collection, shared_ptr >(shards))); + std::make_pair(collection, shared_ptr >(shards))); // insert the collection into the existing map, insert it under its // ID as well as under its name, so that a lookup can be done with @@ -2260,7 +2259,7 @@ std::vector ClusterInfo::getCurrentDBServers () { /// our id //////////////////////////////////////////////////////////////////////////////// -static const std::string prefixTargetServerEndoint = "Target/MapIDToEndpoint/"; +static const std::string prefixTargetServerEndpoint = "Target/MapIDToEndpoint/"; std::string ClusterInfo::getTargetServerEndpoint (ServerID const& serverID) { @@ -2271,12 +2270,12 @@ std::string ClusterInfo::getTargetServerEndpoint (ServerID const& serverID) { AgencyCommLocker locker("Target", "READ"); if (locker.successful()) { - result = _agency.getValues(prefixTargetServerEndoint + serverID, false); + result = _agency.getValues(prefixTargetServerEndpoint + serverID, false); } } if (result.successful()) { - result.parse(prefixTargetServerEndoint, false); + result.parse(prefixTargetServerEndpoint, false); // check if we can find ourselves in the list returned by the agency std::map::const_iterator it = result._values.find(serverID); @@ -2357,9 +2356,9 @@ int ClusterInfo::getResponsibleShard (CollectionID const& collectionID, } int tries = 0; - shared_ptr > shardKeysPtr; + shared_ptr > shardKeysPtr; char const** shardKeys = nullptr; - shared_ptr > shards; + shared_ptr > shards; bool found = false; while (true) { diff --git a/arangod/Makefile.files b/arangod/Makefile.files index b99e584244..da92fdfbcf 100644 --- a/arangod/Makefile.files +++ b/arangod/Makefile.files @@ -199,6 +199,7 @@ arangod_libarangod_a_SOURCES = \ arangod/VocBase/datafile.cpp \ arangod/VocBase/Ditch.cpp \ arangod/VocBase/document-collection.cpp \ + arangod/VocBase/DocumentAccessor.cpp \ arangod/VocBase/ExampleMatcher.cpp \ arangod/VocBase/edge-collection.cpp \ arangod/VocBase/headers.cpp \ diff --git a/arangod/VocBase/DocumentAccessor.cpp b/arangod/VocBase/DocumentAccessor.cpp new file mode 100644 index 0000000000..931f2fb0d2 --- /dev/null +++ b/arangod/VocBase/DocumentAccessor.cpp @@ -0,0 +1,301 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief read-accesses internals of a document +/// +/// @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 2006-2013, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +#include "DocumentAccessor.h" +#include "Basics/conversions.h" +#include "Basics/Exceptions.h" +#include "Utils/CollectionNameResolver.h" +#include "VocBase/VocShaper.h" + +// ----------------------------------------------------------------------------- +// --SECTION-- DocumentAccessor +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors and destructors +// ----------------------------------------------------------------------------- + +DocumentAccessor::DocumentAccessor (triagens::arango::CollectionNameResolver const* resolver, + TRI_document_collection_t* document, + TRI_doc_mptr_t const* mptr) + : _resolver(resolver), + _document(document), + _mptr(mptr), + _json(), + _current(nullptr) { +} + +DocumentAccessor::~DocumentAccessor () { +} + +// ----------------------------------------------------------------------------- +// --SECTION-- public methods +// ----------------------------------------------------------------------------- + +bool DocumentAccessor::hasKey (std::string const& attribute) const { + if (! isObject()) { + return false; + } + + return true; +} + +bool DocumentAccessor::isObject () const { + if (_current != nullptr) { + return TRI_IsObjectJson(_current); + } + + // ok, must be a document/edge + return true; +} + +bool DocumentAccessor::isArray () const { + if (_current != nullptr) { + return TRI_IsArrayJson(_current); + } + + // ok, must be a document/edge + return false; +} + +size_t DocumentAccessor::length () const { + if (! isArray()) { + return 0; + } + // ok, we have confirmed this is an array + TRI_ASSERT(_current != nullptr); + if (_current != nullptr) { + return TRI_LengthArrayJson(_current); + } + return 0; +} + +DocumentAccessor& DocumentAccessor::get (std::string const& name) { + return get(name.c_str(), name.size()); +} + +DocumentAccessor& DocumentAccessor::get (char const* name, size_t nameLength) { + if (_current == nullptr) { + // a document, we need the access its attributes using special methods + lookupDocumentAttribute(name, nameLength); + } + else { + // already a JSON + lookupJsonAttribute(name, nameLength); + } + + return *this; +} + +DocumentAccessor& DocumentAccessor::at (int64_t index) { + if (isArray()) { + size_t length = TRI_LengthArrayJson(_current); + + if (index < 0) { + // a negative position is allowed + index = static_cast(length) + index; + } + + if (index >= 0 && index < static_cast(length)) { + // only look up the value if it is within array bounds + TRI_json_t* found = TRI_LookupArrayJson(_current, static_cast(index)); + + if (found != nullptr) { + _current = found; + return *this; + } + } + // fall-through intentional + } + + setToNull(); + return *this; +} + +triagens::basics::Json DocumentAccessor::toJson () { + if (_current == nullptr) { + return triagens::basics::Json(triagens::basics::Json::Null); + } + + // should always be true + if (_current == _json.get()) { + // _current points at the JSON that we own + TRI_json_t* value = _json.release(); + setToNull(); + return triagens::basics::Json(TRI_UNKNOWN_MEM_ZONE, value); + } + + TRI_json_t* copy = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, _current); + + if (copy != nullptr) { + _json.release(); + return triagens::basics::Json(TRI_UNKNOWN_MEM_ZONE, copy); + } + // fall-through intentional + + return triagens::basics::Json(triagens::basics::Json::Null); +} + +// ----------------------------------------------------------------------------- +// --SECTION-- private methods +// ----------------------------------------------------------------------------- + +void DocumentAccessor::setToNull () { + // check if already null + if (! TRI_IsNullJson(_current)) { + _json.reset(TRI_CreateNullJson(TRI_UNKNOWN_MEM_ZONE)); + _current = _json.get(); + } +} + +void DocumentAccessor::lookupJsonAttribute (char const* name, size_t nameLength) { + TRI_ASSERT(_current != nullptr); + + TRI_json_t* value = TRI_LookupObjectJson(_current, name); + + if (value == nullptr) { + // attribute not found + setToNull(); + } + else { + // found + _current = value; + } +} + +void DocumentAccessor::lookupDocumentAttribute (char const* name, size_t nameLength) { + if (*name == '_' && name[1] != '\0') { + if (name[1] == 'k' && nameLength == 4 && memcmp(name, TRI_VOC_ATTRIBUTE_KEY, nameLength) == 0) { + // _key value is copied into JSON + char const* key = TRI_EXTRACT_MARKER_KEY(_mptr); + if (key == nullptr) { + setToNull(); + return; + } + _json.reset(TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, key, strlen(key))); + _current = _json.get(); + return; + } + + if (name[1] == 'i' && nameLength == 3 && memcmp(name, TRI_VOC_ATTRIBUTE_ID, nameLength) == 0) { + // _id + char buffer[512]; // big enough for max key length + max collection name length + size_t pos = _resolver->getCollectionName(&buffer[0], _document->_info._cid); + buffer[pos++] = '/'; + char const* key = TRI_EXTRACT_MARKER_KEY(_mptr); + if (key == nullptr) { + setToNull(); + return; + } + size_t len = strlen(key); + memcpy(&buffer[pos], key, len); + buffer[pos + len] = '\0'; + _json.reset(TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, &buffer[0], pos + len)); + _current = _json.get(); + return; + } + + if (name[1] == 'r' && nameLength == 4 && memcmp(name, TRI_VOC_ATTRIBUTE_REV, nameLength) == 0) { + // _rev + char buffer[21]; + TRI_voc_rid_t rid = TRI_EXTRACT_MARKER_RID(_mptr); + size_t len = TRI_StringUInt64InPlace(rid, &buffer[0]); + _json.reset(TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, &buffer[0], len)); + _current = _json.get(); + return; + } + + if (name[1] == 'f' && nameLength == 5 && memcmp(name, TRI_VOC_ATTRIBUTE_FROM, nameLength) == 0) { + // _from + char buffer[512]; // big enough for max key length + max collection name length + size_t pos = _resolver->getCollectionNameCluster(&buffer[0], TRI_EXTRACT_MARKER_FROM_CID(_mptr)); + buffer[pos++] = '/'; + char const* key = TRI_EXTRACT_MARKER_FROM_KEY(_mptr); + if (key == nullptr) { + setToNull(); + return; + } + size_t len = strlen(key); + memcpy(&buffer[pos], key, len); + buffer[pos + len] = '\0'; + _json.reset(TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, &buffer[0], pos + len)); + _current = _json.get(); + return; + } + + if (name[1] == 't' && nameLength == 3 && memcmp(name, TRI_VOC_ATTRIBUTE_TO, nameLength) == 0) { + // to + char buffer[512]; // big enough for max key length + max collection name length + size_t pos = _resolver->getCollectionNameCluster(&buffer[0], TRI_EXTRACT_MARKER_TO_CID(_mptr)); + buffer[pos++] = '/'; + char const* key = TRI_EXTRACT_MARKER_TO_KEY(_mptr); + if (key == nullptr) { + setToNull(); + return; + } + size_t len = strlen(key); + memcpy(&buffer[pos], key, len); + buffer[pos + len] = '\0'; + _json.reset(TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, &buffer[0], pos + len)); + _current = _json.get(); + return; + } + + // fall-through intentional + } + + auto shaper = _document->getShaper(); + + TRI_shape_pid_t pid = shaper->lookupAttributePathByName(name); + + if (pid == 0) { + // attribute does not exist + setToNull(); + return; + } + + // attribute exists + TRI_shaped_json_t document; + TRI_EXTRACT_SHAPED_JSON_MARKER(document, _mptr->getDataPtr()); + + TRI_shaped_json_t json; + TRI_shape_t const* shape; + + bool ok = shaper->extractShapedJson(&document, 0, pid, &json, &shape); + + if (ok && shape != nullptr) { + _json.reset(TRI_JsonShapedJson(shaper, &json)); + _current = _json.get(); + return; + } + + // not found + setToNull(); +} + diff --git a/arangod/VocBase/DocumentAccessor.h b/arangod/VocBase/DocumentAccessor.h new file mode 100644 index 0000000000..d7baad7043 --- /dev/null +++ b/arangod/VocBase/DocumentAccessor.h @@ -0,0 +1,130 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief read-accesses internals of a document +/// +/// @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 2006-2013, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +#ifndef ARANGODB_VOC_BASE_DOCUMENT_ACCESSOR_H +#define ARANGODB_VOC_BASE_DOCUMENT_ACCESSOR_H 1 + +#include "Basics/Common.h" +#include "Basics/JsonHelper.h" +#include "VocBase/document-collection.h" +#include "VocBase/shape-accessor.h" +#include "VocBase/shaped-json.h" +#include "VocBase/Shaper.h" + +struct TRI_doc_mptr_t; + +namespace triagens { + namespace arango { + class CollectionNameResolver; + } +} + +// ----------------------------------------------------------------------------- +// --SECTION-- DocumentAccessor +// ----------------------------------------------------------------------------- + +class DocumentAccessor { + + public: + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors and destructors +// ----------------------------------------------------------------------------- + + DocumentAccessor (DocumentAccessor const&); + DocumentAccessor& operator= (DocumentAccessor const&); + + DocumentAccessor (triagens::arango::CollectionNameResolver const* resolver, + TRI_document_collection_t* document, + TRI_doc_mptr_t const* mptr); + + ~DocumentAccessor (); + +// ----------------------------------------------------------------------------- +// --SECTION-- public methods +// ----------------------------------------------------------------------------- + + public: + + bool hasKey (std::string const& attribute) const; + + bool isObject () const; + + bool isArray () const; + + size_t length () const; + + DocumentAccessor& get (char const* name, size_t nameLength); + + DocumentAccessor& get (std::string const& name); + + DocumentAccessor& at (int64_t index); + + triagens::basics::Json toJson (); + +// ----------------------------------------------------------------------------- +// --SECTION-- private methods +// ----------------------------------------------------------------------------- + + private: + + void setToNull (); + + void lookupJsonAttribute (char const* name, size_t nameLength); + + void lookupDocumentAttribute (char const* name, size_t nameLength); + +// ----------------------------------------------------------------------------- +// --SECTION-- private variables +// ----------------------------------------------------------------------------- + + private: + + triagens::arango::CollectionNameResolver const* _resolver; + + TRI_document_collection_t* _document; + + TRI_doc_mptr_t const* _mptr; + + std::unique_ptr _json; // the JSON that we own + + TRI_json_t* _current; // the JSON that we point to + +}; + +#endif + +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + +// Local Variables: +// mode: outline-minor +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" +// End: diff --git a/arangod/VocBase/document-collection.h b/arangod/VocBase/document-collection.h index a58b305357..4a537d0526 100644 --- a/arangod/VocBase/document-collection.h +++ b/arangod/VocBase/document-collection.h @@ -700,6 +700,15 @@ static inline TRI_voc_rid_t TRI_EXTRACT_MARKER_RID (TRI_df_marker_t const* marke return 0; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief extracts the revision id from a master pointer +//////////////////////////////////////////////////////////////////////////////// + +static inline TRI_voc_rid_t TRI_EXTRACT_MARKER_RID (TRI_doc_mptr_t const* mptr) { + TRI_df_marker_t const* marker = static_cast(mptr->getDataPtr()); + return TRI_EXTRACT_MARKER_RID(marker); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief extracts the pointer to the key from a marker //////////////////////////////////////////////////////////////////////////////// diff --git a/js/apps/system/_admin/aardvark/APP/frontend/aqltemplates.json b/js/apps/system/_admin/aardvark/APP/frontend/aqltemplates.json index 65547ad969..bada0eb771 100644 --- a/js/apps/system/_admin/aardvark/APP/frontend/aqltemplates.json +++ b/js/apps/system/_admin/aardvark/APP/frontend/aqltemplates.json @@ -1,31 +1,36 @@ [ { "name" : "FOR .. FILTER .. RETURN template", - "value": "/* Please insert some values for the @@collection and @value placeholders */\nFOR i IN @@collection\n FILTER i.`attribute` == @value\n RETURN i", + "value": "/* Please insert some values for the @@collection and @value bind parameters */\nFOR doc IN @@collection\n FILTER doc.`attribute` == @value\n RETURN doc", "parameter": {"@collection": "my_collection", "value": "my_value"} }, { "name" : "Limit", - "value": "/* Please insert some values for the @@collection, @offset and @count placeholders */\nFOR i IN @@collection\n LIMIT @offset, @count\n RETURN i", - "parameter": {"@collection": "my_collection", "offset": "my_offset", "count": "my_count"} + "value": "/* Please insert some values for the @@collection, @offset and @count bind parameters */\nFOR doc IN @@collection\n LIMIT @offset, @count\n RETURN doc", + "parameter": {"@collection": "my_collection", "offset": 0, "count": 10} + }, + { + "name" : "Inner join", + "value": "/* Please insert some values for the @@collection1 and @@collection2 bind parameters */\nFOR doc1 IN @@collection1\n FOR doc2 IN @@collection2\n FILTER doc1.`attribute` == doc2.`attribute`\n RETURN { doc1, doc2 }", + "parameter": {"@collection1": "collection1", "@collection2": "collection2"} }, { "name" : "Random document", - "value": "/* Please insert some value for the @@collection placeholder */\nFOR i IN @@collection\n SORT RAND()\n LIMIT 1\n RETURN i", + "value": "/* Please insert some value for the @@collection bind parameter */\nFOR doc IN @@collection\n SORT RAND()\n LIMIT 1\n RETURN doc", "parameter": {"@collection": "my_collection"} }, { "name" : "Subquery example", - "value": "/* Please insert some values for the @@collection, @value and @count placeholders */\nFOR i IN @@collection\n LET values = (\n FOR s IN i.`attribute`\n FILTER s.`attribute` == @value\n RETURN s\n )\n FILTER LENGTH(values) > @count\n RETURN {\n i : i,\n values : values\n }", - "parameter": {"@collection": "my_collection", "value": "my_value", "count": "my_count"} + "value": "/* Please insert some values for the @@collection, @value and @count bind parameters */\nFOR doc IN @@collection\n LET values = (\n FOR s IN doc.`attribute`\n FILTER s.`attribute` == @value\n RETURN s\n )\n FILTER LENGTH(values) > @count\n RETURN {\n doc,\n values\n }", + "parameter": {"@collection": "my_collection", "value": "my_value", "count": 10} }, { "name" : "Sequences", - "value": "/* Returns the sequence of integers between 2010 and 2013 (including) */\nFOR year IN 2010..2013\n RETURN year" + "value": "/* Returns the sequence of integers between 2010 and 2015 (including) */\nFOR year IN 2010..2015\n RETURN year" }, { "name" : "Bind parameters", - "value": "/* There are two types of bind parameters:\n - bind parameter names starting with a single @ character, e.g. @count, @value, @test:\n These can be used to ship any JSON-encodable data to the server.\n - bind parameter names starting with two @ characters always refer to collections, e.g. @@collections, @@c:\n These can be used as placeholders for collection names.\n */\nFOR i IN @@collection\n FILTER i.value == @value\n RETURN i", + "value": "/* There are two types of bind parameters:\n - bind parameter names starting with a single @ character, e.g. @count, @value, @test:\n These can be used to ship any JSON-encodable data to the server.\n - bind parameter names starting with two @ characters always refer to collections, e.g. @@collections, @@c:\n These can be used as placeholders for collection names.\n */\nFOR doc IN @@collection\n FILTER doc.value == @value\n RETURN doc", "parameter": {"@collection": "my_collection", "value": "my_value"} } ] diff --git a/js/apps/system/_admin/aardvark/APP/frontend/js/modules/common/org/arangodb/aql/explainer.js b/js/apps/system/_admin/aardvark/APP/frontend/js/modules/common/org/arangodb/aql/explainer.js index 00013b4386..b3dc38b799 100644 --- a/js/apps/system/_admin/aardvark/APP/frontend/js/modules/common/org/arangodb/aql/explainer.js +++ b/js/apps/system/_admin/aardvark/APP/frontend/js/modules/common/org/arangodb/aql/explainer.js @@ -678,7 +678,7 @@ function processQuery (query, explain) { } /* the exposed function */ -function explain (data, options, shouldPrint) { +function explain (data, options, shouldPrint, bindVars) { 'use strict'; if (typeof data === "string") { data = { query: data }; @@ -687,10 +687,20 @@ function explain (data, options, shouldPrint) { throw "ArangoStatement needs initial data"; } + if (options === undefined) { + options = data.options; + } options = options || { }; setColors(options.colors === undefined ? true : options.colors); var stmt = db._createStatement(data); + + if (typeof bindVars === 'object') { + Object.keys(bindVars).forEach(function(key) { + stmt.bind(key, bindVars[key]); + }); + } + var result = stmt.explain(options); stringBuilder.clearOutput(); diff --git a/js/apps/system/_admin/aardvark/APP/frontend/js/templates/dashboardView.ejs b/js/apps/system/_admin/aardvark/APP/frontend/js/templates/dashboardView.ejs index 54d9064937..234d0c105a 100644 --- a/js/apps/system/_admin/aardvark/APP/frontend/js/templates/dashboardView.ejs +++ b/js/apps/system/_admin/aardvark/APP/frontend/js/templates/dashboardView.ejs @@ -135,4 +135,86 @@ <% mediumChart("pageFaultsChart", "Major Page Faults") %> <% mediumChart("systemUserTimeChart", "Used CPU Time per Second") %> + + + +
+
+
+
+
+
Numbers
+
+ + + + + + + + + + + + + + + +
Events
Requests
Failed
+
+
+
+
+
+
Ticks
+
+ + + + + + + + + + + + + + + +
Applied
Processed
Available
+
+
+
+
+
+
Progress
+
+ + + + + + + + + + + + + + + +
Message
Time
Failed
+
+
+
+
+
Replication
+
+
+ diff --git a/js/apps/system/_admin/aardvark/APP/frontend/js/views/dashboardView.js b/js/apps/system/_admin/aardvark/APP/frontend/js/views/dashboardView.js index e7a04ad0b0..fd9c4b5e3e 100644 --- a/js/apps/system/_admin/aardvark/APP/frontend/js/views/dashboardView.js +++ b/js/apps/system/_admin/aardvark/APP/frontend/js/views/dashboardView.js @@ -415,6 +415,45 @@ cuts[counter] : cuts[counter - 1] + " - " + cuts[counter]; }, + renderReplicationStatistics: function(object) { + $('#repl-numbers table tr:nth-child(1) > td:nth-child(2)').html(object.state.totalEvents); + $('#repl-numbers table tr:nth-child(2) > td:nth-child(2)').html(object.state.totalRequests); + $('#repl-numbers table tr:nth-child(3) > td:nth-child(2)').html(object.state.totalFailedConnects); + + $('#repl-ticks table tr:nth-child(1) > td:nth-child(2)').html(object.state.lastAppliedContinuousTick); + $('#repl-ticks table tr:nth-child(2) > td:nth-child(2)').html(object.state.lastProcessedContinuousTick); + $('#repl-ticks table tr:nth-child(3) > td:nth-child(2)').html(object.state.lastAvailableContinuousTick); + + $('#repl-progress table tr:nth-child(1) > td:nth-child(2)').html(object.state.progress.message); + $('#repl-progress table tr:nth-child(2) > td:nth-child(2)').html(object.state.progress.time); + $('#repl-progress table tr:nth-child(3) > td:nth-child(2)').html(object.state.progress.failedConnects); + }, + + getReplicationStatistics: function() { + var self = this; + + $.ajax( + '/_api/replication/applier-state', + {async: true} + ).done( + function (d) { + console.log(d); + if (d.hasOwnProperty('state')) { + var running; + if (d.state.running) { + running = "Enabled"; + } + else { + running = "Disabled"; + } + running = '' + running + ''; + $('#replication-chart .dashboard-sub-bar').html("Replication " + running); + + self.renderReplicationStatistics(d); + } + }); + }, + getStatistics: function (callback) { var self = this; var url = "/_db/_system/_admin/aardvark/statistics/short"; @@ -453,6 +492,8 @@ } self.updateCharts(); }); + + this.getReplicationStatistics(); }, getHistoryStatistics: function (figure) { diff --git a/js/apps/system/_admin/aardvark/APP/frontend/scss/_newDashboard.scss b/js/apps/system/_admin/aardvark/APP/frontend/scss/_newDashboard.scss index 0afba3dc71..062406019b 100644 --- a/js/apps/system/_admin/aardvark/APP/frontend/scss/_newDashboard.scss +++ b/js/apps/system/_admin/aardvark/APP/frontend/scss/_newDashboard.scss @@ -47,6 +47,74 @@ } } +.dashboard-full-width-chart { + @extend %dashboard-chart-basic; + border: 1px solid rgba(104, 103, 102, .10); + border-radius: 3px; + margin-right: 12px; + + .dashboard-full-width-chart-inner { + @extend %dashboard-chart-box; + @extend %pull-left; + background-color: $c-white; + border-left: $dashboard-borderwidth solid $c-white; + border-right: $dashboard-borderwidth solid $c-white; + border-top: $dashboard-borderwidth solid $c-white; + padding-bottom: $dashboard-padding; + padding-top: $dashboard-padding + 2px; + + .dashboard-subtitle-bar { + + &.top { + border-bottom: 1px solid $c-e1grey; + height: 48px; + line-height: 48px; + text-align: right; + } + + } + + .dashboard-interior-chart { + @extend %pull-left; + border-left: 1px solid $c-e1grey; + + .inner { + margin-left: 10px; + margin-right: 10px; + + + table { + margin-top: 10px; + margin-bottom: 10px; + width: 100%; + + td { + white-space: nowrap; + } + + td:first-child { + width: 100px; + } + } + } + + } + + .dashboard-interior-chart:first-child { + border-left: 0; + } + + } + + .state { + background-color: $c-white; + border-radius: 5px; + color: $c-black; + margin-left: 5px; + padding-left: 3px; + padding-right: 5px; + } +} .dashboard-large-chart { @extend %dashboard-chart-basic; diff --git a/js/apps/system/_admin/aardvark/APP/frontend/scss/_resizing.scss b/js/apps/system/_admin/aardvark/APP/frontend/scss/_resizing.scss index 38b32141d6..4d358b7df4 100644 --- a/js/apps/system/_admin/aardvark/APP/frontend/scss/_resizing.scss +++ b/js/apps/system/_admin/aardvark/APP/frontend/scss/_resizing.scss @@ -147,6 +147,26 @@ width: $content-size; } + .dashboard-full-width-chart { + $int-width: $content-size - 3px; + width: $int-width; + + .dashboard-full-width-chart-inner { + width: $int-width - 10px; + + .dashboard-interior-chart { + float: left; + $int-height: (($dashboard-height - 29px) / 2) - $tendency-height-corrector; + height: $int-height; + width: (($int-width - 11px) / 3) - 2px; + } + } + + .dashboard-sub-bar { + width: $int-width - 11px; + } + } + .dashboard-large-chart { $int-width: ($content-size * 2 / 3) - 4px - 2px; width: $int-width; diff --git a/js/apps/system/_admin/aardvark/APP/frontend/src/mode-aql.js b/js/apps/system/_admin/aardvark/APP/frontend/src/mode-aql.js index be9e3ca8f5..516ec4719a 100644 --- a/js/apps/system/_admin/aardvark/APP/frontend/src/mode-aql.js +++ b/js/apps/system/_admin/aardvark/APP/frontend/src/mode-aql.js @@ -88,14 +88,14 @@ var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; var AqlHighlightRules = function() { var keywords = ( - "for|return|filter|sort|limit|let|collect|asc|desc|if|in|into|insert|update|remove|replace|options|with|and|or|not|distinct" + "for|return|filter|sort|limit|let|collect|asc|desc|in|into|insert|update|remove|replace|upsert|options|with|and|or|not|distinct" ); var builtinFunctions = ( "(to_bool|to_number|to_string|to_list|is_null|is_bool|is_number|is_string|is_list|is_document|" + "concat|concat_separator|char_length|lower|upper|substring|left|right|trim|reverse|contains|" + "like|floor|ceil|round|abs|rand|sqrt|length|min|max|average|sum|median|variance_population|" + - "variance_sample|first|last|unique|matches|merge|merge_recursive|has|attributes|values|unset|keep|" + + "variance_sample|first|last|unique|matches|merge|merge_recursive|has|attributes|values|unset|unset_recursive|keep|" + "near|within|within_rectangle|is_in_polygon|fulltext|paths|traversal|traversal_tree|edges|stddev_sample|stddev_population|" + "slice|nth|position|translate|zip|call|apply|push|append|pop|shift|unshift|remove_value|remove_values|" + "remove_nth|graph_paths|shortest_path|graph_shortest_path|graph_distance_to|graph_traversal|graph_traversal_tree|graph_edges|" + @@ -107,7 +107,7 @@ var AqlHighlightRules = function() { "date_add|date_subtract|date_diff|date_compare|date_format|fail|passthru|sleep|not_null|" + "first_list|first_document|parse_identifier|current_user|current_database|" + "collections|document|union|union_distinct|intersection|flatten|" + - "ltrim|rtrim|find_first|find_last|split|substitute|assemble|md5|sha1|random_token|AQL_LAST_ENTRY)" + "ltrim|rtrim|find_first|find_last|split|substitute|md5|sha1|random_token|AQL_LAST_ENTRY)" ); var keywordMapper = this.createKeywordMapper({ @@ -142,7 +142,7 @@ var AqlHighlightRules = function() { regex : "[a-zA-Z_][a-zA-Z0-9_]*\\b" }, { token : "keyword.operator", - regex : "\\+|\\-|\\/|\\/\\/|%|@>|<@|&&|\\||!|<|>|<=|=>|==|!=|=" + regex : "\\+|\\-|\\/|\\/\\/|%|@>|<@|&&|\\|\\||!|<|>|<=|=>|==|!=|=|\\[\\*\\]" }, { token : "paren.lparen", regex : "[\\(\\{]" diff --git a/js/common/modules/org/arangodb/aql/explainer.js b/js/common/modules/org/arangodb/aql/explainer.js index a11e5df0f0..15248e6210 100644 --- a/js/common/modules/org/arangodb/aql/explainer.js +++ b/js/common/modules/org/arangodb/aql/explainer.js @@ -686,6 +686,9 @@ function explain (data, options, shouldPrint, bindVars) { throw "ArangoStatement needs initial data"; } + if (options === undefined) { + options = data.options; + } options = options || { }; setColors(options.colors === undefined ? true : options.colors); diff --git a/js/server/modules/org/arangodb/cluster.js b/js/server/modules/org/arangodb/cluster.js index bc80a12021..ee38fa5637 100644 --- a/js/server/modules/org/arangodb/cluster.js +++ b/js/server/modules/org/arangodb/cluster.js @@ -168,12 +168,12 @@ function getIndexMap (shard) { function writeLocked (lockInfo, cb, args) { var timeout = lockInfo.timeout; if (timeout === undefined) { - timeout = 5; + timeout = 60; } var ttl = lockInfo.ttl; if (ttl === undefined) { - ttl = 10; + ttl = 120; } if (require("internal").coverage || require("internal").valgrind) { ttl *= 10; diff --git a/js/server/tests/replication-ongoing.js b/js/server/tests/replication-ongoing.js index 0f40948482..7315c8c0f8 100644 --- a/js/server/tests/replication-ongoing.js +++ b/js/server/tests/replication-ongoing.js @@ -165,9 +165,9 @@ function ReplicationSuite () { break; } - if (compareTicks(slaveState.state.lastAppliedContinuousTick, syncResult.lastLogTick) > 0 || - compareTicks(slaveState.state.lastProcessedContinuousTick, syncResult.lastLogTick) > 0 || - compareTicks(slaveState.state.lastAvailableContinuousTick, syncResult.lastLogTick) > 0) { + if (compareTicks(slaveState.state.lastAppliedContinuousTick, syncResult.lastLogTick) >= 0 || + compareTicks(slaveState.state.lastProcessedContinuousTick, syncResult.lastLogTick) >= 0) { // || +// compareTicks(slaveState.state.lastAvailableContinuousTick, syncResult.lastLogTick) > 0) { break; } @@ -226,11 +226,11 @@ function ReplicationSuite () { function (state) { // flush the wal logs on the master so the start tick is not available // anymore when we start replicating - for (var i = 0; i < 25; ++i) { + for (var i = 0; i < 30; ++i) { db._collection(cn).save({ value: i }); internal.wal.flush(); //true, true); } - internal.wait(5, false); + internal.wait(6, false); }, function (state) { @@ -239,7 +239,7 @@ function ReplicationSuite () { function (state) { // data loss on slave! - assertTrue(db._collection(cn).count() < 20); + assertTrue(db._collection(cn).count() < 25); }, { requireFromPresent: false @@ -262,16 +262,16 @@ function ReplicationSuite () { function (state) { // flush the wal logs on the master so the start tick is not available // anymore when we start replicating - for (var i = 0; i < 25; ++i) { + for (var i = 0; i < 30; ++i) { db._collection(cn).save({ value: i }); internal.wal.flush(); //true, true); } - internal.wait(5, false); + internal.wait(6, false); }, function (state) { // wait for slave applier to have started and detect the mess - internal.wait(2, false); + internal.wait(3, false); // slave should have stopped assertFalse(replication.applier.state().state.running); @@ -282,7 +282,7 @@ function ReplicationSuite () { function (state) { // data loss on slave! - assertTrue(db._collection(cn).count() < 20); + assertTrue(db._collection(cn).count() < 25); }, { requireFromPresent: true diff --git a/js/server/tests/replication-static.js b/js/server/tests/replication-static.js index 6a6c63678d..812c60389b 100644 --- a/js/server/tests/replication-static.js +++ b/js/server/tests/replication-static.js @@ -159,9 +159,9 @@ function ReplicationSuite () { break; } - if (compareTicks(slaveState.state.lastAppliedContinuousTick, syncResult.lastLogTick) > 0 || - compareTicks(slaveState.state.lastProcessedContinuousTick, syncResult.lastLogTick) > 0 || - compareTicks(slaveState.state.lastAvailableContinuousTick, syncResult.lastLogTick) > 0) { + if (compareTicks(slaveState.state.lastAppliedContinuousTick, syncResult.lastLogTick) >= 0 || + compareTicks(slaveState.state.lastProcessedContinuousTick, syncResult.lastLogTick) >= 0) { //|| +// compareTicks(slaveState.state.lastAvailableContinuousTick, syncResult.lastLogTick) > 0) { break; }