From: Marcin Siodelski Date: Mon, 28 Jan 2019 17:34:05 +0000 (+0100) Subject: [#396,!205] Improved time handling in MySQL CB. X-Git-Tag: 429-Updated-StampedValue-to-support-reals_base~44 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0c62d0a49ceb9b76e4597cc04d52105250566bd8;p=thirdparty%2Fkea.git [#396,!205] Improved time handling in MySQL CB. - Use milliseconds instead of seconds precision - Use local time on Kea side, instead of universal time - Dedicated posix_time / MYSQL_TIME conversion functions --- diff --git a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc index 71b3f4ce0a..b434bb3d4a 100644 --- a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc +++ b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc @@ -286,7 +286,7 @@ public: /// @brief Initialize posix time values used in tests. void initTimestamps() { // Current time minus 1 hour to make sure it is in the past. - timestamps_["today"] = boost::posix_time::second_clock::universal_time() + timestamps_["today"] = boost::posix_time::second_clock::local_time() - boost::posix_time::hours(1); // Yesterday. timestamps_["yesterday"] = timestamps_["today"] - boost::posix_time::hours(24); diff --git a/src/lib/cc/stamped_element.cc b/src/lib/cc/stamped_element.cc index 3144931f87..c1ce7a3924 100644 --- a/src/lib/cc/stamped_element.cc +++ b/src/lib/cc/stamped_element.cc @@ -10,12 +10,12 @@ namespace isc { namespace data { StampedElement::StampedElement() - : timestamp_(boost::posix_time::microsec_clock::universal_time()) { + : timestamp_(boost::posix_time::microsec_clock::local_time()) { } void StampedElement::updateModificationTime() { - setModificationTime(boost::posix_time::microsec_clock::universal_time()); + setModificationTime(boost::posix_time::microsec_clock::local_time()); } } // end of namespace isc::data diff --git a/src/lib/cc/tests/stamped_element_unittest.cc b/src/lib/cc/tests/stamped_element_unittest.cc index 2b82abcd2b..402b3811a9 100644 --- a/src/lib/cc/tests/stamped_element_unittest.cc +++ b/src/lib/cc/tests/stamped_element_unittest.cc @@ -23,7 +23,7 @@ TEST(StampedElementTest, create) { // Checking that the delta between now and the timestamp is within // 5s range should be sufficient. boost::posix_time::time_duration delta = - boost::posix_time::second_clock::universal_time() - + boost::posix_time::second_clock::local_time() - element.getModificationTime(); EXPECT_LT(delta.seconds(), 5); } @@ -52,7 +52,7 @@ TEST(StampedElementTest, update) { // Checking that the delta between now and the timestamp is within // 5s range should be sufficient. boost::posix_time::time_duration delta = - boost::posix_time::second_clock::universal_time() - + boost::posix_time::second_clock::local_time() - element.getModificationTime(); EXPECT_LT(delta.seconds(), 5); } diff --git a/src/lib/database/audit_entry.cc b/src/lib/database/audit_entry.cc index 180d9dd051..c4766487ee 100644 --- a/src/lib/database/audit_entry.cc +++ b/src/lib/database/audit_entry.cc @@ -31,7 +31,7 @@ AuditEntry::AuditEntry(const std::string& object_type, : object_type_(object_type), object_id_(object_id), modification_type_(modification_type), - modification_time_(boost::posix_time::microsec_clock::universal_time()), + modification_time_(boost::posix_time::microsec_clock::local_time()), log_message_(log_message) { // Check if the provided values are sane. validate(); diff --git a/src/lib/database/tests/audit_entry_unittest.cc b/src/lib/database/tests/audit_entry_unittest.cc index 2060b2c263..83dad417b9 100644 --- a/src/lib/database/tests/audit_entry_unittest.cc +++ b/src/lib/database/tests/audit_entry_unittest.cc @@ -28,7 +28,7 @@ public: /// @brief Returns current time. static boost::posix_time::ptime now() { - return (boost::posix_time::microsec_clock::universal_time()); + return (boost::posix_time::microsec_clock::local_time()); } /// @brief Returns always the same time value. diff --git a/src/lib/mysql/mysql_binding.cc b/src/lib/mysql/mysql_binding.cc index b268195935..75b5174d3f 100644 --- a/src/lib/mysql/mysql_binding.cc +++ b/src/lib/mysql/mysql_binding.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -6,8 +6,10 @@ #include +#include #include +using namespace boost::posix_time; using namespace isc::data; namespace isc { @@ -58,18 +60,18 @@ MySqlBinding::getBlobOrDefault(const std::vector& default_value) const return (getBlob()); } -boost::posix_time::ptime +ptime MySqlBinding::getTimestamp() const { // Make sure the binding type is timestamp. - validateAccess(); + validateAccess(); // Copy the buffer contents into native timestamp structure and // then convert it to posix time. const MYSQL_TIME* database_time = reinterpret_cast(&buffer_[0]); return (convertFromDatabaseTime(*database_time)); } -boost::posix_time::ptime -MySqlBinding::getTimestampOrDefault(const boost::posix_time::ptime& default_value) const { +ptime +MySqlBinding::getTimestampOrDefault(const ptime& default_value) const { if (amNull()) { return (default_value); } @@ -104,17 +106,17 @@ MySqlBinding::createBlob(const unsigned long length) { } MySqlBindingPtr -MySqlBinding::createTimestamp(const boost::posix_time::ptime& timestamp) { - MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits::column_type, - MySqlBindingTraits::length)); +MySqlBinding::createTimestamp(const ptime& timestamp) { + MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits::column_type, + MySqlBindingTraits::length)); binding->setTimestampValue(timestamp); return (binding); } MySqlBindingPtr MySqlBinding::createTimestamp() { - MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits::column_type, - MySqlBindingTraits::length)); + MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits::column_type, + MySqlBindingTraits::length)); return (binding); } @@ -126,7 +128,7 @@ MySqlBinding::createNull() { void MySqlBinding::convertToDatabaseTime(const time_t input_time, - MYSQL_TIME& output_time) { + MYSQL_TIME& output_time) { // Convert to broken-out time struct tm time_tm; @@ -143,10 +145,28 @@ MySqlBinding::convertToDatabaseTime(const time_t input_time, output_time.neg = my_bool(0); // Not negative } +void +MySqlBinding::convertToDatabaseTime(const ptime& input_time, + MYSQL_TIME& output_time) { + if (input_time.is_not_a_date_time()) { + isc_throw(BadValue, "Time value is not a valid posix time"); + } + + output_time.year = input_time.date().year(); + output_time.month = input_time.date().month(); + output_time.day = input_time.date().day(); + output_time.hour = input_time.time_of_day().hours(); + output_time.minute = input_time.time_of_day().minutes(); + output_time.second = input_time.time_of_day().seconds(); + output_time.second_part = input_time.time_of_day().fractional_seconds() + *1000000/time_duration::ticks_per_second(); + output_time.neg = my_bool(0); +} + void MySqlBinding::convertToDatabaseTime(const time_t cltt, - const uint32_t valid_lifetime, - MYSQL_TIME& expire) { + const uint32_t valid_lifetime, + MYSQL_TIME& expire) { // Calculate expiry time. Store it in the 64-bit value so as we can detect // overflows. @@ -196,22 +216,16 @@ MySqlBinding::convertFromDatabaseTime(const MYSQL_TIME& expire, cltt = mktime(&expire_tm) - valid_lifetime; } -boost::posix_time::ptime +ptime MySqlBinding::convertFromDatabaseTime(const MYSQL_TIME& database_time) { - // Copy across fields from MYSQL_TIME structure. - struct tm converted_tm; - memset(&converted_tm, 0, sizeof(converted_tm)); - - converted_tm.tm_year = database_time.year - 1900; - converted_tm.tm_mon = database_time.month - 1; - converted_tm.tm_mday = database_time.day; - converted_tm.tm_hour = database_time.hour; - converted_tm.tm_min = database_time.minute; - converted_tm.tm_sec = database_time.second; - converted_tm.tm_isdst = -1; // Let the system work out about DST - - // Convert to local time - return (boost::posix_time::ptime_from_tm(converted_tm)); + long fractional = database_time.second_part * time_duration::ticks_per_second()/1000000; + ptime pt(boost::gregorian::date(database_time.year, + boost::gregorian::greg_month(database_time.month), + database_time.day), + time_duration(database_time.hour, database_time.minute, + database_time.second, fractional)); + + return (pt); } MySqlBinding::MySqlBinding(enum_field_types buffer_type, @@ -244,14 +258,9 @@ MySqlBinding::setBufferLength(const unsigned long length) { } void -MySqlBinding::setTimestampValue(const boost::posix_time::ptime& timestamp) { - // Convert timestamp to tm structure. - tm td_tm = to_tm(timestamp); - // Convert tm value to time_t. - time_t tt = mktime(&td_tm); - // Convert time_t to database time. +MySqlBinding::setTimestampValue(const ptime& timestamp) { MYSQL_TIME database_time; - convertToDatabaseTime(tt, database_time); + convertToDatabaseTime(timestamp, database_time); // Copy database time into the buffer. memcpy(static_cast(&buffer_[0]), reinterpret_cast(&database_time), sizeof(MYSQL_TIME)); diff --git a/src/lib/mysql/mysql_binding.h b/src/lib/mysql/mysql_binding.h index 62847f074f..e317105ae7 100644 --- a/src/lib/mysql/mysql_binding.h +++ b/src/lib/mysql/mysql_binding.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -421,6 +421,14 @@ public: static void convertToDatabaseTime(const time_t input_time, MYSQL_TIME& output_time); + /// @brief Converts posix time value to database time. + /// + /// @param input_time A posix time value representing local time. + /// @param output_time Reference to MYSQL_TIME object where converted time + /// will be put. + static void convertToDatabaseTime(const boost::posix_time::ptime& input_time, + MYSQL_TIME& output_time); + /// @brief Converts Lease Time to Database Times /// /// Within the DHCP servers, times are stored as client last transmit time @@ -470,7 +478,7 @@ public: /// @param database_time Reference to MYSQL_TIME object where database /// time is stored. /// - /// @return Database time converted to posix time. + /// @return Database time converted to local posix time. static boost::posix_time::ptime convertFromDatabaseTime(const MYSQL_TIME& database_time); diff --git a/src/lib/mysql/tests/mysql_binding_unittest.cc b/src/lib/mysql/tests/mysql_binding_unittest.cc index 48d96671ff..55f63b0deb 100644 --- a/src/lib/mysql/tests/mysql_binding_unittest.cc +++ b/src/lib/mysql/tests/mysql_binding_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -83,7 +84,7 @@ TEST(MySqlBindingTest, conditionalInteger) { // This test verifies that default timestamp is returned if binding is null. TEST(MySqlBindingTest, defaultTimestamp) { - boost::posix_time::ptime current_time = boost::posix_time::second_clock::universal_time(); + boost::posix_time::ptime current_time = boost::posix_time::second_clock::local_time(); boost::posix_time::ptime past_time = current_time - boost::posix_time::hours(1); auto binding = MySqlBinding::createNull(); @@ -93,5 +94,23 @@ TEST(MySqlBindingTest, defaultTimestamp) { EXPECT_TRUE(current_time == binding->getTimestampOrDefault(past_time)); } +// This test verifies that the binding preserves fractional seconds in +// millisecond precision. +TEST(MySqlBindingTest, millisecondTimestampPrecision) { + // Set timestamp of 2019-01-28 01:12:10.123 + + // Fractional part depends on the clock resolution. + long fractional = 123*(boost::posix_time::time_duration::ticks_per_second()/1000); + boost::posix_time::ptime + test_time(boost::gregorian::date(2019, boost::gregorian::Jan, 28), + boost::posix_time::time_duration(1, 12, 10, fractional)); + + auto binding = MySqlBinding::createTimestamp(test_time); + + boost::posix_time::ptime returned_test_time = binding->getTimestamp(); + + EXPECT_EQ(returned_test_time, test_time); +} + } diff --git a/src/lib/mysql/tests/mysql_connection_unittest.cc b/src/lib/mysql/tests/mysql_connection_unittest.cc index 6d305df613..3299698748 100644 --- a/src/lib/mysql/tests/mysql_connection_unittest.cc +++ b/src/lib/mysql/tests/mysql_connection_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -105,7 +105,7 @@ public: "bigint_value BIGINT NULL," "string_value TEXT NULL," "blob_value BLOB NULL," - "timestamp_value TIMESTAMP NULL" + "timestamp_value TIMESTAMP(6) NULL" ")"); } @@ -236,7 +236,7 @@ TEST_F(MySqlConnectionTest, select) { MySqlBinding::createInteger(-4096), MySqlBinding::createString("shellfish"), MySqlBinding::createBlob(blob.begin(), blob.end()), - MySqlBinding::createTimestamp(boost::posix_time::microsec_clock::universal_time()) + MySqlBinding::createTimestamp(boost::posix_time::microsec_clock::local_time()) }; testInsertSelect(in_bindings); @@ -252,7 +252,7 @@ TEST_F(MySqlConnectionTest, selectNullInteger) { MySqlBinding::createInteger(-4096), MySqlBinding::createString("shellfish"), MySqlBinding::createBlob(blob.begin(), blob.end()), - MySqlBinding::createTimestamp(boost::posix_time::microsec_clock::universal_time()) + MySqlBinding::createTimestamp(boost::posix_time::microsec_clock::local_time()) }; testInsertSelect(in_bindings); @@ -269,7 +269,7 @@ TEST_F(MySqlConnectionTest, selectNullString) { MySqlBinding::createInteger(-4096), MySqlBinding::createNull(), MySqlBinding::createBlob(blob.begin(), blob.end()), - MySqlBinding::createTimestamp(boost::posix_time::microsec_clock::universal_time()) + MySqlBinding::createTimestamp(boost::posix_time::microsec_clock::local_time()) }; testInsertSelect(in_bindings); @@ -284,7 +284,7 @@ TEST_F(MySqlConnectionTest, selectNullBlob) { MySqlBinding::createInteger(-4096), MySqlBinding::createString("shellfish"), MySqlBinding::createNull(), - MySqlBinding::createTimestamp(boost::posix_time::microsec_clock::universal_time()) + MySqlBinding::createTimestamp(boost::posix_time::microsec_clock::local_time()) }; testInsertSelect(in_bindings); @@ -315,7 +315,7 @@ TEST_F(MySqlConnectionTest, selectEmptyStringBlob) { MySqlBinding::createInteger(-4096), MySqlBinding::createString(""), MySqlBinding::createBlob(blob.begin(), blob.end()), - MySqlBinding::createTimestamp(boost::posix_time::microsec_clock::universal_time()) + MySqlBinding::createTimestamp(boost::posix_time::microsec_clock::local_time()) }; testInsertSelect(in_bindings);