From: Michael Bayer Date: Tue, 21 Jul 2026 13:19:39 +0000 (+0000) Subject: Revert "Use identifier preparer for string type collations" X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=2c1ef72b575076375f3dc596460d88ac0e6d5522;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Revert "Use identifier preparer for string type collations" This reverts commit 06fd857c553a9e9cc7b4f30d179ee7582655e7e7. Reason for revert: this first looked like a small place we needed a workaround, but nothing is ever like that. unfortunately changing it in this way implies that we aren't using `collation_schema` in other places as well, using the awkward quoted_name workaround, which then leads into more awkwardness as we want collation schemas to be reflected, too. would we be creating hand-quoted quoted_names for reflection also? obviously not. I assume I didnt consider these aspects when i tried to do a "less intrusive" change. I would like to do just one gerrit that adds collation_schema in all places we need it, plus full reflection, at once, rather than the drip-drip that was not making the scope of the problem clear. Change-Id: I9b21d87f575625188e6c332889c38a4aa7fa787f --- diff --git a/doc/build/changelog/unreleased_21/9693.rst b/doc/build/changelog/unreleased_21/9693.rst deleted file mode 100644 index dfb2407cc2..0000000000 --- a/doc/build/changelog/unreleased_21/9693.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. change:: - :tags: usecase, schema, postgresql - :tickets: 9693 - - Adjusted string type collation rendering to use the dialect identifier - preparer rather than unconditional quoting. This allows PostgreSQL - schema-qualified collation names to be passed using - :class:`.quoted_name` with ``quote=False``. Pull request courtesy - cjc0013. - - .. seealso:: - - :ref:`postgresql_collation` diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 8d0ac04035..5a815bfa7b 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -1773,46 +1773,6 @@ itself: .. versionadded:: 1.4.0b2 -.. _postgresql_collation: - -Schema-Qualified Collation Names ---------------------------------- - -.. versionchanged:: 2.1 String type collation rendering now uses the - dialect's identifier preparer rather than surrounding the collation - name with double quotes unconditionally. Simple lowercase names are - rendered unquoted; mixed-case or special-character names are quoted - automatically. :class:`.quoted_name` with ``quote=True`` or - ``quote=False`` is now also interpreted. - -PostgreSQL supports collations that are qualified by a schema name, such as -``pg_catalog.default`` or ``myschema.my_collation``. When passing a -schema-qualified collation to the :paramref:`.String.collation` parameter, -use :class:`.quoted_name` with ``quote=False`` so that the dot separator -is not quoted as part of the identifier:: - - from sqlalchemy import Column, Text - from sqlalchemy.sql import quoted_name - - Table( - "my_table", - metadata, - Column( - "data", - Text(collation=quoted_name("pg_catalog.default", False)), - ), - ) - -The above will render the column type as -``TEXT COLLATE pg_catalog.default``. Without :class:`.quoted_name`, the -collation string would be quoted as a single identifier, producing -``TEXT COLLATE "pg_catalog.default"``, which PostgreSQL would reject. - -.. seealso:: - - :class:`.quoted_name` - - :paramref:`.String.collation` """ # noqa: E501 diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index e8dd677205..42b64ec726 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -7687,9 +7687,7 @@ class GenericTypeCompiler(TypeCompiler): if length: text += f"({length})" if collation: - text += ( - f" COLLATE {self.dialect.identifier_preparer.quote(collation)}" - ) + text += f' COLLATE "{collation}"' return text def visit_CHAR(self, type_: sqltypes.CHAR, **kw: Any) -> str: diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index 459d44a650..a3ce81da58 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -237,11 +237,6 @@ class String(Concatenable, TypeEngine[str]): >>> print(select(cast("some string", String(collation="utf8")))) {printsql}SELECT CAST(:param_1 AS VARCHAR COLLATE utf8) AS anon_1 - For backends that support qualified collation names, a - :class:`.quoted_name` with ``quote=False`` may be used to render the - qualified name without applying quotes to the separator. See - :ref:`postgresql_collation` for a PostgreSQL example. - .. note:: In most cases, the :class:`.Unicode` or :class:`.UnicodeText` diff --git a/test/sql/test_types.py b/test/sql/test_types.py index 2568417629..fd82a34ea0 100644 --- a/test/sql/test_types.py +++ b/test/sql/test_types.py @@ -78,7 +78,6 @@ from sqlalchemy.sql import ddl from sqlalchemy.sql import elements from sqlalchemy.sql import null from sqlalchemy.sql import operators -from sqlalchemy.sql import quoted_name from sqlalchemy.sql import sqltypes from sqlalchemy.sql import table from sqlalchemy.sql import type_api @@ -3493,7 +3492,7 @@ class ExpressionTest( (lambda c1: c1.like("qpr"), "q LIKE :q_1->BINDCAST->[TEXT]"), ( lambda c2: c2.like("qpr"), - "q LIKE :q_1->BINDCAST->[TEXT COLLATE xyz]", + 'q LIKE :q_1->BINDCAST->[TEXT COLLATE "xyz"]', ), ( # new behavior, a type with no collation passed into collate() @@ -3501,11 +3500,11 @@ class ExpressionTest( # on the right side bind-cast. previous to #11576 we'd only # get TEXT for the bindcast. lambda c1: collate(c1, "abc").like("qpr"), - "(q COLLATE abc) LIKE :param_1->BINDCAST->[TEXT COLLATE abc]", + '(q COLLATE abc) LIKE :param_1->BINDCAST->[TEXT COLLATE "abc"]', ), ( lambda c2: collate(c2, "abc").like("qpr"), - "(q COLLATE abc) LIKE :param_1->BINDCAST->[TEXT COLLATE abc]", + '(q COLLATE abc) LIKE :param_1->BINDCAST->[TEXT COLLATE "abc"]', ), argnames="testcase,expected", ) @@ -3550,7 +3549,7 @@ class ExpressionTest( ) self.assert_compile( c2.like("qpr"), - "q LIKE :q_1->BINDCAST->[TEXT COLLATE xyz]", + 'q LIKE :q_1->BINDCAST->[TEXT COLLATE "xyz"]', dialect=renders_bind_cast, ) @@ -4034,20 +4033,6 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL): def test_text_collation(self): self.assert_compile(Text(collation="FOO"), 'TEXT COLLATE "FOO"') - @testing.combinations( - ("foo", "TEXT COLLATE foo"), - ("FooIdentifier", 'TEXT COLLATE "FooIdentifier"'), - ("foo identifier", 'TEXT COLLATE "foo identifier"'), - (quoted_name("foo", True), 'TEXT COLLATE "foo"'), - ( - quoted_name("schema_name.collation_name", False), - "TEXT COLLATE schema_name.collation_name", - ), - argnames="collation,expected", - ) - def test_text_collation_identifier_preparer(self, collation, expected): - self.assert_compile(Text(collation=collation), expected) - def test_default_compile_pg_inet(self): self.assert_compile( dialects.postgresql.INET(), "INET", allow_dialect_select=True