mirror of https://gitee.com/bigwinds/arangodb
Revert "Feature/optimizer rule remove filter covered by traversal" (#2724)
* Revert "Feature/authorization query cache (#2720)" This reverts commit83712b7b4a
. * Revert "properly return index in case of unique constraint violation (#2716)" This reverts commitc3f346e0a5
. * Revert "fix https://github.com/arangodb/planning/issues/388 (#2714)" This reverts commit1d944b97a4
. * Revert "fix typo (#2718)" This reverts commit61a80ed697
. * Revert "Feature/optimizer rule remove filter covered by traversal (#2676)" This reverts commitc54b81fb69
.
This commit is contained in:
parent
83712b7b4a
commit
6b47544611
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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, ¬Supported,
|
||||
&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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
Loading…
Reference in New Issue