From 8cae7869d7bbde4750aaef094b58d3c0ba0571bf Mon Sep 17 00:00:00 2001 From: richard Date: Wed, 18 Sep 2002 07:04:39 +0000 Subject: [PATCH] fixes to the rdbms backends git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1192 57a73879-2fb5-44c3-a270-3262357dd7e2 --- roundup/backends/back_sqlite.py | 119 ++++++++++++++++++++++++++++++- roundup/backends/rdbms_common.py | 61 +++++++++++----- test/test_db.py | 23 +++--- 3 files changed, 170 insertions(+), 33 deletions(-) diff --git a/roundup/backends/back_sqlite.py b/roundup/backends/back_sqlite.py index 7dae5df..6f1f359 100644 --- a/roundup/backends/back_sqlite.py +++ b/roundup/backends/back_sqlite.py @@ -1,4 +1,4 @@ -# $Id: back_sqlite.py,v 1.1 2002-09-18 05:07:47 richard Exp $ +# $Id: back_sqlite.py,v 1.2 2002-09-18 07:04:37 richard Exp $ __doc__ = ''' See https://pysqlite.sourceforge.net/ for pysqlite info ''' @@ -88,3 +88,120 @@ class Database(Database): res.append((nodeid, date.Date(date_stamp), user, action, params)) return res + def unserialise(self, classname, node): + ''' Decode the marshalled node data + + SQLite stringifies _everything_... so we need to re-numberificate + Booleans and Numbers. + ''' + if __debug__: + print >>hyperdb.DEBUG, 'unserialise', classname, node + properties = self.getclass(classname).getprops() + d = {} + for k, v in node.items(): + # if the property doesn't exist, or is the "retired" flag then + # it won't be in the properties dict + if not properties.has_key(k): + d[k] = v + continue + + # get the property spec + prop = properties[k] + + if isinstance(prop, Date) and v is not None: + d[k] = date.Date(v) + elif isinstance(prop, Interval) and v is not None: + d[k] = date.Interval(v) + elif isinstance(prop, Password): + p = password.Password() + p.unpack(v) + d[k] = p + elif isinstance(prop, Boolean) and v is not None: + d[k] = int(v) + elif isinstance(prop, Number) and v is not None: + # try int first, then assume it's a float + try: + d[k] = int(v) + except ValueError: + d[k] = float(v) + else: + d[k] = v + return d + +class Class(Class): + _marker = [] + def get(self, nodeid, propname, default=_marker, cache=1): + '''Get the value of a property on an existing node of this class. + + 'nodeid' must be the id of an existing node of this class or an + IndexError is raised. 'propname' must be the name of a property + of this class or a KeyError is raised. + + 'cache' indicates whether the transaction cache should be queried + for the node. If the node has been modified and you need to + determine what its values prior to modification are, you need to + set cache=0. + ''' + if propname == 'id': + return nodeid + + if propname == 'creation': + if not self.do_journal: + raise ValueError, 'Journalling is disabled for this class' + journal = self.db.getjournal(self.classname, nodeid) + if journal: + return self.db.getjournal(self.classname, nodeid)[0][1] + else: + # on the strange chance that there's no journal + return date.Date() + if propname == 'activity': + if not self.do_journal: + raise ValueError, 'Journalling is disabled for this class' + journal = self.db.getjournal(self.classname, nodeid) + if journal: + return self.db.getjournal(self.classname, nodeid)[-1][1] + else: + # on the strange chance that there's no journal + return date.Date() + if propname == 'creator': + if not self.do_journal: + raise ValueError, 'Journalling is disabled for this class' + journal = self.db.getjournal(self.classname, nodeid) + if journal: + name = self.db.getjournal(self.classname, nodeid)[0][2] + else: + return None + try: + return self.db.user.lookup(name) + except KeyError: + # the journaltag user doesn't exist any more + return None + + # get the property (raises KeyErorr if invalid) + prop = self.properties[propname] + + # get the node's dict + d = self.db.getnode(self.classname, nodeid) #, cache=cache) + + if not d.has_key(propname): + if default is self._marker: + if isinstance(prop, Multilink): + return [] + else: + return None + else: + return default + + # special handling for some types + if isinstance(prop, Multilink): + # don't pass our list to other code + return d[propname][:] + elif d[propname] is None: + # always return None right now, no conversion + return None + elif isinstance(prop, Boolean) or isinstance(prop, Number): + # turn Booleans and Numbers into integers + return int(d[propname]) + + return d[propname] + diff --git a/roundup/backends/rdbms_common.py b/roundup/backends/rdbms_common.py index d264a58..1d1f8a3 100644 --- a/roundup/backends/rdbms_common.py +++ b/roundup/backends/rdbms_common.py @@ -1,4 +1,4 @@ -# $Id: rdbms_common.py,v 1.1 2002-09-18 05:07:47 richard Exp $ +# $Id: rdbms_common.py,v 1.2 2002-09-18 07:04:38 richard Exp $ # standard python modules import sys, os, time, re, errno, weakref, copy @@ -70,23 +70,27 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database): attribute actually matches the schema in the database. ''' # now detect changes in the schema + save = 0 for classname, spec in self.classes.items(): if self.database_schema.has_key(classname): dbspec = self.database_schema[classname] - self.update_class(spec, dbspec) - self.database_schema[classname] = spec.schema() + if self.update_class(spec, dbspec): + self.database_schema[classname] = spec.schema() + save = 1 else: self.create_class(spec) self.database_schema[classname] = spec.schema() + save = 1 for classname in self.database_schema.keys(): if not self.classes.has_key(classname): self.drop_class(classname) # update the database version of the schema - cursor = self.conn.cursor() - self.sql(cursor, 'delete from schema') - self.save_dbschema(cursor, self.database_schema) + if save: + cursor = self.conn.cursor() + self.sql(cursor, 'delete from schema') + self.save_dbschema(cursor, self.database_schema) # reindex the db if necessary if self.indexer.should_reindex(): @@ -126,7 +130,8 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database): ''' spec_schema = spec.schema() if spec_schema == dbspec: - return + # no save needed for this one + return 0 if __debug__: print >>hyperdb.DEBUG, 'update_class FIRING' @@ -244,6 +249,7 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database): qs = ','.join([self.arg for x in cols]) sql = 'insert into _%s values (%s)'%(cn, s) cursor.execute(sql, olddata) + return 1 def create_class_table(self, cursor, spec): ''' create the class table for the given spec @@ -1590,6 +1596,7 @@ class Class(hyperdb.Class): frum = ['_'+cn] where = [] args = [] + a = self.db.arg for k, v in filterspec.items(): propclass = props[k] if isinstance(propclass, Multilink): @@ -1605,17 +1612,17 @@ class Class(hyperdb.Class): args.append(v) else: if isinstance(v, type([])): - s = ','.join([self.arg for x in v]) + s = ','.join([a for x in v]) where.append('_%s in (%s)'%(k, s)) args = args + v else: - where.append('_%s=%s'%(k, self.arg)) + where.append('_%s=%s'%(k, a)) args.append(v) # add results of full text search if search_matches is not None: v = search_matches.keys() - s = ','.join([self.arg for x in v]) + s = ','.join([a for x in v]) where.append('id in (%s)'%s) args = args + v @@ -1623,12 +1630,25 @@ class Class(hyperdb.Class): orderby = [] ordercols = [] if sort[0] is not None and sort[1] is not None: - if sort[0] != '-': - orderby.append('_'+sort[1]) - ordercols.append(sort[1]) + direction, colname = sort + if direction != '-': + if colname == 'activity': + orderby.append('activity') + ordercols.append('max(%s__journal.date) as activity'%cn) + frum.append('%s__journal'%cn) + where.append('%s__journal.nodeid = _%s.id'%(cn, cn)) + else: + orderby.append('_'+colname) + ordercols.append('_'+colname) else: - orderby.append('_'+sort[1]+' desc') - ordercols.append(sort[1]) + if colname == 'activity': + orderby.append('activity desc') + ordercols.append('max(%s__journal.date) as activity'%cn) + frum.append('%s__journal'%cn) + where.append('%s__journal.nodeid = _%s.id'%(cn, cn)) + else: + orderby.append('_'+colname+' desc') + ordercols.append('_'+colname) # figure the group by clause groupby = [] @@ -1636,10 +1656,10 @@ class Class(hyperdb.Class): if group[0] is not None and group[1] is not None: if group[0] != '-': groupby.append('_'+group[1]) - groupcols.append(group[1]) + groupcols.append('_'+group[1]) else: groupby.append('_'+group[1]+' desc') - groupcols.append(group[1]) + groupcols.append('_'+group[1]) # construct the SQL frum = ','.join(frum) @@ -1650,7 +1670,7 @@ class Class(hyperdb.Class): order = ' order by %s'%(','.join(orderby)) else: order = '' - if groupby: + if 0: #groupby: cols = cols + groupcols group = ' group by %s'%(','.join(groupby)) else: @@ -1660,10 +1680,13 @@ class Class(hyperdb.Class): group) args = tuple(args) if __debug__: - print >>hyperdb.DEBUG, 'find', (self, sql, args) + print >>hyperdb.DEBUG, 'filter', (self, sql, args) cursor = self.db.conn.cursor() cursor.execute(sql, args) + # return the IDs + return [row[0] for row in cursor.fetchall()] + def count(self): '''Get the number of nodes in this class. diff --git a/test/test_db.py b/test/test_db.py index 7e84e6a..6d5cabc 100644 --- a/test/test_db.py +++ b/test/test_db.py @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: test_db.py,v 1.48 2002-09-18 05:07:48 richard Exp $ +# $Id: test_db.py,v 1.49 2002-09-18 07:04:39 richard Exp $ import unittest, os, shutil, time @@ -143,21 +143,18 @@ class anydbmDBTestCase(MyTestCase): def testBooleanChange(self): userid = self.db.user.create(username='foo', assignable=1) - self.db.user.create(username='foo2', assignable=0) - a = self.db.user.get(userid, 'assignable') + self.assertEqual(1, self.db.user.get(userid, 'assignable')) self.db.user.set(userid, assignable=0) - self.assertNotEqual(self.db.user.get(userid, 'assignable'), a) - self.db.user.set(userid, assignable=0) - self.db.user.set(userid, assignable=1) - self.db.user.set('1', assignable=None) + self.assertEqual(self.db.user.get(userid, 'assignable'), 0) + self.db.user.set(userid, assignable=None) self.assertEqual(self.db.user.get('1', "assignable"), None) def testNumberChange(self): - self.db.user.create(username='foo', age='1') - a = self.db.user.get('1', 'age') - self.db.user.set('1', age='3') - self.assertNotEqual(self.db.user.get('1', 'age'), a) - self.db.user.set('1', age='1.0') + self.db.user.create(username='foo', age=1) + self.assertEqual(1, self.db.user.get('1', 'age')) + self.db.user.set('1', age=3) + self.assertNotEqual(self.db.user.get('1', 'age'), 1) + self.db.user.set('1', age=1.0) self.db.user.set('1', age=None) self.assertEqual(self.db.user.get('1', "age"), None) @@ -681,7 +678,7 @@ def suite(): unittest.makeSuite(anydbmDBTestCase, 'test'), unittest.makeSuite(anydbmReadOnlyDBTestCase, 'test') ] - #return unittest.TestSuite(l) +# return unittest.TestSuite(l) try: import bsddb -- 2.30.2