]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#489,!250] Extend MySQL backend to store floating point values.
authorMarcin Siodelski <marcin@isc.org>
Fri, 1 Mar 2019 14:06:25 +0000 (15:06 +0100)
committerMarcin Siodelski <marcin@isc.org>
Fri, 1 Mar 2019 14:06:25 +0000 (15:06 +0100)
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc
src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h
src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc
src/lib/mysql/mysql_binding.cc
src/lib/mysql/mysql_binding.h
src/lib/mysql/tests/mysql_binding_unittest.cc
src/share/database/scripts/mysql/dhcpdb_create.mysql
src/share/database/scripts/mysql/upgrade_7.0_to_8.0.sh.in

index eb7cfd065af3af45f8551b3a8da8c52c9be897a1..5a2b4d0070408458caff75d3912b7a00f1ca2cdb 100644 (file)
@@ -275,7 +275,10 @@ public:
             MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // option: user_context
             MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // option: shared_network_name
             MySqlBinding::createInteger<uint64_t>(), // option: pool_id
-            MySqlBinding::createTimestamp() //option: modification_ts
+            MySqlBinding::createTimestamp(), //option: modification_ts
+            MySqlBinding::createInteger<uint8_t>(), // calculate_tee_times
+            MySqlBinding::createInteger<float>(), // t1_percent
+            MySqlBinding::createInteger<float>() // t2_percent
         };
 
         uint64_t last_pool_id = 0;
@@ -358,8 +361,7 @@ public:
 
                 // match_client_id
                 if (!out_bindings[8]->amNull()) {
-                    last_subnet->setMatchClientId(static_cast<bool>
-                                                  (out_bindings[8]->getInteger<uint8_t>()));
+                    last_subnet->setMatchClientId(out_bindings[8]->getBool());
                 }
 
                 // modification_ts
@@ -423,6 +425,21 @@ public:
                     last_subnet->setContext(user_context);
                 }
 
+                // calculate_tee_times
+                if (!out_bindings[49]->amNull()) {
+                    last_subnet->setCalculateTeeTimes(out_bindings[49]->getBool());
+                }
+
+                // t1_percent
+                if (!out_bindings[50]->amNull()) {
+                    last_subnet->setT1Percent(out_bindings[50]->getFloat());
+                }
+
+                // t2_percent
+                if (!out_bindings[51]->amNull()) {
+                    last_subnet->setT2Percent(out_bindings[51]->getFloat());
+                }
+
                 // Subnet ready. Add it to the list.
                 subnets.push_back(last_subnet);
             }
@@ -778,7 +795,10 @@ public:
             MySqlBinding::condCreateString(subnet->getSname()),
             shared_network_binding,
             createInputContextBinding(subnet),
-            createBinding(subnet->getValid())
+            createBinding(subnet->getValid()),
+            MySqlBinding::condCreateBool(subnet->getCalculateTeeTimes()),
+            MySqlBinding::condCreateFloat(subnet->getT1Percent()),
+            MySqlBinding::condCreateFloat(subnet->getT2Percent())
         };
 
         MySqlTransaction transaction(conn_);
@@ -1014,8 +1034,7 @@ public:
 
                 // match_client_id
                 if (!out_bindings[4]->amNull()) {
-                    last_network->setMatchClientId(static_cast<bool>
-                                                   (out_bindings[4]->getInteger<uint8_t>()));
+                    last_network->setMatchClientId(out_bindings[4]->getBool());
                 }
 
                 // modification_ts
@@ -1305,7 +1324,7 @@ public:
             createOptionValueBinding(option),
             MySqlBinding::condCreateString(option->formatted_value_),
             MySqlBinding::condCreateString(option->space_name_),
-            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option->persistent_)),
+            MySqlBinding::createBool(option->persistent_),
             MySqlBinding::createNull(),
             MySqlBinding::createNull(),
             MySqlBinding::createInteger<uint8_t>(0),
@@ -1367,7 +1386,7 @@ public:
             createOptionValueBinding(option),
             MySqlBinding::condCreateString(option->formatted_value_),
             MySqlBinding::condCreateString(option->space_name_),
-            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option->persistent_)),
+            MySqlBinding::createBool(option->persistent_),
             MySqlBinding::createNull(),
             MySqlBinding::createInteger<uint32_t>(static_cast<uint32_t>(subnet_id)),
             MySqlBinding::createInteger<uint8_t>(1),
@@ -1465,7 +1484,7 @@ public:
             createOptionValueBinding(option),
             MySqlBinding::condCreateString(option->formatted_value_),
             MySqlBinding::condCreateString(option->space_name_),
-            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option->persistent_)),
+            MySqlBinding::createBool(option->persistent_),
             MySqlBinding::createNull(),
             MySqlBinding::createNull(),
             MySqlBinding::createInteger<uint8_t>(5),
@@ -1531,7 +1550,7 @@ public:
             createOptionValueBinding(option),
             MySqlBinding::condCreateString(option->formatted_value_),
             MySqlBinding::condCreateString(option->space_name_),
-            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option->persistent_)),
+            MySqlBinding::createBool(option->persistent_),
             MySqlBinding::createNull(),
             MySqlBinding::createNull(),
             MySqlBinding::createInteger<uint8_t>(4),
@@ -1607,7 +1626,7 @@ public:
                                        "dhcp4" : option_def->getOptionSpaceName()),
             MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option_def->getType())),
             MySqlBinding::createTimestamp(option_def->getModificationTime()),
-            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option_def->getArrayType())),
+            MySqlBinding::createBool(option_def->getArrayType()),
             MySqlBinding::createString(option_def->getEncapsulatedSpace()),
             record_types_binding,
             createInputContextBinding(option_def)
