tag:github.com,2008:https://github.com/sqlalchemy/sqlalchemy/releasesRelease notes from sqlalchemy2026-03-02T14:39:42Ztag:github.com,2008:Repository/159271175/rel_2_0_482026-03-02T16:07:48Z2.0.48<h1>2.0.48</h1>
<p>Released: March 2, 2026</p>
<h2>engine</h2>
<ul>
<li>
<p><strong>[engine] [bug]</strong> Fixed a critical issue in <code>Engine</code> where connections created in<br>
conjunction with the <code>DialectEvents.do_connect()</code> event listeners<br>
would receive shared, mutable collections for the connection arguments,<br>
leading to a variety of potential issues including unlimited growth of the<br>
argument list as well as elements within the parameter dictionary being<br>
shared among concurrent connection calls. In particular this could impact<br>
do_connect routines making use of complex mutable authentication<br>
structures.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13144" rel="nofollow">#13144</a></p>
</li>
</ul>sqla-testertag:github.com,2008:Repository/159271175/rel_2_0_472026-02-24T17:28:51Z2.0.47<h1>2.0.47</h1>
<p>Released: February 24, 2026</p>
<h2>orm</h2>
<ul>
<li>
<p><strong>[orm] [bug]</strong> Fixed issue when using ORM mappings with Python 3.14's <a href="https://peps.python.org/pep-0649" rel="nofollow">PEP 649</a> feature<br>
that no longer requires "future annotations", where the ORM's introspection<br>
of the <code>__init__</code> method of mapped classes would fail if non-present<br>
identifiers in annotations were present. The vendored <code>getfullargspec()</code><br>
method has been amended to use <code>Format.FORWARDREF</code> under Python 3.14 to<br>
prevent resolution of names that aren't present.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13104" rel="nofollow">#13104</a></p>
</li>
</ul>
<h2>engine</h2>
<ul>
<li>
<p><strong>[engine] [usecase]</strong> The connection object returned by <code>_engine.Engine.raw_connection()</code><br>
now supports the context manager protocol, automatically returning the<br>
connection to the pool when exiting the context.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13116" rel="nofollow">#13116</a></p>
</li>
</ul>
<h2>postgresql</h2>
<ul>
<li>
<p><strong>[postgresql] [bug]</strong> Fixed an issue in the PostgreSQL dialect where foreign key constraint<br>
reflection would incorrectly swap or fail to capture <code>onupdate</code> and<br>
<code>ondelete</code> values when these clauses appeared in a different order than<br>
expected in the constraint definition. This issue primarily affected<br>
PostgreSQL-compatible databases such as CockroachDB, which may return <code>ON DELETE</code> before <code>ON UPDATE</code> in the constraint definition string. The<br>
reflection logic now correctly parses both clauses regardless of their<br>
ordering.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13105" rel="nofollow">#13105</a></p>
</li>
<li>
<p><strong>[postgresql] [bug]</strong> Fixed issue in the <code>engine_insertmanyvalues</code> feature where using<br>
PostgreSQL's <code>ON CONFLICT</code> clause with<br>
<code>_dml.Insert.returning.sort_by_parameter_order</code> enabled would<br>
generate invalid SQL when the insert used an implicit sentinel (server-side<br>
autoincrement primary key). The generated SQL would incorrectly declare a<br>
sentinel counter column in the <code>imp_sen</code> table alias without providing<br>
corresponding values in the <code>VALUES</code> clause, leading to a<br>
<code>ProgrammingError</code> indicating column count mismatch. The fix allows batch<br>
execution mode when <code>embed_values_counter</code> is active, as the embedded<br>
counter provides the ordering capability needed even with upsert behaviors,<br>
rather than unnecessarily downgrading to row-at-a-time execution.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13107" rel="nofollow">#13107</a></p>
</li>
<li>
<p><strong>[postgresql] [bug]</strong> Fixed issue where <code>_postgresql.Insert.on_conflict_do_update()</code><br>
parameters were not respecting compilation options such as<br>
<code>literal_binds=True</code>. Pull request courtesy Loïc Simon.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13110" rel="nofollow">#13110</a></p>
</li>
<li>
<p><strong>[postgresql] [bug]</strong> Fixed issue where <code>_postgresql.Insert.on_conflict_do_update()</code><br>
using parametrized bound parameters in the <code>set_</code> clause would fail<br>
when used with executemany batching. For dialects that use the<br>
<code>use_insertmanyvalues_wo_returning</code> optimization (psycopg2),<br>
insertmanyvalues is now disabled when there is an ON CONFLICT clause.<br>
For cases with RETURNING, row-at-a-time mode is used when the SET<br>
clause contains parametrized bindparams (bindparams that receive<br>
values from the parameters dict), ensuring each row's parameters are<br>
correctly applied. ON CONFLICT statements using expressions like<br>
<code>excluded.<column></code> continue to batch normally.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13130" rel="nofollow">#13130</a></p>
</li>
</ul>
<h2>mysql</h2>
<ul>
<li>
<p><strong>[mysql] [bug]</strong> Fixed issue where DDL compilation options were registered to the hard-coded<br>
dialect name <code>mysql</code>. This made it awkward for MySQL-derived dialects<br>
like MariaDB, StarRocks, etc. to work with such options when different sets<br>
of options exist for different platforms. Options are now registered under<br>
the actual dialect name, and a fallback was added to help avoid errors when<br>
an option does not exist for that dialect.</p>
<p>To maintain backwards compatibility, when using the MariaDB dialect with<br>
the options <code>mysql_with_parser</code> or <code>mysql_using</code> without also specifying<br>
the corresponding <code>mariadb_</code> prefixed options, a deprecation warning will<br>
be emitted. The <code>mysql_</code> prefixed options will continue to work during<br>
the deprecation period. Users should update their code to additionally<br>
specify <code>mariadb_with_parser</code> and <code>mariadb_using</code> when using the<br>
<code>mariadb://</code> dialect, or specify both options to support both dialects.</p>
<p>Pull request courtesy Tiansu Yu.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13134" rel="nofollow">#13134</a></p>
</li>
</ul>
<h2>sqlite</h2>
<ul>
<li>
<p><strong>[sqlite] [bug]</strong> Fixed issue where <code>_sqlite.Insert.on_conflict_do_update()</code><br>
parameters were not respecting compilation options such as<br>
<code>literal_binds=True</code>. Pull request courtesy Loïc Simon.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13110" rel="nofollow">#13110</a></p>
</li>
<li>
<p><strong>[sqlite] [bug]</strong> Fixed issue where <code>_sqlite.Insert.on_conflict_do_update()</code><br>
using parametrized bound parameters in the <code>set_</code> clause would fail<br>
when used with executemany batching. Row-at-a-time mode is now used<br>
for ON CONFLICT statements with RETURNING that contain parametrized<br>
bindparams, ensuring each row's parameters are correctly applied. ON<br>
CONFLICT statements using expressions like <code>excluded.<column></code><br>
continue to batch normally.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13130" rel="nofollow">#13130</a></p>
</li>
</ul>sqla-testertag:github.com,2008:Repository/159271175/rel_2_1_0b12026-01-21T21:19:04Z2.1.0b1<h1>2.1.0b1</h1>
<p>Released: January 21, 2026</p>
<h2>platform</h2>
<ul>
<li>
<p><strong>[platform] [feature]</strong> Free-threaded Python versions are now supported in wheels released on Pypi.<br>
This integrates with overall free-threaded support added as part of<br>
<a href="https://www.sqlalchemy.org/trac/ticket/12881" rel="nofollow">#12881</a> for the 2.0 and 2.1 series, which includes new test suites<br>
as well as a few improvements to race conditions observed under<br>
freethreading.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12881" rel="nofollow">#12881</a></p>
</li>
<li>
<p><strong>[platform] [change]</strong> The <code>greenlet</code> dependency used for asyncio support no longer installs<br>
by default. This dependency does not publish wheel files for every architecture<br>
and is not needed for applications that aren't using asyncio features.<br>
Use the <code>sqlalchemy[asyncio]</code> install target to include this dependency.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10197" rel="nofollow">#10197</a></p>
</li>
<li>
<p><strong>[platform] [change]</strong> Updated the setup manifest definition to use PEP 621-compliant<br>
pyproject.toml. Also updated the extra install dependency to comply with<br>
PEP-685. Thanks for the help of Matt Oberle and KOLANICH on this change.</p>
</li>
<li>
<p><strong>[platform] [change]</strong> Python 3.10 or above is now required; support for Python 3.9, 3.8 and 3.7<br>
is dropped as these versions are EOL.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10357" rel="nofollow">#10357</a>, <a href="https://www.sqlalchemy.org/trac/ticket/12029" rel="nofollow">#12029</a>, <a href="https://www.sqlalchemy.org/trac/ticket/12819" rel="nofollow">#12819</a></p>
</li>
</ul>
<h2>orm</h2>
<ul>
<li>
<p><strong>[orm] [feature]</strong> The <code>_orm.relationship.back_populates</code> argument to<br>
<code>_orm.relationship()</code> may now be passed as a Python callable, which<br>
resolves to either the direct linked ORM attribute, or a string value as<br>
before. ORM attributes are also accepted directly by<br>
<code>_orm.relationship.back_populates</code>. This change allows type<br>
checkers and IDEs to confirm the argument for<br>
<code>_orm.relationship.back_populates</code> is valid. Thanks to Priyanshu<br>
Parikh for the help on suggesting and helping to implement this feature.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10050" rel="nofollow">#10050</a></p>
</li>
<li>
<p><strong>[orm] [feature]</strong> Added new hybrid method <code>hybrid_property.bulk_dml()</code> which<br>
works in a similar way as <code>hybrid_property.update_expression()</code> for<br>
bulk ORM operations. A user-defined class method can now populate a bulk<br>
insert mapping dictionary using the desired hybrid mechanics. New<br>
documentation is added showing how both of these methods can be used<br>
including in combination with the new <code>_sql.from_dml_column()</code><br>
construct.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12496" rel="nofollow">#12496</a></p>
</li>
<li>
<p><strong>[orm] [feature]</strong> Added new parameter <code>_orm.composite.return_none_on</code> to<br>
<code>_orm.composite()</code>, which allows control over if and when this<br>
composite attribute should resolve to <code>None</code> when queried or retrieved<br>
from the object directly. By default, a composite object is always present<br>
on the attribute, including for a pending object which is a behavioral<br>
change since 2.0. When <code>_orm.composite.return_none_on</code> is<br>
specified, a callable is passed that returns True or False to indicate if<br>
the given arguments indicate the composite should be returned as None. This<br>
parameter may also be set automatically when ORM Annotated Declarative is<br>
used; if the annotation is given as <code>Mapped[SomeClass|None]</code>, a<br>
<code>_orm.composite.return_none_on</code> rule is applied that will return<br>
<code>None</code> if all contained columns are themselves <code>None</code>.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12570" rel="nofollow">#12570</a></p>
</li>
<li>
<p><strong>[orm] [feature]</strong> Added support for per-session execution options that are merged into all<br>
queries executed within that session. The <code>_orm.Session</code>,<br>
<code>_orm.sessionmaker</code>, <code>_orm.scoped_session</code>,<br>
<code>_ext.asyncio.AsyncSession</code>, and<br>
<code>_ext.asyncio.async_sessionmaker</code> constructors now accept an<br>
<code>_orm.Session.execution_options</code> parameter that will be applied<br>
to all explicit query executions (e.g. using <code>_orm.Session.execute()</code>,<br>
<code>_orm.Session.get()</code>, <code>_orm.Session.scalars()</code>) for that session<br>
instance.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12659" rel="nofollow">#12659</a></p>
</li>
<li>
<p><strong>[orm] [feature]</strong> Session autoflush behavior has been simplified to unconditionally flush the<br>
session each time an execution takes place, regardless of whether an ORM<br>
statement or Core statement is being executed. This change eliminates the<br>
previous conditional logic that only flushed when ORM-related statements<br>
were detected, which had become difficult to define clearly with the unified<br>
v2 syntax that allows both Core and ORM execution patterns. The change<br>
provides more consistent and predictable session behavior across all types<br>
of SQL execution.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/9809" rel="nofollow">#9809</a></p>
</li>
<li>
<p><strong>[orm] [feature]</strong> Added <code>_orm.RegistryEvents</code> event class that allows event listeners<br>
to be established on a <code>_orm.registry</code> object. The new class<br>
provides three events: <code>_orm.RegistryEvents.resolve_type_annotation()</code><br>
which allows customization of type annotation resolution that can<br>
supplement or replace the use of the<br>
<code>registry.type_annotation_map</code> dictionary, including that it can<br>
be helpful with custom resolution for complex types such as those of<br>
<a href="https://peps.python.org/pep-0695" rel="nofollow">PEP 695</a>, as well as <code>_orm.RegistryEvents.before_configured()</code> and<br>
<code>_orm.RegistryEvents.after_configured()</code>, which are registry-local<br>
forms of the mapper-wide version of these hooks.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/9832" rel="nofollow">#9832</a></p>
</li>
<li>
<p><strong>[orm] [usecase]</strong> The <code>_orm.Session.flush.objects</code> parameter is now<br>
deprecated.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10816" rel="nofollow">#10816</a></p>
</li>
<li>
<p><strong>[orm] [usecase]</strong> Added the utility method <code>_orm.Session.merge_all()</code> and<br>
<code>_orm.Session.delete_all()</code> that operate on a collection<br>
of instances.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/11776" rel="nofollow">#11776</a></p>
</li>
<li>
<p><strong>[orm] [usecase]</strong> Added support for using <code>_orm.with_expression()</code> to populate a<br>
<code>_orm.query_expression()</code> attribute that is also configured as the<br>
<code>polymorphic_on</code> discriminator column. The ORM now detects when a query<br>
expression column is serving as the polymorphic discriminator and updates<br>
it to use the column provided via <code>_orm.with_expression()</code>, enabling<br>
polymorphic loading to work correctly in this scenario. This allows for<br>
patterns such as where the discriminator value is computed from a related<br>
table.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12631" rel="nofollow">#12631</a></p>
</li>
<li>
<p><strong>[orm] [usecase]</strong> Added default implementations of <code>ColumnOperators.desc()</code>,<br>
<code>ColumnOperators.asc()</code>, <code>ColumnOperators.nulls_first()</code>,<br>
<code>ColumnOperators.nulls_last()</code> to <code>_orm.composite()</code> attributes,<br>
by default applying the modifier to all contained columns. Can be<br>
overridden using a custom comparator.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12769" rel="nofollow">#12769</a></p>
</li>
<li>
<p><strong>[orm] [usecase]</strong> The <code>_orm.aliased()</code> object now emits warnings when an attribute is<br>
accessed on an aliased class that cannot be located in the target<br>
selectable, for those cases where the <code>_orm.aliased()</code> is against a<br>
different FROM clause than the regular mapped table (such as a subquery).<br>
This helps users identify cases where column names don't match between the<br>
aliased class and the underlying selectable. When<br>
<code>_orm.aliased.adapt_on_names</code> is <code>True</code>, the warning suggests<br>
checking the column name; when <code>False</code>, it suggests using the<br>
<code>adapt_on_names</code> parameter for name-based matching.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12838" rel="nofollow">#12838</a></p>
</li>
<li>
<p><strong>[orm] [usecase]</strong> Improvements to the use case of using <code>Declarative Dataclass Mapping <orm_declarative_native_dataclasses></code> with intermediary classes that are<br>
unmapped. As was the existing behavior, classes can subclass<br>
<code>_orm.MappedAsDataclass</code> alone without a declarative base to act as<br>
mixins, or along with a declarative base as well as <code>__abstract__ = True</code><br>
to define an abstract base. However, the improved behavior scans ORM<br>
attributes like <code>_orm.mapped_column()</code> in this case to create correct<br>
<code>dataclasses.field()</code> constructs based on their arguments, allowing for<br>
more natural ordering of fields without dataclass errors being thrown.<br>
Additionally, added a new <code>_orm.unmapped_dataclass()</code> decorator<br>
function, which may be used to create unmapped mixins in a mapped hierarchy<br>
that is using the <code>_orm.mapped_dataclass()</code> decorator to create mapped<br>
dataclasses.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12854" rel="nofollow">#12854</a></p>
</li>
<li>
<p><strong>[orm] [usecase]</strong> Added <code>_orm.DictBundle</code> as a subclass of <code>_orm.Bundle</code><br>
that returns <code>dict</code> objects.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12960" rel="nofollow">#12960</a></p>
</li>
<li>
<p><strong>[orm] [change]</strong> A sweep through class and function names in the ORM renames many classes<br>
and functions that have no intent of public visibility to be underscored.<br>
This is to reduce ambiguity as to which APIs are intended to be targeted by<br>
third party applications and extensions. Third parties are encouraged to<br>
propose new public APIs in Discussions to the extent they are needed to<br>
replace those that have been clarified as private.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10497" rel="nofollow">#10497</a></p>
</li>
<li>
<p><strong>[orm] [change]</strong> The <code>first_init</code> ORM event has been removed. This event was<br>
non-functional throughout the 1.4 and 2.0 series and could not be invoked<br>
without raising an internal error, so it is not expected that there is any<br>
real-world use of this event hook.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10500" rel="nofollow">#10500</a></p>
</li>
<li>
<p><strong>[orm] [change]</strong> Removed legacy signatures dating back to 0.9 release from the<br>
<code>_orm.SessionEvents.after_bulk_update()</code> and<br>
<code>_orm.SessionEvents.after_bulk_delete()</code>.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10721" rel="nofollow">#10721</a></p>
</li>
<li>
<p><strong>[orm] [changed]</strong> The "non primary" mapper feature, long deprecated in SQLAlchemy since<br>
version 1.3, has been removed. The sole use case for "non primary"<br>
mappers was that of using <code>_orm.relationship()</code> to link to a mapped<br>
class against an alternative selectable; this use case is now suited by the<br>
<code>relationship_aliased_class</code> feature.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12437" rel="nofollow">#12437</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> The <code>_orm.relationship.secondary</code> parameter no longer uses Python<br>
<code>eval()</code> to evaluate the given string. This parameter when passed a<br>
string should resolve to a table name that's present in the local<br>
<code>MetaData</code> collection only, and never needs to be any kind of<br>
Python expression otherwise. To use a real deferred callable based on a<br>
name that may not be locally present yet, use a lambda instead.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10564" rel="nofollow">#10564</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> Fixed issue where joined eager loading would fail to use the "nested" form<br>
of the query when GROUP BY or DISTINCT were present if the eager joins<br>
being added were many-to-ones, leading to additional columns in the columns<br>
clause which would then cause errors. The check for "nested" is tuned to<br>
be enabled for these queries even for many-to-one joined eager loaders, and<br>
the "only do nested if it's one to many" aspect is now localized to when<br>
the query only has LIMIT or OFFSET added.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/11226" rel="nofollow">#11226</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> Revised the set "binary" operators for the association proxy <code>set()</code><br>
interface to correctly raise <code>TypeError</code> for invalid use of the <code>|</code>,<br>
<code>&</code>, <code>^</code>, and <code>-</code> operators, as well as the in-place mutation<br>
versions of these methods, to match the behavior of standard Python<br>
<code>set()</code> as well as SQLAlchemy ORM's "instrumented" set implementation.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/11349" rel="nofollow">#11349</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> A significant behavioral change has been made to the behavior of the<br>
<code>_orm.mapped_column.default</code> and<br>
<code>_orm.relationship.default</code> parameters, as well as the<br>
<code>_orm.relationship.default_factory</code> parameter with<br>
collection-based relationships, when used with SQLAlchemy's<br>
<code>orm_declarative_native_dataclasses</code> feature introduced in 2.0, where<br>
the given value (assumed to be an immutable scalar value for<br>
<code>_orm.mapped_column.default</code> and a simple collection class for<br>
<code>_orm.relationship.default_factory</code>) is no longer passed to the<br>
<code>@dataclass</code> API as a real default, instead a token that leaves the value<br>
un-set in the object's <code>__dict__</code> is used, in conjunction with a<br>
descriptor-level default. This prevents an un-set default value from<br>
overriding a default that was actually set elsewhere, such as in<br>
relationship / foreign key assignment patterns as well as in<br>
<code>_orm.Session.merge()</code> scenarios. See the full writeup in the<br>
<code>migration_21_toplevel</code> document which includes guidance on how to<br>
re-enable the 2.0 version of the behavior if needed.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12168" rel="nofollow">#12168</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> The behavior of <code>_orm.with_polymorphic()</code> when used with a single<br>
inheritance mapping has been changed such that its behavior should match as<br>
closely as possible to that of an equivalent joined inheritance mapping.<br>
Specifically this means that the base class specified in the<br>
<code>_orm.with_polymorphic()</code> construct will be the basemost class that is<br>
loaded, as well as all descendant classes of that basemost class.<br>
The change includes that the descendant classes named will no longer be<br>
exclusively indicated in "WHERE polymorphic_col IN" criteria; instead, the<br>
whole hierarchy starting with the given basemost class will be loaded. If<br>
the query indicates that rows should only be instances of a specific<br>
subclass within the polymorphic hierarchy, an error is raised if an<br>
incompatible superclass is loaded in the result since it cannot be made to<br>
match the requested class; this behavior is the same as what joined<br>
inheritance has done for many years. The change also allows a single result<br>
set to include column-level results from multiple sibling classes at once<br>
which was not previously possible with single table inheritance.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12395" rel="nofollow">#12395</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> Improved the behavior of standalone "operators" like <code>_sql.desc()</code>,<br>
<code>_sql.asc()</code>, <code>_sql.all_()</code>, etc. so that they consult the given<br>
expression object for an overriding method for that operator, even if the<br>
object is not itself a <code>ClauseElement</code>, such as if it's an ORM attribute.<br>
This allows custom comparators for things like <code>_orm.composite()</code> to<br>
provide custom implementations of methods like <code>desc()</code>, <code>asc()</code>, etc.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12769" rel="nofollow">#12769</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> ORM entities can now be involved within the SQL expressions used within<br>
<code>_orm.relationship.primaryjoin</code> and<br>
<code>_orm.relationship.secondaryjoin</code> parameters without the ORM<br>
entity information being implicitly sanitized, allowing ORM-specific<br>
features such as single-inheritance criteria in subqueries to continue<br>
working even when used in this context. This is made possible by overall<br>
ORM simplifications that occurred as of the 2.0 series. The changes here<br>
also provide a performance boost (up to 20%) for certain query compilation<br>
scenarios.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12843" rel="nofollow">#12843</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> The <code>_events.SessionEvents.do_orm_execute()</code> event now allows direct<br>
mutation or replacement of the <code>ORMExecuteState.parameters</code><br>
dictionary or list, which will take effect when the the statement is<br>
executed. Previously, changes to this collection were not accommodated by<br>
the event hook. Pull request courtesy Shamil.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12921" rel="nofollow">#12921</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> A change in the mechanics of how Python dataclasses are applied to classes<br>
that use <code>MappedAsDataclass</code> or<br>
<code>registry.mapped_as_dataclass()</code> to apply <code>__annotations__</code> that are<br>
as identical as is possible to the original <code>__annotations__</code> given,<br>
while also adding attributes that SQLAlchemy considers to be part of<br>
dataclass <code>__annotations__</code>, then restoring the previous annotations in<br>
exactly the same format as they were, using patterns that work with<br>
<a href="https://peps.python.org/pep-0649" rel="nofollow">PEP 649</a> as closely as possible.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13021" rel="nofollow">#13021</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> Removed the <code>ORDER BY</code> clause from queries generated by<br>
<code>_orm.selectin_polymorphic()</code> and the<br>
<code>_orm.Mapper.polymorphic_load</code> parameter set to <code>"selectin"</code>.<br>
The <code>ORDER BY</code> clause appears to have been an unnecessary implementation<br>
artifact.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13060" rel="nofollow">#13060</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> A significant change to the ORM mechanics involved with both<br>
<code>orm.with_loader_criteria()</code> as well as single table inheritance, to<br>
more aggressively locate WHERE criteria which should be augmented by either<br>
the custom criteria or single-table inheritance criteria; SELECT statements<br>
that do not include the entity within the columns clause or as an explicit<br>
FROM, but still reference the entity within the WHERE clause, are now<br>
covered, in particular this will allow subqueries using <code>EXISTS (SELECT 1)</code> such as those rendered by <code>RelationshipProperty.Comparator.any()</code><br>
and <code>RelationshipProperty.Comparator.has()</code>.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13070" rel="nofollow">#13070</a></p>
</li>
<li>
<p><strong>[orm]</strong> The <code>_orm.noload()</code> relationship loader option and related<br>
<code>lazy='noload'</code> setting is deprecated and will be removed in a future<br>
release. This option was originally intended for custom loader patterns<br>
that are no longer applicable in modern SQLAlchemy.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/11045" rel="nofollow">#11045</a></p>
</li>
<li>
<p><strong>[orm]</strong> Ignore <code>_orm.Session.join_transaction_mode</code> in all cases when<br>
the bind provided to the <code>_orm.Session</code> is an<br>
<code>_engine.Engine</code>.<br>
Previously if an event that executed before the session logic,<br>
like <code>_engine.ConnectionEvents.engine_connect()</code>,<br>
left the connection with an active transaction, the<br>
<code>_orm.Session.join_transaction_mode</code> behavior took<br>
place, leading to a surprising behavior.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/11163" rel="nofollow">#11163</a></p>
</li>
</ul>
<h2>engine</h2>
<ul>
<li>
<p><strong>[engine] [usecase]</strong> Added new execution option<br>
<code>_engine.Connection.execution_options.driver_column_names</code>. This<br>
option disables the "name normalize" step that takes place against the<br>
DBAPI <code>cursor.description</code> for uppercase-default backends like Oracle,<br>
and will cause the keys of a result set (e.g. named tuple names, dictionary<br>
keys in <code>Row._mapping</code>, etc.) to be exactly what was delivered in<br>
cursor.description. This is mostly useful for plain textual statements<br>
using <code>_sql.text()</code> or <code>_engine.Connection.exec_driver_sql()</code>.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10789" rel="nofollow">#10789</a></p>
</li>
<li>
<p><strong>[engine] [change]</strong> An empty sequence passed to any <code>execute()</code> method now<br>
raised a deprecation warning, since such an executemany<br>
is invalid.<br>
Pull request courtesy of Carlos Sousa.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/9647" rel="nofollow">#9647</a></p>
</li>
<li>
<p><strong>[engine] [change]</strong> The private method <code>Connection._execute_compiled</code> is removed. This method may<br>
have been used for some special purposes however the <code>SQLCompiler</code><br>
object has lots of special state that should be set up for an execute call,<br>
which we don't support.</p>
</li>
<li>
<p><strong>[engine] [bug]</strong> Fixed issue in "insertmanyvalues" feature where an INSERT..RETURNING<br>
that also made use of a sentinel column to track results would fail to<br>
filter out the additional column when <code>Result.unique()</code> were used<br>
to uniquify the result set.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10802" rel="nofollow">#10802</a></p>
</li>
<li>
<p><strong>[engine] [bug]</strong> Adjusted URL parsing and stringification to apply url quoting to the<br>
"database" portion of the URL. This allows a URL where the "database"<br>
portion includes special characters such as question marks to be<br>
accommodated.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/11234" rel="nofollow">#11234</a></p>
</li>
<li>
<p><strong>[engine] [bug]</strong> Fixed issue in the <code>ConnectionEvents.after_cursor_execute()</code> method<br>
where the SQL statement and parameter list for an "insertmanyvalues"<br>
operation sent to the event would not be the actual SQL / parameters just<br>
emitted on the cursor, instead being the non-batched form of the statement<br>
that's used as a template to generate the batched statements.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13018" rel="nofollow">#13018</a></p>
</li>
</ul>
<h2>sql</h2>
<ul>
<li>
<p><strong>[sql] [feature]</strong> Added the ability to create custom SQL constructs that can define new<br>
clauses within SELECT, INSERT, UPDATE, and DELETE statements without<br>
needing to modify the construction or compilation code of of<br>
<code>Select</code>, <code>_sql.Insert</code>, <code>Update</code>, or <code>Delete</code><br>
directly. Support for testing these constructs, including caching support,<br>
is present along with an example test suite. The use case for these<br>
constructs is expected to be third party dialects for analytical SQL<br>
(so-called NewSQL) or other novel styles of database that introduce new<br>
clauses to these statements. A new example suite is included which<br>
illustrates the <code>QUALIFY</code> SQL construct used by several NewSQL databases<br>
which includes a cacheable implementation as well as a test suite.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12195" rel="nofollow">#12195</a></p>
</li>
<li>
<p><strong>[sql] [feature] [core]</strong> The Core operator system now includes the <code>matmul</code> operator, i.e. the<br>
<code>@</code> operator in Python as an optional operator.<br>
In addition to the <code>__matmul__</code> and <code>__rmatmul__</code> operator support<br>
this change also adds the missing <code>__rrshift__</code> and <code>__rlshift__</code>.<br>
Pull request courtesy Aramís Segovia.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12479" rel="nofollow">#12479</a></p>
</li>
<li>
<p><strong>[sql] [feature]</strong> Added new Core feature <code>_sql.from_dml_column()</code> that may be used in<br>
expressions inside of <code>UpdateBase.values()</code> for INSERT or UPDATE; this<br>
construct will copy whatever SQL expression is used for the given target<br>
column in the statement to be used with additional columns. The construct<br>
is mostly intended to be a helper with ORM <code>hybrid_property</code> within<br>
DML hooks.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12496" rel="nofollow">#12496</a></p>
</li>
<li>
<p><strong>[sql] [feature]</strong> Added support for Python 3.14+ template strings (t-strings) via the new<br>
<code>_sql.tstring()</code> construct. This feature makes use of Python 3.14<br>
template strings as defined in <a href="https://peps.python.org/pep-0750" rel="nofollow">PEP 750</a>, allowing for ergonomic SQL<br>
statement construction by automatically interpolating Python values and<br>
SQLAlchemy expressions within template strings.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12548" rel="nofollow">#12548</a></p>
</li>
<li>
<p><strong>[sql] [usecase]</strong> Added new generalized aggregate function ordering to functions via the<br>
<code>_functions.FunctionElement.aggregate_order_by()</code> method, which<br>
receives an expression and generates the appropriate embedded "ORDER BY" or<br>
"WITHIN GROUP (ORDER BY)" phrase depending on backend database. This new<br>
function supersedes the use of the PostgreSQL<br>
<code>_postgresql.aggregate_order_by()</code> function, which remains present for<br>
backward compatibility. To complement the new parameter, the<br>
<code>_functions.aggregate_strings.order_by</code> which adds ORDER BY<br>
capability to the <code>_functions.aggregate_strings</code> dialect-agnostic<br>
function which works for all included backends. Thanks much to Reuven<br>
Starodubski with help on this patch.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12853" rel="nofollow">#12853</a></p>
</li>
<li>
<p><strong>[sql] [usecase]</strong> Changed the query style for ORM queries emitted by <code>Session.get()</code> as<br>
well as many-to-one lazy load queries to use the default labeling style,<br>
<code>_sql.SelectLabelStyle.LABEL_STYLE_DISAMBIGUATE_ONLY</code>, which normally<br>
does not apply labels to columns in a SELECT statement. Previously, the<br>
older style <code>_sql.SelectLabelStyle.LABEL_STYLE_TABLENAME_PLUS_COL</code><br>
that labels columns as _ was used for<br>
<code>Session.get()</code> to maintain compatibility with <code>_orm.Query</code>.<br>
The change allows the string representation of ORM queries to be less<br>
verbose in all cases outside of legacy <code>_orm.Query</code> use. Pull<br>
request courtesy Inada Naoki.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12932" rel="nofollow">#12932</a></p>
</li>
<li>
<p><strong>[sql] [usecase]</strong> Added method <code>TableClause.insert_column()</code> to complement<br>
<code>TableClause.append_column()</code>, which inserts the given column at a<br>
specific index. This can be helpful for prepending primary key columns to<br>
tables, etc.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/7910" rel="nofollow">#7910</a></p>
</li>
<li>
<p><strong>[sql] [usecase]</strong> Added support for the pow operator (<code>**</code>), with a default SQL<br>
implementation of the <code>POW()</code> function. On Oracle Database, PostgreSQL<br>
and MSSQL it renders as <code>POWER()</code>. As part of this change, the operator<br>
routes through a new first class <code>func</code> member <code>_functions.pow</code>,<br>
which renders on Oracle Database, PostgreSQL and MSSQL as <code>POWER()</code>.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/8579" rel="nofollow">#8579</a></p>
</li>
<li>
<p><strong>[sql] [usecase] [orm]</strong> The <code>_sql.Select.filter_by()</code>, <code>_sql.Update.filter_by()</code> and<br>
<code>_sql.Delete.filter_by()</code> methods now search across all entities<br>
present in the statement, rather than limiting their search to only the<br>
last joined entity or the first FROM entity. This allows these methods<br>
to locate attributes unambiguously across multiple joined tables,<br>
resolving issues where changing the order of operations such as<br>
<code>_sql.Select.with_only_columns()</code> would cause the method to fail.</p>
<p>If an attribute name exists in more than one FROM clause entity, an<br>
<code>_exc.AmbiguousColumnError</code> is now raised, indicating that<br>
<code>_sql.Select.filter()</code> (or <code>_sql.Select.where()</code>) should be used<br>
instead with explicit table-qualified column references.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/8601" rel="nofollow">#8601</a></p>
</li>
<li>
<p><strong>[sql] [change]</strong> The <code>.c</code> and <code>.columns</code> attributes on the <code>Select</code> and<br>
<code>TextualSelect</code> constructs, which are not instances of<br>
<code>FromClause</code>, have been removed completely, in addition to the<br>
<code>.select()</code> method as well as other codepaths which would implicitly<br>
generate a subquery from a <code>Select</code> without the need to explicitly<br>
call the <code>Select.subquery()</code> method.</p>
<p>In the case of <code>.c</code> and <code>.columns</code>, these attributes were never useful<br>
in practice and have caused a great deal of confusion, hence were<br>
deprecated back in version 1.4, and have emitted warnings since that<br>
version. Accessing the columns that are specific to a <code>Select</code><br>
construct is done via the <code>Select.selected_columns</code> attribute, which<br>
was added in version 1.4 to suit the use case that users often expected<br>
<code>.c</code> to accomplish. In the larger sense, implicit production of<br>
subqueries works against SQLAlchemy's modern practice of making SQL<br>
structure as explicit as possible.</p>
<p>Note that this is <strong>not related</strong> to the usual <code>FromClause.c</code> and<br>
<code>FromClause.columns</code> attributes, common to objects such as<br>
<code>Table</code> and <code>Subquery</code>, which are unaffected by this<br>
change.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10236" rel="nofollow">#10236</a></p>
</li>
<li>
<p><strong>[sql] [change]</strong> the <code>Numeric</code> and <code>Float</code> SQL types have been separated out<br>
so that <code>Float</code> no longer inherits from <code>Numeric</code>; instead,<br>
they both extend from a common mixin <code>NumericCommon</code>. This<br>
corrects for some architectural shortcomings where numeric and float types<br>
are typically separate, and establishes more consistency with<br>
<code>Integer</code> also being a distinct type. The change should not have<br>
any end-user implications except for code that may be using<br>
<code>isinstance()</code> to test for the <code>Numeric</code> datatype; third party<br>
dialects which rely upon specific implementation types for numeric and/or<br>
float may also require adjustment to maintain compatibility.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/5252" rel="nofollow">#5252</a></p>
</li>
<li>
<p><strong>[sql] [change]</strong> Added new implementation for the <code>Select.params()</code> method and that of<br>
similar statements, via a new statement-only<br>
<code>ExecutableStatement.params()</code> method which works more efficiently and<br>
correctly than the previous implementations available from<br>
<code>ClauseElement</code>, by associating the given parameter dictionary with<br>
the statement overall rather than cloning the statement and rewriting its<br>
bound parameters. The <code>_sql.ClauseElement.params()</code> and<br>
<code>_sql.ClauseElement.unique_params()</code> methods, when called on an object<br>
that does not implement <code>ExecutableStatement</code>, will continue to<br>
work the old way of cloning the object, and will emit a deprecation<br>
warning. This issue both resolves the architectural / performance<br>
concerns of <a href="https://www.sqlalchemy.org/trac/ticket/7066" rel="nofollow">#7066</a> and also provides correct ORM compatibility for<br>
functions like <code>_orm.aliased()</code>, reported by <a href="https://www.sqlalchemy.org/trac/ticket/12915" rel="nofollow">#12915</a>.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12915" rel="nofollow">#12915</a>, <a href="https://www.sqlalchemy.org/trac/ticket/7066" rel="nofollow">#7066</a></p>
</li>
<li>
<p><strong>[sql] [bug]</strong> The <code>Double</code> type is now used when a Python float value is detected<br>
as a literal value to be sent as a bound parameter, rather than the<br>
<code>Float</code> type. <code>Double</code> has the same implementation as<br>
<code>Float</code>, but when rendered in a CAST, produces <code>DOUBLE</code> or<br>
<code>DOUBLE PRECISION</code> rather than <code>FLOAT</code>. The former better matches<br>
Python's <code>float</code> datatype which uses 8-byte double-precision storage.<br>
Third party dialects which don't support the <code>Double</code> type directly<br>
may need adjustment so that they render an appropriate keyword (e.g.<br>
<code>FLOAT</code>) when the <code>Double</code> datatype is encountered.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10300" rel="nofollow">#10300</a></p>
</li>
<li>
<p><strong>[sql] [bug]</strong> Fixed issue in name normalization (e.g. "uppercase" backends like Oracle)<br>
where using a <code>TextualSelect</code> would not properly maintain as<br>
uppercase column names that were quoted as uppercase, even though<br>
the <code>TextualSelect</code> includes a <code>Column</code> that explicitly<br>
holds this uppercase name.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10788" rel="nofollow">#10788</a></p>
</li>
<li>
<p><strong>[sql] [bug]</strong> Enhanced the caching structure of the <code>_expression.over.rows</code><br>
and <code>_expression.over.range</code> so that different numerical<br>
values for the rows /<br>
range fields are cached on the same cache key, to the extent that the<br>
underlying SQL does not actually change (i.e. "unbounded", "current row",<br>
negative/positive status will still change the cache key). This prevents<br>
the use of many different numerical range/rows value for a query that is<br>
otherwise identical from filling up the SQL cache.</p>
<p>Note that the semi-private compiler method <code>_format_frame_clause()</code><br>
is removed by this fix, replaced with a new method<br>
<code>visit_frame_clause()</code>. Third party dialects which may have referred<br>
to this method will need to change the name and revise the approach to<br>
rendering the correct SQL for that dialect.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/11515" rel="nofollow">#11515</a></p>
</li>
<li>
<p><strong>[sql] [bug]</strong> Updated the <code>_sql.over()</code> clause to allow non integer values in<br>
<code>_sql.over.range_</code> clause. Previously, only integer values<br>
were allowed and any other values would lead to a failure.<br>
To specify a non-integer value, use the new <code>_sql.FrameClause</code><br>
construct along with the new <code>_sql.FrameClauseType</code> enum to specify<br>
the frame boundaries. For example:</p>
<p>from sqlalchemy import FrameClause, FrameClauseType</p>
<p>select(<br>
func.sum(table.c.value).over(<br>
range_=FrameClause(<br>
3.14,<br>
2.71,<br>
FrameClauseType.PRECEDING,<br>
FrameClauseType.FOLLOWING,<br>
)<br>
)<br>
)References: <a href="https://www.sqlalchemy.org/trac/ticket/12596" rel="nofollow">#12596</a></p>
</li>
<li>
<p><strong>[sql] [bug]</strong> Added a new concept of "operator classes" to the SQL operators supported by<br>
SQLAlchemy, represented within the enum <code>OperatorClass</code>. The<br>
purpose of this structure is to provide an extra layer of validation when a<br>
particular kind of SQL operation is used with a particular datatype, to<br>
catch early the use of an operator that does not have any relevance to the<br>
datatype in use; a simple example is an integer or numeric column used with<br>
a "string match" operator.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12736" rel="nofollow">#12736</a></p>
</li>
<li>
<p><strong>[sql] [bug]</strong> Fixed an issue in <code>_sql.Select.join_from()</code> where the join condition<br>
between the left and right tables specified in the method call could be<br>
incorrectly determined based on an intermediate table already present in<br>
the FROM clause, rather than matching the foreign keys between the<br>
immediate left and right arguments. The join condition is now determined by<br>
matching primary keys between the two tables explicitly passed to<br>
<code>_sql.Select.join_from()</code>, ensuring consistent and predictable join<br>
behavior regardless of the order of join operations or other tables present<br>
in the query. The fix is applied to both the Core and ORM implementations<br>
of <code>_sql.Select.join_from()</code>.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12931" rel="nofollow">#12931</a></p>
</li>
<li>
<p><strong>[sql] [bug]</strong> Fixed issue where anonymous label generation for <code>CTE</code> constructs<br>
could produce name collisions when Python's garbage collector reused memory<br>
addresses during complex query compilation. The anonymous name generation<br>
for <code>CTE</code> and other aliased constructs like <code>Alias</code>,<br>
<code>Subquery</code> and others now use <code>os.urandom()</code> to generate unique<br>
identifiers instead of relying on object <code>id()</code>, ensuring uniqueness even<br>
in cases of aggressive garbage collection and memory reuse.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12990" rel="nofollow">#12990</a></p>
</li>
<li>
<p><strong>[sql]</strong> Removed the automatic coercion of executable objects, such as<br>
<code>_orm.Query</code>, when passed into <code>_orm.Session.execute()</code>.<br>
This usage raised a deprecation warning since the 1.4 series.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12218" rel="nofollow">#12218</a></p>
</li>
</ul>
<h2>schema</h2>
<ul>
<li>
<p><strong>[schema] [feature]</strong> Added support for the SQL <code>CREATE VIEW</code> statement via the new<br>
<code>CreateView</code> DDL class. The new class allows creating database<br>
views from SELECT statements, with support for options such as<br>
<code>TEMPORARY</code>, <code>IF NOT EXISTS</code>, and <code>MATERIALIZED</code> where supported by<br>
the target database. Views defined with <code>CreateView</code> integrate with<br>
<code>MetaData</code> for automated DDL generation and provide a<br>
<code>Table</code> object for querying.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/181" rel="nofollow">#181</a></p>
</li>
<li>
<p><strong>[schema] [feature]</strong> Added support for the SQL <code>CREATE TABLE ... AS SELECT</code> construct via the<br>
new <code>_schema.CreateTableAs</code> DDL construct and the<br>
<code>_sql.Select.into()</code> method. The new construct allows creating a<br>
table directly from the results of a SELECT statement, with support for<br>
options such as <code>TEMPORARY</code> and <code>IF NOT EXISTS</code> where supported by the<br>
target database. Tables defined with <code>_schema.CreateTableAs</code><br>
integrate with <code>MetaData</code> for automated DDL generation and provide<br>
a <code>Table</code> object for querying. Pull request courtesy Greg Jarzab.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/4950" rel="nofollow">#4950</a></p>
</li>
<li>
<p><strong>[schema] [usecase]</strong> The the parameter <code>_schema.DropConstraint.isolate_from_table</code><br>
was deprecated since it has no effect on the drop table behavior.<br>
Its default values was also changed to <code>False</code>.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13006" rel="nofollow">#13006</a></p>
</li>
<li>
<p><strong>[schema] [bug]</strong> The <code>Float</code> and <code>Numeric</code> types are no longer automatically<br>
considered as auto-incrementing columns when the<br>
<code>_schema.Column.autoincrement</code> parameter is left at its default<br>
of <code>"auto"</code> on a <code>_schema.Column</code> that is part of the primary key.<br>
When the parameter is set to <code>True</code>, a <code>Numeric</code> type will be<br>
accepted as an auto-incrementing datatype for primary key columns, but only<br>
if its scale is explicitly given as zero; otherwise, an error is raised.<br>
This is a change from 2.0 where all numeric types including floats were<br>
automatically considered as "autoincrement" for primary key columns.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/11811" rel="nofollow">#11811</a></p>
</li>
<li>
<p><strong>[schema]</strong> Deprecate Oracle only parameters <code>_schema.Sequence.order</code>,<br>
<code>_schema.Identity.order</code> and <code>_schema.Identity.on_null</code>.<br>
They should be configured using the dialect kwargs <code>oracle_order</code> and<br>
<code>oracle_on_null</code>.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10247" rel="nofollow">#10247</a></p>
</li>
</ul>
<h2>typing</h2>
<ul>
<li>
<p><strong>[typing] [feature]</strong> The <code>Row</code> object now no longer makes use of an intermediary<br>
<code>Tuple</code> in order to represent its individual element types; instead,<br>
the individual element types are present directly, via new <a href="https://peps.python.org/pep-0646" rel="nofollow">PEP 646</a><br>
integration, now available in more recent versions of Mypy. Mypy<br>
1.7 or greater is now required for statements, results and rows<br>
to be correctly typed. Pull request courtesy Yurii Karabas.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10635" rel="nofollow">#10635</a></p>
</li>
<li>
<p><strong>[typing]</strong> The default implementation of <code>_sql.TypeEngine.python_type</code> now<br>
returns <code>object</code> instead of <code>NotImplementedError</code>, since that's the<br>
base for all types in Python3.<br>
The <code>python_type</code> of <code>_sql.JSON</code> no longer returns <code>dict</code>,<br>
but instead fallbacks to the generic implementation.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10646" rel="nofollow">#10646</a></p>
</li>
<li>
<p><strong>[typing] [orm]</strong> Removed the deprecated mypy plugin.<br>
The plugin was non-functional with newer version of mypy and it's no<br>
longer needed with modern SQLAlchemy declarative style.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12293" rel="nofollow">#12293</a></p>
</li>
<li>
<p><strong>[typing] [orm]</strong> Deprecated the <code>declarative_mixin</code> decorator since it was used only<br>
by the now removed mypy plugin.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12346" rel="nofollow">#12346</a></p>
</li>
</ul>
<h2>asyncio</h2>
<ul>
<li>
<p><strong>[asyncio] [feature]</strong> The "emulated" exception hierarchies for the asyncio<br>
drivers such as asyncpg, aiomysql, aioodbc, etc. have been standardized<br>
on a common base <code>EmulatedDBAPIException</code>, which is now what's<br>
available from the <code>StatementException.orig</code> attribute on a<br>
SQLAlchemy <code>DBAPIError</code> object. Within <code>EmulatedDBAPIException</code><br>
and the subclasses in its hierarchy, the original driver-level exception is<br>
also now available via the <code>EmulatedDBAPIException.orig</code> attribute,<br>
and is also available from <code>DBAPIError</code> directly using the<br>
<code>DBAPIError.driver_exception</code> attribute.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/8047" rel="nofollow">#8047</a></p>
</li>
<li>
<p><strong>[asyncio] [change]</strong> Added an initialize step to the import of<br>
<code>sqlalchemy.ext.asyncio</code> so that <code>greenlet</code> will<br>
be imported only when the asyncio extension is first imported.<br>
Alternatively, the <code>greenlet</code> library is still imported lazily on<br>
first use to support use case that don't make direct use of the<br>
SQLAlchemy asyncio extension.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10296" rel="nofollow">#10296</a></p>
</li>
<li>
<p><strong>[asyncio] [change]</strong> Adapted all asyncio dialects, including aiosqlite, aiomysql, asyncmy,<br>
psycopg, asyncpg to use the generic asyncio connection adapter first added<br>
in <a href="https://www.sqlalchemy.org/trac/ticket/6521" rel="nofollow">#6521</a> for the aioodbc DBAPI, allowing these dialects to take<br>
advantage of a common framework.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10415" rel="nofollow">#10415</a></p>
</li>
<li>
<p><strong>[asyncio] [change]</strong> Removed the compatibility <code>async_fallback</code> mode for async dialects,<br>
since it's no longer used by SQLAlchemy tests.<br>
Also removed the internal function <code>await_fallback()</code> and renamed<br>
the internal function <code>await_only()</code> to <code>await_()</code>.<br>
No change is expected to user code.</p>
</li>
<li>
<p><strong>[asyncio] [bug]</strong> Refactored all asyncio dialects so that exceptions which occur on failed<br>
connection attempts are appropriately wrapped with SQLAlchemy exception<br>
objects, allowing for consistent error handling.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/11956" rel="nofollow">#11956</a></p>
</li>
</ul>
<h2>postgresql</h2>
<ul>
<li>
<p><strong>[postgresql] [feature]</strong> Adds a new <code>str</code> subclass <code>_postgresql.BitString</code> representing<br>
PostgreSQL bitstrings in python, that includes<br>
functionality for converting to and from <code>int</code> and <code>bytes</code>, in<br>
addition to implementing utility methods and operators for dealing with bits.</p>
<p>This new class is returned automatically by the <code>postgresql.BIT</code> type.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10556" rel="nofollow">#10556</a></p>
</li>
<li>
<p><strong>[postgresql] [feature]</strong> Support for storage parameters in <code>CREATE TABLE</code> using the <code>WITH</code><br>
clause has been added. The <code>postgresql_with</code> dialect option of<br>
<code>_schema.Table</code> accepts a mapping of key/value options.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10909" rel="nofollow">#10909</a></p>
</li>
<li>
<p><strong>[postgresql] [feature]</strong> Added syntax extension <code>_postgresql.distinct_on()</code> to build <code>DISTINCT ON</code> clauses. The old api, that passed columns to<br>
<code>_sql.Select.distinct()</code>, is now deprecated.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12342" rel="nofollow">#12342</a></p>
</li>
<li>
<p><strong>[postgresql] [feature]</strong> Support for <code>VIRTUAL</code> computed columns on PostgreSQL 18 and later has<br>
been added. The default behavior when <code>Computed.persisted</code> is<br>
not specified has been changed to align with PostgreSQL 18's default of<br>
<code>VIRTUAL</code>. When <code>Computed.persisted</code> is not specified, no<br>
keyword is rendered on PostgreSQL 18 and later; on older versions a<br>
warning is emitted and <code>STORED</code> is used as the default. To explicitly<br>
request <code>STORED</code> behavior on all PostgreSQL versions, specify<br>
<code>persisted=True</code>.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12866" rel="nofollow">#12866</a></p>
</li>
<li>
<p><strong>[postgresql] [feature] [sql]</strong> Added support for monotonic server-side functions such as PostgreSQL 18's<br>
<code>uuidv7()</code> to work with the <code>engine_insertmanyvalues</code> feature.<br>
By passing <code>monotonic=True</code> to any <code>Function</code>, the function can<br>
be used as a sentinel for tracking row order in batched INSERT operations<br>
with RETURNING, allowing the ORM and Core to efficiently batch INSERT<br>
statements while maintaining deterministic row ordering.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13014" rel="nofollow">#13014</a></p>
</li>
<li>
<p><strong>[postgresql] [feature]</strong> Added additional emulated error classes for the subclasses of<br>
<code>asyncpg.exception.IntegrityError</code> including <code>RestrictViolationError</code>,<br>
<code>NotNullViolationError</code>, <code>ForeignKeyViolationError</code>,<br>
<code>UniqueViolationError</code> <code>CheckViolationError</code>,<br>
<code>ExclusionViolationError</code>. These exceptions are not directly thrown by<br>
SQLAlchemy's asyncio emulation, however are available from the<br>
newly added <code>DBAPIError.driver_exception</code> attribute when a<br>
<code>IntegrityError</code> is caught.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/8047" rel="nofollow">#8047</a></p>
</li>
<li>
<p><strong>[postgresql] [usecase]</strong> Added new parameter <code>Enum.create_type</code> to the Core<br>
<code>Enum</code> class. This parameter is automatically passed to the<br>
corresponding <code>_postgresql.ENUM</code> native type during DDL operations,<br>
allowing control over whether the PostgreSQL ENUM type is implicitly<br>
created or dropped within DDL operations that are otherwise targeting<br>
tables only. This provides control over the<br>
<code>_postgresql.ENUM.create_type</code> behavior without requiring<br>
explicit creation of a <code>_postgresql.ENUM</code> object.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10604" rel="nofollow">#10604</a></p>
</li>
<li>
<p><strong>[postgresql] [usecase]</strong> The PostgreSQL dialect now support reflection of table options, including<br>
the storage parameters, table access method and table spaces. These options<br>
are automatically reflected when autoloading a table, and are also<br>
available via the <code>_engine.Inspector.get_table_options()</code> and<br>
<code>_engine.Inspector.get_multi_table_optionsmethod()</code> methods.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10909" rel="nofollow">#10909</a></p>
</li>
<li>
<p><strong>[postgresql] [usecase]</strong> Added support for PostgreSQL 14+ HSTORE subscripting syntax. When connected<br>
to PostgreSQL 14 or later, HSTORE columns now automatically use the native<br>
subscript notation <code>hstore_col['key']</code> instead of the arrow operator<br>
<code>hstore_col -> 'key'</code> for both read and write operations. This provides<br>
better compatibility with PostgreSQL's native HSTORE subscripting feature<br>
while maintaining backward compatibility with older PostgreSQL versions.</p>
<p>Indexes in existing PostgreSQL databases which were indexed<br>
on an HSTORE subscript expression would need to be updated in order to<br>
match the new SQL syntax.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12948" rel="nofollow">#12948</a></p>
</li>
<li>
<p><strong>[postgresql] [usecase]</strong> The default DBAPI driver for the PostgreSQL dialect has been changed to<br>
<code>psycopg</code> (psycopg version 3) instead of <code>psycopg2</code>. The <code>psycopg2</code><br>
driver remains fully supported and can be explicitly specified in the<br>
connection URL using <code>postgresql+psycopg2://</code>.</p>
<p>The <code>psycopg</code> (version 3) driver includes improvements over <code>psycopg2</code><br>
including better performance when using C extensions and native support<br>
for async operations.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13010" rel="nofollow">#13010</a></p>
</li>
<li>
<p><strong>[postgresql] [change]</strong> The <code>_types.ARRAY.Comparator.any()</code> and<br>
<code>_types.ARRAY.Comparator.all()</code> methods for the <code>_types.ARRAY</code><br>
type are now deprecated for removal; these two methods along with<br>
<code>_postgresql.Any()</code> and <code>_postgresql.All()</code> have been legacy for<br>
some time as they are superseded by the <code>_sql.any_()</code> and<br>
<code>_sql.all_()</code> functions, which feature more intuitive use.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10821" rel="nofollow">#10821</a></p>
</li>
<li>
<p><strong>[postgresql] [change]</strong> Named types such as <code>_postgresql.ENUM</code> and<br>
<code>_postgresql.DOMAIN</code> (as well as the dialect-agnostic<br>
<code>_types.Enum</code> version) are now more strongly associated with the<br>
<code>_schema.MetaData</code> at the top of the table hierarchy and are<br>
de-associated with any particular <code>_schema.Table</code> they may be a part<br>
of. This better represents how PostgreSQL named types exist independently<br>
of any particular table, and that they may be used across many tables<br>
simultaneously. The change impacts the behavior of the "default schema"<br>
for a named type, as well as the CREATE/DROP behavior in relationship to<br>
the <code>MetaData</code> and <code>Table</code> construct. The change also<br>
includes a new <code>CheckFirst</code> enumeration which allows fine grained<br>
control over "check" queries during DDL operations, as well as that the<br>
<code>_types.SchemaType.inherit_schema</code> parameter is deprecated and<br>
will emit a deprecation warning when used. See the migration notes for<br>
full details.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10594" rel="nofollow">#10594</a>, <a href="https://www.sqlalchemy.org/trac/ticket/12690" rel="nofollow">#12690</a></p>
</li>
<li>
<p><strong>[postgresql] [bug]</strong> A <code>CompileError</code> is raised if attempting to create a PostgreSQL<br>
<code>_postgresql.ENUM</code> or <code>_postgresql.DOMAIN</code> datatype using a<br>
name that matches a known pg_catalog datatype name, and a default schema is<br>
not specified. These types must be explicit within a schema in order to<br>
be differentiated from the built-in pg_catalog type. The "public" or<br>
otherwise default schema is not chosen by default here since the type can<br>
only be reflected back using the explicit schema name as well (it is<br>
otherwise not visible due to the pg_catalog name). Pull request courtesy<br>
Kapil Dagur.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12761" rel="nofollow">#12761</a></p>
</li>
</ul>
<h2>mysql</h2>
<ul>
<li>
<p><strong>[mysql] [feature]</strong> Added new construct <code>_mysql.limit()</code> which can be applied to any<br>
<code>_sql.update()</code> or <code>_sql.delete()</code> to provide the LIMIT keyword to<br>
UPDATE and DELETE. This new construct supersedes the use of the<br>
"mysql_limit" dialect keyword argument.</p>
</li>
<li>
<p><strong>[mysql] [mariadb] [reflection]</strong> Updated the reflection logic for indexes in the MariaDB and MySQL<br>
dialect to avoid setting the undocumented <code>type</code> key in the<br>
<code>_engine.ReflectedIndex</code> dicts returned by<br>
<code>_engine.Inspector.get_indexes</code> method.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12240" rel="nofollow">#12240</a></p>
</li>
</ul>
<h2>mariadb</h2>
<ul>
<li>
<p><strong>[mariadb] [usecase]</strong> Modified the MariaDB dialect so that when using the <code>_sqltypes.Uuid</code><br>
datatype with MariaDB >= 10.7, leaving the<br>
<code>_sqltypes.Uuid.native_uuid</code> parameter at its default of True,<br>
the native <code>UUID</code> datatype will be rendered in DDL and used for database<br>
communication, rather than <code>CHAR(32)</code> (the non-native UUID type) as was<br>
the case previously. This is a behavioral change since 2.0, where the<br>
generic <code>_sqltypes.Uuid</code> datatype delivered <code>CHAR(32)</code> for all<br>
MySQL and MariaDB variants. Support for all major DBAPIs is implemented<br>
including support for less common "insertmanyvalues" scenarios where UUID<br>
values are generated in different ways for primary keys. Thanks much to<br>
Volodymyr Kochetkov for delivering the PR.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10339" rel="nofollow">#10339</a></p>
</li>
<li>
<p><strong>[mariadb] [bug]</strong> Fixes to the MySQL/MariaDB dialect so that mariadb-specific features such<br>
as the <code>mariadb.INET4</code> and <code>mariadb.INET6</code> datatype may be<br>
used with an <code>Engine</code> that uses a <code>mysql://</code> URL, if the backend<br>
database is actually a mariadb database. Previously, support for MariaDB<br>
features when <code>mysql://</code> URLs were used instead of <code>mariadb://</code> URLs<br>
was ad-hoc; with this issue resolution, the full set of schema / compiler /<br>
type features are now available regardless of how the URL was presented.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13076" rel="nofollow">#13076</a></p>
</li>
</ul>
<h2>sqlite</h2>
<ul>
<li>
<p><strong>[sqlite] [bug]</strong> Improved the behavior of JSON accessors <code>JSON.Comparator.as_string()</code>,<br>
<code>JSON.Comparator.as_boolean()</code>, <code>JSON.Comparator.as_float()</code>,<br>
<code>JSON.Comparator.as_integer()</code> to use CAST in a similar way that<br>
the PostgreSQL, MySQL and SQL Server dialects do to help enforce the<br>
expected Python type is returned.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/11074" rel="nofollow">#11074</a></p>
</li>
</ul>
<h2>mssql</h2>
<ul>
<li>
<p><strong>[mssql] [bug]</strong> The <code>JSON.Comparator.as_boolean()</code> method when used on a JSON value on<br>
SQL Server will now force a cast to occur for values that are not simple<br>
true/false JSON literals, forcing SQL Server to attempt to interpret<br>
the given value as a 1/0 BIT, or raise an error if not possible. Previously<br>
the expression would return NULL.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/11074" rel="nofollow">#11074</a></p>
</li>
<li>
<p><strong>[mssql] [bug]</strong> Fix mssql+pyodbc issue where valid plus signs in an already-unquoted<br>
<code>odbc_connect=</code> (raw DBAPI) connection string are replaced with spaces.</p>
<p>The pyodbc connector would unconditionally pass the odbc_connect value<br>
to unquote_plus(), even if it was not required. So, if the (unquoted)<br>
odbc_connect value contained <code>PWD=pass+word</code> that would get changed to<br>
<code>PWD=pass word</code>, and the login would fail. One workaround was to quote<br>
just the plus sign — <code>PWD=pass%2Bword</code> — which would then get unquoted<br>
to <code>PWD=pass+word</code>.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/11250" rel="nofollow">#11250</a></p>
</li>
</ul>
<h2>oracle</h2>
<ul>
<li>
<p><strong>[oracle] [feature]</strong> Added support for native BOOLEAN support in Oracle Database 23c and above.<br>
The Oracle dialect now renders <code>BOOLEAN</code> automatically when<br>
<code>Boolean</code> is used in DDL, and also now supports direct use of the<br>
<code>BOOLEAN</code> datatype, when 23c and above is in use. For Oracle<br>
versions prior to 23c, boolean values continue to be emulated using<br>
SMALLINT as before. Special case handling is also present to ensure a<br>
SMALLINT that's interpreted with the <code>Boolean</code> datatype on Oracle<br>
Database 23c and above continues to return bool values. Pull request<br>
courtesy Yeongbae Jeon.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/11633" rel="nofollow">#11633</a></p>
</li>
<li>
<p><strong>[oracle] [usecase]</strong> The default DBAPI driver for the Oracle Database dialect has been changed<br>
to <code>oracledb</code> instead of <code>cx_oracle</code>. The <code>cx_oracle</code> driver remains<br>
fully supported and can be explicitly specified in the connection URL<br>
using <code>oracle+cx_oracle://</code>.</p>
<p>The <code>oracledb</code> driver is a modernized version of <code>cx_oracle</code> with<br>
better performance characteristics and ongoing active development from<br>
Oracle.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13010" rel="nofollow">#13010</a></p>
</li>
</ul>
<h2>tests</h2>
<ul>
<li><strong>[tests] [change]</strong> The top-level test runner has been changed to use <code>nox</code>, adding a<br>
<code>noxfile.py</code> as well as some included modules. The <code>tox.ini</code> file<br>
remains in place so that <code>tox</code> runs will continue to function in the near<br>
term, however it will be eventually removed and improvements and<br>
maintenance going forward will be only towards <code>noxfile.py</code>.</li>
</ul>
<h2>misc</h2>
<ul>
<li>
<p><strong>[misc] [changed]</strong> Removed multiple api that were deprecated in the 1.3 series and earlier.<br>
The list of removed features includes:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="- The `force` parameter of `IdentifierPreparer.quote` and
`IdentifierPreparer.quote_schema`;
- The `threaded` parameter of the cx-Oracle dialect;
- The `_json_serializer` and `_json_deserializer` parameters of the
SQLite dialect;
- The `collection.converter` decorator;
- The `Mapper.mapped_table` property;
- The `Session.close_all` method;
- Support for multiple arguments in `_orm.defer()` and
`_orm.undefer()`."><pre class="notranslate"><code>- The `force` parameter of `IdentifierPreparer.quote` and
`IdentifierPreparer.quote_schema`;
- The `threaded` parameter of the cx-Oracle dialect;
- The `_json_serializer` and `_json_deserializer` parameters of the
SQLite dialect;
- The `collection.converter` decorator;
- The `Mapper.mapped_table` property;
- The `Session.close_all` method;
- Support for multiple arguments in `_orm.defer()` and
`_orm.undefer()`.
</code></pre></div>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12441" rel="nofollow">#12441</a></p>
</li>
</ul>sqla-testertag:github.com,2008:Repository/159271175/rel_2_0_462026-01-21T19:05:46Z2.0.46<h1>2.0.46</h1>
<p>Released: January 21, 2026</p>
<h2>typing</h2>
<ul>
<li>
<p><strong>[typing] [bug]</strong> Fixed typing issues where ORM mapped classes and aliased entities could not<br>
be used as keys in result row mappings or as join targets in select<br>
statements. Patterns such as <code>row._mapping[User]</code>,<br>
<code>row._mapping[aliased(User)]</code>, <code>row._mapping[with_polymorphic(...)]</code><br>
(rejected by both mypy and Pylance), and <code>.join(aliased(User))</code><br>
(rejected by Pylance) are documented and fully supported at runtime but<br>
were previously rejected by type checkers. The type definitions for<br>
<code>_KeyType</code> and <code>_FromClauseArgument</code> have been updated to<br>
accept these ORM entity types.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13075" rel="nofollow">#13075</a></p>
</li>
</ul>
<h2>postgresql</h2>
<ul>
<li>
<p><strong>[postgresql] [bug]</strong> Fixed issue where PostgreSQL JSONB operators<br>
<code>_postgresql.JSONB.Comparator.path_match()</code> and<br>
<code>_postgresql.JSONB.Comparator.path_exists()</code> were applying incorrect<br>
<code>VARCHAR</code> casts to the right-hand side operand when used with newer<br>
PostgreSQL drivers such as psycopg. The operators now indicate the<br>
right-hand type as <code>JSONPATH</code>, which currently results in no casting<br>
taking place, but is also compatible with explicit casts if the<br>
implementation were require it at a later point.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13059" rel="nofollow">#13059</a></p>
</li>
<li>
<p><strong>[postgresql] [bug]</strong> Fixed regression in PostgreSQL dialect where JSONB subscription syntax<br>
would generate incorrect SQL for <code>cast()</code> expressions returning JSONB,<br>
causing syntax errors. The dialect now properly wraps cast expressions in<br>
parentheses when using the <code>[]</code> subscription syntax, generating<br>
<code>(CAST(...))[index]</code> instead of <code>CAST(...)[index]</code> to comply with<br>
PostgreSQL syntax requirements. This extends the fix from <a href="https://www.sqlalchemy.org/trac/ticket/12778" rel="nofollow">#12778</a><br>
which addressed the same issue for function calls.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13067" rel="nofollow">#13067</a></p>
</li>
<li>
<p><strong>[postgresql] [bug]</strong> Improved the foreign key reflection regular expression pattern used by the<br>
PostgreSQL dialect to be more permissive in matching identifier characters,<br>
allowing it to correctly handle unicode characters in table and column<br>
names. This change improves compatibility with PostgreSQL variants such as<br>
CockroachDB that may use different quoting patterns in combination with<br>
unicode characters in their identifiers. Pull request courtesy Gord<br>
Thompson.</p>
</li>
</ul>
<h2>mariadb</h2>
<ul>
<li>
<p><strong>[mariadb] [bug]</strong> Fixed the SQL compilation for the mariadb sequence "NOCYCLE" keyword that<br>
is to be emitted when the <code>Sequence.cycle</code> parameter is set to<br>
False on a <code>Sequence</code>. Pull request courtesy Diego Dupin.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13070" rel="nofollow">#13070</a></p>
</li>
</ul>
<h2>sqlite</h2>
<ul>
<li>
<p><strong>[sqlite] [bug]</strong> Fixed issue in the aiosqlite driver where SQLAlchemy's setting of<br>
aiosqlite's worker thread to "daemon" stopped working because the aiosqlite<br>
architecture moved the location of the worker thread in version 0.22.0.<br>
This "daemon" flag is necessary so that a program is able to exit if the<br>
SQLite connection itself was not explicitly closed, which is particularly<br>
likely with SQLAlchemy as it maintains SQLite connections in a connection<br>
pool. While it's perfectly fine to call <code>AsyncEngine.dispose()</code><br>
before program exit, this is not historically or technically necessary for<br>
any driver of any known backend, since a primary feature of relational<br>
databases is durability. The change also implements support for<br>
"terminate" with aiosqlite when using version version 0.22.1 or greater,<br>
which implements a sync <code>.stop()</code> method.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13039" rel="nofollow">#13039</a></p>
</li>
</ul>
<h2>mssql</h2>
<ul>
<li>
<p><strong>[mssql] [usecase]</strong> Added support for the <code>IF EXISTS</code> clause when dropping indexes on SQL<br>
Server 2016 (13.x) and later versions. The <code>DropIndex.if_exists</code><br>
parameter is now honored by the SQL Server dialect, allowing conditional<br>
index drops that will not raise an error if the index does not exist.<br>
Pull request courtesy Edgar Ramírez Mondragón.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13045" rel="nofollow">#13045</a></p>
</li>
</ul>sqla-testertag:github.com,2008:Repository/159271175/rel_2_0_452025-12-10T20:14:51Z2.0.45<h1>2.0.45</h1>
<p>Released: December 9, 2025</p>
<h2>orm</h2>
<ul>
<li>
<p><strong>[orm] [bug]</strong> Fixed issue where calling <code>Mapper.add_property()</code> within mapper event<br>
hooks such as <code>MapperEvents.instrument_class()</code>,<br>
<code>MapperEvents.after_mapper_constructed()</code>, or<br>
<code>MapperEvents.before_mapper_configured()</code> would raise an<br>
<code>AttributeError</code> because the mapper's internal property collections were<br>
not yet initialized. The <code>Mapper.add_property()</code> method now handles<br>
early-stage property additions correctly, allowing properties including<br>
column properties, deferred columns, and relationships to be added during<br>
mapper initialization events. Pull request courtesy G Allajmi.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12858" rel="nofollow">#12858</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> Fixed issue in Python 3.14 where dataclass transformation would fail when<br>
a mapped class using <code>MappedAsDataclass</code> included a<br>
<code>relationship()</code> referencing a class that was not available at<br>
runtime (e.g., within a <code>TYPE_CHECKING</code> block). This occurred when using<br>
Python 3.14's <a href="https://peps.python.org/pep-0649" rel="nofollow">PEP 649</a> deferred annotations feature, which is the<br>
default behavior without a <code>from __future__ import annotations</code><br>
directive.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12952" rel="nofollow">#12952</a></p>
</li>
</ul>
<h2>examples</h2>
<ul>
<li><strong>[examples] [bug]</strong> Fixed the "short_selects" performance example where the cache was being<br>
used in all the examples, making it impossible to compare performance with<br>
and without the cache. Less important comparisons like "lambdas" and<br>
"baked queries" have been removed.</li>
</ul>
<h2>sql</h2>
<ul>
<li>
<p><strong>[sql] [bug]</strong> Some improvements to the <code>_sql.ClauseElement.params()</code> method to<br>
replace bound parameters in a query were made, however the ultimate issue<br>
in <a href="https://www.sqlalchemy.org/trac/ticket/12915" rel="nofollow">#12915</a> involving ORM <code>_orm.aliased()</code> cannot be fixed fully<br>
until 2.1, where the method is being rewritten to work without relying on<br>
Core cloned traversal.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12915" rel="nofollow">#12915</a></p>
</li>
<li>
<p><strong>[sql] [bug]</strong> Fixed issue where using the <code>ColumnOperators.in_()</code> operator with a<br>
nested <code>CompoundSelect</code> statement (e.g. an <code>INTERSECT</code> of<br>
<code>UNION</code> queries) would raise a <code>NotImplementedError</code> when the<br>
nested compound select was the first argument to the outer compound select.<br>
The <code>_scalar_type()</code> internal method now properly handles nested compound<br>
selects.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12987" rel="nofollow">#12987</a></p>
</li>
</ul>
<h2>typing</h2>
<ul>
<li>
<p><strong>[typing] [bug]</strong> Fixed typing issue where <code>Select.with_for_update()</code> would not support<br>
lists of ORM entities or other FROM clauses in the<br>
<code>Select.with_for_update.of</code> parameter. Pull request courtesy<br>
Shamil.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12730" rel="nofollow">#12730</a></p>
</li>
<li>
<p><strong>[typing] [bug]</strong> Fixed typing issue where <code>coalesce</code> would not return the correct<br>
return type when a nullable form of that argument were passed, even though<br>
this function is meant to select the non-null entry among possibly null<br>
arguments. Pull request courtesy Yannick PÉROUX.</p>
</li>
</ul>
<h2>postgresql</h2>
<ul>
<li>
<p><strong>[postgresql] [usecase]</strong> Added support for reflection of collation in types for PostgreSQL.<br>
The <code>collation</code> will be set only if different from the default<br>
one for the type.<br>
Pull request courtesy Denis Laxalde.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/6511" rel="nofollow">#6511</a></p>
</li>
<li>
<p><strong>[postgresql] [bug]</strong> Fixed issue where PostgreSQL dialect options such as <code>postgresql_include</code><br>
on <code>PrimaryKeyConstraint</code> and <code>UniqueConstraint</code> were<br>
rendered in the wrong position when combined with constraint deferrability<br>
options like <code>deferrable=True</code>. Pull request courtesy G Allajmi.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12867" rel="nofollow">#12867</a></p>
</li>
<li>
<p><strong>[postgresql] [bug]</strong> Fixed the structure of the SQL string used for the<br>
<code>engine_insertmanyvalues</code> feature when an explicit sequence with<br>
<code>nextval()</code> is used. The SQL function invocation for the sequence has<br>
been moved from being rendered inline within each tuple inside of VALUES to<br>
being rendered once in the SELECT that reads from VALUES. This change<br>
ensures the function is invoked in the correct order as rows are processed,<br>
rather than assuming PostgreSQL will execute inline function calls within<br>
VALUES in a particular order. While current PostgreSQL versions appear to<br>
handle the previous approach correctly, the database does not guarantee<br>
this behavior for future versions.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/13015" rel="nofollow">#13015</a></p>
</li>
</ul>
<h2>mysql</h2>
<ul>
<li>
<p><strong>[mysql] [usecase]</strong> Added support for MySQL 8.0.1 + <code>FOR SHARE</code> to be emitted for the<br>
<code>Select.with_for_update()</code> method, which offers compatibility with<br>
<code>NOWAIT</code> and <code>SKIP LOCKED</code>. The new syntax is used only for MySQL when<br>
version 8.0.1 or higher is detected. Pull request courtesy JetDrag.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12964" rel="nofollow">#12964</a></p>
</li>
</ul>
<h2>sqlite</h2>
<ul>
<li>
<p><strong>[sqlite] [bug] [reflection]</strong> A series of improvements have been made for reflection of CHECK constraints<br>
on SQLite. The reflection logic now correctly handles table names<br>
containing the strings "CHECK" or "CONSTRAINT", properly supports all four<br>
SQLite identifier quoting styles (double quotes, single quotes, brackets,<br>
and backticks) for constraint names, and accurately parses CHECK constraint<br>
expressions containing parentheses within string literals using balanced<br>
parenthesis matching with string context tracking. Big thanks to<br>
GruzdevAV for new test cases and implementation ideas.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12924" rel="nofollow">#12924</a></p>
</li>
<li>
<p><strong>[sqlite] [bug]</strong> Fixed issue where SQLite dialect would fail to reflect constraint names<br>
that contained uppercase letters or other characters requiring quoting. The<br>
regular expressions used to parse primary key, foreign key, and unique<br>
constraint names from the <code>CREATE TABLE</code> statement have been updated to<br>
properly handle both quoted and unquoted constraint names.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12954" rel="nofollow">#12954</a></p>
</li>
</ul>
<h2>tests</h2>
<ul>
<li><strong>[tests] [change]</strong> A noxfile.py has been added to allow testing with nox. This is a direct<br>
port of 2.1's move to nox, however leaves the tox.ini file in place and<br>
retains all test documentation in terms of tox. Version 2.1 will move to<br>
nox fully, including deprecation warnings for tox and new testing<br>
documentation.</li>
</ul>sqla-testertag:github.com,2008:Repository/159271175/rel_2_0_442025-10-10T16:20:28Z2.0.44<h1>2.0.44</h1>
<p>Released: October 10, 2025</p>
<h2>platform</h2>
<ul>
<li><strong>[platform] [bug]</strong> Unblocked automatic greenlet installation for Python 3.14 now that<br>
there are greenlet wheels on pypi for python 3.14.</li>
</ul>
<h2>orm</h2>
<ul>
<li>
<p><strong>[orm] [usecase]</strong> The way ORM Annotated Declarative interprets Python <a href="https://peps.python.org/pep-0695" rel="nofollow">PEP 695</a> type aliases<br>
in <code>Mapped[]</code> annotations has been refined to expand the lookup scheme. A<br>
<a href="https://peps.python.org/pep-0695" rel="nofollow">PEP 695</a> type can now be resolved based on either its direct presence in<br>
<code>_orm.registry.type_annotation_map</code> or its immediate resolved<br>
value, as long as a recursive lookup across multiple <a href="https://peps.python.org/pep-0695" rel="nofollow">PEP 695</a> types is<br>
not required for it to resolve. This change reverses part of the<br>
restrictions introduced in 2.0.37 as part of <a href="https://www.sqlalchemy.org/trac/ticket/11955" rel="nofollow">#11955</a>, which<br>
deprecated (and disallowed in 2.1) the ability to resolve any <a href="https://peps.python.org/pep-0695" rel="nofollow">PEP 695</a><br>
type that was not explicitly present in<br>
<code>_orm.registry.type_annotation_map</code>. Recursive lookups of<br>
<a href="https://peps.python.org/pep-0695" rel="nofollow">PEP 695</a> types remains deprecated in 2.0 and disallowed in version 2.1,<br>
as do implicit lookups of <code>NewType</code> types without an entry in<br>
<code>_orm.registry.type_annotation_map</code>.</p>
<p>Additionally, new support has been added for generic <a href="https://peps.python.org/pep-0695" rel="nofollow">PEP 695</a> aliases that<br>
refer to <a href="https://peps.python.org/pep-0593" rel="nofollow">PEP 593</a> <code>Annotated</code> constructs containing<br>
<code>_orm.mapped_column()</code> configurations. See the sections below for<br>
examples.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12829" rel="nofollow">#12829</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> Fixed a caching issue where <code>_orm.with_loader_criteria()</code> would<br>
incorrectly reuse cached bound parameter values when used with<br>
<code>_sql.CompoundSelect</code> constructs such as <code>_sql.union()</code>. The<br>
issue was caused by the cache key for compound selects not including the<br>
execution options that are part of the <code>_sql.Executable</code> base class,<br>
which <code>_orm.with_loader_criteria()</code> uses to apply its criteria<br>
dynamically. The fix ensures that compound selects and other executable<br>
constructs properly include execution options in their cache key traversal.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12905" rel="nofollow">#12905</a></p>
</li>
</ul>
<h2>engine</h2>
<ul>
<li>
<p><strong>[engine] [bug]</strong> Implemented initial support for free-threaded Python by adding new tests<br>
and reworking the test harness to include Python 3.13t and Python 3.14t in<br>
test runs. Two concurrency issues have been identified and fixed: the first<br>
involves initialization of the <code>.c</code> collection on a <code>FromClause</code>, a<br>
continuation of <a href="https://www.sqlalchemy.org/trac/ticket/12302" rel="nofollow">#12302</a>, where an optional mutex under<br>
free-threading is added; the second involves synchronization of the pool<br>
"first_connect" event, which first received thread synchronization in<br>
<a href="https://www.sqlalchemy.org/trac/ticket/2964" rel="nofollow">#2964</a>, however under free-threading the creation of the mutex<br>
itself runs under the same free-threading mutex. Support for free-threaded<br>
wheels on Pypi is implemented as well within the 2.1 series only. Initial<br>
pull request and test suite courtesy Lysandros Nikolaou.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12881" rel="nofollow">#12881</a></p>
</li>
</ul>
<h2>sql</h2>
<ul>
<li>
<p><strong>[sql] [bug]</strong> Improved the implementation of <code>UpdateBase.returning()</code> to use more<br>
robust logic in setting up the <code>.c</code> collection of a derived statement<br>
such as a CTE. This fixes issues related to RETURNING clauses that feature<br>
expressions based on returned columns with or without qualifying labels.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12271" rel="nofollow">#12271</a></p>
</li>
</ul>
<h2>schema</h2>
<ul>
<li>
<p><strong>[schema] [bug]</strong> Fixed issue where <code>_schema.MetaData.reflect()</code> did not forward<br>
dialect-specific keyword arguments to the <code>_engine.Inspector</code><br>
methods, causing options like <code>oracle_resolve_synonyms</code> to be ignored<br>
during reflection. The method now ensures that all extra kwargs passed to<br>
<code>_schema.MetaData.reflect()</code> are forwarded to<br>
<code>_engine.Inspector.get_table_names()</code> and related reflection methods.<br>
Pull request courtesy Lukáš Kožušník.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12884" rel="nofollow">#12884</a></p>
</li>
</ul>
<h2>typing</h2>
<ul>
<li>
<p><strong>[typing] [bug]</strong> Fixed typing bug where the <code>Session.execute()</code> method advertised that<br>
it would return a <code>CursorResult</code> if given an insert/update/delete<br>
statement. This is not the general case as several flavors of ORM<br>
insert/update do not actually yield a <code>CursorResult</code> which cannot<br>
be differentiated at the typing overload level, so the method now yields<br>
<code>Result</code> in all cases. For those cases where<br>
<code>CursorResult</code> is known to be returned and the <code>.rowcount</code><br>
attribute is required, please use <code>typing.cast()</code>.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12813" rel="nofollow">#12813</a></p>
</li>
<li>
<p><strong>[typing] [bug]</strong> Added new decorator <code>_orm.mapped_as_dataclass()</code>, which is a function<br>
based form of <code>_orm.registry.mapped_as_dataclass()</code>; the method form<br>
<code>_orm.registry.mapped_as_dataclass()</code> does not seem to be correctly<br>
recognized within the scope of <a href="https://peps.python.org/pep-0681" rel="nofollow">PEP 681</a> in recent mypy versions.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12855" rel="nofollow">#12855</a></p>
</li>
</ul>
<h2>asyncio</h2>
<ul>
<li>
<p><strong>[asyncio] [usecase]</strong> Generalize the terminate logic employed by the asyncpg dialect to reuse<br>
it in the aiomysql and asyncmy dialect implementation.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12273" rel="nofollow">#12273</a></p>
</li>
</ul>
<h2>postgresql</h2>
<ul>
<li>
<p><strong>[postgresql] [bug]</strong> Fixed issue where selecting an enum array column containing NULL values<br>
would fail to parse properly in the PostgreSQL dialect. The<br>
<code>_split_enum_values()</code> function now correctly handles NULL entries by<br>
converting them to Python <code>None</code> values.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12847" rel="nofollow">#12847</a></p>
</li>
<li>
<p><strong>[postgresql] [bug]</strong> Fixed issue where the <code>_sql.any_()</code> and <code>_sql.all_()</code> aggregation<br>
operators would not correctly coerce the datatype of the compared value, in<br>
those cases where the compared value were not a simple int/str etc., such<br>
as a Python <code>Enum</code> or other custom value. This would lead to execution<br>
time errors for these values. This issue is essentially the same as<br>
<a href="https://www.sqlalchemy.org/trac/ticket/6515" rel="nofollow">#6515</a> which was for the now-legacy <code>ARRAY.any()</code> and<br>
<code>ARRAY.all()</code> methods.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12874" rel="nofollow">#12874</a></p>
</li>
</ul>
<h2>sqlite</h2>
<ul>
<li>
<p><strong>[sqlite] [bug]</strong> Fixed issue where SQLite table reflection would fail for tables using<br>
<code>WITHOUT ROWID</code> and/or <code>STRICT</code> table options when the table contained<br>
generated columns. The regular expression used to parse <code>CREATE TABLE</code><br>
statements for generated column detection has been updated to properly<br>
handle these SQLite table options that appear after the column definitions.<br>
Pull request courtesy Tip ten Brink.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12864" rel="nofollow">#12864</a></p>
</li>
</ul>
<h2>mssql</h2>
<ul>
<li>
<p><strong>[mssql] [bug]</strong> Improved the base implementation of the asyncio cursor such that it<br>
includes the option for the underlying driver's cursor to be actively<br>
closed in those cases where it requires <code>await</code> in order to complete the<br>
close sequence, rather than relying on garbage collection to "close" it,<br>
when a plain <code>Result</code> is returned that does not use <code>await</code> for<br>
any of its methods. The previous approach of relying on gc was fine for<br>
MySQL and SQLite dialects but has caused problems with the aioodbc<br>
implementation on top of SQL Server. The new option is enabled<br>
for those dialects which have an "awaitable" <code>cursor.close()</code>, which<br>
includes the aioodbc, aiomysql, and asyncmy dialects (aiosqlite is also<br>
modified for 2.1 only).</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12798" rel="nofollow">#12798</a></p>
</li>
<li>
<p><strong>[mssql] [bug]</strong> Fixed issue where the index reflection for SQL Server would<br>
not correctly return the order of the column inside an index<br>
when the order of the columns in the index did not match the<br>
order of the columns in the table.<br>
Pull request courtesy of Allen Chen.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12894" rel="nofollow">#12894</a></p>
</li>
<li>
<p><strong>[mssql] [bug] [reflection]</strong> Fixed issue in the MSSQL dialect's foreign key reflection query where<br>
duplicate rows could be returned when a foreign key column and its<br>
referenced primary key column have the same name, and both the referencing<br>
and referenced tables have indexes with the same name. This resulted in an<br>
"ForeignKeyConstraint with duplicate source column references are not<br>
supported" error when attempting to reflect such tables. The query has been<br>
corrected to exclude indexes on the child table when looking for unique<br>
indexes referenced by foreign keys.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12907" rel="nofollow">#12907</a></p>
</li>
</ul>
<h2>misc</h2>
<ul>
<li>
<p><strong>[bug] [ext]</strong> Fixed issue caused by an unwanted functional change while typing<br>
the <code>MutableList</code> class.<br>
This change also reverts all other functional changes done in<br>
the same change.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12802" rel="nofollow">#12802</a></p>
</li>
</ul>sqla-testertag:github.com,2008:Repository/159271175/rel_2_0_432025-08-11T16:07:16Z2.0.43<h1>2.0.43</h1>
<p>Released: August 11, 2025</p>
<h2>orm</h2>
<ul>
<li>
<p><strong>[orm] [bug]</strong> Fixed issue where using the <code>post_update</code> feature would apply incorrect<br>
"pre-fetched" values to the ORM objects after a multi-row UPDATE process<br>
completed. These "pre-fetched" values would come from any column that had<br>
an <code>Column.onupdate</code> callable or a version id generator used by<br>
<code>orm.Mapper.version_id_generator</code>; for a version id generator<br>
that delivered random identifiers like timestamps or UUIDs, this incorrect<br>
data would lead to a DELETE statement against those same rows to fail in<br>
the next step.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12748" rel="nofollow">#12748</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> Fixed issue where <code>_orm.mapped_column.use_existing_column</code><br>
parameter in <code>_orm.mapped_column()</code> would not work when the<br>
<code>_orm.mapped_column()</code> is used inside of an <code>Annotated</code> type alias in<br>
polymorphic inheritance scenarios. The parameter is now properly recognized<br>
and processed during declarative mapping configuration.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12787" rel="nofollow">#12787</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> Improved the implementation of the <code>_orm.selectin_polymorphic()</code><br>
inheritance loader strategy to properly render the IN expressions using<br>
chunks of 500 records each, in the same manner as that of the<br>
<code>_orm.selectinload()</code> relationship loader strategy. Previously, the IN<br>
expression would be arbitrarily large, leading to failures on databases<br>
that have limits on the size of IN expressions including Oracle Database.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12790" rel="nofollow">#12790</a></p>
</li>
</ul>
<h2>engine</h2>
<ul>
<li>
<p><strong>[engine] [usecase]</strong> Added new parameter <code>create_engine.skip_autocommit_rollback</code><br>
which provides for a per-dialect feature of preventing the DBAPI<br>
<code>.rollback()</code> from being called under any circumstances, if the<br>
connection is detected as being in "autocommit" mode. This improves upon<br>
a critical performance issue identified in MySQL dialects where the network<br>
overhead of the <code>.rollback()</code> call remains prohibitive even if autocommit<br>
mode is set.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12784" rel="nofollow">#12784</a></p>
</li>
</ul>
<h2>postgresql</h2>
<ul>
<li>
<p><strong>[postgresql] [bug]</strong> Fixed regression in PostgreSQL dialect where JSONB subscription syntax<br>
would generate incorrect SQL for JSONB-returning functions, causing syntax<br>
errors. The dialect now properly wraps function calls and expressions in<br>
parentheses when using the <code>[]</code> subscription syntax, generating<br>
<code>(function_call)[index]</code> instead of <code>function_call[index]</code> to comply<br>
with PostgreSQL syntax requirements.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12778" rel="nofollow">#12778</a></p>
</li>
</ul>
<h2>oracle</h2>
<ul>
<li>
<p><strong>[oracle] [usecase]</strong> Extended <code>_oracle.VECTOR</code> to support sparse vectors. This update<br>
introduces <code>_oracle.VectorStorageType</code> to specify sparse or dense<br>
storage and added <code>_oracle.SparseVector</code>. Pull request courtesy<br>
Suraj Shaw.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12711" rel="nofollow">#12711</a></p>
</li>
</ul>sqla-testertag:github.com,2008:Repository/159271175/rel_2_0_422025-07-29T13:39:32Z2.0.42<h1>2.0.42</h1>
<p>Released: July 29, 2025</p>
<h2>orm</h2>
<ul>
<li>
<p><strong>[orm] [usecase]</strong> Added <code>dataclass_metadata</code> argument to all ORM attribute constructors<br>
that accept dataclasses parameters, e.g. <code>mapped_column.dataclass_metadata</code>,<br>
<code>relationship.dataclass_metadata</code>, etc.<br>
It's passed to the underlying dataclass <code>metadata</code> attribute<br>
of the dataclass field. Pull request courtesy Sigmund Lahn.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10674" rel="nofollow">#10674</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> Implemented the <code>_orm.defer()</code>, <code>_orm.undefer()</code> and<br>
<code>_orm.load_only()</code> loader options to work for composite attributes, a<br>
use case that had never been supported previously.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12593" rel="nofollow">#12593</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> Fixed bug where the ORM would pull in the wrong column into an UPDATE when<br>
a key name inside of the <code>ValuesBase.values()</code> method could be located<br>
from an ORM entity mentioned in the statement, but where that ORM entity<br>
was not the actual table that the statement was inserting or updating. An<br>
extra check for this edge case is added to avoid this problem.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12692" rel="nofollow">#12692</a></p>
</li>
</ul>
<h2>engine</h2>
<ul>
<li><strong>[engine]</strong> Improved validation of execution parameters passed to the<br>
<code>_engine.Connection.execute()</code> and similar methods to<br>
provided a better error when tuples are passed in.<br>
Previously the execution would fail with a difficult to<br>
understand error message.</li>
</ul>
<h2>sql</h2>
<ul>
<li>
<p><strong>[sql] [usecase]</strong> The <code>_sql.values()</code> construct gains a new method <code>_sql.Values.cte()</code>,<br>
which allows creation of a named, explicit-columns <code>CTE</code> against an<br>
unnamed <code>VALUES</code> expression, producing a syntax that allows column-oriented<br>
selection from a <code>VALUES</code> construct on modern versions of PostgreSQL, SQLite,<br>
and MariaDB.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12734" rel="nofollow">#12734</a></p>
</li>
<li>
<p><strong>[sql] [bug]</strong> Fixed issue where <code>select()</code> of a free-standing scalar expression that<br>
has a unary operator applied, such as negation, would not apply result<br>
processors to the selected column even though the correct type remains in<br>
place for the unary expression.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12681" rel="nofollow">#12681</a></p>
</li>
<li>
<p><strong>[sql] [bug]</strong> Hardening of the compiler's actions for UPDATE statements that access<br>
multiple tables to report more specifically when tables or aliases are<br>
referenced in the SET clause; on cases where the backend does not support<br>
secondary tables in the SET clause, an explicit error is raised, and on the<br>
MySQL or similar backends that support such a SET clause, more specific<br>
checking for not-properly-included tables is performed. Overall the change<br>
is preventing these erroneous forms of UPDATE statements from being<br>
compiled, whereas previously it was relied on the database to raise an<br>
error, which was not always guaranteed to happen, or to be non-ambiguous,<br>
due to cases where the parent table included the same column name as the<br>
secondary table column being updated.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12692" rel="nofollow">#12692</a></p>
</li>
</ul>
<h2>postgresql</h2>
<ul>
<li>
<p><strong>[postgresql] [usecase]</strong> Added support for PostgreSQL 14+ JSONB subscripting syntax. When connected<br>
to PostgreSQL 14 or later, JSONB columns now automatically use the native<br>
subscript notation <code>jsonb_col['key']</code> instead of the arrow operator<br>
<code>jsonb_col -> 'key'</code> for both read and write operations. This provides<br>
better compatibility with PostgreSQL's native JSONB subscripting feature<br>
while maintaining backward compatibility with older PostgreSQL versions.<br>
JSON columns continue to use the traditional arrow syntax regardless of<br>
PostgreSQL version.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10927" rel="nofollow">#10927</a></p>
</li>
<li>
<p><strong>[postgresql] [usecase]</strong> Added <code>postgresql_ops</code> key to the <code>dialect_options</code> entry in reflected<br>
dictionary. This maps names of columns used in the index to respective<br>
operator class, if distinct from the default one for column's data type.<br>
Pull request courtesy Denis Laxalde.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/8664" rel="nofollow">#8664</a></p>
</li>
<li>
<p><strong>[postgresql] [bug] [reflection]</strong> Fixed regression caused by <a href="https://www.sqlalchemy.org/trac/ticket/10665" rel="nofollow">#10665</a> where the newly modified<br>
constraint reflection query would fail on older versions of PostgreSQL<br>
such as version 9.6. Pull request courtesy Denis Laxalde.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12600" rel="nofollow">#12600</a></p>
</li>
<li>
<p><strong>[postgresql] [bug]</strong> Re-raise catched <code>CancelledError</code> in the terminate method of the<br>
asyncpg dialect to avoid possible hangs of the code execution.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12728" rel="nofollow">#12728</a></p>
</li>
<li>
<p><strong>[postgresql] [bug] [reflection]</strong> Fixes bug that would mistakenly interpret a domain or enum type<br>
with name starting in <code>interval</code> as an <code>INTERVAL</code> type while<br>
reflecting a table.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12744" rel="nofollow">#12744</a></p>
</li>
</ul>
<h2>mysql</h2>
<ul>
<li>
<p><strong>[mysql] [bug]</strong> Fixed yet another regression caused by by the DEFAULT rendering changes in<br>
2.0.40 <a href="https://www.sqlalchemy.org/trac/ticket/12425" rel="nofollow">#12425</a>, similar to <a href="https://www.sqlalchemy.org/trac/ticket/12488" rel="nofollow">#12488</a>, this time where using a<br>
CURRENT_TIMESTAMP function with a fractional seconds portion inside a<br>
textual default value would also fail to be recognized as a<br>
non-parenthesized server default.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12648" rel="nofollow">#12648</a></p>
</li>
</ul>
<h2>mssql</h2>
<ul>
<li>
<p><strong>[mssql] [bug]</strong> Reworked SQL Server column reflection to be based on the <code>sys.columns</code><br>
table rather than <code>information_schema.columns</code> view. By correctly using<br>
the SQL Server <code>object_id()</code> function as a lead and joining to related<br>
tables on object_id rather than names, this repairs a variety of issues in<br>
SQL Server reflection, including:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="- Issue where reflected column comments would not correctly line up
with the columns themselves in the case that the table had been ALTERed
- Correctly targets tables with awkward names such as names with brackets,
when reflecting not just the basic table / columns but also extended
information including IDENTITY, computed columns, comments which
did not work previously
- Correctly targets IDENTITY, computed status from temporary tables
which did not work previously"><pre class="notranslate"><code>- Issue where reflected column comments would not correctly line up
with the columns themselves in the case that the table had been ALTERed
- Correctly targets tables with awkward names such as names with brackets,
when reflecting not just the basic table / columns but also extended
information including IDENTITY, computed columns, comments which
did not work previously
- Correctly targets IDENTITY, computed status from temporary tables
which did not work previously
</code></pre></div>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12654" rel="nofollow">#12654</a></p>
</li>
</ul>sqla-testertag:github.com,2008:Repository/159271175/rel_2_0_412025-05-14T18:01:24Z2.0.41<h1>2.0.41</h1>
<p>Released: May 14, 2025</p>
<h2>platform</h2>
<ul>
<li>
<p><strong>[platform] [bug]</strong> Adjusted the test suite as well as the ORM's method of scanning classes for<br>
annotations to work under current beta releases of Python 3.14 (currently<br>
3.14.0b1) as part of an ongoing effort to support the production release of<br>
this Python release. Further changes to Python's means of working with<br>
annotations is expected in subsequent beta releases for which SQLAlchemy's<br>
test suite will need further adjustments.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12405" rel="nofollow">#12405</a></p>
</li>
</ul>
<h2>engine</h2>
<ul>
<li>
<p><strong>[engine] [bug]</strong> The error message that is emitted when a URL cannot be parsed no longer<br>
includes the URL itself within the error message.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12579" rel="nofollow">#12579</a></p>
</li>
</ul>
<h2>typing</h2>
<ul>
<li>
<p><strong>[typing] [bug]</strong> Removed <code>__getattr__()</code> rule from <code>sqlalchemy/__init__.py</code> that<br>
appeared to be trying to correct for a previous typographical error in the<br>
imports. This rule interferes with type checking and is removed.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12588" rel="nofollow">#12588</a></p>
</li>
</ul>
<h2>postgresql</h2>
<ul>
<li>
<p><strong>[postgresql] [usecase]</strong> Added support for <code>postgresql_include</code> keyword argument to<br>
<code>_schema.UniqueConstraint</code> and <code>_schema.PrimaryKeyConstraint</code>.<br>
Pull request courtesy Denis Laxalde.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/10665" rel="nofollow">#10665</a></p>
</li>
</ul>
<h2>mysql</h2>
<ul>
<li>
<p><strong>[mysql] [bug]</strong> Fixed regression caused by the DEFAULT rendering changes in version 2.0.40<br>
via <a href="https://www.sqlalchemy.org/trac/ticket/12425" rel="nofollow">#12425</a> where using lowercase <code>on update</code> in a MySQL server<br>
default would incorrectly apply parenthesis, leading to errors when MySQL<br>
interpreted the rendered DDL. Pull request courtesy Alexander Ruehe.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12488" rel="nofollow">#12488</a></p>
</li>
</ul>
<h2>sqlite</h2>
<ul>
<li>
<p><strong>[sqlite] [bug]</strong> Fixed and added test support for some SQLite SQL functions hardcoded into<br>
the compiler, most notably the <code>localtimestamp</code> function which rendered<br>
with incorrect internal quoting.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12566" rel="nofollow">#12566</a></p>
</li>
</ul>
<h2>oracle</h2>
<ul>
<li>
<p><strong>[oracle] [usecase]</strong> Added new datatype <code>_oracle.VECTOR</code> and accompanying DDL and DQL<br>
support to fully support this type for Oracle Database. This change<br>
includes the base <code>_oracle.VECTOR</code> type that adds new type-specific<br>
methods <code>l2_distance</code>, <code>cosine_distance</code>, <code>inner_product</code> as well as<br>
new parameters <code>oracle_vector</code> for the <code>Index</code> construct,<br>
allowing vector indexes to be configured, and <code>oracle_fetch_approximate</code><br>
for the <code>Select.fetch()</code> clause. Pull request courtesy Suraj Shaw.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12317" rel="nofollow">#12317</a>, <a href="https://www.sqlalchemy.org/trac/ticket/12341" rel="nofollow">#12341</a></p>
</li>
</ul>
<h2>misc</h2>
<ul>
<li><strong>[bug] [installation]</strong> Removed the "license classifier" from setup.cfg for SQLAlchemy 2.0, which<br>
eliminates loud deprecation warnings when building the package. SQLAlchemy<br>
2.1 will use a full <a href="https://peps.python.org/pep-0639" rel="nofollow">PEP 639</a> configuration in pyproject.toml while<br>
SQLAlchemy 2.0 remains using <code>setup.cfg</code> for setup.</li>
</ul>sqla-testertag:github.com,2008:Repository/159271175/rel_2_0_402025-03-27T18:55:27Z2.0.40<h1>2.0.40</h1>
<p>Released: March 27, 2025</p>
<h2>orm</h2>
<ul>
<li>
<p><strong>[orm] [bug]</strong> Fixed regression which occurred as of 2.0.37 where the checked<br>
<code>ArgumentError</code> that's raised when an inappropriate type or object<br>
is used inside of a <code>Mapped</code> annotation would raise <code>TypeError</code><br>
with "boolean value of this clause is not defined" if the object resolved<br>
into a SQL expression in a boolean context, for programs where future<br>
annotations mode was not enabled. This case is now handled explicitly and<br>
a new error message has also been tailored for this case. In addition, as<br>
there are at least half a dozen distinct error scenarios for intepretation<br>
of the <code>Mapped</code> construct, these scenarios have all been unified<br>
under a new subclass of <code>ArgumentError</code> called<br>
<code>MappedAnnotationError</code>, to provide some continuity between these<br>
different scenarios, even though specific messaging remains distinct.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12329" rel="nofollow">#12329</a></p>
</li>
<li>
<p><strong>[orm] [bug]</strong> Fixed regression in ORM Annotated Declarative class interpretation caused<br>
by <code>typing_extension==4.13.0</code> that introduced a different implementation<br>
for <code>TypeAliasType</code> while SQLAlchemy assumed that it would be equivalent<br>
to the <code>typing</code> version, leading to pep-695 type annotations not<br>
resolving to SQL types as expected.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12473" rel="nofollow">#12473</a></p>
</li>
</ul>
<h2>sql</h2>
<ul>
<li>
<p><strong>[sql] [usecase]</strong> Implemented support for the GROUPS frame specification in window functions<br>
by adding <code>_sql.over.groups</code> option to <code>_sql.over()</code><br>
and <code>FunctionElement.over()</code>. Pull request courtesy Kaan Dikmen.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12450" rel="nofollow">#12450</a></p>
</li>
<li>
<p><strong>[sql] [bug]</strong> Fixed issue in <code>CTE</code> constructs involving multiple DDL<br>
<code>_sql.Insert</code> statements with multiple VALUES parameter sets where the<br>
bound parameter names generated for these parameter sets would conflict,<br>
generating a compile time error.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12363" rel="nofollow">#12363</a></p>
</li>
<li>
<p><strong>[sql] [bug]</strong> Fixed regression caused by <a href="https://www.sqlalchemy.org/trac/ticket/7471" rel="nofollow">#7471</a> leading to a SQL compilation<br>
issue where name disambiguation for two same-named FROM clauses with table<br>
aliasing in use at the same time would produce invalid SQL in the FROM<br>
clause with two "AS" clauses for the aliased table, due to double aliasing.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12451" rel="nofollow">#12451</a></p>
</li>
</ul>
<h2>asyncio</h2>
<ul>
<li>
<p><strong>[asyncio] [bug]</strong> Fixed issue where <code>AsyncSession.get_transaction()</code> and<br>
<code>AsyncSession.get_nested_transaction()</code> would fail with<br>
<code>NotImplementedError</code> if the "proxy transaction" used by<br>
<code>AsyncSession</code> were garbage collected and needed regeneration.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12471" rel="nofollow">#12471</a></p>
</li>
</ul>
<h2>postgresql</h2>
<ul>
<li>
<p><strong>[postgresql] [usecase]</strong> Added support for specifying a list of columns for <code>SET NULL</code> and <code>SET DEFAULT</code> actions of <code>ON DELETE</code> clause of foreign key definition on<br>
PostgreSQL. Pull request courtesy Denis Laxalde.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/11595" rel="nofollow">#11595</a></p>
</li>
<li>
<p><strong>[postgresql] [usecase]</strong> When building a PostgreSQL <code>ARRAY</code> literal using<br>
<code>_postgresql.array</code> with an empty <code>clauses</code> argument, the<br>
<code>_postgresql.array.type_</code> parameter is now significant in that it<br>
will be used to render the resulting <code>ARRAY[]</code> SQL expression with a<br>
cast, such as <code>ARRAY[]::INTEGER</code>. Pull request courtesy Denis Laxalde.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12432" rel="nofollow">#12432</a></p>
</li>
</ul>
<h2>mysql</h2>
<ul>
<li>
<p><strong>[mysql] [bug]</strong> Support has been re-added for the MySQL-Connector/Python DBAPI using the<br>
<code>mysql+mysqlconnector://</code> URL scheme. The DBAPI now works against<br>
modern MySQL versions as well as MariaDB versions (in the latter case it's<br>
required to pass charset/collation explicitly). Note however that<br>
server side cursor support is disabled due to unresolved issues with this<br>
driver.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12332" rel="nofollow">#12332</a></p>
</li>
<li>
<p><strong>[mysql] [bug]</strong> Fixed issue in MySQL server default reflection where a default that has<br>
spaces would not be correctly reflected. Additionally, expanded the rules<br>
for when to apply parenthesis to a server default in DDL to suit the<br>
general case of a default string that contains non-word characters such as<br>
spaces or operators and is not a string literal.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12425" rel="nofollow">#12425</a></p>
</li>
</ul>
<h2>sqlite</h2>
<ul>
<li>
<p><strong>[sqlite] [bug]</strong> Expanded the rules for when to apply parenthesis to a server default in DDL<br>
to suit the general case of a default string that contains non-word<br>
characters such as spaces or operators and is not a string literal.</p>
<p>References: <a href="https://www.sqlalchemy.org/trac/ticket/12425" rel="nofollow">#12425</a></p>
</li>
</ul>sqla-tester