Code

Some speedups - both of the SQL backends can handle using only one cursor.
[roundup.git] / roundup / backends / back_anydbm.py
index f36213d0ecab9f70b8d3ca937793c68405212493..a039f1598d17b0b030c7e1233cecc04846ecc25a 100644 (file)
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-#$Id: back_anydbm.py,v 1.78 2002-09-13 08:20:07 richard Exp $
+#$Id: back_anydbm.py,v 1.84 2002-09-23 00:50:32 richard Exp $
 '''
 This module defines a backend that saves the hyperdatabase in a database
 chosen by anydbm. It is guaranteed to always be available in python
@@ -73,11 +73,21 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         os.umask(0002)
 
     def post_init(self):
-        '''Called once the schema initialisation has finished.'''
+        ''' Called once the schema initialisation has finished.
+        '''
         # reindex the db if necessary
         if self.indexer.should_reindex():
             self.reindex()
 
+        # figure the "curuserid"
+        if self.journaltag is None:
+            self.curuserid = None
+        elif self.journaltag == 'admin':
+            # admin user may not exist, but always has ID 1
+            self.curuserid = '1'
+        else:
+            self.curuserid = self.user.lookup(self.journaltag)
+
     def reindex(self):
         for klass in self.classes.values():
             for nodeid in klass.list():
@@ -121,7 +131,10 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         '''
         if __debug__:
             print >>hyperdb.DEBUG, 'getclass', (self, classname)
-        return self.classes[classname]
+        try:
+            return self.classes[classname]
+        except KeyError:
+            raise KeyError, 'There is no class called "%s"'%classname
 
     #
     # Class DBs
@@ -233,6 +246,15 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         '''
         if __debug__:
             print >>hyperdb.DEBUG, 'addnode', (self, classname, nodeid, node)
+
+        # we'll be supplied these props if we're doing an import
+        if not node.has_key('creator'):
+            # add in the "calculated" properties (dupe so we don't affect
+            # calling code's node assumptions)
+            node = node.copy()
+            node['creator'] = self.curuserid
+            node['creation'] = node['activity'] = date.Date()
+
         self.newnodes.setdefault(classname, {})[nodeid] = 1
         self.cache.setdefault(classname, {})[nodeid] = node
         self.savenode(classname, nodeid, node)
@@ -244,6 +266,11 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
             print >>hyperdb.DEBUG, 'setnode', (self, classname, nodeid, node)
         self.dirtynodes.setdefault(classname, {})[nodeid] = 1
 
+        # update the activity time (dupe so we don't affect
+        # calling code's node assumptions)
+        node = node.copy()
+        node['activity'] = date.Date()
+
         # can't set without having already loaded the node
         self.cache[classname][nodeid] = node
         self.savenode(classname, nodeid, node)
@@ -566,6 +593,9 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         # save the indexer state
         self.indexer.save_index()
 
+        self.clearCache()
+
+    def clearCache(self):
         # all transactions committed, back to normal
         self.cache = {}
         self.dirtynodes = {}
@@ -616,7 +646,7 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         if creator:
             journaltag = creator
         else:
-            journaltag = self.journaltag
+            journaltag = self.curuserid
         if creation:
             journaldate = creation.serialise()
         else:
@@ -934,7 +964,10 @@ class Class(hyperdb.Class):
                 value = pwd
             d[propname] = value
 
-        # extract the extraneous journalling gumpf and nuke it
+        # add the node and journal
+        self.db.addnode(self.classname, newid, d)
+
+        # extract the journalling stuff and nuke it
         if d.has_key('creator'):
             creator = d['creator']
             del d['creator']
@@ -948,8 +981,6 @@ class Class(hyperdb.Class):
         if d.has_key('activity'):
             del d['activity']
 
-        # add the node and journal
-        self.db.addnode(self.classname, newid, d)
         self.db.addjournal(self.classname, newid, 'create', d, creator,
             creation)
         return newid
@@ -972,7 +1003,13 @@ class Class(hyperdb.Class):
         if propname == 'id':
             return nodeid
 
+        # get the node's dict
+        d = self.db.getnode(self.classname, nodeid, cache=cache)
+
+        # check for one of the special props
         if propname == 'creation':
+            if d.has_key('creation'):
+                return d['creation']
             if not self.do_journal:
                 raise ValueError, 'Journalling is disabled for this class'
             journal = self.db.getjournal(self.classname, nodeid)
@@ -982,6 +1019,8 @@ class Class(hyperdb.Class):
                 # on the strange chance that there's no journal
                 return date.Date()
         if propname == 'activity':
+            if d.has_key('activity'):
+                return d['activity']
             if not self.do_journal:
                 raise ValueError, 'Journalling is disabled for this class'
             journal = self.db.getjournal(self.classname, nodeid)
@@ -991,20 +1030,29 @@ class Class(hyperdb.Class):
                 # on the strange chance that there's no journal
                 return date.Date()
         if propname == 'creator':
+            if d.has_key('creator'):
+                return d['creator']
             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][2]
+                num_re = re.compile('^\d+$')
+                value = self.db.getjournal(self.classname, nodeid)[0][2]
+                if num_re.match(value):
+                    return value
+                else:
+                    # old-style "username" journal tag
+                    try:
+                        return self.db.user.lookup(value)
+                    except KeyError:
+                        # user's been retired, return admin
+                        return '1'
             else:
-                return self.db.journaltag
+                return self.db.curuserid
 
         # 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 _marker:
                 if isinstance(prop, Multilink):
@@ -1100,7 +1148,11 @@ class Class(hyperdb.Class):
             # this will raise the KeyError if the property isn't valid
             # ... we don't use getprops() here because we only care about
             # the writeable properties.
-            prop = self.properties[propname]
+            try:
+                prop = self.properties[propname]
+            except KeyError:
+                raise KeyError, '"%s" has no property named "%s"'%(
+                    self.classname, propname)
 
             # if the value's the same as the existing value, no sense in
             # doing anything
@@ -1385,7 +1437,8 @@ class Class(hyperdb.Class):
                     return nodeid
         finally:
             cldb.close()
-        raise KeyError, keyvalue
+        raise KeyError, 'No key (%s) value "%s" for "%s"'%(self.key,
+            keyvalue, self.classname)
 
     # change from spec - allows multiple props to match
     def find(self, **propspec):
@@ -1496,6 +1549,10 @@ class Class(hyperdb.Class):
             "sort" and "group" are (dir, prop) where dir is '+', '-' or None
                                and prop is a prop name or None
             "search_matches" is {nodeid: marker}
+
+            The filter must match all properties specificed - but if the
+            property value to match is a list, any one of the values in the
+            list may match for that property to match.
         '''
         cn = self.classname
 
@@ -1748,9 +1805,7 @@ class Class(hyperdb.Class):
             d['id'] = String()
             d['creation'] = hyperdb.Date()
             d['activity'] = hyperdb.Date()
-            # can't be a link to user because the user might have been
-            # retired since the journal entry was created
-            d['creator'] = hyperdb.String()
+            d['creator'] = hyperdb.Link('user')
         return d
 
     def addprop(self, **properties):
@@ -1874,8 +1929,7 @@ class FileClass(Class):
             modified.
         '''
         d = Class.getprops(self, protected=protected).copy()
-        if protected:
-            d['content'] = hyperdb.String()
+        d['content'] = hyperdb.String()
         return d
 
     def index(self, nodeid):