8d2e57fab7eea4df499d15a544891a1503b66b64
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