]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#489] Updated MySQL CB to handle unspecified subnet and network values.
authorMarcin Siodelski <marcin@isc.org>
Thu, 28 Feb 2019 09:28:27 +0000 (10:28 +0100)
committerMarcin Siodelski <marcin@isc.org>
Thu, 28 Feb 2019 16:38:53 +0000 (17:38 +0100)
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc
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 0332a9ba7d4a1015b23c6f6d87e7e18cf7a9bf09..eb7cfd065af3af45f8551b3a8da8c52c9be897a1 100644 (file)
@@ -343,20 +343,33 @@ public:
                                                        dhcp4o6_subnet_prefix_pair.second);
                 }
                 // boot_file_name
-                last_subnet->setFilename(out_bindings[5]->getStringOrDefault(""));
+                if (!out_bindings[5]->amNull()) {
+                    last_subnet->setFilename(out_bindings[5]->getString());
+                }
+
                 // client_class
                 if (!out_bindings[6]->amNull()) {
                     last_subnet->allowClientClass(out_bindings[6]->getString());
                 }
                 // interface
-                last_subnet->setIface(out_bindings[7]->getStringOrDefault(""));
+                if (!out_bindings[7]->amNull()) {
+                    last_subnet->setIface(out_bindings[7]->getString());
+                }
+
                 // match_client_id
-                last_subnet->setMatchClientId(static_cast<bool>
-                                              (out_bindings[8]->getIntegerOrDefault<uint8_t>(1)));
+                if (!out_bindings[8]->amNull()) {
+                    last_subnet->setMatchClientId(static_cast<bool>
+                                                  (out_bindings[8]->getInteger<uint8_t>()));
+                }
+
                 // modification_ts
                 last_subnet->setModificationTime(out_bindings[9]->getTimestamp());
+
                 // next_server
-                last_subnet->setSiaddr(IOAddress(out_bindings[10]->getIntegerOrDefault<uint32_t>(0)));
+                if (!out_bindings[10]->amNull()) {
+                    last_subnet->setSiaddr(IOAddress(out_bindings[10]->getInteger<uint32_t>()));
+                }
+
                 // relay
                 ElementPtr relay_element = out_bindings[12]->getJSON();
                 if (relay_element) {
@@ -389,12 +402,21 @@ public:
                     }
                 }
                 // reservation_mode
-                last_subnet->setHostReservationMode(static_cast<Subnet4::HRMode>
-                    (out_bindings[15]->getIntegerOrDefault<uint8_t>(Subnet4::HR_ALL)));
+                if (!out_bindings[15]->amNull()) {
+                    last_subnet->setHostReservationMode(static_cast<Subnet4::HRMode>
+                        (out_bindings[15]->getInteger<uint8_t>()));
+                }
+
                 // server_hostname
-                last_subnet->setSname(out_bindings[16]->getStringOrDefault(""));
+                if (!out_bindings[16]->amNull()) {
+                    last_subnet->setSname(out_bindings[16]->getString());
+                }
+
                 // shared_network_name
