Browse Source

[1068] make sure startUpdateZone() can be called multiple times if the
first attempt results in an exception.

JINMEI Tatuya 13 years ago
parent
commit
c5e0ebf85e

+ 23 - 13
src/lib/datasrc/sqlite3_accessor.cc

@@ -454,27 +454,37 @@ SQLite3Accessor::startUpdateZone(const string& zone_name, const bool replace) {
         return (zone_info);
     }
 
-    dbparameters_->updating_zone = true;
-    dbparameters_->updated_zone_id = zone_info.second;
-
     StatementExecuter(*dbparameters_, BEGIN,
                       "start an SQLite3 transaction").exec();
 
     if (replace) {
-        StatementExecuter delzone_exec(*dbparameters_, DEL_ZONE_RECORDS,
-                                       "delete zone records");
+        try {
+            StatementExecuter delzone_exec(*dbparameters_, DEL_ZONE_RECORDS,
+                                           "delete zone records");
+
+            sqlite3_clear_bindings(
+                dbparameters_->statements_[DEL_ZONE_RECORDS]);
+            if (sqlite3_bind_int(dbparameters_->statements_[DEL_ZONE_RECORDS],
+                                 1, zone_info.second) != SQLITE_OK) {
+                isc_throw(DataSourceError,
+                          "failed to bind SQLite3 parameter: " <<
+                          sqlite3_errmsg(dbparameters_->db_));
+            }
 
-        sqlite3_clear_bindings(dbparameters_->statements_[DEL_ZONE_RECORDS]);
-        if (sqlite3_bind_int(dbparameters_->statements_[DEL_ZONE_RECORDS],
-                             1, zone_info.second) != SQLITE_OK) {
-            isc_throw(DataSourceError,
-                      "failed to bind SQLite3 parameter: " <<
-                      sqlite3_errmsg(dbparameters_->db_));
+            delzone_exec.exec();
+        } catch (const DataSourceError&) {
+            // Once we start a transaction, if something unexpected happens
+            // we need to rollback the transaction so that a subsequent update
+            // is still possible with this accessor.
+            StatementExecuter(*dbparameters_, ROLLBACK,
+                      "rollback an SQLite3 transaction").exec();
+            throw;
         }
-
-        delzone_exec.exec();
     }
 
+    dbparameters_->updating_zone = true;
+    dbparameters_->updated_zone_id = zone_info.second;
+
     return (zone_info);
 }
 

+ 6 - 0
src/lib/datasrc/tests/sqlite3_accessor_unittest.cc

@@ -423,6 +423,12 @@ TEST_F(SQLite3Update, updateConflict) {
     EXPECT_THROW(accessor->startUpdateZone("example.com.", true),
                  DataSourceError);
     checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored);
+
+    // Once we rollback the other attempt of change, we should be able to
+    // start and commit the transaction using the main accessor.
+    another_accessor->rollbackUpdateZone();
+    accessor->startUpdateZone("example.com.", true);
+    accessor->commitUpdateZone();
 }
 
 TEST_F(SQLite3Update, duplicateUpdate) {