2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-04 07:55:18 +00:00

[3681_rebase] MySQLLeaseMgr uses, but does not derive from MySqlConnection.

This commit is contained in:
Tomek Mrugalski
2015-08-21 18:27:41 +02:00
parent 8a8b86056e
commit e626cf0d38
6 changed files with 92 additions and 68 deletions

View File

@@ -185,5 +185,21 @@ MySqlConnection::prepareStatements(const TaggedStatement tagged_statements[],
} }
} }
/// @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();
}
} // namespace isc::dhcp } // namespace isc::dhcp
} // namespace isc } // namespace isc

View File

@@ -136,10 +136,11 @@ private:
/// @brief Common MySQL Connector Pool /// @brief Common MySQL Connector Pool
/// ///
/// This class provides common operations for MySQL database connection /// This class provides common operations for MySQL database connection
/// used by both MySqlLeaseMgr and MySqlHostDataSource. It manages connecting /// used by both MySqlLeaseMgr and MySqlHostDataSource. It manages connecting
/// to the database and preparing compiled statements. /// to the database and preparing compiled statements. Its fields are
/// public, because they are used (both set and retrieved) in classes
/// that use instances of MySqlConnection.
class MySqlConnection : public DatabaseConnection { class MySqlConnection : public DatabaseConnection {
public: public:
@@ -151,8 +152,7 @@ public:
} }
/// @brief Destructor /// @brief Destructor
virtual ~MySqlConnection() { virtual ~MySqlConnection();
}
/// @brief Prepare Single Statement /// @brief Prepare Single Statement
/// ///
@@ -190,12 +190,22 @@ public:
/// @throw DbOpenError Error opening the database /// @throw DbOpenError Error opening the database
void openDatabase(); void openDatabase();
protected: /// @brief Prepared statements
///
/// This field is public, because it is used heavily from MySqlConnection
/// and will be from MySqlHostDataSource.
std::vector<MYSQL_STMT*> statements_;
std::vector<MYSQL_STMT*> statements_; ///< Prepared statements /// @brief Raw text of statements
std::vector<std::string> text_statements_; ///< Raw text of statements ///
/// This field is public, because it is used heavily from MySqlConnection
/// and will be from MySqlHostDataSource.
std::vector<std::string> text_statements_;
/// @brief MySQL connection handle /// @brief MySQL connection handle
///
/// This field is public, because it is used heavily from MySqlConnection
/// and will be from MySqlHostDataSource.
MySqlHolder mysql_; MySqlHolder mysql_;
}; };

View File

