Browse Source

[3681] MySQLConnection class implemented

 - patch as sent by Adam Kalmus
Tomek Mrugalski 10 years ago
parent
commit
2a3ac3ef7d

+ 0 - 0
src/lib/dhcpsrv/lease.cc


+ 2 - 10
src/lib/dhcpsrv/lease_mgr.cc

@@ -15,6 +15,7 @@
 #include <config.h>
 
 #include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/mysql_connection.h>
 #include <exceptions/exceptions.h>
 
 #include <boost/foreach.hpp>
@@ -29,21 +30,12 @@
 
 #include <time.h>
 
+
 using namespace std;
 
 namespace isc {
 namespace dhcp {
 
-const time_t LeaseMgr::MAX_DB_TIME = 2147483647;
-
-std::string LeaseMgr::getParameter(const std::string& name) const {
-    ParameterMap::const_iterator param = parameters_.find(name);
-    if (param == parameters_.end()) {
-        isc_throw(BadValue, "Parameter not found");
-    }
-    return (param->second);
-}
-
 Lease6Ptr
 LeaseMgr::getLease6(Lease::Type type, const DUID& duid,
                     uint32_t iaid, SubnetID subnet_id) const {

+ 3 - 38
src/lib/dhcpsrv/lease_mgr.h

@@ -68,27 +68,6 @@
 namespace isc {
 namespace dhcp {
 
-/// @brief Exception thrown if name of database is not specified
-class NoDatabaseName : public Exception {
-public:
-    NoDatabaseName(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
-/// @brief Exception thrown on failure to open database
-class DbOpenError : public Exception {
-public:
-    DbOpenError(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
-/// @brief Exception thrown on failure to execute a database function
-class DbOperationError : public Exception {
-public:
-    DbOperationError(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
 /// @brief Multiple lease records found where one expected
 class MultipleRecords : public Exception {
 public:
@@ -133,19 +112,11 @@ public:
 /// of those classes for details.
 class LeaseMgr {
 public:
-    /// @brief Defines maximum value for time that can be reliably stored.
-    // If I'm still alive I'll be too old to care. You fix it.
-    static const time_t MAX_DB_TIME;
-
-    /// Database configuration parameter map
-    typedef std::map<std::string, std::string> ParameterMap;
-
     /// @brief Constructor
     ///
     /// @param parameters A data structure relating keywords and values
     ///        concerned with the database.
-    LeaseMgr(const ParameterMap& parameters)
-        : parameters_(parameters), io_service_(new asiolink::IOService())
+    LeaseMgr() : io_service_(new asiolink::IOService())
     {}
 
     /// @brief Destructor
@@ -392,8 +363,6 @@ public:
     /// As host reservation is outside of scope for 2012, support for hosts
     /// is currently postponed.
 
-    /// @brief returns value of the parameter
-    virtual std::string getParameter(const std::string& name) const;
 
     /// @brief Returns the interval at which the @c IOService events should
     /// be released.
@@ -420,17 +389,13 @@ public:
         return (io_service_);
     }
 
+
 private:
-    /// @brief list of parameters passed in dbconfig
-    ///
-    /// That will be mostly used for storing database name, username,
-    /// password and other parameters required for DB access. It is not
-    /// intended to keep any DHCP-related parameters.
-    ParameterMap parameters_;
 
     /// @brief Pointer to the IO service object used by the derived classes
     /// to trigger interval timers.
     asiolink::IOServicePtr io_service_;
+
 };
 
 }; // end of isc::dhcp namespace

+ 2 - 1
src/lib/dhcpsrv/memfile_lease_mgr.cc

@@ -17,6 +17,7 @@
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/lease_file_loader.h>
 #include <dhcpsrv/memfile_lease_mgr.h>
+#include <dhcpsrv/mysql_connection.h>
 #include <exceptions/exceptions.h>
 #include <util/pid_file.h>
 #include <util/process_spawn.h>
@@ -222,7 +223,7 @@ LFCSetup::getExitStatus() const {
 
 
 Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
-    : LeaseMgr(parameters),
+    : MySqlConnection(parameters),
       lfc_setup_(new LFCSetup(boost::bind(&Memfile_LeaseMgr::lfcCallback, this),
                               *getIOService()))
     {

+ 2 - 1
src/lib/dhcpsrv/memfile_lease_mgr.h

@@ -21,6 +21,7 @@
 #include <dhcpsrv/csv_lease_file6.h>
 #include <dhcpsrv/memfile_lease_storage.h>
 #include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/mysql_connection.h>
 #include <util/process_spawn.h>
 
 #include <boost/scoped_ptr.hpp>
@@ -85,7 +86,7 @@ class LFCSetup;
 /// is not specified, the default location in the installation
 /// directory is used: var/kea/kea-leases4.csv and
 /// var/kea/kea-leases6.csv.
-class Memfile_LeaseMgr : public LeaseMgr {
+class Memfile_LeaseMgr : public LeaseMgr, public MySqlConnection {
 public:
 
     /// @defgroup versions Specified memfile backend version.

+ 416 - 0
src/lib/dhcpsrv/mysql_connection.cc

@@ -0,0 +1,416 @@
+// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// 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 <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/mysql_connection.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/foreach.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <time.h>
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace std;
+
+
+namespace {
+
+const time_t MySqlConnection::MAX_DB_TIME = 2147483647;
+
+/// @brief MySQL Selection Statements
+///
+/// Each statement is associated with an index, which is used to reference the
+/// associated prepared statement.
+
+struct TaggedStatement {
+    MySqlConnection::StatementIndex index;
+    const char*                   text;
+};
+
+TaggedStatement tagged_statements[] = {
+    {MySqlLeaseMgr::DELETE_LEASE4,
+                    "DELETE FROM lease4 WHERE address = ?"},
+    {MySqlLeaseMgr::DELETE_LEASE6,
+                    "DELETE FROM lease6 WHERE address = ?"},
+    {MySqlLeaseMgr::GET_LEASE4_ADDR,
+                    "SELECT address, hwaddr, client_id, "
+                        "valid_lifetime, expire, subnet_id, "
+                        "fqdn_fwd, fqdn_rev, hostname "
+                            "FROM lease4 "
+                            "WHERE address = ?"},
+    {MySqlLeaseMgr::GET_LEASE4_CLIENTID,
+                    "SELECT address, hwaddr, client_id, "
+                        "valid_lifetime, expire, subnet_id, "
+                        "fqdn_fwd, fqdn_rev, hostname "
+                            "FROM lease4 "
+                            "WHERE client_id = ?"},
+    {MySqlLeaseMgr::GET_LEASE4_CLIENTID_SUBID,
+                    "SELECT address, hwaddr, client_id, "
+                        "valid_lifetime, expire, subnet_id, "
+                        "fqdn_fwd, fqdn_rev, hostname "
+                            "FROM lease4 "
+                            "WHERE client_id = ? AND subnet_id = ?"},
+    {MySqlLeaseMgr::GET_LEASE4_HWADDR,
+                    "SELECT address, hwaddr, client_id, "
+                        "valid_lifetime, expire, subnet_id, "
+                        "fqdn_fwd, fqdn_rev, hostname "
+                            "FROM lease4 "
+                            "WHERE hwaddr = ?"},
+    {MySqlLeaseMgr::GET_LEASE4_HWADDR_SUBID,
+                    "SELECT address, hwaddr, client_id, "
+                        "valid_lifetime, expire, subnet_id, "
+                        "fqdn_fwd, fqdn_rev, hostname "
+                            "FROM lease4 "
+                            "WHERE hwaddr = ? AND subnet_id = ?"},
+    {MySqlLeaseMgr::GET_LEASE6_ADDR,
+                    "SELECT address, duid, valid_lifetime, "
+                        "expire, subnet_id, pref_lifetime, "
+                        "lease_type, iaid, prefix_len, "
+                        "fqdn_fwd, fqdn_rev, hostname, "
+                        "hwaddr, hwtype, hwaddr_source "
+                            "FROM lease6 "
+                            "WHERE address = ? AND lease_type = ?"},
+    {MySqlLeaseMgr::GET_LEASE6_DUID_IAID,
+                    "SELECT address, duid, valid_lifetime, "
+                        "expire, subnet_id, pref_lifetime, "
+                        "lease_type, iaid, prefix_len, "
+                        "fqdn_fwd, fqdn_rev, hostname, "
+                        "hwaddr, hwtype, hwaddr_source "
+                            "FROM lease6 "
+                            "WHERE duid = ? AND iaid = ? AND lease_type = ?"},
+    {MySqlLeaseMgr::GET_LEASE6_DUID_IAID_SUBID,
+                    "SELECT address, duid, valid_lifetime, "
+                        "expire, subnet_id, pref_lifetime, "
+                        "lease_type, iaid, prefix_len, "
+                        "fqdn_fwd, fqdn_rev, hostname, "
+                        "hwaddr, hwtype, hwaddr_source "
+                            "FROM lease6 "
+                            "WHERE duid = ? AND iaid = ? AND subnet_id = ? "
+                            "AND lease_type = ?"},
+    {MySqlLeaseMgr::GET_VERSION,
+                    "SELECT version, minor FROM schema_version"},
+    {MySqlLeaseMgr::INSERT_LEASE4,
+                    "INSERT INTO lease4(address, hwaddr, client_id, "
+                        "valid_lifetime, expire, subnet_id, "
+                        "fqdn_fwd, fqdn_rev, hostname) "
+                            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
+    {MySqlLeaseMgr::INSERT_LEASE6,
+                    "INSERT INTO lease6(address, duid, valid_lifetime, "
+                        "expire, subnet_id, pref_lifetime, "
+                        "lease_type, iaid, prefix_len, "
+                        "fqdn_fwd, fqdn_rev, hostname, "
+                        "hwaddr, hwtype, hwaddr_source) "
+                            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
+    {MySqlLeaseMgr::UPDATE_LEASE4,
+                    "UPDATE lease4 SET address = ?, hwaddr = ?, "
+                        "client_id = ?, valid_lifetime = ?, expire = ?, "
+                        "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, "
+                        "hostname = ? "
+                            "WHERE address = ?"},
+    {MySqlLeaseMgr::UPDATE_LEASE6,
+                    "UPDATE lease6 SET address = ?, duid = ?, "
+                        "valid_lifetime = ?, expire = ?, subnet_id = ?, "
+                        "pref_lifetime = ?, lease_type = ?, iaid = ?, "
+                        "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, "
+                        "hostname = ?, hwaddr = ?, hwtype = ?, hwaddr_source = ? "
+                            "WHERE address = ?"},
+    // End of list sentinel
+    {MySqlLeaseMgr::NUM_STATEMENTS, NULL}
+};
+
+
+};  // Anonymous namespace
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Maximum size of database fields
+///
+/// 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.
+///
+/// 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.
+
+/// @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 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 in header files named TRUE or
+/// FALSE.
+
+const my_bool MLM_FALSE = 0;                ///< False value
+const my_bool MLM_TRUE = 1;                 ///< True value
+
+/// @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;
+
+///@}
+
+
+std::string MySqlConnection::getParameter(const std::string& name) const {
+    ParameterMap::const_iterator param = parameters_.find(name);
+    if (param == parameters_.end()) {
+        isc_throw(BadValue, "Parameter not found");
+    }
+    return (param->second);
+}
+
+// Open the database using the parameters passed to the constructor.
+
+void
+MySqlConnection::openDatabase() {
+
+    // Set up the values of the parameters
+    const char* host = "localhost";
+    string shost;
+    try {
+        shost = getParameter("host");
+        host = shost.c_str();
+    } catch (...) {
+        // No host.  Fine, we'll use "localhost"
+    }
+
+    const char* user = NULL;
+    string suser;
+    try {
+        suser = getParameter("user");
+        user = suser.c_str();
+    } catch (...) {
+        // No user.  Fine, we'll use NULL
+    }
+
+    const char* password = NULL;
+    string spassword;
+    try {
+        spassword = getParameter("password");
+        password = spassword.c_str();
+    } catch (...) {
+        // No password.  Fine, we'll use NULL
+    }
+
+    const char* name = NULL;
+    string sname;
+    try {
+        sname = getParameter("name");
+        name = sname.c_str();
+    } catch (...) {
+        // No database name.  Throw a "NoName" exception
+        isc_throw(NoDatabaseName, "must specified a name for the database");
+    }
+
+    // Set options for the connection:
+    //
+    // Automatic reconnection: after a period of inactivity, the client will
+    // disconnect from the database.  This option causes it to automatically
+    // reconnect when another operation is about to be done.
+    my_bool auto_reconnect = MLM_TRUE;
+    int result = mysql_options(mysql_, MYSQL_OPT_RECONNECT, &auto_reconnect);
+    if (result != 0) {
+        isc_throw(DbOpenError, "unable to set auto-reconnect option: " <<
+                  mysql_error(mysql_));
+    }
+
+    // Set SQL mode options for the connection:  SQL mode governs how what
+    // constitutes insertable data for a given column, and how to handle
+    // invalid data.  We want to ensure we get the strictest behavior and
+    // to reject invalid data with an error.
+    const char *sql_mode = "SET SESSION sql_mode ='STRICT_ALL_TABLES'";
+    result = mysql_options(mysql_, MYSQL_INIT_COMMAND, sql_mode);
+    if (result != 0) {
+        isc_throw(DbOpenError, "unable to set SQL mode options: " <<
+                  mysql_error(mysql_));
+    }
+
+    // Open the database.
+    //
+    // The option CLIENT_FOUND_ROWS is specified so that in an UPDATE,
+    // the affected rows are the number of rows found that match the
+    // WHERE clause of the SQL statement, not the rows changed.  The reason
+    // here is that MySQL apparently does not update a row if data has not
+    // changed and so the "affected rows" (retrievable from MySQL) is zero.
+    // This makes it hard to distinguish whether the UPDATE changed no rows
+    // because no row matching the WHERE clause was found, or because a
+    // row was found but no data was altered.
+    MYSQL* status = mysql_real_connect(mysql_, host, user, password, name,
+                                       0, NULL, CLIENT_FOUND_ROWS);
+    if (status != mysql_) {
+        isc_throw(DbOpenError, mysql_error(mysql_));
+    }
+}
+
+
+// Prepared statement setup.  The textual form of an SQL statement is stored
+// in a vector of strings (text_statements_) and is used in the output of
+// error messages.  The SQL statement is also compiled into a "prepared
+// statement" (stored in statements_), which avoids the overhead of compilation
+// during use.  As prepared statements have resources allocated to them, the
+// class destructor explicitly destroys them.
+
+void
+MySqlConnection::prepareStatement(StatementIndex index, const char* text) {
+    // Validate that there is space for the statement in the statements array
+    // and that nothing has been placed there before.
+    if ((index >= this->statements_.size()) || (this->statements_[index] != NULL)) {
+        isc_throw(InvalidParameter, "invalid prepared statement index (" <<
+                  static_cast<int>(index) << ") or indexed prepared " <<
+                  "statement is not null");
+    }
+
+    // All OK, so prepare the statement
+    this->text_statements_[index] = std::string(text);
+    this->statements_[index] = mysql_stmt_init(mysql_);
+    if (this->statements_[index] == NULL) {
+        isc_throw(DbOperationError, "unable to allocate MySQL prepared "
+                  "statement structure, reason: " << mysql_error(mysql_));
+    }
+
+    int status = mysql_stmt_prepare(this->statements_[index], text, strlen(text));
+    if (status != 0) {
+        isc_throw(DbOperationError, "unable to prepare MySQL statement <" <<
+                  text << ">, reason: " << mysql_error(mysql_));
+    }
+}
+
+
+void
+MySqlConnection::prepareStatements() {
+    // Allocate space for all statements
+    statements_.clear();
+    statements_.resize(NUM_STATEMENTS, NULL);
+
+    text_statements_.clear();
+    text_statements_.resize(NUM_STATEMENTS, std::string(""));
+
+
+    // Created the MySQL prepared statements for each DML statement.
+    for (int i = 0; tagged_statements[i].text != NULL; ++i) {
+        prepareStatement(tagged_statements[i].index,
+                         tagged_statements[i].text);
+    }
+}
+
+
+
+// Miscellaneous database methods.
+
+std::string
+MySqlConnection::getName() const {
+    std::string name = "";
+    try {
+        name = getParameter("name");
+    } catch (...) {
+        // Return an empty name
+    }
+    return (name);
+}
+
+
+std::string
+MySqlConnection::getDescription() const {
+    return (std::string("MySQL Database"));
+}
+
+
+std::pair<uint32_t, uint32_t>
+MySqlConnection::getVersion() const {
+    const StatementIndex stindex = GET_VERSION;
+
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_MYSQL_GET_VERSION);
+
+    uint32_t    major;      // Major version number
+    uint32_t    minor;      // Minor version number
+
+    // Execute the prepared statement
+    int status = mysql_stmt_execute(statements_[stindex]);
+    if (status != 0) {
+        isc_throw(DbOperationError, "unable to execute <"
+                  << text_statements_[stindex] << "> - reason: " <<
+                  mysql_error(mysql_));
+    }
+
+    // Bind the output of the statement to the appropriate variables.
+    MYSQL_BIND bind[2];
+    memset(bind, 0, sizeof(bind));
+
+    bind[0].buffer_type = MYSQL_TYPE_LONG;
+    bind[0].is_unsigned = 1;
+    bind[0].buffer = &major;
+    bind[0].buffer_length = sizeof(major);
+
+    bind[1].buffer_type = MYSQL_TYPE_LONG;
+    bind[1].is_unsigned = 1;
+    bind[1].buffer = &minor;
+    bind[1].buffer_length = sizeof(minor);
+
+    status = mysql_stmt_bind_result(statements_[stindex], bind);
+    if (status != 0) {
+        isc_throw(DbOperationError, "unable to bind result set: " <<
+                  mysql_error(mysql_));
+    }
+
+    // Fetch the data and set up the "release" object to release associated
+    // resources when this method exits then retrieve the data.
+    MySqlFreeResult fetch_release(statements_[stindex]);
+    status = mysql_stmt_fetch(statements_[stindex]);
+    if (status != 0) {
+        isc_throw(DbOperationError, "unable to obtain result set: " <<
+                  mysql_error(mysql_));
+    }
+
+    return (std::make_pair(major, minor));
+}
+
+
+void
+MySqlConnection::commit() {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_COMMIT);
+    if (mysql_commit(mysql_) != 0) {
+        isc_throw(DbOperationError, "commit failed: " << mysql_error(mysql_));
+    }
+}
+
+
+void
+MySqlConnection::rollback() {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_ROLLBACK);
+    if (mysql_rollback(mysql_) != 0) {
+        isc_throw(DbOperationError, "rollback failed: " << mysql_error(mysql_));
+    }
+}
+
+} // namespace isc::dhcp
+} // namespace isc

+ 294 - 0
src/lib/dhcpsrv/mysql_connection.h

@@ -0,0 +1,294 @@
+// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// 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.
+
+#ifndef MYSQL_CONNECTION_H
+#define MYSQL_CONNECTION_H
+
+#include <dhcp/hwaddr.h>
+#include <dhcpsrv/lease_mgr.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/utility.hpp>
+#include <mysql/mysql.h>		// TODO poprawić przed oddaniem
+
+#include <time.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Exception thrown if name of database is not specified
+class NoDatabaseName : public Exception {
+public:
+    NoDatabaseName(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief Exception thrown on failure to open database
+class DbOpenError : public Exception {
+public:
+    DbOpenError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief Exception thrown on failure to execute a database function
+class DbOperationError : public Exception {
+public:
+    DbOperationError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief Fetch and Release MySQL Results
+///
+/// When a MySQL statement is expected, to fetch the results the function
+/// mysql_stmt_fetch() must be called.  As well as getting data, this
+/// allocates internal state.  Subsequent calls to mysql_stmt_fetch can be
+/// made, but when all the data is retrieved, mysql_stmt_free_result must be
+/// called to free up the resources allocated.
+///
+/// Created prior to the first fetch, this class's destructor calls
+/// mysql_stmt_free_result, so eliminating the need for an explicit release
+/// in the method calling mysql_stmt_free_result.  In this way, it guarantees
+/// that the resources are released even if the MySqlLeaseMgr method concerned
+/// exits via an exception.
+
+class MySqlFreeResult {
+public:
+
+    /// @brief Constructor
+    ///
+    /// Store the pointer to the statement for which data is being fetched.
+    ///
+    /// Note that according to the MySQL documentation, mysql_stmt_free_result
+    /// only releases resources if a cursor has been allocated for the
+    /// statement.  This implies that it is a no-op if none have been.  Either
+    /// way, any error from mysql_stmt_free_result is ignored. (Generating
+    /// an exception is not much help, as it will only confuse things if the
+    /// method calling mysql_stmt_fetch is exiting via an exception.)
+    MySqlFreeResult(MYSQL_STMT* statement) : statement_(statement)
+    {}
+
+    /// @brief Destructor
+    ///
+    /// Frees up fetch context if a fetch has been successfully executed.
+    ~MySqlFreeResult() {
+        (void) mysql_stmt_free_result(statement_);
+    }
+
+private:
+    MYSQL_STMT*     statement_;     ///< Statement for which results are freed
+};
+
+/// @brief MySQL Handle Holder
+///
+/// Small RAII object for safer initialization, will close the database
+/// connection upon destruction.  This means that if an exception is thrown
+/// during database initialization, resources allocated to the database are
+/// guaranteed to be freed.
+///
+/// It makes no sense to copy an object of this class.  After the copy, both
+/// objects would contain pointers to the same MySql context object.  The
+/// destruction of one would invalid the context in the remaining object.
+/// For this reason, the class is declared noncopyable.
+class MySqlHolder : public boost::noncopyable {
+public:
+
+    /// @brief Constructor
+    ///
+    /// Initialize MySql and store the associated context object.
+    ///
+    /// @throw DbOpenError Unable to initialize MySql handle.
+    MySqlHolder() : mysql_(mysql_init(NULL)) {
+        if (mysql_ == NULL) {
+            isc_throw(DbOpenError, "unable to initialize MySQL");
+        }
+    }
+
+    /// @brief Destructor
+    ///
+    /// Frees up resources allocated by the initialization of MySql.
+    ~MySqlHolder() {
+        if (mysql_ != NULL) {
+            mysql_close(mysql_);
+        }
+        // The library itself shouldn't be needed anymore
+        mysql_library_end();
+    }
+
+    /// @brief Conversion Operator
+    ///
+    /// Allows the MySqlHolder object to be passed as the context argument to
+    /// mysql_xxx functions.
+    operator MYSQL*() const {
+        return (mysql_);
+    }
+
+private:
+    MYSQL* mysql_;      ///< Initialization context
+};
+
+// Define the current database schema values
+
+const uint32_t CURRENT_VERSION_VERSION = 3;		// version 3: adding host managment features
+const uint32_t CURRENT_VERSION_MINOR = 0;
+
+
+
+class MySqlConnection {
+public:
+
+    /// @brief Defines maximum value for time that can be reliably stored.
+    // If I'm still alive I'll be too old to care. You fix it.
+    static const time_t MAX_DB_TIME;
+
+    /// Database configuration parameter map
+    typedef std::map<std::string, std::string> ParameterMap;
+
+	MySqlConnection(const ParameterMap& parameters)
+    	: parameters_(parameters)
+    {}
+
+	virtual ~MySqlConnection()
+	{}
+
+    /// @brief Statement Tags
+    ///
+    /// The contents of the enum are indexes into the list of SQL statements
+    enum StatementIndex {
+        DELETE_LEASE4,              // Delete from lease4 by address
+        DELETE_LEASE6,              // Delete from lease6 by address
+        GET_LEASE4_ADDR,            // Get lease4 by address
+        GET_LEASE4_CLIENTID,        // Get lease4 by client ID
+        GET_LEASE4_CLIENTID_SUBID,  // Get lease4 by client ID & subnet ID
+        GET_LEASE4_HWADDR,          // Get lease4 by HW address
+        GET_LEASE4_HWADDR_SUBID,    // Get lease4 by HW address & subnet ID
+        GET_LEASE6_ADDR,            // Get lease6 by address
+        GET_LEASE6_DUID_IAID,       // Get lease6 by DUID and IAID
+        GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and subnet ID
+        GET_VERSION,                // Obtain version number
+        INSERT_LEASE4,              // Add entry to lease4 table
+        INSERT_LEASE6,              // Add entry to lease6 table
+        UPDATE_LEASE4,              // Update a Lease4 entry
+        UPDATE_LEASE6,              // Update a Lease6 entry
+        NUM_STATEMENTS              // Number of statements
+    };
+
+    /// @brief returns value of the parameter
+    virtual std::string getParameter(const std::string& name) const;
+
+    /// @brief Return backend type
+    ///
+    /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
+    ///
+    /// @return Type of the backend.
+    virtual std::string getType() const {
+        return (std::string("mysql"));
+    }
+
+    /// @brief Returns backend name.
+    ///
+    /// Each backend have specific name, e.g. "mysql" or "sqlite".
+    ///
+    /// @return Name of the backend.
+    virtual std::string getName() const;
+
+    /// @brief Returns description of the backend.
+    ///
+    /// This description may be multiline text that describes the backend.
+    ///
+    /// @return Description of the backend.
+    virtual std::string getDescription() const;
+
+    /// @brief Returns backend version.
+    ///
+    /// @return Version number as a pair of unsigned integers.  "first" is the
+    ///         major version number, "second" the minor number.
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual std::pair<uint32_t, uint32_t> getVersion() const;
+
+    /// @brief Commit Transactions
+    ///
+    /// Commits all pending database operations.  On databases that don't
+    /// support transactions, this is a no-op.
+    ///
+    /// @throw DbOperationError Iif the commit failed.
+    virtual void commit();
+
+    /// @brief Rollback Transactions
+    ///
+    /// Rolls back all pending database operations.  On databases that don't
+    /// support transactions, this is a no-op.
+    ///
+    /// @throw DbOperationError If the rollback failed.
+    virtual void rollback();
+
+    /// @brief Prepare Single Statement
+    ///
+    /// Creates a prepared statement from the text given and adds it to the
+    /// statements_ vector at the given index.
+    ///
+    /// @param index Index into the statements_ vector into which the text
+    ///        should be placed.  The vector must be big enough for the index
+    ///        to be valid, else an exception will be thrown.
+    /// @param text Text of the SQL statement to be prepared.
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    /// @throw isc::InvalidParameter 'index' is not valid for the vector.
+    void prepareStatement(StatementIndex index, const char* text);
+
+    /// @brief Prepare statements
+    ///
+    /// Creates the prepared statements for all of the SQL statements used
+    /// by the MySQL backend.
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    /// @throw isc::InvalidParameter 'index' is not valid for the vector.  This
+    ///        represents an internal error within the code.
+    void prepareStatements();
+
+    /// @brief Open Database
+    ///
+    /// Opens the database using the information supplied in the parameters
+    /// passed to the constructor.
+    ///
+    /// @throw NoDatabaseName Mandatory database name not given
+    /// @throw DbOpenError Error opening the database
+    void openDatabase();
+
+    std::vector<MYSQL_STMT*> statements_;       ///< Prepared statements
+    std::vector<std::string> text_statements_;  ///< Raw text of statements
+
+
+private:
+    /// @brief list of parameters passed in dbconfig
+    ///
+    /// That will be mostly used for storing database name, username,
+    /// password and other parameters required for DB access. It is not
+    /// intended to keep any DHCP-related parameters.
+    ParameterMap parameters_;
+
+
+    MySqlHolder mysql_;
+
+};
+
+
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // MYSQL_CONNECTION_H

+ 3 - 401
src/lib/dhcpsrv/mysql_lease_mgr.cc

@@ -19,6 +19,7 @@
 #include <dhcp/hwaddr.h>
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/mysql_lease_mgr.h>
+#include <dhcpsrv/mysql_connection.h>
 
 #include <boost/static_assert.hpp>
 #include <mysqld_error.h>
@@ -75,147 +76,6 @@ using namespace std;
 /// - If there is output, copy the data from the bound variables to the output
 ///   lease object.
 
-namespace {
-///@{
-
-/// @brief Maximum size of database fields
-///
-/// 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.
-///
-/// 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.
-
-/// @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 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 in header files named TRUE or
-/// FALSE.
-
-const my_bool MLM_FALSE = 0;                ///< False value
-const my_bool MLM_TRUE = 1;                 ///< True value
-
-/// @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;
-
-///@}
-
-/// @brief MySQL Selection Statements
-///
-/// Each statement is associated with an index, which is used to reference the
-/// associated prepared statement.
-
-struct TaggedStatement {
-    MySqlLeaseMgr::StatementIndex index;
-    const char*                   text;
-};
-
-TaggedStatement tagged_statements[] = {
-    {MySqlLeaseMgr::DELETE_LEASE4,
-                    "DELETE FROM lease4 WHERE address = ?"},
-    {MySqlLeaseMgr::DELETE_LEASE6,
-                    "DELETE FROM lease6 WHERE address = ?"},
-    {MySqlLeaseMgr::GET_LEASE4_ADDR,
-                    "SELECT address, hwaddr, client_id, "
-                        "valid_lifetime, expire, subnet_id, "
-                        "fqdn_fwd, fqdn_rev, hostname "
-                            "FROM lease4 "
-                            "WHERE address = ?"},
-    {MySqlLeaseMgr::GET_LEASE4_CLIENTID,
-                    "SELECT address, hwaddr, client_id, "
-                        "valid_lifetime, expire, subnet_id, "
-                        "fqdn_fwd, fqdn_rev, hostname "
-                            "FROM lease4 "
-                            "WHERE client_id = ?"},
-    {MySqlLeaseMgr::GET_LEASE4_CLIENTID_SUBID,
-                    "SELECT address, hwaddr, client_id, "
-                        "valid_lifetime, expire, subnet_id, "
-                        "fqdn_fwd, fqdn_rev, hostname "
-                            "FROM lease4 "
-                            "WHERE client_id = ? AND subnet_id = ?"},
-    {MySqlLeaseMgr::GET_LEASE4_HWADDR,
-                    "SELECT address, hwaddr, client_id, "
-                        "valid_lifetime, expire, subnet_id, "
-                        "fqdn_fwd, fqdn_rev, hostname "
-                            "FROM lease4 "
-                            "WHERE hwaddr = ?"},
-    {MySqlLeaseMgr::GET_LEASE4_HWADDR_SUBID,
-                    "SELECT address, hwaddr, client_id, "
-                        "valid_lifetime, expire, subnet_id, "
-                        "fqdn_fwd, fqdn_rev, hostname "
-                            "FROM lease4 "
-                            "WHERE hwaddr = ? AND subnet_id = ?"},
-    {MySqlLeaseMgr::GET_LEASE6_ADDR,
-                    "SELECT address, duid, valid_lifetime, "
-                        "expire, subnet_id, pref_lifetime, "
-                        "lease_type, iaid, prefix_len, "
-                        "fqdn_fwd, fqdn_rev, hostname, "
-                        "hwaddr, hwtype, hwaddr_source "
-                            "FROM lease6 "
-                            "WHERE address = ? AND lease_type = ?"},
-    {MySqlLeaseMgr::GET_LEASE6_DUID_IAID,
-                    "SELECT address, duid, valid_lifetime, "
-                        "expire, subnet_id, pref_lifetime, "
-                        "lease_type, iaid, prefix_len, "
-                        "fqdn_fwd, fqdn_rev, hostname, "
-                        "hwaddr, hwtype, hwaddr_source "
-                            "FROM lease6 "
-                            "WHERE duid = ? AND iaid = ? AND lease_type = ?"},
-    {MySqlLeaseMgr::GET_LEASE6_DUID_IAID_SUBID,
-                    "SELECT address, duid, valid_lifetime, "
-                        "expire, subnet_id, pref_lifetime, "
-                        "lease_type, iaid, prefix_len, "
-                        "fqdn_fwd, fqdn_rev, hostname, "
-                        "hwaddr, hwtype, hwaddr_source "
-                            "FROM lease6 "
-                            "WHERE duid = ? AND iaid = ? AND subnet_id = ? "
-                            "AND lease_type = ?"},
-    {MySqlLeaseMgr::GET_VERSION,
-                    "SELECT version, minor FROM schema_version"},
-    {MySqlLeaseMgr::INSERT_LEASE4,
-                    "INSERT INTO lease4(address, hwaddr, client_id, "
-                        "valid_lifetime, expire, subnet_id, "
-                        "fqdn_fwd, fqdn_rev, hostname) "
-                            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
-    {MySqlLeaseMgr::INSERT_LEASE6,
-                    "INSERT INTO lease6(address, duid, valid_lifetime, "
-                        "expire, subnet_id, pref_lifetime, "
-                        "lease_type, iaid, prefix_len, "
-                        "fqdn_fwd, fqdn_rev, hostname, "
-                        "hwaddr, hwtype, hwaddr_source) "
-                            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
-    {MySqlLeaseMgr::UPDATE_LEASE4,
-                    "UPDATE lease4 SET address = ?, hwaddr = ?, "
-                        "client_id = ?, valid_lifetime = ?, expire = ?, "
-                        "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, "
-                        "hostname = ? "
-                            "WHERE address = ?"},
-    {MySqlLeaseMgr::UPDATE_LEASE6,
-                    "UPDATE lease6 SET address = ?, duid = ?, "
-                        "valid_lifetime = ?, expire = ?, subnet_id = ?, "
-                        "pref_lifetime = ?, lease_type = ?, iaid = ?, "
-                        "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, "
-                        "hostname = ?, hwaddr = ?, hwtype = ?, hwaddr_source = ? "
-                            "WHERE address = ?"},
-    // End of list sentinel
-    {MySqlLeaseMgr::NUM_STATEMENTS, NULL}
-};
-
-};  // Anonymous namespace
-
-
 
 namespace isc {
 namespace dhcp {
@@ -1181,51 +1041,12 @@ private:
 };
 
 
-/// @brief Fetch and Release MySQL Results
-///
-/// When a MySQL statement is expected, to fetch the results the function
-/// mysql_stmt_fetch() must be called.  As well as getting data, this
-/// allocates internal state.  Subsequent calls to mysql_stmt_fetch can be
-/// made, but when all the data is retrieved, mysql_stmt_free_result must be
-/// called to free up the resources allocated.
-///
-/// Created prior to the first fetch, this class's destructor calls
-/// mysql_stmt_free_result, so eliminating the need for an explicit release
-/// in the method calling mysql_stmt_free_result.  In this way, it guarantees
-/// that the resources are released even if the MySqlLeaseMgr method concerned
-/// exits via an exception.
-
-class MySqlFreeResult {
-public:
-
-    /// @brief Constructor
-    ///
-    /// Store the pointer to the statement for which data is being fetched.
-    ///
-    /// Note that according to the MySQL documentation, mysql_stmt_free_result
-    /// only releases resources if a cursor has been allocated for the
-    /// statement.  This implies that it is a no-op if none have been.  Either
-    /// way, any error from mysql_stmt_free_result is ignored. (Generating
-    /// an exception is not much help, as it will only confuse things if the
-    /// method calling mysql_stmt_fetch is exiting via an exception.)
-    MySqlFreeResult(MYSQL_STMT* statement) : statement_(statement)
-    {}
-
-    /// @brief Destructor
-    ///
-    /// Frees up fetch context if a fetch has been successfully executed.
-    ~MySqlFreeResult() {
-        (void) mysql_stmt_free_result(statement_);
-    }
 
-private:
-    MYSQL_STMT*     statement_;     ///< Statement for which results are freed
-};
 
 // MySqlLeaseMgr Constructor and Destructor
 
-MySqlLeaseMgr::MySqlLeaseMgr(const LeaseMgr::ParameterMap& parameters)
-    : LeaseMgr(parameters) {
+MySqlLeaseMgr::MySqlLeaseMgr(const MySqlConnection::ParameterMap& parameters)
+    : MySqlConnection(parameters) {
 
     // Open the database.
     openDatabase();
@@ -1340,139 +1161,6 @@ MySqlLeaseMgr::convertFromDatabaseTime(const MYSQL_TIME& expire,
 }
 
 
-
-// Open the database using the parameters passed to the constructor.
-
-void
-MySqlLeaseMgr::openDatabase() {
-
-    // Set up the values of the parameters
-    const char* host = "localhost";
-    string shost;
-    try {
-        shost = getParameter("host");
-        host = shost.c_str();
-    } catch (...) {
-        // No host.  Fine, we'll use "localhost"
-    }
-
-    const char* user = NULL;
-    string suser;
-    try {
-        suser = getParameter("user");
-        user = suser.c_str();
-    } catch (...) {
-        // No user.  Fine, we'll use NULL
-    }
-
-    const char* password = NULL;
-    string spassword;
-    try {
-        spassword = getParameter("password");
-        password = spassword.c_str();
-    } catch (...) {
-        // No password.  Fine, we'll use NULL
-    }
-
-    const char* name = NULL;
-    string sname;
-    try {
-        sname = getParameter("name");
-        name = sname.c_str();
-    } catch (...) {
-        // No database name.  Throw a "NoName" exception
-        isc_throw(NoDatabaseName, "must specified a name for the database");
-    }
-
-    // Set options for the connection:
-    //
-    // Automatic reconnection: after a period of inactivity, the client will
-    // disconnect from the database.  This option causes it to automatically
-    // reconnect when another operation is about to be done.
-    my_bool auto_reconnect = MLM_TRUE;
-    int result = mysql_options(mysql_, MYSQL_OPT_RECONNECT, &auto_reconnect);
-    if (result != 0) {
-        isc_throw(DbOpenError, "unable to set auto-reconnect option: " <<
-                  mysql_error(mysql_));
-    }
-
-    // Set SQL mode options for the connection:  SQL mode governs how what
-    // constitutes insertable data for a given column, and how to handle
-    // invalid data.  We want to ensure we get the strictest behavior and
-    // to reject invalid data with an error.
-    const char *sql_mode = "SET SESSION sql_mode ='STRICT_ALL_TABLES'";
-    result = mysql_options(mysql_, MYSQL_INIT_COMMAND, sql_mode);
-    if (result != 0) {
-        isc_throw(DbOpenError, "unable to set SQL mode options: " <<
-                  mysql_error(mysql_));
-    }
-
-    // Open the database.
-    //
-    // The option CLIENT_FOUND_ROWS is specified so that in an UPDATE,
-    // the affected rows are the number of rows found that match the
-    // WHERE clause of the SQL statement, not the rows changed.  The reason
-    // here is that MySQL apparently does not update a row if data has not
-    // changed and so the "affected rows" (retrievable from MySQL) is zero.
-    // This makes it hard to distinguish whether the UPDATE changed no rows
-    // because no row matching the WHERE clause was found, or because a
-    // row was found but no data was altered.
-    MYSQL* status = mysql_real_connect(mysql_, host, user, password, name,
-                                       0, NULL, CLIENT_FOUND_ROWS);
-    if (status != mysql_) {
-        isc_throw(DbOpenError, mysql_error(mysql_));
-    }
-}
-
-// Prepared statement setup.  The textual form of an SQL statement is stored
-// in a vector of strings (text_statements_) and is used in the output of
-// error messages.  The SQL statement is also compiled into a "prepared
-// statement" (stored in statements_), which avoids the overhead of compilation
-// during use.  As prepared statements have resources allocated to them, the
-// class destructor explicitly destroys them.
-
-void
-MySqlLeaseMgr::prepareStatement(StatementIndex index, const char* text) {
-    // Validate that there is space for the statement in the statements array
-    // and that nothing has been placed there before.
-    if ((index >= statements_.size()) || (statements_[index] != NULL)) {
-        isc_throw(InvalidParameter, "invalid prepared statement index (" <<
-                  static_cast<int>(index) << ") or indexed prepared " <<
-                  "statement is not null");
-    }
-
-    // All OK, so prepare the statement
-    text_statements_[index] = std::string(text);
-    statements_[index] = mysql_stmt_init(mysql_);
-    if (statements_[index] == NULL) {
-        isc_throw(DbOperationError, "unable to allocate MySQL prepared "
-                  "statement structure, reason: " << mysql_error(mysql_));
-    }
-
-    int status = mysql_stmt_prepare(statements_[index], text, strlen(text));
-    if (status != 0) {
-        isc_throw(DbOperationError, "unable to prepare MySQL statement <" <<
-                  text << ">, reason: " << mysql_error(mysql_));
-    }
-}
-
-
-void
-MySqlLeaseMgr::prepareStatements() {
-    // Allocate space for all statements
-    statements_.clear();
-    statements_.resize(NUM_STATEMENTS, NULL);
-
-    text_statements_.clear();
-    text_statements_.resize(NUM_STATEMENTS, std::string(""));
-
-    // Created the MySQL prepared statements for each DML statement.
-    for (int i = 0; tagged_statements[i].text != NULL; ++i) {
-        prepareStatement(tagged_statements[i].index,
-                         tagged_statements[i].text);
-    }
-}
-
 // Add leases to the database.  The two public methods accept a lease object
 // (either V4 of V6), bind the contents to the appropriate prepared
 // statement, then call common code to execute the statement.
@@ -2071,93 +1759,7 @@ MySqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
     }
 }
 
-// Miscellaneous database methods.
-
-std::string
-MySqlLeaseMgr::getName() const {
-    std::string name = "";
-    try {
-        name = getParameter("name");
-    } catch (...) {
-        // Return an empty name
-    }
-    return (name);
-}
-
 
-std::string
-MySqlLeaseMgr::getDescription() const {
-    return (std::string("MySQL Database"));
-}
-
-
-std::pair<uint32_t, uint32_t>
-MySqlLeaseMgr::getVersion() const {
-    const StatementIndex stindex = GET_VERSION;
-
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_MYSQL_GET_VERSION);
-
-    uint32_t    major;      // Major version number
-    uint32_t    minor;      // Minor version number
-
-    // Execute the prepared statement
-    int status = mysql_stmt_execute(statements_[stindex]);
-    if (status != 0) {
-        isc_throw(DbOperationError, "unable to execute <"
-                  << text_statements_[stindex] << "> - reason: " <<
-                  mysql_error(mysql_));
-    }
-
-    // Bind the output of the statement to the appropriate variables.
-    MYSQL_BIND bind[2];
-    memset(bind, 0, sizeof(bind));
-
-    bind[0].buffer_type = MYSQL_TYPE_LONG;
-    bind[0].is_unsigned = 1;
-    bind[0].buffer = &major;
-    bind[0].buffer_length = sizeof(major);
-
-    bind[1].buffer_type = MYSQL_TYPE_LONG;
-    bind[1].is_unsigned = 1;
-    bind[1].buffer = &minor;
-    bind[1].buffer_length = sizeof(minor);
-
-    status = mysql_stmt_bind_result(statements_[stindex], bind);
-    if (status != 0) {
-        isc_throw(DbOperationError, "unable to bind result set: " <<
-                  mysql_error(mysql_));
-    }
-
-    // Fetch the data and set up the "release" object to release associated
-    // resources when this method exits then retrieve the data.
-    MySqlFreeResult fetch_release(statements_[stindex]);
-    status = mysql_stmt_fetch(statements_[stindex]);
-    if (status != 0) {
-        isc_throw(DbOperationError, "unable to obtain result set: " <<
-                  mysql_error(mysql_));
-    }
-
-    return (std::make_pair(major, minor));
-}
-
-
-void
-MySqlLeaseMgr::commit() {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_COMMIT);
-    if (mysql_commit(mysql_) != 0) {
-        isc_throw(DbOperationError, "commit failed: " << mysql_error(mysql_));
-    }
-}
-
-
-void
-MySqlLeaseMgr::rollback() {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_ROLLBACK);
-    if (mysql_rollback(mysql_) != 0) {
-        isc_throw(DbOperationError, "rollback failed: " << mysql_error(mysql_));
-    }
-}
 
 }; // end of isc::dhcp namespace
 }; // end of isc namespace

+ 6 - 107
src/lib/dhcpsrv/mysql_lease_mgr.h

@@ -17,6 +17,7 @@
 
 #include <dhcp/hwaddr.h>
 #include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/mysql_connection.h>
 
 #include <boost/scoped_ptr.hpp>
 #include <boost/utility.hpp>
@@ -93,8 +94,9 @@ class MySqlLease6Exchange;
 /// database.  Use of this backend presupposes that a MySQL database is
 /// available and that the Kea schema has been created within it.
 
-class MySqlLeaseMgr : public LeaseMgr {
+class MySqlLeaseMgr : public LeaseMgr, public MySqlConnection {
 public:
+
     /// @brief Constructor
     ///
     /// Uses the following keywords in the parameters passed to it to
@@ -354,54 +356,6 @@ public:
     ///        failed.
     virtual bool deleteLease(const isc::asiolink::IOAddress& addr);
 
-    /// @brief Return backend type
-    ///
-    /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
-    ///
-    /// @return Type of the backend.
-    virtual std::string getType() const {
-        return (std::string("mysql"));
-    }
-
-    /// @brief Returns backend name.
-    ///
-    /// Each backend have specific name, e.g. "mysql" or "sqlite".
-    ///
-    /// @return Name of the backend.
-    virtual std::string getName() const;
-
-    /// @brief Returns description of the backend.
-    ///
-    /// This description may be multiline text that describes the backend.
-    ///
-    /// @return Description of the backend.
-    virtual std::string getDescription() const;
-
-    /// @brief Returns backend version.
-    ///
-    /// @return Version number as a pair of unsigned integers.  "first" is the
-    ///         major version number, "second" the minor number.
-    ///
-    /// @throw isc::dhcp::DbOperationError An operation on the open database has
-    ///        failed.
-    virtual std::pair<uint32_t, uint32_t> getVersion() const;
-
-    /// @brief Commit Transactions
-    ///
-    /// Commits all pending database operations.  On databases that don't
-    /// support transactions, this is a no-op.
-    ///
-    /// @throw DbOperationError Iif the commit failed.
-    virtual void commit();
-
-    /// @brief Rollback Transactions
-    ///
-    /// Rolls back all pending database operations.  On databases that don't
-    /// support transactions, this is a no-op.
-    ///
-    /// @throw DbOperationError If the rollback failed.
-    virtual void rollback();
-
     ///@{
     /// The following methods are used to convert between times and time
     /// intervals stored in the Lease object, and the times stored in the
@@ -455,63 +409,9 @@ public:
                                  uint32_t valid_lifetime, time_t& cltt);
     ///@}
 
-    /// @brief Statement Tags
-    ///
-    /// The contents of the enum are indexes into the list of SQL statements
-    enum StatementIndex {
-        DELETE_LEASE4,              // Delete from lease4 by address
-        DELETE_LEASE6,              // Delete from lease6 by address
-        GET_LEASE4_ADDR,            // Get lease4 by address
-        GET_LEASE4_CLIENTID,        // Get lease4 by client ID
-        GET_LEASE4_CLIENTID_SUBID,  // Get lease4 by client ID & subnet ID
-        GET_LEASE4_HWADDR,          // Get lease4 by HW address
-        GET_LEASE4_HWADDR_SUBID,    // Get lease4 by HW address & subnet ID
-        GET_LEASE6_ADDR,            // Get lease6 by address
-        GET_LEASE6_DUID_IAID,       // Get lease6 by DUID and IAID
-        GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and subnet ID
-        GET_VERSION,                // Obtain version number
-        INSERT_LEASE4,              // Add entry to lease4 table
-        INSERT_LEASE6,              // Add entry to lease6 table
-        UPDATE_LEASE4,              // Update a Lease4 entry
-        UPDATE_LEASE6,              // Update a Lease6 entry
-        NUM_STATEMENTS              // Number of statements
-    };
 
-private:
-    /// @brief Prepare Single Statement
-    ///
-    /// Creates a prepared statement from the text given and adds it to the
-    /// statements_ vector at the given index.
-    ///
-    /// @param index Index into the statements_ vector into which the text
-    ///        should be placed.  The vector must be big enough for the index
-    ///        to be valid, else an exception will be thrown.
-    /// @param text Text of the SQL statement to be prepared.
-    ///
-    /// @throw isc::dhcp::DbOperationError An operation on the open database has
-    ///        failed.
-    /// @throw isc::InvalidParameter 'index' is not valid for the vector.
-    void prepareStatement(StatementIndex index, const char* text);
 
-    /// @brief Prepare statements
-    ///
-    /// Creates the prepared statements for all of the SQL statements used
-    /// by the MySQL backend.
-    ///
-    /// @throw isc::dhcp::DbOperationError An operation on the open database has
-    ///        failed.
-    /// @throw isc::InvalidParameter 'index' is not valid for the vector.  This
-    ///        represents an internal error within the code.
-    void prepareStatements();
-
-    /// @brief Open Database
-    ///
-    /// Opens the database using the information supplied in the parameters
-    /// passed to the constructor.
-    ///
-    /// @throw NoDatabaseName Mandatory database name not given
-    /// @throw DbOpenError Error opening the database
-    void openDatabase();
+private:
 
     /// @brief Add Lease Common Code
     ///
@@ -688,9 +588,8 @@ private:
     /// declare them as "mutable".)
     boost::scoped_ptr<MySqlLease4Exchange> exchange4_; ///< Exchange object
     boost::scoped_ptr<MySqlLease6Exchange> exchange6_; ///< Exchange object
-    MySqlHolder mysql_;
-    std::vector<MYSQL_STMT*> statements_;       ///< Prepared statements
-    std::vector<std::string> text_statements_;  ///< Raw text of statements
+
+
 };
 
 }; // end of isc::dhcp namespace