summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorClaire Childs <claire.childs@mongodb.com>2020-09-24 19:09:00 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-10-09 22:39:40 +0000
commita8dc889504e467595dd3337913a8f346aa031ad8 (patch)
tree1e24dc574c36a128f5cdb9c2189f1b2874af3bb8 /src/mongo/db
parent393c2ad49763c9342a5d1a7cac85cd25e69785fe (diff)
downloadmongo-a8dc889504e467595dd3337913a8f346aa031ad8.tar.gz
SERVER-49977 support $dateToParts in SBE
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/exec/sbe/SConscript2
-rw-r--r--src/mongo/db/exec/sbe/expressions/expression.cpp5
-rw-r--r--src/mongo/db/exec/sbe/expressions/sbe_date_to_parts_test.cpp126
-rw-r--r--src/mongo/db/exec/sbe/expressions/sbe_iso_date_to_parts_test.cpp127
-rw-r--r--src/mongo/db/exec/sbe/vm/vm.cpp135
-rw-r--r--src/mongo/db/exec/sbe/vm/vm.h6
-rw-r--r--src/mongo/db/query/datetime/date_time_support.cpp5
-rw-r--r--src/mongo/db/query/datetime/date_time_support.h5
-rw-r--r--src/mongo/db/query/sbe_stage_builder_expression.cpp106
9 files changed, 516 insertions, 1 deletions
diff --git a/src/mongo/db/exec/sbe/SConscript b/src/mongo/db/exec/sbe/SConscript
index fd64dd23062..787797e83c2 100644
--- a/src/mongo/db/exec/sbe/SConscript
+++ b/src/mongo/db/exec/sbe/SConscript
@@ -90,6 +90,8 @@ env.CppUnitTest(
'expressions/sbe_bson_size_test.cpp',
'expressions/sbe_coerce_to_string_test.cpp',
'expressions/sbe_concat_test.cpp',
+ 'expressions/sbe_date_to_parts_test.cpp',
+ 'expressions/sbe_iso_date_to_parts_test.cpp',
'expressions/sbe_is_member_builtin_test.cpp',
'expressions/sbe_index_of_test.cpp',
'expressions/sbe_to_upper_to_lower_test.cpp',
diff --git a/src/mongo/db/exec/sbe/expressions/expression.cpp b/src/mongo/db/exec/sbe/expressions/expression.cpp
index b752de92e2d..1d8d43c3299 100644
--- a/src/mongo/db/exec/sbe/expressions/expression.cpp
+++ b/src/mongo/db/exec/sbe/expressions/expression.cpp
@@ -349,6 +349,10 @@ struct BuiltinFn {
*/
static stdx::unordered_map<std::string, BuiltinFn> kBuiltinFunctions = {
{"dateParts", BuiltinFn{[](size_t n) { return n == 9; }, vm::Builtin::dateParts, false}},
+ {"dateToParts",
+ BuiltinFn{[](size_t n) { return n == 3 || n == 4; }, vm::Builtin::dateToParts, false}},
+ {"isoDateToParts",
+ BuiltinFn{[](size_t n) { return n == 3 || n == 4; }, vm::Builtin::isoDateToParts, false}},
{"datePartsWeekYear",
BuiltinFn{[](size_t n) { return n == 9; }, vm::Builtin::datePartsWeekYear, false}},
{"split", BuiltinFn{[](size_t n) { return n == 2; }, vm::Builtin::split, false}},
@@ -400,6 +404,7 @@ static stdx::unordered_map<std::string, BuiltinFn> kBuiltinFunctions = {
BuiltinFn{[](size_t n) { return n == 3 || n == 4; }, vm::Builtin::indexOfBytes, false}},
{"indexOfCP",
BuiltinFn{[](size_t n) { return n == 3 || n == 4; }, vm::Builtin::indexOfCP, false}},
+ {"isTimezone", BuiltinFn{[](size_t n) { return n == 2; }, vm::Builtin::isTimezone, false}},
};
/**
diff --git a/src/mongo/db/exec/sbe/expressions/sbe_date_to_parts_test.cpp b/src/mongo/db/exec/sbe/expressions/sbe_date_to_parts_test.cpp
new file mode 100644
index 00000000000..117a8e86264
--- /dev/null
+++ b/src/mongo/db/exec/sbe/expressions/sbe_date_to_parts_test.cpp
@@ -0,0 +1,126 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/bson/oid.h"
+#include "mongo/db/exec/sbe/expression_test_base.h"
+#include "mongo/db/query/datetime/date_time_support.h"
+
+namespace mongo::sbe {
+
+class SBEDateToPartsTest : public EExpressionTestFixture {
+public:
+ void runAndAssertNothing(const vm::CodeFragment* compiledExpr) {
+ auto [runTag, runVal] = runCompiledExpression(compiledExpr);
+ value::ValueGuard guard(runTag, runVal);
+ ASSERT_EQUALS(runTag, sbe::value::TypeTags::Nothing);
+ ASSERT_EQUALS(runVal, 0);
+ }
+
+ void runAndAssertExpression(const vm::CodeFragment* compiledExpr,
+ const int32_t& year,
+ const int32_t& month,
+ const int32_t& day,
+ const int32_t& hour,
+ const int32_t& minute,
+ const int32_t& second,
+ const int32_t& millisecond) {
+ auto [runTag, runVal] = runCompiledExpression(compiledExpr);
+ value::ValueGuard guard(runTag, runVal);
+ auto obj = value::getObjectView(runVal);
+
+ auto [yearTag, yearVal] = obj->getField("year");
+ ASSERT_EQUALS(yearTag, value::TypeTags::NumberInt32);
+ ASSERT_EQUALS(year, value::bitcastTo<int32_t>(yearVal));
+
+ auto [monthTag, monthVal] = obj->getField("month");
+ ASSERT_EQUALS(monthTag, value::TypeTags::NumberInt32);
+ ASSERT_EQUALS(month, value::bitcastTo<int32_t>(monthVal));
+
+ auto [dayTag, dayVal] = obj->getField("day");
+ ASSERT_EQUALS(dayTag, value::TypeTags::NumberInt32);
+ ASSERT_EQUALS(day, value::bitcastTo<int32_t>(dayVal));
+
+ auto [hourTag, hourVal] = obj->getField("hour");
+ ASSERT_EQUALS(hourTag, value::TypeTags::NumberInt32);
+ ASSERT_EQUALS(hour, value::bitcastTo<int32_t>(hourVal));
+
+ auto [minuteTag, minuteVal] = obj->getField("minute");
+ ASSERT_EQUALS(minuteTag, value::TypeTags::NumberInt32);
+ ASSERT_EQUALS(minute, value::bitcastTo<int32_t>(minuteVal));
+
+ auto [secondTag, secondVal] = obj->getField("second");
+ ASSERT_EQUALS(secondTag, value::TypeTags::NumberInt32);
+ ASSERT_EQUALS(second, value::bitcastTo<int32_t>(secondVal));
+
+ auto [millisecondTag, millisecondVal] = obj->getField("millisecond");
+ ASSERT_EQUALS(millisecondTag, value::TypeTags::NumberInt32);
+ ASSERT_EQUALS(millisecond, value::bitcastTo<int32_t>(millisecondVal));
+ }
+};
+
+TEST_F(SBEDateToPartsTest, BasicDateToParts) {
+ value::OwnedValueAccessor timezoneDBAccessor;
+ auto timezoneDBSlot = bindAccessor(&timezoneDBAccessor);
+ value::OwnedValueAccessor dateAccessor;
+ auto dateSlot = bindAccessor(&dateAccessor);
+ value::OwnedValueAccessor timezoneAccessor;
+ auto timezoneSlot = bindAccessor(&timezoneAccessor);
+
+ auto dateToPartsExpr = sbe::makeE<sbe::EFunction>("dateToParts",
+ sbe::makeEs(makeE<EVariable>(timezoneDBSlot),
+ makeE<EVariable>(dateSlot),
+ makeE<EVariable>(timezoneSlot)));
+ auto compiledDateToParts = compileExpression(*dateToPartsExpr);
+
+ // Test $dateToParts returns the correct date parts.
+ TimeZoneDatabase* tzdb = new TimeZoneDatabase();
+ timezoneDBAccessor.reset(value::TypeTags::timeZoneDB,
+ value::bitcastFrom<TimeZoneDatabase*>(tzdb));
+ dateAccessor.reset(value::TypeTags::Date, value::bitcastFrom<int64_t>(21929999));
+ auto [timezoneTag, timezoneVal] = value::makeNewString("UTC");
+ timezoneAccessor.reset(timezoneTag, timezoneVal);
+ runAndAssertExpression(compiledDateToParts.get(), 1970, 1, 1, 6, 5, 29, 999);
+
+ // Test $dateToParts returns Nothing with invalid date.
+ dateAccessor.reset(value::TypeTags::NumberInt64, value::bitcastFrom<int64_t>(42));
+ runAndAssertNothing(compiledDateToParts.get());
+
+ // Test $dateToParts returns Nothing with invalid timezone.
+ dateAccessor.reset(value::TypeTags::Date, value::bitcastFrom<int64_t>(21929999));
+ timezoneAccessor.reset(value::TypeTags::NumberInt64, value::bitcastFrom<int64_t>(42));
+ runAndAssertNothing(compiledDateToParts.get());
+
+ // Test $dateToParts returns Nothing with invalid timezoneDB.
+ timezoneAccessor.reset(timezoneTag, timezoneVal);
+ timezoneDBAccessor.reset(value::TypeTags::NumberInt64, value::bitcastFrom<int64_t>(42));
+ runAndAssertNothing(compiledDateToParts.get());
+ delete tzdb;
+}
+
+} // namespace mongo::sbe
diff --git a/src/mongo/db/exec/sbe/expressions/sbe_iso_date_to_parts_test.cpp b/src/mongo/db/exec/sbe/expressions/sbe_iso_date_to_parts_test.cpp
new file mode 100644
index 00000000000..090bccf50f3
--- /dev/null
+++ b/src/mongo/db/exec/sbe/expressions/sbe_iso_date_to_parts_test.cpp
@@ -0,0 +1,127 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/bson/oid.h"
+#include "mongo/db/exec/sbe/expression_test_base.h"
+#include "mongo/db/query/datetime/date_time_support.h"
+
+namespace mongo::sbe {
+
+class SBEIsoDateToPartsTest : public EExpressionTestFixture {
+public:
+ void runAndAssertNothing(const vm::CodeFragment* compiledExpr) {
+ auto [runTag, runVal] = runCompiledExpression(compiledExpr);
+ value::ValueGuard guard(runTag, runVal);
+ ASSERT_EQUALS(runTag, sbe::value::TypeTags::Nothing);
+ ASSERT_EQUALS(runVal, 0);
+ }
+
+ void runAndAssertExpression(const vm::CodeFragment* compiledExpr,
+ const int32_t& isoWeekYear,
+ const int32_t& isoWeek,
+ const int32_t& isoDayOfWeek,
+ const int32_t& hour,
+ const int32_t& minute,
+ const int32_t& second,
+ const int32_t& millisecond) {
+ auto [runTag, runVal] = runCompiledExpression(compiledExpr);
+ value::ValueGuard guard(runTag, runVal);
+ auto obj = value::getObjectView(runVal);
+
+ auto [isoWeekYearTag, isoWeekYearVal] = obj->getField("isoWeekYear");
+ ASSERT_EQUALS(isoWeekYearTag, value::TypeTags::NumberInt32);
+ ASSERT_EQUALS(isoWeekYear, value::bitcastTo<int32_t>(isoWeekYearVal));
+
+ auto [isoWeekTag, isoWeekVal] = obj->getField("isoWeek");
+ ASSERT_EQUALS(isoWeekTag, value::TypeTags::NumberInt32);
+ ASSERT_EQUALS(isoWeek, value::bitcastTo<int32_t>(isoWeekVal));
+
+ auto [isoDayOfWeekTag, isoDayOfWeekVal] = obj->getField("isoDayOfWeek");
+ ASSERT_EQUALS(isoDayOfWeekTag, value::TypeTags::NumberInt32);
+ ASSERT_EQUALS(isoDayOfWeek, value::bitcastTo<int32_t>(isoDayOfWeekVal));
+
+ auto [hourTag, hourVal] = obj->getField("hour");
+ ASSERT_EQUALS(hourTag, value::TypeTags::NumberInt32);
+ ASSERT_EQUALS(hour, value::bitcastTo<int32_t>(hourVal));
+
+ auto [minuteTag, minuteVal] = obj->getField("minute");
+ ASSERT_EQUALS(minuteTag, value::TypeTags::NumberInt32);
+ ASSERT_EQUALS(minute, value::bitcastTo<int32_t>(minuteVal));
+
+ auto [secondTag, secondVal] = obj->getField("second");
+ ASSERT_EQUALS(secondTag, value::TypeTags::NumberInt32);
+ ASSERT_EQUALS(second, value::bitcastTo<int32_t>(secondVal));
+
+ auto [millisecondTag, millisecondVal] = obj->getField("millisecond");
+ ASSERT_EQUALS(millisecondTag, value::TypeTags::NumberInt32);
+ ASSERT_EQUALS(millisecond, value::bitcastTo<int32_t>(millisecondVal));
+ }
+};
+
+TEST_F(SBEIsoDateToPartsTest, BasicIsoDateToParts) {
+ value::OwnedValueAccessor timezoneDBAccessor;
+ auto timezoneDBSlot = bindAccessor(&timezoneDBAccessor);
+ value::OwnedValueAccessor dateAccessor;
+ auto dateSlot = bindAccessor(&dateAccessor);
+ value::OwnedValueAccessor timezoneAccessor;
+ auto timezoneSlot = bindAccessor(&timezoneAccessor);
+
+ auto isoDateToPartsExpr =
+ sbe::makeE<sbe::EFunction>("isoDateToParts",
+ sbe::makeEs(makeE<EVariable>(timezoneDBSlot),
+ makeE<EVariable>(dateSlot),
+ makeE<EVariable>(timezoneSlot)));
+ auto compiledIsoDateToParts = compileExpression(*isoDateToPartsExpr);
+
+ // Test $DateToParts with iso8601 returns the correct date parts.
+ TimeZoneDatabase* tzdb = new TimeZoneDatabase();
+ timezoneDBAccessor.reset(value::TypeTags::timeZoneDB,
+ value::bitcastFrom<TimeZoneDatabase*>(tzdb));
+ dateAccessor.reset(value::TypeTags::Date, value::bitcastFrom<int64_t>(21929999));
+ auto [timezoneTag, timezoneVal] = value::makeNewString("UTC");
+ timezoneAccessor.reset(timezoneTag, timezoneVal);
+ runAndAssertExpression(compiledIsoDateToParts.get(), 1970, 1, 4, 6, 5, 29, 999);
+
+ // Test $DateToParts with iso8601 flag and invalid date returns Nothing.
+ dateAccessor.reset(value::TypeTags::NumberInt64, value::bitcastFrom<int64_t>(42));
+ runAndAssertNothing(compiledIsoDateToParts.get());
+
+ // Test $DateToParts with iso8601 flag and invalid timezone returns Nothing.
+ dateAccessor.reset(value::TypeTags::Date, value::bitcastFrom<int64_t>(21929999));
+ timezoneAccessor.reset(value::TypeTags::NumberInt64, value::bitcastFrom<int64_t>(42));
+ runAndAssertNothing(compiledIsoDateToParts.get());
+
+ // Test $DateToParts with iso8601 flag and invalid timezoneDB returns Nothing.
+ timezoneAccessor.reset(timezoneTag, timezoneVal);
+ timezoneDBAccessor.reset(value::TypeTags::NumberInt64, value::bitcastFrom<int64_t>(42));
+ runAndAssertNothing(compiledIsoDateToParts.get());
+ delete tzdb;
+}
+
+} // namespace mongo::sbe
diff --git a/src/mongo/db/exec/sbe/vm/vm.cpp b/src/mongo/db/exec/sbe/vm/vm.cpp
index f2f8dfb6d94..738ccb4ed92 100644
--- a/src/mongo/db/exec/sbe/vm/vm.cpp
+++ b/src/mongo/db/exec/sbe/vm/vm.cpp
@@ -35,6 +35,7 @@
#include <boost/algorithm/string.hpp>
#include <pcrecpp.h>
+#include "mongo/bson/oid.h"
#include "mongo/db/exec/sbe/values/bson.h"
#include "mongo/db/exec/sbe/values/value.h"
#include "mongo/db/query/datetime/date_time_support.h"
@@ -1146,6 +1147,117 @@ std::tuple<bool, value::TypeTags, value::Value> ByteCode::builtinDateWeekYear(ui
timezoneTuple);
}
+TimeZone getTimezone(value::TypeTags timezoneTag,
+ value::Value timezoneVal,
+ TimeZoneDatabase* timezoneDB) {
+ auto timezoneStr = value::getStringView(timezoneTag, timezoneVal);
+ if (timezoneStr == "") {
+ return timezoneDB->utcZone();
+ } else {
+ return timezoneDB->getTimeZone(StringData{timezoneStr.data(), timezoneStr.size()});
+ }
+}
+
+Date_t getDate(value::TypeTags dateTag, value::Value dateVal) {
+ switch (dateTag) {
+ case value::TypeTags::Date: {
+ return Date_t::fromMillisSinceEpoch(value::bitcastTo<int64_t>(dateVal));
+ }
+ case value::TypeTags::Timestamp: {
+ return Date_t::fromMillisSinceEpoch(
+ Timestamp(value::bitcastTo<uint64_t>(dateVal)).getSecs() * 1000LL);
+ }
+ case value::TypeTags::ObjectId: {
+ auto objIdBuf = value::getObjectIdView(dateVal);
+ auto objId = OID::from(objIdBuf);
+ return objId.asDateT();
+ }
+ case value::TypeTags::bsonObjectId: {
+ auto objIdBuf = value::getRawPointerView(dateVal);
+ auto objId = OID::from(objIdBuf);
+ return objId.asDateT();
+ }
+ default:
+ MONGO_UNREACHABLE;
+ }
+}
+
+std::tuple<bool, value::TypeTags, value::Value> ByteCode::builtinDateToParts(uint8_t arity) {
+ auto [timezoneDBOwn, timezoneDBTag, timezoneDBVal] = getFromStack(0);
+ if (timezoneDBTag != value::TypeTags::timeZoneDB) {
+ return {false, value::TypeTags::Nothing, 0};
+ }
+ auto timezoneDB = value::getTimeZoneDBView(timezoneDBVal);
+ auto [dateOwn, dateTag, dateVal] = getFromStack(1);
+
+ // Get timezone.
+ auto [timezoneOwn, timezoneTag, timezoneVal] = getFromStack(2);
+ if (!value::isString(timezoneTag)) {
+ return {false, value::TypeTags::Nothing, 0};
+ }
+ TimeZone timezone = getTimezone(timezoneTag, timezoneVal, timezoneDB);
+
+ // Get date.
+ if (dateTag != value::TypeTags::Date && dateTag != value::TypeTags::Timestamp &&
+ dateTag != value::TypeTags::ObjectId && dateTag != value::TypeTags::bsonObjectId) {
+ return {false, value::TypeTags::Nothing, 0};
+ }
+ Date_t date = getDate(dateTag, dateVal);
+
+ // Get date parts.
+ auto dateParts = timezone.dateParts(date);
+ auto [dateObjTag, dateObjVal] = value::makeNewObject();
+ value::ValueGuard guard{dateObjTag, dateObjVal};
+ auto dateObj = value::getObjectView(dateObjVal);
+ dateObj->push_back("year", value::TypeTags::NumberInt32, dateParts.year);
+ dateObj->push_back("month", value::TypeTags::NumberInt32, dateParts.month);
+ dateObj->push_back("day", value::TypeTags::NumberInt32, dateParts.dayOfMonth);
+ dateObj->push_back("hour", value::TypeTags::NumberInt32, dateParts.hour);
+ dateObj->push_back("minute", value::TypeTags::NumberInt32, dateParts.minute);
+ dateObj->push_back("second", value::TypeTags::NumberInt32, dateParts.second);
+ dateObj->push_back("millisecond", value::TypeTags::NumberInt32, dateParts.millisecond);
+ guard.reset();
+ return {true, dateObjTag, dateObjVal};
+}
+
+std::tuple<bool, value::TypeTags, value::Value> ByteCode::builtinIsoDateToParts(uint8_t arity) {
+ auto [timezoneDBOwn, timezoneDBTag, timezoneDBVal] = getFromStack(0);
+ if (timezoneDBTag != value::TypeTags::timeZoneDB) {
+ return {false, value::TypeTags::Nothing, 0};
+ }
+ auto timezoneDB = value::getTimeZoneDBView(timezoneDBVal);
+ auto [dateOwn, dateTag, dateVal] = getFromStack(1);
+
+ // Get timezone.
+ auto [timezoneOwn, timezoneTag, timezoneVal] = getFromStack(2);
+ if (!value::isString(timezoneTag)) {
+ return {false, value::TypeTags::Nothing, 0};
+ }
+ TimeZone timezone = getTimezone(timezoneTag, timezoneVal, timezoneDB);
+
+ // Get date.
+ if (dateTag != value::TypeTags::Date && dateTag != value::TypeTags::Timestamp &&
+ dateTag != value::TypeTags::ObjectId && dateTag != value::TypeTags::bsonObjectId) {
+ return {false, value::TypeTags::Nothing, 0};
+ }
+ Date_t date = getDate(dateTag, dateVal);
+
+ // Get date parts.
+ auto dateParts = timezone.dateIso8601Parts(date);
+ auto [dateObjTag, dateObjVal] = value::makeNewObject();
+ value::ValueGuard guard{dateObjTag, dateObjVal};
+ auto dateObj = value::getObjectView(dateObjVal);
+ dateObj->push_back("isoWeekYear", value::TypeTags::NumberInt32, dateParts.year);
+ dateObj->push_back("isoWeek", value::TypeTags::NumberInt32, dateParts.weekOfYear);
+ dateObj->push_back("isoDayOfWeek", value::TypeTags::NumberInt32, dateParts.dayOfWeek);
+ dateObj->push_back("hour", value::TypeTags::NumberInt32, dateParts.hour);
+ dateObj->push_back("minute", value::TypeTags::NumberInt32, dateParts.minute);
+ dateObj->push_back("second", value::TypeTags::NumberInt32, dateParts.second);
+ dateObj->push_back("millisecond", value::TypeTags::NumberInt32, dateParts.millisecond);
+ guard.reset();
+ return {true, dateObjTag, dateObjVal};
+}
+
std::tuple<bool, value::TypeTags, value::Value> ByteCode::builtinBitTestPosition(uint8_t arity) {
invariant(arity == 3);
@@ -1603,6 +1715,23 @@ std::tuple<bool, value::TypeTags, value::Value> ByteCode::builtinIndexOfCP(uint8
return {false, value::TypeTags::NumberInt32, value::bitcastFrom<int32_t>(-1)};
}
+std::tuple<bool, value::TypeTags, value::Value> ByteCode::builtinIsTimezone(uint8_t arity) {
+ auto [timezoneDBOwn, timezoneDBTag, timezoneDBVal] = getFromStack(0);
+ if (timezoneDBTag != value::TypeTags::timeZoneDB) {
+ return {false, value::TypeTags::Nothing, 0};
+ }
+ auto timezoneDB = value::getTimeZoneDBView(timezoneDBVal);
+ auto [timezoneOwn, timezoneTag, timezoneVal] = getFromStack(1);
+ if (!value::isString(timezoneTag)) {
+ return {false, value::TypeTags::Boolean, false};
+ }
+ auto timezoneStr = value::getStringView(timezoneTag, timezoneVal);
+ if (timezoneDB->isTimeZoneIdentifier((StringData{timezoneStr.data(), timezoneStr.size()}))) {
+ return {false, value::TypeTags::Boolean, true};
+ }
+ return {false, value::TypeTags::Boolean, false};
+}
+
std::tuple<bool, value::TypeTags, value::Value> ByteCode::dispatchBuiltin(Builtin f,
uint8_t arity) {
switch (f) {
@@ -1610,6 +1739,10 @@ std::tuple<bool, value::TypeTags, value::Value> ByteCode::dispatchBuiltin(Builti
return builtinDate(arity);
case Builtin::datePartsWeekYear:
return builtinDateWeekYear(arity);
+ case Builtin::dateToParts:
+ return builtinDateToParts(arity);
+ case Builtin::isoDateToParts:
+ return builtinIsoDateToParts(arity);
case Builtin::split:
return builtinSplit(arity);
case Builtin::regexMatch:
@@ -1694,6 +1827,8 @@ std::tuple<bool, value::TypeTags, value::Value> ByteCode::dispatchBuiltin(Builti
return builtinIndexOfBytes(arity);
case Builtin::indexOfCP:
return builtinIndexOfCP(arity);
+ case Builtin::isTimezone:
+ return builtinIsTimezone(arity);
}
MONGO_UNREACHABLE;
diff --git a/src/mongo/db/exec/sbe/vm/vm.h b/src/mongo/db/exec/sbe/vm/vm.h
index 3654c9d1190..41081cba611 100644
--- a/src/mongo/db/exec/sbe/vm/vm.h
+++ b/src/mongo/db/exec/sbe/vm/vm.h
@@ -175,6 +175,8 @@ enum class Builtin : uint8_t {
split,
regexMatch,
dateParts,
+ dateToParts,
+ isoDateToParts,
datePartsWeekYear,
dropFields,
newObj,
@@ -216,6 +218,7 @@ enum class Builtin : uint8_t {
isMember,
indexOfBytes,
indexOfCP,
+ isTimezone,
};
class CodeFragment {
@@ -490,6 +493,8 @@ private:
std::tuple<bool, value::TypeTags, value::Value> builtinSplit(uint8_t arity);
std::tuple<bool, value::TypeTags, value::Value> builtinDate(uint8_t arity);
std::tuple<bool, value::TypeTags, value::Value> builtinDateWeekYear(uint8_t arity);
+ std::tuple<bool, value::TypeTags, value::Value> builtinDateToParts(uint8_t arity);
+ std::tuple<bool, value::TypeTags, value::Value> builtinIsoDateToParts(uint8_t arity);
std::tuple<bool, value::TypeTags, value::Value> builtinRegexMatch(uint8_t arity);
std::tuple<bool, value::TypeTags, value::Value> builtinDropFields(uint8_t arity);
std::tuple<bool, value::TypeTags, value::Value> builtinNewObj(uint8_t arity);
@@ -531,6 +536,7 @@ private:
std::tuple<bool, value::TypeTags, value::Value> builtinIsMember(uint8_t arity);
std::tuple<bool, value::TypeTags, value::Value> builtinIndexOfBytes(uint8_t arity);
std::tuple<bool, value::TypeTags, value::Value> builtinIndexOfCP(uint8_t arity);
+ std::tuple<bool, value::TypeTags, value::Value> builtinIsTimezone(uint8_t arity);
std::tuple<bool, value::TypeTags, value::Value> dispatchBuiltin(Builtin f, uint8_t arity);
std::tuple<bool, value::TypeTags, value::Value> getFromStack(size_t offset) {
diff --git a/src/mongo/db/query/datetime/date_time_support.cpp b/src/mongo/db/query/datetime/date_time_support.cpp
index 199a3100453..a0c56878a89 100644
--- a/src/mongo/db/query/datetime/date_time_support.cpp
+++ b/src/mongo/db/query/datetime/date_time_support.cpp
@@ -347,6 +347,11 @@ boost::optional<Seconds> TimeZoneDatabase::parseUtcOffset(StringData offsetSpec)
return boost::none;
}
+bool TimeZoneDatabase::isTimeZoneIdentifier(StringData timeZoneId) const {
+ return (_timeZones.find(timeZoneId) != _timeZones.end()) ||
+ static_cast<bool>(parseUtcOffset(timeZoneId));
+}
+
TimeZone TimeZoneDatabase::getTimeZone(StringData timeZoneId) const {
auto tz = _timeZones.find(timeZoneId);
if (tz != _timeZones.end()) {
diff --git a/src/mongo/db/query/datetime/date_time_support.h b/src/mongo/db/query/datetime/date_time_support.h
index 8035c9239d5..a97a2f4b5fd 100644
--- a/src/mongo/db/query/datetime/date_time_support.h
+++ b/src/mongo/db/query/datetime/date_time_support.h
@@ -421,6 +421,11 @@ public:
static TimeZone utcZone();
/**
+ * Returns a boolean based on if 'timeZoneId' represents a valid timezone.
+ */
+ bool isTimeZoneIdentifier(StringData timeZoneId) const;
+
+ /**
* Returns a TimeZone object representing the zone given by 'timeZoneId', or boost::none if it
* was not a recognized time zone.
*/
diff --git a/src/mongo/db/query/sbe_stage_builder_expression.cpp b/src/mongo/db/query/sbe_stage_builder_expression.cpp
index e9989044064..5f1f84f04cc 100644
--- a/src/mongo/db/query/sbe_stage_builder_expression.cpp
+++ b/src/mongo/db/query/sbe_stage_builder_expression.cpp
@@ -1606,8 +1606,112 @@ public:
_context->pushExpr(sbe::makeE<sbe::ELocalBind>(
frameId, std::move(operands), std::move(computeDateOrNull)));
}
+
void visit(ExpressionDateToParts* expr) final {
- unsupportedExpression("$dateFromString");
+ auto frameId = _context->frameIdGenerator->generate();
+ auto children = expr->getChildren();
+ std::unique_ptr<sbe::EExpression> date, timezone, isoflag;
+ std::unique_ptr<sbe::EExpression> totalExprDateToParts;
+ std::vector<std::unique_ptr<sbe::EExpression>> args;
+ std::vector<std::unique_ptr<sbe::EExpression>> isoargs;
+ std::vector<std::unique_ptr<sbe::EExpression>> operands;
+ sbe::EVariable dateRef(frameId, 0);
+ sbe::EVariable timezoneRef(frameId, 1);
+ sbe::EVariable isoflagRef(frameId, 2);
+
+ // Initialize arguments with values from stack or default values.
+ if (children[2]) {
+ isoflag = _context->popExpr();
+ } else {
+ isoflag = sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Boolean, false);
+ }
+ if (children[1]) {
+ timezone = _context->popExpr();
+ } else {
+ auto [utcTag, utcVal] = sbe::value::makeNewString("UTC");
+ timezone = sbe::makeE<sbe::EConstant>(utcTag, utcVal);
+ }
+ if (children[0]) {
+ date = _context->popExpr();
+ } else {
+ _context->pushExpr(sbe::makeE<sbe::EFail>(ErrorCodes::Error{4997700},
+ "$dateToParts must include a date"));
+ return;
+ }
+
+ // Add timezoneDB to arguments.
+ args.push_back(
+ sbe::makeE<sbe::EVariable>(_context->runtimeEnvironment->getSlot("timeZoneDB"_sd)));
+ isoargs.push_back(
+ sbe::makeE<sbe::EVariable>(_context->runtimeEnvironment->getSlot("timeZoneDB"_sd)));
+
+ // Add date to arguments.
+ uint32_t dateTypeMask = (getBSONTypeMask(sbe::value::TypeTags::Date) |
+ getBSONTypeMask(sbe::value::TypeTags::Timestamp) |
+ getBSONTypeMask(sbe::value::TypeTags::ObjectId) |
+ getBSONTypeMask(sbe::value::TypeTags::bsonObjectId));
+ operands.push_back(std::move(date));
+ args.push_back(dateRef.clone());
+ isoargs.push_back(dateRef.clone());
+
+ // Add timezone to arguments.
+ operands.push_back(std::move(timezone));
+ args.push_back(timezoneRef.clone());
+ isoargs.push_back(timezoneRef.clone());
+
+ // Add iso8601 to arguments.
+ uint32_t isoTypeMask = getBSONTypeMask(sbe::value::TypeTags::Boolean);
+ operands.push_back(std::move(isoflag));
+ args.push_back(isoflagRef.clone());
+ isoargs.push_back(isoflagRef.clone());
+
+ // Determine whether to call dateToParts or isoDateToParts.
+ auto checkIsoflagValue = buildMultiBranchConditional(
+ CaseValuePair{sbe::makeE<sbe::EPrimBinary>(
+ sbe::EPrimBinary::eq,
+ isoflagRef.clone(),
+ sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Boolean, false)),
+ sbe::makeE<sbe::EFunction>("dateToParts", std::move(args))},
+ sbe::makeE<sbe::EFunction>("isoDateToParts", std::move(isoargs)));
+
+ // Check that each argument exists, is not null, and is the correct type.
+ auto totalDateToPartsFunc = buildMultiBranchConditional(
+ CaseValuePair{generateNullOrMissing(frameId, 1),
+ sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Null, 0)},
+ CaseValuePair{
+ sbe::makeE<sbe::EPrimUnary>(
+ sbe::EPrimUnary::logicNot,
+ sbe::makeE<sbe::EFunction>("isString", sbe::makeEs(timezoneRef.clone()))),
+ sbe::makeE<sbe::EFail>(ErrorCodes::Error{4997701},
+ "$dateToParts timezone must be a string")},
+ CaseValuePair{
+ sbe::makeE<sbe::EPrimUnary>(
+ sbe::EPrimUnary::logicNot,
+ sbe::makeE<sbe::EFunction>(
+ "isTimezone",
+ sbe::makeEs(sbe::makeE<sbe::EVariable>(
+ _context->runtimeEnvironment->getSlot("timeZoneDB"_sd)),
+ timezoneRef.clone()))),
+ sbe::makeE<sbe::EFail>(ErrorCodes::Error{4997704},
+ "$dateToParts timezone must be a valid timezone")},
+ CaseValuePair{generateNullOrMissing(frameId, 2),
+ sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Null, 0)},
+ CaseValuePair{sbe::makeE<sbe::EPrimUnary>(
+ sbe::EPrimUnary::logicNot,
+ sbe::makeE<sbe::ETypeMatch>(isoflagRef.clone(), isoTypeMask)),
+ sbe::makeE<sbe::EFail>(ErrorCodes::Error{4997702},
+ "$dateToParts iso8601 must be a boolean")},
+ CaseValuePair{generateNullOrMissing(frameId, 0),
+ sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Null, 0)},
+ CaseValuePair{
+ sbe::makeE<sbe::EPrimUnary>(
+ sbe::EPrimUnary::logicNot,
+ sbe::makeE<sbe::ETypeMatch>(dateRef.clone(), dateTypeMask)),
+ sbe::makeE<sbe::EFail>(ErrorCodes::Error{4997703},
+ "$dateToParts date must have the format of a date")},
+ std::move(checkIsoflagValue));
+ _context->pushExpr(sbe::makeE<sbe::ELocalBind>(
+ frameId, std::move(operands), std::move(totalDateToPartsFunc)));
}
void visit(ExpressionDateToString* expr) final {
unsupportedExpression("$dateFromString");