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_9_7~40 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=bd610650703f1c646c147bf5d3e1e24dcc3a731b;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: test/sql/test_types.py --- diff --git a/doc/build/changelog/changelog_08.rst b/doc/build/changelog/changelog_08.rst index 4b036272bb..01590b0902 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 33991a2c79..4419250dbb 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -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): diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index 671ea1b703..b4d2d23901 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -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) diff --git a/test/sql/test_metadata.py b/test/sql/test_metadata.py index 02d8e65ed7..7711db816e 100644 --- a/test/sql/test_metadata.py +++ b/test/sql/test_metadata.py @@ -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): diff --git a/test/sql/test_types.py b/test/sql/test_types.py index 449fc68404..a048f2e715 100644 --- a/test/sql/test_types.py +++ b/test/sql/test_types.py @@ -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,