Code

- Add roundup-devel@lists.sourceforge.net to announcement list.
[roundup.git] / roundup / instance.py
index 127590b2dc8ce72dfa4c6842a51ba7def64480c7..53fe56bfab597adca7079cbce956320aed2367a4 100644 (file)
@@ -46,6 +46,10 @@ class Tracker:
         """
         self.tracker_home = tracker_home
         self.optimize = optimize
+        # if set, call schema_hook after executing schema.py will get
+        # same variables (in particular db) as schema.py main purpose is
+        # for regression tests
+        self.schema_hook = None
         self.config = configuration.CoreConfig(tracker_home)
         self.actions = {}
         self.cgi_actions = {}
@@ -106,6 +110,8 @@ class Tracker:
         if self.optimize:
             # execute preloaded schema object
             exec(self.schema, vars)
+            if callable (self.schema_hook):
+                self.schema_hook(**vars)
             # use preloaded detectors
             detectors = self.detectors
         else:
@@ -114,6 +120,8 @@ class Tracker:
                 sys.path.insert(1, libdir)
             # execute the schema file
             self._load_python('schema.py', vars)
+            if callable (self.schema_hook):
+                self.schema_hook(**vars)
             # reload extensions and detectors
             for extension in self.get_extensions('extensions'):
                 extension(self)
@@ -128,6 +136,27 @@ class Tracker:
         # or this is the first time the database is opened,
         # do database upgrade checks
         if not (self.optimize and self.db_open):
+            # As a consistency check, ensure that every link property is
+            # pointing at a defined class.  Otherwise, the schema is
+            # internally inconsistent.  This is an important safety
+            # measure as it protects against an accidental schema change
+            # dropping a table while there are still links to the table;
+            # once the table has been dropped, there is no way to get it
+            # back, so it is important to drop it only if we are as sure
+            # as possible that it is no longer needed.
+            classes = db.getclasses()
+            for classname in classes:
+                cl = db.getclass(classname)
+                for propname, prop in cl.getprops().iteritems():
+                    if not isinstance(prop, (hyperdb.Link,
+                                             hyperdb.Multilink)):
+                        continue
+                    linkto = prop.classname
+                    if linkto not in classes:
+                        raise ValueError, \
+                            ("property %s.%s links to non-existent class %s"
+                             % (classname, propname, linkto))
+
             db.post_init()
             self.db_open = 1
         return db