mysql_lease_mgr.cc 80.5 KB
Newer Older
1
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
2 3 4 5 6 7 8 9 10 11 12 13 14 15
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#include <config.h>
16

17
#include <asiolink/io_address.h>
Stephen Morris's avatar
Stephen Morris committed
18
#include <dhcp/duid.h>
19
#include <dhcp/hwaddr.h>
20
#include <dhcpsrv/dhcpsrv_log.h>
21
#include <dhcpsrv/mysql_lease_mgr.h>
22

23
#include <boost/static_assert.hpp>
24
#include <mysqld_error.h>
25 26 27

#include <iostream>
#include <iomanip>
28
#include <sstream>
29 30 31
#include <string>
#include <time.h>

32
using namespace isc;
33
using namespace isc::dhcp;
34 35
using namespace std;

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
/// @file
///
/// This file holds the implementation of the Lease Manager using MySQL.  The
/// implementation uses MySQL's C API, as it comes as standard with the MySQL
/// client libraries.
///
/// In general, each of the database access methods corresponds to one SQL
/// statement.  To avoid the overhead of parsing a statement every time it is
/// used, when the database is opened "prepared statements" are created -
/// essentially doing the SQL parsing up front.  Every time a method is used
/// to access data, the corresponding prepared statement is referenced. Each
/// prepared statement contains a set of placeholders for data, each
/// placeholder being for:
///
/// - data being added to the database (as in adding or updating a lease)
/// - data being retrieved from the database (as in getting lease information)
/// - selection criteria used to determine which records to update/retrieve.
///
/// All such data is associated with the prepared statment using an array of
/// MYSQL_BIND structures.  Each element in the array corresponds to one
/// parameter in the prepared statement - the first element in the array is
/// associated with the first parameter, the second element with the second
/// parameter etc.
///
/// Within this file, the setting up of the MYSQL_BIND arrays for data being
/// passed to and retrieved from the database is handled in the
/// isc::dhcp::MySqlLease4Exchange and isc::dhcp::MySqlLease6Exchange classes.
/// The classes also hold intermediate variables required for exchanging some
/// of the data.
///
/// With these exchange objects in place, many of the methods follow similar
/// logic:
/// - Set up the MYSQL_BIND array for data being transferred to/from the
///   database.  For data being transferred to the database, some of the
///   data is extracted from the lease to intermediate variables, whilst
///   in other cases the MYSQL_BIND arrays point to the data in the lease.
/// - Set up the MYSQL_BIND array for the data selection parameters.
/// - Bind these arrays to the prepared statement.
/// - Execute the statement.
/// - If there is output, copy the data from the bound variables to the output
///   lease object.

78 79
namespace {
///@{
80 81

/// @brief Maximum size of database fields
82 83 84 85 86
///
/// The following constants define buffer sizes for variable length database
/// fields.  The values should be greater than or equal to the length set in
/// the schema definition.
///
87 88 89
/// 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.
90

91 92 93 94 95 96
/// @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;

97 98 99 100
/// @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
101
/// avoid any likely conflicts with variables in header files named TRUE or
102
/// FALSE.
103 104 105 106

const my_bool MLM_FALSE = 0;                ///< False value
const my_bool MLM_TRUE = 1;                 ///< True value

107 108 109 110 111 112
/// @brief Maximum length of the hostname stored in DNS.
///
/// This length is restricted by the length of the domain-name carried
/// in the Client FQDN %Option (see RFC4702 and RFC4704).
const size_t HOSTNAME_MAX_LEN = 255;

113
///@}
114 115 116

/// @brief MySQL Selection Statements
///
117 118 119
/// Each statement is associated with an index, which is used to reference the
/// associated prepared statement.

120 121 122 123 124 125
struct TaggedStatement {
    MySqlLeaseMgr::StatementIndex index;
    const char*                   text;
};

TaggedStatement tagged_statements[] = {
126 127
    {MySqlLeaseMgr::DELETE_LEASE4,
                    "DELETE FROM lease4 WHERE address = ?"},
128 129
    {MySqlLeaseMgr::DELETE_LEASE6,
                    "DELETE FROM lease6 WHERE address = ?"},
130 131
    {MySqlLeaseMgr::GET_LEASE4_ADDR,
                    "SELECT address, hwaddr, client_id, "
132 133
                        "valid_lifetime, expire, subnet_id, "
                        "fqdn_fwd, fqdn_rev, hostname "
134 135 136 137
                            "FROM lease4 "
                            "WHERE address = ?"},
    {MySqlLeaseMgr::GET_LEASE4_CLIENTID,
                    "SELECT address, hwaddr, client_id, "
138 139
                        "valid_lifetime, expire, subnet_id, "
                        "fqdn_fwd, fqdn_rev, hostname "
140 141
                            "FROM lease4 "
                            "WHERE client_id = ?"},
142 143
    {MySqlLeaseMgr::GET_LEASE4_CLIENTID_SUBID,
                    "SELECT address, hwaddr, client_id, "
144 145
                        "valid_lifetime, expire, subnet_id, "
                        "fqdn_fwd, fqdn_rev, hostname "
146 147
                            "FROM lease4 "
                            "WHERE client_id = ? AND subnet_id = ?"},
148 149
    {MySqlLeaseMgr::GET_LEASE4_HWADDR,
                    "SELECT address, hwaddr, client_id, "
150 151
                        "valid_lifetime, expire, subnet_id, "
                        "fqdn_fwd, fqdn_rev, hostname "
152 153
                            "FROM lease4 "
                            "WHERE hwaddr = ?"},
154 155
    {MySqlLeaseMgr::GET_LEASE4_HWADDR_SUBID,
                    "SELECT address, hwaddr, client_id, "
156 157
                        "valid_lifetime, expire, subnet_id, "
                        "fqdn_fwd, fqdn_rev, hostname "
158 159
                            "FROM lease4 "
                            "WHERE hwaddr = ? AND subnet_id = ?"},
160 161 162
    {MySqlLeaseMgr::GET_LEASE6_ADDR,
                    "SELECT address, duid, valid_lifetime, "
                        "expire, subnet_id, pref_lifetime, "
163 164
                        "lease_type, iaid, prefix_len, "
                        "fqdn_fwd, fqdn_rev, hostname "
165 166 167 168 169
                            "FROM lease6 "
                            "WHERE address = ?"},
    {MySqlLeaseMgr::GET_LEASE6_DUID_IAID,
                    "SELECT address, duid, valid_lifetime, "
                        "expire, subnet_id, pref_lifetime, "
170 171
                        "lease_type, iaid, prefix_len, "
                        "fqdn_fwd, fqdn_rev, hostname "
172 173 174 175 176
                            "FROM lease6 "
                            "WHERE duid = ? AND iaid = ?"},
    {MySqlLeaseMgr::GET_LEASE6_DUID_IAID_SUBID,
                    "SELECT address, duid, valid_lifetime, "
                        "expire, subnet_id, pref_lifetime, "
177 178
                        "lease_type, iaid, prefix_len, "
                        "fqdn_fwd, fqdn_rev, hostname "
179 180 181 182
                            "FROM lease6 "
                            "WHERE duid = ? AND iaid = ? AND subnet_id = ?"},
    {MySqlLeaseMgr::GET_VERSION,
                    "SELECT version, minor FROM schema_version"},
183 184
    {MySqlLeaseMgr::INSERT_LEASE4,
                    "INSERT INTO lease4(address, hwaddr, client_id, "
185 186 187
                        "valid_lifetime, expire, subnet_id, "
                        "fqdn_fwd, fqdn_rev, hostname) "
                            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
188 189 190
    {MySqlLeaseMgr::INSERT_LEASE6,
                    "INSERT INTO lease6(address, duid, valid_lifetime, "
                        "expire, subnet_id, pref_lifetime, "
191 192 193
                        "lease_type, iaid, prefix_len, "
                        "fqdn_fwd, fqdn_rev, hostname) "
                            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
194 195 196
    {MySqlLeaseMgr::UPDATE_LEASE4,
                    "UPDATE lease4 SET address = ?, hwaddr = ?, "
                        "client_id = ?, valid_lifetime = ?, expire = ?, "
197 198
                        "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, "
                        "hostname = ? "
199
                            "WHERE address = ?"},
200 201 202 203
    {MySqlLeaseMgr::UPDATE_LEASE6,
                    "UPDATE lease6 SET address = ?, duid = ?, "
                        "valid_lifetime = ?, expire = ?, subnet_id = ?, "
                        "pref_lifetime = ?, lease_type = ?, iaid = ?, "
204 205
                        "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, "
                        "hostname = ? "
206 207 208
                            "WHERE address = ?"},
    // End of list sentinel
    {MySqlLeaseMgr::NUM_STATEMENTS, NULL}
209 210
};

211 212
};  // Anonymous namespace

213 214


215 216 217
namespace isc {
namespace dhcp {

218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
/// @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);
    }
};


281 282 283 284 285
/// @brief Exchange MySQL and Lease4 Data
///
/// On any MySQL operation, arrays of MYSQL_BIND structures must be built to
/// describe the parameters in the prepared statements.  Where information is
/// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
286
/// structure is identical.  This class handles the creation of that array.
287 288
///
/// Owing to the MySQL API, the process requires some intermediate variables
289
/// to hold things like data length etc.  This object holds those variables.
290 291 292 293
///
/// @note There are no unit tests for this class.  It is tested indirectly
/// in all MySqlLeaseMgr::xxx4() calls where it is used.

294
class MySqlLease4Exchange : public MySqlLeaseExchange {
295
    /// @brief Set number of database columns for this lease structure
296
    static const size_t LEASE_COLUMNS = 9;
297

298
public:
299 300
    /// @brief Constructor
    ///
301
    /// The initialization of the variables here is only to satisfy cppcheck -
302
    /// all variables are initialized/set in the methods before they are used.
303 304
    MySqlLease4Exchange() : addr4_(0), hwaddr_length_(0), client_id_length_(0),
                            fqdn_fwd_(false), fqdn_rev_(false), hostname_length_(0) {
305 306
        memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
        memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
307
        memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
308
        std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
309

310 311 312 313 314 315 316
        // 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";
317 318 319 320
        columns_[6] = "fqdn_fwd";
        columns_[7] = "fqdn_rev";
        columns_[8] = "hostname";
        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
321 322 323 324
    }

    /// @brief Create MYSQL_BIND objects for Lease4 Pointer
    ///
325 326
    /// Fills in the MYSQL_BIND array for sending data in the Lease4 object to
    /// the database.
327
    ///
328 329
    /// @param lease Lease object to be added to the database.  None of the
    ///        fields in the lease are modified - the lease data is only read.
330 331 332
    ///
    /// @return Vector of MySQL BIND objects representing the data to be added.
    std::vector<MYSQL_BIND> createBindForSend(const Lease4Ptr& lease) {
333

334 335 336
        // Store lease object to ensure it remains valid.
        lease_ = lease;

337
        // Initialize prior to constructing the array of MYSQL_BIND structures.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
338 339 340 341
        // It sets all fields, including is_null, to zero, so we need to set
        // is_null only if it should be true. This gives up minor performance
        // benefit while being safe approach. For improved readability, the
        // code that explicitly sets is_null is there, but is commented out.
342 343
        memset(bind_, 0, sizeof(bind_));

344 345 346
        // Set up the structures for the various components of the lease4
        // structure.

347
        // Address: uint32_t
348
        // The address in the Lease structure is an IOAddress object.  Convert
349
        // this to an integer for storage.
350 351 352
        addr4_ = static_cast<uint32_t>(lease_->addr_);
        bind_[0].buffer_type = MYSQL_TYPE_LONG;
        bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
353
        bind_[0].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
354 355
        // bind_[0].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
356

357
        // hwaddr: varbinary(128)
358 359 360 361 362 363 364
        // For speed, we avoid copying the data into temporary storage and
        // instead extract it from the lease structure directly.
        hwaddr_length_ = lease_->hwaddr_.size();
        bind_[1].buffer_type = MYSQL_TYPE_BLOB;
        bind_[1].buffer = reinterpret_cast<char*>(&(lease_->hwaddr_[0]));
        bind_[1].buffer_length = hwaddr_length_;
        bind_[1].length = &hwaddr_length_;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
365 366
        // bind_[1].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
367

368
        // client_id: varbinary(128)
369 370 371 372 373 374 375
        if (lease_->client_id_) {
            client_id_ = lease_->client_id_->getClientId();
            client_id_length_ = client_id_.size();
            bind_[2].buffer_type = MYSQL_TYPE_BLOB;
            bind_[2].buffer = reinterpret_cast<char*>(&client_id_[0]);
            bind_[2].buffer_length = client_id_length_;
            bind_[2].length = &client_id_length_;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
376 377
            // bind_[2].is_null = &MLM_FALSE; // commented out for performance
                                              // reasons, see memset() above
378 379 380 381 382 383 384 385
        } else {
            bind_[2].buffer_type = MYSQL_TYPE_NULL;

            // According to http://dev.mysql.com/doc/refman/5.5/en/
            // c-api-prepared-statement-data-structures.html, the other
            // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
            // but let's set them to some sane values in case earlier versions
            // didn't have that assumption.
386
            client_id_null_ = MLM_TRUE;
387
            bind_[2].buffer = NULL;
388
            bind_[2].is_null = &client_id_null_;
389
        }
390 391 392 393

        // valid lifetime: unsigned int
        bind_[3].buffer_type = MYSQL_TYPE_LONG;
        bind_[3].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
394
        bind_[3].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
395 396
        // bind_[3].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
397 398 399 400

        // expire: timestamp
        // The lease structure holds the client last transmission time (cltt_)
        // For convenience for external tools, this is converted to lease
401
        // expiry time (expire).  The relationship is given by:
402 403 404
        //
        // expire = cltt_ + valid_lft_
        //
405
        // @todo Handle overflows - a large enough valid_lft_ could cause
406
        //       an overflow on a 32-bit system.
407 408 409 410 411
        MySqlLeaseMgr::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
                                             expire_);
        bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
        bind_[4].buffer = reinterpret_cast<char*>(&expire_);
        bind_[4].buffer_length = sizeof(expire_);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
412 413
        // bind_[4].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
414 415 416 417 418

        // subnet_id: unsigned int
        // Can use lease_->subnet_id_ directly as it is of type uint32_t.
        bind_[5].buffer_type = MYSQL_TYPE_LONG;
        bind_[5].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
419
        bind_[5].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
420 421
        // bind_[5].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
422

423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
        // fqdn_fwd: boolean
        bind_[6].buffer_type = MYSQL_TYPE_TINY;
        bind_[6].buffer = reinterpret_cast<char*>(&lease_->fqdn_fwd_);
        bind_[6].is_unsigned = MLM_TRUE;
        // bind_[6].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above

        // fqdn_rev: boolean
        bind_[7].buffer_type = MYSQL_TYPE_TINY;
        bind_[7].buffer = reinterpret_cast<char*>(&lease_->fqdn_rev_);
        bind_[7].is_unsigned = MLM_TRUE;
        // bind_[7].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above

        // hostname: varchar(255)
        bind_[8].buffer_type = MYSQL_TYPE_VARCHAR;
        bind_[8].buffer = const_cast<char*>(lease_->hostname_.c_str());
        bind_[8].buffer_length = lease_->hostname_.length();
        // bind_[8].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above

444 445 446 447
        // Add the error flags
        setErrorIndicators(bind_, error_, LEASE_COLUMNS);

        // .. and check that we have the numbers correct at compile time.
448
        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
449

450 451
        // Add the data to the vector.  Note the end element is one after the
        // end of the array.
452
        return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
453 454 455 456 457
    }

    /// @brief Create BIND array to receive data
    ///
    /// Creates a MYSQL_BIND array to receive Lease4 data from the database.
458
    /// After data is successfully received, getLeaseData() can be used to copy
459 460 461 462
    /// it to a Lease6 object.
    ///
    std::vector<MYSQL_BIND> createBindForReceive() {

463
        // Initialize MYSQL_BIND array.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
464 465 466 467
        // It sets all fields, including is_null, to zero, so we need to set
        // is_null only if it should be true. This gives up minor performance
        // benefit while being safe approach. For improved readability, the
        // code that explicitly sets is_null is there, but is commented out.
468 469 470 471 472
        memset(bind_, 0, sizeof(bind_));

        // address:  uint32_t
        bind_[0].buffer_type = MYSQL_TYPE_LONG;
        bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
473
        bind_[0].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
474 475
        // bind_[0].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
476 477 478 479 480 481 482

        // hwaddr: varbinary(20)
        hwaddr_length_ = sizeof(hwaddr_buffer_);
        bind_[1].buffer_type = MYSQL_TYPE_BLOB;
        bind_[1].buffer = reinterpret_cast<char*>(hwaddr_buffer_);
        bind_[1].buffer_length = hwaddr_length_;
        bind_[1].length = &hwaddr_length_;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
483 484
        // bind_[1].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
485 486 487 488 489 490 491

        // client_id: varbinary(128)
        client_id_length_ = sizeof(client_id_buffer_);
        bind_[2].buffer_type = MYSQL_TYPE_BLOB;
        bind_[2].buffer = reinterpret_cast<char*>(client_id_buffer_);
        bind_[2].buffer_length = client_id_length_;
        bind_[2].length = &client_id_length_;
492
        bind_[2].is_null = &client_id_null_;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
493 494
        // bind_[2].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
495 496 497 498

        // lease_time: unsigned int
        bind_[3].buffer_type = MYSQL_TYPE_LONG;
        bind_[3].buffer = reinterpret_cast<char*>(&valid_lifetime_);
499
        bind_[3].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
500 501
        // bind_[3].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
502 503 504 505 506

        // expire: timestamp
        bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
        bind_[4].buffer = reinterpret_cast<char*>(&expire_);
        bind_[4].buffer_length = sizeof(expire_);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
507 508
        // bind_[4].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
509 510 511 512

        // subnet_id: unsigned int
        bind_[5].buffer_type = MYSQL_TYPE_LONG;
        bind_[5].buffer = reinterpret_cast<char*>(&subnet_id_);
513
        bind_[5].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
514 515
        // bind_[5].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
516

517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
        // fqdn_fwd: boolean
        bind_[6].buffer_type = MYSQL_TYPE_TINY;
        bind_[6].buffer = reinterpret_cast<char*>(&fqdn_fwd_);
        bind_[6].is_unsigned = MLM_TRUE;
        // bind_[6].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above

        // fqdn_rev: boolean
        bind_[7].buffer_type = MYSQL_TYPE_TINY;
        bind_[7].buffer = reinterpret_cast<char*>(&fqdn_rev_);
        bind_[7].is_unsigned = MLM_TRUE;
        // bind_[7].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above

        // hostname: varchar(255)
        hostname_length_ = sizeof(hostname_buffer_);
        bind_[8].buffer_type = MYSQL_TYPE_STRING;
        bind_[8].buffer = reinterpret_cast<char*>(hostname_buffer_);
        bind_[8].buffer_length = hostname_length_;
        bind_[8].length = &hostname_length_;
        // bind_[8].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above

540 541 542 543
        // Add the error flags
        setErrorIndicators(bind_, error_, LEASE_COLUMNS);

        // .. and check that we have the numbers correct at compile time.
544
        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
545

546 547
        // Add the data to the vector.  Note the end element is one after the
        // end of the array.
548
        return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
549 550 551 552 553
    }

    /// @brief Copy Received Data into Lease6 Object
    ///
    /// Called after the MYSQL_BIND array created by createBindForReceive()
554
    /// has been used, this copies data from the internal member variables
555
    /// into a Lease4 objec.
556
    ///
557
    /// @return Lease4Ptr Pointer to a Lease6 object holding the relevant
558 559
    ///         data.
    Lease4Ptr getLeaseData() {
560 561
        // Convert times received from the database to times for the lease
        // structure
562 563 564
        time_t cltt = 0;
        MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);

565 566 567 568 569
        if (client_id_null_==MLM_TRUE) {
            // There's no client-id, so we pass client-id_length_ set to 0
            client_id_length_ = 0;
        }

570 571 572 573 574
        // Hostname is passed to Lease4 as a string object. We have to create
        // it from the buffer holding hostname and the buffer length.
        std::string hostname(hostname_buffer_,
                             hostname_buffer_ + hostname_length_);

575
        // note that T1 and T2 are not stored
576 577
        return (Lease4Ptr(new Lease4(addr4_, hwaddr_buffer_, hwaddr_length_,
                                     client_id_buffer_, client_id_length_,
578 579
                                     valid_lifetime_, 0, 0, cltt, subnet_id_,
                                     fqdn_fwd_, fqdn_rev_, hostname)));
580 581
    }

582 583 584 585 586 587 588 589 590 591 592 593 594 595
    /// @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));
    }