@@ -2018,9 +2037,12 @@ TaggedStatementArray tagged_statements = { {
       "  server_hostname,"
       "  shared_network_name,"
       "  user_context,"
-      "  valid_lifetime"
+      "  valid_lifetime,"
+      "  calculate_tee_times,"
+      "  t1_percent,"
+      "  t2_percent"
       ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,"
-      "?, ?, ?, ?, ?, ?, ?, ?)" },
+      "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" },
 
     // Insert association of the subnet with a server.
     { MySqlConfigBackendDHCPv4Impl::INSERT_SUBNET4_SERVER,
@@ -2101,7 +2123,10 @@ TaggedStatementArray tagged_statements = { {
       "  server_hostname = ?,"
       "  shared_network_name = ?,"
       "  user_context = ?,"
-      "  valid_lifetime = ? "
+      "  valid_lifetime = ?,"
+      "  calculate_tee_times = ?,"
+      "  t1_percent = ?,"
+      "  t2_percent = ? "
       "WHERE subnet_id = ?" },
 
     // Update existing shared network.
index f23f2d79ff0bda2fcdeb4530f18b9f016d0a9ab8..f3c0a3a4962704df11aaa67bebe6fbeada2943c9 100644 (file)
@@ -98,7 +98,10 @@ namespace {
     "  o.user_context," \
     "  o.shared_network_name," \
     "  o.pool_id," \
-    "  o.modification_ts " \
+    "  o.modification_ts," \
+    "  s.calculate_tee_times," \
+    "  s.t1_percent," \
+    "  s.t2_percent " \
     "FROM dhcp4_subnet AS s " \
     "INNER JOIN dhcp4_subnet_server AS a " \
     "  ON s.subnet_id = a.subnet_id " \
index 4bcf68b73f06fca598b29bf6d145f966d8f49a55..01703835ccab7b73754d6d6d426f92f3d0b6db0a 100644 (file)
@@ -108,6 +108,9 @@ public:
         subnet->setSname("server-hostname");
         subnet->setContext(user_context);
         subnet->setValid(555555);
+        subnet->setCalculateTeeTimes(true);
+        subnet->setT1Percent(0.345);
+        subnet->setT2Percent(0.444);
 
         Pool4Ptr pool1(new Pool4(IOAddress("192.0.2.10"), IOAddress("192.0.2.20")));
         subnet->addPool(pool1);
index fbde51c7fe1744a4415c5c02a72272ecf7af68a4..bd708ec1d001acd4b42f25a45e113de92e1ff350 100644 (file)
@@ -64,6 +64,17 @@ MySqlBinding::getBlobOrDefault(const std::vector<uint8_t>& default_value) const
     return (getBlob());
 }
 
+float
+MySqlBinding::getFloat() const {
+    // It may seem a bit weird that we use getInteger template method
+    // for getting a floating point value. However, the getInteger method
+    // seems to be generic enough to support it. If we were to redo the
+    // API of this class we would probably introduce a getNumericValue
+    // method instead of getInteger. However, we already have getInteger
+    // used in many places so we should stick to it.
+    return (getInteger<float>());
+}
+
 ptime
 MySqlBinding::getTimestamp() const {
     // Make sure the binding type is timestamp.
@@ -109,6 +120,22 @@ MySqlBinding::createBlob(const unsigned long length) {
     return (binding);
 }
 
+MySqlBindingPtr
+MySqlBinding::createFloat(const float value) {
+    // It may seem a bit weird that we use createInteger template method
+    // for setting a floating point value. However, the setInteger method
+    // seems to be generic enough to support it. If we were to redo the
+    // API of this class we would probably introduce a createNumericValue
+    // method instead of createInteger. However, we already have createInteger
+    // used in many places so we should stick to it.
+    return (createInteger<float>(value));
+}
+
+MySqlBindingPtr
+MySqlBinding::createBool(const bool value) {
+    return (createInteger<uint8_t>(static_cast<uint8_t>(value)));
+}
+
 MySqlBindingPtr
 MySqlBinding::condCreateBool(const util::Optional<bool>& value) {
     if (value.unspecified()) {
index 82f388e894fdd1a527193e3a909791154bcc2e70..f46f45f623ab53a6aafed42d153f01fcd9ed34ce 100644 (file)
@@ -125,6 +125,13 @@ struct MySqlBindingTraits<uint64_t> {
     static const bool am_unsigned = true;
 };
 
+template<>
+struct MySqlBindingTraits<float> {
+    static const enum_field_types column_type = MYSQL_TYPE_FLOAT;
+    static const size_t length = 4;
+    static const bool am_unsigned = false;
+};
+
 /// @brief Forward declaration of @c MySqlBinding class.
 class MySqlBinding;
 
@@ -272,6 +279,30 @@ public:
         return (getInteger<T>());
     }
 
+    /// @brief Returns float value held in the binding.
+    ///
+    /// Call @c MySqlBinding::amNull to verify that the value is not
+    /// null prior to calling this method.
+    ///
+    /// @throw InvalidOperation if the value is NULL or the binding
+    /// type does not match the template parameter.
+    ///
+    /// @return Float value.
+    float getFloat() const;
+
+    /// @brief Returns boolean value held in the binding.
+    ///
+    /// Call @c MySqlBinding::amNull to verify that the value is not
+    /// null prior to calling this method.
+    ///
+    /// @throw InvalidOperation if the value is NULL or the binding
+    /// type is not uint8_t.
+    ///
+    /// @return Boolean value.
+    bool getBool() const {
+        return (static_cast<bool>(getInteger<uint8_t>()));
+    }
+
     /// @brief Returns timestamp value held in the binding.
     ///
     /// Call @c MySqlBinding::amNull to verify that the value is not
@@ -395,6 +426,35 @@ public:
         return (value.unspecified() ? createNull() : createInteger<T>(value.get()));
     }
 
+    /// @brief Creates binding having a float type for sending data.
+    ///
+    /// @param value Float value to be sent to the database.
+    ///
+    /// @return Pointer to the created binding.
+    static MySqlBindingPtr createFloat(const float value);
+
+    /// @Conditionally creates binding of float type for sending data if
+    /// provided value is specified.
+    ///
+    /// @tparam T Floating point type to be converted to float.
+    ///
+    /// @param value Value to be stored in the database as float.
+    ///
+    /// @return Pointer to the created binding.
+    template<typename T>
+    static MySqlBindingPtr condCreateFloat(const util::Optional<T>& value) {
+        return (value.unspecified() ? createNull() :
+                createInteger<float> (static_cast<float>(value.get())));
+    }
+
+    /// @brief Creates binding having a bool type for sending data.
+    ///
+    /// @param value Boolean value to be sent to the database.
+    ///
+    /// @return Pointer to the created binding holding an @c uint8_t
+    /// value representing the boolean value.
+    static MySqlBindingPtr createBool(const bool value);
+
     /// @brief Conditionally creates binding of @c uint8_t type representing
     /// a boolean value if provided value is specified.
     ///
index 1a412cb816c0801572a0a88d5f4811649847affd..f75f83e06593d13cd9b76807b401c040bfc50667 100644 (file)
@@ -14,6 +14,7 @@
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <gtest/gtest.h>
 
+using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::data;
 using namespace isc::db;
@@ -30,7 +31,8 @@ TEST(MySqlBindingTest, defaultString) {
     EXPECT_EQ("bar", binding->getStringOrDefault("foo"));
 }
 
-// This test verifies that null binding is created for unspecified string.
+// This test verifies that null binding is created for unspecified string
+// and the string binding is created for a specified string.
 TEST(MySqlBindingTest, conditionalString) {
     auto binding = MySqlBinding::condCreateString(Optional<std::string>());
     EXPECT_TRUE(binding->amNull());
@@ -40,6 +42,19 @@ TEST(MySqlBindingTest, conditionalString) {
     EXPECT_EQ("foo", binding->getString());
 }
 
+// This test verifies that an error is thrown upon an attempt to use
+// invalid accessor for a string binding.
+TEST(MySqlBindingTest, stringTypeMismatch) {
+    auto binding = MySqlBinding::createString("foo");
+    EXPECT_NO_THROW(static_cast<void>(binding->getString()));
+
+    EXPECT_THROW(static_cast<void>(binding->getBlob()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getInteger<uint16_t>()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getFloat()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getBool()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getTimestamp()), InvalidOperation);
+}
+
 // This test verifies that null JSON is returned if the string binding
 // is null, JSON value is returned when string value is valid JSON and
 // that exception is thrown if the string is not a valid JSON.
@@ -68,6 +83,20 @@ TEST(MySqlBindingTest, defaultBlob) {
     EXPECT_EQ(blob, binding->getBlobOrDefault(default_blob));
 }
 
+// This test verifies that an error is thrown upon an attempt to use
+// invalid accessor for a blob binding.
+TEST(MySqlBindingTest, blobTypeMismatch) {
+    std::vector<uint8_t> blob(10, 1);
+    auto binding = MySqlBinding::createBlob(blob.begin(), blob.end());
+    EXPECT_NO_THROW(static_cast<void>(binding->getBlob()));
+
+    EXPECT_THROW(static_cast<void>(binding->getString()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getInteger<uint16_t>()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getFloat()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getBool()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getTimestamp()), InvalidOperation);
+}
+
 // This test verifies that default number is returned if binding is null.
 TEST(MySqlBindingTest, defaultInteger) {
     auto binding = MySqlBinding::createNull();
@@ -77,7 +106,8 @@ TEST(MySqlBindingTest, defaultInteger) {
     EXPECT_EQ(1024, binding->getIntegerOrDefault<uint32_t>(123));
 }
 
-// This test verifies that null binding is created for unspecified number.
+// This test verifies that null binding is created for unspecified number
+// and the integer binding is created for a specified number.
 TEST(MySqlBindingTest, conditionalInteger) {
     auto binding = MySqlBinding::condCreateInteger<uint16_t>(Optional<uint16_t>());
     EXPECT_TRUE(binding->amNull());
@@ -87,7 +117,49 @@ TEST(MySqlBindingTest, conditionalInteger) {
     EXPECT_EQ(1, binding->getInteger<uint16_t>());
 }
 
+// This test verifies that an error is thrown upon an attempt to use
+// invalid accessor for an integer binding.
+TEST(MySqlBindingTest, integerTypeMismatch) {
+    auto binding = MySqlBinding::createInteger<uint32_t>(123);
+    EXPECT_NO_THROW(static_cast<void>(binding->getInteger<uint32_t>()));
+
+    EXPECT_THROW(static_cast<void>(binding->getString()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getBlob()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getInteger<uint8_t>()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getInteger<uint16_t>()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getFloat()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getBool()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getTimestamp()), InvalidOperation);
+}
+
+// This test verifies that null binding is created for unspecified floating
+// point value and the float binding is created for the specified value.
+TEST(MySqlBindingTest, conditionalFloat) {
+    auto binding = MySqlBinding::condCreateFloat(Optional<float>());
+    EXPECT_TRUE(binding->amNull());
+
+    binding = MySqlBinding::condCreateFloat<float>(1.567f);
+    ASSERT_FALSE(binding->amNull());
+    EXPECT_EQ(1.567f, binding->getFloat());
+}
+
+// This test verifies that an error is thrown upon an attempt to use
+// invalid accessor for a float binding.
+TEST(MySqlBindingTest, floatTypeMismatch) {
+    auto binding = MySqlBinding::createFloat(123.123f);
+    EXPECT_NO_THROW(static_cast<void>(binding->getFloat()));
+
+    EXPECT_THROW(static_cast<void>(binding->getString()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getBlob()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getInteger<uint8_t>()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getInteger<uint16_t>()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getInteger<uint32_t>()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getBool()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getTimestamp()), InvalidOperation);
+}
+
 // This test verifies that null binding is created for unspecified boolean
+// value and the uint8_t binding is created for a specified boolean
 // value.
 TEST(MySqlBindingTest, conditionalBoolean) {
     auto binding = MySqlBinding::condCreateBool(Optional<bool>());
@@ -95,14 +167,30 @@ TEST(MySqlBindingTest, conditionalBoolean) {
 
     binding = MySqlBinding::condCreateBool(false);
     ASSERT_FALSE(binding->amNull());
-    EXPECT_EQ(0, binding->getInteger<uint8_t>());
+    EXPECT_FALSE(binding->getBool());
 
     binding = MySqlBinding::condCreateBool(true);
     ASSERT_FALSE(binding->amNull());
-    EXPECT_NE(binding->getInteger<uint8_t>(), 0);
+    EXPECT_TRUE(binding->getBool());
+}
+
+// This test verifies that an error is thrown upon an attempt to use
+// invalid accessor for a float binding.
+TEST(MySqlBindingTest, booleanTypeMismatch) {
+    auto binding = MySqlBinding::createBool(false);
+    EXPECT_NO_THROW(static_cast<void>(binding->getBool()));
+    EXPECT_NO_THROW(static_cast<void>(binding->getInteger<uint8_t>()));
+
+    EXPECT_THROW(static_cast<void>(binding->getString()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getBlob()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getInteger<uint16_t>()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getInteger<uint32_t>()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getFloat()), InvalidOperation);
+    EXPECT_THROW(static_cast<void>(binding->getTimestamp()), InvalidOperation);
 }
 
-// This test verifies that null binding is created for unspecified address.
+// This test verifies that null binding is created for unspecified address
+// and the uint32_t binding is created for the specified address.
 TEST(MySqlBindingTest, conditionalIPv4Address) {
     auto binding = MySqlBinding::condCreateIPv4Address(Optional<IOAddress>());
     EXPECT_TRUE(binding->amNull());
index d8076313e56bd3444d5fec0f20f0566ed0d347f7..b88dee9579fe45652c6af7bae8ea988f17e09328 100644 (file)
@@ -1343,24 +1343,45 @@ ALTER TABLE dhcp6_options
     MODIFY COLUMN modification_ts TIMESTAMP NOT NULL
     DEFAULT CURRENT_TIMESTAMP;
 
+ALTER TABLE dhcp4_subnet
+    ADD COLUMN calculate_tee_times TINYINT(1) DEFAULT NULL,
+    ADD COLUMN t1_percent FLOAT DEFAULT NULL,
+    ADD COLUMN t2_percent FLOAT DEFAULT NULL;
+
 ALTER TABLE dhcp4_subnet
     MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;
 
 ALTER TABLE dhcp4_subnet
     MODIFY COLUMN match_client_id TINYINT(1) DEFAULT NULL;
 
+ALTER TABLE dhcp4_shared_network
+    ADD COLUMN calculate_tee_times TINYINT(1) DEFAULT NULL,
+    ADD COLUMN t1_percent FLOAT DEFAULT NULL,
+    ADD COLUMN t2_percent FLOAT DEFAULT NULL;
+
 ALTER TABLE dhcp4_shared_network
     MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;
 
 ALTER TABLE dhcp4_shared_network
     MODIFY COLUMN match_client_id TINYINT(1) DEFAULT NULL;
 
+ALTER TABLE dhcp6_subnet
+    ADD COLUMN calculate_tee_times TINYINT(1) DEFAULT NULL,
+    ADD COLUMN t1_percent FLOAT DEFAULT NULL,
+    ADD COLUMN t2_percent FLOAT DEFAULT NULL;
+
 ALTER TABLE dhcp6_subnet
     MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;
 
+ALTER TABLE dhcp6_shared_network
+    ADD COLUMN calculate_tee_times TINYINT(1) DEFAULT NULL,
+    ADD COLUMN t1_percent FLOAT DEFAULT NULL,
+    ADD COLUMN t2_percent FLOAT DEFAULT NULL;
+
 ALTER TABLE dhcp6_shared_network
     MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;
 
+
 -- -----------------------------------------------------
 -- Make sure that constraints on the 7.0 schema tables
 -- have appropriate referential actions. All tables
index b3516d5567e74f4642b1bc8501d3fbf79861ca8f..3be2a7bad064670991de3ad7e585b86889ed8353 100644 (file)
@@ -26,21 +26,41 @@ ALTER TABLE dhcp6_options
     MODIFY COLUMN modification_ts TIMESTAMP NOT NULL
     DEFAULT CURRENT_TIMESTAMP;
 
+ALTER TABLE dhcp4_subnet
+    ADD COLUMN calculate_tee_times TINYINT(1) DEFAULT NULL,
+    ADD COLUMN t1_percent FLOAT DEFAULT NULL,
+    ADD COLUMN t2_percent FLOAT DEFAULT NULL;
+
 ALTER TABLE dhcp4_subnet
     MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;
 
 ALTER TABLE dhcp4_subnet
     MODIFY COLUMN match_client_id TINYINT(1) DEFAULT NULL;
 
+ALTER TABLE dhcp4_shared_network
+    ADD COLUMN calculate_tee_times TINYINT(1) DEFAULT NULL,
+    ADD COLUMN t1_percent FLOAT DEFAULT NULL,
+    ADD COLUMN t2_percent FLOAT DEFAULT NULL;
+
 ALTER TABLE dhcp4_shared_network
     MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;
 
 ALTER TABLE dhcp4_shared_network
     MODIFY COLUMN match_client_id TINYINT(1) DEFAULT NULL;
 
+ALTER TABLE dhcp6_subnet
+    ADD COLUMN calculate_tee_times TINYINT(1) DEFAULT NULL,
+    ADD COLUMN t1_percent FLOAT DEFAULT NULL,
+    ADD COLUMN t2_percent FLOAT DEFAULT NULL;
+
 ALTER TABLE dhcp6_subnet
     MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;
 
+ALTER TABLE dhcp6_shared_network
+    ADD COLUMN calculate_tee_times TINYINT(1) DEFAULT NULL,
+    ADD COLUMN t1_percent FLOAT DEFAULT NULL,
+    ADD COLUMN t2_percent FLOAT DEFAULT NULL;
+
 ALTER TABLE dhcp6_shared_network
     MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;