-                last_subnet->setSharedNetworkName(out_bindings[17]->getStringOrDefault(""));
+                if (!out_bindings[17]->amNull()) {
+                    last_subnet->setSharedNetworkName(out_bindings[17]->getString());
+                }
+
                 // user_context
                 ElementPtr user_context = out_bindings[18]->getJSON();
                 if (user_context) {
@@ -668,16 +690,21 @@ public:
 
         // Convert DHCPv4o6 interface id to text.
         OptionPtr dhcp4o6_interface_id = subnet->get4o6().getInterfaceId();
-        std::string dhcp4o6_interface_id_text;
+        MySqlBindingPtr dhcp4o6_interface_id_binding;
         if (dhcp4o6_interface_id) {
-            dhcp4o6_interface_id_text.assign(dhcp4o6_interface_id->getData().begin(),
-                                             dhcp4o6_interface_id->getData().end());
+            std::string dhcp4o6_interface_id_text(dhcp4o6_interface_id->getData().begin(),
+                                                  dhcp4o6_interface_id->getData().end());
+            dhcp4o6_interface_id_binding = MySqlBinding::createString(dhcp4o6_interface_id_text);
+
+        } else {
+            dhcp4o6_interface_id_binding = MySqlBinding::createNull();
         }
 
         // Convert DHCPv4o6 subnet to text.
-        std::string dhcp4o6_subnet;
-        if (!subnet->get4o6().getSubnet4o6().get().first.isV6Zero() ||
-            (subnet->get4o6().getSubnet4o6().get().second != 128u)) {
+        Optional<std::string> dhcp4o6_subnet;
+        if (!subnet->get4o6().getSubnet4o6().unspecified() &&
+            (!subnet->get4o6().getSubnet4o6().get().first.isV6Zero() ||
+             (subnet->get4o6().getSubnet4o6().get().second != 128u))) {
             std::ostringstream s;
             s << subnet->get4o6().getSubnet4o6().get().first << "/"
               << static_cast<int>(subnet->get4o6().getSubnet4o6().get().second);
@@ -693,6 +720,16 @@ public:
             required_classes_element->add(Element::create(*required_class));
         }
 
+        // Create binding for host reservation mode.
+        MySqlBindingPtr hr_mode_binding;
+        auto hr_mode = subnet->getHostReservationMode();
+        if (!hr_mode.unspecified()) {
+            hr_mode_binding = MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>
+                                                                   (hr_mode.get()));
+        } else {
+            hr_mode_binding = MySqlBinding::createNull();
+        }
+
         // Create binding with shared network name if the subnet belongs to a
         // shared network.
         MySqlBindingPtr shared_network_binding;
@@ -725,19 +762,19 @@ public:
             MySqlBinding::createInteger<uint32_t>(subnet->getID()),
             MySqlBinding::createString(subnet->toText()),
             MySqlBinding::condCreateString(subnet->get4o6().getIface4o6()),
-            MySqlBinding::condCreateString(dhcp4o6_interface_id_text),
+            dhcp4o6_interface_id_binding,
             MySqlBinding::condCreateString(dhcp4o6_subnet),
             MySqlBinding::condCreateString(subnet->getFilename()),
             MySqlBinding::condCreateString(subnet->getClientClass()),
             MySqlBinding::condCreateString(subnet->getIface()),
-            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(subnet->getMatchClientId())),
+            MySqlBinding::condCreateBool(subnet->getMatchClientId()),
             MySqlBinding::createTimestamp(subnet->getModificationTime()),
-            MySqlBinding::condCreateInteger<uint32_t>(subnet->getSiaddr().get().toUint32()),
+            MySqlBinding::condCreateIPv4Address(subnet->getSiaddr()),
             createBinding(subnet->getT2()),
             createInputRelayBinding(subnet),
             createBinding(subnet->getT1()),
             createInputRequiredClassesBinding(subnet),
-            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(subnet->getHostReservationMode())),
+            hr_mode_binding,
             MySqlBinding::condCreateString(subnet->getSname()),
             shared_network_binding,
             createInputContextBinding(subnet),
@@ -971,11 +1008,15 @@ public:
                     last_network->allowClientClass(out_bindings[2]->getString());
                 }
                 // interface
-                last_network->setIface(out_bindings[3]->getStringOrDefault(""));
+                if (!out_bindings[3]->amNull()) {
+                    last_network->setIface(out_bindings[3]->getString());
+                }
 
                 // match_client_id
-                last_network->setMatchClientId(static_cast<bool>
-                    (out_bindings[4]->getIntegerOrDefault<uint8_t>(1)));
+                if (!out_bindings[4]->amNull()) {
+                    last_network->setMatchClientId(static_cast<bool>
+                                                   (out_bindings[4]->getInteger<uint8_t>()));
+                }
 
                 // modification_ts
                 last_network->setModificationTime(out_bindings[5]->getTimestamp());
@@ -1024,8 +1065,10 @@ public:
                 }
 
                 // reservation_mode
-                last_network->setHostReservationMode(static_cast<Subnet4::HRMode>
-                    (out_bindings[10]->getIntegerOrDefault<uint8_t>(Subnet4::HR_ALL)));
+                if (!out_bindings[10]->amNull()) {
+                    last_network->setHostReservationMode(static_cast<Subnet4::HRMode>
+                        (out_bindings[10]->getIntegerOrDefault<uint8_t>(Subnet4::HR_ALL)));
+                }
 
                 // user_context
                 ElementPtr user_context = out_bindings[11]->getJSON();
@@ -1136,18 +1179,27 @@ public:
 
         auto tag = getServerTag(server_selector, "creating or updating shared network");
 