@@ -1151,23 +1151,23 @@ private:
// MySqlLeaseMgr Constructor and Destructor // MySqlLeaseMgr Constructor and Destructor
MySqlLeaseMgr::MySqlLeaseMgr(const MySqlConnection::ParameterMap& parameters) MySqlLeaseMgr::MySqlLeaseMgr(const MySqlConnection::ParameterMap& parameters)
: MySqlConnection(parameters) { : conn_(parameters) {
// Open the database. // Open the database.
openDatabase(); conn_.openDatabase();
// Enable autocommit. To avoid a flush to disk on every commit, the global // Enable autocommit. To avoid a flush to disk on every commit, the global
// parameter innodb_flush_log_at_trx_commit should be set to 2. This will // parameter innodb_flush_log_at_trx_commit should be set to 2. This will
// cause the changes to be written to the log, but flushed to disk in the // cause the changes to be written to the log, but flushed to disk in the
// background every second. Setting the parameter to that value will speed // background every second. Setting the parameter to that value will speed
// up the system, but at the risk of losing data if the system crashes. // up the system, but at the risk of losing data if the system crashes.
my_bool result = mysql_autocommit(mysql_, 1); my_bool result = mysql_autocommit(conn_.mysql_, 1);
if (result != 0) { if (result != 0) {
isc_throw(DbOperationError, mysql_error(mysql_)); isc_throw(DbOperationError, mysql_error(conn_.mysql_));
} }
// Prepare all statements likely to be used. // Prepare all statements likely to be used.
prepareStatements(tagged_statements, MySqlLeaseMgr::NUM_STATEMENTS); conn_.prepareStatements(tagged_statements, MySqlLeaseMgr::NUM_STATEMENTS);
// Create the exchange objects for use in exchanging data between the // Create the exchange objects for use in exchanging data between the
// program and the database. // program and the database.
@@ -1177,16 +1177,6 @@ MySqlLeaseMgr::MySqlLeaseMgr(const MySqlConnection::ParameterMap& parameters)
MySqlLeaseMgr::~MySqlLeaseMgr() { MySqlLeaseMgr::~MySqlLeaseMgr() {
// 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;
}
}
// There is no need to close the database in this destructor: it is // There is no need to close the database in this destructor: it is
// closed in the destructor of the mysql_ member variable. // closed in the destructor of the mysql_ member variable.
} }
@@ -1275,17 +1265,17 @@ MySqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
std::vector<MYSQL_BIND>& bind) { std::vector<MYSQL_BIND>& bind) {
// Bind the parameters to the statement // Bind the parameters to the statement
int status = mysql_stmt_bind_param(statements_[stindex], &bind[0]); int status = mysql_stmt_bind_param(conn_.statements_[stindex], &bind[0]);
checkError(status, stindex, "unable to bind parameters"); checkError(status, stindex, "unable to bind parameters");
// Execute the statement // Execute the statement
status = mysql_stmt_execute(statements_[stindex]); status = mysql_stmt_execute(conn_.statements_[stindex]);
if (status != 0) { if (status != 0) {
// Failure: check for the special case of duplicate entry. If this is // 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. // the case, we return false to indicate that the row was not added.
// Otherwise we throw an exception. // Otherwise we throw an exception.
if (mysql_errno(mysql_) == ER_DUP_ENTRY) { if (mysql_errno(conn_.mysql_) == ER_DUP_ENTRY) {
return (false); return (false);
} }
checkError(status, stindex, "unable to execute"); checkError(status, stindex, "unable to execute");
@@ -1353,43 +1343,43 @@ void MySqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
bool single) const { bool single) const {
// Bind the selection parameters to the statement // Bind the selection parameters to the statement
int status = mysql_stmt_bind_param(statements_[stindex], bind); int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
checkError(status, stindex, "unable to bind WHERE clause parameter"); checkError(status, stindex, "unable to bind WHERE clause parameter");
// Set up the MYSQL_BIND array for the data being returned and bind it to // Set up the MYSQL_BIND array for the data being returned and bind it to
// the statement. // the statement.
std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive(); std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
status = mysql_stmt_bind_result(statements_[stindex], &outbind[0]); status = mysql_stmt_bind_result(conn_.statements_[stindex], &outbind[0]);
checkError(status, stindex, "unable to bind SELECT clause parameters"); checkError(status, stindex, "unable to bind SELECT clause parameters");
// Execute the statement // Execute the statement
status = mysql_stmt_execute(statements_[stindex]); status = mysql_stmt_execute(conn_.statements_[stindex]);
checkError(status, stindex, "unable to execute"); checkError(status, stindex, "unable to execute");
// Ensure that all the lease information is retrieved in one go to avoid // Ensure that all the lease information is retrieved in one go to avoid
// overhead of going back and forth between client and server. // overhead of going back and forth between client and server.
status = mysql_stmt_store_result(statements_[stindex]); status = mysql_stmt_store_result(conn_.statements_[stindex]);
checkError(status, stindex, "unable to set up for storing all results"); checkError(status, stindex, "unable to set up for storing all results");
// Set up the fetch "release" object to release resources associated // Set up the fetch "release" object to release resources associated
// with the call to mysql_stmt_fetch when this method exits, then // with the call to mysql_stmt_fetch when this method exits, then
// retrieve the data. // retrieve the data.
MySqlFreeResult fetch_release(statements_[stindex]); MySqlFreeResult fetch_release(conn_.statements_[stindex]);
int count = 0; int count = 0;
while ((status = mysql_stmt_fetch(statements_[stindex])) == 0) { while ((status = mysql_stmt_fetch(conn_.statements_[stindex])) == 0) {
try { try {
result.push_back(exchange->getLeaseData()); result.push_back(exchange->getLeaseData());
} catch (const isc::BadValue& ex) { } catch (const isc::BadValue& ex) {
// Rethrow the exception with a bit more data. // Rethrow the exception with a bit more data.
isc_throw(BadValue, ex.what() << ". Statement is <" << isc_throw(BadValue, ex.what() << ". Statement is <" <<
text_statements_[stindex] << ">"); conn_.text_statements_[stindex] << ">");
} }
if (single && (++count > 1)) { if (single && (++count > 1)) {
isc_throw(MultipleRecords, "multiple records were found in the " isc_throw(MultipleRecords, "multiple records were found in the "
"database where only one was expected for query " "database where only one was expected for query "
<< text_statements_[stindex]); << conn_.text_statements_[stindex]);
} }
} }
@@ -1399,7 +1389,7 @@ void MySqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
checkError(status, stindex, "unable to fetch results"); checkError(status, stindex, "unable to fetch results");
} else if (status == MYSQL_DATA_TRUNCATED) { } else if (status == MYSQL_DATA_TRUNCATED) {
// Data truncated - throw an exception indicating what was at fault // Data truncated - throw an exception indicating what was at fault
isc_throw(DataTruncated, text_statements_[stindex] isc_throw(DataTruncated, conn_.text_statements_[stindex]
<< " returned truncated data: columns affected are " << " returned truncated data: columns affected are "
<< exchange->getErrorColumns()); << exchange->getErrorColumns());
} }
@@ -1732,16 +1722,16 @@ MySqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind,
const LeasePtr& lease) { const LeasePtr& lease) {
// Bind the parameters to the statement // Bind the parameters to the statement
int status = mysql_stmt_bind_param(statements_[stindex], bind); int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
checkError(status, stindex, "unable to bind parameters"); checkError(status, stindex, "unable to bind parameters");
// Execute // Execute
status = mysql_stmt_execute(statements_[stindex]); status = mysql_stmt_execute(conn_.statements_[stindex]);
checkError(status, stindex, "unable to execute"); checkError(status, stindex, "unable to execute");
// See how many rows were affected. The statement should only update a // See how many rows were affected. The statement should only update a
// single row. // single row.
int affected_rows = mysql_stmt_affected_rows(statements_[stindex]); int affected_rows = mysql_stmt_affected_rows(conn_.statements_[stindex]);
if (affected_rows == 0) { if (affected_rows == 0) {
isc_throw(NoSuchLease, "unable to update lease for address " << isc_throw(NoSuchLease, "unable to update lease for address " <<
lease->addr_ << " as it does not exist"); lease->addr_ << " as it does not exist");
@@ -1818,16 +1808,16 @@ bool
MySqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind) { MySqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind) {
// Bind the input parameters to the statement // Bind the input parameters to the statement
int status = mysql_stmt_bind_param(statements_[stindex], bind); int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
checkError(status, stindex, "unable to bind WHERE clause parameter"); checkError(status, stindex, "unable to bind WHERE clause parameter");
// Execute // Execute
status = mysql_stmt_execute(statements_[stindex]); status = mysql_stmt_execute(conn_.statements_[stindex]);
checkError(status, stindex, "unable to execute"); checkError(status, stindex, "unable to execute");
// See how many rows were affected. Note that the statement may delete // See how many rows were affected. Note that the statement may delete
// multiple rows. // multiple rows.
return (mysql_stmt_affected_rows(statements_[stindex]) > 0); return (mysql_stmt_affected_rows(conn_.statements_[stindex]) > 0);
} }
@@ -1870,7 +1860,7 @@ std::string
MySqlLeaseMgr::getName() const { MySqlLeaseMgr::getName() const {
std::string name = ""; std::string name = "";
try { try {
name = getParameter("name"); name = conn_.getParameter("name");
} catch (...) { } catch (...) {
// Return an empty name // Return an empty name
} }
@@ -1895,11 +1885,11 @@ MySqlLeaseMgr::getVersion() const {
uint32_t minor; // Minor version number uint32_t minor; // Minor version number
// Execute the prepared statement // Execute the prepared statement
int status = mysql_stmt_execute(statements_[stindex]); int status = mysql_stmt_execute(conn_.statements_[stindex]);
if (status != 0) { if (status != 0) {
isc_throw(DbOperationError, "unable to execute <" isc_throw(DbOperationError, "unable to execute <"
<< text_statements_[stindex] << "> - reason: " << << conn_.text_statements_[stindex] << "> - reason: " <<
mysql_error(mysql_)); mysql_error(conn_.mysql_));
} }
// Bind the output of the statement to the appropriate variables. // Bind the output of the statement to the appropriate variables.
@@ -1916,19 +1906,19 @@ MySqlLeaseMgr::getVersion() const {
bind[1].buffer = &minor; bind[1].buffer = &minor;
bind[1].buffer_length = sizeof(minor); bind[1].buffer_length = sizeof(minor);
status = mysql_stmt_bind_result(statements_[stindex], bind); status = mysql_stmt_bind_result(conn_.statements_[stindex], bind);
if (status != 0) { if (status != 0) {
isc_throw(DbOperationError, "unable to bind result set: " << isc_throw(DbOperationError, "unable to bind result set: " <<
mysql_error(mysql_)); mysql_error(conn_.mysql_));
} }
// Fetch the data and set up the "release" object to release associated // Fetch the data and set up the "release" object to release associated
// resources when this method exits then retrieve the data. // resources when this method exits then retrieve the data.
MySqlFreeResult fetch_release(statements_[stindex]); MySqlFreeResult fetch_release(conn_.statements_[stindex]);
status = mysql_stmt_fetch(statements_[stindex]); status = mysql_stmt_fetch(conn_.statements_[stindex]);
if (status != 0) { if (status != 0) {
isc_throw(DbOperationError, "unable to obtain result set: " << isc_throw(DbOperationError, "unable to obtain result set: " <<
mysql_error(mysql_)); mysql_error(conn_.mysql_));
} }
return (std::make_pair(major, minor)); return (std::make_pair(major, minor));
@@ -1938,8 +1928,8 @@ MySqlLeaseMgr::getVersion() const {
void void
MySqlLeaseMgr::commit() { MySqlLeaseMgr::commit() {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_COMMIT); LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_COMMIT);
if (mysql_commit(mysql_) != 0) { if (mysql_commit(conn_.mysql_) != 0) {
isc_throw(DbOperationError, "commit failed: " << mysql_error(mysql_)); isc_throw(DbOperationError, "commit failed: " << mysql_error(conn_.mysql_));
} }
} }
@@ -1947,8 +1937,8 @@ MySqlLeaseMgr::commit() {
void void
MySqlLeaseMgr::rollback() { MySqlLeaseMgr::rollback() {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_ROLLBACK); LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_ROLLBACK);
if (mysql_rollback(mysql_) != 0) { if (mysql_rollback(conn_.mysql_) != 0) {
isc_throw(DbOperationError, "rollback failed: " << mysql_error(mysql_)); isc_throw(DbOperationError, "rollback failed: " << mysql_error(conn_.mysql_));
} }
} }

View File

@@ -46,7 +46,7 @@ class MySqlLease6Exchange;
/// database. Use of this backend presupposes that a MySQL database is /// database. Use of this backend presupposes that a MySQL database is
/// available and that the Kea schema has been created within it. /// available and that the Kea schema has been created within it.
class MySqlLeaseMgr : public LeaseMgr, public MySqlConnection { class MySqlLeaseMgr : public LeaseMgr {
public: public:
/// @brief Constructor /// @brief Constructor
@@ -71,7 +71,7 @@ public:
/// @throw isc::dhcp::DbOpenError Error opening the database /// @throw isc::dhcp::DbOpenError Error opening the database
/// @throw isc::dhcp::DbOperationError An operation on the open database has /// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed. /// failed.
MySqlLeaseMgr(const ParameterMap& parameters); MySqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters);
/// @brief Destructor (closes database) /// @brief Destructor (closes database)
virtual ~MySqlLeaseMgr(); virtual ~MySqlLeaseMgr();
@@ -432,6 +432,8 @@ public:
}; };
private: private:
/// @brief MySQL connection
MySqlConnection conn_;
/// @brief Add Lease Common Code /// @brief Add Lease Common Code
/// ///
@@ -594,9 +596,9 @@ private:
const char* what) const { const char* what) const {
if (status != 0) { if (status != 0) {
isc_throw(DbOperationError, what << " for <" << isc_throw(DbOperationError, what << " for <" <<
text_statements_[index] << ">, reason: " << conn_.text_statements_[index] << ">, reason: " <<
mysql_error(mysql_) << " (error code " << mysql_error(conn_.mysql_) << " (error code " <<
mysql_errno(mysql_) << ")"); mysql_errno(conn_.mysql_) << ")");
} }
} }

