88"""
99__docformat__ = 'restructuredtext'
1010
11- import os , base64 , marshal , shutil , time , logging
11+ import os , marshal , shutil , time , logging
1212
1313from roundup import hyperdb , date , password
1414from roundup .backends import rdbms_common
2222except ImportError :
2323 try :
2424 from pysqlite2 import dbapi2 as sqlite
25- if sqlite .version_info < (2 ,1 , 0 ):
25+ if sqlite .version_info < (2 , 1 , 0 ):
2626 raise ValueError ('pysqlite2 minimum version is 2.1.0+ '
27- '- %s found' % sqlite .version )
27+ '- %s found' % sqlite .version )
2828 sqlite_version = 2
2929 except ImportError :
3030 import sqlite
3131 sqlite_version = 1
3232
33+
3334def db_exists (config ):
3435 return os .path .exists (os .path .join (config .DATABASE , 'db' ))
3536
37+
3638def db_nuke (config ):
3739 shutil .rmtree (config .DATABASE )
3840
41+
3942class Database (rdbms_common .Database ):
4043 """Sqlite DB backend implementation
4144
@@ -47,7 +50,7 @@ class Database(rdbms_common.Database):
4750 """
4851
4952 # char to use for positional arguments
50- if sqlite_version in (2 ,3 ):
53+ if sqlite_version in (2 , 3 ):
5154 arg = '?'
5255 else :
5356 arg = '%s'
@@ -65,19 +68,19 @@ class Database(rdbms_common.Database):
6568 implements_double_precision = False
6669
6770 hyperdb_to_sql_datatypes = {
68- hyperdb .String : 'VARCHAR(255)' ,
69- hyperdb .Date : 'VARCHAR(30)' ,
70- hyperdb .Link : 'INTEGER' ,
71+ hyperdb .String : 'VARCHAR(255)' ,
72+ hyperdb .Date : 'VARCHAR(30)' ,
73+ hyperdb .Link : 'INTEGER' ,
7174 hyperdb .Interval : 'VARCHAR(255)' ,
7275 hyperdb .Password : 'VARCHAR(255)' ,
7376 hyperdb .Boolean : 'BOOLEAN' ,
7477 hyperdb .Number : 'REAL' ,
7578 hyperdb .Integer : 'INTEGER' ,
7679 }
7780 hyperdb_to_sql_value = {
78- hyperdb .String : str ,
79- hyperdb .Date : lambda x : x .serialise (),
80- hyperdb .Link : int ,
81+ hyperdb .String : str ,
82+ hyperdb .Date : lambda x : x .serialise (),
83+ hyperdb .Link : int ,
8184 hyperdb .Interval : str ,
8285 hyperdb .Password : str ,
8386 hyperdb .Boolean : int ,
@@ -86,9 +89,9 @@ class Database(rdbms_common.Database):
8689 hyperdb .Multilink : lambda x : x , # used in journal marshalling
8790 }
8891 sql_to_hyperdb_value = {
89- hyperdb .String : uany2s ,
90- hyperdb .Date : lambda x : date .Date (str (x )),
91- hyperdb .Link : str , # XXX numeric ids
92+ hyperdb .String : uany2s ,
93+ hyperdb .Date : lambda x : date .Date (str (x )),
94+ hyperdb .Link : str , # XXX numeric ids
9295 hyperdb .Interval : date .Interval ,
9396 hyperdb .Password : lambda x : password .Password (encrypted = x ),
9497 hyperdb .Boolean : int ,
@@ -136,7 +139,7 @@ def sql_open_connection(self):
136139 os .makedirs (self .config .DATABASE )
137140
138141 db = os .path .join (self .config .DATABASE , 'db' )
139- logging .getLogger ('roundup.hyperdb' ).info ('open database %r' % db )
142+ logging .getLogger ('roundup.hyperdb' ).info ('open database %r' % db )
140143 # set timeout (30 second default is extraordinarily generous)
141144 # for handling locked database
142145 if sqlite_version == 1 :
@@ -174,23 +177,25 @@ def open_connection(self):
174177
175178 def create_version_2_tables (self ):
176179 self .sql ('create table otks (otk_key varchar, '
177- 'otk_value varchar, otk_time integer)' )
180+ 'otk_value varchar, otk_time integer)' )
178181 self .sql ('create index otks_key_idx on otks(otk_key)' )
179182 self .sql ('create table sessions (session_key varchar, '
180- 'session_time integer, session_value varchar)' )
183+ 'session_time integer, session_value varchar)' )
181184 self .sql ('create index sessions_key_idx on '
182- 'sessions(session_key)' )
185+ 'sessions(session_key)' )
183186
184187 # full-text indexing store
185188 self .sql ('CREATE TABLE __textids (_class varchar, '
186- '_itemid varchar, _prop varchar, _textid integer primary key) ' )
189+ '_itemid varchar, _prop varchar, _textid'
190+ ' integer primary key) ' )
187191 self .sql ('CREATE TABLE __words (_word varchar, '
188- '_textid integer)' )
192+ '_textid integer)' )
189193 self .sql ('CREATE INDEX words_word_ids ON __words(_word)' )
190194 self .sql ('CREATE INDEX words_by_id ON __words (_textid)' )
191195 self .sql ('CREATE UNIQUE INDEX __textids_by_props ON '
192196 '__textids (_class, _itemid, _prop)' )
193- sql = 'insert into ids (name, num) values (%s,%s)' % (self .arg , self .arg )
197+ sql = 'insert into ids (name, num) values (%s,%s)' % (
198+ self .arg , self .arg )
194199 self .sql (sql , ('__textids' , 1 ))
195200
196201 def add_new_columns_v2 (self ):
@@ -224,18 +229,19 @@ def update_class(self, spec, old_spec, force=0, adding_v2=0):
224229 return 0
225230
226231 logging .getLogger ('roundup.hyperdb' ).info (
227- 'update_class %s' % spec .classname )
232+ 'update_class %s' % spec .classname )
228233
229234 # detect multilinks that have been removed, and drop their table
230235 old_has = {}
231236 for name , prop in old_spec [1 ]:
232237 old_has [name ] = 1
233- if name in spec .properties or not isinstance (prop , hyperdb .Multilink ):
238+ if name in spec .properties or not \
239+ isinstance (prop , hyperdb .Multilink ):
234240 continue
235241 # it's a multilink, and it's been removed - drop the old
236242 # table. First drop indexes.
237243 self .drop_multilink_table_indexes (spec .classname , name )
238- sql = 'drop table %s_%s' % (spec .classname , prop )
244+ sql = 'drop table %s_%s' % (spec .classname , prop )
239245 self .sql (sql )
240246
241247 # now figure how we populate the new table
@@ -244,28 +250,28 @@ def update_class(self, spec, old_spec, force=0, adding_v2=0):
244250 else :
245251 fetch = ['_actor' , '_activity' , '_creation' , '_creator' ]
246252 properties = spec .getprops ()
247- for propname ,x in new_spec [1 ]:
253+ for propname , _x in new_spec [1 ]:
248254 prop = properties [propname ]
249255 if isinstance (prop , hyperdb .Multilink ):
250256 if propname not in old_has :
251257 # we need to create the new table
252258 self .create_multilink_table (spec , propname )
253259 elif force :
254- tn = '%s_%s' % (spec .classname , propname )
260+ tn = '%s_%s' % (spec .classname , propname )
255261 # grabe the current values
256- sql = 'select linkid, nodeid from %s' % tn
262+ sql = 'select linkid, nodeid from %s' % tn
257263 self .sql (sql )
258264 rows = self .cursor .fetchall ()
259265
260266 # drop the old table
261267 self .drop_multilink_table_indexes (spec .classname , propname )
262- sql = 'drop table %s' % tn
268+ sql = 'drop table %s' % tn
263269 self .sql (sql )
264270
265271 # re-create and populate the new table
266272 self .create_multilink_table (spec , propname )
267273 sql = """insert into %s (linkid, nodeid) values
268- (%s, %s)""" % (tn , self .arg , self .arg )
274+ (%s, %s)""" % (tn , self .arg , self .arg )
269275 for linkid , nodeid in rows :
270276 self .sql (sql , (int (linkid ), int (nodeid )))
271277 elif propname in old_has :
@@ -277,22 +283,23 @@ def update_class(self, spec, old_spec, force=0, adding_v2=0):
277283 fetch .append ('__retired__' )
278284 fetchcols = ',' .join (fetch )
279285 cn = spec .classname
280- sql = 'select %s from _%s' % (fetchcols , cn )
286+ sql = 'select %s from _%s' % (fetchcols , cn )
281287 self .sql (sql )
282288 olddata = self .cursor .fetchall ()
283289
284290 # TODO: update all the other index dropping code
285291 self .drop_class_table_indexes (cn , old_spec [0 ])
286292
287293 # drop the old table
288- self .sql ('drop table _%s' % cn )
294+ self .sql ('drop table _%s' % cn )
289295
290296 # create the new table
291297 self .create_class_table (spec )
292298
293299 if olddata :
294- inscols = ['id' , '_actor' , '_activity' , '_creation' , '_creator' , '__retired__' ]
295- for propname ,x in new_spec [1 ]:
300+ inscols = ['id' , '_actor' , '_activity' , '_creation' ,
301+ '_creator' , '__retired__' ]
302+ for propname , _x in new_spec [1 ]:
296303 prop = properties [propname ]
297304 if isinstance (prop , hyperdb .Multilink ):
298305 continue
@@ -307,15 +314,15 @@ def update_class(self, spec, old_spec, force=0, adding_v2=0):
307314 # NULL values
308315 args = ',' .join ([self .arg for x in inscols ])
309316 cols = ',' .join (inscols )
310- sql = 'insert into _%s (%s) values (%s)' % (cn , cols , args )
317+ sql = 'insert into _%s (%s) values (%s)' % (cn , cols , args )
311318 for entry in olddata :
312319 d = []
313320 retired_id = None
314321 for name in inscols :
315322 # generate the new value for the Interval int column
316323 if name .endswith ('_int__' ):
317324 name = name [2 :- 6 ]
318- if sqlite_version in (2 ,3 ):
325+ if sqlite_version in (2 , 3 ):
319326 try :
320327 v = hyperdb .Interval (entry [name ]).as_seconds ()
321328 except IndexError :
@@ -324,7 +331,7 @@ def update_class(self, spec, old_spec, force=0, adding_v2=0):
324331 v = hyperdb .Interval (entry [name ]).as_seconds ()
325332 else :
326333 v = None
327- elif sqlite_version in (2 ,3 ):
334+ elif sqlite_version in (2 , 3 ):
328335 try :
329336 v = entry [name ]
330337 except IndexError :
@@ -335,7 +342,8 @@ def update_class(self, spec, old_spec, force=0, adding_v2=0):
335342 v = None
336343 if name == 'id' :
337344 retired_id = v
338- elif name == '__retired__' and retired_id and v not in ['0' , 0 ]:
345+ elif name == '__retired__' and retired_id and \
346+ v not in ['0' , 0 ]:
339347 v = retired_id
340348 d .append (v )
341349 self .sql (sql , tuple (d ))
@@ -363,7 +371,7 @@ def sql_rollback(self):
363371 raise
364372
365373 def __repr__ (self ):
366- return '<roundlite 0x%x>' % id (self )
374+ return '<roundlite 0x%x>' % id (self )
367375
368376 def sql_commit (self ):
369377 """ Actually commit to the database.
@@ -379,7 +387,7 @@ def sql_commit(self):
379387 self .cursor = self .conn .cursor ()
380388
381389 def sql_index_exists (self , table_name , index_name ):
382- self .sql ('pragma index_list(%s)' % table_name )
390+ self .sql ('pragma index_list(%s)' % table_name )
383391 for entry in self .cursor .fetchall ():
384392 if entry [1 ] == index_name :
385393 return 1
@@ -399,26 +407,26 @@ def newid(self, classname):
399407 # on it's own even if we don't want it to end the transaction.
400408 # If we rewrite to use another sqlite library like apsw we
401409 # don't have to deal with this autocommit/autotransact foolishness.
402- self .conn .isolation_level = None ;
410+ self .conn .isolation_level = None
403411 # Manage the transaction locks manually.
404- self .sql ("BEGIN IMMEDIATE" );
412+ self .sql ("BEGIN IMMEDIATE" )
405413
406414 # get the next ID
407- sql = 'select num from ids where name=%s' % self .arg
415+ sql = 'select num from ids where name=%s' % self .arg
408416 self .sql (sql , (classname , ))
409417 newid = int (self .cursor .fetchone ()[0 ])
410418
411419 # leave the next larger number as the next newid
412- sql = 'update ids set num=num+1 where name=%s' % self .arg
420+ sql = 'update ids set num=num+1 where name=%s' % self .arg
413421 vals = (classname ,)
414422 self .sql (sql , vals )
415423
416424 # reset pysqlite's auto transact stuff to default since the
417425 # rest of the code expects it.
418- self .conn .isolation_level = '' ;
426+ self .conn .isolation_level = ''
419427 # commit writing the data, clearing locks for other processes
420428 # and create a new cursor to the database.
421- self .sql_commit ();
429+ self .sql_commit ()
422430
423431 # return as string
424432 return str (newid )
@@ -428,7 +436,7 @@ def setid(self, classname, setid):
428436
429437 We add one to make it behave like the sequences in postgres.
430438 """
431- sql = 'update ids set num=%s where name=%s' % (self .arg , self .arg )
439+ sql = 'update ids set num=%s where name=%s' % (self .arg , self .arg )
432440 vals = (int (setid )+ 1 , classname )
433441 self .sql (sql , vals )
434442
@@ -440,32 +448,37 @@ def clear(self):
440448
441449 def create_class (self , spec ):
442450 rdbms_common .Database .create_class (self , spec )
443- sql = 'insert into ids (name, num) values (%s, %s)' % (self .arg , self .arg )
451+ sql = 'insert into ids (name, num) values (%s, %s)' % (
452+ self .arg , self .arg )
444453 vals = (spec .classname , 1 )
445454 self .sql (sql , vals )
446455
447- if sqlite_version in (2 ,3 ):
456+ if sqlite_version in (2 , 3 ):
448457 def load_journal (self , classname , cols , nodeid ):
449458 """We need to turn the sqlite3.Row into a tuple so it can be
450459 unpacked"""
451460 l = rdbms_common .Database .load_journal (self ,
452- classname , cols , nodeid )
461+ classname , cols , nodeid )
453462 cols = range (5 )
454463 return [[row [col ] for col in cols ] for row in l ]
455464
465+
456466class sqliteClass :
457467 def filter (self , * args , ** kw ):
458468 """ If there's NO matches to a fetch, sqlite returns NULL
459469 instead of nothing
460470 """
461471 return [f for f in rdbms_common .Class .filter (self , * args , ** kw ) if f ]
462472
473+
463474class Class (sqliteClass , rdbms_common .Class ):
464475 pass
465476
477+
466478class IssueClass (sqliteClass , rdbms_common .IssueClass ):
467479 pass
468480
481+
469482class FileClass (sqliteClass , rdbms_common .FileClass ):
470483 pass
471484
0 commit comments