+        // Create binding for host reservation mode.
+        MySqlBindingPtr hr_mode_binding;
+        auto hr_mode = shared_network->getHostReservationMode();
+        if (!hr_mode.unspecified()) {
+            hr_mode_binding = MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>
+                                                                   (hr_mode.get()));
+        } else {
+            hr_mode_binding = MySqlBinding::createNull();
+        }
+
         MySqlBindingCollection in_bindings = {
             MySqlBinding::createString(shared_network->getName()),
             MySqlBinding::condCreateString(shared_network->getClientClass()),
             MySqlBinding::condCreateString(shared_network->getIface()),
-            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(shared_network->getMatchClientId())),
+            MySqlBinding::condCreateBool(shared_network->getMatchClientId()),
             MySqlBinding::createTimestamp(shared_network->getModificationTime()),
             createBinding(shared_network->getT2()),
             createInputRelayBinding(shared_network),
             createBinding(shared_network->getT1()),
             createInputRequiredClassesBinding(shared_network),
-            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>
-                                                 (shared_network->getHostReservationMode())),
+            hr_mode_binding,
             createInputContextBinding(shared_network),
             createBinding(shared_network->getValid())
         };
index f0f1c6e49fbf12dfbf6a1555b5ee2a2a2376401d..4bcf68b73f06fca598b29bf6d145f966d8f49a55 100644 (file)
@@ -158,7 +158,9 @@ public:
         subnet->setValid(null_timer);
         test_subnets_.push_back(subnet);
 
-        subnet.reset(new Subnet4(IOAddress("192.0.4.0"), 24, 30, 40, 60, 4096));
+        // Add a subnet with all defaults.
+        subnet.reset(new Subnet4(IOAddress("192.0.4.0"), 24, Triplet<uint32_t>(),
+                                 Triplet<uint32_t>(), Triplet<uint32_t>(), 4096));
         test_subnets_.push_back(subnet);
     }
 
@@ -622,6 +624,75 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getSubnet4) {
     }
 }
 
+// Test that the information about unspecified optional parameters gets
+// propagated to the database.
+TEST_F(MySqlConfigBackendDHCPv4Test, getSubnet4WithDefaults) {
+    // Insert new subnet.
+    Subnet4Ptr subnet = test_subnets_[2];
+    cbptr_->createUpdateSubnet4(ServerSelector::ALL(), subnet);
+
+    // Fetch this subnet by subnet identifier.
+    Subnet4Ptr returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(),
+                                                    subnet->getID());
+    ASSERT_TRUE(returned_subnet);
+
+    EXPECT_TRUE(returned_subnet->getIface().unspecified());
+    EXPECT_TRUE(returned_subnet->getIface().empty());
+
+    EXPECT_TRUE(returned_subnet->getClientClass().unspecified());
+    EXPECT_TRUE(returned_subnet->getClientClass().empty());
+
+    EXPECT_TRUE(returned_subnet->getValid().unspecified());
+    EXPECT_EQ(0, returned_subnet->getValid().get());
+
+    EXPECT_TRUE(returned_subnet->getT1().unspecified());
+    EXPECT_EQ(0, returned_subnet->getT1().get());
+
+    EXPECT_TRUE(returned_subnet->getT2().unspecified());
+    EXPECT_EQ(0, returned_subnet->getT2().get());
+
+    EXPECT_TRUE(returned_subnet->getHostReservationMode().unspecified());
+    EXPECT_EQ(Network::HR_ALL, returned_subnet->getHostReservationMode().get());
+
+    EXPECT_TRUE(returned_subnet->getCalculateTeeTimes().unspecified());
+    EXPECT_FALSE(returned_subnet->getCalculateTeeTimes().get());
+
+    EXPECT_TRUE(returned_subnet->getT1Percent().unspecified());
+    EXPECT_EQ(0.0, returned_subnet->getT1Percent().get());
+
+    EXPECT_TRUE(returned_subnet->getT2Percent().unspecified());
+    EXPECT_EQ(0.0, returned_subnet->getT2Percent().get());
+
+    EXPECT_TRUE(returned_subnet->getMatchClientId().unspecified());
+    EXPECT_TRUE(returned_subnet->getMatchClientId().get());
+
+    EXPECT_TRUE(returned_subnet->getAuthoritative().unspecified());
+    EXPECT_FALSE(returned_subnet->getAuthoritative().get());
+
+    EXPECT_TRUE(returned_subnet->getSiaddr().unspecified());
+    EXPECT_TRUE(returned_subnet->getSiaddr().get().isV4Zero());
+
+    EXPECT_TRUE(returned_subnet->getSname().unspecified());
+    EXPECT_TRUE(returned_subnet->getSname().empty());
+
+    EXPECT_TRUE(returned_subnet->getFilename().unspecified());
+    EXPECT_TRUE(returned_subnet->getFilename().empty());
+
+    EXPECT_FALSE(returned_subnet->get4o6().enabled());
+
+    EXPECT_TRUE(returned_subnet->get4o6().getIface4o6().unspecified());
+    EXPECT_TRUE(returned_subnet->get4o6().getIface4o6().empty());
+
+    EXPECT_TRUE(returned_subnet->get4o6().getSubnet4o6().unspecified());
+    EXPECT_TRUE(returned_subnet->get4o6().getSubnet4o6().get().first.isV6Zero());
+    EXPECT_EQ(128, returned_subnet->get4o6().getSubnet4o6().get().second);
+
+    // The easiest way to verify whether the returned subnet matches the inserted
+    // subnet is to convert both to text.
+    EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
+}
+
+
 // Test that subnet can be associated with a shared network.
 TEST_F(MySqlConfigBackendDHCPv4Test, getSubnet4SharedNetwork) {
     Subnet4Ptr subnet = test_subnets_[0];
@@ -918,6 +989,53 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getSharedNetwork4) {
               returned_network->toElement()->str());
 }
 
