2.1.0b1
Pre-release2.1.0b1
Released: January 21, 2026
platform
-
[platform] [feature] Free-threaded Python versions are now supported in wheels released on Pypi.
This integrates with overall free-threaded support added as part of
#12881 for the 2.0 and 2.1 series, which includes new test suites
as well as a few improvements to race conditions observed under
freethreading.References: #12881
-
[platform] [change] The
greenletdependency used for asyncio support no longer installs
by default. This dependency does not publish wheel files for every architecture
and is not needed for applications that aren't using asyncio features.
Use thesqlalchemy[asyncio]install target to include this dependency.References: #10197
-
[platform] [change] Updated the setup manifest definition to use PEP 621-compliant
pyproject.toml. Also updated the extra install dependency to comply with
PEP-685. Thanks for the help of Matt Oberle and KOLANICH on this change. -
[platform] [change] Python 3.10 or above is now required; support for Python 3.9, 3.8 and 3.7
is dropped as these versions are EOL.
orm
-
[orm] [feature] The
_orm.relationship.back_populatesargument to
_orm.relationship()may now be passed as a Python callable, which
resolves to either the direct linked ORM attribute, or a string value as
before. ORM attributes are also accepted directly by
_orm.relationship.back_populates. This change allows type
checkers and IDEs to confirm the argument for
_orm.relationship.back_populatesis valid. Thanks to Priyanshu
Parikh for the help on suggesting and helping to implement this feature.References: #10050
-
[orm] [feature] Added new hybrid method
hybrid_property.bulk_dml()which
works in a similar way ashybrid_property.update_expression()for
bulk ORM operations. A user-defined class method can now populate a bulk
insert mapping dictionary using the desired hybrid mechanics. New
documentation is added showing how both of these methods can be used
including in combination with the new_sql.from_dml_column()
construct.References: #12496
-
[orm] [feature] Added new parameter
_orm.composite.return_none_onto
_orm.composite(), which allows control over if and when this
composite attribute should resolve toNonewhen queried or retrieved
from the object directly. By default, a composite object is always present
on the attribute, including for a pending object which is a behavioral
change since 2.0. When_orm.composite.return_none_onis
specified, a callable is passed that returns True or False to indicate if
the given arguments indicate the composite should be returned as None. This
parameter may also be set automatically when ORM Annotated Declarative is
used; if the annotation is given asMapped[SomeClass|None], a
_orm.composite.return_none_onrule is applied that will return
Noneif all contained columns are themselvesNone.References: #12570
-
[orm] [feature] Added support for per-session execution options that are merged into all
queries executed within that session. The_orm.Session,
_orm.sessionmaker,_orm.scoped_session,
_ext.asyncio.AsyncSession, and
_ext.asyncio.async_sessionmakerconstructors now accept an
_orm.Session.execution_optionsparameter that will be applied
to all explicit query executions (e.g. using_orm.Session.execute(),
_orm.Session.get(),_orm.Session.scalars()) for that session
instance.References: #12659
-
[orm] [feature] Session autoflush behavior has been simplified to unconditionally flush the
session each time an execution takes place, regardless of whether an ORM
statement or Core statement is being executed. This change eliminates the
previous conditional logic that only flushed when ORM-related statements
were detected, which had become difficult to define clearly with the unified
v2 syntax that allows both Core and ORM execution patterns. The change
provides more consistent and predictable session behavior across all types
of SQL execution.References: #9809
-
[orm] [feature] Added
_orm.RegistryEventsevent class that allows event listeners
to be established on a_orm.registryobject. The new class
provides three events:_orm.RegistryEvents.resolve_type_annotation()
which allows customization of type annotation resolution that can
supplement or replace the use of the
registry.type_annotation_mapdictionary, including that it can
be helpful with custom resolution for complex types such as those of
PEP 695, as well as_orm.RegistryEvents.before_configured()and
_orm.RegistryEvents.after_configured(), which are registry-local
forms of the mapper-wide version of these hooks.References: #9832
-
[orm] [usecase] The
_orm.Session.flush.objectsparameter is now
deprecated.References: #10816
-
[orm] [usecase] Added the utility method
_orm.Session.merge_all()and
_orm.Session.delete_all()that operate on a collection
of instances.References: #11776
-
[orm] [usecase] Added support for using
_orm.with_expression()to populate a
_orm.query_expression()attribute that is also configured as the
polymorphic_ondiscriminator column. The ORM now detects when a query
expression column is serving as the polymorphic discriminator and updates
it to use the column provided via_orm.with_expression(), enabling
polymorphic loading to work correctly in this scenario. This allows for
patterns such as where the discriminator value is computed from a related
table.References: #12631
-
[orm] [usecase] Added default implementations of
ColumnOperators.desc(),
ColumnOperators.asc(),ColumnOperators.nulls_first(),
ColumnOperators.nulls_last()to_orm.composite()attributes,
by default applying the modifier to all contained columns. Can be
overridden using a custom comparator.References: #12769
-
[orm] [usecase] The
_orm.aliased()object now emits warnings when an attribute is
accessed on an aliased class that cannot be located in the target
selectable, for those cases where the_orm.aliased()is against a
different FROM clause than the regular mapped table (such as a subquery).
This helps users identify cases where column names don't match between the
aliased class and the underlying selectable. When
_orm.aliased.adapt_on_namesisTrue, the warning suggests
checking the column name; whenFalse, it suggests using the
adapt_on_namesparameter for name-based matching.References: #12838
-
[orm] [usecase] Improvements to the use case of using
Declarative Dataclass Mapping <orm_declarative_native_dataclasses>with intermediary classes that are
unmapped. As was the existing behavior, classes can subclass
_orm.MappedAsDataclassalone without a declarative base to act as
mixins, or along with a declarative base as well as__abstract__ = True
to define an abstract base. However, the improved behavior scans ORM
attributes like_orm.mapped_column()in this case to create correct
dataclasses.field()constructs based on their arguments, allowing for
more natural ordering of fields without dataclass errors being thrown.
Additionally, added a new_orm.unmapped_dataclass()decorator
function, which may be used to create unmapped mixins in a mapped hierarchy
that is using the_orm.mapped_dataclass()decorator to create mapped
dataclasses.References: #12854
-
[orm] [usecase] Added
_orm.DictBundleas a subclass of_orm.Bundle
that returnsdictobjects.References: #12960
-
[orm] [change] A sweep through class and function names in the ORM renames many classes
and functions that have no intent of public visibility to be underscored.
This is to reduce ambiguity as to which APIs are intended to be targeted by
third party applications and extensions. Third parties are encouraged to
propose new public APIs in Discussions to the extent they are needed to
replace those that have been clarified as private.References: #10497
-
[orm] [change] The
first_initORM event has been removed. This event was
non-functional throughout the 1.4 and 2.0 series and could not be invoked
without raising an internal error, so it is not expected that there is any
real-world use of this event hook.References: #10500
-
[orm] [change] Removed legacy signatures dating back to 0.9 release from the
_orm.SessionEvents.after_bulk_update()and
_orm.SessionEvents.after_bulk_delete().References: #10721
-
[orm] [changed] The "non primary" mapper feature, long deprecated in SQLAlchemy since
version 1.3, has been removed. The sole use case for "non primary"
mappers was that of using_orm.relationship()to link to a mapped
class against an alternative selectable; this use case is now suited by the
relationship_aliased_classfeature.References: #12437
-
[orm] [bug] The
_orm.relationship.secondaryparameter no longer uses Python
eval()to evaluate the given string. This parameter when passed a
string should resolve to a table name that's present in the local
MetaDatacollection only, and never needs to be any kind of
Python expression otherwise. To use a real deferred callable based on a
name that may not be locally present yet, use a lambda instead.References: #10564
-
[orm] [bug] Fixed issue where joined eager loading would fail to use the "nested" form
of the query when GROUP BY or DISTINCT were present if the eager joins
being added were many-to-ones, leading to additional columns in the columns
clause which would then cause errors. The check for "nested" is tuned to
be enabled for these queries even for many-to-one joined eager loaders, and
the "only do nested if it's one to many" aspect is now localized to when
the query only has LIMIT or OFFSET added.References: #11226
-
[orm] [bug] Revised the set "binary" operators for the association proxy
set()
interface to correctly raiseTypeErrorfor invalid use of the|,
&,^, and-operators, as well as the in-place mutation
versions of these methods, to match the behavior of standard Python
set()as well as SQLAlchemy ORM's "instrumented" set implementation.References: #11349
-
[orm] [bug] A significant behavioral change has been made to the behavior of the
_orm.mapped_column.defaultand
_orm.relationship.defaultparameters, as well as the
_orm.relationship.default_factoryparameter with
collection-based relationships, when used with SQLAlchemy's
orm_declarative_native_dataclassesfeature introduced in 2.0, where
the given value (assumed to be an immutable scalar value for
_orm.mapped_column.defaultand a simple collection class for
_orm.relationship.default_factory) is no longer passed to the
@dataclassAPI as a real default, instead a token that leaves the value
un-set in the object's__dict__is used, in conjunction with a
descriptor-level default. This prevents an un-set default value from
overriding a default that was actually set elsewhere, such as in
relationship / foreign key assignment patterns as well as in
_orm.Session.merge()scenarios. See the full writeup in the
migration_21_topleveldocument which includes guidance on how to
re-enable the 2.0 version of the behavior if needed.References: #12168
-
[orm] [bug] The behavior of
_orm.with_polymorphic()when used with a single
inheritance mapping has been changed such that its behavior should match as
closely as possible to that of an equivalent joined inheritance mapping.
Specifically this means that the base class specified in the
_orm.with_polymorphic()construct will be the basemost class that is
loaded, as well as all descendant classes of that basemost class.
The change includes that the descendant classes named will no longer be
exclusively indicated in "WHERE polymorphic_col IN" criteria; instead, the
whole hierarchy starting with the given basemost class will be loaded. If
the query indicates that rows should only be instances of a specific
subclass within the polymorphic hierarchy, an error is raised if an
incompatible superclass is loaded in the result since it cannot be made to
match the requested class; this behavior is the same as what joined
inheritance has done for many years. The change also allows a single result
set to include column-level results from multiple sibling classes at once
which was not previously possible with single table inheritance.References: #12395
-
[orm] [bug] Improved the behavior of standalone "operators" like
_sql.desc(),
_sql.asc(),_sql.all_(), etc. so that they consult the given
expression object for an overriding method for that operator, even if the
object is not itself aClauseElement, such as if it's an ORM attribute.
This allows custom comparators for things like_orm.composite()to
provide custom implementations of methods likedesc(),asc(), etc.References: #12769
-
[orm] [bug] ORM entities can now be involved within the SQL expressions used within
_orm.relationship.primaryjoinand
_orm.relationship.secondaryjoinparameters without the ORM
entity information being implicitly sanitized, allowing ORM-specific
features such as single-inheritance criteria in subqueries to continue
working even when used in this context. This is made possible by overall
ORM simplifications that occurred as of the 2.0 series. The changes here
also provide a performance boost (up to 20%) for certain query compilation
scenarios.References: #12843
-
[orm] [bug] The
_events.SessionEvents.do_orm_execute()event now allows direct
mutation or replacement of theORMExecuteState.parameters
dictionary or list, which will take effect when the the statement is
executed. Previously, changes to this collection were not accommodated by
the event hook. Pull request courtesy Shamil.References: #12921
-
[orm] [bug] A change in the mechanics of how Python dataclasses are applied to classes
that useMappedAsDataclassor
registry.mapped_as_dataclass()to apply__annotations__that are
as identical as is possible to the original__annotations__given,
while also adding attributes that SQLAlchemy considers to be part of
dataclass__annotations__, then restoring the previous annotations in
exactly the same format as they were, using patterns that work with
PEP 649 as closely as possible.References: #13021
-
[orm] [bug] Removed the
ORDER BYclause from queries generated by
_orm.selectin_polymorphic()and the
_orm.Mapper.polymorphic_loadparameter set to"selectin".
TheORDER BYclause appears to have been an unnecessary implementation
artifact.References: #13060
-
[orm] [bug] A significant change to the ORM mechanics involved with both
orm.with_loader_criteria()as well as single table inheritance, to
more aggressively locate WHERE criteria which should be augmented by either
the custom criteria or single-table inheritance criteria; SELECT statements
that do not include the entity within the columns clause or as an explicit
FROM, but still reference the entity within the WHERE clause, are now
covered, in particular this will allow subqueries usingEXISTS (SELECT 1)such as those rendered byRelationshipProperty.Comparator.any()
andRelationshipProperty.Comparator.has().References: #13070
-
[orm] The
_orm.noload()relationship loader option and related
lazy='noload'setting is deprecated and will be removed in a future
release. This option was originally intended for custom loader patterns
that are no longer applicable in modern SQLAlchemy.References: #11045
-
[orm] Ignore
_orm.Session.join_transaction_modein all cases when
the bind provided to the_orm.Sessionis an
_engine.Engine.
Previously if an event that executed before the session logic,
like_engine.ConnectionEvents.engine_connect(),
left the connection with an active transaction, the
_orm.Session.join_transaction_modebehavior took
place, leading to a surprising behavior.References: #11163
engine
-
[engine] [usecase] Added new execution option
_engine.Connection.execution_options.driver_column_names. This
option disables the "name normalize" step that takes place against the
DBAPIcursor.descriptionfor uppercase-default backends like Oracle,
and will cause the keys of a result set (e.g. named tuple names, dictionary
keys inRow._mapping, etc.) to be exactly what was delivered in
cursor.description. This is mostly useful for plain textual statements
using_sql.text()or_engine.Connection.exec_driver_sql().References: #10789
-
[engine] [change] An empty sequence passed to any
execute()method now
raised a deprecation warning, since such an executemany
is invalid.
Pull request courtesy of Carlos Sousa.References: #9647
-
[engine] [change] The private method
Connection._execute_compiledis removed. This method may
have been used for some special purposes however theSQLCompiler
object has lots of special state that should be set up for an execute call,
which we don't support. -
[engine] [bug] Fixed issue in "insertmanyvalues" feature where an INSERT..RETURNING
that also made use of a sentinel column to track results would fail to
filter out the additional column whenResult.unique()were used
to uniquify the result set.References: #10802
-
[engine] [bug] Adjusted URL parsing and stringification to apply url quoting to the
"database" portion of the URL. This allows a URL where the "database"
portion includes special characters such as question marks to be
accommodated.References: #11234
-
[engine] [bug] Fixed issue in the
ConnectionEvents.after_cursor_execute()method
where the SQL statement and parameter list for an "insertmanyvalues"
operation sent to the event would not be the actual SQL / parameters just
emitted on the cursor, instead being the non-batched form of the statement
that's used as a template to generate the batched statements.References: #13018
sql
-
[sql] [feature] Added the ability to create custom SQL constructs that can define new
clauses within SELECT, INSERT, UPDATE, and DELETE statements without
needing to modify the construction or compilation code of of
Select,_sql.Insert,Update, orDelete
directly. Support for testing these constructs, including caching support,
is present along with an example test suite. The use case for these
constructs is expected to be third party dialects for analytical SQL
(so-called NewSQL) or other novel styles of database that introduce new
clauses to these statements. A new example suite is included which
illustrates theQUALIFYSQL construct used by several NewSQL databases
which includes a cacheable implementation as well as a test suite.References: #12195
-
[sql] [feature] [core] The Core operator system now includes the
matmuloperator, i.e. the
@operator in Python as an optional operator.
In addition to the__matmul__and__rmatmul__operator support
this change also adds the missing__rrshift__and__rlshift__.
Pull request courtesy Aramís Segovia.References: #12479
-
[sql] [feature] Added new Core feature
_sql.from_dml_column()that may be used in
expressions inside ofUpdateBase.values()for INSERT or UPDATE; this
construct will copy whatever SQL expression is used for the given target
column in the statement to be used with additional columns. The construct
is mostly intended to be a helper with ORMhybrid_propertywithin
DML hooks.References: #12496
-
[sql] [feature] Added support for Python 3.14+ template strings (t-strings) via the new
_sql.tstring()construct. This feature makes use of Python 3.14
template strings as defined in PEP 750, allowing for ergonomic SQL
statement construction by automatically interpolating Python values and
SQLAlchemy expressions within template strings.References: #12548
-
[sql] [usecase] Added new generalized aggregate function ordering to functions via the
_functions.FunctionElement.aggregate_order_by()method, which
receives an expression and generates the appropriate embedded "ORDER BY" or
"WITHIN GROUP (ORDER BY)" phrase depending on backend database. This new
function supersedes the use of the PostgreSQL
_postgresql.aggregate_order_by()function, which remains present for
backward compatibility. To complement the new parameter, the
_functions.aggregate_strings.order_bywhich adds ORDER BY
capability to the_functions.aggregate_stringsdialect-agnostic
function which works for all included backends. Thanks much to Reuven
Starodubski with help on this patch.References: #12853
-
[sql] [usecase] Changed the query style for ORM queries emitted by
Session.get()as
well as many-to-one lazy load queries to use the default labeling style,
_sql.SelectLabelStyle.LABEL_STYLE_DISAMBIGUATE_ONLY, which normally
does not apply labels to columns in a SELECT statement. Previously, the
older style_sql.SelectLabelStyle.LABEL_STYLE_TABLENAME_PLUS_COL
that labels columns as _ was used for
Session.get()to maintain compatibility with_orm.Query.
The change allows the string representation of ORM queries to be less
verbose in all cases outside of legacy_orm.Queryuse. Pull
request courtesy Inada Naoki.References: #12932
-
[sql] [usecase] Added method
TableClause.insert_column()to complement
TableClause.append_column(), which inserts the given column at a
specific index. This can be helpful for prepending primary key columns to
tables, etc.References: #7910
-
[sql] [usecase] Added support for the pow operator (
**), with a default SQL
implementation of thePOW()function. On Oracle Database, PostgreSQL
and MSSQL it renders asPOWER(). As part of this change, the operator
routes through a new first classfuncmember_functions.pow,
which renders on Oracle Database, PostgreSQL and MSSQL asPOWER().References: #8579
-
[sql] [usecase] [orm] The
_sql.Select.filter_by(),_sql.Update.filter_by()and
_sql.Delete.filter_by()methods now search across all entities
present in the statement, rather than limiting their search to only the
last joined entity or the first FROM entity. This allows these methods
to locate attributes unambiguously across multiple joined tables,
resolving issues where changing the order of operations such as
_sql.Select.with_only_columns()would cause the method to fail.If an attribute name exists in more than one FROM clause entity, an
_exc.AmbiguousColumnErroris now raised, indicating that
_sql.Select.filter()(or_sql.Select.where()) should be used
instead with explicit table-qualified column references.References: #8601
-
[sql] [change] The
.cand.columnsattributes on theSelectand
TextualSelectconstructs, which are not instances of
FromClause, have been removed completely, in addition to the
.select()method as well as other codepaths which would implicitly
generate a subquery from aSelectwithout the need to explicitly
call theSelect.subquery()method.In the case of
.cand.columns, these attributes were never useful
in practice and have caused a great deal of confusion, hence were
deprecated back in version 1.4, and have emitted warnings since that
version. Accessing the columns that are specific to aSelect
construct is done via theSelect.selected_columnsattribute, which
was added in version 1.4 to suit the use case that users often expected
.cto accomplish. In the larger sense, implicit production of
subqueries works against SQLAlchemy's modern practice of making SQL
structure as explicit as possible.Note that this is not related to the usual
FromClause.cand
FromClause.columnsattributes, common to objects such as
TableandSubquery, which are unaffected by this
change.References: #10236
-
[sql] [change] the
NumericandFloatSQL types have been separated out
so thatFloatno longer inherits fromNumeric; instead,
they both extend from a common mixinNumericCommon. This
corrects for some architectural shortcomings where numeric and float types
are typically separate, and establishes more consistency with
Integeralso being a distinct type. The change should not have
any end-user implications except for code that may be using
isinstance()to test for theNumericdatatype; third party
dialects which rely upon specific implementation types for numeric and/or
float may also require adjustment to maintain compatibility.References: #5252
-
[sql] [change] Added new implementation for the
Select.params()method and that of
similar statements, via a new statement-only
ExecutableStatement.params()method which works more efficiently and
correctly than the previous implementations available from
ClauseElement, by associating the given parameter dictionary with
the statement overall rather than cloning the statement and rewriting its
bound parameters. The_sql.ClauseElement.params()and
_sql.ClauseElement.unique_params()methods, when called on an object
that does not implementExecutableStatement, will continue to
work the old way of cloning the object, and will emit a deprecation
warning. This issue both resolves the architectural / performance
concerns of #7066 and also provides correct ORM compatibility for
functions like_orm.aliased(), reported by #12915. -
[sql] [bug] The
Doubletype is now used when a Python float value is detected
as a literal value to be sent as a bound parameter, rather than the
Floattype.Doublehas the same implementation as
Float, but when rendered in a CAST, producesDOUBLEor
DOUBLE PRECISIONrather thanFLOAT. The former better matches
Python'sfloatdatatype which uses 8-byte double-precision storage.
Third party dialects which don't support theDoubletype directly
may need adjustment so that they render an appropriate keyword (e.g.
FLOAT) when theDoubledatatype is encountered.References: #10300
-
[sql] [bug] Fixed issue in name normalization (e.g. "uppercase" backends like Oracle)
where using aTextualSelectwould not properly maintain as
uppercase column names that were quoted as uppercase, even though
theTextualSelectincludes aColumnthat explicitly
holds this uppercase name.References: #10788
-
[sql] [bug] Enhanced the caching structure of the
_expression.over.rows
and_expression.over.rangeso that different numerical
values for the rows /
range fields are cached on the same cache key, to the extent that the
underlying SQL does not actually change (i.e. "unbounded", "current row",
negative/positive status will still change the cache key). This prevents
the use of many different numerical range/rows value for a query that is
otherwise identical from filling up the SQL cache.Note that the semi-private compiler method
_format_frame_clause()
is removed by this fix, replaced with a new method
visit_frame_clause(). Third party dialects which may have referred
to this method will need to change the name and revise the approach to
rendering the correct SQL for that dialect.References: #11515
-
[sql] [bug] Updated the
_sql.over()clause to allow non integer values in
_sql.over.range_clause. Previously, only integer values
were allowed and any other values would lead to a failure.
To specify a non-integer value, use the new_sql.FrameClause
construct along with the new_sql.FrameClauseTypeenum to specify
the frame boundaries. For example:from sqlalchemy import FrameClause, FrameClauseType
select(
func.sum(table.c.value).over(
range_=FrameClause(
3.14,
2.71,
FrameClauseType.PRECEDING,
FrameClauseType.FOLLOWING,
)
)
)References: #12596 -
[sql] [bug] Added a new concept of "operator classes" to the SQL operators supported by
SQLAlchemy, represented within the enumOperatorClass. The
purpose of this structure is to provide an extra layer of validation when a
particular kind of SQL operation is used with a particular datatype, to
catch early the use of an operator that does not have any relevance to the
datatype in use; a simple example is an integer or numeric column used with
a "string match" operator.References: #12736
-
[sql] [bug] Fixed an issue in
_sql.Select.join_from()where the join condition
between the left and right tables specified in the method call could be
incorrectly determined based on an intermediate table already present in
the FROM clause, rather than matching the foreign keys between the
immediate left and right arguments. The join condition is now determined by
matching primary keys between the two tables explicitly passed to
_sql.Select.join_from(), ensuring consistent and predictable join
behavior regardless of the order of join operations or other tables present
in the query. The fix is applied to both the Core and ORM implementations
of_sql.Select.join_from().References: #12931
-
[sql] [bug] Fixed issue where anonymous label generation for
CTEconstructs
could produce name collisions when Python's garbage collector reused memory
addresses during complex query compilation. The anonymous name generation
forCTEand other aliased constructs likeAlias,
Subqueryand others now useos.urandom()to generate unique
identifiers instead of relying on objectid(), ensuring uniqueness even
in cases of aggressive garbage collection and memory reuse.References: #12990
-
[sql] Removed the automatic coercion of executable objects, such as
_orm.Query, when passed into_orm.Session.execute().
This usage raised a deprecation warning since the 1.4 series.References: #12218
schema
-
[schema] [feature] Added support for the SQL
CREATE VIEWstatement via the new
CreateViewDDL class. The new class allows creating database
views from SELECT statements, with support for options such as
TEMPORARY,IF NOT EXISTS, andMATERIALIZEDwhere supported by
the target database. Views defined withCreateViewintegrate with
MetaDatafor automated DDL generation and provide a
Tableobject for querying.References: #181
-
[schema] [feature] Added support for the SQL
CREATE TABLE ... AS SELECTconstruct via the
new_schema.CreateTableAsDDL construct and the
_sql.Select.into()method. The new construct allows creating a
table directly from the results of a SELECT statement, with support for
options such asTEMPORARYandIF NOT EXISTSwhere supported by the
target database. Tables defined with_schema.CreateTableAs
integrate withMetaDatafor automated DDL generation and provide
aTableobject for querying. Pull request courtesy Greg Jarzab.References: #4950
-
[schema] [usecase] The the parameter
_schema.DropConstraint.isolate_from_table
was deprecated since it has no effect on the drop table behavior.
Its default values was also changed toFalse.References: #13006
-
[schema] [bug] The
FloatandNumerictypes are no longer automatically
considered as auto-incrementing columns when the
_schema.Column.autoincrementparameter is left at its default
of"auto"on a_schema.Columnthat is part of the primary key.
When the parameter is set toTrue, aNumerictype will be
accepted as an auto-incrementing datatype for primary key columns, but only
if its scale is explicitly given as zero; otherwise, an error is raised.
This is a change from 2.0 where all numeric types including floats were
automatically considered as "autoincrement" for primary key columns.References: #11811
-
[schema] Deprecate Oracle only parameters
_schema.Sequence.order,
_schema.Identity.orderand_schema.Identity.on_null.
They should be configured using the dialect kwargsoracle_orderand
oracle_on_null.References: #10247
typing
-
[typing] [feature] The
Rowobject now no longer makes use of an intermediary
Tuplein order to represent its individual element types; instead,
the individual element types are present directly, via new PEP 646
integration, now available in more recent versions of Mypy. Mypy
1.7 or greater is now required for statements, results and rows
to be correctly typed. Pull request courtesy Yurii Karabas.References: #10635
-
[typing] The default implementation of
_sql.TypeEngine.python_typenow
returnsobjectinstead ofNotImplementedError, since that's the
base for all types in Python3.
Thepython_typeof_sql.JSONno longer returnsdict,
but instead fallbacks to the generic implementation.References: #10646
-
[typing] [orm] Removed the deprecated mypy plugin.
The plugin was non-functional with newer version of mypy and it's no
longer needed with modern SQLAlchemy declarative style.References: #12293
-
[typing] [orm] Deprecated the
declarative_mixindecorator since it was used only
by the now removed mypy plugin.References: #12346
asyncio
-
[asyncio] [feature] The "emulated" exception hierarchies for the asyncio
drivers such as asyncpg, aiomysql, aioodbc, etc. have been standardized
on a common baseEmulatedDBAPIException, which is now what's
available from theStatementException.origattribute on a
SQLAlchemyDBAPIErrorobject. WithinEmulatedDBAPIException
and the subclasses in its hierarchy, the original driver-level exception is
also now available via theEmulatedDBAPIException.origattribute,
and is also available fromDBAPIErrordirectly using the
DBAPIError.driver_exceptionattribute.References: #8047
-
[asyncio] [change] Added an initialize step to the import of
sqlalchemy.ext.asyncioso thatgreenletwill
be imported only when the asyncio extension is first imported.
Alternatively, thegreenletlibrary is still imported lazily on
first use to support use case that don't make direct use of the
SQLAlchemy asyncio extension.References: #10296
-
[asyncio] [change] Adapted all asyncio dialects, including aiosqlite, aiomysql, asyncmy,
psycopg, asyncpg to use the generic asyncio connection adapter first added
in #6521 for the aioodbc DBAPI, allowing these dialects to take
advantage of a common framework.References: #10415
-
[asyncio] [change] Removed the compatibility
async_fallbackmode for async dialects,
since it's no longer used by SQLAlchemy tests.
Also removed the internal functionawait_fallback()and renamed
the internal functionawait_only()toawait_().
No change is expected to user code. -
[asyncio] [bug] Refactored all asyncio dialects so that exceptions which occur on failed
connection attempts are appropriately wrapped with SQLAlchemy exception
objects, allowing for consistent error handling.References: #11956
postgresql
-
[postgresql] [feature] Adds a new
strsubclass_postgresql.BitStringrepresenting
PostgreSQL bitstrings in python, that includes
functionality for converting to and fromintandbytes, in
addition to implementing utility methods and operators for dealing with bits.This new class is returned automatically by the
postgresql.BITtype.References: #10556
-
[postgresql] [feature] Support for storage parameters in
CREATE TABLEusing theWITH
clause has been added. Thepostgresql_withdialect option of
_schema.Tableaccepts a mapping of key/value options.References: #10909
-
[postgresql] [feature] Added syntax extension
_postgresql.distinct_on()to buildDISTINCT ONclauses. The old api, that passed columns to
_sql.Select.distinct(), is now deprecated.References: #12342
-
[postgresql] [feature] Support for
VIRTUALcomputed columns on PostgreSQL 18 and later has
been added. The default behavior whenComputed.persistedis
not specified has been changed to align with PostgreSQL 18's default of
VIRTUAL. WhenComputed.persistedis not specified, no
keyword is rendered on PostgreSQL 18 and later; on older versions a
warning is emitted andSTOREDis used as the default. To explicitly
requestSTOREDbehavior on all PostgreSQL versions, specify
persisted=True.References: #12866
-
[postgresql] [feature] [sql] Added support for monotonic server-side functions such as PostgreSQL 18's
uuidv7()to work with theengine_insertmanyvaluesfeature.
By passingmonotonic=Trueto anyFunction, the function can
be used as a sentinel for tracking row order in batched INSERT operations
with RETURNING, allowing the ORM and Core to efficiently batch INSERT
statements while maintaining deterministic row ordering.References: #13014
-
[postgresql] [feature] Added additional emulated error classes for the subclasses of
asyncpg.exception.IntegrityErrorincludingRestrictViolationError,
NotNullViolationError,ForeignKeyViolationError,
UniqueViolationErrorCheckViolationError,
ExclusionViolationError. These exceptions are not directly thrown by
SQLAlchemy's asyncio emulation, however are available from the
newly addedDBAPIError.driver_exceptionattribute when a
IntegrityErroris caught.References: #8047
-
[postgresql] [usecase] Added new parameter
Enum.create_typeto the Core
Enumclass. This parameter is automatically passed to the
corresponding_postgresql.ENUMnative type during DDL operations,
allowing control over whether the PostgreSQL ENUM type is implicitly
created or dropped within DDL operations that are otherwise targeting
tables only. This provides control over the
_postgresql.ENUM.create_typebehavior without requiring
explicit creation of a_postgresql.ENUMobject.References: #10604
-
[postgresql] [usecase] The PostgreSQL dialect now support reflection of table options, including
the storage parameters, table access method and table spaces. These options
are automatically reflected when autoloading a table, and are also
available via the_engine.Inspector.get_table_options()and
_engine.Inspector.get_multi_table_optionsmethod()methods.References: #10909
-
[postgresql] [usecase] Added support for PostgreSQL 14+ HSTORE subscripting syntax. When connected
to PostgreSQL 14 or later, HSTORE columns now automatically use the native
subscript notationhstore_col['key']instead of the arrow operator
hstore_col -> 'key'for both read and write operations. This provides
better compatibility with PostgreSQL's native HSTORE subscripting feature
while maintaining backward compatibility with older PostgreSQL versions.Indexes in existing PostgreSQL databases which were indexed
on an HSTORE subscript expression would need to be updated in order to
match the new SQL syntax.References: #12948
-
[postgresql] [usecase] The default DBAPI driver for the PostgreSQL dialect has been changed to
psycopg(psycopg version 3) instead ofpsycopg2. Thepsycopg2
driver remains fully supported and can be explicitly specified in the
connection URL usingpostgresql+psycopg2://.The
psycopg(version 3) driver includes improvements overpsycopg2
including better performance when using C extensions and native support
for async operations.References: #13010
-
[postgresql] [change] The
_types.ARRAY.Comparator.any()and
_types.ARRAY.Comparator.all()methods for the_types.ARRAY
type are now deprecated for removal; these two methods along with
_postgresql.Any()and_postgresql.All()have been legacy for
some time as they are superseded by the_sql.any_()and
_sql.all_()functions, which feature more intuitive use.References: #10821
-
[postgresql] [change] Named types such as
_postgresql.ENUMand
_postgresql.DOMAIN(as well as the dialect-agnostic
_types.Enumversion) are now more strongly associated with the
_schema.MetaDataat the top of the table hierarchy and are
de-associated with any particular_schema.Tablethey may be a part
of. This better represents how PostgreSQL named types exist independently
of any particular table, and that they may be used across many tables
simultaneously. The change impacts the behavior of the "default schema"
for a named type, as well as the CREATE/DROP behavior in relationship to
theMetaDataandTableconstruct. The change also
includes a newCheckFirstenumeration which allows fine grained
control over "check" queries during DDL operations, as well as that the
_types.SchemaType.inherit_schemaparameter is deprecated and
will emit a deprecation warning when used. See the migration notes for
full details. -
[postgresql] [bug] A
CompileErroris raised if attempting to create a PostgreSQL
_postgresql.ENUMor_postgresql.DOMAINdatatype using a
name that matches a known pg_catalog datatype name, and a default schema is
not specified. These types must be explicit within a schema in order to
be differentiated from the built-in pg_catalog type. The "public" or
otherwise default schema is not chosen by default here since the type can
only be reflected back using the explicit schema name as well (it is
otherwise not visible due to the pg_catalog name). Pull request courtesy
Kapil Dagur.References: #12761
mysql
-
[mysql] [feature] Added new construct
_mysql.limit()which can be applied to any
_sql.update()or_sql.delete()to provide the LIMIT keyword to
UPDATE and DELETE. This new construct supersedes the use of the
"mysql_limit" dialect keyword argument. -
[mysql] [mariadb] [reflection] Updated the reflection logic for indexes in the MariaDB and MySQL
dialect to avoid setting the undocumentedtypekey in the
_engine.ReflectedIndexdicts returned by
_engine.Inspector.get_indexesmethod.References: #12240
mariadb
-
[mariadb] [usecase] Modified the MariaDB dialect so that when using the
_sqltypes.Uuid
datatype with MariaDB >= 10.7, leaving the
_sqltypes.Uuid.native_uuidparameter at its default of True,
the nativeUUIDdatatype will be rendered in DDL and used for database
communication, rather thanCHAR(32)(the non-native UUID type) as was
the case previously. This is a behavioral change since 2.0, where the
generic_sqltypes.Uuiddatatype deliveredCHAR(32)for all
MySQL and MariaDB variants. Support for all major DBAPIs is implemented
including support for less common "insertmanyvalues" scenarios where UUID
values are generated in different ways for primary keys. Thanks much to
Volodymyr Kochetkov for delivering the PR.References: #10339
-
[mariadb] [bug] Fixes to the MySQL/MariaDB dialect so that mariadb-specific features such
as themariadb.INET4andmariadb.INET6datatype may be
used with anEnginethat uses amysql://URL, if the backend
database is actually a mariadb database. Previously, support for MariaDB
features whenmysql://URLs were used instead ofmariadb://URLs
was ad-hoc; with this issue resolution, the full set of schema / compiler /
type features are now available regardless of how the URL was presented.References: #13076
sqlite
-
[sqlite] [bug] Improved the behavior of JSON accessors
JSON.Comparator.as_string(),
JSON.Comparator.as_boolean(),JSON.Comparator.as_float(),
JSON.Comparator.as_integer()to use CAST in a similar way that
the PostgreSQL, MySQL and SQL Server dialects do to help enforce the
expected Python type is returned.References: #11074
mssql
-
[mssql] [bug] The
JSON.Comparator.as_boolean()method when used on a JSON value on
SQL Server will now force a cast to occur for values that are not simple
true/false JSON literals, forcing SQL Server to attempt to interpret
the given value as a 1/0 BIT, or raise an error if not possible. Previously
the expression would return NULL.References: #11074
-
[mssql] [bug] Fix mssql+pyodbc issue where valid plus signs in an already-unquoted
odbc_connect=(raw DBAPI) connection string are replaced with spaces.The pyodbc connector would unconditionally pass the odbc_connect value
to unquote_plus(), even if it was not required. So, if the (unquoted)
odbc_connect value containedPWD=pass+wordthat would get changed to
PWD=pass word, and the login would fail. One workaround was to quote
just the plus sign —PWD=pass%2Bword— which would then get unquoted
toPWD=pass+word.References: #11250
oracle
-
[oracle] [feature] Added support for native BOOLEAN support in Oracle Database 23c and above.
The Oracle dialect now rendersBOOLEANautomatically when
Booleanis used in DDL, and also now supports direct use of the
BOOLEANdatatype, when 23c and above is in use. For Oracle
versions prior to 23c, boolean values continue to be emulated using
SMALLINT as before. Special case handling is also present to ensure a
SMALLINT that's interpreted with theBooleandatatype on Oracle
Database 23c and above continues to return bool values. Pull request
courtesy Yeongbae Jeon.References: #11633
-
[oracle] [usecase] The default DBAPI driver for the Oracle Database dialect has been changed
tooracledbinstead ofcx_oracle. Thecx_oracledriver remains
fully supported and can be explicitly specified in the connection URL
usingoracle+cx_oracle://.The
oracledbdriver is a modernized version ofcx_oraclewith
better performance characteristics and ongoing active development from
Oracle.References: #13010
tests
- [tests] [change] The top-level test runner has been changed to use
nox, adding a
noxfile.pyas well as some included modules. Thetox.inifile
remains in place so thattoxruns will continue to function in the near
term, however it will be eventually removed and improvements and
maintenance going forward will be only towardsnoxfile.py.
misc
-
[misc] [changed] Removed multiple api that were deprecated in the 1.3 series and earlier.
The list of removed features includes:- 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()`.References: #12441