Commit a9b769b8 authored by Jelte Jansen's avatar Jelte Jansen
Browse files

[326] do table creation in an exclusive transaction (python)

parent 9622aed7
......@@ -33,31 +33,48 @@ def create(cur):
Arguments:
cur - sqlite3 cursor.
"""
cur.execute("CREATE TABLE schema_version (version INTEGER NOT NULL)")
cur.execute("INSERT INTO schema_version VALUES (1)")
cur.execute("""CREATE TABLE zones (id INTEGER PRIMARY KEY,
name STRING NOT NULL COLLATE NOCASE,
rdclass STRING NOT NULL COLLATE NOCASE DEFAULT 'IN',
dnssec BOOLEAN NOT NULL DEFAULT 0)""")
cur.execute("CREATE INDEX zones_byname ON zones (name)")
cur.execute("""CREATE TABLE records (id INTEGER PRIMARY KEY,
zone_id INTEGER NOT NULL,
name STRING NOT NULL COLLATE NOCASE,
rname STRING NOT NULL COLLATE NOCASE,
ttl INTEGER NOT NULL,
rdtype STRING NOT NULL COLLATE NOCASE,
sigtype STRING COLLATE NOCASE,
rdata STRING NOT NULL)""")
cur.execute("CREATE INDEX records_byname ON records (name)")
cur.execute("CREATE INDEX records_byrname ON records (rname)")
cur.execute("""CREATE TABLE nsec3 (id INTEGER PRIMARY KEY,
zone_id INTEGER NOT NULL,
hash STRING NOT NULL COLLATE NOCASE,
owner STRING NOT NULL COLLATE NOCASE,
ttl INTEGER NOT NULL,
rdtype STRING NOT NULL COLLATE NOCASE,
rdata STRING NOT NULL)""")
cur.execute("CREATE INDEX nsec3_byhash ON nsec3 (hash)")
# We are creating the database because it apparently had not been at
# the time we tried to read from it. However, another process may have
# had the same idea, resulting in a potential race condition.
# Therefore, we obtain an exclusive lock before we create anything
# When we have it, we check *again* whether the database has been
# initialized. If not, we do so.
# If the database is perpetually locked, it'll time out automatically
# and we just let it fail.
cur.execute("BEGIN EXCLUSIVE TRANSACTION")
try:
cur.execute("SELECT version FROM schema_version")
row = cur.fetchone()
except sqlite3.OperationalError:
cur.execute("CREATE TABLE schema_version (version INTEGER NOT NULL)")
cur.execute("INSERT INTO schema_version VALUES (1)")
cur.execute("""CREATE TABLE zones (id INTEGER PRIMARY KEY,
name STRING NOT NULL COLLATE NOCASE,
rdclass STRING NOT NULL COLLATE NOCASE DEFAULT 'IN',
dnssec BOOLEAN NOT NULL DEFAULT 0)""")
cur.execute("CREATE INDEX zones_byname ON zones (name)")
cur.execute("""CREATE TABLE records (id INTEGER PRIMARY KEY,
zone_id INTEGER NOT NULL,
name STRING NOT NULL COLLATE NOCASE,
rname STRING NOT NULL COLLATE NOCASE,
ttl INTEGER NOT NULL,
rdtype STRING NOT NULL COLLATE NOCASE,
sigtype STRING COLLATE NOCASE,
rdata STRING NOT NULL)""")
cur.execute("CREATE INDEX records_byname ON records (name)")
cur.execute("CREATE INDEX records_byrname ON records (rname)")
cur.execute("""CREATE TABLE nsec3 (id INTEGER PRIMARY KEY,
zone_id INTEGER NOT NULL,
hash STRING NOT NULL COLLATE NOCASE,
owner STRING NOT NULL COLLATE NOCASE,
ttl INTEGER NOT NULL,
rdtype STRING NOT NULL COLLATE NOCASE,
rdata STRING NOT NULL)""")
cur.execute("CREATE INDEX nsec3_byhash ON nsec3 (hash)")
row = [1]
cur.execute("COMMIT TRANSACTION")
return row
def open(dbfile):
""" Open a database, if the database is not yet set up, call create
......@@ -80,10 +97,13 @@ def open(dbfile):
try:
cur.execute("SELECT version FROM schema_version")
row = cur.fetchone()
except:
create(cur)
conn.commit()
row = [1]
except sqlite3.OperationalError:
# temporarily disable automatic transactions so
# we can do our own
iso_lvl = conn.isolation_level
conn.isolation_level = None
row = create(cur)
conn.isolation_level = iso_lvl
if row == None or row[0] != 1:
raise Sqlite3DSError("Bad database schema version")
......
......@@ -23,8 +23,9 @@ TESTDATA_PATH = os.environ['TESTDATA_PATH'] + os.sep
TESTDATA_WRITE_PATH = os.environ['TESTDATA_WRITE_PATH'] + os.sep
READ_ZONE_DB_FILE = TESTDATA_PATH + "example.com.sqlite3"
WRITE_ZONE_DB_FILE = TESTDATA_WRITE_PATH + "example.com.out.sqlite3"
BROKEN_DB_FILE = TESTDATA_PATH + "brokendb.sqlite3"
WRITE_ZONE_DB_FILE = TESTDATA_WRITE_PATH + "example.com.out.sqlite3"
NEW_DB_FILE = TESTDATA_WRITE_PATH + "new_db.sqlite3"
def example_reader():
my_zone = [
......@@ -91,5 +92,41 @@ class TestSqlite3_ds(unittest.TestCase):
# and make sure lock does not stay
sqlite3_ds.load(WRITE_ZONE_DB_FILE, ".", example_reader)
class NewDBFile(unittest.TestCase):
def tearDown(self):
# remove the created database after every test
if (os.path.exists(NEW_DB_FILE)):
os.remove(NEW_DB_FILE)
def setUp(self):
# remove the created database before every test too, just
# in case a test got aborted half-way, and cleanup didn't occur
if (os.path.exists(NEW_DB_FILE)):
os.remove(NEW_DB_FILE)
def test_new_db(self):
self.assertFalse(os.path.exists(NEW_DB_FILE))
sqlite3_ds.load(NEW_DB_FILE, ".", example_reader)
self.assertTrue(os.path.exists(NEW_DB_FILE))
def test_new_db_locked(self):
self.assertFalse(os.path.exists(NEW_DB_FILE))
con = sqlite3.connect(NEW_DB_FILE);
cur = con.cursor()
con.isolation_level = None
cur.execute("BEGIN EXCLUSIVE TRANSACTION")
# load should now fail, since the database is locked
self.assertRaises(sqlite3.OperationalError,
sqlite3_ds.load, NEW_DB_FILE, ".", example_reader)
con.rollback()
cur.close()
con.close()
self.assertTrue(os.path.exists(NEW_DB_FILE))
# now that we closed our connection, load should work again
sqlite3_ds.load(NEW_DB_FILE, ".", example_reader)
if __name__ == '__main__':
unittest.main()
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment