summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/core/bittest.js64
-rw-r--r--src/mongo/db/exec/sbe/expressions/expression.cpp6
-rw-r--r--src/mongo/db/exec/sbe/expressions/expression.h10
-rw-r--r--src/mongo/db/exec/sbe/vm/arith.cpp3
-rw-r--r--src/mongo/db/exec/sbe/vm/vm.cpp137
-rw-r--r--src/mongo/db/exec/sbe/vm/vm.h12
-rw-r--r--src/mongo/db/matcher/expression_leaf.h4
-rw-r--r--src/mongo/db/query/sbe_stage_builder_filter.cpp111
8 files changed, 329 insertions, 18 deletions
diff --git a/jstests/core/bittest.js b/jstests/core/bittest.js
index b23b2aa6821..f746c609bc9 100644
--- a/jstests/core/bittest.js
+++ b/jstests/core/bittest.js
@@ -155,4 +155,68 @@ assertQueryCorrect({
1);
coll.drop();
+
+// Test that NaNs and non-integral ints are filtered out.
+assert.commandWorked(coll.insert({a: 0}));
+assert.commandWorked(coll.insert({a: 1}));
+assert.commandWorked(coll.insert({a: 54}));
+assert.commandWorked(coll.insert({a: 88}));
+assert.commandWorked(coll.insert({a: 255}));
+assert.commandWorked(coll.insert({a: NaN}));
+assert.commandWorked(coll.insert({a: 1.1}));
+assert.commandWorked(coll.insert({a: -1.1}));
+assert.commandWorked(coll.insert({a: -Infinity}));
+assert.commandWorked(coll.insert({a: +Infinity}));
+assert.commandWorked(coll.createIndex({a: 1}));
+
+assertQueryCorrect({a: {$bitsAllSet: 0}}, 5);
+assertQueryCorrect({a: {$bitsAllSet: 1}}, 2);
+assertQueryCorrect({a: {$bitsAllSet: 16}}, 3);
+assertQueryCorrect({a: {$bitsAllSet: 54}}, 2);
+assertQueryCorrect({a: {$bitsAllSet: 55}}, 1);
+assertQueryCorrect({a: {$bitsAllSet: 88}}, 2);
+assertQueryCorrect({a: {$bitsAllSet: 255}}, 1);
+assertQueryCorrect({a: {$bitsAllClear: 0}}, 5);
+assertQueryCorrect({a: {$bitsAllClear: 1}}, 3);
+assertQueryCorrect({a: {$bitsAllClear: 16}}, 2);
+assertQueryCorrect({a: {$bitsAllClear: 129}}, 3);
+assertQueryCorrect({a: {$bitsAllClear: 255}}, 1);
+assertQueryCorrect({a: {$bitsAnySet: 0}}, 0);
+assertQueryCorrect({a: {$bitsAnySet: 9}}, 3);
+assertQueryCorrect({a: {$bitsAnySet: 255}}, 4);
+assertQueryCorrect({a: {$bitsAnyClear: 0}}, 0);
+assertQueryCorrect({a: {$bitsAnyClear: 18}}, 3);
+assertQueryCorrect({a: {$bitsAnyClear: 24}}, 3);
+assertQueryCorrect({a: {$bitsAnyClear: 255}}, 4);
+assert(coll.drop());
+
+// Test variable length binary bitmasks.
+assert.commandWorked(coll.insert({a: BinData(0, "ZG9n")}));
+assert.commandWorked(coll.insert({a: BinData(0, "JA4A8gAxqTwciCuF5GGzAA==")}));
+assert.commandWorked(coll.insert({a: BinData(1, "JA4A8gAxqTwciCuF5GGzAA==")}));
+assert.commandWorked(
+ coll.insert({a: BinData(0, "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==")}));
+assert.commandWorked(
+ coll.insert({a: BinData(2, "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==")}));
+
+assertQueryCorrect({a: {$bitsAllSet: BinData(0, "ZG9n")}}, 1);
+assertQueryCorrect({a: {$bitsAllSet: BinData(0, "JA4A8gAxqTwciCuF5GGzAA==")}}, 2);
+assertQueryCorrect({a: {$bitsAllSet: BinData(2, "JA4A8gAxqTwciCuF5GGzAA==")}}, 2);
+assertQueryCorrect(
+ {a: {$bitsAllSet: BinData(0, "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==")}},
+ 2);
+assertQueryCorrect(
+ {a: {$bitsAllSet: BinData(2, "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==")}},
+ 2);
+assertQueryCorrect({a: {$bitsAllClear: BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAAA")}}, 5);
+assertQueryCorrect({a: {$bitsAllClear: BinData(0, "JA4A8gAxqTwciCuF5GGzAA==")}}, 0);
+assertQueryCorrect({a: {$bitsAnySet: BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAAA")}}, 0);
+assertQueryCorrect({a: {$bitsAnySet: BinData(0, "JA4A8gAxqTwciCuF5GGzAA==")}}, 5);
+assertQueryCorrect({a: {$bitsAnyClear: BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAAA")}}, 0);
+assertQueryCorrect({
+ a: {$bitsAnyClear: BinData(0, "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==")}
+},
+ 3);
+
+assert(coll.drop());
})();
diff --git a/src/mongo/db/exec/sbe/expressions/expression.cpp b/src/mongo/db/exec/sbe/expressions/expression.cpp
index 0d4c7b9a709..c950ac2228c 100644
--- a/src/mongo/db/exec/sbe/expressions/expression.cpp
+++ b/src/mongo/db/exec/sbe/expressions/expression.cpp
@@ -360,6 +360,10 @@ static stdx::unordered_map<std::string, BuiltinFn> kBuiltinFunctions = {
{"addToSet", BuiltinFn{[](size_t n) { return n == 1; }, vm::Builtin::addToSet, true}},
{"doubleDoubleSum",
BuiltinFn{[](size_t n) { return n > 0; }, vm::Builtin::doubleDoubleSum, true}},
+ {"bitTestZero", BuiltinFn{[](size_t n) { return n == 2; }, vm::Builtin::bitTestZero, false}},
+ {"bitTestMask", BuiltinFn{[](size_t n) { return n == 2; }, vm::Builtin::bitTestMask, false}},
+ {"bitTestPosition",
+ BuiltinFn{[](size_t n) { return n == 3; }, vm::Builtin::bitTestPosition, false}},
};
/**
@@ -395,6 +399,8 @@ static stdx::unordered_map<std::string, InstrFn> kInstrFunctions = {
InstrFn{[](size_t n) { return n == 1; }, &vm::CodeFragment::appendIsString, false}},
{"isNumber",
InstrFn{[](size_t n) { return n == 1; }, &vm::CodeFragment::appendIsNumber, false}},
+ {"isBinData",
+ InstrFn{[](size_t n) { return n == 1; }, &vm::CodeFragment::appendIsBinData, false}},
{"sum", InstrFn{[](size_t n) { return n == 1; }, &vm::CodeFragment::appendSum, true}},
{"min", InstrFn{[](size_t n) { return n == 1; }, &vm::CodeFragment::appendMin, true}},
{"max", InstrFn{[](size_t n) { return n == 1; }, &vm::CodeFragment::appendMax, true}},
diff --git a/src/mongo/db/exec/sbe/expressions/expression.h b/src/mongo/db/exec/sbe/expressions/expression.h
index 2192142e4ef..91c55ba9c06 100644
--- a/src/mongo/db/exec/sbe/expressions/expression.h
+++ b/src/mongo/db/exec/sbe/expressions/expression.h
@@ -562,5 +562,15 @@ private:
uint32_t _typeMask;
};
+/**
+ * Behavior variants for bit tests supported by match expressions $bitsAllClear, $bitsAllSet,
+ * $bitsAnyClear, $bitsAnySet.
+ */
+enum class BitTestBehavior : int32_t {
+ AllSet,
+ AnyClear,
+ AllClear,
+ AnySet,
+};
} // namespace sbe
} // namespace mongo
diff --git a/src/mongo/db/exec/sbe/vm/arith.cpp b/src/mongo/db/exec/sbe/vm/arith.cpp
index f1f52996a31..4ea0fc73094 100644
--- a/src/mongo/db/exec/sbe/vm/arith.cpp
+++ b/src/mongo/db/exec/sbe/vm/arith.cpp
@@ -377,7 +377,7 @@ std::pair<value::TypeTags, value::Value> ByteCode::genericNumConvertToPreciseInt
// fit in an int64_t.
if (lhsTag == value::TypeTags::NumberDouble) {
auto d = value::bitcastTo<double>(lhsValue);
- if (d > kDoubleLargestConsecutiveInteger || d < -kDoubleLargestConsecutiveInteger) {
+ if (d >= kDoubleLargestConsecutiveInteger || d <= -kDoubleLargestConsecutiveInteger) {
return {value::TypeTags::Nothing, 0};
}
}
@@ -491,7 +491,6 @@ std::pair<value::TypeTags, value::Value> ByteCode::compare3way(value::TypeTags l
return value::compareValue(lhsTag, lhsValue, rhsTag, rhsValue);
}
-
} // namespace vm
} // namespace sbe
} // namespace mongo
diff --git a/src/mongo/db/exec/sbe/vm/vm.cpp b/src/mongo/db/exec/sbe/vm/vm.cpp
index 63d48d9fdfe..1ff189b1c37 100644
--- a/src/mongo/db/exec/sbe/vm/vm.cpp
+++ b/src/mongo/db/exec/sbe/vm/vm.cpp
@@ -29,6 +29,7 @@
#include "mongo/platform/basic.h"
+#include "mongo/db/exec/sbe/expressions/expression.h"
#include "mongo/db/exec/sbe/vm/vm.h"
#include <pcrecpp.h>
@@ -94,6 +95,7 @@ int Instruction::stackOffset[Instruction::Tags::lastInstruction] = {
0, // isArray
0, // isString
0, // isNumber
+ 0, // isBinData
0, // typeMatch
0, // function is special, the stack offset is encoded in the instruction itself
@@ -308,6 +310,10 @@ void CodeFragment::appendIsNumber() {
appendSimpleInstruction(Instruction::isNumber);
}
+void CodeFragment::appendIsBinData() {
+ appendSimpleInstruction(Instruction::isBinData);
+}
+
void CodeFragment::appendTypeMatch(uint32_t typeMask) {
Instruction i;
i.tag = Instruction::typeMatch;
@@ -1077,6 +1083,119 @@ std::tuple<bool, value::TypeTags, value::Value> ByteCode::builtinDateWeekYear(ui
timezoneTuple);
}
+std::tuple<bool, value::TypeTags, value::Value> ByteCode::builtinBitTestPosition(uint8_t arity) {
+ invariant(arity == 3);
+
+ auto [ownedMask, maskTag, maskValue] = getFromStack(0);
+ auto [ownedInput, valueTag, value] = getFromStack(1);
+
+ // Carries a flag to indicate the desired testing behavior this was invoked under. The testing
+ // behavior is used to determine if we need to bail out of the bit position comparison early in
+ // the depending if a bit is found to be set or unset.
+ auto [_, tagBitTestBehavior, valueBitTestBehavior] = getFromStack(2);
+ invariant(tagBitTestBehavior == value::TypeTags::NumberInt32);
+
+ if (!value::isArray(maskTag) || !value::isBinData(valueTag)) {
+ return {false, value::TypeTags::Nothing, 0};
+ }
+
+ auto bitPositions = value::getArrayView(maskValue);
+ auto binDataSize = value::getBSONBinDataSize(valueTag, value);
+ auto binData = value::getBSONBinData(valueTag, value);
+ auto bitTestBehavior = BitTestBehavior{value::bitcastTo<int32_t>(valueBitTestBehavior)};
+
+ auto isBitSet = false;
+ for (size_t idx = 0; idx < bitPositions->size(); ++idx) {
+ auto [tagBitPosition, valueBitPosition] = bitPositions->getAt(idx);
+ auto bitPosition = value::bitcastTo<uint64_t>(valueBitPosition);
+ if (bitPosition >= binDataSize * 8) {
+ // If position to test is longer than the data to test against, zero-extend.
+ isBitSet = false;
+ } else {
+ // Convert the bit position to a byte position within a byte. Note that byte positions
+ // start at position 0 in the document's value BinData array representation, and bit
+ // positions start at the least significant bit.
+ auto byteIdx = bitPosition / 8;
+ auto currentBit = bitPosition % 8;
+ auto currentByte = binData[byteIdx];
+
+ isBitSet = currentByte & (1 << currentBit);
+ }
+
+ // Bail out early if we succeed with the any case or fail with the all case. To do this, we
+ // negate a test to determine if we need to continue looping over the bit position list. So
+ // the first part of the disjunction checks when a bit is set and the test is invoked by the
+ // AllSet or AnyClear expressions. The second test checks if a bit isn't set and we are
+ // checking the AllClear or the AnySet cases.
+ if (!((isBitSet &&
+ (bitTestBehavior == BitTestBehavior::AllSet ||
+ bitTestBehavior == BitTestBehavior::AnyClear)) ||
+ (!isBitSet &&
+ (bitTestBehavior == BitTestBehavior::AllClear ||
+ bitTestBehavior == BitTestBehavior::AnySet)))) {
+ return {false,
+ value::TypeTags::Boolean,
+ bitTestBehavior == BitTestBehavior::AnyClear ||
+ bitTestBehavior == BitTestBehavior::AnySet};
+ }
+ }
+ return {false,
+ value::TypeTags::Boolean,
+ bitTestBehavior == BitTestBehavior::AllSet ||
+ bitTestBehavior == BitTestBehavior::AllClear};
+}
+
+
+// Converts the value to a NumberInt64 tag/value and checks if the mask tab/value pair is a number.
+// If the inputs aren't numbers or the value can't be converted to a 64-bit integer, or if it's
+// outside of the range where the `lhsTag` type can represent consecutive integers precisely Nothing
+// is returned.
+std::tuple<bool, value::TypeTags, value::Value> ByteCode::convertBitTestValue(
+ value::TypeTags maskTag, value::Value maskVal, value::TypeTags valueTag, value::Value value) {
+ if (!value::isNumber(maskTag) || !value::isNumber(valueTag)) {
+ return {false, value::TypeTags::Nothing, 0};
+ }
+
+ // Bail out if the input can't be converted to a 64-bit integer, or if it's outside of the range
+ // where the `lhsTag` type can represent consecutive integers precisely.
+ auto [numTag, numVal] = genericNumConvertToPreciseInt64(valueTag, value);
+ if (numTag != value::TypeTags::NumberInt64) {
+ return {false, value::TypeTags::Nothing, 0};
+ }
+ return {false, numTag, numVal};
+}
+
+std::tuple<bool, value::TypeTags, value::Value> ByteCode::builtinBitTestZero(uint8_t arity) {
+ invariant(arity == 2);
+
+ auto [ownedMask, maskTag, maskValue] = getFromStack(0);
+ auto [ownedInput, valueTag, value] = getFromStack(1);
+
+ auto [ownedNum, numTag, numVal] = convertBitTestValue(maskTag, maskValue, valueTag, value);
+ if (numTag == value::TypeTags::Nothing) {
+ return {false, value::TypeTags::Nothing, 0};
+ }
+ auto result =
+ (value::numericCast<int64_t>(maskTag, maskValue) & value::bitcastTo<int64_t>(numVal)) == 0;
+ return {false, value::TypeTags::Boolean, result};
+}
+
+std::tuple<bool, value::TypeTags, value::Value> ByteCode::builtinBitTestMask(uint8_t arity) {
+ invariant(arity == 2);
+
+ auto [ownedMask, maskTag, maskValue] = getFromStack(0);
+ auto [ownedInput, valueTag, value] = getFromStack(1);
+
+ auto [ownedNum, numTag, numVal] = convertBitTestValue(maskTag, maskValue, valueTag, value);
+ if (numTag == value::TypeTags::Nothing) {
+ return {false, value::TypeTags::Nothing, 0};
+ }
+
+ auto numMask = value::numericCast<int64_t>(maskTag, maskValue);
+ auto result = (numMask & value::bitcastTo<int64_t>(numVal)) == numMask;
+ return {false, value::TypeTags::Boolean, result};
+}
+
std::tuple<bool, value::TypeTags, value::Value> ByteCode::dispatchBuiltin(Builtin f,
uint8_t arity) {
switch (f) {
@@ -1104,6 +1223,12 @@ std::tuple<bool, value::TypeTags, value::Value> ByteCode::dispatchBuiltin(Builti
return builtinAddToSet(arity);
case Builtin::doubleDoubleSum:
return builtinDoubleDoubleSum(arity);
+ case Builtin::bitTestZero:
+ return builtinBitTestZero(arity);
+ case Builtin::bitTestMask:
+ return builtinBitTestMask(arity);
+ case Builtin::bitTestPosition:
+ return builtinBitTestPosition(arity);
}
MONGO_UNREACHABLE;
@@ -1659,6 +1784,18 @@ std::tuple<uint8_t, value::TypeTags, value::Value> ByteCode::run(const CodeFragm
}
break;
}
+ case Instruction::isBinData: {
+ auto [owned, tag, val] = getFromStack(0);
+
+ if (tag != value::TypeTags::Nothing) {
+ topStack(false, value::TypeTags::Boolean, value::isBinData(tag));
+ }
+
+ if (owned) {
+ value::releaseValue(tag, val);
+ }
+ break;
+ }
case Instruction::typeMatch: {
auto typeMask = value::readFromMemory<uint32_t>(pcPointer);
pcPointer += sizeof(typeMask);
diff --git a/src/mongo/db/exec/sbe/vm/vm.h b/src/mongo/db/exec/sbe/vm/vm.h
index 13010ffe4d6..cf8c4411953 100644
--- a/src/mongo/db/exec/sbe/vm/vm.h
+++ b/src/mongo/db/exec/sbe/vm/vm.h
@@ -148,6 +148,7 @@ struct Instruction {
isArray,
isString,
isNumber,
+ isBinData,
typeMatch,
function,
@@ -181,6 +182,9 @@ enum class Builtin : uint8_t {
addToArray, // agg function to append to an array
addToSet, // agg function to append to a set
doubleDoubleSum, // special double summation
+ bitTestZero, // test bitwise mask & value is zero
+ bitTestMask, // test bitwise mask & value is mask
+ bitTestPosition, // test BinData with a bit position list
};
class CodeFragment {
@@ -253,6 +257,7 @@ public:
void appendIsArray();
void appendIsString();
void appendIsNumber();
+ void appendIsBinData();
void appendTypeMatch(uint32_t typeMask);
void appendFunction(Builtin f, uint8_t arity);
void appendJump(int jumpOffset);
@@ -397,6 +402,10 @@ private:
value::Value accValue,
value::TypeTags fieldTag,
value::Value fieldValue);
+ std::tuple<bool, value::TypeTags, value::Value> convertBitTestValue(value::TypeTags maskTag,
+ value::Value maskValue,
+ value::TypeTags valueTag,
+ value::Value value);
std::tuple<bool, value::TypeTags, value::Value> builtinSplit(uint8_t arity);
std::tuple<bool, value::TypeTags, value::Value> builtinDate(uint8_t arity);
@@ -410,6 +419,9 @@ private:
std::tuple<bool, value::TypeTags, value::Value> builtinAddToArray(uint8_t arity);
std::tuple<bool, value::TypeTags, value::Value> builtinAddToSet(uint8_t arity);
std::tuple<bool, value::TypeTags, value::Value> builtinDoubleDoubleSum(uint8_t arity);
+ std::tuple<bool, value::TypeTags, value::Value> builtinBitTestZero(uint8_t arity);
+ std::tuple<bool, value::TypeTags, value::Value> builtinBitTestMask(uint8_t arity);
+ std::tuple<bool, value::TypeTags, value::Value> builtinBitTestPosition(uint8_t arity);
std::tuple<bool, value::TypeTags, value::Value> dispatchBuiltin(Builtin f, uint8_t arity);
diff --git a/src/mongo/db/matcher/expression_leaf.h b/src/mongo/db/matcher/expression_leaf.h
index 736f6bbb6b2..2f4dabdf965 100644
--- a/src/mongo/db/matcher/expression_leaf.h
+++ b/src/mongo/db/matcher/expression_leaf.h
@@ -658,6 +658,10 @@ public:
return _bitPositions;
}
+ const uint64_t getBitMask() const {
+ return _bitMask;
+ }
+
private:
ExpressionOptimizerFunc getOptimizer() const final {
return [](std::unique_ptr<MatchExpression> expression) { return expression; };
diff --git a/src/mongo/db/query/sbe_stage_builder_filter.cpp b/src/mongo/db/query/sbe_stage_builder_filter.cpp
index fe5e36dca89..183b49bc04a 100644
--- a/src/mongo/db/query/sbe_stage_builder_filter.cpp
+++ b/src/mongo/db/query/sbe_stage_builder_filter.cpp
@@ -520,6 +520,81 @@ void generateAlwaysBoolean(MatchExpressionVisitorContext* context, bool value) {
}
/**
+ * Generates a SBE plan stage sub-tree which implements the bitwise match expression 'expr'. The
+ * various bit test expressions accept a numeric, BinData or position list bitmask. Here we handle
+ * building an EExpression for both the numeric and BinData or position list forms of the bitmask.
+ */
+void generateTraverseForBitTests(MatchExpressionVisitorContext* context,
+ const BitTestMatchExpression* expr,
+ const sbe::BitTestBehavior& bitTestBehavior) {
+ auto makeEExprFn = [expr, bitTestBehavior](sbe::value::SlotId inputSlot) {
+ auto bitPositions = expr->getBitPositions();
+
+ // Build an array set of bit positions for the bitmask, and remove duplicates in the
+ // bitPositions vector since duplicates aren't handled in the match expression parser by
+ // checking if an item has already been seen.
+ auto [bitPosTag, bitPosVal] = sbe::value::makeNewArray();
+ auto arr = sbe::value::getArrayView(bitPosVal);
+ arr->reserve(bitPositions.size());
+
+ std::set<int> seenBits;
+ for (size_t index = 0; index < bitPositions.size(); ++index) {
+ auto currentBit = bitPositions[index];
+ if (auto result = seenBits.insert(currentBit); result.second) {
+ arr->push_back(sbe::value::TypeTags::NumberInt64, currentBit);
+ }
+ }
+
+ // An EExpression for the BinData and position list for the binary case of
+ // BitTestMatchExpressions. This function will be applied to values carrying BinData
+ // elements.
+ auto binaryBitTestEExpr = sbe::makeE<sbe::EFunction>(
+ "bitTestPosition",
+ sbe::makeEs(sbe::makeE<sbe::EConstant>(bitPosTag, bitPosVal),
+ sbe::makeE<sbe::EVariable>(inputSlot),
+ sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::NumberInt32,
+ static_cast<int32_t>(bitTestBehavior))));
+
+ // Build An EExpression for the numeric bitmask case. The AllSet case tests if (mask &
+ // value) == mask, and AllClear case tests if (mask & value) == 0. The AnyClear and the
+ // AnySet case is the negation of the AllSet and AllClear cases, respectively.
+ auto numericBitTestEExpr =
+ sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::NumberInt64, expr->getBitMask());
+ if (bitTestBehavior == sbe::BitTestBehavior::AllSet ||
+ bitTestBehavior == sbe::BitTestBehavior::AnyClear) {
+ numericBitTestEExpr = sbe::makeE<sbe::EFunction>(
+ "bitTestMask",
+ sbe::makeEs(std::move(numericBitTestEExpr), sbe::makeE<sbe::EVariable>(inputSlot)));
+
+ // The AnyClear case is the negation of the AllSet case.
+ if (bitTestBehavior == sbe::BitTestBehavior::AnyClear) {
+ numericBitTestEExpr = sbe::makeE<sbe::EPrimUnary>(sbe::EPrimUnary::logicNot,
+ std::move(numericBitTestEExpr));
+ }
+ } else if (bitTestBehavior == sbe::BitTestBehavior::AllClear ||
+ bitTestBehavior == sbe::BitTestBehavior::AnySet) {
+ numericBitTestEExpr = sbe::makeE<sbe::EFunction>(
+ "bitTestZero",
+ sbe::makeEs(std::move(numericBitTestEExpr), sbe::makeE<sbe::EVariable>(inputSlot)));
+
+ // The AnySet case is the negation of the AllClear case.
+ if (bitTestBehavior == sbe::BitTestBehavior::AnySet) {
+ numericBitTestEExpr = sbe::makeE<sbe::EPrimUnary>(sbe::EPrimUnary::logicNot,
+ std::move(numericBitTestEExpr));
+ }
+ } else {
+ MONGO_UNREACHABLE;
+ }
+ return sbe::makeE<sbe::EIf>(
+ sbe::makeE<sbe::EFunction>("isBinData",
+ sbe::makeEs(sbe::makeE<sbe::EVariable>(inputSlot))),
+ std::move(binaryBitTestEExpr),
+ std::move(numericBitTestEExpr));
+ };
+ generateTraverse(context, expr, std::move(makeEExprFn));
+}
+
+/**
* A match expression pre-visitor used for maintaining nested logical expressions while traversing
* the match expression tree.
*/
@@ -532,18 +607,10 @@ public:
void visit(const AndMatchExpression* expr) final {
_context->nestedLogicalExprs.push({expr, expr->numChildren()});
}
- void visit(const BitsAllClearMatchExpression* expr) final {
- unsupportedExpression(expr);
- }
- void visit(const BitsAllSetMatchExpression* expr) final {
- unsupportedExpression(expr);
- }
- void visit(const BitsAnyClearMatchExpression* expr) final {
- unsupportedExpression(expr);
- }
- void visit(const BitsAnySetMatchExpression* expr) final {
- unsupportedExpression(expr);
- }
+ void visit(const BitsAllClearMatchExpression* expr) final {}
+ void visit(const BitsAllSetMatchExpression* expr) final {}
+ void visit(const BitsAnyClearMatchExpression* expr) final {}
+ void visit(const BitsAnySetMatchExpression* expr) final {}
void visit(const ElemMatchObjectMatchExpression* expr) final {
unsupportedExpression(expr);
}
@@ -696,10 +763,22 @@ public:
generateLogicalAnd(_context, expr);
}
- void visit(const BitsAllClearMatchExpression* expr) final {}
- void visit(const BitsAllSetMatchExpression* expr) final {}
- void visit(const BitsAnyClearMatchExpression* expr) final {}
- void visit(const BitsAnySetMatchExpression* expr) final {}
+ void visit(const BitsAllClearMatchExpression* expr) final {
+ generateTraverseForBitTests(_context, expr, sbe::BitTestBehavior::AllClear);
+ }
+
+ void visit(const BitsAllSetMatchExpression* expr) final {
+ generateTraverseForBitTests(_context, expr, sbe::BitTestBehavior::AllSet);
+ }
+
+ void visit(const BitsAnyClearMatchExpression* expr) final {
+ generateTraverseForBitTests(_context, expr, sbe::BitTestBehavior::AnyClear);
+ }
+
+ void visit(const BitsAnySetMatchExpression* expr) final {
+ generateTraverseForBitTests(_context, expr, sbe::BitTestBehavior::AnySet);
+ }
+
void visit(const ElemMatchObjectMatchExpression* expr) final {}
void visit(const ElemMatchValueMatchExpression* expr) final {}