1
0
Fork 0

changed behavior of array comparison operators for empty arrays

This commit is contained in:
Jan Steemann 2016-09-21 11:01:59 +02:00
parent 06d0b19a70
commit f0e14cff7d
7 changed files with 103 additions and 12 deletions

View File

@ -1,6 +1,25 @@
devel
-----
* changed behavior of AQL array comparison operators for empty arrays:
* `ALL` and `ANY` now always returns `false` when the left-hand operand is an
empty array. The behavior for non-empty arrays does not change:
* `[] ALL == 1` will return `false`
* `[1] ALL == 1` will return `true`
* `[1, 2] ALL == 1` will return `false`
* `[2, 2] ALL == 1` will return `false`
* `[] ANY == 1` will return `false`
* `[1] ANY == 1` will return `true`
* `[1, 2] ANY == 1` will return `true`
* `[2, 2] ANY == 1` will return `false`
* `NONE` now always returns `true` when the left-hand operand is an empty array.
The behavior for non-empty arrays does not change:
* `[] NONE == 1` will return `true`
* `[1] NONE == 1` will return `false`
* `[1, 2] ALL == 1` will return `false`
* `[2, 2] ALL == 1` will return `true`
* added experimental AQL functions `JSON_STRINGIFY` and `JSON_PARSE`
* added experimental support for incoming gzip-compressed requests

View File

@ -1142,6 +1142,18 @@ AqlValue Expression::executeSimpleExpressionArrayComparison(
}
size_t const n = left.length();
if (n == 0) {
if (Quantifier::IsAllOrAny(node->getMember(2))) {
// [] ALL ...
// [] ANY ...
return AqlValue(false);
} else {
// [] NONE ...
return AqlValue(true);
}
}
std::pair<size_t, size_t> requiredMatches = Quantifier::RequiredMatches(n, node->getMember(2));
TRI_ASSERT(requiredMatches.first <= requiredMatches.second);

View File

@ -59,6 +59,16 @@ std::string Quantifier::Stringify(int64_t value) {
return "none";
}
bool Quantifier::IsAllOrAny(AstNode const* quantifier) {
TRI_ASSERT(quantifier != nullptr);
if (quantifier->type == NODE_TYPE_QUANTIFIER) {
auto const value = quantifier->getIntValue(true);
return (value == Quantifier::ALL || value == Quantifier::ANY);
}
return false;
}
/// @brief determine the min/max number of matches for an array comparison
std::pair<size_t, size_t> Quantifier::RequiredMatches(size_t inputSize, AstNode const* quantifier) {
TRI_ASSERT(quantifier != nullptr);

View File

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

View File

@ -1318,12 +1318,21 @@ function RELATIONAL_ARRAY_FUNC (lhs, rhs, quantifier, func) {
var n = lhs.length, min, max;
if (quantifier === 1) {
// NONE
if (n === 0) {
return true;
}
min = max = 0;
} else if (quantifier === 2) {
// ALL
if (n === 0) {
return false;
}
min = max = n;
} else if (quantifier === 3) {
// ANY
if (n === 0) {
return false;
}
min = (n === 0 ? 0 : 1);
max = n;
}

View File

@ -2736,9 +2736,9 @@ function optimizeQuantifierSuite() {
RETURN v._id
`;
let cursor = db._query(query);
assertEqual(cursor.count(), 4);
assertEqual(cursor.count(), 3);
let result = cursor.toArray();
assertEqual(result, [vertices.A, vertices.B, vertices.C, vertices.D]);
assertEqual(result, [vertices.B, vertices.C, vertices.D]);
let stats = cursor.getExtra().stats;
assertEqual(stats.scannedFull, 0);
@ -2747,7 +2747,7 @@ function optimizeQuantifierSuite() {
} else {
assertEqual(stats.scannedIndex, 8);
}
assertEqual(stats.filtered, 1);
assertEqual(stats.filtered, 2);
query = `
FOR v, e, p IN 0..2 OUTBOUND "${vertices.A}" GRAPH "${gn}"
@ -2756,9 +2756,9 @@ function optimizeQuantifierSuite() {
RETURN v._id
`;
cursor = db._query(query);
assertEqual(cursor.count(), 4);
assertEqual(cursor.count(), 3);
result = cursor.toArray();
assertEqual(result, [vertices.A, vertices.E, vertices.F, vertices.G]);
assertEqual(result, [vertices.E, vertices.F, vertices.G]);
stats = cursor.getExtra().stats;
assertEqual(stats.scannedFull, 0);
@ -2767,7 +2767,7 @@ function optimizeQuantifierSuite() {
} else {
assertEqual(stats.scannedIndex, 8);
}
assertEqual(stats.filtered, 1);
assertEqual(stats.filtered, 2);
},
testNoneVerticesSingle: function () {
@ -2872,9 +2872,9 @@ function optimizeQuantifierSuite() {
RETURN v._id
`;
let cursor = db._query(query);
assertEqual(cursor.count(), 3);
assertEqual(cursor.count(), 2);
let result = cursor.toArray();
assertEqual(result, [vertices.A, vertices.B, vertices.C]);
assertEqual(result, [vertices.B, vertices.C]);
let stats = cursor.getExtra().stats;
assertEqual(stats.scannedFull, 0);
@ -2883,7 +2883,7 @@ function optimizeQuantifierSuite() {
} else {
assertEqual(stats.scannedIndex, 7);
}
assertEqual(stats.filtered, 2);
assertEqual(stats.filtered, 3);
},
testAllNoneVerticesMultiple: function () {
@ -2914,9 +2914,9 @@ function optimizeQuantifierSuite() {
RETURN v._id
`;
let cursor = db._query(query);
assertEqual(cursor.count(), 3);
assertEqual(cursor.count(), 2);
let result = cursor.toArray();
assertEqual(result, [vertices.A, vertices.B, vertices.C]);
assertEqual(result, [vertices.B, vertices.C]);
let stats = cursor.getExtra().stats;
assertEqual(stats.scannedFull, 0);
@ -2925,7 +2925,7 @@ function optimizeQuantifierSuite() {
} else {
assertEqual(stats.scannedIndex, 7);
}
assertEqual(stats.filtered, 2);
assertEqual(stats.filtered, 3);
},
testAllVerticesDepth: function () {

View File

@ -51,6 +51,45 @@ function optimizerQuantifiersTestSuite () {
tearDown : function () {
db._drop("UnitTestsCollection");
},
testAllEmpty : function () {
var query = "[] ALL == '1'", result;
result = AQL_EXECUTE("RETURN (" + query + ")").json[0];
assertEqual(false, result);
result = AQL_EXECUTE("RETURN NOOPT(" + query + ")").json[0];
assertEqual(false, result);
result = AQL_EXECUTE("RETURN V8(" + query + ")").json[0];
assertEqual(false, result);
},
testAnyEmpty : function () {
var query = "[] ANY == '1'", result;
result = AQL_EXECUTE("RETURN (" + query + ")").json[0];
assertEqual(false, result);
result = AQL_EXECUTE("RETURN NOOPT(" + query + ")").json[0];
assertEqual(false, result);
result = AQL_EXECUTE("RETURN V8(" + query + ")").json[0];
assertEqual(false, result);
},
testNoneEmpty : function () {
var query = "[] NONE == '1'", result;
result = AQL_EXECUTE("RETURN (" + query + ")").json[0];
assertEqual(true, result);
result = AQL_EXECUTE("RETURN NOOPT(" + query + ")").json[0];
assertEqual(true, result);
result = AQL_EXECUTE("RETURN V8(" + query + ")").json[0];
assertEqual(true, result);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test ALL IN