[LNT] r309065 - DeSQLify the migrations

Chris Matthews via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 25 17:37:56 PDT 2017


Author: cmatthews
Date: Tue Jul 25 17:37:56 2017
New Revision: 309065

URL: http://llvm.org/viewvc/llvm-project?rev=309065&view=rev
Log:
DeSQLify the migrations

The SQL in the LNT migrations has always been fragile.  This removes
the SQL in favor of SQLAlchemy's primatives instead.

In preperation for MySQL support in LNT, I have refactored the
migrations to use more SQLAlchemy features. I have replaced update and
deletes with the corrosponding SQLAlchemy expression langauge calls.  I
have also added some common utility functions to modify table structure,
and made those using SQLAlchemy DDL system, so we should be able to
customize them based on the backend.

I updated all 14 current migrations, so they have no raw SQL.

Added:
    lnt/trunk/lnt/server/db/migrations/util.py
Modified:
    lnt/trunk/lnt/server/db/migrations/upgrade_12_to_13.py
    lnt/trunk/lnt/server/db/migrations/upgrade_13_to_14.py
    lnt/trunk/lnt/server/db/migrations/upgrade_3_to_4.py
    lnt/trunk/lnt/server/db/migrations/upgrade_4_to_5.py
    lnt/trunk/lnt/server/db/migrations/upgrade_5_to_6.py
    lnt/trunk/lnt/server/db/migrations/upgrade_6_to_7.py
    lnt/trunk/lnt/server/db/migrations/upgrade_8_to_9.py
    lnt/trunk/lnt/server/db/migrations/upgrade_9_to_10.py

Modified: lnt/trunk/lnt/server/db/migrations/upgrade_12_to_13.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/db/migrations/upgrade_12_to_13.py?rev=309065&r1=309064&r2=309065&view=diff
==============================================================================
--- lnt/trunk/lnt/server/db/migrations/upgrade_12_to_13.py (original)
+++ lnt/trunk/lnt/server/db/migrations/upgrade_12_to_13.py Tue Jul 25 17:37:56 2017
@@ -3,10 +3,13 @@ import sqlalchemy
 from sqlalchemy import Column, String, Binary
 
 Base = sqlalchemy.ext.declarative.declarative_base()
+
+
 class TestSuiteJSONSchema(Base):
     __tablename__ = "TestSuiteJSONSchemas"
     testsuite_name = Column("TestSuiteName", String(256), primary_key=True)
     jsonschema = Column("JSONSchema", Binary)
 
+
 def upgrade(engine):
     Base.metadata.create_all(engine)

Modified: lnt/trunk/lnt/server/db/migrations/upgrade_13_to_14.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/db/migrations/upgrade_13_to_14.py?rev=309065&r1=309064&r2=309065&view=diff
==============================================================================
--- lnt/trunk/lnt/server/db/migrations/upgrade_13_to_14.py (original)
+++ lnt/trunk/lnt/server/db/migrations/upgrade_13_to_14.py Tue Jul 25 17:37:56 2017
@@ -4,71 +4,86 @@
 # them if they are not empty, as previously the test-suite name was different
 # from the prefix used in the tables. In yaml schemas the name and prefix is
 # always the same so we have to rename from `Compile_XXXX` to `compile_XXX`.
-import sqlalchemy
+import collections
 
+from sqlalchemy import delete, select, update, func
 
