Completed
Pull Request — master (#941)
by James
06:36 queued 19s
created

zipline.assets._downgrade_v1_to_v0()   B

Complexity

Conditions 2

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 2
dl 0
loc 32
rs 8.8571
1
import sqlalchemy as sa
2
from alembic.migration import MigrationContext
3
from alembic.operations import Operations
4
5
from zipline.assets.asset_writer import write_version_info
6
from zipline.errors import AssetDBImpossibleDowngrade
7
8
9
def downgrade(engine, desired_version):
10
    """Downgrades the assets db at the given engine to the desired version.
11
12
    Parameters
13
    ----------
14
    engine : Engine
15
        An SQLAlchemy engine to the assets database.
16
    desired_version : int
17
        The desired resulting version for the assets database.
18
    """
19
20
    # Check the version of the db at the engine
21
    conn = engine.connect()
22
    metadata = sa.MetaData(conn)
23
    metadata.reflect(bind=engine)
24
    version_info_table = metadata.tables['version_info']
25
    starting_version = sa.select((version_info_table.c.version,)).scalar()
26
27
    # Check for accidental upgrade
28
    if starting_version < desired_version:
29
        raise AssetDBImpossibleDowngrade(db_version=starting_version,
30
                                         desired_version=desired_version)
31
32
    # Check if the desired version is already the db version
33
    if starting_version == desired_version:
34
        # No downgrade needed
35
        return
36
37
    # Create alembic context
38
    ctx = MigrationContext.configure(conn)
39
    op = Operations(ctx)
40
41
    # Integer keys of downgrades to run
42
    # E.g.: [5, 4, 3, 2] would downgrade v6 to v2
43
    downgrade_keys = range(desired_version, starting_version)[::-1]
44
45
    # Disable foreign keys until all downgrades are complete
46
    _pragma_foreign_keys(conn, False)
47
48
    # Execute the downgrades in order
49
    for downgrade_key in downgrade_keys:
50
        _downgrade_methods[downgrade_key](op, version_info_table)
51
52
    # Re-enable foreign keys
53
    _pragma_foreign_keys(conn, True)
54
55
56
def _pragma_foreign_keys(connection, on):
57
    """Sets the PRAGMA foreign_keys state of the SQLLite database. Disabling
58
    the pragma allows for batch modification of tables with foreign keys.
59
60
    Parameters
61
    ----------
62
    connection : Connection
63
        A SQLAlchemy connection to the db
64
    on : bool
65
        If true, PRAGMA foreign_keys will be set to ON. Otherwise, the PRAGMA
66
        foreign_keys will be set to OFF.
67
    """
68
    connection.execute("PRAGMA foreign_keys=%s" % ("ON" if on else "OFF"))
69
70
71
def _downgrade_v1_to_v0(op, version_info_table):
72
    """
73
    Downgrade assets db by removing the 'tick_size' column and renaming the
74
    'multiplier' column.
75
    """
76
    version_info_table.delete().execute()
77
78
    # Drop indices before batch
79
    # This is to prevent index collision when creating the temp table
80
    op.drop_index('ix_futures_contracts_root_symbol')
81
    op.drop_index('ix_futures_contracts_symbol')
82
83
    # Execute batch op to allow column modification in SQLLite
84
    with op.batch_alter_table('futures_contracts') as batch_op:
85
86
        # Rename 'multiplier'
87
        batch_op.alter_column(column_name='multiplier',
88
                              new_column_name='contract_multiplier')
89
90
        # Delete 'tick_size'
91
        batch_op.drop_column('tick_size')
92
93
    # Recreate indices after batch
94
    op.create_index('ix_futures_contracts_root_symbol',
95
                    table_name='futures_contracts',
96
                    columns=['root_symbol'])
97
    op.create_index('ix_futures_contracts_symbol',
98
                    table_name='futures_contracts',
99
                    columns=['symbol'],
100
                    unique=True)
101
102
    write_version_info(version_info_table, 0)
103
104
# This dict contains references to downgrade methods that can be applied to an
105
# assets db. The resulting db's version is the key.
106
# e.g. The method at key '0' is the downgrade method from v1 to v0
107
_downgrade_methods = {
108
    0: _downgrade_v1_to_v0,
109
}
110