mirror of https://gitee.com/bigwinds/arangodb
changed behavior of array comparison operators for empty arrays
This commit is contained in:
parent
06d0b19a70
commit
f0e14cff7d
19
CHANGELOG
19
CHANGELOG
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue