From: Mike Bayer Date: Sat, 29 Nov 2014 19:44:26 +0000 (-0500) Subject: - Fixed bug in :meth:`.Table.tometadata` method where the X-Git-Tag: rel_1_0_0~19^2~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=98c2a679707432e6707ba70f1aebd10b28b861a3;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Fixed bug in :meth:`.Table.tometadata` method where the :class:`.CheckConstraint` associated with a :class:`.Boolean` or :class:`.Enum` type object would be doubled in the target table. The copy process now tracks the production of this constraint object as local to a type object. fixes #3260 --- diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst index 4a350370f0..f2bd43a76b 100644 --- a/doc/build/changelog/changelog_10.rst +++ b/doc/build/changelog/changelog_10.rst @@ -21,6 +21,16 @@ series as well. For changes that are specific to 1.0 with an emphasis on compatibility concerns, see :doc:`/changelog/migration_10`. + .. change:: + :tags: bug, sql + :tickets: 3260 + + Fixed bug in :meth:`.Table.tometadata` method where the + :class:`.CheckConstraint` associated with a :class:`.Boolean` + or :class:`.Enum` type object would be doubled in the target table. + The copy process now tracks the production of this constraint object + as local to a type object. + .. change:: :tags: feature, orm :tickets: 3217 diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py index 4093d7115b..b90f7fc539 100644 --- a/lib/sqlalchemy/sql/schema.py +++ b/lib/sqlalchemy/sql/schema.py @@ -824,7 +824,7 @@ class Table(DialectKWArgs, SchemaItem, TableClause): table.append_constraint( c.copy(schema=fk_constraint_schema, target_table=table)) - else: + elif not c._type_bound: table.append_constraint( c.copy(schema=schema, target_table=table)) for index in self.indexes: @@ -1295,7 +1295,7 @@ class Column(SchemaItem, ColumnClause): # Constraint objects plus non-constraint-bound ForeignKey objects args = \ - [c.copy(**kw) for c in self.constraints] + \ + [c.copy(**kw) for c in self.constraints if not c._type_bound] + \ [c.copy(**kw) for c in self.foreign_keys if not c.constraint] type_ = self.type @@ -2254,7 +2254,7 @@ class Constraint(DialectKWArgs, SchemaItem): __visit_name__ = 'constraint' def __init__(self, name=None, deferrable=None, initially=None, - _create_rule=None, info=None, + _create_rule=None, info=None, _type_bound=False, **dialect_kw): """Create a SQL constraint. @@ -2304,6 +2304,7 @@ class Constraint(DialectKWArgs, SchemaItem): if info: self.info = info self._create_rule = _create_rule + self._type_bound = _type_bound util.set_creation_order(self) self._validate_dialect_kwargs(dialect_kw) @@ -2420,7 +2421,7 @@ class CheckConstraint(Constraint): def __init__(self, sqltext, name=None, deferrable=None, initially=None, table=None, info=None, _create_rule=None, - _autoattach=True): + _autoattach=True, _type_bound=False): """Construct a CHECK constraint. :param sqltext: @@ -2450,7 +2451,9 @@ class CheckConstraint(Constraint): """ super(CheckConstraint, self).\ - __init__(name, deferrable, initially, _create_rule, info=info) + __init__( + name, deferrable, initially, _create_rule, info=info, + _type_bound=_type_bound) self.sqltext = _literal_as_text(sqltext, warn=False) if table is not None: self._set_parent_with_dispatch(table) @@ -2485,7 +2488,8 @@ class CheckConstraint(Constraint): deferrable=self.deferrable, _create_rule=self._create_rule, table=target_table, - _autoattach=False) + _autoattach=False, + _type_bound=self._type_bound) return self._schema_item_copy(c) diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index 2729bc83e1..7bf2f337ce 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -998,13 +998,11 @@ class SchemaType(SchemaEventTarget): def adapt(self, impltype, **kw): schema = kw.pop('schema', self.schema) - # don't associate with MetaData as the hosting type + # don't associate with self.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) @@ -1165,7 +1163,8 @@ class Enum(String, SchemaType): type_coerce(column, self).in_(self.enums), name=_defer_name(self.name), _create_rule=util.portable_instancemethod( - self._should_create_constraint) + self._should_create_constraint), + _type_bound=True ) assert e.table is table @@ -1303,7 +1302,8 @@ class Boolean(TypeEngine, SchemaType): type_coerce(column, self).in_([0, 1]), name=_defer_name(self.name), _create_rule=util.portable_instancemethod( - self._should_create_constraint) + self._should_create_constraint), + _type_bound=True ) assert e.table is table diff --git a/test/sql/test_metadata.py b/test/sql/test_metadata.py index 3f24fd07dc..74044e3bbe 100644 --- a/test/sql/test_metadata.py +++ b/test/sql/test_metadata.py @@ -1473,6 +1473,46 @@ class SchemaTypeTest(fixtures.TestBase): m1.create_all(testing.db) + def test_boolean_constraint_type_doesnt_double(self): + m1 = MetaData() + + t1 = Table('x', m1, Column("flag", Boolean())) + eq_( + len([ + c for c in t1.constraints + if isinstance(c, CheckConstraint)]), + 1 + ) + m2 = MetaData() + t2 = t1.tometadata(m2) + + eq_( + len([ + c for c in t2.constraints + if isinstance(c, CheckConstraint)]), + 1 + ) + + def test_enum_constraint_type_doesnt_double(self): + m1 = MetaData() + + t1 = Table('x', m1, Column("flag", Enum('a', 'b', 'c'))) + eq_( + len([ + c for c in t1.constraints + if isinstance(c, CheckConstraint)]), + 1 + ) + m2 = MetaData() + t2 = t1.tometadata(m2) + + eq_( + len([ + c for c in t2.constraints + if isinstance(c, CheckConstraint)]), + 1 + ) + class SchemaTest(fixtures.TestBase, AssertsCompiledSQL):