diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-07-13 18:55:18 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-07-13 18:55:18 -0400 |
| commit | 071a1830985834efc113f63fa8740c7306e0cae3 (patch) | |
| tree | f77bb3d124bebe9e22ad53b859ed4a56ac0d04f0 | |
| parent | 6922a56140d9e014e842a60da344bf6be92f9446 (diff) | |
| download | sqlalchemy-071a1830985834efc113f63fa8740c7306e0cae3.tar.gz | |
- 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
| -rw-r--r-- | doc/build/changelog/changelog_08.rst | 10 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/postgresql/base.py | 3 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/sqltypes.py | 15 | ||||
| -rw-r--r-- | test/sql/test_metadata.py | 57 | ||||
| -rw-r--r-- | test/sql/test_types.py | 6 |
5 files changed, 83 insertions, 8 deletions
diff --git a/doc/build/changelog/changelog_08.rst b/doc/build/changelog/changelog_08.rst index 4b036272b..01590b090 100644 --- a/doc/build/changelog/changelog_08.rst +++ b/doc/build/changelog/changelog_08.rst @@ -14,6 +14,16 @@ .. 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 :tickets: 3102 Fixed a bug within the custom operator plus :meth:`.TypeEngine.with_variant` diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 2ae71c2a7..8081f75dd 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 671ea1b70..b4d2d2390 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 02d8e65ed..7711db816 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 e5c1e7b62..8120208d2 100644 --- a/test/sql/test_types.py +++ b/test/sql/test_types.py @@ -1064,6 +1064,12 @@ class EnumTest(AssertsCompiledSQL, fixtures.TestBase): eq_(e1.adapt(ENUM).name, 'foo') eq_(e1.adapt(ENUM).schema, 'bar') + 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): |
