]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/util/dbutil.py
Add util/dbutil.py: open/create/update sqlite databases given some schema.
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / util / dbutil.py
1
2 import os, sys
3
4 import sqlite3
5 from sqlite3 import IntegrityError
6 [IntegrityError]
7
8
9 class DBError(Exception):
10     pass
11
12
13 def get_db(dbfile, stderr=sys.stderr,
14            create_version=(None, None), updaters={}, just_create=False, dbname="db",
15            journal_mode=None, synchronous=None):
16     """Open or create the given db file. The parent directory must exist.
17     create_version=(SCHEMA, VERNUM), and SCHEMA must have a 'version' table.
18     Updaters is a {newver: commands} mapping, where e.g. updaters[2] is used
19     to get from ver=1 to ver=2. Returns a (sqlite3,db) tuple, or raises
20     DBError.
21     """
22     must_create = not os.path.exists(dbfile)
23     try:
24         db = sqlite3.connect(dbfile)
25     except (EnvironmentError, sqlite3.OperationalError), e:
26         raise DBError("Unable to create/open %s file %s: %s" % (dbname, dbfile, e))
27
28     schema, target_version = create_version
29     c = db.cursor()
30
31     # Enabling foreign keys allows stricter integrity checking.
32     # The default is unspecified according to <http://www.sqlite.org/foreignkeys.html#fk_enable>.
33     c.execute("PRAGMA foreign_keys = ON;")
34
35     if journal_mode is not None:
36         c.execute("PRAGMA journal_mode = %s;" % (journal_mode,))
37
38     if synchronous is not None:
39         c.execute("PRAGMA synchronous = %s;" % (synchronous,))
40
41     if must_create:
42         c.executescript(schema)
43         c.execute("INSERT INTO version (version) VALUES (?)", (target_version,))
44         db.commit()
45
46     try:
47         c.execute("SELECT version FROM version")
48         version = c.fetchone()[0]
49     except sqlite3.DatabaseError, e:
50         # this indicates that the file is not a compatible database format.
51         # Perhaps it was created with an old version, or it might be junk.
52         raise DBError("%s file is unusable: %s" % (dbname, e))
53
54     if just_create: # for tests
55         return (sqlite3, db)
56
57     while version < target_version and version+1 in updaters:
58         c.executescript(updaters[version+1])
59         db.commit()
60         version = version+1
61     if version != target_version:
62         raise DBError("Unable to handle %s version %s" % (dbname, version))
63
64     return (sqlite3, db)
65
66