2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-30 21:45:37 +00:00

[2404] First set of changes as a result of review

* Corrections of miscellaneous typos in comments
* Update database version to 1.0
* Handling of truncation while fetching data from the database
This commit is contained in:
Stephen Morris
2012-11-30 15:49:42 +00:00
parent 46cfa07792
commit ef364ccb84
6 changed files with 280 additions and 39 deletions

View File

@@ -17,8 +17,10 @@
#include <asiolink/io_address.h>
#include <dhcpsrv/mysql_lease_mgr.h>
#include <boost/static_assert.hpp>
#include <mysql/mysqld_error.h>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <string>
@@ -79,21 +81,39 @@ namespace {
/// fields. The values should be greater than or equal to the length set in
/// the schema definition.
///
/// The exception is the length of any VARCHAR fields: these should be set
/// greater than or equal to the length of the field plus 2: this allows for
/// the insertion of a trailing null regardless of whether the data returned
/// contains a trailing null (the documentation is not clear on this point).
/// The exception is the length of any VARCHAR fields: buffers for these should
/// be set greater than or equal to the length of the field plus 1: this allows
/// for the insertion of a trailing null whatever data is returned.
const size_t ADDRESS6_TEXT_MAX_LEN = 42; ///< Max size of a IPv6 text buffer
const size_t DUID_MAX_LEN = 128; ///< Max size of a DUID
const size_t HWADDR_MAX_LEN = 128; ///< Max size of a hardware address
const size_t CLIENT_ID_MAX_LEN = 128; ///< Max size of a client ID
/// @brief Maximum size of an IPv6 address represented as a text string.
///
/// This is 32 hexadecimal characters written in 8 groups of four, plus seven
/// colon separators.
const size_t ADDRESS6_TEXT_MAX_LEN = 39;
/// @brief Maximum size of a DUID.
const size_t DUID_MAX_LEN = 128;
/// @brief Maximum size of a hardware address.
const size_t HWADDR_MAX_LEN = 20;
/// @brief Maximum size of a client identification.
///
/// Note that the value is arbitrarily chosen: RFC 2131 does not specify an
/// upper limit, but this seems long enough.
const size_t CLIENT_ID_MAX_LEN = 128;
/// @brief Number of columns in Lease4 table
const size_t LEASE4_COLUMNS = 6;
/// @brief Number of columns in Lease6 table
const size_t LEASE6_COLUMNS = 9;
/// @brief MySQL True/False constants
///
/// Declare typed values so as to avoid problems of data conversion. These
/// are local to the file but are given the prefix MLM (MySql Lease Manager) to
/// avoid any likely conflicts with variables n header files named TRUE or
/// avoid any likely conflicts with variables in header files named TRUE or
/// FALSE.
const my_bool MLM_FALSE = 0; ///< False value
@@ -136,6 +156,7 @@ TaggedStatement tagged_statements[] = {
"valid_lifetime, expire, subnet_id "
"FROM lease4 "
"WHERE hwaddr = ?"},
{MySqlLeaseMgr::GET_LEASE4_HWADDR_SUBID,
"SELECT address, hwaddr, client_id, "
"valid_lifetime, expire, subnet_id "
@@ -190,6 +211,69 @@ TaggedStatement tagged_statements[] = {
namespace isc {
namespace dhcp {
/// @brief Common MySQL and Lease Data Methods
///
/// The MySqlLease4Exchange and MySqlLease6Exchange classes provide the
/// functionaility to set up binding information between variables in the
/// program and data extracted from the database. This class is the common
/// base to both of them, containing some common methods.
class MySqlLeaseExchange {
public:
/// @brief Set error indicators
///
/// Sets the error indicator for each of the MYSQL_BIND elements. It points
/// the "error" field within an element of the input array to the
/// corresponding element of the passed error array.
///
/// @param bind Array of BIND elements
/// @param error Array of error elements. If there is an error in getting
/// data associated with one of the "bind" elements, the
/// corresponding element in the error array is set to MLM_TRUE.
/// @param count Size of each of the arrays.
void setErrorIndicators(MYSQL_BIND* bind, my_bool* error, size_t count) {
for (size_t i = 0; i < count; ++i) {
error[i] = MLM_FALSE;
bind[i].error = reinterpret_cast<char*>(&error[i]);
}
}
/// @brief Return columns in error
///
/// If an error is returned from a fetch (in particular, a truncated
/// status), this method can be called to get the names of the fields in
/// error. It returns a string comprising the names of the fields
/// separated by commas. In the case of there being no error indicators
/// set, it returns the string "(None)".
///
/// @param error Array of error elements. An element is set to MLM_TRUE
/// if the corresponding column in the database is the source of
/// the error.
/// @param names Array of column names, the same size as the error array.
/// @param count Size of each of the arrays.
std::string getColumnsInError(my_bool* error, std::string* names,
size_t count) {
std::string result = "";
// Accumulate list of column names
for (size_t i = 0; i < count; ++i) {
if (error[i] == MLM_TRUE) {
if (!result.empty()) {
result += ", ";
}
result += names[i];
}
}
if (result.empty()) {
result = "(None)";
}
return (result);
}
};
/// @brief Exchange MySQL and Lease4 Data
///
/// On any MySQL operation, arrays of MYSQL_BIND structures must be built to
@@ -203,15 +287,28 @@ namespace dhcp {
/// @note There are no unit tests for this class. It is tested indirectly
/// in all MySqlLeaseMgr::xxx4() calls where it is used.
class MySqlLease4Exchange {
class MySqlLease4Exchange : public MySqlLeaseExchange {
public:
/// @brief Set number of columns in this lease structure
static const size_t LEASE_COLUMNS = LEASE4_COLUMNS;
/// @brief Constructor
///
/// The initialization of the variables here is nonly to satisfy cppcheck -
/// The initialization of the variables here is only to satisfy cppcheck -
/// all variables are initialized/set in the methods before they are used.
MySqlLease4Exchange() : addr4_(0), hwaddr_length_(0), client_id_length_(0) {
memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
// Set the column names (for error messages)
columns_[0] = "address";
columns_[1] = "hwaddr";
columns_[2] = "client_id";
columns_[3] = "valid_lifetime";
columns_[4] = "expire";
columns_[5] = "subnet_id";
BOOST_STATIC_ASSERT(5 < LEASE_COLUMNS);
}
/// @brief Create MYSQL_BIND objects for Lease4 Pointer
@@ -235,14 +332,14 @@ public:
// structure.
// Address: uint32_t
// The address in the Lease structre is an IOAddress object. Convert
// The address in the Lease structure is an IOAddress object. Convert
// this to an integer for storage.
addr4_ = static_cast<uint32_t>(lease_->addr_);
bind_[0].buffer_type = MYSQL_TYPE_LONG;
bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
bind_[0].is_unsigned = MLM_TRUE;
// hwaddr: varbinary
// hwaddr: varbinary(128)
// For speed, we avoid copying the data into temporary storage and
// instead extract it from the lease structure directly.
hwaddr_length_ = lease_->hwaddr_.size();
@@ -251,7 +348,7 @@ public:
bind_[1].buffer_length = hwaddr_length_;
bind_[1].length = &hwaddr_length_;
// client_id: varbinary
// client_id: varbinary(128)
client_id_ = lease_->client_id_->getClientId();
client_id_length_ = client_id_.size();
bind_[2].buffer_type = MYSQL_TYPE_BLOB;
@@ -285,9 +382,15 @@ public:
bind_[5].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
bind_[5].is_unsigned = MLM_TRUE;
// Add the error flags
setErrorIndicators(bind_, error_, LEASE_COLUMNS);
// .. and check that we have the numbers correct at compile time.
BOOST_STATIC_ASSERT(5 < LEASE_COLUMNS);
// Add the data to the vector. Note the end element is one after the
// end of the array.
return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[6]));
return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
}
/// @brief Create BIND array to receive data
@@ -335,9 +438,15 @@ public:
bind_[5].buffer = reinterpret_cast<char*>(&subnet_id_);
bind_[5].is_unsigned = MLM_TRUE;
// Add the error flags
setErrorIndicators(bind_, error_, LEASE_COLUMNS);
// .. and check that we have the numbers correct at compile time.
BOOST_STATIC_ASSERT(5 < LEASE_COLUMNS);
// Add the data to the vector. Note the end element is one after the
// end of the array.
return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[6]));
return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
}
/// @brief Copy Received Data into Lease6 Object
@@ -359,12 +468,29 @@ public:
valid_lifetime_, cltt, subnet_id_)));
}
/// @brief Return columns in error
///
/// If an error is returned from a fetch (in particular, a truncated
/// status), this method can be called to get the names of the fields in
/// error. It returns a string comprising the names of the fields
/// separated by commas. In the case of there being no error indicators
/// set, it returns the string "(None)".
///
/// @return Comma-separated list of columns in error, or the string
/// "(None)".
std::string getErrorColumns() {
return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
}
private:
// Note: All array lengths are equal to the corresponding variable in the
// schema.
// Note: Arrays are declared fixed length for speed of creation
uint32_t addr4_; ///< IPv4 address
MYSQL_BIND bind_[6]; ///< Bind array
MYSQL_BIND bind_[LEASE_COLUMNS]; ///< Bind array
std::string columns_[LEASE_COLUMNS];///< Column names
my_bool error_[LEASE_COLUMNS]; ///< Error array
std::vector<uint8_t> hwaddr_; ///< Hardware address
uint8_t hwaddr_buffer_[HWADDR_MAX_LEN];
///< Hardware address buffer
@@ -394,7 +520,10 @@ private:
/// @note There are no unit tests for this class. It is tested indirectly
/// in all MySqlLeaseMgr::xxx6() calls where it is used.
class MySqlLease6Exchange {
class MySqlLease6Exchange : public MySqlLeaseExchange {
/// @brief Set number of columns in this lease structure
static const size_t LEASE_COLUMNS = LEASE6_COLUMNS;
public:
/// @brief Constructor
///
@@ -403,6 +532,19 @@ public:
MySqlLease6Exchange() : addr6_length_(0), duid_length_(0) {
memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
memset(duid_buffer_, 0, sizeof(duid_buffer_));
std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
// Set the column names (for error messages)
columns_[0] = "address";
columns_[1] = "duid";
columns_[2] = "valid_lifetime";
columns_[3] = "expire";
columns_[4] = "subnet_id";
columns_[5] = "pref_lifetime";
columns_[6] = "lease_type";
columns_[7] = "iaid";
columns_[8] = "prefix_len";
BOOST_STATIC_ASSERT(5 < LEASE_COLUMNS);
}
/// @brief Create MYSQL_BIND objects for Lease6 Pointer
@@ -421,7 +563,7 @@ public:
// for this lease.
memset(bind_, 0, sizeof(bind_));
// address: varchar(40)
// address: varchar(39)
addr6_ = lease_->addr_.toText();
addr6_length_ = addr6_.size();
@@ -435,7 +577,7 @@ public:
// The const_cast could be avoided by copying the string to a writeable
// buffer and storing the address of that in the "buffer" element.
// However, this introduces a copy operation (with additional overhead)
// purely to get round the strictures introduced by design of the
// purely to get round the structures introduced by design of the
// MySQL interface (which uses the area pointed to by "buffer" as input
// when specifying query parameters and as output when retrieving data).
// For that reason, "const_cast" has been used.
@@ -465,7 +607,7 @@ public:
//
// expire = cltt_ + valid_lft_
//
// @TODO Handle overflows
// @todo Handle overflows
MySqlLeaseMgr::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
expire_);
bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
@@ -503,9 +645,15 @@ public:
bind_[8].buffer = reinterpret_cast<char*>(&lease_->prefixlen_);
bind_[8].is_unsigned = MLM_TRUE;
// Add the error flags
setErrorIndicators(bind_, error_, LEASE_COLUMNS);
// .. and check that we have the numbers correct at compile time.
BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
// Add the data to the vector. Note the end element is one after the
// end of the array.
return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[9]));
return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
}
/// @brief Create BIND array to receive data
@@ -521,10 +669,10 @@ public:
// Initialize MYSQL_BIND array.
memset(bind_, 0, sizeof(bind_));
// address: varchar
// address: varchar(39)
// A Lease6_ address has a maximum of 39 characters. The array is
// a few bytes longer than this to guarantee that we can always null
// terminate it.
// one byte longer than this to guarantee that we can always null
// terminate it whatever is returned.
addr6_length_ = sizeof(addr6_buffer_) - 1;
bind_[0].buffer_type = MYSQL_TYPE_STRING;
bind_[0].buffer = addr6_buffer_;
@@ -573,9 +721,15 @@ public:
bind_[8].buffer = reinterpret_cast<char*>(&prefixlen_);
bind_[8].is_unsigned = MLM_TRUE;
// Add the error flags
setErrorIndicators(bind_, error_, LEASE_COLUMNS);
// .. and check that we have the numbers correct at compile time.
BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
// Add the data to the vector. Note the end element is one after the
// end of the array.
return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[9]));
return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
}
/// @brief Copy Received Data into Lease6 Object
@@ -633,18 +787,34 @@ public:
return (result);
}
/// @brief Return columns in error
///
/// If an error is returned from a fetch (in particular, a truncated
/// status), this method can be called to get the names of the fields in
/// error. It returns a string comprising the names of the fields
/// separated by commas. In the case of there being no error indicators
/// set, it returns the string "(None)".
///
/// @return Comma-separated list of columns in error, or the string
/// "(None)".
std::string getErrorColumns() {
return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
}
private:
// Note: All array lengths are equal to the corresponding variable in the
// schema.
// Note: arrays are declared fixed length for speed of creation
std::string addr6_; ///< String form of address
char addr6_buffer_[ADDRESS6_TEXT_MAX_LEN]; ///< Character
char addr6_buffer_[ADDRESS6_TEXT_MAX_LEN + 1]; ///< Character
///< array form of V6 address
unsigned long addr6_length_; ///< Length of the address
MYSQL_BIND bind_[9]; ///< Bind array
MYSQL_BIND bind_[LEASE_COLUMNS]; ///< Bind array
std::string columns_[LEASE_COLUMNS];///< Column names
std::vector<uint8_t> duid_; ///< Client identification
uint8_t duid_buffer_[DUID_MAX_LEN]; ///< Buffer form of DUID
unsigned long duid_length_; ///< Length of the DUID
my_bool error_[LEASE_COLUMNS]; ///< Error indicators
MYSQL_TIME expire_; ///< Lease expiry time
uint32_t iaid_; ///< Identity association ID
Lease6Ptr lease_; ///< Pointer to lease object
@@ -1055,8 +1225,9 @@ void MySqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
// Error - unable to fetch results
checkError(status, stindex, "unable to fetch results");
} else if (status == MYSQL_DATA_TRUNCATED) {
// @TODO Handle truncation
;
// Data truncated - throw an exception indicating what was at fault
isc_throw(DataTruncated, "returned data truncated column affected: "
<< exchange->getErrorColumns());
}
}
@@ -1281,7 +1452,7 @@ MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid) const {
// Note that the const_cast could be avoided by copying the DUID to
// a writeable buffer and storing the address of that in the "buffer"
// element. However, this introduces a copy operation (with additional
// overhead) purely to get round the strictures introduced by design of
// overhead) purely to get round the structures introduced by design of
// the MySQL interface (which uses the area pointed to by "buffer" as
// input when specifying query parameters and as output when retrieving
// data). For that reason, "const_cast" has been used.