--- /dev/null
+.. change::
+ :tags: bug, schema
+ :tickets: 6152
+
+ Fixed bug first introduced in as some combination of :ticket:`2892`,
+ :ticket:`2919` nnd :ticket:`3832` where the attachment events for a
+ :class:`_types.TypeDecorator` would be doubled up against the "impl" class,
+ if the "impl" were also a :class:`_types.SchemaType`. The real-world case
+ is any :class:`_types.TypeDecorator` against :class:`_types.Enum` or
+ :class:`_types.Boolean` would get a doubled
+ :class:`_schema.CheckConstraint` when the ``create_constraint=True`` flag
+ is set.
+
def _set_parent(self, parent):
"""Associate with this SchemaEvent's parent object."""
- def _set_parent_with_dispatch(self, parent):
+ def _set_parent_with_dispatch(self, parent, **kw):
self.dispatch.before_parent_attach(self, parent)
- self._set_parent(parent)
+ self._set_parent(parent, **kw)
self.dispatch.after_parent_attach(self, parent)
def _translate_schema(self, effective_schema, map_):
return map_.get(effective_schema, effective_schema)
- def _set_parent(self, column):
+ def _set_parent(self, column, **kw):
column._on_table_attach(util.portable_instancemethod(self._set_table))
def _variant_mapping_for_set_table(self, column):
def compare_values(self, x, y):
return x == y
- def _set_parent(self, column):
+ def _set_parent(self, column, **kw):
"""Support SchemaEventTarget"""
if isinstance(self.item_type, SchemaEventTarget):
- self.item_type._set_parent(column)
+ self.item_type._set_parent(column, **kw)
- def _set_parent_with_dispatch(self, parent):
+ def _set_parent_with_dispatch(self, parent, **kw):
"""Support SchemaEventTarget"""
super(ARRAY, self)._set_parent_with_dispatch(parent)
"""
return self.impl._type_affinity
- def _set_parent(self, column):
+ def _set_parent(self, column, outer=False, **kw):
"""Support SchemaEventTarget"""
super(TypeDecorator, self)._set_parent(column)
- if isinstance(self.impl, SchemaEventTarget):
- self.impl._set_parent(column)
+ if not outer and isinstance(self.impl, SchemaEventTarget):
+ self.impl._set_parent(column, outer=False, **kw)
def _set_parent_with_dispatch(self, parent):
"""Support SchemaEventTarget"""
- super(TypeDecorator, self)._set_parent_with_dispatch(parent)
+ super(TypeDecorator, self)._set_parent_with_dispatch(
+ parent, outer=True
+ )
if isinstance(self.impl, SchemaEventTarget):
self.impl._set_parent_with_dispatch(parent)
else:
return self.impl
- def _set_parent(self, column):
+ def _set_parent(self, column, **kw):
"""Support SchemaEventTarget"""
if isinstance(self.impl, SchemaEventTarget):
- self.impl._set_parent(column)
+ self.impl._set_parent(column, **kw)
for impl in self.mapping.values():
if isinstance(impl, SchemaEventTarget):
- impl._set_parent(column)
+ impl._set_parent(column, **kw)
def _set_parent_with_dispatch(self, parent):
"""Support SchemaEventTarget"""
def test_before_parent_attach_typedec_enclosing_schematype(self):
# additional test for [ticket:2919] as part of test for
# [ticket:3832]
+ # this also serves as the test for [ticket:6152]
class MySchemaType(sqltypes.TypeEngine, sqltypes.SchemaType):
pass
impl = target_typ
typ = MyType()
- self._test_before_parent_attach(typ, target_typ, double=True)
+ self._test_before_parent_attach(typ, target_typ)
def test_before_parent_attach_array_enclosing_schematype(self):
# test for [ticket:4141] which is the same idea as [ticket:3832]
typ = MyType()
self._test_before_parent_attach(typ)
- def _test_before_parent_attach(self, typ, evt_target=None, double=False):
+ def _test_before_parent_attach(self, typ, evt_target=None):
canary = mock.Mock()
if evt_target is None:
orig_set_parent = evt_target._set_parent
orig_set_parent_w_dispatch = evt_target._set_parent_with_dispatch
- def _set_parent(parent):
- orig_set_parent(parent)
+ def _set_parent(parent, **kw):
+ orig_set_parent(parent, **kw)
canary._set_parent(parent)
def _set_parent_w_dispatch(parent):
c = Column("q", typ)
- if double:
- # no clean way yet to fix this, inner schema type is called
- # twice, but this is a very unusual use case.
- eq_(
- canary.mock_calls,
- [
- mock.call._set_parent(c),
- mock.call.go(evt_target, c),
- mock.call._set_parent(c),
- mock.call._set_parent_with_dispatch(c),
- ],
- )
- else:
- eq_(
- canary.mock_calls,
- [
- mock.call.go(evt_target, c),
- mock.call._set_parent(c),
- mock.call._set_parent_with_dispatch(c),
- ],
- )
+ eq_(
+ canary.mock_calls,
+ [
+ mock.call.go(evt_target, c),
+ mock.call._set_parent(c),
+ mock.call._set_parent_with_dispatch(c),
+ ],
+ )
def test_independent_schema(self):
m = MetaData()
Float().dialect_impl(pg).__class__,
)
+ @testing.combinations((Boolean,), (Enum,))
+ def test_typedecorator_schematype_constraint(self, typ):
+ class B(TypeDecorator):
+ impl = typ
+
+ t1 = Table("t1", MetaData(), Column("q", B(create_constraint=True)))
+ eq_(
+ len([c for c in t1.constraints if isinstance(c, CheckConstraint)]),
+ 1,
+ )
+
def test_type_decorator_repr(self):
class MyType(TypeDecorator):
impl = VARCHAR