]> 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:22:42 +0000 (12:22 -0400)
other org changes and some sections from old tutorial
ported to new tutorial.

Change-Id: Ic0fba60ec82fff481890887beef9ed0fa271875a
(Cherry-picked and independently modified)

37 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/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/baked.rst
doc/build/orm/extensions/hybrid.rst
doc/build/orm/extensions/mypy.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/data_update.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/row.py
lib/sqlalchemy/engine/url.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/orm/relationships.py
lib/sqlalchemy/orm/util.py
lib/sqlalchemy/sql/dml.py
lib/sqlalchemy/sql/elements.py
lib/sqlalchemy/sql/functions.py
lib/sqlalchemy/sql/selectable.py

index 4d3b84d3b4030e81bd68dcbbebcd0861994635b2..addf624de72e5b49e0b6870d3cf073cf5b9e69a7 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 86c8b1a69b3ccec18a88b037ff7f2f6920cb7a46..64bead3cd1b4a549e20b494e260d366c8f2941e0 100644 (file)
@@ -165,8 +165,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,)]
@@ -2080,11 +2080,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 366f165651ba6917b1ee3ea111f4c8d162e98c5a..5c6fa2e5cbf8614d7b90ee5deba1ffe26a5b5b4e 100644 (file)
@@ -557,6 +557,7 @@ Column, Table, MetaData API
 ---------------------------
 
 .. attribute:: sqlalchemy.schema.BLANK_SCHEMA
+    :noindex:
 
     Symbol indicating that a :class:`_schema.Table` or :class:`.Sequence`
     should have 'None' for its schema, even if the parent
@@ -573,6 +574,7 @@ Column, Table, MetaData API
     .. versionadded:: 1.0.14
 
 .. attribute:: sqlalchemy.schema.RETAIN_SCHEMA
+    :noindex:
 
     Symbol indicating that a :class:`_schema.Table`, :class:`.Sequence`
     or in some cases a :class:`_schema.ForeignKey` object, in situations
index c6ef94a0a7ae2ff5707459575c7a35b028328af3..59223ee7aaae8679e947615712b46662f80a7ca4 100644 (file)
@@ -581,19 +581,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 49dbff71babbc594c0dee090ac9003884b8a66c4..64a6f45f968efd35990c5d2dea4b9061da2206d3 100644 (file)
@@ -77,7 +77,7 @@ construction arguments, are as follows:
 
 .. autoclass:: DOUBLE
     :members: __init__
-
+    :noindex:
 
 .. autoclass:: ENUM
     :members: __init__
index 1c4b982e0a79330d294abfae6e70f5a56f64a4d2..d30c03885d5d69aa8096903e372f5fbdedfcbf15 100644 (file)
@@ -51,6 +51,7 @@ construction arguments, are as follows:
 
 .. autoclass:: DOUBLE_PRECISION
     :members: __init__
+    :noindex:
 
 
 .. autoclass:: ENUM
index 7c4e3e4d4f878aa6543266d93fd82265348304b7..3c0632af692be901bc09395cecf6aaaedd391c4a 100644 (file)
@@ -33,440 +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_c9bf:
-
-A bind was located via legacy bound metadata, but since future=True is set on this Session, this bind is ignored.
--------------------------------------------------------------------------------------------------------------------
-
-The concept of "bound metadata" is being removed in SQLAlchemy 2.0.  This
-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
-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`::
-
-    engine = create_engine("sqlite://")
-    Session = sessionmaker()
-    metadata_obj = MetaData(bind=engine)
-    Base = declarative_base(metadata=metadata_obj)
-
-    class MyClass(Base):
-        # ...
-
-
-    session = Session()
-    session.add(MyClass())
-    session.commit()
-
-The :class:`_engine.Engine` must instead be associated directly with the
-:class:`_orm.sessionmaker` or :class:`_orm.Session`.  The
-:class:`_schema.MetaData` object should no longer be associated with any
-engine::
-
-
-    engine = create_engine("sqlite://")
-    Session = sessionmaker(engine)
-    Base = declarative_base()
-
-    class MyClass(Base):
-        # ...
-
-
-    session = Session()
-    session.add(MyClass())
-    session.commit()
-
-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_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_s9r1:
-
-Object is being merged into a Session along the backref cascade
----------------------------------------------------------------
-
-This message refers to the "backref cascade" behavior of SQLAlchemy,
-which is described at :ref:`backref_cascade`.   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 will be disabled completely.
-
-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:`backref_cascade` - complete description of the cascade backrefs
-    behavior
-
-    :ref:`change_5150` - background on the change for SQLAlchemy 2.0.
-
-.. _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
 ============================
@@ -636,56 +202,6 @@ method.  When a connection is invalidated, any :class:`_engine.Transaction`
 that was in progress is now in an invalid state, and must be explicitly rolled
 back in order to remove it from the :class:`_engine.Connection`.
 
-.. _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.    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 to be removed in SQLAlchemy 2.0 so that this
-particular programming pattern will no longer be available and this
-error message will no longer occur in Core.
 
 .. _error_dbapi:
 
@@ -780,42 +296,131 @@ cursor is not valid anymore, the transaction is out of sync, etc.
 This error is a :ref:`DBAPI Error <error_dbapi>` and originates from
 the database driver (DBAPI), not SQLAlchemy itself.
 
-The ``InternalError`` is sometimes raised by drivers in the context
-of the database connection being dropped, or not being able to connect
-to the database.   For tips on how to deal with this, see the section
-:ref:`pool_disconnects`.
+The ``InternalError`` is sometimes raised by drivers in the context
+of the database connection being dropped, or not being able to connect
+to the database.   For tips on how to deal with this, see the section
+:ref:`pool_disconnects`.
+
+.. _error_f405:
+
+ProgrammingError
+----------------
+
+Exception raised for programming errors, e.g. table not found or already
+exists, syntax error in the SQL statement, wrong number of parameters
+specified, etc.
+
+This error is a :ref:`DBAPI Error <error_dbapi>` and originates from
+the database driver (DBAPI), not SQLAlchemy itself.
+
+The ``ProgrammingError`` is sometimes raised by drivers in the context
+of the database connection being dropped, or not being able to connect
+to the database.   For tips on how to deal with this, see the section
+:ref:`pool_disconnects`.
+
+.. _error_tw8g:
+
+NotSupportedError
+------------------
+
+Exception raised in case a method or database API was used which is not
+supported by the database, e.g. requesting a .rollback() on a connection that
+does not support transaction or has transactions turned off.
+
+This error is a :ref:`DBAPI Error <error_dbapi>` and originates from
+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.
 
-.. _error_f405:
+For the above two reasons, SQLAlchemy's caching system is **extremely
+conservative** about deciding to cache the SQL corresponding to an object.
 
-ProgrammingError
-----------------
+Assertion attributes for caching
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Exception raised for programming errors, e.g. table not found or already
-exists, syntax error in the SQL statement, wrong number of parameters
-specified, etc.
+The warning is emitted based on the criteria below.  For further detail on
+each, see the section :ref:`faq_new_caching`.
 
-This error is a :ref:`DBAPI Error <error_dbapi>` and originates from
-the database driver (DBAPI), not SQLAlchemy itself.
+* 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.
 
-The ``ProgrammingError`` is sometimes raised by drivers in the context
-of the database connection being dropped, or not being able to connect
-to the database.   For tips on how to deal with this, see the section
-:ref:`pool_disconnects`.
+* 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.
 
-.. _error_tw8g:
+* 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`.
 
-NotSupportedError
-------------------
+.. seealso::
 
-Exception raised in case a method or database API was used which is not
-supported by the database, e.g. requesting a .rollback() on a connection that
-does not support transaction or has transactions turned off.
+    :ref:`sql_caching_logging` - background on observing cache behavior
+    and efficiency
 
-This error is a :ref:`DBAPI Error <error_dbapi>` and originates from
-the database driver (DBAPI), not SQLAlchemy itself.
+    :ref:`faq_new_caching` - in the :ref:`faq_toplevel` section
 
-SQL Expression Language
-=======================
 
 .. _error_l7de:
 
@@ -931,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:
@@ -1038,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:
 
@@ -1090,13 +653,188 @@ therefore requires that :meth:`_expression.SelectBase.subquery` is used::
 
     subq = stmt.subquery()
 
-    new_stmt_1 = select(subq)
+    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
+        }
+
+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
 
-    new_stmt_2 = select(some_table).select_from(some_table.join(subq))
+If we then wanted to use :func:`_orm.contains_eager` to populate the
+``reports_to`` attribute, we refer to the alias::
 
-.. seealso::
+    >>> stmt =select(Employee).join(
+    ...     Employee.reports_to.of_type(manager_alias)
+    ... ).options(
+    ...     contains_eager(Employee.reports_to.of_type(manager_alias))
+    ... )
 
-  :ref:`change_4617`
+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
@@ -1657,3 +1395,269 @@ See :ref:`orm_exceptions_toplevel` for ORM exception classes.
 
 
 
+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.
+-------------------------------------------------------------------------------------------------------------------
+
+The concept of "bound metadata" is present up until SQLAlchemy 1.4; as
+of SQLAlchemy 2.0 it's been removed.
+
+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
+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`::
+
+    engine = create_engine("sqlite://")
+    Session = sessionmaker()
+    metadata_obj = MetaData(bind=engine)
+    Base = declarative_base(metadata=metadata_obj)
+
+    class MyClass(Base):
+        # ...
+
+
+    session = Session()
+    session.add(MyClass())
+    session.commit()
+
+The :class:`_engine.Engine` must instead be associated directly with the
+:class:`_orm.sessionmaker` or :class:`_orm.Session`.  The
+:class:`_schema.MetaData` object should no longer be associated with any
+engine::
+
+
+    engine = create_engine("sqlite://")
+    Session = sessionmaker(engine)
+    Base = declarative_base()
+
+    class MyClass(Base):
+        # ...
+
+
+    session = Session()
+    session.add(MyClass())
+    session.commit()
+
+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_2afi:
+
+This Compiled object is not bound to any Engine or Connection
+-------------------------------------------------------------
+
+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`::
+
+ 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:`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 504c47485b670b65b1e4d91b4084d707fea899cb..27ba5f4ed5c8a394d0a8e8cee2fcaf962574a796 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 dc1336dad00da2d6349936c6e8de75c0f31d22e1..1145a408fadeb6070d7e56f089ff57eed699a248 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 1e6635024146d1580f906184c5370ee0be39b3b9..b9b0002e844b07bb3938c56e4269c150a09a22d8 100644 (file)
@@ -158,7 +158,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 f22e28fa5acf5d5eb395b9553d2a677a891c9f78..b3c21716a2a7e9e7025f3fdb38b80f7385dcbd96 100644 (file)
@@ -475,4 +475,5 @@ API Documentation
 
 .. autoclass:: Result
     :members:
+    :noindex:
 
index 16cdafebcca42d859298ff60efbddbb23648cf8b..962148459370e88b25e82c7d515003f72cd4abe4 100644 (file)
@@ -15,7 +15,7 @@ API Reference
     :members:
 
 .. autoclass:: Comparator
-     
+
 
 .. autodata:: HYBRID_METHOD
 
index 368c151009b456c2875cb5b945719a339bcf5385..0d808f5c8aa2d478ca31628dfd0d643641edcf4b 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
 
@@ -59,7 +59,7 @@ The Mypy plugin depends upon new stubs for SQLAlchemy packaged at
 `sqlalchemy2-stubs <https://pypi.org/project/sqlalchemy2-stubs/>`_.  These
 stubs necessarily fully replace the previous ``sqlalchemy-stubs`` typing
 annotations published by Dropbox, as they occupy the same ``sqlalchemy-stubs``
-namespace as specified by :pep:`561`.  The `Mypy <https://pypi.org/project/mypy/>`_
+namespace as specified by :pep:`561`.  The Mypy_
 package itself is also a dependency.
 
 Both packages may be installed using the "mypy" extras hook using pip::
@@ -595,3 +595,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 9815605b2ce198c0a09ac1c3143c9e1569a61cc9..112ac5a319f4a4ec8d6929118429d445d80d83e7 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 d176087a8838a39482fc5360616852fab3d71792..012206ba7c5ba6fe0af4bf821c919ae6d060d7bc 100644 (file)
@@ -295,6 +295,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
@@ -477,7 +478,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 2f1c021020b935c7d93f488e6fa32f740d1080ea..71b7a06efd6cfcab8f688eccdae172aa041abb10 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 bed901712d5b3abb782fb5f376e94af7dcbc7c84..b747246c042ba1879a76189316ecf45a751f616d 100644 (file)
@@ -708,7 +708,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 90180154b7d7a6a8e6a6425d3f91394c69b58feb..63aeb51a0892dd60a650ccf89a8c71cb4631df18 100644 (file)
@@ -8,6 +8,7 @@
 
 .. rst-class:: core-header
 
+
 .. _tutorial_core_insert:
 
 Inserting Rows with Core
index c8fac288e629319f24307d7f958b0f485c61f787..78a0f1746184ff76d4dad570baf9641179bc80f8 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,78 @@ 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 8813dda9889a9fc40fa6c5a151fba21de07acdb9..1091bccf64538a993912e6b52befde35fc013a8c 100644 (file)
@@ -175,6 +175,8 @@ order to refer to additional tables::
   WHERE user_account.id = address.user_id AND address.email_address = %s
 
 
+.. _tutorial_parameter_ordered_updates:
+
 Parameter Ordered Updates
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
index a9dff8f38515fa3fbd88ad6c6c662554c307cc42..d7ac0b87c42feb66946c30910a87346de88a1f62 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 e4d89b2dce24cd9ab846991cbbf8815010e6081b..26af6eb799f4fa8c8a82ebb83b64c55fc2380a44 100644 (file)
@@ -560,7 +560,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 790733cbfdab68b44e4cda0ec3e6b492d9cf4166..0b508fe49def51617a6e5e71a45a3546984654a0 100644 (file)
@@ -97,7 +97,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 f7121a82a1a3962a6962a68c8b36dce2b4d9e88d..663a17b2aae35b93882604cc5712318c8741f982 100644 (file)
@@ -208,7 +208,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()``
@@ -284,7 +284,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 c76632db1c0d9490a6b2df3ed42041f7e8700646..e6c32977391d7ec10b8490537c18c7659c984de0 100644 (file)
@@ -191,7 +191,7 @@ class Row(BaseRow, collections_abc.Sequence):
 
     .. seealso::
 
-        :ref:`coretutorial_selecting` - includes examples of selecting
+        :ref:`tutorial_selecting_data` - includes examples of selecting
         rows from SELECT statements.
 
         :class:`.LegacyRow` - Compatibility interface introduced in SQLAlchemy
index 5c46676f77c724f27cfd5c0af8123c6d4f698590..db971c2ab50af94d15b3c4fd1f3f7a73baa8728a 100644 (file)
@@ -67,13 +67,7 @@ class URL(
     * :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
index 88910ba0624976741610a3402447f5e6f8fbc500..ba5e448acd338a373f7d09389c884b16daf7b212 100644 (file)
@@ -3239,9 +3239,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 efa6c63f017bef9b8930794438c51bd4eebea60f..f58277e32ea5c45f1d6956dd14032d0cc9dfbbf2 100644 (file)
@@ -212,7 +212,7 @@ class RelationshipProperty(StrategizedProperty):
           :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,
@@ -279,9 +279,6 @@ class RelationshipProperty(StrategizedProperty):
               :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.
 
@@ -392,9 +389,6 @@ class RelationshipProperty(StrategizedProperty):
             :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=True:
           A boolean value indicating if the ``save-update`` cascade should
           operate along an assignment event intercepted by a backref.
@@ -1535,7 +1529,7 @@ class RelationshipProperty(StrategizedProperty):
             See :meth:`~.RelationshipProperty.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 4afcd0fb862e672f1954baefeebbf14625c45929..a5c571ad75d1c4a3d9cbc404ca81f21bf18b0e7a 100644 (file)
@@ -1255,8 +1255,6 @@ def aliased(element, alias=None, name=None, flat=False, adapt_on_names=False):
 
         :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 4f3280373bdef941f8a1abf8fe77d71fb656198c..dea5d6119df376da05b665394de9555351d44ae0 100644 (file)
@@ -703,7 +703,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.
 
@@ -974,9 +974,6 @@ class Insert(ValuesBase):
 
         .. seealso::
 
-            :ref:`coretutorial_insert_expressions` - in the
-            :ref:`1.x tutorial <sqlexpression_toplevel>`
-
             :ref:`tutorial_core_insert` - in the :ref:`unified_tutorial`
 
 
@@ -1018,9 +1015,7 @@ class Insert(ValuesBase):
 
         .. seealso::
 
-            :ref:`coretutorial_insert_expressions` - SQL Expression Tutorial
-
-            :ref:`inserts_and_updates` - SQL Expression Tutorial
+            :ref:`tutorial_core_insert` - in the :ref:`unified_tutorial`
 
         """
         super(Insert, self).__init__(table, values, prefixes)
@@ -1135,16 +1130,6 @@ class DMLWhereBase(object):
 
         .. 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`
@@ -1276,15 +1261,6 @@ class Update(DMLWhereBase, ValuesBase):
         :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.
@@ -1396,7 +1372,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 0462b26482f4fd8448d3839bab870628521dc70e..42ec3e0e7d2ec28913f6d46bd5ee19f973e95523 100644 (file)
@@ -148,7 +148,7 @@ def literal(value, type_=None):
 
 
 def outparam(key, type_=None):
-    """Create an 'OUT' parameter for usage in functions (stored procedures),
+    r"""Create an 'OUT' parameter for usage in functions (stored procedures),
     for databases which support them.
 
     The ``outparam`` can be used like a regular function parameter.
@@ -998,7 +998,7 @@ class ColumnElement(
 
         .. seealso::
 
-            :ref:`coretutorial_casts`
+            :ref:`tutorial_casts`
 
             :func:`_expression.cast`
 
@@ -1465,15 +1465,6 @@ class BindParameter(roles.InElementRole, ColumnElement):
           .. 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
@@ -1495,6 +1486,11 @@ class BindParameter(roles.InElementRole, ColumnElement):
 
                 :ref:`change_4808`.
 
+        .. seealso::
+
+            :ref:`tutorial_sending_parameters` - in the
+            :ref:`unified_tutorial`
+
         """
         if required is NO_ARG:
             required = value is NO_ARG and callable_ is None
@@ -1906,7 +1902,7 @@ class TextClause(
 
         .. seealso::
 
-            :ref:`sqlexpression_text` - in the Core tutorial
+            :ref:`tutorial_select_arbitrary_text`
 
 
         """
@@ -3057,7 +3053,7 @@ class Cast(WrapsColumnExpression, ColumnElement):
 
     .. seealso::
 
-        :ref:`coretutorial_casts`
+        :ref:`tutorial_casts`
 
         :func:`.cast`
 
@@ -3118,7 +3114,7 @@ class Cast(WrapsColumnExpression, ColumnElement):
 
         .. 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
@@ -3239,7 +3235,7 @@ class TypeCoerce(WrapsColumnExpression, ColumnElement):
 
         .. seealso::
 
-            :ref:`coretutorial_casts`
+            :ref:`tutorial_casts`
 
             :func:`.cast`
 
@@ -4881,7 +4877,7 @@ class ColumnClause(
 
             :func:`_expression.text`
 
-            :ref:`sqlexpression_literal_column`
+            :ref:`tutorial_select_arbitrary_text`
 
         """
         self.key = self.name = text
index 584782b28186287db1b0a1d143967fcfa611a6c4..963108d7c4c98a7c9e979ebfa59ff121db55afe4 100644 (file)
@@ -71,7 +71,7 @@ class FunctionElement(Executable, ColumnElement, FromClause, Generative):
 
     .. seealso::
 
-        :ref:`coretutorial_functions` - in the Core tutorial
+        :ref:`tutorial_functions` - in the :ref:`unified_tutorial`
 
         :class:`.Function` - named SQL function.
 
@@ -800,7 +800,7 @@ class _FunctionGenerator(object):
 
     .. seealso::
 
-        :ref:`coretutorial_functions` - in the Core Tutorial
+        :ref:`tutorial_functions` - in the :ref:`unified_tutorial`
 
         :class:`.Function`
 
index 353f37b25400bc25020db921fb2967f2e2e08d28..829f26030c4ca9acbc1efcb611eeb408684e6998 100644 (file)
@@ -170,7 +170,7 @@ class Selectable(ReturnsRows):
 
         .. seealso::
 
-            :ref:`lateral_selects` -  overview of usage.
+            :ref:`tutorial_lateral_correlation` -  overview of usage.
 
         """
         return Lateral._construct(self, name)
@@ -607,7 +607,7 @@ class FromClause(roles.AnonymizedFromClauseRole, Selectable):
 
         .. seealso::
 
-            :ref:`core_tutorial_aliases`
+            :ref:`tutorial_using_aliases`
 
             :func:`_expression.alias`
 
@@ -1920,7 +1920,7 @@ class Lateral(AliasedReturnsRows):
 
     .. seealso::
 
-        :ref:`lateral_selects` -  overview of usage.
+        :ref:`tutorial_lateral_correlation` -  overview of usage.
 
     """
 
@@ -1947,7 +1947,8 @@ class Lateral(AliasedReturnsRows):
 
         .. seealso::
 
-            :ref:`lateral_selects` -  overview of usage.
+            :ref:`tutorial_lateral_correlation` -  overview of usage.
+
 
         """
         return coercions.expect(
@@ -2129,7 +2130,7 @@ class CTE(
 
         .. seealso::
 
-            :ref:`core_tutorial_aliases`
+            :ref:`tutorial_using_aliases`
 
             :func:`_expression.alias`
 
@@ -2964,7 +2965,7 @@ class Values(Generative, FromClause):
 
         .. seealso::
 
-            :ref:`core_tutorial_aliases`
+            :ref:`tutorial_using_aliases`
 
             :func:`_expression.alias`
 
@@ -3201,8 +3202,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)
@@ -3230,7 +3229,7 @@ class SelectBase(
 
         .. seealso::
 
-            :ref:`lateral_selects` -  overview of usage.
+            :ref:`tutorial_lateral_correlation` -  overview of usage.
 
         """
         return Lateral._factory(self, name)
@@ -4848,8 +4847,6 @@ class Select(
 
         :func:`_sql.select`
 
-        :ref:`coretutorial_selecting` - in the 1.x tutorial
-
         :ref:`tutorial_selecting_data` - in the 2.0 tutorial
 
     """
@@ -4956,8 +4953,7 @@ class Select(
 
         .. seealso::
 
-            :ref:`coretutorial_selecting` - Core Tutorial description of
-            :func:`_expression.select`.
+            :ref:`tutorial_selecting_data` - in the :ref:`unified_tutorial`
 
         :param columns:
           A list of :class:`_expression.ColumnElement` or
@@ -6078,7 +6074,7 @@ class Select(
 
             :meth:`_expression.Select.correlate_except`
 
-            :ref:`correlated_subqueries`
+            :ref:`tutorial_scalar_subquery`
 
         """
 
@@ -6116,7 +6112,7 @@ class Select(
 
             :meth:`_expression.Select.correlate`
 
-            :ref:`correlated_subqueries`
+            :ref:`tutorial_scalar_subquery`
 
         """
 
@@ -6559,8 +6555,6 @@ class ScalarSelect(roles.InElementRole, Generative, Grouping):
 
         :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial
 
-        :ref:`scalar_selects` - in the 1.x tutorial
-
     """
 
     _from_objects = []
@@ -6619,8 +6613,6 @@ class ScalarSelect(roles.InElementRole, Generative, Grouping):
 
             :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial
 
-            :ref:`correlated_subqueries` - in the 1.x tutorial
-
 
         """
         self.element = self.element.correlate(*fromclauses)
@@ -6652,8 +6644,6 @@ class ScalarSelect(roles.InElementRole, Generative, Grouping):
 
             :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial
 
-            :ref:`correlated_subqueries` - in the 1.x tutorial
-
 
         """