Fix issue 94 - it was impossible to add a column with a non-unique index.

Also implement more functionality with unique and foreign key constrains for sqlite.
This commit is contained in:
chrisw 2010-09-09 15:38:42 +01:00
parent a085ffa590
commit adf4113a0b
5 changed files with 59 additions and 26 deletions

View File

@ -14,6 +14,11 @@ Fixed bugs
- fixed bug with column dropping in sqlite (issue 96)
- fixed bug with column dropping involving foreign keys
- implemented column adding when foreign keys are present for sqlite
- fixed bug that prevented non-unique indexes being created (issue 94)
- implemented columns adding with unique constraints for sqlite
- implemented adding unique and foreign key constraints to columns
for sqlite
- fixed bug when dropping columns with unique constraints in sqlite
0.6 (11.07.2010)
---------------------------

View File

@ -108,10 +108,7 @@ class ANSIColumnGenerator(AlterTableVisitor, SchemaGenerator):
# add indexes and unique constraints
if column.index_name:
ix = Index(column.index_name,
column,
unique=bool(column.index_name or column.index))
ix.create()
Index(column.index_name,column).create()
elif column.unique_name:
constraint.UniqueConstraint(column,
name=column.unique_name).create()

View File

@ -26,13 +26,7 @@ class SQLiteCommon(object):
class SQLiteHelper(SQLiteCommon):
def visit_column(self, delta):
if isinstance(delta, DictMixin):
column = delta.result_column
table = self._to_table(delta.table)
else:
column = delta
table = self._to_table(column.table)
def recreate_table(self,table,column=None,delta=None):
table_name = self.preparer.format_table(table)
# we remove all indexes so as not to have
@ -50,6 +44,15 @@ class SQLiteHelper(SQLiteCommon):
self.execute()
self.append('DROP TABLE migration_tmp')
self.execute()
def visit_column(self, delta):
if isinstance(delta, DictMixin):
column = delta.result_column
table = self._to_table(delta.table)
else:
column = delta
table = self._to_table(column.table)
self.recreate_table(table,column,delta)
class SQLiteColumnGenerator(SQLiteSchemaGenerator,
ansisql.ANSIColumnGenerator,
@ -93,7 +96,7 @@ class SQLiteSchemaChanger(SQLiteHelper, ansisql.ANSISchemaChanger):
self._not_supported('ALTER INDEX')
class SQLiteConstraintGenerator(ansisql.ANSIConstraintGenerator, SQLiteCommon):
class SQLiteConstraintGenerator(ansisql.ANSIConstraintGenerator, SQLiteHelper, SQLiteCommon):
def visit_migrate_primary_key_constraint(self, constraint):
tmpl = "CREATE UNIQUE INDEX %s ON %s ( %s )"
@ -104,11 +107,14 @@ class SQLiteConstraintGenerator(ansisql.ANSIConstraintGenerator, SQLiteCommon):
self.append(msg)
self.execute()
def _modify_table(self, table, column, delta):
return 'INSERT INTO %(table_name)s SELECT * from migration_tmp'
def visit_migrate_foreign_key_constraint(self, *p, **k):
self._not_supported('ALTER TABLE ADD CONSTRAINT')
self.recreate_table(p[0].table)
def visit_migrate_unique_constraint(self, *p, **k):
self._not_supported('ALTER TABLE ADD CONSTRAINT')
self.recreate_table(p[0].table)
class SQLiteConstraintDropper(ansisql.ANSIColumnDropper,

View File

@ -8,6 +8,7 @@ from UserDict import DictMixin
import sqlalchemy
from sqlalchemy.schema import ForeignKeyConstraint
from sqlalchemy.schema import UniqueConstraint
from migrate.exceptions import *
from migrate.changeset import SQLA_06
@ -570,6 +571,9 @@ populated with defaults
if table is not None and self.table is None:
self._set_parent(table)
def _col_name_in_constraint(self,cons,name):
return False
def remove_from_table(self, table, unset_table=True):
# TODO: remove primary keys, constraints, etc
if unset_table:
@ -590,14 +594,13 @@ populated with defaults
to_drop = set()
for cons in table.constraints:
# TODO: deal with other types of constraint
if isinstance(cons,ForeignKeyConstraint):
col_names = []
if isinstance(cons,(ForeignKeyConstraint,
UniqueConstraint)):
for col_name in cons.columns:
if not isinstance(col_name,basestring):
col_name = col_name.name
col_names.append(col_name)
if self.name in col_names:
to_drop.add(cons)
if self.name==col_name:
to_drop.add(cons)
table.constraints = table.constraints - to_drop
if table.c.contains_column(self):

View File

@ -223,11 +223,11 @@ class TestAddDropColumn(fixture.DB):
col.drop()
@fixture.usedb(not_supported='sqlite')
def test_unique(self):
"""Can create columns with unique constraint"""
@fixture.usedb()
def test_unique_constraint(self):
self.assertRaises(exceptions.InvalidConstraintError,
Column('data', Integer, unique=True).create, self.table)
col = Column('data', Integer)
col.create(self.table, unique_name='data_unique')
@ -244,14 +244,34 @@ class TestAddDropColumn(fixture.DB):
col.drop(self.table)
# TODO: remove already attached columns with indexes, uniques, pks, fks ..
def _check_index(self,expected):
if 'mysql' in self.engine.name or 'postgres' in self.engine.name:
for index in tuple(
Table(self.table.name, MetaData(),
autoload=True, autoload_with=self.engine).indexes
):
if index.name=='ix_data':
break
self.assertEqual(expected,index.unique)
@fixture.usedb()
def test_index(self):
"""Can create columns with indexes"""
self.assertRaises(exceptions.InvalidConstraintError,
Column('data', Integer).create, self.table, index_name=True)
col = Column('data', Integer)
col.create(self.table, index_name='ix_data')
self._check_index(False)
Index('ix_data', col).drop(bind=self.engine)
col.drop()
@fixture.usedb()
def test_index_unique(self):
# shows how to create a unique index
col = Column('data', Integer)
col.create(self.table)
Index('ix_data', col, unique=True).create(bind=self.engine)
# check if index was added
self.table.insert(values={'data': 5}).execute()
try:
@ -262,9 +282,11 @@ class TestAddDropColumn(fixture.DB):
else:
self.fail()
self._check_index(True)
Index('ix_data', col).drop(bind=self.engine)
col.drop()
@fixture.usedb()
def test_server_defaults(self):
"""Can create columns with server_default values"""