596
private:
597

598
    // Note: All array lengths are equal to the corresponding variable in the
599 600
    //       schema.
    // Note: Arrays are declared fixed length for speed of creation
601
    uint32_t        addr4_;             ///< IPv4 address
602 603 604
    MYSQL_BIND      bind_[LEASE_COLUMNS]; ///< Bind array
    std::string     columns_[LEASE_COLUMNS];///< Column names
    my_bool         error_[LEASE_COLUMNS];  ///< Error array
605
    std::vector<uint8_t> hwaddr_;       ///< Hardware address
606
    uint8_t         hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN];
607
                                        ///< Hardware address buffer
608 609
    unsigned long   hwaddr_length_;     ///< Hardware address length
    std::vector<uint8_t> client_id_;    ///< Client identification
Stephen Morris's avatar
Stephen Morris committed
610
    uint8_t         client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN];
611
                                        ///< Client ID buffer
612
    unsigned long   client_id_length_;  ///< Client ID address length
613 614
    my_bool         client_id_null_;    ///< Is Client ID null?

615 616 617
    MYSQL_TIME      expire_;            ///< Lease expiry time
    Lease4Ptr       lease_;             ///< Pointer to lease object
    uint32_t        subnet_id_;         ///< Subnet identification
618
    uint32_t        valid_lifetime_;    ///< Lease time
