2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-01 06:25:34 +00:00

[#489,!250] Extend MySQL backend to store floating point values.

This commit is contained in:
Marcin Siodelski
2019-03-01 15:06:25 +01:00
parent ffde8dc785
commit 108a86cbc5
8 changed files with 267 additions and 20 deletions

View 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.

View 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 " \

View 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);

View 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()) {

View 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.
///

View 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 null binding is created for unspecified address.
// 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
// and the uint32_t binding is created for the specified address.
TEST(MySqlBindingTest, conditionalIPv4Address) {
auto binding = MySqlBinding::condCreateIPv4Address(Optional<IOAddress>());
EXPECT_TRUE(binding->amNull());

View 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

View 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;