]> git.ipfire.org Git - thirdparty/sqlalchemy/alembic.git/commitdiff
- Indexes and unique constraints are now included in the
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 2 Nov 2014 23:33:51 +0000 (18:33 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 2 Nov 2014 23:33:51 +0000 (18:33 -0500)
:paramref:`.EnvironmentContext.configure.include_object` hook.
Indexes are sent with type ``"index"`` and unique constraints with
type ``"unique_constraint"``.
fixes #203

alembic/autogenerate/compare.py
alembic/environment.py
docs/build/changelog.rst
tests/test_autogen_indexes.py
tests/test_autogenerate.py

index 1a87d6673cab3859af62c7280068d07330b5049f..b60321291d5ce7cb9e79c51ae4f5da2885c4bbd3 100644 (file)
@@ -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]
index 1dfb00065c127d4d78b1e3a00666ac1e8dafc475..59718e891ebda27b9b1f6362b7fddfeda77fe55b 100644 (file)
@@ -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.
index d8b85b82514bcc1365d80dcf2714ad47ad546ce6..560cc6d8896fac5ed32a2ecc9e07ef44390c037e 100644 (file)
@@ -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
index 53c65366ccd1777e851cedf89999b19574941e30..dee4977252e8355aee3c5419381080170f02192d 100644 (file)
@@ -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)
index 1f25101da06bd897cd972dfc5d694d08961fdf20..046d9cb2649f6027b3e3981e50bb3ff02d2b5ce9 100644 (file)
@@ -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