diff --git a/docs/changelog.rst b/docs/changelog.rst index 823734f..7a8576f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -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) --------------------------- diff --git a/migrate/changeset/ansisql.py b/migrate/changeset/ansisql.py index e5500b8..021885b 100644 --- a/migrate/changeset/ansisql.py +++ b/migrate/changeset/ansisql.py @@ -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() diff --git a/migrate/changeset/databases/sqlite.py b/migrate/changeset/databases/sqlite.py index 2c49b03..66a8f5a 100644 --- a/migrate/changeset/databases/sqlite.py +++ b/migrate/changeset/databases/sqlite.py @@ -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, diff --git a/migrate/changeset/schema.py b/migrate/changeset/schema.py index bf5aebe..ef8dd85 100644 --- a/migrate/changeset/schema.py +++ b/migrate/changeset/schema.py @@ -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): diff --git a/migrate/tests/changeset/test_changeset.py b/migrate/tests/changeset/test_changeset.py index 8eff822..5c06ef2 100644 --- a/migrate/tests/changeset/test_changeset.py +++ b/migrate/tests/changeset/test_changeset.py @@ -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"""