619 620 621 622 623 624 625 626 627

    my_bool         fqdn_fwd_;          ///< Has forward DNS update been
                                        ///< performed
    my_bool         fqdn_rev_;          ///< Has reverse DNS update been
                                        ///< performed
    char            hostname_buffer_[HOSTNAME_MAX_LEN];
                                        ///< Client hostname
    unsigned long   hostname_length_;   ///< Client hostname length

628 629 630 631
};



632
/// @brief Exchange MySQL and Lease6 Data
633
///
634 635
/// On any MySQL operation, arrays of MYSQL_BIND structures must be built to
/// describe the parameters in the prepared statements.  Where information is
636
/// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
637
/// structure is identical.  This class handles the creation of that array.
638
///
639
/// Owing to the MySQL API, the process requires some intermediate variables
640
/// to hold things like data length etc.  This object holds those variables.
641 642 643
///
/// @note There are no unit tests for this class.  It is tested indirectly
/// in all MySqlLeaseMgr::xxx6() calls where it is used.
644

645
class MySqlLease6Exchange : public MySqlLeaseExchange {
646
    /// @brief Set number of database columns for this lease structure
647
    static const size_t LEASE_COLUMNS = 12;
648

649 650
public:
    /// @brief Constructor
651
    ///
652 653
    /// The initialization of the variables here is nonly to satisfy cppcheck -
    /// all variables are initialized/set in the methods before they are used.
654 655 656
    MySqlLease6Exchange() : addr6_length_(0), duid_length_(0),
                            fqdn_fwd_(false), fqdn_rev_(false),
                            hostname_length_(0) {
657 658
        memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
        memset(duid_buffer_, 0, sizeof(duid_buffer_));
659
        memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
660
        std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
661

662
        // Set the column names (for error messages)
663 664 665 666 667 668 669 670 671 672 673 674
        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";
        columns_[9]  = "fqdn_fwd";
        columns_[10] = "fqdn_rev";
        columns_[11] = "hostname";
675
        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
676 677 678 679
    }

    /// @brief Create MYSQL_BIND objects for Lease6 Pointer
    ///
680 681
    /// Fills in the MYSQL_BIND array for sending data in the Lease4 object to
    /// the database.
682
    ///
683 684 685 686
    /// @param lease Lease object to be added to the database.
    ///
    /// @return Vector of MySQL BIND objects representing the data to be added.
    std::vector<MYSQL_BIND> createBindForSend(const Lease6Ptr& lease) {
687 688 689
        // Store lease object to ensure it remains valid.
        lease_ = lease;

690 691
        // Ensure bind_ array clear for constructing the MYSQL_BIND structures
        // for this lease.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
692 693 694 695
        // It sets all fields, including is_null, to zero, so we need to set
        // is_null only if it should be true. This gives up minor performance
        // benefit while being safe approach. For improved readability, the
        // code that explicitly sets is_null is there, but is commented out.
696 697
        memset(bind_, 0, sizeof(bind_));

698
        // address: varchar(39)
699 700 701
        addr6_ = lease_->addr_.toText();
        addr6_length_ = addr6_.size();

702 703 704
        // In the following statement, the string is being read.  However, the
        // MySQL C interface does not use "const", so the "buffer" element
        // is declared as "char*" instead of "const char*".  To resolve this,
705
        // the "const" is discarded.  (Note that the address of addr6_.c_str()
706
        // is guaranteed to be valid until the next non-const operation on
707 708
        // addr6_.)
        //
709 710 711
        // 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)
712
        // purely to get round the structures introduced by design of the
713 714 715
        // 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.
716 717 718 719
        bind_[0].buffer_type = MYSQL_TYPE_STRING;
        bind_[0].buffer = const_cast<char*>(addr6_.c_str());
        bind_[0].buffer_length = addr6_length_;
        bind_[0].length = &addr6_length_;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
720 721
        // bind_[0].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
722

723 724 725
        // duid: varchar(128)
        duid_ = lease_->duid_->getDuid();
        duid_length_ = duid_.size();
726

727 728 729 730
        bind_[1].buffer_type = MYSQL_TYPE_BLOB;
        bind_[1].buffer = reinterpret_cast<char*>(&(duid_[0]));
        bind_[1].buffer_length = duid_length_;
        bind_[1].length = &duid_length_;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
731 732
        // bind_[1].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
733

734
        // valid lifetime: unsigned int
735
        bind_[2].buffer_type = MYSQL_TYPE_LONG;
736
        bind_[2].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
737
        bind_[2].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
738 739
        // bind_[2].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
740 741

        // expire: timestamp
742 743 744 745 746
        // The lease structure holds the client last transmission time (cltt_)
        // For convenience for external tools, this is converted to lease
        /// expiry time (expire).  The relationship is given by:
        //
        // expire = cltt_ + valid_lft_
747
        //
748
        // @todo Handle overflows
749 750
        MySqlLeaseMgr::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
                                             expire_);
751 752 753
        bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
        bind_[3].buffer = reinterpret_cast<char*>(&expire_);
        bind_[3].buffer_length = sizeof(expire_);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
754 755
        // bind_[3].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
756 757 758

