mirror of https://gitee.com/bigwinds/arangodb
honor index hints in optimizer rule (#8940)
This commit is contained in:
parent
c43ec49eae
commit
c1d48c0fef
|
@ -28,6 +28,7 @@
|
||||||
#include "Aql/ExecutionNode.h"
|
#include "Aql/ExecutionNode.h"
|
||||||
#include "Aql/ExecutionPlan.h"
|
#include "Aql/ExecutionPlan.h"
|
||||||
#include "Aql/Function.h"
|
#include "Aql/Function.h"
|
||||||
|
#include "Aql/IndexHint.h"
|
||||||
#include "Aql/IndexNode.h"
|
#include "Aql/IndexNode.h"
|
||||||
#include "Aql/ModificationNodes.h"
|
#include "Aql/ModificationNodes.h"
|
||||||
#include "Aql/Optimizer.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
|
// we must never have a projection on _id, as producing _id is not supported yet
|
||||||
// by the primary index iterator
|
// by the primary index iterator
|
||||||
EnumerateCollectionNode const* en = ExecutionNode::castTo<EnumerateCollectionNode const*>(n);
|
EnumerateCollectionNode const* en = ExecutionNode::castTo<EnumerateCollectionNode const*>(n);
|
||||||
|
auto const& hint = en->hint();
|
||||||
|
|
||||||
// now check all indexes if they cover the projection
|
// now check all indexes if they cover the projection
|
||||||
auto trx = plan->getAst()->query()->trx();
|
auto trx = plan->getAst()->query()->trx();
|
||||||
|
@ -202,23 +204,39 @@ void RocksDBOptimizerRules::reduceExtractionToProjectionRule(
|
||||||
indexes = en->collection()->getCollection()->getIndexes();
|
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)) {
|
if (!idx->hasCoveringIterator() || !idx->covers(attributes)) {
|
||||||
// index doesn't cover the projection
|
// index doesn't cover the projection
|
||||||
continue;
|
return false;
|
||||||
}
|
}
|
||||||
if (idx->type() != arangodb::Index::IndexType::TRI_IDX_TYPE_PRIMARY_INDEX &&
|
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_HASH_INDEX &&
|
||||||
idx->type() != arangodb::Index::IndexType::TRI_IDX_TYPE_SKIPLIST_INDEX &&
|
idx->type() != arangodb::Index::IndexType::TRI_IDX_TYPE_SKIPLIST_INDEX &&
|
||||||
idx->type() != arangodb::Index::IndexType::TRI_IDX_TYPE_PERSISTENT_INDEX) {
|
idx->type() != arangodb::Index::IndexType::TRI_IDX_TYPE_PERSISTENT_INDEX) {
|
||||||
// only the above index types are supported
|
// only the above index types are supported
|
||||||
continue;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (picked == nullptr ||
|
picked = idx;
|
||||||
picked->fields().size() > idx->fields().size()) {
|
return true;
|
||||||
// found an index that would cover the projection
|
};
|
||||||
picked = idx;
|
|
||||||
|
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
|
// primary index colum family. thus in disk-bound workloads scanning the
|
||||||
// documents via the primary index should be faster
|
// documents via the primary index should be faster
|
||||||
EnumerateCollectionNode* en = ExecutionNode::castTo<EnumerateCollectionNode*>(n);
|
EnumerateCollectionNode* en = ExecutionNode::castTo<EnumerateCollectionNode*>(n);
|
||||||
|
auto const& hint = en->hint();
|
||||||
|
|
||||||
auto trx = plan->getAst()->query()->trx();
|
auto trx = plan->getAst()->query()->trx();
|
||||||
std::shared_ptr<Index> picked;
|
std::shared_ptr<Index> picked;
|
||||||
|
@ -277,10 +296,30 @@ void RocksDBOptimizerRules::reduceExtractionToProjectionRule(
|
||||||
indexes = en->collection()->getCollection()->getIndexes();
|
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) {
|
if (idx->type() == arangodb::Index::IndexType::TRI_IDX_TYPE_PRIMARY_INDEX) {
|
||||||
picked = idx;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,9 +84,32 @@ function optimizerRuleTestSuite () {
|
||||||
assertEqual(-1, result.plan.rules.indexOf(ruleName), query);
|
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 () {
|
testActive : function () {
|
||||||
var queries = [
|
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.value1",
|
||||||
"FOR doc IN @@cn FILTER doc.value1 == 1 RETURN doc.value2",
|
"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",
|
"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 () {
|
testActiveWithIndex : function () {
|
||||||
c.ensureIndex({ type: "skiplist", fields: ["value1"] });
|
c.ensureIndex({ type: "skiplist", fields: ["value1"] });
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue