diff options
-rw-r--r-- | buildscripts/gdb/mongo_printers.py | 17 | ||||
-rw-r--r-- | jstests/libs/sbe_assert_error_override.js | 16 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/expressions/expression.cpp | 37 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/expressions/expression.h | 6 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/expressions/sbe_date_trunc_test.cpp | 67 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/values/arith_common.cpp | 21 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/values/arith_common.h | 3 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/vm/arith.cpp | 20 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/vm/vm.cpp | 89 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/vm/vm.h | 14 | ||||
-rw-r--r-- | src/mongo/db/query/datetime/date_time_support.cpp | 21 | ||||
-rw-r--r-- | src/mongo/db/query/datetime/date_time_support.h | 19 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_expression.cpp | 248 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_helpers.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_helpers.h | 4 |
15 files changed, 401 insertions, 189 deletions
diff --git a/buildscripts/gdb/mongo_printers.py b/buildscripts/gdb/mongo_printers.py index 2fdd409ebaf..bfb4f670def 100644 --- a/buildscripts/gdb/mongo_printers.py +++ b/buildscripts/gdb/mongo_printers.py @@ -774,7 +774,11 @@ class SbeCodeFragmentPrinter(object): value_size = gdb.lookup_type('mongo::sbe::value::Value').sizeof uint8_size = gdb.lookup_type('uint8_t').sizeof uint32_size = gdb.lookup_type('uint32_t').sizeof + uint64_size = gdb.lookup_type('uint64_t').sizeof builtin_size = gdb.lookup_type('mongo::sbe::vm::Builtin').sizeof + time_unit_size = gdb.lookup_type('mongo::TimeUnit').sizeof + timezone_size = gdb.lookup_type('mongo::TimeZone').sizeof + day_of_week_size = gdb.lookup_type('mongo::DayOfWeek').sizeof cur_op = self.pdata end_op = self.pdata + self.size @@ -838,6 +842,19 @@ class SbeCodeFragmentPrinter(object): elif op_name in ['applyClassicMatcher']: args = 'MatchExpression* ' + hex(read_as_integer(cur_op, ptr_size)) cur_op += ptr_size + elif op_name in ['dateTruncImm']: + unit = read_as_integer(cur_op, time_unit_size) + cur_op += time_unit_size + args = 'unit: ' + str(unit) + bin_size = read_as_integer(cur_op, uint64_size) + cur_op += uint64_size + args += ', binSize: ' + str(bin_size) + timezone = read_as_integer(cur_op, timezone_size) + cur_op += timezone_size + args += ', timezone: ' + hex(timezone) + day_of_week = read_as_integer(cur_op, day_of_week_size) + cur_op += day_of_week_size + args += ', dayOfWeek: ' + str(day_of_week) yield hex(op_addr), '{} ({})'.format(op_name, args) diff --git a/jstests/libs/sbe_assert_error_override.js b/jstests/libs/sbe_assert_error_override.js index 6bb6c0369e4..233358e1743 100644 --- a/jstests/libs/sbe_assert_error_override.js +++ b/jstests/libs/sbe_assert_error_override.js @@ -118,15 +118,15 @@ const equivalentErrorCodesList = [ [6045000, 5166606], [146, 13548], [ErrorCodes.TypeMismatch, 5156200, 5156201], - [5439100, 40517], - [5439101, 40485], + [5439100, 40517, 7003907], + [5439101, 40485, 7007908], [5439102, 5439012], - [5439103, 5439013], - [5439104, 9], - [5439105, 5439017], - [5439105, 5439018], - [5439106, 5439015], - [5439107, 5439016], + [5439103, 5439013, 7003902], + [5439104, 9, 7003903], + [5439105, 5439017, 7003904, 7003905], + [5439105, 5439018, 7003906], + [5439106, 5439015, 7003909], + [5439107, 5439016, 7003910], ]; // This map is generated based on the contents of 'equivalentErrorCodesList'. This map should _not_ diff --git a/src/mongo/db/exec/sbe/expressions/expression.cpp b/src/mongo/db/exec/sbe/expressions/expression.cpp index d3457dd2ba8..30d73ec7d7e 100644 --- a/src/mongo/db/exec/sbe/expressions/expression.cpp +++ b/src/mongo/db/exec/sbe/expressions/expression.cpp @@ -37,6 +37,8 @@ #include "mongo/db/exec/sbe/size_estimator.h" #include "mongo/db/exec/sbe/stages/spool.h" #include "mongo/db/exec/sbe/stages/stages.h" +#include "mongo/db/exec/sbe/values/arith_common.h" +#include "mongo/db/exec/sbe/vm/datetime.h" #include "mongo/util/str.h" namespace mongo { @@ -518,8 +520,7 @@ static stdx::unordered_map<std::string, BuiltinFn> kBuiltinFunctions = { {"tsSecond", BuiltinFn{[](size_t n) { return n == 1; }, vm::Builtin::tsSecond, false}}, {"tsIncrement", BuiltinFn{[](size_t n) { return n == 1; }, vm::Builtin::tsIncrement, false}}, {"typeMatch", BuiltinFn{[](size_t n) { return n == 2; }, vm::Builtin::typeMatch, false}}, - {"dateTrunc", - BuiltinFn{[](size_t n) { return n == 5 || n == 6; }, vm::Builtin::dateTrunc, false}}}; + {"dateTrunc", BuiltinFn{[](size_t n) { return n == 6; }, vm::Builtin::dateTrunc, false}}}; /** * The code generation function. @@ -619,6 +620,38 @@ vm::CodeFragment EFunction::compileDirect(CompileCtx& ctx) const { return code; } + } else if (_name == "dateTrunc" && _nodes[2]->as<EConstant>() && + _nodes[3]->as<EConstant>() && _nodes[4]->as<EConstant>() && + _nodes[5]->as<EConstant>()) { + // The validation for the arguments has been omitted here because the constants + // have already been validated in the stage builder. + auto [timezoneDBTag, timezoneDBVal] = + ctx.getRuntimeEnvAccessor(_nodes[0]->as<EVariable>()->getSlotId()) + ->getViewOfValue(); + auto timezoneDB = value::getTimeZoneDBView(timezoneDBVal); + + auto [unitTag, unitVal] = _nodes[2]->as<EConstant>()->getConstant(); + auto unitString = value::getStringView(unitTag, unitVal); + auto unit = parseTimeUnit(unitString); + + auto [binSizeTag, binSizeValue] = _nodes[3]->as<EConstant>()->getConstant(); + auto [binSizeLongOwn, binSizeLongTag, binSizeLongValue] = + genericNumConvert(binSizeTag, binSizeValue, value::TypeTags::NumberInt64); + auto binSize = value::bitcastTo<int64_t>(binSizeLongValue); + + auto [timezoneTag, timezoneVal] = _nodes[4]->as<EConstant>()->getConstant(); + auto timezone = vm::getTimezone(timezoneTag, timezoneVal, timezoneDB); + + DayOfWeek startOfWeek{kStartOfWeekDefault}; + if (unit == TimeUnit::week) { + auto [startOfWeekTag, startOfWeekVal] = _nodes[5]->as<EConstant>()->getConstant(); + auto startOfWeekString = value::getStringView(startOfWeekTag, startOfWeekVal); + startOfWeek = parseDayOfWeek(startOfWeekString); + } + + code.append(_nodes[1]->compileDirect(ctx)); + code.appendDateTrunc(unit, binSize, timezone, startOfWeek); + return code; } for (size_t idx = arity; idx-- > 0;) { diff --git a/src/mongo/db/exec/sbe/expressions/expression.h b/src/mongo/db/exec/sbe/expressions/expression.h index 60e370ee0a7..4ab8f46f2bb 100644 --- a/src/mongo/db/exec/sbe/expressions/expression.h +++ b/src/mongo/db/exec/sbe/expressions/expression.h @@ -448,6 +448,12 @@ public: size_t estimateSize() const final { return sizeof(*this); } + boost::optional<FrameId> getFrameId() const { + return _frameId; + } + value::SlotId getSlotId() const { + return _var; + } private: value::SlotId _var; diff --git a/src/mongo/db/exec/sbe/expressions/sbe_date_trunc_test.cpp b/src/mongo/db/exec/sbe/expressions/sbe_date_trunc_test.cpp index 7ee0a83d838..6f10d3c5eec 100644 --- a/src/mongo/db/exec/sbe/expressions/sbe_date_trunc_test.cpp +++ b/src/mongo/db/exec/sbe/expressions/sbe_date_trunc_test.cpp @@ -116,26 +116,16 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { value::OwnedValueAccessor startOfWeekAccessor; auto startOfWeekSlot = bindAccessor(&startOfWeekAccessor); - // Construct an invocation of "dateTrunc" function without 'startOfWeek' parameter. + // Construct an invocation of "dateTrunc" function. auto dateTruncExpression = sbe::makeE<sbe::EFunction>("dateTrunc", sbe::makeEs(makeE<EVariable>(timezoneDBSlot), makeE<EVariable>(dateSlot), makeE<EVariable>(unitSlot), makeE<EVariable>(binSizeSlot), - makeE<EVariable>(timezoneSlot))); - auto compiledDateTrunc = compileExpression(*dateTruncExpression); - - // Construct an invocation of "dateTrunc" function with 'startOfWeek' parameter. - dateTruncExpression = - sbe::makeE<sbe::EFunction>("dateTrunc", - sbe::makeEs(makeE<EVariable>(timezoneDBSlot), - makeE<EVariable>(dateSlot), - makeE<EVariable>(unitSlot), - makeE<EVariable>(binSizeSlot), makeE<EVariable>(timezoneSlot), makeE<EVariable>(startOfWeekSlot))); - auto compiledDateTruncWithStartOfWeek = compileExpression(*dateTruncExpression); + auto compiledDateTrunc = compileExpression(*dateTruncExpression); // Setup timezone database. auto timezoneDatabase = std::make_unique<TimeZoneDatabase>(); @@ -148,12 +138,14 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { std::pair<value::TypeTags, value::Value> date; std::pair<value::TypeTags, value::Value> unit; std::pair<value::TypeTags, value::Value> binSize; + std::pair<value::TypeTags, value::Value> startOfWeek; std::pair<value::TypeTags, value::Value> expectedValue; // Output. - boost::optional<std::pair<value::TypeTags, value::Value>> startOfWeek; }; const std::pair<value::TypeTags, value::Value> kNothing{value::TypeTags::Nothing, 0}; const std::pair<value::TypeTags, value::Value> kDate{makeDateValue(2022, 9, 12, 12, 24, 36)}; + const std::pair<value::TypeTags, value::Value> kDateOID{ + makeDateValueOID(2022, 9, 12, 12, 24, 36)}; const std::pair<value::TypeTags, value::Value> kHourTruncatedDate{ makeDateValue(2022, 9, 12, 12, 0, 0)}; std::vector<TestCase> testCases{ @@ -163,14 +155,16 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, value::makeNewString("hour"), makeLongValue(1), + value::makeNewString("sun"), kHourTruncatedDate, }, { // Accepts OID values. value::makeNewString("GMT"), - kDate, + kDateOID, value::makeNewString("hour"), makeLongValue(1), + value::makeNewString("sun"), kHourTruncatedDate, }, { @@ -179,6 +173,7 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { convertTimestampToSbeValue(Timestamp{Hours{3}, 0}), value::makeNewString("hour"), makeLongValue(2), + value::makeNewString("sun"), makeDateValue(1970, 1, 1, 2, 0, 0), }, {// 'timezone' is Nothing. @@ -186,18 +181,21 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, value::makeNewString("hour"), makeLongValue(1), + value::makeNewString("sun"), kNothing}, {// 'timezone' is not a valid type. makeLongValue(0), kDate, value::makeNewString("hour"), makeLongValue(1), + value::makeNewString("sun"), kNothing}, {// 'timezone' is not a recognized value. value::makeNewString("Arctic/North_Pole"), kDate, value::makeNewString("hour"), makeLongValue(1), + value::makeNewString("sun"), kNothing}, { // 'date' is Nothing. @@ -205,6 +203,7 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kNothing, value::makeNewString("hour"), makeLongValue(1), + value::makeNewString("sun"), kNothing, }, { @@ -213,6 +212,7 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { makeLongValue(0), value::makeNewString("hour"), makeLongValue(1), + value::makeNewString("sun"), kNothing, }, { @@ -221,6 +221,7 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, kNothing, makeLongValue(1), + value::makeNewString("sun"), kNothing, }, { @@ -229,6 +230,7 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, makeLongValue(0), makeLongValue(1), + value::makeNewString("sun"), kNothing, }, { @@ -237,6 +239,7 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, value::makeNewString("century"), makeLongValue(1), + value::makeNewString("sun"), kNothing, }, { @@ -245,6 +248,7 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, value::makeNewString("hour"), kNothing, + value::makeNewString("sun"), kNothing, }, { @@ -253,6 +257,7 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, value::makeNewString("hour"), kNothing, + value::makeNewString("sun"), kNothing, }, { @@ -261,6 +266,7 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, value::makeNewString("hour"), value::makeNewString("one"), + value::makeNewString("sun"), kNothing, }, { @@ -269,6 +275,7 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, value::makeNewString("hour"), makeDoubleValue(1.5), + value::makeNewString("sun"), kNothing, }, { @@ -277,6 +284,7 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, value::makeNewString("hour"), makeLongValue(0), + value::makeNewString("sun"), kNothing, }, { @@ -285,6 +293,7 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, value::makeNewString("hour"), makeLongValue(-1), + value::makeNewString("sun"), kNothing, }, { @@ -293,6 +302,7 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, value::makeNewString("hour"), makeIntValue(1), + value::makeNewString("sun"), kHourTruncatedDate, }, { @@ -301,6 +311,7 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, value::makeNewString("hour"), makeDoubleValue(1.0), + value::makeNewString("sun"), kHourTruncatedDate, }, { @@ -309,6 +320,7 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, value::makeNewString("hour"), makeDecimalValue("1"), + value::makeNewString("sun"), kHourTruncatedDate, }, {// 'startOfWeek' is present and invalid type. @@ -316,16 +328,16 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, value::makeNewString("hour"), makeLongValue(1), - kNothing, - makeLongValue(0)}, + makeLongValue(0), + kHourTruncatedDate}, { // 'startOfWeek' is present, valid type but invalid value, unit is not week. value::makeNewString("UTC"), kDate, value::makeNewString("hour"), makeLongValue(1), - kHourTruncatedDate, value::makeNewString("holiday"), + kHourTruncatedDate, }, { // 'startOfWeek' is Nothing, unit is week. @@ -342,8 +354,8 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, value::makeNewString("week"), makeLongValue(1), - kNothing, makeLongValue(0), + kNothing, }, { // 'startOfWeek' is invalid value, unit is week. @@ -351,8 +363,8 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { kDate, value::makeNewString("week"), makeLongValue(1), - kNothing, value::makeNewString("holiday"), + kNothing, }, { // 'startOfWeek' is valid value, unit is week. @@ -360,16 +372,8 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { makeDateValue(2022, 9, 12, 12, 24, 36), value::makeNewString("week"), makeLongValue(1), - makeDateValue(2022, 9, 10, 0, 0, 0), value::makeNewString("Saturday"), - }, - { - // 'startOfWeek' is not specified (should default to Sunday), unit is week. - value::makeNewString("UTC"), - makeDateValue(2022, 9, 12, 12, 24, 36), - value::makeNewString("week"), - makeLongValue(1), - makeDateValue(2022, 9, 11, 0, 0, 0), + makeDateValue(2022, 9, 10, 0, 0, 0), }, }; @@ -379,13 +383,10 @@ TEST_F(SBEDateTruncTest, BasicDateTrunc) { dateAccessor.reset(testCase.date.first, testCase.date.second); unitAccessor.reset(testCase.unit.first, testCase.unit.second); binSizeAccessor.reset(testCase.binSize.first, testCase.binSize.second); - if (testCase.startOfWeek) { - startOfWeekAccessor.reset(testCase.startOfWeek->first, testCase.startOfWeek->second); - } + startOfWeekAccessor.reset(testCase.startOfWeek.first, testCase.startOfWeek.second); // Execute the "dateTrunc" function. - auto result = runCompiledExpression( - (testCase.startOfWeek ? compiledDateTruncWithStartOfWeek : compiledDateTrunc).get()); + auto result = runCompiledExpression(compiledDateTrunc.get()); auto [resultTag, resultValue] = result; value::ValueGuard resultGuard(resultTag, resultValue); diff --git a/src/mongo/db/exec/sbe/values/arith_common.cpp b/src/mongo/db/exec/sbe/values/arith_common.cpp index 12b34639289..f7646f2c4f1 100644 --- a/src/mongo/db/exec/sbe/values/arith_common.cpp +++ b/src/mongo/db/exec/sbe/values/arith_common.cpp @@ -268,4 +268,25 @@ FastTuple<bool, value::TypeTags, value::Value> genericMul(value::TypeTags lhsTag return genericArithmeticOp<Multiplication>(lhsTag, lhsValue, rhsTag, rhsValue); } +FastTuple<bool, value::TypeTags, value::Value> genericNumConvert(value::TypeTags lhsTag, + value::Value lhsValue, + value::TypeTags targetTag) { + if (value::isNumber(lhsTag)) { + switch (lhsTag) { + case value::TypeTags::NumberInt32: + return numericConvLossless<int32_t>(value::bitcastTo<int32_t>(lhsValue), targetTag); + case value::TypeTags::NumberInt64: + return numericConvLossless<int64_t>(value::bitcastTo<int64_t>(lhsValue), targetTag); + case value::TypeTags::NumberDouble: + return numericConvLossless<double>(value::bitcastTo<double>(lhsValue), targetTag); + case value::TypeTags::NumberDecimal: + return numericConvLossless<Decimal128>(value::bitcastTo<Decimal128>(lhsValue), + targetTag); + default: + MONGO_UNREACHABLE + } + } + return {false, value::TypeTags::Nothing, 0}; +} + } // namespace mongo::sbe::value diff --git a/src/mongo/db/exec/sbe/values/arith_common.h b/src/mongo/db/exec/sbe/values/arith_common.h index 4e7682d0a3b..b14e6df49b4 100644 --- a/src/mongo/db/exec/sbe/values/arith_common.h +++ b/src/mongo/db/exec/sbe/values/arith_common.h @@ -44,4 +44,7 @@ FastTuple<bool, value::TypeTags, value::Value> genericMul(value::TypeTags lhsTag value::Value lhsValue, value::TypeTags rhsTag, value::Value rhsValue); +FastTuple<bool, value::TypeTags, value::Value> genericNumConvert(value::TypeTags lhsTag, + value::Value lhsValue, + value::TypeTags targetTag); } // namespace mongo::sbe::value diff --git a/src/mongo/db/exec/sbe/vm/arith.cpp b/src/mongo/db/exec/sbe/vm/arith.cpp index a325d31f676..1386ac832ab 100644 --- a/src/mongo/db/exec/sbe/vm/arith.cpp +++ b/src/mongo/db/exec/sbe/vm/arith.cpp @@ -557,26 +557,6 @@ FastTuple<bool, value::TypeTags, value::Value> ByteCode::genericMod(value::TypeT return {false, value::TypeTags::Nothing, 0}; } -FastTuple<bool, value::TypeTags, value::Value> ByteCode::genericNumConvert( - value::TypeTags lhsTag, value::Value lhsValue, value::TypeTags targetTag) { - if (value::isNumber(lhsTag)) { - switch (lhsTag) { - case value::TypeTags::NumberInt32: - return numericConvLossless<int32_t>(value::bitcastTo<int32_t>(lhsValue), targetTag); - case value::TypeTags::NumberInt64: - return numericConvLossless<int64_t>(value::bitcastTo<int64_t>(lhsValue), targetTag); - case value::TypeTags::NumberDouble: - return numericConvLossless<double>(value::bitcastTo<double>(lhsValue), targetTag); - case value::TypeTags::NumberDecimal: - return numericConvLossless<Decimal128>(value::bitcastTo<Decimal128>(lhsValue), - targetTag); - default: - MONGO_UNREACHABLE - } - } - return {false, value::TypeTags::Nothing, 0}; -} - FastTuple<bool, value::TypeTags, value::Value> ByteCode::genericAbs(value::TypeTags operandTag, value::Value operandValue) { switch (operandTag) { diff --git a/src/mongo/db/exec/sbe/vm/vm.cpp b/src/mongo/db/exec/sbe/vm/vm.cpp index 7875ef7f499..38a17246d7e 100644 --- a/src/mongo/db/exec/sbe/vm/vm.cpp +++ b/src/mongo/db/exec/sbe/vm/vm.cpp @@ -157,6 +157,7 @@ int Instruction::stackOffset[Instruction::Tags::lastInstruction] = { -1, // fail 0, // applyClassicMatcher + 0, // dateTruncImm }; void ByteCode::growAndResize() noexcept { @@ -325,6 +326,21 @@ std::string CodeFragment::toString() const { ss << "f: " << static_cast<uint8_t>(f) << ", arity: " << arity; break; } + case Instruction::dateTruncImm: { + auto unit = readFromMemory<TimeUnit>(pcPointer); + pcPointer += sizeof(unit); + auto binSize = readFromMemory<int64_t>(pcPointer); + pcPointer += sizeof(binSize); + auto timezone = readFromMemory<TimeZone>(pcPointer); + pcPointer += sizeof(timezone); + auto startOfWeek = readFromMemory<DayOfWeek>(pcPointer); + pcPointer += sizeof(startOfWeek); + ss << "unit: " << static_cast<int32_t>(unit) << ", binSize: " << binSize + << ", timezoneTzInfo: " << static_cast<void*>(timezone.getTzInfo()) + << ", timezoneUtcOffset: " << timezone.getUtcOffset() + << ", startOfWeek: " << static_cast<int8_t>(startOfWeek); + break; + } default: ss << "unknown"; } @@ -677,6 +693,24 @@ void CodeFragment::appendTypeMatch(uint32_t mask) { offset += writeToMemory(offset, mask); } +void CodeFragment::appendDateTrunc(TimeUnit unit, + int64_t binSize, + TimeZone timezone, + DayOfWeek startOfWeek) { + Instruction i; + i.tag = Instruction::dateTruncImm; + adjustStackSimple(i); + + auto offset = allocateSpace(sizeof(Instruction) + sizeof(unit) + sizeof(binSize) + + sizeof(timezone) + sizeof(startOfWeek)); + + offset += writeToMemory(offset, i); + offset += writeToMemory(offset, unit); + offset += writeToMemory(offset, binSize); + offset += writeToMemory(offset, timezone); + offset += writeToMemory(offset, startOfWeek); +} + void CodeFragment::appendFunction(Builtin f, ArityType arity) { Instruction i; const bool isSmallArity = (arity <= std::numeric_limits<SmallArityType>::max()); @@ -2676,7 +2710,7 @@ FastTuple<bool, value::TypeTags, value::Value> ByteCode::builtinDateDiff(ArityTy } FastTuple<bool, value::TypeTags, value::Value> ByteCode::builtinDateTrunc(ArityType arity) { - invariant(arity == 5 || arity == 6); // 6th parameter is 'startOfWeek'. + invariant(arity == 6); auto [timezoneDBOwn, timezoneDBTag, timezoneDBValue] = getFromStack(0); if (timezoneDBTag != value::TypeTags::timeZoneDB) { @@ -2686,10 +2720,6 @@ FastTuple<bool, value::TypeTags, value::Value> ByteCode::builtinDateTrunc(ArityT // Get date. auto [dateOwn, dateTag, dateValue] = getFromStack(1); - if (!coercibleToDate(dateTag)) { - return {false, value::TypeTags::Nothing, 0}; - } - auto date = getDate(dateTag, dateValue); // Get unit. auto [unitOwn, unitTag, unitValue] = getFromStack(2); @@ -2726,20 +2756,33 @@ FastTuple<bool, value::TypeTags, value::Value> ByteCode::builtinDateTrunc(ArityT // Get startOfWeek, if 'startOfWeek' parameter was passed and time unit is the week. DayOfWeek startOfWeek{kStartOfWeekDefault}; - if (6 == arity) { + if (TimeUnit::week == unit) { auto [startOfWeekOwn, startOfWeekTag, startOfWeekValue] = getFromStack(5); if (!value::isString(startOfWeekTag)) { return {false, value::TypeTags::Nothing, 0}; } - if (TimeUnit::week == unit) { - auto startOfWeekString = value::getStringView(startOfWeekTag, startOfWeekValue); - if (!isValidDayOfWeek(startOfWeekString)) { - return {false, value::TypeTags::Nothing, 0}; - } - startOfWeek = parseDayOfWeek(startOfWeekString); + auto startOfWeekString = value::getStringView(startOfWeekTag, startOfWeekValue); + if (!isValidDayOfWeek(startOfWeekString)) { + return {false, value::TypeTags::Nothing, 0}; } + startOfWeek = parseDayOfWeek(startOfWeekString); } + return dateTrunc(dateTag, dateValue, unit, binSize, timezone, startOfWeek); +} + +FastTuple<bool, value::TypeTags, value::Value> ByteCode::dateTrunc(value::TypeTags dateTag, + value::Value dateValue, + TimeUnit unit, + int64_t binSize, + TimeZone timezone, + DayOfWeek startOfWeek) { + // Get date. + if (!coercibleToDate(dateTag)) { + return {false, value::TypeTags::Nothing, 0}; + } + auto date = getDate(dateTag, dateValue); + auto truncatedDate = truncateDate(date, unit, binSize, timezone, startOfWeek); return {false, value::TypeTags::Date, @@ -5838,6 +5881,28 @@ void ByteCode::runInternal(const CodeFragment* code, int64_t position) { runClassicMatcher(matcher); break; } + case Instruction::dateTruncImm: { + auto unit = readFromMemory<TimeUnit>(pcPointer); + pcPointer += sizeof(unit); + auto binSize = readFromMemory<int64_t>(pcPointer); + pcPointer += sizeof(binSize); + auto timezone = readFromMemory<TimeZone>(pcPointer); + pcPointer += sizeof(timezone); + auto startOfWeek = readFromMemory<DayOfWeek>(pcPointer); + pcPointer += sizeof(startOfWeek); + + auto [dateOwned, dateTag, dateVal] = getFromStack(0); + + auto [owned, tag, val] = + dateTrunc(dateTag, dateVal, unit, binSize, timezone, startOfWeek); + + topStack(owned, tag, val); + + if (dateOwned) { + value::releaseValue(dateTag, dateVal); + } + break; + } default: MONGO_UNREACHABLE; } diff --git a/src/mongo/db/exec/sbe/vm/vm.h b/src/mongo/db/exec/sbe/vm/vm.h index db435950d33..3b7b1501d99 100644 --- a/src/mongo/db/exec/sbe/vm/vm.h +++ b/src/mongo/db/exec/sbe/vm/vm.h @@ -324,6 +324,8 @@ struct Instruction { applyClassicMatcher, // Instruction which calls into the classic engine MatchExpression. + dateTruncImm, + lastInstruction // this is just a marker used to calculate number of instructions }; @@ -507,6 +509,8 @@ struct Instruction { return "fail"; case applyClassicMatcher: return "applyClassicMatcher"; + case dateTruncImm: + return "dateTruncImm"; default: return "unrecognized"; } @@ -758,6 +762,7 @@ public: appendSimpleInstruction(Instruction::setField); } void appendGetArraySize(); + void appendDateTrunc(TimeUnit unit, int64_t binSize, TimeZone timezone, DayOfWeek startOfWeek); void appendSum(); void appendMin(); @@ -925,9 +930,6 @@ private: value::Value rhsVal, value::TypeTags collTag, value::Value collVal); - FastTuple<bool, value::TypeTags, value::Value> genericNumConvert(value::TypeTags lhsTag, - value::Value lhsValue, - value::TypeTags rhsTag); std::pair<value::TypeTags, value::Value> compare3way( value::TypeTags lhsTag, @@ -1066,6 +1068,12 @@ private: value::Value timezoneValue); FastTuple<bool, value::TypeTags, value::Value> genericNewKeyString( ArityType arity, CollatorInterface* collator = nullptr); + FastTuple<bool, value::TypeTags, value::Value> dateTrunc(value::TypeTags dateTag, + value::Value dateValue, + TimeUnit unit, + int64_t binSize, + TimeZone timezone, + DayOfWeek startOfWeek); FastTuple<bool, value::TypeTags, value::Value> builtinSplit(ArityType arity); FastTuple<bool, value::TypeTags, value::Value> builtinDate(ArityType arity); diff --git a/src/mongo/db/query/datetime/date_time_support.cpp b/src/mongo/db/query/datetime/date_time_support.cpp index d11bd53f325..fa18b4f1084 100644 --- a/src/mongo/db/query/datetime/date_time_support.cpp +++ b/src/mongo/db/query/datetime/date_time_support.cpp @@ -166,6 +166,12 @@ void TimeZoneDatabase::TimelibErrorContainerDeleter::operator()( timelib_error_container_dtor(errorContainer); } +void TimeZoneDatabase::TimelibTZInfoDeleter::operator()(timelib_tzinfo* tzInfo) { + if (tzInfo) { + timelib_tzinfo_dtor(tzInfo); + } +} + void TimeZoneDatabase::loadTimeZoneInfo( std::unique_ptr<timelib_tzdb, TimeZoneDBDeleter> timeZoneDatabase) { invariant(timeZoneDatabase); @@ -193,6 +199,8 @@ void TimeZoneDatabase::loadTimeZoneInfo( _timeZones[entry.id] = TimeZone{nullptr}; timelib_tzinfo_dtor(tzInfo); } else { + _timeZoneInfos.emplace_back( + std::unique_ptr<_timelib_tzinfo, TimelibTZInfoDeleter>(tzInfo)); _timeZones[entry.id] = TimeZone{tzInfo}; } } @@ -395,7 +403,7 @@ std::vector<std::string> TimeZoneDatabase::getTimeZoneStrings() const { void TimeZone::adjustTimeZone(timelib_time* timelibTime) const { if (isTimeZoneIDZone()) { - timelib_set_timezone(timelibTime, _tzInfo.get()); + timelib_set_timezone(timelibTime, _tzInfo); } else if (isUtcOffsetZone()) { timelib_set_timezone_from_offset(timelibTime, durationCount<Seconds>(_utcOffset)); } @@ -488,14 +496,7 @@ TimeZone::Iso8601DateParts::Iso8601DateParts(const timelib_time& timelib_time, D } -void TimeZone::TimelibTZInfoDeleter::operator()(timelib_tzinfo* tzInfo) { - if (tzInfo) { - timelib_tzinfo_dtor(tzInfo); - } -} - -TimeZone::TimeZone(timelib_tzinfo* tzInfo) - : _tzInfo(tzInfo, TimelibTZInfoDeleter()), _utcOffset(0) {} +TimeZone::TimeZone(timelib_tzinfo* tzInfo) : _tzInfo(tzInfo), _utcOffset(0) {} TimeZone::TimeZone(Seconds utcOffsetSeconds) : _tzInfo(nullptr), _utcOffset(utcOffsetSeconds) {} @@ -580,7 +581,7 @@ Seconds TimeZone::utcOffset(Date_t date) const { int32_t timezoneOffsetFromUTC = 0; int result = timelib_get_time_zone_offset_info(durationCount<Seconds>(date.toDurationSinceEpoch()), - _tzInfo.get(), + _tzInfo, &timezoneOffsetFromUTC, nullptr, nullptr); diff --git a/src/mongo/db/query/datetime/date_time_support.h b/src/mongo/db/query/datetime/date_time_support.h index 8673f597e77..77cba17d131 100644 --- a/src/mongo/db/query/datetime/date_time_support.h +++ b/src/mongo/db/query/datetime/date_time_support.h @@ -344,10 +344,14 @@ public: static void validateFromStringFormat(StringData format); std::unique_ptr<_timelib_time, TimelibTimeDeleter> getTimelibTime(Date_t) const; - std::shared_ptr<_timelib_tzinfo> getTzInfo() const { + _timelib_tzinfo* getTzInfo() const { return _tzInfo; } + Seconds getUtcOffset() const { + return _utcOffset; + } + private: /** * Only works with 1 <= spaces <= 4 and 0 <= number <= 9999. If spaces is less than the digit @@ -381,12 +385,8 @@ private: return Status::OK(); } - struct TimelibTZInfoDeleter { - void operator()(_timelib_tzinfo* tzInfo); - }; - // null if this TimeZone represents the default UTC time zone, or a UTC-offset time zone - std::shared_ptr<_timelib_tzinfo> _tzInfo; + _timelib_tzinfo* _tzInfo; // represents the UTC offset in seconds if _tzInfo is null and it is not 0 Seconds _utcOffset{0}; @@ -486,6 +486,10 @@ public: std::string toString() const; private: + struct TimelibTZInfoDeleter { + void operator()(_timelib_tzinfo* tzInfo); + }; + /** * Populates '_timeZones' with parsed time zone rules for each timezone specified by * 'timeZoneDatabase'. @@ -504,6 +508,9 @@ private: // The timelib structure which provides timezone information. std::unique_ptr<_timelib_tzdb, TimeZoneDBDeleter> _timeZoneDatabase; + + // The list of pre-load _timelib_tzinfo objects. + std::vector<std::unique_ptr<_timelib_tzinfo, TimelibTZInfoDeleter>> _timeZoneInfos; }; /** diff --git a/src/mongo/db/query/sbe_stage_builder_expression.cpp b/src/mongo/db/query/sbe_stage_builder_expression.cpp index f903d68b1cb..5f612f65840 100644 --- a/src/mongo/db/query/sbe_stage_builder_expression.cpp +++ b/src/mongo/db/query/sbe_stage_builder_expression.cpp @@ -42,6 +42,7 @@ #include "mongo/db/exec/sbe/stages/project.h" #include "mongo/db/exec/sbe/stages/traverse.h" #include "mongo/db/exec/sbe/stages/union.h" +#include "mongo/db/exec/sbe/values/arith_common.h" #include "mongo/db/exec/sbe/values/bson.h" #include "mongo/db/operation_context.h" #include "mongo/db/pipeline/accumulator.h" @@ -1749,19 +1750,6 @@ public: unsupportedExpression("$dateFromString"); } void visit(const ExpressionDateTrunc* expr) final { - auto frameId = _context->state.frameId(); - sbe::EExpression::Vector arguments; - sbe::EExpression::Vector bindings; - sbe::EVariable dateRef(frameId, 0); - sbe::EVariable unitRef(frameId, 1); - sbe::EVariable binSizeRef(frameId, 2); - sbe::EVariable timezoneRef(frameId, 3); - sbe::EVariable startOfWeekRef(frameId, 4); - - // An auxiliary boolean variable to hold a value of a common subexpression 'unit'=="week" - // (string). - sbe::EVariable unitIsWeekRef(frameId, 5); - auto children = expr->getChildren(); invariant(children.size() == 5); _context->ensureArity(2 + (expr->isBinSizeSpecified() ? 1 : 0) + @@ -1769,7 +1757,8 @@ public: (expr->isStartOfWeekSpecified() ? 1 : 0)); // Get child expressions. - auto startOfWeekExpression = expr->isStartOfWeekSpecified() ? _context->popExpr() : nullptr; + auto startOfWeekExpression = + expr->isStartOfWeekSpecified() ? _context->popExpr() : makeConstant("sun"_sd); auto timezoneExpression = expr->isTimezoneSpecified() ? _context->popExpr() : makeConstant("UTC"_sd); auto binSizeExpression = expr->isBinSizeSpecified() @@ -1779,109 +1768,186 @@ public: auto dateExpression = _context->popExpr(); auto timezoneDBSlot = _context->state.data->env->getSlot("timeZoneDB"_sd); + auto [timezoneDBTag, timezoneDBVal] = + _context->state.data->env->getAccessor(timezoneDBSlot)->getViewOfValue(); + tassert(7003901, + "$dateTrunc first argument must be a timezoneDB object", + timezoneDBTag == sbe::value::TypeTags::timeZoneDB); + auto timezoneDB = sbe::value::getTimeZoneDBView(timezoneDBVal); + + // Local bind to hold the date expression result + auto dateFrameId = _context->state.frameId(); + sbe::EExpression::Vector dateBindings; + sbe::EVariable dateRef(dateFrameId, 0); + dateBindings.push_back(std::move(dateExpression)); // Set parameters for an invocation of built-in "dateTrunc" function. + sbe::EExpression::Vector arguments; arguments.push_back(makeVariable(timezoneDBSlot)); arguments.push_back(dateRef.clone()); - arguments.push_back(unitRef.clone()); - arguments.push_back(binSizeRef.clone()); - arguments.push_back(timezoneRef.clone()); - if (expr->isStartOfWeekSpecified()) { - // Parameter "startOfWeek" - if the time unit is the week, then pass value of parameter - // "startOfWeek" of "$dateTrunc" expression, otherwise pass a valid default value, since - // "dateTrunc" built-in function does not accept non-string type values for this - // parameter. - arguments.push_back(sbe::makeE<sbe::EIf>( - unitIsWeekRef.clone(), startOfWeekRef.clone(), makeConstant("sun"_sd))); - } - - // Set bindings for the frame. - bindings.push_back(std::move(dateExpression)); - bindings.push_back(std::move(unitExpression)); - bindings.push_back(std::move(binSizeExpression)); - bindings.push_back(std::move(timezoneExpression)); - if (expr->isStartOfWeekSpecified()) { - bindings.push_back(std::move(startOfWeekExpression)); - bindings.push_back(generateIsEqualToStringCheck(unitRef, "week"_sd)); - } + arguments.push_back(unitExpression->clone()); + arguments.push_back(binSizeExpression->clone()); + arguments.push_back(timezoneExpression->clone()); + arguments.push_back(startOfWeekExpression->clone()); // Create an expression to invoke built-in "dateTrunc" function. auto dateTruncFunctionCall = sbe::makeE<sbe::EFunction>("dateTrunc"_sd, std::move(arguments)); + // Local bind to hold the $dateTrunc function call result + auto dateTruncFrameId = _context->state.frameId(); + sbe::EExpression::Vector dateTruncBindings; + sbe::EVariable dateTruncRef(dateTruncFrameId, 0); + dateTruncBindings.push_back(std::move(dateTruncFunctionCall)); + + // Local bind to hold the unitIsWeek common subexpression + auto unitIsWeekFrameId = _context->state.frameId(); + sbe::EExpression::Vector unitIsWeekBindings; + sbe::EVariable unitIsWeekRef(unitIsWeekFrameId, 0); + unitIsWeekBindings.push_back(generateIsEqualToStringCheck(*unitExpression, "week"_sd)); + // Create expressions to check that each argument to "dateTrunc" function exists, is not // null, and is of the correct type. std::vector<CaseValuePair> inputValidationCases; // Return null if any of the parameters is either null or missing. inputValidationCases.push_back(generateReturnNullIfNullOrMissing(dateRef)); - inputValidationCases.push_back(generateReturnNullIfNullOrMissing(unitRef)); - inputValidationCases.push_back(generateReturnNullIfNullOrMissing(binSizeRef)); - inputValidationCases.push_back(generateReturnNullIfNullOrMissing(timezoneRef)); - if (expr->isStartOfWeekSpecified()) { - inputValidationCases.emplace_back(makeBinaryOp(sbe::EPrimBinary::logicAnd, - unitIsWeekRef.clone(), - generateNullOrMissing(startOfWeekRef)), - makeConstant(sbe::value::TypeTags::Null, 0)); - } + inputValidationCases.push_back(generateReturnNullIfNullOrMissing(unitExpression->clone())); + inputValidationCases.push_back( + generateReturnNullIfNullOrMissing(binSizeExpression->clone())); + inputValidationCases.push_back( + generateReturnNullIfNullOrMissing(timezoneExpression->clone())); + inputValidationCases.emplace_back( + makeBinaryOp(sbe::EPrimBinary::logicAnd, + unitIsWeekRef.clone(), + generateNullOrMissing(startOfWeekExpression->clone())), + makeConstant(sbe::value::TypeTags::Null, 0)); // "timezone" parameter validation. - inputValidationCases.emplace_back( - generateNonStringCheck(timezoneRef), - makeFail(5439100, "$dateTrunc parameter 'timezone' must be a string")); - inputValidationCases.emplace_back( - makeNot(makeFunction("isTimezone", makeVariable(timezoneDBSlot), timezoneRef.clone())), - makeFail(5439101, "$dateTrunc parameter 'timezone' must be a valid timezone")); + if (timezoneExpression->as<sbe::EConstant>()) { + auto [timezoneTag, timezoneVal] = + timezoneExpression->as<sbe::EConstant>()->getConstant(); + tassert(7003907, + "$dateTrunc parameter 'timezone' must be a string", + sbe::value::isString(timezoneTag)); + tassert(7003908, + "$dateTrunc parameter 'timezone' must be a valid timezone", + sbe::vm::isValidTimezone(timezoneTag, timezoneVal, timezoneDB)); + } else { + inputValidationCases.emplace_back( + generateNonStringCheck(*timezoneExpression), + makeFail(5439100, "$dateTrunc parameter 'timezone' must be a string")); + inputValidationCases.emplace_back( + makeNot(makeFunction( + "isTimezone", makeVariable(timezoneDBSlot), timezoneExpression->clone())), + makeFail(5439101, "$dateTrunc parameter 'timezone' must be a valid timezone")); + } // "date" parameter validation. inputValidationCases.emplace_back(generateFailIfNotCoercibleToDate( dateRef, ErrorCodes::Error{5439102}, "$dateTrunc"_sd, "date"_sd)); // "unit" parameter validation. - inputValidationCases.emplace_back( - generateNonStringCheck(unitRef), - makeFail(5439103, "$dateTrunc parameter 'unit' must be a string")); - inputValidationCases.emplace_back( - makeNot(makeFunction("isTimeUnit", unitRef.clone())), - makeFail(5439104, "$dateTrunc parameter 'unit' must be a valid time unit")); + if (unitExpression->as<sbe::EConstant>()) { + auto [unitTag, unitVal] = unitExpression->as<sbe::EConstant>()->getConstant(); + tassert(7003902, + "$dateTrunc parameter 'unit' must be a string", + sbe::value::isString(unitTag)); + auto unitString = sbe::value::getStringView(unitTag, unitVal); + tassert(7003903, + "$dateTrunc parameter 'unit' must be a valid time unit", + isValidTimeUnit(unitString)); + } else { + inputValidationCases.emplace_back( + generateNonStringCheck(*unitExpression), + makeFail(5439103, "$dateTrunc parameter 'unit' must be a string")); + inputValidationCases.emplace_back( + makeNot(makeFunction("isTimeUnit", unitExpression->clone())), + makeFail(5439104, "$dateTrunc parameter 'unit' must be a valid time unit")); + } // "binSize" parameter validation. if (expr->isBinSizeSpecified()) { - inputValidationCases.emplace_back( - makeNot(makeBinaryOp( - sbe::EPrimBinary::logicAnd, - makeBinaryOp( + if (binSizeExpression->as<sbe::EConstant>()) { + auto [binSizeTag, binSizeValue] = + binSizeExpression->as<sbe::EConstant>()->getConstant(); + tassert( + 7003904, + "$dateTrunc parameter 'binSize' must be coercible to a positive 64-bit integer", + sbe::value::isNumber(binSizeTag)); + auto [binSizeLongOwn, binSizeLongTag, binSizeLongValue] = + sbe::value::genericNumConvert( + binSizeTag, binSizeValue, sbe::value::TypeTags::NumberInt64); + tassert( + 7003905, + "$dateTrunc parameter 'binSize' must be coercible to a positive 64-bit integer", + binSizeLongTag != sbe::value::TypeTags::Nothing); + auto binSize = sbe::value::bitcastTo<int64_t>(binSizeLongValue); + tassert( + 7003906, + "$dateTrunc parameter 'binSize' must be coercible to a positive 64-bit integer", + binSize > 0); + } else { + inputValidationCases.emplace_back( + makeNot(makeBinaryOp( sbe::EPrimBinary::logicAnd, - makeFunction("isNumber", binSizeRef.clone()), - makeFunction("exists", - sbe::makeE<sbe::ENumericConvert>( - binSizeRef.clone(), sbe::value::TypeTags::NumberInt64))), - generatePositiveCheck(binSizeRef))), - makeFail(5439105, - "$dateTrunc parameter 'binSize' must be coercible to a positive 64-bit " - "integer")); + makeBinaryOp(sbe::EPrimBinary::logicAnd, + makeFunction("isNumber", binSizeExpression->clone()), + makeFunction("exists", + sbe::makeE<sbe::ENumericConvert>( + binSizeExpression->clone(), + sbe::value::TypeTags::NumberInt64))), + generatePositiveCheck(*binSizeExpression))), + makeFail( + 5439105, + "$dateTrunc parameter 'binSize' must be coercible to a positive 64-bit " + "integer")); + } } // "startOfWeek" parameter validation. if (expr->isStartOfWeekSpecified()) { - // If 'timeUnit' value is equal to "week" then validate "startOfWeek" parameter. - inputValidationCases.emplace_back( - makeBinaryOp(sbe::EPrimBinary::logicAnd, - unitIsWeekRef.clone(), - generateNonStringCheck(startOfWeekRef)), - makeFail(5439106, "$dateTrunc parameter 'startOfWeek' must be a string")); - inputValidationCases.emplace_back( - makeBinaryOp(sbe::EPrimBinary::logicAnd, - unitIsWeekRef.clone(), - makeNot(makeFunction("isDayOfWeek", startOfWeekRef.clone()))), - makeFail(5439107, - "$dateTrunc parameter 'startOfWeek' must be a valid day of the week")); + if (startOfWeekExpression->as<sbe::EConstant>()) { + auto [startOfWeekTag, startOfWeekVal] = + startOfWeekExpression->as<sbe::EConstant>()->getConstant(); + tassert(7003909, + "$dateTrunc parameter 'startOfWeek' must be a string", + sbe::value::isString(startOfWeekTag)); + auto startOfWeekString = sbe::value::getStringView(startOfWeekTag, startOfWeekVal); + tassert(7003910, + "$dateTrunc parameter 'startOfWeek' must be a valid day of the week", + isValidDayOfWeek(startOfWeekString)); + } else { + // If 'timeUnit' value is equal to "week" then validate "startOfWeek" parameter. + inputValidationCases.emplace_back( + makeBinaryOp(sbe::EPrimBinary::logicAnd, + unitIsWeekRef.clone(), + generateNonStringCheck(*startOfWeekExpression)), + makeFail(5439106, "$dateTrunc parameter 'startOfWeek' must be a string")); + inputValidationCases.emplace_back( + makeBinaryOp( + sbe::EPrimBinary::logicAnd, + unitIsWeekRef.clone(), + makeNot(makeFunction("isDayOfWeek", startOfWeekExpression->clone()))), + makeFail(5439107, + "$dateTrunc parameter 'startOfWeek' must be a valid day of the week")); + } } - auto dateTruncExpression = buildMultiBranchConditionalFromCaseValuePairs( - std::move(inputValidationCases), std::move(dateTruncFunctionCall)); _context->pushExpr(sbe::makeE<sbe::ELocalBind>( - frameId, std::move(bindings), std::move(dateTruncExpression))); + dateFrameId, + std::move(dateBindings), + sbe::makeE<sbe::ELocalBind>( + dateTruncFrameId, + std::move(dateTruncBindings), + sbe::makeE<sbe::EIf>(makeFunction("exists", dateTruncRef.clone()), + dateTruncRef.clone(), + sbe::makeE<sbe::ELocalBind>( + unitIsWeekFrameId, + std::move(unitIsWeekBindings), + buildMultiBranchConditionalFromCaseValuePairs( + std::move(inputValidationCases), + makeConstant(sbe::value::TypeTags::Nothing, 0))))))); } void visit(const ExpressionDivide* expr) final { _context->ensureArity(2); @@ -3418,16 +3484,20 @@ private: return {generateNullOrMissing(variable), makeConstant(sbe::value::TypeTags::Null, 0)}; } + static CaseValuePair generateReturnNullIfNullOrMissing(std::unique_ptr<sbe::EExpression> expr) { + return {generateNullOrMissing(std::move(expr)), + makeConstant(sbe::value::TypeTags::Null, 0)}; + } + /** * Creates a boolean expression to check if 'variable' is equal to string 'string'. */ static std::unique_ptr<sbe::EExpression> generateIsEqualToStringCheck( - const sbe::EVariable& variable, StringData string) { - return sbe::makeE<sbe::EPrimBinary>(sbe::EPrimBinary::logicAnd, - makeFunction("isString", variable.clone()), - sbe::makeE<sbe::EPrimBinary>(sbe::EPrimBinary::eq, - variable.clone(), - makeConstant(string))); + const sbe::EExpression& expr, StringData string) { + return sbe::makeE<sbe::EPrimBinary>( + sbe::EPrimBinary::logicAnd, + makeFunction("isString", expr.clone()), + sbe::makeE<sbe::EPrimBinary>(sbe::EPrimBinary::eq, expr.clone(), makeConstant(string))); } /** diff --git a/src/mongo/db/query/sbe_stage_builder_helpers.cpp b/src/mongo/db/query/sbe_stage_builder_helpers.cpp index 949a189c4ff..b6da4d12a04 100644 --- a/src/mongo/db/query/sbe_stage_builder_helpers.cpp +++ b/src/mongo/db/query/sbe_stage_builder_helpers.cpp @@ -173,9 +173,9 @@ std::unique_ptr<sbe::EExpression> generateNonPositiveCheck(const sbe::EVariable& sbe::value::bitcastFrom<int32_t>(0))); } -std::unique_ptr<sbe::EExpression> generatePositiveCheck(const sbe::EVariable& var) { +std::unique_ptr<sbe::EExpression> generatePositiveCheck(const sbe::EExpression& expr) { return makeBinaryOp(sbe::EPrimBinary::EPrimBinary::greater, - var.clone(), + expr.clone(), sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::NumberInt32, sbe::value::bitcastFrom<int32_t>(0))); } @@ -191,8 +191,8 @@ std::unique_ptr<sbe::EExpression> generateNonObjectCheck(const sbe::EVariable& v return makeNot(makeFunction("isObject", var.clone())); } -std::unique_ptr<sbe::EExpression> generateNonStringCheck(const sbe::EVariable& var) { - return makeNot(makeFunction("isString", var.clone())); +std::unique_ptr<sbe::EExpression> generateNonStringCheck(const sbe::EExpression& expr) { + return makeNot(makeFunction("isString", expr.clone())); } std::unique_ptr<sbe::EExpression> generateNullishOrNotRepresentableInt32Check( diff --git a/src/mongo/db/query/sbe_stage_builder_helpers.h b/src/mongo/db/query/sbe_stage_builder_helpers.h index a9e78644419..3225859bd9a 100644 --- a/src/mongo/db/query/sbe_stage_builder_helpers.h +++ b/src/mongo/db/query/sbe_stage_builder_helpers.h @@ -114,7 +114,7 @@ std::unique_ptr<sbe::EExpression> generateNonPositiveCheck(const sbe::EVariable& * Generates an EExpression that checks if the input expression is a positive number (i.e. > 0) * _assuming that_ it has already been verified to be numeric. */ -std::unique_ptr<sbe::EExpression> generatePositiveCheck(const sbe::EVariable& var); +std::unique_ptr<sbe::EExpression> generatePositiveCheck(const sbe::EExpression& expr); /** * Generates an EExpression that checks if the input expression is a negative (i.e., < 0) number @@ -132,7 +132,7 @@ std::unique_ptr<sbe::EExpression> generateNonObjectCheck(const sbe::EVariable& v * Generates an EExpression that checks if the input expression is not a string, _assuming that * it has already been verified to be neither null nor missing. */ -std::unique_ptr<sbe::EExpression> generateNonStringCheck(const sbe::EVariable& var); +std::unique_ptr<sbe::EExpression> generateNonStringCheck(const sbe::EExpression& expr); /** * Generates an EExpression that checks whether the input expression is null, missing, or |