        // subnet_id: unsigned int
        // Can use lease_->subnet_id_ directly as it is of type uint32_t.
759 760
        bind_[4].buffer_type = MYSQL_TYPE_LONG;
        bind_[4].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
761
        bind_[4].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
762 763
        // bind_[4].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
764 765 766

        // pref_lifetime: unsigned int
        // Can use lease_->preferred_lft_ directly as it is of type uint32_t.
767 768
        bind_[5].buffer_type = MYSQL_TYPE_LONG;
        bind_[5].buffer = reinterpret_cast<char*>(&lease_->preferred_lft_);
769
        bind_[5].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
770 771
        // bind_[5].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
772 773

        // lease_type: tinyint
774
        // Must convert to uint8_t as lease_->type_ is a LeaseType variable.
775
        lease_type_ = lease_->type_;
776 777
        bind_[6].buffer_type = MYSQL_TYPE_TINY;
        bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
778
        bind_[6].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
779 780
        // bind_[6].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
781 782 783

        // iaid: unsigned int
        // Can use lease_->iaid_ directly as it is of type uint32_t.
784 785
        bind_[7].buffer_type = MYSQL_TYPE_LONG;
        bind_[7].buffer = reinterpret_cast<char*>(&lease_->iaid_);
786
        bind_[7].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
787 788
        // bind_[7].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
789 790 791

