1 # $Id: back_sqlite.py,v 1.6 2002-09-27 01:04:38 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 import sqlite
9 class Database(Database):
10 # char to use for positional arguments
11 arg = '%s'
13 def open_connection(self):
14 # ensure files are group readable and writable
15 os.umask(0002)
16 db = os.path.join(self.config.DATABASE, 'db')
17 self.conn = sqlite.connect(db=db)
18 self.cursor = self.conn.cursor()
19 try:
20 self.database_schema = self.load_dbschema()
21 except sqlite.DatabaseError, error:
22 if str(error) != 'no such table: schema':
23 raise
24 self.database_schema = {}
25 self.cursor.execute('create table schema (schema varchar)')
26 self.cursor.execute('create table ids (name varchar, num integer)')
28 def close(self):
29 ''' Close off the connection.
31 Squash any error caused by us already having closed the
32 connection.
33 '''
34 try:
35 self.conn.close()
36 except sqlite.ProgrammingError, value:
37 if str(value) != 'close failed - Connection is closed.':
38 raise
41 def rollback(self):
42 ''' Reverse all actions from the current transaction.
44 Undo all the changes made since the database was opened or the
45 last commit() or rollback() was performed.
47 Squash any error caused by us having closed the connection (and
48 therefore not having anything to roll back)
49 '''
50 if __debug__:
51 print >>hyperdb.DEBUG, 'rollback', (self,)
53 # roll back
54 try:
55 self.conn.rollback()
56 except sqlite.ProgrammingError, value:
57 if str(value) != 'rollback failed - Connection is closed.':
58 raise
60 # roll back "other" transaction stuff
61 for method, args in self.transactions:
62 # delete temporary files
63 if method == self.doStoreFile:
64 self.rollbackStoreFile(*args)
65 self.transactions = []
67 def __repr__(self):
68 return '<roundlite 0x%x>'%id(self)
70 def sql_fetchone(self):
71 ''' Fetch a single row. If there's nothing to fetch, return None.
72 '''
73 return self.cursor.fetchone()
75 def sql_fetchall(self):
76 ''' Fetch a single row. If there's nothing to fetch, return [].
77 '''
78 return self.cursor.fetchall()
80 def sql_commit(self):
81 ''' Actually commit to the database.
83 Ignore errors if there's nothing to commit.
84 '''
85 try:
86 self.conn.commit()
87 except sqlite.DatabaseError, error:
88 if str(error) != 'cannot commit - no transaction is active':
89 raise
91 def save_dbschema(self, schema):
92 ''' Save the schema definition that the database currently implements
93 '''
94 s = repr(self.database_schema)
95 self.sql('insert into schema values (%s)', (s,))
97 def load_dbschema(self):
98 ''' Load the schema definition that the database currently implements
99 '''
100 self.cursor.execute('select schema from schema')
101 return eval(self.cursor.fetchone()[0])
103 def save_journal(self, classname, cols, nodeid, journaldate,
104 journaltag, action, params):
105 ''' Save the journal entry to the database
106 '''
107 # make the params db-friendly
108 params = repr(params)
109 entry = (nodeid, journaldate, journaltag, action, params)
111 # do the insert
112 a = self.arg
113 sql = 'insert into %s__journal (%s) values (%s,%s,%s,%s,%s)'%(classname,
114 cols, a, a, a, a, a)
115 if __debug__:
116 print >>hyperdb.DEBUG, 'addjournal', (self, sql, entry)
117 self.cursor.execute(sql, entry)
119 def load_journal(self, classname, cols, nodeid):
120 ''' Load the journal from the database
121 '''
122 # now get the journal entries
123 sql = 'select %s from %s__journal where nodeid=%s'%(cols, classname,
124 self.arg)
125 if __debug__:
126 print >>hyperdb.DEBUG, 'getjournal', (self, sql, nodeid)
127 self.cursor.execute(sql, (nodeid,))
128 res = []
129 for nodeid, date_stamp, user, action, params in self.cursor.fetchall():
130 params = eval(params)
131 res.append((nodeid, date.Date(date_stamp), user, action, params))
132 return res
134 def unserialise(self, classname, node):
135 ''' Decode the marshalled node data
137 SQLite stringifies _everything_... so we need to re-numberificate
138 Booleans and Numbers.
139 '''
140 if __debug__:
141 print >>hyperdb.DEBUG, 'unserialise', classname, node
142 properties = self.getclass(classname).getprops()
143 d = {}
144 for k, v in node.items():
145 # if the property doesn't exist, or is the "retired" flag then
146 # it won't be in the properties dict
147 if not properties.has_key(k):
148 d[k] = v
149 continue
151 # get the property spec
152 prop = properties[k]
154 if isinstance(prop, Date) and v is not None:
155 d[k] = date.Date(v)
156 elif isinstance(prop, Interval) and v is not None:
157 d[k] = date.Interval(v)
158 elif isinstance(prop, Password):
159 p = password.Password()
160 p.unpack(v)
161 d[k] = p
162 elif isinstance(prop, Boolean) and v is not None:
163 d[k] = int(v)
164 elif isinstance(prop, Number) and v is not None:
165 # try int first, then assume it's a float
166 try:
167 d[k] = int(v)
168 except ValueError:
169 d[k] = float(v)
170 else:
171 d[k] = v
172 return d