]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#887,!572] implement mysql thread handle
authorRazvan Becheriu <razvan@isc.org>
Mon, 28 Oct 2019 16:10:55 +0000 (18:10 +0200)
committerRazvan Becheriu <razvan@isc.org>
Wed, 6 Nov 2019 17:28:25 +0000 (19:28 +0200)
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc
src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc
src/lib/dhcpsrv/mysql_host_data_source.cc
src/lib/dhcpsrv/mysql_lease_mgr.cc
src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc
src/lib/dhcpsrv/testutils/mysql_generic_backend_unittest.cc
src/lib/mysql/mysql_connection.cc
src/lib/mysql/mysql_connection.h
src/lib/mysql/tests/mysql_connection_unittest.cc

index bec9a1ccf0395b613f4a62b241c866f741eea2a5..8541bf51a5e30ad7a7eb6a12c29fcb9424643a81 100644 (file)
@@ -220,7 +220,7 @@ public:
                               in_bindings);
 
             // Let's first get the primary key of the global parameter.
-            uint64_t id = mysql_insert_id(conn_.mysql_);
+            uint64_t id = mysql_insert_id(conn_.handle());
 
             // Successfully inserted global parameter. Now, we have to associate it
             // with the server tag.
@@ -1046,7 +1046,7 @@ public:
         // Run INSERT.
         conn_.insertQuery(INSERT_POOL4, in_bindings);
 
-        uint64_t pool_id = mysql_insert_id(conn_.mysql_);
+        uint64_t pool_id = mysql_insert_id(conn_.handle());
         auto option_spaces = pool->getCfgOption()->getOptionSpaceNames();
         for (auto option_space : option_spaces) {
             OptionContainerPtr options = pool->getCfgOption()->getAll(option_space);
@@ -1577,7 +1577,7 @@ public:
 
         // Fetch primary key value of the inserted option. We will use it in the
         // next INSERT statement to associate this option with the server.
-        auto option_id = mysql_insert_id(conn_.mysql_);
+        auto option_id = mysql_insert_id(conn_.handle());
 
         // Timestamp is expected to be in this input binding.
         auto timestamp_binding = in_bindings[11];
index 14ffaf363c4474816843efad22644da7013b878e..c8099391950789026afa872461aac62ee58db32b 100644 (file)
@@ -231,7 +231,7 @@ public:
             // with the server tag.
 
             // Let's first get the primary key of the global parameter.
-            uint64_t id = mysql_insert_id(conn_.mysql_);
+            uint64_t id = mysql_insert_id(conn_.handle());
 
             // Successfully inserted global parameter. Now, we have to associate it
             // with the server tag.
@@ -1300,7 +1300,7 @@ public:
         // Run INSERT.
         conn_.insertQuery(INSERT_POOL6, in_bindings);
 
-        uint64_t pool_id = mysql_insert_id(conn_.mysql_);
+        uint64_t pool_id = mysql_insert_id(conn_.handle());
         auto option_spaces = pool->getCfgOption()->getOptionSpaceNames();
         for (auto option_space : option_spaces) {
             OptionContainerPtr options = pool->getCfgOption()->getAll(option_space);
@@ -1351,7 +1351,7 @@ public:
         // Run INSERT.
         conn_.insertQuery(INSERT_PD_POOL, in_bindings);
 
-        uint64_t pd_pool_id = mysql_insert_id(conn_.mysql_);
+        uint64_t pd_pool_id = mysql_insert_id(conn_.handle());
         auto option_spaces = pd_pool->getCfgOption()->getOptionSpaceNames();
         for (auto option_space : option_spaces) {
             OptionContainerPtr options = pd_pool->getCfgOption()->getAll(option_space);
@@ -1912,7 +1912,7 @@ public:
 
         // Fetch primary key value of the inserted option. We will use it in the
         // next INSERT statement to associate this option with the server.
-        auto option_id = mysql_insert_id(conn_.mysql_);
+        auto option_id = mysql_insert_id(conn_.handle());
 
         // Timestamp is expected to be in this input binding.
         auto timestamp_binding = in_bindings[11];
index b3a9a32a5e60ee3db7135ab64772ae5512f46ac7..d082f8e20e008e1e359cea64edb028bc2ebbe006 100644 (file)
@@ -57,30 +57,9 @@ MySqlConfigBackendImpl(const DatabaseConnection::ParameterMap& parameters)
                   << " found version:  " << db_version.first << "."
                   << db_version.second);
     } */
-
-    // Enable autocommit. In case transaction is explicitly used, this
-    // setting will be overwritten for the transaction. However, there are
-    // cases when lack of autocommit could cause transactions to hang
-    // until commit or rollback is explicitly called. This already
-    // caused issues for some unit tests which were unable to cleanup
-    // the database after the test because of pending transactions.
-    // Use of autocommit will eliminate this problem.
-    my_bool result = mysql_autocommit(conn_.mysql_, 1);
-    if (result != MLM_FALSE) {
-        isc_throw(DbOperationError, mysql_error(conn_.mysql_));
-    }
 }
 
 MySqlConfigBackendImpl::~MySqlConfigBackendImpl() {
-    // Free up the prepared statements, ignoring errors. (What would we do
-    // about them? We're destroying this object and are not really concerned
-    // with errors on a database connection that is about to go away.)
-    for (int i = 0; i < conn_.statements_.size(); ++i) {
-        if (conn_.statements_[i] != NULL) {
-            (void) mysql_stmt_close(conn_.statements_[i]);
-            conn_.statements_[i] = NULL;
-        }
-    }
 }
 
 MySqlBindingPtr
@@ -570,7 +549,7 @@ MySqlConfigBackendImpl::createUpdateOptionDef(const db::ServerSelector& server_s
 
         // Fetch unique identifier of the inserted option definition and use it
         // as input to the next query.
-        uint64_t id = mysql_insert_id(conn_.mysql_);
+        uint64_t id = mysql_insert_id(conn_.handle());
 
         // Insert associations of the option definition with servers.
         attachElementToServers(insert_option_def_server,
index fc87fe5ab17707d58f26b42575687c287f779d33..1c0230b9e1a10b488848dbee0d46d1a593dbe750 100644 (file)
@@ -2482,18 +2482,6 @@ MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters) :
                   << db_version.second);
     }
 
-    // Enable autocommit. In case transaction is explicitly used, this
-    // setting will be overwritten for the transaction. However, there are
-    // cases when lack of autocommit could cause transactions to hang
-    // until commit or rollback is explicitly called. This already
-    // caused issues for some unit tests which were unable to cleanup
-    // the database after the test because of pending transactions.
-    // Use of autocommit will eliminate this problem.
-    my_bool result = mysql_autocommit(conn_.mysql_, 1);
-    if (result != 0) {
-        isc_throw(DbOperationError, mysql_error(conn_.mysql_));
-    }
-
     // Prepare query statements. Those are will be only used to retrieve
     // information from the database, so they can be used even if the
     // database is read only for the current user.
@@ -2516,18 +2504,6 @@ MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters) :
 }
 
 MySqlHostDataSourceImpl::~MySqlHostDataSourceImpl() {
-    // Free up the prepared statements, ignoring errors. (What would we do
-    // about them? We're destroying this object and are not really concerned
-    // with errors on a database connection that is about to go away.)
-    for (int i = 0; i < conn_.statements_.size(); ++i) {
-        if (conn_.statements_[i] != NULL) {
-            (void) mysql_stmt_close(conn_.statements_[i]);
-            conn_.statements_[i] = NULL;
-        }
-    }
-
-    // There is no need to close the database in this destructor: it is
-    // closed in the destructor of the mysql_ member variable.
 }
 
 std::pair<uint32_t, uint32_t>
@@ -2535,11 +2511,13 @@ MySqlHostDataSourceImpl::getVersion() const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MYSQL_HOST_DB_GET_VERSION);
 
+    MySqlHolder& holderHandle = conn_.handle();
+
     // Allocate a new statement.
-    MYSQL_STMT *stmt = mysql_stmt_init(conn_.mysql_);
+    MYSQL_STMT *stmt = mysql_stmt_init(holderHandle);
     if (stmt == NULL) {
         isc_throw(DbOperationError, "unable to allocate MySQL prepared "
-                  "statement structure, reason: " << mysql_error(conn_.mysql_));
+                  "statement structure, reason: " << mysql_error(holderHandle));
     }
 
     // Prepare the statement from SQL text.
@@ -2548,14 +2526,14 @@ MySqlHostDataSourceImpl::getVersion() const {
     if (status != 0) {
         mysql_stmt_close(stmt);
         isc_throw(DbOperationError, "unable to prepare MySQL statement <"
-                  << version_sql << ">, reason: " << mysql_errno(conn_.mysql_));
+                  << version_sql << ">, reason: " << mysql_error(holderHandle));
     }
 
     // Execute the prepared statement.
     if (mysql_stmt_execute(stmt) != 0) {
         mysql_stmt_close(stmt);
         isc_throw(DbOperationError, "cannot execute schema version query <"
-                  << version_sql << ">, reason: " << mysql_errno(conn_.mysql_));
+                  << version_sql << ">, reason: " << mysql_error(holderHandle));
     }
 
     // Bind the output of the statement to the appropriate variables.
@@ -2576,14 +2554,14 @@ MySqlHostDataSourceImpl::getVersion() const {
 
     if (mysql_stmt_bind_result(stmt, bind)) {
         isc_throw(DbOperationError, "unable to bind result set for <"
-                  << version_sql << ">, reason: " << mysql_errno(conn_.mysql_));
+                  << version_sql << ">, reason: " << mysql_error(holderHandle));
     }
 
     // Fetch the data.
     if (mysql_stmt_fetch(stmt)) {
         mysql_stmt_close(stmt);
         isc_throw(DbOperationError, "unable to bind result set for <"
-                  << version_sql << ">, reason: " << mysql_errno(conn_.mysql_));
+                  << version_sql << ">, reason: " << mysql_error(holderHandle));
     }
 
     // Discard the statement and its resources
@@ -2595,17 +2573,18 @@ MySqlHostDataSourceImpl::getVersion() const {
 void
 MySqlHostDataSourceImpl::addStatement(StatementIndex stindex,
                                       std::vector<MYSQL_BIND>& bind) {
+    MySqlHolder& holderHandle = conn_.handle();
 
     // Bind the parameters to the statement
-    int status = mysql_stmt_bind_param(conn_.statements_[stindex], &bind[0]);
+    int status = mysql_stmt_bind_param(holderHandle.statements_[stindex], &bind[0]);
     checkError(status, stindex, "unable to bind parameters");
 
     // Execute the statement
-    status = mysql_stmt_execute(conn_.statements_[stindex]);
+    status = mysql_stmt_execute(holderHandle.statements_[stindex]);
 
     if (status != 0) {
         // Failure: check for the special case of duplicate entry.
-        if (mysql_errno(conn_.mysql_) == ER_DUP_ENTRY) {
+        if (mysql_errno(holderHandle) == ER_DUP_ENTRY) {
             isc_throw(DuplicateEntry, "Database duplicate entry error");
         }
         checkError(status, stindex, "unable to execute");
@@ -2615,19 +2594,21 @@ MySqlHostDataSourceImpl::addStatement(StatementIndex stindex,
 bool
 MySqlHostDataSourceImpl::delStatement(StatementIndex stindex,
                                       MYSQL_BIND* bind) {
+    MySqlHolder& holderHandle = conn_.handle();
+
     // Bind the parameters to the statement
-    int status = mysql_stmt_bind_param(conn_.statements_[stindex], &bind[0]);
+    int status = mysql_stmt_bind_param(holderHandle.statements_[stindex], &bind[0]);
     checkError(status, stindex, "unable to bind parameters");
 
     // Execute the statement
-    status = mysql_stmt_execute(conn_.statements_[stindex]);
+    status = mysql_stmt_execute(holderHandle.statements_[stindex]);
 
     if (status != 0) {
         checkError(status, stindex, "unable to execute");
     }
 
     // Let's check how many hosts were deleted.
-    my_ulonglong numrows = mysql_stmt_affected_rows(conn_.statements_[stindex]);
+    my_ulonglong numrows = mysql_stmt_affected_rows(holderHandle.statements_[stindex]);
     return (numrows != 0);
 }
 
@@ -2696,32 +2677,33 @@ MySqlHostDataSourceImpl::
 getHostCollection(StatementIndex stindex, MYSQL_BIND* bind,
                   std::shared_ptr<MySqlHostExchange> exchange,
                   ConstHostCollection& result, bool single) const {
+    MySqlHolder& holderHandle = conn_.handle();
 
     // Bind the selection parameters to the statement
-    int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
+    int status = mysql_stmt_bind_param(holderHandle.statements_[stindex], bind);
     checkError(status, stindex, "unable to bind WHERE clause parameter");
 
     // Set up the MYSQL_BIND array for the data being returned and bind it to
     // the statement.
     std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
-    status = mysql_stmt_bind_result(conn_.statements_[stindex], &outbind[0]);
+    status = mysql_stmt_bind_result(holderHandle.statements_[stindex], &outbind[0]);
     checkError(status, stindex, "unable to bind SELECT clause parameters");
 
     // Execute the statement
-    status = mysql_stmt_execute(conn_.statements_[stindex]);
+    status = mysql_stmt_execute(holderHandle.statements_[stindex]);
     checkError(status, stindex, "unable to execute");
 
     // Ensure that all the lease information is retrieved in one go to avoid
     // overhead of going back and forth between client and server.
-    status = mysql_stmt_store_result(conn_.statements_[stindex]);
+    status = mysql_stmt_store_result(holderHandle.statements_[stindex]);
     checkError(status, stindex, "unable to set up for storing all results");
 
     // Set up the fetch "release" object to release resources associated
     // with the call to mysql_stmt_fetch when this method exits, then
     // retrieve the data. mysql_stmt_fetch return value equal to 0 represents
     // successful data fetch.
-    MySqlFreeResult fetch_release(conn_.statements_[stindex]);
-    while ((status = mysql_stmt_fetch(conn_.statements_[stindex])) ==
+    MySqlFreeResult fetch_release(holderHandle.statements_[stindex]);
+    while ((status = mysql_stmt_fetch(holderHandle.statements_[stindex])) ==
            MLM_MYSQL_FETCH_SUCCESS) {
         try {
             exchange->processFetchedData(result);
@@ -2817,6 +2799,8 @@ MySqlHostDataSource::~MySqlHostDataSource() {
 
 void
 MySqlHostDataSource::add(const HostPtr& host) {
+    MySqlHolder& holderHandle = impl_->conn_.handle();
+
     // If operating in read-only mode, throw exception.
     impl_->checkReadOnly();
 
@@ -2836,7 +2820,7 @@ MySqlHostDataSource::add(const HostPtr& host) {
     impl_->addStatement(MySqlHostDataSourceImpl::INSERT_HOST, bind);
 
     // Gets the last inserted hosts id
-    uint64_t host_id = mysql_insert_id(impl_->conn_.mysql_);
+    uint64_t host_id = mysql_insert_id(holderHandle);
 
     // Insert DHCPv4 options.
     ConstCfgOptionPtr cfg_option4 = host->getCfgOption4();
index 102bc739814102dca07058977db31963a5cb43d6..769b5e39b8023e62e3977d59edb9d1717066287d 100644 (file)
@@ -1489,7 +1489,7 @@ public:
           // Set the number of columns in the bind array based on fetch_type
           // This is the number of columns expected in the result set
           bind_(fetch_type_ ? 4 : 3),
-          subnet_id_(0), lease_type_(0), lease_state_(0), state_count_(0) {
+          subnet_id_(0), lease_type_(0), state_(0), state_count_(0) {
           validateStatement();
     }
 
@@ -1509,7 +1509,7 @@ public:
           // Set the number of columns in the bind array based on fetch_type
           // This is the number of columns expected in the result set
           bind_(fetch_type_ ? 4 : 3),
-          subnet_id_(0), lease_type_(0), lease_state_(0), state_count_(0) {
+          subnet_id_(0), lease_type_(0), state_(0), state_count_(0) {
           validateStatement();
     }
 
@@ -1533,7 +1533,7 @@ public:
           // Set the number of columns in the bind array based on fetch_type
           // This is the number of columns expected in the result set
           bind_(fetch_type_ ? 4 : 3),
-          subnet_id_(0), lease_type_(0), lease_state_(0), state_count_(0) {
+          subnet_id_(0), lease_type_(0), state_(0), state_count_(0) {
           validateStatement();
     }
 
@@ -1592,7 +1592,7 @@ public:
 
         // state: uint32_t
         bind_[col].buffer_type = MYSQL_TYPE_LONG;
-        bind_[col].buffer = reinterpret_cast<char*>(&lease_state_);
+        bind_[col].buffer = reinterpret_cast<char*>(&state_);
         bind_[col].is_unsigned = MLM_TRUE;
         ++col;
 
@@ -1633,7 +1633,7 @@ public:
         if (status == MLM_MYSQL_FETCH_SUCCESS) {
             row.subnet_id_ = static_cast<SubnetID>(subnet_id_);
             row.lease_type_ = static_cast<Lease::Type>(lease_type_);
-            row.lease_state_ = lease_state_;
+            row.lease_state_ = state_;
             row.state_count_ = state_count_;
             have_row = true;
         } else if (status != MYSQL_NO_DATA) {
@@ -1648,12 +1648,14 @@ private:
     /// Safely fetch the statement from the connection based on statement index
     /// @throw  BadValue if statement index is out of range
     void validateStatement() {
+        MySqlHolder& holderHandle = conn_.handle();
+
         if (statement_index_ >= MySqlLeaseMgr::NUM_STATEMENTS) {
             isc_throw(BadValue, "MySqlLeaseStatsQuery"
                       " - invalid statement index" << statement_index_);
         }
 
-        statement_ = conn_.statements_[statement_index_];
+        statement_ = holderHandle.statements_[statement_index_];
     }
 
     /// @brief Database connection to use to execute the query
@@ -1676,7 +1678,7 @@ private:
     /// @brief Receives the lease type when fetching a row
     uint32_t lease_type_;
     /// @brief Receives the lease state when fetching a row
-    uint32_t lease_state_;
+    uint32_t state_;
     /// @brief Receives the state count when fetching a row
     int64_t state_count_;
 };
@@ -1700,16 +1702,6 @@ MySqlLeaseMgr::MySqlLeaseMgr(const MySqlConnection::ParameterMap& parameters) :
                   << db_version.second);
     }
 
-    // Enable autocommit.  To avoid a flush to disk on every commit, the global
-    // parameter innodb_flush_log_at_trx_commit should be set to 2.  This will
-    // cause the changes to be written to the log, but flushed to disk in the
-    // background every second.  Setting the parameter to that value will speed
-    // up the system, but at the risk of losing data if the system crashes.
-    my_bool result = mysql_autocommit(conn_.mysql_, 1);
-    if (result != 0) {
-        isc_throw(DbOperationError, mysql_error(conn_.mysql_));
-    }
-
     // Prepare all statements likely to be used.
     conn_.prepareStatements(tagged_statements.begin(), tagged_statements.end());
 }
@@ -1735,19 +1727,20 @@ MySqlLeaseMgr::getDBVersion() {
 bool
 MySqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
                               std::vector<MYSQL_BIND>& bind) {
+    MySqlHolder& holderHandle = conn_.handle();
 
     // Bind the parameters to the statement
-    int status = mysql_stmt_bind_param(conn_.statements_[stindex], &bind[0]);
+    int status = mysql_stmt_bind_param(holderHandle.statements_[stindex], &bind[0]);
     checkError(status, stindex, "unable to bind parameters");
 
     // Execute the statement
-    status = mysql_stmt_execute(conn_.statements_[stindex]);
+    status = mysql_stmt_execute(holderHandle.statements_[stindex]);
     if (status != 0) {
 
         // Failure: check for the special case of duplicate entry.  If this is
         // the case, we return false to indicate that the row was not added.
         // Otherwise we throw an exception.
-        if (mysql_errno(conn_.mysql_) == ER_DUP_ENTRY) {
+        if (mysql_errno(holderHandle) == ER_DUP_ENTRY) {
             return (false);
         }
         checkError(status, stindex, "unable to execute");
@@ -1819,36 +1812,36 @@ void MySqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
                                        Exchange& exchange,
                                        LeaseCollection& result,
                                        bool single) const {
-
+    MySqlHolder& holderHandle = conn_.handle();
     int status;
 
     if (bind) {
         // Bind the selection parameters to the statement
-        status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
+        status = mysql_stmt_bind_param(holderHandle.statements_[stindex], bind);
         checkError(status, stindex, "unable to bind WHERE clause parameter");
     }
 
     // Set up the MYSQL_BIND array for the data being returned and bind it to
     // the statement.
     std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
-    status = mysql_stmt_bind_result(conn_.statements_[stindex], &outbind[0]);
+    status = mysql_stmt_bind_result(holderHandle.statements_[stindex], &outbind[0]);
     checkError(status, stindex, "unable to bind SELECT clause parameters");
 
     // Execute the statement
-    status = mysql_stmt_execute(conn_.statements_[stindex]);
+    status = mysql_stmt_execute(holderHandle.statements_[stindex]);
     checkError(status, stindex, "unable to execute");
 
     // Ensure that all the lease information is retrieved in one go to avoid
     // overhead of going back and forth between client and server.
-    status = mysql_stmt_store_result(conn_.statements_[stindex]);
+    status = mysql_stmt_store_result(holderHandle.statements_[stindex]);
     checkError(status, stindex, "unable to set up for storing all results");
 
     // Set up the fetch "release" object to release resources associated
     // with the call to mysql_stmt_fetch when this method exits, then
     // retrieve the data.
-    MySqlFreeResult fetch_release(conn_.statements_[stindex]);
+    MySqlFreeResult fetch_release(holderHandle.statements_[stindex]);
     int count = 0;
-    while ((status = mysql_stmt_fetch(conn_.statements_[stindex])) == 0) {
+    while ((status = mysql_stmt_fetch(holderHandle.statements_[stindex])) == 0) {
         try {
             result.push_back(exchange->getLeaseData());
 
@@ -2515,18 +2508,19 @@ template <typename LeasePtr>
 void
 MySqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind,
                                  const LeasePtr& lease) {
+    MySqlHolder& holderHandle = conn_.handle();
 
     // Bind the parameters to the statement
-    int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
+    int status = mysql_stmt_bind_param(holderHandle.statements_[stindex], bind);
     checkError(status, stindex, "unable to bind parameters");
 
     // Execute
-    status = mysql_stmt_execute(conn_.statements_[stindex]);
+    status = mysql_stmt_execute(holderHandle.statements_[stindex]);
     checkError(status, stindex, "unable to execute");
 
     // See how many rows were affected.  The statement should only update a
     // single row.
-    int affected_rows = mysql_stmt_affected_rows(conn_.statements_[stindex]);
+    int affected_rows = mysql_stmt_affected_rows(holderHandle.statements_[stindex]);
     if (affected_rows == 0) {
         isc_throw(NoSuchLease, "unable to update lease for address " <<
                   lease->addr_ << " as it does not exist");
@@ -2605,18 +2599,19 @@ MySqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
 
 uint64_t
 MySqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind) {
+    MySqlHolder& holderHandle = conn_.handle();
 
     // Bind the input parameters to the statement
-    int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
+    int status = mysql_stmt_bind_param(holderHandle.statements_[stindex], bind);
     checkError(status, stindex, "unable to bind WHERE clause parameter");
 
     // Execute
-    status = mysql_stmt_execute(conn_.statements_[stindex]);
+    status = mysql_stmt_execute(holderHandle.statements_[stindex]);
     checkError(status, stindex, "unable to execute");
 
     // See how many rows were affected.  Note that the statement may delete
     // multiple rows.
-    return (static_cast<uint64_t>(mysql_stmt_affected_rows(conn_.statements_[stindex])));
+    return (static_cast<uint64_t>(mysql_stmt_affected_rows(holderHandle.statements_[stindex])));
 }
 
 bool
@@ -2790,11 +2785,13 @@ MySqlLeaseMgr::getVersion() const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MYSQL_GET_VERSION);
 
+    MySqlHolder& holderHandle = conn_.handle();
+
     // Allocate a new statement.
-    MYSQL_STMT *stmt = mysql_stmt_init(conn_.mysql_);
+    MYSQL_STMT *stmt = mysql_stmt_init(holderHandle);
     if (stmt == NULL) {
         isc_throw(DbOperationError, "unable to allocate MySQL prepared "
-                "statement structure, reason: " << mysql_error(conn_.mysql_));
+                  "statement structure, reason: " << mysql_error(holderHandle));
     }
 
     // Prepare the statement from SQL text.
@@ -2803,14 +2800,14 @@ MySqlLeaseMgr::getVersion() const {
     if (status != 0) {
         mysql_stmt_close(stmt);
         isc_throw(DbOperationError, "unable to prepare MySQL statement <"
-                  << version_sql << ">, reason: " << mysql_error(conn_.mysql_));
+                  << version_sql << ">, reason: " << mysql_error(holderHandle));
     }
 
     // Execute the prepared statement.
     if (mysql_stmt_execute(stmt) != 0) {
         mysql_stmt_close(stmt);
         isc_throw(DbOperationError, "cannot execute schema version query <"
-                  << version_sql << ">, reason: " << mysql_errno(conn_.mysql_));
+                  << version_sql << ">, reason: " << mysql_error(holderHandle));
     }
 
     // Bind the output of the statement to the appropriate variables.
@@ -2831,14 +2828,14 @@ MySqlLeaseMgr::getVersion() const {
 
     if (mysql_stmt_bind_result(stmt, bind)) {
         isc_throw(DbOperationError, "unable to bind result set for <"
-                << version_sql << ">, reason: " << mysql_errno(conn_.mysql_));
+                  << version_sql << ">, reason: " << mysql_error(holderHandle));
     }
 
     // Fetch the data.
     if (mysql_stmt_fetch(stmt)) {
         mysql_stmt_close(stmt);
         isc_throw(DbOperationError, "unable to bind result set for <"
-                << version_sql << ">, reason: " << mysql_errno(conn_.mysql_));
+                  << version_sql << ">, reason: " << mysql_error(holderHandle));
     }
 
     // Discard the statement and its resources
@@ -2850,16 +2847,22 @@ MySqlLeaseMgr::getVersion() const {
 void
 MySqlLeaseMgr::commit() {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_COMMIT);
-    if (mysql_commit(conn_.mysql_) != 0) {
-        isc_throw(DbOperationError, "commit failed: " << mysql_error(conn_.mysql_));
+
+    MySqlHolder& holderHandle = conn_.handle();
+
+    if (mysql_commit(holderHandle) != 0) {
+        isc_throw(DbOperationError, "commit failed: " << mysql_error(holderHandle));
     }
 }
 
 void
 MySqlLeaseMgr::rollback() {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_ROLLBACK);
-    if (mysql_rollback(conn_.mysql_) != 0) {
-        isc_throw(DbOperationError, "rollback failed: " << mysql_error(conn_.mysql_));
+
+    MySqlHolder& holderHandle = conn_.handle();
+
+    if (mysql_rollback(holderHandle) != 0) {
+        isc_throw(DbOperationError, "rollback failed: " << mysql_error(holderHandle));
     }
 }
 
index 54f294ad517b70ba7e387a2198422beaed43233b..c3f6c21fb4236440f869876b7662dd85684080c4 100644 (file)
@@ -120,12 +120,14 @@ public:
         MySqlConnection conn(params);
         conn.openDatabase();
 
-        int status = mysql_query(conn.mysql_, query.c_str());
+        MySqlHolder& holderHandle = conn.handle();
+
+        int status = mysql_query(holderHandle, query.c_str());
         if (status !=0) {
-            isc_throw(DbOperationError, "Query failed: " << mysql_error(conn.mysql_));
+            isc_throw(DbOperationError, "Query failed: " << mysql_error(holderHandle));
         }
 
-        MYSQL_RES * res = mysql_store_result(conn.mysql_);
+        MYSQL_RES * res = mysql_store_result(holderHandle);
         int numrows = static_cast<int>(mysql_num_rows(res));
         mysql_free_result(res);
 
@@ -658,9 +660,13 @@ TEST_F(MySqlHostDataSourceTest, testAddRollback) {
     MySqlConnection conn(params);
     ASSERT_NO_THROW(conn.openDatabase());
 
-    int status = mysql_query(conn.mysql_,
-                             "DROP TABLE IF EXISTS ipv6_reservations");
-    ASSERT_EQ(0, status) << mysql_error(conn.mysql_);
+    MySqlHolder& holderHandle = conn.handle();
+
+    ConstHostCollection collection = hdsptr_->getAll4(0);
+    ASSERT_EQ(collection.size(), 0);
+
+    int status = mysql_query(holderHandle, "DROP TABLE IF EXISTS ipv6_reservations");
+    ASSERT_EQ(0, status) << mysql_error(holderHandle);
 
     // Create a host with a reservation.
     HostPtr host = HostDataSourceUtils::initializeHost6("2001:db8:1::1",
index b45aa7e1ea4a36473c1132b17a49916c4523900f..44ee8bce548da4602114dad3fba870163b326e7b 100644 (file)
@@ -19,16 +19,18 @@ MySqlGenericBackendTest::MySqlGenericBackendTest()
 
 size_t
 MySqlGenericBackendTest::countRows(MySqlConnection& conn, const std::string& table) const {
+    MySqlHolder& holderHandle = conn.handle();
+
     // Execute a simple select query on all rows.
     std::string query = "SELECT * FROM " + table;
-    auto status = mysql_query(conn.mysql_, query.c_str());
+    auto status = mysql_query(holderHandle, query.c_str());
     if (status != 0) {
-        ADD_FAILURE() << "Query failed: " << mysql_error(conn.mysql_);
+        ADD_FAILURE() << "Query failed: " << mysql_error(holderHandle);
         return (0);
     }
 
     // Get the number of rows returned and free the result.
-    MYSQL_RES * res = mysql_store_result(conn.mysql_);
+    MYSQL_RES * res = mysql_store_result(holderHandle);
     unsigned numrows = static_cast<unsigned>(mysql_num_rows(res));
     mysql_free_result(res);
 
index 8efc9a06ca13792ff7279b0f02a56803d54cc764..17a80004e4500d47b8ba9007002e4f5e9c79fb60 100644 (file)
@@ -23,7 +23,69 @@ using namespace std;
 namespace isc {
 namespace db {
 
-bool MySqlHolder::atexit_ = false;
+bool MySqlHolder::atexit_ = []{atexit([]{mysql_library_end();});return true;};
+
+void
+MySqlHolder::setConnection(MYSQL* connection) {
+    clearPrepared();
+    if (mysql_ != NULL) {
+        mysql_close(mysql_);
+    }
+    mysql_ = connection;
+    connected_ = false;
+    prepared_ = false;
+}
+
+void
+MySqlHolder::clearPrepared() {
+    // Free up the prepared statements, ignoring errors. (What would we do
+    // about them? We're destroying this object and are not really concerned
+    // with errors on a database connection that is about to go away.)
+    for (int i = 0; i < statements_.size(); ++i) {
+        if (statements_[i] != NULL) {
+            (void) mysql_stmt_close(statements_[i]);
+            statements_[i] = NULL;
+       }
+    }
+    statements_.clear();
+}
+
+void
+MySqlHolder::openDatabase(MySqlConnection& connection) {
+    if (connected_) {
+        return;
+    }
+    connected_ = true;
+    prepared_ = true;
+    connection.openDatabase();
+    prepared_ = false;
+}
+
+void
+MySqlHolder::prepareStatements(MySqlConnection& connection) {
+    if (prepared_) {
+        return;
+    }
+    clearPrepared();
+    statements_.resize(connection.text_statements_.size(), NULL);
+    uint32_t index = 0;
+    for (auto it = connection.text_statements_.begin();
+        it != connection.text_statements_.end(); ++it) {
+        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], it->c_str(), it->size());
+        if (status != 0) {
+            isc_throw(DbOperationError, "unable to prepare MySQL statement <" <<
+                      *it << ">, reason: " << mysql_error(mysql_));
+        }
+        ++index;
+    }
+    prepared_ = true;
+}
 
 /// @todo: Migrate this default value to src/bin/dhcpX/simple_parserX.cc
 const int MYSQL_DEFAULT_CONNECTION_TIMEOUT = 5; // seconds
@@ -37,7 +99,10 @@ MySqlTransaction::~MySqlTransaction() {
     // Rollback if the MySqlTransaction::commit wasn't explicitly
     // called.
     if (!committed_) {
-        conn_.rollback();
+        try {
+            conn_.rollback();
+        } catch (...) {
+        }
     }
 }
 
@@ -126,10 +191,8 @@ MySqlConnection::openDatabase() {
         // No timeout parameter, we are going to use the default timeout.
         stimeout = "";
     }
-
     if (stimeout.size() > 0) {
         // Timeout was given, so try to convert it to an integer.
-
         try {
             connect_timeout = boost::lexical_cast<unsigned int>(stimeout);
         } catch (...) {
@@ -162,18 +225,26 @@ MySqlConnection::openDatabase() {
     // connection after a reconnect as among other things, it drops all our
     // pre-compiled statements.
     my_bool auto_reconnect = MLM_FALSE;
-    int result = mysql_options(mysql_, MYSQL_OPT_RECONNECT, &auto_reconnect);
+
+    MYSQL* new_conn = mysql_init(NULL);
+    if (new_conn == NULL) {
+        isc_throw(db::DbOpenError, "unable to initialize MySQL");
+    }
+
+    int result = mysql_options(new_conn, MYSQL_OPT_RECONNECT, &auto_reconnect);
     if (result != 0) {
+        mysql_close(new_conn);
         isc_throw(DbOpenError, "unable to set auto-reconnect option: " <<
-                  mysql_error(mysql_));
+                  mysql_error(new_conn));
     }
 
     // Make sure we have a large idle time window ... say 30 days...
     const char *wait_time = "SET SESSION wait_timeout = 30 * 86400";
-    result = mysql_options(mysql_, MYSQL_INIT_COMMAND, wait_time);
+    result = mysql_options(new_conn, MYSQL_INIT_COMMAND, wait_time);
     if (result != 0) {
+        mysql_close(new_conn);
         isc_throw(DbOpenError, "unable to set wait_timeout " <<
-                  mysql_error(mysql_));
+                  mysql_error(new_conn));
     }
 
     // Set SQL mode options for the connection:  SQL mode governs how what
@@ -181,18 +252,20 @@ MySqlConnection::openDatabase() {
     // 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);
+    result = mysql_options(new_conn, MYSQL_INIT_COMMAND, sql_mode);
     if (result != 0) {
+        mysql_close(new_conn);
         isc_throw(DbOpenError, "unable to set SQL mode options: " <<
-                  mysql_error(mysql_));
+                  mysql_error(new_conn));
     }
 
     // Connection timeout, the amount of time taken for the client to drop
     // the connection if the server is not responding.
-    result = mysql_options(mysql_, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
+    result = mysql_options(new_conn, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
     if (result != 0) {
+        mysql_close(new_conn);
         isc_throw(DbOpenError, "unable to set database connection timeout: " <<
-                  mysql_error(mysql_));
+                  mysql_error(new_conn));
     }
 
     // Open the database.
@@ -205,11 +278,31 @@ MySqlConnection::openDatabase() {
     // 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,
+    MYSQL* status = mysql_real_connect(new_conn, host, user, password, name,
                                        port, NULL, CLIENT_FOUND_ROWS);
-    if (status != mysql_) {
-        isc_throw(DbOpenError, mysql_error(mysql_));
+    if (status != new_conn) {
+        mysql_close(new_conn);
+        isc_throw(DbOpenError, mysql_error(new_conn));
     }
+
+    // Enable autocommit. In case transaction is explicitly used, this
+    // setting will be overwritten for the transaction. However, there are
+    // cases when lack of autocommit could cause transactions to hang
+    // until commit or rollback is explicitly called. This already
+    // caused issues for some unit tests which were unable to cleanup
+    // the database after the test because of pending transactions.
+    // Use of autocommit will eliminate this problem.
+    my_bool auto_commit = mysql_autocommit(new_conn, 1);
+    if (auto_commit != MLM_FALSE) {
+        mysql_close(new_conn);
+        isc_throw(DbOperationError, mysql_error(new_conn));
+    }
+
+    // We have a valid connection, so let's save it to our holder
+    MySqlHolder& holderHandle = handle();
+    holderHandle.setConnection(new_conn);
+    holderHandle.connected_ = true;
+    connected_ = true;
 }
 
 
@@ -224,25 +317,13 @@ void
 MySqlConnection::prepareStatement(uint32_t 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)) {
+    if (index >= text_statements_.size()) {
         isc_throw(InvalidParameter, "invalid prepared statement index (" <<
-                  static_cast<int>(index) << ") or indexed prepared " <<
-                  "statement is not null");
+                  static_cast<int>(index) << ")");
     }
 
     // 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
@@ -251,34 +332,20 @@ MySqlConnection::prepareStatements(const TaggedStatement* start_statement,
     // Created the MySQL prepared statements for each DML statement.
     for (const TaggedStatement* tagged_statement = start_statement;
          tagged_statement != end_statement; ++tagged_statement) {
-        if (tagged_statement->index >= statements_.size()) {
-            statements_.resize(tagged_statement->index + 1, NULL);
+        if (tagged_statement->index >= text_statements_.size()) {
             text_statements_.resize(tagged_statement->index + 1,
                                     std::string(""));
         }
         prepareStatement(tagged_statement->index,
                          tagged_statement->text);
     }
-}
-
-void MySqlConnection::clearStatements() {
-    statements_.clear();
-    text_statements_.clear();
+    prepared_ = true;
 }
 
 /// @brief Destructor
 MySqlConnection::~MySqlConnection() {
-    // Free up the prepared statements, ignoring errors. (What would we do
-    // about them? We're destroying this object and are not really concerned
-    // with errors on a database connection that is about to go away.)
-    for (int i = 0; i < statements_.size(); ++i) {
-        if (statements_[i] != NULL) {
-            (void) mysql_stmt_close(statements_[i]);
-            statements_[i] = NULL;
-        }
-    }
-    statements_.clear();
     text_statements_.clear();
+    handle().clear();
 }
 
 // Time conversion methods.
@@ -299,8 +366,8 @@ MySqlConnection::convertToDatabaseTime(const time_t input_time,
 
 void
 MySqlConnection::convertToDatabaseTime(const time_t cltt,
-                                     const uint32_t valid_lifetime,
-                                     MYSQL_TIME& expire) {
+                                       const uint32_t valid_lifetime,
+                                       MYSQL_TIME& expire) {
     MySqlBinding::convertToDatabaseTime(cltt, valid_lifetime, expire);
 }
 
@@ -315,31 +382,39 @@ MySqlConnection::startTransaction() {
     DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, MYSQL_START_TRANSACTION);
     // We create prepared statements for all other queries, but MySQL
     // don't support prepared statements for START TRANSACTION.
-    int status = mysql_query(mysql_, "START TRANSACTION");
+
+    MySqlHolder& holderHandle = handle();
+
+    int status = mysql_query(holderHandle, "START TRANSACTION");
     if (status != 0) {
         isc_throw(DbOperationError, "unable to start transaction, "
-                  "reason: " << mysql_error(mysql_));
+                  "reason: " << mysql_error(holderHandle));
     }
 }
 
 void
 MySqlConnection::commit() {
     DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, MYSQL_COMMIT);
-    if (mysql_commit(mysql_) != 0) {
+
+    MySqlHolder& holderHandle = handle();
+
+    if (mysql_commit(holderHandle) != 0) {
         isc_throw(DbOperationError, "commit failed: "
-                  << mysql_error(mysql_));
+                  << mysql_error(holderHandle));
     }
 }
 
 void
 MySqlConnection::rollback() {
     DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, MYSQL_ROLLBACK);
-    if (mysql_rollback(mysql_) != 0) {
+
+    MySqlHolder& holderHandle = handle();
+
+    if (mysql_rollback(holderHandle) != 0) {
         isc_throw(DbOperationError, "rollback failed: "
-                  << mysql_error(mysql_));
+                  << mysql_error(holderHandle));
     }
 }
 
-
-} // namespace isc::db
-} // namespace isc
+}  // namespace db
+}  // namespace isc
index 929fbc16e56db15c69aa97d7c75d3ed83fda3ddf..41da43ff316668558aed44161e241cbd15b25c2c 100644 (file)
@@ -10,6 +10,7 @@
 #include <database/database_connection.h>
 #include <database/db_exceptions.h>
 #include <database/db_log.h>
+#include <dhcpsrv/thread_resource_mgr.h>
 #include <exceptions/exceptions.h>
 #include <mysql/mysql_binding.h>
 #include <mysql/mysql_constants.h>
@@ -24,7 +25,6 @@
 namespace isc {
 namespace db {
 
-
 /// @brief Fetch and Release MySQL Results
 ///
 /// When a MySQL statement is expected, to fetch the results the function
@@ -41,7 +41,6 @@ namespace db {
 
 class MySqlFreeResult {
 public:
-
     /// @brief Constructor
     ///
     /// Store the pointer to the statement for which data is being fetched.
@@ -52,18 +51,18 @@ public:
     /// 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)
-    {}
+    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_);
+        (void)mysql_stmt_free_result(statement_);
     }
 
 private:
-    MYSQL_STMT*     statement_;     ///< Statement for which results are freed
+    MYSQL_STMT* statement_;  ///< Statement for which results are freed
 };
 
 /// @brief MySQL Selection Statements
@@ -76,6 +75,10 @@ struct TaggedStatement {
     const char* text;
 };
 
+
+/// @brief Forward declaration to @ref MySqlConnection.
+class MySqlConnection;
+
 /// @brief MySQL Handle Holder
 ///
 /// Small RAII object for safer initialization, will close the database
@@ -89,33 +92,28 @@ struct TaggedStatement {
 /// For this reason, the class is declared noncopyable.
 class MySqlHolder : public boost::noncopyable {
 public:
-
     /// @brief Constructor
     ///
     /// Push a call to mysql_library_end() at exit time.
     /// Initialize MySql and store the associated context object.
     ///
     /// @throw DbOpenError Unable to initialize MySql handle.
-    MySqlHolder() : mysql_(mysql_init(NULL)) {
-        if (!atexit_) {
-            atexit([]{ mysql_library_end(); });
-            atexit_ = true;
-        }
-        if (mysql_ == NULL) {
-            isc_throw(db::DbOpenError, "unable to initialize MySQL");
-        }
+    MySqlHolder() : connected_(false), prepared_(false), mysql_(NULL) {
     }
 
     /// @brief Destructor
     ///
     /// Frees up resources allocated by the initialization of MySql.
     ~MySqlHolder() {
-        if (mysql_ != NULL) {
-            mysql_close(mysql_);
-        }
+        clear();
         // @note Moved the call to mysql_library_end() to atexit.
     }
 
+    /// @brief Sets the connection to the value given
+    ///
+    /// @param connection - pointer to the MYSQL connection instance
+    void setConnection(MYSQL* connection);
+
     /// @brief Conversion Operator
     ///
     /// Allows the MySqlHolder object to be passed as the context argument to
@@ -124,15 +122,32 @@ public:
         return (mysql_);
     }
 
+    void clear() {
+        setConnection(NULL);
+    }
+
+    void clearPrepared();
+
+    void openDatabase(MySqlConnection& connection);
+
+    void prepareStatements(MySqlConnection& connection);
+
+    /// @brief Prepared statements
+    ///
+    /// This field is public, because it is used heavily from MySqlConnection
+    /// and from MySqlHostDataSource.
+    std::vector<MYSQL_STMT*> statements_;
+
+    bool connected_;     ///< Flag to indicate openDatabase has been called
+
 private:
+    bool prepared_;      ///< Flag to indicate prepareStatements has been called
+
     static bool atexit_; ///< Flag to call atexit once.
 
     MYSQL* mysql_;       ///< Initialization context
 };
 
-/// @brief Forward declaration to @ref MySqlConnection.
-class MySqlConnection;
-
 /// @brief RAII object representing MySQL transaction.
 ///
 /// An instance of this class should be created in a scope where multiple
@@ -155,7 +170,6 @@ class MySqlConnection;
 /// database which don't use transactions will still be auto committed.
 class MySqlTransaction : public boost::noncopyable {
 public:
-
     /// @brief Constructor.
     ///
     /// Starts transaction by making a "START TRANSACTION" query.
@@ -175,7 +189,6 @@ public:
     void commit();
 
 private:
-
     /// @brief Holds reference to the MySQL database connection.
     MySqlConnection& conn_;
 
@@ -186,7 +199,6 @@ private:
     bool committed_;
 };
 
-
 /// @brief Common MySQL Connector Pool
 ///
 /// This class provides common operations for MySQL database connection
@@ -196,15 +208,14 @@ private:
 /// that use instances of MySqlConnection.
 class MySqlConnection : public db::DatabaseConnection {
 public:
-
     /// @brief Function invoked to process fetched row.
     typedef std::function<void(MySqlBindingCollection&)> ConsumeResultFun;
 
     /// @brief Constructor
     ///
     /// Initialize MySqlConnection object with parameters needed for connection.
-    MySqlConnection(const ParameterMap& parameters)
-        : DatabaseConnection(parameters) {
+    MySqlConnection(const ParameterMap& parameters) :
+        DatabaseConnection(parameters), connected_(false), prepared_(false) {
     }
 
     /// @brief Destructor
@@ -242,9 +253,6 @@ public:
     void prepareStatements(const TaggedStatement* start_statement,
                            const TaggedStatement* end_statement);
 
-    /// @brief Clears prepared statements and text statements.
-    void clearStatements();
-
     /// @brief Open Database
     ///
     /// Opens the database using the information supplied in the parameters
@@ -350,6 +358,8 @@ public:
                      const MySqlBindingCollection& in_bindings,
                      MySqlBindingCollection& out_bindings,
                      ConsumeResultFun process_result) {
+        MySqlHolder& holderHandle = handle();
+
         // Extract native input bindings.
         std::vector<MYSQL_BIND> in_bind_vec;
         for (MySqlBindingPtr in_binding : in_bindings) {
@@ -359,7 +369,7 @@ public:
         int status = 0;
         if (!in_bind_vec.empty()) {
             // Bind parameters to the prepared statement.
-            status = mysql_stmt_bind_param(statements_[index],
+            status = mysql_stmt_bind_param(holderHandle.statements_[index],
                                            in_bind_vec.empty() ? 0 : &in_bind_vec[0]);
             checkError(status, index, "unable to bind parameters for select");
         }
@@ -370,20 +380,20 @@ public:
             out_bind_vec.push_back(out_binding->getMySqlBinding());
         }
         if (!out_bind_vec.empty()) {
-            status = mysql_stmt_bind_result(statements_[index], &out_bind_vec[0]);
+            status = mysql_stmt_bind_result(holderHandle.statements_[index], &out_bind_vec[0]);
             checkError(status, index, "unable to bind result parameters for select");
         }
 
         // Execute query.
-        status = mysql_stmt_execute(statements_[index]);
+        status = mysql_stmt_execute(holderHandle.statements_[index]);
         checkError(status, index, "unable to execute");
 
-        status = mysql_stmt_store_result(statements_[index]);
+        status = mysql_stmt_store_result(holderHandle.statements_[index]);
         checkError(status, index, "unable to set up for storing all results");
 
         // Fetch results.
-        MySqlFreeResult fetch_release(statements_[index]);
-        while ((status = mysql_stmt_fetch(statements_[index])) ==
+        MySqlFreeResult fetch_release(holderHandle.statements_[index]);
+        while ((status = mysql_stmt_fetch(holderHandle.statements_[index])) ==
                MLM_MYSQL_FETCH_SUCCESS) {
             try {
                 // For each returned row call user function which should
@@ -427,26 +437,28 @@ public:
     template<typename StatementIndex>
     void insertQuery(const StatementIndex& index,
                      const MySqlBindingCollection& in_bindings) {
+        MySqlHolder& holderHandle = handle();
         std::vector<MYSQL_BIND> in_bind_vec;
+
         for (MySqlBindingPtr in_binding : in_bindings) {
             in_bind_vec.push_back(in_binding->getMySqlBinding());
         }
 
         // Bind the parameters to the statement
-        int status = mysql_stmt_bind_param(statements_[index],
+        int status = mysql_stmt_bind_param(holderHandle.statements_[index],
                                            in_bind_vec.empty() ? 0 : &in_bind_vec[0]);
         checkError(status, index, "unable to bind parameters");
 
         // Execute the statement
-        status = mysql_stmt_execute(statements_[index]);
+        status = mysql_stmt_execute(holderHandle.statements_[index]);
 
         if (status != 0) {
             // Failure: check for the special case of duplicate entry.
-            if (mysql_errno(mysql_) == ER_DUP_ENTRY) {
+            if (mysql_errno(holderHandle) == ER_DUP_ENTRY) {
                 isc_throw(DuplicateEntry, "Database duplicate entry error");
             }
             // Failure: check for the special case of WHERE returning NULL.
-            if (mysql_errno(mysql_) == ER_BAD_NULL_ERROR) {
+            if (mysql_errno(holderHandle) == ER_BAD_NULL_ERROR) {
                 isc_throw(NullKeyError, "Database bad NULL error");
             }
             checkError(status, index, "unable to execute");
@@ -470,30 +482,32 @@ public:
     template<typename StatementIndex>
     uint64_t updateDeleteQuery(const StatementIndex& index,
                                const MySqlBindingCollection& in_bindings) {
+        MySqlHolder& holderHandle = handle();
         std::vector<MYSQL_BIND> in_bind_vec;
+
         for (MySqlBindingPtr in_binding : in_bindings) {
             in_bind_vec.push_back(in_binding->getMySqlBinding());
         }
 
         // Bind the parameters to the statement
-        int status = mysql_stmt_bind_param(statements_[index],
+        int status = mysql_stmt_bind_param(holderHandle.statements_[index],
                                            in_bind_vec.empty() ? 0 : &in_bind_vec[0]);
         checkError(status, index, "unable to bind parameters");
 
         // Execute the statement
-        status = mysql_stmt_execute(statements_[index]);
+        status = mysql_stmt_execute(holderHandle.statements_[index]);
 
         if (status != 0) {
             // Failure: check for the special case of duplicate entry.
-            if ((mysql_errno(mysql_) == ER_DUP_ENTRY)
+            if ((mysql_errno(holderHandle) == ER_DUP_ENTRY)
 #ifdef ER_FOREIGN_DUPLICATE_KEY
-                || (mysql_errno(mysql_) == ER_FOREIGN_DUPLICATE_KEY)
+                || (mysql_errno(holderHandle) == ER_FOREIGN_DUPLICATE_KEY)
 #endif
 #ifdef ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO
-                || (mysql_errno(mysql_) == ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO)
+                || (mysql_errno(holderHandle) == ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO)
 #endif
 #ifdef ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO
-                || (mysql_errno(mysql_) == ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO)
+                || (mysql_errno(holderHandle) == ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO)
 #endif
                 ) {
                 isc_throw(DuplicateEntry, "Database duplicate entry error");
@@ -502,7 +516,7 @@ public:
         }
 
         // Let's return how many rows were affected.
-        return (static_cast<uint64_t>(mysql_stmt_affected_rows(statements_[index])));
+        return (static_cast<uint64_t>(mysql_stmt_affected_rows(holderHandle.statements_[index])));
     }
 
 
@@ -552,11 +566,13 @@ public:
     ///
     /// @throw isc::db::DbOperationError An operation on the open database has
     ///        failed.
-    template<typename StatementIndex>
+    template <typename StatementIndex>
     void checkError(const int status, const StatementIndex& index,
                     const char* what) const {
+        MySqlHolder& holderHandle = handle();
+
         if (status != 0) {
-            switch(mysql_errno(mysql_)) {
+            switch(mysql_errno(holderHandle)) {
                 // These are the ones we consider fatal. Remember this method is
                 // used to check errors of API calls made subsequent to successfully
                 // connecting.  Errors occurring while attempting to connect are
@@ -570,13 +586,13 @@ public:
                 DB_LOG_ERROR(db::MYSQL_FATAL_ERROR)
                     .arg(what)
                     .arg(text_statements_[static_cast<int>(index)])
-                    .arg(mysql_error(mysql_))
-                    .arg(mysql_errno(mysql_));
+                    .arg(mysql_error(holderHandle))
+                    .arg(mysql_errno(holderHandle));
 
                 // If there's no lost db callback or it returns false,
                 // then we're not attempting to recover so we're done
                 if (!invokeDbLostCallback()) {
-                    exit (-1);
+                    exit(-1);
                 }
 
                 // We still need to throw so caller can error out of the current
@@ -588,32 +604,43 @@ public:
                 isc_throw(db::DbOperationError, what << " for <"
                           << text_statements_[static_cast<int>(index)]
                           << ">, reason: "
-                          << mysql_error(mysql_) << " (error code "
-                          << mysql_errno(mysql_) << ")");
+                          << mysql_error(holderHandle) << " (error code "
+                          << mysql_errno(holderHandle) << ")");
             }
         }
     }
 
-    /// @brief Prepared statements
-    ///
-    /// This field is public, because it is used heavily from MySqlConnection
-    /// and will be from MySqlHostDataSource.
-    std::vector<MYSQL_STMT*> statements_;
-
     /// @brief Raw text of statements
     ///
     /// This field is public, because it is used heavily from MySqlConnection
-    /// and will be from MySqlHostDataSource.
+    /// and from MySqlHostDataSource.
     std::vector<std::string> text_statements_;
 
     /// @brief MySQL connection handle
     ///
     /// This field is public, because it is used heavily from MySqlConnection
-    /// and will be from MySqlHostDataSource.
-    MySqlHolder mysql_;
+    /// and from MySqlHostDataSource.
+    MySqlHolder& handle() const {
+        auto result = handles_.resource();
+        // thread_local std::shared_ptr<MySqlHolder> result(std::make_shared<MySqlHolder>());
+        if (connected_) {
+            result->openDatabase(*(const_cast<MySqlConnection*>(this)));
+        }
+        if (prepared_) {
+            result->prepareStatements(*(const_cast<MySqlConnection*>(this)));
+        }
+        return *result;
+    }
+
+private:
+    bool connected_;     ///< Flag to indicate openDatabase has been called
+
+    bool prepared_;      ///< Flag to indicate prepareStatements has been called
+
+    mutable isc::dhcp::ThreadResourceMgr<MySqlHolder> handles_;
 };
 
-}; // end of isc::db namespace
-}; // end of isc namespace
+}  // namespace db
+}  // namespace isc
 
-#endif // MYSQL_CONNECTION_H
+#endif  // MYSQL_CONNECTION_H
index 7d0a04d674cd4c594a1755fd9ad630aded40aa86..85e620fa1917d1f1d91fc333949cd04a45d0ae53 100644 (file)
@@ -63,11 +63,6 @@ public:
         try {
             // Open new connection.
             conn_.openDatabase();
-            my_bool result = mysql_autocommit(conn_.mysql_, 1);
-            if (result != 0) {
-                isc_throw(DbOperationError, "failed to set autocommit option "
-                          "for test MySQL connection");
-            }
 
             // Create mysql_connection_test table.
             createTestTable();
@@ -124,22 +119,24 @@ public:
     ///
     /// @param sql Query in the textual form.
     void runQuery(const std::string& sql) {
-        MYSQL_STMT *stmt = mysql_stmt_init(conn_.mysql_);
+        MySqlHolder& holderHandle = conn_.handle();
+
+        MYSQL_STMT *stmt = mysql_stmt_init(holderHandle);
         if (stmt == NULL) {
             isc_throw(DbOperationError, "unable to allocate MySQL prepared "
-                  "statement structure, reason: " << mysql_error(conn_.mysql_));
+                  "statement structure, reason: " << mysql_error(holderHandle));
         }
 
         int status = mysql_stmt_prepare(stmt, sql.c_str(), sql.length());
         if (status != 0) {
             isc_throw(DbOperationError, "unable to prepare MySQL statement <"
-                      << sql << ">, reason: " << mysql_errno(conn_.mysql_));
+                      << sql << ">, reason: " << mysql_errno(holderHandle));
         }
 
         // Execute the prepared statement.
         if (mysql_stmt_execute(stmt) != 0) {
             isc_throw(DbOperationError, "cannot execute MySQL query <"
-                  << sql << ">, reason: " << mysql_errno(conn_.mysql_));
+                  << sql << ">, reason: " << mysql_errno(holderHandle));
         }
 
         // Discard the statement and its resources