diff options
author | Tess Avitabile <tess.avitabile@mongodb.com> | 2016-04-08 15:07:20 -0400 |
---|---|---|
committer | Tess Avitabile <tess.avitabile@mongodb.com> | 2016-04-13 10:05:36 -0400 |
commit | 4c1e3619a67041f589eb65da29a146629e88e280 (patch) | |
tree | e02298ac73578110888c90a492e9e0d63f54b0b8 /src | |
parent | 56a903eb64f5b9865f1b54b90334f02cb5adf6dd (diff) | |
download | mongo-4c1e3619a67041f589eb65da29a146629e88e280.tar.gz |
SERVER-23348 Add a collator to ComparisonMatchExpression, InMatchExpression
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/matcher/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_algo.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_array_test.cpp | 63 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_leaf.cpp | 20 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_leaf.h | 56 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_leaf_test.cpp | 297 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_parser.cpp | 38 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_test.cpp | 15 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_tree_test.cpp | 64 | ||||
-rw-r--r-- | src/mongo/db/query/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/query/canonical_query.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/query/canonical_query_test.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/query/collation/collator_interface.h | 13 | ||||
-rw-r--r-- | src/mongo/db/query/collation/collator_interface_mock_test.cpp | 22 | ||||
-rw-r--r-- | src/mongo/db/query/planner_ixselect.cpp | 12 |
15 files changed, 446 insertions, 176 deletions
diff --git a/src/mongo/db/matcher/SConscript b/src/mongo/db/matcher/SConscript index 462113742d1..5edaa348843 100644 --- a/src/mongo/db/matcher/SConscript +++ b/src/mongo/db/matcher/SConscript @@ -49,6 +49,7 @@ env.Library( '$BUILD_DIR/mongo/bson/util/bson_extract', '$BUILD_DIR/mongo/db/common', '$BUILD_DIR/mongo/db/fts/fts_query_noop', + '$BUILD_DIR/mongo/db/query/collation/collator_interface', '$BUILD_DIR/third_party/shim_pcrecpp', 'path', ], @@ -63,6 +64,7 @@ env.CppUnitTest( 'expression_tree_test.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/query/collation/collator_interface_mock', 'expressions', ], ) diff --git a/src/mongo/db/matcher/expression_algo.cpp b/src/mongo/db/matcher/expression_algo.cpp index 79059c204d1..39f22830480 100644 --- a/src/mongo/db/matcher/expression_algo.cpp +++ b/src/mongo/db/matcher/expression_algo.cpp @@ -154,7 +154,8 @@ bool _isSubsetOf(const MatchExpression* lhs, const ComparisonMatchExpression* rh } for (BSONElement elem : arrayEntries.equalities()) { // Each element in the $in-array represents an equality predicate. - EqualityMatchExpression equality; + // TODO SERVER-23618: pass the appropriate collator to EqualityMatchExpression(). + EqualityMatchExpression equality(nullptr); equality.init(lhs->path(), elem); if (!_isSubsetOf(&equality, rhs)) { return false; diff --git a/src/mongo/db/matcher/expression_array_test.cpp b/src/mongo/db/matcher/expression_array_test.cpp index 201d11fbed4..e3ef241ef87 100644 --- a/src/mongo/db/matcher/expression_array_test.cpp +++ b/src/mongo/db/matcher/expression_array_test.cpp @@ -44,7 +44,8 @@ TEST(ElemMatchObjectMatchExpression, MatchesElementSingle) { BSONObj baseOperand = BSON("b" << 5); BSONObj match = BSON("a" << BSON_ARRAY(BSON("b" << 5.0))); BSONObj notMatch = BSON("a" << BSON_ARRAY(BSON("b" << 6))); - unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator)); ASSERT(eq->init("b", baseOperand["b"]).isOK()); ElemMatchObjectMatchExpression op; ASSERT(op.init("a", eq.release()).isOK()); @@ -56,7 +57,8 @@ TEST(ElemMatchObjectMatchExpression, MatchesElementArray) { BSONObj baseOperand = BSON("1" << 5); BSONObj match = BSON("a" << BSON_ARRAY(BSON_ARRAY('s' << 5.0))); BSONObj notMatch = BSON("a" << BSON_ARRAY(BSON_ARRAY(5 << 6))); - unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator)); ASSERT(eq->init("1", baseOperand["1"]).isOK()); ElemMatchObjectMatchExpression op; ASSERT(op.init("a", eq.release()).isOK()); @@ -72,11 +74,12 @@ TEST(ElemMatchObjectMatchExpression, MatchesElementMultiple) { BSONObj notMatch2 = BSON("a" << BSON_ARRAY(BSON("b" << 6 << "c" << 7))); BSONObj notMatch3 = BSON("a" << BSON_ARRAY(BSON("b" << BSON_ARRAY(5 << 6)))); BSONObj match = BSON("a" << BSON_ARRAY(BSON("b" << BSON_ARRAY(5 << 6) << "c" << 7))); - unique_ptr<ComparisonMatchExpression> eq1(new EqualityMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> eq1(new EqualityMatchExpression(collator)); ASSERT(eq1->init("b", baseOperand1["b"]).isOK()); - unique_ptr<ComparisonMatchExpression> eq2(new EqualityMatchExpression()); + unique_ptr<ComparisonMatchExpression> eq2(new EqualityMatchExpression(collator)); ASSERT(eq2->init("b", baseOperand2["b"]).isOK()); - unique_ptr<ComparisonMatchExpression> eq3(new EqualityMatchExpression()); + unique_ptr<ComparisonMatchExpression> eq3(new EqualityMatchExpression(collator)); ASSERT(eq3->init("c", baseOperand3["c"]).isOK()); unique_ptr<AndMatchExpression> andOp(new AndMatchExpression()); @@ -94,7 +97,8 @@ TEST(ElemMatchObjectMatchExpression, MatchesElementMultiple) { TEST(ElemMatchObjectMatchExpression, MatchesNonArray) { BSONObj baseOperand = BSON("b" << 5); - unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator)); ASSERT(eq->init("b", baseOperand["b"]).isOK()); ElemMatchObjectMatchExpression op; ASSERT(op.init("a", eq.release()).isOK()); @@ -107,7 +111,8 @@ TEST(ElemMatchObjectMatchExpression, MatchesNonArray) { TEST(ElemMatchObjectMatchExpression, MatchesArrayObject) { BSONObj baseOperand = BSON("b" << 5); - unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator)); ASSERT(eq->init("b", baseOperand["b"]).isOK()); ElemMatchObjectMatchExpression op; ASSERT(op.init("a", eq.release()).isOK()); @@ -119,7 +124,8 @@ TEST(ElemMatchObjectMatchExpression, MatchesArrayObject) { TEST(ElemMatchObjectMatchExpression, MatchesMultipleNamedValues) { BSONObj baseOperand = BSON("c" << 5); - unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator)); ASSERT(eq->init("c", baseOperand["c"]).isOK()); ElemMatchObjectMatchExpression op; ASSERT(op.init("a.b", eq.release()).isOK()); @@ -131,7 +137,8 @@ TEST(ElemMatchObjectMatchExpression, MatchesMultipleNamedValues) { TEST(ElemMatchObjectMatchExpression, ElemMatchKey) { BSONObj baseOperand = BSON("c" << 6); - unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator)); ASSERT(eq->init("c", baseOperand["c"]).isOK()); ElemMatchObjectMatchExpression op; ASSERT(op.init("a.b", eq.release()).isOK()); @@ -171,7 +178,8 @@ TEST(ElemMatchValueMatchExpression, MatchesElementSingle) { BSONObj baseOperand = BSON("$gt" << 5); BSONObj match = BSON("a" << BSON_ARRAY(6)); BSONObj notMatch = BSON("a" << BSON_ARRAY(4)); - unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression(collator)); ASSERT(gt->init("", baseOperand["$gt"]).isOK()); ElemMatchValueMatchExpression op; ASSERT(op.init("a", gt.release()).isOK()); @@ -185,9 +193,10 @@ TEST(ElemMatchValueMatchExpression, MatchesElementMultiple) { BSONObj notMatch1 = BSON("a" << BSON_ARRAY(0 << 1)); BSONObj notMatch2 = BSON("a" << BSON_ARRAY(10 << 11)); BSONObj match = BSON("a" << BSON_ARRAY(0 << 5 << 11)); - unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression(collator)); ASSERT(gt->init("", baseOperand1["$gt"]).isOK()); - unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression()); + unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression(collator)); ASSERT(lt->init("", baseOperand2["$lt"]).isOK()); ElemMatchValueMatchExpression op; @@ -202,7 +211,8 @@ TEST(ElemMatchValueMatchExpression, MatchesElementMultiple) { TEST(ElemMatchValueMatchExpression, MatchesNonArray) { BSONObj baseOperand = BSON("$gt" << 5); - unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression(collator)); ASSERT(gt->init("", baseOperand["$gt"]).isOK()); ElemMatchObjectMatchExpression op; ASSERT(op.init("a", gt.release()).isOK()); @@ -214,7 +224,8 @@ TEST(ElemMatchValueMatchExpression, MatchesNonArray) { TEST(ElemMatchValueMatchExpression, MatchesArrayScalar) { BSONObj baseOperand = BSON("$gt" << 5); - unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression(collator)); ASSERT(gt->init("", baseOperand["$gt"]).isOK()); ElemMatchValueMatchExpression op; ASSERT(op.init("a", gt.release()).isOK()); @@ -225,7 +236,8 @@ TEST(ElemMatchValueMatchExpression, MatchesArrayScalar) { TEST(ElemMatchValueMatchExpression, MatchesMultipleNamedValues) { BSONObj baseOperand = BSON("$gt" << 5); - unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression(collator)); ASSERT(gt->init("", baseOperand["$gt"]).isOK()); ElemMatchValueMatchExpression op; ASSERT(op.init("a.b", gt.release()).isOK()); @@ -237,7 +249,8 @@ TEST(ElemMatchValueMatchExpression, MatchesMultipleNamedValues) { TEST(ElemMatchValueMatchExpression, ElemMatchKey) { BSONObj baseOperand = BSON("$gt" << 6); - unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression(collator)); ASSERT(gt->init("", baseOperand["$gt"]).isOK()); ElemMatchValueMatchExpression op; ASSERT(op.init("a.b", gt.release()).isOK()); @@ -274,11 +287,12 @@ TEST( ElemMatchValueMatchExpression, MatchesIndexKey ) { TEST(AndOfElemMatch, MatchesElement) { BSONObj baseOperanda1 = BSON("a" << 1); - unique_ptr<ComparisonMatchExpression> eqa1(new EqualityMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> eqa1(new EqualityMatchExpression(collator)); ASSERT(eqa1->init("a", baseOperanda1["a"]).isOK()); BSONObj baseOperandb1 = BSON("b" << 1); - unique_ptr<ComparisonMatchExpression> eqb1(new EqualityMatchExpression()); + unique_ptr<ComparisonMatchExpression> eqb1(new EqualityMatchExpression(collator)); ASSERT(eqb1->init("b", baseOperandb1["b"]).isOK()); unique_ptr<AndMatchExpression> and1(new AndMatchExpression()); @@ -291,11 +305,11 @@ TEST(AndOfElemMatch, MatchesElement) { // elemMatch1 = { x : { $elemMatch : { a : 1, b : 1 } } } BSONObj baseOperanda2 = BSON("a" << 2); - unique_ptr<ComparisonMatchExpression> eqa2(new EqualityMatchExpression()); + unique_ptr<ComparisonMatchExpression> eqa2(new EqualityMatchExpression(collator)); ASSERT(eqa2->init("a", baseOperanda2["a"]).isOK()); BSONObj baseOperandb2 = BSON("b" << 2); - unique_ptr<ComparisonMatchExpression> eqb2(new EqualityMatchExpression()); + unique_ptr<ComparisonMatchExpression> eqb2(new EqualityMatchExpression(collator)); ASSERT(eqb2->init("b", baseOperandb2["b"]).isOK()); unique_ptr<AndMatchExpression> and2(new AndMatchExpression()); @@ -331,11 +345,12 @@ TEST(AndOfElemMatch, MatchesElement) { TEST(AndOfElemMatch, Matches) { BSONObj baseOperandgt1 = BSON("$gt" << 1); - unique_ptr<ComparisonMatchExpression> gt1(new GTMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> gt1(new GTMatchExpression(collator)); ASSERT(gt1->init("", baseOperandgt1["$gt"]).isOK()); BSONObj baseOperandlt1 = BSON("$lt" << 10); - unique_ptr<ComparisonMatchExpression> lt1(new LTMatchExpression()); + unique_ptr<ComparisonMatchExpression> lt1(new LTMatchExpression(collator)); ASSERT(lt1->init("", baseOperandlt1["$lt"]).isOK()); unique_ptr<ElemMatchValueMatchExpression> elemMatch1(new ElemMatchValueMatchExpression()); @@ -345,11 +360,11 @@ TEST(AndOfElemMatch, Matches) { // elemMatch1 = { x : { $elemMatch : { $gt : 1 , $lt : 10 } } } BSONObj baseOperandgt2 = BSON("$gt" << 101); - unique_ptr<ComparisonMatchExpression> gt2(new GTMatchExpression()); + unique_ptr<ComparisonMatchExpression> gt2(new GTMatchExpression(collator)); ASSERT(gt2->init("", baseOperandgt2["$gt"]).isOK()); BSONObj baseOperandlt2 = BSON("$lt" << 110); - unique_ptr<ComparisonMatchExpression> lt2(new LTMatchExpression()); + unique_ptr<ComparisonMatchExpression> lt2(new LTMatchExpression(collator)); ASSERT(lt2->init("", baseOperandlt2["$lt"]).isOK()); unique_ptr<ElemMatchValueMatchExpression> elemMatch2(new ElemMatchValueMatchExpression()); diff --git a/src/mongo/db/matcher/expression_leaf.cpp b/src/mongo/db/matcher/expression_leaf.cpp index 491643ef6ec..a59028078ba 100644 --- a/src/mongo/db/matcher/expression_leaf.cpp +++ b/src/mongo/db/matcher/expression_leaf.cpp @@ -40,6 +40,7 @@ #include "mongo/db/field_ref.h" #include "mongo/db/jsobj.h" #include "mongo/db/matcher/path.h" +#include "mongo/db/query/collation/collator_interface.h" #include "mongo/stdx/memory.h" #include "mongo/util/mongoutils/str.h" @@ -73,6 +74,10 @@ bool ComparisonMatchExpression::equivalent(const MatchExpression* other) const { const ComparisonMatchExpression* realOther = static_cast<const ComparisonMatchExpression*>(other); + if (!CollatorInterface::collatorsMatch(_collator, realOther->_collator)) { + return false; + } + return path() == realOther->path() && _rhs.valuesEqual(realOther->_rhs); } @@ -143,7 +148,7 @@ bool ComparisonMatchExpression::matchesSingleElement(const BSONElement& e) const } } - int x = compareElementValues(e, _rhs); + int x = compareElementValues(e, _rhs, _collator); // log() << "\t\t" << x << endl; @@ -519,10 +524,8 @@ bool TypeMatchExpression::equivalent(const MatchExpression* other) const { // -------- -ArrayFilterEntries::ArrayFilterEntries() { - _hasNull = false; - _hasEmptyArray = false; -} +ArrayFilterEntries::ArrayFilterEntries(CollatorInterface* collator) + : _hasNull(false), _hasEmptyArray(false), _equalities(collator), _collator(collator) {} ArrayFilterEntries::~ArrayFilterEntries() { for (unsigned i = 0; i < _regexes.size(); i++) @@ -564,6 +567,10 @@ bool ArrayFilterEntries::equivalent(const ArrayFilterEntries& other) const { if (!_regexes[i]->equivalent(other._regexes[i])) return false; + if (!CollatorInterface::collatorsMatch(_collator, other._collator)) { + return false; + } + return _equalities == other._equalities; } @@ -666,7 +673,8 @@ bool InMatchExpression::equivalent(const MatchExpression* other) const { } std::unique_ptr<MatchExpression> InMatchExpression::shallowClone() const { - std::unique_ptr<InMatchExpression> next = stdx::make_unique<InMatchExpression>(); + std::unique_ptr<InMatchExpression> next = + stdx::make_unique<InMatchExpression>(_arrayEntries.getCollator()); copyTo(next.get()); if (getTag()) { next->setTag(getTag()->clone()); diff --git a/src/mongo/db/matcher/expression_leaf.h b/src/mongo/db/matcher/expression_leaf.h index 1f1b305e141..957f328f1b1 100644 --- a/src/mongo/db/matcher/expression_leaf.h +++ b/src/mongo/db/matcher/expression_leaf.h @@ -43,6 +43,8 @@ class RE; namespace mongo { +class CollatorInterface; + /** * This file contains leaves in the parse tree that are not array-based. * @@ -83,7 +85,11 @@ private: */ class ComparisonMatchExpression : public LeafMatchExpression { public: - ComparisonMatchExpression(MatchType type) : LeafMatchExpression(type) {} + /** + * 'collator' must outlive the ComparisonMatchExpression and any clones made of it. + */ + ComparisonMatchExpression(MatchType type, CollatorInterface* collator) + : LeafMatchExpression(type), _collator(collator) {} Status init(StringData path, const BSONElement& rhs); @@ -105,8 +111,13 @@ public: return _rhs; } + CollatorInterface* getCollator() const { + return _collator; + } + protected: BSONElement _rhs; + CollatorInterface* _collator; }; // @@ -115,9 +126,11 @@ protected: class EqualityMatchExpression : public ComparisonMatchExpression { public: - EqualityMatchExpression() : ComparisonMatchExpression(EQ) {} + EqualityMatchExpression(CollatorInterface* collator) + : ComparisonMatchExpression(EQ, collator) {} virtual std::unique_ptr<MatchExpression> shallowClone() const { - std::unique_ptr<ComparisonMatchExpression> e = stdx::make_unique<EqualityMatchExpression>(); + std::unique_ptr<ComparisonMatchExpression> e = + stdx::make_unique<EqualityMatchExpression>(_collator); e->init(path(), _rhs); if (getTag()) { e->setTag(getTag()->clone()); @@ -128,9 +141,10 @@ public: class LTEMatchExpression : public ComparisonMatchExpression { public: - LTEMatchExpression() : ComparisonMatchExpression(LTE) {} + LTEMatchExpression(CollatorInterface* collator) : ComparisonMatchExpression(LTE, collator) {} virtual std::unique_ptr<MatchExpression> shallowClone() const { - std::unique_ptr<ComparisonMatchExpression> e = stdx::make_unique<LTEMatchExpression>(); + std::unique_ptr<ComparisonMatchExpression> e = + stdx::make_unique<LTEMatchExpression>(_collator); e->init(path(), _rhs); if (getTag()) { e->setTag(getTag()->clone()); @@ -141,9 +155,10 @@ public: class LTMatchExpression : public ComparisonMatchExpression { public: - LTMatchExpression() : ComparisonMatchExpression(LT) {} + LTMatchExpression(CollatorInterface* collator) : ComparisonMatchExpression(LT, collator) {} virtual std::unique_ptr<MatchExpression> shallowClone() const { - std::unique_ptr<ComparisonMatchExpression> e = stdx::make_unique<LTMatchExpression>(); + std::unique_ptr<ComparisonMatchExpression> e = + stdx::make_unique<LTMatchExpression>(_collator); e->init(path(), _rhs); if (getTag()) { e->setTag(getTag()->clone()); @@ -154,9 +169,10 @@ public: class GTMatchExpression : public ComparisonMatchExpression { public: - GTMatchExpression() : ComparisonMatchExpression(GT) {} + GTMatchExpression(CollatorInterface* collator) : ComparisonMatchExpression(GT, collator) {} virtual std::unique_ptr<MatchExpression> shallowClone() const { - std::unique_ptr<ComparisonMatchExpression> e = stdx::make_unique<GTMatchExpression>(); + std::unique_ptr<ComparisonMatchExpression> e = + stdx::make_unique<GTMatchExpression>(_collator); e->init(path(), _rhs); if (getTag()) { e->setTag(getTag()->clone()); @@ -167,9 +183,10 @@ public: class GTEMatchExpression : public ComparisonMatchExpression { public: - GTEMatchExpression() : ComparisonMatchExpression(GTE) {} + GTEMatchExpression(CollatorInterface* collator) : ComparisonMatchExpression(GTE, collator) {} virtual std::unique_ptr<MatchExpression> shallowClone() const { - std::unique_ptr<ComparisonMatchExpression> e = stdx::make_unique<GTEMatchExpression>(); + std::unique_ptr<ComparisonMatchExpression> e = + stdx::make_unique<GTEMatchExpression>(_collator); e->init(path(), _rhs); if (getTag()) { e->setTag(getTag()->clone()); @@ -298,7 +315,7 @@ class ArrayFilterEntries { MONGO_DISALLOW_COPYING(ArrayFilterEntries); public: - ArrayFilterEntries(); + ArrayFilterEntries(CollatorInterface* collator); ~ArrayFilterEntries(); Status addEquality(const BSONElement& e); @@ -331,6 +348,10 @@ public: return _equalities.size() + _regexes.size(); } + CollatorInterface* getCollator() const { + return _collator; + } + bool equivalent(const ArrayFilterEntries& other) const; void copyTo(ArrayFilterEntries& toFillIn) const; @@ -344,6 +365,7 @@ private: bool _hasEmptyArray; BSONElementSet _equalities; std::vector<RegexMatchExpression*> _regexes; + CollatorInterface* _collator; }; /** @@ -351,7 +373,11 @@ private: */ class InMatchExpression : public LeafMatchExpression { public: - InMatchExpression() : LeafMatchExpression(MATCH_IN) {} + /** + * 'collator' must outlive the InMatchExpression and any clones made of it. + */ + InMatchExpression(CollatorInterface* collator) + : LeafMatchExpression(MATCH_IN), _arrayEntries(collator) {} Status init(StringData path); virtual std::unique_ptr<MatchExpression> shallowClone() const; @@ -374,6 +400,10 @@ public: return _arrayEntries; } + CollatorInterface* getCollator() const { + return _arrayEntries.getCollator(); + } + private: bool _matchesRealElement(const BSONElement& e) const; ArrayFilterEntries _arrayEntries; diff --git a/src/mongo/db/matcher/expression_leaf_test.cpp b/src/mongo/db/matcher/expression_leaf_test.cpp index b2946f5f546..68935fbd8b9 100644 --- a/src/mongo/db/matcher/expression_leaf_test.cpp +++ b/src/mongo/db/matcher/expression_leaf_test.cpp @@ -35,17 +35,57 @@ #include "mongo/db/matcher/expression_parser.h" #include "mongo/db/matcher/expression.h" #include "mongo/db/matcher/expression_leaf.h" +#include "mongo/db/query/collation/collator_interface_mock.h" namespace mongo { using std::string; +TEST(ComparisonMatchExpression, ComparisonMatchExpressionsWithUnequalCollatorsAreUnequal) { + CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kReverseString); + EqualityMatchExpression eq1(&collator1); + CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kAlwaysEqual); + EqualityMatchExpression eq2(&collator2); + ASSERT(!eq1.equivalent(&eq2)); +} + +TEST(ComparisonMatchExpression, ComparisonMatchExpressionsWithEqualCollatorsAreEqual) { + CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kAlwaysEqual); + EqualityMatchExpression eq1(&collator1); + CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kAlwaysEqual); + EqualityMatchExpression eq2(&collator2); + ASSERT(eq1.equivalent(&eq2)); +} + +TEST(ComparisonMatchExpression, StringMatchingWithNullCollatorUsesBinaryComparison) { + BSONObj operand = BSON("a" + << "string"); + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq(collator); + ASSERT(eq.init("a", operand["a"]).isOK()); + ASSERT(!eq.matchesBSON(BSON("a" + << "string2"), + NULL)); +} + +TEST(ComparisonMatchExpression, StringMatchingRespectsCollation) { + BSONObj operand = BSON("a" + << "string"); + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); + EqualityMatchExpression eq(&collator); + ASSERT(eq.init("a", operand["a"]).isOK()); + ASSERT(eq.matchesBSON(BSON("a" + << "string2"), + NULL)); +} + TEST(EqOp, MatchesElement) { BSONObj operand = BSON("a" << 5); BSONObj match = BSON("a" << 5.0); BSONObj notMatch = BSON("a" << 6); - EqualityMatchExpression eq; + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq(collator); eq.init("", operand["a"]); ASSERT(eq.matchesSingleElement(match.firstElement())); ASSERT(!eq.matchesSingleElement(notMatch.firstElement())); @@ -55,13 +95,15 @@ TEST(EqOp, MatchesElement) { TEST(EqOp, InvalidEooOperand) { BSONObj operand; - EqualityMatchExpression eq; + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq(collator); ASSERT(!eq.init("", operand.firstElement()).isOK()); } TEST(EqOp, MatchesScalar) { BSONObj operand = BSON("a" << 5); - EqualityMatchExpression eq; + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq(collator); eq.init("a", operand["a"]); ASSERT(eq.matchesBSON(BSON("a" << 5.0), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << 4), NULL)); @@ -69,7 +111,8 @@ TEST(EqOp, MatchesScalar) { TEST(EqOp, MatchesArrayValue) { BSONObj operand = BSON("a" << 5); - EqualityMatchExpression eq; + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq(collator); eq.init("a", operand["a"]); ASSERT(eq.matchesBSON(BSON("a" << BSON_ARRAY(5.0 << 6)), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << BSON_ARRAY(6 << 7)), NULL)); @@ -77,7 +120,8 @@ TEST(EqOp, MatchesArrayValue) { TEST(EqOp, MatchesReferencedObjectValue) { BSONObj operand = BSON("a.b" << 5); - EqualityMatchExpression eq; + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq(collator); eq.init("a.b", operand["a.b"]); ASSERT(eq.matchesBSON(BSON("a" << BSON("b" << 5)), NULL)); ASSERT(eq.matchesBSON(BSON("a" << BSON("b" << BSON_ARRAY(5))), NULL)); @@ -86,7 +130,8 @@ TEST(EqOp, MatchesReferencedObjectValue) { TEST(EqOp, MatchesReferencedArrayValue) { BSONObj operand = BSON("a.0" << 5); - EqualityMatchExpression eq; + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq(collator); eq.init("a.0", operand["a.0"]); ASSERT(eq.matchesBSON(BSON("a" << BSON_ARRAY(5)), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(5))), NULL)); @@ -94,7 +139,8 @@ TEST(EqOp, MatchesReferencedArrayValue) { TEST(EqOp, MatchesNull) { BSONObj operand = BSON("a" << BSONNULL); - EqualityMatchExpression eq; + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq(collator); eq.init("a", operand["a"]); ASSERT(eq.matchesBSON(BSONObj(), NULL)); ASSERT(eq.matchesBSON(BSON("a" << BSONNULL), NULL)); @@ -107,7 +153,8 @@ TEST(EqOp, MatchesNull) { // not necessarily how it should work ideally. TEST(EqOp, MatchesNestedNull) { BSONObj operand = BSON("a.b" << BSONNULL); - EqualityMatchExpression eq; + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq(collator); eq.init("a.b", operand["a.b"]); // null matches any empty object that is on a subpath of a.b ASSERT(eq.matchesBSON(BSONObj(), NULL)); @@ -127,7 +174,8 @@ TEST(EqOp, MatchesNestedNull) { TEST(EqOp, MatchesMinKey) { BSONObj operand = BSON("a" << MinKey); - EqualityMatchExpression eq; + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq(collator); eq.init("a", operand["a"]); ASSERT(eq.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << MaxKey), NULL)); @@ -137,7 +185,8 @@ TEST(EqOp, MatchesMinKey) { TEST(EqOp, MatchesMaxKey) { BSONObj operand = BSON("a" << MaxKey); - EqualityMatchExpression eq; + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq(collator); ASSERT(eq.init("a", operand["a"]).isOK()); ASSERT(eq.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << MinKey), NULL)); @@ -146,7 +195,8 @@ TEST(EqOp, MatchesMaxKey) { TEST(EqOp, MatchesFullArray) { BSONObj operand = BSON("a" << BSON_ARRAY(1 << 2)); - EqualityMatchExpression eq; + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq(collator); ASSERT(eq.init("a", operand["a"]).isOK()); ASSERT(eq.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2)), NULL)); ASSERT(!eq.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2 << 3)), NULL)); @@ -156,7 +206,8 @@ TEST(EqOp, MatchesFullArray) { TEST(EqOp, MatchesThroughNestedArray) { BSONObj operand = BSON("a.b.c.d" << 3); - EqualityMatchExpression eq; + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq(collator); eq.init("a.b.c.d", operand["a.b.c.d"]); BSONObj obj = fromjson("{a:{b:[{c:[{d:1},{d:2}]},{c:[{d:3}]}]}}"); ASSERT(eq.matchesBSON(obj, NULL)); @@ -164,7 +215,8 @@ TEST(EqOp, MatchesThroughNestedArray) { TEST(EqOp, ElemMatchKey) { BSONObj operand = BSON("a" << 5); - EqualityMatchExpression eq; + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq(collator); ASSERT(eq.init("a", operand["a"]).isOK()); MatchDetails details; details.requestElemMatchKey(); @@ -183,7 +235,8 @@ TEST(EqOp, ElemMatchKey) { TEST(EqOp, ElemMatchKeyWithImplicitAndExplicitTraversal) { BSONObj operand = BSON("a.0.b" << 3); BSONElement operandFirstElt = operand.firstElement(); - EqualityMatchExpression eq; + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq(collator); ASSERT(eq.init(operandFirstElt.fieldName(), operandFirstElt).isOK()); MatchDetails details; details.requestElemMatchKey(); @@ -194,9 +247,10 @@ TEST(EqOp, ElemMatchKeyWithImplicitAndExplicitTraversal) { } TEST(EqOp, Equality1) { - EqualityMatchExpression eq1; - EqualityMatchExpression eq2; - EqualityMatchExpression eq3; + CollatorInterface* collator = nullptr; + EqualityMatchExpression eq1(collator); + EqualityMatchExpression eq2(collator); + EqualityMatchExpression eq3(collator); BSONObj operand = BSON("a" << 5 << "b" << 5 << "c" << 4); @@ -266,7 +320,8 @@ TEST(LtOp, MatchesElement) { BSONObj notMatchEqual = BSON("a" << 5); BSONObj notMatchWrongType = BSON("a" << "foo"); - LTMatchExpression lt; + CollatorInterface* collator = nullptr; + LTMatchExpression lt(collator); ASSERT(lt.init("", operand["$lt"]).isOK()); ASSERT(lt.matchesSingleElement(match.firstElement())); ASSERT(!lt.matchesSingleElement(notMatch.firstElement())); @@ -276,13 +331,15 @@ TEST(LtOp, MatchesElement) { TEST(LtOp, InvalidEooOperand) { BSONObj operand; - LTMatchExpression lt; + CollatorInterface* collator = nullptr; + LTMatchExpression lt(collator); ASSERT(!lt.init("", operand.firstElement()).isOK()); } TEST(LtOp, MatchesScalar) { BSONObj operand = BSON("$lt" << 5); - LTMatchExpression lt; + CollatorInterface* collator = nullptr; + LTMatchExpression lt(collator); ASSERT(lt.init("a", operand["$lt"]).isOK()); ASSERT(lt.matchesBSON(BSON("a" << 4.5), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << 6), NULL)); @@ -290,7 +347,8 @@ TEST(LtOp, MatchesScalar) { TEST(LtOp, MatchesScalarEmptyKey) { BSONObj operand = BSON("$lt" << 5); - LTMatchExpression lt; + CollatorInterface* collator = nullptr; + LTMatchExpression lt(collator); ASSERT(lt.init("", operand["$lt"]).isOK()); ASSERT(lt.matchesBSON(BSON("" << 4.5), NULL)); ASSERT(!lt.matchesBSON(BSON("" << 6), NULL)); @@ -298,7 +356,8 @@ TEST(LtOp, MatchesScalarEmptyKey) { TEST(LtOp, MatchesArrayValue) { BSONObj operand = BSON("$lt" << 5); - LTMatchExpression lt; + CollatorInterface* collator = nullptr; + LTMatchExpression lt(collator); ASSERT(lt.init("a", operand["$lt"]).isOK()); ASSERT(lt.matchesBSON(BSON("a" << BSON_ARRAY(6 << 4.5)), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSON_ARRAY(6 << 7)), NULL)); @@ -306,7 +365,8 @@ TEST(LtOp, MatchesArrayValue) { TEST(LtOp, MatchesWholeArray) { BSONObj operand = BSON("$lt" << BSON_ARRAY(5)); - LTMatchExpression lt; + CollatorInterface* collator = nullptr; + LTMatchExpression lt(collator); ASSERT(lt.init("a", operand["$lt"]).isOK()); ASSERT(lt.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSON_ARRAY(5)), NULL)); @@ -319,7 +379,8 @@ TEST(LtOp, MatchesWholeArray) { TEST(LtOp, MatchesNull) { BSONObj operand = BSON("$lt" << BSONNULL); - LTMatchExpression lt; + CollatorInterface* collator = nullptr; + LTMatchExpression lt(collator); ASSERT(lt.init("a", operand["$lt"]).isOK()); ASSERT(!lt.matchesBSON(BSONObj(), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSONNULL), NULL)); @@ -330,7 +391,8 @@ TEST(LtOp, MatchesNull) { TEST(LtOp, MatchesDotNotationNull) { BSONObj operand = BSON("$lt" << BSONNULL); - LTMatchExpression lt; + CollatorInterface* collator = nullptr; + LTMatchExpression lt(collator); ASSERT(lt.init("a.b", operand["$lt"]).isOK()); ASSERT(!lt.matchesBSON(BSONObj(), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << BSONNULL), NULL)); @@ -344,7 +406,8 @@ TEST(LtOp, MatchesDotNotationNull) { TEST(LtOp, MatchesMinKey) { BSONObj operand = BSON("a" << MinKey); - LTMatchExpression lt; + CollatorInterface* collator = nullptr; + LTMatchExpression lt(collator); ASSERT(lt.init("a", operand["a"]).isOK()); ASSERT(!lt.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(!lt.matchesBSON(BSON("a" << MaxKey), NULL)); @@ -353,7 +416,8 @@ TEST(LtOp, MatchesMinKey) { TEST(LtOp, MatchesMaxKey) { BSONObj operand = BSON("a" << MaxKey); - LTMatchExpression lt; + CollatorInterface* collator = nullptr; + LTMatchExpression lt(collator); ASSERT(lt.init("a", operand["a"]).isOK()); ASSERT(!lt.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(lt.matchesBSON(BSON("a" << MinKey), NULL)); @@ -362,7 +426,8 @@ TEST(LtOp, MatchesMaxKey) { TEST(LtOp, ElemMatchKey) { BSONObj operand = BSON("$lt" << 5); - LTMatchExpression lt; + CollatorInterface* collator = nullptr; + LTMatchExpression lt(collator); ASSERT(lt.init("a", operand["$lt"]).isOK()); MatchDetails details; details.requestElemMatchKey(); @@ -432,7 +497,8 @@ TEST(LteOp, MatchesElement) { BSONObj notMatch = BSON("a" << 6); BSONObj notMatchWrongType = BSON("a" << "foo"); - LTEMatchExpression lte; + CollatorInterface* collator = nullptr; + LTEMatchExpression lte(collator); ASSERT(lte.init("", operand["$lte"]).isOK()); ASSERT(lte.matchesSingleElement(match.firstElement())); ASSERT(lte.matchesSingleElement(equalMatch.firstElement())); @@ -442,13 +508,15 @@ TEST(LteOp, MatchesElement) { TEST(LteOp, InvalidEooOperand) { BSONObj operand; - LTEMatchExpression lte; + CollatorInterface* collator = nullptr; + LTEMatchExpression lte(collator); ASSERT(!lte.init("", operand.firstElement()).isOK()); } TEST(LteOp, MatchesScalar) { BSONObj operand = BSON("$lte" << 5); - LTEMatchExpression lte; + CollatorInterface* collator = nullptr; + LTEMatchExpression lte(collator); ASSERT(lte.init("a", operand["$lte"]).isOK()); ASSERT(lte.matchesBSON(BSON("a" << 4.5), NULL)); ASSERT(!lte.matchesBSON(BSON("a" << 6), NULL)); @@ -456,7 +524,8 @@ TEST(LteOp, MatchesScalar) { TEST(LteOp, MatchesArrayValue) { BSONObj operand = BSON("$lte" << 5); - LTEMatchExpression lte; + CollatorInterface* collator = nullptr; + LTEMatchExpression lte(collator); ASSERT(lte.init("a", operand["$lte"]).isOK()); ASSERT(lte.matchesBSON(BSON("a" << BSON_ARRAY(6 << 4.5)), NULL)); ASSERT(!lte.matchesBSON(BSON("a" << BSON_ARRAY(6 << 7)), NULL)); @@ -464,7 +533,8 @@ TEST(LteOp, MatchesArrayValue) { TEST(LteOp, MatchesWholeArray) { BSONObj operand = BSON("$lte" << BSON_ARRAY(5)); - LTEMatchExpression lte; + CollatorInterface* collator = nullptr; + LTEMatchExpression lte(collator); ASSERT(lte.init("a", operand["$lte"]).isOK()); ASSERT(lte.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL)); ASSERT(lte.matchesBSON(BSON("a" << BSON_ARRAY(5)), NULL)); @@ -477,7 +547,8 @@ TEST(LteOp, MatchesWholeArray) { TEST(LteOp, MatchesNull) { BSONObj operand = BSON("$lte" << BSONNULL); - LTEMatchExpression lte; + CollatorInterface* collator = nullptr; + LTEMatchExpression lte(collator); ASSERT(lte.init("a", operand["$lte"]).isOK()); ASSERT(lte.matchesBSON(BSONObj(), NULL)); ASSERT(lte.matchesBSON(BSON("a" << BSONNULL), NULL)); @@ -488,7 +559,8 @@ TEST(LteOp, MatchesNull) { TEST(LteOp, MatchesDotNotationNull) { BSONObj operand = BSON("$lte" << BSONNULL); - LTEMatchExpression lte; + CollatorInterface* collator = nullptr; + LTEMatchExpression lte(collator); ASSERT(lte.init("a.b", operand["$lte"]).isOK()); ASSERT(lte.matchesBSON(BSONObj(), NULL)); ASSERT(lte.matchesBSON(BSON("a" << BSONNULL), NULL)); @@ -502,7 +574,8 @@ TEST(LteOp, MatchesDotNotationNull) { TEST(LteOp, MatchesMinKey) { BSONObj operand = BSON("a" << MinKey); - LTEMatchExpression lte; + CollatorInterface* collator = nullptr; + LTEMatchExpression lte(collator); ASSERT(lte.init("a", operand["a"]).isOK()); ASSERT(lte.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(!lte.matchesBSON(BSON("a" << MaxKey), NULL)); @@ -511,7 +584,8 @@ TEST(LteOp, MatchesMinKey) { TEST(LteOp, MatchesMaxKey) { BSONObj operand = BSON("a" << MaxKey); - LTEMatchExpression lte; + CollatorInterface* collator = nullptr; + LTEMatchExpression lte(collator); ASSERT(lte.init("a", operand["a"]).isOK()); ASSERT(lte.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(lte.matchesBSON(BSON("a" << MinKey), NULL)); @@ -521,7 +595,8 @@ TEST(LteOp, MatchesMaxKey) { TEST(LteOp, ElemMatchKey) { BSONObj operand = BSON("$lte" << 5); - LTEMatchExpression lte; + CollatorInterface* collator = nullptr; + LTEMatchExpression lte(collator); ASSERT(lte.init("a", operand["$lte"]).isOK()); MatchDetails details; details.requestElemMatchKey(); @@ -601,13 +676,15 @@ TEST(LteOp, ElemMatchKey) { TEST(GtOp, InvalidEooOperand) { BSONObj operand; - GTMatchExpression gt; + CollatorInterface* collator = nullptr; + GTMatchExpression gt(collator); ASSERT(!gt.init("", operand.firstElement()).isOK()); } TEST(GtOp, MatchesScalar) { BSONObj operand = BSON("$gt" << 5); - GTMatchExpression gt; + CollatorInterface* collator = nullptr; + GTMatchExpression gt(collator); ASSERT(gt.init("a", operand["$gt"]).isOK()); ASSERT(gt.matchesBSON(BSON("a" << 5.5), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << 4), NULL)); @@ -615,7 +692,8 @@ TEST(GtOp, MatchesScalar) { TEST(GtOp, MatchesArrayValue) { BSONObj operand = BSON("$gt" << 5); - GTMatchExpression gt; + CollatorInterface* collator = nullptr; + GTMatchExpression gt(collator); ASSERT(gt.init("a", operand["$gt"]).isOK()); ASSERT(gt.matchesBSON(BSON("a" << BSON_ARRAY(3 << 5.5)), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << BSON_ARRAY(2 << 4)), NULL)); @@ -623,7 +701,8 @@ TEST(GtOp, MatchesArrayValue) { TEST(GtOp, MatchesWholeArray) { BSONObj operand = BSON("$gt" << BSON_ARRAY(5)); - GTMatchExpression gt; + CollatorInterface* collator = nullptr; + GTMatchExpression gt(collator); ASSERT(gt.init("a", operand["$gt"]).isOK()); ASSERT(!gt.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << BSON_ARRAY(5)), NULL)); @@ -638,7 +717,8 @@ TEST(GtOp, MatchesWholeArray) { TEST(GtOp, MatchesNull) { BSONObj operand = BSON("$gt" << BSONNULL); - GTMatchExpression gt; + CollatorInterface* collator = nullptr; + GTMatchExpression gt(collator); ASSERT(gt.init("a", operand["$gt"]).isOK()); ASSERT(!gt.matchesBSON(BSONObj(), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << BSONNULL), NULL)); @@ -649,7 +729,8 @@ TEST(GtOp, MatchesNull) { TEST(GtOp, MatchesDotNotationNull) { BSONObj operand = BSON("$gt" << BSONNULL); - GTMatchExpression gt; + CollatorInterface* collator = nullptr; + GTMatchExpression gt(collator); ASSERT(gt.init("a.b", operand["$gt"]).isOK()); ASSERT(!gt.matchesBSON(BSONObj(), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << BSONNULL), NULL)); @@ -663,7 +744,8 @@ TEST(GtOp, MatchesDotNotationNull) { TEST(GtOp, MatchesMinKey) { BSONObj operand = BSON("a" << MinKey); - GTMatchExpression gt; + CollatorInterface* collator = nullptr; + GTMatchExpression gt(collator); ASSERT(gt.init("a", operand["a"]).isOK()); ASSERT(!gt.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(gt.matchesBSON(BSON("a" << MaxKey), NULL)); @@ -672,7 +754,8 @@ TEST(GtOp, MatchesMinKey) { TEST(GtOp, MatchesMaxKey) { BSONObj operand = BSON("a" << MaxKey); - GTMatchExpression gt; + CollatorInterface* collator = nullptr; + GTMatchExpression gt(collator); ASSERT(gt.init("a", operand["a"]).isOK()); ASSERT(!gt.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(!gt.matchesBSON(BSON("a" << MinKey), NULL)); @@ -681,7 +764,8 @@ TEST(GtOp, MatchesMaxKey) { TEST(GtOp, ElemMatchKey) { BSONObj operand = BSON("$gt" << 5); - GTMatchExpression gt; + CollatorInterface* collator = nullptr; + GTMatchExpression gt(collator); ASSERT(gt.init("a", operand["$gt"]).isOK()); MatchDetails details; details.requestElemMatchKey(); @@ -745,14 +829,15 @@ TEST(GtOp, ElemMatchKey) { } */ -TEST(ComparisonMatchExpression, MatchesElement) { +TEST(GteOp, MatchesElement) { BSONObj operand = BSON("$gte" << 5); BSONObj match = BSON("a" << 5.5); BSONObj equalMatch = BSON("a" << 5); BSONObj notMatch = BSON("a" << 4); BSONObj notMatchWrongType = BSON("a" << "foo"); - GTEMatchExpression gte; + CollatorInterface* collator = nullptr; + GTEMatchExpression gte(collator); ASSERT(gte.init("", operand["$gte"]).isOK()); ASSERT(gte.matchesSingleElement(match.firstElement())); ASSERT(gte.matchesSingleElement(equalMatch.firstElement())); @@ -760,31 +845,35 @@ TEST(ComparisonMatchExpression, MatchesElement) { ASSERT(!gte.matchesSingleElement(notMatchWrongType.firstElement())); } -TEST(ComparisonMatchExpression, InvalidEooOperand) { +TEST(GteOp, InvalidEooOperand) { BSONObj operand; - GTEMatchExpression gte; + CollatorInterface* collator = nullptr; + GTEMatchExpression gte(collator); ASSERT(!gte.init("", operand.firstElement()).isOK()); } -TEST(ComparisonMatchExpression, MatchesScalar) { +TEST(GteOp, MatchesScalar) { BSONObj operand = BSON("$gte" << 5); - GTEMatchExpression gte; + CollatorInterface* collator = nullptr; + GTEMatchExpression gte(collator); ASSERT(gte.init("a", operand["$gte"]).isOK()); ASSERT(gte.matchesBSON(BSON("a" << 5.5), NULL)); ASSERT(!gte.matchesBSON(BSON("a" << 4), NULL)); } -TEST(ComparisonMatchExpression, MatchesArrayValue) { +TEST(GteOp, MatchesArrayValue) { BSONObj operand = BSON("$gte" << 5); - GTEMatchExpression gte; + CollatorInterface* collator = nullptr; + GTEMatchExpression gte(collator); ASSERT(gte.init("a", operand["$gte"]).isOK()); ASSERT(gte.matchesBSON(BSON("a" << BSON_ARRAY(4 << 5.5)), NULL)); ASSERT(!gte.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2)), NULL)); } -TEST(ComparisonMatchExpression, MatchesWholeArray) { +TEST(GteOp, MatchesWholeArray) { BSONObj operand = BSON("$gte" << BSON_ARRAY(5)); - GTEMatchExpression gte; + CollatorInterface* collator = nullptr; + GTEMatchExpression gte(collator); ASSERT(gte.init("a", operand["$gte"]).isOK()); ASSERT(!gte.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL)); ASSERT(gte.matchesBSON(BSON("a" << BSON_ARRAY(5)), NULL)); @@ -796,9 +885,10 @@ TEST(ComparisonMatchExpression, MatchesWholeArray) { ASSERT(gte.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(6))), NULL)); } -TEST(ComparisonMatchExpression, MatchesNull) { +TEST(GteOp, MatchesNull) { BSONObj operand = BSON("$gte" << BSONNULL); - GTEMatchExpression gte; + CollatorInterface* collator = nullptr; + GTEMatchExpression gte(collator); ASSERT(gte.init("a", operand["$gte"]).isOK()); ASSERT(gte.matchesBSON(BSONObj(), NULL)); ASSERT(gte.matchesBSON(BSON("a" << BSONNULL), NULL)); @@ -807,9 +897,10 @@ TEST(ComparisonMatchExpression, MatchesNull) { ASSERT(gte.matchesBSON(BSON("b" << 4), NULL)); } -TEST(ComparisonMatchExpression, MatchesDotNotationNull) { +TEST(GteOp, MatchesDotNotationNull) { BSONObj operand = BSON("$gte" << BSONNULL); - GTEMatchExpression gte; + CollatorInterface* collator = nullptr; + GTEMatchExpression gte(collator); ASSERT(gte.init("a.b", operand["$gte"]).isOK()); ASSERT(gte.matchesBSON(BSONObj(), NULL)); ASSERT(gte.matchesBSON(BSON("a" << BSONNULL), NULL)); @@ -821,27 +912,30 @@ TEST(ComparisonMatchExpression, MatchesDotNotationNull) { ASSERT(!gte.matchesBSON(BSON("a" << BSON_ARRAY(BSON("b" << 4))), NULL)); } -TEST(ComparisonMatchExpression, MatchesMinKey) { +TEST(GteOp, MatchesMinKey) { BSONObj operand = BSON("a" << MinKey); - GTEMatchExpression gte; + CollatorInterface* collator = nullptr; + GTEMatchExpression gte(collator); ASSERT(gte.init("a", operand["a"]).isOK()); ASSERT(gte.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(gte.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(gte.matchesBSON(BSON("a" << 4), NULL)); } -TEST(ComparisonMatchExpression, MatchesMaxKey) { +TEST(GteOp, MatchesMaxKey) { BSONObj operand = BSON("a" << MaxKey); - GTEMatchExpression gte; + CollatorInterface* collator = nullptr; + GTEMatchExpression gte(collator); ASSERT(gte.init("a", operand["a"]).isOK()); ASSERT(gte.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(!gte.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(!gte.matchesBSON(BSON("a" << 4), NULL)); } -TEST(ComparisonMatchExpression, ElemMatchKey) { +TEST(GteOp, ElemMatchKey) { BSONObj operand = BSON("$gte" << 5); - GTEMatchExpression gte; + CollatorInterface* collator = nullptr; + GTEMatchExpression gte(collator); ASSERT(gte.init("a", operand["$gte"]).isOK()); MatchDetails details; details.requestElemMatchKey(); @@ -1501,14 +1595,16 @@ TEST(InMatchExpression, MatchesElementSingle) { BSONArray operand = BSON_ARRAY(1); BSONObj match = BSON("a" << 1); BSONObj notMatch = BSON("a" << 2); - InMatchExpression in; + CollatorInterface* collator = nullptr; + InMatchExpression in(collator); in.getArrayFilterEntries()->addEquality(operand.firstElement()); ASSERT(in.matchesSingleElement(match["a"])); ASSERT(!in.matchesSingleElement(notMatch["a"])); } TEST(InMatchExpression, MatchesEmpty) { - InMatchExpression in; + CollatorInterface* collator = nullptr; + InMatchExpression in(collator); in.init("a"); BSONObj notMatch = BSON("a" << 2); @@ -1519,7 +1615,8 @@ TEST(InMatchExpression, MatchesEmpty) { TEST(InMatchExpression, MatchesElementMultiple) { BSONObj operand = BSON_ARRAY(1 << "r" << true << 1); - InMatchExpression in; + CollatorInterface* collator = nullptr; + InMatchExpression in(collator); in.getArrayFilterEntries()->addEquality(operand[0]); in.getArrayFilterEntries()->addEquality(operand[1]); in.getArrayFilterEntries()->addEquality(operand[2]); @@ -1539,7 +1636,8 @@ TEST(InMatchExpression, MatchesElementMultiple) { TEST(InMatchExpression, MatchesScalar) { BSONObj operand = BSON_ARRAY(5); - InMatchExpression in; + CollatorInterface* collator = nullptr; + InMatchExpression in(collator); in.init("a"); in.getArrayFilterEntries()->addEquality(operand.firstElement()); @@ -1549,7 +1647,8 @@ TEST(InMatchExpression, MatchesScalar) { TEST(InMatchExpression, MatchesArrayValue) { BSONObj operand = BSON_ARRAY(5); - InMatchExpression in; + CollatorInterface* collator = nullptr; + InMatchExpression in(collator); in.init("a"); in.getArrayFilterEntries()->addEquality(operand.firstElement()); @@ -1561,7 +1660,8 @@ TEST(InMatchExpression, MatchesArrayValue) { TEST(InMatchExpression, MatchesNull) { BSONObj operand = BSON_ARRAY(BSONNULL); - InMatchExpression in; + CollatorInterface* collator = nullptr; + InMatchExpression in(collator); in.init("a"); in.getArrayFilterEntries()->addEquality(operand.firstElement()); @@ -1575,7 +1675,8 @@ TEST(InMatchExpression, MatchesNull) { TEST(InMatchExpression, MatchesUndefined) { BSONObj operand = BSON_ARRAY(BSONUndefined); - InMatchExpression in; + CollatorInterface* collator = nullptr; + InMatchExpression in(collator); in.init("a"); Status s = in.getArrayFilterEntries()->addEquality(operand.firstElement()); ASSERT_NOT_OK(s); @@ -1583,7 +1684,8 @@ TEST(InMatchExpression, MatchesUndefined) { TEST(InMatchExpression, MatchesMinKey) { BSONObj operand = BSON_ARRAY(MinKey); - InMatchExpression in; + CollatorInterface* collator = nullptr; + InMatchExpression in(collator); in.init("a"); in.getArrayFilterEntries()->addEquality(operand.firstElement()); @@ -1594,7 +1696,8 @@ TEST(InMatchExpression, MatchesMinKey) { TEST(InMatchExpression, MatchesMaxKey) { BSONObj operand = BSON_ARRAY(MaxKey); - InMatchExpression in; + CollatorInterface* collator = nullptr; + InMatchExpression in(collator); in.init("a"); in.getArrayFilterEntries()->addEquality(operand.firstElement()); @@ -1605,7 +1708,8 @@ TEST(InMatchExpression, MatchesMaxKey) { TEST(InMatchExpression, MatchesFullArray) { BSONObj operand = BSON_ARRAY(BSON_ARRAY(1 << 2) << 4 << 5); - InMatchExpression in; + CollatorInterface* collator = nullptr; + InMatchExpression in(collator); in.init("a"); in.getArrayFilterEntries()->addEquality(operand[0]); in.getArrayFilterEntries()->addEquality(operand[1]); @@ -1619,7 +1723,8 @@ TEST(InMatchExpression, MatchesFullArray) { TEST(InMatchExpression, ElemMatchKey) { BSONObj operand = BSON_ARRAY(5 << 2); - InMatchExpression in; + CollatorInterface* collator = nullptr; + InMatchExpression in(collator); in.init("a"); in.getArrayFilterEntries()->addEquality(operand[0]); in.getArrayFilterEntries()->addEquality(operand[1]); @@ -1635,6 +1740,42 @@ TEST(InMatchExpression, ElemMatchKey) { ASSERT_EQUALS("1", details.elemMatchKey()); } +TEST(InMatchExpression, InMatchExpressionsWithUnequalCollatorsAreUnequal) { + CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kReverseString); + InMatchExpression eq1(&collator1); + CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kAlwaysEqual); + InMatchExpression eq2(&collator2); + ASSERT(!eq1.equivalent(&eq2)); +} + +TEST(InMatchExpression, InMatchExpressionsWithEqualCollatorsAreEqual) { + CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kAlwaysEqual); + InMatchExpression eq1(&collator1); + CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kAlwaysEqual); + InMatchExpression eq2(&collator2); + ASSERT(eq1.equivalent(&eq2)); +} + +TEST(InMatchExpression, StringMatchingWithNullCollatorUsesBinaryComparison) { + BSONArray operand = BSON_ARRAY("string"); + BSONObj notMatch = BSON("a" + << "string2"); + CollatorInterface* collator = nullptr; + InMatchExpression in(collator); + in.getArrayFilterEntries()->addEquality(operand.firstElement()); + ASSERT(!in.matchesSingleElement(notMatch["a"])); +} + +TEST(InMatchExpression, StringMatchingRespectsCollation) { + BSONArray operand = BSON_ARRAY("string"); + BSONObj match = BSON("a" + << "string2"); + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); + InMatchExpression in(&collator); + in.getArrayFilterEntries()->addEquality(operand.firstElement()); + ASSERT(in.matchesSingleElement(match["a"])); +} + /** TEST( InMatchExpression, MatchesIndexKeyScalar ) { BSONObj operand = BSON( "$in" << BSON_ARRAY( 6 << 5 ) ); diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp index fd6b8802524..f86ba0f55e4 100644 --- a/src/mongo/db/matcher/expression_parser.cpp +++ b/src/mongo/db/matcher/expression_parser.cpp @@ -95,7 +95,8 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c // TODO: these should move to getGtLtOp, or its replacement if (mongoutils::str::equals("$eq", e.fieldName())) - return _parseComparison(name, new EqualityMatchExpression(), e); + // TODO SERVER-23608: Pass our CollatorInterface* to EqualityMatchExpression(). + return _parseComparison(name, new EqualityMatchExpression(nullptr), e); if (mongoutils::str::equals("$not", e.fieldName())) { return _parseNot(name, e, level); @@ -112,20 +113,26 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c return {Status(ErrorCodes::BadValue, mongoutils::str::stream() << "unknown operator: " << e.fieldName())}; case BSONObj::LT: - return _parseComparison(name, new LTMatchExpression(), e); + // TODO SERVER-23608: Pass our CollatorInterface* to LTMatchExpression(). + return _parseComparison(name, new LTMatchExpression(nullptr), e); case BSONObj::LTE: - return _parseComparison(name, new LTEMatchExpression(), e); + // TODO SERVER-23608: Pass our CollatorInterface* to LTEMatchExpression(). + return _parseComparison(name, new LTEMatchExpression(nullptr), e); case BSONObj::GT: - return _parseComparison(name, new GTMatchExpression(), e); + // TODO SERVER-23608: Pass our CollatorInterface* to GTMatchExpression(). + return _parseComparison(name, new GTMatchExpression(nullptr), e); case BSONObj::GTE: - return _parseComparison(name, new GTEMatchExpression(), e); + // TODO SERVER-23608: Pass our CollatorInterface* to GTEMatchExpression(). + return _parseComparison(name, new GTEMatchExpression(nullptr), e); case BSONObj::NE: { if (RegEx == e.type()) { // Just because $ne can be rewritten as the negation of an // equality does not mean that $ne of a regex is allowed. See SERVER-1705. return {Status(ErrorCodes::BadValue, "Can't have regex as arg to $ne.")}; } - StatusWithMatchExpression s = _parseComparison(name, new EqualityMatchExpression(), e); + // TODO SERVER-23608: Pass our CollatorInterface* to EqualityMatchExpression(). + StatusWithMatchExpression s = + _parseComparison(name, new EqualityMatchExpression(nullptr), e); if (!s.isOK()) return s; std::unique_ptr<NotMatchExpression> n = stdx::make_unique<NotMatchExpression>(); @@ -135,12 +142,14 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c return {std::move(n)}; } case BSONObj::Equality: - return _parseComparison(name, new EqualityMatchExpression(), e); + // TODO SERVER-23608: Pass our CollatorInterface* to EqualityMatchExpression(). + return _parseComparison(name, new EqualityMatchExpression(nullptr), e); case BSONObj::opIN: { if (e.type() != Array) return {Status(ErrorCodes::BadValue, "$in needs an array")}; - std::unique_ptr<InMatchExpression> temp = stdx::make_unique<InMatchExpression>(); + // TODO SERVER-23608: Pass our CollatorInterface* to InMatchExpression(). + std::unique_ptr<InMatchExpression> temp = stdx::make_unique<InMatchExpression>(nullptr); Status s = temp->init(name); if (!s.isOK()) return s; @@ -153,7 +162,8 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c case BSONObj::NIN: { if (e.type() != Array) return {Status(ErrorCodes::BadValue, "$nin needs an array")}; - std::unique_ptr<InMatchExpression> temp = stdx::make_unique<InMatchExpression>(); + // TODO SERVER-23608: Pass our CollatorInterface* to InMatchExpression(). + std::unique_ptr<InMatchExpression> temp = stdx::make_unique<InMatchExpression>(nullptr); Status s = temp->init(name); if (!s.isOK()) return s; @@ -344,8 +354,10 @@ StatusWithMatchExpression MatchExpressionParser::_parse(const BSONObj& obj, int } else if (mongoutils::str::equals("ref", rest) || mongoutils::str::equals("id", rest) || mongoutils::str::equals("db", rest)) { // DBRef fields. + // TODO SERVER-23608: Pass our CollatorInterface* to EqualityMatchExpression() in + // the "id" case. std::unique_ptr<ComparisonMatchExpression> eq = - stdx::make_unique<EqualityMatchExpression>(); + stdx::make_unique<EqualityMatchExpression>(nullptr); Status s = eq->init(e.fieldName(), e); if (!s.isOK()) return s; @@ -375,8 +387,9 @@ StatusWithMatchExpression MatchExpressionParser::_parse(const BSONObj& obj, int continue; } + // TODO SERVER-23608: Pass our CollatorInterface* to EqualityMatchExpression(). std::unique_ptr<ComparisonMatchExpression> eq = - stdx::make_unique<EqualityMatchExpression>(); + stdx::make_unique<EqualityMatchExpression>(nullptr); Status s = eq->init(e.fieldName(), e); if (!s.isOK()) return s; @@ -794,8 +807,9 @@ StatusWithMatchExpression MatchExpressionParser::_parseAll(const char* name, } else if (e.type() == Object && e.Obj().firstElement().getGtLtOp(-1) != -1) { return {Status(ErrorCodes::BadValue, "no $ expressions in $all")}; } else { + // TODO SERVER-23608: Pass our CollatorInterface* to EqualityMatchExpression(). std::unique_ptr<EqualityMatchExpression> x = - stdx::make_unique<EqualityMatchExpression>(); + stdx::make_unique<EqualityMatchExpression>(nullptr); Status s = x->init(name, e); if (!s.isOK()) return s; diff --git a/src/mongo/db/matcher/expression_test.cpp b/src/mongo/db/matcher/expression_test.cpp index 90d50cf5f47..57e8f1c8f7f 100644 --- a/src/mongo/db/matcher/expression_test.cpp +++ b/src/mongo/db/matcher/expression_test.cpp @@ -47,7 +47,8 @@ TEST(MatchExpressionTest, Parse1) { TEST(LeafMatchExpressionTest, Equal1) { BSONObj temp = BSON("x" << 5); - EqualityMatchExpression e; + CollatorInterface* collator = nullptr; + EqualityMatchExpression e(collator); e.init("x", temp["x"]); ASSERT_TRUE(e.matchesBSON(fromjson("{ x : 5 }"))); @@ -66,7 +67,8 @@ TEST(LeafMatchExpressionTest, Comp1) { BSONObj temp = BSON("x" << 5); { - LTEMatchExpression e; + CollatorInterface* collator = nullptr; + LTEMatchExpression e(collator); e.init("x", temp["x"]); ASSERT_TRUE(e.matchesBSON(fromjson("{ x : 5 }"))); ASSERT_TRUE(e.matchesBSON(fromjson("{ x : 4 }"))); @@ -75,7 +77,8 @@ TEST(LeafMatchExpressionTest, Comp1) { } { - LTMatchExpression e; + CollatorInterface* collator = nullptr; + LTMatchExpression e(collator); e.init("x", temp["x"]); ASSERT_FALSE(e.matchesBSON(fromjson("{ x : 5 }"))); ASSERT_TRUE(e.matchesBSON(fromjson("{ x : 4 }"))); @@ -84,7 +87,8 @@ TEST(LeafMatchExpressionTest, Comp1) { } { - GTEMatchExpression e; + CollatorInterface* collator = nullptr; + GTEMatchExpression e(collator); e.init("x", temp["x"]); ASSERT_TRUE(e.matchesBSON(fromjson("{ x : 5 }"))); ASSERT_FALSE(e.matchesBSON(fromjson("{ x : 4 }"))); @@ -93,7 +97,8 @@ TEST(LeafMatchExpressionTest, Comp1) { } { - GTMatchExpression e; + CollatorInterface* collator = nullptr; + GTMatchExpression e(collator); e.init("x", temp["x"]); ASSERT_FALSE(e.matchesBSON(fromjson("{ x : 5 }"))); ASSERT_FALSE(e.matchesBSON(fromjson("{ x : 4 }"))); diff --git a/src/mongo/db/matcher/expression_tree_test.cpp b/src/mongo/db/matcher/expression_tree_test.cpp index 17ec2f9e2ac..e8aa987ab32 100644 --- a/src/mongo/db/matcher/expression_tree_test.cpp +++ b/src/mongo/db/matcher/expression_tree_test.cpp @@ -42,7 +42,8 @@ using std::unique_ptr; TEST(NotMatchExpression, MatchesScalar) { BSONObj baseOperand = BSON("$lt" << 5); - unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression(collator)); ASSERT(lt->init("a", baseOperand["$lt"]).isOK()); NotMatchExpression notOp; ASSERT(notOp.init(lt.release()).isOK()); @@ -52,7 +53,8 @@ TEST(NotMatchExpression, MatchesScalar) { TEST(NotMatchExpression, MatchesArray) { BSONObj baseOperand = BSON("$lt" << 5); - unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression(collator)); ASSERT(lt->init("a", baseOperand["$lt"]).isOK()); NotMatchExpression notOp; ASSERT(notOp.init(lt.release()).isOK()); @@ -64,7 +66,8 @@ TEST(NotMatchExpression, MatchesArray) { TEST(NotMatchExpression, ElemMatchKey) { BSONObj baseOperand = BSON("$lt" << 5); - unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression(collator)); ASSERT(lt->init("a", baseOperand["$lt"]).isOK()); NotMatchExpression notOp; ASSERT(notOp.init(lt.release()).isOK()); @@ -127,9 +130,10 @@ TEST(AndOp, MatchesElementThreeClauses) { BSONObj notMatch3 = BSON("a" << "r"); - unique_ptr<ComparisonMatchExpression> sub1(new LTMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> sub1(new LTMatchExpression(collator)); ASSERT(sub1->init("a", baseOperand1["$lt"]).isOK()); - unique_ptr<ComparisonMatchExpression> sub2(new GTMatchExpression()); + unique_ptr<ComparisonMatchExpression> sub2(new GTMatchExpression(collator)); ASSERT(sub2->init("a", baseOperand2["$gt"]).isOK()); unique_ptr<RegexMatchExpression> sub3(new RegexMatchExpression()); ASSERT(sub3->init("a", "1", "").isOK()); @@ -147,7 +151,8 @@ TEST(AndOp, MatchesElementThreeClauses) { TEST(AndOp, MatchesSingleClause) { BSONObj baseOperand = BSON("$ne" << 5); - unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator)); ASSERT(eq->init("a", baseOperand["$ne"]).isOK()); unique_ptr<NotMatchExpression> ne(new NotMatchExpression()); ASSERT(ne->init(eq.release()).isOK()); @@ -166,13 +171,14 @@ TEST(AndOp, MatchesThreeClauses) { BSONObj baseOperand2 = BSON("$lt" << 10); BSONObj baseOperand3 = BSON("$lt" << 100); - unique_ptr<ComparisonMatchExpression> sub1(new GTMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> sub1(new GTMatchExpression(collator)); ASSERT(sub1->init("a", baseOperand1["$gt"]).isOK()); - unique_ptr<ComparisonMatchExpression> sub2(new LTMatchExpression()); + unique_ptr<ComparisonMatchExpression> sub2(new LTMatchExpression(collator)); ASSERT(sub2->init("a", baseOperand2["$lt"]).isOK()); - unique_ptr<ComparisonMatchExpression> sub3(new LTMatchExpression()); + unique_ptr<ComparisonMatchExpression> sub3(new LTMatchExpression(collator)); ASSERT(sub3->init("b", baseOperand3["$lt"]).isOK()); AndMatchExpression andOp; @@ -191,10 +197,11 @@ TEST(AndOp, ElemMatchKey) { BSONObj baseOperand1 = BSON("a" << 1); BSONObj baseOperand2 = BSON("b" << 2); - unique_ptr<ComparisonMatchExpression> sub1(new EqualityMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> sub1(new EqualityMatchExpression(collator)); ASSERT(sub1->init("a", baseOperand1["a"]).isOK()); - unique_ptr<ComparisonMatchExpression> sub2(new EqualityMatchExpression()); + unique_ptr<ComparisonMatchExpression> sub2(new EqualityMatchExpression(collator)); ASSERT(sub2->init("b", baseOperand2["b"]).isOK()); AndMatchExpression andOp; @@ -311,7 +318,8 @@ TEST( OrOp, MatchesElementThreeClauses ) { */ TEST(OrOp, MatchesSingleClause) { BSONObj baseOperand = BSON("$ne" << 5); - unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator)); ASSERT(eq->init("a", baseOperand["$ne"]).isOK()); unique_ptr<NotMatchExpression> ne(new NotMatchExpression()); ASSERT(ne->init(eq.release()).isOK()); @@ -329,11 +337,12 @@ TEST(OrOp, MatchesThreeClauses) { BSONObj baseOperand1 = BSON("$gt" << 10); BSONObj baseOperand2 = BSON("$lt" << 0); BSONObj baseOperand3 = BSON("b" << 100); - unique_ptr<ComparisonMatchExpression> sub1(new GTMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> sub1(new GTMatchExpression(collator)); ASSERT(sub1->init("a", baseOperand1["$gt"]).isOK()); - unique_ptr<ComparisonMatchExpression> sub2(new LTMatchExpression()); + unique_ptr<ComparisonMatchExpression> sub2(new LTMatchExpression(collator)); ASSERT(sub2->init("a", baseOperand2["$lt"]).isOK()); - unique_ptr<ComparisonMatchExpression> sub3(new EqualityMatchExpression()); + unique_ptr<ComparisonMatchExpression> sub3(new EqualityMatchExpression(collator)); ASSERT(sub3->init("b", baseOperand3["b"]).isOK()); OrMatchExpression orOp; @@ -353,9 +362,10 @@ TEST(OrOp, MatchesThreeClauses) { TEST(OrOp, ElemMatchKey) { BSONObj baseOperand1 = BSON("a" << 1); BSONObj baseOperand2 = BSON("b" << 2); - unique_ptr<ComparisonMatchExpression> sub1(new EqualityMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> sub1(new EqualityMatchExpression(collator)); ASSERT(sub1->init("a", baseOperand1["a"]).isOK()); - unique_ptr<ComparisonMatchExpression> sub2(new EqualityMatchExpression()); + unique_ptr<ComparisonMatchExpression> sub2(new EqualityMatchExpression(collator)); ASSERT(sub2->init("b", baseOperand2["b"]).isOK()); OrMatchExpression orOp; @@ -472,7 +482,8 @@ TEST( NorOp, MatchesElementThreeClauses ) { TEST(NorOp, MatchesSingleClause) { BSONObj baseOperand = BSON("$ne" << 5); - unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator)); ASSERT(eq->init("a", baseOperand["$ne"]).isOK()); unique_ptr<NotMatchExpression> ne(new NotMatchExpression()); ASSERT(ne->init(eq.release()).isOK()); @@ -491,11 +502,12 @@ TEST(NorOp, MatchesThreeClauses) { BSONObj baseOperand2 = BSON("$lt" << 0); BSONObj baseOperand3 = BSON("b" << 100); - unique_ptr<ComparisonMatchExpression> sub1(new GTMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> sub1(new GTMatchExpression(collator)); ASSERT(sub1->init("a", baseOperand1["$gt"]).isOK()); - unique_ptr<ComparisonMatchExpression> sub2(new LTMatchExpression()); + unique_ptr<ComparisonMatchExpression> sub2(new LTMatchExpression(collator)); ASSERT(sub2->init("a", baseOperand2["$lt"]).isOK()); - unique_ptr<ComparisonMatchExpression> sub3(new EqualityMatchExpression()); + unique_ptr<ComparisonMatchExpression> sub3(new EqualityMatchExpression(collator)); ASSERT(sub3->init("b", baseOperand3["b"]).isOK()); NorMatchExpression norOp; @@ -515,9 +527,10 @@ TEST(NorOp, MatchesThreeClauses) { TEST(NorOp, ElemMatchKey) { BSONObj baseOperand1 = BSON("a" << 1); BSONObj baseOperand2 = BSON("b" << 2); - unique_ptr<ComparisonMatchExpression> sub1(new EqualityMatchExpression()); + CollatorInterface* collator = nullptr; + unique_ptr<ComparisonMatchExpression> sub1(new EqualityMatchExpression(collator)); ASSERT(sub1->init("a", baseOperand1["a"]).isOK()); - unique_ptr<ComparisonMatchExpression> sub2(new EqualityMatchExpression()); + unique_ptr<ComparisonMatchExpression> sub2(new EqualityMatchExpression(collator)); ASSERT(sub2->init("b", baseOperand2["b"]).isOK()); NorMatchExpression norOp; @@ -539,9 +552,10 @@ TEST(NorOp, ElemMatchKey) { TEST(NorOp, Equivalent) { BSONObj baseOperand1 = BSON("a" << 1); BSONObj baseOperand2 = BSON("b" << 2); - EqualityMatchExpression sub1; + CollatorInterface* collator = nullptr; + EqualityMatchExpression sub1(collator); ASSERT(sub1.init("a", baseOperand1["a"]).isOK()); - EqualityMatchExpression sub2; + EqualityMatchExpression sub2(collator); ASSERT(sub2.init("b", baseOperand2["b"]).isOK()); NorMatchExpression e1; diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript index 7c6834a63e4..9c536d41f6b 100644 --- a/src/mongo/db/query/SConscript +++ b/src/mongo/db/query/SConscript @@ -208,6 +208,7 @@ env.CppUnitTest( "canonical_query_test.cpp" ], LIBDEPS=[ + "collation/collator_interface_mock", "query_planner", ], ) diff --git a/src/mongo/db/query/canonical_query.cpp b/src/mongo/db/query/canonical_query.cpp index 95aa24b31ec..5e3f5a2b1a1 100644 --- a/src/mongo/db/query/canonical_query.cpp +++ b/src/mongo/db/query/canonical_query.cpp @@ -457,7 +457,7 @@ MatchExpression* CanonicalQuery::normalizeTree(MatchExpression* root) { return normalizeTree(re.release()); } - auto eq = stdx::make_unique<EqualityMatchExpression>(); + auto eq = stdx::make_unique<EqualityMatchExpression>(in->getCollator()); eq->init(in->path(), *(inArrayEntries->equalities().begin())); if (in->getTag()) { eq->setTag(in->getTag()->clone()); diff --git a/src/mongo/db/query/canonical_query_test.cpp b/src/mongo/db/query/canonical_query_test.cpp index f597fa6e0ce..50574135059 100644 --- a/src/mongo/db/query/canonical_query_test.cpp +++ b/src/mongo/db/query/canonical_query_test.cpp @@ -33,6 +33,7 @@ #include "mongo/db/matcher/extensions_callback_disallow_extensions.h" #include "mongo/db/matcher/extensions_callback_noop.h" #include "mongo/db/namespace_string.h" +#include "mongo/db/query/collation/collator_interface_mock.h" #include "mongo/db/query/index_tag.h" #include "mongo/unittest/unittest.h" @@ -636,6 +637,19 @@ TEST(CanonicalQueryTest, NormalizeWithInAndRegexPreservesTags) { ASSERT_EQ(2U, tag->index); } +TEST(CanonicalQueryTest, NormalizeWithInPreservesCollator) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); + auto inMatchExpression = stdx::make_unique<InMatchExpression>(&collator); + BSONObj obj = fromjson("{'': 'string'}"); + inMatchExpression->getArrayFilterEntries()->addEquality(obj.firstElement()); + unique_ptr<MatchExpression> matchExpression( + CanonicalQuery::normalizeTree(inMatchExpression.release())); + ASSERT(matchExpression->matchType() == MatchExpression::MatchType::EQ); + EqualityMatchExpression* eqMatchExpression = + static_cast<EqualityMatchExpression*>(matchExpression.get()); + ASSERT_EQ(eqMatchExpression->getCollator(), &collator); +} + TEST(CanonicalQueryTest, CanonicalizeFromBaseQuery) { const bool isExplain = true; const std::string cmdStr = diff --git a/src/mongo/db/query/collation/collator_interface.h b/src/mongo/db/query/collation/collator_interface.h index 1dad55a1b67..962ccadb718 100644 --- a/src/mongo/db/query/collation/collator_interface.h +++ b/src/mongo/db/query/collation/collator_interface.h @@ -125,6 +125,19 @@ public: return _spec; } + /** + * Returns true if lhs and rhs are both nullptr, or if they point to equivalent collators. + */ + static bool collatorsMatch(const CollatorInterface* lhs, const CollatorInterface* rhs) { + if (lhs == nullptr && rhs == nullptr) { + return true; + } + if (lhs == nullptr || rhs == nullptr) { + return false; + } + return (*lhs == *rhs); + } + protected: static ComparisonKey makeComparisonKey(std::string key) { return ComparisonKey(std::move(key)); diff --git a/src/mongo/db/query/collation/collator_interface_mock_test.cpp b/src/mongo/db/query/collation/collator_interface_mock_test.cpp index 39e4a76d46c..843685806c6 100644 --- a/src/mongo/db/query/collation/collator_interface_mock_test.cpp +++ b/src/mongo/db/query/collation/collator_interface_mock_test.cpp @@ -54,6 +54,28 @@ TEST(CollatorInterfaceMockSelfTest, MocksOfDifferentTypesAreNotEqual) { ASSERT(reverseMock != alwaysEqualMock); } +TEST(CollatorInterfaceMockSelfTest, NullMockPointersMatch) { + ASSERT(CollatorInterface::collatorsMatch(nullptr, nullptr)); +} + +TEST(CollatorInterfaceMockSelfTest, NullMockPointerDoesNotMatchNonNullMockPointer) { + CollatorInterfaceMock reverseMock(CollatorInterfaceMock::MockType::kReverseString); + ASSERT(!CollatorInterface::collatorsMatch(nullptr, &reverseMock)); + ASSERT(!CollatorInterface::collatorsMatch(&reverseMock, nullptr)); +} + +TEST(CollatorInterfaceMockSelfTest, PointersToMocksOfSameTypeMatch) { + CollatorInterfaceMock reverseMock1(CollatorInterfaceMock::MockType::kReverseString); + CollatorInterfaceMock reverseMock2(CollatorInterfaceMock::MockType::kReverseString); + ASSERT(CollatorInterface::collatorsMatch(&reverseMock1, &reverseMock2)); +} + +TEST(CollatorInterfaceMockSelfTest, PointersToMocksOfDifferentTypesDoNotMatch) { + CollatorInterfaceMock reverseMock(CollatorInterfaceMock::MockType::kReverseString); + CollatorInterfaceMock alwaysEqualMock(CollatorInterfaceMock::MockType::kAlwaysEqual); + ASSERT(!CollatorInterface::collatorsMatch(&reverseMock, &alwaysEqualMock)); +} + TEST(CollatorInterfaceMockSelfTest, ReverseMockComparesInReverse) { CollatorInterfaceMock reverseMock(CollatorInterfaceMock::MockType::kReverseString); ASSERT_EQ(reverseMock.compare("abc", "abc"), 0); diff --git a/src/mongo/db/query/planner_ixselect.cpp b/src/mongo/db/query/planner_ixselect.cpp index 506b1b24836..3e0da4422bf 100644 --- a/src/mongo/db/query/planner_ixselect.cpp +++ b/src/mongo/db/query/planner_ixselect.cpp @@ -73,16 +73,6 @@ static bool twoDWontWrap(const Circle& circle, const IndexEntry& index) { return ret; } -static bool collatorsMatch(const CollatorInterface* lhs, const CollatorInterface* rhs) { - if (lhs == nullptr && rhs == nullptr) { - return true; - } - if (lhs == nullptr || rhs == nullptr) { - return false; - } - return (*lhs == *rhs); -} - // Checks whether 'node' contains any comparison to an element of type 'type'. Nested objects and // arrays are not checked recursively. We assume 'node' is bounds-generating or is a recursive child // of a bounds-generating node, i.e. it does not contain AND, OR, ELEM_MATCH_OBJECT, or NOR. @@ -188,7 +178,7 @@ bool QueryPlannerIXSelect::compatible(const BSONElement& elt, // String comparisons require the collators to match. if (boundsGeneratingNodeContainsComparisonToType(node, BSONType::String) && - !collatorsMatch(collator, index.collator)) { + !CollatorInterface::collatorsMatch(collator, index.collator)) { return false; } |