        // prefix_len: unsigned tinyint
        // Can use lease_->prefixlen_ directly as it is uint32_t.
792 793
        bind_[8].buffer_type = MYSQL_TYPE_TINY;
        bind_[8].buffer = reinterpret_cast<char*>(&lease_->prefixlen_);
794
        bind_[8].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
795 796
        // bind_[8].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
797

798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
        // fqdn_fwd: boolean
        bind_[9].buffer_type = MYSQL_TYPE_TINY;
        bind_[9].buffer = reinterpret_cast<char*>(&lease_->fqdn_fwd_);
        bind_[9].is_unsigned = MLM_TRUE;
        // bind_[7].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above

        // fqdn_rev: boolean
        bind_[10].buffer_type = MYSQL_TYPE_TINY;
        bind_[10].buffer = reinterpret_cast<char*>(&lease_->fqdn_rev_);
        bind_[10].is_unsigned = MLM_TRUE;
        // bind_[10].is_null = &MLM_FALSE; // commented out for performance
                                           // reasons, see memset() above

        // hostname: varchar(255)
        bind_[11].buffer_type = MYSQL_TYPE_VARCHAR;
        bind_[11].buffer = const_cast<char*>(lease_->hostname_.c_str());
        bind_[11].buffer_length = lease_->hostname_.length();
        // bind_[11].is_null = &MLM_FALSE; // commented out for performance
                                           // reasons, see memset() above

819 820 821 822
        // Add the error flags
        setErrorIndicators(bind_, error_, LEASE_COLUMNS);

        // .. and check that we have the numbers correct at compile time.
823
        BOOST_STATIC_ASSERT(11 < LEASE_COLUMNS);
824

825 826
        // Add the data to the vector.  Note the end element is one after the
        // end of the array.
827
        return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
828 829
    }

830 831 832 833 834 835
    /// @brief Create BIND array to receive data
    ///
    /// Creates a MYSQL_BIND array to receive Lease6 data from the database.
    /// After data is successfully received, getLeaseData() is used to copy
    /// it to a Lease6 object.
    ///
836 837
    /// @return Vector of MySQL BIND objects passed to the MySQL data retrieval
    ///         functions.
838
    std::vector<MYSQL_BIND> createBindForReceive() {
839

840
        // Initialize MYSQL_BIND array.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
841 842 843 844
        // It sets all fields, including is_null, to zero, so we need to set
        // is_null only if it should be true. This gives up minor performance
        // benefit while being safe approach. For improved readability, the
        // code that explicitly sets is_null is there, but is commented out.
845 846
        memset(bind_, 0, sizeof(bind_));

847
        // address:  varchar(39)
848
        // A Lease6_ address has a maximum of 39 characters.  The array is
849 850
        // one byte longer than this to guarantee that we can always null
        // terminate it whatever is returned.
851 852 853 854 855
        addr6_length_ = sizeof(addr6_buffer_) - 1;
        bind_[0].buffer_type = MYSQL_TYPE_STRING;
        bind_[0].buffer = addr6_buffer_;
        bind_[0].buffer_length = addr6_length_;
        bind_[0].length = &addr6_length_;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
856 857
        // bind_[0].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
858 859

        // client_id: varbinary(128)
860
        duid_length_ = sizeof(duid_buffer_);
861 862 863 864
        bind_[1].buffer_type = MYSQL_TYPE_BLOB;
        bind_[1].buffer = reinterpret_cast<char*>(duid_buffer_);
        bind_[1].buffer_length = duid_length_;
        bind_[1].length = &duid_length_;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
865 866
        // bind_[1].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
867 868

        // lease_time: unsigned int
869 870
        bind_[2].buffer_type = MYSQL_TYPE_LONG;
        bind_[2].buffer = reinterpret_cast<char*>(&valid_lifetime_);
871
        bind_[2].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
872 873
        // bind_[2].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
874 875

        // expire: timestamp
876 877 878
        bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
        bind_[3].buffer = reinterpret_cast<char*>(&expire_);
        bind_[3].buffer_length = sizeof(expire_);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
879 880
        // bind_[3].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
881 882

        // subnet_id: unsigned int
883 884
        bind_[4].buffer_type = MYSQL_TYPE_LONG;
        bind_[4].buffer = reinterpret_cast<char*>(&subnet_id_);
885
        bind_[4].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
886 887
        // bind_[4].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
888 889

        // pref_lifetime: unsigned int
890
        bind_[5].buffer_type = MYSQL_TYPE_LONG;
891
        bind_[5].buffer = reinterpret_cast<char*>(&pref_lifetime_);
892
        bind_[5].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
893 894
        // bind_[5].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
895

896 897 898
        // lease_type: tinyint
        bind_[6].buffer_type = MYSQL_TYPE_TINY;
        bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
899
        bind_[6].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
900 901
        // bind_[6].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
902

903 904 905
        // iaid: unsigned int
        bind_[7].buffer_type = MYSQL_TYPE_LONG;
        bind_[7].buffer = reinterpret_cast<char*>(&iaid_);
906
        bind_[7].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
907 908
        // bind_[7].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
909

910
        // prefix_len: unsigned tinyint
911 912
        bind_[8].buffer_type = MYSQL_TYPE_TINY;
        bind_[8].buffer = reinterpret_cast<char*>(&prefixlen_);
913
        bind_[8].is_unsigned = MLM_TRUE;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
914 915
        // bind_[8].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above
916

917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939
        // fqdn_fwd: boolean
        bind_[9].buffer_type = MYSQL_TYPE_TINY;
        bind_[9].buffer = reinterpret_cast<char*>(&fqdn_fwd_);
        bind_[9].is_unsigned = MLM_TRUE;
        // bind_[9].is_null = &MLM_FALSE; // commented out for performance
                                          // reasons, see memset() above

        // fqdn_rev: boolean
        bind_[10].buffer_type = MYSQL_TYPE_TINY;
        bind_[10].buffer = reinterpret_cast<char*>(&fqdn_rev_);
        bind_[10].is_unsigned = MLM_TRUE;
        // bind_[10].is_null = &MLM_FALSE; // commented out for performance
                                           // reasons, see memset() above

        // hostname: varchar(255)
        hostname_length_ = sizeof(hostname_buffer_);
        bind_[11].buffer_type = MYSQL_TYPE_STRING;
        bind_[11].buffer = reinterpret_cast<char*>(hostname_buffer_);
        bind_[11].buffer_length = hostname_length_;
        bind_[11].length = &hostname_length_;
        // bind_[11].is_null = &MLM_FALSE; // commented out for performance
                                           // reasons, see memset() above

940 941 942 943
        // Add the error flags
        setErrorIndicators(bind_, error_, LEASE_COLUMNS);

        // .. and check that we have the numbers correct at compile time.
944
        BOOST_STATIC_ASSERT(11 < LEASE_COLUMNS);
945

946 947
        // Add the data to the vector.  Note the end element is one after the
        // end of the array.
948
        return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
949 950 951 952 953
    }

    /// @brief Copy Received Data into Lease6 Object
    ///
    /// Called after the MYSQL_BIND array created by createBindForReceive()
