From: Federico Caselli Date: Wed, 26 Nov 2025 21:13:50 +0000 (+0100) Subject: Deprecate DropConstraint.isolate_from_table X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8e90bfd9b4a1ca8bd62bae3aa373e70fd781cc49;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Deprecate DropConstraint.isolate_from_table The the parameter :paramref:`_schema.DropConstraint.isolate_from_table` was deprecated since it has no effect on the drop table behavior. Its default values was also changed to ``False``. Fixes: #13006 Change-Id: Ibaa68e78da301a4f2a200f8c7abb2dc099fef37a --- diff --git a/doc/build/changelog/unreleased_21/13006.rst b/doc/build/changelog/unreleased_21/13006.rst new file mode 100644 index 0000000000..fff4e54c29 --- /dev/null +++ b/doc/build/changelog/unreleased_21/13006.rst @@ -0,0 +1,7 @@ +.. change:: + :tags: schema, usecase + :tickets: 13006 + + The the parameter :paramref:`_schema.DropConstraint.isolate_from_table` + was deprecated since it has no effect on the drop table behavior. + Its default values was also changed to ``False``. diff --git a/lib/sqlalchemy/dialects/mssql/provision.py b/lib/sqlalchemy/dialects/mssql/provision.py index 03bdbca113..d8950001a3 100644 --- a/lib/sqlalchemy/dialects/mssql/provision.py +++ b/lib/sqlalchemy/dialects/mssql/provision.py @@ -173,7 +173,7 @@ def drop_all_schema_objects_pre_tables(cfg, eng): DropConstraint( ForeignKeyConstraint( [tb.c.x], [tb.c.y], name=fk["name"] - ) + ), ) ) diff --git a/lib/sqlalchemy/sql/ddl.py b/lib/sqlalchemy/sql/ddl.py index 2ede92aa74..caaaa32bf5 100644 --- a/lib/sqlalchemy/sql/ddl.py +++ b/lib/sqlalchemy/sql/ddl.py @@ -33,8 +33,10 @@ from . import coercions from . import roles from . import util as sql_util from .base import _generative +from .base import _NoArg from .base import DialectKWArgs from .base import Executable +from .base import NO_ARG from .base import SchemaVisitor from .elements import ClauseElement from .selectable import SelectBase @@ -175,7 +177,9 @@ class ExecutableDDLElement(roles.DDLRole, Executable, BaseDDLElement): event.listen( users, "after_create", - AddConstraint(constraint).execute_if(dialect="postgresql"), + AddConstraint(constraint, isolate_from_table=True).execute_if( + dialect="postgresql" + ), ) .. seealso:: @@ -1126,19 +1130,22 @@ class AddConstraint(_CreateBase["Constraint"]): __visit_name__ = "add_constraint" def __init__( - self, - element: Constraint, - *, - isolate_from_table: bool = True, + self, element: Constraint, *, isolate_from_table: bool = True ) -> None: """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`. + :param isolate_from_table: optional boolean. Prevents the target + :class:`.Constraint` from being rendered inline in a "CONSTRAINT" + clause within a CREATE TABLE statement, in the case that the + constraint is associated with a :class:`.Table` which is later + created using :meth:`.Table.create` or :meth:`.MetaData.create_all`. + This occurs by modifying the state of the :class:`.Constraint` + object itself such that the CREATE TABLE DDL process will skip it. + Used for the case when a separate `ALTER TABLE...ADD CONSTRAINT` + call will be emitted after the `CREATE TABLE` has already occurred. + ``True`` by default. .. versionadded:: 2.0.39 - added :paramref:`.AddConstraint.isolate_from_table`, defaulting @@ -1163,32 +1170,48 @@ class DropConstraint(_DropBase["Constraint"]): *, cascade: bool = False, if_exists: bool = False, - isolate_from_table: bool = True, + isolate_from_table: bool | _NoArg = NO_ARG, **kw: Any, ) -> None: """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`. + + :param isolate_from_table: optional boolean. This is a deprecated + setting that when ``True``, does the same thing that + :paramref:`.AddConstraint.isolate_from_table` does, which is prevents + the constraint from being associated with an inline ``CREATE TABLE`` + statement. It does not have any effect on the DROP process for a + table and is an artifact of older SQLAlchemy versions, + and will be removed in a future release. .. 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. + .. versionchanged:: 2.1 - This parameter has been deprecated and + the default value of the flag was changed to ``False``. + """ self.cascade = cascade super().__init__(element, if_exists=if_exists, **kw) - if isolate_from_table: - element._create_rule = self._create_rule_disable + if isolate_from_table is not NO_ARG: + util.warn_deprecated( + "The ``isolate_from_table`` is deprecated and it be removed " + "in a future release.", + "2.1", + ) + + if isolate_from_table: + element._create_rule = self._create_rule_disable class SetTableComment(_CreateDropBase["Table"]): @@ -1480,7 +1503,9 @@ class SchemaGenerator(InvokeCreateDDLBase): return with self.with_ddl_events(constraint): - AddConstraint(constraint)._invoke_with(self.connection) + AddConstraint(constraint, isolate_from_table=True)._invoke_with( + self.connection + ) def visit_sequence(self, sequence, create_ok=False): if not create_ok and not self._can_create_sequence(sequence): diff --git a/lib/sqlalchemy/testing/util.py b/lib/sqlalchemy/testing/util.py index 21dddfa2ec..43e3f071bc 100644 --- a/lib/sqlalchemy/testing/util.py +++ b/lib/sqlalchemy/testing/util.py @@ -445,7 +445,7 @@ def drop_all_tables( conn.execute( DropConstraint( ForeignKeyConstraint([tb.c.x], [tb.c.y], name=fkc) - ) + ), ) diff --git a/test/dialect/postgresql/test_compiler.py b/test/dialect/postgresql/test_compiler.py index cb240a74a5..40b6523835 100644 --- a/test/dialect/postgresql/test_compiler.py +++ b/test/dialect/postgresql/test_compiler.py @@ -797,7 +797,7 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL): tbl.c.data, name="uq_data1", postgresql_nulls_not_distinct=True, - ) + ), ), ( "ALTER TABLE test_tbl ADD CONSTRAINT uq_data1 UNIQUE " @@ -810,7 +810,7 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL): tbl.c.data2, name="uq_data2", postgresql_nulls_not_distinct=False, - ) + ), ), ( "ALTER TABLE test_tbl ADD CONSTRAINT uq_data2 UNIQUE " @@ -819,10 +819,7 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL): ), ( lambda tbl: schema.AddConstraint( - schema.UniqueConstraint( - tbl.c.data3, - name="uq_data3", - ) + schema.UniqueConstraint(tbl.c.data3, name="uq_data3"), ), "ALTER TABLE test_tbl ADD CONSTRAINT uq_data3 UNIQUE (data3)", ), @@ -844,7 +841,7 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL): @testing.combinations( ( lambda tbl: schema.AddConstraint( - UniqueConstraint(tbl.c.id, postgresql_include=[tbl.c.value]) + UniqueConstraint(tbl.c.id, postgresql_include=[tbl.c.value]), ), "ALTER TABLE foo ADD UNIQUE (id) INCLUDE (value)", ), @@ -852,7 +849,7 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL): lambda tbl: schema.AddConstraint( PrimaryKeyConstraint( tbl.c.id, postgresql_include=[tbl.c.value, "misc"] - ) + ), ), "ALTER TABLE foo ADD PRIMARY KEY (id) INCLUDE (value, misc)", ), diff --git a/test/engine/test_ddlevents.py b/test/engine/test_ddlevents.py index 16880f5260..8b1bd4cf78 100644 --- a/test/engine/test_ddlevents.py +++ b/test/engine/test_ddlevents.py @@ -673,7 +673,8 @@ class DDLExecutionTest(AssertsCompiledSQL, fixtures.TestBase): assert "xyzzy" in strings assert "fnord" in strings - def test_conditional_constraint(self): + @testing.variation("flag", [True, False, "nope"]) + def test_conditional_constraint(self, flag): metadata, users = self.metadata, self.users nonpg_mock = engines.mock_engine(dialect_name="sqlite") pg_mock = engines.mock_engine(dialect_name="postgresql") @@ -682,12 +683,15 @@ class DDLExecutionTest(AssertsCompiledSQL, fixtures.TestBase): ) # by placing the constraint in an Add/Drop construct, the - # 'inline_ddl' flag is set to False + # 'inline_ddl' flag is set to False if isolate_from_table != False + kw = {} + if not flag.nope: + kw["isolate_from_table"] = bool(flag) event.listen( users, "after_create", - AddConstraint(constraint).execute_if(dialect="postgresql"), + AddConstraint(constraint, **kw).execute_if(dialect="postgresql"), ) event.listen( @@ -698,10 +702,10 @@ class DDLExecutionTest(AssertsCompiledSQL, fixtures.TestBase): metadata.create_all(bind=nonpg_mock) strings = " ".join(str(x) for x in nonpg_mock.mock) - assert "my_test_constraint" not in strings + eq_("my_test_constraint" not in strings, not flag.not_flag) metadata.drop_all(bind=nonpg_mock) strings = " ".join(str(x) for x in nonpg_mock.mock) - assert "my_test_constraint" not in strings + eq_("my_test_constraint" not in strings, not flag.not_flag) metadata.create_all(bind=pg_mock) strings = " ".join(str(x) for x in pg_mock.mock) assert "my_test_constraint" in strings diff --git a/test/sql/test_constraints.py b/test/sql/test_constraints.py index 70055ace1e..7d89b80f11 100644 --- a/test/sql/test_constraints.py +++ b/test/sql/test_constraints.py @@ -1,3 +1,5 @@ +from contextlib import nullcontext + from sqlalchemy import CheckConstraint from sqlalchemy import Column from sqlalchemy import exc @@ -22,6 +24,7 @@ from sqlalchemy.testing import AssertsExecutionResults from sqlalchemy.testing import engines from sqlalchemy.testing import eq_ from sqlalchemy.testing import fixtures +from sqlalchemy.testing.assertions import expect_deprecated from sqlalchemy.testing.assertions import expect_warnings from sqlalchemy.testing.assertsql import AllOf from sqlalchemy.testing.assertsql import CompiledSQL @@ -227,6 +230,50 @@ class ConstraintGenTest(fixtures.TestBase, AssertsExecutionResults): ) self._assert_cyclic_constraint(metadata, auto=False) + @testing.provide_metadata + @testing.skip_if( + lambda: not testing.db.dialect.supports_alter, "does not support ALTER" + ) + @testing.combinations(True, False) + def test_fk_column_use_alter_drop_constraint(self, isolate): + metadata = self.metadata + + Table( + "a", + metadata, + Column("id", Integer, primary_key=True), + Column("bid", Integer, ForeignKey("b.id", name="afk")), + ) + b = Table( + "b", + metadata, + Column("id", Integer, primary_key=True), + Column("aid", Integer, ForeignKey("a.id", name="bfk")), + ) + + metadata.create_all(testing.db) + + (fk,) = (c for c in b.constraints if c.name == "bfk") + + with expect_deprecated( + "The ``isolate_from_table`` is deprecated and it be removed " + "in a future release." + ): + schema.DropConstraint(fk, isolate_from_table=isolate) + + assertions = [ + AllOf( + CompiledSQL("ALTER TABLE b DROP CONSTRAINT bfk"), + CompiledSQL("ALTER TABLE a DROP CONSTRAINT afk"), + ), + CompiledSQL("DROP TABLE b"), + CompiledSQL("DROP TABLE a"), + ] + + with self.sql_execution_asserter() as asserter: + metadata.drop_all(testing.db, checkfirst=False) + asserter.assert_(*assertions) + def _assert_cyclic_constraint( self, metadata, auto=False, sqlite_warning=False ): @@ -302,7 +349,7 @@ class ConstraintGenTest(fixtures.TestBase, AssertsExecutionResults): ] with self.sql_execution_asserter() as asserter: - metadata.drop_all(testing.db, checkfirst=False), + metadata.drop_all(testing.db, checkfirst=False) asserter.assert_(*assertions) def _assert_cyclic_constraint_no_alter( @@ -366,10 +413,10 @@ class ConstraintGenTest(fixtures.TestBase, AssertsExecutionResults): if sqlite_warning: with expect_warnings("Can't sort tables for DROP; "): with self.sql_execution_asserter() as asserter: - metadata.drop_all(testing.db, checkfirst=False), + metadata.drop_all(testing.db, checkfirst=False) else: with self.sql_execution_asserter() as asserter: - metadata.drop_all(testing.db, checkfirst=False), + metadata.drop_all(testing.db, checkfirst=False) asserter.assert_(*assertions) @testing.force_drop_names("a", "b") @@ -1021,7 +1068,9 @@ class ConstraintCompilationTest(fixtures.TestBase, AssertsCompiledSQL): ) self.assert_compile( - schema.AddConstraint(list(t.foreign_keys)[0].constraint), + schema.AddConstraint( + list(t.foreign_keys)[0].constraint, isolate_from_table=False + ), "ALTER TABLE tbl ADD FOREIGN KEY(b) " "REFERENCES tbl (a) MATCH SIMPLE", ) @@ -1214,12 +1263,12 @@ class ConstraintCompilationTest(fixtures.TestBase, AssertsCompiledSQL): ) self.assert_compile( - schema.AddConstraint(constraint), + schema.AddConstraint(constraint, isolate_from_table=False), "ALTER TABLE tbl ADD CONSTRAINT my_test_constraint " "CHECK (a < b) DEFERRABLE INITIALLY DEFERRED", ) - @testing.variation("isolate", [True, False]) + @testing.combinations(True, False, "default", argnames="isolate") @testing.variation("type_", ["add", "drop"]) def test_external_ck_constraint_cancels_internal( self, isolate: testing.Variation, type_: testing.Variation @@ -1235,14 +1284,23 @@ class ConstraintCompilationTest(fixtures.TestBase, AssertsCompiledSQL): ) if type_.add: + ctx = nullcontext() cls = schema.AddConstraint elif type_.drop: + ctx = expect_deprecated( + "The ``isolate_from_table`` is deprecated and it be removed " + "in a future release." + ) cls = schema.DropConstraint else: type_.fail() - if not isolate: - cls(constraint, isolate_from_table=False) + if not isolate or (type_.drop and isolate == "default"): + if isolate == "default": + cls(constraint) + else: + with ctx: + cls(constraint, isolate_from_table=False) self.assert_compile( schema.CreateTable(t), "CREATE TABLE tbl (a INTEGER, b INTEGER, " @@ -1250,7 +1308,11 @@ class ConstraintCompilationTest(fixtures.TestBase, AssertsCompiledSQL): "DEFERRABLE INITIALLY DEFERRED)", ) else: - cls(constraint) + if isolate == "default": + cls(constraint) + else: + with ctx: + cls(constraint, isolate_from_table=True) self.assert_compile( schema.CreateTable(t), "CREATE TABLE tbl (a INTEGER, b INTEGER)", @@ -1304,7 +1366,7 @@ class ConstraintCompilationTest(fixtures.TestBase, AssertsCompiledSQL): constraint = ForeignKeyConstraint(["b"], ["t2.a"]) t.append_constraint(constraint) self.assert_compile( - schema.AddConstraint(constraint), + schema.AddConstraint(constraint, isolate_from_table=False), "ALTER TABLE tbl ADD FOREIGN KEY(b) REFERENCES t2 (a)", ) @@ -1314,7 +1376,7 @@ class ConstraintCompilationTest(fixtures.TestBase, AssertsCompiledSQL): constraint = ForeignKeyConstraint([t.c.a], [t2.c.b]) t.append_constraint(constraint) self.assert_compile( - schema.AddConstraint(constraint), + schema.AddConstraint(constraint, isolate_from_table=False), "ALTER TABLE tbl ADD FOREIGN KEY(a) REFERENCES t2 (b)", ) @@ -1324,7 +1386,7 @@ class ConstraintCompilationTest(fixtures.TestBase, AssertsCompiledSQL): constraint = UniqueConstraint("a", "b", name="uq_cst") t2.append_constraint(constraint) self.assert_compile( - schema.AddConstraint(constraint), + schema.AddConstraint(constraint, isolate_from_table=False), "ALTER TABLE t2 ADD CONSTRAINT uq_cst UNIQUE (a, b)", ) @@ -1333,7 +1395,7 @@ class ConstraintCompilationTest(fixtures.TestBase, AssertsCompiledSQL): constraint = UniqueConstraint(t2.c.a, t2.c.b, name="uq_cs2") self.assert_compile( - schema.AddConstraint(constraint), + schema.AddConstraint(constraint, isolate_from_table=False), "ALTER TABLE t2 ADD CONSTRAINT uq_cs2 UNIQUE (a, b)", ) @@ -1344,7 +1406,7 @@ class ConstraintCompilationTest(fixtures.TestBase, AssertsCompiledSQL): constraint = PrimaryKeyConstraint(t.c.a) assert t.c.a.primary_key is True self.assert_compile( - schema.AddConstraint(constraint), + schema.AddConstraint(constraint, isolate_from_table=False), "ALTER TABLE tbl ADD PRIMARY KEY (a)", ) @@ -1354,7 +1416,7 @@ class ConstraintCompilationTest(fixtures.TestBase, AssertsCompiledSQL): constraint = CheckConstraint(t.c.a > 5) self.assert_compile( - schema.AddConstraint(constraint), + schema.AddConstraint(constraint, isolate_from_table=False), "ALTER TABLE tbl ADD CHECK (a > 5)", ) diff --git a/test/sql/test_metadata.py b/test/sql/test_metadata.py index 43cbc08ba9..166e1597b9 100644 --- a/test/sql/test_metadata.py +++ b/test/sql/test_metadata.py @@ -5813,7 +5813,8 @@ class NamingConventionTest(fixtures.TestBase, AssertsCompiledSQL): is_(const[0].name, None) self.assert_compile( - AddConstraint(const[0]), "ALTER TABLE foo ADD UNIQUE (id)" + AddConstraint(const[0]), + "ALTER TABLE foo ADD UNIQUE (id)", ) @testing.combinations(