From: Mike Bayer Date: Sun, 13 Jul 2014 22:55:18 +0000 (-0400) Subject: - Fixed bug in :class:`.Enum` and other :class:`.SchemaType` X-Git-Tag: rel_0_8_7~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=53b72c35f4e22f3d0615fcaea6a57e656c1fe9df;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - 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`. fixes #3124 Conflicts: lib/sqlalchemy/sql/sqltypes.py test/sql/test_types.py --- diff --git a/doc/build/changelog/changelog_08.rst b/doc/build/changelog/changelog_08.rst index 6c21a47aa0..6ef185b72f 100644 --- a/doc/build/changelog/changelog_08.rst +++ b/doc/build/changelog/changelog_08.rst @@ -11,6 +11,16 @@ .. 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 diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 2e2396b4d1..796e85599a 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -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): diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py index b53d92632e..27eb40e51e 100644 --- a/lib/sqlalchemy/types.py +++ b/lib/sqlalchemy/types.py @@ -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, diff --git a/test/sql/test_metadata.py b/test/sql/test_metadata.py index 4921672e17..65aa387e66 100644 --- a/test/sql/test_metadata.py +++ b/test/sql/test_metadata.py @@ -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): diff --git a/test/sql/test_types.py b/test/sql/test_types.py index 2556e8adc2..cce465b603 100644 --- a/test/sql/test_types.py +++ b/test/sql/test_types.py @@ -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,