--- /dev/null
+.. change::
+ :tags: usecase, postgresql
+ :tickets: 6511
+
+ Added support for reflection of collation in types for PostgreSQL.
+ Pull request courtesy Denis Laxalde.
pg_catalog.pg_attribute.c.attnotnull.label("not_null"),
pg_catalog.pg_class.c.relname.label("table_name"),
pg_catalog.pg_description.c.description.label("comment"),
+ pg_catalog.pg_collation.c.collname.label("collation"),
generated,
identity,
)
== pg_catalog.pg_attribute.c.attnum,
),
)
+ .outerjoin(
+ pg_catalog.pg_type,
+ pg_catalog.pg_type.c.oid == pg_catalog.pg_attribute.c.atttypid,
+ )
+ .outerjoin(
+ pg_catalog.pg_collation,
+ sql.and_(
+ pg_catalog.pg_attribute.c.attcollation
+ != pg_catalog.pg_type.c.typcollation,
+ pg_catalog.pg_collation.c.oid
+ == pg_catalog.pg_attribute.c.attcollation,
+ ),
+ )
.where(self._pg_class_relkind_condition(relkinds))
.order_by(
pg_catalog.pg_class.c.relname, pg_catalog.pg_attribute.c.attnum
domains: Dict[str, ReflectedDomain],
enums: Dict[str, ReflectedEnum],
type_description: str,
+ collation: Optional[str],
) -> sqltypes.TypeEngine[Any]:
"""
Attempts to reconstruct a column type defined in ischema_names based
domains,
enums,
type_description="DOMAIN '%s'" % domain["name"],
+ collation=domain["collation"],
)
args = (domain["name"], data_type)
)
return sqltypes.NULLTYPE
+ if collation is not None:
+ kwargs["collation"] = collation
+
data_type = schema_type(*args, **kwargs)
if array_dim >= 1:
# postgres does not preserve dimensionality or size of array types.
domains,
enums,
type_description="column '%s'" % row_dict["name"],
+ collation=row_dict["collation"],
)
default = row_dict["default"]
and their reflection"""
return exclusions.closed()
+ @property
+ def column_collation_reflection(self):
+ """Indicates if the database support column collation reflection"""
+ return exclusions.open()
+
@property
def view_column_reflection(self):
"""target database must support retrieval of the columns in a view,
"default": None,
"not_null": False,
"comment": None,
+ "collation": None,
"generated": "",
"identity_options": None,
}
"default": None,
"not_null": False,
"comment": None,
+ "collation": None,
"generated": "",
"identity_options": None,
}
from sqlalchemy.testing import is_
from sqlalchemy.testing import is_false
from sqlalchemy.testing import is_instance_of
+from sqlalchemy.testing import is_none
from sqlalchemy.testing import is_not
from sqlalchemy.testing import is_true
from sqlalchemy.testing import mock
eq_(t3.comment, "t1 comment")
eq_(t3.c.id.comment, "c1 comment")
+ @testing.requires.column_collation_reflection
+ def test_column_collation_reflection(self, connection, metadata):
+ m1 = metadata
+ Table(
+ "t",
+ m1,
+ Column("collated", sa.String(collation="C")),
+ Column("not_collated", sa.String()),
+ )
+ m1.create_all(connection)
+
+ m2 = MetaData()
+ t2 = Table("t", m2, autoload_with=connection)
+
+ eq_(t2.c.collated.type.collation, "C")
+ is_none(t2.c.not_collated.type.collation)
+
+ insp = inspect(connection)
+ collated, not_collated = insp.get_columns("t")
+ eq_(collated["type"].collation, "C")
+ is_none(not_collated["type"].collation)
+
@testing.requires.check_constraint_reflection
def test_check_constraint_reflection(self, connection, metadata):
m1 = metadata
def constraint_comment_reflection(self):
return only_on(["postgresql"])
+ @property
+ def column_collation_reflection(self):
+ return only_on(["postgresql"])
+
@property
def unbounded_varchar(self):
"""Target database must support VARCHAR with no length"""