[svn] commit: r2875 - in /branches/trac324/src/lib: datasrc/sqlite3_datasrc.cc python/isc/datasrc/sqlite3_ds.py

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Sep 8 22:06:23 UTC 2010


Author: each
Date: Wed Sep  8 22:06:23 2010
New Revision: 2875

Log:
Fixed a race condition where multiple processes could simultaneously try to
update the database schema from version 1 to 2.  (This should also
address a similar race condition that can occur when starting bind10 for
the first time and initializing the data source, see trac #326.)

Modified:
    branches/trac324/src/lib/datasrc/sqlite3_datasrc.cc
    branches/trac324/src/lib/python/isc/datasrc/sqlite3_ds.py

Modified: branches/trac324/src/lib/datasrc/sqlite3_datasrc.cc
==============================================================================
--- branches/trac324/src/lib/datasrc/sqlite3_datasrc.cc (original)
+++ branches/trac324/src/lib/datasrc/sqlite3_datasrc.cc Wed Sep  8 22:06:23 2010
@@ -56,6 +56,7 @@
 namespace {
 // SQL statements to create a database file. schema version 2.
 const char* const SCHEMA_LIST[] = {
+    "BEGIN EXCLUSIVE TRANSACTION",
     "CREATE TABLE schema_version (version INTEGER NOT NULL)",
     "INSERT INTO schema_version VALUES (2)",
     "CREATE TABLE zones (id INTEGER PRIMARY KEY, "
@@ -78,6 +79,7 @@
     "rdata STRING NOT NULL)",
     "CREATE INDEX nsec3_byhash ON nsec3 (zone_id, hash, rdtype, "
     "id, ttl, rdata)",
+    "COMMIT",
     NULL
 };
 
@@ -92,6 +94,7 @@
     "CREATE INDEX nsec3_byhash ON nsec3 (zone_id, hash, rdtype, "
     "id, ttl, rdata)",
     "UPDATE schema_version SET version = 2",
+    "COMMIT",
     NULL
 };
 
@@ -647,11 +650,33 @@
 checkVersion(Sqlite3Initializer* initializer) {
     sqlite3* const db = initializer->params_.db_;
     int version = initializer->params_.version_;
+    sqlite3_stmt* prepared = NULL;
+
     switch (version) {
         case 1:
+            // First we must confirm, within an exclusive transaction, that
+            // the database schema has not been changed under us by another
+            // process
+            sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", NULL, NULL, NULL);
+            if (sqlite3_prepare_v2(db, "SELECT version FROM schema_version", -1,
+                                   &prepared, NULL) == SQLITE_OK &&
+                sqlite3_step(prepared) == SQLITE_ROW)
+            {
+                int current = sqlite3_column_int(prepared, 0);
+                sqlite3_finalize(prepared);
+                if (current != version) {
+                    sqlite3_exec(db, "COMMIT", NULL, NULL, NULL);
+                    return;
+                }
+            } else {
+                sqlite3_exec(db, "COMMIT", NULL, NULL, NULL);
+                return;
+            }
+
             // XXX: replace this with a logging call
             cerr << "[sqlite3] Old database version detected, "
                  << "updating to schema version 2" << endl;
+
             for (int i = 0; V1_TO_V2[i] != NULL; ++i) {
                 if (sqlite3_exec(db, V1_TO_V2[i],
                                  NULL, NULL, NULL) != SQLITE_OK)
@@ -662,6 +687,7 @@
             }
             initializer->params_.version_ = 2;
             break;
+
         case 2:
             // Current version, no action necessary
             break;

Modified: branches/trac324/src/lib/python/isc/datasrc/sqlite3_ds.py
==============================================================================
--- branches/trac324/src/lib/python/isc/datasrc/sqlite3_ds.py (original)
+++ branches/trac324/src/lib/python/isc/datasrc/sqlite3_ds.py Wed Sep  8 22:06:23 2010
@@ -15,7 +15,7 @@
 
 # $Id$
 
-import sqlite3, re, random
+import sqlite3, re, random, sys
 import isc
 
 
@@ -36,6 +36,7 @@
 #########################################################################
 def create(cur):
     """Create new zone database"""
+    cur.execute("BEGIN EXCLUSIVE TRANSACTION")
     cur.execute("CREATE TABLE schema_version (version INTEGER NOT NULL)")
     cur.execute("INSERT INTO schema_version VALUES (2)")
     cur.execute("""CREATE TABLE zones (id INTEGER PRIMARY KEY, 
@@ -70,13 +71,21 @@
 #                to the current one
 # input: cur - database cursor
 #        version - the current version number
-# returns: the new version
-#########################################################################
-def check_version(cur, version):
+#########################################################################
+def check_version(conn, cur, version):
     if version == 1:
+        # Double-check that the version hasn't been changed under us
+        cur.execute("BEGIN EXCLUSIVE TRANSACTION")
+        cur.execute("SELECT version FROM schema_version")
+        row = cur.fetchone()
+        if not row or row[0] != version:
+            conn.commit()
+            return
+
         # XXX: replace this with a logging call
         print ("[sqlite3] Old database version detected, " +
                "updating to schema version 2", file=sys.stderr)
+
         cur.execute("DROP INDEX records_byname")
         cur.execute("""CREATE INDEX records_byname ON records (zone_id, name,
                        rdtype, sigtype, id, ttl, rdata)""")
@@ -87,6 +96,7 @@
         cur.execute("""CREATE INDEX nsec3_byhash ON nsec3 (zone_id, hash,
                        rdtype, id, ttl, rdata)""")
         cur.execute("UPDATE schema_version SET version = 2")
+        conn.commit()
     elif version != 2:
         raise Sqlite3DSError("Unknown database schema version " + version)
     
@@ -116,12 +126,13 @@
         create(cur)
         conn.commit()
         row = [1]
+        return conn, cur
 
     if row == None:
         raise Sqlite3DSError("Database schema version not found")
-    else:
-        check_version(cur, row[0]);
-
+
+    check_version(conn, cur, row[0]);
+    conn.commit()
     return conn, cur
 
 #########################################################################




More information about the bind10-changes mailing list