1
0
Fork 0

added tests for filter order, not optimization etc.

This commit is contained in:
Jan Steemann 2015-01-22 14:16:58 +01:00
parent 8aeded5c86
commit 1c935b6ab2
4 changed files with 388 additions and 3 deletions

View File

@ -1369,7 +1369,7 @@ static RangeInfoMapVec* BuildRangeInfo (ExecutionPlan* plan,
} }
// distribute AND into OR // distribute AND into OR
return andCombineRangeInfoMapVecs(lhs, rhs); return andCombineRangeInfoMapVecsIgnoreEmpty(lhs, rhs);
} }
if (node->type == NODE_TYPE_OPERATOR_BINARY_IN) { if (node->type == NODE_TYPE_OPERATOR_BINARY_IN) {
@ -1512,6 +1512,7 @@ class FilterToEnumCollFinder : public WalkerWorker<ExecutionNode> {
std::string attr; std::string attr;
Variable const* enumCollVar = nullptr; Variable const* enumCollVar = nullptr;
auto expression = node->expression()->node(); auto expression = node->expression()->node();
// there is an implicit AND between FILTER statements // there is an implicit AND between FILTER statements
if (_rangeInfoMapVec == nullptr) { if (_rangeInfoMapVec == nullptr) {
// don't yet have anything to AND-combine // don't yet have anything to AND-combine
@ -1519,7 +1520,7 @@ class FilterToEnumCollFinder : public WalkerWorker<ExecutionNode> {
} }
else { else {
// AND-combine with previous ranges // AND-combine with previous ranges
_rangeInfoMapVec = andCombineRangeInfoMapVecs( _rangeInfoMapVec = andCombineRangeInfoMapVecsIgnoreEmpty(
_rangeInfoMapVec, _rangeInfoMapVec,
BuildRangeInfo(_plan, expression, enumCollVar, attr, _mustNotUseRanges) BuildRangeInfo(_plan, expression, enumCollVar, attr, _mustNotUseRanges)
); );

View File

@ -669,6 +669,37 @@ RangeInfoMapVec* triagens::aql::andCombineRangeInfoMapVecs (RangeInfoMapVec* lhs
return rimv; return rimv;
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief andCombineRangeInfoMapVecs: same as before, but will return the
/// mapvec even if one side is a nullptr
////////////////////////////////////////////////////////////////////////////////
RangeInfoMapVec* triagens::aql::andCombineRangeInfoMapVecsIgnoreEmpty (RangeInfoMapVec* lhs,
RangeInfoMapVec* rhs) {
if (lhs == nullptr && rhs == nullptr) {
return nullptr;
}
if ((lhs == nullptr || lhs->empty()) && rhs != nullptr) {
if (lhs != nullptr) {
delete lhs;
}
return rhs;
}
if (lhs != nullptr && (rhs == nullptr || rhs->empty())) {
if (rhs != nullptr) {
delete rhs;
}
return lhs;
}
TRI_ASSERT(lhs != nullptr);
TRI_ASSERT(rhs != nullptr);
return andCombineRangeInfoMapVecs(lhs, rhs);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief comparison of range infos /// @brief comparison of range infos
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -921,6 +921,13 @@ namespace triagens {
RangeInfoMapVec* andCombineRangeInfoMapVecs (RangeInfoMapVec*, RangeInfoMapVec*); RangeInfoMapVec* andCombineRangeInfoMapVecs (RangeInfoMapVec*, RangeInfoMapVec*);
////////////////////////////////////////////////////////////////////////////////
/// @brief andCombineRangeInfoMapVecs: same as before, but will return the
/// mapvec even if one side is a nullptr
////////////////////////////////////////////////////////////////////////////////
RangeInfoMapVec* andCombineRangeInfoMapVecsIgnoreEmpty (RangeInfoMapVec*, RangeInfoMapVec*);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief IndexOrCondition, type for vector of vector of RangeInfo. The meaning /// @brief IndexOrCondition, type for vector of vector of RangeInfo. The meaning
/// is: the outer vector means an implicit "OR" between the entries. Each /// is: the outer vector means an implicit "OR" between the entries. Each

View File

@ -1009,6 +1009,78 @@ function optimizerIndexesTestSuite () {
assertEqual(0, results.stats.scannedFull); assertEqual(0, results.stats.scannedFull);
assertTrue(results.stats.scannedIndex > 0); assertTrue(results.stats.scannedIndex > 0);
}, },
////////////////////////////////////////////////////////////////////////////////
/// @brief test index usage
////////////////////////////////////////////////////////////////////////////////
testIndexOrNoIndexBecauseOfDifferentAttributes : function () {
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value, value3: 1 } IN " + c.name());
var queries = [
"FOR i IN " + c.name() + " FILTER i.value == 1 || i.value2 == 2 RETURN i.value",
"FOR i IN " + c.name() + " FILTER i.value2 == 2 || i.value == 1 RETURN i.value",
"FOR i IN " + c.name() + " FILTER (i.value == 1 || i.value == 2) || i.value2 == 2 RETURN i.value",
"FOR i IN " + c.name() + " FILTER i.value2 == 2 || (i.value == 1 || i.value == 2) RETURN i.value",
"FOR i IN " + c.name() + " FILTER i.value == 1 || (i.value == 2 || i.value2 == 2) RETURN i.value",
"FOR i IN " + c.name() + " FILTER (i.value2 == 2 || i.value == 1) || i.value == 2 RETURN i.value",
"FOR i IN " + c.name() + " FILTER (i.value == 1 && i.value2 == 1) || (i.value == 2 || i.value3 != 1) RETURN i.value",
"FOR i IN " + c.name() + " FILTER (i.value2 == 1 && i.value == 1) || (i.value3 != 1 || i.value == 2) RETURN i.value",
"FOR i IN " + c.name() + " FILTER (i.value == 1 && i.value2 == 1) || (i.value == 2 || i.value3 == 0) RETURN i.value",
"FOR i IN " + c.name() + " FILTER (i.value2 == 1 && i.value == 1) || (i.value3 == 0 || i.value == 2) RETURN i.value"
];
queries.forEach(function(query) {
var plan = AQL_EXPLAIN(query).plan;
var nodeTypes = plan.nodes.map(function(node) {
return node.type;
});
assertEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
var results = AQL_EXECUTE(query);
assertEqual(2, results.json.length);
assertTrue(results.stats.scannedFull > 0);
assertEqual(0, results.stats.scannedIndex);
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test index usage
////////////////////////////////////////////////////////////////////////////////
testIndexOrNoIndexBecauseOfOperator : function () {
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value, value3: 1 } IN " + c.name());
var queries = [
"FOR i IN " + c.name() + " FILTER i.value == 1 || i.value2 != i.value RETURN i.value",
"FOR i IN " + c.name() + " FILTER i.value2 != i.value || i.value == 1 RETURN i.value",
"FOR i IN " + c.name() + " FILTER (i.value != 1 || i.value != 2) && i.value == 3 RETURN i.value",
"FOR i IN " + c.name() + " FILTER i.value == 3 && (i.value != 1 || i.value != 2) RETURN i.value",
"FOR i IN " + c.name() + " FILTER (i.value2 != 1 || i.value2 != 2) && i.value == 3 RETURN i.value",
"FOR i IN " + c.name() + " FILTER i.value == 3 && (i.value2 != 1 || i.value2 != 2) RETURN i.value",
"FOR i IN " + c.name() + " FILTER i.value != 1 && PASSTHRU(i.value2) == 2 RETURN i.value",
"FOR i IN " + c.name() + " FILTER PASSTHRU(i.value2) == 2 && i.value != 1 RETURN i.value",
"FOR i IN " + c.name() + " FILTER i.value2 != 2 && PASSTHRU(i.value) == 1 RETURN i.value",
"FOR i IN " + c.name() + " FILTER PASSTHRU(i.value) == 1 && i.value2 != 2 RETURN i.value",
"FOR i IN " + c.name() + " FILTER i.value == 1 || i.value3 != 1 RETURN i.value",
"FOR i IN " + c.name() + " FILTER i.value3 != 1 || i.value == 1 RETURN i.value",
"FOR i IN " + c.name() + " FILTER i.value3 != 1 || i.value2 == 2 RETURN i.value",
"FOR i IN " + c.name() + " FILTER i.value2 == 2 || i.value3 != 1 RETURN i.value"
];
queries.forEach(function(query) {
var plan = AQL_EXPLAIN(query).plan;
var nodeTypes = plan.nodes.map(function(node) {
return node.type;
});
assertEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
var results = AQL_EXECUTE(query);
assertEqual(1, results.json.length, query);
assertTrue(results.stats.scannedFull > 0);
assertEqual(0, results.stats.scannedIndex);
});
},
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief test index usage /// @brief test index usage
@ -1124,7 +1196,9 @@ function optimizerIndexesTestSuite () {
"LET a = PASSTHRU(0) FOR i IN " + c.name() + " FILTER i.value == 1 && a + 1 == 1 RETURN i.value", "LET a = PASSTHRU(0) FOR i IN " + c.name() + " FILTER i.value == 1 && a + 1 == 1 RETURN i.value",
"LET a = PASSTHRU(0) FOR i IN " + c.name() + " FILTER a + 1 == 1 && i.value == 1 RETURN i.value", "LET a = PASSTHRU(0) FOR i IN " + c.name() + " FILTER a + 1 == 1 && i.value == 1 RETURN i.value",
"LET a = PASSTHRU(0) FOR i IN " + c.name() + " FILTER i.value == 1 && a IN [ 0, 1 ] RETURN i.value", "LET a = PASSTHRU(0) FOR i IN " + c.name() + " FILTER i.value == 1 && a IN [ 0, 1 ] RETURN i.value",
"LET a = PASSTHRU(0) FOR i IN " + c.name() + " FILTER a IN [ 0, 1 ] && i.value == 1 RETURN i.value" "LET a = PASSTHRU(0) FOR i IN " + c.name() + " FILTER a IN [ 0, 1 ] && i.value == 1 RETURN i.value",
"FOR i IN " + c.name() + " FILTER i.value == 1 && NOT (i.value != 1) RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 1 || NOT NOT (i.value == 1) RETURN i"
]; ];
queries.forEach(function(query) { queries.forEach(function(query) {
@ -1140,6 +1214,35 @@ function optimizerIndexesTestSuite () {
assertEqual(0, results.stats.scannedFull); assertEqual(0, results.stats.scannedFull);
}); });
}, },
////////////////////////////////////////////////////////////////////////////////
/// @brief test index usage
////////////////////////////////////////////////////////////////////////////////
testIndexAndDespiteOr : function () {
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value, value3: 1 } IN " + c.name());
var queries = [
"FOR i IN " + c.name() + " FILTER (i.value == 1 && i.value2 == 1) || (i.value == 2 && i.value2 == 2) RETURN i.value",
"FOR i IN " + c.name() + " FILTER (i.value2 == 1 && i.value == 1) || (i.value2 == 2 && i.value == 2) RETURN i.value",
"FOR i IN " + c.name() + " FILTER (i.value2 == 1 && i.value == 1) || (i.value3 != 2 && i.value == 2) RETURN i.value",
"FOR i IN " + c.name() + " FILTER (i.value == 1 && i.value2 == 1) || (i.value == 2 && i.value3 != 2) RETURN i.value",
"FOR i IN " + c.name() + " FILTER (i.value2 == 1 && i.value == 1) || (i.value3 == 1 && i.value == 2) RETURN i.value"
];
queries.forEach(function(query) {
var plan = AQL_EXPLAIN(query).plan;
var nodeTypes = plan.nodes.map(function(node) {
return node.type;
});
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
var results = AQL_EXECUTE(query);
assertEqual(2, results.json.length, query);
assertTrue(results.stats.scannedIndex > 0);
assertEqual(0, results.stats.scannedFull);
});
},
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief test index usage /// @brief test index usage
@ -1304,6 +1407,249 @@ function optimizerIndexesTestSuite () {
}); });
}, },
////////////////////////////////////////////////////////////////////////////////
/// @brief test index usage
////////////////////////////////////////////////////////////////////////////////
testIndexAndSkiplistIndexedNonIndexed : function () {
// value2 is not indexed
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value } IN " + c.name());
var queries = [
"FOR i IN " + c.name() + " FILTER i.value == 2 FILTER i.value2 == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 2 && i.value2 == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 == 2 FILTER i.value == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 == 2 && i.value == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 2 FILTER i.value2 != 1 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 2 && i.value2 != 1 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 != 1 FILTER i.value == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 != 1 && i.value == 2 RETURN i",
];
queries.forEach(function(query) {
var plan = AQL_EXPLAIN(query).plan;
var nodeTypes = plan.nodes.map(function(node) {
return node.type;
});
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
var results = AQL_EXECUTE(query);
assertEqual(1, results.json.length);
assertEqual(2, results.json[0].value);
assertTrue(results.stats.scannedIndex > 0);
assertEqual(0, results.stats.scannedFull);
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test index usage
////////////////////////////////////////////////////////////////////////////////
testIndexAndHashIndexedNonIndexed : function () {
// drop the skiplist index
c.dropIndex(c.getIndexes()[1]);
c.ensureHashIndex("value");
// value2 is not indexed
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value } IN " + c.name());
var queries = [
"FOR i IN " + c.name() + " FILTER i.value == 2 FILTER i.value2 == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 2 && i.value2 == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 == 2 FILTER i.value == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 == 2 && i.value == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 2 FILTER i.value2 != 1 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 2 && i.value2 != 1 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 != 1 FILTER i.value == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 != 1 && i.value == 2 RETURN i"
];
queries.forEach(function(query) {
var plan = AQL_EXPLAIN(query).plan;
var nodeTypes = plan.nodes.map(function(node) {
return node.type;
});
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
var results = AQL_EXECUTE(query);
assertEqual(1, results.json.length);
assertEqual(2, results.json[0].value);
assertTrue(results.stats.scannedIndex > 0);
assertEqual(0, results.stats.scannedFull);
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test index usage
////////////////////////////////////////////////////////////////////////////////
testIndexAndAlternativeHashAndSkiplistIndexedNonIndexed : function () {
// create a competitor index
c.ensureHashIndex("value");
// value2 is not indexed
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value } IN " + c.name());
var queries = [
"FOR i IN " + c.name() + " FILTER i.value == 2 FILTER i.value2 == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 2 && i.value2 == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 == 2 FILTER i.value == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 == 2 && i.value == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 2 FILTER i.value2 != 1 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 2 && i.value2 != 1 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 != 1 FILTER i.value == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 != 1 && i.value == 2 RETURN i"
];
queries.forEach(function(query) {
var plan = AQL_EXPLAIN(query).plan;
var nodeTypes = plan.nodes.map(function(node) {
return node.type;
});
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
var results = AQL_EXECUTE(query);
assertEqual(1, results.json.length);
assertEqual(2, results.json[0].value);
assertTrue(results.stats.scannedIndex > 0);
assertEqual(0, results.stats.scannedFull);
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test index usage
////////////////////////////////////////////////////////////////////////////////
testIndexAndSkiplistPartialIndexedNonIndexed : function () {
// drop the skiplist index
c.dropIndex(c.getIndexes()[1]);
// create an alternative index
c.ensureSkiplist("value", "value3");
// value2 is not indexed
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value } IN " + c.name());
var queries = [
"FOR i IN " + c.name() + " FILTER i.value == 2 FILTER i.value2 == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 2 && i.value2 == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 == 2 FILTER i.value == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 == 2 && i.value == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 2 FILTER i.value2 != 1 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 2 && i.value2 != 1 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 != 1 FILTER i.value == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 != 1 && i.value == 2 RETURN i"
];
queries.forEach(function(query) {
var plan = AQL_EXPLAIN(query).plan;
var nodeTypes = plan.nodes.map(function(node) {
return node.type;
});
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
var results = AQL_EXECUTE(query);
assertEqual(1, results.json.length);
assertEqual(2, results.json[0].value);
assertTrue(results.stats.scannedIndex > 0);
assertEqual(0, results.stats.scannedFull);
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test index usage
////////////////////////////////////////////////////////////////////////////////
testIndexAndAlternativeSkiplistPartialIndexedNonIndexed : function () {
// create a competitor index
c.ensureSkiplist("value", "value3");
// value2 is not indexed
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value } IN " + c.name());
var queries = [
"FOR i IN " + c.name() + " FILTER i.value == 2 FILTER i.value2 == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 2 && i.value2 == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 == 2 FILTER i.value == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 == 2 && i.value == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 2 FILTER i.value2 != 1 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value == 2 && i.value2 != 1 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 != 1 FILTER i.value == 2 RETURN i",
"FOR i IN " + c.name() + " FILTER i.value2 != 1 && i.value == 2 RETURN i"
];
queries.forEach(function(query) {
var plan = AQL_EXPLAIN(query).plan;
var nodeTypes = plan.nodes.map(function(node) {
return node.type;
});
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
var results = AQL_EXECUTE(query);
assertEqual(1, results.json.length);
assertEqual(2, results.json[0].value);
assertTrue(results.stats.scannedIndex > 0);
assertEqual(0, results.stats.scannedFull);
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test index usage
////////////////////////////////////////////////////////////////////////////////
testIndexNotNoIndex : function () {
c.ensureSkiplist("value2");
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value } IN " + c.name());
var queries = [
[ "FOR i IN " + c.name() + " FILTER NOT (i.value == 1 || i.value == 2) RETURN i", 1998 ],
[ "FOR i IN " + c.name() + " FILTER NOT (i.value == 1 && i.value == 2) RETURN i", 2000 ],
[ "FOR i IN " + c.name() + " FILTER i.value == 1 || NOT (i.value == -1) RETURN i", 2000 ]
];
queries.forEach(function(query) {
var plan = AQL_EXPLAIN(query[0]).plan;
var nodeTypes = plan.nodes.map(function(node) {
return node.type;
});
assertEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
var results = AQL_EXECUTE(query[0]);
assertEqual(query[1], results.json.length);
assertTrue(results.stats.scannedFull > 0);
assertEqual(0, results.stats.scannedIndex);
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test index usage
////////////////////////////////////////////////////////////////////////////////
testIndexNotUseIndex : function () {
c.ensureSkiplist("value2");
AQL_EXECUTE("FOR i IN " + c.name() + " UPDATE i WITH { value2: i.value } IN " + c.name());
var queries = [
[ "FOR i IN " + c.name() + " FILTER NOT (i.value == 2) && i.value == 1 RETURN i", 1 ],
[ "FOR i IN " + c.name() + " FILTER i.value == 1 && NOT (i.value == 2) RETURN i", 1 ],
[ "FOR i IN " + c.name() + " FILTER i.value == 1 && NOT (i.value != 1) RETURN i", 1 ],
[ "FOR i IN " + c.name() + " FILTER NOT (i.value != 1) && i.value == 1 RETURN i", 1 ],
[ "FOR i IN " + c.name() + " FILTER i.value == 1 || NOT (i.value != -1) RETURN i", 1 ]
];
queries.forEach(function(query) {
var plan = AQL_EXPLAIN(query[0]).plan;
var nodeTypes = plan.nodes.map(function(node) {
return node.type;
});
assertNotEqual(-1, nodeTypes.indexOf("IndexRangeNode"), query);
var results = AQL_EXECUTE(query[0]);
assertEqual(query[1], results.json.length);
assertTrue(results.stats.scannedIndex > 0);
assertEqual(0, results.stats.scannedFull);
});
}
}; };
} }