From: Razvan Becheriu Date: Sun, 7 Apr 2019 09:24:15 +0000 (+0300) Subject: implemented automatic creation of thread connection to backend X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eae76c0633b01a84fd0c30fade7063354d699486;p=thirdparty%2Fkea.git implemented automatic creation of thread connection to backend --- diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc index bd3e2c5cb4..0c06e43720 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc @@ -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(id), // option_def_id diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc index 40ebbdcb48..c7c81c4007 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc @@ -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; } } } diff --git a/src/lib/dhcpsrv/mysql_host_data_source.cc b/src/lib/dhcpsrv/mysql_host_data_source.cc index d5e562972b..dc987749d2 100644 --- a/src/lib/dhcpsrv/mysql_host_data_source.cc +++ b/src/lib/dhcpsrv/mysql_host_data_source.cc @@ -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& 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 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")); } diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc index 8869dcd6e9..a11ebf62c6 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.cc +++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc @@ -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(&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(&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(&lease_->state_); bind_[15].is_unsigned = MLM_TRUE; @@ -1278,7 +1278,7 @@ public: bind_[14].buffer = reinterpret_cast(&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(&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(&lease_state_); + bind_[col].buffer = reinterpret_cast(&state_); bind_[col].is_unsigned = MLM_TRUE; ++col; @@ -1618,7 +1618,7 @@ public: if (status == MLM_MYSQL_FETCH_SUCCESS) { row.subnet_id_ = static_cast(subnet_id_); row.lease_type_ = static_cast(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& 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 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(&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(mysql_stmt_affected_rows(conn_.statements_[stindex]))); + return (static_cast(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())); } } diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.h b/src/lib/dhcpsrv/mysql_lease_mgr.h index e918e15512..9d976ad76e 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.h +++ b/src/lib/dhcpsrv/mysql_lease_mgr.h @@ -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 diff --git a/src/lib/dhcpsrv/pgsql_host_data_source.cc b/src/lib/dhcpsrv/pgsql_host_data_source.cc index 12db8f6267..363a009f1f 100644 --- a/src/lib/dhcpsrv/pgsql_host_data_source.cc +++ b/src/lib/dhcpsrv/pgsql_host_data_source.cc @@ -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 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 diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.cc b/src/lib/dhcpsrv/pgsql_lease_mgr.cc index 2fddbb7058..45c59627ff 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.cc +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.cc @@ -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 diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.h b/src/lib/dhcpsrv/pgsql_lease_mgr.h index f2d4a87b89..938577c07f 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.h +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.h @@ -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 diff --git a/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc index 5f92dc37ad..fc75ec707f 100644 --- a/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc @@ -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(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); diff --git a/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc index 8c49402b9f..01582ce422 100644 --- a/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc @@ -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", diff --git a/src/lib/dhcpsrv/thread_pool.cc b/src/lib/dhcpsrv/thread_pool.cc index 191cc330b4..2b666e3e3c 100644 --- a/src/lib/dhcpsrv/thread_pool.cc +++ b/src/lib/dhcpsrv/thread_pool.cc @@ -1,3 +1,21 @@ +// Copyright (C) 2017-2019 Deutsche Telekom AG. +// +// Authors: Andrei Pavel +// Cristian Secareanu +// Razvan Becheriu +// +// 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 #include #include diff --git a/src/lib/dhcpsrv/thread_pool.h b/src/lib/dhcpsrv/thread_pool.h index c04bf4383a..4ee93287fc 100644 --- a/src/lib/dhcpsrv/thread_pool.h +++ b/src/lib/dhcpsrv/thread_pool.h @@ -1,3 +1,21 @@ +// Copyright (C) 2017-2019 Deutsche Telekom AG. +// +// Authors: Andrei Pavel +// Cristian Secareanu +// Razvan Becheriu +// +// 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 index 0000000000..78011ce269 --- /dev/null +++ b/src/lib/dhcpsrv/thread_resource_mgr.h @@ -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 + +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + +template +class ThreadResourceMgr { + typedef boost::shared_ptr ResourcePtr; +public: + ResourcePtr resource() { + isc::util::thread::LockGuard 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 map_; +}; + +} // namespace dhcp +} // namespace isc + +#endif // THREAD_RESOURCE_MGR_H diff --git a/src/lib/mysql/mysql_connection.cc b/src/lib/mysql/mysql_connection.cc index 89ebf341d1..874b43884d 100644 --- a/src/lib/mysql/mysql_connection.cc +++ b/src/lib/mysql/mysql_connection.cc @@ -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(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(index) << ") or indexed prepared " << - "statement is not null"); + static_cast(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 diff --git a/src/lib/mysql/mysql_connection.h b/src/lib/mysql/mysql_connection.h index f2d6c95d21..7ce62009d5 100644 --- a/src/lib/mysql/mysql_connection.h +++ b/src/lib/mysql/mysql_connection.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -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 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 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(mysql_stmt_affected_rows(statements_[index]))); + return (static_cast(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 + template 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(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(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 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 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(this))); + } + if (prepared_) { + result->prepareStatements(*(const_cast(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 handles_; }; -}; // end of isc::db namespace -}; // end of isc namespace +} // namespace db +} // namespace isc -#endif // MYSQL_CONNECTION_H +#endif // MYSQL_CONNECTION_H diff --git a/src/lib/mysql/tests/mysql_connection_unittest.cc b/src/lib/mysql/tests/mysql_connection_unittest.cc index 7895b47115..3b3f2daef0 100644 --- a/src/lib/mysql/tests/mysql_connection_unittest.cc +++ b/src/lib/mysql/tests/mysql_connection_unittest.cc @@ -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 diff --git a/src/lib/pgsql/pgsql_connection.cc b/src/lib/pgsql/pgsql_connection.cc index af98f098cb..b130233b05 100644 --- a/src/lib/pgsql/pgsql_connection.cc +++ b/src/lib/pgsql/pgsql_connection.cc @@ -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 : ""); // 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 : "") @@ -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); } } diff --git a/src/lib/pgsql/pgsql_connection.h b/src/lib/pgsql/pgsql_connection.h index 57e118f2e0..e9b33927c4 100644 --- a/src/lib/pgsql/pgsql_connection.h +++ b/src/lib/pgsql/pgsql_connection.h @@ -7,6 +7,7 @@ #define PGSQL_CONNECTION_H #include +#include #include #include @@ -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 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(this))); + } + if (prepared_) { + result->prepareStatements(*(const_cast(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 handles_; }; }; // end of isc::db namespace diff --git a/src/lib/pgsql/pgsql_exchange.h b/src/lib/pgsql/pgsql_exchange.h index 4aa82b2235..fd9518a00f 100644 --- a/src/lib/pgsql/pgsql_exchange.h +++ b/src/lib/pgsql/pgsql_exchange.h @@ -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::vectorcolumns_; + std::vector columns_; }; }; // end of isc::db namespace diff --git a/src/lib/pgsql/tests/pgsql_exchange_unittest.cc b/src/lib/pgsql/tests/pgsql_exchange_unittest.cc index 173665ac94..cc44a785f2 100644 --- a/src/lib/pgsql/tests/pgsql_exchange_unittest.cc +++ b/src/lib/pgsql/tests/pgsql_exchange_unittest.cc @@ -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