]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#396,!205] Improved time handling in MySQL CB.
authorMarcin Siodelski <marcin@isc.org>
Mon, 28 Jan 2019 17:34:05 +0000 (18:34 +0100)
committerMarcin Siodelski <marcin@isc.org>
Wed, 30 Jan 2019 09:18:59 +0000 (10:18 +0100)
- Use milliseconds instead of seconds precision
- Use local time on Kea side, instead of universal time
- Dedicated posix_time / MYSQL_TIME conversion functions

src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc
src/lib/cc/stamped_element.cc
src/lib/cc/tests/stamped_element_unittest.cc
src/lib/database/audit_entry.cc
src/lib/database/tests/audit_entry_unittest.cc
src/lib/mysql/mysql_binding.cc
src/lib/mysql/mysql_binding.h
src/lib/mysql/tests/mysql_binding_unittest.cc
src/lib/mysql/tests/mysql_connection_unittest.cc

index 71b3f4ce0a5ba413c44ee411ff3fa8948beb44df..b434bb3d4a735657aae4904a04e0858e2581efa5 100644 (file)
@@ -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);
index 3144931f877c967b9f95bf557d0e141a22e94263..c1ce7a3924f4580e622a22f5254244a3c6bee0e9 100644 (file)
@@ -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
index 2b82abcd2b5d165a8e744665702ef7ea4719416a..402b3811a94088ba29c934dbadb17ba546a87126 100644 (file)
@@ -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);
 }
index 180d9dd05153b468f9b28ef1841463356e9041dc..c4766487eef32208ee84ca6f1b93304ae60c4c5c 100644 (file)
@@ -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();
index 2060b2c263705102e6473bdc49556f2145fb6356..83dad417b90e61a7752d6c6d9dfa3cbdd15024f2 100644 (file)
@@ -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.
index b268195935384bb92bf3486c191b05ea944e18c3..75b5174d3f7873fa20e0578ce1d7fdc0148028cd 100644 (file)
@@ -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 <config.h>
 
+#include <boost/date_time/gregorian/gregorian.hpp>
 #include <mysql/mysql_binding.h>
 
+using namespace boost::posix_time;
 using namespace isc::data;
 
 namespace isc {
@@ -58,18 +60,18 @@ MySqlBinding::getBlobOrDefault(const std::vector<uint8_t>& default_value) const
     return (getBlob());
 }
 
-boost::posix_time::ptime
+ptime
 MySqlBinding::getTimestamp() const {
     // Make sure the binding type is timestamp.
-    validateAccess<boost::posix_time::ptime>();
+    validateAccess<ptime>();
     // Copy the buffer contents into native timestamp structure and
     // then convert it to posix time.
     const MYSQL_TIME* database_time = reinterpret_cast<const MYSQL_TIME*>(&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<boost::posix_time::ptime>::column_type,
-                                   MySqlBindingTraits<boost::posix_time::ptime>::length));
+MySqlBinding::createTimestamp(const ptime& timestamp) {
+    MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits<ptime>::column_type,
+                                   MySqlBindingTraits<ptime>::length));
     binding->setTimestampValue(timestamp);
     return (binding);
 }
 
 MySqlBindingPtr
 MySqlBinding::createTimestamp() {
-    MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits<boost::posix_time::ptime>::column_type,
-                                             MySqlBindingTraits<boost::posix_time::ptime>::length));
+    MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits<ptime>::column_type,
+                                             MySqlBindingTraits<ptime>::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<void*>(&buffer_[0]), reinterpret_cast<char*>(&database_time),
            sizeof(MYSQL_TIME));
index 62847f074f65eb23d347a98acd1b458d523dd5cf..e317105ae7f95ed68c6fcd98e84d237f040b078b 100644 (file)
@@ -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);
 
index 48d96671ff016930c81bbe0342481274dd6ceb3e..55f63b0deba0ae20ae33571673aa1c29c0ef33d3 100644 (file)
@@ -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 <config.h>
 
 #include <mysql/mysql_binding.h>
+#include <boost/date_time/gregorian/gregorian.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <gtest/gtest.h>
 
@@ -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);
+}
+
 }
 
index 6d305df6137d6a4adda88e04482cdecbc7d0e48a..3299698748397a78040b2ce33b02354e32289b46 100644 (file)
@@ -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<int64_t>(-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<int64_t>(-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<int64_t>(-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<int64_t>(-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<int64_t>(-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);