From: Federico Caselli Date: Sun, 23 Jun 2024 13:01:40 +0000 (+0200) Subject: The ``Enum.inherit_schema`` now defaults to true X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=084761c090061c7b65e5c68a93df01e206ed824b;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git The ``Enum.inherit_schema`` now defaults to true Changed the default value of :paramref:`_types.Enum.inherit_schema` to ``True`` when :paramref:`_types.Enum.schema` and :paramref:`_types.Enum.metadata` parameters are not provided. The same behavior has been applied also to PostgreSQL :class:`_postgresql.DOMAIN` type. Fixes: #10594 Change-Id: Id3d819e3608974353e365cd063d9c5e40a071e73 --- diff --git a/doc/build/changelog/unreleased_21/10594.rst b/doc/build/changelog/unreleased_21/10594.rst new file mode 100644 index 0000000000..ad868b6ee7 --- /dev/null +++ b/doc/build/changelog/unreleased_21/10594.rst @@ -0,0 +1,9 @@ +.. change:: + :tags: change, schema + :tickets: 10594 + + Changed the default value of :paramref:`_types.Enum.inherit_schema` to + ``True`` when :paramref:`_types.Enum.schema` and + :paramref:`_types.Enum.metadata` parameters are not provided. + The same behavior has been applied also to PostgreSQL + :class:`_postgresql.DOMAIN` type. diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index 90c93bcef1..7d9a65bac8 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -6,9 +6,7 @@ # the MIT License: https://www.opensource.org/licenses/mit-license.php # mypy: allow-untyped-defs, allow-untyped-calls -"""SQL specific types. - -""" +"""SQL specific types.""" from __future__ import annotations import collections.abc as collections_abc @@ -40,6 +38,7 @@ from . import elements from . import operators from . import roles from . import type_api +from .base import _NoArg from .base import _NONE_NAME from .base import NO_ARG from .base import SchemaEventTarget @@ -75,6 +74,7 @@ if TYPE_CHECKING: from .elements import ColumnElement from .operators import OperatorType from .schema import MetaData + from .schema import SchemaConst from .type_api import _BindProcessorType from .type_api import _ComparatorFactory from .type_api import _LiteralProcessorType @@ -1053,9 +1053,9 @@ class SchemaType(SchemaEventTarget, TypeEngineMixin): def __init__( self, name: Optional[str] = None, - schema: Optional[str] = None, + schema: Optional[Union[str, Literal[SchemaConst.BLANK_SCHEMA]]] = None, metadata: Optional[MetaData] = None, - inherit_schema: bool = False, + inherit_schema: Union[bool, _NoArg] = NO_ARG, quote: Optional[bool] = None, _create_events: bool = True, _adapted_from: Optional[SchemaType] = None, @@ -1066,7 +1066,18 @@ class SchemaType(SchemaEventTarget, TypeEngineMixin): self.name = None self.schema = schema self.metadata = metadata - self.inherit_schema = inherit_schema + + if inherit_schema is True and schema is not None: + raise exc.ArgumentError( + "Ambiguously setting inherit_schema=True while " + "also passing a non-None schema argument" + ) + self.inherit_schema = ( + inherit_schema + if inherit_schema is not NO_ARG + else (schema is None and metadata is None) + ) + # breakpoint() self._create_events = _create_events if _create_events and self.metadata: @@ -1114,6 +1125,9 @@ class SchemaType(SchemaEventTarget, TypeEngineMixin): elif self.metadata and self.schema is None and self.metadata.schema: self.schema = self.metadata.schema + if self.schema is not None: + self.inherit_schema = False + if not self._create_events: return @@ -1443,21 +1457,28 @@ class Enum(String, SchemaType, Emulated, TypeEngine[Union[str, enum.Enum]]): :class:`_schema.MetaData` object if present, when passed using the :paramref:`_types.Enum.metadata` parameter. - Otherwise, if the :paramref:`_types.Enum.inherit_schema` flag is set - to ``True``, the schema will be inherited from the associated + Otherwise, the schema will be inherited from the associated :class:`_schema.Table` object if any; when - :paramref:`_types.Enum.inherit_schema` is at its default of + :paramref:`_types.Enum.inherit_schema` is set to ``False``, the owning table's schema is **not** used. :param quote: Set explicit quoting preferences for the type's name. :param inherit_schema: When ``True``, the "schema" from the owning - :class:`_schema.Table` - will be copied to the "schema" attribute of this - :class:`.Enum`, replacing whatever value was passed for the - ``schema`` attribute. This also takes effect when using the + :class:`_schema.Table` will be copied to the "schema" + attribute of this :class:`.Enum`, replacing whatever value was + passed for the :paramref:`_types.Enum.schema` attribute. + This also takes effect when using the :meth:`_schema.Table.to_metadata` operation. + Set to ``False`` to retain the schema value provided. + By default the behavior will be to inherit the table schema unless + either :paramref:`_types.Enum.schema` and / or + :paramref:`_types.Enum.metadata` are set. + + .. versionchanged:: 2.1 The default value of this parameter + was changed to ``True`` when :paramref:`_types.Enum.schema` + and :paramref:`_types.Enum.metadata` are not provided. :param validate_strings: when True, string values that are being passed to the database in a SQL statement will be checked @@ -1545,12 +1566,13 @@ class Enum(String, SchemaType, Emulated, TypeEngine[Union[str, enum.Enum]]): # new Enum classes. if self.enum_class and values: kw.setdefault("name", self.enum_class.__name__.lower()) + SchemaType.__init__( self, name=kw.pop("name", None), + inherit_schema=kw.pop("inherit_schema", NO_ARG), schema=kw.pop("schema", None), metadata=kw.pop("metadata", None), - inherit_schema=kw.pop("inherit_schema", False), quote=kw.pop("quote", None), _create_events=kw.pop("_create_events", True), _adapted_from=kw.pop("_adapted_from", None), diff --git a/test/dialect/postgresql/test_types.py b/test/dialect/postgresql/test_types.py index 795a897699..df370f043b 100644 --- a/test/dialect/postgresql/test_types.py +++ b/test/dialect/postgresql/test_types.py @@ -266,7 +266,7 @@ class NamedTypeTest( ("create_type", False, "create_type"), ("create_type", True, "create_type"), ("schema", "someschema", "schema"), - ("inherit_schema", True, "inherit_schema"), + ("inherit_schema", False, "inherit_schema"), ("metadata", MetaData(), "metadata"), ("values_callable", lambda x: None, "values_callable"), ) @@ -443,7 +443,8 @@ class NamedTypeTest( t1.drop(conn, checkfirst=True) @testing.combinations( - ("local_schema",), + ("inherit_schema_false",), + ("inherit_schema_not_provided",), ("metadata_schema_only",), ("inherit_table_schema",), ("override_metadata_schema",), @@ -457,6 +458,7 @@ class NamedTypeTest( """test #6373""" metadata.schema = testing.config.test_schema + default_schema = testing.config.db.dialect.default_schema_name def make_type(**kw): if datatype == "enum": @@ -481,14 +483,14 @@ class NamedTypeTest( ) assert_schema = testing.config.test_schema_2 elif test_case == "inherit_table_schema": - enum = make_type( - metadata=metadata, - inherit_schema=True, - ) + enum = make_type(metadata=metadata, inherit_schema=True) assert_schema = testing.config.test_schema_2 - elif test_case == "local_schema": + elif test_case == "inherit_schema_not_provided": enum = make_type() - assert_schema = testing.config.db.dialect.default_schema_name + assert_schema = testing.config.test_schema_2 + elif test_case == "inherit_schema_false": + enum = make_type(inherit_schema=False) + assert_schema = default_schema else: assert False @@ -509,13 +511,11 @@ class NamedTypeTest( "labels": ["four", "five", "six"], "name": "mytype", "schema": assert_schema, - "visible": assert_schema - == testing.config.db.dialect.default_schema_name, + "visible": assert_schema == default_schema, } ], ) elif datatype == "domain": - def_schame = testing.config.db.dialect.default_schema_name eq_( inspect(connection).get_domains(schema=assert_schema), [ @@ -525,7 +525,7 @@ class NamedTypeTest( "nullable": True, "default": None, "schema": assert_schema, - "visible": assert_schema == def_schame, + "visible": assert_schema == default_schema, "constraints": [ { "name": "mytype_check", diff --git a/test/sql/test_metadata.py b/test/sql/test_metadata.py index ac43b1bf62..0b5f705732 100644 --- a/test/sql/test_metadata.py +++ b/test/sql/test_metadata.py @@ -55,6 +55,7 @@ from sqlalchemy.sql.schema import RETAIN_SCHEMA from sqlalchemy.testing import assert_raises from sqlalchemy.testing import assert_raises_message from sqlalchemy.testing import AssertsCompiledSQL +from sqlalchemy.testing import combinations from sqlalchemy.testing import ComparesTables from sqlalchemy.testing import emits_warning from sqlalchemy.testing import eq_ @@ -2409,6 +2410,23 @@ class SchemaTypeTest(fixtures.TestBase): ], ) + def test_adapt_to_schema(self): + m = MetaData() + type_ = self.MyType() + eq_(type_.inherit_schema, True) + t1 = Table("x", m, Column("y", type_), schema="z") + eq_(t1.c.y.type.schema, "z") + + adapted = t1.c.y.type.adapt(self.MyType) + + eq_(type_.inherit_schema, False) + eq_(adapted.inherit_schema, False) + + eq_(adapted.schema, "z") + + adapted2 = t1.c.y.type.adapt(self.MyType, schema="q") + eq_(adapted2.schema, "q") + def test_independent_schema(self): m = MetaData() type_ = self.MyType(schema="q") @@ -2438,22 +2456,59 @@ class SchemaTypeTest(fixtures.TestBase): def test_inherit_schema(self): m = MetaData() - type_ = self.MyType(schema="q", inherit_schema=True) + type_ = self.MyType(inherit_schema=True) t1 = Table("x", m, Column("y", type_), schema="z") eq_(t1.c.y.type.schema, "z") - def test_independent_schema_enum(self): - m = MetaData() - type_ = sqltypes.Enum("a", schema="q") + @combinations({}, {"inherit_schema": False}, argnames="enum_kw") + @combinations({}, {"schema": "m"}, argnames="meta_kw") + @combinations({}, {"schema": "t"}, argnames="table_kw") + def test_independent_schema_enum_explicit_schema( + self, enum_kw, meta_kw, table_kw + ): + m = MetaData(**meta_kw) + type_ = sqltypes.Enum("a", schema="e", **enum_kw) + t1 = Table("x", m, Column("y", type_), **table_kw) + eq_(t1.c.y.type.schema, "e") + + def test_explicit_schema_w_inherit_raises(self): + with expect_raises_message( + exc.ArgumentError, + "Ambiguously setting inherit_schema=True while also passing " + "a non-None schema argument", + ): + sqltypes.Enum("a", schema="e", inherit_schema=True) + + def test_independent_schema_off_no_explicit_schema(self): + m = MetaData(schema="m") + type_ = sqltypes.Enum("a", inherit_schema=False) t1 = Table("x", m, Column("y", type_), schema="z") - eq_(t1.c.y.type.schema, "q") + eq_(t1.c.y.type.schema, None) - def test_inherit_schema_enum(self): + def test_inherit_schema_enum_auto(self): m = MetaData() - type_ = sqltypes.Enum("a", "b", "c", schema="q", inherit_schema=True) + type_ = sqltypes.Enum("a", "b", "c") t1 = Table("x", m, Column("y", type_), schema="z") eq_(t1.c.y.type.schema, "z") + def test_inherit_schema_enum_meta(self): + m = MetaData(schema="q") + type_ = sqltypes.Enum("a", "b", "c") + t1 = Table("x", m, Column("y", type_), schema="z") + eq_(t1.c.y.type.schema, "z") + + def test_inherit_schema_enum_set_meta(self): + m = MetaData(schema="q") + type_ = sqltypes.Enum("a", "b", "c", metadata=m) + t1 = Table("x", m, Column("y", type_), schema="z") + eq_(t1.c.y.type.schema, "q") + + def test_inherit_schema_enum_set_meta_explicit(self): + m = MetaData(schema="q") + type_ = sqltypes.Enum("a", "b", "c", metadata=m, schema="e") + t1 = Table("x", m, Column("y", type_), schema="z") + eq_(t1.c.y.type.schema, "e") + @testing.variation("assign_metadata", [True, False]) def test_to_metadata_copy_type(self, assign_metadata): m1 = MetaData() @@ -2493,16 +2548,24 @@ class SchemaTypeTest(fixtures.TestBase): t2 = t1.to_metadata(m2) eq_(t2.c.y.type.schema, "z") - def test_to_metadata_independent_schema(self): + @testing.variation("inherit_schema", ["novalue", True, False]) + def test_to_metadata_independent_schema(self, inherit_schema): m1 = MetaData() - type_ = self.MyType() + if inherit_schema.novalue: + type_ = self.MyType() + else: + type_ = self.MyType(inherit_schema=bool(inherit_schema)) + t1 = Table("x", m1, Column("y", type_)) m2 = MetaData() t2 = t1.to_metadata(m2, schema="bar") - eq_(t2.c.y.type.schema, None) + if inherit_schema.novalue or inherit_schema: + eq_(t2.c.y.type.schema, "bar") + else: + eq_(t2.c.y.type.schema, None) @testing.combinations( ("name", "foobar", "name"), @@ -2518,15 +2581,10 @@ class SchemaTypeTest(fixtures.TestBase): eq_(getattr(e1_copy, attrname), value) - @testing.variation("already_has_a_schema", [True, False]) - def test_to_metadata_inherit_schema(self, already_has_a_schema): + def test_to_metadata_inherit_schema(self): m1 = MetaData() - if already_has_a_schema: - type_ = self.MyType(schema="foo", inherit_schema=True) - eq_(type_.schema, "foo") - else: - type_ = self.MyType(inherit_schema=True) + type_ = self.MyType(inherit_schema=True) t1 = Table("x", m1, Column("y", type_)) # note that inherit_schema means the schema mutates to be that diff --git a/test/sql/test_types.py b/test/sql/test_types.py index eb4b420129..1a173f89d1 100644 --- a/test/sql/test_types.py +++ b/test/sql/test_types.py @@ -2820,21 +2820,23 @@ class EnumTest(AssertsCompiledSQL, fixtures.TablesTest): e = Enum("x", "y", name="somename", create_constraint=True) eq_( repr(e), - "Enum('x', 'y', name='somename', create_constraint=True)", + "Enum('x', 'y', name='somename', inherit_schema=True, " + "create_constraint=True)", ) def test_repr_three(self): e = Enum("x", "y", native_enum=False, length=255) eq_( repr(e), - "Enum('x', 'y', native_enum=False, length=255)", + "Enum('x', 'y', inherit_schema=True, " + "native_enum=False, length=255)", ) def test_repr_four(self): e = Enum("x", "y", length=255) eq_( repr(e), - "Enum('x', 'y', length=255)", + "Enum('x', 'y', inherit_schema=True, length=255)", ) def test_length_native(self): @@ -2867,7 +2869,11 @@ class EnumTest(AssertsCompiledSQL, fixtures.TablesTest): def test_none_length_non_native(self): e = Enum("x", "y", native_enum=False, length=None) eq_(e.length, None) - eq_(repr(e), "Enum('x', 'y', native_enum=False, length=None)") + eq_( + repr(e), + "Enum('x', 'y', inherit_schema=True, " + "native_enum=False, length=None)", + ) self.assert_compile(e, "VARCHAR", dialect="default") def test_omit_aliases(self, connection):