You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
5.3 KiB
136 lines
5.3 KiB
def execute_safely(manager, statement):
|
|
try:
|
|
manager.execute(statement)
|
|
except Exception:
|
|
print 'Statement %r failed (this is not fatal)' % statement
|
|
|
|
|
|
def delete_duplicates(manager, table, first_id, second_id):
|
|
rows = manager.execute(
|
|
'SELECT %s, %s, COUNT(1) AS count FROM %s '
|
|
'GROUP BY %s, %s HAVING count > 1' %
|
|
(first_id, second_id, table, first_id, second_id))
|
|
for first_id_value, second_id_value, count_unused in rows:
|
|
manager.execute('DELETE FROM %s '
|
|
'WHERE %s = %%s AND %s = %%s LIMIT 1' %
|
|
(table, first_id, second_id),
|
|
first_id_value, second_id_value)
|
|
if rows:
|
|
print 'Deleted %s duplicate rows from %s' % (len(rows), table)
|
|
|
|
|
|
def delete_invalid_foriegn_keys(manager, pivot_table, foreign_key_field,
|
|
destination_table):
|
|
manager.execute(
|
|
'DELETE %(table)s.* FROM %(table)s '
|
|
'LEFT JOIN %(destination_table)s '
|
|
'ON %(table)s.%(field)s = %(destination_table)s.id '
|
|
'WHERE %(destination_table)s.id IS NULL' %
|
|
dict(table=pivot_table, field=foreign_key_field,
|
|
destination_table=destination_table))
|
|
deleted_count = manager._database.rowcount
|
|
if deleted_count:
|
|
print ('Deleted %s invalid foreign key references from %s (%s)' %
|
|
(deleted_count, pivot_table, foreign_key_field))
|
|
|
|
|
|
def unique_index_name(table):
|
|
return table + '_both_ids'
|
|
|
|
|
|
def basic_index_name(table, field):
|
|
if field == 'aclgroup_id':
|
|
field = 'acl_group_id'
|
|
return table + '_' + field
|
|
|
|
|
|
def create_unique_index(manager, pivot_table, first_field, second_field):
|
|
index_name = unique_index_name(pivot_table)
|
|
manager.execute('CREATE UNIQUE INDEX %s ON %s (%s, %s)' %
|
|
(index_name, pivot_table, first_field, second_field))
|
|
|
|
# these indices are in the migrations but may not exist for historical
|
|
# reasons
|
|
old_index_name = basic_index_name(pivot_table, first_field)
|
|
execute_safely(manager, 'DROP INDEX %s ON %s' %
|
|
(old_index_name, pivot_table))
|
|
|
|
|
|
def drop_unique_index(manager, pivot_table, first_field):
|
|
index_name = unique_index_name(pivot_table)
|
|
manager.execute('DROP INDEX %s ON %s' % (index_name, pivot_table))
|
|
|
|
old_index_name = basic_index_name(pivot_table, first_field)
|
|
manager.execute('CREATE INDEX %s ON %s (%s)' %
|
|
(old_index_name, pivot_table, first_field))
|
|
|
|
|
|
def foreign_key_name(table, field):
|
|
return '_'.join([table, field, 'fk'])
|
|
|
|
|
|
def create_foreign_key_constraint(manager, table, field, destination_table):
|
|
key_name = foreign_key_name(table, field)
|
|
manager.execute('ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) '
|
|
'REFERENCES %s (id) ON DELETE NO ACTION' %
|
|
(table, key_name, field, destination_table))
|
|
|
|
|
|
def drop_foreign_key_constraint(manager, table, field):
|
|
key_name = foreign_key_name(table, field)
|
|
manager.execute('ALTER TABLE %s DROP FOREIGN KEY %s' % (table, key_name))
|
|
|
|
|
|
def cleanup_m2m_pivot(manager, pivot_table, first_field, first_table,
|
|
second_field, second_table, create_unique):
|
|
delete_duplicates(manager, pivot_table, first_field, second_field)
|
|
delete_invalid_foriegn_keys(manager, pivot_table, first_field, first_table)
|
|
delete_invalid_foriegn_keys(manager, pivot_table, second_field,
|
|
second_table)
|
|
|
|
if create_unique:
|
|
# first field is the more commonly used one, so we'll replace the
|
|
# less-commonly-used index with the larger unique index
|
|
create_unique_index(manager, pivot_table, second_field, first_field)
|
|
|
|
create_foreign_key_constraint(manager, pivot_table, first_field,
|
|
first_table)
|
|
create_foreign_key_constraint(manager, pivot_table, second_field,
|
|
second_table)
|
|
|
|
|
|
def reverse_cleanup_m2m_pivot(manager, pivot_table, first_field, second_field,
|
|
drop_unique):
|
|
drop_foreign_key_constraint(manager, pivot_table, second_field)
|
|
drop_foreign_key_constraint(manager, pivot_table, first_field)
|
|
if drop_unique:
|
|
drop_unique_index(manager, pivot_table, second_field)
|
|
|
|
|
|
TABLES = (
|
|
('hosts_labels', 'host_id', 'hosts', 'label_id', 'labels', True),
|
|
('acl_groups_hosts', 'host_id', 'hosts', 'aclgroup_id', 'acl_groups',
|
|
True),
|
|
('acl_groups_users', 'user_id', 'users', 'aclgroup_id', 'acl_groups',
|
|
True),
|
|
('autotests_dependency_labels', 'test_id', 'autotests', 'label_id',
|
|
'labels', False),
|
|
('jobs_dependency_labels', 'job_id', 'jobs', 'label_id', 'labels',
|
|
False),
|
|
('ineligible_host_queues', 'job_id', 'jobs', 'host_id', 'hosts', True),
|
|
)
|
|
|
|
|
|
def migrate_up(manager):
|
|
for (table, first_field, first_table, second_field, second_table,
|
|
create_unique) in TABLES:
|
|
cleanup_m2m_pivot(manager, table, first_field, first_table,
|
|
second_field, second_table, create_unique)
|
|
|
|
|
|
def migrate_down(manager):
|
|
for (table, first_field, first_table, second_field, second_table,
|
|
drop_unique) in reversed(TABLES):
|
|
reverse_cleanup_m2m_pivot(manager, table, first_field, second_field,
|
|
drop_unique)
|