Code

fixes to the rdbms backends
authorrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Wed, 18 Sep 2002 07:04:39 +0000 (07:04 +0000)
committerrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Wed, 18 Sep 2002 07:04:39 +0000 (07:04 +0000)
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1192 57a73879-2fb5-44c3-a270-3262357dd7e2

roundup/backends/back_sqlite.py
roundup/backends/rdbms_common.py
test/test_db.py

index 7dae5df67776641c5b47b78801bbe8e6495ffa7f..6f1f3590178c00b9b00fde3fe46946dcb2790b03 100644 (file)
@@ -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]
+
index d264a581b8d0f0d4c4b83ca1c19c6695fcb51afd..1d1f8a3c2921c135c5eb38e3121a171f1b82efa5 100644 (file)
@@ -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.
 
index 7e84e6a3f3e9bb99a0c5b1e7994b85ff9f34e7b2..6d5cabc0cd6cbc9bafe2c7101cba30e14c5a55cc 100644 (file)
@@ -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