summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrii Dobroshynskyi <andrii.dobroshynskyi@mongodb.com>2020-07-28 16:42:31 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-08-05 20:58:09 +0000
commit441f18826c8469f871c1055032cf49be69e6d314 (patch)
tree144f4e1e55af80b3012a4e179655ed86dd3e4d2c
parent6da20f433cc8a6ae47664f89085ee8873817d651 (diff)
downloadmongo-441f18826c8469f871c1055032cf49be69e6d314.tar.gz
SERVER-49839 Implement AlwaysFalse and AlwaysTrue match expressions in SBE
-rw-r--r--jstests/core/always_true_false.js54
-rw-r--r--jstests/core/or_always_false.js16
-rw-r--r--src/mongo/db/query/sbe_stage_builder_filter.cpp30
3 files changed, 76 insertions, 24 deletions
diff --git a/jstests/core/always_true_false.js b/jstests/core/always_true_false.js
new file mode 100644
index 00000000000..0b32ac22a16
--- /dev/null
+++ b/jstests/core/always_true_false.js
@@ -0,0 +1,54 @@
+// Tests $alwaysTrue and $alwaysFalse behavior for match expressions.
+(function() {
+"use strict";
+
+const coll = db.always_true_false;
+coll.drop();
+
+assert.commandWorked(
+ coll.insert([{a: []}, {a: false}, {a: null}, {}, {a: false, b: 2}, {a: false, b: 1}]));
+
+// Check alwaysFalse.
+assert.eq(0, coll.find({$alwaysFalse: 1}).itcount());
+assert.eq(0, coll.find({$alwaysFalse: 1, a: false}).itcount());
+assert.eq(0, coll.find({$alwaysFalse: 1, b: 1}).itcount());
+
+// Check alwaysFalse with $and, $or.
+assert.eq(1, coll.find({$or: [{b: 1}, {$alwaysFalse: 1}]}).itcount());
+assert.eq(0, coll.find({$or: [{$alwaysFalse: 1}]}).itcount());
+assert.eq(0, coll.find({$or: [{$alwaysFalse: 1}, {$alwaysFalse: 1}]}).itcount());
+assert.eq(0, coll.find({$or: [{$alwaysFalse: 1}, {a: {$all: []}}, {$alwaysFalse: 1}]}).itcount());
+assert.eq(0, coll.find({$and: [{b: 1}, {$alwaysFalse: 1}]}).itcount());
+assert.eq(0, coll.find({$and: [{a: false}, {$alwaysFalse: 1}, {$alwaysFalse: 1}]}).itcount());
+
+// Check alwaysTrue.
+assert.eq(6, coll.find({$alwaysTrue: 1}).itcount());
+assert.eq(3, coll.find({$alwaysTrue: 1, a: false}).itcount());
+assert.eq(1, coll.find({$alwaysTrue: 1, b: 1}).itcount());
+
+// Check alwaysTrue with $and, $or.
+assert.eq(3, coll.find({$and: [{a: false}, {$alwaysTrue: 1}, {$alwaysTrue: 1}]}).itcount());
+assert.eq(0, coll.find({$and: [{a: false}, {$alwaysTrue: 1}, {$alwaysFalse: 1}]}).itcount());
+assert.eq(6, coll.find({$or: [{b: 1}, {$alwaysTrue: 1}]}).itcount());
+assert.eq(6, coll.find({$or: [{b: 1}, {$alwaysFalse: 1}, {$alwaysTrue: 1}]}).itcount());
+
+assert(coll.drop());
+
+// Check that a rooted-$or query with each clause false will not return any results.
+assert.commandWorked(coll.insert([{}, {}, {}]));
+const emptyOrError = assert.throws(() => coll.find({$or: []}).itcount());
+assert.eq(emptyOrError.code, ErrorCodes.BadValue);
+
+assert.eq(coll.find({$or: [{$alwaysFalse: 1}]}).itcount(), 0);
+assert.eq(coll.find({$or: [{a: {$all: []}}]}).itcount(), 0);
+assert.eq(coll.find({$or: [{$alwaysFalse: 1}, {$alwaysFalse: 1}]}).itcount(), 0);
+assert.eq(coll.find({$or: [{$alwaysFalse: 1}, {a: {$all: []}}, {$alwaysFalse: 1}]}).itcount(), 0);
+
+// Check failure cases.
+assert.commandFailedWithCode(db.runCommand({find: coll.getName(), filter: {$alwaysTrue: 0}}),
+ ErrorCodes.FailedToParse);
+assert.commandFailedWithCode(db.runCommand({find: coll.getName(), filter: {$alwaysFalse: 0}}),
+ ErrorCodes.FailedToParse);
+assert.commandFailedWithCode(db.runCommand({find: coll.getName(), filter: {a: {$alwaysFalse: 1}}}),
+ ErrorCodes.BadValue);
+}());
diff --git a/jstests/core/or_always_false.js b/jstests/core/or_always_false.js
deleted file mode 100644
index 0766806a223..00000000000
--- a/jstests/core/or_always_false.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// Tests that a rooted-$or query with each clause provably false will not return any results.
-(function() {
-"use strict";
-
-const coll = db.or_always_false;
-coll.drop();
-
-assert.commandWorked(coll.insert([{}, {}, {}]));
-const emptyOrError = assert.throws(() => coll.find({$or: []}).itcount());
-assert.eq(emptyOrError.code, ErrorCodes.BadValue);
-
-assert.eq(coll.find({$or: [{$alwaysFalse: 1}]}).itcount(), 0);
-assert.eq(coll.find({$or: [{a: {$all: []}}]}).itcount(), 0);
-assert.eq(coll.find({$or: [{$alwaysFalse: 1}, {$alwaysFalse: 1}]}).itcount(), 0);
-assert.eq(coll.find({$or: [{$alwaysFalse: 1}, {a: {$all: []}}, {$alwaysFalse: 1}]}).itcount(), 0);
-}());
diff --git a/src/mongo/db/query/sbe_stage_builder_filter.cpp b/src/mongo/db/query/sbe_stage_builder_filter.cpp
index 550dafaff31..3d9550279d0 100644
--- a/src/mongo/db/query/sbe_stage_builder_filter.cpp
+++ b/src/mongo/db/query/sbe_stage_builder_filter.cpp
@@ -508,6 +508,20 @@ void generateLogicalAnd(MatchExpressionVisitorContext* context, const AndMatchEx
}
/**
+ * Generates and pushes a constant boolean expression for either alwaysTrue or alwaysFalse.
+ */
+void generateAlwaysBoolean(MatchExpressionVisitorContext* context, bool value) {
+ context->predicateVars.push(context->slotIdGenerator->generate());
+ context->inputStage =
+ sbe::makeProjectStage(std::move(context->inputStage),
+ context->predicateVars.top(),
+ sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Boolean, value));
+
+ // Check if can bail out early from the $and predicate if this expression is part of branch.
+ checkForShortCircuitFromLogicalAnd(context);
+}
+
+/**
* A match expression pre-visitor used for maintaining nested logical expressions while traversing
* the match expression tree.
*/
@@ -515,12 +529,8 @@ class MatchExpressionPreVisitor final : public MatchExpressionConstVisitor {
public:
MatchExpressionPreVisitor(MatchExpressionVisitorContext* context) : _context(context) {}
- void visit(const AlwaysFalseMatchExpression* expr) final {
- unsupportedExpression(expr);
- }
- void visit(const AlwaysTrueMatchExpression* expr) final {
- unsupportedExpression(expr);
- }
+ void visit(const AlwaysFalseMatchExpression* expr) final {}
+ void visit(const AlwaysTrueMatchExpression* expr) final {}
void visit(const AndMatchExpression* expr) final {
_context->nestedLogicalExprs.push({expr, expr->numChildren()});
}
@@ -669,8 +679,12 @@ class MatchExpressionPostVisitor final : public MatchExpressionConstVisitor {
public:
MatchExpressionPostVisitor(MatchExpressionVisitorContext* context) : _context(context) {}
- void visit(const AlwaysFalseMatchExpression* expr) final {}
- void visit(const AlwaysTrueMatchExpression* expr) final {}
+ void visit(const AlwaysFalseMatchExpression* expr) final {
+ generateAlwaysBoolean(_context, false);
+ }
+ void visit(const AlwaysTrueMatchExpression* expr) final {
+ generateAlwaysBoolean(_context, true);
+ }
void visit(const AndMatchExpression* expr) final {
_context->nestedLogicalExprs.pop();
generateLogicalAnd(_context, expr);