Code

more batching cleanup
[roundup.git] / roundup / backends / back_metakit.py
index b4065b0592a918f25fd474d97a2676fc9dad1705..89193822f499a5be15aacfbdd4082f98087052f7 100755 (executable)
@@ -2,15 +2,31 @@ from roundup import hyperdb, date, password, roundupdb, security
 import metakit
 from sessions import Sessions
 import re, marshal, os, sys, weakref, time, calendar
-from roundup import indexer 
+from roundup import indexer
+import locking
 
-class Database(hyperdb.Database):
+_dbs = {}
+
+def Database(config, journaltag=None):
+    db = _dbs.get(config.DATABASE, None)
+    if db is None or db._db is None:
+        db = _Database(config, journaltag)
+        _dbs[config.DATABASE] = db
+    else:
+        db.journaltag = journaltag
+        try:
+            delattr(db, 'curuserid')
+        except AttributeError:
+            pass
+    return db
+
+class _Database(hyperdb.Database):
     def __init__(self, config, journaltag=None):
         self.config = config
         self.journaltag = journaltag
         self.classes = {}
-        self._classes = []
         self.dirty = 0
+        self.lockfile = None
         self._db = self.__open()
         self.indexer = Indexer(self.config.DATABASE, self._db)
         self.sessions = Sessions(self.config)
@@ -36,6 +52,8 @@ class Database(hyperdb.Database):
             except KeyError:
                 x = 0
             return x
+        elif classname == 'transactions':
+            return self.dirty
         return self.getclass(classname)
     def getclass(self, classname):
         return self.classes[classname]
@@ -60,13 +78,13 @@ class Database(hyperdb.Database):
         for cl in self.classes.values():
             cl._clear()
     def hasnode(self, classname, nodeid):
-        return self.getclass(clasname).hasnode(nodeid)
+        return self.getclass(classname).hasnode(nodeid)
     def pack(self, pack_before):
         pass
     def addclass(self, cl):
         self.classes[cl.classname] = cl
-       if self.tables.find(name=cl.classname) < 0:
-           self.tables.append(name=cl.classname)
+        if self.tables.find(name=cl.classname) < 0:
+            self.tables.append(name=cl.classname)
     def addjournal(self, tablenm, nodeid, action, params):
         tblid = self.tables.find(name=tablenm)
         if tblid == -1:
@@ -102,12 +120,19 @@ class Database(hyperdb.Database):
         for cl in self.classes.values():
             cl.db = None
         self._db = None
+        locking.release_lock(self.lockfile)
+        del _dbs[self.config.DATABASE]
+        self.lockfile.close()
         self.classes = {}
         self.indexer = None
 
     # --- internal
     def __open(self):
         self.dbnm = db = os.path.join(self.config.DATABASE, 'tracker.mk4')
+        lockfilenm = db[:-3]+'lck'
+        self.lockfile = locking.acquire_lock(lockfilenm)
+        self.lockfile.write(str(os.getpid()))
+        self.lockfile.flush()
         self.fastopen = 0
         if os.path.exists(db):
             dbtm = os.path.getmtime(db)
@@ -153,7 +178,8 @@ _ALLOWSETTINGPRIVATEPROPS = 0
 class Class:    
     privateprops = None
     def __init__(self, db, classname, **properties):
-        self.db = weakref.proxy(db)
+        #self.db = weakref.proxy(db)
+        self.db = db
         self.classname = classname
         self.keyname = None
         self.ruprops = properties
@@ -253,7 +279,7 @@ class Class:
         if not isnew:
             self.fireAuditors('set', nodeid, propvalues)
         if not propvalues:
-            return
+            return propvalues
         if propvalues.has_key('id'):
             raise KeyError, '"id" is reserved'
         if self.db.journaltag is None:
@@ -304,9 +330,14 @@ class Class:
             # do stuff based on the prop type
             if isinstance(prop, hyperdb.Link):
                 link_class = prop.classname
+                # must be a string or None
+                if value is not None and not isinstance(value, type('')):
+                    raise ValueError, 'property "%s" link value be a string'%(
+                        propname)
+                # Roundup sets to "unselected" by passing None
+                if value is None:
+                    value = 0   
                 # if it isn't a number, it's a key
-                if type(value) != _STRINGTYPE:
-                    raise ValueError, 'link value must be String'
                 try:
                     int(value)
                 except ValueError:
@@ -316,7 +347,8 @@ class Class:
                         raise IndexError, 'new property "%s": %s not a %s'%(
                             key, value, prop.classname)
 
-                if not self.db.getclass(link_class).hasnode(value):
+                if (value is not None and
+                        not self.db.getclass(link_class).hasnode(value)):
                     raise IndexError, '%s has no node %s'%(link_class, value)
 
                 setattr(row, key, int(value))
@@ -325,11 +357,13 @@ class Class:
                 if self.do_journal and prop.do_journal:
                     # register the unlink with the old linked node
                     if oldvalue:
-                        self.db.addjournal(link_class, value, _UNLINK, (self.classname, str(row.id), key))
+                        self.db.addjournal(link_class, value, _UNLINK,
+                            (self.classname, str(row.id), key))
 
                     # register the link with the newly linked node
                     if value:
-                        self.db.addjournal(link_class, value, _LINK, (self.classname, str(row.id), key))
+                        self.db.addjournal(link_class, value, _LINK,
+                            (self.classname, str(row.id), key))
 
             elif isinstance(prop, hyperdb.Multilink):
                 if type(value) != _LISTTYPE:
@@ -383,6 +417,8 @@ class Class:
                 for id in adds:
                     sv.append(fid=int(id))
                 changes[key] = oldvalue
+                if not rmvd and not adds:
+                    del propvalues[key]
                     
 
             elif isinstance(prop, hyperdb.String):
@@ -431,7 +467,7 @@ class Class:
 
         # nothing to do?
         if not propvalues:
-            return
+            return propvalues
         if not propvalues.has_key('activity'):
             row.activity = int(time.time())
         if isnew:
@@ -449,6 +485,8 @@ class Class:
                 self.db.addjournal(self.classname, nodeid, _SET, changes)
                 self.fireReactors('set', nodeid, oldnode)
 
+        return propvalues
+    
     def retire(self, nodeid):
         self.fireAuditors('retire', nodeid, None)
         view = self.getview(1)
@@ -461,10 +499,11 @@ class Class:
         row._isdel = 1
         if self.do_journal:
             self.db.addjournal(self.classname, nodeid, _RETIRE, {})
-        iv = self.getindexview(1)
-        ndx = iv.find(k=getattr(row, self.keyname),i=row.id)
-        if ndx > -1:
-            iv.delete(ndx)
+        if self.keyname:
+            iv = self.getindexview(1)
+            ndx = iv.find(k=getattr(row, self.keyname),i=row.id)
+            if ndx > -1:
+                iv.delete(ndx)
         self.db.dirty = 1
         self.fireReactors('retire', nodeid, None)
     def history(self, nodeid):
@@ -599,7 +638,9 @@ class Class:
         # search_matches is None or a set (dict of {nodeid: {propname:[nodeid,...]}})
         # filterspec is a dict {propname:value}
         # sort and group are lists of propnames
-        
+        # sort and group are (dir, prop) where dir is '+', '-' or None
+        #                    and prop is a prop name or None
+
         where = {'_isdel':0}
         mlcriteria = {}
         regexes = {}
@@ -706,15 +747,32 @@ class Class:
         if sort or group:
             sortspec = []
             rev = []
-            for propname in group + sort:
+            for dir, propname in group, sort:
+                if propname is None: continue
                 isreversed = 0
-                if propname[0] == '-':
-                    propname = propname[1:]
+                if dir == '-':
                     isreversed = 1
                 try:
                     prop = getattr(v, propname)
                 except AttributeError:
+                    print "MK has no property %s" % propname
                     continue
+                propclass = self.ruprops.get(propname, None)
+                if propclass is None:
+                    propclass = self.privateprops.get(propname, None)
+                    if propclass is None:
+                        print "Schema has no property %s" % propname
+                        continue
+                if isinstance(propclass, hyperdb.Link):
+                    linkclass = self.db.getclass(propclass.classname)
+                    lv = linkclass.getview()
+                    lv = lv.rename('id', propname)
+                    v = v.join(lv, prop, 1)
+                    if linkclass.getprops().has_key('order'):
+                        propname = 'order'
+                    else:
+                        propname = linkclass.labelprop()
+                    prop = getattr(v, propname)
                 if isreversed:
                     rev.append(prop)
                 sortspec.append(prop)
@@ -831,10 +889,8 @@ class Class:
             else:
                 mkprop = None
             if mkprop is None:
-                print "%s missing prop %s (%s)" % (self.classname, nm, rutyp.__class__.__name__)
                 break
             if _typmap[rutyp.__class__] != mkprop.type:
-                print "%s - prop %s (%s) has wrong mktyp (%s)" % (self.classname, nm, rutyp.__class__.__name__, mkprop.type)
                 break
         else:
             return view.ordered(1)
@@ -923,9 +979,6 @@ class FileClass(Class):
         newid = Class.create(self, **propvalues)
         if not content:
             return newid
-        if content.startswith('/tracker/download.php?'):
-            self.set(newid, content='http://sourceforge.net'+content)
-            return newid
         nm = bnm = '%s%s' % (self.classname, newid)
         sd = str(int(int(newid) / 1000))
         d = os.path.join(self.db.config.DATABASE, 'files', self.classname, sd)
@@ -962,7 +1015,9 @@ class IssueClass(Class, roundupdb.IssueClass):
         if not properties.has_key('files'):
             properties['files'] = hyperdb.Multilink("file")
         if not properties.has_key('nosy'):
-            properties['nosy'] = hyperdb.Multilink("user")
+            # note: journalling is turned off as it really just wastes
+            # space. this behaviour may be overridden in an instance
+            properties['nosy'] = hyperdb.Multilink("user", do_journal="no")
         if not properties.has_key('superseder'):
             properties['superseder'] = hyperdb.Multilink(classname)
         Class.__init__(self, db, classname, **properties)
@@ -1071,7 +1126,3 @@ class Indexer(indexer.Indexer):
         if self.changed:
             self.db.commit()
         self.changed = 0
-            
-            
-                
-