Repository navigation
2.1.7
2.1.7
Released: November 3, 2026
orm
-
[orm] [feature] Added
_orm.SessionTransaction.connectionsand
_orm.SessionTransaction.bound_connection(), which return the
connections a_orm.SessionTransactionhas already acquired,
without acquiring a new one. This allows, for example, checking whether
any connection in the transaction has been invalidated before emitting
further SQL. A SAVEPOINT transaction returned by
_orm.Session.begin_nested()reports only the connections used
within that SAVEPOINT; the root transaction returned by
_orm.Session.get_transaction()reports all connections. The same
accessors are available on_asyncio.AsyncSessionTransaction,
returning_asyncio.AsyncConnectionobjects. Pull request
courtesy Artur Shiriev.References: #13685
-
[orm] [bug] An informative
InvalidRequestErroris now raised when more than
onePropComparator.of_type()loader option is applied along the
same relationship using targets of differing classes, such as two
_orm.aliased()constructs against different subclasses, which
previously failed with a bareAssertionErrorduring compilation.
These options can't be combined since each target brings its own aliased
copy of the base table; to load multiple subclasses along a single
relationship, a single_orm.with_polymorphic()object that includes
all of the target classes should be used.This change is also backported to: 2.0.56
References: #13751
-
[orm] [bug] Fixed issue where criteria applied to a relationship using
PropComparator.and_()within_orm.selectinload()or
_orm.lazyload()would not be parenthesized against the
relationship's join condition when the enclosing statement was retrieved
from the compiled cache, so that criteria consisting of an
_sql.or_()would render asa = ? AND b = ? OR c = ?. For
_orm.lazyload(), this could load rows belonging to other parents into
the collection. The issue typically appeared as a race condition in
multithreaded applications, but could also occur within a single thread
depending on the order in which statements were executed.This change is also backported to: 2.0.56
References: #13753
-
[orm] [bug] [regression] Fixed regression caused by #13070 where using two or more
orm.with_loader_criteria()options for different entities, where
the criteria of each referred to the other entity, such as by using
RelationshipProperty.Comparator.any()and
RelationshipProperty.Comparator.has(), would fail with a
RecursionErrorwhen compiling. A loader criteria option is now
no longer applied to a SELECT that's nested at any level within its own
criteria; previously this was only checked for the immediate SELECT
within the criteria. This also repairs the sameRecursionErrorfor
criteria which refer to each other using explicit subqueries, an issue
also present in the 2.0 series. No backport is planned at the moment as
this is an unusual use case.References: #13714
-
[orm] [bug] Fixed issue where
_orm.Session.merge_all()did not disable autoflush
while merging, as_orm.Session.merge()does, so that a lazy load
emitted during the merge cascade could autoflush a partially merged
object, leading to integrity errors or other failures.References: #13715
-
[orm] [bug] [regression] Fixed regression caused by #12168 where a collection-based
_orm.relationship()using the
orm_declarative_native_dataclassesfeature with a custom
_orm.relationship.collection_classsuch as
_orm.attribute_keyed_dict()would raise an error if
_orm.relationship.default_factorywere set to the plain
dict,listorsettype that the collection class is based on,
such asdefault_factory=dictfor_orm.attribute_keyed_dict().
The plain collection type is now accepted as the
_orm.relationship.default_factoryin addition to the exact
collection class.References: #13718
-
[orm] [bug] [regression] Fixed regression caused by #5987 where the
selectinload()
loader strategy, when loading a many-to-many relationship against an
aliased target, such as when usingPropComparator.of_type()with a
single-inheritance subclass or analiased()construct, would render
the join between the association table and the aliased target using the
unaliased table name, producing invalid SQL.References: #13726
-
[orm] [bug] [regression] Fixed issue where a SAVEPOINT started by
_orm.Session.begin_nested()
would apply execution options to the connection a second time, causing
options such asisolation_levelto raise. Additionally, execution
options passed to_orm.Session.connection()within a SAVEPOINT are
now ignored with a warning if the connection is already present, as
documented, where previously they were applied. -
[orm] [bug] [regression] Fixed regression caused by #12168 where a
_orm.mapped_column()using
_orm.mapped_column.use_existing_columnalong with
_orm.mapped_column.defaultin an
orm_declarative_native_dataclassesmapping would not persist the
given default value. As the existing_schema.Columnfrom the
superclass table is mapped in this case, the default value would not take
effect for INSERT, and the default from the superclass column, if any,
would be used instead. The unit of work now delivers the dataclass
default of an attribute that was never set to the INSERT statement in
those cases where the mapped_schema.Columndoes not supply that
same default, while the attribute continues to remain un-set until the
object is flushed, so that the behaviors established by #12168
are maintained.This includes a behavioral change for an attribute that is set to
None
explicitly, which the ORM treats as the default case for a column that has
a default, as described atsession_forcing_null. Such an attribute
now receives the dataclass default of the subclass, consistent with an
attribute that was never set, where previously the default of the
superclass column would take effect.References: #13748
engine
-
[engine] [bug] Fixed issue where the
_sa.create_engine.hide_parameters
parameter was not honored when logging bound parameters for "side"
statements that are executed separately from the main statement, such as
pre-executed SQL expression column defaults, so that parameter values were
written to the log in plain form. These parameters are now hidden in the
same way as those of the main statement.This change is also backported to: 2.0.56
References: #13724
-
[engine] [bug] Fixed issue where event listeners established on the
_engine.Connectionclass itself, such as
_events.ConnectionEvents.before_execute(), would not be invoked
for connections procured from an_engine.Enginethat did not
itself have any event listeners associated with it.This change is also backported to: 2.0.56
References: #13743
-
[engine] [bug] Fixed issue where
URL.difference_update_query()would not remove
any parameters when passed a generator or other single-use iterable, as
the iterable was consumed twice internally. Pull request courtesy
Huang-404-Q.This change is also backported to: 2.0.56
References: #13757
-
[engine] [bug] [regression] Fixed regression where a checked-out pool connection that was garbage
collected as part of a reference cycle would not be returned to the pool
on Python 3.15, permanently consuming a slot in the pool. This occurred
because Python 3.15 changed the garbage collector to clear weak references
that have no callback only after weak reference callbacks have run, which
interfered with the pool'sweakref.finalize()based cleanup introduced
in 2.1.0rc2. An ORM_orm.Sessionor_engine.Connection
that was dropped without being closed while in a transaction was affected.References: #13738
sql
-
[sql] [bug] [oracle] [postgresql] Reworked the validation applied to the keyword phrases accepted by the
_schema.ForeignKeyConstraint.ondelete,
_schema.ForeignKeyConstraint.onupdate,
_schema.ForeignKeyConstraint.initiallyand
_schema.ForeignKeyConstraint.matchparameters so that it is
applied consistently on all backends. Previously, some of these phrases
were checked against a fixed list of keywords, which rejected
backend-specific syntaxes, while others were not validated at all,
includingMATCHon all backends,ON DELETEon the Oracle dialect,
and the column list accepted by PostgreSQL forSET NULL/SET DEFAULT, allowing arbitrary text to be rendered into DDL. These phrases
are now accepted as any sequence of SQL keywords, leaving it to the
database to determine if the keywords are valid, and a
CompileErroris raised for a phrase that contains other
characters such as punctuation or quotes. On PostgreSQL, the column list
forSET NULL/SET DEFAULTis accepted as a comma-separated list
of plain or double-quoted column names. Pull request courtesy
basavaraj-sm05.This change is also backported to: 2.0.56
References: #13727
-
[sql] [bug] [regression] Fixed regression caused by #12736 where the
_sql.ColumnOperators.is_()and_sql.ColumnOperators.is_not()
operators were assigned to theOperatorClass.COMPARISONoperator
class, which is not present on all datatypes. As these operators are valid
for every SQL datatype, they are now part ofOperatorClass.BASE,
so that expressions such ashstore_column.is_not(None)no longer emit a
deprecation warning.References: #13719
typing
-
[typing] [bug] Fixed typing issue where constructs that infer their Python type from a
SQL type argument could not be assigned to a wider declared type, such as
x: Column[int | None] = Column(Integer)orx: ColumnElement[int | None] = cast(expr, Integer), which raised an incompatible argument error
under mypy and an incompatible assignment error under pyright. The
construct still infers its type from the given SQL type, where
Column(Integer)is typed asColumn[int]; this type may now be
widened by the declared type, which is also where nullability is
indicated. This applies to_schema.Column,cast(),
_sql.try_cast(),type_coerce(),bindparam(),
_sql.literal(),_sql.column(),_sql.literal_column(),
_sql.label(),_expression.ColumnElement.cast(),
Function,_types.ARRAYand
_postgresql.ARRAY, including when these are passed to ORM
constructs such as_orm.column_property(),_orm.deferred()
and_orm.query_expression()that are assigned to a wider
Mapped[]annotation.This includes assignment of a
_schema.Columnto a wider
_schema.Namedannotation within a_schema.TypedColumns
class, e.g.x: Named[int | None] = Column(SmallInteger), for which
_schema.Namedis now covariant.References: #13654
asyncio
-
[asyncio] [bug] Fixed issue where an asyncio cancellation delivered to a task waiting for
a connection fromAsyncAdaptedQueuePoolwould be lost on Python
3.11, if a connection were returned to the pool in the same event loop
iteration in which the task was cancelled. The task would receive the
connection and continue running as though it had not been cancelled.
This was due to a known issue inasyncio.wait_for()which is fixed in
Python 3.12; on Python 3.11, the pool now usesasyncio.timeout()in
place ofasyncio.wait_for()when waiting for a connection. The issue
remains on Python 3.10 and earlier, whereasyncio.timeout()is not
available.This change is also backported to: 2.0.56
References: #13749
mssql
-
[mssql] [bug] [reflection] Reflection of SQL Server local temporary tables, such as
#tmp, now
supports tables of the same name existing in multiple sessions at once,
each session reflecting its own table. Previously, the table was located
without the use of theobject_id()function, which is what limits the
lookup to the current session, so that same-named tables in other sessions
would interfere with reflection. The change also eliminates blocking and
deadlocks which could occur when reflecting a temporary table while other
sessions were creating temporary tables of a similar name within a
transaction.This change is also backported to: 2.0.56
References: #13754