View File

@@ -943,8 +943,8 @@ private:
}; };
PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters) PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
: LeaseMgr(), DatabaseConnection(parameters), exchange4_(new PgSqlLease4Exchange()), : LeaseMgr(), exchange4_(new PgSqlLease4Exchange()),
exchange6_(new PgSqlLease6Exchange()), conn_(NULL) { exchange6_(new PgSqlLease6Exchange()), dbconn_(parameters), conn_(NULL) {
openDatabase(); openDatabase();
prepareStatements(); prepareStatements();
} }
@@ -1000,7 +1000,7 @@ PgSqlLeaseMgr::openDatabase() {
string dbconnparameters; string dbconnparameters;
string shost = "localhost"; string shost = "localhost";
try { try {
shost = getParameter("host"); shost = dbconn_.getParameter("host");
} catch(...) { } catch(...) {
// No host. Fine, we'll use "localhost" // No host. Fine, we'll use "localhost"
} }
@@ -1009,7 +1009,7 @@ PgSqlLeaseMgr::openDatabase() {
string suser; string suser;
try { try {
suser = getParameter("user"); suser = dbconn_.getParameter("user");
dbconnparameters += " user = '" + suser + "'"; dbconnparameters += " user = '" + suser + "'";
} catch(...) { } catch(...) {
// No user. Fine, we'll use NULL // No user. Fine, we'll use NULL
@@ -1017,7 +1017,7 @@ PgSqlLeaseMgr::openDatabase() {
string spassword; string spassword;
try { try {
spassword = getParameter("password"); spassword = dbconn_.getParameter("password");
dbconnparameters += " password = '" + spassword + "'"; dbconnparameters += " password = '" + spassword + "'";
} catch(...) { } catch(...) {
// No password. Fine, we'll use NULL // No password. Fine, we'll use NULL
@@ -1025,7 +1025,7 @@ PgSqlLeaseMgr::openDatabase() {
string sname; string sname;
try { try {
sname= getParameter("name"); sname= dbconn_.getParameter("name");
dbconnparameters += " dbname = '" + sname + "'"; dbconnparameters += " dbname = '" + sname + "'";
} catch(...) { } catch(...) {
// No database name. Throw a "NoDatabaseName" exception // No database name. Throw a "NoDatabaseName" exception
@@ -1498,7 +1498,7 @@ string
PgSqlLeaseMgr::getName() const { PgSqlLeaseMgr::getName() const {
string name = ""; string name = "";
try { try {
name = getParameter("name"); name = dbconn_.getParameter("name");
} catch (...) { } catch (...) {
// Return an empty name // Return an empty name
} }

View File

@@ -121,7 +121,7 @@ const uint32_t PG_CURRENT_MINOR = 0;
/// This class provides the \ref isc::dhcp::LeaseMgr interface to the PostgreSQL /// This class provides the \ref isc::dhcp::LeaseMgr interface to the PostgreSQL
/// database. Use of this backend presupposes that a PostgreSQL database is /// database. Use of this backend presupposes that a PostgreSQL database is
/// available and that the Kea schema has been created within it. /// available and that the Kea schema has been created within it.
class PgSqlLeaseMgr : public LeaseMgr, DatabaseConnection { class PgSqlLeaseMgr : public LeaseMgr {
public: public:
/// @brief Constructor /// @brief Constructor
@@ -619,6 +619,12 @@ private:
boost::scoped_ptr<PgSqlLease4Exchange> exchange4_; ///< Exchange object boost::scoped_ptr<PgSqlLease4Exchange> exchange4_; ///< Exchange object
boost::scoped_ptr<PgSqlLease6Exchange> exchange6_; ///< Exchange object boost::scoped_ptr<PgSqlLease6Exchange> exchange6_; ///< Exchange object
/// Database connection object
///
/// @todo: Implement PgSQLConnection object and collapse
/// dbconn_ and conn_ into a single object.
DatabaseConnection dbconn_;
/// PostgreSQL connection handle /// PostgreSQL connection handle
PGconn* conn_; PGconn* conn_;
}; };