2.1.0b3
- Add selectinload.chunksize parameter to selectinload() allowing users to configure the number of primary keys sent per IN clause when loading relationships
- Honor populate_existing execution option when passed in Session.get.execution_options dict, with Session.get.populate_existing parameter taking precedence if specified
- Update _orm.ORMExecuteState.user_defined_options to include options added to the statement before calling Select.with_only_columns() or _orm.Query.with_entities()
- Optimize _orm.selectinload() to skip the .unique() call on inner result sets when no nested _orm.joinedload() on a collection is present
- Make Session level _orm.Session.execution_options take effect for Core level SQL emitted by unit of work operations
- Process ORM result rows as plain tuples rather than constructing Row objects for improved performance
- Improve performance of _orm.selectinload() and _orm.subqueryload() result handling by selecting primary key columns directly and converting rows to plain tuples
- Enable omit_join optimization for many-to-many non-self-referential relationships in selectinload() loader strategy
- Fix issue where declarative class registry would not consider class-level MetaData objects set on abstract mixin classes when resolving string-based table references in relationship() configurations
- Fix issue where _engine.Result.unique() filter was not properly validated against _engine.Result.yield_per() method
- Emit warning when a Declarative attribute name is named metadata or registry
2.1.0b3
Released: June 27, 2026
orm
-
[orm] [feature] Added
selectinload.chunksizeparameter toselectinload()allowing users to configure the number of primary keys sent per IN clause when loading relationships. Pull request courtesy bekapono.References: #11450
-
[orm] [usecase] The
populate_existingexecution option is now honored when passed in theSession.get.execution_optionsdict by the methodSession.get()and analogous in other session kinds. The currentSession.get.populate_existingparameter will takes precedence if specified, overriding the value of the execution options.References: #10610
-
[orm] [usecase] Updated the attribute
_orm.ORMExecuteState.user_defined_optionsto include options that were added to the statement before callingSelect.with_only_columns()or_orm.Query.with_entities().References: #13309
-
[orm] [usecase] [performance] Optimized
_orm.selectinload()to skip the.unique()call on inner result sets when no nested_orm.joinedload()on a collection is present. The uniquing pass is only required when a joined eager load inflates rows due to a one-to-many or many-to-many JOIN; in the common case of a leaf selectin load, rows are already unique by construction and the per-row hashing overhead can be avoided. As a side effect,yield_perset in ado_orm_executeevent for a_orm.selectinload()relationship load no longer raisesInvalidRequestErrorwhen no nested collection joinedload is in effect, since.unique()is no longer called in that path. Pull request courtesy Oliver Parker.References: #13339
-
[orm] [usecase] Session level
_orm.Session.execution_optionsnow take effect for Core level SQL emitted by unit of work operations, in addition to their existing use within ORM statement executions. This is to provide for Core options such as_engine.Connection.execution_options.schema_translate_mapto be applicable to aSessionoverall.References: #13346
-
[orm] [performance] ORM result row fetching now processes rows as plain tuples rather than constructing
Rowobjects, as ORM loaders use position-based access and do not require theRowinterface.Rowconstruction is still used when engine-level debug logging is enabled so that individual rows can be logged. Benchmarks show a 3-16% improvement in ORM entity load times depending on query shape. Pull request courtesy Oliver Parker.References: #13363
-
[orm] [performance] Improved performance of
_orm.selectinload()and_orm.subqueryload()result handling:- in selectinloader, the primary key columns used to correlate related rows are now selected directly rather than being wrapped in a `Bundle`, and are read from positional slices of each result row. This removes the per-row `Row` construction that the `Bundle` introduced, including for the common single-column primary key case. - removed use of `groupby()` + `lambda` against `Row` objects in subqueryloader; rows are converted to plain tuples and the result lists are built via `append()`. - many-to-one selectinload reads foreign key values directly from the parent instance dictionary when present, falling back to attribute-level access only for expired or deferred attributes.Pull request courtesy Oliver Parker.
References: #13363
-
[orm] [performance] The
selectinload()loader strategy now selects theomit_joinoptimization for many-to-many non-self-referential relationships, reducing the number of joins in the secondary SELECT by selecting from the secondary table directly rather than joining back to the parent entity.omit_joinis enabled automatically when the join condition determines that the secondary table's foreign keys fully cover the parent's primary key. As always,omit_joincan be disabled by settingrelationship.omit_jointoFalse. Pull request courtesy bekapono.References: #5987
-
[orm] [bug] Fixed issue where the declarative class registry would not consider class-level
MetaDataobjects set on abstract mixin classes when resolving string-based table references inrelationship()configurations. The registry now uses the same metadata resolution logic as table creation, first checking for a class-specificmetadataattribute before falling back toregistry.metadata.References: #13291
-
[orm] [bug] Fixed issue where the
_engine.Result.unique()filter was not properly validated against the_engine.Result.yield_per()method when both were called as methods on the result object, such asresult.unique().yield_per(N)orresult.yield_per(N).unique(). The uniquing filter was previously only checked whenyield_perwas set via_engine.Connection.execution_options.yield_per. Since these two features are fundamentally incompatible for ORM results, anInvalidRequestErroris now raised in all cases.References: #13293
-
[orm] [bug] A warning is now emitted when a Declarative attribute name is named
metadataorregistry. Previously, no warning was emitted forregistry, and using the namemetadatawould raise an InvalidRequestError. Since these names can be used for attributes that are mapped as backrefs or using imperative mappings, usage under Declarative has been relaxed formetadatabut also warns for both names as they may have unintended interactions with the Declarative reserved names.References: #13333
-
[orm] [bug] Fixed issue where the declarative class resolver would not consider the
MetaData.schemadefault schema when resolving a string table name for therelationship.secondaryparameter as well as within string-basedrelationship.primaryjoinandrelationship.secondaryjoinexpressions. The resolution now matches the behavior ofForeignKey, where an unqualified table name is implicitly resolved under the default schema. A deprecation warning is emitted when an unqualified name resolves to a :data:.BLANK_SCHEMAtable in aMetaDatathat has a default schema set, as this implicit resolution will change in a future version.Unknown interpreted text role "data".
References: #8068
engine
-
[engine] [bug] Expanded try/except error handling to encompass the
_events.ConnectionEvents.before_cursor_execute()and_events.ConnectionEvents.after_cursor_execute()event hooks, so that exceptions raised within these hooks, includingBaseExceptionsubclasses such asasyncio.CancelledError, are properly handled via the error handling path used for DBAPI errors. This ensures proper connection invalidation and pool notification when exit-type exceptions are raised in event hooks. As part of this change, DBAPI errors raised from within these event hooks will now be wrapped as SQLAlchemy exceptions.References: #13381
-
[engine] [reflection] Removed the legacy
include_columnskey from the dictionary returned by the index reflection methods of some dialects. This information is now part of thedialect_optionsdictionary under the key{dialect_name}_include, such aspostgresql_includeormssql_include.References: #13350
sql
-
[sql] [usecase] Added
_sql.Delete.using(), allowing explicit FROM expressions such as joins to be rendered in backend-specific multiple-table DELETE forms including MySQL/MariaDBDELETE .. USING. Pull request courtesy cjc0013.References: #8130
-
[sql] [bug] Fixed issue where negation of comparison expressions involving
func.any(),func.all(), andfunc.some()SQL functions would incorrectly flip the comparison operator (e.g.=to!=) rather than wrapping the expression withNOT. These functions are now registered as collection aggregate functions that prevent operator flipping on negation, consistent with the behavior of the standalone_expression.any_()and_expression.all_()constructs.References: #13343
postgresql
-
[postgresql] [usecase] Changed the default backslash escape value in the PostgreSQL dialect to
Falseto align it with the default value ofstandard_conforming_strings=on. This change should not affect most users since the value is set at driver initialization on first connect.References: #13268
mysql
-
[mysql] [bug] Improved the regular expression used to parse index
COMMENTclauses in MySQLSHOW CREATE TABLEreflection to use an unambiguous single-quoted-string pattern; the previous pattern was theoretically subject to backtracking on malformed input, though such input is not producible by MySQL itself. Fix courtesy of Javid Khan.References: #13393
sqlite
-
[sqlite] [feature] Added
_sqlite.JSONBtype for SQLite's binary JSON storage format, available as of SQLite version 3.45.0. Values are stored via thejsonb()SQL function and retrieved viajson(), while the Python-side behavior remains identical to_sqlite.JSON. Pull request courtesy Shamil Abdulaev.References: #13260
mssql
-
[mssql] [performance] [reflection] Implemented native multi-table reflection methods for the SQL Server dialect, providing
MSDialect.get_multi_columns(),MSDialect.get_multi_pk_constraint(),MSDialect.get_multi_foreign_keys(),MSDialect.get_multi_indexes()andMSDialect.get_multi_table_comment(). Previously the SQL Server dialect relied on the default dialect default implementation which calls the per-table methods in a loop; the new implementations issue a single bulk query per object type against thesys.*catalog views, avoiding the per-table round trips. The single-table reflection methods are now thin wrappers over the multi-table ones, matching the pattern used by the PostgreSQL and Oracle dialects. Pull request courtesy Gaurav Sharma.References: #8430