From: Mike Bayer Date: Thu, 27 Feb 2025 17:04:12 +0000 (-0500) Subject: allow control of constraint isolation w/ add/drop constraint X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d6f11d9030b325d5afabf87869a6e3542edda54b;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git allow control of constraint isolation w/ add/drop constraint Added new parameters :paramref:`.AddConstraint.isolate_from_table` and :paramref:`.DropConstraint.isolate_from_table`, defaulting to True, which both document and allow to be controllable the long-standing behavior of these two constructs blocking the given constraint from being included inline within the "CREATE TABLE" sequence, under the assumption that separate add/drop directives were to be used. Fixes: #12382 Change-Id: I53c4170ccb5803f69945ba7aa3d3a143131508eb --- diff --git a/doc/build/changelog/unreleased_20/12382.rst b/doc/build/changelog/unreleased_20/12382.rst new file mode 100644 index 0000000000..80f4630969 --- /dev/null +++ b/doc/build/changelog/unreleased_20/12382.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, sql + :tickets: 12382 + + Added new parameters :paramref:`.AddConstraint.isolate_from_table` and + :paramref:`.DropConstraint.isolate_from_table`, defaulting to True, which + both document and allow to be controllable the long-standing behavior of + these two constructs blocking the given constraint from being included + inline within the "CREATE TABLE" sequence, under the assumption that + separate add/drop directives were to be used. diff --git a/lib/sqlalchemy/sql/ddl.py b/lib/sqlalchemy/sql/ddl.py index 7210d930a1..4e1973ea02 100644 --- a/lib/sqlalchemy/sql/ddl.py +++ b/lib/sqlalchemy/sql/ddl.py @@ -751,11 +751,33 @@ class AddConstraint(_CreateBase): __visit_name__ = "add_constraint" - def __init__(self, element): + def __init__( + self, + element: Constraint, + *, + isolate_from_table: bool = True, + ): + """Construct a new :class:`.AddConstraint` construct. + + :param element: a :class:`.Constraint` object + + :param isolate_from_table: optional boolean, defaults to True. Has + the effect of the incoming constraint being isolated from being + included in a CREATE TABLE sequence when associated with a + :class:`.Table`. + + .. versionadded:: 2.0.39 - added + :paramref:`.AddConstraint.isolate_from_table`, defaulting + to True. Previously, the behavior of this parameter was implicitly + turned on in all cases. + + """ super().__init__(element) - element._create_rule = util.portable_instancemethod( - self._create_rule_disable - ) + + if isolate_from_table: + element._create_rule = util.portable_instancemethod( + self._create_rule_disable + ) class DropConstraint(_DropBase): @@ -763,12 +785,40 @@ class DropConstraint(_DropBase): __visit_name__ = "drop_constraint" - def __init__(self, element, cascade=False, if_exists=False, **kw): + def __init__( + self, + element: Constraint, + *, + cascade: bool = False, + if_exists: bool = False, + isolate_from_table: bool = True, + **kw: Any, + ): + """Construct a new :class:`.DropConstraint` construct. + + :param element: a :class:`.Constraint` object + :param cascade: optional boolean, indicates backend-specific + "CASCADE CONSTRAINT" directive should be rendered if available + :param if_exists: optional boolean, indicates backend-specific + "IF EXISTS" directive should be rendered if available + :param isolate_from_table: optional boolean, defaults to True. Has + the effect of the incoming constraint being isolated from being + included in a CREATE TABLE sequence when associated with a + :class:`.Table`. + + .. versionadded:: 2.0.39 - added + :paramref:`.DropConstraint.isolate_from_table`, defaulting + to True. Previously, the behavior of this parameter was implicitly + turned on in all cases. + + """ self.cascade = cascade super().__init__(element, if_exists=if_exists, **kw) - element._create_rule = util.portable_instancemethod( - self._create_rule_disable - ) + + if isolate_from_table: + element._create_rule = util.portable_instancemethod( + self._create_rule_disable + ) class SetTableComment(_CreateDropBase): diff --git a/test/sql/test_constraints.py b/test/sql/test_constraints.py index 93c385ba4d..ebd44cdcb5 100644 --- a/test/sql/test_constraints.py +++ b/test/sql/test_constraints.py @@ -1219,7 +1219,11 @@ class ConstraintCompilationTest(fixtures.TestBase, AssertsCompiledSQL): "CHECK (a < b) DEFERRABLE INITIALLY DEFERRED", ) - def test_external_ck_constraint_cancels_internal(self): + @testing.variation("isolate", [True, False]) + @testing.variation("type_", ["add", "drop"]) + def test_external_ck_constraint_cancels_internal( + self, isolate: testing.Variation, type_: testing.Variation + ): t, t2 = self._constraint_create_fixture() constraint = CheckConstraint( @@ -1230,15 +1234,27 @@ class ConstraintCompilationTest(fixtures.TestBase, AssertsCompiledSQL): table=t, ) - schema.AddConstraint(constraint) - - # once we make an AddConstraint, - # inline compilation of the CONSTRAINT - # is disabled - self.assert_compile( - schema.CreateTable(t), - "CREATE TABLE tbl (a INTEGER, b INTEGER)", - ) + if type_.add: + cls = schema.AddConstraint + elif type_.drop: + cls = schema.DropConstraint + else: + type_.fail() + + if not isolate: + cls(constraint, isolate_from_table=False) + self.assert_compile( + schema.CreateTable(t), + "CREATE TABLE tbl (a INTEGER, b INTEGER, " + "CONSTRAINT my_test_constraint CHECK (a < b) " + "DEFERRABLE INITIALLY DEFERRED)", + ) + else: + cls(constraint) + self.assert_compile( + schema.CreateTable(t), + "CREATE TABLE tbl (a INTEGER, b INTEGER)", + ) def test_render_drop_constraint(self): t, t2 = self._constraint_create_fixture()