1
0
Fork 0

fix optimization of subqueries

Conflicts:
	CHANGELOG
This commit is contained in:
Jan Steemann 2014-11-26 15:01:55 +01:00 committed by Frank Celler
parent bea5cd0e26
commit 544f7d4ab4
5 changed files with 185 additions and 10 deletions

View File

@ -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)

View File

@ -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 \

View File

@ -1019,12 +1019,28 @@ void ExecutionPlan::checkLinkage () {
struct VarUsageFinder : public WalkerWorker<ExecutionNode> {
std::unordered_set<Variable const*> _usedLater;
std::unordered_set<Variable const*> _valid;
std::unordered_map<VariableId, ExecutionNode*> _varSetBy;
std::unordered_map<VariableId, ExecutionNode*>* _varSetBy;
bool const _ownsVarSetBy;
VarUsageFinder () {
VarUsageFinder ()
: _varSetBy(new std::unordered_map<VariableId, ExecutionNode*>()),
_ownsVarSetBy(true) {
TRI_ASSERT(_varSetBy != nullptr);
}
explicit VarUsageFinder (std::unordered_map<VariableId, ExecutionNode*>* 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<ExecutionNode> {
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<ExecutionNode> {
void ExecutionPlan::findVarUsage () {
::VarUsageFinder finder;
root()->walk(&finder);
_varSetBy = finder._varSetBy;
_varSetBy = *finder._varSetBy;
_varUsageComputed = true;
}

View File

@ -916,7 +916,7 @@ class FilterToEnumCollFinder : public WalkerWorker<ExecutionNode> {
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<ExecutionNode> {
void buildRangeInfo (AstNode const* node,
Variable const*& enumCollVar,
std::string& attr) {
if (node->type == NODE_TYPE_REFERENCE) {
auto x = static_cast<Variable*>(node->getData());
auto setter = _plan->getVarSetBy(x->id);
@ -1098,6 +1099,7 @@ class FilterToEnumCollFinder : public WalkerWorker<ExecutionNode> {
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) {

View File

@ -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: