Code

6f1f3590178c00b9b00fde3fe46946dcb2790b03
[roundup.git] / roundup / backends / back_sqlite.py
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]