Add some helper methods to deal with sqlite migrations

SQLite has very little support for altering tables.  Add some helper
methods mostly written by Sergey Lukjanov to work around these
issues.

Change-Id: I4a68fc0d6291e72c88c511d5f64befa41363df2c
Co-Authored-By: Sergey Lukjanov <slukjanov@mirantis.com>
This commit is contained in:
James E. Blair 2014-05-07 21:33:30 -04:00
parent ff8506a32b
commit 2c590b82b7
1 changed files with 105 additions and 0 deletions

105
gertty/dbsupport.py Normal file
View File

@ -0,0 +1,105 @@
# Copyright 2014 Mirantis Inc.
# Copyright 2014 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
import uuid
from alembic import op
import sqlalchemy
def sqlite_alter_columns(table_name, column_defs):
"""Implement alter columns for SQLite.
The ALTER COLUMN command isn't supported by SQLite specification.
Instead of calling ALTER COLUMN it uses the following workaround:
* create temp table '{table_name}_{rand_uuid}', with some column
defs replaced;
* copy all data to the temp table;
* drop old table;
* rename temp table to the old table name.
"""
connection = op.get_bind()
meta = sqlalchemy.MetaData(bind=connection)
meta.reflect()
changed_columns = {}
indexes = []
for col in column_defs:
# If we are to have an index on the column, don't create it
# immediately, instead, add it to a list of indexes to create
# after the table rename.
if col.index:
indexes.append(('ix_%s_%s' % (table_name, col.name),
table_name,
[col.name],
col.unique))
col.unique = False
col.index = False
changed_columns[col.name] = col
# construct lists of all columns and their names
old_columns = []
new_columns = []
column_names = []
for column in meta.tables[table_name].columns:
column_names.append(column.name)
old_columns.append(column)
if column.name in changed_columns.keys():
new_columns.append(changed_columns[column.name])
else:
col_copy = column.copy()
new_columns.append(col_copy)
for key in meta.tables[table_name].foreign_keys:
constraint = key.constraint
con_copy = constraint.copy()
new_columns.append(con_copy)
for index in meta.tables[table_name].indexes:
# If this is a single column index for a changed column, don't
# copy it because we may already be creating a new version of
# it (or removing it).
idx_columns = [col.name for col in index.columns]
if len(idx_columns)==1 and idx_columns[0] in changed_columns.keys():
continue
# Otherwise, recreate the index.
indexes.append((index.name,
table_name,
[col.name for col in index.columns],
index.unique))
# create temp table
tmp_table_name = "%s_%s" % (table_name, six.text_type(uuid.uuid4()))
op.create_table(tmp_table_name, *new_columns)
meta.reflect()
try:
# copy data from the old table to the temp one
sql_select = sqlalchemy.sql.select(old_columns)
connection.execute(sqlalchemy.sql.insert(meta.tables[tmp_table_name])
.from_select(column_names, sql_select))
except Exception:
op.drop_table(tmp_table_name)
raise
# drop the old table and rename temp table to the old table name
op.drop_table(table_name)
op.rename_table(tmp_table_name, table_name)
# (re-)create indexes
for index in indexes:
op.create_index(op.f(index[0]), index[1], index[2], unique=index[3])