From 09a8867edd9bf32ec594bea869cf691ba86a9f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Domen=20Ko=C5=BEar?= <domen@dev.si> Date: Sat, 5 Feb 2011 14:16:00 +0100 Subject: [PATCH 01/16] fixes #106 --- docs/changelog.rst | 3 +++ migrate/versioning/script/py.py | 13 +++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index c1a2eeb..8b1e31a 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -13,7 +13,10 @@ Fixed bugs ****************** - updated tests for Python 2.7 +- repository keyword in :func:`api.version_control` can also be unicode - added if main condition for manage.py script +- make :func:`migrate.changeset.constraint.ForeignKeyConstraint.autoname` + work with SQLAlchemy 0.5 and 0.6 - fixed case sensitivity in setup.py dependencies - moved :mod:`migrate.changeset.exceptions` and :mod:`migrate.versioning.exceptions` to :mod:`migrate.exceptions` diff --git a/migrate/versioning/script/py.py b/migrate/versioning/script/py.py index ed5b87e..5089d18 100644 --- a/migrate/versioning/script/py.py +++ b/migrate/versioning/script/py.py @@ -4,6 +4,7 @@ import shutil import warnings import logging +import inspect from StringIO import StringIO import migrate @@ -136,12 +137,12 @@ class PythonScript(base.BaseScript): funcname = base.operations[op] script_func = self._func(funcname) - try: - script_func(engine) - except TypeError: - warnings.warn("upgrade/downgrade functions must accept engine" - " parameter (since version > 0.5.4)", MigrateDeprecationWarning) - raise + # check for old way of using engine + if not inspect.getargspec(script_func).args: + raise TypeError("upgrade/downgrade functions must accept engine" + " parameter (since version 0.5.4)") + + script_func(engine) @property def module(self): From 1b37b76d2e7d8632b03161775edb68c48a504e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Domen=20Ko=C5=BEar?= <domen@dev.si> Date: Sat, 5 Feb 2011 14:25:25 +0100 Subject: [PATCH 02/16] fixes #107 --- docs/changelog.rst | 7 ++++--- migrate/versioning/genmodel.py | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 8b1e31a..f07cadb 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -20,11 +20,12 @@ Fixed bugs - fixed case sensitivity in setup.py dependencies - moved :mod:`migrate.changeset.exceptions` and :mod:`migrate.versioning.exceptions` to :mod:`migrate.exceptions` -- cleared up test output and improved testing of deprecation warnings. +- cleared up test output and improved testing of deprecation warnings. - some documentation fixes -- fixed bug with column dropping in sqlite (issue 96) +- #107: fixed syntax error in genmodel.py +- #96: fixed bug with column dropping in sqlite +- #94: fixed bug that prevented non-unique indexes being created - fixed bug with column dropping involving foreign keys -- fixed bug that prevented non-unique indexes being created (issue 94) - fixed bug when dropping columns with unique constraints in sqlite - rewrite of the schema diff internals, now supporting column differences in additon to missing columns and tables. diff --git a/migrate/versioning/genmodel.py b/migrate/versioning/genmodel.py index 5a43437..b3449b6 100644 --- a/migrate/versioning/genmodel.py +++ b/migrate/versioning/genmodel.py @@ -170,11 +170,11 @@ class ModelGenerator(object): modelTable, col.name)) for modelCol, databaseCol, modelDecl, databaseDecl in diffDecl: upgradeCommands.append( - 'assert False, "Can\'t alter columns: %s:%s=>%s"', - modelTable, modelCol.name, databaseCol.name) + 'assert False, "Can\'t alter columns: %s:%s=>%s"' % ( + modelTable, modelCol.name, databaseCol.name)) downgradeCommands.append( - 'assert False, "Can\'t alter columns: %s:%s=>%s"', - modelTable, modelCol.name, databaseCol.name) + 'assert False, "Can\'t alter columns: %s:%s=>%s"' % ( + modelTable, modelCol.name, databaseCol.name)) pre_command = ' meta.bind = migrate_engine' return ( From 7bccd681a0bbdcc82b61cd982dad061ab97d28a6 Mon Sep 17 00:00:00 2001 From: Jan Dittberner <jan.dittberner@googlemail.com> Date: Sat, 5 Feb 2011 14:27:26 +0100 Subject: [PATCH 03/16] use mirrored copy of kinterbasedb to cope with SourceForge reliability problems --- test-req.pip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-req.pip b/test-req.pip index 58011eb..46c0196 100644 --- a/test-req.pip +++ b/test-req.pip @@ -9,6 +9,6 @@ pytz http://initd.org/psycopg/tarballs/psycopg2-2.2.2.tar.gz pysqlite mysql-python -http://downloads.sourceforge.net/firebird/kinterbasdb-3.3.0.tar.bz2 +http://jenkins.gnuviech-server.de/userContent/kinterbasdb-3.3.0.tar.bz2 virtualenv unittest2 From b2db52ff4e974cd3011acc1ea126ea6dfdc6f29e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Domen=20Ko=C5=BEar?= <domen@dev.si> Date: Sat, 5 Feb 2011 18:19:37 +0100 Subject: [PATCH 04/16] fix py2.4 and py2.5 --- migrate/versioning/script/py.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrate/versioning/script/py.py b/migrate/versioning/script/py.py index 5089d18..35fe4aa 100644 --- a/migrate/versioning/script/py.py +++ b/migrate/versioning/script/py.py @@ -138,7 +138,7 @@ class PythonScript(base.BaseScript): script_func = self._func(funcname) # check for old way of using engine - if not inspect.getargspec(script_func).args: + if not inspect.getargspec(script_func)[0]: raise TypeError("upgrade/downgrade functions must accept engine" " parameter (since version 0.5.4)") From 9c9fe2fc2425e5fe251415d622eb5b6639584b37 Mon Sep 17 00:00:00 2001 From: Chris Withers <chris@simplistix.co.uk> Date: Thu, 10 Feb 2011 15:15:22 +0000 Subject: [PATCH 05/16] Only alter the SA objects after running the visitor, so the visitor may inspect --- migrate/changeset/schema.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/migrate/changeset/schema.py b/migrate/changeset/schema.py index ef8dd85..00e1a3e 100644 --- a/migrate/changeset/schema.py +++ b/migrate/changeset/schema.py @@ -559,11 +559,10 @@ populated with defaults if table is not None: self.table = table engine = self.table.bind - if self.alter_metadata: - self.remove_from_table(self.table, unset_table=False) visitorcallable = get_engine_visitor(engine, 'columndropper') engine._run_visitor(visitorcallable, self, connection, **kwargs) if self.alter_metadata: + self.remove_from_table(self.table, unset_table=False) self.table = None return self From f749919240a4cf32cc6d6c1962276191ecbd8f41 Mon Sep 17 00:00:00 2001 From: Chris Withers <chris@simplistix.co.uk> Date: Thu, 10 Feb 2011 15:16:15 +0000 Subject: [PATCH 06/16] These drop indexes appear to only be for firebird. Once firebird is fixed, they're not needed. --- migrate/tests/changeset/test_changeset.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/migrate/tests/changeset/test_changeset.py b/migrate/tests/changeset/test_changeset.py index 5c06ef2..4f4291d 100644 --- a/migrate/tests/changeset/test_changeset.py +++ b/migrate/tests/changeset/test_changeset.py @@ -262,7 +262,6 @@ class TestAddDropColumn(fixture.DB): self._check_index(False) - Index('ix_data', col).drop(bind=self.engine) col.drop() @fixture.usedb() @@ -284,7 +283,6 @@ class TestAddDropColumn(fixture.DB): self._check_index(True) - Index('ix_data', col).drop(bind=self.engine) col.drop() @fixture.usedb() From b1745bee521b36b3c2e3ca0b224f8cfdbf085400 Mon Sep 17 00:00:00 2001 From: Chris Withers <chris@simplistix.co.uk> Date: Thu, 10 Feb 2011 15:16:35 +0000 Subject: [PATCH 07/16] firebird can only drop named foreign keys --- migrate/tests/changeset/test_changeset.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/migrate/tests/changeset/test_changeset.py b/migrate/tests/changeset/test_changeset.py index 4f4291d..1218b54 100644 --- a/migrate/tests/changeset/test_changeset.py +++ b/migrate/tests/changeset/test_changeset.py @@ -422,7 +422,8 @@ class TestAddDropColumn(fixture.DB): Column('r1', Integer), Column('r2', Integer), ForeignKeyConstraint(['r1','r2'], - [reftable.c.id,reftable.c.jd]) + [reftable.c.id,reftable.c.jd], + name='test_fk') ) self.table.create() From 500cb6f5df5cbdf19b6155820e6f51e672e64649 Mon Sep 17 00:00:00 2001 From: Chris Withers <chris@simplistix.co.uk> Date: Thu, 10 Feb 2011 15:17:28 +0000 Subject: [PATCH 08/16] fix sqlite column dropper now that the table is only modified after the visitor is run --- migrate/changeset/databases/sqlite.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/migrate/changeset/databases/sqlite.py b/migrate/changeset/databases/sqlite.py index 66a8f5a..01e48ef 100644 --- a/migrate/changeset/databases/sqlite.py +++ b/migrate/changeset/databases/sqlite.py @@ -80,10 +80,19 @@ class SQLiteColumnDropper(SQLiteHelper, ansisql.ANSIColumnDropper): """SQLite ColumnDropper""" def _modify_table(self, table, column, delta): + columns = ' ,'.join(map(self.preparer.format_column, table.columns)) return 'INSERT INTO %(table_name)s SELECT ' + columns + \ ' from migration_tmp' + def visit_column(self,column): + # For SQLite, we *have* to remove the column so the table + # is re-created properly. + # This violates the alter_metadata settting, but that + # is going away... + column.remove_from_table(column.table,unset_table=False) + super(SQLiteColumnDropper,self).visit_column(column) + class SQLiteSchemaChanger(SQLiteHelper, ansisql.ANSISchemaChanger): """SQLite SchemaChanger""" From c80120da555f41a9b2eff9310b5d4bda1d0c149e Mon Sep 17 00:00:00 2001 From: Chris Withers <chris@simplistix.co.uk> Date: Thu, 10 Feb 2011 15:19:00 +0000 Subject: [PATCH 09/16] work around firebird's insistence that indexes and constraints are dropped before columns that are references by them. --- migrate/changeset/databases/firebird.py | 28 ++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/migrate/changeset/databases/firebird.py b/migrate/changeset/databases/firebird.py index f880035..b296fa5 100644 --- a/migrate/changeset/databases/firebird.py +++ b/migrate/changeset/databases/firebird.py @@ -2,7 +2,7 @@ Firebird database specific implementations of changeset classes. """ from sqlalchemy.databases import firebird as sa_base - +from sqlalchemy.schema import PrimaryKeyConstraint from migrate import exceptions from migrate.changeset import ansisql, SQLA_06 @@ -27,13 +27,27 @@ class FBColumnDropper(ansisql.ANSIColumnDropper): if column.table.primary_key.columns.contains_column(column): column.table.primary_key.drop() # TODO: recreate primary key if it references more than this column - if column.unique or getattr(column, 'unique_name', None): - for cons in column.table.constraints: - if cons.contains_column(column): - cons.drop() - # TODO: recreate unique constraint if it refenrences more than this column - table = self.start_alter_table(column) + for index in column.table.indexes: + # "column in index.columns" causes problems as all + # column objects compare equal and return a SQL expression + if column.name in [col.name for col in index.columns]: + index.drop() + # TODO: recreate index if it references more than this column + + for cons in column.table.constraints: + if isinstance(cons,PrimaryKeyConstraint): + # will be deleted only when the column its on + # is deleted! + continue + if cons.contains_column(column) and cons.name: + self.start_alter_table(column) + self.append("DROP CONSTRAINT ") + self.append(self.preparer.format_constraint(cons)) + self.execute() + # TODO: recreate unique constraint if it refenrences more than this column + + self.start_alter_table(column) self.append('DROP %s' % self.preparer.format_column(column)) self.execute() From b5a02cb7cfc1b6c032df3fed7002e968c1932162 Mon Sep 17 00:00:00 2001 From: Chris Withers <chris@simplistix.co.uk> Date: Thu, 10 Feb 2011 16:49:20 +0000 Subject: [PATCH 10/16] remove the alter_metadata feature --- docs/changelog.rst | 3 +- migrate/changeset/databases/sqlite.py | 4 +- migrate/changeset/schema.py | 87 ++++++----------------- migrate/tests/changeset/test_changeset.py | 68 +++++------------- 4 files changed, 42 insertions(+), 120 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index f07cadb..732f703 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -7,7 +7,7 @@ Features - implemented columns adding with unique constraints for sqlite - implemented adding unique and foreign key constraints to columns for sqlite - +- remove experimental `alter_metadata` parameter Fixed bugs ****************** @@ -31,6 +31,7 @@ Fixed bugs differences in additon to missing columns and tables. - fixed bug when passing empty list in :func:`migrate.versioning.shell.main` failed +- #108: Fixed issues with firebird support. 0.6 (11.07.2010) --------------------------- diff --git a/migrate/changeset/databases/sqlite.py b/migrate/changeset/databases/sqlite.py index 01e48ef..447412d 100644 --- a/migrate/changeset/databases/sqlite.py +++ b/migrate/changeset/databases/sqlite.py @@ -86,10 +86,8 @@ class SQLiteColumnDropper(SQLiteHelper, ansisql.ANSIColumnDropper): ' from migration_tmp' def visit_column(self,column): - # For SQLite, we *have* to remove the column so the table + # For SQLite, we *have* to remove the column here so the table # is re-created properly. - # This violates the alter_metadata settting, but that - # is going away... column.remove_from_table(column.table,unset_table=False) super(SQLiteColumnDropper,self).visit_column(column) diff --git a/migrate/changeset/schema.py b/migrate/changeset/schema.py index 00e1a3e..8c4f458 100644 --- a/migrate/changeset/schema.py +++ b/migrate/changeset/schema.py @@ -29,9 +29,6 @@ __all__ = [ 'ColumnDelta', ] -DEFAULT_ALTER_METADATA = True - - def create_column(column, table=None, *p, **kw): """Create a column, given the table. @@ -109,19 +106,11 @@ def alter_column(*p, **k): The :class:`~sqlalchemy.engine.base.Engine` to use for table reflection and schema alterations. - :param alter_metadata: - If `True`, which is the default, the - :class:`~sqlalchemy.schema.Column` will also modified. - If `False`, the :class:`~sqlalchemy.schema.Column` will be left - as it was. - :returns: A :class:`ColumnDelta` instance representing the change. """ - k.setdefault('alter_metadata', DEFAULT_ALTER_METADATA) - if 'table' not in k and isinstance(p[0], sqlalchemy.Column): k['table'] = p[0].table if 'engine' not in k: @@ -188,11 +177,10 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem): :param table: Table at which current Column should be bound to.\ If table name is given, reflection will be used. :type table: string or Table instance - :param alter_metadata: If True, it will apply changes to metadata. - :type alter_metadata: bool - :param metadata: If `alter_metadata` is true, \ - metadata is used to reflect table names into - :type metadata: :class:`MetaData` instance + + :param metadata: A :class:`MetaData` instance to store + reflected table names + :param engine: When reflecting tables, either engine or metadata must \ be specified to acquire engine object. :type engine: :class:`Engine` instance @@ -213,7 +201,6 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem): __visit_name__ = 'column' def __init__(self, *p, **kw): - self.alter_metadata = kw.pop("alter_metadata", False) self.meta = kw.pop("metadata", None) self.engine = kw.pop("engine", None) @@ -237,8 +224,7 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem): self.apply_diffs(diffs) def __repr__(self): - return '<ColumnDelta altermetadata=%r, %s>' % (self.alter_metadata, - super(ColumnDelta, self).__repr__()) + return '<ColumnDelta %s>' % super(ColumnDelta, self).__repr__() def __getitem__(self, key): if key not in self.keys(): @@ -314,7 +300,7 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem): self.result_column.type = self.result_column.type() # add column to the table - if self.table is not None and self.alter_metadata: + if self.table is not None: self.result_column.add_to_table(self.table) def are_column_types_eq(self, old_type, new_type): @@ -376,38 +362,27 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem): def _set_table(self, table): if isinstance(table, basestring): - if self.alter_metadata: - if not self.meta: - raise ValueError("metadata must be specified for table" - " reflection when using alter_metadata") - meta = self.meta - if self.engine: - meta.bind = self.engine - else: - if not self.engine and not self.meta: - raise ValueError("engine or metadata must be specified" - " to reflect tables") - if not self.engine: - self.engine = self.meta.bind - meta = sqlalchemy.MetaData(bind=self.engine) + if not self.engine and not self.meta: + raise ValueError("engine or metadata must be specified" + " to reflect tables") + if not self.meta: + self.meta = sqlalchemy.MetaData() + meta = self.meta + if self.engine: + meta.bind = self.engine self._table = sqlalchemy.Table(table, meta, autoload=True) elif isinstance(table, sqlalchemy.Table): self._table = table - if not self.alter_metadata: - self._table.meta = sqlalchemy.MetaData(bind=self._table.bind) def _get_result_column(self): return getattr(self, '_result_column', None) def _set_result_column(self, column): - """Set Column to Table based on alter_metadata evaluation.""" + """Set Column to Table.""" self.process_column(column) if not hasattr(self, 'current_name'): self.current_name = column.name - if self.alter_metadata: - self._result_column = column - else: - self._result_column = column.copy_fixed() + self._result_column = column table = property(_get_table, _set_table) result_column = property(_get_result_column, _set_result_column) @@ -456,22 +431,18 @@ class ChangesetTable(object): :param name: New name of the table. :type name: string - :param alter_metadata: If True, table will be removed from metadata - :type alter_metadata: bool :param connection: reuse connection istead of creating new one. :type connection: :class:`sqlalchemy.engine.base.Connection` instance """ - self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA) engine = self.bind self.new_name = name visitorcallable = get_engine_visitor(engine, 'schemachanger') run_single_visitor(engine, visitorcallable, self, connection, **kwargs) # Fix metadata registration - if self.alter_metadata: - self.name = name - self.deregister() - self._set_parent(self.metadata) + self.name = name + self.deregister() + self._set_parent(self.metadata) def _meta_key(self): return sqlalchemy.schema._get_table_key(self.name, self.schema) @@ -510,7 +481,6 @@ class ChangesetColumn(object): `~migrate.changeset.constraint.UniqueConstraint` on this column. :param primary_key_name: Creates :class:\ `~migrate.changeset.constraint.PrimaryKeyConstraint` on this column. - :param alter_metadata: If True, column will be added to table object. :param populate_default: If True, created column will be \ populated with defaults :param connection: reuse connection istead of creating new one. @@ -518,22 +488,19 @@ populated with defaults :type index_name: string :type unique_name: string :type primary_key_name: string - :type alter_metadata: bool :type populate_default: bool :type connection: :class:`sqlalchemy.engine.base.Connection` instance :returns: self """ self.populate_default = populate_default - self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA) self.index_name = index_name self.unique_name = unique_name self.primary_key_name = primary_key_name for cons in ('index_name', 'unique_name', 'primary_key_name'): self._check_sanity_constraints(cons) - if self.alter_metadata: - self.add_to_table(table) + self.add_to_table(table) engine = self.table.bind visitorcallable = get_engine_visitor(engine, 'columngenerator') engine._run_visitor(visitorcallable, self, connection, **kwargs) @@ -550,20 +517,16 @@ populated with defaults ``ALTER TABLE DROP COLUMN``, for most databases. - :param alter_metadata: If True, column will be removed from table object. - :type alter_metadata: bool :param connection: reuse connection istead of creating new one. :type connection: :class:`sqlalchemy.engine.base.Connection` instance """ - self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA) if table is not None: self.table = table engine = self.table.bind visitorcallable = get_engine_visitor(engine, 'columndropper') engine._run_visitor(visitorcallable, self, connection, **kwargs) - if self.alter_metadata: - self.remove_from_table(self.table, unset_table=False) - self.table = None + self.remove_from_table(self.table, unset_table=False) + self.table = None return self def add_to_table(self, table): @@ -642,18 +605,14 @@ class ChangesetIndex(object): :param name: New name of the Index. :type name: string - :param alter_metadata: If True, Index object will be altered. - :type alter_metadata: bool :param connection: reuse connection istead of creating new one. :type connection: :class:`sqlalchemy.engine.base.Connection` instance """ - self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA) engine = self.table.bind self.new_name = name visitorcallable = get_engine_visitor(engine, 'schemachanger') engine._run_visitor(visitorcallable, self, connection, **kwargs) - if self.alter_metadata: - self.name = name + self.name = name class ChangesetDefaultClause(object): diff --git a/migrate/tests/changeset/test_changeset.py b/migrate/tests/changeset/test_changeset.py index 1218b54..ce6fdcc 100644 --- a/migrate/tests/changeset/test_changeset.py +++ b/migrate/tests/changeset/test_changeset.py @@ -704,20 +704,6 @@ class TestColumnChange(fixture.DB): finally: cw.__exit__() - @fixture.usedb() - def test_alter_metadata(self): - """Test if alter_metadata is respected""" - - self.table.c.data.alter(type=String(100)) - - self.assert_(isinstance(self.table.c.data.type, String)) - self.assertEqual(self.table.c.data.type.length, 100) - - # nothing should change - self.table.c.data.alter(type=String(200),alter_metadata=False) - self.assert_(isinstance(self.table.c.data.type, String)) - self.assertEqual(self.table.c.data.type.length, 100) - @fixture.usedb() def test_alter_returns_delta(self): """Test if alter constructs return delta""" @@ -741,8 +727,7 @@ class TestColumnChange(fixture.DB): kw = dict(nullable=False, server_default='foobar', name='data_new', - type=String(50), - alter_metadata=True) + type=String(50)) if self.engine.name == 'firebird': del kw['nullable'] self.table.c.data.alter(**kw) @@ -805,13 +790,15 @@ class TestColumnDelta(fixture.DB): def test_deltas_two_columns(self): """Testing ColumnDelta with two columns""" - col_orig = self.mkcol(primary_key=True) - col_new = self.mkcol(name='ids', primary_key=True) - self.verify([], col_orig, col_orig) - self.verify(['name'], col_orig, col_orig, 'ids') - self.verify(['name'], col_orig, col_orig, name='ids') - self.verify(['name'], col_orig, col_new) - self.verify(['name', 'type'], col_orig, col_new, type=String) + col_orig = lambda :self.mkcol(primary_key=True) + col_new = lambda: self.mkcol(name='ids', primary_key=True) + # we have to create new columns, since comparing the ColumnDelta + # will apply differences + self.verify([], col_orig(), col_orig()) + self.verify(['name'], col_orig(), col_orig(), 'ids') + self.verify(['name'], col_orig(), col_orig(), name='ids') + self.verify(['name'], col_orig(), col_new()) + self.verify(['name', 'type'], col_orig(), col_new(), type=String) # Type comparisons self.verify([], self.mkcol(type=String), self.mkcol(type=String)) @@ -839,23 +826,16 @@ class TestColumnDelta(fixture.DB): self.verify([], self.mkcol(server_default='foobar'), self.mkcol('id', String, DefaultClause('foobar'))) self.verify(['type'], self.mkcol(server_default='foobar'), self.mkcol('id', Text, DefaultClause('foobar'))) - # test alter_metadata col = self.mkcol(server_default='foobar') - self.verify(['type'], col, self.mkcol('id', Text, DefaultClause('foobar')), alter_metadata=True) + self.verify(['type'], col, self.mkcol('id', Text, DefaultClause('foobar'))) self.assert_(isinstance(col.type, Text)) col = self.mkcol() - self.verify(['name', 'server_default', 'type'], col, self.mkcol('beep', Text, DefaultClause('foobar')), alter_metadata=True) + self.verify(['name', 'server_default', 'type'], col, self.mkcol('beep', Text, DefaultClause('foobar'))) self.assert_(isinstance(col.type, Text)) self.assertEqual(col.name, 'beep') self.assertEqual(col.server_default.arg, 'foobar') - col = self.mkcol() - self.verify(['name', 'server_default', 'type'], col, self.mkcol('beep', Text, DefaultClause('foobar')), alter_metadata=False) - self.assertFalse(isinstance(col.type, Text)) - self.assertNotEqual(col.name, 'beep') - self.assertFalse(col.server_default) - @fixture.usedb() def test_deltas_zero_columns(self): """Testing ColumnDelta with zero columns""" @@ -866,30 +846,20 @@ class TestColumnDelta(fixture.DB): self.verify(['type'], 'ids', table=self.table.name, type=String(80), engine=self.engine) self.verify(['type'], 'ids', table=self.table.name, type=String(80), metadata=self.meta) - # check if alter_metadata is respected self.meta.clear() - delta = self.verify(['type'], 'ids', table=self.table.name, type=String(80), alter_metadata=True, metadata=self.meta) + delta = self.verify(['type'], 'ids', table=self.table.name, type=String(80), metadata=self.meta) self.assert_(self.table.name in self.meta) self.assertEqual(delta.result_column.type.length, 80) self.assertEqual(self.meta.tables.get(self.table.name).c.ids.type.length, 80) - self.meta.clear() - self.verify(['type'], 'ids', table=self.table.name, type=String(80), alter_metadata=False, engine=self.engine) - self.assert_(self.table.name not in self.meta) - - self.meta.clear() - self.verify(['type'], 'ids', table=self.table.name, type=String(80), alter_metadata=False, metadata=self.meta) - self.assert_(self.table.name not in self.meta) - # test defaults self.meta.clear() - self.verify(['server_default'], 'ids', table=self.table.name, server_default='foobar', alter_metadata=True, metadata=self.meta) + self.verify(['server_default'], 'ids', table=self.table.name, server_default='foobar', metadata=self.meta) self.meta.tables.get(self.table.name).c.ids.server_default.arg == 'foobar' # test missing parameters self.assertRaises(ValueError, ColumnDelta, table=self.table.name) - self.assertRaises(ValueError, ColumnDelta, 'ids', table=self.table.name, alter_metadata=True) - self.assertRaises(ValueError, ColumnDelta, 'ids', table=self.table.name, alter_metadata=False) + self.assertRaises(ValueError, ColumnDelta, 'ids', table=self.table.name) def test_deltas_one_column(self): """Testing ColumnDelta with one column""" @@ -907,17 +877,11 @@ class TestColumnDelta(fixture.DB): self.assertEquals(delta.get('name'), 'blah') self.assertEquals(delta.current_name, 'id') - # check if alter_metadata is respected col_orig = self.mkcol(primary_key=True) - self.verify(['name', 'type'], col_orig, name='id12', type=Text, alter_metadata=True) + self.verify(['name', 'type'], col_orig, name='id12', type=Text) self.assert_(isinstance(col_orig.type, Text)) self.assertEqual(col_orig.name, 'id12') - col_orig = self.mkcol(primary_key=True) - self.verify(['name', 'type'], col_orig, name='id12', type=Text, alter_metadata=False) - self.assert_(isinstance(col_orig.type, String)) - self.assertEqual(col_orig.name, 'id') - # test server default col_orig = self.mkcol(primary_key=True) delta = self.verify(['server_default'], col_orig, DefaultClause('foobar')) From cda65c38b53ec0a0ffca0bcd30c675386e24c450 Mon Sep 17 00:00:00 2001 From: Chris Withers <chris@simplistix.co.uk> Date: Thu, 10 Feb 2011 17:02:42 +0000 Subject: [PATCH 11/16] try to get firebird stuff working with 0.6.6 --- migrate/changeset/databases/firebird.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/migrate/changeset/databases/firebird.py b/migrate/changeset/databases/firebird.py index b296fa5..675666c 100644 --- a/migrate/changeset/databases/firebird.py +++ b/migrate/changeset/databases/firebird.py @@ -40,7 +40,12 @@ class FBColumnDropper(ansisql.ANSIColumnDropper): # will be deleted only when the column its on # is deleted! continue - if cons.contains_column(column) and cons.name: + + if SQLA_06: + should_drop = column.name in cons.columns + else: + should_drop = cons.contains_column(column) and cons.name + if should_drop: self.start_alter_table(column) self.append("DROP CONSTRAINT ") self.append(self.preparer.format_constraint(cons)) From 0645c2fc9a052d8ef52b4120c7383aa6c6cf5b6f Mon Sep 17 00:00:00 2001 From: Chris Withers <chris@simplistix.co.uk> Date: Fri, 11 Feb 2011 10:27:39 +0000 Subject: [PATCH 12/16] Bring back alter_metadata on ColumnDelta: it seems intertwined with a lot of the tests. So, it's a private API now... --- migrate/changeset/schema.py | 52 +++++++++++++++++------ migrate/tests/changeset/test_changeset.py | 33 +++++++------- 2 files changed, 56 insertions(+), 29 deletions(-) diff --git a/migrate/changeset/schema.py b/migrate/changeset/schema.py index 8c4f458..75cd3fd 100644 --- a/migrate/changeset/schema.py +++ b/migrate/changeset/schema.py @@ -124,6 +124,12 @@ def alter_column(*p, **k): MigrateDeprecationWarning ) engine = k['engine'] + + # enough tests seem to break when metadata is always altered + # that this crutch has to be left in until they can be sorted + # out + k['alter_metadata']=True + delta = ColumnDelta(*p, **k) visitorcallable = get_engine_visitor(engine, 'schemachanger') @@ -201,6 +207,11 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem): __visit_name__ = 'column' def __init__(self, *p, **kw): + # 'alter_metadata' is not a public api. It exists purely + # as a crutch until the tests that fail when 'alter_metadata' + # behaviour always happens can be sorted out + self.alter_metadata = kw.pop("alter_metadata", False) + self.meta = kw.pop("metadata", None) self.engine = kw.pop("engine", None) @@ -224,8 +235,11 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem): self.apply_diffs(diffs) def __repr__(self): - return '<ColumnDelta %s>' % super(ColumnDelta, self).__repr__() - + return '<ColumnDelta altermetadata=%r, %s>' % ( + self.alter_metadata, + super(ColumnDelta, self).__repr__() + ) + def __getitem__(self, key): if key not in self.keys(): raise KeyError("No such diff key, available: %s" % self.diffs ) @@ -300,7 +314,7 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem): self.result_column.type = self.result_column.type() # add column to the table - if self.table is not None: + if self.table is not None and self.alter_metadata: self.result_column.add_to_table(self.table) def are_column_types_eq(self, old_type, new_type): @@ -362,27 +376,37 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem): def _set_table(self, table): if isinstance(table, basestring): - if not self.engine and not self.meta: - raise ValueError("engine or metadata must be specified" - " to reflect tables") - if not self.meta: - self.meta = sqlalchemy.MetaData() - meta = self.meta - if self.engine: - meta.bind = self.engine + if self.alter_metadata: + if not self.meta: + raise ValueError("metadata must be specified for table" + " reflection when using alter_metadata") + meta = self.meta + if self.engine: + meta.bind = self.engine + else: + if not self.engine and not self.meta: + raise ValueError("engine or metadata must be specified" + " to reflect tables") + if not self.engine: + self.engine = self.meta.bind + meta = sqlalchemy.MetaData(bind=self.engine) self._table = sqlalchemy.Table(table, meta, autoload=True) elif isinstance(table, sqlalchemy.Table): self._table = table - + if not self.alter_metadata: + self._table.meta = sqlalchemy.MetaData(bind=self._table.bind) def _get_result_column(self): return getattr(self, '_result_column', None) def _set_result_column(self, column): - """Set Column to Table.""" + """Set Column to Table based on alter_metadata evaluation.""" self.process_column(column) if not hasattr(self, 'current_name'): self.current_name = column.name - self._result_column = column + if self.alter_metadata: + self._result_column = column + else: + self._result_column = column.copy_fixed() table = property(_get_table, _set_table) result_column = property(_get_result_column, _set_result_column) diff --git a/migrate/tests/changeset/test_changeset.py b/migrate/tests/changeset/test_changeset.py index ce6fdcc..2b50922 100644 --- a/migrate/tests/changeset/test_changeset.py +++ b/migrate/tests/changeset/test_changeset.py @@ -790,15 +790,13 @@ class TestColumnDelta(fixture.DB): def test_deltas_two_columns(self): """Testing ColumnDelta with two columns""" - col_orig = lambda :self.mkcol(primary_key=True) - col_new = lambda: self.mkcol(name='ids', primary_key=True) - # we have to create new columns, since comparing the ColumnDelta - # will apply differences - self.verify([], col_orig(), col_orig()) - self.verify(['name'], col_orig(), col_orig(), 'ids') - self.verify(['name'], col_orig(), col_orig(), name='ids') - self.verify(['name'], col_orig(), col_new()) - self.verify(['name', 'type'], col_orig(), col_new(), type=String) + col_orig = self.mkcol(primary_key=True) + col_new = self.mkcol(name='ids', primary_key=True) + self.verify([], col_orig, col_orig) + self.verify(['name'], col_orig, col_orig, 'ids') + self.verify(['name'], col_orig, col_orig, name='ids') + self.verify(['name'], col_orig, col_new) + self.verify(['name', 'type'], col_orig, col_new, type=String) # Type comparisons self.verify([], self.mkcol(type=String), self.mkcol(type=String)) @@ -827,11 +825,12 @@ class TestColumnDelta(fixture.DB): self.verify(['type'], self.mkcol(server_default='foobar'), self.mkcol('id', Text, DefaultClause('foobar'))) col = self.mkcol(server_default='foobar') - self.verify(['type'], col, self.mkcol('id', Text, DefaultClause('foobar'))) + self.verify(['type'], col, self.mkcol('id', Text, DefaultClause('foobar')), alter_metadata=True) self.assert_(isinstance(col.type, Text)) col = self.mkcol() - self.verify(['name', 'server_default', 'type'], col, self.mkcol('beep', Text, DefaultClause('foobar'))) + self.verify(['name', 'server_default', 'type'], col, self.mkcol('beep', Text, DefaultClause('foobar')), + alter_metadata=True) self.assert_(isinstance(col.type, Text)) self.assertEqual(col.name, 'beep') self.assertEqual(col.server_default.arg, 'foobar') @@ -847,19 +846,23 @@ class TestColumnDelta(fixture.DB): self.verify(['type'], 'ids', table=self.table.name, type=String(80), metadata=self.meta) self.meta.clear() - delta = self.verify(['type'], 'ids', table=self.table.name, type=String(80), metadata=self.meta) + delta = self.verify(['type'], 'ids', table=self.table.name, type=String(80), metadata=self.meta, + alter_metadata=True) self.assert_(self.table.name in self.meta) self.assertEqual(delta.result_column.type.length, 80) self.assertEqual(self.meta.tables.get(self.table.name).c.ids.type.length, 80) # test defaults self.meta.clear() - self.verify(['server_default'], 'ids', table=self.table.name, server_default='foobar', metadata=self.meta) + self.verify(['server_default'], 'ids', table=self.table.name, server_default='foobar', + metadata=self.meta, + alter_metadata=True) self.meta.tables.get(self.table.name).c.ids.server_default.arg == 'foobar' # test missing parameters self.assertRaises(ValueError, ColumnDelta, table=self.table.name) - self.assertRaises(ValueError, ColumnDelta, 'ids', table=self.table.name) + self.assertRaises(ValueError, ColumnDelta, 'ids', table=self.table.name, alter_metadata=True) + self.assertRaises(ValueError, ColumnDelta, 'ids', table=self.table.name, alter_metadata=False) def test_deltas_one_column(self): """Testing ColumnDelta with one column""" @@ -878,7 +881,7 @@ class TestColumnDelta(fixture.DB): self.assertEquals(delta.current_name, 'id') col_orig = self.mkcol(primary_key=True) - self.verify(['name', 'type'], col_orig, name='id12', type=Text) + self.verify(['name', 'type'], col_orig, name='id12', type=Text, alter_metadata=True) self.assert_(isinstance(col_orig.type, Text)) self.assertEqual(col_orig.name, 'id12') From 45c37c9188dcdc4b014b7a46ff94222c6b8979d3 Mon Sep 17 00:00:00 2001 From: Jan Dittberner <jan.dittberner@googlemail.com> Date: Fri, 11 Feb 2011 21:11:17 +0100 Subject: [PATCH 13/16] finalize changelog for 0.6.1 --- docs/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 732f703..24d09bf 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,4 +1,4 @@ -0.6.1 (xxxxxxx) +0.6.1 (2011-02-11) --------------------------- Features From cd653ddfe25410cfc319eb34f4b36a46e6d8361c Mon Sep 17 00:00:00 2001 From: Jan Dittberner <jan.dittberner@googlemail.com> Date: Fri, 11 Feb 2011 21:12:15 +0100 Subject: [PATCH 14/16] Added tag v0.6.1 for changeset c2526dce0768 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index f0a7f11..a6e8fed 100644 --- a/.hgtags +++ b/.hgtags @@ -1 +1,2 @@ cb01bf174b05b1590258d6c996b89f60ebd88e5a v0.6 +c2526dce0768f11e6bf88afb641a6a9058fa685c v0.6.1 From 21bbca280a9e5e24661b0e3526e842ac6ce56eec Mon Sep 17 00:00:00 2001 From: Jan Dittberner <jan.dittberner@googlemail.com> Date: Fri, 11 Feb 2011 21:30:04 +0100 Subject: [PATCH 15/16] update version in docs/conf.py --- docs/conf.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index e49fcce..be9220d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,16 +47,16 @@ master_doc = 'index' # General information about the project. project = u'SQLAlchemy Migrate' -copyright = u'2010, Evan Rosson, Jan Dittberner, Domen Kožar' +copyright = u'2011, Evan Rosson, Jan Dittberner, Domen Kožar, Chris Withers' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '0.6' +version = '0.6.1' # The full version, including alpha/beta/rc tags. -release = '0.6' +release = '0.6.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 4e601ff4054b89f71fb914324e2aa8888084e7e9 Mon Sep 17 00:00:00 2001 From: Jan Dittberner <jan.dittberner@googlemail.com> Date: Fri, 11 Feb 2011 23:25:18 +0100 Subject: [PATCH 16/16] start next iteration --- docs/changelog.rst | 3 +++ docs/conf.py | 4 ++-- setup.py | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 24d09bf..7bbb9a8 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,3 +1,6 @@ +0.6.2 (XXXX-XX-XX) +--------------------------- + 0.6.1 (2011-02-11) --------------------------- diff --git a/docs/conf.py b/docs/conf.py index be9220d..b8346bb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -54,9 +54,9 @@ copyright = u'2011, Evan Rosson, Jan Dittberner, Domen Kožar, Chris Withers' # built documents. # # The short X.Y version. -version = '0.6.1' +version = '0.6.2' # The full version, including alpha/beta/rc tags. -release = '0.6.1' +release = '0.6.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index c0b6f6b..a2daa11 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ readme_file = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), setup( name = "sqlalchemy-migrate", - version = "0.6.1", + version = "0.6.2", packages = find_packages(exclude=["migrate.tests*"]), include_package_data = True, description = "Database schema migration for SQLAlchemy",