tag:github.com,2008:https://github.com/sqlalchemy/sqlalchemy/releases Release notes from sqlalchemy 2026-03-02T14:39:42Z tag:github.com,2008:Repository/159271175/rel_2_0_48 2026-03-02T16:07:48Z 2.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-tester tag:github.com,2008:Repository/159271175/rel_2_0_47 2026-02-24T17:28:51Z 2.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.&lt;column&gt;</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.&lt;column&gt;</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-tester tag:github.com,2008:Repository/159271175/rel_2_1_0b1 2026-01-21T21:19:04Z 2.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 &lt;orm_declarative_native_dataclasses&gt;</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>&amp;</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 -&gt; '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 &gt;= 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-tester tag:github.com,2008:Repository/159271175/rel_2_0_46 2026-01-21T19:05:46Z 2.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-tester tag:github.com,2008:Repository/159271175/rel_2_0_45 2025-12-10T20:14:51Z 2.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-tester tag:github.com,2008:Repository/159271175/rel_2_0_44 2025-10-10T16:20:28Z 2.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-tester tag:github.com,2008:Repository/159271175/rel_2_0_43 2025-08-11T16:07:16Z 2.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-tester tag:github.com,2008:Repository/159271175/rel_2_0_42 2025-07-29T13:39:32Z 2.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 -&gt; '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-tester tag:github.com,2008:Repository/159271175/rel_2_0_41 2025-05-14T18:01:24Z 2.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-tester tag:github.com,2008:Repository/159271175/rel_2_0_40 2025-03-27T18:55:27Z 2.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