diff options
-rw-r--r-- | jstests/aggregation/expressions/date_from_string.js | 236 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 73 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.h | 19 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_test.cpp | 39 | ||||
-rw-r--r-- | src/mongo/db/query/datetime/date_time_support.cpp | 46 | ||||
-rw-r--r-- | src/mongo/db/query/datetime/date_time_support.h | 5 |
6 files changed, 0 insertions, 418 deletions
diff --git a/jstests/aggregation/expressions/date_from_string.js b/jstests/aggregation/expressions/date_from_string.js deleted file mode 100644 index a6fc7b9b09b..00000000000 --- a/jstests/aggregation/expressions/date_from_string.js +++ /dev/null @@ -1,236 +0,0 @@ -load("jstests/aggregation/extras/utils.js"); // For assertErrorCode - -(function() { - "use strict"; - - const coll = db.date_from_string; - - /* --------------------------------------------------------------------------------------- */ - /* Normal format tests. */ - - coll.drop(); - assert.writeOK(coll.insert({_id: 0})); - - let testCases = [ - {expect: "2017-07-04T11:56:02Z", inputString: "2017-07-04T11:56:02Z"}, - {expect: "2017-07-04T11:56:02.813Z", inputString: "2017-07-04T11:56:02.813Z"}, - {expect: "2017-07-04T11:56:02.810Z", inputString: "2017-07-04T11:56:02.81Z"}, - {expect: "2017-07-04T11:56:02.800Z", inputString: "2017-07-04T11:56:02.8Z"}, - {expect: "2017-07-04T11:56:02Z", inputString: "2017-07-04T11:56.02"}, - {expect: "2017-07-04T11:56:02.813Z", inputString: "2017-07-04T11:56.02.813"}, - {expect: "2017-07-04T11:56:02.810Z", inputString: "2017-07-04T11:56.02.81"}, - {expect: "2017-07-04T11:56:02.800Z", inputString: "2017-07-04T11:56.02.8"}, - ]; - testCases.forEach(function(testCase) { - assert.eq( - [{_id: 0, date: ISODate(testCase.expect)}], - coll.aggregate( - {$project: {date: {'$dateFromString': {"dateString": testCase.inputString}}}}) - .toArray(), - tojson(testCase)); - }); - - /* --------------------------------------------------------------------------------------- */ - /* Normal format tests from data. */ - - coll.drop(); - assert.writeOK(coll.insert([ - {_id: 0, dateString: "2017-07-06T12:35:37Z"}, - {_id: 1, dateString: "2017-07-06T12:35:37.513Z"}, - {_id: 2, dateString: "2017-07-06T12:35:37"}, - {_id: 3, dateString: "2017-07-06T12:35:37.513"}, - {_id: 4, dateString: "1960-07-10T12:10:37.448"}, - ])); - - assert.eq( - [ - {"_id": 0, "date": ISODate("2017-07-06T12:35:37Z")}, - {"_id": 1, "date": ISODate("2017-07-06T12:35:37.513Z")}, - {"_id": 2, "date": ISODate("2017-07-06T12:35:37Z")}, - {"_id": 3, "date": ISODate("2017-07-06T12:35:37.513Z")}, - {"_id": 4, "date": ISODate("1960-07-10T12:10:37.448Z")}, - ], - coll.aggregate([ - { - $project: {date: {'$dateFromString': {dateString: "$dateString"}}}, - }, - {$sort: {_id: 1}} - ]) - .toArray()); - - assert.eq( - [ - {"_id": 0, "date": new Date(1499344537000)}, - {"_id": 1, "date": new Date(1499344537513)}, - {"_id": 2, "date": new Date(1499344537000)}, - {"_id": 3, "date": new Date(1499344537513)}, - {"_id": 4, "date": new Date(-299072962552)}, - ], - coll.aggregate([ - { - $project: {date: {'$dateFromString': {dateString: "$dateString"}}}, - }, - {$sort: {_id: 1}} - ]) - .toArray()); - - /* --------------------------------------------------------------------------------------- */ - /* BI format tests. */ - - coll.drop(); - assert.writeOK(coll.insert({_id: 0})); - - let pipelines = [ - { - expect: "2017-01-01T00:00:00Z", - pipeline: {$project: {date: {'$dateFromString': {"dateString": "2017-01-01 00:00:00"}}}} - }, - { - expect: "2017-07-01T00:00:00Z", - pipeline: {$project: {date: {'$dateFromString': {"dateString": "2017-07-01 00:00:00"}}}} - }, - { - expect: "2017-07-06T00:00:00Z", - pipeline: {$project: {date: {'$dateFromString': {"dateString": "2017-07-06"}}}} - }, - { - expect: "2017-07-06T00:00:00Z", - pipeline: {$project: {date: {'$dateFromString': {"dateString": "2017-07-06 00:00:00"}}}} - }, - { - expect: "2017-07-06T11:00:00Z", - pipeline: {$project: {date: {'$dateFromString': {"dateString": "2017-07-06 11:00:00"}}}} - }, - { - expect: "2017-07-06T11:36:00Z", - pipeline: {$project: {date: {'$dateFromString': {"dateString": "2017-07-06 11:36:00"}}}} - }, - { - expect: "2017-07-06T11:36:54Z", - pipeline: {$project: {date: {'$dateFromString': {"dateString": "2017-07-06 11:36:54"}}}} - }, - ]; - pipelines.forEach(function(pipeline) { - assert.eq([{_id: 0, date: ISODate(pipeline.expect)}], - coll.aggregate(pipeline.pipeline).toArray(), - tojson(pipeline)); - }); - - /* --------------------------------------------------------------------------------------- */ - /* BI format tests from data. */ - - coll.drop(); - assert.writeOK(coll.insert([ - {_id: 0, dateString: "2017-01-01 00:00:00"}, - {_id: 1, dateString: "2017-07-01 00:00:00"}, - {_id: 2, dateString: "2017-07-06"}, - {_id: 3, dateString: "2017-07-06 00:00:00"}, - {_id: 4, dateString: "2017-07-06 11:00:00"}, - {_id: 5, dateString: "2017-07-06 11:36:00"}, - {_id: 6, dateString: "2017-07-06 11:36:54"}, - ])); - - assert.eq( - [ - {"_id": 0, "date": ISODate("2017-01-01T00:00:00Z")}, - {"_id": 1, "date": ISODate("2017-07-01T00:00:00Z")}, - {"_id": 2, "date": ISODate("2017-07-06T00:00:00Z")}, - {"_id": 3, "date": ISODate("2017-07-06T00:00:00Z")}, - {"_id": 4, "date": ISODate("2017-07-06T11:00:00Z")}, - {"_id": 5, "date": ISODate("2017-07-06T11:36:00Z")}, - {"_id": 6, "date": ISODate("2017-07-06T11:36:54Z")} - ], - coll.aggregate([ - { - $project: {date: {'$dateFromString': {dateString: "$dateString"}}}, - }, - {$sort: {_id: 1}} - ]) - .toArray()); - - /* --------------------------------------------------------------------------------------- */ - /* Wacky format tests from data. */ - - coll.drop(); - assert.writeOK(coll.insert([ - {_id: 0, dateString: "July 4th, 2017"}, - {_id: 1, dateString: "July 4th, 2017 12:39:30 BST"}, - {_id: 2, dateString: "July 4th, 2017 11am"}, - {_id: 3, dateString: "July 4th, 2017 12pm"}, - {_id: 4, dateString: "7/4/17"}, - {_id: 5, dateString: "04-07-2017"}, - {_id: 6, dateString: "2017-Jul-04 noon"}, - {_id: 7, dateString: "2017-07-04 12:48:07 GMT+0545"}, - {_id: 8, dateString: "2017-07-04 12:48:07 GMT-0200"}, - ])); - - assert.eq( - [ - {"_id": 0, "date": ISODate("2017-07-04T00:00:00Z")}, - {"_id": 1, "date": ISODate("2017-07-04T11:39:30Z")}, - {"_id": 2, "date": ISODate("2017-07-04T11:00:00Z")}, - {"_id": 3, "date": ISODate("2017-07-04T12:00:00Z")}, - {"_id": 4, "date": ISODate("2017-07-04T00:00:00Z")}, - {"_id": 5, "date": ISODate("2017-07-04T00:00:00Z")}, - {"_id": 6, "date": ISODate("2017-07-04T12:00:00Z")}, - {"_id": 7, "date": ISODate("2017-07-04T07:03:07Z")}, - {"_id": 8, "date": ISODate("2017-07-04T14:48:07Z")}, - ], - coll.aggregate([ - { - $project: {date: {'$dateFromString': {dateString: "$dateString"}}}, - }, - {$sort: {_id: 1}} - ]) - .toArray()); - - /* --------------------------------------------------------------------------------------- */ - /* Testing whether it throws the right assert for missing elements of a date/time string. */ - - coll.drop(); - - assert.writeOK(coll.insert([ - {_id: 0}, - ])); - - pipelines = [ - [{'$project': {date: {'$dateFromString': {dateString: "July 4th"}}}}], - [{'$project': {date: {'$dateFromString': {dateString: "2017, 12:50:53"}}}}], - ]; - - pipelines.forEach(function(pipeline) { - assertErrorCode(coll, pipeline, 40545, tojson(pipeline)); - }); - - /* --------------------------------------------------------------------------------------- */ - /* NULL returns. */ - - coll.drop(); - assert.writeOK(coll.insert([ - {_id: 0, date: new ISODate("2017-06-19T15:13:25.713Z")}, - {_id: 1, date: new ISODate("2017-06-19T15:13:25.713Z"), tz: null}, - {_id: 2, date: new ISODate("2017-06-19T15:13:25.713Z"), tz: undefined}, - ])); - - pipelines = [ - [{$project: {date: {'$dateFromString': {"dateString": "$tz"}}}}, {$sort: {_id: 1}}], - ]; - pipelines.forEach(function(pipeline) { - assert.eq([{_id: 0, date: null}, {_id: 1, date: null}, {_id: 2, date: null}], - coll.aggregate(pipeline).toArray(), - tojson(pipeline)); - }); - - /* --------------------------------------------------------------------------------------- */ - /* Parse errors. */ - - let pipeline = {$project: {date: {'$dateFromString': "no-object"}}}; - assertErrorCode(coll, pipeline, 40540); - - pipeline = {$project: {date: {'$dateFromString': {"unknown": "$tz"}}}}; - assertErrorCode(coll, pipeline, 40541); - - pipeline = {$project: {date: {'$dateFromString': {"dateString": 5}}}}; - assertErrorCode(coll, pipeline, 40543); - -})(); diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 8b3b15143b8..798d05ca285 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -1314,79 +1314,6 @@ void ExpressionDateFromParts::addDependencies(DepsTracker* deps) const { } } -/* ---------------------- ExpressionDateFromString --------------------- */ - -REGISTER_EXPRESSION(dateFromString, ExpressionDateFromString::parse); -intrusive_ptr<Expression> ExpressionDateFromString::parse( - const boost::intrusive_ptr<ExpressionContext>& expCtx, - BSONElement expr, - const VariablesParseState& vps) { - - uassert(40540, - str::stream() << "$dateFromString only supports an object as an argument, found: " - << typeName(expr.type()), - expr.type() == BSONType::Object); - - BSONElement dateStringElem; - const BSONObj args = expr.embeddedObject(); - for (auto&& arg : args) { - if (arg.fieldNameStringData() == "dateString"_sd) { - dateStringElem = arg; - } else { - uasserted(40541, - str::stream() << "Unrecognized argument to $dateFromString: " - << arg.fieldName()); - } - } - - uassert(40542, "Missing 'dateString' parameter to $dateFromString", dateStringElem); - - return new ExpressionDateFromString(expCtx, parseOperand(expCtx, dateStringElem, vps)); -} - -ExpressionDateFromString::ExpressionDateFromString( - const boost::intrusive_ptr<ExpressionContext>& expCtx, intrusive_ptr<Expression> dateString) - : Expression(expCtx), _dateString(dateString) {} - -intrusive_ptr<Expression> ExpressionDateFromString::optimize() { - _dateString = _dateString->optimize(); - - if (dynamic_cast<ExpressionConstant*>(_dateString.get())) { - // Everything is a constant, so we can turn into a constant. - return ExpressionConstant::create(getExpressionContext(), evaluate(Document{})); - } - return this; -} - -Value ExpressionDateFromString::serialize(bool explain) const { - return Value( - Document{{"$dateFromString", Document{{"dateString", _dateString->serialize(explain)}}}}); -} - -Value ExpressionDateFromString::evaluate(const Document& root) const { - const Value dateString = _dateString->evaluate(root); - - if (dateString.nullish()) { - return Value(BSONNULL); - } - - uassert(40543, - str::stream() << "$dateFromString requires that 'dateString' be a string, found: " - << typeName(dateString.getType()) - << " with value " - << dateString.toString(), - dateString.getType() == BSONType::String); - const std::string& dateTimeString = dateString.getString(); - - auto tzdb = TimeZoneDatabase::get(getExpressionContext()->opCtx->getServiceContext()); - - return Value(tzdb->fromString(dateTimeString)); -} - -void ExpressionDateFromString::addDependencies(DepsTracker* deps) const { - _dateString->addDependencies(deps); -} - /* ---------------------- ExpressionDateToParts ----------------------- */ REGISTER_EXPRESSION(dateToParts, ExpressionDateToParts::parse); diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h index c7889305aa0..aad2ced9b55 100644 --- a/src/mongo/db/pipeline/expression.h +++ b/src/mongo/db/pipeline/expression.h @@ -828,25 +828,6 @@ private: typedef ExpressionFixedArity<ExpressionCond, 3> Base; }; -class ExpressionDateFromString final : public Expression { -public: - boost::intrusive_ptr<Expression> optimize() final; - Value serialize(bool explain) const final; - Value evaluate(const Document&) const final; - void addDependencies(DepsTracker*) const final; - - static boost::intrusive_ptr<Expression> parse( - const boost::intrusive_ptr<ExpressionContext>& expCtx, - BSONElement expr, - const VariablesParseState& vps); - -private: - ExpressionDateFromString(const boost::intrusive_ptr<ExpressionContext>& expCtx, - boost::intrusive_ptr<Expression> dateString); - - boost::intrusive_ptr<Expression> _dateString; -}; - class ExpressionDateFromParts final : public Expression { public: boost::intrusive_ptr<Expression> optimize() final; diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp index 3ae780066e0..f89735aff56 100644 --- a/src/mongo/db/pipeline/expression_test.cpp +++ b/src/mongo/db/pipeline/expression_test.cpp @@ -4884,45 +4884,6 @@ TEST_F(DateExpressionTest, DoesResultInNullIfGivenNullishInput) { } // namespace DateExpressionsTest -namespace ExpressionDateFromStringTest { - -// This provides access to an ExpressionContext that has a valid ServiceContext with a -// TimeZoneDatabase via getExpCtx(), but we'll use a different name for this test suite. -using ExpressionDateFromStringTest = AggregationContextFixture; - -TEST_F(ExpressionDateFromStringTest, SerializesToObjectSyntax) { - auto expCtx = getExpCtx(); - - // Test that it serializes to the full format if given an object specification. - BSONObj spec = BSON("$dateFromString" << BSON("dateString" - << "2017-07-04T13:06:44Z")); - auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - auto expectedSerialization = Value( - Document{{"$dateFromString", - Document{{"dateString", Document{{"$const", "2017-07-04T13:06:44Z"_sd}}}}}}); - ASSERT_VALUE_EQ(dateExp->serialize(true), expectedSerialization); - ASSERT_VALUE_EQ(dateExp->serialize(false), expectedSerialization); -} - -TEST_F(ExpressionDateFromStringTest, OptimizesToConstantIfAllInputsAreConstant) { - auto expCtx = getExpCtx(); - auto spec = BSON("$dateFromString" << BSON("dateString" - << "2017-07-04T13:09:57Z")); - auto dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT(dynamic_cast<ExpressionConstant*>(dateExp->optimize().get())); - - Date_t dateVal = Date_t::fromMillisSinceEpoch(1499173797000); - ASSERT_VALUE_EQ(Value(dateVal), dateExp->evaluate(Document{})); - - // Test that it does *not* become a constant if dateString is not a constant. - spec = BSON("$dateFromString" << BSON("dateString" - << "$date")); - dateExp = Expression::parseExpression(expCtx, spec, expCtx->variablesParseState); - ASSERT_FALSE(dynamic_cast<ExpressionConstant*>(dateExp->optimize().get())); -} - -} // namespace ExpressionDateFromStringTest - class All : public Suite { public: All() : Suite("expression") {} diff --git a/src/mongo/db/query/datetime/date_time_support.cpp b/src/mongo/db/query/datetime/date_time_support.cpp index 88ba04d5549..1356356665d 100644 --- a/src/mongo/db/query/datetime/date_time_support.cpp +++ b/src/mongo/db/query/datetime/date_time_support.cpp @@ -122,52 +122,6 @@ TimeZone TimeZoneDatabase::utcZone() { return TimeZone{nullptr}; } -extern "C" { -static timelib_tzinfo* timezonedatabase_gettzinfowrapper(char* tz_id, - const _timelib_tzdb* db, - int* error) { - uasserted( - 40544, - str::stream() - << "passing a time zone identifier as part of the string is not allowed, found: \"" - << tz_id - << "\""); - - return nullptr; -} - -} // extern "C" - -Date_t TimeZoneDatabase::fromString(StringData dateString) const { - std::unique_ptr<timelib_time, TimeZone::TimelibTimeDeleter> t( - timelib_strtotime(const_cast<char*>(dateString.toString().c_str()), - dateString.size(), - nullptr, - _timeZoneDatabase.get(), - timezonedatabase_gettzinfowrapper)); - - // If the time portion is fully missing, initialize to 0. This allows for the '%Y-%m-%d' format - // to be passed too, which is what the BI connector may request - if (t->h == TIMELIB_UNSET && t->i == TIMELIB_UNSET && t->s == TIMELIB_UNSET) { - t->h = t->i = t->s = t->f = 0; - } - - if (t->y == TIMELIB_UNSET || t->m == TIMELIB_UNSET || t->d == TIMELIB_UNSET || - t->h == TIMELIB_UNSET || t->i == TIMELIB_UNSET || t->s == TIMELIB_UNSET) { - uasserted(40545, - str::stream() - << "an incomplete date/time string has been found, with elements missing: \"" - << dateString - << "\""); - } - - timelib_update_ts(t.get(), nullptr); - timelib_unixtime2local(t.get(), t->sse); - - return Date_t::fromMillisSinceEpoch((static_cast<double>(t->sse) + static_cast<double>(t->f)) * - 1000); -} - 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 0482cb70827..6f1e43aafad 100644 --- a/src/mongo/db/query/datetime/date_time_support.h +++ b/src/mongo/db/query/datetime/date_time_support.h @@ -303,11 +303,6 @@ public: std::unique_ptr<TimeZoneDatabase> timeZoneDatabase); /** - * Use the timezone database to create a Date_t from a string. - */ - Date_t fromString(StringData dateString) const; - - /** * Returns a TimeZone object representing the UTC time zone. */ static TimeZone utcZone(); |