mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'aql-feature-lazy-index' of https://github.com/triAGENS/ArangoDB into devel
This commit is contained in:
commit
5fda62b725
|
@ -557,11 +557,12 @@ SHELL_SERVER_AQL = @top_srcdir@/js/server/tests/aql-arithmetic.js \
|
||||||
@top_srcdir@/js/server/tests/aql-optimizer-rule-move-calculations-up.js \
|
@top_srcdir@/js/server/tests/aql-optimizer-rule-move-calculations-up.js \
|
||||||
@top_srcdir@/js/server/tests/aql-optimizer-rule-move-filters-up.js \
|
@top_srcdir@/js/server/tests/aql-optimizer-rule-move-filters-up.js \
|
||||||
@top_srcdir@/js/server/tests/aql-optimizer-rule-remove-redundant-calculations.js \
|
@top_srcdir@/js/server/tests/aql-optimizer-rule-remove-redundant-calculations.js \
|
||||||
|
@top_srcdir@/js/server/tests/aql-optimizer-rule-remove-redundant-or.js \
|
||||||
@top_srcdir@/js/server/tests/aql-optimizer-rule-remove-redundant-sorts.js \
|
@top_srcdir@/js/server/tests/aql-optimizer-rule-remove-redundant-sorts.js \
|
||||||
@top_srcdir@/js/server/tests/aql-optimizer-rule-remove-unnecessary-calculations.js \
|
@top_srcdir@/js/server/tests/aql-optimizer-rule-remove-unnecessary-calculations.js \
|
||||||
@top_srcdir@/js/server/tests/aql-optimizer-rule-remove-unnecessary-filters.js \
|
@top_srcdir@/js/server/tests/aql-optimizer-rule-remove-unnecessary-filters.js \
|
||||||
@top_srcdir@/js/server/tests/aql-optimizer-rule-use-index-for-sort.js \
|
|
||||||
@top_srcdir@/js/server/tests/aql-optimizer-rule-replace-or-with-in.js \
|
@top_srcdir@/js/server/tests/aql-optimizer-rule-replace-or-with-in.js \
|
||||||
|
@top_srcdir@/js/server/tests/aql-optimizer-rule-use-index-for-sort.js \
|
||||||
@top_srcdir@/js/server/tests/aql-parse.js \
|
@top_srcdir@/js/server/tests/aql-parse.js \
|
||||||
@top_srcdir@/js/server/tests/aql-primary-index-noncluster.js \
|
@top_srcdir@/js/server/tests/aql-primary-index-noncluster.js \
|
||||||
@top_srcdir@/js/server/tests/aql-queries-collection.js \
|
@top_srcdir@/js/server/tests/aql-queries-collection.js \
|
||||||
|
|
|
@ -81,7 +81,7 @@ AstNode const Ast::EmptyStringNode{ "", VALUE_TYPE_STRING };
|
||||||
/// @brief inverse comparison operators
|
/// @brief inverse comparison operators
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
std::unordered_map<int, AstNodeType> const Ast::ReverseOperators{
|
std::unordered_map<int, AstNodeType> const Ast::NegatedOperators{
|
||||||
{ static_cast<int>(NODE_TYPE_OPERATOR_BINARY_EQ), NODE_TYPE_OPERATOR_BINARY_NE },
|
{ static_cast<int>(NODE_TYPE_OPERATOR_BINARY_EQ), NODE_TYPE_OPERATOR_BINARY_NE },
|
||||||
{ static_cast<int>(NODE_TYPE_OPERATOR_BINARY_NE), NODE_TYPE_OPERATOR_BINARY_EQ },
|
{ static_cast<int>(NODE_TYPE_OPERATOR_BINARY_NE), NODE_TYPE_OPERATOR_BINARY_EQ },
|
||||||
{ static_cast<int>(NODE_TYPE_OPERATOR_BINARY_GT), NODE_TYPE_OPERATOR_BINARY_LE },
|
{ static_cast<int>(NODE_TYPE_OPERATOR_BINARY_GT), NODE_TYPE_OPERATOR_BINARY_LE },
|
||||||
|
@ -92,6 +92,18 @@ std::unordered_map<int, AstNodeType> const Ast::ReverseOperators{
|
||||||
{ static_cast<int>(NODE_TYPE_OPERATOR_BINARY_NIN), NODE_TYPE_OPERATOR_BINARY_IN }
|
{ static_cast<int>(NODE_TYPE_OPERATOR_BINARY_NIN), NODE_TYPE_OPERATOR_BINARY_IN }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief reverse comparison operators
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
std::unordered_map<int, AstNodeType> const Ast::ReversedOperators{
|
||||||
|
{ static_cast<int>(NODE_TYPE_OPERATOR_BINARY_EQ), NODE_TYPE_OPERATOR_BINARY_EQ },
|
||||||
|
{ static_cast<int>(NODE_TYPE_OPERATOR_BINARY_GT), NODE_TYPE_OPERATOR_BINARY_LT },
|
||||||
|
{ static_cast<int>(NODE_TYPE_OPERATOR_BINARY_GE), NODE_TYPE_OPERATOR_BINARY_LE },
|
||||||
|
{ static_cast<int>(NODE_TYPE_OPERATOR_BINARY_LT), NODE_TYPE_OPERATOR_BINARY_GT },
|
||||||
|
{ static_cast<int>(NODE_TYPE_OPERATOR_BINARY_LE), NODE_TYPE_OPERATOR_BINARY_GE }
|
||||||
|
};
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// --SECTION-- constructors / destructors
|
// --SECTION-- constructors / destructors
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -1088,6 +1100,19 @@ AstNode* Ast::clone (AstNode const* node) {
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief get the reversed operator for a comparison operator
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
AstNodeType Ast::ReverseOperator (AstNodeType type) {
|
||||||
|
auto it = ReversedOperators.find(static_cast<int>(type));
|
||||||
|
if (it == ReversedOperators.end()) {
|
||||||
|
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "invalid node type for inversed operator");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*it).second;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// --SECTION-- private methods
|
// --SECTION-- private methods
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -1227,8 +1252,8 @@ AstNode* Ast::optimizeNotExpression (AstNode* node) {
|
||||||
auto lhs = operand->getMember(0);
|
auto lhs = operand->getMember(0);
|
||||||
auto rhs = operand->getMember(1);
|
auto rhs = operand->getMember(1);
|
||||||
|
|
||||||
auto it = ReverseOperators.find(static_cast<int>(operand->type));
|
auto it = NegatedOperators.find(static_cast<int>(operand->type));
|
||||||
TRI_ASSERT(it != ReverseOperators.end());
|
TRI_ASSERT(it != NegatedOperators.end());
|
||||||
|
|
||||||
return createNodeBinaryOperator((*it).second, lhs, rhs);
|
return createNodeBinaryOperator((*it).second, lhs, rhs);
|
||||||
}
|
}
|
||||||
|
|
|
@ -392,13 +392,13 @@ namespace triagens {
|
||||||
/// @brief create an AST null value node
|
/// @brief create an AST null value node
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
AstNode* createNodeValueNull ();
|
static AstNode* createNodeValueNull ();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief create an AST bool value node
|
/// @brief create an AST bool value node
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
AstNode* createNodeValueBool (bool);
|
static AstNode* createNodeValueBool (bool);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief create an AST int value node
|
/// @brief create an AST int value node
|
||||||
|
@ -489,6 +489,12 @@ namespace triagens {
|
||||||
|
|
||||||
AstNode* clone (AstNode const*);
|
AstNode* clone (AstNode const*);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief get the reversed operator for a comparison operator
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static AstNodeType ReverseOperator (AstNodeType);
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// --SECTION-- private methods
|
// --SECTION-- private methods
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -630,11 +636,16 @@ namespace triagens {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief inverse comparison operators
|
/// @brief negated comparison operators
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static std::unordered_map<int, AstNodeType> const ReverseOperators;
|
static std::unordered_map<int, AstNodeType> const NegatedOperators;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief reverse comparison operators
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static std::unordered_map<int, AstNodeType> const ReversedOperators;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// --SECTION-- private variables
|
// --SECTION-- private variables
|
||||||
|
|
|
@ -133,11 +133,73 @@ std::unordered_map<int, std::string const> const AstNode::valueTypeNames{
|
||||||
// --SECTION-- static helper functions
|
// --SECTION-- static helper functions
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief resolve an attribute access
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static AstNode const* ResolveAttribute (AstNode const* node) {
|
||||||
|
TRI_ASSERT(node != nullptr);
|
||||||
|
TRI_ASSERT(node->type == NODE_TYPE_ATTRIBUTE_ACCESS);
|
||||||
|
|
||||||
|
std::vector<std::string> attributeNames;
|
||||||
|
|
||||||
|
while (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||||
|
char const* attributeName = node->getStringValue();
|
||||||
|
|
||||||
|
TRI_ASSERT(attributeName != nullptr);
|
||||||
|
attributeNames.push_back(attributeName);
|
||||||
|
node = node->getMember(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
TRI_ASSERT(! attributeNames.empty());
|
||||||
|
|
||||||
|
TRI_ASSERT(node->type == NODE_TYPE_VALUE ||
|
||||||
|
node->type == NODE_TYPE_LIST ||
|
||||||
|
node->type == NODE_TYPE_ARRAY);
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
if (node->type == NODE_TYPE_ARRAY) {
|
||||||
|
char const* attributeName = attributeNames.back().c_str();
|
||||||
|
attributeNames.pop_back();
|
||||||
|
|
||||||
|
size_t const n = node->numMembers();
|
||||||
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
auto member = node->getMember(i);
|
||||||
|
|
||||||
|
if (member != nullptr && strcmp(member->getStringValue(), attributeName) == 0) {
|
||||||
|
// found the attribute
|
||||||
|
node = member->getMember(0);
|
||||||
|
if (attributeNames.empty()) {
|
||||||
|
// we found what we looked for
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// we found the correct attribute but there is now an attribute access on the result
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// attribute not found or non-array
|
||||||
|
return Ast::createNodeValueNull();
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief get the node type for inter-node comparisons
|
/// @brief get the node type for inter-node comparisons
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static TRI_json_type_e GetNodeCompareType (AstNode const* node) {
|
static TRI_json_type_e GetNodeCompareType (AstNode const* node) {
|
||||||
|
TRI_ASSERT(node != nullptr);
|
||||||
|
|
||||||
if (node->type == NODE_TYPE_VALUE) {
|
if (node->type == NODE_TYPE_VALUE) {
|
||||||
switch (node->value.type) {
|
switch (node->value.type) {
|
||||||
case VALUE_TYPE_NULL:
|
case VALUE_TYPE_NULL:
|
||||||
|
@ -158,7 +220,11 @@ static TRI_json_type_e GetNodeCompareType (AstNode const* node) {
|
||||||
else if (node->type == NODE_TYPE_ARRAY) {
|
else if (node->type == NODE_TYPE_ARRAY) {
|
||||||
return TRI_JSON_ARRAY;
|
return TRI_JSON_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we should never get here
|
||||||
TRI_ASSERT(false);
|
TRI_ASSERT(false);
|
||||||
|
|
||||||
|
// return null in case assertions are turned off
|
||||||
return TRI_JSON_NULL;
|
return TRI_JSON_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +232,15 @@ static TRI_json_type_e GetNodeCompareType (AstNode const* node) {
|
||||||
/// @brief compare two nodes
|
/// @brief compare two nodes
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static int CompareNodes (AstNode const* lhs, AstNode const* rhs) {
|
int triagens::aql::CompareAstNodes (AstNode const* lhs,
|
||||||
|
AstNode const* rhs) {
|
||||||
|
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||||
|
lhs = ResolveAttribute(lhs);
|
||||||
|
}
|
||||||
|
if (rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||||
|
rhs = ResolveAttribute(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
auto lType = GetNodeCompareType(lhs);
|
auto lType = GetNodeCompareType(lhs);
|
||||||
auto rType = GetNodeCompareType(rhs);
|
auto rType = GetNodeCompareType(rhs);
|
||||||
|
|
||||||
|
@ -195,7 +269,7 @@ static int CompareNodes (AstNode const* lhs, AstNode const* rhs) {
|
||||||
size_t const n = ((numLhs > numRhs) ? numRhs : numLhs);
|
size_t const n = ((numLhs > numRhs) ? numRhs : numLhs);
|
||||||
|
|
||||||
for (size_t i = 0; i < n; ++i) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
int res = CompareNodes(lhs->getMember(i), rhs->getMember(i));
|
int res = triagens::aql::CompareAstNodes(lhs->getMember(i), rhs->getMember(i));
|
||||||
if (res != 0) {
|
if (res != 0) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -492,7 +566,7 @@ void AstNode::sort () {
|
||||||
auto const l = static_cast<AstNode const*>(lhs);
|
auto const l = static_cast<AstNode const*>(lhs);
|
||||||
auto const r = static_cast<AstNode const*>(rhs);
|
auto const r = static_cast<AstNode const*>(rhs);
|
||||||
|
|
||||||
return (CompareNodes(l, r) < 0);
|
return (triagens::aql::CompareAstNodes(l, r) < 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
setFlag(FLAG_SORTED);
|
setFlag(FLAG_SORTED);
|
||||||
|
@ -621,6 +695,23 @@ TRI_json_t* AstNode::toJsonValue (TRI_memory_zone_t* zone) const {
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||||
|
TRI_json_t* j = getMember(0)->toJsonValue(zone);
|
||||||
|
|
||||||
|
if (j != nullptr) {
|
||||||
|
if (TRI_IsArrayJson(j)) {
|
||||||
|
TRI_json_t* v = TRI_LookupArrayJson(j, getStringValue());
|
||||||
|
if (v != nullptr) {
|
||||||
|
TRI_json_t* copy = TRI_CopyJson(zone, v);
|
||||||
|
TRI_FreeJson(zone, j);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TRI_FreeJson(zone, j);
|
||||||
|
return TRI_CreateNullJson(zone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1099,10 +1190,12 @@ bool AstNode::isSimple () const {
|
||||||
|
|
||||||
bool AstNode::isConstant () const {
|
bool AstNode::isConstant () const {
|
||||||
if (hasFlag(FLAG_CONSTANT)) {
|
if (hasFlag(FLAG_CONSTANT)) {
|
||||||
|
TRI_ASSERT(! hasFlag(FLAG_DYNAMIC));
|
||||||
// fast track exit
|
// fast track exit
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (hasFlag(FLAG_DYNAMIC)) {
|
if (hasFlag(FLAG_DYNAMIC)) {
|
||||||
|
TRI_ASSERT(! hasFlag(FLAG_CONSTANT));
|
||||||
// fast track exit
|
// fast track exit
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1154,6 +1247,13 @@ bool AstNode::isConstant () const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||||
|
if (getMember(0)->isConstant()) {
|
||||||
|
setFlag(FLAG_CONSTANT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setFlag(FLAG_DYNAMIC);
|
setFlag(FLAG_DYNAMIC);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1505,6 +1605,12 @@ void AstNode::stringify (triagens::basics::StringBuffer* buffer,
|
||||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, message);
|
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string AstNode::toString () const {
|
||||||
|
triagens::basics::StringBuffer buffer(TRI_UNKNOWN_MEM_ZONE);
|
||||||
|
stringify(&buffer, false);
|
||||||
|
return std::string(buffer.c_str(), buffer.length());
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// --SECTION-- private methods
|
// --SECTION-- private methods
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -649,6 +649,9 @@ namespace triagens {
|
||||||
void stringify (triagens::basics::StringBuffer*,
|
void stringify (triagens::basics::StringBuffer*,
|
||||||
bool) const;
|
bool) const;
|
||||||
|
|
||||||
|
std::string toString () const;
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// --SECTION-- private methods
|
// --SECTION-- private methods
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -705,6 +708,7 @@ namespace triagens {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int CompareAstNodes (AstNode const*, AstNode const*);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -409,7 +409,7 @@ size_t ExecutionBlock::skipSome (size_t atLeast, size_t atMost) {
|
||||||
bool ExecutionBlock::skip (size_t number) {
|
bool ExecutionBlock::skip (size_t number) {
|
||||||
size_t skipped = skipSome(number, number);
|
size_t skipped = skipSome(number, number);
|
||||||
size_t nr = skipped;
|
size_t nr = skipped;
|
||||||
while ( nr != 0 && skipped < number ){
|
while (nr != 0 && skipped < number) {
|
||||||
nr = skipSome(number - skipped, number - skipped);
|
nr = skipSome(number - skipped, number - skipped);
|
||||||
skipped += nr;
|
skipped += nr;
|
||||||
}
|
}
|
||||||
|
@ -473,8 +473,7 @@ int ExecutionBlock::getOrSkipSome (size_t atLeast,
|
||||||
return TRI_ERROR_NO_ERROR;
|
return TRI_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (! getBlock(atLeast - skipped,
|
if (! getBlock(atLeast - skipped, atMost - skipped)) {
|
||||||
(std::max)(atMost - skipped, DefaultBatchSize))) {
|
|
||||||
_done = true;
|
_done = true;
|
||||||
break; // must still put things in the result from the collector . . .
|
break; // must still put things in the result from the collector . . .
|
||||||
}
|
}
|
||||||
|
@ -486,7 +485,7 @@ int ExecutionBlock::getOrSkipSome (size_t atLeast,
|
||||||
|
|
||||||
if (cur->size() - _pos > atMost - skipped) {
|
if (cur->size() - _pos > atMost - skipped) {
|
||||||
// The current block is too large for atMost:
|
// The current block is too large for atMost:
|
||||||
if (! skipping){
|
if (! skipping) {
|
||||||
unique_ptr<AqlItemBlock> more(cur->slice(_pos, _pos + (atMost - skipped)));
|
unique_ptr<AqlItemBlock> more(cur->slice(_pos, _pos + (atMost - skipped)));
|
||||||
collector.push_back(more.get());
|
collector.push_back(more.get());
|
||||||
more.release(); // do not delete it!
|
more.release(); // do not delete it!
|
||||||
|
@ -497,7 +496,7 @@ int ExecutionBlock::getOrSkipSome (size_t atLeast,
|
||||||
else if (_pos > 0) {
|
else if (_pos > 0) {
|
||||||
// The current block fits into our result, but it is already
|
// The current block fits into our result, but it is already
|
||||||
// half-eaten:
|
// half-eaten:
|
||||||
if(! skipping){
|
if (! skipping) {
|
||||||
unique_ptr<AqlItemBlock> more(cur->slice(_pos, cur->size()));
|
unique_ptr<AqlItemBlock> more(cur->slice(_pos, cur->size()));
|
||||||
collector.push_back(more.get());
|
collector.push_back(more.get());
|
||||||
more.release();
|
more.release();
|
||||||
|
@ -580,7 +579,7 @@ int SingletonBlock::shutdown (int errorCode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int SingletonBlock::getOrSkipSome (size_t, // atLeast,
|
int SingletonBlock::getOrSkipSome (size_t, // atLeast,
|
||||||
size_t, // atMost,
|
size_t atMost, // atMost,
|
||||||
bool skipping,
|
bool skipping,
|
||||||
AqlItemBlock*& result,
|
AqlItemBlock*& result,
|
||||||
size_t& skipped) {
|
size_t& skipped) {
|
||||||
|
@ -591,7 +590,7 @@ int SingletonBlock::getOrSkipSome (size_t, // atLeast,
|
||||||
return TRI_ERROR_NO_ERROR;
|
return TRI_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(! skipping){
|
if (! skipping) {
|
||||||
result = new AqlItemBlock(1, getPlanNode()->getRegisterPlan()->nrRegs[getPlanNode()->getDepth()]);
|
result = new AqlItemBlock(1, getPlanNode()->getRegisterPlan()->nrRegs[getPlanNode()->getDepth()]);
|
||||||
try {
|
try {
|
||||||
if (_inputRegisterValues != nullptr) {
|
if (_inputRegisterValues != nullptr) {
|
||||||
|
@ -849,9 +848,12 @@ IndexRangeBlock::IndexRangeBlock (ExecutionEngine* engine,
|
||||||
: ExecutionBlock(engine, en),
|
: ExecutionBlock(engine, en),
|
||||||
_collection(en->collection()),
|
_collection(en->collection()),
|
||||||
_posInDocs(0),
|
_posInDocs(0),
|
||||||
_allBoundsConstant(true) {
|
_allBoundsConstant(true),
|
||||||
|
_skiplistIterator(nullptr),
|
||||||
|
_condition(&en->_ranges),
|
||||||
|
_freeCondition(false) {
|
||||||
|
|
||||||
std::vector<std::vector<RangeInfo>> const& orRanges = en->_ranges;
|
std::vector<std::vector<RangeInfo>> const& orRanges = en->_ranges;//TODO replace this with _condition
|
||||||
TRI_ASSERT(en->_index != nullptr);
|
TRI_ASSERT(en->_index != nullptr);
|
||||||
|
|
||||||
TRI_ASSERT(orRanges.size() == 1); // OR expressions not yet implemented
|
TRI_ASSERT(orRanges.size() == 1); // OR expressions not yet implemented
|
||||||
|
@ -868,6 +870,10 @@ IndexRangeBlock::~IndexRangeBlock () {
|
||||||
delete e;
|
delete e;
|
||||||
}
|
}
|
||||||
_allVariableBoundExpressions.clear();
|
_allVariableBoundExpressions.clear();
|
||||||
|
|
||||||
|
if (_freeCondition && _condition != nullptr) {
|
||||||
|
delete _condition;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int IndexRangeBlock::initialize () {
|
int IndexRangeBlock::initialize () {
|
||||||
|
@ -931,38 +937,38 @@ int IndexRangeBlock::initialize () {
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { // _allBoundsConstant
|
|
||||||
readIndex();
|
|
||||||
}
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IndexRangeBlock::readIndex () {
|
// init the index for reading, this should be called once per new incoming
|
||||||
// This is either called from initialize if all bounds are constant,
|
// block!
|
||||||
// in this case it is never called again. If there is at least one
|
//
|
||||||
// variable bound, then readIndex is called once for every item coming
|
// This is either called every time we get a new incoming block.
|
||||||
// in from our dependency. In that case, it is guaranteed that
|
// If all the bounds are constant, then in the case of hash, primary or edges
|
||||||
// _buffer is not empty, in particular _buffer.front() is defined
|
// indexes it does nothing. In the case of a skiplist index, it creates a
|
||||||
// _pos points to a position in _buffer.front()
|
// skiplistIterator which is used by readIndex. If at least one bound is
|
||||||
// Therefore, we can use the register values in _buffer.front() in row
|
// variable, then this this also evaluates the IndexOrCondition required to
|
||||||
// _pos to evaluate the variable bounds.
|
// determine the values of the bounds.
|
||||||
|
//
|
||||||
if (_documents.empty()) {
|
// It is guaranteed that
|
||||||
_documents.reserve(DefaultBatchSize);
|
// _buffer is not empty, in particular _buffer.front() is defined
|
||||||
}
|
// _pos points to a position in _buffer.front()
|
||||||
else {
|
// Therefore, we can use the register values in _buffer.front() in row
|
||||||
_documents.clear();
|
// _pos to evaluate the variable bounds.
|
||||||
}
|
|
||||||
|
|
||||||
|
bool IndexRangeBlock::initIndex () {
|
||||||
|
ENTER_BLOCK
|
||||||
|
_flag = true;
|
||||||
auto en = static_cast<IndexRangeNode const*>(getPlanNode());
|
auto en = static_cast<IndexRangeNode const*>(getPlanNode());
|
||||||
IndexOrCondition const* condition = &en->_ranges;
|
freeCondition();
|
||||||
|
_condition = &en->_ranges;
|
||||||
|
_freeCondition = false;
|
||||||
|
|
||||||
TRI_ASSERT(en->_index != nullptr);
|
TRI_ASSERT(en->_index != nullptr);
|
||||||
|
|
||||||
std::unique_ptr<IndexOrCondition> newCondition;
|
|
||||||
|
|
||||||
// Find out about the actual values for the bounds in the variable bound case:
|
// Find out about the actual values for the bounds in the variable bound case:
|
||||||
if (! _allBoundsConstant) {
|
if (! _allBoundsConstant) {
|
||||||
|
std::unique_ptr<IndexOrCondition> newCondition;
|
||||||
// The following are needed to evaluate expressions with local data from
|
// The following are needed to evaluate expressions with local data from
|
||||||
// the current incoming item:
|
// the current incoming item:
|
||||||
AqlItemBlock* cur = _buffer.front();
|
AqlItemBlock* cur = _buffer.front();
|
||||||
|
@ -1051,29 +1057,86 @@ bool IndexRangeBlock::readIndex () {
|
||||||
newCondition.get()->at(0).push_back(actualRange);
|
newCondition.get()->at(0).push_back(actualRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
condition = newCondition.get();
|
freeCondition();
|
||||||
|
_condition = newCondition.release();
|
||||||
|
_freeCondition = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (en->_index->type == TRI_IDX_TYPE_PRIMARY_INDEX) {
|
if (en->_index->type == TRI_IDX_TYPE_PRIMARY_INDEX) {
|
||||||
readPrimaryIndex(*condition);
|
return true; //no initialization here!
|
||||||
}
|
}
|
||||||
else if (en->_index->type == TRI_IDX_TYPE_HASH_INDEX) {
|
else if (en->_index->type == TRI_IDX_TYPE_HASH_INDEX) {
|
||||||
readHashIndex(*condition);
|
return true; //no initialization here!
|
||||||
}
|
}
|
||||||
else if (en->_index->type == TRI_IDX_TYPE_SKIPLIST_INDEX) {
|
if (en->_index->type == TRI_IDX_TYPE_SKIPLIST_INDEX) {
|
||||||
readSkiplistIndex(*condition);
|
initSkiplistIndex(*_condition);
|
||||||
|
return (_skiplistIterator != nullptr);
|
||||||
}
|
}
|
||||||
else if (en->_index->type == TRI_IDX_TYPE_EDGE_INDEX) {
|
else if (en->_index->type == TRI_IDX_TYPE_EDGE_INDEX) {
|
||||||
readEdgeIndex(*condition);
|
return true; //no initialization here!
|
||||||
|
}
|
||||||
|
|
||||||
|
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "unexpected index type");
|
||||||
|
LEAVE_BLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IndexRangeBlock::freeCondition () {
|
||||||
|
if (_condition != nullptr && _freeCondition) {
|
||||||
|
delete _condition;
|
||||||
|
_condition = nullptr;
|
||||||
|
_freeCondition = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is called every time everything in _documents has been passed on
|
||||||
|
|
||||||
|
bool IndexRangeBlock::readIndex (size_t atMost) {
|
||||||
|
ENTER_BLOCK;
|
||||||
|
// this is called every time we want more in _documents.
|
||||||
|
// For non-skiplist indexes (currently hash, primary, edge), this
|
||||||
|
// only reads the index once, and never again (although there might be
|
||||||
|
// multiple calls to this function). For skiplists indexes, initIndex creates
|
||||||
|
// a skiplistIterator and readIndex just reads from the iterator until it is
|
||||||
|
// done. Then initIndex is read again and so on. This is to avoid reading the
|
||||||
|
// entire index when we only want a small number of documents.
|
||||||
|
|
||||||
|
if (_documents.empty()) {
|
||||||
|
_documents.reserve(atMost);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_documents.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto en = static_cast<IndexRangeNode const*>(getPlanNode());
|
||||||
|
|
||||||
|
if (en->_index->type == TRI_IDX_TYPE_PRIMARY_INDEX) {
|
||||||
|
if (_flag) {
|
||||||
|
readPrimaryIndex(*_condition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (en->_index->type == TRI_IDX_TYPE_HASH_INDEX) {
|
||||||
|
if (_flag) {
|
||||||
|
readHashIndex(*_condition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (en->_index->type == TRI_IDX_TYPE_SKIPLIST_INDEX) {
|
||||||
|
readSkiplistIndex(atMost);
|
||||||
|
}
|
||||||
|
else if (en->_index->type == TRI_IDX_TYPE_EDGE_INDEX) {
|
||||||
|
if (_flag) {
|
||||||
|
readEdgeIndex(*_condition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
TRI_ASSERT(false);
|
TRI_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
_flag = false;
|
||||||
return (!_documents.empty());
|
return (! _documents.empty());
|
||||||
|
LEAVE_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int IndexRangeBlock::initializeCursor (AqlItemBlock* items, size_t pos) {
|
int IndexRangeBlock::initializeCursor (AqlItemBlock* items, size_t pos) {
|
||||||
|
ENTER_BLOCK;
|
||||||
int res = ExecutionBlock::initializeCursor(items, pos);
|
int res = ExecutionBlock::initializeCursor(items, pos);
|
||||||
if (res != TRI_ERROR_NO_ERROR) {
|
if (res != TRI_ERROR_NO_ERROR) {
|
||||||
return res;
|
return res;
|
||||||
|
@ -1081,19 +1144,17 @@ int IndexRangeBlock::initializeCursor (AqlItemBlock* items, size_t pos) {
|
||||||
_pos = 0;
|
_pos = 0;
|
||||||
_posInDocs = 0;
|
_posInDocs = 0;
|
||||||
|
|
||||||
if (_allBoundsConstant && _documents.size() == 0) {
|
|
||||||
_done = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRI_ERROR_NO_ERROR;
|
return TRI_ERROR_NO_ERROR;
|
||||||
|
LEAVE_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief getSome
|
/// @brief getSome
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
AqlItemBlock* IndexRangeBlock::getSome (size_t, // atLeast
|
AqlItemBlock* IndexRangeBlock::getSome (size_t atLeast,
|
||||||
size_t atMost) {
|
size_t atMost) {
|
||||||
|
ENTER_BLOCK;
|
||||||
if (_done) {
|
if (_done) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -1107,18 +1168,44 @@ AqlItemBlock* IndexRangeBlock::getSome (size_t, // atLeast
|
||||||
// try again!
|
// try again!
|
||||||
|
|
||||||
if (_buffer.empty()) {
|
if (_buffer.empty()) {
|
||||||
if (! ExecutionBlock::getBlock(DefaultBatchSize, DefaultBatchSize)) {
|
if (! ExecutionBlock::getBlock(DefaultBatchSize, DefaultBatchSize)
|
||||||
|
|| (! initIndex())) {
|
||||||
_done = true;
|
_done = true;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
_pos = 0; // this is in the first block
|
_pos = 0; // this is in the first block
|
||||||
|
|
||||||
// This is a new item, so let's read the index if bounds are variable:
|
// This is a new item, so let's read the index (it is already
|
||||||
if (! _allBoundsConstant) {
|
// initialised).
|
||||||
readIndex();
|
readIndex(atMost);
|
||||||
|
_posInDocs = 0; // position in _documents . . .
|
||||||
|
}
|
||||||
|
else if (_posInDocs >= _documents.size()) {
|
||||||
|
// we have exhausted our local documents buffer,
|
||||||
|
|
||||||
|
_posInDocs = 0;
|
||||||
|
AqlItemBlock* cur = _buffer.front();
|
||||||
|
|
||||||
|
if (! readIndex(atMost)) { //no more output from this version of the index
|
||||||
|
if (++_pos >= cur->size()) {
|
||||||
|
_buffer.pop_front(); // does not throw
|
||||||
|
delete cur;
|
||||||
|
_pos = 0;
|
||||||
|
}
|
||||||
|
if (_buffer.empty()) {
|
||||||
|
if (! ExecutionBlock::getBlock(DefaultBatchSize, DefaultBatchSize) ) {
|
||||||
|
_done = true;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
_pos = 0; // this is in the first block
|
||||||
}
|
}
|
||||||
|
|
||||||
_posInDocs = 0; // position in _documents . . .
|
if(! initIndex()) {
|
||||||
|
_done = true;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
readIndex(atMost);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, we do have _buffer.front() and _pos points into it
|
// If we get here, we do have _buffer.front() and _pos points into it
|
||||||
|
@ -1130,7 +1217,8 @@ AqlItemBlock* IndexRangeBlock::getSome (size_t, // atLeast
|
||||||
|
|
||||||
if (toSend > 0) {
|
if (toSend > 0) {
|
||||||
|
|
||||||
res.reset(new AqlItemBlock(toSend, getPlanNode()->getRegisterPlan()->nrRegs[getPlanNode()->getDepth()]));
|
res.reset(new AqlItemBlock(toSend,
|
||||||
|
getPlanNode()->getRegisterPlan()->nrRegs[getPlanNode()->getDepth()]));
|
||||||
|
|
||||||
// automatically freed should we throw
|
// automatically freed should we throw
|
||||||
TRI_ASSERT(curRegs <= res->getNrRegs());
|
TRI_ASSERT(curRegs <= res->getNrRegs());
|
||||||
|
@ -1139,7 +1227,8 @@ AqlItemBlock* IndexRangeBlock::getSome (size_t, // atLeast
|
||||||
inheritRegisters(cur, res.get(), _pos);
|
inheritRegisters(cur, res.get(), _pos);
|
||||||
|
|
||||||
// set our collection for our output register
|
// set our collection for our output register
|
||||||
res->setDocumentCollection(static_cast<triagens::aql::RegisterId>(curRegs), _trx->documentCollection(_collection->cid()));
|
res->setDocumentCollection(static_cast<triagens::aql::RegisterId>(curRegs),
|
||||||
|
_trx->documentCollection(_collection->cid()));
|
||||||
|
|
||||||
for (size_t j = 0; j < toSend; j++) {
|
for (size_t j = 0; j < toSend; j++) {
|
||||||
if (j > 0) {
|
if (j > 0) {
|
||||||
|
@ -1161,32 +1250,13 @@ AqlItemBlock* IndexRangeBlock::getSome (size_t, // atLeast
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance read position:
|
|
||||||
if (_posInDocs >= _documents.size()) {
|
|
||||||
// we have exhausted our local documents buffer,
|
|
||||||
|
|
||||||
_posInDocs = 0;
|
|
||||||
|
|
||||||
if (++_pos >= cur->size()) {
|
|
||||||
_buffer.pop_front(); // does not throw
|
|
||||||
delete cur;
|
|
||||||
_pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// let's read the index if bounds are variable:
|
|
||||||
if (! _buffer.empty() && ! _allBoundsConstant) {
|
|
||||||
readIndex();
|
|
||||||
}
|
|
||||||
// If _buffer is empty, then we will fetch a new block in the next call
|
|
||||||
// and then read the index.
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
while (res.get() == nullptr);
|
while (res.get() == nullptr);
|
||||||
|
|
||||||
// Clear out registers no longer needed later:
|
// Clear out registers no longer needed later:
|
||||||
clearRegisters(res.get());
|
clearRegisters(res.get());
|
||||||
return res.release();
|
return res.release();
|
||||||
|
LEAVE_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1202,19 +1272,17 @@ size_t IndexRangeBlock::skipSome (size_t atLeast,
|
||||||
|
|
||||||
size_t skipped = 0;
|
size_t skipped = 0;
|
||||||
|
|
||||||
while (skipped < atLeast ){
|
while (skipped < atLeast) {
|
||||||
if (_buffer.empty()) {
|
if (_buffer.empty()) {
|
||||||
if (! ExecutionBlock::getBlock(DefaultBatchSize, DefaultBatchSize)) {
|
if (! ExecutionBlock::getBlock(DefaultBatchSize, DefaultBatchSize)
|
||||||
|
|| (! initIndex())) {
|
||||||
_done = true;
|
_done = true;
|
||||||
return skipped;
|
return skipped;
|
||||||
}
|
}
|
||||||
_pos = 0; // this is in the first block
|
_pos = 0; // this is in the first block
|
||||||
|
|
||||||
// This is a new item, so let's read the index if bounds are variable:
|
// This is a new item, so let's read the index if bounds are variable:
|
||||||
if (! _allBoundsConstant) {
|
readIndex(atMost);
|
||||||
readIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
_posInDocs = 0; // position in _documents . . .
|
_posInDocs = 0; // position in _documents . . .
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1238,15 +1306,18 @@ size_t IndexRangeBlock::skipSome (size_t atLeast,
|
||||||
}
|
}
|
||||||
|
|
||||||
// let's read the index if bounds are variable:
|
// let's read the index if bounds are variable:
|
||||||
if (! _buffer.empty() && ! _allBoundsConstant) {
|
if (! _buffer.empty()) {
|
||||||
readIndex();
|
if(! initIndex()) {
|
||||||
|
_done = true;
|
||||||
|
return skipped;
|
||||||
|
}
|
||||||
|
readIndex(atMost);
|
||||||
}
|
}
|
||||||
_posInDocs = 0;
|
_posInDocs = 0;
|
||||||
|
|
||||||
// If _buffer is empty, then we will fetch a new block in the next round
|
// If _buffer is empty, then we will fetch a new block in the next round
|
||||||
// and then read the index.
|
// and then read the index.
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return skipped;
|
return skipped;
|
||||||
|
@ -1257,6 +1328,7 @@ size_t IndexRangeBlock::skipSome (size_t atLeast,
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void IndexRangeBlock::readPrimaryIndex (IndexOrCondition const& ranges) {
|
void IndexRangeBlock::readPrimaryIndex (IndexOrCondition const& ranges) {
|
||||||
|
ENTER_BLOCK;
|
||||||
TRI_primary_index_t* primaryIndex = &(_collection->documentCollection()->_primaryIndex);
|
TRI_primary_index_t* primaryIndex = &(_collection->documentCollection()->_primaryIndex);
|
||||||
|
|
||||||
std::string key;
|
std::string key;
|
||||||
|
@ -1310,9 +1382,10 @@ void IndexRangeBlock::readPrimaryIndex (IndexOrCondition const& ranges) {
|
||||||
|
|
||||||
auto found = static_cast<TRI_doc_mptr_t const*>(TRI_LookupByKeyPrimaryIndex(primaryIndex, key.c_str()));
|
auto found = static_cast<TRI_doc_mptr_t const*>(TRI_LookupByKeyPrimaryIndex(primaryIndex, key.c_str()));
|
||||||
if (found != nullptr) {
|
if (found != nullptr) {
|
||||||
_documents.push_back(*found);
|
_documents.emplace_back(*found);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LEAVE_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1320,6 +1393,7 @@ void IndexRangeBlock::readPrimaryIndex (IndexOrCondition const& ranges) {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void IndexRangeBlock::readHashIndex (IndexOrCondition const& ranges) {
|
void IndexRangeBlock::readHashIndex (IndexOrCondition const& ranges) {
|
||||||
|
ENTER_BLOCK;
|
||||||
auto en = static_cast<IndexRangeNode const*>(getPlanNode());
|
auto en = static_cast<IndexRangeNode const*>(getPlanNode());
|
||||||
TRI_index_t* idx = en->_index->data;
|
TRI_index_t* idx = en->_index->data;
|
||||||
TRI_ASSERT(idx != nullptr);
|
TRI_ASSERT(idx != nullptr);
|
||||||
|
@ -1364,21 +1438,20 @@ void IndexRangeBlock::readHashIndex (IndexOrCondition const& ranges) {
|
||||||
// here x->_low->_bound = x->_high->_bound
|
// here x->_low->_bound = x->_high->_bound
|
||||||
searchValue._values[i] = *shaped;
|
searchValue._values[i] = *shaped;
|
||||||
TRI_Free(shaper->_memoryZone, shaped);
|
TRI_Free(shaper->_memoryZone, shaped);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
setupSearchValue();
|
setupSearchValue();
|
||||||
TRI_index_result_t list = TRI_LookupHashIndex(idx, &searchValue);
|
TRI_index_result_t list = TRI_LookupHashIndex(idx, &searchValue);
|
||||||
|
|
||||||
destroySearchValue();
|
destroySearchValue();
|
||||||
|
|
||||||
size_t const n = list._length;
|
size_t const n = list._length;
|
||||||
try {
|
try {
|
||||||
for (size_t i = 0; i < n; ++i) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
_documents.push_back(*(list._documents[i]));
|
_documents.emplace_back(*(list._documents[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
_engine->_stats.scannedIndex += static_cast<int64_t>(n);
|
_engine->_stats.scannedIndex += static_cast<int64_t>(n);
|
||||||
|
@ -1388,6 +1461,7 @@ void IndexRangeBlock::readHashIndex (IndexOrCondition const& ranges) {
|
||||||
TRI_DestroyIndexResult(&list);
|
TRI_DestroyIndexResult(&list);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
LEAVE_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1395,6 +1469,7 @@ void IndexRangeBlock::readHashIndex (IndexOrCondition const& ranges) {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void IndexRangeBlock::readEdgeIndex (IndexOrCondition const& ranges) {
|
void IndexRangeBlock::readEdgeIndex (IndexOrCondition const& ranges) {
|
||||||
|
ENTER_BLOCK;
|
||||||
TRI_document_collection_t* document = _collection->documentCollection();
|
TRI_document_collection_t* document = _collection->documentCollection();
|
||||||
|
|
||||||
std::string key;
|
std::string key;
|
||||||
|
@ -1435,12 +1510,13 @@ void IndexRangeBlock::readEdgeIndex (IndexOrCondition const& ranges) {
|
||||||
// silently ignore all errors due to wrong _from / _to specifications
|
// silently ignore all errors due to wrong _from / _to specifications
|
||||||
auto&& result = TRI_LookupEdgesDocumentCollection(document, direction, documentCid, (TRI_voc_key_t) documentKey.c_str());
|
auto&& result = TRI_LookupEdgesDocumentCollection(document, direction, documentCid, (TRI_voc_key_t) documentKey.c_str());
|
||||||
for (auto it : result) {
|
for (auto it : result) {
|
||||||
_documents.push_back((it));
|
_documents.emplace_back(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
_engine->_stats.scannedIndex += static_cast<int64_t>(result.size());
|
_engine->_stats.scannedIndex += static_cast<int64_t>(result.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LEAVE_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1479,7 +1555,10 @@ void IndexRangeBlock::readEdgeIndex (IndexOrCondition const& ranges) {
|
||||||
// (i.e. the 1 in x.c >= 1) cannot be lists or arrays.
|
// (i.e. the 1 in x.c >= 1) cannot be lists or arrays.
|
||||||
//
|
//
|
||||||
|
|
||||||
void IndexRangeBlock::readSkiplistIndex (IndexOrCondition const& ranges) {
|
void IndexRangeBlock::initSkiplistIndex (IndexOrCondition const& ranges) {
|
||||||
|
ENTER_BLOCK;
|
||||||
|
TRI_ASSERT(_skiplistIterator == nullptr);
|
||||||
|
|
||||||
auto en = static_cast<IndexRangeNode const*>(getPlanNode());
|
auto en = static_cast<IndexRangeNode const*>(getPlanNode());
|
||||||
TRI_index_t* idx = en->_index->data;
|
TRI_index_t* idx = en->_index->data;
|
||||||
TRI_ASSERT(idx != nullptr);
|
TRI_ASSERT(idx != nullptr);
|
||||||
|
@ -1542,12 +1621,12 @@ void IndexRangeBlock::readSkiplistIndex (IndexOrCondition const& ranges) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TRI_skiplist_iterator_t* skiplistIterator = TRI_LookupSkiplistIndex(idx, skiplistOperator, en->_reverse);
|
_skiplistIterator = TRI_LookupSkiplistIndex(idx, skiplistOperator, en->_reverse);
|
||||||
if (skiplistOperator != nullptr) {
|
if (skiplistOperator != nullptr) {
|
||||||
TRI_FreeIndexOperator(skiplistOperator);
|
TRI_FreeIndexOperator(skiplistOperator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skiplistIterator == nullptr) {
|
if (_skiplistIterator == nullptr) {
|
||||||
int res = TRI_errno();
|
int res = TRI_errno();
|
||||||
if (res == TRI_RESULT_ELEMENT_NOT_FOUND) {
|
if (res == TRI_RESULT_ELEMENT_NOT_FOUND) {
|
||||||
return;
|
return;
|
||||||
|
@ -1555,23 +1634,38 @@ void IndexRangeBlock::readSkiplistIndex (IndexOrCondition const& ranges) {
|
||||||
|
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_NO_INDEX);
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_ARANGO_NO_INDEX);
|
||||||
}
|
}
|
||||||
|
LEAVE_BLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IndexRangeBlock::readSkiplistIndex (size_t atMost) {
|
||||||
|
ENTER_BLOCK;
|
||||||
|
if (_skiplistIterator == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (true) {
|
size_t nrSent = 0;
|
||||||
TRI_skiplist_index_element_t* indexElement = skiplistIterator->next(skiplistIterator);
|
TRI_skiplist_index_element_t* indexElement;
|
||||||
|
while (nrSent < atMost) {
|
||||||
|
indexElement = _skiplistIterator->next(_skiplistIterator);
|
||||||
|
|
||||||
if (indexElement == nullptr) {
|
if (indexElement == nullptr) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_documents.push_back(*(indexElement->_document));
|
_documents.emplace_back(*(indexElement->_document));
|
||||||
|
++nrSent;
|
||||||
++_engine->_stats.scannedIndex;
|
++_engine->_stats.scannedIndex;
|
||||||
}
|
}
|
||||||
TRI_FreeSkiplistIterator(skiplistIterator);
|
if (indexElement == nullptr) {
|
||||||
|
TRI_FreeSkiplistIterator(_skiplistIterator);
|
||||||
|
_skiplistIterator = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
TRI_FreeSkiplistIterator(skiplistIterator);
|
TRI_FreeSkiplistIterator(_skiplistIterator);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
LEAVE_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -1584,7 +1678,7 @@ EnumerateListBlock::EnumerateListBlock (ExecutionEngine* engine,
|
||||||
_inVarRegId(ExecutionNode::MaxRegisterId) {
|
_inVarRegId(ExecutionNode::MaxRegisterId) {
|
||||||
|
|
||||||
auto it = en->getRegisterPlan()->varInfo.find(en->_inVariable->id);
|
auto it = en->getRegisterPlan()->varInfo.find(en->_inVariable->id);
|
||||||
if (it == en->getRegisterPlan()->varInfo.end()){
|
if (it == en->getRegisterPlan()->varInfo.end()) {
|
||||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "variable not found");
|
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "variable not found");
|
||||||
}
|
}
|
||||||
_inVarRegId = (*it).second.registerId;
|
_inVarRegId = (*it).second.registerId;
|
||||||
|
@ -1729,7 +1823,7 @@ AqlItemBlock* EnumerateListBlock::getSome (size_t, size_t atMost) {
|
||||||
_thisblock = 0;
|
_thisblock = 0;
|
||||||
_seen = 0;
|
_seen = 0;
|
||||||
// advance read position in the current block . . .
|
// advance read position in the current block . . .
|
||||||
if (++_pos == cur->size() ) {
|
if (++_pos == cur->size()) {
|
||||||
delete cur;
|
delete cur;
|
||||||
_buffer.pop_front(); // does not throw
|
_buffer.pop_front(); // does not throw
|
||||||
_pos = 0;
|
_pos = 0;
|
||||||
|
@ -1751,7 +1845,7 @@ size_t EnumerateListBlock::skipSome (size_t atLeast, size_t atMost) {
|
||||||
|
|
||||||
size_t skipped = 0;
|
size_t skipped = 0;
|
||||||
|
|
||||||
while ( skipped < atLeast ) {
|
while (skipped < atLeast) {
|
||||||
if (_buffer.empty()) {
|
if (_buffer.empty()) {
|
||||||
if (! ExecutionBlock::getBlock(DefaultBatchSize, DefaultBatchSize)) {
|
if (! ExecutionBlock::getBlock(DefaultBatchSize, DefaultBatchSize)) {
|
||||||
_done = true;
|
_done = true;
|
||||||
|
@ -1783,7 +1877,7 @@ size_t EnumerateListBlock::skipSome (size_t atLeast, size_t atMost) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case AqlValue::DOCVEC: {
|
case AqlValue::DOCVEC: {
|
||||||
if( _index == 0) { // this is a (maybe) new DOCVEC
|
if (_index == 0) { // this is a (maybe) new DOCVEC
|
||||||
_DOCVECsize = 0;
|
_DOCVECsize = 0;
|
||||||
//we require the total number of items
|
//we require the total number of items
|
||||||
for (size_t i = 0; i < inVarReg._vector->size(); i++) {
|
for (size_t i = 0; i < inVarReg._vector->size(); i++) {
|
||||||
|
@ -1837,7 +1931,7 @@ AqlValue EnumerateListBlock::getAqlValue (AqlValue inVarReg) {
|
||||||
case AqlValue::DOCVEC: { // incoming doc vec has a single column
|
case AqlValue::DOCVEC: { // incoming doc vec has a single column
|
||||||
AqlValue out = inVarReg._vector->at(_thisblock)->getValue(_index -
|
AqlValue out = inVarReg._vector->at(_thisblock)->getValue(_index -
|
||||||
_seen, 0).clone();
|
_seen, 0).clone();
|
||||||
if(++_index == (inVarReg._vector->at(_thisblock)->size() + _seen)){
|
if (++_index == (inVarReg._vector->at(_thisblock)->size() + _seen)) {
|
||||||
_seen += inVarReg._vector->at(_thisblock)->size();
|
_seen += inVarReg._vector->at(_thisblock)->size();
|
||||||
_thisblock++;
|
_thisblock++;
|
||||||
}
|
}
|
||||||
|
@ -1979,7 +2073,7 @@ AqlItemBlock* CalculationBlock::getSome (size_t atLeast,
|
||||||
size_t atMost) {
|
size_t atMost) {
|
||||||
|
|
||||||
unique_ptr<AqlItemBlock> res(ExecutionBlock::getSomeWithoutRegisterClearout(
|
unique_ptr<AqlItemBlock> res(ExecutionBlock::getSomeWithoutRegisterClearout(
|
||||||
atLeast, atMost));
|
DefaultBatchSize, DefaultBatchSize));
|
||||||
|
|
||||||
if (res.get() == nullptr) {
|
if (res.get() == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -2227,7 +2321,7 @@ int FilterBlock::getOrSkipSome (size_t atLeast,
|
||||||
try {
|
try {
|
||||||
result = AqlItemBlock::concatenate(collector);
|
result = AqlItemBlock::concatenate(collector);
|
||||||
}
|
}
|
||||||
catch (...){
|
catch (...) {
|
||||||
for (auto x : collector) {
|
for (auto x : collector) {
|
||||||
delete x;
|
delete x;
|
||||||
}
|
}
|
||||||
|
@ -2396,7 +2490,7 @@ int AggregateBlock::getOrSkipSome (size_t atLeast,
|
||||||
|
|
||||||
if (newGroup) {
|
if (newGroup) {
|
||||||
if (! _currentGroup.groupValues[0].isEmpty()) {
|
if (! _currentGroup.groupValues[0].isEmpty()) {
|
||||||
if(! skipping){
|
if (! skipping) {
|
||||||
// need to emit the current group first
|
// need to emit the current group first
|
||||||
emitGroup(cur, res.get(), skipped);
|
emitGroup(cur, res.get(), skipped);
|
||||||
}
|
}
|
||||||
|
@ -2443,7 +2537,7 @@ int AggregateBlock::getOrSkipSome (size_t atLeast,
|
||||||
// no more input. we're done
|
// no more input. we're done
|
||||||
try {
|
try {
|
||||||
// emit last buffered group
|
// emit last buffered group
|
||||||
if(! skipping){
|
if (! skipping) {
|
||||||
emitGroup(cur, res.get(), skipped);
|
emitGroup(cur, res.get(), skipped);
|
||||||
++skipped;
|
++skipped;
|
||||||
TRI_ASSERT(skipped > 0);
|
TRI_ASSERT(skipped > 0);
|
||||||
|
@ -2474,7 +2568,7 @@ int AggregateBlock::getOrSkipSome (size_t atLeast,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(! skipping){
|
if (! skipping) {
|
||||||
TRI_ASSERT(skipped > 0);
|
TRI_ASSERT(skipped > 0);
|
||||||
res->shrink(skipped);
|
res->shrink(skipped);
|
||||||
}
|
}
|
||||||
|
@ -3517,7 +3611,7 @@ int GatherBlock::initializeCursor (AqlItemBlock* items, size_t pos) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_isSimple) {
|
if (! _isSimple) {
|
||||||
for (std::deque<AqlItemBlock*>& x : _gatherBlockBuffer) {
|
for (std::deque<AqlItemBlock*>& x : _gatherBlockBuffer) {
|
||||||
for (AqlItemBlock* y: x) {
|
for (AqlItemBlock* y: x) {
|
||||||
delete y;
|
delete y;
|
||||||
|
@ -3589,13 +3683,13 @@ bool GatherBlock::hasMore () {
|
||||||
|
|
||||||
if (_isSimple) {
|
if (_isSimple) {
|
||||||
for (size_t i = 0; i < _dependencies.size(); i++) {
|
for (size_t i = 0; i < _dependencies.size(); i++) {
|
||||||
if(_dependencies.at(i)->hasMore()) {
|
if (_dependencies.at(i)->hasMore()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (size_t i = 0; i < _gatherBlockBuffer.size(); i++){
|
for (size_t i = 0; i < _gatherBlockBuffer.size(); i++) {
|
||||||
if (! _gatherBlockBuffer.at(i).empty()) {
|
if (! _gatherBlockBuffer.at(i).empty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -3893,7 +3987,7 @@ BlockWithClients::BlockWithClients (ExecutionEngine* engine,
|
||||||
: ExecutionBlock(engine, ep),
|
: ExecutionBlock(engine, ep),
|
||||||
_nrClients(shardIds.size()),
|
_nrClients(shardIds.size()),
|
||||||
_ignoreInitCursor(false),
|
_ignoreInitCursor(false),
|
||||||
_ignoreShutdown(false){
|
_ignoreShutdown(false) {
|
||||||
|
|
||||||
_shardIdMap.reserve(_nrClients);
|
_shardIdMap.reserve(_nrClients);
|
||||||
for (size_t i = 0; i < _nrClients; i++) {
|
for (size_t i = 0; i < _nrClients; i++) {
|
||||||
|
@ -4170,7 +4264,7 @@ int ScatterBlock::getOrSkipSomeForShard (size_t atLeast,
|
||||||
|
|
||||||
skipped = (std::min)(available, atMost); //nr rows in outgoing block
|
skipped = (std::min)(available, atMost); //nr rows in outgoing block
|
||||||
|
|
||||||
if (! skipping){
|
if (! skipping) {
|
||||||
result = _buffer.at(pos.first)->slice(pos.second, pos.second + skipped);
|
result = _buffer.at(pos.first)->slice(pos.second, pos.second + skipped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4343,7 +4437,7 @@ int DistributeBlock::getOrSkipSomeForShard (size_t atLeast,
|
||||||
skipped = (std::min)(buf.size(), atMost);
|
skipped = (std::min)(buf.size(), atMost);
|
||||||
|
|
||||||
if (skipping) {
|
if (skipping) {
|
||||||
for (size_t i = 0; i < skipped; i++){
|
for (size_t i = 0; i < skipped; i++) {
|
||||||
buf.pop_front();
|
buf.pop_front();
|
||||||
}
|
}
|
||||||
freeCollector();
|
freeCollector();
|
||||||
|
|
|
@ -562,11 +562,24 @@ namespace triagens {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief free _condition if it belongs to us
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void freeCondition ();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief continue fetching of documents
|
/// @brief continue fetching of documents
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
bool readIndex ();
|
bool readIndex (size_t atMost);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief set up the index for reading. This should be called once per incoming
|
||||||
|
/// block.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool initIndex ();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief read using the primary index
|
/// @brief read using the primary index
|
||||||
|
@ -584,7 +597,13 @@ namespace triagens {
|
||||||
/// @brief read using a skiplist index
|
/// @brief read using a skiplist index
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void readSkiplistIndex (IndexOrCondition const&);
|
void readSkiplistIndex (size_t atMost);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief this tries to create a skiplistIterator to read from the index.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void initSkiplistIndex (IndexOrCondition const&);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief read using a hash index
|
/// @brief read using a hash index
|
||||||
|
@ -633,7 +652,7 @@ namespace triagens {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief _inVars, a vector containing for each expression above
|
/// @brief _inVars, a vector containing for each expression above
|
||||||
/// a vector of Variable*, used to execute the expression
|
/// a vector of Variable*, used to execute the expression
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
std::vector<std::vector<Variable*>> _inVars;
|
std::vector<std::vector<Variable*>> _inVars;
|
||||||
|
|
||||||
|
@ -644,6 +663,39 @@ namespace triagens {
|
||||||
|
|
||||||
std::vector<std::vector<RegisterId>> _inRegs;
|
std::vector<std::vector<RegisterId>> _inRegs;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief _skiplistIterator: holds the skiplist iterator found using
|
||||||
|
/// initSkiplistIndex (if any) so that it can be read in chunks and not
|
||||||
|
/// necessarily all at once.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
TRI_skiplist_iterator_t* _skiplistIterator;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief _condition: holds the IndexOrCondition for the current incoming block,
|
||||||
|
/// this is just the _ranges member of the plan node if _allBoundsConstant
|
||||||
|
/// otherwise it is reevaluated every time initIndex is called, i.e. once per
|
||||||
|
/// incoming block.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
IndexOrCondition const* _condition;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief _flag: since readIndex for primary, hash, edges indexes reads the
|
||||||
|
/// whole index, this is <true> if initIndex has been called but readIndex has
|
||||||
|
/// not been called, otherwise it is <false> to avoid rereading the entire index
|
||||||
|
/// with successive calls to readIndex.
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool _flag;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief _freeCondition: whether or not the _condition is owned by the
|
||||||
|
/// IndexRangeBlock and must be freed
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool _freeCondition;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -459,11 +459,17 @@ void Optimizer::setupRules () {
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// try to replace simple OR conditions with IN
|
// try to replace simple OR conditions with IN
|
||||||
registerRule("replace-OR-with-IN",
|
registerRule("replace-or-with-in",
|
||||||
replaceOrWithIn,
|
replaceOrWithIn,
|
||||||
replaceOrWithIn_pass6,
|
replaceOrWithIn_pass6,
|
||||||
true);
|
true);
|
||||||
|
|
||||||
|
// try to remove redundant OR conditions
|
||||||
|
registerRule("remove-redundant-or",
|
||||||
|
removeRedundantOr,
|
||||||
|
removeRedundantOr_pass6,
|
||||||
|
true);
|
||||||
|
|
||||||
// try to find a filter after an enumerate collection and find an index . . .
|
// try to find a filter after an enumerate collection and find an index . . .
|
||||||
registerRule("use-index-range",
|
registerRule("use-index-range",
|
||||||
useIndexRange,
|
useIndexRange,
|
||||||
|
@ -478,7 +484,7 @@ void Optimizer::setupRules () {
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// try to remove filters which are covered by index ranges
|
// try to remove filters which are covered by index ranges
|
||||||
// rule seems to work, but tests are still missing
|
// TODO: rule seems to work, but tests are still missing
|
||||||
registerRule("remove-filter-covered-by-index",
|
registerRule("remove-filter-covered-by-index",
|
||||||
removeFiltersCoveredByIndex,
|
removeFiltersCoveredByIndex,
|
||||||
removeFiltersCoveredByIndex_pass6,
|
removeFiltersCoveredByIndex_pass6,
|
||||||
|
|
|
@ -134,14 +134,17 @@ namespace triagens {
|
||||||
// replace simple OR conditions with IN
|
// replace simple OR conditions with IN
|
||||||
replaceOrWithIn_pass6 = 810,
|
replaceOrWithIn_pass6 = 810,
|
||||||
|
|
||||||
|
// remove redundant OR conditions
|
||||||
|
removeRedundantOr_pass6 = 820,
|
||||||
|
|
||||||
// try to find a filter after an enumerate collection and find an index . . .
|
// try to find a filter after an enumerate collection and find an index . . .
|
||||||
useIndexRange_pass6 = 820,
|
useIndexRange_pass6 = 830,
|
||||||
|
|
||||||
// try to find sort blocks which are superseeded by indexes
|
// try to find sort blocks which are superseeded by indexes
|
||||||
useIndexForSort_pass6 = 830,
|
useIndexForSort_pass6 = 840,
|
||||||
|
|
||||||
// try to remove filters covered by index ranges
|
// try to remove filters covered by index ranges
|
||||||
removeFiltersCoveredByIndex_pass6 = 840,
|
removeFiltersCoveredByIndex_pass6 = 850,
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
/// "Pass 10": final transformations for the cluster
|
/// "Pass 10": final transformations for the cluster
|
||||||
|
|
|
@ -1556,9 +1556,7 @@ int triagens::aql::useIndexForSort (Optimizer* opt,
|
||||||
return TRI_ERROR_NO_ERROR;
|
return TRI_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
// TODO: finish rule and test it
|
// TODO: finish rule and test it
|
||||||
|
|
||||||
struct FilterCondition {
|
struct FilterCondition {
|
||||||
std::string variableName;
|
std::string variableName;
|
||||||
std::string attributeName;
|
std::string attributeName;
|
||||||
|
@ -1640,19 +1638,17 @@ struct FilterCondition {
|
||||||
lhs = node->getMember(1);
|
lhs = node->getMember(1);
|
||||||
rhs = node->getMember(0);
|
rhs = node->getMember(0);
|
||||||
|
|
||||||
auto it = Ast::ReverseOperators.find(static_cast<int>(node->type));
|
op = Ast::ReverseOperator(node->type);
|
||||||
TRI_ASSERT(it != Ast::ReverseOperators.end());
|
|
||||||
|
|
||||||
op = (*it).second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
TRI_ASSERT(lhs->type == NODE_TYPE_VALUE);
|
TRI_ASSERT(lhs->type == NODE_TYPE_VALUE);
|
||||||
TRI_ASSERT(rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS);
|
TRI_ASSERT(rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS);
|
||||||
|
|
||||||
std::function<void(AstNode const*)> buildName = [&] (AstNode const* node) -> void {
|
std::function<void(AstNode const*, std::string&, std::string&)> buildName =
|
||||||
|
[&] (AstNode const* node, std::string& variableName, std::string& attributeName) -> void {
|
||||||
if (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
if (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||||
buildName(node->getMember(0));
|
buildName(node->getMember(0), variableName, attributeName);
|
||||||
|
|
||||||
if (! attributeName.empty()) {
|
if (! attributeName.empty()) {
|
||||||
attributeName.push_back('.');
|
attributeName.push_back('.');
|
||||||
|
@ -1667,7 +1663,9 @@ struct FilterCondition {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (attributeName.empty()) {
|
if (attributeName.empty()) {
|
||||||
buildName(rhs);
|
TRI_ASSERT(! variableName.empty());
|
||||||
|
|
||||||
|
buildName(rhs, variableName, attributeName);
|
||||||
if (op == NODE_TYPE_OPERATOR_BINARY_EQ ||
|
if (op == NODE_TYPE_OPERATOR_BINARY_EQ ||
|
||||||
op == NODE_TYPE_OPERATOR_BINARY_NE) {
|
op == NODE_TYPE_OPERATOR_BINARY_NE) {
|
||||||
lowInclusive = true;
|
lowInclusive = true;
|
||||||
|
@ -1694,10 +1692,19 @@ struct FilterCondition {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// else if (attributeName == std::string(buffer.c_str(), buffer.length())) {
|
else {
|
||||||
|
// already have collected something, now check if the next condition
|
||||||
|
// is for the same variable / attribute
|
||||||
|
std::string compareVariableName;
|
||||||
|
std::string compareAttributeName;
|
||||||
|
buildName(rhs, compareVariableName, compareAttributeName);
|
||||||
|
|
||||||
|
if (variableName == compareVariableName &&
|
||||||
|
attributeName == compareAttributeName) {
|
||||||
// same attribute
|
// same attribute
|
||||||
// TODO
|
// TODO
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fall-through
|
// fall-through
|
||||||
}
|
}
|
||||||
|
@ -1717,7 +1724,6 @@ struct FilterCondition {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief try to remove filters which are covered by indexes
|
/// @brief try to remove filters which are covered by indexes
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1736,7 +1742,9 @@ int triagens::aql::removeFiltersCoveredByIndex (Optimizer* opt,
|
||||||
// auto outVar = cn->getVariablesSetHere();
|
// auto outVar = cn->getVariablesSetHere();
|
||||||
|
|
||||||
auto setter = plan->getVarSetBy(inVar[0]->id);
|
auto setter = plan->getVarSetBy(inVar[0]->id);
|
||||||
TRI_ASSERT(setter != nullptr);
|
if (setter == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (setter->getType() != EN::CALCULATION) {
|
if (setter->getType() != EN::CALCULATION) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -1753,9 +1761,6 @@ int triagens::aql::removeFiltersCoveredByIndex (Optimizer* opt,
|
||||||
while (current != nullptr) {
|
while (current != nullptr) {
|
||||||
if (current->getType() == EN::INDEX_RANGE) {
|
if (current->getType() == EN::INDEX_RANGE) {
|
||||||
// found an index range, now check if the expression is covered by the index
|
// found an index range, now check if the expression is covered by the index
|
||||||
auto variable = static_cast<IndexRangeNode const*>(current)->outVariable();
|
|
||||||
TRI_ASSERT(variable != nullptr);
|
|
||||||
|
|
||||||
auto const& ranges = static_cast<IndexRangeNode const*>(current)->ranges();
|
auto const& ranges = static_cast<IndexRangeNode const*>(current)->ranges();
|
||||||
|
|
||||||
// TODO: this is not prepared for OR conditions
|
// TODO: this is not prepared for OR conditions
|
||||||
|
@ -1796,7 +1801,6 @@ int triagens::aql::removeFiltersCoveredByIndex (Optimizer* opt,
|
||||||
|
|
||||||
return TRI_ERROR_NO_ERROR;
|
return TRI_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief helper to compute lots of permutation tuples
|
/// @brief helper to compute lots of permutation tuples
|
||||||
|
@ -2594,22 +2598,121 @@ int triagens::aql::undistributeRemoveAfterEnumColl (Optimizer* opt,
|
||||||
return TRI_ERROR_NO_ERROR;
|
return TRI_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief auxilliary struct for finding common nodes in OR conditions
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct CommonNodeFinder {
|
||||||
|
std::vector<AstNode const*> possibleNodes;
|
||||||
|
|
||||||
|
bool find (AstNode const* node,
|
||||||
|
AstNodeType condition,
|
||||||
|
AstNode const*& commonNode,
|
||||||
|
std::string& commonName ) {
|
||||||
|
|
||||||
|
if (node->type == NODE_TYPE_OPERATOR_BINARY_OR) {
|
||||||
|
return (find(node->getMember(0), condition, commonNode, commonName)
|
||||||
|
&& find(node->getMember(1), condition, commonNode, commonName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->type == NODE_TYPE_VALUE) {
|
||||||
|
possibleNodes.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->type == condition
|
||||||
|
|| (condition != NODE_TYPE_OPERATOR_BINARY_EQ
|
||||||
|
&& ( node->type == NODE_TYPE_OPERATOR_BINARY_LE
|
||||||
|
|| node->type == NODE_TYPE_OPERATOR_BINARY_LT
|
||||||
|
|| node->type == NODE_TYPE_OPERATOR_BINARY_GE
|
||||||
|
|| node->type == NODE_TYPE_OPERATOR_BINARY_GT ))) {
|
||||||
|
|
||||||
|
auto lhs = node->getMember(0);
|
||||||
|
auto rhs = node->getMember(1);
|
||||||
|
|
||||||
|
if (lhs->isConstant()) {
|
||||||
|
commonNode = rhs;
|
||||||
|
commonName = commonNode->toString();
|
||||||
|
possibleNodes.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rhs->isConstant()) {
|
||||||
|
commonNode = lhs;
|
||||||
|
commonName = commonNode->toString();
|
||||||
|
possibleNodes.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rhs->type == NODE_TYPE_FCALL ||
|
||||||
|
rhs->type == NODE_TYPE_FCALL_USER ||
|
||||||
|
rhs->type == NODE_TYPE_REFERENCE) {
|
||||||
|
commonNode = lhs;
|
||||||
|
commonName = commonNode->toString();
|
||||||
|
possibleNodes.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lhs->type == NODE_TYPE_FCALL ||
|
||||||
|
lhs->type == NODE_TYPE_FCALL_USER ||
|
||||||
|
lhs->type == NODE_TYPE_REFERENCE) {
|
||||||
|
commonNode = rhs;
|
||||||
|
commonName = commonNode->toString();
|
||||||
|
possibleNodes.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS ||
|
||||||
|
lhs->type == NODE_TYPE_INDEXED_ACCESS) {
|
||||||
|
if (possibleNodes.size() == 2) {
|
||||||
|
for (size_t i = 0; i < 2; i++) {
|
||||||
|
if (lhs->toString() == possibleNodes[i]->toString()) {
|
||||||
|
commonNode = possibleNodes[i];
|
||||||
|
commonName = commonNode->toString();
|
||||||
|
possibleNodes.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// don't return, must consider the other side of the condition
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
possibleNodes.push_back(lhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS ||
|
||||||
|
rhs->type == NODE_TYPE_INDEXED_ACCESS) {
|
||||||
|
if (possibleNodes.size() == 2) {
|
||||||
|
for (size_t i = 0; i < 2; i++) {
|
||||||
|
if (rhs->toString() == possibleNodes[i]->toString()) {
|
||||||
|
commonNode = possibleNodes[i];
|
||||||
|
commonName = commonNode->toString();
|
||||||
|
possibleNodes.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
possibleNodes.push_back(rhs);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
possibleNodes.clear();
|
||||||
|
return (! commonName.empty());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief auxilliary struct for the OR-to-IN conversion
|
/// @brief auxilliary struct for the OR-to-IN conversion
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
struct OrToInConverter {
|
struct OrToInConverter {
|
||||||
AstNode const* variableNode;
|
|
||||||
std::string variableName;
|
|
||||||
std::vector<AstNode const*> valueNodes;
|
|
||||||
std::vector<AstNode const*> possibleNodes;
|
|
||||||
std::vector<AstNode const*> orConditions;
|
|
||||||
|
|
||||||
std::string getString (AstNode const* node) {
|
std::vector<AstNode const*> valueNodes;
|
||||||
triagens::basics::StringBuffer buffer(TRI_UNKNOWN_MEM_ZONE);
|
CommonNodeFinder finder;
|
||||||
node->stringify(&buffer, false);
|
AstNode const* commonNode = nullptr;
|
||||||
return std::string(buffer.c_str(), buffer.length());
|
std::string commonName;
|
||||||
}
|
|
||||||
|
|
||||||
AstNode* buildInExpression (Ast* ast) {
|
AstNode* buildInExpression (Ast* ast) {
|
||||||
// the list of comparison values
|
// the list of comparison values
|
||||||
|
@ -2620,115 +2723,37 @@ struct OrToInConverter {
|
||||||
|
|
||||||
// return a new IN operator node
|
// return a new IN operator node
|
||||||
return ast->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_IN,
|
return ast->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_IN,
|
||||||
variableNode->clone(ast),
|
commonNode->clone(ast),
|
||||||
list);
|
list);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool flattenOr (AstNode const* node) {
|
|
||||||
if (node->type == NODE_TYPE_OPERATOR_BINARY_OR) {
|
|
||||||
return (flattenOr(node->getMember(0)) && flattenOr(node->getMember(1)));
|
|
||||||
}
|
|
||||||
if (node->type == NODE_TYPE_OPERATOR_BINARY_EQ) {
|
|
||||||
orConditions.push_back(node);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (node->type == NODE_TYPE_VALUE) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool findCommonNode (AstNode const* node) {
|
|
||||||
|
|
||||||
if (! flattenOr(node)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
TRI_ASSERT(orConditions.size() > 1);
|
|
||||||
|
|
||||||
for (AstNode const* n: orConditions) {
|
|
||||||
|
|
||||||
auto lhs = n->getMember(0);
|
|
||||||
auto rhs = n->getMember(1);
|
|
||||||
|
|
||||||
if (lhs->isConstant()) {
|
|
||||||
variableNode = rhs;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rhs->isConstant()) {
|
|
||||||
variableNode = lhs;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rhs->type == NODE_TYPE_FCALL ||
|
|
||||||
rhs->type == NODE_TYPE_FCALL_USER ||
|
|
||||||
rhs->type == NODE_TYPE_REFERENCE) {
|
|
||||||
variableNode = lhs;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lhs->type == NODE_TYPE_FCALL ||
|
|
||||||
lhs->type == NODE_TYPE_FCALL_USER ||
|
|
||||||
lhs->type == NODE_TYPE_REFERENCE) {
|
|
||||||
variableNode = rhs;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS ||
|
|
||||||
lhs->type == NODE_TYPE_INDEXED_ACCESS) {
|
|
||||||
if (possibleNodes.size() == 2) {
|
|
||||||
for (size_t i = 0; i < 2; i++) {
|
|
||||||
if (getString(lhs) == getString(possibleNodes[i])) {
|
|
||||||
variableNode = possibleNodes[i];
|
|
||||||
variableName = getString(variableNode);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
possibleNodes.push_back(lhs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS ||
|
|
||||||
rhs->type == NODE_TYPE_INDEXED_ACCESS) {
|
|
||||||
if (possibleNodes.size() == 2) {
|
|
||||||
for (size_t i = 0; i < 2; i++) {
|
|
||||||
if (getString(rhs) == getString(possibleNodes[i])) {
|
|
||||||
variableNode = possibleNodes[i];
|
|
||||||
variableName = getString(variableNode);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
possibleNodes.push_back(rhs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool canConvertExpression (AstNode const* node) {
|
bool canConvertExpression (AstNode const* node) {
|
||||||
|
if(finder.find(node, NODE_TYPE_OPERATOR_BINARY_EQ, commonNode, commonName)){
|
||||||
|
return canConvertExpressionWalker(node);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canConvertExpressionWalker (AstNode const* node) {
|
||||||
if (node->type == NODE_TYPE_OPERATOR_BINARY_OR) {
|
if (node->type == NODE_TYPE_OPERATOR_BINARY_OR) {
|
||||||
return (canConvertExpression(node->getMember(0)) &&
|
return (canConvertExpressionWalker(node->getMember(0)) &&
|
||||||
canConvertExpression(node->getMember(1)));
|
canConvertExpressionWalker(node->getMember(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node->type == NODE_TYPE_OPERATOR_BINARY_EQ) {
|
if (node->type == NODE_TYPE_OPERATOR_BINARY_EQ) {
|
||||||
auto lhs = node->getMember(0);
|
auto lhs = node->getMember(0);
|
||||||
auto rhs = node->getMember(1);
|
auto rhs = node->getMember(1);
|
||||||
|
|
||||||
if (canConvertExpression(rhs) && ! canConvertExpression(lhs)) {
|
if (canConvertExpressionWalker(rhs) && ! canConvertExpressionWalker(lhs)) {
|
||||||
valueNodes.push_back(lhs);
|
valueNodes.push_back(lhs);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canConvertExpression(lhs) && ! canConvertExpression(rhs)) {
|
if (canConvertExpressionWalker(lhs) && ! canConvertExpressionWalker(rhs)) {
|
||||||
valueNodes.push_back(rhs);
|
valueNodes.push_back(rhs);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// if canConvertExpression(lhs) and canConvertExpression(rhs), then one of
|
// if canConvertExpressionWalker(lhs) and canConvertExpressionWalker(rhs), then one of
|
||||||
// the equalities in the OR statement is of the form x == x
|
// the equalities in the OR statement is of the form x == x
|
||||||
// fall-through intentional
|
// fall-through intentional
|
||||||
}
|
}
|
||||||
|
@ -2736,8 +2761,7 @@ struct OrToInConverter {
|
||||||
node->type == NODE_TYPE_ATTRIBUTE_ACCESS ||
|
node->type == NODE_TYPE_ATTRIBUTE_ACCESS ||
|
||||||
node->type == NODE_TYPE_INDEXED_ACCESS) {
|
node->type == NODE_TYPE_INDEXED_ACCESS) {
|
||||||
// get a string representation of the node for comparisons
|
// get a string representation of the node for comparisons
|
||||||
std::string nodeString = getString(node);
|
return (node->toString() == commonName);
|
||||||
return nodeString == getString(variableNode);
|
|
||||||
} else if (node->isBoolValue()) {
|
} else if (node->isBoolValue()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2784,8 +2808,7 @@ int triagens::aql::replaceOrWithIn (Optimizer* opt,
|
||||||
}
|
}
|
||||||
|
|
||||||
OrToInConverter converter;
|
OrToInConverter converter;
|
||||||
if (converter.findCommonNode(cn->expression()->node())
|
if (converter.canConvertExpression(cn->expression()->node())) {
|
||||||
&& converter.canConvertExpression(cn->expression()->node())) {
|
|
||||||
Expression* expr = nullptr;
|
Expression* expr = nullptr;
|
||||||
ExecutionNode* newNode = nullptr;
|
ExecutionNode* newNode = nullptr;
|
||||||
auto inNode = converter.buildInExpression(plan->getAst());
|
auto inNode = converter.buildInExpression(plan->getAst());
|
||||||
|
@ -2821,6 +2844,199 @@ int triagens::aql::replaceOrWithIn (Optimizer* opt,
|
||||||
LEAVE_BLOCK;
|
LEAVE_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RemoveRedundantOr {
|
||||||
|
AstNode const* bestValue = nullptr;
|
||||||
|
AstNodeType comparison;
|
||||||
|
bool inclusive;
|
||||||
|
bool isComparisonSet = false;
|
||||||
|
CommonNodeFinder finder;
|
||||||
|
AstNode const* commonNode = nullptr;
|
||||||
|
std::string commonName;
|
||||||
|
|
||||||
|
AstNode* createReplacementNode (Ast* ast) {
|
||||||
|
TRI_ASSERT(commonNode != nullptr);
|
||||||
|
TRI_ASSERT(bestValue != nullptr);
|
||||||
|
TRI_ASSERT(isComparisonSet == true);
|
||||||
|
return ast->createNodeBinaryOperator(comparison, commonNode->clone(ast),
|
||||||
|
bestValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isInclusiveBound (AstNodeType type) {
|
||||||
|
return (type == NODE_TYPE_OPERATOR_BINARY_GE || type == NODE_TYPE_OPERATOR_BINARY_LE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int isCompatibleBound (AstNodeType type, AstNode const* value) {
|
||||||
|
|
||||||
|
if ((comparison == NODE_TYPE_OPERATOR_BINARY_LE
|
||||||
|
|| comparison == NODE_TYPE_OPERATOR_BINARY_LT) &&
|
||||||
|
(type == NODE_TYPE_OPERATOR_BINARY_LE
|
||||||
|
|| type == NODE_TYPE_OPERATOR_BINARY_LT)) {
|
||||||
|
return -1; //high bound
|
||||||
|
}
|
||||||
|
else if ((comparison == NODE_TYPE_OPERATOR_BINARY_GE
|
||||||
|
|| comparison == NODE_TYPE_OPERATOR_BINARY_GT) &&
|
||||||
|
(type == NODE_TYPE_OPERATOR_BINARY_GE
|
||||||
|
|| type == NODE_TYPE_OPERATOR_BINARY_GT)) {
|
||||||
|
return 1; //low bound
|
||||||
|
}
|
||||||
|
return 0; //incompatible bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns false if the existing value is better and true if the input value is
|
||||||
|
// better
|
||||||
|
bool compareBounds(AstNodeType type, AstNode const* value, int lowhigh) {
|
||||||
|
|
||||||
|
int cmp = CompareAstNodes(bestValue, value);
|
||||||
|
|
||||||
|
if (cmp == 0 && (isInclusiveBound(comparison) != isInclusiveBound(type))) {
|
||||||
|
return (isInclusiveBound(type) ? true : false);
|
||||||
|
}
|
||||||
|
return (cmp * lowhigh == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasRedundantCondition (AstNode const* node) {
|
||||||
|
if (finder.find(node, NODE_TYPE_OPERATOR_BINARY_LT, commonNode, commonName)) {
|
||||||
|
return hasRedundantConditionWalker(node);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasRedundantConditionWalker (AstNode const* node) {
|
||||||
|
AstNodeType type = node->type;
|
||||||
|
|
||||||
|
if (type == NODE_TYPE_OPERATOR_BINARY_OR) {
|
||||||
|
return (hasRedundantConditionWalker(node->getMember(0)) &&
|
||||||
|
hasRedundantConditionWalker(node->getMember(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == NODE_TYPE_OPERATOR_BINARY_LE
|
||||||
|
|| type == NODE_TYPE_OPERATOR_BINARY_LT
|
||||||
|
|| type == NODE_TYPE_OPERATOR_BINARY_GE
|
||||||
|
|| type == NODE_TYPE_OPERATOR_BINARY_GT) {
|
||||||
|
|
||||||
|
auto lhs = node->getMember(0);
|
||||||
|
auto rhs = node->getMember(1);
|
||||||
|
|
||||||
|
if (hasRedundantConditionWalker(rhs)
|
||||||
|
&& ! hasRedundantConditionWalker(lhs)
|
||||||
|
&& lhs->isConstant()) {
|
||||||
|
|
||||||
|
if (! isComparisonSet) {
|
||||||
|
comparison = Ast::ReverseOperator(type);
|
||||||
|
bestValue = lhs;
|
||||||
|
isComparisonSet = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lowhigh = isCompatibleBound(Ast::ReverseOperator(type), lhs);
|
||||||
|
if (lowhigh == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compareBounds(type, lhs, lowhigh)) {
|
||||||
|
comparison = Ast::ReverseOperator(type);
|
||||||
|
bestValue = lhs;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hasRedundantConditionWalker(lhs)
|
||||||
|
&& ! hasRedundantConditionWalker(rhs)
|
||||||
|
&& rhs->isConstant()) {
|
||||||
|
if (! isComparisonSet) {
|
||||||
|
comparison = type;
|
||||||
|
bestValue = rhs;
|
||||||
|
isComparisonSet = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lowhigh = isCompatibleBound(type, rhs);
|
||||||
|
if (lowhigh == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compareBounds(type, rhs, lowhigh)) {
|
||||||
|
comparison = type;
|
||||||
|
bestValue = rhs;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// if hasRedundantConditionWalker(lhs) and
|
||||||
|
// hasRedundantConditionWalker(rhs), then one of the conditions in the OR
|
||||||
|
// statement is of the form x == x fall-through intentional
|
||||||
|
}
|
||||||
|
else if (type == NODE_TYPE_REFERENCE ||
|
||||||
|
type == NODE_TYPE_ATTRIBUTE_ACCESS ||
|
||||||
|
type == NODE_TYPE_INDEXED_ACCESS) {
|
||||||
|
// get a string representation of the node for comparisons
|
||||||
|
return (node->toString() == commonName);
|
||||||
|
}
|
||||||
|
else if (node->isBoolValue()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int triagens::aql::removeRedundantOr (Optimizer* opt,
|
||||||
|
ExecutionPlan* plan,
|
||||||
|
Optimizer::Rule const* rule) {
|
||||||
|
ENTER_BLOCK;
|
||||||
|
std::vector<ExecutionNode*> nodes
|
||||||
|
= plan->findNodesOfType(EN::FILTER, true);
|
||||||
|
|
||||||
|
bool modified = false;
|
||||||
|
for (auto n : nodes) {
|
||||||
|
auto deps = n->getDependencies();
|
||||||
|
TRI_ASSERT(deps.size() == 1);
|
||||||
|
if (deps[0]->getType() != EN::CALCULATION) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fn = static_cast<FilterNode*>(n);
|
||||||
|
auto cn = static_cast<CalculationNode*>(deps[0]);
|
||||||
|
|
||||||
|
auto inVar = fn->getVariablesUsedHere();
|
||||||
|
auto outVar = cn->getVariablesSetHere();
|
||||||
|
|
||||||
|
if (outVar.size() != 1 || outVar[0]->id != inVar[0]->id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cn->expression()->node()->type != NODE_TYPE_OPERATOR_BINARY_OR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveRedundantOr remover;
|
||||||
|
if (remover.hasRedundantCondition(cn->expression()->node())) {
|
||||||
|
Expression* expr = nullptr;
|
||||||
|
ExecutionNode* newNode = nullptr;
|
||||||
|
auto astNode = remover.createReplacementNode(plan->getAst());
|
||||||
|
|
||||||
|
expr = new Expression(plan->getAst(), astNode);
|
||||||
|
|
||||||
|
try {
|
||||||
|
newNode = new CalculationNode(plan, plan->nextId(), expr, outVar[0]);
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
delete expr;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
plan->registerNode(newNode);
|
||||||
|
plan->replaceNode(cn, newNode);
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modified) {
|
||||||
|
plan->findVarUsage();
|
||||||
|
}
|
||||||
|
opt->addPlan(plan, rule->level, modified);
|
||||||
|
|
||||||
|
return TRI_ERROR_NO_ERROR;
|
||||||
|
LEAVE_BLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
// Local Variables:
|
// Local Variables:
|
||||||
// mode: outline-minor
|
// mode: outline-minor
|
||||||
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
|
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
|
||||||
|
|
|
@ -188,6 +188,8 @@ namespace triagens {
|
||||||
|
|
||||||
int replaceOrWithIn (Optimizer*, ExecutionPlan*, Optimizer::Rule const*);
|
int replaceOrWithIn (Optimizer*, ExecutionPlan*, Optimizer::Rule const*);
|
||||||
|
|
||||||
|
int removeRedundantOr (Optimizer*, ExecutionPlan*, Optimizer::Rule const*);
|
||||||
|
|
||||||
} // namespace aql
|
} // namespace aql
|
||||||
} // namespace triagens
|
} // namespace triagens
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,692 @@
|
||||||
|
/*jshint strict: false, maxlen: 500 */
|
||||||
|
/*global require, assertEqual, assertTrue, AQL_EXPLAIN, AQL_EXECUTE */
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief tests for Ahuacatl, skiplist index queries
|
||||||
|
///
|
||||||
|
/// @file
|
||||||
|
///
|
||||||
|
/// DISCLAIMER
|
||||||
|
///
|
||||||
|
/// Copyright 2010-2012 triagens 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 triAGENS GmbH, Cologne, Germany
|
||||||
|
///
|
||||||
|
/// @author
|
||||||
|
/// @author Copyright 2014, triAGENS GmbH, Cologne, Germany
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TODO add some test which don't use number values!
|
||||||
|
|
||||||
|
var jsunity = require("jsunity");
|
||||||
|
var helper = require("org/arangodb/aql-helper");
|
||||||
|
var getQueryResults = helper.getQueryResults;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test suite
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function NewAqlRemoveRedundantORTestSuite () {
|
||||||
|
var ruleName = "remove-redundant-or";
|
||||||
|
|
||||||
|
var isRuleUsed = function (query, params) {
|
||||||
|
var result = AQL_EXPLAIN(query, params, { optimizer: { rules: [ "-all", "+" + ruleName ] } });
|
||||||
|
assertTrue(result.plan.rules.indexOf(ruleName) !== -1, query);
|
||||||
|
result = AQL_EXPLAIN(query, params, { optimizer: { rules: [ "-all" ] } });
|
||||||
|
assertTrue(result.plan.rules.indexOf(ruleName) === -1, query);
|
||||||
|
};
|
||||||
|
|
||||||
|
var ruleIsNotUsed = function (query, params) {
|
||||||
|
var result = AQL_EXPLAIN(query, params, { optimizer: { rules: [ "-all", "+" + ruleName ] } });
|
||||||
|
assertTrue(result.plan.rules.indexOf(ruleName) === -1, query);
|
||||||
|
};
|
||||||
|
|
||||||
|
var executeWithRule = function (query, params) {
|
||||||
|
return AQL_EXECUTE(query, params, { optimizer: { rules: [ "-all", "+" + ruleName ] } }).json;
|
||||||
|
};
|
||||||
|
|
||||||
|
var executeWithoutRule = function (query, params) {
|
||||||
|
return AQL_EXECUTE(query, params, { optimizer: { rules: [ "-all" ] } }).json;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief set up
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
setUp : function () {
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief tear down
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
tearDown : function () {
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test the rule fires for actual values
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testFiresGtGt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i > 1 || i > 2 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresGtLt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] FILTER i > 1 || 2 < i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLtLt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] FILTER 1 < i || 2 < i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLtGt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] FILTER 1 < i || i > 2 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testFiresGtGe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i > 1 || i >= 2 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresGtLe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i > 1 || 2 <= i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLtLe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 < i || 2 <= i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLtGe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 < i || i >= 2 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testFiresGeGt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || i > 2 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresGeLt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || 2 < i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLeLt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || 2 < i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLeGt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || i > 2 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testFiresGeGe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || i >= 2 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresGeLe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || 2 <= i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLeLe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || 2 <= i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLeGe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || i >= 2 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testFiresGtGt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i > 1 || i > 1 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresGtLt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] FILTER i > 1 || 1 < i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLtLt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] FILTER 1 < i || 1 < i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLtGt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] FILTER 1 < i || i > 1 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testFiresGtGe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i > 1 || i >= 1 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresGtLe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i > 1 || 1 <= i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLtLe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] FILTER 1 < i || 1 <= i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLtGe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 < i || i >= 1 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testFiresGeGt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || i > 1 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresGeLt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || 1 < i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLeLt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || 1 < i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLeGt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || i > 1 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testFiresGeGe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || i >= 1 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresGeLe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || 1 <= i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLeLe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || 1 <= i RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
testFiresLeGe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || i >= 1 RETURN i";
|
||||||
|
|
||||||
|
isRuleUsed(query, {});
|
||||||
|
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
|
||||||
|
var actual = getQueryResults(query);
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
assertEqual(executeWithRule(query, {}), executeWithoutRule(query, {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testDudGtGt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i > 1 || 2 > i RETURN i";
|
||||||
|
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudGtLt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] FILTER i > 1 || i < 2 RETURN i";
|
||||||
|
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLtLt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] FILTER 1 < i || i < 2 RETURN i";
|
||||||
|
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLtGt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] FILTER 1 < i || 2 > i RETURN i";
|
||||||
|
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testDudGtGe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i > 1 || 2 >= i RETURN i";
|
||||||
|
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudGtLe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i > 1 || i <= 2 RETURN i";
|
||||||
|
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLtLe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 < i || i <= 2 RETURN i";
|
||||||
|
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLtGe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 < i || 2 >= i RETURN i";
|
||||||
|
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testDudGeGt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || 2 > i RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudGeLt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || i < 2 RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLeLt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || i < 2 RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLeGt1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || 2 > i RETURN i";
|
||||||
|
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testDudGeGe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || 2 >= i RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudGeLe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || i <= 2 RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLeLe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || i <= 2 RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLeGe1 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || 2 >= i RETURN i";
|
||||||
|
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testDudGtGt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i > 1 || i < 1 RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudGtLt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] FILTER i > 1 || 1 > i RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLtLt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] FILTER 1 < i || 1 > i RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLtGt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] FILTER 1 < i || i < 1 RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testDudGtGe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i > 1 || 1 >= i RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudGtLe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i > 1 || i <= 1 RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLtLe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] FILTER 1 < i || i <= 1 RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLtGe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 < i || 1 >= i RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testDudGeGt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || 1 > i RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudGeLt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || i < 1 RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLeLt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || i < 1 RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLeGt2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || 1 > i RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testDudGeGe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || 1 >= i RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudGeLe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER i >= 1 || i <= 1 RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLeLe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || i <= 1 RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
testDudLeGe2 : function () {
|
||||||
|
var query = "FOR i IN [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] "
|
||||||
|
+ " FILTER 1 <= i || 1 >= i RETURN i";
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
jsunity.run(NewAqlRemoveRedundantORTestSuite);
|
||||||
|
|
||||||
|
return jsunity.done();
|
||||||
|
|
|
@ -39,7 +39,7 @@ var getQueryResults = helper.getQueryResults;
|
||||||
|
|
||||||
function NewAqlReplaceORWithINTestSuite () {
|
function NewAqlReplaceORWithINTestSuite () {
|
||||||
var replace;
|
var replace;
|
||||||
var ruleName = "replace-OR-with-IN";
|
var ruleName = "replace-or-with-in";
|
||||||
|
|
||||||
var isRuleUsed = function (query, params) {
|
var isRuleUsed = function (query, params) {
|
||||||
var result = AQL_EXPLAIN(query, params, { optimizer: { rules: [ "-all", "+" + ruleName ] } });
|
var result = AQL_EXPLAIN(query, params, { optimizer: { rules: [ "-all", "+" + ruleName ] } });
|
||||||
|
@ -389,16 +389,19 @@ function NewAqlReplaceORWithINTestSuite () {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
testFiresCommonConstant: function () {
|
testDudCommonConstant1: function () {
|
||||||
var query = "LET x = {a:@a} FOR v IN " + replace.name()
|
var query = "LET x = {a:@a} FOR v IN " + replace.name()
|
||||||
+ " FILTER x.a == v.value || x.a == v._key RETURN v._key";
|
+ " FILTER x.a == v.value || x.a == v._key RETURN v._key";
|
||||||
|
|
||||||
var key = replace.any()._key;
|
var key = replace.any()._key;
|
||||||
isRuleUsed(query, {a: key});
|
ruleIsNotUsed(query, {a: key});
|
||||||
|
},
|
||||||
|
|
||||||
var actual = getQueryResults(query, {a: key});
|
testDudCommonConstant2: function () {
|
||||||
assertEqual(key, actual.toString());
|
var query = "LET x = {a:1} FOR v IN " + replace.name()
|
||||||
|
+ " FILTER x.a == v.value || x.a == v._key RETURN v._key";
|
||||||
|
|
||||||
|
ruleIsNotUsed(query, {});
|
||||||
},
|
},
|
||||||
|
|
||||||
testDudAlwaysTrue: function () {
|
testDudAlwaysTrue: function () {
|
||||||
|
|
|
@ -203,9 +203,12 @@ int TRI_CompareValuesJson (TRI_json_t const* lhs,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TRI_ASSERT(lWeight == rWeight);
|
||||||
|
|
||||||
// lhs and rhs have equal weights
|
// lhs and rhs have equal weights
|
||||||
if (lhs == nullptr) {
|
if (lhs == nullptr) {
|
||||||
// both lhs and rhs are NULL, so they are equal
|
// both lhs and rhs are NULL, so they are equal
|
||||||
|
TRI_ASSERT(rhs == nullptr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue