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