-def _drop_suite(trans, name):
-    trans.execute('''
-DELETE FROM "TestSuiteOrderFields"
-    WHERE "TestSuiteID" IN
-        (SELECT "ID" FROM "TestSuite" WHERE "Name" = \'compile\')
-''')
-    trans.execute('''
-DELETE FROM "TestSuiteMachineFields"
-    WHERE "TestSuiteID" IN
-        (SELECT "ID" FROM "TestSuite" WHERE "Name" = \'compile\')
-''')
-    trans.execute('''
-DELETE FROM "TestSuiteRunFields"
-    WHERE "TestSuiteID" IN
-        (SELECT "ID" FROM "TestSuite" WHERE "Name" = \'compile\')
-''')
-    trans.execute('''
-DELETE FROM "TestSuiteSampleFields"
-    WHERE "TestSuiteID" IN
-        (SELECT "ID" FROM "TestSuite" WHERE "Name" = \'compile\')
-''')
-    trans.execute('DELETE FROM "TestSuite" WHERE "Name" = \'compile\'')
+from lnt.server.db.migrations.util import introspect_table, rename_table
+
+
+def _drop_suite(trans, name, engine):
+    """Drop the suite name.
+
+    This patches up the suite description tables for Order Fields,
+    Machine Fields, Run Fields and Sample Fields.
+
+    After than remove the suite directly from the TestSuite table.
+    """
+
+    test_suite = introspect_table(engine, 'TestSuite')
+
+    test_suite_id = trans.execute(
+        select([test_suite.c.ID]).where(test_suite.c.Name == name)) \
+        .scalar()
+
+    drop_fields(engine, test_suite_id, 'TestSuiteOrderFields', trans)
+    drop_fields(engine, test_suite_id, 'TestSuiteMachineFields', trans)
+    drop_fields(engine, test_suite_id, 'TestSuiteRunFields', trans)
+    drop_fields(engine, test_suite_id, 'TestSuiteSampleFields', trans)
+
+    trans.execute(delete(test_suite).where(test_suite.c.Name == name))
+
+
+def drop_fields(engine, test_suite_id, name, trans):
+    """In the *Fields Tables, drop entries related to the test_suite_id.
+    """
+    fields_table = introspect_table(engine, name)
+    order_files = delete(fields_table,
+                         fields_table.c.TestSuiteID == test_suite_id)
+    trans.execute(order_files)
+    return fields_table
+
+
+TableRename = collections.namedtuple('TableRename', 'old_name new_name')
 
 
 def upgrade(engine):
-    tablenames = [
-        ('Compile_Baseline', 'compile_Baseline'),
-        ('Compile_ChangeIgnore', 'compile_ChangeIgnore'),
-        ('Compile_RegressionIndicator', 'compile_RegressionIndicator'),
-        ('Compile_FieldChange', 'compile_FieldChange'),
-        ('Compile_FieldChangeV2', 'compile_FieldChangeV2'),
-        ('Compile_Profile', 'compile_Profile'),
-        ('Compile_Regression', 'compile_Regression'),
-        ('Compile_Sample', 'compile_Sample'),
-        ('Compile_Run', 'compile_Run'),
-        ('Compile_Order', 'compile_Order'),
-        ('Compile_Test', 'compile_Test'),
-        ('Compile_Machine', 'compile_Machine'),
+    table_renames = [
+        TableRename('Compile_Baseline', 'compile_Baseline'),
+        TableRename('Compile_ChangeIgnore', 'compile_ChangeIgnore'),
+        TableRename('Compile_RegressionIndicator', 'compile_RegressionIndicator'),
+        TableRename('Compile_FieldChange', 'compile_FieldChange'),
+        TableRename('Compile_FieldChangeV2', 'compile_FieldChangeV2'),
+        TableRename('Compile_Profile', 'compile_Profile'),
+        TableRename('Compile_Regression', 'compile_Regression'),
+        TableRename('Compile_Sample', 'compile_Sample'),
+        TableRename('Compile_Run', 'compile_Run'),
+        TableRename('Compile_Order', 'compile_Order'),
+        TableRename('Compile_Test', 'compile_Test'),
+        TableRename('Compile_Machine', 'compile_Machine'),
     ]
     all_empty = True
-    for name, _ in tablenames:
-        num = engine.execute('SELECT COUNT(*) FROM "%s"' % name).first()
-        if num[0] > 0:
+    for rename in table_renames:
+        tab = introspect_table(engine, rename.old_name)
+        size = select([func.count(tab.c.ID)])
+        num = engine.execute(size).scalar()
+
+        if num > 0:
             all_empty = False
             break
-
+    test_suite = introspect_table(engine, 'TestSuite')
     with engine.begin() as trans:
-        # If nobody ever put data into the compile suite drop it
+        # If nobody ever put data into the compile suite drop it.
         if all_empty:
-            for name, _ in tablenames:
-                trans.execute('DROP TABLE "%s"' % name)
-            _drop_suite(trans, 'compile')
+            for name, _ in table_renames:
+                tab = introspect_table(engine, name)
+                tab.drop()
+            _drop_suite(trans, 'compile', engine)
         else:
-            for old_name, new_name in tablenames:
-                env = {'old_name': old_name, 'new_name': new_name}
-                trans.execute('''
-ALTER TABLE "%(old_name)s" RENAME TO "%(new_name)s_x"
-''' % env)
-                trans.execute('''
-ALTER TABLE "%(new_name)s_x" RENAME TO \"%(new_name)s\"
-''' % env)
+            for rename in table_renames:
+                tab = introspect_table(engine, rename.old_name)
+                rename_table(engine, tab, rename.new_name)
             # Just change the DB_Key to match the name
-            trans.execute('''
-UPDATE "TestSuite" SET "DBKeyName" = \'compile\' WHERE "Name" = \'compile\'
-''')
+            trans.execute(update(test_suite)
+                          .where(test_suite.c.Name == 'compile')
+                          .values(DBKeyName='compile'))

Modified: lnt/trunk/lnt/server/db/migrations/upgrade_3_to_4.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/db/migrations/upgrade_3_to_4.py?rev=309065&r1=309064&r2=309065&view=diff
==============================================================================
--- lnt/trunk/lnt/server/db/migrations/upgrade_3_to_4.py (original)
+++ lnt/trunk/lnt/server/db/migrations/upgrade_3_to_4.py Tue Jul 25 17:37:56 2017
@@ -1,18 +1,12 @@
 # Version 4 of the database adds the bigger_is_better column to StatusField.
 
-import os
-import sys
+from sqlalchemy import Column, Integer
 
-import sqlalchemy
+from lnt.server.db.migrations.util import introspect_table, add_column
 
-###
-# Upgrade TestSuite
 
 def upgrade(engine):
-    # Add our new column. SQLAlchemy doesn't really support adding a new column to an
-    # existing table, so instead of requiring SQLAlchemy-Migrate, just execute the raw SQL.
-    with engine.begin() as trans:
-        trans.execute("""
-ALTER TABLE "TestSuiteSampleFields"
-ADD COLUMN "bigger_is_better" INTEGER DEFAULT 0
-""")
+    test_suite_sample_fields = introspect_table(engine,
+                                                'TestSuiteSampleFields')
+    bigger_is_better = Column('bigger_is_better', Integer, default=0)
+    add_column(engine, test_suite_sample_fields, bigger_is_better)

Modified: lnt/trunk/lnt/server/db/migrations/upgrade_4_to_5.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/db/migrations/upgrade_4_to_5.py?rev=309065&r1=309064&r2=309065&view=diff
==============================================================================
--- lnt/trunk/lnt/server/db/migrations/upgrade_4_to_5.py (original)
+++ lnt/trunk/lnt/server/db/migrations/upgrade_4_to_5.py Tue Jul 25 17:37:56 2017
@@ -1,45 +1,41 @@
 # Version 5 adds a "score" Sample type.
 
-import os
-import sys
-
-import sqlalchemy
-from sqlalchemy import *
-
-###
-# Upgrade TestSuite
-
 # Import the original schema from upgrade_0_to_1 since upgrade_4_to_5 does not
 # change the actual schema.
+from sqlalchemy import update, Column, Float
+from sqlalchemy.orm import sessionmaker
+
 import lnt.server.db.migrations.upgrade_0_to_1 as upgrade_0_to_1
+from lnt.server.db.migrations.util import add_column, introspect_table
+
 
 def upgrade(engine):
     # Create a session.
-    session = sqlalchemy.orm.sessionmaker(engine)()
+    session = sessionmaker(engine)()
 
-    real_sample_type = session.query(upgrade_0_to_1.SampleType).\
-        filter_by(name = "Real").first()
+    real_sample_type = session.query(upgrade_0_to_1.SampleType). \
+        filter_by(name="Real").first()
 
     ts = session.query(upgrade_0_to_1.TestSuite).filter_by(name='nts').first()
     score = upgrade_0_to_1.SampleField(name="score", type=real_sample_type,
-                                       info_key=".score",)
+                                       info_key=".score")
     ts.sample_fields.append(score)
     session.add(ts)
 
     session.commit()
     session.close()
 
-    # upgrade_3_to_4.py added this column, so it is not in the ORM.
+    test_suite_sample_fields = introspect_table(engine, 'TestSuiteSampleFields')
+
+    set_scores = update(test_suite_sample_fields) \
+        .where(test_suite_sample_fields.c.Name == "score") \
+        .values(bigger_is_better=1)
+
     with engine.begin() as trans:
-        trans.execute("""
-UPDATE "TestSuiteSampleFields"
-SET bigger_is_better=1
-WHERE "Name"='score'
-""")
-        # FIXME: This is obviously not the right way to do this, but I gave up
-        # trying to find out how to do it properly in SQLAlchemy without
-        # SQLAlchemy-migrate installed.
-        trans.execute("""
-ALTER TABLE "NT_Sample"
-ADD COLUMN "score" FLOAT
-""")
+        trans.execute(set_scores)
+
+    # Give the NT table a score column.
+
+    nt_sample = introspect_table(engine, 'NT_Sample')
+    score = Column('score', Float)
+    add_column(engine, nt_sample, score)

Modified: lnt/trunk/lnt/server/db/migrations/upgrade_5_to_6.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/db/migrations/upgrade_5_to_6.py?rev=309065&r1=309064&r2=309065&view=diff
==============================================================================
--- lnt/trunk/lnt/server/db/migrations/upgrade_5_to_6.py (original)
+++ lnt/trunk/lnt/server/db/migrations/upgrade_5_to_6.py Tue Jul 25 17:37:56 2017
@@ -1,10 +1,8 @@
 # Version 6 adds a "mem_bytes"" Sample type to "nts".
 
-import os
-import sys
-
 import sqlalchemy
 from sqlalchemy import *
+from lnt.server.db.migrations.util import add_column, introspect_table
 
 ###
 # Upgrade TestSuite
@@ -13,12 +11,13 @@ from sqlalchemy import *
 # change the actual schema.
 import lnt.server.db.migrations.upgrade_0_to_1 as upgrade_0_to_1
 
+
 def upgrade(engine):
     # Create a session.
     session = sqlalchemy.orm.sessionmaker(engine)()
 
     real_sample_type = session.query(upgrade_0_to_1.SampleType).\
-        filter_by(name = "Real").first()
+        filter_by(name="Real").first()
 
     ts = session.query(upgrade_0_to_1.TestSuite).filter_by(name='nts').first()
     mem_bytes = upgrade_0_to_1.SampleField(name="mem_bytes",
@@ -29,18 +28,16 @@ def upgrade(engine):
     session.commit()
     session.close()
 
+    test_suite_sample_fields = introspect_table(engine, 'TestSuiteSampleFields')
+
+    set_mem = update(test_suite_sample_fields) \
+        .where(test_suite_sample_fields.c.Name == "mem_bytes") \
+        .values(bigger_is_better=0)
+
     # upgrade_3_to_4.py added this column, so it is not in the ORM.
     with engine.begin() as trans:
-        trans.execute("""
-UPDATE "TestSuiteSampleFields"
-SET bigger_is_better=0
-WHERE "Name"='mem_bytes'
-""")
-
-        # FIXME: This is obviously not the right way to do this, but I gave up
-        # trying to find out how to do it properly in SQLAlchemy without
-        # SQLAlchemy-migrate installed.
-        trans.execute("""
-ALTER TABLE "NT_Sample"
-ADD COLUMN "mem_bytes" FLOAT
-""")
+        trans.execute(set_mem)
+
+    nt_sample = introspect_table(engine, 'NT_Sample')
+    mem_bytes = Column('mem_bytes', Float)
+    add_column(engine, nt_sample, mem_bytes)

Modified: lnt/trunk/lnt/server/db/migrations/upgrade_6_to_7.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/db/migrations/upgrade_6_to_7.py?rev=309065&r1=309064&r2=309065&view=diff
==============================================================================
--- lnt/trunk/lnt/server/db/migrations/upgrade_6_to_7.py (original)
+++ lnt/trunk/lnt/server/db/migrations/upgrade_6_to_7.py Tue Jul 25 17:37:56 2017
@@ -1,9 +1,6 @@
 # Version 7 adds a "hash" Sample type & adds a sample field of
 # this type for the NTS test suite.
 
-import os
-import sys
-
 import sqlalchemy
 
 ###
@@ -14,6 +11,8 @@ import sqlalchemy
 from lnt.server.db.migrations.upgrade_0_to_1 \
   import SampleType, TestSuite, SampleField
 
+from lnt.server.db.migrations.util import add_column, introspect_table
+
 
 def upgrade(engine):
     # Create a session.
@@ -41,15 +40,8 @@ def upgrade(engine):
     session.commit()
     session.close()
 
-    with engine.begin() as trans:
-        trans.execute("""
-ALTER TABLE "NT_Sample"
-ADD COLUMN "hash_status" INTEGER
-""")
-        # For MD5 hashes, 32 characters is enough to store the full has.
-        # Assume that for hashing schemes producing longer hashes, storing
-        # just the first 32 characters is good enough for our use case.
-        trans.execute("""
-    ALTER TABLE "NT_Sample"
-    ADD COLUMN "hash" VARCHAR(32)
-    """)
+    nt_sample = introspect_table(engine, 'NT_Sample')
+    hash_status = sqlalchemy.Column('hash_status', sqlalchemy.Integer)
+    hash_string = sqlalchemy.Column('hash', sqlalchemy.String(32))
+    add_column(engine, nt_sample, hash_status)
+    add_column(engine, nt_sample, hash_string)

Modified: lnt/trunk/lnt/server/db/migrations/upgrade_8_to_9.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/db/migrations/upgrade_8_to_9.py?rev=309065&r1=309064&r2=309065&view=diff
==============================================================================
--- lnt/trunk/lnt/server/db/migrations/upgrade_8_to_9.py (original)
+++ lnt/trunk/lnt/server/db/migrations/upgrade_8_to_9.py Tue Jul 25 17:37:56 2017
@@ -1,22 +1,17 @@
 # Version 9 of the database updates Sample to add the profile field, and
 # adds Profiles.
 
-import os
-import sys
-
 import sqlalchemy
 from sqlalchemy import *
-from sqlalchemy.schema import Index
 from sqlalchemy.orm import relation
 
 # Import the original schema from upgrade_0_to_1 since upgrade_1_to_2 does not
 # change the actual schema, but rather adds functionality vis-a-vis orders.
 import lnt.server.db.migrations.upgrade_0_to_1 as upgrade_0_to_1
 import lnt.server.db.migrations.upgrade_2_to_3 as upgrade_2_to_3
+from lnt.server.db.migrations.util import add_column, introspect_table
 
 
-###
-# Upgrade TestSuite
 def add_profiles(test_suite):
     """Given a test suite with a database connection and a test-suite
     name, make the profile sqlalchemy database objects for that test-suite.
@@ -30,23 +25,24 @@ def add_profiles(test_suite):
 
     class Profile(Base):
         __tablename__ = db_key_name + '_Profile'
-        
+
         id = Column("ID", Integer, primary_key=True)
         created_time = Column("CreatedTime", DateTime)
         accessed_time = Column("AccessedTime", DateTime)
         filename = Column("Filename", String(256))
         counters = Column("Counters", String(512))
-    
+
     return Base
 
+
 def upgrade_testsuite(engine, name):
     # Grab Test Suite.
     session = sqlalchemy.orm.sessionmaker(engine)()
-    test_suite = session.query(upgrade_0_to_1.TestSuite).\
-                 filter_by(name=name).first()
-    assert(test_suite is not None)
+    test_suite = session.query(upgrade_0_to_1.TestSuite). \
+        filter_by(name=name).first()
+    assert (test_suite is not None)
     db_key_name = test_suite.db_key_name
-    
+
     # Add FieldChange to the test suite.
     Base = add_profiles(test_suite)
     Base.metadata.create_all(engine)
@@ -55,11 +51,10 @@ def upgrade_testsuite(engine, name):
     session.commit()
     session.close()
 
-    with engine.begin() as trans:
-        trans.execute("""
-ALTER TABLE "%s_Sample"
-ADD COLUMN "ProfileID" INTEGER
-""" % (db_key_name,))
+    ts_sample_table = introspect_table(engine, "{}_Sample".format(db_key_name))
+    profile_id = Column('ProfileID', Integer)
+    add_column(engine, ts_sample_table, profile_id)
+
 
 def upgrade(engine):
     # Create our FieldChangeField table and commit.

Modified: lnt/trunk/lnt/server/db/migrations/upgrade_9_to_10.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/db/migrations/upgrade_9_to_10.py?rev=309065&r1=309064&r2=309065&view=diff
==============================================================================
--- lnt/trunk/lnt/server/db/migrations/upgrade_9_to_10.py (original)
+++ lnt/trunk/lnt/server/db/migrations/upgrade_9_to_10.py Tue Jul 25 17:37:56 2017
@@ -6,8 +6,12 @@ import sqlalchemy
 ###
 # Import the original schema from upgrade_0_to_1 since upgrade_5_to_6 does not
 # change the actual schema.
+from sqlalchemy import update, Column, Float
+
 import lnt.server.db.migrations.upgrade_0_to_1 as upgrade_0_to_1
 
+from lnt.server.db.migrations.util import add_column, introspect_table
+
 
 def upgrade(engine):
     # Create a session.
@@ -25,14 +29,15 @@ def upgrade(engine):
     session.commit()
     session.close()
 
+    test_suite_sample_fields = introspect_table(engine, 'TestSuiteSampleFields')
+    update_code_size = update(test_suite_sample_fields) \
+        .where(test_suite_sample_fields.c.Name == "code_size") \
+        .values(bigger_is_better=0)
     # upgrade_3_to_4.py added this column, so it is not in the ORM.
+
     with engine.begin() as trans:
-        trans.execute("""
-UPDATE "TestSuiteSampleFields"
-SET bigger_is_better=0
-WHERE "Name"='code_size'
-""")
-        trans.execute("""
-ALTER TABLE "NT_Sample"
-ADD COLUMN "code_size" FLOAT
-""")
+        trans.execute(update_code_size)
+
+    nt_sample = introspect_table(engine, 'NT_Sample')
+    code_size = Column('code_size', Float)
+    add_column(engine, nt_sample, code_size)

Added: lnt/trunk/lnt/server/db/migrations/util.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/db/migrations/util.py?rev=309065&view=auto
==============================================================================
--- lnt/trunk/lnt/server/db/migrations/util.py (added)
+++ lnt/trunk/lnt/server/db/migrations/util.py Tue Jul 25 17:37:56 2017
@@ -0,0 +1,61 @@
+import sqlalchemy
+from sqlalchemy import DDL
+
+
+def add_column(engine, table_to_alter, column_to_add):
+    # type: (sqlalchemy.engine.Engine, sqlalchemy.Table, sqlalchemy.Column) -> None
+    """Add this column to the table.
+
+    This is a stopgap to a real migration system.  Inspect the Column pass
+    and try to issue an ALTER command to make the column.  Detect Column
+    default, and add that.
+
+    Be careful, this does not support corner cases like most Column keywords
+    or any fancy Column settings.
+
+    :param engine: to execute on.
+    :param table_to_alter: the Table to add the column to.
+    :param column_to_add: Column that does not have anything fancy like
+    autoincrement.
+
+    """
+    column_name = column_to_add.name
+    col_type = column_to_add.type
+    if not column_to_add.default:
+        default = ""
+    else:
+        default = "DEFAULT {}".format(column_to_add.default.arg)
+    add_score = DDL("ALTER TABLE %(table)s ADD COLUMN %(column_name)s %(col_type)s %(default)s",
+                    context=dict(column_name=column_name,
+                                 col_type=col_type,
+                                 default=default))
+    add_score.execute(bind=engine, target=table_to_alter)
+
+
+def introspect_table(engine, name):
+    # type: (sqlalchemy.engine.Engine, str) -> sqlalchemy.Table
+    """Create a SQLAlchemy Table from the table name in the current DB.
+
+    Used to make a Table object from something already in the DB."""
+    md = sqlalchemy.MetaData(engine)
+    target_table = sqlalchemy.Table(name, md, autoload=True)
+    return target_table
+
+
+def rename_table(engine, old_table, new_name):
+    # type: (sqlalchemy.engine.Engine, sqlalchemy.Table, str) -> None
+    """Rename the old_table to new_table.
+
+    Renames the table by Old_Table -> New_Table_x -> New_Table.
+
+    :param engine: to execute on.
+    :param old_table: the Table to rename.
+    :param new_name: the string name to change the table to.
+
+    """
+    rename = DDL("ALTER TABLE %(table)s RENAME TO %(new_name)s_x",
+                 context=dict(new_name=new_name))
+    rename.execute(bind=engine, target=old_table)
+    rename = DDL("ALTER TABLE %(new_name)s_x RENAME TO %(new_name)s",
+                 context=dict(new_name=new_name))
+    rename.execute(bind=engine)




More information about the llvm-commits mailing list