]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed bug in :class:`.Enum` and other :class:`.SchemaType`
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 13 Jul 2014 22:55:18 +0000 (18:55 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 13 Jul 2014 22:56:52 +0000 (18:56 -0400)
subclasses where direct association of the type with a
:class:`.MetaData` would lead to a hang when events
(like create events) were emitted on the :class:`.MetaData`.
fixes #3124

Conflicts:
test/sql/test_types.py

doc/build/changelog/changelog_08.rst
lib/sqlalchemy/dialects/postgresql/base.py
lib/sqlalchemy/sql/sqltypes.py
test/sql/test_metadata.py
test/sql/test_types.py

index 4b036272bb59cdbfaa2c93bbcb0c326bc2371cbd..01590b09024ab5c8df70c94394ff7a4ff744e28c 100644 (file)
 .. changelog::
     :version: 0.8.7
 
+    .. change::
+        :tags: bug, sql
+        :versions: 1.0.0, 0.9.7
+        :tickets: 3124
+
+        Fixed bug in :class:`.Enum` and other :class:`.SchemaType`
+        subclasses where direct association of the type with a
+        :class:`.MetaData` would lead to a hang when events
+        (like create events) were emitted on the :class:`.MetaData`.
+
     .. change::
         :tags: bug, sql
         :versions: 1.0.0, 0.9.7
index 33991a2c792d28bd185484d19f25d1273828be8b..4419250dbbbf7948bf3d9a56bb6168b41d9fdd47 100644 (file)
@@ -1100,8 +1100,7 @@ class ENUM(sqltypes.Enum):
             self.create(bind=bind, checkfirst=checkfirst)
 
     def _on_metadata_create(self, target, bind, checkfirst, **kw):
-        if self.metadata is not None and \
-            not self._check_for_name_in_memos(checkfirst, kw):
+        if not self._check_for_name_in_memos(checkfirst, kw):
             self.create(bind=bind, checkfirst=checkfirst)
 
     def _on_metadata_drop(self, target, bind, checkfirst, **kw):
index 671ea1b703fd3e2cbddbf7f62aaf0022de46a21a..b4d2d23901a2cc4588ff0e4ef5815dc0213ebb8e 100644 (file)
@@ -918,6 +918,7 @@ class SchemaType(SchemaEventTarget):
         self.schema = schema
         self.metadata = metadata
         self.inherit_schema = inherit_schema
+
         if self.metadata:
             event.listen(
                 self.metadata,
@@ -967,13 +968,16 @@ class SchemaType(SchemaEventTarget):
 
     def adapt(self, impltype, **kw):
         schema = kw.pop('schema', self.schema)
-        metadata = kw.pop('metadata', self.metadata)
+
+        # don't associate with MetaData as the hosting type
+        # is already associated with it, avoid creating event
+        # listeners
+        metadata = kw.pop('metadata', None)
         return impltype(name=self.name,
                     schema=schema,
                     metadata=metadata,
                     inherit_schema=self.inherit_schema,
-                    **kw
-                    )
+                    **kw)
 
     @property
     def bind(self):
@@ -1136,7 +1140,7 @@ class Enum(String, SchemaType):
 
     def adapt(self, impltype, **kw):
         schema = kw.pop('schema', self.schema)
-        metadata = kw.pop('metadata', self.metadata)
+        metadata = kw.pop('metadata', None)
         if issubclass(impltype, Enum):
             return impltype(name=self.name,
                         schema=schema,
@@ -1145,8 +1149,7 @@ class Enum(String, SchemaType):
                         native_enum=self.native_enum,
                         inherit_schema=self.inherit_schema,
                         *self.enums,
-                        **kw
-                        )
+                        **kw)
         else:
             return super(Enum, self).adapt(impltype, **kw)
 
index 02d8e65ed7afb88fc7f8b31c5b5b59706c777b94..7711db816e38c87f2caed94020fb5833c6a65b41 100644 (file)
@@ -1083,8 +1083,22 @@ class SchemaTypeTest(fixtures.TestBase):
             self.table = table
 
         def _on_table_create(self, target, bind, **kw):
