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
event.listen(
users,
"after_create",
- AddConstraint(constraint).execute_if(dialect="postgresql"),
+ AddConstraint(constraint, isolate_from_table=True).execute_if(
+ dialect="postgresql"
+ ),
)
.. seealso::
__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
*,
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"]):
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):
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")
)
# 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(
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
+from contextlib import nullcontext
+
from sqlalchemy import CheckConstraint
from sqlalchemy import Column
from sqlalchemy import exc
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
)
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
):
]
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(
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")
)
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",
)
)
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
)
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, "
"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)",
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)",
)
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)",
)
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)",
)
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)",
)
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)",
)
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)",
)