[llvm-commits] [LNT] r154555 - in /lnt/trunk/lnt: lnttool/create.py server/db/migrate.py server/db/migrations/README.txt server/db/migrations/upgrade_0_to_1.py server/db/v4db.py

Daniel Dunbar daniel at zuster.org
Wed Apr 11 16:15:00 PDT 2012


Author: ddunbar
Date: Wed Apr 11 18:14:59 2012
New Revision: 154555

URL: http://llvm.org/viewvc/llvm-project?rev=154555&view=rev
Log:
Rework how we manage database initialization in the presence of migrations.

 - Execute a full update process on newly created databases to bring them up to the current version. This is somewhat wasteful, but simple, and also ensures we are always testing migration well. We can revisit later if need be.

 - Instead of expecting V4DB to "maybe initialize" the table, redefine version 0 to be an empty database, and version 1 to match the current structure. We just have to take some care to make sure we handle databases "in the wild" which migration will treat as being at version 0, but in fact already are version 1.

Added:
    lnt/trunk/lnt/server/db/migrations/README.txt
Modified:
    lnt/trunk/lnt/lnttool/create.py
    lnt/trunk/lnt/server/db/migrate.py
    lnt/trunk/lnt/server/db/migrations/upgrade_0_to_1.py
    lnt/trunk/lnt/server/db/v4db.py

Modified: lnt/trunk/lnt/lnttool/create.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/lnttool/create.py?rev=154555&r1=154554&r2=154555&view=diff
==============================================================================
--- lnt/trunk/lnt/lnttool/create.py (original)
+++ lnt/trunk/lnt/lnttool/create.py Wed Apr 11 18:14:59 2012
@@ -83,7 +83,7 @@
 ###
 
 import lnt.testing
-import lnt.server.db.v4db
+import lnt.server.db.migrate
 
 def action_create(name, args):
     """create an LLVM nightly test installation"""
@@ -156,9 +156,8 @@
     wsgi_file.close()
     os.chmod(wsgi_path, 0755)
 
-    db_class = lnt.server.db.v4db.V4DB
-    db = db_class('sqlite:///' + db_path)
-    db.commit()
+    # Execute an upgrade on the database to initialize the schema.
+    lnt.server.db.migrate.update_path(db_path)
 
     print 'created LNT configuration in %r' % basepath
     print '  configuration file: %s' % cfg_path

Modified: lnt/trunk/lnt/server/db/migrate.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/db/migrate.py?rev=154555&r1=154554&r2=154555&view=diff
==============================================================================
--- lnt/trunk/lnt/server/db/migrate.py (original)
+++ lnt/trunk/lnt/server/db/migrate.py Wed Apr 11 18:14:59 2012
@@ -61,6 +61,10 @@
                                               'migrations')
         schema_migrations = {}
         for item in os.listdir(schema_migrations_path):
+            # Ignore certain known non-scripts.
+            if item in ('README.txt',):
+                continue
+
             # Ignore non-matching files.
             m = upgrade_script_rex.match(item)
             if m is None:

Added: lnt/trunk/lnt/server/db/migrations/README.txt
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/db/migrations/README.txt?rev=154555&view=auto
==============================================================================
--- lnt/trunk/lnt/server/db/migrations/README.txt (added)
+++ lnt/trunk/lnt/server/db/migrations/README.txt Wed Apr 11 18:14:59 2012
@@ -0,0 +1,7 @@
+This directory contains "upgrade scripts" for updating databases from one
+version to another.
+
+It is important that the scripts be "stable", in that they always function the
+same even as the LNT software itself changes. For this reason, the scripts
+generally define their own model definitions to match the schema of the LNT
+database at the time of the version they are upgrading from (or to).

Modified: lnt/trunk/lnt/server/db/migrations/upgrade_0_to_1.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/db/migrations/upgrade_0_to_1.py?rev=154555&r1=154554&r2=154555&view=diff
==============================================================================
--- lnt/trunk/lnt/server/db/migrations/upgrade_0_to_1.py (original)
+++ lnt/trunk/lnt/server/db/migrations/upgrade_0_to_1.py Wed Apr 11 18:14:59 2012
@@ -1,6 +1,105 @@
-def upgrade(engine):
-    # Do nothing.
+# Version 0 is an empty database.
+#
+# Version 1 is the schema state at the time when we started doing DB versioning.
+
+import sqlalchemy
+from sqlalchemy import *
+from sqlalchemy.schema import Index
+from sqlalchemy.orm import relation
+
+Base = sqlalchemy.ext.declarative.declarative_base()
+
+class SampleType(Base):
+    __tablename__ = 'SampleType'
+    id = Column("ID", Integer, primary_key=True)
+    name = Column("Name", String(256), unique=True)
+
+class StatusKind(Base):
+    __tablename__ = 'StatusKind'
+    id = Column("ID", Integer, primary_key=True)
+    name = Column("Name", String(256), unique=True)
+
+class TestSuite(Base):
+    __tablename__ = 'TestSuite'
+    id = Column("ID", Integer, primary_key=True)
+    name = Column("Name", String(256), unique=True)
+    db_key_name = Column("DBKeyName", String(256))
+    version = Column("Version", String(16))
+    machine_fields = relation('MachineField', backref='test_suite')
+    order_fields = relation('OrderField', backref='test_suite')
+    run_fields = relation('RunField', backref='test_suite')
+    sample_fields = relation('SampleField', backref='test_suite')
+
+class MachineField(Base):
+    __tablename__ = 'TestSuiteMachineFields'
+    id = Column("ID", Integer, primary_key=True)
+    test_suite_id = Column("TestSuiteID", Integer, ForeignKey('TestSuite.ID'),
+                           index=True)
+    name = Column("Name", String(256))
+    info_key = Column("InfoKey", String(256))
+
+class OrderField(Base):
+    __tablename__ = 'TestSuiteOrderFields'
+    id = Column("ID", Integer, primary_key=True)
+    test_suite_id = Column("TestSuiteID", Integer, ForeignKey('TestSuite.ID'),
+                           index=True)
+    name = Column("Name", String(256))
+    info_key = Column("InfoKey", String(256))
+    ordinal = Column("Ordinal", Integer)
+
+class RunField(Base):
+    __tablename__ = 'TestSuiteRunFields'
+    id = Column("ID", Integer, primary_key=True)
+    test_suite_id = Column("TestSuiteID", Integer, ForeignKey('TestSuite.ID'),
+                           index=True)
+    name = Column("Name", String(256))
+    info_key = Column("InfoKey", String(256))
+
+class SampleField(Base):
+    __tablename__ = 'TestSuiteSampleFields'
+    id = Column("ID", Integer, primary_key=True)
+    test_suite_id = Column("TestSuiteID", Integer, ForeignKey('TestSuite.ID'),
+                           index=True)
+    name = Column("Name", String(256))
+    type_id = Column("Type", Integer, ForeignKey('SampleType.ID'))
+    type = relation(SampleType)
+    info_key = Column("InfoKey", String(256))
+    status_field_id = Column("status_field", Integer, ForeignKey(
+            'TestSuiteSampleFields.ID'))
+    status_field = relation('SampleField', remote_side=id)
+
+def create_model_instance(class_, **kwargs):
+    instance = class_()
+    for key,value in kwargs.items():
+        setattr(instance, key, value)
+    return instance
+
+def initialize_core(engine):
+    # Create the tables.
+    Base.metadata.create_all(engine)
+
+    # Create a session.
+    session = sqlalchemy.orm.sessionmaker(engine)()
+
+    # Create the fixed sample kinds.
     #
-    # Version 0 conceptually represents nothing, and version 1 is where we
-    # started versioning the database.
-    pass
+    # NOTE: The IDs here are proscribed and should match those from
+    # 'lnt.testing'.
+    session.add(create_model_instance(StatusKind, id=0, name="PASS"))
+    session.add(create_model_instance(StatusKind, id=1, name="FAIL"))
+    session.add(create_model_instance(StatusKind, id=2, name="XFAIL"))
+
+    # Create the fixed status kinds.
+    session.add(create_model_instance(SampleType, name="Real"))
+    session.add(create_model_instance(SampleType, name="Status"))
+
+    session.commit()
+
+def upgrade(engine):
+    # This upgrade script is special in that it needs to handle databases "in
+    # the wild" which have contents but existed before versioning.
+
+    # If the TestSuite table exists, assume the database is pre-versioning but
+    # already has the core initalized.
+    if not TestSuite.__table__.exists(engine):
+        initialize_core(engine)

Modified: lnt/trunk/lnt/server/db/v4db.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/db/v4db.py?rev=154555&r1=154554&r2=154555&view=diff
==============================================================================
--- lnt/trunk/lnt/server/db/v4db.py (original)
+++ lnt/trunk/lnt/server/db/v4db.py Wed Apr 11 18:14:59 2012
@@ -93,9 +93,6 @@
         # Proxy object for implementing dict-like .testsuite property.
         self._testsuite_proxy = None
 
-        # Create the common tables in case this is a new database.
-        testsuite.Base.metadata.create_all(self.engine)
-
         self.session = sqlalchemy.orm.sessionmaker(self.engine)()
 
         # Add several shortcut aliases.
@@ -113,34 +110,21 @@
         # Resolve or create the known status kinds.
         self.pass_status_kind = self.query(testsuite.StatusKind)\
             .filter_by(id = lnt.testing.PASS).first()
-        if self.pass_status_kind is None:
-            self.pass_status_kind = testsuite.StatusKind(lnt.testing.PASS,
-                                                         "PASS")
-            self.add(self.pass_status_kind)
         self.fail_status_kind = self.query(testsuite.StatusKind)\
             .filter_by(id = lnt.testing.FAIL).first()
-        if self.fail_status_kind is None:
-            self.fail_status_kind = testsuite.StatusKind(lnt.testing.FAIL,
-                                                         "FAIL")
-            self.add(self.fail_status_kind)
         self.xfail_status_kind = self.query(testsuite.StatusKind)\
             .filter_by(id = lnt.testing.XFAIL).first()
-        if self.xfail_status_kind is None:
-            self.xfail_status_kind = testsuite.StatusKind(lnt.testing.XFAIL,
-                                                         "XFAIL")
-            self.add(self.xfail_status_kind)
+        assert (self.pass_status_kind, self.fail_status_kind,
+                self.xfail_status_kind), \
+                "status kinds not initialized!"
 
         # Resolve or create the known sample types.
         self.real_sample_type = self.query(testsuite.SampleType)\
             .filter_by(name = "Real").first()
-        if self.real_sample_type is None:
-            self.real_sample_type = testsuite.SampleType("Real")
-            self.add(self.real_sample_type)
         self.status_sample_type = self.query(testsuite.SampleType)\
             .filter_by(name = "Status").first()
-        if self.status_sample_type is None:
-            self.status_sample_type = testsuite.SampleType("Status")
-            self.add(self.status_sample_type)
+        assert (self.real_sample_type and self.status_sample_type), \
+            "sample types not initialized!"
 
     @property
     def testsuite(self):





More information about the llvm-commits mailing list