.. 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
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
(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()
# 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",
)
)
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,
)
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