+// Test that the information about unspecified optional parameters gets
+// propagated to the database.
+TEST_F(MySqlConfigBackendDHCPv4Test, getSharedNetwork4WithDefaults) {
+    // Insert new shared network.
+    SharedNetwork4Ptr shared_network = test_networks_[2];
+    cbptr_->createUpdateSharedNetwork4(ServerSelector::ALL(), shared_network);
+
+    // Fetch this shared network by name.
+    SharedNetwork4Ptr
+        returned_network = cbptr_->getSharedNetwork4(ServerSelector::ALL(),
+                                                     test_networks_[2]->getName());
+    ASSERT_TRUE(returned_network);
+
+    EXPECT_TRUE(returned_network->getIface().unspecified());
+    EXPECT_TRUE(returned_network->getIface().empty());
+
+    EXPECT_TRUE(returned_network->getClientClass().unspecified());
+    EXPECT_TRUE(returned_network->getClientClass().empty());
+
+    EXPECT_TRUE(returned_network->getValid().unspecified());
+    EXPECT_EQ(0, returned_network->getValid().get());
+
+    EXPECT_TRUE(returned_network->getT1().unspecified());
+    EXPECT_EQ(0, returned_network->getT1().get());
+
+    EXPECT_TRUE(returned_network->getT2().unspecified());
+    EXPECT_EQ(0, returned_network->getT2().get());
+
+    EXPECT_TRUE(returned_network->getHostReservationMode().unspecified());
+    EXPECT_EQ(Network::HR_ALL, returned_network->getHostReservationMode().get());
+
+    EXPECT_TRUE(returned_network->getCalculateTeeTimes().unspecified());
+    EXPECT_FALSE(returned_network->getCalculateTeeTimes().get());
+
+    EXPECT_TRUE(returned_network->getT1Percent().unspecified());
+    EXPECT_EQ(0.0, returned_network->getT1Percent().get());
+
+    EXPECT_TRUE(returned_network->getT2Percent().unspecified());
+    EXPECT_EQ(0.0, returned_network->getT2Percent().get());
+
+    EXPECT_TRUE(returned_network->getMatchClientId().unspecified());
+    EXPECT_TRUE(returned_network->getMatchClientId().get());
+
+    EXPECT_TRUE(returned_network->getAuthoritative().unspecified());
+    EXPECT_FALSE(returned_network->getAuthoritative().get());
+}
+
 // Test that all shared networks can be fetched.
 TEST_F(MySqlConfigBackendDHCPv4Test, getAllSharedNetworks4) {
     // Insert test shared networks into the database. Note that the second shared
index 737623011ffd2b47bd2236ce7628862b5e79906e..fbde51c7fe1744a4415c5c02a72272ecf7af68a4 100644 (file)
@@ -6,11 +6,15 @@
 
 #include <config.h>
 
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
 #include <boost/date_time/gregorian/gregorian.hpp>
 #include <mysql/mysql_binding.h>
 
 using namespace boost::posix_time;
+using namespace isc::asiolink;
 using namespace isc::data;
+using namespace isc::util;
 
 namespace isc {
 namespace db {
@@ -94,8 +98,8 @@ MySqlBinding::createString(const std::string& value) {
 }
 
 MySqlBindingPtr
-MySqlBinding::condCreateString(const std::string& value) {
-    return (value.empty() ? MySqlBinding::createNull() : createString(value));
+MySqlBinding::condCreateString(const Optional<std::string>& value) {
+    return (value.unspecified() ? MySqlBinding::createNull() : createString(value));
 }
 
 MySqlBindingPtr
@@ -105,6 +109,31 @@ MySqlBinding::createBlob(const unsigned long length) {
     return (binding);
 }
 
+MySqlBindingPtr
+MySqlBinding::condCreateBool(const util::Optional<bool>& value) {
+    if (value.unspecified()) {
+        return (MySqlBinding::createNull());
+    }
+
+    return (createInteger<uint8_t>(static_cast<uint8_t>(value.get())));
+}
+
+MySqlBindingPtr
+MySqlBinding::condCreateIPv4Address(const Optional<IOAddress>& value) {
+    // If the value is unspecified it doesn't matter what the value is.
+    if (value.unspecified()) {
+        return (MySqlBinding::createNull());
+    }
+
+    // Make sure it is an IPv4 address.
+    if (!value.get().isV4()) {
+        isc_throw(BadValue, "unable to create a MySQL binding: specified value '"
+                  << value.get().toText() << "' is not an IPv4 address");
+    }
+
+    return (createInteger<uint32_t>(value.get().toUint32()));
+}
+
 MySqlBindingPtr
 MySqlBinding::createTimestamp(const boost::posix_time::ptime& timestamp) {
     MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits<ptime>::column_type,
index d5bdaf3e942bb7ef6272e86b08b6757e4fd8f742..82f388e894fdd1a527193e3a909791154bcc2e70 100644 (file)
@@ -7,9 +7,11 @@
 #ifndef MYSQL_BINDING_H
 #define MYSQL_BINDING_H
 
+#include <asiolink/io_address.h>
 #include <cc/data.h>
 #include <database/database_connection.h>
 #include <exceptions/exceptions.h>
+#include <util/optional.h>
 #include <boost/date_time/posix_time/conversion.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/shared_ptr.hpp>
@@ -316,12 +318,12 @@ public:
     static MySqlBindingPtr createString(const std::string& value);
 
     /// @brief Conditionally creates binding of text type for sending
-    /// data if provided value is not empty.
+    /// data if provided value is unspecified.
     ///
     /// @param value String value to be sent to the database.
     ///
     /// @return Pointer to the created binding.
-    static MySqlBindingPtr condCreateString(const std::string& value);
+    static MySqlBindingPtr condCreateString(const util::Optional<std::string>& value);
 
     /// @brief Creates binding of blob type for receiving data.
     ///
@@ -380,19 +382,36 @@ public:
     }
 
     /// @brief Conditionally creates binding of numeric type for sending
-    /// data if provided value is not 0.
+    /// data if provided value is specified.
     ///
-    /// @tparam Numeric type corresponding to the binding type, e.g.
+    /// @tparam Numeric type corresponding to the binding type, e.g.
     /// @c uint8_t, @c uint16_t etc.
     ///
     /// @param value Numeric value to be sent to the database.
     ///
     /// @return Pointer to the created binding.
     template<typename T>
-    static MySqlBindingPtr condCreateInteger(T value) {
-        return (value == 0 ? createNull() : createInteger(value));
+    static MySqlBindingPtr condCreateInteger(const util::Optional<T>& value) {
+        return (value.unspecified() ? createNull() : createInteger<T>(value.get()));
     }
 
+    /// @brief Conditionally creates binding of @c uint8_t type representing
+    /// a boolean value if provided value is specified.
+    ///
+    /// @param value Boolean value for which the binding should be created.
+    ///
+    /// @return Pointer to the created binding.
+    static MySqlBindingPtr condCreateBool(const util::Optional<bool>& value);
+
+    /// @brief Conditionally creates binding of @c uint32_t type representing
+    /// an IPv4 address if provided value is specified.
+    ///
+    /// @param value @c IOAddress encapsulating an IPv4 address.
+    ///
+    /// @return Pointer to the created binding.
+    static MySqlBindingPtr
+    condCreateIPv4Address(const util::Optional<asiolink::IOAddress>& value);
+
     /// @brief Creates binding of timestamp type for receiving data.
     ///
     /// @return Pointer to the created binding.
index 776b0ef4326f5c52a3f1fed2009075dc7d9532a0..1a412cb816c0801572a0a88d5f4811649847affd 100644 (file)
@@ -6,13 +6,18 @@
 
 #include <config.h>
 
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
 #include <mysql/mysql_binding.h>
+#include <util/optional.h>
 #include <boost/date_time/gregorian/gregorian.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <gtest/gtest.h>
 
+using namespace isc::asiolink;
 using namespace isc::data;
 using namespace isc::db;
+using namespace isc::util;
 
 namespace {
 
@@ -25,9 +30,9 @@ TEST(MySqlBindingTest, defaultString) {
     EXPECT_EQ("bar", binding->getStringOrDefault("foo"));
 }
 
-// This test verifies that null binding is created for empty string.
+// This test verifies that null binding is created for unspecified string.
 TEST(MySqlBindingTest, conditionalString) {
-    auto binding = MySqlBinding::condCreateString("");
+    auto binding = MySqlBinding::condCreateString(Optional<std::string>());
     EXPECT_TRUE(binding->amNull());
 
     binding = MySqlBinding::condCreateString("foo");
@@ -72,9 +77,9 @@ TEST(MySqlBindingTest, defaultInteger) {
     EXPECT_EQ(1024, binding->getIntegerOrDefault<uint32_t>(123));
 }
 
-// This test verifies that null binding is created for 0 number.
+// This test verifies that null binding is created for unspecified number.
 TEST(MySqlBindingTest, conditionalInteger) {
-    auto binding = MySqlBinding::condCreateInteger<uint16_t>(0);
+    auto binding = MySqlBinding::condCreateInteger<uint16_t>(Optional<uint16_t>());
     EXPECT_TRUE(binding->amNull());
 
     binding = MySqlBinding::condCreateInteger<uint16_t>(1);
@@ -82,6 +87,34 @@ TEST(MySqlBindingTest, conditionalInteger) {
     EXPECT_EQ(1, binding->getInteger<uint16_t>());
 }
 
+// This test verifies that null binding is created for unspecified boolean
+// value.
+TEST(MySqlBindingTest, conditionalBoolean) {
+    auto binding = MySqlBinding::condCreateBool(Optional<bool>());
+    EXPECT_TRUE(binding->amNull());
+
+    binding = MySqlBinding::condCreateBool(false);
+    ASSERT_FALSE(binding->amNull());
+    EXPECT_EQ(0, binding->getInteger<uint8_t>());
+
+    binding = MySqlBinding::condCreateBool(true);
+    ASSERT_FALSE(binding->amNull());
+    EXPECT_NE(binding->getInteger<uint8_t>(), 0);
+}
+
+// This test verifies that null binding is created for unspecified address.
+TEST(MySqlBindingTest, conditionalIPv4Address) {
+    auto binding = MySqlBinding::condCreateIPv4Address(Optional<IOAddress>());
+    EXPECT_TRUE(binding->amNull());
+
+    binding = MySqlBinding::condCreateIPv4Address(IOAddress("192.0.2.1"));
+    ASSERT_FALSE(binding->amNull());
+    EXPECT_EQ(0xC0000201, binding->getInteger<uint32_t>());
+
+    EXPECT_THROW(MySqlBinding::condCreateIPv4Address(IOAddress("2001:db8:1::1")),
+                 isc::BadValue);
+}
+
 // 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::local_time();
index d4fdd06f3dece25ac6a5146ed0c69626814ac8a2..d8076313e56bd3444d5fec0f20f0566ed0d347f7 100644 (file)
@@ -1343,6 +1343,24 @@ ALTER TABLE dhcp6_options
     MODIFY COLUMN modification_ts TIMESTAMP NOT NULL
     DEFAULT CURRENT_TIMESTAMP;
 
+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
+    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
+    MODIFY COLUMN reservation_mode TINYINT(3) 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 7db2ec4fe33424bb4591783d4a27e6eb16a33c39..b3516d5567e74f4642b1bc8501d3fbf79861ca8f 100644 (file)
@@ -26,6 +26,24 @@ ALTER TABLE dhcp6_options
     MODIFY COLUMN modification_ts TIMESTAMP NOT NULL
     DEFAULT CURRENT_TIMESTAMP;
 
+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
+    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
+    MODIFY COLUMN reservation_mode TINYINT(3) 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