summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRui Liu <rui.liu@mongodb.com>2022-10-03 15:05:39 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-10-03 16:29:41 +0000
commit3192d7a4b18508e1941cb99aba80965950d51207 (patch)
treeb4449c94cfa7e24e3afeff1d059359791dfb213b
parentc345a3d78dd12fcc0adcf770552f7232a3e335a2 (diff)
downloadmongo-3192d7a4b18508e1941cb99aba80965950d51207.tar.gz
SERVER-70039 Implement optimization for $dateTrunc
-rw-r--r--buildscripts/gdb/mongo_printers.py17
-rw-r--r--jstests/libs/sbe_assert_error_override.js16
-rw-r--r--src/mongo/db/exec/sbe/expressions/expression.cpp37
-rw-r--r--src/mongo/db/exec/sbe/expressions/expression.h6
-rw-r--r--src/mongo/db/exec/sbe/expressions/sbe_date_trunc_test.cpp67
-rw-r--r--src/mongo/db/exec/sbe/values/arith_common.cpp21
-rw-r--r--src/mongo/db/exec/sbe/values/arith_common.h3
-rw-r--r--src/mongo/db/exec/sbe/vm/arith.cpp20
-rw-r--r--src/mongo/db/exec/sbe/vm/vm.cpp89
-rw-r--r--src/mongo/db/exec/sbe/vm/vm.h14
-rw-r--r--src/mongo/db/query/datetime/date_time_support.cpp21
-rw-r--r--src/mongo/db/query/datetime/date_time_support.h19
-rw-r--r--src/mongo/db/query/sbe_stage_builder_expression.cpp248
-rw-r--r--src/mongo/db/query/sbe_stage_builder_helpers.cpp8
-rw-r--r--src/mongo/db/query/sbe_stage_builder_helpers.h4
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