diff options
author | Shaun Verch <shaun.verch@mongodb.com> | 2014-05-13 17:01:04 -0400 |
---|---|---|
committer | Shaun Verch <shaun.verch@mongodb.com> | 2014-05-30 14:49:02 -0400 |
commit | ad9df9f23b3ff4c831ff642817bd2befb8bf4609 (patch) | |
tree | 6f05d60ec0fd9c3a10e979277867e9a1f92750cb | |
parent | c73f7c12cde09575b11db1dba91d852c724d076c (diff) | |
download | mongo-ad9df9f23b3ff4c831ff642817bd2befb8bf4609.tar.gz |
SERVER-13760 Do not call dateToISOString if date is not formatable
(cherry picked from commit 99fa4e6058a24d6c4d7fde19ce940719c5bbc210)
-rw-r--r-- | jstests/tool/exportimport_date.js | 46 | ||||
-rw-r--r-- | src/mongo/db/jsobj.cpp | 26 | ||||
-rw-r--r-- | src/mongo/dbtests/jsontests.cpp | 8 | ||||
-rw-r--r-- | src/mongo/tools/export.cpp | 5 | ||||
-rw-r--r-- | src/mongo/util/time_support.cpp | 10 | ||||
-rw-r--r-- | src/mongo/util/time_support.h | 1 | ||||
-rw-r--r-- | src/mongo/util/time_support_test.cpp | 13 |
7 files changed, 101 insertions, 8 deletions
diff --git a/jstests/tool/exportimport_date.js b/jstests/tool/exportimport_date.js new file mode 100644 index 00000000000..81476471c2f --- /dev/null +++ b/jstests/tool/exportimport_date.js @@ -0,0 +1,46 @@ +var tt = new ToolTest('exportimport_date_test'); + +var exportimport_db = tt.startDB(); + +var src = exportimport_db.src; +var dst = exportimport_db.dst; + +src.drop(); +dst.drop(); + +// Insert a date that we can format +var formatable = ISODate("1970-01-01T05:00:00Z"); +assert.eq(formatable.valueOf(), 18000000); +src.insert({ "_id" : formatable }); + +// Insert a date that we cannot format as an ISODate string +var nonformatable = ISODate("3001-01-01T00:00:00Z"); +assert.eq(nonformatable.valueOf(), 32535216000000); +src.insert({ "_id" : nonformatable }); + +data = 'data/exportimport_date_test.json'; + +print('About to call mongoexport on: ' + exportimport_db.getName() + '.' + src.getName() + + ' with file: ' + data); +tt.runTool('export', '--out' , data, '-d', exportimport_db.getName(), '-c', src.getName()); + +print('About to call mongoimport on: ' + exportimport_db.getName() + '.' + dst.getName() + + ' with file: ' + data); +tt.runTool('import', '--file', data, '-d', exportimport_db.getName(), '-c', dst.getName()); + +print('About to verify that source and destination collections match'); + +src_cursor = src.find().sort({ _id : 1 }); +dst_cursor = dst.find().sort({ _id : 1 }); + +var documentCount = 0; +while (src_cursor.hasNext()) { + assert(dst_cursor.hasNext(), 'Source has more documents than destination. ' + + 'Destination has ' + documentCount + ' documents.'); + assert.eq(src_cursor.next(), dst_cursor.next(), 'Mismatch on document ' + documentCount); + ++documentCount; +} +assert(!dst_cursor.hasNext(), 'Destination has more documents than source. ' + + 'Source has ' + documentCount + ' documents.'); + +print('Verified that source and destination collections match'); diff --git a/src/mongo/db/jsobj.cpp b/src/mongo/db/jsobj.cpp index 02a8ebd6549..ea5e1e48fb0 100644 --- a/src/mongo/db/jsobj.cpp +++ b/src/mongo/db/jsobj.cpp @@ -211,11 +211,17 @@ namespace mongo { if (format == Strict) { Date_t d = date(); s << "{ \"$date\" : "; - if (static_cast<long long>(d.millis) < 0) { - s << "{ \"$numberLong\" : \"" << static_cast<long long>(d.millis) << "\" }"; + // The two cases in which we cannot convert Date_t::millis to an ISO Date string are + // when the date is too large to format (SERVER-13760), and when the date is before + // the epoch (SERVER-11273). Since Date_t internally stores millis as an unsigned + // long long, despite the fact that it is logically signed (SERVER-8573), this check + // handles both the case where Date_t::millis is too large, and the case where + // Date_t::millis is negative (before the epoch). + if (d.isFormatable()) { + s << "\"" << dateToISOStringLocal(date()) << "\""; } else { - s << "\"" << dateToISOStringLocal(date()) << "\""; + s << "{ \"$numberLong\" : \"" << static_cast<long long>(d.millis) << "\" }"; } s << " }"; } @@ -223,14 +229,20 @@ namespace mongo { s << "Date( "; if (pretty) { Date_t d = date(); - if (static_cast<long long>(d.millis) < 0) { + // The two cases in which we cannot convert Date_t::millis to an ISO Date string + // are when the date is too large to format (SERVER-13760), and when the date is + // before the epoch (SERVER-11273). Since Date_t internally stores millis as an + // unsigned long long, despite the fact that it is logically signed + // (SERVER-8573), this check handles both the case where Date_t::millis is too + // large, and the case where Date_t::millis is negative (before the epoch). + if (d.isFormatable()) { + s << "\"" << dateToISOStringLocal(date()) << "\""; + } + else { // FIXME: This is not parseable by the shell, since it may not fit in a // float s << d.millis; } - else { - s << "\"" << dateToISOStringLocal(date()) << "\""; - } } else { s << date().asInt64(); diff --git a/src/mongo/dbtests/jsontests.cpp b/src/mongo/dbtests/jsontests.cpp index c5d63739207..a8e17ca4ba5 100644 --- a/src/mongo/dbtests/jsontests.cpp +++ b/src/mongo/dbtests/jsontests.cpp @@ -418,6 +418,14 @@ namespace JsonTests { built.jsonString( Strict ) ); ASSERT_EQUALS( "{ \"a\" : Date( 0 ) }", built.jsonString( TenGen ) ); ASSERT_EQUALS( "{ \"a\" : Date( 0 ) }", built.jsonString( JS ) ); + + // Test dates above our maximum formattable date. See SERVER-13760. + BSONObjBuilder b2; + b2.appendDate("a", 32535262800000ULL); + BSONObj built2 = b2.done(); + ASSERT_EQUALS( + "{ \"a\" : { \"$date\" : { \"$numberLong\" : \"32535262800000\" } } }", + built2.jsonString( Strict ) ); } private: diff --git a/src/mongo/tools/export.cpp b/src/mongo/tools/export.cpp index 5e9bca22432..75460f76a15 100644 --- a/src/mongo/tools/export.cpp +++ b/src/mongo/tools/export.cpp @@ -92,7 +92,10 @@ public: case jstOID: return "ObjectID(" + object.OID().toString() + ")"; // OIDs are always 24 bytes case Date: - return dateToISOStringUTC(object.Date()); + // We need to check if we can actually format this date. See SERVER-13760. + return object.Date().isFormatable() ? + dateToISOStringUTC(object.Date()) : + csvEscape(object.jsonString(Strict, false)); case Timestamp: return csvEscape(object.jsonString(Strict, false)); case RegEx: diff --git a/src/mongo/util/time_support.cpp b/src/mongo/util/time_support.cpp index 146d5ad02a4..8de951a9608 100644 --- a/src/mongo/util/time_support.cpp +++ b/src/mongo/util/time_support.cpp @@ -49,6 +49,15 @@ timegm(struct tm *const tmp); namespace mongo { + bool Date_t::isFormatable() const { + if (sizeof(time_t) == sizeof(int32_t)) { + return millis < 2147483647000ULL; // "2038-01-19T03:14:07Z" + } + else { + return millis < 32535215999000ULL; // "3000-12-31T23:59:59Z" + } + } + // jsTime_virtual_skew is just for testing. a test command manipulates it. long long jsTime_virtual_skew = 0; boost::thread_specific_ptr<long long> jsTime_virtual_thread_skew; @@ -118,6 +127,7 @@ namespace mongo { } static inline std::string _dateToISOString(Date_t date, bool local) { + invariant(date.isFormatable()); const int bufSize = 32; char buf[bufSize]; struct tm t; diff --git a/src/mongo/util/time_support.h b/src/mongo/util/time_support.h index 32f6522152c..8073229dc52 100644 --- a/src/mongo/util/time_support.h +++ b/src/mongo/util/time_support.h @@ -44,6 +44,7 @@ namespace mongo { int64_t asInt64() const { return static_cast<int64_t>(millis); } + bool isFormatable() const; }; // uses ISO 8601 dates without trailing Z diff --git a/src/mongo/util/time_support_test.cpp b/src/mongo/util/time_support_test.cpp index 318125c7fdb..d6e318bddec 100644 --- a/src/mongo/util/time_support_test.cpp +++ b/src/mongo/util/time_support_test.cpp @@ -126,6 +126,10 @@ namespace { swull = dateFromISOString("2058-02-20T18:29:11.100Z"); ASSERT_OK(swull.getStatus()); ASSERT_EQUALS(swull.getValue(), 2781455351100ULL); + + swull = dateFromISOString("3001-01-01T08:00:00.000Z"); + ASSERT_OK(swull.getStatus()); + ASSERT_EQUALS(swull.getValue(), 32535244800000ULL); } swull = dateFromISOString("2013-02-20T18:29:11.100Z"); @@ -196,6 +200,15 @@ namespace { swull = dateFromISOString("2058-02-20T13:29:11.100-0500"); ASSERT_OK(swull.getStatus()); ASSERT_EQUALS(swull.getValue(), 2781455351100ULL); + + swull = dateFromISOString("3000-12-31T23:59:59Z"); + ASSERT_OK(swull.getStatus()); + ASSERT_EQUALS(swull.getValue(), 32535215999000ULL); + } + else { + swull = dateFromISOString("2038-01-19T03:14:07Z"); + ASSERT_OK(swull.getStatus()); + ASSERT_EQUALS(swull.getValue(), 2147483647000ULL); } swull = dateFromISOString("2013-02-20T13:29:11.100-0500"); |