diff --git a/3rdParty/velocypack/include/velocypack/Options.h b/3rdParty/velocypack/include/velocypack/Options.h index bacdd81d11..fafd10f331 100644 --- a/3rdParty/velocypack/include/velocypack/Options.h +++ b/3rdParty/velocypack/include/velocypack/Options.h @@ -84,6 +84,9 @@ struct Options { // keep top-level object/array open when building objects with the Parser bool keepTopLevelOpen = false; + // clear builder before starting to parse in Parser + bool clearBuilderBeforeParse = true; + // validate UTF-8 strings when JSON-parsing with Parser bool validateUtf8Strings = false; diff --git a/3rdParty/velocypack/include/velocypack/Parser.h b/3rdParty/velocypack/include/velocypack/Parser.h index 6c78741df6..0fbe90d8a4 100644 --- a/3rdParty/velocypack/include/velocypack/Parser.h +++ b/3rdParty/velocypack/include/velocypack/Parser.h @@ -146,7 +146,9 @@ class Parser { _start = start; _size = size; _pos = 0; - _b->clear(); + if (options->clearBuilderBeforeParse) { + _b->clear(); + } _b->options = options; return parseInternal(multi); } diff --git a/Documentation/Books/Users/NewFeatures/NewFeatures28.mdpp b/Documentation/Books/Users/NewFeatures/NewFeatures28.mdpp index 9acf3da9e3..810a931d8d 100644 --- a/Documentation/Books/Users/NewFeatures/NewFeatures28.mdpp +++ b/Documentation/Books/Users/NewFeatures/NewFeatures28.mdpp @@ -46,12 +46,12 @@ FOR foaf, e, path IN 2 ANY @startUser GRAPH "relations" RETURN DISTINCT foaf ``` -Also optimizer rules have been implemented to gain performance of the traversal statement. -This rules move filter statements into the traversal statement s.t. paths which can never +Optimizer rules have been implemented to gain performance of the traversal statement. +These rules move filter statements into the traversal statement s.t. paths which can never pass the filter are not emitted to the variables. As an example take the query above and assume there are edges that do not have `type == "friend"`. If in the first edge step there is such a non-friend edge the second steps will never -be computed as they cannot fulfill the filter condition. +be computed for these edges as they cannot fulfill the filter condition. !SUBSECTION Array Indexes @@ -184,10 +184,10 @@ A deadlock is a situation in which two or more concurrent operations (user trans or AQL queries) try to access the same resources (collections, documents) and need to wait for the others to finish, but none of them can make any progress. -In case of such deadlock, there would be no progress for any of the involved +In case of such a deadlock, there would be no progress for any of the involved transactions, and none of the involved transactions could ever complete. This is completely undesirable, so the new automatic deadlock detection mechanism in ArangoDB -will automatically kick in and abort one of the transactions involved in such deadlock. +will automatically kick in and abort one of the transactions involved in such a deadlock. Aborting means that all changes done by the transaction will be rolled back and error 29 (`deadlock detected`) will be thrown. @@ -222,7 +222,7 @@ have been backported to ArangoDB 2.7 as well): slave will (intentionally) idle and not poll for master log changes in case the master had sent the full logs already. The `idleMaxWaitTime` value will only be used when `adapativePolling` is set - to `true`. When `adaptivePolling` is disable, only `idleMinWaitTime` will be + to `true`. When `adaptivePolling` is disabled, only `idleMinWaitTime` will be used as a constant time span in which the slave will not poll the master for further changes. The default values are 0.5 seconds for `idleMinWaitTime` and 2.5 seconds for `idleMaxWaitTime`, which correspond to the hard-coded values diff --git a/arangod/Aql/ClusterBlocks.cpp b/arangod/Aql/ClusterBlocks.cpp index 68f9d74be7..01ebe43789 100644 --- a/arangod/Aql/ClusterBlocks.cpp +++ b/arangod/Aql/ClusterBlocks.cpp @@ -1133,7 +1133,7 @@ size_t DistributeBlock::sendToClient (AqlItemBlock* cur) { bool hasCreatedKeyAttribute = false; - if (TRI_IsStringJson(json)) { + if (TRI_IsStringJson(json) && static_cast(_exeNode)->_allowKeyConversionToObject) { TRI_json_t* obj = TRI_CreateObjectJson(TRI_UNKNOWN_MEM_ZONE, 1); if (obj == nullptr) { diff --git a/arangod/Aql/ClusterNodes.cpp b/arangod/Aql/ClusterNodes.cpp index 65c632cde7..8bb69d9127 100644 --- a/arangod/Aql/ClusterNodes.cpp +++ b/arangod/Aql/ClusterNodes.cpp @@ -153,7 +153,8 @@ DistributeNode::DistributeNode (ExecutionPlan* plan, _collection(plan->getAst()->query()->collections()->get(JsonHelper::checkAndGetStringValue(base.json(), "collection"))), _varId(JsonHelper::checkAndGetNumericValue(base.json(), "varId")), _alternativeVarId(JsonHelper::checkAndGetNumericValue(base.json(), "alternativeVarId")), - _createKeys(JsonHelper::checkAndGetBooleanValue(base.json(), "createKeys")) { + _createKeys(JsonHelper::checkAndGetBooleanValue(base.json(), "createKeys")), + _allowKeyConversionToObject(JsonHelper::checkAndGetBooleanValue(base.json(), "allowKeyConversionToObject")) { } void DistributeNode::toJsonHelper (triagens::basics::Json& nodes, @@ -169,7 +170,8 @@ void DistributeNode::toJsonHelper (triagens::basics::Json& nodes, ("collection", triagens::basics::Json(_collection->getName())) ("varId", triagens::basics::Json(static_cast(_varId))) ("alternativeVarId", triagens::basics::Json(static_cast(_alternativeVarId))) - ("createKeys", triagens::basics::Json(_createKeys)); + ("createKeys", triagens::basics::Json(_createKeys)) + ("allowKeyConversionToObject", triagens::basics::Json(_allowKeyConversionToObject)); // And add it: nodes(json); diff --git a/arangod/Aql/ClusterNodes.h b/arangod/Aql/ClusterNodes.h index 2621c168be..5b01a20a36 100644 --- a/arangod/Aql/ClusterNodes.h +++ b/arangod/Aql/ClusterNodes.h @@ -372,13 +372,15 @@ namespace triagens { Collection const* collection, VariableId const varId, VariableId const alternativeVarId, - bool createKeys) + bool createKeys, + bool allowKeyConversionToObject) : ExecutionNode(plan, id), _vocbase(vocbase), _collection(collection), _varId(varId), _alternativeVarId(alternativeVarId), - _createKeys(createKeys) { + _createKeys(createKeys), + _allowKeyConversionToObject(allowKeyConversionToObject) { } DistributeNode (ExecutionPlan* plan, @@ -386,8 +388,9 @@ namespace triagens { TRI_vocbase_t* vocbase, Collection const* collection, VariableId const varId, - bool createKeys) - : DistributeNode(plan, id, vocbase, collection, varId, varId, createKeys) { + bool createKeys, + bool allowKeyConversionToObject) + : DistributeNode(plan, id, vocbase, collection, varId, varId, createKeys, allowKeyConversionToObject) { // just delegates to the other constructor } @@ -417,7 +420,7 @@ namespace triagens { ExecutionNode* clone (ExecutionPlan* plan, bool withDependencies, bool withProperties) const override final { - auto c = new DistributeNode(plan, _id, _vocbase, _collection, _varId, _alternativeVarId, _createKeys); + auto c = new DistributeNode(plan, _id, _vocbase, _collection, _varId, _alternativeVarId, _createKeys, _allowKeyConversionToObject); cloneHelper(c, plan, withDependencies, withProperties); @@ -479,6 +482,12 @@ namespace triagens { bool const _createKeys; +//////////////////////////////////////////////////////////////////////////////// +/// @brief allow conversion of key to object +//////////////////////////////////////////////////////////////////////////////// + + bool const _allowKeyConversionToObject; + }; // ----------------------------------------------------------------------------- diff --git a/arangod/Aql/OptimizerRules.cpp b/arangod/Aql/OptimizerRules.cpp index f8c87f76ca..76ae61832d 100644 --- a/arangod/Aql/OptimizerRules.cpp +++ b/arangod/Aql/OptimizerRules.cpp @@ -2572,7 +2572,7 @@ int triagens::aql::distributeInClusterRule (Optimizer* opt, bool const createKeys = (nodeType == ExecutionNode::INSERT); inputVariable = node->getVariablesUsedHere()[0]; distNode = new DistributeNode(plan, plan->nextId(), - vocbase, collection, inputVariable->id, createKeys); + vocbase, collection, inputVariable->id, createKeys, true); } else if (nodeType == ExecutionNode::REPLACE) { std::vector v = node->getVariablesUsedHere(); @@ -2585,7 +2585,7 @@ int triagens::aql::distributeInClusterRule (Optimizer* opt, inputVariable = v[0]; } distNode = new DistributeNode(plan, plan->nextId(), - vocbase, collection, inputVariable->id, false); + vocbase, collection, inputVariable->id, false, v.size() > 1); } else if (nodeType == ExecutionNode::UPDATE) { std::vector v = node->getVariablesUsedHere(); @@ -2600,7 +2600,7 @@ int triagens::aql::distributeInClusterRule (Optimizer* opt, inputVariable = v[0]; } distNode = new DistributeNode(plan, plan->nextId(), - vocbase, collection, inputVariable->id, false); + vocbase, collection, inputVariable->id, false, v.size() > 1); } else if (nodeType == ExecutionNode::UPSERT) { // an UPSERT nodes has two input variables! @@ -2608,7 +2608,7 @@ int triagens::aql::distributeInClusterRule (Optimizer* opt, TRI_ASSERT(v.size() >= 2); distNode = new DistributeNode(plan, plan->nextId(), - vocbase, collection, v[0]->id, v[2]->id, false); + vocbase, collection, v[0]->id, v[2]->id, false, true); } else { TRI_ASSERT(false); @@ -2940,7 +2940,8 @@ class RemoveToEnumCollFinder final : public WalkerWorker { const Variable* _variable; ExecutionNode* _lastNode; - public: + public: + RemoveToEnumCollFinder (ExecutionPlan* plan, std::unordered_set& toUnlink) : _plan(plan), @@ -2960,7 +2961,9 @@ class RemoveToEnumCollFinder final : public WalkerWorker { bool before (ExecutionNode* en) override final { switch (en->getType()) { case EN::REMOVE: { - TRI_ASSERT(_remove == false); + if (_remove) { + break; + } // find the variable we are removing . . . auto rn = static_cast(en); diff --git a/js/server/modules/org/arangodb/aql.js b/js/server/modules/org/arangodb/aql.js index 374b3002ac..3c235dd86c 100644 --- a/js/server/modules/org/arangodb/aql.js +++ b/js/server/modules/org/arangodb/aql.js @@ -2718,12 +2718,6 @@ function AQL_IS_DATESTRING (value) { // argument is a string - // append zulu time specifier if no other present - if (! value.match(/([zZ]|[+\-]\d+(:\d+)?)$/) || - (value.match(/-\d+(:\d+)?$/) && ! value.match(/[tT ]/))) { - value += 'Z'; - } - // detect invalid dates ("foo" -> "fooZ" -> getTime() == NaN) var date = new Date(value); if (isNaN(date)) { diff --git a/js/server/tests/aql-functions-numeric-novalgrind.js b/js/server/tests/aql-functions-numeric.js similarity index 99% rename from js/server/tests/aql-functions-numeric-novalgrind.js rename to js/server/tests/aql-functions-numeric.js index ff1c71bad9..f2dc75599a 100644 --- a/js/server/tests/aql-functions-numeric-novalgrind.js +++ b/js/server/tests/aql-functions-numeric.js @@ -1974,21 +1974,27 @@ function ahuacatlNumericFunctionsTestSuite () { [ 1.321e-45 , 1.321e-45 , 1 ] ]; + var valgrind = require("internal").valgrind; + data.forEach(function (value) { + if (valgrind && (value[0] === 0 || value[1] >= 0 && value[1] < 1)) { + // V8 under Valgrind has an issue with Math.pow(0, 0.1); + return; + } var query = "RETURN POW(" + JSON.stringify(value[0]) + ", " + JSON.stringify(value[1]) + ")"; var actual = getQueryResults(query); if (value[2] === null) { assertNull(actual[0]); } else { - assertEqual(value[2].toPrecision(6), actual[0].toPrecision(6), query); + assertEqual(value[2].toPrecision(4), actual[0].toPrecision(4), value); } actual = getQueryResults("RETURN NOOPT(POW(" + JSON.stringify(value[0]) + ", " + JSON.stringify(value[1]) + "))"); if (value[2] === null) { assertNull(actual[0]); } else { - assertEqual(value[2].toPrecision(6), actual[0].toPrecision(6)); + assertEqual(value[2].toPrecision(4), actual[0].toPrecision(4), value); } query = "RETURN NOOPT(V8(POW(" + JSON.stringify(value[0]) + ", " + JSON.stringify(value[1]) + ")))"; actual = getQueryResults(query); @@ -1996,7 +2002,7 @@ function ahuacatlNumericFunctionsTestSuite () { assertNull(actual[0], query); } else { - assertEqual(value[2].toPrecision(6), actual[0].toPrecision(6), query); + assertEqual(value[2].toPrecision(4), actual[0].toPrecision(4), value); } }); }, diff --git a/js/server/tests/aql-modify-cluster.js b/js/server/tests/aql-modify-cluster.js index 85c4c74b72..f2e99ef6c3 100644 --- a/js/server/tests/aql-modify-cluster.js +++ b/js/server/tests/aql-modify-cluster.js @@ -81,62 +81,6 @@ function ahuacatlModifySuite () { c2 = null; }, -//////////////////////////////////////////////////////////////////////////////// -/// @brief test subquery -//////////////////////////////////////////////////////////////////////////////// - - testRemoveInSubquery : function () { - assertQueryError(errors.ERROR_QUERY_MODIFY_IN_SUBQUERY.code, "FOR d IN @@cn LET x = (REMOVE d.foobar IN @@cn) RETURN d", { "@cn": cn1 }); - }, - -//////////////////////////////////////////////////////////////////////////////// -/// @brief test subquery -//////////////////////////////////////////////////////////////////////////////// - - testInsertInSubquery : function () { - assertQueryError(errors.ERROR_QUERY_MODIFY_IN_SUBQUERY.code, "FOR d IN @@cn LET x = (INSERT { _key: 'test' } IN @@cn) RETURN d", { "@cn": cn1 }); - }, - -//////////////////////////////////////////////////////////////////////////////// -/// @brief test subquery -//////////////////////////////////////////////////////////////////////////////// - - testUpdateInSubquery : function () { - assertQueryError(errors.ERROR_QUERY_MODIFY_IN_SUBQUERY.code, "FOR d IN @@cn LET x = (UPDATE { _key: 'test' } IN @@cn) RETURN d", { "@cn": cn1 }); - }, - -//////////////////////////////////////////////////////////////////////////////// -/// @brief test subquery -//////////////////////////////////////////////////////////////////////////////// - - testReplaceInSubquery : function () { - assertQueryError(errors.ERROR_QUERY_MODIFY_IN_SUBQUERY.code, "FOR d IN @@cn LET x = (REPLACE { _key: 'test' } IN @@cn) RETURN d", { "@cn": cn1 }); - }, - -//////////////////////////////////////////////////////////////////////////////// -/// @brief test subquery -//////////////////////////////////////////////////////////////////////////////// - - testReplaceInSubquery2 : function () { - assertQueryError(errors.ERROR_QUERY_MODIFY_IN_SUBQUERY.code, "FOR d IN @@cn LET x = (FOR i IN 1..2 REPLACE { _key: 'test' } IN @@cn) RETURN d", { "@cn": cn1 }); - }, - -//////////////////////////////////////////////////////////////////////////////// -/// @brief test subquery -//////////////////////////////////////////////////////////////////////////////// - - testMultiModify : function () { - assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR d IN @@cn1 REMOVE d IN @@cn1 FOR e IN @@cn2 REMOVE e IN @@cn2", { "@cn1": cn1, "@cn2": cn2 }); - }, - -//////////////////////////////////////////////////////////////////////////////// -/// @brief test subquery -//////////////////////////////////////////////////////////////////////////////// - - testMultiModify2 : function () { - assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR d IN @@cn1 FOR e IN @@cn2 REMOVE d IN @@cn1 REMOVE e IN @@cn2", { "@cn1": cn1, "@cn2": cn2 }); - }, - //////////////////////////////////////////////////////////////////////////////// /// @brief test subquery //////////////////////////////////////////////////////////////////////////////// @@ -1162,12 +1106,20 @@ function ahuacatlUpdateSuite () { assertQueryError(errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, "FOR d IN @@cn UPDATE [ ] IN @@cn", { "@cn": cn1 }); }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief test update +//////////////////////////////////////////////////////////////////////////////// + + testUpdateInvalidType : function () { + assertQueryError(errors.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, "FOR d IN @@cn UPDATE 'foo' IN @@cn", { "@cn": cn1 }); + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief test update //////////////////////////////////////////////////////////////////////////////// testUpdateInvalidKey : function () { - assertQueryError(errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, "FOR d IN @@cn UPDATE 'foo' IN @@cn", { "@cn": cn1 }); + assertQueryError(errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code, "FOR d IN @@cn UPDATE { _key: 'foo' } IN @@cn", { "@cn": cn1 }); }, ////////////////////////////////////////////////////////////////////////////////