Code

8d2e57fab7eea4df499d15a544891a1503b66b64
[roundup.git] / roundup / backends / back_sqlite.py
1 # $Id: back_sqlite.py,v 1.9 2003-03-06 06:03:51 richard Exp $
2 __doc__ = '''
3 See https://pysqlite.sourceforge.net/ for pysqlite info
4 '''
5 import base64, marshal
6 from roundup.backends.rdbms_common import *
7 from roundup.backends import locking
8 import sqlite
10 class Database(Database):
11     # char to use for positional arguments
12     arg = '%s'
14     def open_connection(self):
15         # ensure files are group readable and writable
16         os.umask(0002)
17         db = os.path.join(self.config.DATABASE, 'db')
19         # lock it
20         lockfilenm = db[:-3] + 'lck'
21         self.lockfile = locking.acquire_lock(lockfilenm)
22         self.lockfile.write(str(os.getpid()))
23         self.lockfile.flush()
25         self.conn = sqlite.connect(db=db)
26         self.cursor = self.conn.cursor()
27         try:
28             self.database_schema = self.load_dbschema()
29         except sqlite.DatabaseError, error:
30             if str(error) != 'no such table: schema':
31                 raise
32             self.database_schema = {}
33             self.cursor.execute('create table schema (schema varchar)')
34             self.cursor.execute('create table ids (name varchar, num integer)')
36     def close(self):
37         ''' Close off the connection.
39             Squash any error caused by us already having closed the
40             connection.
41         '''
42         try:
43             self.conn.close()
44         except sqlite.ProgrammingError, value:
45             if str(value) != 'close failed - Connection is closed.':
46                 raise
48         # release the lock too
49         if self.lockfile is not None:
50             locking.release_lock(self.lockfile)
51         if self.lockfile is not None:
52             self.lockfile.close()
53             self.lockfile = None
55     def rollback(self):
56         ''' Reverse all actions from the current transaction.
58             Undo all the changes made since the database was opened or the
59             last commit() or rollback() was performed.
61             Squash any error caused by us having closed the connection (and
62             therefore not having anything to roll back)
63         '''
64         if __debug__:
65             print >>hyperdb.DEBUG, 'rollback', (self,)
67         # roll back
68         try:
69             self.conn.rollback()
70         except sqlite.ProgrammingError, value:
71             if str(value) != 'rollback failed - Connection is closed.':
72                 raise
74         # roll back "other" transaction stuff
75         for method, args in self.transactions:
76             # delete temporary files
77             if method == self.doStoreFile:
78                 self.rollbackStoreFile(*args)
79         self.transactions = []
81         # clear the cache
82         self.clearCache()
84     def __repr__(self):
85         return '<roundlite 0x%x>'%id(self)
87     def sql_fetchone(self):
88         ''' Fetch a single row. If there's nothing to fetch, return None.
89         '''
90         return self.cursor.fetchone()
92     def sql_fetchall(self):
93         ''' Fetch a single row. If there's nothing to fetch, return [].
94         '''
95         return self.cursor.fetchall()
97     def sql_commit(self):
98         ''' Actually commit to the database.
100             Ignore errors if there's nothing to commit.
101         '''
102         try:
103             self.conn.commit()
104         except sqlite.DatabaseError, error:
105             if str(error) != 'cannot commit - no transaction is active':
106                 raise
108     def save_dbschema(self, schema):
109         ''' Save the schema definition that the database currently implements
110         '''
111         s = repr(self.database_schema)
112         self.sql('insert into schema values (%s)', (s,))
114     def load_dbschema(self):
115         ''' Load the schema definition that the database currently implements
116         '''
117         self.cursor.execute('select schema from schema')
118         return eval(self.cursor.fetchone()[0])
120     def save_journal(self, classname, cols, nodeid, journaldate,
121             journaltag, action, params):
122         ''' Save the journal entry to the database
123         '''
124         # make the params db-friendly
125         params = repr(params)
126         entry = (nodeid, journaldate, journaltag, action, params)
128         # do the insert
129         a = self.arg
130         sql = 'insert into %s__journal (%s) values (%s,%s,%s,%s,%s)'%(classname,
131             cols, a, a, a, a, a)
132         if __debug__:
133             print >>hyperdb.DEBUG, 'addjournal', (self, sql, entry)
134         self.cursor.execute(sql, entry)
136     def load_journal(self, classname, cols, nodeid):
137         ''' Load the journal from the database
138         '''
139         # now get the journal entries
140         sql = 'select %s from %s__journal where nodeid=%s'%(cols, classname,
141             self.arg)
142         if __debug__:
143             print >>hyperdb.DEBUG, 'getjournal', (self, sql, nodeid)
144         self.cursor.execute(sql, (nodeid,))
145         res = []
146         for nodeid, date_stamp, user, action, params in self.cursor.fetchall():
147             params = eval(params)
148             res.append((nodeid, date.Date(date_stamp), user, action, params))
149         return res
151     def unserialise(self, classname, node):
152         ''' Decode the marshalled node data
154             SQLite stringifies _everything_... so we need to re-numberificate
155             Booleans and Numbers.
156         '''
157         if __debug__:
158             print >>hyperdb.DEBUG, 'unserialise', classname, node
159         properties = self.getclass(classname).getprops()
160         d = {}
161         for k, v in node.items():
162             # if the property doesn't exist, or is the "retired" flag then
163             # it won't be in the properties dict
164             if not properties.has_key(k):
165                 d[k] = v
166                 continue
168             # get the property spec
169             prop = properties[k]
171             if isinstance(prop, Date) and v is not None:
172                 d[k] = date.Date(v)
173             elif isinstance(prop, Interval) and v is not None:
174                 d[k] = date.Interval(v)
175             elif isinstance(prop, Password) and v is not None:
176                 p = password.Password()
177                 p.unpack(v)
178                 d[k] = p
179             elif isinstance(prop, Boolean) and v is not None:
180                 d[k] = int(v)
181             elif isinstance(prop, Number) and v is not None:
182                 # try int first, then assume it's a float
183                 try:
184                     d[k] = int(v)
185                 except ValueError:
186                     d[k] = float(v)
187             else:
188                 d[k] = v
189         return d