]> 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 23:01:15 +0000 (19:01 -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:
lib/sqlalchemy/sql/sqltypes.py
test/sql/test_types.py

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

index 6c21a47aa09842dec8e417ed2782c5b316dfb135..6ef185b72f5fa9cf72dd75e195b66a044dc17763 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 2e2396b4d131114961bff221dcec10a2b050296b..796e85599a94efdd08ac6e95b9a1748515536f58 100644 (file)
@@ -870,8 +870,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 b53d92632ecb9feb64e10f0d18e467526596322a..27eb40e51e51484fd58d657d872b846ba9f17a3e 100644 (file)
@@ -1913,7 +1913,11 @@ class SchemaType(events.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,
                     quote=self.quote,
                     schema=schema,
@@ -2087,7 +2091,11 @@ class Enum(String, SchemaType):
 
     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)
         if issubclass(impltype, Enum):
             return impltype(name=self.name,
                         quote=self.quote,
index 4921672e1757e91cd15ce3028960ae55ac4a8a46..65aa387e66e4d1284a31cbe60107b03c8cceb6d6 100644 (file)
@@ -770,8 +770,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")
@@ -852,6 +866,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 2556e8adc246c0db898f8672d98e0ad579e6b0f0..cce465b603bd1a8db27ecac29640244541fac3be 100644 (file)
@@ -873,9 +873,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,