From: Mike Bayer Date: Sat, 4 Jun 2022 19:53:34 +0000 (-0400) Subject: migrate labels to new tutorial X-Git-Tag: rel_1_4_38~21 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7eba007385a75aa0f29a1466cfbb335aa6abbe8b;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git migrate labels to new tutorial other org changes and some sections from old tutorial ported to new tutorial. Change-Id: Ic0fba60ec82fff481890887beef9ed0fa271875a (Cherry-picked and independently modified) --- diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst index 4d3b84d3b4..addf624de7 100644 --- a/doc/build/changelog/changelog_10.rst +++ b/doc/build/changelog/changelog_10.rst @@ -811,7 +811,7 @@ .. seealso:: - :ref:`updates_order_parameters` + :ref:`tutorial_parameter_ordered_updates` .. change:: :tags: bug, orm diff --git a/doc/build/changelog/migration_11.rst b/doc/build/changelog/migration_11.rst index a2c88ae11d..5c1b842b61 100644 --- a/doc/build/changelog/migration_11.rst +++ b/doc/build/changelog/migration_11.rst @@ -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 `_ DBAPI is now supported. -.. seealso:: - - :ref:`dialect-postgresql-pygresql` The "postgres" module is removed -------------------------------- diff --git a/doc/build/changelog/migration_12.rst b/doc/build/changelog/migration_12.rst index bc1d0739e9..7073660f78 100644 --- a/doc/build/changelog/migration_12.rst +++ b/doc/build/changelog/migration_12.rst @@ -905,7 +905,7 @@ would render as:: .. seealso:: - :ref:`multi_table_deletes` + :ref:`tutorial_multi_table_deletes` :ticket:`959` diff --git a/doc/build/changelog/migration_20.rst b/doc/build/changelog/migration_20.rst index 86c8b1a69b..64bead3cd1 100644 --- a/doc/build/changelog/migration_20.rst +++ b/doc/build/changelog/migration_20.rst @@ -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 ` -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 diff --git a/doc/build/core/metadata.rst b/doc/build/core/metadata.rst index 366f165651..5c6fa2e5cb 100644 --- a/doc/build/core/metadata.rst +++ b/doc/build/core/metadata.rst @@ -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 diff --git a/doc/build/core/pooling.rst b/doc/build/core/pooling.rst index c6ef94a0a7..59223ee7aa 100644 --- a/doc/build/core/pooling.rst +++ b/doc/build/core/pooling.rst @@ -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 diff --git a/doc/build/dialects/mysql.rst b/doc/build/dialects/mysql.rst index 49dbff71ba..64a6f45f96 100644 --- a/doc/build/dialects/mysql.rst +++ b/doc/build/dialects/mysql.rst @@ -77,7 +77,7 @@ construction arguments, are as follows: .. autoclass:: DOUBLE :members: __init__ - + :noindex: .. autoclass:: ENUM :members: __init__ diff --git a/doc/build/dialects/postgresql.rst b/doc/build/dialects/postgresql.rst index 1c4b982e0a..d30c03885d 100644 --- a/doc/build/dialects/postgresql.rst +++ b/doc/build/dialects/postgresql.rst @@ -51,6 +51,7 @@ construction arguments, are as follows: .. autoclass:: DOUBLE_PRECISION :members: __init__ + :noindex: .. autoclass:: ENUM diff --git a/doc/build/errors.rst b/doc/build/errors.rst index 7c4e3e4d4f..3c0632af69 100644 --- a/doc/build/errors.rst +++ b/doc/build/errors.rst @@ -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 `. 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 `. - -.. seealso:: - - :ref:`migration_20_5284` - - :ref:`migration_20_toplevel` - - - -.. _error_b8d9: - -The in SQLAlchemy 2.0 will no longer --------------------------------------------------------------------------------------------- - -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 ` 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 - ` 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 `. 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 ` 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 ` 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 ` 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 ` 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 ` 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 + ` 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 ` 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 `. 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 in SQLAlchemy 2.0 will no longer +-------------------------------------------------------------------------------------------- + +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 `. + +.. 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. + + + diff --git a/doc/build/faq/connections.rst b/doc/build/faq/connections.rst index 504c47485b..27ba5f4ed5 100644 --- a/doc/build/faq/connections.rst +++ b/doc/build/faq/connections.rst @@ -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 ` 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 diff --git a/doc/build/faq/sessions.rst b/doc/build/faq/sessions.rst index dc1336dad0..1145a408fa 100644 --- a/doc/build/faq/sessions.rst +++ b/doc/build/faq/sessions.rst @@ -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. diff --git a/doc/build/glossary.rst b/doc/build/glossary.rst index 1e66350241..b9b0002e84 100644 --- a/doc/build/glossary.rst +++ b/doc/build/glossary.rst @@ -158,7 +158,7 @@ Glossary `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 diff --git a/doc/build/orm/extensions/baked.rst b/doc/build/orm/extensions/baked.rst index f22e28fa5a..b3c21716a2 100644 --- a/doc/build/orm/extensions/baked.rst +++ b/doc/build/orm/extensions/baked.rst @@ -475,4 +475,5 @@ API Documentation .. autoclass:: Result :members: + :noindex: diff --git a/doc/build/orm/extensions/hybrid.rst b/doc/build/orm/extensions/hybrid.rst index 16cdafebcc..9621484593 100644 --- a/doc/build/orm/extensions/hybrid.rst +++ b/doc/build/orm/extensions/hybrid.rst @@ -15,7 +15,7 @@ API Reference :members: .. autoclass:: Comparator - + .. autodata:: HYBRID_METHOD diff --git a/doc/build/orm/extensions/mypy.rst b/doc/build/orm/extensions/mypy.rst index 368c151009..0d808f5c8a 100644 --- a/doc/build/orm/extensions/mypy.rst +++ b/doc/build/orm/extensions/mypy.rst @@ -4,7 +4,7 @@ Mypy / Pep-484 Support for ORM Mappings ======================================== Support for :pep:`484` typing annotations as well as the -`Mypy `_ 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 `_. 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 `_ +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/ diff --git a/doc/build/orm/persistence_techniques.rst b/doc/build/orm/persistence_techniques.rst index 9815605b2c..112ac5a319 100644 --- a/doc/build/orm/persistence_techniques.rst +++ b/doc/build/orm/persistence_techniques.rst @@ -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 diff --git a/doc/build/orm/queryguide.rst b/doc/build/orm/queryguide.rst index d176087a88..012206ba7c 100644 --- a/doc/build/orm/queryguide.rst +++ b/doc/build/orm/queryguide.rst @@ -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 diff --git a/doc/build/orm/relationships.rst b/doc/build/orm/relationships.rst index 8a4fe36a1d..b9111741cc 100644 --- a/doc/build/orm/relationships.rst +++ b/doc/build/orm/relationships.rst @@ -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 diff --git a/doc/build/orm/self_referential.rst b/doc/build/orm/self_referential.rst index 2f1c021020..71b7a06efd 100644 --- a/doc/build/orm/self_referential.rst +++ b/doc/build/orm/self_referential.rst @@ -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: diff --git a/doc/build/orm/session_basics.rst b/doc/build/orm/session_basics.rst index bed901712d..b747246c04 100644 --- a/doc/build/orm/session_basics.rst +++ b/doc/build/orm/session_basics.rst @@ -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 diff --git a/doc/build/tutorial/data_insert.rst b/doc/build/tutorial/data_insert.rst index 90180154b7..63aeb51a08 100644 --- a/doc/build/tutorial/data_insert.rst +++ b/doc/build/tutorial/data_insert.rst @@ -8,6 +8,7 @@ .. rst-class:: core-header + .. _tutorial_core_insert: Inserting Rows with Core diff --git a/doc/build/tutorial/data_select.rst b/doc/build/tutorial/data_select.rst index c8fac288e6..78a0f17461 100644 --- a/doc/build/tutorial/data_select.rst +++ b/doc/build/tutorial/data_select.rst @@ -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. diff --git a/doc/build/tutorial/data_update.rst b/doc/build/tutorial/data_update.rst index 8813dda988..1091bccf64 100644 --- a/doc/build/tutorial/data_update.rst +++ b/doc/build/tutorial/data_update.rst @@ -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 ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/build/tutorial/dbapi_transactions.rst b/doc/build/tutorial/dbapi_transactions.rst index a9dff8f385..d7ac0b87c4 100644 --- a/doc/build/tutorial/dbapi_transactions.rst +++ b/doc/build/tutorial/dbapi_transactions.rst @@ -179,6 +179,7 @@ purposes. .. rst-class:: core-header +.. _tutorial_statement_execution: Basics of Statement Execution ----------------------------- diff --git a/doc/build/tutorial/orm_related_objects.rst b/doc/build/tutorial/orm_related_objects.rst index 59691cf818..2eacc39e36 100644 --- a/doc/build/tutorial/orm_related_objects.rst +++ b/doc/build/tutorial/orm_related_objects.rst @@ -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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py index e4d89b2dce..26af6eb799 100644 --- a/lib/sqlalchemy/dialects/mysql/base.py +++ b/lib/sqlalchemy/dialects/mysql/base.py @@ -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: diff --git a/lib/sqlalchemy/dialects/mysql/dml.py b/lib/sqlalchemy/dialects/mysql/dml.py index 790733cbfd..0b508fe49d 100644 --- a/lib/sqlalchemy/dialects/mysql/dml.py +++ b/lib/sqlalchemy/dialects/mysql/dml.py @@ -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")]) diff --git a/lib/sqlalchemy/dialects/postgresql/psycopg2.py b/lib/sqlalchemy/dialects/postgresql/psycopg2.py index f7121a82a1..663a17b2aa 100644 --- a/lib/sqlalchemy/dialects/postgresql/psycopg2.py +++ b/lib/sqlalchemy/dialects/postgresql/psycopg2.py @@ -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 `. The ORM also uses this mode internally whenever +sets `. 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. diff --git a/lib/sqlalchemy/engine/row.py b/lib/sqlalchemy/engine/row.py index c76632db1c..e6c3297739 100644 --- a/lib/sqlalchemy/engine/row.py +++ b/lib/sqlalchemy/engine/row.py @@ -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 diff --git a/lib/sqlalchemy/engine/url.py b/lib/sqlalchemy/engine/url.py index 5c46676f77..db971c2ab5 100644 --- a/lib/sqlalchemy/engine/url.py +++ b/lib/sqlalchemy/engine/url.py @@ -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 diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 88910ba062..ba5e448acd 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -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 ` is desired, the values can be - passed as a list of 2-tuples; - this requires that the + mode ` 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. diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py index efa6c63f01..f58277e32e 100644 --- a/lib/sqlalchemy/orm/relationships.py +++ b/lib/sqlalchemy/orm/relationships.py @@ -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 diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py index 4afcd0fb86..a5c571ad75 100644 --- a/lib/sqlalchemy/orm/util.py +++ b/lib/sqlalchemy/orm/util.py @@ -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. diff --git a/lib/sqlalchemy/sql/dml.py b/lib/sqlalchemy/sql/dml.py index 4f3280373b..dea5d6119d 100644 --- a/lib/sqlalchemy/sql/dml.py +++ b/lib/sqlalchemy/sql/dml.py @@ -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 ` - :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 ` - - :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` diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index 0462b26482..42ec3e0e7d 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -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 diff --git a/lib/sqlalchemy/sql/functions.py b/lib/sqlalchemy/sql/functions.py index 584782b281..963108d7c4 100644 --- a/lib/sqlalchemy/sql/functions.py +++ b/lib/sqlalchemy/sql/functions.py @@ -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` diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py index 353f37b254..829f26030c 100644 --- a/lib/sqlalchemy/sql/selectable.py +++ b/lib/sqlalchemy/sql/selectable.py @@ -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 - """