SQLAlchemy 2.0.0, the first production release of SQLAlchemy 2.0, is
now available.

With this release, the default version of SQLAlchemy that will install
when one runs pip install sqlalchemy will be version 2.0. Note that
version 2.0 has significant API changes compared to the 1.4 series, so
applications that run on the 1.x series of SQLAlchemy which have not gone
through the migration process
should seek to ensure requirements
are set to maintain the desired major SQLAlchemy release series in place.

The history of the SQLAlchemy 2.0 series starts over four years ago on August
8, 2018, with some short ideas for how SQLAlchemy’s notion of Core and ORM
querying might be unified. The first plan for a real “SQLAlchemy 2.0” concept
formed in November of that year, focused on the two areas of drastically
simplifying Core execution and transactional APIs as well as seeking to unify
querying across Core and ORM.

The changes to foundational concepts were dramatic enough that SQLAlchemy 2.0
was separated into two major phases. The first phase was the SQLAlchemy 1.4
series, which provided an entirely new unified Core / ORM SQL querying system,
all while building on top of a new universal statement caching architecture.
This phase provided the complete implementation for SQLAlchemy 2.0’s SQL
construction approach (minus pep-484 typing support), while maintaining the
legacy Query API completely. Along with this release, a comprehensive
migration path
inspired by lessons learned from the Python 2->3 migration process was
provided, which describes how to port applications so that they can continue
to run in SQLAlchemy 1.4 while being entirely forwards compatible with
SQLAlchemy 2.0.

The second phase is the SQLAlchemy 2.0 series, which removes the majority
of deprecated elements, relegating the remaining ones (primarily Query) to
long term “legacy” status, fully moves to Python 3 only, and at the same time
adds many new features that build on top of the new architecture, taking full
advantage of Python 3 features (including dataclasses, enums, inline
annotations) as well as the new unified query architecture.

The key advantage to this approach is that the most significant and by far the
riskiest architectural changes, that of the rewrite of Core/ORM querying on top
of a new caching layer, have already been in production use with SQLAlchemy 1.4
for nearly two years. So while SQLAlchemy 2.0 will certainly have a lot of new
issues once it is used by the full developer audience, they should not be of
the “new cracks in the foundational approach” variety, as the architectural
foundations are already in widespread use. We expect that the vast majority of
issues will be related to the new typing system as well as issues of existing
applications adjusting to use the new APIs.

SQLAlchemy 2.0 is a big enough release that it has two
migration guides; the
Major Migration Guide
documents how to reach API compatibility for an
application to be able to run in SQLAlchemy 1.4 or 2.0 equally; the
What’s New in SQLAlchemy 2.0?
guide then provides for all the new features and APIs that are available once
an application is running on SQLAlchemy 2.0.

With that introduction in mind, here’s a top level bullet list of what’s
totally new in SQLAlchemy 2.0:

  • All new plugin-free pep-484 compatible ORM syntaxes – The ORM
    Declarative style of mapper configuration now has an all new look, borrowing
    heavily from systems such as Python dataclasses and SQLModel to
    provide for Annotation-driven ORM declarations,
    using runtime interpretation of pep-484 annotations to produce mapped classes
    that are fully typed and compatible with any type checker or IDE.
  • pep-484 support for both new-style and legacy ORM queries – using Annotated
    Declarative models, the SQL constructs one creates, such as a select()
    object or a legacy Query construct, are themselves typed as to the
    kind of columns returned within a row, and this typing
    carries forth all the way to the Python values extracted from the result
    object returned after executing the query.
    While Python typing still has limitations for doing this kind of thing,
    the new typing support draws from some techniques used by SQLModel so that
    it works without additional annotations for the vast majority
    of queries generated from ORM model classes, with varying
    degrees of support for result-set-typing of Core-oriented or hybrid
    Core/ORM queries as well. Some examples are at
    SQL Expression Typing – Examples.
  • Declarative fully integrated with Python Dataclasses – SQLAlchemy 2.0 introduces
    support for an Annotated Declarative mapped class to be generated as a
    Python dataclass directly; this will provide an ORM
    mapped class declared like any other that features auto-generated dataclasses
    methods such as __init__(), __repr__(), __eq__(), and all the
    rest. This new approach is vastly improved over the interim “hybrid”
    approaches introduced in SQLAlchemy 1.4.
  • An all-new, fully ORM-integrated approach to bulk INSERT that is typically
    an order of magnitude faster on most backends
    – Most of SQLAlchemy’s
    supported databases and drivers have now added or improved their support for
    INSERT RETURNING syntax, and SQLAlchemy 2.0 has taken advantage of this
    by supporting and improving RETURNING support for all of its backends, with
    the sole exception of MySQL (MySQL only; MariaDB supports RETURNING).
    Part of this improvement is the
    ability to INSERT thousands of rows in one batched statement that also
    returns a complete result set of server generated values needed by the ORM,
    which was never possible before with the sole exception of an interim
    implementation that relied upon the psycopg2 driver. In 2.0,
    the work has been done to optimize INSERT for all backends so that
    thousands of ORM objects with or without primary keys can be INSERTed with
    one database round trip
    ,
    and this capability is fully integrated into the ORM for all INSERT
    operations, both “regular” ORM unit of work operations as well as for “bulk”
    approaches, which are also
    newly revised
    in version 2.0.
  • An all-new bulk-optimized schema reflection architecture – operations that
    reflect full schemas as once such as MetaData.reflect(engine) and newly
    added schema-wide operations with the inspect(engine) construct now build on top of a
    foundation that assumes operations that work across hundreds or thousands of tables at once,
    rather than table-at-a-time. Dialects can “opt in” to the new architecture
    by providing for new reflection queries that work across many tables at once,
    allowing for very large schemas to be fully reflected much more efficently.
    The new architecture is enabled for the
    PostgreSQL and Oracle backends,
    which were the two backends that had the biggest performance issues for
    large schemas, where it grants a 250% improvement for PostgreSQL and
    a 900% improvement for Oracle. The SQL Server dialect is the next
    target for the new architecture. Any third party dialect can
    opt in to the new “many tables at once” system or remain on the previous
    “table at a time” approach that remains fully compatible without any changes.
  • Native extensions ported to Cython – SQLAlchemy’s C extensions have been
    replaced with an updated approach using Cython. The Cython version of the
    extensions in most cases benches as fast as, and sometimes faster than,
    the previous C extensions, and allows SQLAlchemy to provide new native
    extensions in more areas of the library much more easily without risk
    of memory or stability issues.

SQLAlchemy 2.0 includes many more features and with new architectures and
a lot of older baggage being shed, hopes to have plenty of room for the future.

Links to the detailed changelog for 2.0.0 is at Changelog
note that the 2.0 series changelog spans across four beta and three release
candidate releases.

SQLAlchemy 2.0.0 is available on the Download Page.

Read More