Mercurial > p > roundup > code
comparison roundup/backends/back_sqlite.py @ 4076:59d02edfaa9c
Uniformly use """...""" instead of '''...''' for comments.
| author | Stefan Seefeld <stefan@seefeld.name> |
|---|---|
| date | Tue, 24 Feb 2009 03:15:57 +0000 |
| parents | bb30bbfc7cdd |
| children | 09e79cbeb827 |
comparison
equal
deleted
inserted
replaced
| 4075:b87d022a2f40 | 4076:59d02edfaa9c |
|---|---|
| 1 # $Id: back_sqlite.py,v 1.51 2007-06-21 07:35:50 schlatterbeck Exp $ | 1 """Implements a backend for SQLite. |
| 2 '''Implements a backend for SQLite. | |
| 3 | 2 |
| 4 See https://pysqlite.sourceforge.net/ for pysqlite info | 3 See https://pysqlite.sourceforge.net/ for pysqlite info |
| 5 | 4 |
| 6 | 5 |
| 7 NOTE: we use the rdbms_common table creation methods which define datatypes | 6 NOTE: we use the rdbms_common table creation methods which define datatypes |
| 8 for the columns, but sqlite IGNORES these specifications. | 7 for the columns, but sqlite IGNORES these specifications. |
| 9 ''' | 8 """ |
| 10 __docformat__ = 'restructuredtext' | 9 __docformat__ = 'restructuredtext' |
| 11 | 10 |
| 12 import os, base64, marshal, shutil, time, logging | 11 import os, base64, marshal, shutil, time, logging |
| 13 | 12 |
| 14 from roundup import hyperdb, date, password | 13 from roundup import hyperdb, date, password |
| 88 time_to_sleep = 0.01 * (2 << min(5, count)) | 87 time_to_sleep = 0.01 * (2 << min(5, count)) |
| 89 time.sleep(time_to_sleep) | 88 time.sleep(time_to_sleep) |
| 90 return 1 | 89 return 1 |
| 91 | 90 |
| 92 def sql_open_connection(self): | 91 def sql_open_connection(self): |
| 93 '''Open a standard, non-autocommitting connection. | 92 """Open a standard, non-autocommitting connection. |
| 94 | 93 |
| 95 pysqlite will automatically BEGIN TRANSACTION for us. | 94 pysqlite will automatically BEGIN TRANSACTION for us. |
| 96 ''' | 95 """ |
| 97 # make sure the database directory exists | 96 # make sure the database directory exists |
| 98 # database itself will be created by sqlite if needed | 97 # database itself will be created by sqlite if needed |
| 99 if not os.path.isdir(self.config.DATABASE): | 98 if not os.path.isdir(self.config.DATABASE): |
| 100 os.makedirs(self.config.DATABASE) | 99 os.makedirs(self.config.DATABASE) |
| 101 | 100 |
| 163 def fix_version_3_tables(self): | 162 def fix_version_3_tables(self): |
| 164 # NOOP - no restriction on column length here | 163 # NOOP - no restriction on column length here |
| 165 pass | 164 pass |
| 166 | 165 |
| 167 def update_class(self, spec, old_spec, force=0, adding_v2=0): | 166 def update_class(self, spec, old_spec, force=0, adding_v2=0): |
| 168 ''' Determine the differences between the current spec and the | 167 """ Determine the differences between the current spec and the |
| 169 database version of the spec, and update where necessary. | 168 database version of the spec, and update where necessary. |
| 170 | 169 |
| 171 If 'force' is true, update the database anyway. | 170 If 'force' is true, update the database anyway. |
| 172 | 171 |
| 173 SQLite doesn't have ALTER TABLE, so we have to copy and | 172 SQLite doesn't have ALTER TABLE, so we have to copy and |
| 174 regenerate the tables with the new schema. | 173 regenerate the tables with the new schema. |
| 175 ''' | 174 """ |
| 176 new_has = spec.properties.has_key | 175 new_has = spec.properties.has_key |
| 177 new_spec = spec.schema() | 176 new_spec = spec.schema() |
| 178 new_spec[1].sort() | 177 new_spec[1].sort() |
| 179 old_spec[1].sort() | 178 old_spec[1].sort() |
| 180 if not force and new_spec == old_spec: | 179 if not force and new_spec == old_spec: |
| 220 sql = 'drop table %s'%tn | 219 sql = 'drop table %s'%tn |
| 221 self.sql(sql) | 220 self.sql(sql) |
| 222 | 221 |
| 223 # re-create and populate the new table | 222 # re-create and populate the new table |
| 224 self.create_multilink_table(spec, propname) | 223 self.create_multilink_table(spec, propname) |
| 225 sql = '''insert into %s (linkid, nodeid) values | 224 sql = """insert into %s (linkid, nodeid) values |
| 226 (%s, %s)'''%(tn, self.arg, self.arg) | 225 (%s, %s)"""%(tn, self.arg, self.arg) |
| 227 for linkid, nodeid in rows: | 226 for linkid, nodeid in rows: |
| 228 self.sql(sql, (int(linkid), int(nodeid))) | 227 self.sql(sql, (int(linkid), int(nodeid))) |
| 229 elif old_has(propname): | 228 elif old_has(propname): |
| 230 # we copy this col over from the old table | 229 # we copy this col over from the old table |
| 231 fetch.append('_'+propname) | 230 fetch.append('_'+propname) |
| 294 self.sql(sql, tuple(d)) | 293 self.sql(sql, tuple(d)) |
| 295 | 294 |
| 296 return 1 | 295 return 1 |
| 297 | 296 |
| 298 def sql_close(self): | 297 def sql_close(self): |
| 299 ''' Squash any error caused by us already having closed the | 298 """ Squash any error caused by us already having closed the |
| 300 connection. | 299 connection. |
| 301 ''' | 300 """ |
| 302 try: | 301 try: |
| 303 self.conn.close() | 302 self.conn.close() |
| 304 except sqlite.ProgrammingError, value: | 303 except sqlite.ProgrammingError, value: |
| 305 if str(value) != 'close failed - Connection is closed.': | 304 if str(value) != 'close failed - Connection is closed.': |
| 306 raise | 305 raise |
| 307 | 306 |
| 308 def sql_rollback(self): | 307 def sql_rollback(self): |
| 309 ''' Squash any error caused by us having closed the connection (and | 308 """ Squash any error caused by us having closed the connection (and |
| 310 therefore not having anything to roll back) | 309 therefore not having anything to roll back) |
| 311 ''' | 310 """ |
| 312 try: | 311 try: |
| 313 self.conn.rollback() | 312 self.conn.rollback() |
| 314 except sqlite.ProgrammingError, value: | 313 except sqlite.ProgrammingError, value: |
| 315 if str(value) != 'rollback failed - Connection is closed.': | 314 if str(value) != 'rollback failed - Connection is closed.': |
| 316 raise | 315 raise |
| 317 | 316 |
| 318 def __repr__(self): | 317 def __repr__(self): |
| 319 return '<roundlite 0x%x>'%id(self) | 318 return '<roundlite 0x%x>'%id(self) |
| 320 | 319 |
| 321 def sql_commit(self, fail_ok=False): | 320 def sql_commit(self, fail_ok=False): |
| 322 ''' Actually commit to the database. | 321 """ Actually commit to the database. |
| 323 | 322 |
| 324 Ignore errors if there's nothing to commit. | 323 Ignore errors if there's nothing to commit. |
| 325 ''' | 324 """ |
| 326 try: | 325 try: |
| 327 self.conn.commit() | 326 self.conn.commit() |
| 328 except sqlite.DatabaseError, error: | 327 except sqlite.DatabaseError, error: |
| 329 if str(error) != 'cannot commit - no transaction is active': | 328 if str(error) != 'cannot commit - no transaction is active': |
| 330 raise | 329 raise |
| 338 return 1 | 337 return 1 |
| 339 return 0 | 338 return 0 |
| 340 | 339 |
| 341 # old-skool id generation | 340 # old-skool id generation |
| 342 def newid(self, classname): | 341 def newid(self, classname): |
| 343 ''' Generate a new id for the given class | 342 """ Generate a new id for the given class |
| 344 ''' | 343 """ |
| 345 # get the next ID | 344 # get the next ID |
| 346 sql = 'select num from ids where name=%s'%self.arg | 345 sql = 'select num from ids where name=%s'%self.arg |
| 347 self.sql(sql, (classname, )) | 346 self.sql(sql, (classname, )) |
| 348 newid = int(self.cursor.fetchone()[0]) | 347 newid = int(self.cursor.fetchone()[0]) |
| 349 | 348 |
| 354 | 353 |
| 355 # return as string | 354 # return as string |
| 356 return str(newid) | 355 return str(newid) |
| 357 | 356 |
| 358 def setid(self, classname, setid): | 357 def setid(self, classname, setid): |
| 359 ''' Set the id counter: used during import of database | 358 """ Set the id counter: used during import of database |
| 360 | 359 |
| 361 We add one to make it behave like the sequences in postgres. | 360 We add one to make it behave like the sequences in postgres. |
| 362 ''' | 361 """ |
| 363 sql = 'update ids set num=%s where name=%s'%(self.arg, self.arg) | 362 sql = 'update ids set num=%s where name=%s'%(self.arg, self.arg) |
| 364 vals = (int(setid)+1, classname) | 363 vals = (int(setid)+1, classname) |
| 365 self.sql(sql, vals) | 364 self.sql(sql, vals) |
| 366 | 365 |
| 367 def clear(self): | 366 def clear(self): |
| 376 vals = (spec.classname, 1) | 375 vals = (spec.classname, 1) |
| 377 self.sql(sql, vals) | 376 self.sql(sql, vals) |
| 378 | 377 |
| 379 if sqlite_version in (2,3): | 378 if sqlite_version in (2,3): |
| 380 def load_journal(self, classname, cols, nodeid): | 379 def load_journal(self, classname, cols, nodeid): |
| 381 '''We need to turn the sqlite3.Row into a tuple so it can be | 380 """We need to turn the sqlite3.Row into a tuple so it can be |
| 382 unpacked''' | 381 unpacked""" |
| 383 l = rdbms_common.Database.load_journal(self, | 382 l = rdbms_common.Database.load_journal(self, |
| 384 classname, cols, nodeid) | 383 classname, cols, nodeid) |
| 385 cols = range(5) | 384 cols = range(5) |
| 386 return [[row[col] for col in cols] for row in l] | 385 return [[row[col] for col in cols] for row in l] |
| 387 | 386 |
| 388 class sqliteClass: | 387 class sqliteClass: |
| 389 def filter(self, search_matches, filterspec, sort=(None,None), | 388 def filter(self, search_matches, filterspec, sort=(None,None), |
| 390 group=(None,None)): | 389 group=(None,None)): |
| 391 ''' If there's NO matches to a fetch, sqlite returns NULL | 390 """ If there's NO matches to a fetch, sqlite returns NULL |
| 392 instead of nothing | 391 instead of nothing |
| 393 ''' | 392 """ |
| 394 return filter(None, rdbms_common.Class.filter(self, search_matches, | 393 return filter(None, rdbms_common.Class.filter(self, search_matches, |
| 395 filterspec, sort=sort, group=group)) | 394 filterspec, sort=sort, group=group)) |
| 396 | 395 |
| 397 class Class(sqliteClass, rdbms_common.Class): | 396 class Class(sqliteClass, rdbms_common.Class): |
| 398 pass | 397 pass |
