1
0
Fork 0

honor index hints in optimizer rule (#8940)

This commit is contained in:
Jan 2019-05-09 18:47:36 +02:00 committed by GitHub
parent c43ec49eae
commit c1d48c0fef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 83 additions and 9 deletions

View File

@ -28,6 +28,7 @@
#include "Aql/ExecutionNode.h"
#include "Aql/ExecutionPlan.h"
#include "Aql/Function.h"
#include "Aql/IndexHint.h"
#include "Aql/IndexNode.h"
#include "Aql/ModificationNodes.h"
#include "Aql/Optimizer.h"
@ -193,6 +194,7 @@ void RocksDBOptimizerRules::reduceExtractionToProjectionRule(
// we must never have a projection on _id, as producing _id is not supported yet
// by the primary index iterator
EnumerateCollectionNode const* en = ExecutionNode::castTo<EnumerateCollectionNode const*>(n);
auto const& hint = en->hint();
// now check all indexes if they cover the projection
auto trx = plan->getAst()->query()->trx();
@ -202,23 +204,39 @@ void RocksDBOptimizerRules::reduceExtractionToProjectionRule(
indexes = en->collection()->getCollection()->getIndexes();
}
for (auto const& idx : indexes) {
auto selectIndexIfPossible = [&picked, &attributes](std::shared_ptr<Index> const& idx) -> bool {
if (!idx->hasCoveringIterator() || !idx->covers(attributes)) {
// index doesn't cover the projection
continue;
return false;
}
if (idx->type() != arangodb::Index::IndexType::TRI_IDX_TYPE_PRIMARY_INDEX &&
idx->type() != arangodb::Index::IndexType::TRI_IDX_TYPE_HASH_INDEX &&
idx->type() != arangodb::Index::IndexType::TRI_IDX_TYPE_SKIPLIST_INDEX &&
idx->type() != arangodb::Index::IndexType::TRI_IDX_TYPE_PERSISTENT_INDEX) {
// only the above index types are supported
continue;
return false;
}
if (picked == nullptr ||
picked->fields().size() > idx->fields().size()) {
// found an index that would cover the projection
picked = idx;
picked = idx;
return true;
};
bool forced = false;
if (hint.type() == aql::IndexHint::HintType::Simple) {
forced = hint.isForced();
for (std::string const& hinted : hint.hint()) {
auto idx = en->collection()->getCollection()->lookupIndex(hinted);
if (idx && selectIndexIfPossible(idx)) {
break;
}
}
}
if (!picked && !forced) {
for (auto const& idx : indexes) {
if (selectIndexIfPossible(idx)) {
break;
}
}
}
@ -269,6 +287,7 @@ void RocksDBOptimizerRules::reduceExtractionToProjectionRule(
// primary index colum family. thus in disk-bound workloads scanning the
// documents via the primary index should be faster
EnumerateCollectionNode* en = ExecutionNode::castTo<EnumerateCollectionNode*>(n);
auto const& hint = en->hint();
auto trx = plan->getAst()->query()->trx();
std::shared_ptr<Index> picked;
@ -277,10 +296,30 @@ void RocksDBOptimizerRules::reduceExtractionToProjectionRule(
indexes = en->collection()->getCollection()->getIndexes();
}
for (auto const& idx : indexes) {
auto selectIndexIfPossible = [&picked](std::shared_ptr<Index> const& idx) -> bool {
if (idx->type() == arangodb::Index::IndexType::TRI_IDX_TYPE_PRIMARY_INDEX) {
picked = idx;
break;
return true;
}
return false;
};
bool forced = false;
if (hint.type() == aql::IndexHint::HintType::Simple) {
forced = hint.isForced();
for (std::string const& hinted : hint.hint()) {
auto idx = en->collection()->getCollection()->lookupIndex(hinted);
if (idx && selectIndexIfPossible(idx)) {
break;
}
}
}
if (!picked && !forced) {
for (auto const& idx : indexes) {
if (selectIndexIfPossible(idx)) {
break;
}
}
}

View File

@ -84,9 +84,32 @@ function optimizerRuleTestSuite () {
assertEqual(-1, result.plan.rules.indexOf(ruleName), query);
});
},
testNotActiveBecauseIndexHint : function () {
// these queries may actually use projections, but they must not use the primary
// index for scanning
var queries = [
"FOR doc IN @@cn OPTIONS { indexHint: 'aha', forceIndexHint: true } RETURN 1",
"FOR doc IN @@cn OPTIONS { indexHint: 'aha', forceIndexHint: true } RETURN doc",
"FOR doc IN @@cn OPTIONS { indexHint: 'aha', forceIndexHint: true } RETURN doc.value1",
"FOR doc IN @@cn OPTIONS { indexHint: 'aha', forceIndexHint: true } RETURN doc.value2",
"FOR doc IN @@cn OPTIONS { indexHint: 'aha', forceIndexHint: true } RETURN doc._key",
"FOR doc IN @@cn OPTIONS { indexHint: 'primary' } RETURN doc",
"FOR doc IN @@cn OPTIONS { indexHint: 'primary' } RETURN doc.value1",
"FOR doc IN @@cn OPTIONS { indexHint: 'primary' } RETURN doc.value2",
];
queries.forEach(function(query) {
var result = AQL_EXPLAIN(query, { "@cn" : cn }).plan;
let nodeTypes = result.nodes.map(function(node) { return node.type; });
assertEqual(-1, nodeTypes.indexOf("IndexNode"));
});
},
testActive : function () {
var queries = [
"FOR doc IN @@cn OPTIONS { indexHint: 'primary' } RETURN 1",
"FOR doc IN @@cn OPTIONS { indexHint: 'primary' } RETURN doc._key",
"FOR doc IN @@cn FILTER doc.value1 == 1 RETURN doc.value1",
"FOR doc IN @@cn FILTER doc.value1 == 1 RETURN doc.value2",
"FOR doc IN @@cn FILTER doc.value1 == 1 && doc.value2 == 1 && doc.value3 == 1 RETURN doc.value1",
@ -120,6 +143,18 @@ function optimizerRuleTestSuite () {
});
},
testActiveScanOnly : function () {
var queries = [
"FOR doc IN @@cn RETURN 1",
"FOR doc IN @@cn OPTIONS { indexHint: 'primary' } RETURN 1",
];
queries.forEach(function(query) {
var result = AQL_EXPLAIN(query, { "@cn" : cn });
assertNotEqual(-1, result.plan.rules.indexOf(ruleName), query);
});
},
testActiveWithIndex : function () {
c.ensureIndex({ type: "skiplist", fields: ["value1"] });