summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-07-13 18:55:18 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2014-07-13 18:55:18 -0400
commit071a1830985834efc113f63fa8740c7306e0cae3 (patch)
treef77bb3d124bebe9e22ad53b859ed4a56ac0d04f0
parent6922a56140d9e014e842a60da344bf6be92f9446 (diff)
downloadsqlalchemy-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.rst10
-rw-r--r--lib/sqlalchemy/dialects/postgresql/base.py3
-rw-r--r--lib/sqlalchemy/sql/sqltypes.py15
-rw-r--r--test/sql/test_metadata.py57
-rw-r--r--test/sql/test_types.py6
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):