2.1.2
Released: October 2, 2026
orm
-
[orm] [bug] [regression] Fixed regression caused by #5987 where the
selectinload()loader strategy would ignore additional criteria present in the_orm.relationship.primaryjoinof a many-to-many relationship, such as a comparison against a column on the association table or on the parent table, loading related rows that should have been excluded. Theomit_joinoptimization for many-to-many relationships is now only used when the primaryjoin consists solely of comparisons between the parent's primary key columns and the association table.References: #13626
engine
-
[engine] [bug] [regression] Fixed regression where result rows delivered by the DBAPI as a subclass of
tuplewould fail to be processed when the Cython extensions were in use, raisingTypeError: Expected tuple. Such rows are now converted to a plaintuple, as was the case in 2.0. Rows delivered as a plaintuple, or as a sequence that is not atupleat all such as asyncpg'sRecord, were not affected. Drivers such as the Databricks SQL connector return rows that aretuplesubclasses.References: #13619
sql
-
[sql] [bug] The plain string portions of a
_sql.tstring()construct are no longer scanned for bound parameter names in the:nameformat, and no longer interpret the\:escape sequence; the text is now rendered exactly as given. Previously, each string portion was parsed in the same way as_sql.text(), so that a colon within a quoted SQL string, such as'time is :now', would be rendered as a bound parameter, failing at execution time.This change is a breaking change in the behavior of the newly introduced
_sql.tstring()feature, as the former use oftext()inadvertently provided a behavior that wasn't intended. In order to embed an explicit parameter in the SQL generated by_sql.tstring(),_sql.bindparam()may be used directly, e.g.tstring(t"SELECT {bindparam('p')}").References: #13616
schema
-
[schema] [bug] [regression] Fixed issue where the internal copies of
Enum,_postgresql.DOMAINand otherSchemaTypetypes produced when the type is adapted to a dialect-specific implementation would register themselves with theMetaDatacollection that is queried byMetaData.get_schema_objects(), in addition to the original type. As a new dialect-level copy is produced for each newDialectinstance, such as each timestr()is called on a statement referring to a column usingEnum, the collection would grow without bound and lead to excessive memory use. Only the type constructed by the user is now registered.References: #13625