954
    /// has been used, this copies data from the internal member variables
955 956 957 958 959
    /// into a Lease6 object.
    ///
    /// @return Lease6Ptr Pointer to a Lease6 object holding the relevant
    ///         data.
    ///
960
    /// @throw isc::BadValue Unable to convert Lease Type value in database
961
    Lease6Ptr getLeaseData() {
962 963
        // The address buffer is declared larger than the buffer size passed
        // to the access function so that we can always append a null byte.
964
        // Create the IOAddress object corresponding to the received data.
965 966
        addr6_buffer_[addr6_length_] = '\0';
        std::string address = addr6_buffer_;
967
        isc::asiolink::IOAddress addr(address);
968

969
        // Set the lease type in a variable of the appropriate data type, which
970
        // has been initialized with an arbitrary (but valid) value.
971
        Lease6::LeaseType type = Lease6::LEASE_IA_NA;
972 973
        switch (lease_type_) {
            case Lease6::LEASE_IA_NA:
974
                type = Lease6::LEASE_IA_NA;
975 976 977
                break;

            case Lease6::LEASE_IA_TA:
978
                type = Lease6::LEASE_IA_TA;
979 980 981
                break;

            case Lease6::LEASE_IA_PD:
982
                type = Lease6::LEASE_IA_PD;
983 984 985
                break;

            default:
986
                isc_throw(BadValue, "invalid lease type returned (" <<
987 988 989
                          static_cast<int>(lease_type_) << ") for lease with "
                          << "address " << address << ". Only 0, 1, or 2 are "
                          << "allowed.");
990
        }
991 992 993 994

        // Set up DUID,
        DuidPtr duid_ptr(new DUID(duid_buffer_, duid_length_));

995 996 997 998 999
        // Hostname is passed to Lease6 as a string object, so we have to
        // create it from the hostname buffer and length.
        std::string hostname(hostname_buffer_,
                             hostname_buffer_ + hostname_length_);

1000 1001 1002 1003
        // Create the lease and set the cltt (after converting from the
        // expire time retrieved from the database).
        Lease6Ptr result(new Lease6(type, addr, duid_ptr, iaid_,
                                    pref_lifetime_, valid_lifetime_, 0, 0,
1004 1005
                                    subnet_id_, fqdn_fwd_, fqdn_rev_,
                                    hostname, prefixlen_));
1006 1007 1008
        time_t cltt = 0;
        MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
        result->cltt_ = cltt;
1009 1010 1011 1012

        return (result);
    }

1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
    /// @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));
    }