]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[4281] Creation of a new host in MySQL is wrapped in transaction.
authorMarcin Siodelski <marcin@isc.org>
Mon, 16 May 2016 14:44:09 +0000 (16:44 +0200)
committerMarcin Siodelski <marcin@isc.org>
Mon, 16 May 2016 14:44:09 +0000 (16:44 +0200)
src/lib/dhcpsrv/mysql_connection.cc
src/lib/dhcpsrv/mysql_connection.h
src/lib/dhcpsrv/mysql_host_data_source.cc
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.h
src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc

index 196ecce850d8a4f9c874a1f3f64dbe2a5de108dc..8096cfd7aacb9011ea4ae0b85ce8339d56f9e990 100755 (executable)
@@ -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
index ba0798cba2cbb170a674f9b866088c5d6223b2b6..866bf6472af543e0ec0656aa528b4369304fbf5d 100755 (executable)
@@ -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
 
index ada7c49647f500747bc7a559008c0c27e020bbc7..3bd484dd4516b763d8bc28b975a24b19e146790d 100644 (file)
@@ -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
index 94c3ff8ce7b333b87c4fbc05a9a61dd1a5288d7f..7c0d8c363e164dbda88b5e41d5c3451440549835 100644 (file)
@@ -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_);
index def3d9b60f0a85d7a0f22d52de3136bd89a776af..14a68e6071f612e8fbbf6beb18e942824ac164b2 100644 (file)
@@ -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.
     ///
index 3fae611c3ca8f8ec036a2ae225ed867b74e7711f..64801a6c6d5c9a583d76660b1e4943f47188a689 100644 (file)
@@ -442,4 +442,8 @@ TEST_F(MySqlHostDataSourceTest, formattedOptionsReservations46) {
     testOptionsReservations46(true);
 }
 
+TEST_F(MySqlHostDataSourceTest, testAddRollback) {
+    testAddRollback();
+}
+
 }; // Of anonymous namespace