diff options
-rw-r--r-- | jstests/core/bittest.js | 64 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/expressions/expression.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/expressions/expression.h | 10 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/vm/arith.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/vm/vm.cpp | 137 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/vm/vm.h | 12 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_leaf.h | 4 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_filter.cpp | 111 |
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 {} |