]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
implemented automatic creation of thread connection to backend
authorRazvan Becheriu <razvan@isc.org>
Sun, 7 Apr 2019 09:24:15 +0000 (12:24 +0300)
committerRazvan Becheriu <razvan@isc.org>
Fri, 12 Apr 2019 12:10:41 +0000 (15:10 +0300)
20 files changed:
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.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/mysql_lease_mgr.h
src/lib/dhcpsrv/pgsql_host_data_source.cc
src/lib/dhcpsrv/pgsql_lease_mgr.cc
src/lib/dhcpsrv/pgsql_lease_mgr.h
src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc
src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc
src/lib/dhcpsrv/thread_pool.cc
src/lib/dhcpsrv/thread_pool.h
src/lib/dhcpsrv/thread_resource_mgr.h [new file with mode: 0644]
src/lib/mysql/mysql_connection.cc
src/lib/mysql/mysql_connection.h
src/lib/mysql/tests/mysql_connection_unittest.cc
src/lib/pgsql/pgsql_connection.cc
src/lib/pgsql/pgsql_connection.h
src/lib/pgsql/pgsql_exchange.h
src/lib/pgsql/tests/pgsql_exchange_unittest.cc

index bd3e2c5cb40b5c593d899d276b42abcb4d5d5a60..0c06e4372098d9dd1a3646039b33078d077725b1 100644 (file)
@@ -192,7 +192,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());
 
             // Create bindings for inserting the association into
             // dhcp4_global_parameter_server table.
@@ -887,7 +887,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);
@@ -1350,7 +1350,7 @@ public:
                           in_bindings);
 
         // Fetch unique identifier of the inserted option.
-        uint64_t id = mysql_insert_id(conn_.mysql_);
+        uint64_t id = mysql_insert_id(conn_.handle());
 
         // Create bindings needed to insert association of that option with
         // a server into the dhcp4_options_server table.
@@ -1731,7 +1731,7 @@ public:
 
             // 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());
 
             MySqlBindingCollection in_server_bindings = {
                 MySqlBinding::createInteger<uint64_t>(id), // option_def_id
index 40ebbdcb4849d19e987fcc4bb2329a34bfae7883..c7c81c4007f3f6e35bf2a9340e3f1502ce3b06e7 100644 (file)
@@ -63,10 +63,10 @@ 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;
+    for (int i = 0; i < conn_.handle().statements_.size(); ++i) {
+        if (conn_.handle().statements_[i] != NULL) {
+            (void) mysql_stmt_close(conn_.handle().statements_[i]);
+            conn_.handle().statements_[i] = NULL;
         }
     }
 }
index d5e562972b20da9f5cf674c1464c069cb3c0af23..dc987749d2131db32d0ed343522d5ab201648c33 100644 (file)
@@ -2265,7 +2265,6 @@ TaggedStatementArray tagged_statements = { {
                 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
                 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
                 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
-
                 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
                 "h.dhcp4_boot_file_name, h.auth_key, "
                 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
@@ -2395,7 +2394,6 @@ TaggedStatementArray tagged_statements = { {
                 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
                 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
                 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
-
                 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
                 "h.dhcp4_boot_file_name, h.auth_key, "
                 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
@@ -2460,10 +2458,10 @@ 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;
+    for (int i = 0; i < conn_.handle().statements_.size(); ++i) {
+        if (conn_.handle().statements_[i] != NULL) {
+            (void) mysql_stmt_close(conn_.handle().statements_[i]);
+            conn_.handle().statements_[i] = NULL;
         }
     }
 
@@ -2477,24 +2475,26 @@ MySqlHostDataSourceImpl::getVersion() const {
               DHCPSRV_MYSQL_HOST_DB_GET_VERSION);
 
     // Allocate a new statement.
-    MYSQL_STMT *stmt = mysql_stmt_init(conn_.mysql_);
+    MYSQL_STMT *stmt = mysql_stmt_init(conn_.handle());
     if (stmt == NULL) {
         isc_throw(DbOperationError, "unable to allocate MySQL prepared "
-                  "statement structure, reason: " << mysql_error(conn_.mysql_));
+                  "statement structure, reason: " << mysql_error(conn_.handle()));
     }
 
     // Prepare the statement from SQL text.
     const char* version_sql = "SELECT version, minor FROM schema_version";
     int status = mysql_stmt_prepare(stmt, version_sql, strlen(version_sql));
     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_errno(conn_.handle()));
     }
 
     // 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_errno(conn_.handle()));
     }
 
     // Bind the output of the statement to the appropriate variables.
@@ -2515,14 +2515,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_errno(conn_.handle()));
     }
 
     // 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_errno(conn_.handle()));
     }
 
     // Discard the statement and its resources
