1
0
Fork 0

Revert "Feature/optimizer rule remove filter covered by traversal" (#2724)

* Revert "Feature/authorization query cache (#2720)"

This reverts commit 83712b7b4a.

* Revert "properly return index in case of unique constraint violation (#2716)"

This reverts commit c3f346e0a5.

* Revert "fix https://github.com/arangodb/planning/issues/388 (#2714)"

This reverts commit 1d944b97a4.

* Revert "fix typo (#2718)"

This reverts commit 61a80ed697.

* Revert "Feature/optimizer rule remove filter covered by traversal (#2676)"

This reverts commit c54b81fb69.
This commit is contained in:
Frank Celler 2017-07-04 11:13:36 +02:00 committed by GitHub
parent 83712b7b4a
commit 6b47544611
17 changed files with 155 additions and 689 deletions

View File

@ -29,10 +29,10 @@
#include "Aql/Query.h"
#include "Aql/Scopes.h"
#include "Aql/types.h"
#include "Basics/fasthash.h"
#include "Basics/StringBuffer.h"
#include "Basics/Utf8Helper.h"
#include "Basics/VelocyPackHelper.h"
#include "Basics/fasthash.h"
#include "Transaction/Methods.h"
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
@ -50,11 +50,11 @@ using namespace arangodb::aql;
/// @brief quick translation array from an AST node value type to a VPack type
static std::array<VPackValueType, 5> const ValueTypes{{
VPackValueType::Null, // VALUE_TYPE_NULL = 0,
VPackValueType::Bool, // VALUE_TYPE_BOOL = 1,
VPackValueType::Int, // VALUE_TYPE_INT = 2,
VPackValueType::Double, // VALUE_TYPE_DOUBLE = 3,
VPackValueType::String // VALUE_TYPE_STRING = 4
VPackValueType::Null, // VALUE_TYPE_NULL = 0,
VPackValueType::Bool, // VALUE_TYPE_BOOL = 1,
VPackValueType::Int, // VALUE_TYPE_INT = 2,
VPackValueType::Double, // VALUE_TYPE_DOUBLE = 3,
VPackValueType::String // VALUE_TYPE_STRING = 4
}};
static_assert(AstNodeValueType::VALUE_TYPE_NULL == 0,
@ -207,6 +207,7 @@ static VPackValueType GetNodeCompareType(AstNode const* node) {
// return null in case assertions are turned off
return VPackValueType::Null;
}
}
/// @brief compare two nodes
@ -236,16 +237,17 @@ int arangodb::aql::CompareAstNodes(AstNode const* lhs, AstNode const* rhs,
if (diff < 0) {
return -1;
}
}
return 1;
}
switch (lType) {
case VPackValueType::Null: {
case VPackValueType::Null: {
return 0;
}
case VPackValueType::Bool: {
case VPackValueType::Bool: {
int diff = static_cast<int>(lhs->getIntValue() - rhs->getIntValue());
if (diff != 0) {
@ -257,8 +259,8 @@ int arangodb::aql::CompareAstNodes(AstNode const* lhs, AstNode const* rhs,
return 0;
}
case VPackValueType::Int:
case VPackValueType::Double: {
case VPackValueType::Int:
case VPackValueType::Double: {
// TODO
double d = lhs->getDoubleValue() - rhs->getDoubleValue();
if (d != 0.0) {
@ -270,17 +272,16 @@ int arangodb::aql::CompareAstNodes(AstNode const* lhs, AstNode const* rhs,
return 0;
}
case VPackValueType::String: {
case VPackValueType::String: {
if (compareUtf8) {
int res =
TRI_compare_utf8(lhs->getStringValue(), lhs->getStringLength(),
rhs->getStringValue(), rhs->getStringLength());
int res = TRI_compare_utf8(lhs->getStringValue(), lhs->getStringLength(),
rhs->getStringValue(), rhs->getStringLength());
if (res != 0) {
return res < 0 ? -1 : 1;
}
return 0;
}
size_t const minLength =
(std::min)(lhs->getStringLength(), rhs->getStringLength());
@ -291,7 +292,7 @@ int arangodb::aql::CompareAstNodes(AstNode const* lhs, AstNode const* rhs,
}
int diff = static_cast<int>(lhs->getStringLength()) -
static_cast<int>(rhs->getStringLength());
static_cast<int>(rhs->getStringLength());
if (diff != 0) {
return diff < 0 ? -1 : 1;
@ -299,14 +300,14 @@ int arangodb::aql::CompareAstNodes(AstNode const* lhs, AstNode const* rhs,
return 0;
}
case VPackValueType::Array: {
case VPackValueType::Array: {
size_t const numLhs = lhs->numMembers();
size_t const numRhs = rhs->numMembers();
size_t const n = ((numLhs > numRhs) ? numRhs : numLhs);
for (size_t i = 0; i < n; ++i) {
int res = arangodb::aql::CompareAstNodes(
lhs->getMember(i), rhs->getMember(i), compareUtf8);
int res = arangodb::aql::CompareAstNodes(lhs->getMember(i),
rhs->getMember(i), compareUtf8);
if (res != 0) {
return res;
@ -320,7 +321,7 @@ int arangodb::aql::CompareAstNodes(AstNode const* lhs, AstNode const* rhs,
return 0;
}
case VPackValueType::Object: {
case VPackValueType::Object: {
// this is a rather exceptional case, so we can
// afford the inefficiency to convert the node to VPack
// for comparison (this saves us from writing our own compare function
@ -328,8 +329,7 @@ int arangodb::aql::CompareAstNodes(AstNode const* lhs, AstNode const* rhs,
auto l = lhs->toVelocyPackValue();
auto r = rhs->toVelocyPackValue();
return basics::VelocyPackHelper::compare(l->slice(), r->slice(),
compareUtf8);
return basics::VelocyPackHelper::compare(l->slice(), r->slice(), compareUtf8);
}
default: {
@ -465,8 +465,7 @@ AstNode::AstNode(Ast* ast, arangodb::velocypack::Slice const& slice)
break;
}
case NODE_TYPE_FCALL: {
setData(
query->executor()->getFunctionByName(slice.get("name").copyString()));
setData(query->executor()->getFunctionByName(slice.get("name").copyString()));
break;
}
case NODE_TYPE_OBJECT_ELEMENT: {
@ -481,8 +480,8 @@ AstNode::AstNode(Ast* ast, arangodb::velocypack::Slice const& slice)
break;
}
case NODE_TYPE_OPERATOR_BINARY_IN:
case NODE_TYPE_OPERATOR_BINARY_NIN:
case NODE_TYPE_OPERATOR_BINARY_ARRAY_IN:
case NODE_TYPE_OPERATOR_BINARY_NIN:
case NODE_TYPE_OPERATOR_BINARY_ARRAY_IN:
case NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN: {
bool sorted = false;
VPackSlice v = slice.get("sorted");
@ -542,7 +541,7 @@ AstNode::AstNode(Ast* ast, arangodb::velocypack::Slice const& slice)
case NODE_TYPE_OPERATOR_BINARY_ARRAY_LT:
case NODE_TYPE_OPERATOR_BINARY_ARRAY_LE:
case NODE_TYPE_OPERATOR_BINARY_ARRAY_GT:
case NODE_TYPE_OPERATOR_BINARY_ARRAY_GE:
case NODE_TYPE_OPERATOR_BINARY_ARRAY_GE:
case NODE_TYPE_OPERATOR_TERNARY:
case NODE_TYPE_SUBQUERY:
case NODE_TYPE_BOUND_ATTRIBUTE_ACCESS:
@ -594,10 +593,10 @@ AstNode::AstNode(Ast* ast, arangodb::velocypack::Slice const& slice)
ast->query()->addNode(this);
}
/// @brief create the node
/// @brief create the node
AstNode::AstNode(std::function<void(AstNode*)> registerNode,
std::function<char const*(std::string const&)> registerString,
arangodb::velocypack::Slice const& slice)
arangodb::velocypack::Slice const& slice)
: AstNode(getNodeTypeFromVPack(slice)) {
TRI_ASSERT(flags == 0);
TRI_ASSERT(computedValue == nullptr);
@ -739,13 +738,12 @@ AstNode::~AstNode() {
delete[] computedValue;
}
}
/// @brief return the string value of a node, as an std::string
std::string AstNode::getString() const {
TRI_ASSERT(type == NODE_TYPE_VALUE || type == NODE_TYPE_OBJECT_ELEMENT ||
type == NODE_TYPE_ATTRIBUTE_ACCESS ||
type == NODE_TYPE_PARAMETER || type == NODE_TYPE_COLLECTION ||
type == NODE_TYPE_BOUND_ATTRIBUTE_ACCESS ||
TRI_ASSERT(type == NODE_TYPE_VALUE || type == NODE_TYPE_OBJECT_ELEMENT ||
type == NODE_TYPE_ATTRIBUTE_ACCESS || type == NODE_TYPE_PARAMETER ||
type == NODE_TYPE_COLLECTION || type == NODE_TYPE_BOUND_ATTRIBUTE_ACCESS ||
type == NODE_TYPE_FCALL_USER);
TRI_ASSERT(value.type == VALUE_TYPE_STRING);
return std::string(getStringValue(), getStringLength());
@ -926,8 +924,7 @@ void AstNode::validateValueType(int type) {
}
/// @brief fetch a node's type from VPack
AstNodeType AstNode::getNodeTypeFromVPack(
arangodb::velocypack::Slice const& slice) {
AstNodeType AstNode::getNodeTypeFromVPack(arangodb::velocypack::Slice const& slice) {
int type = slice.get("typeID").getNumericValue<int>();
validateType(type);
return static_cast<AstNodeType>(type);
@ -962,8 +959,7 @@ void AstNode::toVelocyPackValue(VPackBuilder& builder) const {
builder.add(VPackValue(value.value._double));
break;
case VALUE_TYPE_STRING:
builder.add(VPackValuePair(value.value._string, value.length,
VPackValueType::String));
builder.add(VPackValuePair(value.value._string, value.length, VPackValueType::String));
break;
}
return;
@ -990,9 +986,7 @@ void AstNode::toVelocyPackValue(VPackBuilder& builder) const {
for (size_t i = 0; i < n; ++i) {
auto member = getMemberUnchecked(i);
if (member != nullptr) {
builder.add(VPackValuePair(member->getStringValue(),
member->getStringLength(),
VPackValueType::String));
builder.add(VPackValuePair(member->getStringValue(), member->getStringLength(), VPackValueType::String));
member->getMember(0)->toVelocyPackValue(builder);
}
}
@ -1043,8 +1037,7 @@ void AstNode::toVelocyPack(VPackBuilder& builder, bool verbose) const {
type == NODE_TYPE_ATTRIBUTE_ACCESS ||
type == NODE_TYPE_OBJECT_ELEMENT || type == NODE_TYPE_FCALL_USER) {
// dump "name" of node
builder.add("name", VPackValuePair(getStringValue(), getStringLength(),
VPackValueType::String));
builder.add("name", VPackValuePair(getStringValue(), getStringLength(), VPackValueType::String));
}
if (type == NODE_TYPE_FCALL) {
auto func = static_cast<Function*>(getData());
@ -1056,7 +1049,7 @@ void AstNode::toVelocyPack(VPackBuilder& builder, bool verbose) const {
// transport information about a node's sortedness
builder.add("sorted", VPackValue(hasFlag(VALUE_SORTED)));
}
if (type == NODE_TYPE_VALUE) {
// dump value of "value" node
builder.add(VPackValue("value"));
@ -1338,7 +1331,7 @@ bool AstNode::isFalse() const {
/// of attribute names in the parameter passed by reference
bool AstNode::isAttributeAccessForVariable(
std::pair<Variable const*, std::vector<arangodb::basics::AttributeName>>&
result, bool allowIndexedAccess) const {
result) const {
if (type != NODE_TYPE_ATTRIBUTE_ACCESS && type != NODE_TYPE_EXPANSION) {
return false;
}
@ -1350,30 +1343,13 @@ bool AstNode::isAttributeAccessForVariable(
result.second.clear();
}
auto node = this;
basics::StringBuffer indexBuff(TRI_UNKNOWN_MEM_ZONE, false);
while (node->type == NODE_TYPE_ATTRIBUTE_ACCESS ||
node->type == NODE_TYPE_INDEXED_ACCESS ||
node->type == NODE_TYPE_EXPANSION) {
if (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
arangodb::basics::AttributeName attr(node->getString(), expandNext);
if (indexBuff.length() > 0) {
attr.name.append(indexBuff.c_str(), indexBuff.length());
indexBuff.clear();
}
result.second.insert(result.second.begin(), std::move(attr));
node = node->getMember(0);
expandNext = false;
} else if (node->type == NODE_TYPE_INDEXED_ACCESS) {
TRI_ASSERT(node->numMembers() == 2);
AstNode* val = node->getMemberUnchecked(1);
if (!allowIndexedAccess || val->type != NODE_TYPE_VALUE) {
break;// also exclude all non trivial index accesses
}
indexBuff.appendChar('[');
node->getMember(1)->stringify(&indexBuff, false, false);
indexBuff.appendChar(']');
result.second.insert(
result.second.begin(),
arangodb::basics::AttributeName(node->getString(), expandNext));
node = node->getMember(0);
expandNext = false;
} else {
@ -1390,7 +1366,7 @@ bool AstNode::isAttributeAccessForVariable(
}
if (node->getMember(1)->type != NODE_TYPE_REFERENCE) {
if (!node->getMember(1)->isAttributeAccessForVariable(result, allowIndexedAccess)) {
if (!node->getMember(1)->isAttributeAccessForVariable(result)) {
result.second.clear();
return false;
}
@ -1439,8 +1415,7 @@ bool AstNode::isSimple() const {
if (type == NODE_TYPE_ARRAY || type == NODE_TYPE_OBJECT ||
type == NODE_TYPE_EXPANSION || type == NODE_TYPE_ITERATOR ||
type == NODE_TYPE_ARRAY_LIMIT ||
type == NODE_TYPE_CALCULATED_OBJECT_ELEMENT ||
type == NODE_TYPE_ARRAY_LIMIT || type == NODE_TYPE_CALCULATED_OBJECT_ELEMENT ||
type == NODE_TYPE_OPERATOR_TERNARY) {
size_t const n = numMembers();
@ -1458,8 +1433,7 @@ bool AstNode::isSimple() const {
}
if (type == NODE_TYPE_OBJECT_ELEMENT || type == NODE_TYPE_ATTRIBUTE_ACCESS ||
type == NODE_TYPE_OPERATOR_UNARY_NOT ||
type == NODE_TYPE_OPERATOR_UNARY_PLUS ||
type == NODE_TYPE_OPERATOR_UNARY_NOT || type == NODE_TYPE_OPERATOR_UNARY_PLUS ||
type == NODE_TYPE_OPERATOR_UNARY_MINUS) {
TRI_ASSERT(numMembers() == 1);
@ -1553,7 +1527,7 @@ bool AstNode::isSimple() const {
type == NODE_TYPE_OPERATOR_BINARY_GT ||
type == NODE_TYPE_OPERATOR_BINARY_GE ||
type == NODE_TYPE_OPERATOR_BINARY_IN ||
type == NODE_TYPE_OPERATOR_BINARY_NIN ||
type == NODE_TYPE_OPERATOR_BINARY_NIN ||
type == NODE_TYPE_OPERATOR_BINARY_ARRAY_EQ ||
type == NODE_TYPE_OPERATOR_BINARY_ARRAY_NE ||
type == NODE_TYPE_OPERATOR_BINARY_ARRAY_LT ||
@ -1561,7 +1535,8 @@ bool AstNode::isSimple() const {
type == NODE_TYPE_OPERATOR_BINARY_ARRAY_GT ||
type == NODE_TYPE_OPERATOR_BINARY_ARRAY_GE ||
type == NODE_TYPE_OPERATOR_BINARY_ARRAY_IN ||
type == NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN || type == NODE_TYPE_RANGE ||
type == NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN ||
type == NODE_TYPE_RANGE ||
type == NODE_TYPE_INDEXED_ACCESS || type == NODE_TYPE_PASSTHRU) {
// a logical operator is simple if its operands are simple
// a comparison operator is simple if both bounds are simple
@ -1575,8 +1550,7 @@ bool AstNode::isSimple() const {
return true;
}
if (type == NODE_TYPE_OPERATOR_NARY_AND ||
type == NODE_TYPE_OPERATOR_NARY_OR) {
if (type == NODE_TYPE_OPERATOR_NARY_AND || type == NODE_TYPE_OPERATOR_NARY_OR) {
// a logical operator is simple if all its operands are simple
for (auto const& it : members) {
if (!it->isSimple()) {
@ -1817,7 +1791,7 @@ bool AstNode::mustCheckUniqueness() const {
}
}
}
if (mustCheck) {
// we have duplicate keys, or we can't tell yet
setFlag(DETERMINED_CHECKUNIQUENESS, VALUE_CHECKUNIQUENESS);
@ -2403,7 +2377,7 @@ AstNode const* AstNode::findReference(AstNode const* findme) const {
}
if (member != nullptr) {
ret = member->findReference(findme);
if (ret != nullptr) {
if (ret != nullptr) {
return ret;
}
}
@ -2580,8 +2554,7 @@ void AstNode::appendValue(arangodb::basics::StringBuffer* buffer) const {
case VALUE_TYPE_DOUBLE: {
double const v = value.value._double;
if (std::isnan(v) || !std::isfinite(v) || v == HUGE_VAL ||
v == -HUGE_VAL) {
if (std::isnan(v) || !std::isfinite(v) || v == HUGE_VAL || v == -HUGE_VAL) {
buffer->appendText(TRI_CHAR_LENGTH_PAIR("null"));
} else {
buffer->appendDecimal(value.value._double);
@ -2628,6 +2601,7 @@ void AstNode::removeMembersInOtherAndNode(AstNode const* other) {
}
}
}
}
/// @brief append the AstNode to an output stream

View File

@ -430,8 +430,7 @@ struct AstNode {
/// returns true if yes, and then also returns variable reference and array
/// of attribute names in the parameter passed by reference
bool isAttributeAccessForVariable(
std::pair<Variable const*, std::vector<arangodb::basics::AttributeName>>&,
bool allowIndexedAccess = false)
std::pair<Variable const*, std::vector<arangodb::basics::AttributeName>>&)
const;
/// @brief locate a variable including the direct path vector leading to it.

View File

@ -26,7 +26,6 @@
#include "Aql/AstNode.h"
#include "Aql/Collection.h"
#include "Aql/ExecutionPlan.h"
#include "Aql/Quantifier.h"
#include "Aql/Query.h"
#include "Aql/SortCondition.h"
#include "Aql/Variable.h"
@ -262,34 +261,11 @@ bool ConditionPart::isCoveredBy(ConditionPart const& other,
operatorType == NODE_TYPE_OPERATOR_BINARY_IN &&
other.operatorType == NODE_TYPE_OPERATOR_BINARY_IN &&
other.valueNode->isConstant()) {
return CompareAstNodes(other.valueNode, valueNode, false) == 0;
}
bool a = operatorNode->isArrayComparisonOperator();
bool b = other.operatorNode->isArrayComparisonOperator();
if (a || b) {
if (a != b) {
return false;
}
TRI_ASSERT(operatorNode->numMembers() == 3 &&
other.operatorNode->numMembers() == 3);
AstNode* q1 = operatorNode->getMemberUnchecked(2);
TRI_ASSERT(q1->type == NODE_TYPE_QUANTIFIER);
AstNode* q2 = other.operatorNode->getMemberUnchecked(2);
TRI_ASSERT(q2->type == NODE_TYPE_QUANTIFIER);
// do only cover ALL and NONE when both sides have same quantifier
if (q1->getIntValue() != q2->getIntValue() ||
q1->getIntValue() == Quantifier::ANY) {
return false;
}
if (isExpanded && other.isExpanded &&
operatorType == NODE_TYPE_OPERATOR_BINARY_ARRAY_IN &&
other.operatorType == NODE_TYPE_OPERATOR_BINARY_ARRAY_IN &&
other.valueNode->isConstant()) {
return CompareAstNodes(other.valueNode, valueNode, false) == 0;
if (CompareAstNodes(other.valueNode, valueNode, false) == 0) {
return true;
}
return false;
}
// Results are -1, 0, 1, move to 0, 1, 2 for the lookup:
@ -503,12 +479,9 @@ void Condition::normalize() {
#endif
}
void Condition::CollectOverlappingMembers(ExecutionPlan const* plan,
Variable const* variable,
AstNode* andNode,
AstNode* otherAndNode,
std::unordered_set<size_t>& toRemove,
bool isFromTraverser) {
void Condition::CollectOverlappingMembers(
ExecutionPlan const* plan, Variable const* variable, AstNode* andNode,
AstNode* otherAndNode, std::unordered_set<size_t>& toRemove) {
std::pair<Variable const*, std::vector<arangodb::basics::AttributeName>>
result;
@ -516,28 +489,22 @@ void Condition::CollectOverlappingMembers(ExecutionPlan const* plan,
for (size_t i = 0; i < n; ++i) {
auto operand = andNode->getMemberUnchecked(i);
bool allowOps = operand->isComparisonOperator();
if (isFromTraverser) {
allowOps = allowOps || operand->isArrayComparisonOperator();
} else {
allowOps = allowOps && operand->type != NODE_TYPE_OPERATOR_BINARY_NE &&
operand->type != NODE_TYPE_OPERATOR_BINARY_NIN;
}
if (allowOps) {
if (operand->isComparisonOperator() &&
operand->type != NODE_TYPE_OPERATOR_BINARY_NE &&
operand->type != NODE_TYPE_OPERATOR_BINARY_NIN) {
auto lhs = operand->getMember(0);
auto rhs = operand->getMember(1);
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS ||
(isFromTraverser && lhs->type == NODE_TYPE_EXPANSION)) {
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
clearAttributeAccess(result);
if (lhs->isAttributeAccessForVariable(result, isFromTraverser) &&
if (lhs->isAttributeAccessForVariable(result) &&
result.first == variable) {
ConditionPart current(variable, result.second, operand,
ATTRIBUTE_LEFT, nullptr);
if (CanRemove(plan, current, otherAndNode, isFromTraverser)) {
if (CanRemove(plan, current, otherAndNode)) {
toRemove.emplace(i);
}
}
@ -547,12 +514,12 @@ void Condition::CollectOverlappingMembers(ExecutionPlan const* plan,
rhs->type == NODE_TYPE_EXPANSION) {
clearAttributeAccess(result);
if (rhs->isAttributeAccessForVariable(result, isFromTraverser) &&
if (rhs->isAttributeAccessForVariable(result) &&
result.first == variable) {
ConditionPart current(variable, result.second, operand,
ATTRIBUTE_RIGHT, nullptr);
if (CanRemove(plan, current, otherAndNode, isFromTraverser)) {
if (CanRemove(plan, current, otherAndNode)) {
toRemove.emplace(i);
}
}
@ -586,8 +553,8 @@ AstNode* Condition::removeIndexCondition(ExecutionPlan const* plan,
size_t const n = andNode->numMembers();
std::unordered_set<size_t> toRemove;
CollectOverlappingMembers(plan, variable, andNode, otherAndNode, toRemove,
false);
CollectOverlappingMembers(plan, variable, andNode, otherAndNode, toRemove);
if (toRemove.empty()) {
return _root;
@ -614,56 +581,6 @@ AstNode* Condition::removeIndexCondition(ExecutionPlan const* plan,
return newNode;
}
/// @brief remove filter conditions already covered by the traversal
AstNode* Condition::removeTraversalCondition(ExecutionPlan const* plan,
Variable const* variable,
AstNode* other) {
if (_root == nullptr || other == nullptr) {
return _root;
}
TRI_ASSERT(_root != nullptr);
TRI_ASSERT(_root->type == NODE_TYPE_OPERATOR_NARY_OR);
TRI_ASSERT(other != nullptr);
TRI_ASSERT(other->type == NODE_TYPE_OPERATOR_NARY_OR);
if (other->numMembers() != 1 && _root->numMembers() != 1) {
return _root;
}
auto andNode = _root->getMemberUnchecked(0);
TRI_ASSERT(andNode->type == NODE_TYPE_OPERATOR_NARY_AND);
auto otherAndNode = other->getMemberUnchecked(0);
TRI_ASSERT(otherAndNode->type == NODE_TYPE_OPERATOR_NARY_AND);
size_t const n = andNode->numMembers();
std::unordered_set<size_t> toRemove;
CollectOverlappingMembers(plan, variable, andNode, otherAndNode, toRemove,
true);
if (toRemove.empty()) {
return _root;
}
// build a new AST condition
AstNode* newNode = nullptr;
for (size_t i = 0; i < n; ++i) {
if (toRemove.find(i) == toRemove.end()) {
auto what = andNode->getMemberUnchecked(i);
if (newNode == nullptr) {
// the only node so far
newNode = what;
} else {
// AND-combine with existing node
newNode = _ast->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_AND,
newNode, what);
}
}
}
return newNode;
}
/// @brief remove (now) invalid variables from the condition
bool Condition::removeInvalidVariables(
std::unordered_set<Variable const*> const& validVars) {
@ -1079,8 +996,7 @@ void Condition::validateAst(AstNode const* node, int level) {
/// @brief checks if the current condition is covered by the other
bool Condition::CanRemove(ExecutionPlan const* plan, ConditionPart const& me,
arangodb::aql::AstNode const* andNode,
bool isFromTraverser) {
arangodb::aql::AstNode const* andNode) {
TRI_ASSERT(andNode != nullptr);
TRI_ASSERT(andNode->type == NODE_TYPE_OPERATOR_NARY_AND);
@ -1108,16 +1024,14 @@ bool Condition::CanRemove(ExecutionPlan const* plan, ConditionPart const& me,
for (size_t i = 0; i < n; ++i) {
auto operand = andNode->getMemberUnchecked(i);
if (operand->isComparisonOperator() ||
(isFromTraverser && operand->isArrayComparisonOperator())) {
if (operand->isComparisonOperator()) {
auto lhs = operand->getMember(0);
auto rhs = operand->getMember(1);
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS ||
(isFromTraverser && lhs->type == NODE_TYPE_EXPANSION)) {
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
clearAttributeAccess(result);
if (lhs->isAttributeAccessForVariable(result, isFromTraverser)) {
if (lhs->isAttributeAccessForVariable(result)) {
if (rhs->isConstant()) {
ConditionPart indexCondition(result.first, result.second, operand,
ATTRIBUTE_LEFT, nullptr);
@ -1138,7 +1052,7 @@ bool Condition::CanRemove(ExecutionPlan const* plan, ConditionPart const& me,
rhs->type == NODE_TYPE_EXPANSION) {
clearAttributeAccess(result);
if (rhs->isAttributeAccessForVariable(result, isFromTraverser)) {
if (rhs->isAttributeAccessForVariable(result)) {
if (lhs->isConstant()) {
ConditionPart indexCondition(result.first, result.second, operand,
ATTRIBUTE_RIGHT, nullptr);

View File

@ -70,22 +70,16 @@ struct ConditionPart {
inline int whichCompareOperation() const {
switch (operatorType) {
case NODE_TYPE_OPERATOR_BINARY_EQ:
case NODE_TYPE_OPERATOR_BINARY_ARRAY_EQ:
return 0;
case NODE_TYPE_OPERATOR_BINARY_NE:
case NODE_TYPE_OPERATOR_BINARY_ARRAY_NE:
return 1;
case NODE_TYPE_OPERATOR_BINARY_LT:
case NODE_TYPE_OPERATOR_BINARY_ARRAY_LT:
return 2;
case NODE_TYPE_OPERATOR_BINARY_LE:
case NODE_TYPE_OPERATOR_BINARY_ARRAY_LE:
return 3;
case NODE_TYPE_OPERATOR_BINARY_GE:
case NODE_TYPE_OPERATOR_BINARY_ARRAY_GE:
return 4;
case NODE_TYPE_OPERATOR_BINARY_GT:
case NODE_TYPE_OPERATOR_BINARY_ARRAY_GT:
return 5;
default:
return 6; // not a compare operator.
@ -186,8 +180,7 @@ class Condition {
public:
static void CollectOverlappingMembers(
ExecutionPlan const* plan, Variable const* variable, AstNode* andNode,
AstNode* otherAndNode, std::unordered_set<size_t>& toRemove,
bool isFromTraverser);
AstNode* otherAndNode, std::unordered_set<size_t>& toRemove);
/// @brief return the condition root
inline AstNode* root() const { return _root; }
@ -230,9 +223,6 @@ class Condition {
/// @brief removes condition parts from another
AstNode* removeIndexCondition(ExecutionPlan const*, Variable const*, AstNode*);
/// @brief removes condition parts from another
AstNode* removeTraversalCondition(ExecutionPlan const*, Variable const*, AstNode*);
/// @brief remove (now) invalid variables from the condition
bool removeInvalidVariables(std::unordered_set<Variable const*> const&);
@ -269,8 +259,7 @@ class Condition {
#endif
/// @brief checks if the current condition covers the other
static bool CanRemove(ExecutionPlan const*, ConditionPart const&, AstNode const*,
bool isFromTraverser);
static bool CanRemove(ExecutionPlan const*, ConditionPart const&, AstNode const*);
/// @brief deduplicate IN condition values
/// this may modify the node in place

View File

@ -1180,13 +1180,13 @@ AqlValue Expression::executeSimpleExpressionArrayComparison(
size_t const n = left.length();
if (n == 0) {
if (Quantifier::IsAllOrNone(node->getMember(2))) {
if (Quantifier::IsAllOrAny(node->getMember(2))) {
// [] ALL ...
// [] ANY ...
return AqlValue(false);
} else {
// [] NONE ...
return AqlValue(true);
} else {
// [] ANY ...
return AqlValue(false);
}
}

View File

@ -151,16 +151,11 @@ struct OptimizerRule {
// sort values used in IN comparisons of remaining filters
sortInValuesRule_pass6,
// remove calculations that are never necessary
removeUnnecessaryCalculationsRule_pass6,
// merge filters into graph traversals
optimizeTraversalsRule_pass6,
// remove redundant filters statements
removeFiltersCoveredByTraversal_pass6,
// remove calculations that are redundant
// needs to run after filter removal
removeUnnecessaryCalculationsRule_pass6,
// remove now obsolete path variables
removeTraversalPathVariable_pass6,
prepareTraversalsRule_pass6,
/// Pass 9: push down calculations beyond FILTERs and LIMITs

View File

@ -3714,144 +3714,6 @@ void arangodb::aql::optimizeTraversalsRule(Optimizer* opt,
opt->addPlan(std::move(plan), rule, modified);
}
// remove filter nodes already covered by a traversal
void arangodb::aql::removeFiltersCoveredByTraversal(Optimizer* opt, std::unique_ptr<ExecutionPlan> plan,
OptimizerRule const* rule) {
SmallVector<ExecutionNode*>::allocator_type::arena_type a;
SmallVector<ExecutionNode*> fNodes{a};
plan->findNodesOfType(fNodes, EN::FILTER, true);
if (fNodes.empty()) {
// no filters present
opt->addPlan(std::move(plan), rule, false);
return;
}
bool modified = false;
std::unordered_set<ExecutionNode*> toUnlink;
for (auto const& node : fNodes) {
auto fn = static_cast<FilterNode const*>(node);
// find the node with the filter expression
auto inVar = fn->getVariablesUsedHere();
TRI_ASSERT(inVar.size() == 1);
auto setter = plan->getVarSetBy(inVar[0]->id);
if (setter == nullptr || setter->getType() != EN::CALCULATION) {
continue;
}
auto calculationNode = static_cast<CalculationNode*>(setter);
auto conditionNode = calculationNode->expression()->node();
// build the filter condition
auto condition = std::make_unique<Condition>(plan->getAst());
condition->andCombine(conditionNode);
condition->normalize(plan.get());
if (condition->root() == nullptr) {
continue;
}
size_t const n = condition->root()->numMembers();
if (n != 1) {
// either no condition or multiple ORed conditions...
continue;
}
bool handled = false;
auto current = node;
while (current != nullptr) {
if (current->getType() == EN::TRAVERSAL) {
auto traversalNode = static_cast<TraversalNode const*>(current);
// found a traversal node, now check if the expression
// is covered by the traversal
auto traversalCondition = traversalNode->condition();
if (traversalCondition != nullptr && !traversalCondition->isEmpty()) {
/*auto const& indexesUsed = traversalNode->get //indexNode->getIndexes();
if (indexesUsed.size() == 1) {*/
// single index. this is something that we can handle
Variable const* outVariable = traversalNode->pathOutVariable();
std::unordered_set<Variable const*> varsUsedByCondition;
Ast::getReferencedVariables(condition->root(), varsUsedByCondition);
if (outVariable != nullptr &&
varsUsedByCondition.find(outVariable) != varsUsedByCondition.end()) {
auto newNode = condition->removeTraversalCondition(plan.get(), outVariable, traversalCondition->root());
if (newNode == nullptr) {
// no condition left...
// FILTER node can be completely removed
toUnlink.emplace(node);
// note: we must leave the calculation node intact, in case it is
// still used by other nodes in the plan
modified = true;
handled = true;
} else if (newNode != condition->root()) {
// some condition is left, but it is a different one than
// the one from the FILTER node
auto expr = std::make_unique<Expression>(plan->getAst(), newNode);
CalculationNode* cn =
new CalculationNode(plan.get(), plan->nextId(), expr.get(),
calculationNode->outVariable());
expr.release();
plan->registerNode(cn);
plan->replaceNode(setter, cn);
modified = true;
handled = true;
}
}
}
if (handled) {
break;
}
}
if (handled || current->getType() == EN::LIMIT ||
!current->hasDependency()) {
break;
}
current = current->getFirstDependency();
}
}
if (!toUnlink.empty()) {
plan->unlinkNodes(toUnlink);
}
opt->addPlan(std::move(plan), rule, modified);
}
/// @brief removes redundant path variables, after applying
/// `removeFiltersCoveredByTraversal`. Should significantly reduce overhead
void arangodb::aql::removeTraversalPathVariable(Optimizer* opt,
std::unique_ptr<ExecutionPlan> plan,
OptimizerRule const* rule) {
SmallVector<ExecutionNode*>::allocator_type::arena_type a;
SmallVector<ExecutionNode*> tNodes{a};
plan->findNodesOfType(tNodes, EN::TRAVERSAL, true);
bool modified = false;
// first make a pass over all traversal nodes and remove unused
// variables from them
for (auto const& n : tNodes) {
TraversalNode* traversal = static_cast<TraversalNode*>(n);
auto varsUsedLater = n->getVarsUsedLater();
auto outVariable = traversal->pathOutVariable();
if (outVariable != nullptr &&
varsUsedLater.find(outVariable) == varsUsedLater.end()) {
// traversal path outVariable not used later
traversal->setPathOutput(nullptr);
modified = true;
}
}
opt->addPlan(std::move(plan), rule, modified);
}
/// @brief prepares traversals for execution (hidden rule)
void arangodb::aql::prepareTraversalsRule(Optimizer* opt,
std::unique_ptr<ExecutionPlan> plan,

View File

@ -191,15 +191,6 @@ void patchUpdateStatementsRule(Optimizer*, std::unique_ptr<ExecutionPlan>,
/// merges filter nodes into graph traversal nodes
void optimizeTraversalsRule(Optimizer* opt, std::unique_ptr<ExecutionPlan> plan,
OptimizerRule const* rule);
/// @brief removes filter nodes already covered by the traversal and removes unused variables
void removeFiltersCoveredByTraversal(Optimizer* opt, std::unique_ptr<ExecutionPlan> plan,
OptimizerRule const* rule);
/// @brief removes redundant path variables, after applying
/// `removeFiltersCoveredByTraversal`. Should significantly reduce overhead
void removeTraversalPathVariable(Optimizer* opt, std::unique_ptr<ExecutionPlan> plan,
OptimizerRule const* rule);
/// @brief prepares traversals for execution (hidden rule)
void prepareTraversalsRule(Optimizer* opt, std::unique_ptr<ExecutionPlan> plan,

View File

@ -154,15 +154,6 @@ void OptimizerRulesFeature::addRules() {
// merge filters into traversals
registerRule("optimize-traversals", optimizeTraversalsRule,
OptimizerRule::optimizeTraversalsRule_pass6, DoesNotCreateAdditionalPlans, CanBeDisabled);
// optimize unneccessary filters already applied by the traversal
registerRule("remove-filters-covered-by-traversal", removeFiltersCoveredByTraversal,
OptimizerRule::removeFiltersCoveredByTraversal_pass6, DoesNotCreateAdditionalPlans, CanBeDisabled);
// optimize unneccessary filters already applied by the traversal. Only ever does something if previous
// rule remove all filters using the path variable
registerRule("remove-redundant-path-var", removeTraversalPathVariable,
OptimizerRule::removeTraversalPathVariable_pass6, DoesNotCreateAdditionalPlans, CanBeDisabled);
// prepare traversal info
registerRule("prepare-traversals", prepareTraversalsRule,

View File

@ -59,12 +59,12 @@ std::string Quantifier::Stringify(int64_t value) {
return "none";
}
bool Quantifier::IsAllOrNone(AstNode const* quantifier) {
bool Quantifier::IsAllOrAny(AstNode const* quantifier) {
TRI_ASSERT(quantifier != nullptr);
if (quantifier->type == NODE_TYPE_QUANTIFIER) {
auto const value = quantifier->getIntValue(true);
return (value == Quantifier::ALL || value == Quantifier::NONE);
return (value == Quantifier::ALL || value == Quantifier::ANY);
}
return false;
}

View File

@ -42,7 +42,7 @@ struct Quantifier {
/// @brief converts a quantifier int value into its string equivalent
static std::string Stringify(int64_t value);
static bool IsAllOrNone(AstNode const* quantifier);
static bool IsAllOrAny(AstNode const* quantifier);
/// @brief determine the min/max number of matches for an array comparison
static std::pair<size_t, size_t> RequiredMatches(size_t inputSize, AstNode const* quantifier);

View File

@ -26,7 +26,6 @@
#include "Aql/ExecutionPlan.h"
#include "Aql/Quantifier.h"
#include "Aql/TraversalNode.h"
#include "VocBase/TraverserOptions.h"
using namespace arangodb::aql;
using EN = arangodb::aql::ExecutionNode;
@ -218,8 +217,7 @@ static bool IsSupportedNode(Variable const* pathVar, AstNode const* node) {
static bool checkPathVariableAccessFeasible(Ast* ast, AstNode* parent,
size_t testIndex, TraversalNode* tn,
Variable const* pathVar,
bool& conditionIsImpossible,
int64_t& indexedAccessDepth) {
bool& conditionIsImpossible) {
AstNode* node = parent->getMemberUnchecked(testIndex);
if (!IsSupportedNode(pathVar, node)) {
return false;
@ -257,8 +255,7 @@ static bool checkPathVariableAccessFeasible(Ast* ast, AstNode* parent,
};
auto searchPattern = [&patternStep, &isEdge, &depth, &pathVar, &notSupported,
&parentOfReplace, &replaceIdx,
&indexedAccessDepth](AstNode* node, void* unused) -> AstNode* {
&parentOfReplace, &replaceIdx](AstNode* node, void* unused) -> AstNode* {
if (notSupported) {
// Short circuit, this condition cannot be fulfilled.
return node;
@ -323,16 +320,6 @@ static bool checkPathVariableAccessFeasible(Ast* ast, AstNode* parent,
// Search for the parent having this node.
patternStep = 6;
parentOfReplace = node;
// we need to know the depth at which a filter condition will
// access a path. Otherwise there are too many results
TRI_ASSERT(node->numMembers() == 2);
AstNode* indexVal = node->getMemberUnchecked(1);
if (indexVal->isIntValue()) {
indexedAccessDepth = indexVal->getIntValue() + (isEdge?1:0);
} else { // should cause the caller to not remove a filter
indexedAccessDepth = INT64_MAX;
}
return node;
}
if (node->type == NODE_TYPE_EXPANSION) {
@ -545,7 +532,7 @@ bool TraversalConditionFinder::before(ExecutionNode* en) {
// No condition, no optimize
break;
}
auto options = static_cast<traverser::TraverserOptions*>(node->options());
auto const& varsValidInTraversal = node->getVarsValid();
bool conditionIsImpossible = false;
@ -579,8 +566,7 @@ bool TraversalConditionFinder::before(ExecutionNode* en) {
TRI_ASSERT(andNode->type == NODE_TYPE_OPERATOR_NARY_AND);
std::unordered_set<Variable const*> varsUsedByCondition;
auto originalFilterConditions = std::make_unique<Condition>(_plan->getAst());
for (size_t i = andNode->numMembers(); i > 0; --i) {
// Whenever we do not support a of the condition we have to throw it out
@ -618,14 +604,10 @@ bool TraversalConditionFinder::before(ExecutionNode* en) {
continue;
}
AstNode* cloned = andNode->getMember(i - 1)->clone(_plan->getAst());
int64_t indexedAccessDepth = -1;
// If we get here we can optimize this condition
if (!checkPathVariableAccessFeasible(_plan->getAst(), andNode, i - 1,
node, pathVar,
conditionIsImpossible,
indexedAccessDepth)) {
conditionIsImpossible)) {
andNode->removeMemberUnchecked(i - 1);
if (conditionIsImpossible) {
// If we get here we cannot fulfill the condition
@ -633,23 +615,6 @@ bool TraversalConditionFinder::before(ExecutionNode* en) {
andNode->clearMembers();
break;
}
} else {
TRI_ASSERT(!conditionIsImpossible);
// remember the original filter conditions if we can remove them later
if (indexedAccessDepth == -1) {
originalFilterConditions->andCombine(cloned);
} else if ((uint64_t)indexedAccessDepth <= options->maxDepth) {
// if we had an index access then indexedAccessDepth
// is in [0..maxDepth], if the depth is not a concrete value
// then indexedAccessDepth would be INT64_MAX
originalFilterConditions->andCombine(cloned);
// do not return paths shorter than the deepest path access
if ((int64_t)options->minDepth < indexedAccessDepth) {
options->minDepth = indexedAccessDepth;
}
} // otherwise do not remove the filter statement
}
}
@ -685,9 +650,7 @@ bool TraversalConditionFinder::before(ExecutionNode* en) {
}
if (!isEmpty) {
//node->setCondition(_condition.release());
originalFilterConditions->normalize();
node->setCondition(originalFilterConditions.release());
node->setCondition(_condition.release());
// We restart here with an empty condition.
// All Filters that have been collected thus far
// depend on sth issued by this traverser or later

View File

@ -272,7 +272,7 @@ void BaseOptions::injectLookupInfoInList(std::vector<LookupInfo>& list,
// It is sufficient to only check member one.
// We build the condition this way.
auto mem = eq->getMemberUnchecked(0);
if (mem->isAttributeAccessForVariable(pathCmp, true)) {
if (mem->isAttributeAccessForVariable(pathCmp)) {
if (pathCmp.first != _tmpVar) {
continue;
}
@ -286,7 +286,7 @@ void BaseOptions::injectLookupInfoInList(std::vector<LookupInfo>& list,
}
}
std::unordered_set<size_t> toRemove;
aql::Condition::CollectOverlappingMembers(plan, _tmpVar, condition, info.indexCondition, toRemove, false);
aql::Condition::CollectOverlappingMembers(plan, _tmpVar, condition, info.indexCondition, toRemove);
size_t n = condition->numMembers();
if (n == toRemove.size()) {
// FastPath, all covered.

View File

@ -1307,7 +1307,7 @@ function RELATIONAL_ARRAY_FUNC (lhs, rhs, quantifier, func) {
} else if (quantifier === 2) {
// ALL
if (n === 0) {
return true;
return false;
}
min = max = n;
} else if (quantifier === 3) {

View File

@ -1990,9 +1990,9 @@ function complexFilteringSuite () {
// 2 Edge Lookups (2 B) (0 D)
// 2 Primary Lookups (C, F)
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 9);
assertEqual(stats.scannedIndex, 9);
} else {
assertTrue(stats.scannedIndex <= 5);
assertEqual(stats.scannedIndex, 5);
}
}
else {
@ -2007,14 +2007,11 @@ function complexFilteringSuite () {
// assertEqual(stats.scannedIndex, 9);
// Without traverser-read-cache
assertTrue(stats.scannedIndex <= 17);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 17);
} else {
assertEqual(stats.scannedIndex, 13);
}
*/
}
// 1 Filter On D
assertEqual(stats.filtered, 1);
@ -2042,9 +2039,9 @@ function complexFilteringSuite () {
// 2 Primary lookup B,D
// 4 Primary Lookups (C, F, E, G)
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 13);
assertEqual(stats.scannedIndex, 13);
} else {
assertTrue(stats.scannedIndex <= 7);
assertEqual(stats.scannedIndex, 7);
}
}
else {
@ -2059,18 +2056,15 @@ function complexFilteringSuite () {
// assertEqual(stats.scannedIndex, 13);
// With traverser-read-cache
assertTrue(stats.scannedIndex <= 24);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 24);
} else {
assertEqual(stats.scannedIndex, 18);
}
*/
}
// 2 Filter (B, C) too short
// 2 Filter (E, G)
assertTrue(stats.filtered <= 4);
assertEqual(stats.filtered, 4);
},
testVertexLevelsCombined: function () {
@ -2095,9 +2089,9 @@ function complexFilteringSuite () {
// 2 Edge Lookups (0 B) (2 D)
// 2 Primary Lookups (E, G)
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 9);
assertEqual(stats.scannedIndex, 9);
} else {
assertTrue(stats.scannedIndex <= 5);
assertEqual(stats.scannedIndex, 5);
}
}
else {
@ -2110,18 +2104,15 @@ function complexFilteringSuite () {
// assertEqual(stats.scannedIndex, 9);
// Without traverser-read-cache
assertTrue(stats.scannedIndex <= 11);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 11);
} else {
assertEqual(stats.scannedIndex, 7);
}
*/
}
// 2 Filter (B, D) too short
// 2 Filter (E, G)
assertTrue(stats.filtered <= 4);
assertEqual(stats.filtered, 4);
},
testEdgeLevel0: function () {
@ -2145,9 +2136,9 @@ function complexFilteringSuite () {
// 2 Edge
// 2 Primary (C,F)
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 8);
assertEqual(stats.scannedIndex, 8);
} else {
assertTrue(stats.scannedIndex <= 4);
assertEqual(stats.scannedIndex, 4);
}
}
else {
@ -2160,14 +2151,11 @@ function complexFilteringSuite () {
// assertEqual(stats.scannedIndex, 8);
// Without traverser-read-cache
assertTrue(stats.scannedIndex <= 15);
/*
if (mmfilesEngine){
assertEqual(stats.scannedIndex, 15);
} else {
assertEqual(stats.scannedIndex, 11);
}
*/
}
// 1 Filter (A->D)
assertEqual(stats.filtered, 1);
@ -2197,9 +2185,9 @@ function complexFilteringSuite () {
// lazy loads all vertices in it.
if (stats.scannedIndex !== 8) {
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 11);
assertEqual(stats.scannedIndex, 11);
} else {
assertTrue(stats.scannedIndex <= 5);
assertEqual(stats.scannedIndex, 5);
}
}
}
@ -2214,18 +2202,15 @@ function complexFilteringSuite () {
// assertEqual(stats.scannedIndex, 11);
// Without traverser-read-cache
assertTrue(stats.scannedIndex <= 20);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 20);
} else {
assertEqual(stats.scannedIndex, 14);
}
*/
}
// 2 Filter On (B, D) too short
// 2 Filter On (D->E, D->G)
assertTrue(stats.filtered <= 4);
assertEqual(stats.filtered, 4);
},
testVertexLevel1Less: function () {
@ -2261,11 +2246,9 @@ function complexFilteringSuite () {
// 2 Edge Lookups (2 B) (0 D)
// 2 Primary Lookups (C, F)
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 9);
assertEqual(stats.scannedIndex, 9);
} else {
// FIXME this used to be 5 ??
assertTrue(stats.scannedIndex <= 5, stats.scannedIndex);
//assertEqual(stats.scannedIndex <= 5);
assertEqual(stats.scannedIndex, 5);
}
}
else {
@ -2283,14 +2266,11 @@ function complexFilteringSuite () {
// assertEqual(stats.scannedIndex, 9);
// Without traverser-read-cache
assertTrue(stats.scannedIndex <= 17);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 17);
} else {
assertEqual(stats.scannedIndex, 13);
}
*/
}
// 1 Filter On D
assertEqual(stats.filtered, 1);
@ -2330,9 +2310,9 @@ function complexFilteringSuite () {
// 2 Edge Lookups (2 B) (0 D)
// 2 Primary Lookups (C, F)
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 9);
assertEqual(stats.scannedIndex, 9);
} else {
assertTrue(stats.scannedIndex <= 5);
assertEqual(stats.scannedIndex, 5);
}
}
else {
@ -2350,14 +2330,11 @@ function complexFilteringSuite () {
// assertEqual(stats.scannedIndex, 9);
// Without traverser-read-cache
assertTrue(stats.scannedIndex <= 17);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 17);
} else {
assertEqual(stats.scannedIndex, 13);
}
*/
}
// 1 Filter On D
assertEqual(stats.filtered, 1);
@ -3014,9 +2991,9 @@ function optimizeQuantifierSuite() {
assertEqual(stats.scannedFull, 0);
if (isCluster) {
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 9);
assertEqual(stats.scannedIndex, 9);
} else {
assertTrue(stats.scannedIndex <= 5);
assertEqual(stats.scannedIndex, 5);
}
} else {
// With traverser-read-cache
@ -3025,14 +3002,11 @@ function optimizeQuantifierSuite() {
// Without traverser-read-cache
// TODO Check for Optimization
// assertEqual(stats.scannedIndex, 23);
assertTrue(stats.scannedIndex <= 22);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 22);
} else {
assertEqual(stats.scannedIndex, 18);
}
*/
}
assertEqual(stats.filtered, 1);
@ -3059,17 +3033,17 @@ function optimizeQuantifierSuite() {
RETURN v._id
`;
let cursor = db._query(query);
assertEqual(cursor.count(), 4);
assertEqual(cursor.count(), 3);
let result = cursor.toArray();
assertEqual(result, [vertices.A, vertices.B, vertices.C, vertices.D]);
assertEqual(result, [vertices.B, vertices.C, vertices.D]);
let stats = cursor.getExtra().stats;
assertEqual(stats.scannedFull, 0);
if (isCluster) {
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 8);
assertEqual(stats.scannedIndex, 8);
} else {
assertTrue(stats.scannedIndex <= 4);
assertEqual(stats.scannedIndex, 4);
}
} else {
// With traverser-read-cache
@ -3078,16 +3052,13 @@ function optimizeQuantifierSuite() {
// TODO Check for Optimization
// Without traverser-read-cache
// assertEqual(stats.scannedIndex, 18);
assertTrue(stats.scannedIndex <= 17);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 17);
} else {
assertEqual(stats.scannedIndex, 13);
}
*/
}
assertTrue(stats.filtered <= 2);
assertEqual(stats.filtered, 2);
query = `
FOR v, e, p IN 0..2 OUTBOUND "${vertices.A}" GRAPH "${gn}"
@ -3096,17 +3067,17 @@ function optimizeQuantifierSuite() {
RETURN v._id
`;
cursor = db._query(query);
assertEqual(cursor.count(), 4);
assertEqual(cursor.count(), 3);
result = cursor.toArray();
assertEqual(result, [vertices.A, vertices.E, vertices.F, vertices.G]);
assertEqual(result, [vertices.E, vertices.F, vertices.G]);
stats = cursor.getExtra().stats;
assertEqual(stats.scannedFull, 0);
if (isCluster) {
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 8);
assertEqual(stats.scannedIndex, 8);
} else {
assertTrue(stats.scannedIndex <= 4);
assertEqual(stats.scannedIndex, 4);
}
} else {
// With traverser-read-cache
@ -3114,16 +3085,13 @@ function optimizeQuantifierSuite() {
// Without traverser-read-cache
// assertEqual(stats.scannedIndex, 18);
assertTrue(stats.scannedIndex <= 17);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 17);
} else {
assertEqual(stats.scannedIndex, 13);
}
*/
}
assertTrue(stats.filtered <= 2);
assertEqual(stats.filtered, 2);
},
testNoneVerticesSingle: function () {
@ -3142,9 +3110,9 @@ function optimizeQuantifierSuite() {
assertEqual(stats.scannedFull, 0);
if (isCluster) {
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 9);
assertEqual(stats.scannedIndex, 9);
} else {
assertTrue(stats.scannedIndex <= 5);
assertEqual(stats.scannedIndex, 5);
}
} else {
// With traverser-read-cache
@ -3153,14 +3121,11 @@ function optimizeQuantifierSuite() {
// Without traverser-read-cache
// TODO Check for Optimization
// assertEqual(stats.scannedIndex, 23);
assertTrue(stats.scannedIndex <= 22);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 22);
} else {
assertEqual(stats.scannedIndex, 18);
}
*/
}
assertEqual(stats.filtered, 1);
@ -3195,9 +3160,9 @@ function optimizeQuantifierSuite() {
assertEqual(stats.scannedFull, 0);
if (isCluster) {
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 8);
assertEqual(stats.scannedIndex, 8);
} else {
assertTrue(stats.scannedIndex <= 4);
assertEqual(stats.scannedIndex, 4);
}
} else {
// With traverser-read-cache
@ -3205,14 +3170,11 @@ function optimizeQuantifierSuite() {
// Without traverser-read-cache
// assertEqual(stats.scannedIndex, 18);
assertTrue(stats.scannedIndex <= 17);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 17);
} else {
assertEqual(stats.scannedIndex, 13);
}
*/
}
assertEqual(stats.filtered, 1);
@ -3231,9 +3193,9 @@ function optimizeQuantifierSuite() {
assertEqual(stats.scannedFull, 0);
if (isCluster) {
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 8);
assertEqual(stats.scannedIndex, 8);
} else {
assertTrue(stats.scannedIndex <= 4);
assertEqual(stats.scannedIndex, 4);
}
} else {
// With traverser-read-cache
@ -3241,14 +3203,11 @@ function optimizeQuantifierSuite() {
// Without traverser-read-cache
// TODO Check for Optimization
assertTrue(stats.scannedIndex <= 17);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 17);
} else {
assertEqual(stats.scannedIndex, 13);
}
*/
}
assertEqual(stats.filtered, 1);
},
@ -3270,9 +3229,9 @@ function optimizeQuantifierSuite() {
assertEqual(stats.scannedFull, 0);
if (isCluster) {
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 9);
assertEqual(stats.scannedIndex, 9);
} else {
assertTrue(stats.scannedIndex <= 5);
assertEqual(stats.scannedIndex, 5);
}
} else {
// With traverser-read-cache
@ -3280,14 +3239,11 @@ function optimizeQuantifierSuite() {
// Without traverser-read-cache
// assertEqual(stats.scannedIndex, 17);
assertTrue(stats.scannedIndex <= 18);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 18);
} else {
assertEqual(stats.scannedIndex, 14);
}
*/
}
assertEqual(stats.filtered, 2);
},
@ -3301,17 +3257,17 @@ function optimizeQuantifierSuite() {
RETURN v._id
`;
let cursor = db._query(query);
assertEqual(cursor.count(), 3);
assertEqual(cursor.count(), 2);
let result = cursor.toArray();
assertEqual(result, [vertices.A, vertices.B, vertices.C]);
assertEqual(result, [vertices.B, vertices.C]);
let stats = cursor.getExtra().stats;
assertEqual(stats.scannedFull, 0);
if (isCluster) {
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 7);
assertEqual(stats.scannedIndex, 7);
} else {
assertTrue(stats.scannedIndex <= 3);
assertEqual(stats.scannedIndex, 3);
}
} else {
// With activated traverser-read-cache:
@ -3319,16 +3275,13 @@ function optimizeQuantifierSuite() {
// Without traverser-read-cache
// assertEqual(stats.scannedIndex, 12);
assertTrue(stats.scannedIndex <= 13);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 13);
} else {
assertEqual(stats.scannedIndex, 9);
}
*/
}
assertTrue(stats.filtered <= 3);
assertEqual(stats.filtered, 3);
},
testAllNoneVerticesMultiple: function () {
@ -3348,9 +3301,9 @@ function optimizeQuantifierSuite() {
assertEqual(stats.scannedFull, 0);
if (isCluster) {
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 9);
assertEqual(stats.scannedIndex, 9);
} else {
assertTrue(stats.scannedIndex <= 5);
assertEqual(stats.scannedIndex, 5);
}
} else {
// With traverser-read-cache
@ -3359,14 +3312,11 @@ function optimizeQuantifierSuite() {
// Without traverser-read-cache
// TODO Check for Optimization
// assertEqual(stats.scannedIndex, 17);
assertTrue(stats.scannedIndex <= 18);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 18);
} else {
assertEqual(stats.scannedIndex, 14);
}
*/
}
assertEqual(stats.filtered, 2);
},
@ -3380,17 +3330,17 @@ function optimizeQuantifierSuite() {
RETURN v._id
`;
let cursor = db._query(query);
assertEqual(cursor.count(), 3);
assertEqual(cursor.count(), 2);
let result = cursor.toArray();
assertEqual(result, [vertices.A, vertices.B, vertices.C]);
assertEqual(result, [vertices.B, vertices.C]);
let stats = cursor.getExtra().stats;
assertEqual(stats.scannedFull, 0);
if (isCluster) {
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 7);
assertEqual(stats.scannedIndex, 7);
} else {
assertTrue(stats.scannedIndex <= 3);
assertEqual(stats.scannedIndex, 3);
}
} else {
// With activated traverser-read-cache:
@ -3399,16 +3349,13 @@ function optimizeQuantifierSuite() {
// Without traverser-read-cache
// TODO Check for Optimization
// assertEqual(stats.scannedIndex, 12);
assertTrue(stats.scannedIndex <= 13);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 13);
} else {
assertEqual(stats.scannedIndex, 9);
}
*/
}
assertTrue(stats.filtered <= 3);
assertEqual(stats.filtered, 3);
},
testAllVerticesDepth: function () {
@ -3428,9 +3375,9 @@ function optimizeQuantifierSuite() {
assertEqual(stats.scannedFull, 0);
if (isCluster) {
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 9);
assertEqual(stats.scannedIndex, 9);
} else {
assertTrue(stats.scannedIndex <= 5);
assertEqual(stats.scannedIndex, 5);
}
} else {
// With activated traverser-read-cache:
@ -3439,16 +3386,13 @@ function optimizeQuantifierSuite() {
// Without traverser-read-cache
// TODO Check for Optimization
// assertEqual(stats.scannedIndex, 17);
assertTrue(stats.scannedIndex <= 18);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 18);
} else {
assertEqual(stats.scannedIndex, 14);
}
*/
}
assertTrue(stats.filtered <= 4);
assertEqual(stats.filtered, 4);
},
testAllEdgesAndDepth: function () {
@ -3468,9 +3412,9 @@ function optimizeQuantifierSuite() {
assertEqual(stats.scannedFull, 0);
if (isCluster) {
if (mmfilesEngine) {
assertTrue(stats.scannedIndex <= 7);
assertEqual(stats.scannedIndex, 7);
} else {
assertTrue(stats.scannedIndex <= 3);
assertEqual(stats.scannedIndex, 3);
}
} else {
// With activated traverser-read-cache:
@ -3479,16 +3423,13 @@ function optimizeQuantifierSuite() {
// Without traverser-read-cache
// TODO Check for Optimization
// assertEqual(stats.scannedIndex, 12);
assertTrue(stats.scannedIndex <= 13);
/*
if(mmfilesEngine){
assertEqual(stats.scannedIndex, 13);
} else {
assertEqual(stats.scannedIndex, 9);
}
*/
}
assertTrue(stats.filtered <= 4);
assertEqual(stats.filtered, 4);
}
};
};

View File

@ -56,13 +56,13 @@ function optimizerQuantifiersTestSuite () {
var query = "[] ALL == '1'", result;
result = AQL_EXECUTE("RETURN (" + query + ")").json[0];
assertEqual(true, result);
assertEqual(false, result);
result = AQL_EXECUTE("RETURN NOOPT(" + query + ")").json[0];
assertEqual(true, result);
assertEqual(false, result);
result = AQL_EXECUTE("RETURN V8(" + query + ")").json[0];
assertEqual(true, result);
assertEqual(false, result);
},
testAnyEmpty : function () {

View File

@ -1,153 +0,0 @@
/* global describe, it, before, after, AQL_EXPLAIN*/
'use strict';
////////////////////////////////////////////////////////////////////////////////
/// @brief tests for optimizer rules
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2017 ArangoDB 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 ArangoDB GmbH, Cologne, Germany
///
/// @author Michael Hackstein
/// @author Copyright 2017, ArangoDB GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
const expect = require('chai').expect;
const db = require('internal').db;
const OptimizeTraversalRule = "optimize-traversals";
const FilterRemoveRule = "remove-filter-covered-by-traversal";
const deactivateOptimizer = { optimizer: { rules: [ "-all" ] } };
const activateOptimizer = { optimizer: { rules: [ "+all" ] } };
const helper = require("@arangodb/aql-helper");
const findExecutionNodes = helper.findExecutionNodes;
describe('Single Traversal Optimizer', function () {
const vertexCollection = 'UnitTestsOptimizerVertices';
const edgeCollection = 'UnitTestsOptimizerEdges';
const startId = `${vertexCollection}/start`;
const bindVars = {
"@vertices": vertexCollection,
"@edges": edgeCollection,
start: startId
};
const dropCollections = () => {
db._drop(vertexCollection);
db._drop(edgeCollection);
};
const hasNoFilterNode = (plan) => {
expect(findExecutionNodes(plan, "FilterNode").length).to.equal(0, "Plan should have no filter node");
};
const validateResult = (query, bindVars) => {
let resultOpt = db._query(query, bindVars, activateOptimizer).toArray().sort();
let resultNoOpt = db._query(query, bindVars, deactivateOptimizer).toArray().sort();
expect(resultOpt).to.deep.equal(resultNoOpt);
};
before(function () {
dropCollections();
let v = db._create(vertexCollection);
let e = db._createEdgeCollection(edgeCollection);
let vertices = [
{_key: "start"}
];
let edges = [
];
for (let i = 0; i < 10; ++i) {
vertices.push({_key: `a${i}`, foo: i});
edges.push({_from: startId, _to: `${vertexCollection}/a${i}`, foo: i});
for (let j = 0; j < 10; ++j) {
vertices.push({_key: `a${i}${j}`, foo: j});
edges.push({_from: `${vertexCollection}/a${i}`, _to: `${vertexCollection}/a${i}${j}`, foo: j});
}
}
v.save(vertices);
e.save(edges);
});
after(dropCollections);
describe('should remove a single', () => {
describe('equality filter', () => {
it('on p.vertices[1].foo', () => {
let query = `WITH @@vertices
FOR v, e, p IN 2 OUTBOUND @start @@edges
FILTER p.vertices[1].foo == 3
RETURN v`;
let plan = AQL_EXPLAIN(query, bindVars, activateOptimizer);
hasNoFilterNode(plan);
validateResult(query, bindVars);
});
it('on p.vertices[2]', () => {
let query = `WITH @@vertices
FOR v, e, p IN 2 OUTBOUND @start @@edges
FILTER p.vertices[2].foo == 3
RETURN v`;
let plan = AQL_EXPLAIN(query, bindVars, activateOptimizer);
hasNoFilterNode(plan);
validateResult(query, bindVars);
});
it('on p.vertices[*] ALL', () => {
let query = `WITH @@vertices
FOR v, e, p IN 2 OUTBOUND @start @@edges
FILTER p.vertices[*].foo ALL == 3
RETURN v`;
let plan = AQL_EXPLAIN(query, bindVars, activateOptimizer);
hasNoFilterNode(plan);
validateResult(query, bindVars);
});
it('on p.edges[*] ALL', () => {
let query = `WITH @@vertices
FOR v, e, p IN 2 ANY @start @@edges
FILTER p.edges[*].foo ALL == 3
RETURN v`;
let plan = AQL_EXPLAIN(query, bindVars, activateOptimizer);
hasNoFilterNode(plan);
validateResult(query, bindVars);
});
it('on p.edges[*] ALL AND p.vertices[*] NONE', () => {
let query = `WITH @@vertices
FOR v, e, p IN 0..2 ANY @start @@edges
FILTER p.edges[*].foo ALL <= 5
FILTER p.vertices[*].foo NONE > 6
RETURN v`;
let plan = AQL_EXPLAIN(query, bindVars, activateOptimizer);
hasNoFilterNode(plan);
validateResult(query, bindVars);
});
});
});
});