+            super(SchemaTypeTest.MyType, self)._on_table_create(
+                target, bind, **kw)
             self.evt_targets += (target,)
 
+        def _on_metadata_create(self, target, bind, **kw):
+            super(SchemaTypeTest.MyType, self)._on_metadata_create(
+                target, bind, **kw)
+            self.evt_targets += (target,)
+
+    class MyTypeWImpl(MyType):
+        def _gen_dialect_impl(self, dialect):
+            return self.adapt(SchemaTypeTest.MyTypeImpl)
+
+    class MyTypeImpl(MyTypeWImpl):
+        pass
+
     def test_independent_schema(self):
         m = MetaData()
         type_ = self.MyType(schema="q")
@@ -1165,6 +1179,49 @@ class SchemaTypeTest(fixtures.TestBase):
         eq_(t1.c.y.type.evt_targets, (t1,))
         eq_(t2.c.y.type.evt_targets, (t2, t2))
 
+    def test_metadata_dispatch_no_new_impl(self):
+        m1 = MetaData()
+        typ = self.MyType(metadata=m1)
+        m1.dispatch.before_create(m1, testing.db)
+        eq_(typ.evt_targets, (m1, ))
+
+        dialect_impl = typ.dialect_impl(testing.db.dialect)
+        eq_(dialect_impl.evt_targets, ())
+
+    def test_metadata_dispatch_new_impl(self):
+        m1 = MetaData()
+        typ = self.MyTypeWImpl(metadata=m1)
+        m1.dispatch.before_create(m1, testing.db)
+        eq_(typ.evt_targets, (m1, ))
+
+        dialect_impl = typ.dialect_impl(testing.db.dialect)
+        eq_(dialect_impl.evt_targets, (m1, ))
+
+    def test_table_dispatch_no_new_impl(self):
+        m1 = MetaData()
+        typ = self.MyType()
+        t1 = Table('t1', m1, Column('x', typ))
+        m1.dispatch.before_create(t1, testing.db)
+        eq_(typ.evt_targets, (t1, ))
+
+        dialect_impl = typ.dialect_impl(testing.db.dialect)
+        eq_(dialect_impl.evt_targets, ())
+
+    def test_table_dispatch_new_impl(self):
+        m1 = MetaData()
+        typ = self.MyTypeWImpl()
+        t1 = Table('t1', m1, Column('x', typ))
+        m1.dispatch.before_create(t1, testing.db)
+        eq_(typ.evt_targets, (t1, ))
+
+        dialect_impl = typ.dialect_impl(testing.db.dialect)
+        eq_(dialect_impl.evt_targets, (t1, ))
+
+    def test_create_metadata_bound_no_crash(self):
+        m1 = MetaData()
+        self.MyType(metadata=m1)
+
+        m1.create_all(testing.db)
 
 
 class SchemaTest(fixtures.TestBase, AssertsCompiledSQL):
index 449fc6840465f4d1149c35a16dd9b243ceeedf74..a048f2e715d91c7d21cfba58fc22592a7d1e2541 100644 (file)
@@ -1036,9 +1036,14 @@ class EnumTest(AssertsCompiledSQL, fixtures.TestBase):
         eq_(e1.adapt(ENUM).name, 'foo')
         eq_(e1.adapt(ENUM).schema, 'bar')
 
-    @testing.crashes('mysql',
-                    'Inconsistent behavior across various OS/drivers'
-                )
+    def test_create_metadata_bound_no_crash(self):
+        m1 = MetaData()
+        Enum('a', 'b', 'c', metadata=m1)
+
+        m1.create_all(testing.db)
+
+    @testing.crashes(
+        'mysql', 'Inconsistent behavior across various OS/drivers')
     def test_constraint(self):
         assert_raises(exc.DBAPIError,
             enum_table.insert().execute,