From: Mike Bayer Date: Sun, 2 Nov 2014 23:33:51 +0000 (-0500) Subject: - Indexes and unique constraints are now included in the X-Git-Tag: rel_0_7_1~11^2~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e7d17fb89bf3c0f4a8ebc72947765db62e9ab0f1;p=thirdparty%2Fsqlalchemy%2Falembic.git - Indexes and unique constraints are now included in the :paramref:`.EnvironmentContext.configure.include_object` hook. Indexes are sent with type ``"index"`` and unique constraints with type ``"unique_constraint"``. fixes #203 --- diff --git a/alembic/autogenerate/compare.py b/alembic/autogenerate/compare.py index 1a87d667..b6032129 100644 --- a/alembic/autogenerate/compare.py +++ b/alembic/autogenerate/compare.py @@ -331,12 +331,13 @@ def _compare_indexes_and_uniques(schema, tname, object_filters, conn_table, def obj_added(obj): if obj.is_index: - diffs.append(("add_index", obj.const)) - log.info("Detected added index '%s' on %s", - obj.name, ', '.join([ - "'%s'" % obj.column_names - ]) - ) + if _run_filters( + obj.const, obj.name, "index", False, None, object_filters): + diffs.append(("add_index", obj.const)) + log.info("Detected added index '%s' on %s", + obj.name, ', '.join([ + "'%s'" % obj.column_names + ])) else: if not supports_unique_constraints: # can't report unique indexes as added if we don't @@ -345,12 +346,14 @@ def _compare_indexes_and_uniques(schema, tname, object_filters, conn_table, if is_create_table: # unique constraints are created inline with table defs return - diffs.append(("add_constraint", obj.const)) - log.info("Detected added unique constraint '%s' on %s", - obj.name, ', '.join([ - "'%s'" % obj.column_names - ]) - ) + if _run_filters( + obj.const, obj.name, + "unique_constraint", False, None, object_filters): + diffs.append(("add_constraint", obj.const)) + log.info("Detected added unique constraint '%s' on %s", + obj.name, ', '.join([ + "'%s'" % obj.column_names + ])) def obj_removed(obj): if obj.is_index: @@ -360,27 +363,39 @@ def _compare_indexes_and_uniques(schema, tname, object_filters, conn_table, # be sure what we're doing here return - diffs.append(("remove_index", obj.const)) - log.info("Detected removed index '%s' on '%s'", obj.name, tname) + if _run_filters( + obj.const, obj.name, "index", True, None, object_filters): + diffs.append(("remove_index", obj.const)) + log.info( + "Detected removed index '%s' on '%s'", obj.name, tname) else: - diffs.append(("remove_constraint", obj.const)) - log.info("Detected removed unique constraint '%s' on '%s'", - obj.name, tname - ) + if _run_filters( + obj.const, obj.name, + "unique_constraint", True, None, object_filters): + diffs.append(("remove_constraint", obj.const)) + log.info("Detected removed unique constraint '%s' on '%s'", + obj.name, tname + ) def obj_changed(old, new, msg): if old.is_index: - log.info("Detected changed index '%s' on '%s':%s", - old.name, tname, ', '.join(msg) - ) - diffs.append(("remove_index", old.const)) - diffs.append(("add_index", new.const)) + if _run_filters( + new.const, new.name, "index", + False, old.const, object_filters): + log.info("Detected changed index '%s' on '%s':%s", + old.name, tname, ', '.join(msg) + ) + diffs.append(("remove_index", old.const)) + diffs.append(("add_index", new.const)) else: - log.info("Detected changed unique constraint '%s' on '%s':%s", - old.name, tname, ', '.join(msg) - ) - diffs.append(("remove_constraint", old.const)) - diffs.append(("add_constraint", new.const)) + if _run_filters( + new.const, new.name, + "unique_constraint", False, old.const, object_filters): + log.info("Detected changed unique constraint '%s' on '%s':%s", + old.name, tname, ', '.join(msg) + ) + diffs.append(("remove_constraint", old.const)) + diffs.append(("add_constraint", new.const)) for added_name in sorted(set(metadata_names).difference(conn_names)): obj = metadata_names[added_name] diff --git a/alembic/environment.py b/alembic/environment.py index 1dfb0006..59718e89 100644 --- a/alembic/environment.py +++ b/alembic/environment.py @@ -464,12 +464,19 @@ class EnvironmentContext(object): The function accepts the following positional arguments: * ``object``: a :class:`~sqlalchemy.schema.SchemaItem` object such - as a :class:`~sqlalchemy.schema.Table` or - :class:`~sqlalchemy.schema.Column` object + as a :class:`~sqlalchemy.schema.Table`, + :class:`~sqlalchemy.schema.Column`, + :class:`~sqlalchemy.schema.Index` + or :class:`~sqlalchemy.schema.UniqueConstraint` object * ``name``: the name of the object. This is typically available via ``object.name``. * ``type``: a string describing the type of object; currently - ``"table"`` or ``"column"`` + ``"table"``, ``"column"``, ``"index"`` or ``"unique_constraint"``. + + .. versionadded:: 0.7.0 Support for indexes and unique constraints + within the + :paramref:`~.EnvironmentContext.configure.include_object` hook. + * ``reflected``: ``True`` if the given object was produced based on table reflection, ``False`` if it's from a local :class:`.MetaData` object. diff --git a/docs/build/changelog.rst b/docs/build/changelog.rst index d8b85b82..560cc6d8 100644 --- a/docs/build/changelog.rst +++ b/docs/build/changelog.rst @@ -5,6 +5,15 @@ Changelog .. changelog:: :version: 0.7.0 + .. change:: + :tags: feature, autogenerate + :tickets: 203 + + Indexes and unique constraints are now included in the + :paramref:`.EnvironmentContext.configure.include_object` hook. + Indexes are sent with type ``"index"`` and unique constraints with + type ``"unique_constraint"``. + .. change:: :tags: bug, autogenerate :tickets: 219 diff --git a/tests/test_autogen_indexes.py b/tests/test_autogen_indexes.py index 53c65366..dee49772 100644 --- a/tests/test_autogen_indexes.py +++ b/tests/test_autogen_indexes.py @@ -663,3 +663,161 @@ class NoUqReportsIndAsUqTest(NoUqReflectionIndexTest): eng.dialect.get_unique_constraints = unimpl eng.dialect.get_indexes = get_indexes return eng + + +class IncludeHooksTest(AutogenFixtureTest, TestBase): + __backend__ = True + + def test_remove_connection_index(self): + m1 = MetaData() + m2 = MetaData() + + t1 = Table('t', m1, Column('x', Integer)) + Index('ix1', t1.c.x) + Index('ix2', t1.c.x) + + Table('t', m2, Column('x', Integer)) + + def include_object(object_, name, type_, reflected, compare_to): + if type_ == 'unique_constraint': + return False + return not ( + isinstance(object_, Index) and + type_ == 'index' and reflected and name == 'ix1') + + diffs = self._fixture(m1, m2, object_filters=[include_object]) + + eq_(diffs[0][0], 'remove_index') + eq_(diffs[0][1].name, 'ix2') + eq_(len(diffs), 1) + + def test_remove_connection_uq(self): + m1 = MetaData() + m2 = MetaData() + + Table( + 't', m1, Column('x', Integer), Column('y', Integer), + UniqueConstraint('x', name='uq1'), + UniqueConstraint('y', name='uq2'), + ) + + Table('t', m2, Column('x', Integer), Column('y', Integer)) + + def include_object(object_, name, type_, reflected, compare_to): + if type_ == 'index': + return False + return not ( + isinstance(object_, UniqueConstraint) and + type_ == 'unique_constraint' and reflected and name == 'uq1') + + diffs = self._fixture(m1, m2, object_filters=[include_object]) + + eq_(diffs[0][0], 'remove_constraint') + eq_(diffs[0][1].name, 'uq2') + eq_(len(diffs), 1) + + def test_add_metadata_index(self): + m1 = MetaData() + m2 = MetaData() + + Table('t', m1, Column('x', Integer)) + + t2 = Table('t', m2, Column('x', Integer)) + Index('ix1', t2.c.x) + Index('ix2', t2.c.x) + + def include_object(object_, name, type_, reflected, compare_to): + return not ( + isinstance(object_, Index) and + type_ == 'index' and not reflected and name == 'ix1') + + diffs = self._fixture(m1, m2, object_filters=[include_object]) + + eq_(diffs[0][0], 'add_index') + eq_(diffs[0][1].name, 'ix2') + eq_(len(diffs), 1) + + def test_add_metadata_unique(self): + m1 = MetaData() + m2 = MetaData() + + Table('t', m1, Column('x', Integer)) + + Table( + 't', m2, Column('x', Integer), + UniqueConstraint('x', name='uq1'), + UniqueConstraint('x', name='uq2') + ) + + def include_object(object_, name, type_, reflected, compare_to): + return not ( + isinstance(object_, UniqueConstraint) and + type_ == 'unique_constraint' and + not reflected and name == 'uq1') + + diffs = self._fixture(m1, m2, object_filters=[include_object]) + + eq_(diffs[0][0], 'add_constraint') + eq_(diffs[0][1].name, 'uq2') + eq_(len(diffs), 1) + + def test_change_index(self): + m1 = MetaData() + m2 = MetaData() + + t1 = Table('t', m1, Column('x', Integer), Column('y', Integer)) + Index('ix1', t1.c.x) + Index('ix2', t1.c.x) + + t2 = Table('t', m2, Column('x', Integer), Column('y', Integer)) + Index('ix1', t2.c.x, t2.c.y) + Index('ix2', t2.c.x, t2.c.y) + + def include_object(object_, name, type_, reflected, compare_to): + return not ( + isinstance(object_, Index) and + type_ == 'index' and not reflected and name == 'ix1' + and isinstance(compare_to, Index)) + + diffs = self._fixture(m1, m2, object_filters=[include_object]) + + eq_(diffs[0][0], 'remove_index') + eq_(diffs[0][1].name, 'ix2') + eq_(diffs[1][0], 'add_index') + eq_(diffs[1][1].name, 'ix2') + eq_(len(diffs), 2) + + def test_change_unique(self): + m1 = MetaData() + m2 = MetaData() + + Table( + 't', m1, Column('x', Integer), + Column('y', Integer), Column('z', Integer), + UniqueConstraint('x', name='uq1'), + UniqueConstraint('y', name='uq2') + ) + + Table( + 't', m2, Column('x', Integer), Column('y', Integer), + Column('z', Integer), + UniqueConstraint('x', 'z', name='uq1'), + UniqueConstraint('y', 'z', name='uq2') + ) + + def include_object(object_, name, type_, reflected, compare_to): + if type_ == 'index': + return False + return not ( + isinstance(object_, UniqueConstraint) and + type_ == 'unique_constraint' and + not reflected and name == 'uq1' + and isinstance(compare_to, UniqueConstraint)) + + diffs = self._fixture(m1, m2, object_filters=[include_object]) + + eq_(diffs[0][0], 'remove_constraint') + eq_(diffs[0][1].name, 'uq2') + eq_(diffs[1][0], 'add_constraint') + eq_(diffs[1][1].name, 'uq2') + eq_(len(diffs), 2) diff --git a/tests/test_autogenerate.py b/tests/test_autogenerate.py index 1f25101d..046d9cb2 100644 --- a/tests/test_autogenerate.py +++ b/tests/test_autogenerate.py @@ -54,8 +54,7 @@ class AutogenTest(object): cls.m2 = cls._get_model_schema() conn = cls.bind.connect() - opts = cls.configure_opts.copy() - opts.update({ + ctx_opts = { 'compare_type': True, 'compare_server_default': True, 'target_metadata': cls.m2, @@ -63,10 +62,12 @@ class AutogenTest(object): 'downgrade_token': "downgrades", 'alembic_module_prefix': 'op.', 'sqlalchemy_module_prefix': 'sa.', - }) + } + if cls.configure_opts: + ctx_opts.update(cls.configure_opts) cls.context = context = MigrationContext.configure( connection=conn, - opts=opts + opts=ctx_opts ) connection = context.bind @@ -85,22 +86,27 @@ class AutogenTest(object): class AutogenFixtureTest(object): - def _fixture(self, m1, m2, include_schemas=False): + def _fixture( + self, m1, m2, include_schemas=False, + opts=None, object_filters=_default_object_filters): self.metadata, model_metadata = m1, m2 self.metadata.create_all(self.bind) with self.bind.connect() as conn: + ctx_opts = { + 'compare_type': True, + 'compare_server_default': True, + 'target_metadata': model_metadata, + 'upgrade_token': "upgrades", + 'downgrade_token': "downgrades", + 'alembic_module_prefix': 'op.', + 'sqlalchemy_module_prefix': 'sa.', + } + if opts: + ctx_opts.update(opts) self.context = context = MigrationContext.configure( connection=conn, - opts={ - 'compare_type': True, - 'compare_server_default': True, - 'target_metadata': model_metadata, - 'upgrade_token': "upgrades", - 'downgrade_token': "downgrades", - 'alembic_module_prefix': 'op.', - 'sqlalchemy_module_prefix': 'sa.', - } + opts=ctx_opts ) connection = context.bind @@ -114,7 +120,7 @@ class AutogenFixtureTest(object): autogenerate._produce_net_changes( connection, model_metadata, diffs, autogen_context, - object_filters=_default_object_filters, + object_filters=object_filters, include_schemas=include_schemas ) return diffs