mirror of https://gitee.com/bigwinds/arangodb
fixed some potential leaks
This commit is contained in:
parent
f09d8903a9
commit
4a8146ed02
|
@ -482,17 +482,13 @@ bool ExecutionBlock::getBlock (size_t atLeast, size_t atMost) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
TRI_IF_FAILURE("ExecutionBlock::getBlock") {
|
||||||
TRI_IF_FAILURE("ExecutionBlock::getBlock") {
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
|
||||||
}
|
|
||||||
_buffer.emplace_back(docs.get());
|
|
||||||
docs.release();
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_buffer.emplace_back(docs.get());
|
||||||
|
docs.release();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,6 +504,7 @@ AqlItemBlock* ExecutionBlock::getSomeWithoutRegisterClearout (size_t atLeast,
|
||||||
size_t atMost) {
|
size_t atMost) {
|
||||||
TRI_ASSERT(0 < atLeast && atLeast <= atMost);
|
TRI_ASSERT(0 < atLeast && atLeast <= atMost);
|
||||||
size_t skipped = 0;
|
size_t skipped = 0;
|
||||||
|
|
||||||
AqlItemBlock* result = nullptr;
|
AqlItemBlock* result = nullptr;
|
||||||
int out = getOrSkipSome(atLeast, atMost, false, result, skipped);
|
int out = getOrSkipSome(atLeast, atMost, false, result, skipped);
|
||||||
|
|
||||||
|
@ -528,12 +525,16 @@ void ExecutionBlock::clearRegisters (AqlItemBlock* result) {
|
||||||
size_t ExecutionBlock::skipSome (size_t atLeast, size_t atMost) {
|
size_t ExecutionBlock::skipSome (size_t atLeast, size_t atMost) {
|
||||||
TRI_ASSERT(0 < atLeast && atLeast <= atMost);
|
TRI_ASSERT(0 < atLeast && atLeast <= atMost);
|
||||||
size_t skipped = 0;
|
size_t skipped = 0;
|
||||||
|
|
||||||
AqlItemBlock* result = nullptr;
|
AqlItemBlock* result = nullptr;
|
||||||
int out = getOrSkipSome(atLeast, atMost, true, result, skipped);
|
int out = getOrSkipSome(atLeast, atMost, true, result, skipped);
|
||||||
|
|
||||||
TRI_ASSERT(result == nullptr);
|
TRI_ASSERT(result == nullptr);
|
||||||
|
|
||||||
if (out != TRI_ERROR_NO_ERROR) {
|
if (out != TRI_ERROR_NO_ERROR) {
|
||||||
THROW_ARANGO_EXCEPTION(out);
|
THROW_ARANGO_EXCEPTION(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
return skipped;
|
return skipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,12 +583,13 @@ int ExecutionBlock::getOrSkipSome (size_t atLeast,
|
||||||
size_t& skipped) {
|
size_t& skipped) {
|
||||||
|
|
||||||
TRI_ASSERT(result == nullptr && skipped == 0);
|
TRI_ASSERT(result == nullptr && skipped == 0);
|
||||||
|
|
||||||
if (_done) {
|
if (_done) {
|
||||||
return TRI_ERROR_NO_ERROR;
|
return TRI_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if _buffer.size() is > 0 then _pos points to a valid place . . .
|
// if _buffer.size() is > 0 then _pos points to a valid place . . .
|
||||||
vector<AqlItemBlock*> collector;
|
std::vector<AqlItemBlock*> collector;
|
||||||
|
|
||||||
auto freeCollector = [&collector]() {
|
auto freeCollector = [&collector]() {
|
||||||
for (auto x : collector) {
|
for (auto x : collector) {
|
||||||
|
@ -635,7 +637,7 @@ int ExecutionBlock::getOrSkipSome (size_t atLeast,
|
||||||
// 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()));
|
std::unique_ptr<AqlItemBlock> more(cur->slice(_pos, cur->size()));
|
||||||
|
|
||||||
TRI_IF_FAILURE("ExecutionBlock::getOrSkipSome2") {
|
TRI_IF_FAILURE("ExecutionBlock::getOrSkipSome2") {
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
|
@ -653,6 +655,8 @@ int ExecutionBlock::getOrSkipSome (size_t atLeast,
|
||||||
// The current block fits into our result and is fresh:
|
// The current block fits into our result and is fresh:
|
||||||
skipped += cur->size();
|
skipped += cur->size();
|
||||||
if (! skipping) {
|
if (! skipping) {
|
||||||
|
// if any of the following statements throw, then cur is not lost,
|
||||||
|
// as it is still contained in _buffer
|
||||||
TRI_IF_FAILURE("ExecutionBlock::getOrSkipSome3") {
|
TRI_IF_FAILURE("ExecutionBlock::getOrSkipSome3") {
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
}
|
}
|
||||||
|
@ -671,6 +675,8 @@ int ExecutionBlock::getOrSkipSome (size_t atLeast,
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TRI_ASSERT(result == nullptr);
|
||||||
|
|
||||||
if (! skipping) {
|
if (! skipping) {
|
||||||
if (collector.size() == 1) {
|
if (collector.size() == 1) {
|
||||||
result = collector[0];
|
result = collector[0];
|
||||||
|
@ -2941,8 +2947,7 @@ void CalculationBlock::doEvaluation (AqlItemBlock* result) {
|
||||||
AqlItemBlock* CalculationBlock::getSome (size_t atLeast,
|
AqlItemBlock* CalculationBlock::getSome (size_t atLeast,
|
||||||
size_t atMost) {
|
size_t atMost) {
|
||||||
|
|
||||||
unique_ptr<AqlItemBlock> res(ExecutionBlock::getSomeWithoutRegisterClearout(
|
std::unique_ptr<AqlItemBlock> res(ExecutionBlock::getSomeWithoutRegisterClearout(atLeast, atMost));
|
||||||
atLeast, atMost));
|
|
||||||
|
|
||||||
if (res.get() == nullptr) {
|
if (res.get() == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -3023,6 +3028,11 @@ AqlItemBlock* SubqueryBlock::getSome (size_t atLeast,
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// initial subquery execution or subquery is not constant
|
// initial subquery execution or subquery is not constant
|
||||||
|
|
||||||
|
// prevent accidental double-freeing in case of exception
|
||||||
|
subqueryResults = nullptr;
|
||||||
|
|
||||||
|
// and execute the subquery
|
||||||
subqueryResults = executeSubquery();
|
subqueryResults = executeSubquery();
|
||||||
TRI_ASSERT(subqueryResults != nullptr);
|
TRI_ASSERT(subqueryResults != nullptr);
|
||||||
|
|
||||||
|
@ -3039,8 +3049,8 @@ AqlItemBlock* SubqueryBlock::getSome (size_t atLeast,
|
||||||
}
|
}
|
||||||
|
|
||||||
throwIfKilled(); // check if we were aborted
|
throwIfKilled(); // check if we were aborted
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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();
|
||||||
|
@ -3179,7 +3189,8 @@ int FilterBlock::getOrSkipSome (size_t atLeast,
|
||||||
}
|
}
|
||||||
|
|
||||||
// if _buffer.size() is > 0 then _pos is valid
|
// if _buffer.size() is > 0 then _pos is valid
|
||||||
vector<AqlItemBlock*> collector;
|
std::vector<AqlItemBlock*> collector;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (skipped < atLeast) {
|
while (skipped < atLeast) {
|
||||||
if (_buffer.empty()) {
|
if (_buffer.empty()) {
|
||||||
|
@ -3196,8 +3207,7 @@ int FilterBlock::getOrSkipSome (size_t atLeast,
|
||||||
if (_chosen.size() - _pos + skipped > atMost) {
|
if (_chosen.size() - _pos + skipped > atMost) {
|
||||||
// The current block of chosen ones is too large for atMost:
|
// The current block of chosen ones is too large for atMost:
|
||||||
if (! skipping) {
|
if (! skipping) {
|
||||||
unique_ptr<AqlItemBlock> more(cur->slice(_chosen,
|
std::unique_ptr<AqlItemBlock> more(cur->slice(_chosen, _pos, _pos + (atMost - skipped)));
|
||||||
_pos, _pos + (atMost - skipped)));
|
|
||||||
|
|
||||||
TRI_IF_FAILURE("FilterBlock::getOrSkipSome1") {
|
TRI_IF_FAILURE("FilterBlock::getOrSkipSome1") {
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
|
@ -3213,7 +3223,7 @@ int FilterBlock::getOrSkipSome (size_t atLeast,
|
||||||
// The current block fits into our result, but it is already
|
// The current block fits into our result, but it is already
|
||||||
// half-eaten or needs to be copied anyway:
|
// half-eaten or needs to be copied anyway:
|
||||||
if (! skipping) {
|
if (! skipping) {
|
||||||
unique_ptr<AqlItemBlock> more(cur->steal(_chosen, _pos, _chosen.size()));
|
std::unique_ptr<AqlItemBlock> more(cur->steal(_chosen, _pos, _chosen.size()));
|
||||||
|
|
||||||
TRI_IF_FAILURE("FilterBlock::getOrSkipSome2") {
|
TRI_IF_FAILURE("FilterBlock::getOrSkipSome2") {
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
|
@ -3233,6 +3243,8 @@ int FilterBlock::getOrSkipSome (size_t atLeast,
|
||||||
// takes them all, so we can just hand it on:
|
// takes them all, so we can just hand it on:
|
||||||
skipped += cur->size();
|
skipped += cur->size();
|
||||||
if (! skipping) {
|
if (! skipping) {
|
||||||
|
// if any of the following statements throw, then cur is not lost,
|
||||||
|
// as it is still contained in _buffer
|
||||||
TRI_IF_FAILURE("FilterBlock::getOrSkipSome3") {
|
TRI_IF_FAILURE("FilterBlock::getOrSkipSome3") {
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
}
|
}
|
||||||
|
@ -3408,7 +3420,7 @@ int SortedAggregateBlock::getOrSkipSome (size_t atLeast,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool const isTotalAggregation = _aggregateRegisters.empty();
|
bool const isTotalAggregation = _aggregateRegisters.empty();
|
||||||
unique_ptr<AqlItemBlock> res;
|
std::unique_ptr<AqlItemBlock> res;
|
||||||
|
|
||||||
if (_buffer.empty()) {
|
if (_buffer.empty()) {
|
||||||
if (! ExecutionBlock::getBlock(atLeast, atMost)) {
|
if (! ExecutionBlock::getBlock(atLeast, atMost)) {
|
||||||
|
@ -3514,7 +3526,17 @@ int SortedAggregateBlock::getOrSkipSome (size_t atLeast,
|
||||||
bool hasMore = ! _buffer.empty();
|
bool hasMore = ! _buffer.empty();
|
||||||
|
|
||||||
if (! hasMore) {
|
if (! hasMore) {
|
||||||
hasMore = ExecutionBlock::getBlock(atLeast, atMost);
|
try {
|
||||||
|
TRI_IF_FAILURE("SortedAggregateBlock::hasMore") {
|
||||||
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
|
}
|
||||||
|
hasMore = ExecutionBlock::getBlock(atLeast, atMost);
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
// prevent leak
|
||||||
|
delete cur;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! hasMore) {
|
if (! hasMore) {
|
||||||
|
@ -3534,14 +3556,12 @@ int SortedAggregateBlock::getOrSkipSome (size_t atLeast,
|
||||||
++skipped;
|
++skipped;
|
||||||
}
|
}
|
||||||
delete cur;
|
delete cur;
|
||||||
cur = nullptr;
|
|
||||||
_done = true;
|
_done = true;
|
||||||
result = res.release();
|
result = res.release();
|
||||||
return TRI_ERROR_NO_ERROR;
|
return TRI_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
delete cur;
|
delete cur;
|
||||||
cur = nullptr;
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3719,7 +3739,6 @@ int HashedAggregateBlock::getOrSkipSome (size_t atLeast,
|
||||||
GroupKeyHash(_trx, colls),
|
GroupKeyHash(_trx, colls),
|
||||||
GroupKeyEqual(_trx, colls)
|
GroupKeyEqual(_trx, colls)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
auto buildResult = [&] (AqlItemBlock const* src) {
|
auto buildResult = [&] (AqlItemBlock const* src) {
|
||||||
auto planNode = static_cast<AggregateNode const*>(getPlanNode());
|
auto planNode = static_cast<AggregateNode const*>(getPlanNode());
|
||||||
|
@ -3742,12 +3761,13 @@ int HashedAggregateBlock::getOrSkipSome (size_t atLeast,
|
||||||
|
|
||||||
size_t row = 0;
|
size_t row = 0;
|
||||||
for (auto const& it : allGroups) {
|
for (auto const& it : allGroups) {
|
||||||
auto const& keys = it.first;
|
auto& keys = it.first;
|
||||||
|
|
||||||
TRI_ASSERT_EXPENSIVE(keys.size() == n);
|
TRI_ASSERT_EXPENSIVE(keys.size() == n);
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (auto const& key : keys) {
|
for (auto& key : keys) {
|
||||||
result->setValue(row, _aggregateRegisters[i++].first, key);
|
result->setValue(row, _aggregateRegisters[i++].first, key);
|
||||||
|
const_cast<AqlValue*>(&key)->erase(); // to prevent double-freeing later
|
||||||
}
|
}
|
||||||
|
|
||||||
if (planNode->_count) {
|
if (planNode->_count) {
|
||||||
|
@ -3770,76 +3790,88 @@ int HashedAggregateBlock::getOrSkipSome (size_t atLeast,
|
||||||
std::vector<AqlValue> group;
|
std::vector<AqlValue> group;
|
||||||
group.reserve(n);
|
group.reserve(n);
|
||||||
|
|
||||||
while (skipped < atMost) {
|
try {
|
||||||
groupValues.clear();
|
while (skipped < atMost) {
|
||||||
|
groupValues.clear();
|
||||||
|
|
||||||
// for hashing simply re-use the aggregate registers, without cloning their contents
|
// for hashing simply re-use the aggregate registers, without cloning their contents
|
||||||
for (size_t i = 0; i < n; ++i) {
|
|
||||||
groupValues.emplace_back(cur->getValueReference(_pos, _aggregateRegisters[i].second));
|
|
||||||
}
|
|
||||||
|
|
||||||
// now check if we already know this group
|
|
||||||
auto it = allGroups.find(groupValues);
|
|
||||||
|
|
||||||
if (it == allGroups.end()) {
|
|
||||||
// new group
|
|
||||||
group.clear();
|
|
||||||
|
|
||||||
// copy the group values before they get invalidated
|
|
||||||
for (size_t i = 0; i < n; ++i) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
group.emplace_back(cur->getValueReference(_pos, _aggregateRegisters[i].second).clone());
|
groupValues.emplace_back(cur->getValueReference(_pos, _aggregateRegisters[i].second));
|
||||||
}
|
}
|
||||||
|
|
||||||
allGroups.emplace(group, 1);
|
// now check if we already know this group
|
||||||
}
|
auto it = allGroups.find(groupValues);
|
||||||
else {
|
|
||||||
// existing group. simply increase the counter
|
|
||||||
(*it).second++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (++_pos >= cur->size()) {
|
if (it == allGroups.end()) {
|
||||||
_buffer.pop_front();
|
// new group
|
||||||
_pos = 0;
|
group.clear();
|
||||||
|
|
||||||
bool hasMore = ! _buffer.empty();
|
// copy the group values before they get invalidated
|
||||||
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
group.emplace_back(cur->getValueReference(_pos, _aggregateRegisters[i].second).clone());
|
||||||
|
}
|
||||||
|
|
||||||
if (! hasMore) {
|
allGroups.emplace(group, 1);
|
||||||
hasMore = ExecutionBlock::getBlock(atLeast, atMost);
|
}
|
||||||
|
else {
|
||||||
|
// existing group. simply increase the counter
|
||||||
|
(*it).second++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! hasMore) {
|
if (++_pos >= cur->size()) {
|
||||||
// no more input. we're done
|
_buffer.pop_front();
|
||||||
try {
|
_pos = 0;
|
||||||
// emit last buffered group
|
|
||||||
if (! skipping) {
|
bool hasMore = ! _buffer.empty();
|
||||||
TRI_IF_FAILURE("HashedAggregateBlock::getOrSkipSome") {
|
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
if (! hasMore) {
|
||||||
|
hasMore = ExecutionBlock::getBlock(atLeast, atMost);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! hasMore) {
|
||||||
|
// no more input. we're done
|
||||||
|
try {
|
||||||
|
// emit last buffered group
|
||||||
|
if (! skipping) {
|
||||||
|
TRI_IF_FAILURE("HashedAggregateBlock::getOrSkipSome") {
|
||||||
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++skipped;
|
||||||
|
result = buildResult(cur);
|
||||||
|
|
||||||
|
returnBlock(cur);
|
||||||
|
_done = true;
|
||||||
|
|
||||||
|
allGroups.clear();
|
||||||
|
groupValues.clear();
|
||||||
|
|
||||||
|
return TRI_ERROR_NO_ERROR;
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
returnBlock(cur);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
++skipped;
|
|
||||||
result = buildResult(cur);
|
|
||||||
|
|
||||||
returnBlock(cur);
|
|
||||||
_done = true;
|
|
||||||
|
|
||||||
allGroups.clear();
|
|
||||||
groupValues.clear();
|
|
||||||
|
|
||||||
return TRI_ERROR_NO_ERROR;
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
returnBlock(cur);
|
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasMore
|
||||||
|
|
||||||
|
returnBlock(cur);
|
||||||
|
cur = _buffer.front();
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasMore
|
|
||||||
|
|
||||||
returnBlock(cur);
|
|
||||||
cur = _buffer.front();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (...) {
|
||||||
|
// clean up
|
||||||
|
for (auto& it : allGroups) {
|
||||||
|
for (auto& it2 : it.first) {
|
||||||
|
const_cast<AqlValue*>(&it2)->destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allGroups.clear();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
allGroups.clear();
|
allGroups.clear();
|
||||||
groupValues.clear();
|
groupValues.clear();
|
||||||
|
@ -4198,6 +4230,7 @@ int LimitBlock::getOrSkipSome (size_t atLeast,
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecutionBlock::getOrSkipSome(atLeast, atMost, skipping, result, skipped);
|
ExecutionBlock::getOrSkipSome(atLeast, atMost, skipping, result, skipped);
|
||||||
|
|
||||||
if (skipped == 0) {
|
if (skipped == 0) {
|
||||||
return TRI_ERROR_NO_ERROR;
|
return TRI_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -4222,10 +4255,12 @@ int LimitBlock::getOrSkipSome (size_t atLeast,
|
||||||
skipped = 0;
|
skipped = 0;
|
||||||
AqlItemBlock* ignore = nullptr;
|
AqlItemBlock* ignore = nullptr;
|
||||||
ExecutionBlock::getOrSkipSome(atLeast, atMost, skipping, ignore, skipped);
|
ExecutionBlock::getOrSkipSome(atLeast, atMost, skipping, ignore, skipped);
|
||||||
|
|
||||||
if (ignore != nullptr) {
|
if (ignore != nullptr) {
|
||||||
_engine->_stats.fullCount += static_cast<int64_t>(ignore->size());
|
_engine->_stats.fullCount += static_cast<int64_t>(ignore->size());
|
||||||
delete ignore;
|
delete ignore;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skipped == 0) {
|
if (skipped == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,10 +141,10 @@ std::unordered_map<std::string, Function const> const Executor::FunctionNames{
|
||||||
|
|
||||||
// list functions
|
// list functions
|
||||||
{ "RANGE", Function("RANGE", "AQL_RANGE", "n,n|n", true, false, true) },
|
{ "RANGE", Function("RANGE", "AQL_RANGE", "n,n|n", true, false, true) },
|
||||||
{ "UNION", Function("UNION", "AQL_UNION", "l,l|+",true, false, true) },
|
{ "UNION", Function("UNION", "AQL_UNION", "l,l|+",true, false, true, &Functions::Union) },
|
||||||
{ "UNION_DISTINCT", Function("UNION_DISTINCT", "AQL_UNION_DISTINCT", "l,l|+", true, false, true) },
|
{ "UNION_DISTINCT", Function("UNION_DISTINCT", "AQL_UNION_DISTINCT", "l,l|+", true, false, true, &Functions::UnionDistinct) },
|
||||||
{ "MINUS", Function("MINUS", "AQL_MINUS", "l,l|+", true, false, true) },
|
{ "MINUS", Function("MINUS", "AQL_MINUS", "l,l|+", true, false, true) },
|
||||||
{ "INTERSECTION", Function("INTERSECTION", "AQL_INTERSECTION", "l,l|+", true, false, true) },
|
{ "INTERSECTION", Function("INTERSECTION", "AQL_INTERSECTION", "l,l|+", true, false, true, &Functions::Intersection) },
|
||||||
{ "FLATTEN", Function("FLATTEN", "AQL_FLATTEN", "l|n", true, false, true) },
|
{ "FLATTEN", Function("FLATTEN", "AQL_FLATTEN", "l|n", true, false, true) },
|
||||||
{ "LENGTH", Function("LENGTH", "AQL_LENGTH", "las", true, false, true, &Functions::Length) },
|
{ "LENGTH", Function("LENGTH", "AQL_LENGTH", "las", true, false, true, &Functions::Length) },
|
||||||
{ "MIN", Function("MIN", "AQL_MIN", "l", true, false, true, &Functions::Min) },
|
{ "MIN", Function("MIN", "AQL_MIN", "l", true, false, true, &Functions::Min) },
|
||||||
|
|
|
@ -620,10 +620,17 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
|
||||||
TRI_ASSERT(member->type == NODE_TYPE_ARRAY);
|
TRI_ASSERT(member->type == NODE_TYPE_ARRAY);
|
||||||
|
|
||||||
AqlValue result = executeSimpleExpression(member, &myCollection, trx, argv, startPos, vars, regs);
|
AqlValue result = executeSimpleExpression(member, &myCollection, trx, argv, startPos, vars, regs);
|
||||||
|
|
||||||
auto res2 = func->implementation(_ast->query(), trx, myCollection, result);
|
try {
|
||||||
result.destroy();
|
auto res2 = func->implementation(_ast->query(), trx, myCollection, result);
|
||||||
return res2;
|
result.destroy();
|
||||||
|
return res2;
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
// prevent leak and rethrow error
|
||||||
|
result.destroy();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (node->type == NODE_TYPE_RANGE) {
|
else if (node->type == NODE_TYPE_RANGE) {
|
||||||
|
|
|
@ -831,7 +831,7 @@ AqlValue Functions::Unique (triagens::aql::Query* query,
|
||||||
}
|
}
|
||||||
|
|
||||||
TRI_json_t const* valueJson = value.json();
|
TRI_json_t const* valueJson = value.json();
|
||||||
size_t const n = valueJson->_value._objects._length;
|
size_t const n = TRI_LengthArrayJson(valueJson);
|
||||||
|
|
||||||
std::unordered_set<TRI_json_t const*, triagens::basics::JsonHash, triagens::basics::JsonEqual> values(
|
std::unordered_set<TRI_json_t const*, triagens::basics::JsonHash, triagens::basics::JsonEqual> values(
|
||||||
512,
|
512,
|
||||||
|
@ -888,27 +888,35 @@ AqlValue Functions::Union (triagens::aql::Query* query,
|
||||||
}
|
}
|
||||||
|
|
||||||
TRI_json_t const* valueJson = value.json();
|
TRI_json_t const* valueJson = value.json();
|
||||||
|
size_t const nrValues = TRI_LengthArrayJson(valueJson);
|
||||||
|
|
||||||
std::unique_ptr<TRI_json_t> copy(TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, valueJson));
|
if (TRI_ReserveVector(&(result.get()->_value._objects), nrValues) != TRI_ERROR_NO_ERROR) {
|
||||||
|
|
||||||
if (copy == nullptr) {
|
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t const toAppend = TRI_LengthArrayJson(copy.get());
|
TRI_IF_FAILURE("AqlFunctions::OutOfMemory1") {
|
||||||
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
if (TRI_ReserveVector(&(result.get()->_value._objects), toAppend) != TRI_ERROR_NO_ERROR) {
|
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this passes ownership for the JSON contens into result
|
// this passes ownership for the JSON contens into result
|
||||||
for (size_t j = 0; j < toAppend; ++j) {
|
for (size_t j = 0; j < nrValues; ++j) {
|
||||||
TRI_PushBack2ArrayJson(result.get(), static_cast<TRI_json_t const*>(TRI_AddressVector(&(copy.get()->_value._objects), j)));
|
TRI_json_t* copy = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, TRI_LookupArrayJson(valueJson, j));
|
||||||
}
|
|
||||||
|
|
||||||
// free the top-level pointer of copy (but not its internals as they have been handed over into result)
|
if (copy == nullptr) {
|
||||||
TRI_Free(TRI_UNKNOWN_MEM_ZONE, copy.release());
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRI_PushBack3ArrayJson(TRI_UNKNOWN_MEM_ZONE, result.get(), copy);
|
||||||
|
|
||||||
|
TRI_IF_FAILURE("AqlFunctions::OutOfMemory2") {
|
||||||
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TRI_IF_FAILURE("AqlFunctions::OutOfMemory3") {
|
||||||
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
auto jr = new Json(TRI_UNKNOWN_MEM_ZONE, result.get());
|
auto jr = new Json(TRI_UNKNOWN_MEM_ZONE, result.get());
|
||||||
result.release();
|
result.release();
|
||||||
|
@ -923,47 +931,77 @@ AqlValue Functions::UnionDistinct (triagens::aql::Query* query,
|
||||||
triagens::arango::AqlTransaction* trx,
|
triagens::arango::AqlTransaction* trx,
|
||||||
TRI_document_collection_t const* collection,
|
TRI_document_collection_t const* collection,
|
||||||
AqlValue const parameters) {
|
AqlValue const parameters) {
|
||||||
std::unordered_set<TRI_json_t const*, triagens::basics::JsonHash, triagens::basics::JsonEqual> values(
|
std::unordered_set<TRI_json_t*, triagens::basics::JsonHash, triagens::basics::JsonEqual> values(
|
||||||
512,
|
512,
|
||||||
triagens::basics::JsonHash(),
|
triagens::basics::JsonHash(),
|
||||||
triagens::basics::JsonEqual()
|
triagens::basics::JsonEqual()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
auto freeValues = [&values] () -> void {
|
||||||
|
for (auto& it : values) {
|
||||||
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, it);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<TRI_json_t> result;
|
||||||
size_t const n = parameters.arraySize();
|
size_t const n = parameters.arraySize();
|
||||||
|
|
||||||
for (size_t i = 0; i < n; ++i) {
|
try {
|
||||||
Json value(parameters.extractArrayMember(trx, collection, i, false));
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
Json value(parameters.extractArrayMember(trx, collection, i, false));
|
||||||
|
|
||||||
if (! value.isArray()) {
|
if (! value.isArray()) {
|
||||||
// not an array
|
// not an array
|
||||||
RegisterWarning(query, "UNION_DISTINCT", TRI_ERROR_QUERY_ARRAY_EXPECTED);
|
freeValues();
|
||||||
return AqlValue(new Json(Json::Null));
|
RegisterWarning(query, "UNION_DISTINCT", TRI_ERROR_QUERY_ARRAY_EXPECTED);
|
||||||
}
|
return AqlValue(new Json(Json::Null));
|
||||||
|
|
||||||
TRI_json_t const* valueJson = value.json();
|
|
||||||
size_t const nrValues = valueJson->_value._objects._length;
|
|
||||||
|
|
||||||
for (size_t j = 0; j < nrValues; ++j) {
|
|
||||||
auto value = static_cast<TRI_json_t const*>(TRI_AddressVector(&valueJson->_value._objects, j));
|
|
||||||
|
|
||||||
if (value == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
values.emplace(value);
|
TRI_json_t const* valueJson = value.json();
|
||||||
|
size_t const nrValues = TRI_LengthArrayJson(valueJson);
|
||||||
|
|
||||||
|
for (size_t j = 0; j < nrValues; ++j) {
|
||||||
|
auto value = static_cast<TRI_json_t*>(TRI_AddressVector(&valueJson->_value._objects, j));
|
||||||
|
|
||||||
|
if (values.find(value) == values.end()) {
|
||||||
|
std::unique_ptr<TRI_json_t> copy(TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, value));
|
||||||
|
|
||||||
|
if (copy == nullptr) {
|
||||||
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRI_IF_FAILURE("AqlFunctions::OutOfMemory1") {
|
||||||
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
values.emplace(copy.get());
|
||||||
|
copy.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<TRI_json_t> result(TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE, values.size()));
|
result.reset(TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE, values.size()));
|
||||||
|
|
||||||
for (auto const& it : values) {
|
|
||||||
auto copy = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, it);
|
|
||||||
|
|
||||||
if (copy == nullptr) {
|
if (result == nullptr) {
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
TRI_PushBack3ArrayJson(TRI_UNKNOWN_MEM_ZONE, result.get(), copy);
|
TRI_IF_FAILURE("AqlFunctions::OutOfMemory2") {
|
||||||
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& it : values) {
|
||||||
|
TRI_PushBack3ArrayJson(TRI_UNKNOWN_MEM_ZONE, result.get(), it);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
freeValues();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRI_IF_FAILURE("AqlFunctions::OutOfMemory3") {
|
||||||
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto jr = new Json(TRI_UNKNOWN_MEM_ZONE, result.get());
|
auto jr = new Json(TRI_UNKNOWN_MEM_ZONE, result.get());
|
||||||
|
@ -979,62 +1017,108 @@ AqlValue Functions::Intersection (triagens::aql::Query* query,
|
||||||
triagens::arango::AqlTransaction* trx,
|
triagens::arango::AqlTransaction* trx,
|
||||||
TRI_document_collection_t const* collection,
|
TRI_document_collection_t const* collection,
|
||||||
AqlValue const parameters) {
|
AqlValue const parameters) {
|
||||||
std::unordered_set<TRI_json_t const*, triagens::basics::JsonHash, triagens::basics::JsonEqual> values(
|
std::unordered_map<TRI_json_t*, size_t, triagens::basics::JsonHash, triagens::basics::JsonEqual> values(
|
||||||
512,
|
512,
|
||||||
triagens::basics::JsonHash(),
|
triagens::basics::JsonHash(),
|
||||||
triagens::basics::JsonEqual()
|
triagens::basics::JsonEqual()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
auto freeValues = [&values] () -> void {
|
||||||
|
for (auto& it : values) {
|
||||||
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, it.first);
|
||||||
|
}
|
||||||
|
values.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<TRI_json_t> result;
|
||||||
size_t const n = parameters.arraySize();
|
size_t const n = parameters.arraySize();
|
||||||
|
|
||||||
for (size_t i = 0; i < n; ++i) {
|
try {
|
||||||
Json value(parameters.extractArrayMember(trx, collection, i, false));
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
Json value(parameters.extractArrayMember(trx, collection, i, false));
|
||||||
|
|
||||||
if (! value.isArray()) {
|
if (! value.isArray()) {
|
||||||
// not an array
|
// not an array
|
||||||
RegisterWarning(query, "INTERSECTION", TRI_ERROR_QUERY_ARRAY_EXPECTED);
|
freeValues();
|
||||||
return AqlValue(new Json(Json::Null));
|
RegisterWarning(query, "INTERSECTION", TRI_ERROR_QUERY_ARRAY_EXPECTED);
|
||||||
}
|
return AqlValue(new Json(Json::Null));
|
||||||
|
|
||||||
// create a copy of the previous set
|
|
||||||
auto old(values);
|
|
||||||
values.clear();
|
|
||||||
|
|
||||||
TRI_json_t const* valueJson = value.json();
|
|
||||||
size_t const nrValues = valueJson->_value._objects._length;
|
|
||||||
|
|
||||||
for (size_t j = 0; j < nrValues; ++j) {
|
|
||||||
auto value = static_cast<TRI_json_t const*>(TRI_AddressVector(&valueJson->_value._objects, j));
|
|
||||||
|
|
||||||
if (value == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == 0) {
|
TRI_json_t const* valueJson = value.json();
|
||||||
// round one
|
size_t const nrValues = TRI_LengthArrayJson(valueJson);
|
||||||
values.emplace(value);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// check if we have seen the same element before
|
|
||||||
auto it = old.find(value);
|
|
||||||
|
|
||||||
if (it != old.end()) {
|
for (size_t j = 0; j < nrValues; ++j) {
|
||||||
values.emplace(value);
|
auto value = static_cast<TRI_json_t const*>(TRI_AddressVector(&valueJson->_value._objects, j));
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
// round one
|
||||||
|
std::unique_ptr<TRI_json_t> copy(TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, value));
|
||||||
|
|
||||||
|
if (copy == nullptr) {
|
||||||
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRI_IF_FAILURE("AqlFunctions::OutOfMemory1") {
|
||||||
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto r = values.emplace(copy.get(), 1);
|
||||||
|
|
||||||
|
if (r.second) {
|
||||||
|
// successfully inserted
|
||||||
|
copy.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// check if we have seen the same element before
|
||||||
|
auto it = values.find(const_cast<TRI_json_t*>(value));
|
||||||
|
|
||||||
|
if (it != values.end()) {
|
||||||
|
// already seen
|
||||||
|
TRI_ASSERT((*it).second > 0);
|
||||||
|
++((*it).second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<TRI_json_t> result(TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE, values.size()));
|
|
||||||
|
|
||||||
for (auto const& it : values) {
|
// count how many valid we have
|
||||||
auto copy = TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, it);
|
size_t total = 0;
|
||||||
|
|
||||||
if (copy == nullptr) {
|
for (auto const& it : values) {
|
||||||
|
if (it.second == n) {
|
||||||
|
++total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.reset(TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE, total));
|
||||||
|
|
||||||
|
if (result == nullptr) {
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
TRI_PushBack3ArrayJson(TRI_UNKNOWN_MEM_ZONE, result.get(), copy);
|
TRI_IF_FAILURE("AqlFunctions::OutOfMemory2") {
|
||||||
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& it : values) {
|
||||||
|
if (it.second == n) {
|
||||||
|
TRI_PushBack3ArrayJson(TRI_UNKNOWN_MEM_ZONE, result.get(), it.first);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, it.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values.clear();
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
freeValues();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRI_IF_FAILURE("AqlFunctions::OutOfMemory3") {
|
||||||
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto jr = new Json(TRI_UNKNOWN_MEM_ZONE, result.get());
|
auto jr = new Json(TRI_UNKNOWN_MEM_ZONE, result.get());
|
||||||
|
|
|
@ -74,6 +74,51 @@ function ahuacatlFailureSuite () {
|
||||||
c = null;
|
c = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test UNION for memleaks
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testAqlFunctionUnion : function () {
|
||||||
|
var where = [ "AqlFunctions::OutOfMemory1", "AqlFunctions::OutOfMemory2", "AqlFunctions::OutOfMemory3" ];
|
||||||
|
|
||||||
|
where.forEach(function(w) {
|
||||||
|
internal.debugClearFailAt();
|
||||||
|
|
||||||
|
internal.debugSetFailAt(w);
|
||||||
|
assertFailingQuery("RETURN NOOPT(UNION([ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11 ]))");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test UNION_DISTINCT for memleaks
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testAqlFunctionUnionDistinct : function () {
|
||||||
|
var where = [ "AqlFunctions::OutOfMemory1", "AqlFunctions::OutOfMemory2", "AqlFunctions::OutOfMemory3" ];
|
||||||
|
|
||||||
|
where.forEach(function(w) {
|
||||||
|
internal.debugClearFailAt();
|
||||||
|
|
||||||
|
internal.debugSetFailAt(w);
|
||||||
|
assertFailingQuery("RETURN NOOPT(UNION_DISTINCT([ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11 ]))");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test INTERSECTION for memleaks
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testAqlFunctionIntersection : function () {
|
||||||
|
var where = [ "AqlFunctions::OutOfMemory1", "AqlFunctions::OutOfMemory2", "AqlFunctions::OutOfMemory3" ];
|
||||||
|
|
||||||
|
where.forEach(function(w) {
|
||||||
|
internal.debugClearFailAt();
|
||||||
|
|
||||||
|
internal.debugSetFailAt(w);
|
||||||
|
assertFailingQuery("RETURN NOOPT(INTERSECTION([ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11 ]))");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief test failure
|
/// @brief test failure
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -95,6 +140,17 @@ function ahuacatlFailureSuite () {
|
||||||
assertFailingQuery("FOR i IN 1..10000 COLLECT key = i INTO g RETURN [ key, g ]");
|
assertFailingQuery("FOR i IN 1..10000 COLLECT key = i INTO g RETURN [ key, g ]");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test failure
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testSortedAggregateBlock3 : function () {
|
||||||
|
internal.debugSetFailAt("SortedAggregateBlock::hasMore");
|
||||||
|
assertFailingQuery("FOR i IN " + c.name() + " COLLECT key = i.value INTO g RETURN [ key, g ]");
|
||||||
|
assertFailingQuery("FOR i IN " + c.name() + " COLLECT key = i.value2 INTO g RETURN [ key, g ]");
|
||||||
|
assertFailingQuery("FOR i IN 1..10000 COLLECT key = i INTO g RETURN [ key, g ]");
|
||||||
|
},
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief test failure
|
/// @brief test failure
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -910,7 +910,6 @@ function ahuacatlFunctionsTestSuite () {
|
||||||
} } ], actual);
|
} } ], actual);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief test merge_recursive function
|
/// @brief test merge_recursive function
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1149,7 +1148,7 @@ function ahuacatlFunctionsTestSuite () {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
testUnionDistinct1 : function () {
|
testUnionDistinct1 : function () {
|
||||||
var expected = [ [ 1, 2, 3, ] ];
|
var expected = [ [ 1, 2, 3 ] ];
|
||||||
var actual = getQueryResults("RETURN UNION_DISTINCT([ 1, 2, 3 ], [ 1, 2, 3 ])");
|
var actual = getQueryResults("RETURN UNION_DISTINCT([ 1, 2, 3 ], [ 1, 2, 3 ])");
|
||||||
assertEqual(expected, actual);
|
assertEqual(expected, actual);
|
||||||
},
|
},
|
||||||
|
@ -1228,6 +1227,174 @@ function ahuacatlFunctionsTestSuite () {
|
||||||
assertQueryWarningAndNull(errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH.code, "RETURN UNION_DISTINCT({ }, [ ])");
|
assertQueryWarningAndNull(errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH.code, "RETURN UNION_DISTINCT({ }, [ ])");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test union function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testUnionCxx1 : function () {
|
||||||
|
var expected = [ [ 1, 2, 3, 1, 2, 3 ] ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(UNION([ 1, 2, 3 ], [ 1, 2, 3 ]))");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test union function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testUnionCxx2 : function () {
|
||||||
|
var expected = [ [ 1, 2, 3, 3, 2, 1 ] ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(UNION([ 1, 2, 3 ], [ 3, 2, 1 ]))");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test union function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testUnionCxx3 : function () {
|
||||||
|
var expected = [ "Fred", "John", "John", "Amy" ];
|
||||||
|
var actual = getQueryResults("FOR u IN NOOPT(UNION([ \"Fred\", \"John\" ], [ \"John\", \"Amy\"])) RETURN u");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test union function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testUnionCxxInvalid : function () {
|
||||||
|
assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN NOOPT(UNION())");
|
||||||
|
assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN NOOPT(UNION([ ]))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION([ ], null))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION([ ], true))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION([ ], 3))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION([ ], \"yes\"))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION([ ], { }))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION([ ], [ ], null))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION([ ], [ ], true))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION([ ], [ ], 3))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION([ ], [ ], \"yes\"))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION([ ], [ ], { }))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION(null, [ ]))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION(true, [ ]))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION(3, [ ]))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION(\"yes\", [ ]))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION({ }, [ ]))");
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test union function indexed access
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testUnionCxxIndexedAccess1 : function () {
|
||||||
|
var expected = [ "Fred" ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(UNION([ \"Fred\", \"John\" ], [ \"John\", \"Amy\"]))[0]");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test union function indexed access
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testUnionCxxIndexedAccess2 : function () {
|
||||||
|
var expected = [ "John" ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(UNION([ \"Fred\", \"John\" ], [ \"John\", \"Amy\"]))[1]");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test union function indexed access
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testUnionCxxIndexedAccess3 : function () {
|
||||||
|
var expected = [ "bar" ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(UNION([ { title : \"foo\" } ], [ { title : \"bar\" } ]))[1].title");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test union_distinct function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testUnionDistinctCxx1 : function () {
|
||||||
|
var expected = [ 1, 2, 3 ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(UNION_DISTINCT([ 1, 2, 3 ], [ 1, 2, 3 ]))");
|
||||||
|
assertEqual(expected, actual[0].sort());
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test union_distinct function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testUnionDistinctCxx2 : function () {
|
||||||
|
var expected = [ 1, 2, 3 ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(UNION_DISTINCT([ 1, 2, 3 ], [ 3, 2, 1 ]))");
|
||||||
|
assertEqual(expected, actual[0].sort());
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test union_distinct function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testUnionDistinctCxx3 : function () {
|
||||||
|
var expected = [ "Amy", "Fred", "John" ];
|
||||||
|
var actual = getQueryResults("FOR u IN NOOPT(UNION_DISTINCT([ \"Fred\", \"John\" ], [ \"John\", \"Amy\"])) RETURN u");
|
||||||
|
assertEqual(expected, actual.sort());
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test union_distinct function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testUnionDistinctCxx4 : function () {
|
||||||
|
var expected = [ 1, 2, 3, 4, 5, 6 ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(UNION_DISTINCT([ 1, 2, 3 ], [ 3, 2, 1 ], [ 4 ], [ 5, 6, 1 ]))");
|
||||||
|
assertEqual(expected, actual[0].sort());
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test union_distinct function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testUnionDistinctCxx5 : function () {
|
||||||
|
var expected = [ [ ] ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(UNION_DISTINCT([ ], [ ], [ ]))");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test union_distinct function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testUnionDistinctCxx6 : function () {
|
||||||
|
var expected = [ false, true ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(UNION_DISTINCT([ ], [ false ], [ ], [ true ]))");
|
||||||
|
assertEqual(expected, actual[0].sort());
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test union_distinct function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testUnionDistinctCxxInvalid : function () {
|
||||||
|
assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN NOOPT(UNION_DISTINCT())");
|
||||||
|
assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN NOOPT(UNION_DISTINCT([ ]))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION_DISTINCT([ ], null))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION_DISTINCT([ ], true))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION_DISTINCT([ ], 3))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION_DISTINCT([ ], \"yes\"))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION_DISTINCT([ ], { }))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION_DISTINCT([ ], [ ], null))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION_DISTINCT([ ], [ ], true))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION_DISTINCT([ ], [ ], 3))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION_DISTINCT([ ], [ ], \"yes\"))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION_DISTINCT([ ], [ ], { }))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION_DISTINCT(null, [ ]))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION_DISTINCT(true, [ ]))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION_DISTINCT(3, [ ]))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION_DISTINCT(\"yes\", [ ]))");
|
||||||
|
assertQueryWarningAndNull(errors.ERROR_QUERY_ARRAY_EXPECTED.code, "RETURN NOOPT(UNION_DISTINCT({ }, [ ]))");
|
||||||
|
},
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief test range function
|
/// @brief test range function
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1405,9 +1572,9 @@ function ahuacatlFunctionsTestSuite () {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
testIntersection1 : function () {
|
testIntersection1 : function () {
|
||||||
var expected = [ [ 1, -3 ] ];
|
var expected = [ -3, 1 ];
|
||||||
var actual = getQueryResults("RETURN INTERSECTION([ 1, -3 ], [ -3, 1 ])");
|
var actual = getQueryResults("RETURN INTERSECTION([ 1, -3 ], [ -3, 1 ])");
|
||||||
assertEqual(expected, actual);
|
assertEqual(expected, actual[0].sort());
|
||||||
},
|
},
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1445,9 +1612,9 @@ function ahuacatlFunctionsTestSuite () {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
testIntersection5 : function () {
|
testIntersection5 : function () {
|
||||||
var expected = [ [ 2, 4 ] ];
|
var expected = [ 2, 4 ];
|
||||||
var actual = getQueryResults("RETURN INTERSECTION([ 1, 3, 2, 4 ], [ 2, 3, 1, 4 ], [ 4, 5, 6, 2 ])");
|
var actual = getQueryResults("RETURN INTERSECTION([ 1, 3, 2, 4 ], [ 2, 3, 1, 4 ], [ 4, 5, 6, 2 ])");
|
||||||
assertEqual(expected, actual);
|
assertEqual(expected, actual[0].sort());
|
||||||
},
|
},
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1495,11 +1662,111 @@ function ahuacatlFunctionsTestSuite () {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
testIntersection10 : function () {
|
testIntersection10 : function () {
|
||||||
var expected = [ [ 2, 4, 5 ] ];
|
var expected = [ 2, 4, 5 ];
|
||||||
var actual = getQueryResults("RETURN INTERSECTION([ 1, 2, 3, 3, 4, 4, 5, 1 ], [ 2, 4, 5 ])");
|
var actual = getQueryResults("RETURN INTERSECTION([ 1, 2, 3, 3, 4, 4, 5, 1 ], [ 2, 4, 5 ])");
|
||||||
|
assertEqual(expected, actual[0].sort());
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test intersection function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIntersectionCxx1 : function () {
|
||||||
|
var expected = [ -3, 1 ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(INTERSECTION([ 1, -3 ], [ -3, 1 ]))");
|
||||||
|
assertEqual(expected, actual[0].sort());
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test intersect function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIntersectionCxx2 : function () {
|
||||||
|
var expected = [ [ ] ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(INTERSECTION([ ], [ 1 ]))");
|
||||||
assertEqual(expected, actual);
|
assertEqual(expected, actual);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test intersect function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIntersectionCxx3 : function () {
|
||||||
|
var expected = [ [ ] ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(INTERSECTION([ 1 ], [ ]))");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test intersect function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIntersectionCxx4 : function () {
|
||||||
|
var expected = [ [ ] ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(INTERSECTION([ 1 ], [ 2, 3, 1 ], [ 4, 5, 6 ]))");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test intersect function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIntersectionCxx5 : function () {
|
||||||
|
var expected = [ 2, 4 ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(INTERSECTION([ 1, 3, 2, 4 ], [ 2, 3, 1, 4 ], [ 4, 5, 6, 2 ]))");
|
||||||
|
assertEqual(expected, actual[0].sort());
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test intersect function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIntersectionCxx6 : function () {
|
||||||
|
var expected = [ [ ] ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(INTERSECTION([ [ 1, 2 ] ], [ 2, 1 ]))");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test intersect function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIntersectionCxx7 : function () {
|
||||||
|
var expected = [ [ ] ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(INTERSECTION([ [ 1, 2 ] ], [ 1, 2 ]))");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test intersect function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIntersectionCxx8 : function () {
|
||||||
|
var expected = [ [ [ 1, 2 ] ] ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(INTERSECTION([ [ 1, 2 ] ], [ [ 1, 2 ] ]))");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test intersect function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIntersectionCxx9 : function () {
|
||||||
|
var expected = [ [ { foo: 'test' } ] ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(INTERSECTION([ { foo: 'bar' }, { foo: 'test' } ], [ { foo: 'test' } ]))");
|
||||||
|
assertEqual(expected, actual);
|
||||||
|
},
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief test intersect function
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
testIntersectionCxx10 : function () {
|
||||||
|
var expected = [ 2, 4, 5 ];
|
||||||
|
var actual = getQueryResults("RETURN NOOPT(INTERSECTION([ 1, 2, 3, 3, 4, 4, 5, 1 ], [ 2, 4, 5 ]))");
|
||||||
|
assertEqual(expected, actual[0].sort());
|
||||||
|
},
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief test flatten function
|
/// @brief test flatten function
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
Loading…
Reference in New Issue