@@ -2537,15 +2537,15 @@ MySqlHostDataSourceImpl::addStatement(StatementIndex stindex,
                                       std::vector<MYSQL_BIND>& bind) {
 
     // Bind the parameters to the statement
-    int status = mysql_stmt_bind_param(conn_.statements_[stindex], &bind[0]);
+    int status = mysql_stmt_bind_param(conn_.handle().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(conn_.handle().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(conn_.handle()) == ER_DUP_ENTRY) {
             isc_throw(DuplicateEntry, "Database duplicate entry error");
         }
         checkError(status, stindex, "unable to execute");
@@ -2556,18 +2556,18 @@ bool
 MySqlHostDataSourceImpl::delStatement(StatementIndex stindex,
                                       MYSQL_BIND* bind) {
     // Bind the parameters to the statement
-    int status = mysql_stmt_bind_param(conn_.statements_[stindex], &bind[0]);
+    int status = mysql_stmt_bind_param(conn_.handle().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(conn_.handle().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(conn_.handle().statements_[stindex]);
     return (numrows != 0);
 }
 
@@ -2638,30 +2638,30 @@ getHostCollection(StatementIndex stindex, MYSQL_BIND* bind,
                   ConstHostCollection& result, bool single) const {
 
     // Bind the selection parameters to the statement
-    int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
+    int status = mysql_stmt_bind_param(conn_.handle().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(conn_.handle().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(conn_.handle().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(conn_.handle().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(conn_.handle().statements_[stindex]);
+    while ((status = mysql_stmt_fetch(conn_.handle().statements_[stindex])) ==
            MLM_MYSQL_FETCH_SUCCESS) {
         try {
             exchange->processFetchedData(result);
@@ -2776,7 +2776,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(impl_->conn_.handle());
 
     // Insert DHCPv4 options.
     ConstCfgOptionPtr cfg_option4 = host->getCfgOption4();
@@ -2830,7 +2830,7 @@ MySqlHostDataSource::del(const SubnetID& subnet_id, const asiolink::IOAddress& a
     }
 
     // v6
-    ConstHostPtr host = get6(subnet_id, addr);
+    ConstHostPtr host(get6(subnet_id, addr));
     if (!host) {
         return (false);
     }
@@ -3224,7 +3224,8 @@ MySqlHostDataSource::get6(const SubnetID& subnet_id,
 
 // Miscellaneous database methods.
 
-std::string MySqlHostDataSource::getName() const {
+std::string
+MySqlHostDataSource::getName() const {
     std::string name = "";
     try {
         name = impl_->conn_.getParameter("name");
@@ -3234,7 +3235,8 @@ std::string MySqlHostDataSource::getName() const {
     return (name);
 }
 
-std::string MySqlHostDataSource::getDescription() const {
+std::string
+MySqlHostDataSource::getDescription() const {
     return (std::string("Host data source that stores host information"
                         "in MySQL database"));
 }
index 8869dcd6e980cffb3a02e0690e5834a6836ba693..a11ebf62c649d90132d262b55a091e1716f57a91 100644 (file)
@@ -513,7 +513,7 @@ public:
                 bind_[2].buffer_length = client_id_length_;
                 bind_[2].length = &client_id_length_;
                 // bind_[2].is_null = &MLM_FALSE; // commented out for performance
-                                                 // reasons, see memset() above
+                                                  // reasons, see memset() above
             } else {
                 bind_[2].buffer_type = MYSQL_TYPE_NULL;
                 // According to http://dev.mysql.com/doc/refman/5.5/en/
@@ -579,7 +579,7 @@ public:
             // bind_[8].is_null = &MLM_FALSE; // commented out for performance
                                               // reasons, see memset() above
 
-            // state: uint32_t.
+            // state: uint32_t
             bind_[9].buffer_type = MYSQL_TYPE_LONG;
             bind_[9].buffer = reinterpret_cast<char*>(&lease_->state_);
             bind_[9].is_unsigned = MLM_TRUE;
@@ -705,7 +705,7 @@ public:
         // bind_[8].is_null = &MLM_FALSE; // commented out for performance
                                           // reasons, see memset() above
 
-        // state:  uint32_t
+        // state: uint32_t
         bind_[9].buffer_type = MYSQL_TYPE_LONG;
         bind_[9].buffer = reinterpret_cast<char*>(&state_);
         bind_[9].is_unsigned = MLM_TRUE;
@@ -1111,7 +1111,7 @@ public:
                 bind_[14].is_null = &hwaddr_null_;
             }
 
-            // state:  uint32_t
+            // state: uint32_t
             bind_[15].buffer_type = MYSQL_TYPE_LONG;
             bind_[15].buffer = reinterpret_cast<char*>(&lease_->state_);
             bind_[15].is_unsigned = MLM_TRUE;
@@ -1278,7 +1278,7 @@ public:
         bind_[14].buffer = reinterpret_cast<char*>(&hwaddr_source_);
         bind_[14].is_unsigned = MLM_TRUE;
 
-        // state:  uint32_t
+        // state: uint32_t
         bind_[15].buffer_type = MYSQL_TYPE_LONG;
         bind_[15].buffer = reinterpret_cast<char*>(&state_);
         bind_[15].is_unsigned = MLM_TRUE;
@@ -1474,7 +1474,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();
     }
 
@@ -1494,7 +1494,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();
     }
 
@@ -1518,7 +1518,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();
     }
 
@@ -1577,7 +1577,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;
 
@@ -1618,7 +1618,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) {
@@ -1638,7 +1638,7 @@ private:
                       " - invalid statement index" << statement_index_);
         }
 
-        statement_ = conn_.statements_[statement_index_];
+        statement_ = conn_.handle().statements_[statement_index_];
     }
 
     /// @brief Database connection to use to execute the query
@@ -1661,7 +1661,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_;
 };
@@ -1712,17 +1712,17 @@ MySqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
                               std::vector<MYSQL_BIND>& bind) {
 
     // Bind the parameters to the statement
-    int status = mysql_stmt_bind_param(conn_.statements_[stindex], &bind[0]);
+    int status = mysql_stmt_bind_param(conn_.handle().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(conn_.handle().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(conn_.handle()) == ER_DUP_ENTRY) {
             return (false);
         }
         checkError(status, stindex, "unable to execute");
@@ -1797,31 +1797,31 @@ void MySqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
 
     if (bind) {
         // Bind the selection parameters to the statement
-        status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
+        status = mysql_stmt_bind_param(conn_.handle().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(conn_.handle().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(conn_.handle().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(conn_.handle().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(conn_.handle().statements_[stindex]);
     int count = 0;
-    while ((status = mysql_stmt_fetch(conn_.statements_[stindex])) == 0) {
+    while ((status = mysql_stmt_fetch(conn_.handle().statements_[stindex])) == 0) {
         try {
             result.push_back(exchange->getLeaseData());
 
@@ -2317,7 +2317,7 @@ Lease6Collection
 MySqlLeaseMgr::getLeases6(const DUID& duid) const {
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_DUID)
              .arg(duid.toText());
-   
+
     // Set up the WHERE clause value
     MYSQL_BIND inbind[1];
     memset(inbind, 0, sizeof(inbind));
@@ -2330,9 +2330,9 @@ MySqlLeaseMgr::getLeases6(const DUID& duid) const {
             const_cast<uint8_t*>(&duid_vector[0]));
     inbind[0].buffer_length = duid_length;
     inbind[0].length = &duid_length;
-    
+
     Lease6Collection result;
-    
+
     getLeaseCollection(GET_LEASE6_DUID, inbind, result);
 
     return result;
@@ -2444,16 +2444,16 @@ MySqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind,
                                  const LeasePtr& lease) {
 
     // Bind the parameters to the statement
-    int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
+    int status = mysql_stmt_bind_param(conn_.handle().statements_[stindex], bind);
     checkError(status, stindex, "unable to bind parameters");
 
     // Execute
-    status = mysql_stmt_execute(conn_.statements_[stindex]);
+    status = mysql_stmt_execute(conn_.handle().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(conn_.handle().statements_[stindex]);
     if (affected_rows == 0) {
         isc_throw(NoSuchLease, "unable to update lease for address " <<
                   lease->addr_ << " as it does not exist");
@@ -2532,16 +2532,16 @@ uint64_t
 MySqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind) {
 
     // Bind the input parameters to the statement
-    int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
+    int status = mysql_stmt_bind_param(conn_.handle().statements_[stindex], bind);
     checkError(status, stindex, "unable to bind WHERE clause parameter");
 
     // Execute
-    status = mysql_stmt_execute(conn_.statements_[stindex]);
+    status = mysql_stmt_execute(conn_.handle().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(conn_.handle().statements_[stindex])));
 }
 
 bool
@@ -2716,10 +2716,10 @@ MySqlLeaseMgr::getVersion() const {
               DHCPSRV_MYSQL_GET_VERSION);
 
     // Allocate a new statement.
-    MYSQL_STMT *stmt = mysql_stmt_init(conn_.mysql_);
+    MYSQL_STMT *stmt = mysql_stmt_init(conn_.handle());
     if (stmt == NULL) {
         isc_throw(DbOperationError, "unable to allocate MySQL prepared "
-                "statement structure, reason: " << mysql_error(conn_.mysql_));
+                "statement structure, reason: " << mysql_error(conn_.handle()));
     }
 
     // Prepare the statement from SQL text.
@@ -2727,13 +2727,13 @@ MySqlLeaseMgr::getVersion() const {
     int status = mysql_stmt_prepare(stmt, version_sql, strlen(version_sql));
     if (status != 0) {
         isc_throw(DbOperationError, "unable to prepare MySQL statement <"
-                  << version_sql << ">, reason: " << mysql_error(conn_.mysql_));
+                  << version_sql << ">, reason: " << mysql_error(conn_.handle()));
     }
 
     // Execute the prepared statement.
     if (mysql_stmt_execute(stmt) != 0) {
         isc_throw(DbOperationError, "cannot execute schema version query <"
-                  << version_sql << ">, reason: " << mysql_errno(conn_.mysql_));
+                  << version_sql << ">, reason: " << mysql_errno(conn_.handle()));
     }
 
     // Bind the output of the statement to the appropriate variables.
@@ -2754,14 +2754,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_errno(conn_.handle()));
     }
 
     // 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_errno(conn_.handle()));
     }
 
     // Discard the statement and its resources
@@ -2773,16 +2773,16 @@ 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_));
+    if (mysql_commit(conn_.handle()) != 0) {
+        isc_throw(DbOperationError, "commit failed: " << mysql_error(conn_.handle()));
     }
 }
 
 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_));
+    if (mysql_rollback(conn_.handle()) != 0) {
+        isc_throw(DbOperationError, "rollback failed: " << mysql_error(conn_.handle()));
     }
 }
 
index e918e15512ac6bc614243a40bf56bd66b5deb187..9d976ad76e478d98d9c7088754520e724ca13c96 100644 (file)
@@ -316,7 +316,7 @@ public:
     /// @return Lease collection (may be empty if no IPv6 lease found)
     /// for the DUID.
     virtual Lease6Collection getLeases6(const DUID& duid) const;
-    
+
     /// @brief Returns range of IPv6 leases using paging.
     ///
     /// This method implements paged browsing of the lease database. The first
index 12db8f62674f2265f8823fed3038ec1e835124ea..363a009f1fcfbcbe44cfa56ac35642d747dae1bf 100644 (file)
@@ -297,7 +297,7 @@ public:
         // most recently added host is different than the host id of the
         // currently processed host.
         if (hosts.empty() || row_host_id != hosts.back()->getHostId()) {
-            HostPtr host = retrieveHost(r, row, row_host_id);
+            HostPtr host(retrieveHost(r, row, row_host_id));
             hosts.push_back(host);
         }
     }
@@ -1263,7 +1263,7 @@ private:
     OptionPtr option_;
 };
 
-} // end of anonymous namespace
+}  // namespace
 
 namespace isc {
 namespace dhcp {
@@ -1624,9 +1624,8 @@ TaggedStatementArray tagged_statements = { {
     // PgSqlHostDataSourceImpl::INSERT_HOST
     // Inserts a host into the 'hosts' table. Returns the inserted host id.
     {13,
-     { OID_BYTEA, OID_INT2,
-       OID_INT8, OID_INT8, OID_INT8, OID_VARCHAR,
-       OID_VARCHAR, OID_VARCHAR, OID_TEXT },
+     { OID_BYTEA, OID_INT2, OID_INT8, OID_INT8, OID_INT8, OID_VARCHAR, OID_VARCHAR, OID_VARCHAR,
+       OID_TEXT, OID_INT8, OID_VARCHAR, OID_VARCHAR, OID_VARCHAR},
      "insert_host",
      "INSERT INTO hosts(dhcp_identifier, dhcp_identifier_type, "
      "  dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
@@ -1800,7 +1799,7 @@ TaggedStatementArray tagged_statements = { {
 }
 };
 
-}; // end anonymous namespace
+}  // namespace
 
 PgSqlHostDataSourceImpl::
 PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters)
@@ -1818,7 +1817,7 @@ PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters)
         isc_throw(DbOpenError,
                   "PostgreSQL schema version mismatch: need version: "
                       << code_version.first << "." << code_version.second
-                      << " found version:  " << db_version.first << "."
+                      << " found version: " << db_version.first << "."
                       << db_version.second);
     }
 
@@ -1849,7 +1848,7 @@ PgSqlHostDataSourceImpl::addStatement(StatementIndex stindex,
                                       PsqlBindArrayPtr& bind_array,
                                       const bool return_last_id) {
     uint64_t last_id = 0;
-    PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
+    PgSqlResult r(PQexecPrepared(conn_.handle(), tagged_statements[stindex].name,
                                  tagged_statements[stindex].nbparams,
                                  &bind_array->values_[0],
                                  &bind_array->lengths_[0],
@@ -1878,7 +1877,7 @@ PgSqlHostDataSourceImpl::addStatement(StatementIndex stindex,
 bool
 PgSqlHostDataSourceImpl::delStatement(StatementIndex stindex,
                                       PsqlBindArrayPtr& bind_array) {
-    PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
+    PgSqlResult r(PQexecPrepared(conn_.handle(), tagged_statements[stindex].name,
                                  tagged_statements[stindex].nbparams,
                                  &bind_array->values_[0],
                                  &bind_array->lengths_[0],
@@ -1961,7 +1960,7 @@ getHostCollection(StatementIndex stindex, PsqlBindArrayPtr bind_array,
                   ConstHostCollection& result, bool single) const {
 
     exchange->clear();
-    PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
+    PgSqlResult r(PQexecPrepared(conn_.handle(), tagged_statements[stindex].name,
                                  tagged_statements[stindex].nbparams,
                                  &bind_array->values_[0],
                                  &bind_array->lengths_[0],
@@ -2018,10 +2017,10 @@ std::pair<uint32_t, uint32_t> PgSqlHostDataSourceImpl::getVersion() const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_PGSQL_HOST_DB_GET_VERSION);
     const char* version_sql =  "SELECT version, minor FROM schema_version;";
-    PgSqlResult r(PQexec(conn_, version_sql));
+    PgSqlResult r(PQexec(conn_.handle(), version_sql));
     if(PQresultStatus(r) != PGRES_TUPLES_OK) {
         isc_throw(DbOperationError, "unable to execute PostgreSQL statement <"
-                  << version_sql << ">, reason: " << PQerrorMessage(conn_));
+                  << version_sql << ">, reason: " << PQerrorMessage(conn_.handle()));
     }
 
     uint32_t version;
@@ -2425,7 +2424,8 @@ PgSqlHostDataSource::get6(const SubnetID& subnet_id,
 
 // Miscellaneous database methods.
 
-std::string PgSqlHostDataSource::getName() const {
+std::string
+PgSqlHostDataSource::getName() const {
     std::string name = "";
     try {
         name = impl_->conn_.getParameter("name");
@@ -2435,7 +2435,8 @@ std::string PgSqlHostDataSource::getName() const {
     return (name);
 }
 
-std::string PgSqlHostDataSource::getDescription() const {
+std::string
+PgSqlHostDataSource::getDescription() const {
     return (std::string("Host data source that stores host information"
                         "in PostgreSQL database"));
 }
@@ -2458,5 +2459,5 @@ PgSqlHostDataSource::rollback() {
     impl_->conn_.rollback();
 }
 
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
+}  // namespace dhcp
+}  // namespace isc
index 2fddbb70580853d379e964a31de7124e438c8ae5..45c59627ff04ee0f8d0d6d55f68ad236960a96f3 100644 (file)
@@ -133,7 +133,7 @@ PgSqlTaggedStatement tagged_statements[] = {
       "SELECT address, hwaddr, client_id, "
         "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, "
         "fqdn_fwd, fqdn_rev, hostname, "
-      "state, user_context "
+        "state, user_context "
       "FROM lease4 "
       "WHERE subnet_id = $1"},
 
@@ -285,12 +285,12 @@ PgSqlTaggedStatement tagged_statements[] = {
         "prefix_len = $9, fqdn_fwd = $10, fqdn_rev = $11, hostname = $12, "
         "hwaddr = $13, hwtype = $14, hwaddr_source = $15, "
         "state = $16, user_context = $17 "
-      "WHERE address = $18"},
+      "WHERE address = $18" },
     // ALL_LEASE4_STATS
     { 0, { OID_NONE },
       "all_lease4_stats",
       "SELECT subnet_id, state, leases as state_count"
-      "  FROM lease4_stat ORDER BY subnet_id, state"},
+      "  FROM lease4_stat ORDER BY subnet_id, state" },
 
     // SUBNET_LEASE4_STATS
     { 1, { OID_INT8 },
@@ -298,7 +298,7 @@ PgSqlTaggedStatement tagged_statements[] = {
       "SELECT subnet_id, state, leases as state_count"
       "  FROM lease4_stat "
       "  WHERE subnet_id = $1 "
-      "  ORDER BY state"},
+      "  ORDER BY state" },
 
     // SUBNET_RANGE_LEASE4_STATS
     { 2, { OID_INT8, OID_INT8 },
@@ -306,7 +306,7 @@ PgSqlTaggedStatement tagged_statements[] = {
       "SELECT subnet_id, state, leases as state_count"
       "  FROM lease4_stat "
       "  WHERE subnet_id >= $1 and subnet_id <= $2 "
-      "  ORDER BY subnet_id, state"},
+      "  ORDER BY subnet_id, state" },
 
     // ALL_LEASE6_STATS,
     { 0, { OID_NONE },
@@ -987,7 +987,7 @@ public:
 
         if (getSelectMode() == ALL_SUBNETS) {
             // Run the query with no where clause parameters.
-            result_set_.reset(new PgSqlResult(PQexecPrepared(conn_, statement_.name,
+            result_set_.reset(new PgSqlResult(PQexecPrepared(conn_.handle(), statement_.name,
                                                              0, 0, 0, 0, 0)));
         } else {
             // Set up the WHERE clause values
@@ -1005,7 +1005,7 @@ public:
             }
 
             // Run the query with where clause parameters.
-            result_set_.reset(new PgSqlResult(PQexecPrepared(conn_, statement_.name,
+            result_set_.reset(new PgSqlResult(PQexecPrepared(conn_.handle(), statement_.name,
                                               parms.size(), &parms.values_[0],
                                               &parms.lengths_[0], &parms.formats_[0], 0)));
         }
@@ -1097,7 +1097,7 @@ PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
 
     // Now prepare the SQL statements.
     int i = 0;
-    for( ; tagged_statements[i].text != NULL ; ++i) {
+    for(; tagged_statements[i].text != NULL; ++i) {
         conn_.prepareStatement(tagged_statements[i]);
     }
 
@@ -1123,7 +1123,7 @@ PgSqlLeaseMgr::getDBVersion() {
 bool
 PgSqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
                               PsqlBindArray& bind_array) {
-    PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
+    PgSqlResult r(PQexecPrepared(conn_.handle(), tagged_statements[stindex].name,
                                  tagged_statements[stindex].nbparams,
                                  &bind_array.values_[0],
                                  &bind_array.lengths_[0],
@@ -1176,7 +1176,7 @@ void PgSqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
                                        LeaseCollection& result,
                                        bool single) const {
     const int n = tagged_statements[stindex].nbparams;
-    PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name, n,
+    PgSqlResult r(PQexecPrepared(conn_.handle(), tagged_statements[stindex].name, n,
                                  n > 0 ? &bind_array.values_[0] : NULL,
                                  n > 0 ? &bind_array.lengths_[0] : NULL,
                                  n > 0 ? &bind_array.formats_[0] : NULL, 0));
@@ -1661,7 +1661,7 @@ PgSqlLeaseMgr::updateLeaseCommon(StatementIndex stindex,
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_PGSQL_ADD_ADDR4).arg(tagged_statements[stindex].name);
 
-    PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
+    PgSqlResult r(PQexecPrepared(conn_.handle(), tagged_statements[stindex].name,
                                  tagged_statements[stindex].nbparams,
                                  &bind_array.values_[0],
                                  &bind_array.lengths_[0],
@@ -1733,7 +1733,7 @@ PgSqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
 uint64_t
 PgSqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex,
                                  PsqlBindArray& bind_array) {
-    PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
+    PgSqlResult r(PQexecPrepared(conn_.handle(), tagged_statements[stindex].name,
                                  tagged_statements[stindex].nbparams,
                                  &bind_array.values_[0],
                                  &bind_array.lengths_[0],
@@ -1885,24 +1885,19 @@ PgSqlLeaseMgr::getVersion() const {
               DHCPSRV_PGSQL_GET_VERSION);
 
     const char* version_sql =  "SELECT version, minor FROM schema_version;";
-    PgSqlResult r(PQexec(conn_, version_sql));
+    PgSqlResult r(PQexec(conn_.handle(), version_sql));
     if(PQresultStatus(r) != PGRES_TUPLES_OK) {
         isc_throw(DbOperationError, "unable to execute PostgreSQL statement <"
-                  << version_sql << ", reason: " << PQerrorMessage(conn_));
+                  << version_sql << ", reason: " << PQerrorMessage(conn_.handle()));
     }
 
-    istringstream tmp;
-    uint32_t version;
-    tmp.str(PQgetvalue(r, 0, 0));
-    tmp >> version;
-    tmp.str("");
-    tmp.clear();
+    uint32_t major;
+    PgSqlExchange::getColumnValue(r, 0, 0, major);
 
     uint32_t minor;
-    tmp.str(PQgetvalue(r, 0, 1));
-    tmp >> minor;
+    PgSqlExchange::getColumnValue(r, 0, 1, minor);
 
-    return (make_pair(version, minor));
+    return (make_pair(major, minor));
 }
 
 void
@@ -1915,5 +1910,5 @@ PgSqlLeaseMgr::rollback() {
     conn_.rollback();
 }
 
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
+}  // namespace dhcp
+}  // namespace isc
index f2d4a87b89d5e22579aa0c29692f1dc21c5ee9ce..938577c07f53416180aaf6ef8e1d3a765d974359 100644 (file)
@@ -288,7 +288,7 @@ public:
     /// @return Lease collection (may be empty if no IPv6 lease found)
     /// for the DUID
     virtual Lease6Collection getLeases6(const DUID& duid) const;
-    
+
     /// @brief Returns range of IPv6 leases using paging.
     ///
     /// This method implements paged browsing of the lease database. The first
index 5f92dc37ad451595a1d883a7d61f0748d473cf39..fc75ec707f78e4a6202af6cd423ddbf3a17c6cef 100644 (file)
@@ -120,12 +120,12 @@ public:
         MySqlConnection conn(params);
         conn.openDatabase();
 
-        int status = mysql_query(conn.mysql_, query.c_str());
+        int status = mysql_query(conn.handle(), query.c_str());
         if (status !=0) {
-            isc_throw(DbOperationError, "Query failed: " << mysql_error(conn.mysql_));
+            isc_throw(DbOperationError, "Query failed: " << mysql_error(conn.handle()));
         }
 
-        MYSQL_RES * res = mysql_store_result(conn.mysql_);
+        MYSQL_RES * res = mysql_store_result(conn.handle());
         int numrows = static_cast<int>(mysql_num_rows(res));
         mysql_free_result(res);
 
@@ -641,9 +641,9 @@ TEST_F(MySqlHostDataSourceTest, testAddRollback) {
     MySqlConnection conn(params);
     ASSERT_NO_THROW(conn.openDatabase());
 
-    int status = mysql_query(conn.mysql_,
+    int status = mysql_query(conn.handle(),
                              "DROP TABLE IF EXISTS ipv6_reservations");
-    ASSERT_EQ(0, status) << mysql_error(conn.mysql_);
+    ASSERT_EQ(0, status) << mysql_error(conn.handle());
 
     // Create a host with a reservation.
     HostPtr host = HostDataSourceUtils::initializeHost6("2001:db8:1::1", Host::IDENT_HWADDR, false);
index 8c49402b9fd28a04457dd74ddfd6642a8c951573..01582ce4222f392eb7fbe8a8e57e29d2706198d7 100644 (file)
@@ -119,9 +119,9 @@ public:
         PgSqlConnection conn(params);
         conn.openDatabase();
 
-        PgSqlResult r(PQexec(conn, query.c_str()));
+        PgSqlResult r(PQexec(conn.handle(), query.c_str()));
         if (PQresultStatus(r) != PGRES_TUPLES_OK) {
-            isc_throw(DbOperationError, "Query failed:" << PQerrorMessage(conn));
+            isc_throw(DbOperationError, "Query failed:" << PQerrorMessage(conn.handle()));
         }
 
         int numrows = PQntuples(r);
@@ -626,9 +626,9 @@ TEST_F(PgSqlHostDataSourceTest, testAddRollback) {
     PgSqlConnection conn(params);
     ASSERT_NO_THROW(conn.openDatabase());
 
-    PgSqlResult r(PQexec(conn, "DROP TABLE IF EXISTS ipv6_reservations"));
+    PgSqlResult r(PQexec(conn.handle(), "DROP TABLE IF EXISTS ipv6_reservations"));
     ASSERT_TRUE (PQresultStatus(r) == PGRES_COMMAND_OK)
-                 << " drop command failed :" << PQerrorMessage(conn);
+                 << " drop command failed :" << PQerrorMessage(conn.handle());
 
     // Create a host with a reservation.
     HostPtr host = HostDataSourceUtils::initializeHost6("2001:db8:1::1",
index 191cc330b4822a702e9f66df6ad959441773e2a0..2b666e3e3c42d441c0372a77127f24c79b9991b2 100644 (file)
@@ -1,3 +1,21 @@
+// Copyright (C) 2017-2019 Deutsche Telekom AG.
+//
+// Authors: Andrei Pavel <andrei.pavel@qualitance.com>
+//          Cristian Secareanu <cristian.secareanu@qualitance.com>
+//          Razvan Becheriu <razvan.becheriu@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//           http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 #include <cassert>
 #include <config.h>
 #include <dhcpsrv/dhcpsrv_log.h>
index c04bf4383a8ec94c5adc353864ba6b63f60159c2..4ee93287fc1d7eb066649f93434c374d36a191f7 100644 (file)
@@ -1,3 +1,21 @@
+// Copyright (C) 2017-2019 Deutsche Telekom AG.
+//
+// Authors: Andrei Pavel <andrei.pavel@qualitance.com>
+//          Cristian Secareanu <cristian.secareanu@qualitance.com>
+//          Razvan Becheriu <razvan.becheriu@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//           http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 #ifndef THREAD_POOL_H
 #define THREAD_POOL_H
 
diff --git a/src/lib/dhcpsrv/thread_resource_mgr.h b/src/lib/dhcpsrv/thread_resource_mgr.h
new file mode 100644 (file)
index 0000000..78011ce
--- /dev/null
@@ -0,0 +1,42 @@
+// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef THREAD_RESOURCE_MGR_H
+#define THREAD_RESOURCE_MGR_H
+
+#include <util/threads/lock_guard.h>
+
+#include <boost/shared_ptr.hpp>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+
+namespace isc {
+namespace dhcp {
+
+template <typename Resource>
+class ThreadResourceMgr {
+    typedef boost::shared_ptr<Resource> ResourcePtr;
+public:
+    ResourcePtr resource() {
+        isc::util::thread::LockGuard<std::mutex> lock(&mutex_);
+        auto id = std::this_thread::get_id();
+        if (map_.find(id) != map_.end()) {
+            return map_[id];
+        }
+        ResourcePtr result(new Resource());
+        map_[id] = result;
+        return result;
+    }
+private:
+    std::mutex mutex_;
+    std::unordered_map<std::thread::id, ResourcePtr> map_;
+};
+
+}  // namespace dhcp
+}  // namespace isc
+
+#endif  // THREAD_RESOURCE_MGR_H
index 89ebf341d154b12d6f4151936e1276788373477c..874b43884d0c89063a8f546d78ec896f6f5546f6 100644 (file)
@@ -23,7 +23,44 @@ using namespace std;
 namespace isc {
 namespace db {
 
-bool MySqlHolder::atexit_ = false;
+bool MySqlHolder::atexit_ = []{atexit([]{mysql_library_end();});return true;};
+
+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 +74,10 @@ MySqlTransaction::~MySqlTransaction() {
     // Rollback if the MySqlTransaction::commit wasn't explicitly
     // called.
     if (!committed_) {
-        conn_.rollback();
+        try {
+            conn_.rollback();
+        } catch (...) {
+        }
     }
 }
 
@@ -126,10 +166,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 +200,18 @@ 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);
+    int result = mysql_options(handle(), MYSQL_OPT_RECONNECT, &auto_reconnect);
     if (result != 0) {
         isc_throw(DbOpenError, "unable to set auto-reconnect option: " <<
-                  mysql_error(mysql_));
+                  mysql_error(handle()));
     }
 
     // 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(handle(), MYSQL_INIT_COMMAND, wait_time);
     if (result != 0) {
         isc_throw(DbOpenError, "unable to set wait_timeout " <<
-                  mysql_error(mysql_));
+                  mysql_error(handle()));
     }
 
     // Set SQL mode options for the connection:  SQL mode governs how what
@@ -181,18 +219,18 @@ 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(handle(), MYSQL_INIT_COMMAND, sql_mode);
     if (result != 0) {
         isc_throw(DbOpenError, "unable to set SQL mode options: " <<
-                  mysql_error(mysql_));
+                  mysql_error(handle()));
     }
 
     // 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(handle(), MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
     if (result != 0) {
         isc_throw(DbOpenError, "unable to set database connection timeout: " <<
-                  mysql_error(mysql_));
+                  mysql_error(handle()));
     }
 
     // Open the database.
@@ -205,10 +243,10 @@ 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(handle(), host, user, password, name,
                                        port, NULL, CLIENT_FOUND_ROWS);
-    if (status != mysql_) {
-        isc_throw(DbOpenError, mysql_error(mysql_));
+    if (status != handle()) {
+        isc_throw(DbOpenError, mysql_error(handle()));
     }
 
     // Enable autocommit. In case transaction is explicitly used, this
@@ -218,10 +256,12 @@ MySqlConnection::openDatabase() {
     // 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(mysql_, 1);
+    my_bool auto_commit = mysql_autocommit(handle(), 1);
     if (auto_commit != MLM_FALSE) {
-        isc_throw(DbOperationError, mysql_error(mysql_));
+        isc_throw(DbOperationError, mysql_error(handle()));
     }
+    handle().connected_ = true;
+    connected_ = true;
 }
 
 
@@ -236,25 +276,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
@@ -263,28 +291,18 @@ 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);
     }
+    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();
 }
 
@@ -306,8 +324,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);
 }
 
@@ -322,31 +340,30 @@ 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");
+    int status = mysql_query(handle(), "START TRANSACTION");
     if (status != 0) {
         isc_throw(DbOperationError, "unable to start transaction, "
-                  "reason: " << mysql_error(mysql_));
+                  "reason: " << mysql_error(handle()));
     }
 }
 
 void
 MySqlConnection::commit() {
     DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, MYSQL_COMMIT);
-    if (mysql_commit(mysql_) != 0) {
+    if (mysql_commit(handle()) != 0) {
         isc_throw(DbOperationError, "commit failed: "
-                  << mysql_error(mysql_));
+                  << mysql_error(handle()));
     }
 }
 
 void
 MySqlConnection::rollback() {
     DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, MYSQL_ROLLBACK);
-    if (mysql_rollback(mysql_) != 0) {
+    if (mysql_rollback(handle()) != 0) {
         isc_throw(DbOperationError, "rollback failed: "
-                  << mysql_error(mysql_));
+                  << mysql_error(handle()));
     }
 }
 
-
-} // namespace isc::db
-} // namespace isc
+}  // namespace db
+}  // namespace isc
index f2d6c95d21a9f7e7b40aba05bf795b93e2d66a75..7ce62009d5cc06f61a4c938b311975e9a1be7131 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,18 +92,14 @@ 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;
-        }
+    MySqlHolder() : connected_(false), prepared_(false),
+            mysql_(mysql_init(NULL)) {
         if (mysql_ == NULL) {
             isc_throw(db::DbOpenError, "unable to initialize MySQL");
         }
@@ -110,6 +109,7 @@ public:
     ///
     /// Frees up resources allocated by the initialization of MySql.
     ~MySqlHolder() {
+        clearPrepared();
         if (mysql_ != NULL) {
             mysql_close(mysql_);
         }
@@ -124,15 +124,39 @@ public:
         return (mysql_);
     }
 
+    void 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 openDatabase(MySqlConnection& connection);
+
+    void prepareStatements(MySqlConnection& connection);
+
+    /// @brief Prepared statements
+    ///
+    /// This field is public, because it is used heavily from MySqlConnection
+    /// and will be 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 +179,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 +198,6 @@ public:
     void commit();
 
 private:
-
     /// @brief Holds reference to the MySQL database connection.
     MySqlConnection& conn_;
 
@@ -186,7 +208,6 @@ private:
     bool committed_;
 };
 
-
 /// @brief Common MySQL Connector Pool
 ///
 /// This class provides common operations for MySQL database connection
@@ -196,7 +217,6 @@ 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;
 
@@ -204,7 +224,7 @@ public:
     ///
     /// Initialize MySqlConnection object with parameters needed for connection.
     MySqlConnection(const ParameterMap& parameters)
-        : DatabaseConnection(parameters) {
+        : DatabaseConnection(parameters), connected_(false), prepared_(false) {
     }
 
     /// @brief Destructor
@@ -356,7 +376,7 @@ public:
         int status = 0;
         if (!in_bind_vec.empty()) {
             // Bind parameters to the prepared statement.
-            status = mysql_stmt_bind_param(statements_[index], &in_bind_vec[0]);
+            status = mysql_stmt_bind_param(handle().statements_[index], &in_bind_vec[0]);
             checkError(status, index, "unable to bind parameters for select");
         }
 
@@ -366,20 +386,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(handle().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(handle().statements_[index]);
         checkError(status, index, "unable to execute");
 
-        status = mysql_stmt_store_result(statements_[index]);
+        status = mysql_stmt_store_result(handle().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(handle().statements_[index]);
+        while ((status = mysql_stmt_fetch(handle().statements_[index])) ==
                MLM_MYSQL_FETCH_SUCCESS) {
             try {
                 // For each returned row call user function which should
@@ -429,15 +449,15 @@ public:
         }
 
         // Bind the parameters to the statement
-        int status = mysql_stmt_bind_param(statements_[index], &in_bind_vec[0]);
+        int status = mysql_stmt_bind_param(handle().statements_[index], &in_bind_vec[0]);
         checkError(status, index, "unable to bind parameters");
 
         // Execute the statement
-        status = mysql_stmt_execute(statements_[index]);
+        status = mysql_stmt_execute(handle().statements_[index]);
 
         if (status != 0) {
             // Failure: check for the special case of duplicate entry.
-            if (mysql_errno(mysql_) == ER_DUP_ENTRY) {
+            if (mysql_errno(handle()) == ER_DUP_ENTRY) {
                 isc_throw(DuplicateEntry, "Database duplicate entry error");
             }
             checkError(status, index, "unable to execute");
@@ -467,23 +487,23 @@ public:
         }
 
         // Bind the parameters to the statement
-        int status = mysql_stmt_bind_param(statements_[index], &in_bind_vec[0]);
+        int status = mysql_stmt_bind_param(handle().statements_[index], &in_bind_vec[0]);
         checkError(status, index, "unable to bind parameters");
 
         // Execute the statement
-        status = mysql_stmt_execute(statements_[index]);
+        status = mysql_stmt_execute(handle().statements_[index]);
 
         if (status != 0) {
             // Failure: check for the special case of duplicate entry.
-            if ((mysql_errno(mysql_) == ER_DUP_ENTRY)
+            if ((mysql_errno(handle()) == ER_DUP_ENTRY)
 #ifdef ER_FOREIGN_DUPLICATE_KEY
-                || (mysql_errno(mysql_) == ER_FOREIGN_DUPLICATE_KEY)
+                || (mysql_errno(handle()) == 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(handle()) == 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(handle()) == ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO)
 #endif
                 ) {
                 isc_throw(DuplicateEntry, "Database duplicate entry error");
@@ -492,7 +512,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(handle().statements_[index])));
     }
 
 
@@ -542,11 +562,11 @@ 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 {
         if (status != 0) {
-            switch(mysql_errno(mysql_)) {
+            switch(mysql_errno(handle())) {
                 // 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
@@ -560,13 +580,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(handle()))
+                    .arg(mysql_errno(handle()));
 
                 // 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
@@ -578,32 +598,42 @@ public:
                 isc_throw(db::DbOperationError, what << " for <"
                           << text_statements_[static_cast<int>(index)]
                           << ">, reason: "
-                          << mysql_error(mysql_) << " (error code "
-                          << mysql_errno(mysql_) << ")");
+                          << mysql_error(handle()) << " (error code "
+                          << mysql_errno(handle()) << ")");
             }
         }
     }
 
-    /// @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();
+        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 7895b471157f89def7881b23fe968d0099cf72ea..3b3f2daef051635172fbdbb6585b9ae345f4de23 100644 (file)
@@ -119,22 +119,22 @@ public:
     ///
     /// @param sql Query in the textual form.
     void runQuery(const std::string& sql) {
-        MYSQL_STMT *stmt = mysql_stmt_init(conn_.mysql_);
+        MYSQL_STMT *stmt = mysql_stmt_init(conn_.handle());
         if (stmt == NULL) {
             isc_throw(DbOperationError, "unable to allocate MySQL prepared "
-                  "statement structure, reason: " << mysql_error(conn_.mysql_));
+                  "statement structure, reason: " << mysql_error(conn_.handle()));
         }
 
         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(conn_.handle()));
         }
 
         // 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(conn_.handle()));
         }
 
         // Discard the statement and its resources
index af98f098cb1468af6300cdb1743326d979fd1dc6..b130233b05e3f9a8f4704d84444bd5a8a7ebbd83 100644 (file)
@@ -37,6 +37,50 @@ const int PGSQL_DEFAULT_CONNECTION_TIMEOUT = 5; // seconds
 
 const char PgSqlConnection::DUPLICATE_KEY[] = ERRCODE_UNIQUE_VIOLATION;
 
+PgSqlHolder::~PgSqlHolder() {
+    if (pgconn_ != NULL) {
+        // Deallocate the prepared queries.
+        if (PQstatus(pgconn_) == CONNECTION_OK) {
+            PgSqlResult r(PQexec(pgconn_, "DEALLOCATE all"));
+            if(PQresultStatus(r) != PGRES_COMMAND_OK) {
+                // Highly unlikely but we'll log it and go on.
+                DB_LOG_ERROR(PGSQL_DEALLOC_ERROR)
+                    .arg(PQerrorMessage(pgconn_));
+            }
+        }
+        PQfinish(pgconn_);
+    }
+}
+
+void
+PgSqlHolder::openDatabase(PgSqlConnection& connection) {
+    if (connected_) {
+        return;
+    }
+    connected_ = true;
+    prepared_ = true;
+    connection.openDatabase();
+    prepared_ = false;
+}
+
+void
+PgSqlHolder::prepareStatements(PgSqlConnection& connection) {
+    if (prepared_) {
+        return;
+    }
+    // Prepare all statements queries with all known fields datatype
+    for (auto it = connection.statements_.begin();
+         it != connection.statements_.end(); ++it) {
+        PgSqlResult r(PQprepare(pgconn_, (*it)->name, (*it)->text,
+                                (*it)->nbparams, (*it)->types));
+        if(PQresultStatus(r) != PGRES_COMMAND_OK) {
+            isc_throw(DbOperationError, "unable to prepare PostgreSQL statement: "
+                      << (*it)->text << ", reason: " << PQerrorMessage(pgconn_));
+        }
+    }
+    prepared_ = true;
+}
+
 PgSqlResult::PgSqlResult(PGresult *result)
     : result_(result), rows_(0), cols_(0) {
     if (!result) {
@@ -103,7 +147,10 @@ PgSqlTransaction::PgSqlTransaction(PgSqlConnection& conn)
 PgSqlTransaction::~PgSqlTransaction() {
     // If commit() wasn't explicitly called, rollback.
     if (!committed_) {
-        conn_.rollback();
+        try {
+            conn_.rollback();
+        } catch (...) {
+        }
     }
 }
 
@@ -114,28 +161,11 @@ PgSqlTransaction::commit() {
 }
 
 PgSqlConnection::~PgSqlConnection() {
-    if (conn_) {
-        // Deallocate the prepared queries.
-        if (PQstatus(conn_) == CONNECTION_OK) {
-            PgSqlResult r(PQexec(conn_, "DEALLOCATE all"));
-            if(PQresultStatus(r) != PGRES_COMMAND_OK) {
-                // Highly unlikely but we'll log it and go on.
-                DB_LOG_ERROR(PGSQL_DEALLOC_ERROR)
-                    .arg(PQerrorMessage(conn_));
-            }
-        }
-    }
 }
 
 void
 PgSqlConnection::prepareStatement(const PgSqlTaggedStatement& statement) {
-    // Prepare all statements queries with all known fields datatype
-    PgSqlResult r(PQprepare(conn_, statement.name, statement.text,
-                            statement.nbparams, statement.types));
-    if(PQresultStatus(r) != PGRES_COMMAND_OK) {
-        isc_throw(DbOperationError, "unable to prepare PostgreSQL statement: "
-                  << statement.text << ", reason: " << PQerrorMessage(conn_));
-    }
+    statements_.push_back(&statement);
 }
 
 void
@@ -146,6 +176,7 @@ PgSqlConnection::prepareStatements(const PgSqlTaggedStatement* start_statement,
          tagged_statement != end_statement; ++tagged_statement) {
         prepareStatement(*tagged_statement);
     }
+    prepared_ = true;
 }
 
 void
@@ -276,7 +307,9 @@ PgSqlConnection::openDatabase() {
     }
 
     // We have a valid connection, so let's save it to our holder
-    conn_.setConnection(new_conn);
+    handle().setConnection(new_conn);
+    handle().connected_ = true;
+    connected_ = true;
 }
 
 bool
@@ -305,7 +338,7 @@ PgSqlConnection::checkStatementError(const PgSqlResult& r,
              (memcmp(sqlstate, "58", 2) == 0))) { // System error
             DB_LOG_ERROR(PGSQL_FATAL_ERROR)
                 .arg(statement.name)
-                .arg(PQerrorMessage(conn_))
+                .arg(PQerrorMessage(handle()))
                 .arg(sqlstate ? sqlstate : "<sqlstate null>");
 
             // If there's no lost db callback or it returns false,
@@ -321,7 +354,7 @@ PgSqlConnection::checkStatementError(const PgSqlResult& r,
         }
 
         // Apparently it wasn't fatal, so we throw with a helpful message.
-        const char* error_message = PQerrorMessage(conn_);
+        const char* error_message = PQerrorMessage(handle());
         isc_throw(DbOperationError, "Statement exec failed:" << " for: "
                 << statement.name << ", status: " << s
                 << "sqlstate:[ " << (sqlstate ? sqlstate : "<null>")
@@ -332,9 +365,9 @@ PgSqlConnection::checkStatementError(const PgSqlResult& r,
 void
 PgSqlConnection::startTransaction() {
     DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, PGSQL_START_TRANSACTION);
-    PgSqlResult r(PQexec(conn_, "START TRANSACTION"));
+    PgSqlResult r(PQexec(handle(), "START TRANSACTION"));
     if (PQresultStatus(r) != PGRES_COMMAND_OK) {
-        const char* error_message = PQerrorMessage(conn_);
+        const char* error_message = PQerrorMessage(handle());
         isc_throw(DbOperationError, "unable to start transaction"
                   << error_message);
     }
@@ -343,9 +376,9 @@ PgSqlConnection::startTransaction() {
 void
 PgSqlConnection::commit() {
     DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, PGSQL_COMMIT);
-    PgSqlResult r(PQexec(conn_, "COMMIT"));
+    PgSqlResult r(PQexec(handle(), "COMMIT"));
     if (PQresultStatus(r) != PGRES_COMMAND_OK) {
-        const char* error_message = PQerrorMessage(conn_);
+        const char* error_message = PQerrorMessage(handle());
         isc_throw(DbOperationError, "commit failed: " << error_message);
     }
 }
@@ -353,9 +386,9 @@ PgSqlConnection::commit() {
 void
 PgSqlConnection::rollback() {
     DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, PGSQL_ROLLBACK);
-    PgSqlResult r(PQexec(conn_, "ROLLBACK"));
+    PgSqlResult r(PQexec(handle(), "ROLLBACK"));
     if (PQresultStatus(r) != PGRES_COMMAND_OK) {
-        const char* error_message = PQerrorMessage(conn_);
+        const char* error_message = PQerrorMessage(handle());
         isc_throw(DbOperationError, "rollback failed: " << error_message);
     }
 }
index 57e118f2e0c0861e4f0964a2b6da62cc2e23be5c..e9b33927c4d874cbf689b60d5492fbbbb48d8cf7 100644 (file)
@@ -7,6 +7,7 @@
 #define PGSQL_CONNECTION_H
 
 #include <database/database_connection.h>
+#include <dhcpsrv/thread_resource_mgr.h>
 
 #include <libpq-fe.h>
 #include <boost/scoped_ptr.hpp>
@@ -160,11 +161,13 @@ public:
     }
 
 private:
-    PGresult*     result_;     ///< Result set to be freed
-    int rows_;   ///< Number of rows in the result set
-    int cols_;   ///< Number of columns in the result set
+    PGresult* result_;     ///< Result set to be freed
+    int rows_;             ///< Number of rows in the result set
+    int cols_;             ///< Number of columns in the result set
 };
 
+/// @brief Forward declaration to @ref PgSqlConnection.
+class PgSqlConnection;
 
 /// @brief Postgresql connection handle Holder
 ///
@@ -179,22 +182,17 @@ private:
 /// For this reason, the class is declared noncopyable.
 class PgSqlHolder : public boost::noncopyable {
 public:
-
     /// @brief Constructor
     ///
     /// Sets the Postgresql API connector handle to NULL.
     ///
-    PgSqlHolder() : pgconn_(NULL) {
+    PgSqlHolder() : connected_(false), prepared_(false), pgconn_(NULL) {
     }
 
     /// @brief Destructor
     ///
     /// Frees up resources allocated by the connection.
-    ~PgSqlHolder() {
-        if (pgconn_ != NULL) {
-            PQfinish(pgconn_);
-        }
-    }
+    ~PgSqlHolder();
 
     /// @brief Sets the connection to the value given
     ///
@@ -209,6 +207,10 @@ public:
         pgconn_ = connection;
     }
 
+    void openDatabase(PgSqlConnection& connection);
+
+    void prepareStatements(PgSqlConnection& connection);
+
     /// @brief Conversion Operator
     ///
     /// Allows the PgSqlHolder object to be passed as the context argument to
@@ -217,19 +219,13 @@ public:
         return (pgconn_);
     }
 
-    /// @brief Boolean Operator
-    ///
-    /// Allows testing the connection for emptiness: "if (holder)"
-    operator bool() const {
-        return (pgconn_);
-    }
+    bool connected_;     ///< Flag to indicate openDatabase has been called
 
 private:
-    PGconn* pgconn_;      ///< Postgresql connection
-};
+    bool prepared_;      ///< Flag to indicate prepareStatements has been called
 
-/// @brief Forward declaration to @ref PgSqlConnection.
-class PgSqlConnection;
+    PGconn* pgconn_;     ///< Postgresql connection
+};
 
 /// @brief RAII object representing a PostgreSQL transaction.
 ///
@@ -305,7 +301,7 @@ public:
     ///
     /// Initialize PgSqlConnection object with parameters needed for connection.
     PgSqlConnection(const ParameterMap& parameters)
-        : DatabaseConnection(parameters) {
+        : DatabaseConnection(parameters), connected_(false), prepared_(false) {
     }
 
     /// @brief Destructor
@@ -400,27 +396,33 @@ public:
     void checkStatementError(const PgSqlResult& r,
                              PgSqlTaggedStatement& statement) const;
 
+    /// @brief Raw statements
+    ///
+    /// This field is public, because it is used heavily from PgSqlConnection
+    /// and will be from MySqlHostDataSource.
+    std::vector<const PgSqlTaggedStatement*> statements_;
+
     /// @brief PgSql connection handle
     ///
     /// This field is public, because it is used heavily from PgSqlLeaseMgr
     /// and from PgSqlHostDataSource.
-    PgSqlHolder conn_;
-
-    /// @brief Conversion Operator
-    ///
-    /// Allows the PgConnection object to be passed as the context argument to
-    /// PQxxxx functions.
-    operator PGconn*() const {
-        return (conn_);
+    PgSqlHolder& handle() const {
+        auto result = handles_.resource();
+        if (connected_) {
+            result->openDatabase(*(const_cast<PgSqlConnection*>(this)));
+        }
+        if (prepared_) {
+            result->prepareStatements(*(const_cast<PgSqlConnection*>(this)));
+        }
+        return *result;
     }
 
-    /// @brief Boolean Operator
-    ///
-    /// Allows testing the PgConnection for initialized connection
-    operator bool() const {
-        return (conn_);
-    }
+private:
+    bool connected_;     ///< Flag to indicate openDatabase has been called
+
+    bool prepared_;      ///< Flag to indicate prepareStatements has been called
 
+    mutable isc::dhcp::ThreadResourceMgr<PgSqlHolder> handles_;
 };
 
 }; // end of isc::db namespace
index 4aa82b22357491dbf0194dc39b5c998b5c45be34..fd9518a00fa2f2353137514c6386a4bd00d59cca 100644 (file)
@@ -70,7 +70,6 @@ struct PsqlBindArray {
     /// @return Returns true if there are no entries in the array, false
     /// otherwise.
     bool empty() const {
-
         return (values_.empty());
     }
 
@@ -393,7 +392,7 @@ public:
 protected:
     /// @brief Stores text labels for columns, currently only used for
     /// logging and errors.
-    std::vector<std::string>columns_;
+    std::vector<std::string> columns_;
 };
 
 }; // end of isc::db namespace
index 173665ac94c3e768c4914527b6cb811a8749bef2..cc44a785f2a656836f5d412fd0eb821efcaa27cd 100644 (file)
@@ -198,18 +198,18 @@ public:
             "    varchar_col VARCHAR(255) "
             "); ";
 
-        PgSqlResult r(PQexec(*conn_, sql));
+        PgSqlResult r(PQexec(conn_->handle(), sql));
         ASSERT_EQ(PQresultStatus(r), PGRES_COMMAND_OK)
-                 << " create basics table failed: " << PQerrorMessage(*conn_);
+                 << " create basics table failed: " << PQerrorMessage(conn_->handle());
     }
 
     /// @brief Destroys the basics table
     /// Asserts if the destruction fails
     void destroySchema() {
         if (conn_) {
-            PgSqlResult r(PQexec(*conn_, "DROP TABLE IF EXISTS basics;"));
+            PgSqlResult r(PQexec(conn_->handle(), "DROP TABLE IF EXISTS basics;"));
             ASSERT_EQ(PQresultStatus(r), PGRES_COMMAND_OK)
-                 << " drop basics table failed: " << PQerrorMessage(*conn_);
+                 << " drop basics table failed: " << PQerrorMessage(conn_->handle());
         }
     }
 
@@ -227,10 +227,10 @@ public:
     /// Asserts if the result set status does not equal the expected outcome.
     void runSql(PgSqlResultPtr& r, const std::string& sql, int exp_outcome,
                 int lineno) {
-        r.reset(new PgSqlResult(PQexec(*conn_, sql.c_str())));
+        r.reset(new PgSqlResult(PQexec(conn_->handle(), sql.c_str())));
         ASSERT_EQ(PQresultStatus(*r), exp_outcome)
                   << " runSql at line: " << lineno << " failed, sql:[" << sql
-                  << "]\n reason: " << PQerrorMessage(*conn_);
+                  << "]\n reason: " << PQerrorMessage(conn_->handle());
     }
 
     /// @brief Executes a SQL statement and tests for an expected outcome
@@ -250,7 +250,7 @@ public:
                               PgSqlTaggedStatement& statement,
                               PsqlBindArrayPtr bind_array, int exp_outcome,
                               int lineno) {
-        r.reset(new PgSqlResult(PQexecPrepared(*conn_, statement.name,
+        r.reset(new PgSqlResult(PQexecPrepared(conn_->handle(), statement.name,
                                 statement.nbparams,
                                 &bind_array->values_[0],
                                 &bind_array->lengths_[0],
@@ -258,7 +258,7 @@ public:
         ASSERT_EQ(PQresultStatus(*r), exp_outcome)
                   << " runPreparedStatement at line: " << lineno
                   << " statement name:[" << statement.name
-                  << "]\n reason: " << PQerrorMessage(*conn_);
+                  << "]\n reason: " << PQerrorMessage(conn_->handle());
     }
 
     /// @brief Fetches all of the rows currently in the table