diff --git a/CHANGELOG b/CHANGELOG index 2b135de7ac..84af5da4b5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,12 @@ v2.3.1 (2014-11-28) * fixed issue #1126 +* fixed non-working subquery index optimizations + +* do not restrict summary of Foxx applications to 60 characters + +* fixed display of "required" path parameters in Foxx application documentation + * added more optimizations of constants values in AQL FILTER conditions * fixed invalid or-to-in optimization for FILTERs containing comparisons @@ -19,9 +25,8 @@ v2.3.1 (2014-11-28) * fixed AQL optimizer cost estimation for LIMIT node - -v2.3.0 (2014-11-18) -------------------- +* prevent Foxx queues from permanently writing to the journal even when + server is idle v2.3.0 (2014-11-18) diff --git a/UnitTests/Makefile.unittests b/UnitTests/Makefile.unittests index e626b7907a..4608b38379 100755 --- a/UnitTests/Makefile.unittests +++ b/UnitTests/Makefile.unittests @@ -556,6 +556,7 @@ SHELL_SERVER_AQL = @top_srcdir@/js/server/tests/aql-arithmetic.js \ @top_srcdir@/js/server/tests/aql-operators.js \ @top_srcdir@/js/server/tests/aql-optimizer-dynamic-bounds.js \ @top_srcdir@/js/server/tests/aql-optimizer-filters.js \ + @top_srcdir@/js/server/tests/aql-optimizer-indexes.js \ @top_srcdir@/js/server/tests/aql-optimizer-rule-interchange-adjacent-enumerations-noncluster.js \ @top_srcdir@/js/server/tests/aql-optimizer-rule-move-calculations-up.js \ @top_srcdir@/js/server/tests/aql-optimizer-rule-move-filters-up.js \ diff --git a/arangod/Aql/ExecutionPlan.cpp b/arangod/Aql/ExecutionPlan.cpp index f4fe8b5bd2..08b5aea4ea 100644 --- a/arangod/Aql/ExecutionPlan.cpp +++ b/arangod/Aql/ExecutionPlan.cpp @@ -1019,12 +1019,28 @@ void ExecutionPlan::checkLinkage () { struct VarUsageFinder : public WalkerWorker { std::unordered_set _usedLater; std::unordered_set _valid; - std::unordered_map _varSetBy; + std::unordered_map* _varSetBy; + bool const _ownsVarSetBy; - VarUsageFinder () { + VarUsageFinder () + : _varSetBy(new std::unordered_map()), + _ownsVarSetBy(true) { + + TRI_ASSERT(_varSetBy != nullptr); } - + + explicit VarUsageFinder (std::unordered_map* varSetBy) + : _varSetBy(varSetBy), + _ownsVarSetBy(false) { + + TRI_ASSERT(_varSetBy != nullptr); + } + ~VarUsageFinder () { + if (_ownsVarSetBy) { + TRI_ASSERT(_varSetBy != nullptr); + delete _varSetBy; + } } bool before (ExecutionNode* en) override final { @@ -1043,14 +1059,14 @@ struct VarUsageFinder : public WalkerWorker { auto&& setHere = en->getVariablesSetHere(); for (auto v : setHere) { _valid.insert(v); - _varSetBy.emplace(std::make_pair(v->id, en)); + _varSetBy->emplace(std::make_pair(v->id, en)); } en->setVarsValid(_valid); en->setVarUsageValid(); } bool enterSubquery (ExecutionNode*, ExecutionNode* sub) override final { - VarUsageFinder subfinder; + VarUsageFinder subfinder(_varSetBy); subfinder._valid = _valid; // need a copy for the subquery! sub->walk(&subfinder); @@ -1066,7 +1082,7 @@ struct VarUsageFinder : public WalkerWorker { void ExecutionPlan::findVarUsage () { ::VarUsageFinder finder; root()->walk(&finder); - _varSetBy = finder._varSetBy; + _varSetBy = *finder._varSetBy; _varUsageComputed = true; } diff --git a/arangod/Aql/OptimizerRules.cpp b/arangod/Aql/OptimizerRules.cpp index 377aca91f7..38abddc87e 100644 --- a/arangod/Aql/OptimizerRules.cpp +++ b/arangod/Aql/OptimizerRules.cpp @@ -916,7 +916,7 @@ class FilterToEnumCollFinder : public WalkerWorker { bool valid = true; // are all the range infos valid? - for(auto x: *map) { + for (auto x: *map) { valid &= x.second.isValid(); if (! valid) { break; @@ -1074,6 +1074,7 @@ class FilterToEnumCollFinder : public WalkerWorker { void buildRangeInfo (AstNode const* node, Variable const*& enumCollVar, std::string& attr) { + if (node->type == NODE_TYPE_REFERENCE) { auto x = static_cast(node->getData()); auto setter = _plan->getVarSetBy(x->id); @@ -1098,6 +1099,7 @@ class FilterToEnumCollFinder : public WalkerWorker { if (node->type == NODE_TYPE_OPERATOR_BINARY_EQ) { auto lhs = node->getMember(0); auto rhs = node->getMember(1); + if (rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) { buildRangeInfo(rhs, enumCollVar, attr); if (enumCollVar != nullptr) { diff --git a/js/server/tests/aql-optimizer-indexes.js b/js/server/tests/aql-optimizer-indexes.js new file mode 100644 index 0000000000..f773980bd9 --- /dev/null +++ b/js/server/tests/aql-optimizer-indexes.js @@ -0,0 +1,151 @@ +/*jshint strict: false, maxlen: 500 */ +/*global require, assertEqual, assertNotEqual, AQL_EXPLAIN, AQL_EXECUTE */ + +//////////////////////////////////////////////////////////////////////////////// +/// @brief tests for index usage +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2010-2012 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 triAGENS GmbH, Cologne, Germany +/// +/// @author Jan Steemann +/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +var jsunity = require("jsunity"); +var db = require("org/arangodb").db; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite +//////////////////////////////////////////////////////////////////////////////// + +function optimizerIndexesTestSuite () { + var c; + + return { + setUp : function () { + db._drop("UnitTestsCollection"); + c = db._create("UnitTestsCollection"); + + for (var i = 0; i < 2000; ++i) { + c.save({ value: i }); + } + + c.ensureSkiplist("value"); + }, + + tearDown : function () { + db._drop("UnitTestsCollection"); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test index usage +//////////////////////////////////////////////////////////////////////////////// + + testUseIndexSimple : function () { + var query = "FOR i IN " + c.name() + " FILTER i.value >= 10 SORT i.value LIMIT 10 RETURN i.value"; + + var plan = AQL_EXPLAIN(query).plan; + var nodeTypes = plan.nodes.map(function(node) { + return node.type; + }); + + assertEqual("SingletonNode", nodeTypes[0], query); + assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query); + assertEqual(-1, nodeTypes.indexOf("SortNode"), query); + assertEqual("ReturnNode", nodeTypes[nodeTypes.length - 1], query); + + var results = AQL_EXECUTE(query); + assertEqual([ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ], results.json, query); + assertEqual(0, results.stats.scannedFull); + assertTrue(results.stats.scannedIndex > 0); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test index usage +//////////////////////////////////////////////////////////////////////////////// + + testUseIndexSubquery : function () { + var query = "LET results = (FOR i IN " + c.name() + " FILTER i.value >= 10 SORT i.value LIMIT 10 RETURN i.value) RETURN results"; + + var plan = AQL_EXPLAIN(query).plan; + var nodeTypes = plan.nodes.map(function(node) { + return node.type; + }); + + assertEqual("SingletonNode", nodeTypes[0], query); + assertEqual("SubqueryNode", nodeTypes[1], query); + + var subNodeTypes = plan.nodes[1].subquery.nodes.map(function(node) { + return node.type; + }); + assertNotEqual(-1, subNodeTypes.indexOf("IndexRangeNode"), query); + assertEqual(-1, subNodeTypes.indexOf("SortNode"), query); + assertEqual("ReturnNode", nodeTypes[nodeTypes.length - 1], query); + + var results = AQL_EXECUTE(query); + assertEqual([ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ], results.json[0], query); + assertEqual(0, results.stats.scannedFull); + assertTrue(results.stats.scannedIndex > 0); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test index usage +//////////////////////////////////////////////////////////////////////////////// + + testUseIndexSubSubquery : function () { + var query = "FOR i IN " + c.name() + " LIMIT 1 RETURN (FOR j IN " + c.name() + " FILTER j._key == i._key RETURN j.value)"; + + var plan = AQL_EXPLAIN(query).plan; + var nodeTypes = plan.nodes.map(function(node) { + return node.type; + }); + + assertEqual("SingletonNode", nodeTypes[0], query); + var idx = nodeTypes.indexOf("SubqueryNode"); + assertNotEqual(-1, idx, query); + + var subNodeTypes = plan.nodes[idx].subquery.nodes.map(function(node) { + return node.type; + }); + assertNotEqual(-1, subNodeTypes.indexOf("IndexRangeNode"), query); + assertEqual(-1, subNodeTypes.indexOf("SortNode"), query); + assertEqual("ReturnNode", nodeTypes[nodeTypes.length - 1], query); + + var results = AQL_EXECUTE(query); + // require("internal").print(results); + assertTrue(results.stats.scannedFull > 0); // for the outer query + assertTrue(results.stats.scannedIndex > 0); // for the inner query + } + + }; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes the test suite +//////////////////////////////////////////////////////////////////////////////// + +jsunity.run(optimizerIndexesTestSuite); + +return jsunity.done(); + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// End: