]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
migrate labels to new tutorial
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 4 Jun 2022 19:53:34 +0000 (15:53 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 7 Jun 2022 16:25:19 +0000 (12:25 -0400)
other org changes and some sections from old tutorial
ported to new tutorial.

Change-Id: Ic0fba60ec82fff481890887beef9ed0fa271875a

48 files changed:
doc/build/changelog/changelog_10.rst
doc/build/changelog/migration_11.rst
doc/build/changelog/migration_12.rst
doc/build/changelog/migration_20.rst
doc/build/changelog/unreleased_20/7998.rst
doc/build/conf.py
doc/build/core/engines.rst
doc/build/core/index.rst
doc/build/core/metadata.rst
doc/build/core/pooling.rst
doc/build/dialects/mysql.rst
doc/build/dialects/postgresql.rst
doc/build/errors.rst
doc/build/faq/connections.rst
doc/build/faq/sessions.rst
doc/build/glossary.rst
doc/build/orm/extensions/associationproxy.rst
doc/build/orm/extensions/baked.rst
doc/build/orm/extensions/hybrid.rst
doc/build/orm/extensions/mypy.rst
doc/build/orm/index.rst
doc/build/orm/internals.rst
doc/build/orm/persistence_techniques.rst
doc/build/orm/queryguide.rst
doc/build/orm/relationships.rst
doc/build/orm/self_referential.rst
doc/build/orm/session_basics.rst
doc/build/tutorial/data_insert.rst
doc/build/tutorial/data_select.rst
doc/build/tutorial/dbapi_transactions.rst
doc/build/tutorial/orm_related_objects.rst
lib/sqlalchemy/dialects/mysql/base.py
lib/sqlalchemy/dialects/mysql/dml.py
lib/sqlalchemy/dialects/postgresql/psycopg2.py
lib/sqlalchemy/engine/cursor.py
lib/sqlalchemy/engine/row.py
lib/sqlalchemy/engine/url.py
lib/sqlalchemy/ext/asyncio/result.py
lib/sqlalchemy/orm/_orm_constructors.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/orm/relationships.py
lib/sqlalchemy/sql/_dml_constructors.py
lib/sqlalchemy/sql/_elements_constructors.py
lib/sqlalchemy/sql/_selectable_constructors.py
lib/sqlalchemy/sql/dml.py
lib/sqlalchemy/sql/elements.py
lib/sqlalchemy/sql/functions.py
lib/sqlalchemy/sql/selectable.py

index 95a5da35b9733bf5189fb3a1d095648c6050a149..1db674078fe3d050c7912f235ae954cce485d250 100644 (file)
 
         .. seealso::
 
-            :ref:`updates_order_parameters`
+            :ref:`tutorial_parameter_ordered_updates`
 
     .. change::
         :tags: bug, orm
index a2c88ae11d283122cc7bd60d7ea04a246be316c4..5c1b842b61e85a3b151e34c9307e8d4a399883c7 100644 (file)
@@ -1213,7 +1213,7 @@ RANGE and ROWS expressions for window functions::
 
 .. seealso::
 
-    :ref:`window_functions`
+    :ref:`tutorial_window_functions`
 
 :ticket:`3049`
 
@@ -1242,7 +1242,7 @@ selectable, e.g. lateral correlation::
 
 .. seealso::
 
-    :ref:`lateral_selects`
+    :ref:`tutorial_lateral_correlation`
 
     :class:`_expression.Lateral`
 
@@ -1478,7 +1478,7 @@ this behavioral change for applications using it are at :ref:`behavior_change_35
 
 .. seealso::
 
-    :ref:`sqlexpression_text_columns` - in the Core tutorial
+  :ref:`tutorial_select_arbitrary_text`
 
     :ref:`behavior_change_3501` - backwards compatibility remarks
 
@@ -2606,9 +2606,6 @@ Support for PyGreSQL
 
 The `PyGreSQL <https://pypi.org/project/PyGreSQL>`_ DBAPI is now supported.
 
-.. seealso::
-
-    :ref:`dialect-postgresql-pygresql`
 
 The "postgres" module is removed
 --------------------------------
index bc1d0739e9d63e2e363c64523e0a735c78679b7c..7073660f788320fd80e231d212aa8dc645995593 100644 (file)
@@ -905,7 +905,7 @@ would render as::
 
 .. seealso::
 
-    :ref:`multi_table_deletes`
+    :ref:`tutorial_multi_table_deletes`
 
 :ticket:`959`
 
index 6e9aae04a587a46dda9a71e0de4dff84a4b39317..05ef1dcc717670cd896285f3bc70ebcbf3e3c912 100644 (file)
@@ -583,8 +583,8 @@ Given the example program below::
 
 The above program uses several patterns that many users will already identify
 as "legacy", namely the use of the :meth:`_engine.Engine.execute` method
-that's part of the :ref:`connectionless execution <dbengine_implicit>`
-system.  When we run the above program against 1.4, it returns a single line::
+that's part of the "connectionless execution" API.  When we run the above
+program against 1.4, it returns a single line::
 
   $ python test3.py
   [(1,)]
@@ -2498,11 +2498,84 @@ explicit use of :meth:`_orm.Session.begin`, which is now solved by 1.4,
 as well as to allow the use of "subtransactions", which are also removed in
 2.0.
 
+.. _migration_20_session_subtransaction:
+
 Session "subtransaction" behavior removed
 ------------------------------------------
 
-See the section :ref:`session_subtransactions` for background on this
-change.
+**Synopsis**
+
+The "subtransaction" pattern that was often used with autocommit mode is
+also deprecated in 1.4.  This pattern allowed the use of the
+:meth:`_orm.Session.begin` method when a transaction were already begun,
+resulting in a construct called a "subtransaction", which was essentially
+a block that would prevent the :meth:`_orm.Session.commit` method from actually
+committing.
+
+**Migration to 2.0**
+
+
+To provide backwards compatibility for applications that make use of this
+pattern, the following context manager or a similar implementation based on
+a decorator may be used::
+
+
+    import contextlib
+
+    @contextlib.contextmanager
+    def transaction(session):
+        if not session.in_transaction():
+            with session.begin():
+                yield
+        else:
+            yield
+
+
+The above context manager may be used in the same way the
+"subtransaction" flag works, such as in the following example::
+
+
+    # method_a starts a transaction and calls method_b
+    def method_a(session):
+        with transaction(session):
+            method_b(session)
+
+    # method_b also starts a transaction, but when
+    # called from method_a participates in the ongoing
+    # transaction.
+    def method_b(session):
+        with transaction(session):
+            session.add(SomeObject('bat', 'lala'))
+
+    Session = sessionmaker(engine)
+
+    # create a Session and call method_a
+    with Session() as session:
+        method_a(session)
+
+To compare towards the preferred idiomatic pattern, the begin block should
+be at the outermost level.  This removes the need for individual functions
+or methods to be concerned with the details of transaction demarcation::
+
+    def method_a(session):
+        method_b(session)
+
+    def method_b(session):
+        session.add(SomeObject('bat', 'lala'))
+
+    Session = sessionmaker(engine)
+
+    # create a Session and call method_a
+    with Session() as session:
+        with session.begin():
+            method_a(session)
+
+**Discussion**
+
+This pattern has been shown to be confusing in real world applications, and it
+is preferable for an application to ensure that the top-most level of database
+operations are performed with a single begin/commit pair.
+
 
 
 2.0 Migration - ORM Extension and Recipe Changes
index 9c88903f202b46aea77791110c14d74f8221768a..dff34093df2956339980bf30e2f3ce2859cb7547 100644 (file)
@@ -2,18 +2,18 @@
     :tags: usecase, sql
     :tickets: 7998
 
-    Altered the compilation mechanics of the :class:`.Insert` construct such
-    that the "autoincrement primary key" column value will be fetched via
+    Altered the compilation mechanics of the :class:`_dml.Insert` construct
+    such that the "autoincrement primary key" column value will be fetched via
     ``cursor.lastrowid`` or RETURNING even if present in the parameter set or
-    within the :meth:`.Insert.values` method as a plain bound value, for
+    within the :meth:`_dml.Insert.values` method as a plain bound value, for
     single-row INSERT statements on specific backends that are known to
     generate autoincrementing values even when explicit NULL is passed. This
     restores a behavior that was in the 1.3 series for both the use case of
-    separate parameter set as well as :meth:`.Insert.values`. In 1.4, the
+    separate parameter set as well as :meth:`_dml.Insert.values`. In 1.4, the
     parameter set behavior unintentionally changed to no longer do this, but
-    the :meth:`.Insert.values` method would still fetch autoincrement values up
-    until 1.4.21 where :ticket:`6770` changed the behavior yet again again
-    unintentionally as this use case was never covered.
+    the :meth:`_dml.Insert.values` method would still fetch autoincrement
+    values up until 1.4.21 where :ticket:`6770` changed the behavior yet again
+    again unintentionally as this use case was never covered.
 
     The behavior is now defined as "working" to suit the case where databases
     such as SQLite, MySQL and MariaDB will ignore an explicit NULL primary key
index 46fd6147f035c2f80714bca788eb1351cc081429..7f47d754060ca551307b5f5572de9980b7e50072 100644 (file)
@@ -28,7 +28,7 @@ os.environ["DISABLE_SQLALCHEMY_CEXT_RUNTIME"] = "true"
 # -- General configuration --------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
-needs_sphinx = "3.5.0"
+needs_sphinx = "5.0.1"
 
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
@@ -112,7 +112,15 @@ changelog_render_changeset = "https://www.sqlalchemy.org/trac/changeset/%s"
 exclude_patterns = ["build", "**/unreleased*/*", "**/*_include.rst"]
 
 autodoc_class_signature = "separated"
-autodoc_typehints_format = "short"
+
+
+# to use this, we need:
+# 1. fix sphinx-paramlinks to work with "description" typing
+# 2. we need a huge autodoc_type_aliases map as we have extensive type aliasing
+# present, and typing is largely not very legible w/ the aliases
+# autodoc_typehints = "description"
+# autodoc_typehints_format = "short"
+# autodoc_typehints_description_target = "documented"
 
 # zzzeeksphinx makes these conversions when it is rendering the
 # docstrings classes, methods, and functions within the scope of
index e92d010ced0a3494350aa783effe64cf1caa6856..6edd01090a3e777b3f355e1a6b3e938b4415dfe0 100644 (file)
@@ -204,68 +204,6 @@ Engine Creation API
 .. autoclass:: sqlalchemy.engine.URL
     :members:
 
-    .. py:attribute:: drivername
-        :annotation: str
-
-        database backend and driver name, such as
-        ``postgresql+psycopg2``
-
-    .. py:attribute::  username
-        :annotation: str
-
-        username string
-
-    .. py:attribute::  password
-        :annotation: str
-
-        password, which is normally a string but may also be any
-        object that has a ``__str__()`` method.
-
-    .. py:attribute::  host
-        :annotation: str
-
-        string hostname
-
-    .. py:attribute::  port
-        :annotation: int
-
-        integer port number
-
-    .. py:attribute::  database
-        :annotation: str
-
-        string database name
-
-    .. py:attribute::  query
-        :annotation: Mapping[str, Union[str, Sequence[str]]]
-
-        an immutable mapping representing the query string.  contains strings
-        for keys and either strings or tuples of strings for values, e.g.::
-
-            >>> from sqlalchemy.engine import make_url
-            >>> url = make_url("postgresql+psycopg2://user:pass@host/dbname?alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt")
-            >>> url.query
-            immutabledict({'alt_host': ('host1', 'host2'), 'ssl_cipher': '/path/to/crt'})
-
-         To create a mutable copy of this mapping, use the ``dict`` constructor::
-
-            mutable_query_opts = dict(url.query)
-
-        .. seealso::
-
-          :attr:`_engine.URL.normalized_query` - normalizes all values into sequences
-          for consistent processing
-
-          Methods for altering the contents of :attr:`_engine.URL.query`:
-
-          :meth:`_engine.URL.update_query_dict`
-
-          :meth:`_engine.URL.update_query_string`
-
-          :meth:`_engine.URL.update_query_pairs`
-
-          :meth:`_engine.URL.difference_update_query`
-
 Pooling
 =======
 
index fda2b65ed9c19c85a116187d2e858dd8c1142f82..0c99aa5723627bbe99fa544b75d40711cc5b9b15 100644 (file)
@@ -19,6 +19,6 @@ Language provides a schema-centric usage paradigm.
     future
 
 .. toctree::
-   :hidden:
+    :hidden:
 
     tutorial
\ No newline at end of file
index 38edab642e6ad9091c77bbdc7fdc30d0bba2aad0..97b30c4934a0beb51fcc212ecf4b820ec6dc4783 100644 (file)
@@ -557,10 +557,12 @@ Column, Table, MetaData API
 ---------------------------
 
 .. attribute:: sqlalchemy.schema.BLANK_SCHEMA
+    :noindex:
 
     Refers to :attr:`.SchemaConst.BLANK_SCHEMA`.
 
 .. attribute:: sqlalchemy.schema.RETAIN_SCHEMA
+    :noindex:
 
     Refers to :attr:`.SchemaConst.RETAIN_SCHEMA`
 
index 0a7b8a6ddac86bd3f1c048e1fc3979c89a9a1bd2..a0e8157048eaee6a04903e653a67fe4516990ad2 100644 (file)
@@ -597,19 +597,16 @@ API Documentation - Available Pool Implementations
 
 .. autoclass:: sqlalchemy.pool.Pool
 
-   .. automethod:: __init__
    .. automethod:: connect
    .. automethod:: dispose
    .. automethod:: recreate
 
 .. autoclass:: sqlalchemy.pool.QueuePool
 
-   .. automethod:: __init__
    .. automethod:: connect
 
 .. autoclass:: SingletonThreadPool
 
-   .. automethod:: __init__
 
 .. autoclass:: AssertionPool
 
index 83aa30bcbd18d77fd1c352f4ded50e5f49380def..fea4936426605997863296d8382e2d878a21ed63 100644 (file)
@@ -77,7 +77,7 @@ construction arguments, are as follows:
 
 .. autoclass:: DOUBLE
     :members: __init__
-
+    :noindex:
 
 .. autoclass:: ENUM
     :members: __init__
index 5be5da7cd51c471a40e5e9e3e8700fec8729ae9b..81f0a0c4e13c38b35d543a2f1bdc71676594bb7e 100644 (file)
@@ -51,6 +51,7 @@ construction arguments, are as follows:
 
 .. autoclass:: DOUBLE_PRECISION
     :members: __init__
+    :noindex:
 
 
 .. autoclass:: ENUM
index d56f11e72be7fe2dd48a4d5d8c3c505717712dd3..64e30bf593b9724803917ffcd7de2256fa685812 100644 (file)
@@ -33,358 +33,6 @@ Within this section, the goal is to try to provide background on some of the
 most common runtime errors as well as programming time errors.
 
 
-Legacy API Features
-===================
-
-.. the reason we need this section here distinct from the migration notes
-   is because this is actually an ArgumentError that's raised by select()
-   when the "legacy" and "future" mode styles are used together.
-
-.. _error_c9ae:
-
-select() construct created in "legacy" mode; keyword arguments, etc.
---------------------------------------------------------------------
-
-The :func:`_expression.select` construct has been updated as of SQLAlchemy
-1.4 to support the newer calling style that will be standard in
-:ref:`SQLAlchemy 2.0 <error_b8d9>`.   For backwards compatibility in the
-interim, the construct accepts arguments in both the "legacy" style as well
-as the "new" style.
-
-The "new" style features that column and table expressions are passed
-positionally to the :func:`_expression.select` construct only; any other
-modifiers to the object must be passed using subsequent method chaining::
-
-    # this is the way to do it going forward
-    stmt = select(table1.c.myid).where(table1.c.myid == table2.c.otherid)
-
-For comparison, a :func:`_expression.select` in legacy forms of SQLAlchemy,
-before methods like :meth:`.Select.where` were even added, would like::
-
-    # this is how it was documented in original SQLAlchemy versions
-    # many years ago
-    stmt = select([table1.c.myid], whereclause=table1.c.myid == table2.c.otherid)
-
-Or even that the "whereclause" would be passed positionally::
-
-    # this is also how it was documented in original SQLAlchemy versions
-    # many years ago
-    stmt = select([table1.c.myid], table1.c.myid == table2.c.otherid)
-
-For some years now, the additional "whereclause" and other arguments that are
-accepted have been removed from most narrative documentation, leading to a
-calling style that is most familiar as the list of column arguments passed
-as a list, but no further arguments::
-
-    # this is how it's been documented since around version 1.0 or so
-    stmt = select([table1.c.myid]).where(table1.c.myid == table2.c.otherid)
-
-The document at :ref:`migration_20_5284` describes this change in terms
-of :ref:`2.0 Migration <migration_20_toplevel>`.
-
-.. seealso::
-
-    :ref:`migration_20_5284`
-
-    :ref:`migration_20_toplevel`
-
-
-
-.. _error_b8d9:
-
-The <some function> in SQLAlchemy 2.0 will no longer <something>
---------------------------------------------------------------------------------------------
-
-SQLAlchemy 2.0 is expected to be a major shift for a wide variety of key
-SQLAlchemy usage patterns in both the Core and ORM components.   The goal
-of this release is to make a slight readjustment in some of the most
-fundamental assumptions of SQLAlchemy since its early beginnings, and
-to deliver a newly streamlined usage model that is hoped to be significantly
-more minimalist and consistent between the Core and ORM components, as well as
-more capable.
-
-Introduced at :ref:`migration_20_toplevel`, the SQLAlchemy 2.0 project includes
-a comprehensive future compatibility system that is to be integrated into the
-1.4 series of SQLAlchemy, such that applications will have a clear,
-unambiguous, and incremental upgrade path in order to migrate applications to
-being fully 2.0 compatible.   The :class:`.exc.RemovedIn20Warning` deprecation
-warning is at the base of this system to provide guidance on what behaviors in
-an existing codebase will need to be modified.  An overview of how to enable
-this warning is at :ref:`deprecation_20_mode`.
-
-.. seealso::
-
-    :ref:`migration_20_toplevel`  - An overview of the upgrade process from
-    the 1.x series, as well as the current goals and progress of SQLAlchemy
-    2.0.
-
-
-    :ref:`deprecation_20_mode` - specific guidelines on how to use
-    "2.0 deprecations mode" in SQLAlchemy 1.4.
-
-.. _error_cprf:
-.. _caching_caveats:
-
-Object will not produce a cache key, Performance Implications
---------------------------------------------------------------
-
-SQLAlchemy as of version 1.4 includes a
-:ref:`SQL compilation caching facility <sql_caching>` which will allow
-Core and ORM SQL constructs to cache their stringified form, along with other
-structural information used to fetch results from the statement, allowing the
-relatively expensive string compilation process to be skipped when another
-structurally equivalent construct is next used. This system
-relies upon functionality that is implemented for all SQL constructs, including
-objects such as  :class:`_schema.Column`,
-:func:`_sql.select`, and :class:`_types.TypeEngine` objects, to produce a
-**cache key** which fully represents their state to the degree that it affects
-the SQL compilation process.
-
-If the warnings in question refer to widely used objects such as
-:class:`_schema.Column` objects, and are shown to be affecting the majority of
-SQL constructs being emitted (using the estimation techniques described at
-:ref:`sql_caching_logging`) such that caching is generally not enabled for an
-application, this will negatively impact performance and can in some cases
-effectively produce a **performance degradation** compared to prior SQLAlchemy
-versions. The FAQ at :ref:`faq_new_caching` covers this in additional detail.
-
-Caching disables itself if there's any doubt
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Caching relies on being able to generate a cache key that accurately represents
-the **complete structure** of a statement in a **consistent** fashion. If a particular
-SQL construct (or type) does not have the appropriate directives in place which
-allow it to generate a proper cache key, then caching cannot be safely enabled:
-
-* The cache key must represent the **complete structure**: If the usage of two
-  separate instances of that construct may result in different SQL being
-  rendered, caching the SQL against the first instance of the element using a
-  cache key that does not capture the distinct differences between the first and
-  second elements will result in incorrect SQL being cached and rendered for the
-  second instance.
-
-* The cache key must be **consistent**: If a construct represents state that
-  changes every time, such as a literal value, producing unique SQL for every
-  instance of it, this construct is also not safe to cache, as repeated use of
-  the construct will quickly fill up the statement cache with unique SQL strings
-  that will likely not be used again, defeating the purpose of the cache.
-
-For the above two reasons, SQLAlchemy's caching system is **extremely
-conservative** about deciding to cache the SQL corresponding to an object.
-
-Assertion attributes for caching
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The warning is emitted based on the criteria below.  For further detail on
-each, see the section :ref:`faq_new_caching`.
-
-* The :class:`.Dialect` itself (i.e. the module that is specified by the
-  first part of the URL we pass to :func:`_sa.create_engine`, like
-  ``postgresql+psycopg2://``), must indicate it has been reviewed and tested
-  to support caching correctly, which is indicated by the
-  :attr:`.Dialect.supports_statement_cache` attribute being set to ``True``.
-  When using third party dialects, consult with the maintainers of the dialect
-  so that they may follow the :ref:`steps to ensure caching may be enabled
-  <engine_thirdparty_caching>` in their dialect and publish a new release.
-
-* Third party or user defined types that inherit from either
-  :class:`.TypeDecorator` or :class:`.UserDefinedType` must include the
-  :attr:`.ExternalType.cache_ok` attribute in their definition, including for
-  all derived subclasses, following the guidelines described in the docstring
-  for :attr:`.ExternalType.cache_ok`. As before, if these datatypes are
-  imported from third party libraries, consult with the maintainers of that
-  library so that they may provide the necessary changes to their library and
-  publish a new release.
-
-* Third party or user defined SQL constructs that subclass from classes such
-  as :class:`.ClauseElement`, :class:`_schema.Column`, :class:`_dml.Insert`
-  etc, including simple subclasses as well as those which are designed to
-  work with the :ref:`sqlalchemy.ext.compiler_toplevel`, should normally
-  include the :attr:`.HasCacheKey.inherit_cache` attribute set to ``True``
-  or ``False`` based on the design of the construct, following the guidelines
-  described at :ref:`compilerext_caching`.
-
-.. seealso::
-
-    :ref:`sql_caching_logging` - background on observing cache behavior
-    and efficiency
-
-    :ref:`faq_new_caching` - in the :ref:`faq_toplevel` section
-
-.. _error_xaj1:
-
-An alias is being generated automatically for raw clauseelement
-----------------------------------------------------------------
-
-.. versionadded:: 1.4.26
-
-This deprecation warning refers to a very old and likely not well known pattern
-that applies to the legacy :meth:`_orm.Query.join` method as well as the
-:term:`2.0 style` :meth:`_sql.Select.join` method, where a join can be stated
-in terms of a :func:`_orm.relationship` but the target is the
-:class:`_schema.Table` or other Core selectable to which the class is mapped,
-rather than an ORM entity such as a mapped class or :func:`_orm.aliased`
-construct::
-
-    a1 = Address.__table__
-
-    q = s.query(User).\
-        join(a1, User.addresses).\
-        filter(Address.email_address == 'ed@foo.com').all()
-
-
-The above pattern also allows an arbitrary selectable, such as
-a Core :class:`_sql.Join` or :class:`_sql.Alias` object,
-however there is no automatic adaptation of this element, meaning the
-Core element would need to be referred towards directly::
-
-    a1 = Address.__table__.alias()
-
-    q = s.query(User).\
-        join(a1, User.addresses).\
-        filter(a1.c.email_address == 'ed@foo.com').all()
-
-The correct way to specify a join target is always by using the mapped
-class itself or an :class:`_orm.aliased` object, in the latter case using the
-:meth:`_orm.PropComparator.of_type` modifier to set up an alias::
-
-    # normal join to relationship entity
-    q = s.query(User).\
-        join(User.addresses).\
-        filter(Address.email_address == 'ed@foo.com')
-
-    # name Address target explicitly, not necessary but legal
-    q = s.query(User).\
-        join(Address, User.addresses).\
-        filter(Address.email_address == 'ed@foo.com')
-
-Join to an alias::
-
-    from sqlalchemy.orm import aliased
-
-    a1 = aliased(Address)
-
-    # of_type() form; recommended
-    q = s.query(User).\
-        join(User.addresses.of_type(a1)).\
-        filter(a1.email_address == 'ed@foo.com')
-
-    # target, onclause form
-    q = s.query(User).\
-        join(a1, User.addresses).\
-        filter(a1.email_address == 'ed@foo.com')
-
-
-.. _error_xaj2:
-
-An alias is being generated automatically due to overlapping tables
--------------------------------------------------------------------
-
-.. versionadded:: 1.4.26
-
-This warning is typically generated when querying using the
-:meth:`_sql.Select.join` method or the legacy :meth:`_orm.Query.join` method
-with mappings that involve joined table inheritance. The issue is that when
-joining between two joined inheritance models that share a common base table, a
-proper SQL JOIN between the two entities cannot be formed without applying an
-alias to one side or the other; SQLAlchemy applies an alias to the right side
-of the join. For example given a joined inheritance mapping as::
-
-    class Employee(Base):
-        __tablename__ = 'employee'
-        id = Column(Integer, primary_key=True)
-        manager_id = Column(ForeignKey("manager.id"))
-        name = Column(String(50))
-        type = Column(String(50))
-
-        reports_to = relationship("Manager", foreign_keys=manager_id)
-
-        __mapper_args__ = {
-            'polymorphic_identity':'employee',
-            'polymorphic_on':type,
-        }
-
-    class Manager(Employee):
-        __tablename__ = 'manager'
-        id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
-
-        __mapper_args__ = {
-            'polymorphic_identity':'manager',
-            'inherit_condition': id == Employee.id
-        }
-
-The above mapping includes a relationship between the ``Employee`` and
-``Manager`` classes.  Since both classes make use of the "employee" database
-table, from a SQL perspective this is a
-:ref:`self referential relationship <self_referential>`.  If we wanted to
-query from both the ``Employee`` and ``Manager`` models using a join, at the
-SQL level the "employee" table needs to be included twice in the query, which
-means it must be aliased.   When we create such a join using the SQLAlchemy
-ORM, we get SQL that looks like the following:
-
-.. sourcecode:: pycon+sql
-
-    >>> stmt = select(Employee, Manager).join(Employee.reports_to)
-    >>> print(stmt)
-    {opensql}SELECT employee.id, employee.manager_id, employee.name,
-    employee.type, manager_1.id AS id_1, employee_1.id AS id_2,
-    employee_1.manager_id AS manager_id_1, employee_1.name AS name_1,
-    employee_1.type AS type_1
-    FROM employee JOIN
-    (employee AS employee_1 JOIN manager AS manager_1 ON manager_1.id = employee_1.id)
-    ON manager_1.id = employee.manager_id
-
-Above, the SQL selects FROM the ``employee`` table, representing the
-``Employee`` entity in the query. It then joins to a right-nested join of
-``employee AS employee_1 JOIN manager AS manager_1``, where the ``employee``
-table is stated again, except as an anonymous alias ``employee_1``. This is the
-"automatic generation of an alias" that the warning message refers towards.
-
-When SQLAlchemy loads ORM rows that each contain an ``Employee`` and a
-``Manager`` object, the ORM must adapt rows from what above is the
-``employee_1`` and ``manager_1`` table aliases into those of the un-aliased
-``Manager`` class. This process is internally complex and does not accommodate
-for all API features, notably when trying to use eager loading features such as
-:func:`_orm.contains_eager` with more deeply nested queries than are shown
-here.  As the pattern is unreliable for more complex scenarios and involves
-implicit decisionmaking that is difficult to anticipate and follow,
-the warning is emitted and this pattern may be considered a legacy feature. The
-better way to write this query is to use the same patterns that apply to any
-other self-referential relationship, which is to use the :func:`_orm.aliased`
-construct explicitly.  For joined-inheritance and other join-oriented mappings,
-it is usually desirable to add the use of the :paramref:`_orm.aliased.flat`
-parameter, which will allow a JOIN of two or more tables to be aliased by
-applying an alias to the individual tables within the join, rather than
-embedding the join into a new subquery:
-
-.. sourcecode:: pycon+sql
-
-    >>> from sqlalchemy.orm import aliased
-    >>> manager_alias = aliased(Manager, flat=True)
-    >>> stmt = select(Employee, manager_alias).join(Employee.reports_to.of_type(manager_alias))
-    >>> print(stmt)
-    {opensql}SELECT employee.id, employee.manager_id, employee.name,
-    employee.type, manager_1.id AS id_1, employee_1.id AS id_2,
-    employee_1.manager_id AS manager_id_1, employee_1.name AS name_1,
-    employee_1.type AS type_1
-    FROM employee JOIN
-    (employee AS employee_1 JOIN manager AS manager_1 ON manager_1.id = employee_1.id)
-    ON manager_1.id = employee.manager_id
-
-If we then wanted to use :func:`_orm.contains_eager` to populate the
-``reports_to`` attribute, we refer to the alias::
-
-    >>> stmt =select(Employee).join(
-    ...     Employee.reports_to.of_type(manager_alias)
-    ... ).options(
-    ...     contains_eager(Employee.reports_to.of_type(manager_alias))
-    ... )
-
-Without using the explicit :func:`_orm.aliased` object, in some more nested
-cases the :func:`_orm.contains_eager` option does not have enough context to
-know where to get its data from, in the case that the ORM is "auto-aliasing"
-in a very nested context.  Therefore it's best not to rely on this feature
-and instead keep the SQL construction as explicit as possible.
 
 Connections and Transactions
 ============================
@@ -684,6 +332,95 @@ the database driver (DBAPI), not SQLAlchemy itself.
 
 SQL Expression Language
 =======================
+.. _error_cprf:
+.. _caching_caveats:
+
+Object will not produce a cache key, Performance Implications
+--------------------------------------------------------------
+
+SQLAlchemy as of version 1.4 includes a
+:ref:`SQL compilation caching facility <sql_caching>` which will allow
+Core and ORM SQL constructs to cache their stringified form, along with other
+structural information used to fetch results from the statement, allowing the
+relatively expensive string compilation process to be skipped when another
+structurally equivalent construct is next used. This system
+relies upon functionality that is implemented for all SQL constructs, including
+objects such as  :class:`_schema.Column`,
+:func:`_sql.select`, and :class:`_types.TypeEngine` objects, to produce a
+**cache key** which fully represents their state to the degree that it affects
+the SQL compilation process.
+
+If the warnings in question refer to widely used objects such as
+:class:`_schema.Column` objects, and are shown to be affecting the majority of
+SQL constructs being emitted (using the estimation techniques described at
+:ref:`sql_caching_logging`) such that caching is generally not enabled for an
+application, this will negatively impact performance and can in some cases
+effectively produce a **performance degradation** compared to prior SQLAlchemy
+versions. The FAQ at :ref:`faq_new_caching` covers this in additional detail.
+
+Caching disables itself if there's any doubt
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Caching relies on being able to generate a cache key that accurately represents
+the **complete structure** of a statement in a **consistent** fashion. If a particular
+SQL construct (or type) does not have the appropriate directives in place which
+allow it to generate a proper cache key, then caching cannot be safely enabled:
+
+* The cache key must represent the **complete structure**: If the usage of two
+  separate instances of that construct may result in different SQL being
+  rendered, caching the SQL against the first instance of the element using a
+  cache key that does not capture the distinct differences between the first and
+  second elements will result in incorrect SQL being cached and rendered for the
+  second instance.
+
+* The cache key must be **consistent**: If a construct represents state that
+  changes every time, such as a literal value, producing unique SQL for every
+  instance of it, this construct is also not safe to cache, as repeated use of
+  the construct will quickly fill up the statement cache with unique SQL strings
+  that will likely not be used again, defeating the purpose of the cache.
+
+For the above two reasons, SQLAlchemy's caching system is **extremely
+conservative** about deciding to cache the SQL corresponding to an object.
+
+Assertion attributes for caching
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The warning is emitted based on the criteria below.  For further detail on
+each, see the section :ref:`faq_new_caching`.
+
+* The :class:`.Dialect` itself (i.e. the module that is specified by the
+  first part of the URL we pass to :func:`_sa.create_engine`, like
+  ``postgresql+psycopg2://``), must indicate it has been reviewed and tested
+  to support caching correctly, which is indicated by the
+  :attr:`.Dialect.supports_statement_cache` attribute being set to ``True``.
+  When using third party dialects, consult with the maintainers of the dialect
+  so that they may follow the :ref:`steps to ensure caching may be enabled
+  <engine_thirdparty_caching>` in their dialect and publish a new release.
+
+* Third party or user defined types that inherit from either
+  :class:`.TypeDecorator` or :class:`.UserDefinedType` must include the
+  :attr:`.ExternalType.cache_ok` attribute in their definition, including for
+  all derived subclasses, following the guidelines described in the docstring
+  for :attr:`.ExternalType.cache_ok`. As before, if these datatypes are
+  imported from third party libraries, consult with the maintainers of that
+  library so that they may provide the necessary changes to their library and
+  publish a new release.
+
+* Third party or user defined SQL constructs that subclass from classes such
+  as :class:`.ClauseElement`, :class:`_schema.Column`, :class:`_dml.Insert`
+  etc, including simple subclasses as well as those which are designed to
+  work with the :ref:`sqlalchemy.ext.compiler_toplevel`, should normally
+  include the :attr:`.HasCacheKey.inherit_cache` attribute set to ``True``
+  or ``False`` based on the design of the construct, following the guidelines
+  described at :ref:`compilerext_caching`.
+
+.. seealso::
+
+    :ref:`sql_caching_logging` - background on observing cache behavior
+    and efficiency
+
+    :ref:`faq_new_caching` - in the :ref:`faq_toplevel` section
+
 
 .. _error_l7de:
 
@@ -799,46 +536,6 @@ The solution is to access the :class:`_schema.Column` directly using the
             CheckConstraint(cprop.expression > 5),
         )
 
-.. _error_2afi:
-
-This Compiled object is not bound to any Engine or Connection
--------------------------------------------------------------
-
-This error refers to the concept of "bound metadata", described at
-:ref:`dbengine_implicit`.   The issue occurs when one invokes the
-:meth:`.Executable.execute` method directly off of a Core expression object
-that is not associated with any :class:`_engine.Engine`::
-
- metadata_obj = MetaData()
- table = Table('t', metadata_obj, Column('q', Integer))
-
- stmt = select(table)
- result = stmt.execute()   # <--- raises
-
-What the logic is expecting is that the :class:`_schema.MetaData` object has
-been **bound** to a :class:`_engine.Engine`::
-
- engine = create_engine("mysql+pymysql://user:pass@host/db")
- metadata_obj = MetaData(bind=engine)
-
-Where above, any statement that derives from a :class:`_schema.Table` which
-in turn derives from that :class:`_schema.MetaData` will implicitly make use of
-the given :class:`_engine.Engine` in order to invoke the statement.
-
-Note that the concept of bound metadata is a **legacy pattern** and in most
-cases is **highly discouraged**.   The best way to invoke the statement is
-to pass it to the :meth:`_engine.Connection.execute` method of a :class:`_engine.Connection`::
-
- with engine.connect() as conn:
-   result = conn.execute(stmt)
-
-When using the ORM, a similar facility is available via the :class:`.Session`::
-
- result = session.execute(stmt)
-
-.. seealso::
-
- :ref:`dbengine_implicit`
 
 
 .. _error_cd3x:
@@ -906,9 +603,7 @@ Since "b" is required, pass it as ``None`` so that the INSERT may proceed::
 
 .. seealso::
 
- :ref:`coretutorial_bind_param`
-
- :ref:`execute_multiple`
+  :ref:`tutorial_sending_parameters`
 
 .. _error_89ve:
 
@@ -937,34 +632,209 @@ Above, ``stmt`` represents a SELECT statement.  The error is produced when we wa
 to use ``stmt`` directly as a FROM clause in another SELECT, such as if we
 attempted to select from it::
 
-    new_stmt_1 = select(stmt)
+    new_stmt_1 = select(stmt)
+
+Or if we wanted to use it in a FROM clause such as in a JOIN::
+
+    new_stmt_2 = select(some_table).select_from(some_table.join(stmt))
+
+In previous versions of SQLAlchemy, using a SELECT inside of another SELECT
+would produce a parenthesized, unnamed subquery.   In most cases, this form of
+SQL is not very useful as databases like MySQL and PostgreSQL require that
+subqueries in FROM clauses have named aliases, which means using the
+:meth:`_expression.SelectBase.alias` method or as of 1.4 using the
+:meth:`_expression.SelectBase.subquery` method to produce this.   On other databases, it
+is still much clearer for the subquery to have a name to resolve any ambiguity
+on future references to column  names inside the subquery.
+
+Beyond the above practical reasons, there are a lot of other SQLAlchemy-oriented
+reasons the change is being made.  The correct form of the above two statements
+therefore requires that :meth:`_expression.SelectBase.subquery` is used::
+
+    subq = stmt.subquery()
+
+    new_stmt_1 = select(subq)
+
+    new_stmt_2 = select(some_table).select_from(some_table.join(subq))
+
+.. seealso::
+
+  :ref:`change_4617`
+
+.. _error_xaj1:
+
+An alias is being generated automatically for raw clauseelement
+----------------------------------------------------------------
+
+.. versionadded:: 1.4.26
+
+This deprecation warning refers to a very old and likely not well known pattern
+that applies to the legacy :meth:`_orm.Query.join` method as well as the
+:term:`2.0 style` :meth:`_sql.Select.join` method, where a join can be stated
+in terms of a :func:`_orm.relationship` but the target is the
+:class:`_schema.Table` or other Core selectable to which the class is mapped,
+rather than an ORM entity such as a mapped class or :func:`_orm.aliased`
+construct::
+
+    a1 = Address.__table__
+
+    q = s.query(User).\
+        join(a1, User.addresses).\
+        filter(Address.email_address == 'ed@foo.com').all()
+
+
+The above pattern also allows an arbitrary selectable, such as
+a Core :class:`_sql.Join` or :class:`_sql.Alias` object,
+however there is no automatic adaptation of this element, meaning the
+Core element would need to be referred towards directly::
+
+    a1 = Address.__table__.alias()
+
+    q = s.query(User).\
+        join(a1, User.addresses).\
+        filter(a1.c.email_address == 'ed@foo.com').all()
+
+The correct way to specify a join target is always by using the mapped
+class itself or an :class:`_orm.aliased` object, in the latter case using the
+:meth:`_orm.PropComparator.of_type` modifier to set up an alias::
+
+    # normal join to relationship entity
+    q = s.query(User).\
+        join(User.addresses).\
+        filter(Address.email_address == 'ed@foo.com')
+
+    # name Address target explicitly, not necessary but legal
+    q = s.query(User).\
+        join(Address, User.addresses).\
+        filter(Address.email_address == 'ed@foo.com')
+
+Join to an alias::
+
+    from sqlalchemy.orm import aliased
+
+    a1 = aliased(Address)
+
+    # of_type() form; recommended
+    q = s.query(User).\
+        join(User.addresses.of_type(a1)).\
+        filter(a1.email_address == 'ed@foo.com')
+
+    # target, onclause form
+    q = s.query(User).\
+        join(a1, User.addresses).\
+        filter(a1.email_address == 'ed@foo.com')
+
+
+.. _error_xaj2:
+
+An alias is being generated automatically due to overlapping tables
+-------------------------------------------------------------------
+
+.. versionadded:: 1.4.26
+
+This warning is typically generated when querying using the
+:meth:`_sql.Select.join` method or the legacy :meth:`_orm.Query.join` method
+with mappings that involve joined table inheritance. The issue is that when
+joining between two joined inheritance models that share a common base table, a
+proper SQL JOIN between the two entities cannot be formed without applying an
+alias to one side or the other; SQLAlchemy applies an alias to the right side
+of the join. For example given a joined inheritance mapping as::
+
+    class Employee(Base):
+        __tablename__ = 'employee'
+        id = Column(Integer, primary_key=True)
+        manager_id = Column(ForeignKey("manager.id"))
+        name = Column(String(50))
+        type = Column(String(50))
+
+        reports_to = relationship("Manager", foreign_keys=manager_id)
+
+        __mapper_args__ = {
+            'polymorphic_identity':'employee',
+            'polymorphic_on':type,
+        }
+
+    class Manager(Employee):
+        __tablename__ = 'manager'
+        id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
+
+        __mapper_args__ = {
+            'polymorphic_identity':'manager',
+            'inherit_condition': id == Employee.id
+        }
 
-Or if we wanted to use it in a FROM clause such as in a JOIN::
+The above mapping includes a relationship between the ``Employee`` and
+``Manager`` classes.  Since both classes make use of the "employee" database
+table, from a SQL perspective this is a
+:ref:`self referential relationship <self_referential>`.  If we wanted to
+query from both the ``Employee`` and ``Manager`` models using a join, at the
+SQL level the "employee" table needs to be included twice in the query, which
+means it must be aliased.   When we create such a join using the SQLAlchemy
+ORM, we get SQL that looks like the following:
 
-    new_stmt_2 = select(some_table).select_from(some_table.join(stmt))
+.. sourcecode:: pycon+sql
 
-In previous versions of SQLAlchemy, using a SELECT inside of another SELECT
-would produce a parenthesized, unnamed subquery.   In most cases, this form of
-SQL is not very useful as databases like MySQL and PostgreSQL require that
-subqueries in FROM clauses have named aliases, which means using the
-:meth:`_expression.SelectBase.alias` method or as of 1.4 using the
-:meth:`_expression.SelectBase.subquery` method to produce this.   On other databases, it
-is still much clearer for the subquery to have a name to resolve any ambiguity
-on future references to column  names inside the subquery.
+    >>> stmt = select(Employee, Manager).join(Employee.reports_to)
+    >>> print(stmt)
+    {opensql}SELECT employee.id, employee.manager_id, employee.name,
+    employee.type, manager_1.id AS id_1, employee_1.id AS id_2,
+    employee_1.manager_id AS manager_id_1, employee_1.name AS name_1,
+    employee_1.type AS type_1
+    FROM employee JOIN
+    (employee AS employee_1 JOIN manager AS manager_1 ON manager_1.id = employee_1.id)
+    ON manager_1.id = employee.manager_id
 
-Beyond the above practical reasons, there are a lot of other SQLAlchemy-oriented
-reasons the change is being made.  The correct form of the above two statements
-therefore requires that :meth:`_expression.SelectBase.subquery` is used::
+Above, the SQL selects FROM the ``employee`` table, representing the
+``Employee`` entity in the query. It then joins to a right-nested join of
+``employee AS employee_1 JOIN manager AS manager_1``, where the ``employee``
+table is stated again, except as an anonymous alias ``employee_1``. This is the
+"automatic generation of an alias" that the warning message refers towards.
 
-    subq = stmt.subquery()
+When SQLAlchemy loads ORM rows that each contain an ``Employee`` and a
+``Manager`` object, the ORM must adapt rows from what above is the
+``employee_1`` and ``manager_1`` table aliases into those of the un-aliased
+``Manager`` class. This process is internally complex and does not accommodate
+for all API features, notably when trying to use eager loading features such as
+:func:`_orm.contains_eager` with more deeply nested queries than are shown
+here.  As the pattern is unreliable for more complex scenarios and involves
+implicit decisionmaking that is difficult to anticipate and follow,
+the warning is emitted and this pattern may be considered a legacy feature. The
+better way to write this query is to use the same patterns that apply to any
+other self-referential relationship, which is to use the :func:`_orm.aliased`
+construct explicitly.  For joined-inheritance and other join-oriented mappings,
+it is usually desirable to add the use of the :paramref:`_orm.aliased.flat`
+parameter, which will allow a JOIN of two or more tables to be aliased by
+applying an alias to the individual tables within the join, rather than
+embedding the join into a new subquery:
 
-    new_stmt_1 = select(subq)
+.. sourcecode:: pycon+sql
 
-    new_stmt_2 = select(some_table).select_from(some_table.join(subq))
+    >>> from sqlalchemy.orm import aliased
+    >>> manager_alias = aliased(Manager, flat=True)
+    >>> stmt = select(Employee, manager_alias).join(Employee.reports_to.of_type(manager_alias))
+    >>> print(stmt)
+    {opensql}SELECT employee.id, employee.manager_id, employee.name,
+    employee.type, manager_1.id AS id_1, employee_1.id AS id_2,
+    employee_1.manager_id AS manager_id_1, employee_1.name AS name_1,
+    employee_1.type AS type_1
+    FROM employee JOIN
+    (employee AS employee_1 JOIN manager AS manager_1 ON manager_1.id = employee_1.id)
+    ON manager_1.id = employee.manager_id
 
-.. seealso::
+If we then wanted to use :func:`_orm.contains_eager` to populate the
+``reports_to`` attribute, we refer to the alias::
 
-  :ref:`change_4617`
+    >>> stmt =select(Employee).join(
+    ...     Employee.reports_to.of_type(manager_alias)
+    ... ).options(
+    ...     contains_eager(Employee.reports_to.of_type(manager_alias))
+    ... )
+
+Without using the explicit :func:`_orm.aliased` object, in some more nested
+cases the :func:`_orm.contains_eager` option does not have enough context to
+know where to get its data from, in the case that the ORM is "auto-aliasing"
+in a very nested context.  Therefore it's best not to rely on this feature
+and instead keep the SQL construction as explicit as possible.
 
 
 Object Relational Mapping
@@ -1531,24 +1401,134 @@ Legacy Exceptions
 Exceptions in this section are not generated by current SQLAlchemy
 versions, however are provided here to suit exception message hyperlinks.
 
+.. _error_b8d9:
+
+The <some function> in SQLAlchemy 2.0 will no longer <something>
+--------------------------------------------------------------------------------------------
+
+SQLAlchemy 2.0 represents a major shift for a wide variety of key
+SQLAlchemy usage patterns in both the Core and ORM components.   The goal
+of the 2.0 release is to make a slight readjustment in some of the most
+fundamental assumptions of SQLAlchemy since its early beginnings, and
+to deliver a newly streamlined usage model that is hoped to be significantly
+more minimalist and consistent between the Core and ORM components, as well as
+more capable.
+
+Introduced at :ref:`migration_20_toplevel`, the SQLAlchemy 2.0 project includes
+a comprehensive future compatibility system that's integrated into the
+1.4 series of SQLAlchemy, such that applications will have a clear,
+unambiguous, and incremental upgrade path in order to migrate applications to
+being fully 2.0 compatible.   The :class:`.exc.RemovedIn20Warning` deprecation
+warning is at the base of this system to provide guidance on what behaviors in
+an existing codebase will need to be modified.  An overview of how to enable
+this warning is at :ref:`deprecation_20_mode`.
+
+.. seealso::
+
+    :ref:`migration_20_toplevel`  - An overview of the upgrade process from
+    the 1.x series, as well as the current goals and progress of SQLAlchemy
+    2.0.
+
+
+    :ref:`deprecation_20_mode` - specific guidelines on how to use
+    "2.0 deprecations mode" in SQLAlchemy 1.4.
+
+
+.. _error_s9r1:
+
+Object is being merged into a Session along the backref cascade
+---------------------------------------------------------------
+
+This message refers to the "backref cascade" behavior of SQLAlchemy,
+removed in version 2.0.  This refers to the action of
+an object being added into a :class:`_orm.Session` as a result of another
+object that's already present in that session being associated with it.
+As this behavior has been shown to be more confusing than helpful,
+the :paramref:`_orm.relationship.cascade_backrefs` and
+:paramref:`_orm.backref.cascade_backrefs` parameters were added, which can
+be set to ``False`` to disable it, and in SQLAlchemy 2.0 the "cascade backrefs"
+behavior has been removed entirely.
+
+For older SQLAlchemy versions, to set
+:paramref:`_orm.relationship.cascade_backrefs` to ``False`` on a backref that
+is currently configured using the :paramref:`_orm.relationship.backref` string
+parameter, the backref must be declared using the :func:`_orm.backref` function
+first so that the :paramref:`_orm.backref.cascade_backrefs` parameter may be
+passed.
+
+Alternatively, the entire "cascade backrefs" behavior can be turned off
+across the board by using the :class:`_orm.Session` in "future" mode,
+by passing ``True`` for the :paramref:`_orm.Session.future` parameter.
+
+.. seealso::
+
+    :ref:`change_5150` - background on the change for SQLAlchemy 2.0.
+
+
+.. _error_c9ae:
+
+select() construct created in "legacy" mode; keyword arguments, etc.
+--------------------------------------------------------------------
+
+The :func:`_expression.select` construct has been updated as of SQLAlchemy
+1.4 to support the newer calling style that is standard in
+SQLAlchemy 2.0.   For backwards compatibility within
+the 1.4 series, the construct accepts arguments in both the "legacy" style as well
+as the "new" style.
+
+The "new" style features that column and table expressions are passed
+positionally to the :func:`_expression.select` construct only; any other
+modifiers to the object must be passed using subsequent method chaining::
+
+    # this is the way to do it going forward
+    stmt = select(table1.c.myid).where(table1.c.myid == table2.c.otherid)
+
+For comparison, a :func:`_expression.select` in legacy forms of SQLAlchemy,
+before methods like :meth:`.Select.where` were even added, would like::
+
+    # this is how it was documented in original SQLAlchemy versions
+    # many years ago
+    stmt = select([table1.c.myid], whereclause=table1.c.myid == table2.c.otherid)
+
+Or even that the "whereclause" would be passed positionally::
+
+    # this is also how it was documented in original SQLAlchemy versions
+    # many years ago
+    stmt = select([table1.c.myid], table1.c.myid == table2.c.otherid)
+
+For some years now, the additional "whereclause" and other arguments that are
+accepted have been removed from most narrative documentation, leading to a
+calling style that is most familiar as the list of column arguments passed
+as a list, but no further arguments::
+
+    # this is how it's been documented since around version 1.0 or so
+    stmt = select([table1.c.myid]).where(table1.c.myid == table2.c.otherid)
+
+The document at :ref:`migration_20_5284` describes this change in terms
+of :ref:`2.0 Migration <migration_20_toplevel>`.
+
+.. seealso::
+
+    :ref:`migration_20_5284`
+
+    :ref:`migration_20_toplevel`
 
 .. _error_c9bf:
 
 A bind was located via legacy bound metadata, but since future=True is set on this Session, this bind is ignored.
 -------------------------------------------------------------------------------------------------------------------
 
-.. note:: This is a legacy error message that is not in the 2.x series of
-   SQLAlchemy.
+The concept of "bound metadata" is present up until SQLAlchemy 1.4; as
+of SQLAlchemy 2.0 it's been removed.
 
-The concept of "bound metadata" is being removed in SQLAlchemy 2.0.  This
-refers to the :paramref:`_schema.MetaData.bind` parameter on the
+This error refers to the :paramref:`_schema.MetaData.bind` parameter on the
 :class:`_schema.MetaData` object that in turn allows objects like the ORM
 :class:`_orm.Session` to associate a particular mapped class with an
-:class:`_orm.Engine`.   In SQLAlchemy 2.0, the :class:`_orm.Session` must be
+:class:`_orm.Engine`. In SQLAlchemy 2.0, the :class:`_orm.Session` must be
 linked to each :class:`_orm.Engine` directly. That is, instead of instantiating
-the :class:`_orm.Session` or
-:class:`_orm.sessionmaker` without any arguments, and associating the
-:class:`_engine.Engine` with the :class:`_schema.MetaData`::
+the :class:`_orm.Session` or :class:`_orm.sessionmaker` without any arguments,
+and associating the :class:`_engine.Engine` with the
+:class:`_schema.MetaData`::
 
     engine = create_engine("sqlite://")
     Session = sessionmaker()
@@ -1585,33 +1565,99 @@ In SQLAlchemy 1.4, this :term:`2.0 style` behavior is enabled when the
 :paramref:`_orm.Session.future` flag is set on :class:`_orm.sessionmaker`
 or :class:`_orm.Session`.
 
-.. _error_s9r1:
 
-Object is being merged into a Session along the backref cascade
----------------------------------------------------------------
+.. _error_2afi:
 
-This message refers to the "backref cascade" behavior of SQLAlchemy,
-removed in version 2.0.  This refers to the action of
-an object being added into a :class:`_orm.Session` as a result of another
-object that's already present in that session being associated with it.
-As this behavior has been shown to be more confusing than helpful,
-the :paramref:`_orm.relationship.cascade_backrefs` and
-:paramref:`_orm.backref.cascade_backrefs` parameters were added, which can
-be set to ``False`` to disable it, and in SQLAlchemy 2.0 the "cascade backrefs"
-behavior has been removed entirely.
+This Compiled object is not bound to any Engine or Connection
+-------------------------------------------------------------
 
-For older SQLAlchemy versions, to set
-:paramref:`_orm.relationship.cascade_backrefs` to ``False`` on a backref that
-is currently configured using the :paramref:`_orm.relationship.backref` string
-parameter, the backref must be declared using the :func:`_orm.backref` function
-first so that the :paramref:`_orm.backref.cascade_backrefs` parameter may be
-passed.
+This error refers to the concept of "bound metadata", which is a legacy
+SQLAlchemy pattern present only in 1.x versions. The issue occurs when one invokes
+the :meth:`.Executable.execute` method directly off of a Core expression object
+that is not associated with any :class:`_engine.Engine`::
 
-Alternatively, the entire "cascade backrefs" behavior can be turned off
-across the board by using the :class:`_orm.Session` in "future" mode,
-by passing ``True`` for the :paramref:`_orm.Session.future` parameter.
+ metadata_obj = MetaData()
+ table = Table('t', metadata_obj, Column('q', Integer))
+
+ stmt = select(table)
+ result = stmt.execute()   # <--- raises
+
+What the logic is expecting is that the :class:`_schema.MetaData` object has
+been **bound** to a :class:`_engine.Engine`::
+
+ engine = create_engine("mysql+pymysql://user:pass@host/db")
+ metadata_obj = MetaData(bind=engine)
+
+Where above, any statement that derives from a :class:`_schema.Table` which
+in turn derives from that :class:`_schema.MetaData` will implicitly make use of
+the given :class:`_engine.Engine` in order to invoke the statement.
+
+Note that the concept of bound metadata is **not present in SQLAlchemy 2.0**.
+The correct way to invoke statements is via
+the :meth:`_engine.Connection.execute` method of a :class:`_engine.Connection`::
+
+ with engine.connect() as conn:
+   result = conn.execute(stmt)
+
+When using the ORM, a similar facility is available via the :class:`.Session`::
+
+ result = session.execute(stmt)
 
 .. seealso::
 
-    :ref:`change_5150` - background on the change for SQLAlchemy 2.0.
+    :ref:`tutorial_statement_execution`
+
+.. _error_8s2a:
+
+This connection is on an inactive transaction.  Please rollback() fully before proceeding
+------------------------------------------------------------------------------------------
+
+This error condition was added to SQLAlchemy as of version 1.4, and does not
+apply to SQLAlchemy 2.0.    The error
+refers to the state where a :class:`_engine.Connection` is placed into a
+transaction using a method like :meth:`_engine.Connection.begin`, and then a
+further "marker" transaction is created within that scope; the "marker"
+transaction is then rolled back using :meth:`.Transaction.rollback` or closed
+using :meth:`.Transaction.close`, however the outer transaction is still
+present in an "inactive" state and must be rolled back.
+
+The pattern looks like::
+
+    engine = create_engine(...)
+
+    connection = engine.connect()
+    transaction1 = connection.begin()
+
+    # this is a "sub" or "marker" transaction, a logical nesting
+    # structure based on "real" transaction transaction1
+    transaction2 = connection.begin()
+    transaction2.rollback()
+
+    # transaction1 is still present and needs explicit rollback,
+    # so this will raise
+    connection.execute(text("select 1"))
+
+Above, ``transaction2`` is a "marker" transaction, which indicates a logical
+nesting of transactions within an outer one; while the inner transaction
+can roll back the whole transaction via its rollback() method, its commit()
+method has no effect except to close the scope of the "marker" transaction
+itself.   The call to ``transaction2.rollback()`` has the effect of
+**deactivating** transaction1 which means it is essentially rolled back
+at the database level, however is still present in order to accommodate
+a consistent nesting pattern of transactions.
+
+The correct resolution is to ensure the outer transaction is also
+rolled back::
+
+    transaction1.rollback()
+
+This pattern is not commonly used in Core.  Within the ORM, a similar issue can
+occur which is the product of the ORM's "logical" transaction structure; this
+is described in the FAQ entry at :ref:`faq_session_rollback`.
+
+The "subtransaction" pattern is removed in SQLAlchemy 2.0 so that this
+particular programming pattern is no longer be available, preventing
+this error message.
+
+
 
index 9450dfdfa037af29b618a5179b46eae276043872..4a21318f406722d8afa0d27b0c977042b6cb6a25 100644 (file)
@@ -167,18 +167,12 @@ a new transaction when it is first used that remains in effect for subsequent
 statements, until the DBAPI-level ``connection.commit()`` or
 ``connection.rollback()`` method is invoked.
 
-As discussed at :ref:`autocommit`, there is a library level "autocommit"
-feature which is deprecated in 1.4 that causes :term:`DML` and :term:`DDL`
-executions to commit automatically after individual statements are executed;
-however, outside of this deprecated case, modern use of SQLAlchemy works with
-this transaction in all cases and does not commit any data unless explicitly
-told to commit.
-
-At the ORM level, a similar situation where the ORM
-:class:`_orm.Session` object also presents a legacy "autocommit" operation is
-present; however even if this legacy mode of operation is used, the
-:class:`_orm.Session` still makes use of transactions internally,
-particularly within the :meth:`_orm.Session.flush` process.
+In modern use of SQLAlchemy, a series of SQL statements are always invoked
+within this transactional state, assuming
+:ref:`DBAPI autocommit mode <dbapi_autocommit>` is not enabled (more on that in
+the next section), meaning that no single statement is automatically committed;
+if an operation fails, the effects of all statements within the current
+transaction will be lost.
 
 The implication that this has for the notion of "retrying" a statement is that
 in the default case, when a connection is lost, **the entire transaction is
@@ -188,9 +182,10 @@ SQLAlchemy does not have a transparent "reconnection" feature that works
 mid-transaction, for the case when the database connection has disconnected
 while being used. The canonical approach to dealing with mid-operation
 disconnects is to **retry the entire operation from the start of the
-transaction**, often by using a Python "retry" decorator, or to otherwise
+transaction**, often by using a custom Python decorator that will
+"retry" a particular function several times until it succeeds, or to otherwise
 architect the application in such a way that it is resilient against
-transactions that are dropped.
+transactions that are dropped that then cause operations to fail.
 
 There is also the notion of extensions that can keep track of all of the
 statements that have proceeded within a transaction and then replay them all in
index 8281b4bf55b694f3709586f68e3ab1aa653439a7..0f9f7575b8c0e7e2bf7bf00e9c5759ae91168455 100644 (file)
@@ -350,7 +350,7 @@ How Do I use Textual SQL with ORM Queries?
 
 See:
 
-* :ref:`orm_tutorial_literal_sql` - Ad-hoc textual blocks with :class:`_query.Query`
+* :ref:`orm_queryguide_selecting_text` - Ad-hoc textual blocks with :class:`_query.Query`
 
 * :ref:`session_sql_expressions` - Using :class:`.Session` with textual SQL directly.
 
index a54d7715e5a5bf76c99c3cd360875abdafbca717..3d7ebeb88074facd9c012268d570b956ba93fd37 100644 (file)
@@ -110,7 +110,7 @@ Glossary
 
             `bind parameters <https://use-the-index-luke.com/sql/where-clause/bind-parameters>`_ - at Use The Index, Luke!
 
-
+            :ref:`tutorial_sending_parameters` - in the :ref:`unified_tutorial`
 
     selectable
         A term used in SQLAlchemy to describe a SQL construct that represents
index 2f26517b26d2db655ad6a76c2bd0a65dba18db7d..5e76e5bf78811bcdd341c0120e8941f55b75aa82 100644 (file)
@@ -606,3 +606,4 @@ API Documentation
    :inherited-members:
 
 .. autoclass:: AssociationProxyExtensionType
+   :members:
\ No newline at end of file
index f22e28fa5acf5d5eb395b9553d2a677a891c9f78..b3c21716a2a7e9e7025f3fdb38b80f7385dcbd96 100644 (file)
@@ -475,4 +475,5 @@ API Documentation
 
 .. autoclass:: Result
     :members:
+    :noindex:
 
index 571aca72221e10982edc6c1d32dcc056036f5eb7..d403c196ff9bf6361adfc7ff25d9fb81ddcd93fb 100644 (file)
@@ -18,3 +18,4 @@ API Reference
 
 
 .. autoclass:: HybridExtensionType
+   :members:
\ No newline at end of file
index 2fe23331138c257bd12ee1ecfce80c5c2b847e6f..c1fcbc747bb6c83f5e8c3025f119f6a05e032c71 100644 (file)
@@ -4,7 +4,7 @@ Mypy  / Pep-484 Support for ORM Mappings
 ========================================
 
 Support for :pep:`484` typing annotations as well as the
-`Mypy <https://mypy.readthedocs.io/>`_ type checking tool.
+MyPy_ type checking tool.
 
 .. topic:: SQLAlchemy Mypy Plugin Status Update
 
@@ -62,7 +62,7 @@ TODO: document uninstallation of existing stubs:
 
 SQLAlchemy 2.0 is expected to be directly typed.
 
-The `Mypy <https://pypi.org/project/mypy/>`_ package itself is a dependency.
+The Mypy_ package itself is a dependency.
 
 Both packages may be installed using the "mypy" extras hook using pip::
 
@@ -597,3 +597,5 @@ With the above recipe, the attributes listed in ``_mypy_mapped_attrs``
 will be applied with the :class:`_orm.Mapped` typing information so that the
 ``User`` class will behave as a SQLAlchemy mapped class when used in a
 class-bound context.
+
+.. _Mypy: https://mypy.readthedocs.io/
index 853f72cf5647720ab8663bac009a81dfd461aa74..997f0dd6abbb48711b20023d7487ffcedb601da1 100644 (file)
@@ -21,6 +21,6 @@ tutorial.
     examples
 
 .. toctree::
-   :hidden:
+    :hidden:
 
     tutorial
index ee006427110d82a3ba39d36024369d3116d4cf01..b2551bbfae144272b3f4b0b45bdbe92d9f4e206d 100644 (file)
@@ -88,6 +88,7 @@ sections, are listed here.
             :attr:`.SchemaItem.info`
 
 .. autoclass:: InspectionAttrExtensionType
+    :members:
 
 .. autoclass:: NotExtension
     :members:
index 8d18ac7ceb0dcbec0807a14472c8413ee5dbaebb..cd83f7fc8b637d0ed6f42c2afeac4260780af1cd 100644 (file)
@@ -988,7 +988,7 @@ Comparison to Core Insert / Update Constructs
 The bulk methods offer performance that under particular circumstances
 can be close to that of using the core :class:`_expression.Insert` and
 :class:`_expression.Update` constructs in an "executemany" context (for a description
-of "executemany", see :ref:`execute_multiple` in the Core tutorial).
+of "executemany", see :ref:`tutorial_multiple_parameters` in the Core tutorial).
 In order to achieve this, the
 :paramref:`.Session.bulk_insert_mappings.return_defaults`
 flag should be disabled so that rows can be batched together.   The example
index f184c4c446b02b76769b2a9fe3e276aec6abefb7..f4e2a0ac685a74d7dd31ab018b5897f70a21c5a2 100644 (file)
@@ -309,6 +309,7 @@ The :class:`_orm.aliased` construct is also central to making use of subqueries
 with the ORM; the sections :ref:`orm_queryguide_subqueries` and
 :ref:`orm_queryguide_join_subqueries` discusses this further.
 
+
 .. _orm_queryguide_selecting_text:
 
 Getting ORM Results from Textual and Core Statements
@@ -491,7 +492,6 @@ and order by criteria based on its exported columns::
 
     :ref:`tutorial_orm_union` - in the :ref:`unified_tutorial`
 
-
 .. _orm_queryguide_joins:
 
 Joins
index 8a4fe36a1d8287c4f86c6bbb6673cc41bdd7cf2a..b9111741ccf25e7edf18e71e19d42e93a78323b8 100644 (file)
@@ -7,7 +7,7 @@ Relationship Configuration
 
 This section describes the :func:`relationship` function and in depth discussion
 of its usage.   For an introduction to relationships, start with the
-:ref:`ormtutorial_toplevel` and head into :ref:`orm_tutorial_relationship`.
+:ref:`ormtutorial_toplevel` and head into :ref:`tutorial_orm_related_objects`.
 
 .. toctree::
     :maxdepth: 3
index b1afb1a46131d656f5725293aa204f16255d6761..ac17e6f4d29bb9f0c1c5a12873b049db44ae5a2c 100644 (file)
@@ -137,7 +137,7 @@ the foreign key from one level of the tree to the next.  In SQL,
 a join from a table to itself requires that at least one side of the
 expression be "aliased" so that it can be unambiguously referred to.
 
-Recall from :ref:`ormtutorial_aliases` in the ORM tutorial that the
+Recall from :ref:`orm_queryguide_orm_aliases` in the ORM tutorial that the
 :func:`_orm.aliased` construct is normally used to provide an "alias" of
 an ORM entity.  Joining from ``Node`` to itself using this technique
 looks like:
index 22c391d540872a79ab0c6c17aae37f15723ab3c7..bad011d8e3fdba87335c4ddf286d87fb0f1c340d 100644 (file)
@@ -711,7 +711,7 @@ values for ``synchronize_session`` are supported:
       automatically.   If the operation is against multiple tables, typically
       individual UPDATE / DELETE statements against the individual tables
       should be used.   Some databases support multiple table UPDATEs.
-      Similar guidelines as those detailed at :ref:`multi_table_updates`
+      Similar guidelines as those detailed at :ref:`tutorial_update_from`
       may be applied.
 
     * The WHERE criteria needed in order to limit the polymorphic identity to
index a8b1a49a25c73619e2574a7caa95881648bfe2ca..767e7995bdd88da49139208df254903fef119473 100644 (file)
@@ -8,6 +8,7 @@
 
 .. rst-class:: core-header
 
+
 .. _tutorial_core_insert:
 
 Inserting Rows with Core
index c8fac288e629319f24307d7f958b0f485c61f787..f30f7a587369a6581db341a5956b4450cdd95e7d 100644 (file)
@@ -248,7 +248,7 @@ when referring to arbitrary SQL expressions in a result row by name:
     :ref:`tutorial_order_by_label` - the label names we create may also be
     referred towards in the ORDER BY or GROUP BY clause of the :class:`_sql.Select`.
 
-.. _tutorial_select_arbtrary_text:
+.. _tutorial_select_arbitrary_text:
 
 Selecting with Textual Column Expressions
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1050,6 +1050,75 @@ The statement then can return the data for this column like any other:
      ('sandy', 'sandy@squirrelpower.org', 2)]
     {opensql}ROLLBACK{stop}
 
+
+.. _tutorial_lateral_correlation:
+
+LATERAL correlation
+~~~~~~~~~~~~~~~~~~~
+
+LATERAL correlation is a special sub-category of SQL correlation which
+allows a selectable unit to refer to another selectable unit within a
+single FROM clause.  This is an extremely special use case which, while
+part of the SQL standard, is only known to be supported by recent
+versions of PostgreSQL.
+
+Normally, if a SELECT statement refers to
+``table1 JOIN (SELECT ...) AS subquery`` in its FROM clause, the subquery
+on the right side may not refer to the "table1" expression from the left side;
+correlation may only refer to a table that is part of another SELECT that
+entirely encloses this SELECT.  The LATERAL keyword allows us to turn this
+behavior around and allow correlation from the right side JOIN.
+
+SQLAlchemy supports this feature using the :meth:`_expression.Select.lateral`
+method, which creates an object known as :class:`.Lateral`. :class:`.Lateral`
+is in the same family as :class:`.Subquery` and :class:`.Alias`, but also
+includes correlation behavior when the construct is added to the FROM clause of
+an enclosing SELECT. The following example illustrates a SQL query that makes
+use of LATERAL, selecting the "user account / count of email address" data as
+was discussed in the previous section::
+
+    >>> subq = (
+    ...   select(
+    ...       func.count(address_table.c.id).label("address_count"),
+    ...       address_table.c.email_address,
+    ...       address_table.c.user_id,
+    ...    ).
+    ...    where(user_table.c.id == address_table.c.user_id).
+    ...    lateral()
+    ... )
+    >>> stmt = select(
+    ...     user_table.c.name,
+    ...     subq.c.address_count,
+    ...     subq.c.email_address
+    ... ).\
+    ... join_from(user_table, subq).\
+    ... order_by(user_table.c.id, subq.c.email_address)
+    >>> print(stmt)
+    {opensql}SELECT user_account.name, anon_1.address_count, anon_1.email_address
+    FROM user_account
+    JOIN LATERAL (SELECT count(address.id) AS address_count,
+    address.email_address AS email_address, address.user_id AS user_id
+    FROM address
+    WHERE user_account.id = address.user_id) AS anon_1
+    ON user_account.id = anon_1.user_id
+    ORDER BY user_account.id, anon_1.email_address
+
+Above, the right side of the JOIN is a subquery that correlates to the
+``user_account`` table that's on the left side of the join.
+
+When using :meth:`_expression.Select.lateral`, the behavior of
+:meth:`_expression.Select.correlate` and
+:meth:`_expression.Select.correlate_except` methods is applied to the
+:class:`.Lateral` construct as well.
+
+.. seealso::
+
+    :class:`_expression.Lateral`
+
+    :meth:`_expression.Select.lateral`
+
+
+
 .. _tutorial_union:
 
 UNION, UNION ALL and other set operations
@@ -1258,6 +1327,7 @@ clause:
     [('patrick',)]
     {opensql}ROLLBACK{stop}
 
+
 .. _tutorial_functions:
 
 Working with SQL Functions
@@ -1577,8 +1647,8 @@ using the :meth:`_functions.FunctionElement.filter` method::
     count(address.email_address) FILTER (WHERE user_account.name = ?) AS anon_2
     FROM user_account JOIN address ON user_account.id = address.user_id
     [...] ('sandy', 'spongebob')
-    [(2, 1)]
-    ROLLBACK
+    {stop}[(2, 1)]
+    {opensql}ROLLBACK
 
 .. _tutorial_functions_table_valued:
 
@@ -1614,16 +1684,16 @@ modern versions of SQLite::
 
     >>> onetwothree = func.json_each('["one", "two", "three"]').table_valued("value")
     >>> stmt = select(onetwothree).where(onetwothree.c.value.in_(["two", "three"]))
-    >>> with engine.connect() as conn:  # doctest:+SKIP
+    >>> with engine.connect() as conn:
     ...     result = conn.execute(stmt)
-    ...     print(result.all())
+    ...     result.all()
     {opensql}BEGIN (implicit)
     SELECT anon_1.value
     FROM json_each(?) AS anon_1
     WHERE anon_1.value IN (?, ?)
     [...] ('["one", "two", "three"]', 'two', 'three')
-    [('two',), ('three',)]
-    ROLLBACK
+    {stop}[('two',), ('three',)]
+    {opensql}ROLLBACK{stop}
 
 Above, we used the ``json_each()`` JSON function supported by SQLite and
 PostgreSQL to generate a table valued expression with a single column referred
@@ -1671,4 +1741,77 @@ it is usable for custom SQL functions::
 
     :ref:`postgresql_column_valued` - in the :ref:`postgresql_toplevel` documentation.
 
+.. _tutorial_casts:
+
+Data Casts and Type Coercion
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In SQL, we often need to indicate the datatype of an expression explicitly,
+either to tell the database what type is expected in an otherwise ambiguous
+expression, or in some cases when we want to convert the implied datatype
+of a SQL expression into something else.   The SQL CAST keyword is used for
+this task, which in SQLAlchemy is provided by the :func:`.cast` function.
+This function accepts a column expression and a data type
+object as arguments, as demonstrated below where we produce a SQL expression
+``CAST(user_account.id AS VARCHAR)`` from the ``user_table.c.id`` column
+object::
+
+    >>> from sqlalchemy import cast
+    >>> stmt = select(cast(user_table.c.id, String))
+    >>> with engine.connect() as conn:
+    ...     result = conn.execute(stmt)
+    ...     result.all()
+    {opensql}BEGIN (implicit)
+    SELECT CAST(user_account.id AS VARCHAR) AS id
+    FROM user_account
+    [...] ()
+    {stop}[('1',), ('2',), ('3',)]
+    {opensql}ROLLBACK{stop}
+
+The :func:`.cast` function not only renders the SQL CAST syntax, it also
+produces a SQLAlchemy column expression that will act as the given datatype on
+the Python side as well. A string expression that is :func:`.cast` to
+:class:`_sqltypes.JSON` will gain JSON subscript and comparison operators, for example::
 
+    >>> from sqlalchemy import JSON
+    >>> print(cast("{'a': 'b'}", JSON)["a"])
+    CAST(:param_1 AS JSON)[:param_2]
+
+
+type_coerce() - a Python-only "cast"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes there is the need to have SQLAlchemy know the datatype of an
+expression, for all the reasons mentioned above, but to not render the CAST
+expression itself on the SQL side, where it may interfere with a SQL operation
+that already works without it.  For this fairly common use case there is
+another function :func:`.type_coerce` which is closely related to
+:func:`.cast`, in that it sets up a Python expression as having a specific SQL
+database type, but does not render the ``CAST`` keyword or datatype on the
+database side.    :func:`.type_coerce` is particularly important when dealing
+with the :class:`_types.JSON` datatype, which typically has an intricate
+relationship with string-oriented datatypes on different platforms and
+may not even be an explicit datatype, such as on SQLite and MariaDB.
+Below, we use :func:`.type_coerce` to deliver a Python structure as a JSON
+string into one of MySQL's JSON functions:
+
+.. sourcecode:: pycon+sql
+
+    >>> import json
+    >>> from sqlalchemy import JSON
+    >>> from sqlalchemy import type_coerce
+    >>> from sqlalchemy.dialects import mysql
+    >>> s = select(
+    ... type_coerce(
+    ...        {'some_key': {'foo': 'bar'}}, JSON
+    ...    )['some_key']
+    ... )
+    >>> print(s.compile(dialect=mysql.dialect()))
+    SELECT JSON_EXTRACT(%s, %s) AS anon_1
+
+Above, MySQL's ``JSON_EXTRACT`` SQL function was invoked
+because we used :func:`.type_coerce` to indicate that our Python dictionary
+should be treated as :class:`_types.JSON`.  The Python ``__getitem__``
+operator, ``['some_key']`` in this case, became available as a result and
+allowed a ``JSON_EXTRACT`` path expression (not shown, however in this
+case it would ultimately be ``'$."some_key"'``) to be rendered.
index e5a499786e31b22e0b42db984bbc2808d1ab9462..545a0d1291d710d5da542221bac2ce5647345f1e 100644 (file)
@@ -179,6 +179,7 @@ purposes.
 
 .. rst-class:: core-header
 
+.. _tutorial_statement_execution:
 
 Basics of Statement Execution
 -----------------------------
index 59691cf818da57f352ca0653a68c5c3c4b5493e4..2eacc39e3692a2e9ce602005529c31ef590843a1 100644 (file)
@@ -5,6 +5,7 @@
 
 .. include:: tutorial_nav_include.rst
 
+
 .. _tutorial_orm_related_objects:
 
 Working with Related Objects
@@ -129,6 +130,9 @@ of the ``Address.user`` attribute after the fact::
   # equivalent effect as a2 = Address(user=u1)
   >>> a2.user = u1
 
+
+.. _tutorial_orm_cascades:
+
 Cascading Objects into the Session
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
index b585ea992c87df2d516db4cb46f54456cd4d2d66..f91f3ef3375b9a13f2ff851caab9665c15c26320 100644 (file)
@@ -562,7 +562,7 @@ forms are accepted, including a single dictionary:
 
 as well as a list of 2-tuples, which will automatically provide
 a parameter-ordered UPDATE statement in a manner similar to that described
-at :ref:`updates_order_parameters`.  Unlike the :class:`_expression.Update`
+at :ref:`tutorial_parameter_ordered_updates`.  Unlike the :class:`_expression.Update`
 object,
 no special flag is needed to specify the intent since the argument form is
 this context is unambiguous:
index f5e4f03e9706cf6e29e3acc97e6902e99af4bf67..b93116c389dcb2bef4e3a4d254a7658a4f6d548e 100644 (file)
@@ -128,7 +128,7 @@ class Insert(StandardInsert):
          in the UPDATE clause should be ordered as sent, in a manner similar
          to that described for the :class:`_expression.Update`
          construct overall
-         in :ref:`updates_order_parameters`::
+         in :ref:`tutorial_parameter_ordered_updates`::
 
             insert().on_duplicate_key_update(
                 [("name", "some name"), ("value", "some value")])
index f5d84a5a35b443fcc4ea9497517875356ecefd95..cf0ac87f01bbe7b794e5240df12550d4e6bf01f3 100644 (file)
@@ -205,7 +205,7 @@ performance, primarily with INSERT statements, by multiple orders of magnitude.
 SQLAlchemy internally makes use of these extensions for ``executemany()`` style
 calls, which correspond to lists of parameters being passed to
 :meth:`_engine.Connection.execute` as detailed in :ref:`multiple parameter
-sets <execute_multiple>`.   The ORM also uses this mode internally whenever
+sets <tutorial_multiple_parameters>`.   The ORM also uses this mode internally whenever
 possible.
 
 The two available extensions on the psycopg2 side are the ``execute_values()``
@@ -281,7 +281,7 @@ size defaults to 100.  These can be affected by passing new values to
 
 .. seealso::
 
-    :ref:`execute_multiple` - General information on using the
+    :ref:`tutorial_multiple_parameters` - General information on using the
     :class:`_engine.Connection`
     object to execute statements in such a way as to make
     use of the DBAPI ``.executemany()`` method.
index ec1e1abe187d199f7ed9f5f2290b74a75d13f0e3..34a762a15f2a27435ff87e1bd3c2230d4301de4a 100644 (file)
@@ -1244,7 +1244,7 @@ class CursorResult(Result[_T]):
 
     .. seealso::
 
-        :ref:`coretutorial_selecting` - introductory material for accessing
+        :ref:`tutorial_selecting_data` - introductory material for accessing
         :class:`_engine.CursorResult` and :class:`.Row` objects.
 
     """
index 7c9eacb78c11108cf77a36526850191f80a670a1..06976dd4ba14012773dc50b3a3a13bcf365d825d 100644 (file)
@@ -65,7 +65,7 @@ class Row(BaseRow, Sequence[Any], Generic[_TP]):
 
     .. seealso::
 
-        :ref:`coretutorial_selecting` - includes examples of selecting
+        :ref:`tutorial_selecting_data` - includes examples of selecting
         rows from SELECT statements.
 
     .. versionchanged:: 1.4
index 5558b397c5e3c081ad58893a45fc20988489c819..6dea3677e972cdc9d02b943b412c9a90891537c3 100644 (file)
@@ -75,13 +75,7 @@ class URL(NamedTuple):
     * :attr:`_engine.URL.drivername`: database backend and driver name, such as
       ``postgresql+psycopg2``
     * :attr:`_engine.URL.username`: username string
-    * :attr:`_engine.URL.password`: password string, or object that includes
-      a ``__str__()`` method that produces a password.
-
-      .. note::  A password-producing object will be stringified only
-         **once** per :class:`_engine.Engine` object.  For dynamic password
-         generation per connect, see :ref:`engines_dynamic_tokens`.
-
+    * :attr:`_engine.URL.password`: password string
     * :attr:`_engine.URL.host`: string hostname
     * :attr:`_engine.URL.port`: integer port number
     * :attr:`_engine.URL.database`: string database name
@@ -93,12 +87,57 @@ class URL(NamedTuple):
     """
 
     drivername: str
+    """database backend and driver name, such as
+    ``postgresql+psycopg2``
+
+    """
+
     username: Optional[str]
+    "username string"
+
     password: Optional[str]
+    """password, which is normally a string but may also be any
+    object that has a ``__str__()`` method."""
+
     host: Optional[str]
+    """hostname or IP number.  May also be a data source name for some
+    drivers."""
+
     port: Optional[int]
+    """integer port number"""
+
     database: Optional[str]
+    """database name"""
+
     query: util.immutabledict[str, Union[Tuple[str, ...], str]]
+    """an immutable mapping representing the query string.  contains strings
+       for keys and either strings or tuples of strings for values, e.g.::
+
+            >>> from sqlalchemy.engine import make_url
+            >>> url = make_url("postgresql+psycopg2://user:pass@host/dbname?alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt")
+            >>> url.query
+            immutabledict({'alt_host': ('host1', 'host2'), 'ssl_cipher': '/path/to/crt'})
+
+         To create a mutable copy of this mapping, use the ``dict`` constructor::
+
+            mutable_query_opts = dict(url.query)
+
+       .. seealso::
+
+          :attr:`_engine.URL.normalized_query` - normalizes all values into sequences
+          for consistent processing
+
+          Methods for altering the contents of :attr:`_engine.URL.query`:
+
+          :meth:`_engine.URL.update_query_dict`
+
+          :meth:`_engine.URL.update_query_string`
+
+          :meth:`_engine.URL.update_query_pairs`
+
+          :meth:`_engine.URL.difference_update_query`
+
+    """  # noqa: E501
 
     @classmethod
     def create(
index ff3dcf41744cee54234fe14b0476fd76ab8c0636..8a1b1be32e4650092e4c8787ea2aac7ca73846a2 100644 (file)
@@ -18,6 +18,7 @@ from typing import TypeVar
 
 from . import exc as async_exc
 from ... import util
+from ...engine import Result
 from ...engine.result import _NO_ROW
 from ...engine.result import _R
 from ...engine.result import FilterResult
@@ -31,7 +32,6 @@ from ...util.typing import Literal
 
 if TYPE_CHECKING:
     from ...engine import CursorResult
-    from ...engine import Result
     from ...engine.result import _KeyIndexType
     from ...engine.result import _UniqueFilterType
     from ...engine.result import RMKeyView
index ece6a52be82807aea4846aa76b03ee26cbd77856..e682828650f42f4544228f9411aae2be70e09fd7 100644 (file)
@@ -856,7 +856,7 @@ def relationship(
       :ref:`relationship_config_toplevel` - Full introductory and
       reference documentation for :func:`_orm.relationship`.
 
-      :ref:`orm_tutorial_relationship` - ORM tutorial introduction.
+      :ref:`tutorial_orm_related_objects` - ORM tutorial introduction.
 
     :param argument:
       A mapped class, or actual :class:`_orm.Mapper` instance,
@@ -923,9 +923,6 @@ def relationship(
           :ref:`relationships_many_to_many` - Reference example of "many
           to many".
 
-          :ref:`orm_tutorial_many_to_many` - ORM tutorial introduction to
-          many-to-many relationships.
-
           :ref:`self_referential_many_to_many` - Specifics on using
           many-to-many in a self-referential case.
 
@@ -1029,9 +1026,6 @@ def relationship(
         :ref:`unitofwork_cascades` - Full detail on each of the available
         cascade options.
 
-        :ref:`tutorial_delete_cascade` - Tutorial example describing
-        a delete cascade.
-
     :param cascade_backrefs=False:
       Legacy; this flag is always False.
 
@@ -2095,8 +2089,6 @@ def aliased(
 
         :ref:`orm_queryguide_orm_aliases` - in the :ref:`queryguide_toplevel`
 
-    :ref:`ormtutorial_aliases` - in the legacy :ref:`ormtutorial_toplevel`
-
     :param element: element to be aliased.  Is normally a mapped class,
      but for convenience can also be a :class:`_expression.FromClause`
      element.
index 419891708cdff8a503c2e197a552b1c8fa4b7ce3..596e9109986005ee3608f9305b57583bb7b82126 100644 (file)
@@ -3050,9 +3050,8 @@ class Query(
         :param values: a dictionary with attributes names, or alternatively
          mapped attributes or SQL expressions, as keys, and literal
          values or sql expressions as values.   If :ref:`parameter-ordered
-         mode <updates_order_parameters>` is desired, the values can be
-         passed as a list of 2-tuples;
-         this requires that the
+         mode <tutorial_parameter_ordered_updates>` is desired, the values can
+         be passed as a list of 2-tuples; this requires that the
          :paramref:`~sqlalchemy.sql.expression.update.preserve_parameter_order`
          flag is passed to the :paramref:`.Query.update.update_args` dictionary
          as well.
index deaf5214720bbf7080844d5bbb804c646d9ccef7..630f6898faa93a556283af6139a08677e9b8adf8 100644 (file)
@@ -985,7 +985,7 @@ class Relationship(
             See :meth:`~.Relationship.Comparator.any` for
             a less-performant alternative using EXISTS, or refer
             to :meth:`_query.Query.outerjoin`
-            as well as :ref:`ormtutorial_joins`
+            as well as :ref:`orm_queryguide_joins`
             for more details on constructing outer joins.
 
             kwargs may be ignored by this operator but are required for API
index 926e5257babba23fb4eb1392408519e66748ee60..293d225f98a4fdaf148ce97edeccf21ac8923cb0 100644 (file)
@@ -35,9 +35,6 @@ def insert(table: _DMLTableArgument) -> Insert:
 
     .. seealso::
 
-        :ref:`coretutorial_insert_expressions` - in the
-        :ref:`1.x tutorial <sqlexpression_toplevel>`
-
         :ref:`tutorial_core_insert` - in the :ref:`unified_tutorial`
 
 
@@ -79,9 +76,7 @@ def insert(table: _DMLTableArgument) -> Insert:
 
     .. seealso::
 
-        :ref:`coretutorial_insert_expressions` - SQL Expression Tutorial
-
-        :ref:`inserts_and_updates` - SQL Expression Tutorial
+        :ref:`tutorial_core_insert` - in the :ref:`unified_tutorial`
 
     """
     return Insert(table)
@@ -104,15 +99,6 @@ def update(table: _DMLTableArgument) -> Update:
     :meth:`_expression.TableClause.update` method on
     :class:`_schema.Table`.
 
-    .. seealso::
-
-        :ref:`inserts_and_updates` - in the
-        :ref:`1.x tutorial <sqlexpression_toplevel>`
-
-        :ref:`tutorial_core_update_delete` - in the :ref:`unified_tutorial`
-
-
-
     :param table: A :class:`_schema.Table`
      object representing the database
      table to be updated.
index f6dd9286540784efedcde4cd2b47c90b31f00ab9..8b8f6b010e685c3c61d3eccb944e111e92945fb1 100644 (file)
@@ -605,15 +605,6 @@ def bindparam(
       .. versionchanged:: 1.3 the "expanding" bound parameter feature now
          supports empty lists.
 
-
-      .. seealso::
-
-        :ref:`coretutorial_bind_param`
-
-        :ref:`coretutorial_insert_expressions`
-
-        :func:`.outparam`
-
     :param literal_execute:
       if True, the bound parameter will be rendered in the compile phase
       with a special "POSTCOMPILE" token, and the SQLAlchemy compiler will
@@ -635,6 +626,12 @@ def bindparam(
 
             :ref:`change_4808`.
 
+    .. seealso::
+
+        :ref:`tutorial_sending_parameters` - in the
+        :ref:`unified_tutorial`
+
+
     """
     return BindParameter(
         key,
@@ -827,7 +824,7 @@ def cast(
 
     .. seealso::
 
-        :ref:`coretutorial_casts`
+        :ref:`tutorial_casts`
 
         :func:`.type_coerce` - an alternative to CAST that coerces the type
         on the Python side only, which is often sufficient to generate the
@@ -932,7 +929,7 @@ def column(
 
         :func:`_expression.text`
 
-        :ref:`sqlexpression_literal_column`
+        :ref:`tutorial_select_arbitrary_text`
 
     """
     return ColumnClause(text, type_, is_literal, _selectable)
@@ -1468,8 +1465,7 @@ def text(text: str) -> TextClause:
 
     .. seealso::
 
-        :ref:`sqlexpression_text` - in the Core tutorial
-
+        :ref:`tutorial_select_arbitrary_text`
 
     """
     return TextClause(text)
@@ -1615,7 +1611,7 @@ def type_coerce(
 
     .. seealso::
 
-        :ref:`coretutorial_casts`
+        :ref:`tutorial_casts`
 
         :func:`.cast`
 
index ea824d6229dfec1e84b75580fa69b23e57614e6b..b661d6f4776d0ae362d0699f33e478366c92b14b 100644 (file)
@@ -290,7 +290,7 @@ def lateral(
 
     .. seealso::
 
-        :ref:`lateral_selects` -  overview of usage.
+        :ref:`tutorial_lateral_correlation` -  overview of usage.
 
     """
     return Lateral._factory(selectable, name=name)
@@ -466,8 +466,7 @@ def select(*entities: _ColumnsClauseArgument[Any], **__kw: Any) -> Select[Any]:
 
     .. seealso::
 
-        :ref:`coretutorial_selecting` - Core Tutorial description of
-        :func:`_expression.select`.
+        :ref:`tutorial_selecting_data` - in the :ref:`unified_tutorial`
 
     :param \*entities:
       Entities to SELECT from.  For Core usage, this is typically a series
index e63a34454d68a856b527504e0bea8b31dd9dddde..955eb4109cb97552632c02b12e340792127b0607 100644 (file)
@@ -780,7 +780,7 @@ class ValuesBase(UpdateBase):
 
            .. seealso::
 
-               :ref:`execute_multiple` - an introduction to
+               :ref:`tutorial_multiple_parameters` - an introduction to
                the traditional Core method of multiple parameter set
                invocation for INSERTs and other statements.
 
@@ -1236,16 +1236,6 @@ class DMLWhereBase:
 
         .. seealso::
 
-            **1.x Tutorial Examples**
-
-            :ref:`tutorial_1x_correlated_updates`
-
-            :ref:`multi_table_updates`
-
-            :ref:`multi_table_deletes`
-
-            **2.0 Tutorial Examples**
-
             :ref:`tutorial_correlated_updates`
 
             :ref:`tutorial_update_from`
@@ -1361,7 +1351,7 @@ class Update(DMLWhereBase, ValuesBase):
 
         .. seealso::
 
-           :ref:`updates_order_parameters` - full example of the
+           :ref:`tutorial_parameter_ordered_updates` - full example of the
            :meth:`_expression.Update.ordered_values` method.
 
         .. versionchanged:: 1.4 The :meth:`_expression.Update.ordered_values`
index 6032253c26160252dbae1fc9a887f15ccb0ee583..625e1d94bd2d3587d1e3b67084962cad1d9afc93 100644 (file)
@@ -190,7 +190,7 @@ def literal_column(
 
         :func:`_expression.text`
 
-        :ref:`sqlexpression_literal_column`
+        :ref:`tutorial_select_arbitrary_text`
 
     """
     return ColumnClause(text, type_=type_, is_literal=True)
@@ -1568,7 +1568,7 @@ class ColumnElement(
 
         .. seealso::
 
-            :ref:`coretutorial_casts`
+            :ref:`tutorial_casts`
 
             :func:`_expression.cast`
 
@@ -3198,7 +3198,7 @@ class Cast(WrapsColumnExpression[_T]):
 
     .. seealso::
 
-        :ref:`coretutorial_casts`
+        :ref:`tutorial_casts`
 
         :func:`.cast`
 
index 0cba1a1a8f54ebffd1e9e863ca5667ae2e4ebc37..befd262ecf5de85dd542cfeb582d89f8d64e9350 100644 (file)
@@ -102,7 +102,7 @@ class FunctionElement(Executable, ColumnElement[_T], FromClause, Generative):
 
     .. seealso::
 
-        :ref:`coretutorial_functions` - in the Core tutorial
+        :ref:`tutorial_functions` - in the :ref:`unified_tutorial`
 
         :class:`.Function` - named SQL function.
 
@@ -821,7 +821,7 @@ class _FunctionGenerator:
 
     .. seealso::
 
-        :ref:`coretutorial_functions` - in the Core Tutorial
+        :ref:`tutorial_functions` - in the :ref:`unified_tutorial`
 
         :class:`.Function`
 
index 53dcf51c773db1739215d6bc08c8b66208009fbf..fe19e2d7f689e06b20e2c0fd20135a5f4b4f3ba5 100644 (file)
@@ -105,6 +105,7 @@ and_ = BooleanClauseList.and_
 _T = TypeVar("_T", bound=Any)
 
 if TYPE_CHECKING:
+    import sqlalchemy
     from ._typing import _ColumnExpressionArgument
     from ._typing import _FromClauseArgument
     from ._typing import _JoinTargetArgument
@@ -129,7 +130,6 @@ if TYPE_CHECKING:
     from .cache_key import _CacheKeyTraversalType
     from .compiler import SQLCompiler
     from .dml import Delete
-    from .dml import Insert
     from .dml import Update
     from .elements import KeyedColumnElement
     from .elements import Label
@@ -292,7 +292,7 @@ class Selectable(ReturnsRows):
 
         .. seealso::
 
-            :ref:`lateral_selects` -  overview of usage.
+            :ref:`tutorial_lateral_correlation` -  overview of usage.
 
         """
         return Lateral._construct(self, name)
@@ -751,7 +751,7 @@ class FromClause(roles.AnonymizedFromClauseRole, Selectable):
 
         .. seealso::
 
-            :ref:`core_tutorial_aliases`
+            :ref:`tutorial_using_aliases`
 
             :func:`_expression.alias`
 
@@ -1889,7 +1889,7 @@ class Lateral(FromClauseAlias, LateralFromClause):
 
     .. seealso::
 
-        :ref:`lateral_selects` -  overview of usage.
+        :ref:`tutorial_lateral_correlation` -  overview of usage.
 
     """
 
@@ -2059,7 +2059,7 @@ class CTE(
 
         .. seealso::
 
-            :ref:`core_tutorial_aliases`
+            :ref:`tutorial_using_aliases`
 
             :func:`_expression.alias`
 
@@ -2992,7 +2992,7 @@ class TableClause(roles.DMLTableRole, Immutable, NamedFromClause):
         c.table = self
 
     @util.preload_module("sqlalchemy.sql.dml")
-    def insert(self) -> Insert:
+    def insert(self) -> sqlalchemy.sql.expression.Insert:
         """Generate an :func:`_expression.insert` construct against this
         :class:`_expression.TableClause`.
 
@@ -3176,7 +3176,7 @@ class Values(Generative, LateralFromClause):
 
         .. seealso::
 
-            :ref:`core_tutorial_aliases`
+            :ref:`tutorial_using_aliases`
 
             :func:`_expression.alias`
 
@@ -3458,8 +3458,6 @@ class SelectBase(
 
             :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial
 
-            :ref:`scalar_selects` - in the 1.x tutorial
-
         """
         if self._label_style is not LABEL_STYLE_NONE:
             self = self.set_label_style(LABEL_STYLE_NONE)
@@ -3487,7 +3485,7 @@ class SelectBase(
 
         .. seealso::
 
-            :ref:`lateral_selects` -  overview of usage.
+            :ref:`tutorial_lateral_correlation` -  overview of usage.
 
         """
         return Lateral._factory(self, name)
@@ -4966,8 +4964,6 @@ class Select(
 
         :func:`_sql.select`
 
-        :ref:`coretutorial_selecting` - in the 1.x tutorial
-
         :ref:`tutorial_selecting_data` - in the 2.0 tutorial
 
     """
@@ -6018,7 +6014,7 @@ class Select(
 
             :meth:`_expression.Select.correlate_except`
 
-            :ref:`correlated_subqueries`
+            :ref:`tutorial_scalar_subquery`
 
         """
 
@@ -6068,7 +6064,7 @@ class Select(
 
             :meth:`_expression.Select.correlate`
 
-            :ref:`correlated_subqueries`
+            :ref:`tutorial_scalar_subquery`
 
         """
 
@@ -6341,8 +6337,6 @@ class ScalarSelect(
 
         :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial
 
-        :ref:`scalar_selects` - in the 1.x tutorial
-
     """
 
     _traverse_internals: _TraverseInternalsType = [
@@ -6440,8 +6434,6 @@ class ScalarSelect(
 
             :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial
 
-            :ref:`correlated_subqueries` - in the 1.x tutorial
-
 
         """
         self.element = cast("Select[Any]", self.element).correlate(
@@ -6479,8 +6471,6 @@ class ScalarSelect(
 
             :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial
 
-            :ref:`correlated_subqueries` - in the 1.x tutorial
-
 
         """