Browse Source

[4281] Creation of a new host in MySQL is wrapped in transaction.

Marcin Siodelski 9 years ago
parent
commit
60192f6630

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

@@ -27,6 +27,29 @@ const my_bool MLM_TRUE = 1;
 const int MLM_MYSQL_FETCH_SUCCESS = 0;
 const int MLM_MYSQL_FETCH_FAILURE = 1;
 
+
+MySqlTransaction::MySqlTransaction(MySqlConnection& conn)
+    : conn_(conn), committed_(false) {
+    int status = mysql_query(conn_.mysql_, "START TRANSACTION");
+    if (status != 0) {
+        isc_throw(DbOperationError, "unable to start transaction, "
+                  "reason: " << mysql_error(conn_.mysql_));
+    }
+}
+
+MySqlTransaction::~MySqlTransaction() {
+    if (!committed_) {
+        conn_.rollback();
+    }
+}
+
+void
+MySqlTransaction::commit() {
+    conn_.commit();
+    committed_ = true;
+}
+
+
 // Open the database using the parameters passed to the constructor.
 
 void

+ 20 - 2
src/lib/dhcpsrv/mysql_connection.h

@@ -140,6 +140,26 @@ private:
     MYSQL* mysql_;      ///< Initialization context
 };
 
+class MySqlConnection;
+
+class MySqlTransaction : public boost::noncopyable {
+public:
+
+    MySqlTransaction(MySqlConnection& conn);
+
+    ~MySqlTransaction();
+
+    void commit();
+
+private:
+
+    MySqlConnection& conn_;
+
+    bool committed_;
+
+};
+
+
 /// @brief Common MySQL Connector Pool
 ///
 /// This class provides common operations for MySQL database connection
@@ -294,8 +314,6 @@ public:
     MySqlHolder mysql_;
 };
 
-
-
 }; // end of isc::dhcp namespace
 }; // end of isc namespace
 

+ 4 - 0
src/lib/dhcpsrv/mysql_host_data_source.cc

@@ -1982,6 +1982,8 @@ MySqlHostDataSource::~MySqlHostDataSource() {
 
 void
 MySqlHostDataSource::add(const HostPtr& host) {
+    MySqlTransaction transaction(impl_->conn_);
+
     // Create the MYSQL_BIND array for the host
     std::vector<MYSQL_BIND> bind = impl_->host_exchange_->createBindForSend(host);
 
@@ -2009,6 +2011,8 @@ MySqlHostDataSource::add(const HostPtr& host) {
     if (cfg_option6) {
         impl_->addOptions(INSERT_V6_OPTION, cfg_option6, host_id);
     }
+
+    transaction.commit();
 }
 
 ConstHostCollection

+ 32 - 0
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc

@@ -12,6 +12,7 @@
 #include <dhcp/option_string.h>
 #include <dhcp/option_int.h>
 #include <dhcp/option_vendor.h>
+#include <dhcpsrv/mysql_connection.h>
 #include <dhcpsrv/tests/generic_host_data_source_unittest.h>
 #include <dhcpsrv/tests/test_utils.h>
 #include <dhcpsrv/database_connection.h>
@@ -967,6 +968,37 @@ void GenericHostDataSourceTest::testAddDuplicate4() {
     EXPECT_NO_THROW(hdsptr_->add(host));
 }
 
+void GenericHostDataSourceTest::testAddRollback() {
+    // Make sure we have the pointer to the host data source.
+    ASSERT_TRUE(hdsptr_);
+
+    MySqlConnection::ParameterMap params;
+    params["name"] = "keatest";
+    params["user"] = "keatest";
+    params["password"] = "keatest";
+    MySqlConnection conn(params);
+    ASSERT_NO_THROW(conn.openDatabase());
+    int status = mysql_query(conn.mysql_, "DROP TABLE IF EXISTS ipv6_reservations");
+    ASSERT_EQ(0, status) << mysql_error(conn.mysql_);
+
+    // Create a host reservations.
+    HostPtr host = initializeHost6("2001:db8:1::1", Host::IDENT_HWADDR, false);
+
+    host->setIPv4SubnetID(SubnetID(4));
+
+    // Create IPv6 reservation (for an address) and add it to the host
+    IPv6Resrv resv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::2"), 128);
+    host->addReservation(resv);
+
+    ASSERT_THROW(hdsptr_->add(host), DbOperationError);
+
+    ConstHostPtr from_hds = hdsptr_->get4(host->getIPv4SubnetID(),
+                                          host->getIdentifierType(),
+                                          &host->getIdentifier()[0],
+                                          host->getIdentifier().size());
+    ASSERT_FALSE(from_hds);
+}
+
 void GenericHostDataSourceTest::testAddr6AndPrefix(){
     // Make sure we have the pointer to the host data source.
     ASSERT_TRUE(hdsptr_);

+ 2 - 0
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.h

@@ -342,6 +342,8 @@ public:
     /// Uses gtest macros to report failures.
     void testAddDuplicate4();
 
+    void testAddRollback();
+
     /// @brief Test that DHCPv4 options can be inserted and retrieved from
     /// the database.
     ///

+ 4 - 0
src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc

@@ -442,4 +442,8 @@ TEST_F(MySqlHostDataSourceTest, formattedOptionsReservations46) {
     testOptionsReservations46(true);
 }
 
+TEST_F(MySqlHostDataSourceTest, testAddRollback) {
+    testAddRollback();
+}
+
 }; // Of anonymous namespace