6f1f3590178c00b9b00fde3fe46946dcb2790b03
1 # $Id: back_sqlite.py,v 1.2 2002-09-18 07:04:37 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 cursor = self.conn.cursor()
19 try:
20 self.database_schema = self.load_dbschema(cursor)
21 except sqlite.DatabaseError, error:
22 if str(error) != 'no such table: schema':
23 raise
24 self.database_schema = {}
25 cursor = self.conn.cursor()
26 cursor.execute('create table schema (schema varchar)')
27 cursor.execute('create table ids (name varchar, num integer)')
29 def __repr__(self):
30 return '<roundlite 0x%x>'%id(self)
32 def sql_fetchone(self, cursor):
33 ''' Fetch a single row. If there's nothing to fetch, return None.
34 '''
35 return cursor.fetchone()
37 def sql_commit(self):
38 ''' Actually commit to the database.
40 Ignore errors if there's nothing to commit.
41 '''
42 try:
43 self.conn.commit()
44 except sqlite.DatabaseError, error:
45 if str(error) != 'cannot commit - no transaction is active':
46 raise
48 def save_dbschema(self, cursor, schema):
49 ''' Save the schema definition that the database currently implements
50 '''
51 s = repr(self.database_schema)
52 self.sql(cursor, 'insert into schema values (%s)', (s,))
54 def load_dbschema(self, cursor):
55 ''' Load the schema definition that the database currently implements
56 '''
57 cursor.execute('select schema from schema')
58 return eval(cursor.fetchone()[0])
60 def save_journal(self, cursor, classname, cols, nodeid, journaldate,
61 journaltag, action, params):
62 ''' Save the journal entry to the database
63 '''
64 # make the params db-friendly
65 params = repr(params)
66 entry = (nodeid, journaldate, journaltag, action, params)
68 # do the insert
69 a = self.arg
70 sql = 'insert into %s__journal (%s) values (%s,%s,%s,%s,%s)'%(classname,
71 cols, a, a, a, a, a)
72 if __debug__:
73 print >>hyperdb.DEBUG, 'addjournal', (self, sql, entry)
74 cursor.execute(sql, entry)
76 def load_journal(self, cursor, classname, cols, nodeid):
77 ''' Load the journal from the database
78 '''
79 # now get the journal entries
80 sql = 'select %s from %s__journal where nodeid=%s'%(cols, classname,
81 self.arg)
82 if __debug__:
83 print >>hyperdb.DEBUG, 'getjournal', (self, sql, nodeid)
84 cursor.execute(sql, (nodeid,))
85 res = []
86 for nodeid, date_stamp, user, action, params in cursor.fetchall():
87 params = eval(params)
88 res.append((nodeid, date.Date(date_stamp), user, action, params))
89 return res
91 def unserialise(self, classname, node):
92 ''' Decode the marshalled node data
94 SQLite stringifies _everything_... so we need to re-numberificate
95 Booleans and Numbers.
96 '''
97 if __debug__:
98 print >>hyperdb.DEBUG, 'unserialise', classname, node
99 properties = self.getclass(classname).getprops()
100 d = {}
101 for k, v in node.items():
102 # if the property doesn't exist, or is the "retired" flag then
103 # it won't be in the properties dict
104 if not properties.has_key(k):
105 d[k] = v
106 continue
108 # get the property spec
109 prop = properties[k]
111 if isinstance(prop, Date) and v is not None:
112 d[k] = date.Date(v)
113 elif isinstance(prop, Interval) and v is not None:
114 d[k] = date.Interval(v)
115 elif isinstance(prop, Password):
116 p = password.Password()
117 p.unpack(v)
118 d[k] = p
119 elif isinstance(prop, Boolean) and v is not None:
120 d[k] = int(v)
121 elif isinstance(prop, Number) and v is not None:
122 # try int first, then assume it's a float
123 try:
124 d[k] = int(v)
125 except ValueError:
126 d[k] = float(v)
127 else:
128 d[k] = v
129 return d
131 class Class(Class):
132 _marker = []
133 def get(self, nodeid, propname, default=_marker, cache=1):
134 '''Get the value of a property on an existing node of this class.
136 'nodeid' must be the id of an existing node of this class or an
137 IndexError is raised. 'propname' must be the name of a property
138 of this class or a KeyError is raised.
140 'cache' indicates whether the transaction cache should be queried
141 for the node. If the node has been modified and you need to
142 determine what its values prior to modification are, you need to
143 set cache=0.
144 '''
145 if propname == 'id':
146 return nodeid
148 if propname == 'creation':
149 if not self.do_journal:
150 raise ValueError, 'Journalling is disabled for this class'
151 journal = self.db.getjournal(self.classname, nodeid)
152 if journal:
153 return self.db.getjournal(self.classname, nodeid)[0][1]
154 else:
155 # on the strange chance that there's no journal
156 return date.Date()
157 if propname == 'activity':
158 if not self.do_journal:
159 raise ValueError, 'Journalling is disabled for this class'
160 journal = self.db.getjournal(self.classname, nodeid)
161 if journal:
162 return self.db.getjournal(self.classname, nodeid)[-1][1]
163 else:
164 # on the strange chance that there's no journal
165 return date.Date()
166 if propname == 'creator':
167 if not self.do_journal:
168 raise ValueError, 'Journalling is disabled for this class'
169 journal = self.db.getjournal(self.classname, nodeid)
170 if journal:
171 name = self.db.getjournal(self.classname, nodeid)[0][2]
172 else:
173 return None
174 try:
175 return self.db.user.lookup(name)
176 except KeyError:
177 # the journaltag user doesn't exist any more
178 return None
180 # get the property (raises KeyErorr if invalid)
181 prop = self.properties[propname]
183 # get the node's dict
184 d = self.db.getnode(self.classname, nodeid) #, cache=cache)
186 if not d.has_key(propname):
187 if default is self._marker:
188 if isinstance(prop, Multilink):
189 return []
190 else:
191 return None
192 else:
193 return default
195 # special handling for some types
196 if isinstance(prop, Multilink):
197 # don't pass our list to other code
198 return d[propname][:]
199 elif d[propname] is None:
200 # always return None right now, no conversion
201 return None
202 elif isinstance(prop, Boolean) or isinstance(prop, Number):
203 # turn Booleans and Numbers into integers
204 return int(d[propname])
206 return d[propname]