comparison roundup/backends/rdbms_common.py @ 1183:08a13a84ed43

Some speedups - both of the SQL backends can handle using only one cursor. Better date unserialisation too.
author Richard Jones <richard@users.sourceforge.net>
date Mon, 23 Sep 2002 06:48:35 +0000
parents 49aebf5a8691
children 3b0735ef8207
comparison
equal deleted inserted replaced
1182:cb35cf3db0e0 1183:08a13a84ed43
1 # $Id: rdbms_common.py,v 1.10 2002-09-23 00:50:32 richard Exp $ 1 # $Id: rdbms_common.py,v 1.11 2002-09-23 06:48:35 richard Exp $
2 2
3 # standard python modules 3 # standard python modules
4 import sys, os, time, re, errno, weakref, copy 4 import sys, os, time, re, errno, weakref, copy
5 5
6 # roundup modules 6 # roundup modules
51 def open_connection(self): 51 def open_connection(self):
52 ''' Open a connection to the database, creating it if necessary 52 ''' Open a connection to the database, creating it if necessary
53 ''' 53 '''
54 raise NotImplemented 54 raise NotImplemented
55 55
56 def sql(self, cursor, sql, args=None): 56 def sql(self, sql, args=None):
57 ''' Execute the sql with the optional args. 57 ''' Execute the sql with the optional args.
58 ''' 58 '''
59 if __debug__: 59 if __debug__:
60 print >>hyperdb.DEBUG, (self, sql, args) 60 print >>hyperdb.DEBUG, (self, sql, args)
61 if args: 61 if args:
62 cursor.execute(sql, args) 62 self.cursor.execute(sql, args)
63 else: 63 else:
64 cursor.execute(sql) 64 self.cursor.execute(sql)
65 65
66 def sql_fetchone(self, cursor): 66 def sql_fetchone(self):
67 ''' Fetch a single row. If there's nothing to fetch, return None. 67 ''' Fetch a single row. If there's nothing to fetch, return None.
68 ''' 68 '''
69 raise NotImplemented 69 raise NotImplemented
70 70
71 def sql_stringquote(self, value): 71 def sql_stringquote(self, value):
72 ''' Quote the string so it's safe to put in the 'sql quotes' 72 ''' Quote the string so it's safe to put in the 'sql quotes'
73 ''' 73 '''
74 return re.sub("'", "''", str(value)) 74 return re.sub("'", "''", str(value))
75 75
76 def save_dbschema(self, cursor, schema): 76 def save_dbschema(self, schema):
77 ''' Save the schema definition that the database currently implements 77 ''' Save the schema definition that the database currently implements
78 ''' 78 '''
79 raise NotImplemented 79 raise NotImplemented
80 80
81 def load_dbschema(self, cursor): 81 def load_dbschema(self):
82 ''' Load the schema definition that the database currently implements 82 ''' Load the schema definition that the database currently implements
83 ''' 83 '''
84 raise NotImplemented 84 raise NotImplemented
85 85
86 def post_init(self): 86 def post_init(self):
106 if not self.classes.has_key(classname): 106 if not self.classes.has_key(classname):
107 self.drop_class(classname) 107 self.drop_class(classname)
108 108
109 # update the database version of the schema 109 # update the database version of the schema
110 if save: 110 if save:
111 cursor = self.conn.cursor() 111 self.sql('delete from schema')
112 self.sql(cursor, 'delete from schema') 112 self.save_dbschema(self.database_schema)
113 self.save_dbschema(cursor, self.database_schema)
114 113
115 # reindex the db if necessary 114 # reindex the db if necessary
116 if self.indexer.should_reindex(): 115 if self.indexer.should_reindex():
117 self.reindex() 116 self.reindex()
118 117
178 dbspec_propnames,dbspec_props = [],{} 177 dbspec_propnames,dbspec_props = [],{}
179 for propname,prop in dbspec[1]: 178 for propname,prop in dbspec[1]:
180 dbspec_propnames.append(propname) 179 dbspec_propnames.append(propname)
181 dbspec_props[propname] = prop 180 dbspec_props[propname] = prop
182 181
183 # we're going to need one of these
184 cursor = self.conn.cursor()
185
186 # now compare 182 # now compare
187 for propname in spec_propnames: 183 for propname in spec_propnames:
188 prop = spec_props[propname] 184 prop = spec_props[propname]
189 if dbspec_props.has_key(propname) and prop==dbspec_props[propname]: 185 if dbspec_props.has_key(propname) and prop==dbspec_props[propname]:
190 continue 186 continue
193 189
194 if not dbspec_props.has_key(propname): 190 if not dbspec_props.has_key(propname):
195 # add the property 191 # add the property
196 if isinstance(prop, Multilink): 192 if isinstance(prop, Multilink):
197 # all we have to do here is create a new table, easy! 193 # all we have to do here is create a new table, easy!
198 self.create_multilink_table(cursor, spec, propname) 194 self.create_multilink_table(spec, propname)
199 continue 195 continue
200 196
201 # no ALTER TABLE, so we: 197 # no ALTER TABLE, so we:
202 # 1. pull out the data, including an extra None column 198 # 1. pull out the data, including an extra None column
203 oldcols, x = self.determine_columns(dbspec[1]) 199 oldcols, x = self.determine_columns(dbspec[1])
205 oldcols.append('__retired__') 201 oldcols.append('__retired__')
206 cn = spec.classname 202 cn = spec.classname
207 sql = 'select %s,%s from _%s'%(','.join(oldcols), self.arg, cn) 203 sql = 'select %s,%s from _%s'%(','.join(oldcols), self.arg, cn)
208 if __debug__: 204 if __debug__:
209 print >>hyperdb.DEBUG, 'update_class', (self, sql, None) 205 print >>hyperdb.DEBUG, 'update_class', (self, sql, None)
210 cursor.execute(sql, (None,)) 206 self.cursor.execute(sql, (None,))
211 olddata = cursor.fetchall() 207 olddata = self.cursor.fetchall()
212 208
213 # 2. drop the old table 209 # 2. drop the old table
214 cursor.execute('drop table _%s'%cn) 210 self.cursor.execute('drop table _%s'%cn)
215 211
216 # 3. create the new table 212 # 3. create the new table
217 cols, mls = self.create_class_table(cursor, spec) 213 cols, mls = self.create_class_table(spec)
218 # ensure the new column is last 214 # ensure the new column is last
219 cols.remove('_'+propname) 215 cols.remove('_'+propname)
220 assert oldcols == cols, "Column lists don't match!" 216 assert oldcols == cols, "Column lists don't match!"
221 cols.append('_'+propname) 217 cols.append('_'+propname)
222 218
230 # XXX this isn't necessary in sqlite :( 226 # XXX this isn't necessary in sqlite :(
231 self.conn.commit() 227 self.conn.commit()
232 228
233 # do the insert 229 # do the insert
234 for row in olddata: 230 for row in olddata:
235 self.sql(cursor, sql, tuple(row)) 231 self.sql(sql, tuple(row))
236 232
237 else: 233 else:
238 # modify the property 234 # modify the property
239 if __debug__: 235 if __debug__:
240 print >>hyperdb.DEBUG, 'update_class NOOP' 236 print >>hyperdb.DEBUG, 'update_class NOOP'
251 # delete the property 247 # delete the property
252 if isinstance(prop, Multilink): 248 if isinstance(prop, Multilink):
253 sql = 'drop table %s_%s'%(spec.classname, prop) 249 sql = 'drop table %s_%s'%(spec.classname, prop)
254 if __debug__: 250 if __debug__:
255 print >>hyperdb.DEBUG, 'update_class', (self, sql) 251 print >>hyperdb.DEBUG, 'update_class', (self, sql)
256 cursor.execute(sql) 252 self.cursor.execute(sql)
257 else: 253 else:
258 # no ALTER TABLE, so we: 254 # no ALTER TABLE, so we:
259 # 1. pull out the data, excluding the removed column 255 # 1. pull out the data, excluding the removed column
260 oldcols, x = self.determine_columns(spec.properties.items()) 256 oldcols, x = self.determine_columns(spec.properties.items())
261 oldcols.append('id') 257 oldcols.append('id')
262 oldcols.append('__retired__') 258 oldcols.append('__retired__')
263 # remove the missing column 259 # remove the missing column
264 oldcols.remove('_'+propname) 260 oldcols.remove('_'+propname)
265 cn = spec.classname 261 cn = spec.classname
266 sql = 'select %s from _%s'%(','.join(oldcols), cn) 262 sql = 'select %s from _%s'%(','.join(oldcols), cn)
267 cursor.execute(sql, (None,)) 263 self.cursor.execute(sql, (None,))
268 olddata = sql.fetchall() 264 olddata = sql.fetchall()
269 265
270 # 2. drop the old table 266 # 2. drop the old table
271 cursor.execute('drop table _%s'%cn) 267 self.cursor.execute('drop table _%s'%cn)
272 268
273 # 3. create the new table 269 # 3. create the new table
274 cols, mls = self.create_class_table(self, cursor, spec) 270 cols, mls = self.create_class_table(self, spec)
275 assert oldcols != cols, "Column lists don't match!" 271 assert oldcols != cols, "Column lists don't match!"
276 272
277 # 4. populate with the data from step one 273 # 4. populate with the data from step one
278 qs = ','.join([self.arg for x in cols]) 274 qs = ','.join([self.arg for x in cols])
279 sql = 'insert into _%s values (%s)'%(cn, s) 275 sql = 'insert into _%s values (%s)'%(cn, s)
280 cursor.execute(sql, olddata) 276 self.cursor.execute(sql, olddata)
281 return 1 277 return 1
282 278
283 def create_class_table(self, cursor, spec): 279 def create_class_table(self, spec):
284 ''' create the class table for the given spec 280 ''' create the class table for the given spec
285 ''' 281 '''
286 cols, mls = self.determine_columns(spec.properties.items()) 282 cols, mls = self.determine_columns(spec.properties.items())
287 283
288 # add on our special columns 284 # add on our special columns
292 # create the base table 288 # create the base table
293 scols = ','.join(['%s varchar'%x for x in cols]) 289 scols = ','.join(['%s varchar'%x for x in cols])
294 sql = 'create table _%s (%s)'%(spec.classname, scols) 290 sql = 'create table _%s (%s)'%(spec.classname, scols)
295 if __debug__: 291 if __debug__:
296 print >>hyperdb.DEBUG, 'create_class', (self, sql) 292 print >>hyperdb.DEBUG, 'create_class', (self, sql)
297 cursor.execute(sql) 293 self.cursor.execute(sql)
298 294
299 return cols, mls 295 return cols, mls
300 296
301 def create_journal_table(self, cursor, spec): 297 def create_journal_table(self, spec):
302 ''' create the journal table for a class given the spec and 298 ''' create the journal table for a class given the spec and
303 already-determined cols 299 already-determined cols
304 ''' 300 '''
305 # journal table 301 # journal table
306 cols = ','.join(['%s varchar'%x 302 cols = ','.join(['%s varchar'%x
307 for x in 'nodeid date tag action params'.split()]) 303 for x in 'nodeid date tag action params'.split()])
308 sql = 'create table %s__journal (%s)'%(spec.classname, cols) 304 sql = 'create table %s__journal (%s)'%(spec.classname, cols)
309 if __debug__: 305 if __debug__:
310 print >>hyperdb.DEBUG, 'create_class', (self, sql) 306 print >>hyperdb.DEBUG, 'create_class', (self, sql)
311 cursor.execute(sql) 307 self.cursor.execute(sql)
312 308
313 def create_multilink_table(self, cursor, spec, ml): 309 def create_multilink_table(self, spec, ml):
314 ''' Create a multilink table for the "ml" property of the class 310 ''' Create a multilink table for the "ml" property of the class
315 given by the spec 311 given by the spec
316 ''' 312 '''
317 sql = 'create table %s_%s (linkid varchar, nodeid varchar)'%( 313 sql = 'create table %s_%s (linkid varchar, nodeid varchar)'%(
318 spec.classname, ml) 314 spec.classname, ml)
319 if __debug__: 315 if __debug__:
320 print >>hyperdb.DEBUG, 'create_class', (self, sql) 316 print >>hyperdb.DEBUG, 'create_class', (self, sql)
321 cursor.execute(sql) 317 self.cursor.execute(sql)
322 318
323 def create_class(self, spec): 319 def create_class(self, spec):
324 ''' Create a database table according to the given spec. 320 ''' Create a database table according to the given spec.
325 ''' 321 '''
326 cursor = self.conn.cursor() 322 cols, mls = self.create_class_table(spec)
327 cols, mls = self.create_class_table(cursor, spec) 323 self.create_journal_table(spec)
328 self.create_journal_table(cursor, spec)
329 324
330 # now create the multilink tables 325 # now create the multilink tables
331 for ml in mls: 326 for ml in mls:
332 self.create_multilink_table(cursor, spec, ml) 327 self.create_multilink_table(spec, ml)
333 328
334 # ID counter 329 # ID counter
335 sql = 'insert into ids (name, num) values (%s,%s)'%(self.arg, self.arg) 330 sql = 'insert into ids (name, num) values (%s,%s)'%(self.arg, self.arg)
336 vals = (spec.classname, 1) 331 vals = (spec.classname, 1)
337 if __debug__: 332 if __debug__:
338 print >>hyperdb.DEBUG, 'create_class', (self, sql, vals) 333 print >>hyperdb.DEBUG, 'create_class', (self, sql, vals)
339 cursor.execute(sql, vals) 334 self.cursor.execute(sql, vals)
340 335
341 def drop_class(self, spec): 336 def drop_class(self, spec):
342 ''' Drop the given table from the database. 337 ''' Drop the given table from the database.
343 338
344 Drop the journal and multilink tables too. 339 Drop the journal and multilink tables too.
346 # figure the multilinks 341 # figure the multilinks
347 mls = [] 342 mls = []
348 for col, prop in spec.properties.items(): 343 for col, prop in spec.properties.items():
349 if isinstance(prop, Multilink): 344 if isinstance(prop, Multilink):
350 mls.append(col) 345 mls.append(col)
351 cursor = self.conn.cursor()
352 346
353 sql = 'drop table _%s'%spec.classname 347 sql = 'drop table _%s'%spec.classname
354 if __debug__: 348 if __debug__:
355 print >>hyperdb.DEBUG, 'drop_class', (self, sql) 349 print >>hyperdb.DEBUG, 'drop_class', (self, sql)
356 cursor.execute(sql) 350 self.cursor.execute(sql)
357 351
358 sql = 'drop table %s__journal'%spec.classname 352 sql = 'drop table %s__journal'%spec.classname
359 if __debug__: 353 if __debug__:
360 print >>hyperdb.DEBUG, 'drop_class', (self, sql) 354 print >>hyperdb.DEBUG, 'drop_class', (self, sql)
361 cursor.execute(sql) 355 self.cursor.execute(sql)
362 356
363 for ml in mls: 357 for ml in mls:
364 sql = 'drop table %s_%s'%(spec.classname, ml) 358 sql = 'drop table %s_%s'%(spec.classname, ml)
365 if __debug__: 359 if __debug__:
366 print >>hyperdb.DEBUG, 'drop_class', (self, sql) 360 print >>hyperdb.DEBUG, 'drop_class', (self, sql)
367 cursor.execute(sql) 361 self.cursor.execute(sql)
368 362
369 # 363 #
370 # Classes 364 # Classes
371 # 365 #
372 def __getattr__(self, classname): 366 def __getattr__(self, classname):
415 Note: I don't commit here, which is different behaviour to the 409 Note: I don't commit here, which is different behaviour to the
416 "nuke from orbit" behaviour in the *dbms. 410 "nuke from orbit" behaviour in the *dbms.
417 ''' 411 '''
418 if __debug__: 412 if __debug__:
419 print >>hyperdb.DEBUG, 'clear', (self,) 413 print >>hyperdb.DEBUG, 'clear', (self,)
420 cursor = self.conn.cursor()
421 for cn in self.classes.keys(): 414 for cn in self.classes.keys():
422 sql = 'delete from _%s'%cn 415 sql = 'delete from _%s'%cn
423 if __debug__: 416 if __debug__:
424 print >>hyperdb.DEBUG, 'clear', (self, sql) 417 print >>hyperdb.DEBUG, 'clear', (self, sql)
425 cursor.execute(sql) 418 self.cursor.execute(sql)
426 419
427 # 420 #
428 # Node IDs 421 # Node IDs
429 # 422 #
430 def newid(self, classname): 423 def newid(self, classname):
431 ''' Generate a new id for the given class 424 ''' Generate a new id for the given class
432 ''' 425 '''
433 # get the next ID 426 # get the next ID
434 cursor = self.conn.cursor()
435 sql = 'select num from ids where name=%s'%self.arg 427 sql = 'select num from ids where name=%s'%self.arg
436 if __debug__: 428 if __debug__:
437 print >>hyperdb.DEBUG, 'newid', (self, sql, classname) 429 print >>hyperdb.DEBUG, 'newid', (self, sql, classname)
438 cursor.execute(sql, (classname, )) 430 self.cursor.execute(sql, (classname, ))
439 newid = cursor.fetchone()[0] 431 newid = self.cursor.fetchone()[0]
440 432
441 # update the counter 433 # update the counter
442 sql = 'update ids set num=%s where name=%s'%(self.arg, self.arg) 434 sql = 'update ids set num=%s where name=%s'%(self.arg, self.arg)
443 vals = (int(newid)+1, classname) 435 vals = (int(newid)+1, classname)
444 if __debug__: 436 if __debug__:
445 print >>hyperdb.DEBUG, 'newid', (self, sql, vals) 437 print >>hyperdb.DEBUG, 'newid', (self, sql, vals)
446 cursor.execute(sql, vals) 438 self.cursor.execute(sql, vals)
447 439
448 # return as string 440 # return as string
449 return str(newid) 441 return str(newid)
450 442
451 def setid(self, classname, setid): 443 def setid(self, classname, setid):
452 ''' Set the id counter: used during import of database 444 ''' Set the id counter: used during import of database
453 ''' 445 '''
454 cursor = self.conn.cursor()
455 sql = 'update ids set num=%s where name=%s'%(self.arg, self.arg) 446 sql = 'update ids set num=%s where name=%s'%(self.arg, self.arg)
456 vals = (setid, classname) 447 vals = (setid, classname)
457 if __debug__: 448 if __debug__:
458 print >>hyperdb.DEBUG, 'setid', (self, sql, vals) 449 print >>hyperdb.DEBUG, 'setid', (self, sql, vals)
459 cursor.execute(sql, vals) 450 self.cursor.execute(sql, vals)
460 451
461 # 452 #
462 # Nodes 453 # Nodes
463 # 454 #
464 455
495 vals = tuple([node[col[1:]] for col in cols]) + (nodeid, 0) 486 vals = tuple([node[col[1:]] for col in cols]) + (nodeid, 0)
496 s = ','.join([self.arg for x in cols]) + ',%s,%s'%(self.arg, self.arg) 487 s = ','.join([self.arg for x in cols]) + ',%s,%s'%(self.arg, self.arg)
497 cols = ','.join(cols) + ',id,__retired__' 488 cols = ','.join(cols) + ',id,__retired__'
498 489
499 # perform the inserts 490 # perform the inserts
500 cursor = self.conn.cursor()
501 sql = 'insert into _%s (%s) values (%s)'%(classname, cols, s) 491 sql = 'insert into _%s (%s) values (%s)'%(classname, cols, s)
502 if __debug__: 492 if __debug__:
503 print >>hyperdb.DEBUG, 'addnode', (self, sql, vals) 493 print >>hyperdb.DEBUG, 'addnode', (self, sql, vals)
504 cursor.execute(sql, vals) 494 self.cursor.execute(sql, vals)
505 495
506 # insert the multilink rows 496 # insert the multilink rows
507 for col in mls: 497 for col in mls:
508 t = '%s_%s'%(classname, col) 498 t = '%s_%s'%(classname, col)
509 for entry in node[col]: 499 for entry in node[col]:
510 sql = 'insert into %s (linkid, nodeid) values (%s,%s)'%(t, 500 sql = 'insert into %s (linkid, nodeid) values (%s,%s)'%(t,
511 self.arg, self.arg) 501 self.arg, self.arg)
512 self.sql(cursor, sql, (entry, nodeid)) 502 self.sql(sql, (entry, nodeid))
513 503
514 # make sure we do the commit-time extra stuff for this node 504 # make sure we do the commit-time extra stuff for this node
515 self.transactions.append((self.doSaveNode, (classname, nodeid, node))) 505 self.transactions.append((self.doSaveNode, (classname, nodeid, node)))
516 506
517 def setnode(self, classname, nodeid, values, multilink_changes): 507 def setnode(self, classname, nodeid, values, multilink_changes):
544 mls.append(col) 534 mls.append(col)
545 else: 535 else:
546 cols.append('_'+col) 536 cols.append('_'+col)
547 cols.sort() 537 cols.sort()
548 538
549 cursor = self.conn.cursor()
550
551 # if there's any updates to regular columns, do them 539 # if there's any updates to regular columns, do them
552 if cols: 540 if cols:
553 # make sure the ordering is correct for column name -> column value 541 # make sure the ordering is correct for column name -> column value
554 sqlvals = tuple([values[col[1:]] for col in cols]) + (nodeid,) 542 sqlvals = tuple([values[col[1:]] for col in cols]) + (nodeid,)
555 s = ','.join(['%s=%s'%(x, self.arg) for x in cols]) 543 s = ','.join(['%s=%s'%(x, self.arg) for x in cols])
557 545
558 # perform the update 546 # perform the update
559 sql = 'update _%s set %s where id=%s'%(classname, s, self.arg) 547 sql = 'update _%s set %s where id=%s'%(classname, s, self.arg)
560 if __debug__: 548 if __debug__:
561 print >>hyperdb.DEBUG, 'setnode', (self, sql, sqlvals) 549 print >>hyperdb.DEBUG, 'setnode', (self, sql, sqlvals)
562 cursor.execute(sql, sqlvals) 550 self.cursor.execute(sql, sqlvals)
563 551
564 # now the fun bit, updating the multilinks ;) 552 # now the fun bit, updating the multilinks ;)
565 for col, (add, remove) in multilink_changes.items(): 553 for col, (add, remove) in multilink_changes.items():
566 tn = '%s_%s'%(classname, col) 554 tn = '%s_%s'%(classname, col)
567 if add: 555 if add:
568 sql = 'insert into %s (nodeid, linkid) values (%s,%s)'%(tn, 556 sql = 'insert into %s (nodeid, linkid) values (%s,%s)'%(tn,
569 self.arg, self.arg) 557 self.arg, self.arg)
570 for addid in add: 558 for addid in add:
571 self.sql(cursor, sql, (nodeid, addid)) 559 self.sql(sql, (nodeid, addid))
572 if remove: 560 if remove:
573 sql = 'delete from %s where nodeid=%s and linkid=%s'%(tn, 561 sql = 'delete from %s where nodeid=%s and linkid=%s'%(tn,
574 self.arg, self.arg) 562 self.arg, self.arg)
575 for removeid in remove: 563 for removeid in remove:
576 self.sql(cursor, sql, (nodeid, removeid)) 564 self.sql(sql, (nodeid, removeid))
577 565
578 # make sure we do the commit-time extra stuff for this node 566 # make sure we do the commit-time extra stuff for this node
579 self.transactions.append((self.doSaveNode, (classname, nodeid, values))) 567 self.transactions.append((self.doSaveNode, (classname, nodeid, values)))
580 568
581 def getnode(self, classname, nodeid): 569 def getnode(self, classname, nodeid):
597 cl = self.classes[classname] 585 cl = self.classes[classname]
598 cols, mls = self.determine_columns(cl.properties.items()) 586 cols, mls = self.determine_columns(cl.properties.items())
599 scols = ','.join(cols) 587 scols = ','.join(cols)
600 588
601 # perform the basic property fetch 589 # perform the basic property fetch
602 cursor = self.conn.cursor()
603 sql = 'select %s from _%s where id=%s'%(scols, classname, self.arg) 590 sql = 'select %s from _%s where id=%s'%(scols, classname, self.arg)
604 self.sql(cursor, sql, (nodeid,)) 591 self.sql(sql, (nodeid,))
605 592
606 values = self.sql_fetchone(cursor) 593 values = self.sql_fetchone()
607 if values is None: 594 if values is None:
608 raise IndexError, 'no such %s node %s'%(classname, nodeid) 595 raise IndexError, 'no such %s node %s'%(classname, nodeid)
609 596
610 # make up the node 597 # make up the node
611 node = {} 598 node = {}
615 # now the multilinks 602 # now the multilinks
616 for col in mls: 603 for col in mls:
617 # get the link ids 604 # get the link ids
618 sql = 'select linkid from %s_%s where nodeid=%s'%(classname, col, 605 sql = 'select linkid from %s_%s where nodeid=%s'%(classname, col,
619 self.arg) 606 self.arg)
620 cursor.execute(sql, (nodeid,)) 607 self.cursor.execute(sql, (nodeid,))
621 # extract the first column from the result 608 # extract the first column from the result
622 node[col] = [x[0] for x in cursor.fetchall()] 609 node[col] = [x[0] for x in self.cursor.fetchall()]
623 610
624 # un-dbificate the node data 611 # un-dbificate the node data
625 node = self.unserialise(classname, node) 612 node = self.unserialise(classname, node)
626 613
627 # save off in the cache 614 # save off in the cache
652 for entry in self.transactions[:]: 639 for entry in self.transactions[:]:
653 if entry[1][:2] == (classname, nodeid): 640 if entry[1][:2] == (classname, nodeid):
654 self.transactions.remove(entry) 641 self.transactions.remove(entry)
655 642
656 # now do the SQL 643 # now do the SQL
657 cursor = self.conn.cursor()
658 sql = 'delete from _%s where id=%s'%(classname, self.arg) 644 sql = 'delete from _%s where id=%s'%(classname, self.arg)
659 self.sql(cursor, sql, (nodeid,)) 645 self.sql(sql, (nodeid,))
660 646
661 # remove from multilnks 647 # remove from multilnks
662 cl = self.getclass(classname) 648 cl = self.getclass(classname)
663 x, mls = self.determine_columns(cl.properties.items()) 649 x, mls = self.determine_columns(cl.properties.items())
664 for col in mls: 650 for col in mls:
665 # get the link ids 651 # get the link ids
666 sql = 'delete from %s_%s where nodeid=%s'%(classname, col, self.arg) 652 sql = 'delete from %s_%s where nodeid=%s'%(classname, col, self.arg)
667 cursor.execute(sql, (nodeid,)) 653 self.cursor.execute(sql, (nodeid,))
668 654
669 # remove journal entries 655 # remove journal entries
670 sql = 'delete from %s__journal where nodeid=%s'%(classname, self.arg) 656 sql = 'delete from %s__journal where nodeid=%s'%(classname, self.arg)
671 self.sql(cursor, sql, (nodeid,)) 657 self.sql(sql, (nodeid,))
672 658
673 def serialise(self, classname, node): 659 def serialise(self, classname, node):
674 '''Copy the node contents, converting non-marshallable data into 660 '''Copy the node contents, converting non-marshallable data into
675 marshallable data. 661 marshallable data.
676 ''' 662 '''
728 return d 714 return d
729 715
730 def hasnode(self, classname, nodeid): 716 def hasnode(self, classname, nodeid):
731 ''' Determine if the database has a given node. 717 ''' Determine if the database has a given node.
732 ''' 718 '''
733 cursor = self.conn.cursor()
734 sql = 'select count(*) from _%s where id=%s'%(classname, self.arg) 719 sql = 'select count(*) from _%s where id=%s'%(classname, self.arg)
735 if __debug__: 720 if __debug__:
736 print >>hyperdb.DEBUG, 'hasnode', (self, sql, nodeid) 721 print >>hyperdb.DEBUG, 'hasnode', (self, sql, nodeid)
737 cursor.execute(sql, (nodeid,)) 722 self.cursor.execute(sql, (nodeid,))
738 return int(cursor.fetchone()[0]) 723 return int(self.cursor.fetchone()[0])
739 724
740 def countnodes(self, classname): 725 def countnodes(self, classname):
741 ''' Count the number of nodes that exist for a particular Class. 726 ''' Count the number of nodes that exist for a particular Class.
742 ''' 727 '''
743 cursor = self.conn.cursor()
744 sql = 'select count(*) from _%s'%classname 728 sql = 'select count(*) from _%s'%classname
745 if __debug__: 729 if __debug__:
746 print >>hyperdb.DEBUG, 'countnodes', (self, sql) 730 print >>hyperdb.DEBUG, 'countnodes', (self, sql)
747 cursor.execute(sql) 731 self.cursor.execute(sql)
748 return cursor.fetchone()[0] 732 return self.cursor.fetchone()[0]
749 733
750 def getnodeids(self, classname, retired=0): 734 def getnodeids(self, classname, retired=0):
751 ''' Retrieve all the ids of the nodes for a particular Class. 735 ''' Retrieve all the ids of the nodes for a particular Class.
752 736
753 Set retired=None to get all nodes. Otherwise it'll get all the 737 Set retired=None to get all nodes. Otherwise it'll get all the
754 retired or non-retired nodes, depending on the flag. 738 retired or non-retired nodes, depending on the flag.
755 ''' 739 '''
756 cursor = self.conn.cursor()
757 # flip the sense of the flag if we don't want all of them 740 # flip the sense of the flag if we don't want all of them
758 if retired is not None: 741 if retired is not None:
759 retired = not retired 742 retired = not retired
760 sql = 'select id from _%s where __retired__ <> %s'%(classname, self.arg) 743 sql = 'select id from _%s where __retired__ <> %s'%(classname, self.arg)
761 if __debug__: 744 if __debug__:
762 print >>hyperdb.DEBUG, 'getnodeids', (self, sql, retired) 745 print >>hyperdb.DEBUG, 'getnodeids', (self, sql, retired)
763 cursor.execute(sql, (retired,)) 746 self.cursor.execute(sql, (retired,))
764 return [x[0] for x in cursor.fetchall()] 747 return [x[0] for x in self.cursor.fetchall()]
765 748
766 def addjournal(self, classname, nodeid, action, params, creator=None, 749 def addjournal(self, classname, nodeid, action, params, creator=None,
767 creation=None): 750 creation=None):
768 ''' Journal the Action 751 ''' Journal the Action
769 'action' may be: 752 'action' may be:
793 776
794 if __debug__: 777 if __debug__:
795 print >>hyperdb.DEBUG, 'addjournal', (nodeid, journaldate, 778 print >>hyperdb.DEBUG, 'addjournal', (nodeid, journaldate,
796 journaltag, action, params) 779 journaltag, action, params)
797 780
798 cursor = self.conn.cursor() 781 self.save_journal(classname, cols, nodeid, journaldate,
799 self.save_journal(cursor, classname, cols, nodeid, journaldate,
800 journaltag, action, params) 782 journaltag, action, params)
801 783
802 def save_journal(self, cursor, classname, cols, nodeid, journaldate, 784 def save_journal(self, classname, cols, nodeid, journaldate,
803 journaltag, action, params): 785 journaltag, action, params):
804 ''' Save the journal entry to the database 786 ''' Save the journal entry to the database
805 ''' 787 '''
806 raise NotImplemented 788 raise NotImplemented
807 789
810 ''' 792 '''
811 # make sure the node exists 793 # make sure the node exists
812 if not self.hasnode(classname, nodeid): 794 if not self.hasnode(classname, nodeid):
813 raise IndexError, '%s has no node %s'%(classname, nodeid) 795 raise IndexError, '%s has no node %s'%(classname, nodeid)
814 796
815 cursor = self.conn.cursor()
816 cols = ','.join('nodeid date tag action params'.split()) 797 cols = ','.join('nodeid date tag action params'.split())
817 return self.load_journal(cursor, classname, cols, nodeid) 798 return self.load_journal(classname, cols, nodeid)
818 799
819 def load_journal(self, cursor, classname, cols, nodeid): 800 def load_journal(self, classname, cols, nodeid):
820 ''' Load the journal from the database 801 ''' Load the journal from the database
821 ''' 802 '''
822 raise NotImplemented 803 raise NotImplemented
823 804
824 def pack(self, pack_before): 805 def pack(self, pack_before):
826 ''' 807 '''
827 # get a 'yyyymmddhhmmss' version of the date 808 # get a 'yyyymmddhhmmss' version of the date
828 date_stamp = pack_before.serialise() 809 date_stamp = pack_before.serialise()
829 810
830 # do the delete 811 # do the delete
831 cursor = self.conn.cursor()
832 for classname in self.classes.keys(): 812 for classname in self.classes.keys():
833 sql = "delete from %s__journal where date<%s and "\ 813 sql = "delete from %s__journal where date<%s and "\
834 "action<>'create'"%(classname, self.arg) 814 "action<>'create'"%(classname, self.arg)
835 if __debug__: 815 if __debug__:
836 print >>hyperdb.DEBUG, 'pack', (self, sql, date_stamp) 816 print >>hyperdb.DEBUG, 'pack', (self, sql, date_stamp)
837 cursor.execute(sql, (date_stamp,)) 817 self.cursor.execute(sql, (date_stamp,))
838 818
839 def sql_commit(self): 819 def sql_commit(self):
840 ''' Actually commit to the database. 820 ''' Actually commit to the database.
841 ''' 821 '''
842 self.conn.commit() 822 self.conn.commit()
1468 methods, and other nodes may reuse the values of their key properties. 1448 methods, and other nodes may reuse the values of their key properties.
1469 ''' 1449 '''
1470 if self.db.journaltag is None: 1450 if self.db.journaltag is None:
1471 raise DatabaseError, 'Database open read-only' 1451 raise DatabaseError, 'Database open read-only'
1472 1452
1473 cursor = self.db.conn.cursor()
1474 sql = 'update _%s set __retired__=1 where id=%s'%(self.classname, 1453 sql = 'update _%s set __retired__=1 where id=%s'%(self.classname,
1475 self.db.arg) 1454 self.db.arg)
1476 if __debug__: 1455 if __debug__:
1477 print >>hyperdb.DEBUG, 'retire', (self, sql, nodeid) 1456 print >>hyperdb.DEBUG, 'retire', (self, sql, nodeid)
1478 cursor.execute(sql, (nodeid,)) 1457 self.db.cursor.execute(sql, (nodeid,))
1479 1458
1480 def is_retired(self, nodeid): 1459 def is_retired(self, nodeid):
1481 '''Return true if the node is rerired 1460 '''Return true if the node is rerired
1482 ''' 1461 '''
1483 cursor = self.db.conn.cursor()
1484 sql = 'select __retired__ from _%s where id=%s'%(self.classname, 1462 sql = 'select __retired__ from _%s where id=%s'%(self.classname,
1485 self.db.arg) 1463 self.db.arg)
1486 if __debug__: 1464 if __debug__:
1487 print >>hyperdb.DEBUG, 'is_retired', (self, sql, nodeid) 1465 print >>hyperdb.DEBUG, 'is_retired', (self, sql, nodeid)
1488 cursor.execute(sql, (nodeid,)) 1466 self.db.cursor.execute(sql, (nodeid,))
1489 return int(cursor.fetchone()[0]) 1467 return int(self.db.sql_fetchone()[0])
1490 1468
1491 def destroy(self, nodeid): 1469 def destroy(self, nodeid):
1492 '''Destroy a node. 1470 '''Destroy a node.
1493 1471
1494 WARNING: this method should never be used except in extremely rare 1472 WARNING: this method should never be used except in extremely rare
1582 otherwise a KeyError is raised. 1560 otherwise a KeyError is raised.
1583 ''' 1561 '''
1584 if not self.key: 1562 if not self.key:
1585 raise TypeError, 'No key property set for class %s'%self.classname 1563 raise TypeError, 'No key property set for class %s'%self.classname
1586 1564
1587 cursor = self.db.conn.cursor()
1588 sql = 'select id,__retired__ from _%s where _%s=%s'%(self.classname, 1565 sql = 'select id,__retired__ from _%s where _%s=%s'%(self.classname,
1589 self.key, self.db.arg) 1566 self.key, self.db.arg)
1590 self.db.sql(cursor, sql, (keyvalue,)) 1567 self.db.sql(sql, (keyvalue,))
1591 1568
1592 # see if there was a result that's not retired 1569 # see if there was a result that's not retired
1593 l = cursor.fetchall() 1570 l = self.db.cursor.fetchall()
1594 if not l or int(l[0][1]): 1571 if not l or int(l[0][1]):
1595 raise KeyError, 'No key (%s) value "%s" for "%s"'%(self.key, 1572 raise KeyError, 'No key (%s) value "%s" for "%s"'%(self.key,
1596 keyvalue, self.classname) 1573 keyvalue, self.classname)
1597 1574
1598 # return the id 1575 # return the id
1624 allvalues += tuple(values.keys()) 1601 allvalues += tuple(values.keys())
1625 a = self.db.arg 1602 a = self.db.arg
1626 tables.append('select nodeid from %s_%s where linkid in (%s)'%( 1603 tables.append('select nodeid from %s_%s where linkid in (%s)'%(
1627 self.classname, prop, ','.join([a for x in values.keys()]))) 1604 self.classname, prop, ','.join([a for x in values.keys()])))
1628 sql = '\nintersect\n'.join(tables) 1605 sql = '\nintersect\n'.join(tables)
1629 if __debug__: 1606 self.db.sql(sql, allvalues)
1630 print >>hyperdb.DEBUG, 'find', (self, sql, allvalues)
1631 cursor = self.db.conn.cursor()
1632 cursor.execute(sql, allvalues)
1633 try: 1607 try:
1634 l = [x[0] for x in cursor.fetchall()] 1608 l = [x[0] for x in self.db.cursor.fetchall()]
1635 except gadfly.database.error, message: 1609 except gadfly.database.error, message:
1636 if message == 'no more results': 1610 if message == 'no more results':
1637 l = [] 1611 l = []
1638 raise 1612 raise
1639 if __debug__: 1613 if __debug__:
1770 cols = ','.join(cols) 1744 cols = ','.join(cols)
1771 sql = 'select %s from %s %s%s%s'%(cols, frum, where, group, order) 1745 sql = 'select %s from %s %s%s%s'%(cols, frum, where, group, order)
1772 args = tuple(args) 1746 args = tuple(args)
1773 if __debug__: 1747 if __debug__:
1774 print >>hyperdb.DEBUG, 'filter', (self, sql, args) 1748 print >>hyperdb.DEBUG, 'filter', (self, sql, args)
1775 cursor = self.db.conn.cursor() 1749 self.db.cursor.execute(sql, args)
1776 cursor.execute(sql, args) 1750 l = self.db.cursor.fetchall()
1777 l = cursor.fetchall()
1778 1751
1779 # return the IDs (the first column) 1752 # return the IDs (the first column)
1780 # XXX The filter(None, l) bit is sqlite-specific... if there's _NO_ 1753 # XXX The filter(None, l) bit is sqlite-specific... if there's _NO_
1781 # XXX matches to a fetch, it returns NULL instead of nothing!?! 1754 # XXX matches to a fetch, it returns NULL instead of nothing!?!
1782 return filter(None, [row[0] for row in l]) 1755 return filter(None, [row[0] for row in l])

Roundup Issue Tracker: http://roundup-tracker.org/