]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
remove 2.0-removed Query elements
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 30 Dec 2021 00:26:08 +0000 (19:26 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 1 Jan 2022 16:28:24 +0000 (11:28 -0500)
* :meth:`_orm.Query.join` no longer accepts the "aliased" and
"from_joinpoint" arguments

* :meth:`_orm.Query.join` no longer accepts chains of multiple join
targets in one method call.

* ``Query.from_self()`` and ``Query.with_polymorphic()``
are removed.

Change-Id: I534d04b53a538a4fc374966eb2bc8eb98a16497d
References: #7257

17 files changed:
doc/build/changelog/unreleased_20/7257.rst
lib/sqlalchemy/ext/baked.py
lib/sqlalchemy/orm/context.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/orm/util.py
lib/sqlalchemy/sql/coercions.py
lib/sqlalchemy/sql/roles.py
lib/sqlalchemy/sql/selectable.py
test/base/test_warnings.py
test/ext/test_baked.py
test/orm/inheritance/test_deprecations.py
test/orm/inheritance/test_polymorphic_rel.py
test/orm/inheritance/test_relationship.py
test/orm/inheritance/test_single.py
test/orm/test_deprecations.py
test/orm/test_joins.py
test/orm/test_query.py

index 3600c3321b32ea9785793a8efc7f81135972bbe1..6a3e4ffda4ec6a383764220969bfee030c578283 100644 (file)
       relationship names; the long-documented approach of using
       ``Class.attrname`` for join targets is now standard.
 
+    * :meth:`_orm.Query.join` no longer accepts the "aliased" and
+      "from_joinpoint" arguments
+
+    * :meth:`_orm.Query.join` no longer accepts chains of multiple join
+      targets in one method call.
+
+    * ``Query.from_self()``, ``Query.select_entity_from()`` and
+      ``Query.with_polymorphic()`` are removed.
+
     * Loader options no longer accept strings for attribute names.  The
       long-documented approach of using ``Class.attrname`` for loader option
       targets is now standard.
index a2db9dbec5188b558ab6f8f848f72b682987dfa8..a609482b3c0a86442ac496c1a931c2e9ab2410fd 100644 (file)
@@ -432,7 +432,7 @@ class Result:
         """
 
         col = func.count(literal_column("*"))
-        bq = self.bq.with_criteria(lambda q: q._from_self(col))
+        bq = self.bq.with_criteria(lambda q: q._legacy_from_self(col))
         return bq.for_session(self.session).params(self._params).scalar()
 
     def scalar(self):
index e59bc487153e2b79606d1cca23632ad1a104e3ae..7c2d7295486794675a9546986ed6df00710c65e0 100644 (file)
@@ -27,7 +27,6 @@ from ..sql import expression
 from ..sql import roles
 from ..sql import util as sql_util
 from ..sql import visitors
-from ..sql.base import _entity_namespace_key
 from ..sql.base import _select_iterables
 from ..sql.base import CacheableOptions
 from ..sql.base import CompileState
@@ -36,7 +35,6 @@ from ..sql.selectable import LABEL_STYLE_DISAMBIGUATE_ONLY
 from ..sql.selectable import LABEL_STYLE_NONE
 from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL
 from ..sql.selectable import SelectState
-from ..sql.visitors import ExtendedInternalTraversal
 from ..sql.visitors import InternalTraversal
 
 _path_registry = PathRegistry.root
@@ -156,23 +154,14 @@ _orm_load_exec_options = util.immutabledict(
 
 
 class ORMCompileState(CompileState):
-    # note this is a dictionary, but the
-    # default_compile_options._with_polymorphic_adapt_map is a tuple
-    _with_polymorphic_adapt_map = _EMPTY_DICT
-
     class default_compile_options(CacheableOptions):
         _cache_key_traversal = [
             ("_use_legacy_query_style", InternalTraversal.dp_boolean),
             ("_for_statement", InternalTraversal.dp_boolean),
             ("_bake_ok", InternalTraversal.dp_boolean),
-            (
-                "_with_polymorphic_adapt_map",
-                ExtendedInternalTraversal.dp_has_cache_key_tuples,
-            ),
             ("_current_path", InternalTraversal.dp_has_cache_key),
             ("_enable_single_crit", InternalTraversal.dp_boolean),
             ("_enable_eagerloads", InternalTraversal.dp_boolean),
-            ("_orm_only_from_obj_alias", InternalTraversal.dp_boolean),
             ("_only_load_props", InternalTraversal.dp_plain_obj),
             ("_set_base_alias", InternalTraversal.dp_boolean),
             ("_for_refresh_state", InternalTraversal.dp_boolean),
@@ -192,11 +181,9 @@ class ORMCompileState(CompileState):
         _for_statement = False
 
         _bake_ok = True
-        _with_polymorphic_adapt_map = ()
         _current_path = _path_registry
         _enable_single_crit = True
         _enable_eagerloads = True
-        _orm_only_from_obj_alias = True
         _only_load_props = None
         _set_base_alias = False
         _for_refresh_state = False
@@ -391,7 +378,6 @@ class ORMCompileState(CompileState):
 
 @sql.base.CompileState.plugin_for("orm", "orm_from_statement")
 class ORMFromStatementCompileState(ORMCompileState):
-    _aliased_generations = util.immutabledict()
     _from_obj_alias = None
     _has_mapper_entities = False
 
@@ -524,7 +510,7 @@ class ORMFromStatementCompileState(ORMCompileState):
 
 @sql.base.CompileState.plugin_for("orm", "select")
 class ORMSelectCompileState(ORMCompileState, SelectState):
-    _joinpath = _joinpoint = _EMPTY_DICT
+    _already_joined_edges = ()
 
     _memoized_entities = _EMPTY_DICT
 
@@ -580,17 +566,9 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
 
         self._entities = []
         self._primary_entity = None
-        self._aliased_generations = {}
         self._polymorphic_adapters = {}
         self._no_yield_pers = set()
 
-        # legacy: only for query.with_polymorphic()
-        if select_statement._compile_options._with_polymorphic_adapt_map:
-            self._with_polymorphic_adapt_map = dict(
-                select_statement._compile_options._with_polymorphic_adapt_map
-            )
-            self._setup_with_polymorphics()
-
         self.compile_options = select_statement._compile_options
 
         if not toplevel:
@@ -735,18 +713,10 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
                     memoized_entities._setup_joins,
                     self._memoized_entities[memoized_entities],
                 )
-            if memoized_entities._legacy_setup_joins:
-                self._legacy_join(
-                    memoized_entities._legacy_setup_joins,
-                    self._memoized_entities[memoized_entities],
-                )
 
         if query._setup_joins:
             self._join(query._setup_joins, self._entities)
 
-        if query._legacy_setup_joins:
-            self._legacy_join(query._legacy_setup_joins, self._entities)
-
         current_adapter = self._get_current_adapter()
 
         if query._where_criteria:
@@ -876,19 +846,8 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
 
         self._entities = []
         self._primary_entity = None
-        self._aliased_generations = {}
         self._polymorphic_adapters = {}
 
-        compile_options = cls.default_compile_options.safe_merge(
-            query._compile_options
-        )
-        # legacy: only for query.with_polymorphic()
-        if compile_options._with_polymorphic_adapt_map:
-            self._with_polymorphic_adapt_map = dict(
-                compile_options._with_polymorphic_adapt_map
-            )
-            self._setup_with_polymorphics()
-
         self._label_convention = self._column_naming_convention(
             query._label_style, legacy
         )
@@ -904,15 +863,7 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
     def determine_last_joined_entity(cls, statement):
         setup_joins = statement._setup_joins
 
-        if not setup_joins:
-            return None
-
-        (target, onclause, from_, flags) = setup_joins[-1]
-
-        if isinstance(target, interfaces.PropComparator):
-            return target.entity
-        else:
-            return target
+        return _determine_last_joined_entity(setup_joins, None)
 
     @classmethod
     def all_selected_columns(cls, statement):
@@ -966,11 +917,6 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
         )
         return stmt
 
-    def _setup_with_polymorphics(self):
-        # legacy: only for query.with_polymorphic()
-        for ext_info, wp in self._with_polymorphic_adapt_map.items():
-            self._mapper_loads_polymorphically_with(ext_info, wp._adapter)
-
     def _set_select_from_alias(self):
 
         query = self.select_statement  # query
@@ -1271,19 +1217,6 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
         if alias:
             return alias.adapt_clause(element)
 
-    def _adapt_aliased_generation(self, element):
-        # this is crazy logic that I look forward to blowing away
-        # when aliased=True is gone :)
-        if "aliased_generation" in element._annotations:
-            for adapter in self._aliased_generations.get(
-                element._annotations["aliased_generation"], ()
-            ):
-                replaced_elem = adapter.replace(element)
-                if replaced_elem is not None:
-                    return replaced_elem
-
-        return None
-
     def _adapt_col_list(self, cols, current_adapter):
         if current_adapter:
             return [current_adapter(o, True) for o in cols]
@@ -1306,19 +1239,11 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
             # to all SQL constructs.
             adapters.append(
                 (
-                    False
-                    if self.compile_options._orm_only_from_obj_alias
-                    else True,
+                    True,
                     self._from_obj_alias.replace,
                 )
             )
 
-        # vvvvvvvvvvvvvvv legacy vvvvvvvvvvvvvvvvvv
-        # this can totally go away when we remove join(..., aliased=True)
-        if self._aliased_generations:
-            adapters.append((False, self._adapt_aliased_generation))
-        # ^^^^^^^^^^^^^ legacy ^^^^^^^^^^^^^^^^^^^^^
-
         # this was *hopefully* the only adapter we were going to need
         # going forward...however, we unfortunately need _from_obj_alias
         # for query.union(), which we can't drop
@@ -1351,29 +1276,18 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
         for (right, onclause, from_, flags) in args:
             isouter = flags["isouter"]
             full = flags["full"]
-            # maybe?
-            self._reset_joinpoint()
 
             right = inspect(right)
             if onclause is not None:
                 onclause = inspect(onclause)
 
-            if onclause is None and isinstance(
-                right, interfaces.PropComparator
-            ):
-                # determine onclause/right_entity.  still need to think
-                # about how to best organize this since we are getting:
-                #
-                #
-                # q.join(Entity, Parent.property)
-                # q.join(Parent.property)
-                # q.join(Parent.property.of_type(Entity))
-                # q.join(some_table)
-                # q.join(some_table, some_parent.c.id==some_table.c.parent_id)
-                #
-                # is this still too many choices?  how do we handle this
-                # when sometimes "right" is implied and sometimes not?
-                #
+            if isinstance(right, interfaces.PropComparator):
+                if onclause is not None:
+                    raise sa_exc.InvalidRequestError(
+                        "No 'on clause' argument may be passed when joining "
+                        "to a relationship path as a target"
+                    )
+
                 onclause = right
                 right = None
             elif "parententity" in right._annotations:
@@ -1421,8 +1335,10 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
                 if not isinstance(onclause, attributes.QueryableAttribute):
                     onclause = prop
 
-                # TODO: this is where "check for path already present"
-                # would occur. see if this still applies?
+                # check for this path already present.  don't render in that
+                # case.
+                if (left, right, prop.key) in self._already_joined_edges:
+                    continue
 
                 if from_ is not None:
                     if (
@@ -1454,165 +1370,10 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
                 right,
                 onclause,
                 prop,
-                False,
-                False,
                 isouter,
                 full,
             )
 
-    def _legacy_join(self, args, entities_collection):
-        """consumes arguments from join() or outerjoin(), places them into a
-        consistent format with which to form the actual JOIN constructs.
-
-        """
-        for (right, onclause, left, flags) in args:
-
-            outerjoin = flags["isouter"]
-            create_aliases = flags["aliased"]
-            from_joinpoint = flags["from_joinpoint"]
-            full = flags["full"]
-            aliased_generation = flags["aliased_generation"]
-
-            # do a quick inspect to accommodate for a lambda
-            if right is not None and not isinstance(right, str):
-                right = inspect(right)
-            if onclause is not None and not isinstance(onclause, str):
-                onclause = inspect(onclause)
-
-            # legacy vvvvvvvvvvvvvvvvvvvvvvvvvv
-            if not from_joinpoint:
-                self._reset_joinpoint()
-            else:
-                prev_aliased_generation = self._joinpoint.get(
-                    "aliased_generation", None
-                )
-                if not aliased_generation:
-                    aliased_generation = prev_aliased_generation
-                elif prev_aliased_generation:
-                    self._aliased_generations[
-                        aliased_generation
-                    ] = self._aliased_generations.get(
-                        prev_aliased_generation, ()
-                    )
-            # legacy ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-            if (
-                isinstance(right, interfaces.PropComparator)
-                and onclause is None
-            ):
-                onclause = right
-                right = None
-            elif "parententity" in right._annotations:
-                right = right._annotations["parententity"]
-
-            if onclause is None:
-                if not right.is_selectable and not hasattr(right, "mapper"):
-                    raise sa_exc.ArgumentError(
-                        "Expected mapped entity or "
-                        "selectable/table as join target"
-                    )
-
-            if isinstance(onclause, interfaces.PropComparator):
-                of_type = getattr(onclause, "_of_type", None)
-            else:
-                of_type = None
-
-            if isinstance(onclause, str):
-                raise sa_exc.ArgumentError(
-                    "ORM mapped attributes as join targets must be "
-                    "stated the attribute itself, not string name"
-                )
-
-            # legacy vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
-            # check for q.join(Class.propname, from_joinpoint=True)
-            # and Class corresponds at the mapper level to the current
-            # joinpoint.  this match intentionally looks for a non-aliased
-            # class-bound descriptor as the onclause and if it matches the
-            # current joinpoint at the mapper level, it's used.  This
-            # is a very old use case that is intended to make it easier
-            # to work with the aliased=True flag, which is also something
-            # that probably shouldn't exist on join() due to its high
-            # complexity/usefulness ratio
-            if from_joinpoint and isinstance(
-                onclause, interfaces.PropComparator
-            ):
-                jp0 = self._joinpoint_zero()
-                info = inspect(jp0)
-
-                if getattr(info, "mapper", None) is onclause._parententity:
-                    onclause = _entity_namespace_key(info, onclause.key)
-            # legacy ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-            if isinstance(onclause, interfaces.PropComparator):
-                # descriptor/property given (or determined); this tells us
-                # explicitly what the expected "left" side of the join is.
-                if right is None:
-                    if of_type:
-                        right = of_type
-                    else:
-                        right = onclause.property
-
-                        try:
-                            right = right.entity
-                        except AttributeError as err:
-                            raise sa_exc.ArgumentError(
-                                "Join target %s does not refer to a "
-                                "mapped entity" % right
-                            ) from err
-
-                left = onclause._parententity
-
-                alias = self._polymorphic_adapters.get(left, None)
-
-                # could be None or could be ColumnAdapter also
-                if isinstance(alias, ORMAdapter) and alias.mapper.isa(left):
-                    left = alias.aliased_class
-                    onclause = getattr(left, onclause.key)
-
-                prop = onclause.property
-                if not isinstance(onclause, attributes.QueryableAttribute):
-                    onclause = prop
-
-                if not create_aliases:
-                    # check for this path already present.
-                    # don't render in that case.
-                    edge = (left, right, prop.key)
-                    if edge in self._joinpoint:
-                        # The child's prev reference might be stale --
-                        # it could point to a parent older than the
-                        # current joinpoint.  If this is the case,
-                        # then we need to update it and then fix the
-                        # tree's spine with _update_joinpoint.  Copy
-                        # and then mutate the child, which might be
-                        # shared by a different query object.
-                        jp = self._joinpoint[edge].copy()
-                        jp["prev"] = (edge, self._joinpoint)
-                        self._update_joinpoint(jp)
-
-                        continue
-
-            else:
-                # no descriptor/property given; we will need to figure out
-                # what the effective "left" side is
-                prop = left = None
-
-            # figure out the final "left" and "right" sides and create an
-            # ORMJoin to add to our _from_obj tuple
-            self._join_left_to_right(
-                entities_collection,
-                left,
-                right,
-                onclause,
-                prop,
-                create_aliases,
-                aliased_generation,
-                outerjoin,
-                full,
-            )
-
-    def _joinpoint_zero(self):
-        return self._joinpoint.get("_joinpoint_entity", self._entity_zero())
-
     def _join_left_to_right(
         self,
         entities_collection,
@@ -1620,8 +1381,6 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
         right,
         onclause,
         prop,
-        create_aliases,
-        aliased_generation,
         outerjoin,
         full,
     ):
@@ -1653,7 +1412,7 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
                 use_entity_index,
             ) = self._join_place_explicit_left_side(entities_collection, left)
 
-        if left is right and not create_aliases:
+        if left is right:
             raise sa_exc.InvalidRequestError(
                 "Can't construct a join from %s to %s, they "
                 "are the same entity" % (left, right)
@@ -1663,7 +1422,7 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
         # a lot of things can be wrong with it.  handle all that and
         # get back the new effective "right" side
         r_info, right, onclause = self._join_check_and_adapt_right_side(
-            left, right, onclause, prop, create_aliases, aliased_generation
+            left, right, onclause, prop
         )
 
         if not r_info.is_selectable:
@@ -1886,9 +1645,7 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
 
         return replace_from_obj_index, use_entity_index
 
-    def _join_check_and_adapt_right_side(
-        self, left, right, onclause, prop, create_aliases, aliased_generation
-    ):
+    def _join_check_and_adapt_right_side(self, left, right, onclause, prop):
         """transform the "right" side of the join as well as the onclause
         according to polymorphic mapping translations, aliasing on the query
         or on the join, special cases where the right and left side have
@@ -1900,26 +1657,24 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
         r_info = inspect(right)
 
         overlap = False
-        if not create_aliases:
-            right_mapper = getattr(r_info, "mapper", None)
-            # if the target is a joined inheritance mapping,
-            # be more liberal about auto-aliasing.
-            if right_mapper and (
-                right_mapper.with_polymorphic
-                or isinstance(right_mapper.persist_selectable, expression.Join)
-            ):
-                for from_obj in self.from_clauses or [l_info.selectable]:
-                    if sql_util.selectables_overlap(
-                        l_info.selectable, from_obj
-                    ) and sql_util.selectables_overlap(
-                        from_obj, r_info.selectable
-                    ):
-                        overlap = True
-                        break
 
-        if (
-            overlap or not create_aliases
-        ) and l_info.selectable is r_info.selectable:
+        right_mapper = getattr(r_info, "mapper", None)
+        # if the target is a joined inheritance mapping,
+        # be more liberal about auto-aliasing.
+        if right_mapper and (
+            right_mapper.with_polymorphic
+            or isinstance(right_mapper.persist_selectable, expression.Join)
+        ):
+            for from_obj in self.from_clauses or [l_info.selectable]:
+                if sql_util.selectables_overlap(
+                    l_info.selectable, from_obj
+                ) and sql_util.selectables_overlap(
+                    from_obj, r_info.selectable
+                ):
+                    overlap = True
+                    break
+
+        if overlap and l_info.selectable is r_info.selectable:
             raise sa_exc.InvalidRequestError(
                 "Can't join table/selectable '%s' to itself"
                 % l_info.selectable
@@ -2002,21 +1757,12 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
                     code="xaj1",
                 )
 
-            elif create_aliases:
-                # it *could* work, but it doesn't right now and I'd rather
-                # get rid of aliased=True completely
-                raise sa_exc.InvalidRequestError(
-                    "The aliased=True parameter on query.join() only works "
-                    "with an ORM entity, not a plain selectable, as the "
-                    "target."
-                )
-
         # test for overlap:
         # orm/inheritance/relationships.py
         # SelfReferentialM2MTest
         aliased_entity = right_mapper and not right_is_aliased and overlap
 
-        if not need_adapter and (create_aliases or aliased_entity):
+        if not need_adapter and aliased_entity:
             # there are a few places in the ORM that automatic aliasing
             # is still desirable, and can't be automatic with a Core
             # only approach.  For illustrations of "overlaps" see
@@ -2026,17 +1772,15 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
             right = aliased(right, flat=True)
             need_adapter = True
 
-            if not create_aliases:
-                util.warn(
-                    "An alias is being generated automatically against "
-                    "joined entity %s due to overlapping tables.  This is a "
-                    "legacy pattern which may be "
-                    "deprecated in a later release.  Use the "
-                    "aliased(<entity>, flat=True) "
-                    "construct explicitly, see the linked example."
-                    % right_mapper,
-                    code="xaj2",
-                )
+            util.warn(
+                "An alias is being generated automatically against "
+                "joined entity %s due to overlapping tables.  This is a "
+                "legacy pattern which may be "
+                "deprecated in a later release.  Use the "
+                "aliased(<entity>, flat=True) "
+                "construct explicitly, see the linked example." % right_mapper,
+                code="xaj2",
+            )
 
         if need_adapter:
             assert right_mapper
@@ -2049,13 +1793,7 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
             # which is intended to wrap a the right side in a subquery,
             # ensure that columns retrieved from this target in the result
             # set are also adapted.
-            if not create_aliases:
-                self._mapper_loads_polymorphically_with(right_mapper, adapter)
-            elif aliased_generation:
-                adapter._debug = True
-                self._aliased_generations[aliased_generation] = (
-                    adapter,
-                ) + self._aliased_generations.get(aliased_generation, ())
+            self._mapper_loads_polymorphically_with(right_mapper, adapter)
         elif (
             not r_info.is_clause_element
             and not right_is_aliased
@@ -2090,37 +1828,11 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
 
         # if joining on a MapperProperty path,
         # track the path to prevent redundant joins
-        if not create_aliases and prop:
-            self._update_joinpoint(
-                {
-                    "_joinpoint_entity": right,
-                    "prev": ((left, right, prop.key), self._joinpoint),
-                    "aliased_generation": aliased_generation,
-                }
-            )
-        else:
-            self._joinpoint = {
-                "_joinpoint_entity": right,
-                "aliased_generation": aliased_generation,
-            }
+        if prop:
+            self._already_joined_edges += ((left, right, prop.key),)
 
         return inspect(right), right, onclause
 
-    def _update_joinpoint(self, jp):
-        self._joinpoint = jp
-        # copy backwards to the root of the _joinpath
-        # dict, so that no existing dict in the path is mutated
-        while "prev" in jp:
-            f, prev = jp["prev"]
-            prev = dict(prev)
-            prev[f] = jp.copy()
-            jp["prev"] = (f, prev)
-            jp = prev
-        self._joinpath = jp
-
-    def _reset_joinpoint(self):
-        self._joinpoint = self._joinpath
-
     @property
     def _select_args(self):
         return {
@@ -2249,7 +1961,7 @@ def _column_descriptions(
 
 def _legacy_filter_by_entity_zero(query_or_augmented_select):
     self = query_or_augmented_select
-    if self._legacy_setup_joins:
+    if self._setup_joins:
         _last_joined_entity = self._last_joined_entity
         if _last_joined_entity is not None:
             return _last_joined_entity
@@ -2277,71 +1989,16 @@ def _entity_from_pre_ent_zero(query_or_augmented_select):
         return ent
 
 
-def _legacy_determine_last_joined_entity(setup_joins, entity_zero):
-    """given the legacy_setup_joins collection at a point in time,
-    figure out what the "filter by entity" would be in terms
-    of those joins.
-
-    in 2.0 this logic should hopefully be much simpler as there will
-    be far fewer ways to specify joins with the ORM
-
-    """
-
+def _determine_last_joined_entity(setup_joins, entity_zero=None):
     if not setup_joins:
-        return entity_zero
-
-    # CAN BE REMOVED IN 2.0:
-    # 1. from_joinpoint
-    # 2. aliased_generation
-    # 3. aliased
-    # 4. any treating of prop as str
-    # 5. tuple madness
-    # 6. won't need recursive call anymore without #4
-    # 7. therefore can pass in just the last setup_joins record,
-    #    don't need entity_zero
-
-    (right, onclause, left_, flags) = setup_joins[-1]
-
-    from_joinpoint = flags["from_joinpoint"]
-
-    if onclause is None and isinstance(
-        right, (str, interfaces.PropComparator)
-    ):
-        onclause = right
-        right = None
-
-    if right is not None and "parententity" in right._annotations:
-        right = right._annotations["parententity"].entity
-
-    if right is not None:
-        last_entity = right
-        insp = inspect(last_entity)
-        if insp.is_clause_element or insp.is_aliased_class or insp.is_mapper:
-            return insp
-
-    last_entity = onclause
-    if isinstance(last_entity, interfaces.PropComparator):
-        return last_entity.entity
-
-    # legacy vvvvvvvvvvvvvvvvvvvvvvvvvvv
-    if isinstance(onclause, str):
-        if from_joinpoint:
-            prev = _legacy_determine_last_joined_entity(
-                setup_joins[0:-1], entity_zero
-            )
-        else:
-            prev = entity_zero
-
-        if prev is None:
-            return None
+        return None
 
-        prev = inspect(prev)
-        attr = getattr(prev.entity, onclause, None)
-        if attr is not None:
-            return attr.property.entity
-    # legacy ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+    (target, onclause, from_, flags) = setup_joins[-1]
 
-    return None
+    if isinstance(target, interfaces.PropComparator):
+        return target.entity
+    else:
+        return target
 
 
 class _QueryEntity:
@@ -2455,43 +2112,22 @@ class _MapperEntity(_QueryEntity):
         self.is_aliased_class = ext_info.is_aliased_class
         self.path = ext_info._path_registry
 
-        if ext_info in compile_state._with_polymorphic_adapt_map:
-            # this codepath occurs only if query.with_polymorphic() were
-            # used
-
-            wp = inspect(compile_state._with_polymorphic_adapt_map[ext_info])
+        self.selectable = ext_info.selectable
+        self._with_polymorphic_mappers = ext_info.with_polymorphic_mappers
+        self._polymorphic_discriminator = ext_info.polymorphic_on
 
-            if self.is_aliased_class:
-                # TODO: invalidrequest ?
-                raise NotImplementedError(
-                    "Can't use with_polymorphic() against an Aliased object"
-                )
-
-            mappers, from_obj = mapper._with_polymorphic_args(
-                wp.with_polymorphic_mappers, wp.selectable
+        if (
+            mapper.with_polymorphic
+            # controversy - only if inheriting mapper is also
+            # polymorphic?
+            # or (mapper.inherits and mapper.inherits.with_polymorphic)
+            or mapper.inherits
+            or mapper._requires_row_aliasing
+        ):
+            compile_state._create_with_polymorphic_adapter(
+                ext_info, self.selectable
             )
 
-            self._with_polymorphic_mappers = mappers
-            self.selectable = from_obj
-            self._polymorphic_discriminator = wp.polymorphic_on
-
-        else:
-            self.selectable = ext_info.selectable
-            self._with_polymorphic_mappers = ext_info.with_polymorphic_mappers
-            self._polymorphic_discriminator = ext_info.polymorphic_on
-
-            if (
-                mapper.with_polymorphic
-                # controversy - only if inheriting mapper is also
-                # polymorphic?
-                # or (mapper.inherits and mapper.inherits.with_polymorphic)
-                or mapper.inherits
-                or mapper._requires_row_aliasing
-            ):
-                compile_state._create_with_polymorphic_adapter(
-                    ext_info, self.selectable
-                )
-
     supports_single_entity = True
 
     _non_hashable_value = True
index 0e99139b4bd34f855c90aedc4675211a16782001..2d574c6b22dbc56fbe81c397e8492f001c736809 100644 (file)
@@ -21,7 +21,6 @@ database to return iterable result sets.
 import collections.abc as collections_abc
 import itertools
 import operator
-import types
 import typing
 
 from . import exc as orm_exc
@@ -30,7 +29,7 @@ from . import loading
 from . import util as orm_util
 from .base import _assertions
 from .context import _column_descriptions
-from .context import _legacy_determine_last_joined_entity
+from .context import _determine_last_joined_entity
 from .context import _legacy_filter_by_entity_zero
 from .context import LABEL_STYLE_LEGACY_ORM
 from .context import ORMCompileState
@@ -38,10 +37,8 @@ from .context import ORMFromStatementCompileState
 from .context import QueryContext
 from .interfaces import ORMColumnsClauseRole
 from .util import aliased
-from .util import AliasedClass
 from .util import object_mapper
 from .util import with_parent
-from .util import with_polymorphic
 from .. import exc as sa_exc
 from .. import inspect
 from .. import inspection
@@ -49,7 +46,6 @@ from .. import log
 from .. import sql
 from .. import util
 from ..sql import coercions
-from ..sql import elements
 from ..sql import expression
 from ..sql import roles
 from ..sql import Select
@@ -123,7 +119,6 @@ class Query(
     _auto_correlate = True
     _from_obj = ()
     _setup_joins = ()
-    _legacy_setup_joins = ()
     _label_style = LABEL_STYLE_LEGACY_ORM
 
     _memoized_select_entities = ()
@@ -138,7 +133,6 @@ class Query(
 
     # local Query builder state, not needed for
     # compilation or execution
-    _aliased_generation = None
     _enable_assertions = True
     _last_joined_entity = None
     _statement = None
@@ -261,7 +255,7 @@ class Query(
             self._where_criteria
             or self._statement is not None
             or self._from_obj
-            or self._legacy_setup_joins
+            or self._setup_joins
             or self._limit_clause is not None
             or self._offset_clause is not None
             or self._group_by_clauses
@@ -276,7 +270,7 @@ class Query(
     def _no_criterion_condition(self, meth, order_by=True, distinct=True):
         self._no_criterion_assertion(meth, order_by, distinct)
 
-        self._from_obj = self._legacy_setup_joins = ()
+        self._from_obj = self._setup_joins = ()
         if self._statement is not None:
             self._compile_options += {"_statement": None}
         self._where_criteria = ()
@@ -384,10 +378,7 @@ class Query(
         # from there, it starts to look much like Query itself won't be
         # passed into the execute process and wont generate its own cache
         # key; this will all occur in terms of the ORM-enabled Select.
-        if (
-            not self._compile_options._set_base_alias
-            and not self._compile_options._with_polymorphic_adapt_map
-        ):
+        if not self._compile_options._set_base_alias:
             # if we don't have legacy top level aliasing features in use
             # then convert to a future select() directly
             stmt = self._statement_20(for_statement=True)
@@ -790,55 +781,6 @@ class Query(
         self._compile_options += {"_current_path": path}
         return self
 
-    @_generative
-    @_assertions(_no_clauseelement_condition)
-    @util.deprecated_20(
-        ":meth:`_orm.Query.with_polymorphic`",
-        alternative="Use the orm.with_polymorphic() standalone function",
-    )
-    def with_polymorphic(
-        self: SelfQuery, cls_or_mappers, selectable=None, polymorphic_on=None
-    ) -> SelfQuery:
-        """Load columns for inheriting classes.
-
-        This is a legacy method which is replaced by the
-        :func:`_orm.with_polymorphic` function.
-
-        .. warning:: The :meth:`_orm.Query.with_polymorphic` method does
-           **not** support 1.4/2.0 style features including
-           :func:`_orm.with_loader_criteria`.  Please migrate code
-           to use :func:`_orm.with_polymorphic`.
-
-        :meth:`_query.Query.with_polymorphic` applies transformations
-        to the "main" mapped class represented by this :class:`_query.Query`.
-        The "main" mapped class here means the :class:`_query.Query`
-        object's first argument is a full class, i.e.
-        ``session.query(SomeClass)``. These transformations allow additional
-        tables to be present in the FROM clause so that columns for a
-        joined-inheritance subclass are available in the query, both for the
-        purposes of load-time efficiency as well as the ability to use
-        these columns at query time.
-
-        .. seealso::
-
-            :ref:`with_polymorphic` - illustrates current patterns
-
-        """
-
-        entity = _legacy_filter_by_entity_zero(self)
-
-        wp = with_polymorphic(
-            entity,
-            cls_or_mappers,
-            selectable=selectable,
-            polymorphic_on=polymorphic_on,
-        )
-
-        self._compile_options = self._compile_options.add_to_element(
-            "_with_polymorphic_adapt_map", ((entity, inspect(wp)),)
-        )
-        return self
-
     @_generative
     def yield_per(self: SelfQuery, count) -> SelfQuery:
         r"""Yield only ``count`` rows at a time.
@@ -1162,191 +1104,10 @@ class Query(
         self.session = session
         return self
 
-    @util.deprecated_20(
-        ":meth:`_query.Query.from_self`",
-        alternative="The new approach is to use the :func:`.orm.aliased` "
-        "construct in conjunction with a subquery.  See the section "
-        ":ref:`Selecting from the query itself as a subquery "
-        "<migration_20_query_from_self>` in the 2.0 migration notes for an "
-        "example.",
-    )
-    def from_self(self, *entities):
-        r"""return a Query that selects from this Query's
-        SELECT statement.
-
-        :meth:`_query.Query.from_self` essentially turns the SELECT statement
-        into a SELECT of itself.  Given a query such as::
-
-            q = session.query(User).filter(User.name.like('e%'))
-
-        Given the :meth:`_query.Query.from_self` version::
-
-            q = session.query(User).filter(User.name.like('e%')).from_self()
-
-        This query renders as:
-
-        .. sourcecode:: sql
-
-            SELECT anon_1.user_id AS anon_1_user_id,
-                   anon_1.user_name AS anon_1_user_name
-            FROM (SELECT "user".id AS user_id, "user".name AS user_name
-            FROM "user"
-            WHERE "user".name LIKE :name_1) AS anon_1
-
-        There are lots of cases where :meth:`_query.Query.from_self`
-        may be useful.
-        A simple one is where above, we may want to apply a row LIMIT to
-        the set of user objects we query against, and then apply additional
-        joins against that row-limited set::
-
-            q = session.query(User).filter(User.name.like('e%')).\
-                limit(5).from_self().\
-                join(User.addresses).filter(Address.email.like('q%'))
-
-        The above query joins to the ``Address`` entity but only against the
-        first five results of the ``User`` query:
-
-        .. sourcecode:: sql
-
-            SELECT anon_1.user_id AS anon_1_user_id,
-                   anon_1.user_name AS anon_1_user_name
-            FROM (SELECT "user".id AS user_id, "user".name AS user_name
-            FROM "user"
-            WHERE "user".name LIKE :name_1
-             LIMIT :param_1) AS anon_1
-            JOIN address ON anon_1.user_id = address.user_id
-            WHERE address.email LIKE :email_1
-
-        **Automatic Aliasing**
-
-        Another key behavior of :meth:`_query.Query.from_self`
-        is that it applies
-        **automatic aliasing** to the entities inside the subquery, when
-        they are referenced on the outside.  Above, if we continue to
-        refer to the ``User`` entity without any additional aliasing applied
-        to it, those references will be in terms of the subquery::
+    def _legacy_from_self(self, *entities):
+        # used for query.count() as well as for the same
+        # function in BakedQuery, as well as some old tests in test_baked.py.
 
-            q = session.query(User).filter(User.name.like('e%')).\
-                limit(5).from_self().\
-                join(User.addresses).filter(Address.email.like('q%')).\
-                order_by(User.name)
-
-        The ORDER BY against ``User.name`` is aliased to be in terms of the
-        inner subquery:
-
-        .. sourcecode:: sql
-
-            SELECT anon_1.user_id AS anon_1_user_id,
-                   anon_1.user_name AS anon_1_user_name
-            FROM (SELECT "user".id AS user_id, "user".name AS user_name
-            FROM "user"
-            WHERE "user".name LIKE :name_1
-             LIMIT :param_1) AS anon_1
-            JOIN address ON anon_1.user_id = address.user_id
-            WHERE address.email LIKE :email_1 ORDER BY anon_1.user_name
-
-        The automatic aliasing feature only works in a **limited** way,
-        for simple filters and orderings.   More ambitious constructions
-        such as referring to the entity in joins should prefer to use
-        explicit subquery objects, typically making use of the
-        :meth:`_query.Query.subquery`
-        method to produce an explicit subquery object.
-        Always test the structure of queries by viewing the SQL to ensure
-        a particular structure does what's expected!
-
-        **Changing the Entities**
-
-        :meth:`_query.Query.from_self`
-        also includes the ability to modify what
-        columns are being queried.   In our example, we want ``User.id``
-        to be queried by the inner query, so that we can join to the
-        ``Address`` entity on the outside, but we only wanted the outer
-        query to return the ``Address.email`` column::
-
-            q = session.query(User).filter(User.name.like('e%')).\
-                limit(5).from_self(Address.email).\
-                join(User.addresses).filter(Address.email.like('q%'))
-
-        yielding:
-
-        .. sourcecode:: sql
-
-            SELECT address.email AS address_email
-            FROM (SELECT "user".id AS user_id, "user".name AS user_name
-            FROM "user"
-            WHERE "user".name LIKE :name_1
-             LIMIT :param_1) AS anon_1
-            JOIN address ON anon_1.user_id = address.user_id
-            WHERE address.email LIKE :email_1
-
-        **Looking out for Inner / Outer Columns**
-
-        Keep in mind that when referring to columns that originate from
-        inside the subquery, we need to ensure they are present in the
-        columns clause of the subquery itself; this is an ordinary aspect of
-        SQL.  For example, if we wanted to load from a joined entity inside
-        the subquery using :func:`.contains_eager`, we need to add those
-        columns.   Below illustrates a join of ``Address`` to ``User``,
-        then a subquery, and then we'd like :func:`.contains_eager` to access
-        the ``User`` columns::
-
-            q = session.query(Address).join(Address.user).\
-                filter(User.name.like('e%'))
-
-            q = q.add_entity(User).from_self().\
-                options(contains_eager(Address.user))
-
-        We use :meth:`_query.Query.add_entity` above **before** we call
-        :meth:`_query.Query.from_self`
-        so that the ``User`` columns are present
-        in the inner subquery, so that they are available to the
-        :func:`.contains_eager` modifier we are using on the outside,
-        producing:
-
-        .. sourcecode:: sql
-
-            SELECT anon_1.address_id AS anon_1_address_id,
-                   anon_1.address_email AS anon_1_address_email,
-                   anon_1.address_user_id AS anon_1_address_user_id,
-                   anon_1.user_id AS anon_1_user_id,
-                   anon_1.user_name AS anon_1_user_name
-            FROM (
-                SELECT address.id AS address_id,
-                address.email AS address_email,
-                address.user_id AS address_user_id,
-                "user".id AS user_id,
-                "user".name AS user_name
-            FROM address JOIN "user" ON "user".id = address.user_id
-            WHERE "user".name LIKE :name_1) AS anon_1
-
-        If we didn't call ``add_entity(User)``, but still asked
-        :func:`.contains_eager` to load the ``User`` entity, it would be
-        forced to add the table on the outside without the correct
-        join criteria - note the ``anon1, "user"`` phrase at
-        the end:
-
-        .. sourcecode:: sql
-
-            -- incorrect query
-            SELECT anon_1.address_id AS anon_1_address_id,
-                   anon_1.address_email AS anon_1_address_email,
-                   anon_1.address_user_id AS anon_1_address_user_id,
-                   "user".id AS user_id,
-                   "user".name AS user_name
-            FROM (
-                SELECT address.id AS address_id,
-                address.email AS address_email,
-                address.user_id AS address_user_id
-            FROM address JOIN "user" ON "user".id = address.user_id
-            WHERE "user".name LIKE :name_1) AS anon_1, "user"
-
-        :param \*entities: optional list of entities which will replace
-         those being selected.
-
-        """
-        return self._from_self(*entities)
-
-    def _from_self(self, *entities):
         fromclause = (
             self.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
             .correlate(None)
@@ -1376,7 +1137,7 @@ class Query(
             "_limit_clause",
             "_offset_clause",
             "_last_joined_entity",
-            "_legacy_setup_joins",
+            "_setup_joins",
             "_memoized_select_entities",
             "_distinct",
             "_distinct_on",
@@ -1390,12 +1151,6 @@ class Query(
             "_enable_single_crit": False,
         }
 
-        # this enables clause adaptation for non-ORM
-        # expressions.
-        # legacy.  see test/orm/test_froms.py for various
-        # "oldstyle" tests that rely on this and the corresponding
-        # "newtyle" that do not.
-        self._compile_options += {"_orm_only_from_obj_alias": False}
         return self
 
     @util.deprecated(
@@ -1721,21 +1476,14 @@ class Query(
                 roles.WhereHavingRole, criterion, apply_propagate_attrs=self
             )
 
-            # legacy vvvvvvvvvvvvvvvvvvvvvvvvvvv
-            if self._aliased_generation:
-                criterion = sql_util._deep_annotate(
-                    criterion, {"aliased_generation": self._aliased_generation}
-                )
-            # legacy ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
             self._where_criteria += (criterion,)
         return self
 
     @util.memoized_property
     def _last_joined_entity(self):
-        if self._legacy_setup_joins:
-            return _legacy_determine_last_joined_entity(
-                self._legacy_setup_joins, self._entity_from_pre_ent_zero()
+        if self._setup_joins:
+            return _determine_last_joined_entity(
+                self._setup_joins,
             )
         else:
             return None
@@ -1746,7 +1494,7 @@ class Query(
 
         """
 
-        if self._legacy_setup_joins:
+        if self._setup_joins:
             _last_joined_entity = self._last_joined_entity
             if _last_joined_entity is not None:
                 return _last_joined_entity
@@ -1856,18 +1604,6 @@ class Query(
                 coercions.expect(roles.OrderByRole, clause)
                 for clause in clauses
             )
-            # legacy vvvvvvvvvvvvvvvvvvvvvvvvvvv
-            if self._aliased_generation:
-                criterion = tuple(
-                    [
-                        sql_util._deep_annotate(
-                            o, {"aliased_generation": self._aliased_generation}
-                        )
-                        for o in criterion
-                    ]
-                )
-            # legacy ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
             self._order_by_clauses += criterion
         return self
 
@@ -1900,18 +1636,6 @@ class Query(
                 coercions.expect(roles.GroupByRole, clause)
                 for clause in clauses
             )
-            # legacy vvvvvvvvvvvvvvvvvvvvvvvvvvv
-            if self._aliased_generation:
-                criterion = tuple(
-                    [
-                        sql_util._deep_annotate(
-                            o, {"aliased_generation": self._aliased_generation}
-                        )
-                        for o in criterion
-                    ]
-                )
-            # legacy ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
             self._group_by_clauses += criterion
         return self
 
@@ -2028,15 +1752,11 @@ class Query(
         """
         return self._set_op(expression.except_all, *q)
 
-    def _next_aliased_generation(self):
-        if "_aliased_generation_counter" not in self.__dict__:
-            self._aliased_generation_counter = 0
-        self._aliased_generation_counter += 1
-        return self._aliased_generation_counter
-
     @_generative
     @_assertions(_no_statement_condition, _no_limit_offset)
-    def join(self: SelfQuery, target, *props, **kwargs) -> SelfQuery:
+    def join(
+        self: SelfQuery, target, onclause=None, *, isouter=False, full=False
+    ) -> SelfQuery:
         r"""Create a SQL JOIN against this :class:`_query.Query`
         object's criterion
         and apply generatively, returning the newly resulting
@@ -2206,71 +1926,6 @@ class Query(
                 JOIN address ON user.id=address.user_id
                 WHERE user.name = :name_1
 
-        **Legacy Features of Query.join()**
-
-        .. deprecated:: 1.4 The following features are deprecated and will
-           be removed in SQLAlchemy 2.0.
-
-        The :meth:`_query.Query.join` method currently supports several
-        usage patterns and arguments that are considered to be legacy
-        as of SQLAlchemy 1.3.   A deprecation path will follow
-        in the 1.4 series for the following features:
-
-
-        * Automatic aliasing with the ``aliased=True`` flag::
-
-            session.query(Node).join(Node.children, aliased=True).\
-                filter(Node.name == 'some name')
-
-          **Why it's legacy**:  the automatic aliasing feature of
-          :class:`_query.Query` is intensely complicated, both in its internal
-          implementation as well as in its observed behavior, and is almost
-          never used.  It is difficult to know upon inspection where and when
-          its aliasing of a target entity, ``Node`` in the above case, will be
-          applied and when it won't, and additionally the feature has to use
-          very elaborate heuristics to achieve this implicit behavior.
-
-          **Modern calling pattern**: Use the :func:`_orm.aliased` construct
-          explicitly::
-
-            from sqlalchemy.orm import aliased
-
-            n1 = aliased(Node)
-
-            session.query(Node).join(Node.children.of_type(n1)).\
-                filter(n1.name == 'some name')
-
-        * Multiple joins in one call::
-
-            session.query(User).join("orders", "items")
-
-            session.query(User).join(User.orders, Order.items)
-
-            session.query(User).join(
-                (Order, User.orders),
-                (Item, Item.order_id == Order.id)
-            )
-
-            session.query(User).join(Order, Item)
-
-            # ... and several more forms actually
-
-          **Why it's legacy**: being able to chain multiple ON clauses in one
-          call to :meth:`_query.Query.join` is yet another attempt to solve
-          the problem of being able to specify what entity to join from,
-          and is the source of a large variety of potential calling patterns
-          that are internally expensive and complicated to parse and
-          accommodate.
-
-          **Modern calling pattern**:  Use relationship-bound attributes
-          or SQL-oriented ON clauses within separate calls, so that
-          each call to :meth:`_query.Query.join` knows what the left
-          side should be::
-
-            session.query(User).join(User.orders).join(
-                Item, Item.order_id == Order.id)
-
-
         :param \*props: Incoming arguments for :meth:`_query.Query.join`,
          the props collection in modern use should be considered to be a  one
          or two argument form, either as a single "target" entity or ORM
@@ -2285,168 +1940,41 @@ class Query(
 
          .. versionadded:: 1.1
 
-        :param from_joinpoint=False: When using ``aliased=True``, a setting
-         of True here will cause the join to be from the most recent
-         joined target, rather than starting back from the original
-         FROM clauses of the query.
-
-         .. note:: This flag is considered legacy.
-
-        :param aliased=False: If True, indicate that the JOIN target should be
-         anonymously aliased.  Subsequent calls to :meth:`_query.Query.filter`
-         and similar will adapt the incoming criterion to the target
-         alias, until :meth:`_query.Query.reset_joinpoint` is called.
-
-         .. note:: This flag is considered legacy.
-
         """
 
-        aliased, from_joinpoint, isouter, full = (
-            kwargs.pop("aliased", False),
-            kwargs.pop("from_joinpoint", False),
-            kwargs.pop("isouter", False),
-            kwargs.pop("full", False),
+        target = coercions.expect(
+            roles.JoinTargetRole,
+            target,
+            apply_propagate_attrs=self,
+            legacy=True,
         )
-
-        if aliased or from_joinpoint:
-            util.warn_deprecated_20(
-                "The ``aliased`` and ``from_joinpoint`` keyword arguments "
-                "to Query.join() are deprecated and will be removed "
-                "in SQLAlchemy 2.0."
-            )
-
-        if kwargs:
-            raise TypeError(
-                "unknown arguments: %s" % ", ".join(sorted(kwargs))
+        if onclause is not None:
+            onclause = coercions.expect(
+                roles.OnClauseRole, onclause, legacy=True
             )
-
-        # legacy vvvvvvvvvvvvvvvvvvvvvvvvvvv
-        if not from_joinpoint:
-            self._last_joined_entity = None
-            self._aliased_generation = None
-        # legacy ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-        if props:
-            onclause, legacy = props[0], props[1:]
-        else:
-            onclause = legacy = None
-
-        if not legacy and onclause is None and not isinstance(target, tuple):
-            # non legacy argument form
-            _props = [(target,)]
-        elif (
-            not legacy
-            and isinstance(
+        self._setup_joins += (
+            (
                 target,
-                (
-                    expression.Selectable,
-                    type,
-                    AliasedClass,
-                    types.FunctionType,
-                ),
-            )
-            and isinstance(
                 onclause,
-                (
-                    elements.ColumnElement,
-                    interfaces.PropComparator,
-                    types.FunctionType,
-                ),
-            )
-        ):
-            # non legacy argument form
-            _props = [(target, onclause)]
-        else:
-            # legacy forms.   more time consuming :)
-            _props = []
-            _single = []
-            for prop in (target,) + props:
-                if isinstance(prop, tuple):
-                    util.warn_deprecated_20(
-                        "Query.join() will no longer accept tuples as "
-                        "arguments in SQLAlchemy 2.0."
-                    )
-                    if _single:
-                        _props.extend((_s,) for _s in _single)
-                    _single = []
-
-                    # this checks for an extremely ancient calling form of
-                    # reversed tuples.
-                    if isinstance(prop[0], interfaces.PropComparator):
-                        prop = (prop[1], prop[0])
-
-                    _props.append(prop)
-                else:
-                    _single.append(prop)
-            if _single:
-                _props.extend((_s,) for _s in _single)
-
-        # legacy vvvvvvvvvvvvvvvvvvvvvvvvvvv
-        if aliased:
-            self._aliased_generation = self._next_aliased_generation()
-
-        if self._aliased_generation:
-            _props = [
-                (
-                    prop[0],
-                    sql_util._deep_annotate(
-                        prop[1],
-                        {"aliased_generation": self._aliased_generation},
-                    )
-                    if isinstance(prop[1], expression.ClauseElement)
-                    else prop[1],
-                )
-                if len(prop) == 2
-                else prop
-                for prop in _props
-            ]
-
-        # legacy ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-        joins_to_add = tuple(
-            (
-                coercions.expect(
-                    roles.JoinTargetRole,
-                    prop[0],
-                    legacy=True,
-                    apply_propagate_attrs=self,
-                ),
-                (coercions.expect(roles.OnClauseRole, prop[1], legacy=True))
-                if len(prop) == 2
-                else None,
                 None,
                 {
                     "isouter": isouter,
-                    "aliased": aliased,
-                    "from_joinpoint": True if i > 0 else from_joinpoint,
                     "full": full,
-                    "aliased_generation": self._aliased_generation,
                 },
-            )
-            for i, prop in enumerate(_props)
+            ),
         )
 
-        if len(joins_to_add) > 1:
-            util.warn_deprecated_20(
-                "Passing a chain of multiple join conditions to Query.join() "
-                "is deprecated and will be removed in SQLAlchemy 2.0. "
-                "Please use individual join() calls per relationship."
-            )
-
-        self._legacy_setup_joins += joins_to_add
-
         self.__dict__.pop("_last_joined_entity", None)
         return self
 
-    def outerjoin(self, target, *props, **kwargs):
+    def outerjoin(self, target, onclause=None, *, full=False):
         """Create a left outer join against this ``Query`` object's criterion
         and apply generatively, returning the newly resulting ``Query``.
 
         Usage is the same as the ``join()`` method.
 
         """
-        kwargs["isouter"] = True
-        return self.join(target, *props, **kwargs)
+        return self.join(target, onclause=onclause, isouter=True, full=full)
 
     @_generative
     @_assertions(_no_statement_condition)
@@ -2461,7 +1989,7 @@ class Query(
 
         """
         self._last_joined_entity = None
-        self._aliased_generation = None
+
         return self
 
     @_generative
@@ -2514,128 +2042,6 @@ class Query(
         self._set_select_from(from_obj, False)
         return self
 
-    @util.deprecated_20(
-        ":meth:`_orm.Query.select_entity_from`",
-        alternative="Use the :func:`_orm.aliased` construct instead",
-    )
-    @_generative
-    @_assertions(_no_clauseelement_condition)
-    def select_entity_from(self: SelfQuery, from_obj) -> SelfQuery:
-        r"""Set the FROM clause of this :class:`_query.Query` to a
-        core selectable, applying it as a replacement FROM clause
-        for corresponding mapped entities.
-
-        The :meth:`_query.Query.select_entity_from`
-        method supplies an alternative
-        approach to the use case of applying an :func:`.aliased` construct
-        explicitly throughout a query.  Instead of referring to the
-        :func:`.aliased` construct explicitly,
-        :meth:`_query.Query.select_entity_from` automatically *adapts* all
-        occurrences of the entity to the target selectable.
-
-        Given a case for :func:`.aliased` such as selecting ``User``
-        objects from a SELECT statement::
-
-            select_stmt = select(User).where(User.id == 7)
-            user_alias = aliased(User, select_stmt)
-
-            q = session.query(user_alias).\
-                filter(user_alias.name == 'ed')
-
-        Above, we apply the ``user_alias`` object explicitly throughout the
-        query.  When it's not feasible for ``user_alias`` to be referenced
-        explicitly in many places, :meth:`_query.Query.select_entity_from`
-        may be
-        used at the start of the query to adapt the existing ``User`` entity::
-
-            q = session.query(User).\
-                select_entity_from(select_stmt.subquery()).\
-                filter(User.name == 'ed')
-
-        Above, the generated SQL will show that the ``User`` entity is
-        adapted to our statement, even in the case of the WHERE clause:
-
-        .. sourcecode:: sql
-
-            SELECT anon_1.id AS anon_1_id, anon_1.name AS anon_1_name
-            FROM (SELECT "user".id AS id, "user".name AS name
-            FROM "user"
-            WHERE "user".id = :id_1) AS anon_1
-            WHERE anon_1.name = :name_1
-
-        The :meth:`_query.Query.select_entity_from` method is similar to the
-        :meth:`_query.Query.select_from` method,
-        in that it sets the FROM clause
-        of the query.  The difference is that it additionally applies
-        adaptation to the other parts of the query that refer to the
-        primary entity.  If above we had used :meth:`_query.Query.select_from`
-        instead, the SQL generated would have been:
-
-        .. sourcecode:: sql
-
-            -- uses plain select_from(), not select_entity_from()
-            SELECT "user".id AS user_id, "user".name AS user_name
-            FROM "user", (SELECT "user".id AS id, "user".name AS name
-            FROM "user"
-            WHERE "user".id = :id_1) AS anon_1
-            WHERE "user".name = :name_1
-
-        To supply textual SQL to the :meth:`_query.Query.select_entity_from`
-        method,
-        we can make use of the :func:`_expression.text` construct.  However,
-        the
-        :func:`_expression.text`
-        construct needs to be aligned with the columns of our
-        entity, which is achieved by making use of the
-        :meth:`_expression.TextClause.columns` method::
-
-            text_stmt = text("select id, name from user").columns(
-                User.id, User.name).subquery()
-            q = session.query(User).select_entity_from(text_stmt)
-
-        :meth:`_query.Query.select_entity_from` itself accepts an
-        :func:`.aliased`
-        object, so that the special options of :func:`.aliased` such as
-        :paramref:`.aliased.adapt_on_names` may be used within the
-        scope of the :meth:`_query.Query.select_entity_from`
-        method's adaptation
-        services.  Suppose
-        a view ``user_view`` also returns rows from ``user``.    If
-        we reflect this view into a :class:`_schema.Table`, this view has no
-        relationship to the :class:`_schema.Table` to which we are mapped,
-        however
-        we can use name matching to select from it::
-
-            user_view = Table('user_view', metadata,
-                              autoload_with=engine)
-            user_view_alias = aliased(
-                User, user_view, adapt_on_names=True)
-            q = session.query(User).\
-                select_entity_from(user_view_alias).\
-                order_by(User.name)
-
-        .. versionchanged:: 1.1.7 The :meth:`_query.Query.select_entity_from`
-           method now accepts an :func:`.aliased` object as an alternative
-           to a :class:`_expression.FromClause` object.
-
-        :param from_obj: a :class:`_expression.FromClause`
-         object that will replace
-         the FROM clause of this :class:`_query.Query`.
-         It also may be an instance
-         of :func:`.aliased`.
-
-
-
-        .. seealso::
-
-            :meth:`_query.Query.select_from`
-
-        """
-
-        self._set_select_from([from_obj], True)
-        self._compile_options += {"_enable_single_crit": False}
-        return self
-
     def __getitem__(self, item):
         return orm_util._getitem(
             self,
@@ -3137,7 +2543,7 @@ class Query(
 
         """
         col = sql.func.count(sql.literal_column("*"))
-        return self._from_self(col).enable_eagerloads(False).scalar()
+        return self._legacy_from_self(col).enable_eagerloads(False).scalar()
 
     def delete(self, synchronize_session="evaluate"):
         r"""Perform a DELETE with an arbitrary WHERE clause.
@@ -3452,7 +2858,7 @@ class BulkUD:
                 operator.eq,
             ),
             (
-                "_legacy_setup_joins",
+                "_setup_joins",
                 "join(), outerjoin(), select_from(), or from_self()",
                 (),
                 operator.eq,
index 58ad3ab5f9559b021b2fdc4b99caa8e0815bac47..b6983a0360028a71e5982ac5c7fdf9f26803c8f3 100644 (file)
@@ -1212,13 +1212,6 @@ class LoaderCriteriaOption(CriteriaOption):
         # if options to limit the criteria to immediate query only,
         # use compile_state.attributes instead
 
-        if compile_state.compile_options._with_polymorphic_adapt_map:
-            util.warn(
-                "The with_loader_criteria() function may not work "
-                "correctly with the legacy Query.with_polymorphic() feature.  "
-                "Please migrate code to use the with_polymorphic() standalone "
-                "function before using with_loader_criteria()."
-            )
         if not compile_state.compile_options._for_refresh_state:
             self.get_global_criteria(compile_state.global_attributes)
 
index fa21fbeeff89681a5af0183ac83cdda8cf143cfc..1a0336c67583fedd471a13952c4d7eeedf47118b 100644 (file)
@@ -606,22 +606,15 @@ class InElementImpl(RoleImpl):
             return element
 
 
-class OnClauseImpl(_CoerceLiterals, _ColumnCoercions, RoleImpl):
+class OnClauseImpl(_ColumnCoercions, RoleImpl):
     __slots__ = ()
 
     _coerce_consts = True
 
-    def _text_coercion(self, element, argname=None, legacy=False):
-        if legacy and isinstance(element, str):
-            util.warn_deprecated_20(
-                "Using strings to indicate relationship names in "
-                "Query.join() is deprecated and will be removed in "
-                "SQLAlchemy 2.0.  Please use the class-bound attribute "
-                "directly."
-            )
-            return element
-
-        return super(OnClauseImpl, self)._text_coercion(element, argname)
+    def _literal_coercion(
+        self, element, name=None, type_=None, argname=None, is_crud=False, **kw
+    ):
+        self._raise_for_expected(element)
 
     def _post_coercion(self, resolved, original_element=None, **kw):
         # this is a hack right now as we want to use coercion on an
@@ -936,8 +929,6 @@ class JoinTargetImpl(RoleImpl):
             # #6550, unless JoinTargetImpl._skip_clauseelement_for_target_match
             # were set to False.
             return original_element
-        elif legacy and isinstance(resolved, roles.WhereHavingRole):
-            return resolved
         elif legacy and resolved._is_select_statement:
             util.warn_deprecated(
                 "Implicit coercion of SELECT and textual SELECT "
index 1f6a8ddf2ae0965ba5cde0f3fe0d7597a1e5265f..f076f109209b9d03146323f6ff4ffc5d5ef91b64 100644 (file)
@@ -120,7 +120,10 @@ class StatementOptionRole(StructuralRole):
 
 class OnClauseRole(AllowsLambdaRole, StructuralRole):
     __slots__ = ()
-    _role_name = "SQL expression for ON clause"
+    _role_name = (
+        "ON clause, typically a SQL expression or "
+        "ORM relationship attribute"
+    )
 
 
 class WhereHavingRole(OnClauseRole):
index 8b35dc6ace7ad76de8f447533f7716a2e5eed54a..b53235a83600c479fdddc36a39c87d7df08bc67d 100644 (file)
@@ -4636,7 +4636,7 @@ class SelectState(util.MemoizedSlots, CompileState):
 class _SelectFromElements:
     def _iterate_from_elements(self):
         # note this does not include elements
-        # in _setup_joins or _legacy_setup_joins
+        # in _setup_joins
 
         seen = set()
         for element in self._raw_columns:
@@ -4666,7 +4666,6 @@ class _MemoizedSelectEntities(
     _traverse_internals = [
         ("_raw_columns", InternalTraversal.dp_clauseelement_list),
         ("_setup_joins", InternalTraversal.dp_setup_join_tuple),
-        ("_legacy_setup_joins", InternalTraversal.dp_setup_join_tuple),
         ("_with_options", InternalTraversal.dp_executable_options),
     ]
 
@@ -4680,22 +4679,15 @@ class _MemoizedSelectEntities(
 
     @classmethod
     def _generate_for_statement(cls, select_stmt):
-        if (
-            select_stmt._setup_joins
-            or select_stmt._legacy_setup_joins
-            or select_stmt._with_options
-        ):
+        if select_stmt._setup_joins or select_stmt._with_options:
             self = _MemoizedSelectEntities()
             self._raw_columns = select_stmt._raw_columns
             self._setup_joins = select_stmt._setup_joins
-            self._legacy_setup_joins = select_stmt._legacy_setup_joins
             self._with_options = select_stmt._with_options
 
             select_stmt._memoized_select_entities += (self,)
             select_stmt._raw_columns = (
                 select_stmt._setup_joins
-            ) = (
-                select_stmt._legacy_setup_joins
             ) = select_stmt._with_options = ()
 
 
@@ -4730,7 +4722,6 @@ class Select(
     __visit_name__ = "select"
 
     _setup_joins = ()
-    _legacy_setup_joins = ()
     _memoized_select_entities = ()
 
     _distinct = False
@@ -4757,7 +4748,6 @@ class Select(
             ("_order_by_clauses", InternalTraversal.dp_clauseelement_tuple),
             ("_group_by_clauses", InternalTraversal.dp_clauseelement_tuple),
             ("_setup_joins", InternalTraversal.dp_setup_join_tuple),
-            ("_legacy_setup_joins", InternalTraversal.dp_setup_join_tuple),
             ("_correlate", InternalTraversal.dp_clauseelement_tuple),
             ("_correlate_except", InternalTraversal.dp_clauseelement_tuple),
             ("_limit_clause", InternalTraversal.dp_clauseelement),
@@ -4921,7 +4911,7 @@ class Select(
 
     @_generative
     def join(
-        self: SelfSelect, target, onclause=None, isouter=False, full=False
+        self: SelfSelect, target, onclause=None, *, isouter=False, full=False
     ) -> SelfSelect:
         r"""Create a SQL JOIN against this :class:`_expression.Select`
         object's criterion
@@ -4990,7 +4980,7 @@ class Select(
         )
         return self
 
-    def outerjoin_from(self, from_, target, onclause=None, full=False):
+    def outerjoin_from(self, from_, target, onclause=None, *, full=False):
         r"""Create a SQL LEFT OUTER JOIN against this :class:`_expression.Select`
         object's criterion
         and apply generatively, returning the newly resulting
@@ -5009,8 +4999,9 @@ class Select(
         from_,
         target,
         onclause=None,
+        *,
         isouter=False,
-        full=False,
+        full=False
     ) -> SelfSelect:
         r"""Create a SQL JOIN against this :class:`_expression.Select`
         object's criterion
@@ -5071,7 +5062,7 @@ class Select(
         )
         return self
 
-    def outerjoin(self, target, onclause=None, full=False):
+    def outerjoin(self, target, onclause=None, *, full=False):
         """Create a left outer join.
 
         Parameters are the same as that of :meth:`_expression.Select.join`.
index 0cbab7f28247ce194806efeee7557b34d686bee7..e32c8f5ab523067e15b935ec15814337d25f035e 100644 (file)
@@ -20,7 +20,7 @@ class WarnDeprecatedLimitedTest(fixtures.TestBase):
 
         actually just verifying that _hash_limit_string works as expected
         """
-        occurrences = 100
+        occurrences = 500
         cap = 10
 
         printouts = set()
index b3d6ebec2835c1bde63af4c3fc9dba37819135fc..a06bbf46b686ab1250abef3fa819e59527bf7f91 100644 (file)
@@ -647,7 +647,9 @@ class ResultTest(BakedTest):
 
         bq = self.bakery(lambda s: s.query(User.id, User.name))
 
-        bq += lambda q: q._from_self().with_entities(func.count(User.id))
+        bq += lambda q: q._legacy_from_self().with_entities(
+            func.count(User.id)
+        )
 
         for i in range(3):
             session = fixture_session()
@@ -682,7 +684,7 @@ class ResultTest(BakedTest):
                     bq += lambda q: q.filter(User.name == "jack")
 
                 if cond4:
-                    bq += lambda q: q._from_self().with_entities(
+                    bq += lambda q: q._legacy_from_self().with_entities(
                         func.count(User.id)
                     )
                 sess = fixture_session()
index 6f370d5e47db7ece5cf8fdd9599c9cc2c7210a12..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
-from sqlalchemy import exc as sa_exc
-from sqlalchemy import ForeignKey
-from sqlalchemy import func
-from sqlalchemy import Integer
-from sqlalchemy import LABEL_STYLE_TABLENAME_PLUS_COL
-from sqlalchemy import null
-from sqlalchemy import select
-from sqlalchemy import String
-from sqlalchemy import testing
-from sqlalchemy.orm import aliased
-from sqlalchemy.orm import joinedload
-from sqlalchemy.orm import polymorphic_union
-from sqlalchemy.orm import relationship
-from sqlalchemy.orm import selectinload
-from sqlalchemy.orm import Session
-from sqlalchemy.testing import assert_raises
-from sqlalchemy.testing import AssertsCompiledSQL
-from sqlalchemy.testing import eq_
-from sqlalchemy.testing import fixtures
-from sqlalchemy.testing.assertions import expect_deprecated_20
-from sqlalchemy.testing.fixtures import fixture_session
-from sqlalchemy.testing.schema import Column
-from sqlalchemy.testing.schema import Table
-from ._poly_fixtures import _Polymorphic
-from ._poly_fixtures import _PolymorphicAliasedJoins
-from ._poly_fixtures import _PolymorphicJoins
-from ._poly_fixtures import _PolymorphicPolymorphic
-from ._poly_fixtures import _PolymorphicUnions
-from ._poly_fixtures import Boss
-from ._poly_fixtures import Company
-from ._poly_fixtures import Engineer
-from ._poly_fixtures import Machine
-from ._poly_fixtures import Manager
-from ._poly_fixtures import Paperwork
-from ._poly_fixtures import Person
-
-
-aliased_jp_dep = (
-    r"The ``aliased`` and ``from_joinpoint`` keyword arguments "
-    r"to Query.join\(\) are deprecated"
-)
-
-with_polymorphic_dep = (
-    r"The Query.with_polymorphic\(\) method is considered legacy as of "
-    r"the 1.x series of SQLAlchemy and will be removed in 2.0"
-)
-
-
-class _PolymorphicTestBase(fixtures.NoCache):
-    __backend__ = True
-    __dialect__ = "default_enhanced"
-
-    @classmethod
-    def setup_mappers(cls):
-        super(_PolymorphicTestBase, cls).setup_mappers()
-        global people, engineers, managers, boss
-        global companies, paperwork, machines
-        people, engineers, managers, boss, companies, paperwork, machines = (
-            cls.tables.people,
-            cls.tables.engineers,
-            cls.tables.managers,
-            cls.tables.boss,
-            cls.tables.companies,
-            cls.tables.paperwork,
-            cls.tables.machines,
-        )
-
-    @classmethod
-    def insert_data(cls, connection):
-        super(_PolymorphicTestBase, cls).insert_data(connection)
-
-        global all_employees, c1_employees, c2_employees
-        global c1, c2, e1, e2, e3, b1, m1
-        c1, c2, all_employees, c1_employees, c2_employees = (
-            cls.c1,
-            cls.c2,
-            cls.all_employees,
-            cls.c1_employees,
-            cls.c2_employees,
-        )
-        e1, e2, e3, b1, m1 = cls.e1, cls.e2, cls.e3, cls.b1, cls.m1
-
-    def test_join_from_polymorphic_flag_aliased_one(self):
-        sess = fixture_session()
-        with expect_deprecated_20(aliased_jp_dep):
-            eq_(
-                sess.query(Person)
-                .order_by(Person.person_id)
-                .join(Person.paperwork, aliased=True)
-                .filter(Paperwork.description.like("%review%"))
-                .all(),
-                [b1, m1],
-            )
-
-    def test_join_from_polymorphic_flag_aliased_two(self):
-        sess = fixture_session()
-        with expect_deprecated_20(aliased_jp_dep):
-            eq_(
-                sess.query(Person)
-                .order_by(Person.person_id)
-                .join(Person.paperwork, aliased=True)
-                .filter(Paperwork.description.like("%#2%"))
-                .all(),
-                [e1, m1],
-            )
-
-    def test_join_from_with_polymorphic_flag_aliased_one(self):
-        sess = fixture_session()
-        with expect_deprecated_20(aliased_jp_dep, with_polymorphic_dep):
-            eq_(
-                sess.query(Person)
-                .with_polymorphic(Manager)
-                .join(Person.paperwork, aliased=True)
-                .filter(Paperwork.description.like("%review%"))
-                .all(),
-                [b1, m1],
-            )
-
-    def test_join_from_with_polymorphic_flag_aliased_two(self):
-        sess = fixture_session()
-        with expect_deprecated_20(aliased_jp_dep, with_polymorphic_dep):
-            eq_(
-                sess.query(Person)
-                .with_polymorphic([Manager, Engineer])
-                .order_by(Person.person_id)
-                .join(Person.paperwork, aliased=True)
-                .filter(Paperwork.description.like("%#2%"))
-                .all(),
-                [e1, m1],
-            )
-
-    def test_join_to_polymorphic_flag_aliased(self):
-        sess = fixture_session()
-        with expect_deprecated_20(aliased_jp_dep):
-            eq_(
-                sess.query(Company)
-                .join(Company.employees, aliased=True)
-                .filter(Person.name == "vlad")
-                .one(),
-                c2,
-            )
-
-    def test_polymorphic_any_flag_alias_two(self):
-        sess = fixture_session()
-        # test that the aliasing on "Person" does not bleed into the
-        # EXISTS clause generated by any()
-        any_ = Company.employees.any(Person.name == "wally")
-        with expect_deprecated_20(aliased_jp_dep):
-            eq_(
-                sess.query(Company)
-                .join(Company.employees, aliased=True)
-                .filter(Person.name == "dilbert")
-                .filter(any_)
-                .all(),
-                [c1],
-            )
-
-    def test_join_from_polymorphic_flag_aliased_three(self):
-        sess = fixture_session()
-        with expect_deprecated_20(aliased_jp_dep):
-            eq_(
-                sess.query(Engineer)
-                .order_by(Person.person_id)
-                .join(Person.paperwork, aliased=True)
-                .filter(Paperwork.description.like("%#2%"))
-                .all(),
-                [e1],
-            )
-
-    def test_with_polymorphic_one(self):
-        sess = fixture_session()
-
-        def go():
-            with expect_deprecated_20(with_polymorphic_dep):
-                eq_(
-                    sess.query(Person)
-                    .with_polymorphic(Engineer)
-                    .filter(Engineer.primary_language == "java")
-                    .all(),
-                    self._emps_wo_relationships_fixture()[0:1],
-                )
-
-        self.assert_sql_count(testing.db, go, 1)
-
-    def test_with_polymorphic_two(self):
-        sess = fixture_session()
-
-        def go():
-            with expect_deprecated_20(with_polymorphic_dep):
-                eq_(
-                    sess.query(Person)
-                    .with_polymorphic("*")
-                    .order_by(Person.person_id)
-                    .all(),
-                    self._emps_wo_relationships_fixture(),
-                )
-
-        self.assert_sql_count(testing.db, go, 1)
-
-    def test_with_polymorphic_three(self):
-        sess = fixture_session()
-
-        def go():
-            with expect_deprecated_20(with_polymorphic_dep):
-                eq_(
-                    sess.query(Person)
-                    .with_polymorphic(Engineer)
-                    .order_by(Person.person_id)
-                    .all(),
-                    self._emps_wo_relationships_fixture(),
-                )
-
-        self.assert_sql_count(testing.db, go, 3)
-
-    def test_with_polymorphic_four(self):
-        sess = fixture_session()
-
-        def go():
-            with expect_deprecated_20(with_polymorphic_dep):
-                eq_(
-                    sess.query(Person)
-                    .with_polymorphic(Engineer, people.outerjoin(engineers))
-                    .order_by(Person.person_id)
-                    .all(),
-                    self._emps_wo_relationships_fixture(),
-                )
-
-        self.assert_sql_count(testing.db, go, 3)
-
-    def test_with_polymorphic_five(self):
-        sess = fixture_session()
-
-        def go():
-            # limit the polymorphic join down to just "Person",
-            # overriding select_table
-            with expect_deprecated_20(with_polymorphic_dep):
-                eq_(
-                    sess.query(Person).with_polymorphic(Person).all(),
-                    self._emps_wo_relationships_fixture(),
-                )
-
-        self.assert_sql_count(testing.db, go, 6)
-
-    def test_with_polymorphic_six(self):
-        sess = fixture_session()
-
-        with expect_deprecated_20(with_polymorphic_dep):
-            assert_raises(
-                sa_exc.InvalidRequestError,
-                sess.query(Person).with_polymorphic,
-                Paperwork,
-            )
-        with expect_deprecated_20(with_polymorphic_dep):
-            assert_raises(
-                sa_exc.InvalidRequestError,
-                sess.query(Engineer).with_polymorphic,
-                Boss,
-            )
-        with expect_deprecated_20(with_polymorphic_dep):
-            assert_raises(
-                sa_exc.InvalidRequestError,
-                sess.query(Engineer).with_polymorphic,
-                Person,
-            )
-
-    def test_with_polymorphic_seven(self):
-        sess = fixture_session()
-        # compare to entities without related collections to prevent
-        # additional lazy SQL from firing on loaded entities
-        with expect_deprecated_20(with_polymorphic_dep):
-            eq_(
-                sess.query(Person)
-                .with_polymorphic("*")
-                .order_by(Person.person_id)
-                .all(),
-                self._emps_wo_relationships_fixture(),
-            )
-
-    def test_joinedload_on_subclass(self):
-        sess = fixture_session()
-        expected = [
-            Engineer(
-                name="dilbert",
-                engineer_name="dilbert",
-                primary_language="java",
-                status="regular engineer",
-                machines=[
-                    Machine(name="IBM ThinkPad"),
-                    Machine(name="IPhone"),
-                ],
-            )
-        ]
-
-        def go():
-            # test load People with joinedload to engineers + machines
-            with expect_deprecated_20(with_polymorphic_dep):
-                eq_(
-                    sess.query(Person)
-                    .with_polymorphic("*")
-                    .options(joinedload(Engineer.machines))
-                    .filter(Person.name == "dilbert")
-                    .all(),
-                    expected,
-                )
-
-        self.assert_sql_count(testing.db, go, 1)
-
-    def test_primary_eager_aliasing_three(self):
-
-        # assert the JOINs don't over JOIN
-
-        sess = fixture_session()
-
-        def go():
-            with expect_deprecated_20(with_polymorphic_dep):
-                eq_(
-                    sess.query(Person)
-                    .with_polymorphic("*")
-                    .order_by(Person.person_id)
-                    .options(joinedload(Engineer.machines))[1:3],
-                    all_employees[1:3],
-                )
-
-        self.assert_sql_count(testing.db, go, 3)
-
-        with expect_deprecated_20(with_polymorphic_dep):
-            eq_(
-                sess.scalar(
-                    select(func.count("*")).select_from(
-                        sess.query(Person)
-                        .with_polymorphic("*")
-                        .options(joinedload(Engineer.machines))
-                        .order_by(Person.person_id)
-                        .limit(2)
-                        .offset(1)
-                        .subquery()
-                    )
-                ),
-                2,
-            )
-
-    def test_join_from_with_polymorphic_nonaliased_one(self):
-        sess = fixture_session()
-        with expect_deprecated_20(with_polymorphic_dep):
-            eq_(
-                sess.query(Person)
-                .with_polymorphic(Manager)
-                .order_by(Person.person_id)
-                .join(Person.paperwork)
-                .filter(Paperwork.description.like("%review%"))
-                .all(),
-                [b1, m1],
-            )
-
-    def test_join_from_with_polymorphic_nonaliased_two(self):
-        sess = fixture_session()
-        with expect_deprecated_20(with_polymorphic_dep):
-            eq_(
-                sess.query(Person)
-                .with_polymorphic([Manager, Engineer])
-                .order_by(Person.person_id)
-                .join(Person.paperwork)
-                .filter(Paperwork.description.like("%#2%"))
-                .all(),
-                [e1, m1],
-            )
-
-    def test_join_from_with_polymorphic_nonaliased_three(self):
-        sess = fixture_session()
-        with expect_deprecated_20(with_polymorphic_dep):
-            eq_(
-                sess.query(Person)
-                .with_polymorphic([Manager, Engineer])
-                .order_by(Person.person_id)
-                .join(Person.paperwork)
-                .filter(Person.name.like("%dog%"))
-                .filter(Paperwork.description.like("%#2%"))
-                .all(),
-                [m1],
-            )
-
-    def test_join_from_with_polymorphic_explicit_aliased_one(self):
-        sess = fixture_session()
-        pa = aliased(Paperwork)
-
-        with expect_deprecated_20(with_polymorphic_dep):
-            eq_(
-                sess.query(Person)
-                .with_polymorphic(Manager)
-                .join(pa, Person.paperwork)
-                .filter(pa.description.like("%review%"))
-                .all(),
-                [b1, m1],
-            )
-
-    def test_join_from_with_polymorphic_explicit_aliased_two(self):
-        sess = fixture_session()
-        pa = aliased(Paperwork)
-
-        with expect_deprecated_20(with_polymorphic_dep):
-            eq_(
-                sess.query(Person)
-                .with_polymorphic([Manager, Engineer])
-                .order_by(Person.person_id)
-                .join(pa, Person.paperwork)
-                .filter(pa.description.like("%#2%"))
-                .all(),
-                [e1, m1],
-            )
-
-    def test_join_from_with_polymorphic_aliased_three(self):
-        sess = fixture_session()
-        pa = aliased(Paperwork)
-
-        with expect_deprecated_20(with_polymorphic_dep):
-            eq_(
-                sess.query(Person)
-                .with_polymorphic([Manager, Engineer])
-                .order_by(Person.person_id)
-                .join(pa, Person.paperwork)
-                .filter(Person.name.like("%dog%"))
-                .filter(pa.description.like("%#2%"))
-                .all(),
-                [m1],
-            )
-
-
-class PolymorphicTest(_PolymorphicTestBase, _Polymorphic):
-    pass
-
-
-class PolymorphicPolymorphicTest(
-    _PolymorphicTestBase, _PolymorphicPolymorphic
-):
-    pass
-
-
-class PolymorphicUnionsTest(_PolymorphicTestBase, _PolymorphicUnions):
-    pass
-
-
-class PolymorphicAliasedJoinsTest(
-    _PolymorphicTestBase, _PolymorphicAliasedJoins
-):
-    pass
-
-
-class PolymorphicJoinsTest(_PolymorphicTestBase, _PolymorphicJoins):
-    pass
-
-
-class RelationshipToSingleTest(
-    testing.AssertsCompiledSQL, fixtures.MappedTest
-):
-    __dialect__ = "default"
-
-    @classmethod
-    def define_tables(cls, metadata):
-        Table(
-            "employees",
-            metadata,
-            Column(
-                "employee_id",
-                Integer,
-                primary_key=True,
-                test_needs_autoincrement=True,
-            ),
-            Column("name", String(50)),
-            Column("manager_data", String(50)),
-            Column("engineer_info", String(50)),
-            Column("type", String(20)),
-            Column("company_id", Integer, ForeignKey("companies.company_id")),
-        )
-
-        Table(
-            "companies",
-            metadata,
-            Column(
-                "company_id",
-                Integer,
-                primary_key=True,
-                test_needs_autoincrement=True,
-            ),
-            Column("name", String(50)),
-        )
-
-    @classmethod
-    def setup_classes(cls):
-        class Company(cls.Comparable):
-            pass
-
-        class Employee(cls.Comparable):
-            pass
-
-        class Manager(Employee):
-            pass
-
-        class Engineer(Employee):
-            pass
-
-        class JuniorEngineer(Engineer):
-            pass
-
-    def test_of_type_aliased_fromjoinpoint(self):
-        Company, Employee, Engineer = (
-            self.classes.Company,
-            self.classes.Employee,
-            self.classes.Engineer,
-        )
-        companies, employees = self.tables.companies, self.tables.employees
-
-        self.mapper_registry.map_imperatively(
-            Company, companies, properties={"employee": relationship(Employee)}
-        )
-        self.mapper_registry.map_imperatively(
-            Employee, employees, polymorphic_on=employees.c.type
-        )
-        self.mapper_registry.map_imperatively(
-            Engineer, inherits=Employee, polymorphic_identity="engineer"
-        )
-
-        sess = fixture_session()
-
-        with expect_deprecated_20(aliased_jp_dep):
-            self.assert_compile(
-                sess.query(Company).outerjoin(
-                    Company.employee.of_type(Engineer),
-                    aliased=True,
-                    from_joinpoint=True,
-                ),
-                "SELECT companies.company_id AS companies_company_id, "
-                "companies.name AS companies_name FROM companies "
-                "LEFT OUTER JOIN employees AS employees_1 ON "
-                "companies.company_id = employees_1.company_id "
-                "AND employees_1.type IN (__[POSTCOMPILE_type_1])",
-            )
-
-
-class SingleOnJoinedTest(fixtures.MappedTest):
-    @classmethod
-    def define_tables(cls, metadata):
-        global persons_table, employees_table
-
-        persons_table = Table(
-            "persons",
-            metadata,
-            Column(
-                "person_id",
-                Integer,
-                primary_key=True,
-                test_needs_autoincrement=True,
-            ),
-            Column("name", String(50)),
-            Column("type", String(20), nullable=False),
-        )
-
-        employees_table = Table(
-            "employees",
-            metadata,
-            Column(
-                "person_id",
-                Integer,
-                ForeignKey("persons.person_id"),
-                primary_key=True,
-            ),
-            Column("employee_data", String(50)),
-            Column("manager_data", String(50)),
-        )
-
-    def test_single_on_joined(self):
-        class Person(fixtures.ComparableEntity):
-            pass
-
-        class Employee(Person):
-            pass
-
-        class Manager(Employee):
-            pass
-
-        self.mapper_registry.map_imperatively(
-            Person,
-            persons_table,
-            polymorphic_on=persons_table.c.type,
-            polymorphic_identity="person",
-        )
-        self.mapper_registry.map_imperatively(
-            Employee,
-            employees_table,
-            inherits=Person,
-            polymorphic_identity="engineer",
-        )
-        self.mapper_registry.map_imperatively(
-            Manager, inherits=Employee, polymorphic_identity="manager"
-        )
-
-        sess = fixture_session()
-        sess.add(Person(name="p1"))
-        sess.add(Employee(name="e1", employee_data="ed1"))
-        sess.add(Manager(name="m1", employee_data="ed2", manager_data="md1"))
-        sess.flush()
-        sess.expunge_all()
-
-        eq_(
-            sess.query(Person).order_by(Person.person_id).all(),
-            [
-                Person(name="p1"),
-                Employee(name="e1", employee_data="ed1"),
-                Manager(name="m1", employee_data="ed2", manager_data="md1"),
-            ],
-        )
-        sess.expunge_all()
-
-        eq_(
-            sess.query(Employee).order_by(Person.person_id).all(),
-            [
-                Employee(name="e1", employee_data="ed1"),
-                Manager(name="m1", employee_data="ed2", manager_data="md1"),
-            ],
-        )
-        sess.expunge_all()
-
-        eq_(
-            sess.query(Manager).order_by(Person.person_id).all(),
-            [Manager(name="m1", employee_data="ed2", manager_data="md1")],
-        )
-        sess.expunge_all()
-
-        def go():
-            with expect_deprecated_20(with_polymorphic_dep):
-                eq_(
-                    sess.query(Person)
-                    .with_polymorphic("*")
-                    .order_by(Person.person_id)
-                    .all(),
-                    [
-                        Person(name="p1"),
-                        Employee(name="e1", employee_data="ed1"),
-                        Manager(
-                            name="m1", employee_data="ed2", manager_data="md1"
-                        ),
-                    ],
-                )
-
-        self.assert_sql_count(testing.db, go, 1)
-
-
-class SingleFromPolySelectableTest(
-    fixtures.DeclarativeMappedTest, AssertsCompiledSQL
-):
-    __dialect__ = "default"
-
-    @classmethod
-    def setup_classes(cls, with_polymorphic=None, include_sub_defaults=False):
-        Base = cls.DeclarativeBasic
-
-        class Employee(Base):
-            __tablename__ = "employee"
-            id = Column(Integer, primary_key=True)
-            name = Column(String(50))
-            type = Column(String(50))
-
-            __mapper_args__ = {
-                "polymorphic_identity": "employee",
-                "polymorphic_on": type,
-            }
-
-        class Engineer(Employee):
-            __tablename__ = "engineer"
-            id = Column(Integer, ForeignKey("employee.id"), primary_key=True)
-            engineer_info = Column(String(50))
-            manager_id = Column(ForeignKey("manager.id"))
-            __mapper_args__ = {"polymorphic_identity": "engineer"}
-
-        class Manager(Employee):
-            __tablename__ = "manager"
-            id = Column(Integer, ForeignKey("employee.id"), primary_key=True)
-            manager_data = Column(String(50))
-            __mapper_args__ = {"polymorphic_identity": "manager"}
-
-        class Boss(Manager):
-            __mapper_args__ = {"polymorphic_identity": "boss"}
-
-    def _with_poly_fixture(self):
-        employee = self.classes.Employee.__table__
-        engineer = self.classes.Engineer.__table__
-        manager = self.classes.Manager.__table__
-
-        poly = (
-            select(
-                employee.c.id,
-                employee.c.type,
-                employee.c.name,
-                manager.c.manager_data,
-                null().label("engineer_info"),
-                null().label("manager_id"),
-            )
-            .select_from(employee.join(manager))
-            .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
-            .union_all(
-                select(
-                    employee.c.id,
-                    employee.c.type,
-                    employee.c.name,
-                    null().label("manager_data"),
-                    engineer.c.engineer_info,
-                    engineer.c.manager_id,
-                )
-                .select_from(employee.join(engineer))
-                .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
-            )
-            .alias()
-        )
-
-        return poly
-
-    def test_query_wpoly_single_inh_subclass(self):
-        Boss = self.classes.Boss
-
-        poly = self._with_poly_fixture()
-
-        s = fixture_session()
-
-        with expect_deprecated_20(with_polymorphic_dep):
-            q = s.query(Boss).with_polymorphic(Boss, poly)
-        self.assert_compile(
-            q,
-            "SELECT anon_1.employee_id AS anon_1_employee_id, "
-            "anon_1.employee_name AS anon_1_employee_name, "
-            "anon_1.employee_type AS anon_1_employee_type, "
-            "anon_1.manager_manager_data AS anon_1_manager_manager_data "
-            "FROM (SELECT employee.id AS employee_id, employee.type "
-            "AS employee_type, employee.name AS employee_name, "
-            "manager.manager_data AS manager_manager_data, "
-            "NULL AS engineer_info, NULL AS manager_id FROM employee "
-            "JOIN manager ON employee.id = manager.id "
-            "UNION ALL SELECT employee.id AS employee_id, "
-            "employee.type AS employee_type, employee.name AS employee_name, "
-            "NULL AS manager_data, "
-            "engineer.engineer_info AS engineer_engineer_info, "
-            "engineer.manager_id AS engineer_manager_id "
-            "FROM employee JOIN engineer ON employee.id = engineer.id) "
-            "AS anon_1 WHERE anon_1.employee_type IN (__[POSTCOMPILE_type_1])",
-        )
-
-
-class SameNamedPropTwoPolymorphicSubClassesTest(fixtures.MappedTest):
-    """test pathing when two subclasses contain a different property
-    for the same name, and polymorphic loading is used.
-
-    #2614
-
-    """
-
-    run_setup_classes = "once"
-    run_setup_mappers = "once"
-    run_inserts = "once"
-    run_deletes = None
-
-    @classmethod
-    def define_tables(cls, metadata):
-        Table(
-            "a",
-            metadata,
-            Column(
-                "id", Integer, primary_key=True, test_needs_autoincrement=True
-            ),
-            Column("type", String(10)),
-        )
-        Table(
-            "b",
-            metadata,
-            Column("id", Integer, ForeignKey("a.id"), primary_key=True),
-        )
-        Table(
-            "btod",
-            metadata,
-            Column("bid", Integer, ForeignKey("b.id"), nullable=False),
-            Column("did", Integer, ForeignKey("d.id"), nullable=False),
-        )
-        Table(
-            "c",
-            metadata,
-            Column("id", Integer, ForeignKey("a.id"), primary_key=True),
-        )
-        Table(
-            "ctod",
-            metadata,
-            Column("cid", Integer, ForeignKey("c.id"), nullable=False),
-            Column("did", Integer, ForeignKey("d.id"), nullable=False),
-        )
-        Table(
-            "d",
-            metadata,
-            Column(
-                "id", Integer, primary_key=True, test_needs_autoincrement=True
-            ),
-        )
-
-    @classmethod
-    def setup_classes(cls):
-        class A(cls.Comparable):
-            pass
-
-        class B(A):
-            pass
-
-        class C(A):
-            pass
-
-        class D(cls.Comparable):
-            pass
-
-    @classmethod
-    def setup_mappers(cls):
-        A = cls.classes.A
-        B = cls.classes.B
-        C = cls.classes.C
-        D = cls.classes.D
-
-        cls.mapper_registry.map_imperatively(
-            A, cls.tables.a, polymorphic_on=cls.tables.a.c.type
-        )
-        cls.mapper_registry.map_imperatively(
-            B,
-            cls.tables.b,
-            inherits=A,
-            polymorphic_identity="b",
-            properties={"related": relationship(D, secondary=cls.tables.btod)},
-        )
-        cls.mapper_registry.map_imperatively(
-            C,
-            cls.tables.c,
-            inherits=A,
-            polymorphic_identity="c",
-            properties={"related": relationship(D, secondary=cls.tables.ctod)},
-        )
-        cls.mapper_registry.map_imperatively(D, cls.tables.d)
-
-    @classmethod
-    def insert_data(cls, connection):
-        B = cls.classes.B
-        C = cls.classes.C
-        D = cls.classes.D
-
-        session = Session(connection)
-
-        d = D()
-        session.add_all([B(related=[d]), C(related=[d])])
-        session.commit()
-
-    def test_fixed_w_poly_subquery(self):
-        A = self.classes.A
-        B = self.classes.B
-        C = self.classes.C
-        D = self.classes.D
-
-        session = fixture_session()
-        d = session.query(D).one()
-
-        def go():
-            # NOTE: subqueryload is broken for this case, first found
-            # when cartesian product detection was added.
-            with expect_deprecated_20(with_polymorphic_dep):
-                for a in (
-                    session.query(A)
-                    .with_polymorphic([B, C])
-                    .options(selectinload(B.related), selectinload(C.related))
-                ):
-                    eq_(a.related, [d])
-
-        self.assert_sql_count(testing.db, go, 3)
-
-    def test_fixed_w_poly_joined(self):
-        A = self.classes.A
-        B = self.classes.B
-        C = self.classes.C
-        D = self.classes.D
-
-        session = fixture_session()
-        d = session.query(D).one()
-
-        def go():
-            with expect_deprecated_20(with_polymorphic_dep):
-                for a in (
-                    session.query(A)
-                    .with_polymorphic([B, C])
-                    .options(joinedload(B.related), joinedload(C.related))
-                ):
-                    eq_(a.related, [d])
-
-        self.assert_sql_count(testing.db, go, 1)
-
-
-class ConcreteTest(fixtures.MappedTest):
-    @classmethod
-    def define_tables(cls, metadata):
-        Table(
-            "companies",
-            metadata,
-            Column(
-                "id", Integer, primary_key=True, test_needs_autoincrement=True
-            ),
-            Column("name", String(50)),
-        )
-        Table(
-            "employees",
-            metadata,
-            Column(
-                "employee_id",
-                Integer,
-                primary_key=True,
-                test_needs_autoincrement=True,
-            ),
-            Column("name", String(50)),
-            Column("company_id", Integer, ForeignKey("companies.id")),
-        )
-        Table(
-            "managers",
-            metadata,
-            Column(
-                "employee_id",
-                Integer,
-                primary_key=True,
-                test_needs_autoincrement=True,
-            ),
-            Column("name", String(50)),
-            Column("manager_data", String(50)),
-            Column("company_id", Integer, ForeignKey("companies.id")),
-        )
-        Table(
-            "engineers",
-            metadata,
-            Column(
-                "employee_id",
-                Integer,
-                primary_key=True,
-                test_needs_autoincrement=True,
-            ),
-            Column("name", String(50)),
-            Column("engineer_info", String(50)),
-            Column("company_id", Integer, ForeignKey("companies.id")),
-        )
-        Table(
-            "hackers",
-            metadata,
-            Column(
-                "employee_id",
-                Integer,
-                primary_key=True,
-                test_needs_autoincrement=True,
-            ),
-            Column("name", String(50)),
-            Column("engineer_info", String(50)),
-            Column("company_id", Integer, ForeignKey("companies.id")),
-            Column("nickname", String(50)),
-        )
-
-    @classmethod
-    def setup_classes(cls):
-        class Employee(cls.Basic):
-            def __init__(self, name):
-                self.name = name
-
-            def __repr__(self):
-                return self.__class__.__name__ + " " + self.name
-
-        class Manager(Employee):
-            def __init__(self, name, manager_data):
-                self.name = name
-                self.manager_data = manager_data
-
-            def __repr__(self):
-                return (
-                    self.__class__.__name__
-                    + " "
-                    + self.name
-                    + " "
-                    + self.manager_data
-                )
-
-        class Engineer(Employee):
-            def __init__(self, name, engineer_info):
-                self.name = name
-                self.engineer_info = engineer_info
-
-            def __repr__(self):
-                return (
-                    self.__class__.__name__
-                    + " "
-                    + self.name
-                    + " "
-                    + self.engineer_info
-                )
-
-        class Hacker(Engineer):
-            def __init__(self, name, nickname, engineer_info):
-                self.name = name
-                self.nickname = nickname
-                self.engineer_info = engineer_info
-
-            def __repr__(self):
-                return (
-                    self.__class__.__name__
-                    + " "
-                    + self.name
-                    + " '"
-                    + self.nickname
-                    + "' "
-                    + self.engineer_info
-                )
-
-        class Company(cls.Basic):
-            pass
-
-    def test_without_default_polymorphic(self):
-        Employee, Engineer, Manager = self.classes(
-            "Employee", "Engineer", "Manager"
-        )
-        (Hacker,) = self.classes("Hacker")
-        (employees_table,) = self.tables("employees")
-        engineers_table, managers_table = self.tables("engineers", "managers")
-        (hackers_table,) = self.tables("hackers")
-
-        pjoin = polymorphic_union(
-            {
-                "employee": employees_table,
-                "manager": managers_table,
-                "engineer": engineers_table,
-                "hacker": hackers_table,
-            },
-            "type",
-            "pjoin",
-        )
-        pjoin2 = polymorphic_union(
-            {"engineer": engineers_table, "hacker": hackers_table},
-            "type",
-            "pjoin2",
-        )
-        employee_mapper = self.mapper_registry.map_imperatively(
-            Employee, employees_table, polymorphic_identity="employee"
-        )
-        self.mapper_registry.map_imperatively(
-            Manager,
-            managers_table,
-            inherits=employee_mapper,
-            concrete=True,
-            polymorphic_identity="manager",
-        )
-        engineer_mapper = self.mapper_registry.map_imperatively(
-            Engineer,
-            engineers_table,
-            inherits=employee_mapper,
-            concrete=True,
-            polymorphic_identity="engineer",
-        )
-        self.mapper_registry.map_imperatively(
-            Hacker,
-            hackers_table,
-            inherits=engineer_mapper,
-            concrete=True,
-            polymorphic_identity="hacker",
-        )
-        session = fixture_session()
-        jdoe = Employee("Jdoe")
-        tom = Manager("Tom", "knows how to manage things")
-        jerry = Engineer("Jerry", "knows how to program")
-        hacker = Hacker("Kurt", "Badass", "knows how to hack")
-        session.add_all((jdoe, tom, jerry, hacker))
-        session.flush()
-
-        with expect_deprecated_20(with_polymorphic_dep):
-            eq_(
-                len(
-                    session.connection()
-                    .execute(
-                        session.query(Employee)
-                        .with_polymorphic("*", pjoin, pjoin.c.type)
-                        .statement
-                    )
-                    .fetchall()
-                ),
-                4,
-            )
-        eq_(session.get(Employee, jdoe.employee_id), jdoe)
-        eq_(session.get(Engineer, jerry.employee_id), jerry)
-        with expect_deprecated_20(with_polymorphic_dep):
-            eq_(
-                set(
-                    [
-                        repr(x)
-                        for x in session.query(Employee).with_polymorphic(
-                            "*", pjoin, pjoin.c.type
-                        )
-                    ]
-                ),
-                set(
-                    [
-                        "Employee Jdoe",
-                        "Engineer Jerry knows how to program",
-                        "Manager Tom knows how to manage things",
-                        "Hacker Kurt 'Badass' knows how to hack",
-                    ]
-                ),
-            )
-        eq_(
-            set([repr(x) for x in session.query(Manager)]),
-            set(["Manager Tom knows how to manage things"]),
-        )
-        with expect_deprecated_20(with_polymorphic_dep):
-            eq_(
-                set(
-                    [
-                        repr(x)
-                        for x in session.query(Engineer).with_polymorphic(
-                            "*", pjoin2, pjoin2.c.type
-                        )
-                    ]
-                ),
-                set(
-                    [
-                        "Engineer Jerry knows how to program",
-                        "Hacker Kurt 'Badass' knows how to hack",
-                    ]
-                ),
-            )
-        eq_(
-            set([repr(x) for x in session.query(Hacker)]),
-            set(["Hacker Kurt 'Badass' knows how to hack"]),
-        )
-
-        # test adaption of the column by wrapping the query in a
-        # subquery
-
-        with testing.expect_deprecated(
-            r"The Query.from_self\(\) method", with_polymorphic_dep
-        ):
-            eq_(
-                len(
-                    session.connection()
-                    .execute(
-                        session.query(Engineer)
-                        .with_polymorphic("*", pjoin2, pjoin2.c.type)
-                        .from_self()
-                        .statement
-                    )
-                    .fetchall()
-                ),
-                2,
-            )
-        with testing.expect_deprecated(
-            r"The Query.from_self\(\) method", with_polymorphic_dep
-        ):
-            eq_(
-                set(
-                    [
-                        repr(x)
-                        for x in session.query(Engineer)
-                        .with_polymorphic("*", pjoin2, pjoin2.c.type)
-                        .from_self()
-                    ]
-                ),
-                set(
-                    [
-                        "Engineer Jerry knows how to program",
-                        "Hacker Kurt 'Badass' knows how to hack",
-                    ]
-                ),
-            )
index 60235bd86cd28d53f333449903bf9c6976723d23..8a218847df5571de5187590f62400ae296ebf737 100644 (file)
@@ -1481,44 +1481,6 @@ class _PolymorphicTestBase(fixtures.NoCache):
             expected,
         )
 
-    def test_self_referential_two(self):
-
-        sess = fixture_session()
-        palias = aliased(Person)
-        expected = [(m1, e1), (m1, e2), (m1, b1)]
-
-        with testing.expect_deprecated(r"The Query.from_self\(\) method"):
-            eq_(
-                sess.query(Person, palias)
-                .filter(Person.company_id == palias.company_id)
-                .filter(Person.name == "dogbert")
-                .filter(Person.person_id > palias.person_id)
-                .from_self()
-                .order_by(Person.person_id, palias.person_id)
-                .all(),
-                expected,
-            )
-
-    def test_self_referential_two_point_five(self):
-        """Using two aliases, the above case works."""
-        sess = fixture_session()
-        palias = aliased(Person)
-        palias2 = aliased(Person)
-
-        expected = [(m1, e1), (m1, e2), (m1, b1)]
-
-        with testing.expect_deprecated(r"The Query.from_self\(\) method"):
-            eq_(
-                sess.query(palias, palias2)
-                .filter(palias.company_id == palias2.company_id)
-                .filter(palias.name == "dogbert")
-                .filter(palias.person_id > palias2.person_id)
-                .from_self()
-                .order_by(palias.person_id, palias2.person_id)
-                .all(),
-                expected,
-            )
-
     def test_self_referential_two_future(self):
         # TODO: this is the SECOND test *EVER* of an aliased class of
         # an aliased class.
index fa66f90b8266a502de475899323c235b90b961b0..34cae1f412cc95bee188d0175e8193eec5f655e9 100644 (file)
@@ -1819,31 +1819,6 @@ class SubClassToSubClassMultiTest(AssertsCompiledSQL, fixtures.MappedTest):
             "JOIN ep2 ON base2.id = ep2.base2_id",
         )
 
-    def test_six_legacy(self):
-        Parent, Base1, Base2, Sub1, Sub2, EP1, EP2 = self._classes()
-
-        s = fixture_session()
-
-        # as of from_self() changing in
-        # I3abfb45dd6e50f84f29d39434caa0b550ce27864,
-        # this query is coming out instead which is equivalent, but not
-        # totally sure where this happens
-
-        with testing.expect_deprecated(r"The Query.from_self\(\) method"):
-            self.assert_compile(
-                s.query(Sub2).from_self().join(Sub2.ep1).join(Sub2.ep2),
-                "SELECT anon_1.sub2_id AS anon_1_sub2_id, "
-                "anon_1.base2_base1_id AS anon_1_base2_base1_id, "
-                "anon_1.base2_data AS anon_1_base2_data, "
-                "anon_1.sub2_subdata AS anon_1_sub2_subdata "
-                "FROM (SELECT sub2.id AS sub2_id, base2.id AS base2_id, "
-                "base2.base1_id AS base2_base1_id, base2.data AS base2_data, "
-                "sub2.subdata AS sub2_subdata "
-                "FROM base2 JOIN sub2 ON base2.id = sub2.id) AS anon_1 "
-                "JOIN ep1 ON anon_1.sub2_id = ep1.base2_id "
-                "JOIN ep2 ON anon_1.sub2_id = ep2.base2_id",
-            )
-
     def test_six(self):
         Parent, Base1, Base2, Sub1, Sub2, EP1, EP2 = self._classes()
 
@@ -1879,48 +1854,6 @@ class SubClassToSubClassMultiTest(AssertsCompiledSQL, fixtures.MappedTest):
             "JOIN ep2 ON anon_1.sub2_id = ep2.base2_id",
         )
 
-    def test_seven_legacy(self):
-        Parent, Base1, Base2, Sub1, Sub2, EP1, EP2 = self._classes()
-
-        s = fixture_session()
-
-        # as of from_self() changing in
-        # I3abfb45dd6e50f84f29d39434caa0b550ce27864,
-        # this query is coming out instead which is equivalent, but not
-        # totally sure where this happens
-        with testing.expect_deprecated(r"The Query.from_self\(\) method"):
-
-            self.assert_compile(
-                # adding Sub2 to the entities list helps it,
-                # otherwise the joins for Sub2.ep1/ep2 don't have columns
-                # to latch onto.   Can't really make it better than this
-                s.query(Parent, Sub2)
-                .join(Parent.sub1)
-                .join(Sub1.sub2)
-                .from_self()
-                .join(Sub2.ep1)
-                .join(Sub2.ep2),
-                "SELECT anon_1.parent_id AS anon_1_parent_id, "
-                "anon_1.parent_data AS anon_1_parent_data, "
-                "anon_1.sub2_id AS anon_1_sub2_id, "
-                "anon_1.base2_base1_id AS anon_1_base2_base1_id, "
-                "anon_1.base2_data AS anon_1_base2_data, "
-                "anon_1.sub2_subdata AS anon_1_sub2_subdata "
-                "FROM (SELECT parent.id AS parent_id, "
-                "parent.data AS parent_data, "
-                "sub2.id AS sub2_id, "
-                "base2.id AS base2_id, "
-                "base2.base1_id AS base2_base1_id, "
-                "base2.data AS base2_data, "
-                "sub2.subdata AS sub2_subdata "
-                "FROM parent JOIN (base1 JOIN sub1 ON base1.id = sub1.id) "
-                "ON parent.id = sub1.parent_id JOIN "
-                "(base2 JOIN sub2 ON base2.id = sub2.id) "
-                "ON base1.id = base2.base1_id) AS anon_1 "
-                "JOIN ep1 ON anon_1.sub2_id = ep1.base2_id "
-                "JOIN ep2 ON anon_1.sub2_id = ep2.base2_id",
-            )
-
     def test_seven(self):
         Parent, Base1, Base2, Sub1, Sub2, EP1, EP2 = self._classes()
 
index 1b3c6db74da2f9b1ef0f484677db41b309eb3433..8424332198d5c38e31d198277742c06e0d9f3eb1 100644 (file)
@@ -6,7 +6,6 @@ from sqlalchemy import func
 from sqlalchemy import inspect
 from sqlalchemy import Integer
 from sqlalchemy import literal
-from sqlalchemy import literal_column
 from sqlalchemy import null
 from sqlalchemy import select
 from sqlalchemy import String
@@ -306,35 +305,6 @@ class SingleInheritanceTest(testing.AssertsCompiledSQL, fixtures.MappedTest):
             [e2id],
         )
 
-    def test_from_self_legacy(self):
-        Engineer = self.classes.Engineer
-
-        sess = fixture_session()
-        with testing.expect_deprecated(r"The Query.from_self\(\) method"):
-            self.assert_compile(
-                sess.query(Engineer).from_self(),
-                "SELECT anon_1.employees_employee_id AS "
-                "anon_1_employees_employee_id, "
-                "anon_1.employees_name AS "
-                "anon_1_employees_name, "
-                "anon_1.employees_manager_data AS "
-                "anon_1_employees_manager_data, "
-                "anon_1.employees_engineer_info AS "
-                "anon_1_employees_engineer_info, "
-                "anon_1.employees_type AS "
-                "anon_1_employees_type FROM (SELECT "
-                "employees.employee_id AS "
-                "employees_employee_id, employees.name AS "
-                "employees_name, employees.manager_data AS "
-                "employees_manager_data, "
-                "employees.engineer_info AS "
-                "employees_engineer_info, employees.type "
-                "AS employees_type FROM employees WHERE "
-                "employees.type IN (__[POSTCOMPILE_type_1])) AS "
-                "anon_1",
-                use_default_dialect=True,
-            )
-
     def test_from_subq(self):
         Engineer = self.classes.Engineer
 
@@ -501,21 +471,6 @@ class SingleInheritanceTest(testing.AssertsCompiledSQL, fixtures.MappedTest):
             "GROUP BY employees.employee_id HAVING employees.name = :name_1",
         )
 
-    def test_from_self_count(self):
-        Engineer = self.classes.Engineer
-
-        sess = fixture_session()
-        col = func.count(literal_column("*"))
-        with testing.expect_deprecated(r"The Query.from_self\(\) method"):
-            self.assert_compile(
-                sess.query(Engineer.employee_id).from_self(col),
-                "SELECT count(*) AS count_1 "
-                "FROM (SELECT employees.employee_id AS employees_employee_id "
-                "FROM employees "
-                "WHERE employees.type IN (__[POSTCOMPILE_type_1])) AS anon_1",
-                use_default_dialect=True,
-            )
-
     def test_select_from_count(self):
         Manager, Engineer = (self.classes.Manager, self.classes.Engineer)
 
index bae2a9707aab2f93c3139fff4f572119ca4ab477..64db9a893f97dfd83bb6bcf88d9990f2d72b1d15 100644 (file)
@@ -3,24 +3,17 @@ from unittest.mock import call
 from unittest.mock import Mock
 
 import sqlalchemy as sa
-from sqlalchemy import and_
 from sqlalchemy import cast
-from sqlalchemy import column
 from sqlalchemy import desc
 from sqlalchemy import event
 from sqlalchemy import exc as sa_exc
 from sqlalchemy import ForeignKey
 from sqlalchemy import func
 from sqlalchemy import Integer
-from sqlalchemy import join
-from sqlalchemy import LABEL_STYLE_TABLENAME_PLUS_COL
-from sqlalchemy import lateral
 from sqlalchemy import literal_column
 from sqlalchemy import MetaData
-from sqlalchemy import or_
 from sqlalchemy import select
 from sqlalchemy import String
-from sqlalchemy import table
 from sqlalchemy import testing
 from sqlalchemy import text
 from sqlalchemy import true
@@ -35,7 +28,6 @@ from sqlalchemy.orm import column_property
 from sqlalchemy.orm import configure_mappers
 from sqlalchemy.orm import contains_alias
 from sqlalchemy.orm import contains_eager
-from sqlalchemy.orm import declarative_base
 from sqlalchemy.orm import defaultload
 from sqlalchemy.orm import defer
 from sqlalchemy.orm import deferred
@@ -52,7 +44,6 @@ from sqlalchemy.orm import sessionmaker
 from sqlalchemy.orm import subqueryload
 from sqlalchemy.orm import synonym
 from sqlalchemy.orm import undefer
-from sqlalchemy.orm import with_loader_criteria
 from sqlalchemy.orm import with_parent
 from sqlalchemy.orm import with_polymorphic
 from sqlalchemy.orm.collections import attribute_mapped_collection
@@ -68,12 +59,9 @@ from sqlalchemy.testing import fixtures
 from sqlalchemy.testing import is_
 from sqlalchemy.testing import is_true
 from sqlalchemy.testing import mock
-from sqlalchemy.testing.assertsql import CompiledSQL
-from sqlalchemy.testing.fixtures import ComparableEntity
 from sqlalchemy.testing.fixtures import fixture_session
 from sqlalchemy.testing.schema import Column
 from sqlalchemy.testing.schema import Table
-from sqlalchemy.testing.util import resolve_lambda
 from . import _fixtures
 from .inheritance import _poly_fixtures
 from .inheritance._poly_fixtures import Manager
@@ -125,11 +113,6 @@ query_wparent_dep = (
 
 query_get_dep = r"The Query.get\(\) method is considered legacy as of the 1.x"
 
-sef_dep = (
-    r"The Query.select_entity_from\(\) method is considered "
-    "legacy as of the 1.x"
-)
-
 with_polymorphic_dep = (
     r"The Query.with_polymorphic\(\) method is considered legacy as of "
     r"the 1.x series of SQLAlchemy and will be removed in 2.0"
@@ -459,6 +442,24 @@ class DeprecatedQueryTest(_fixtures.FixtureTest, AssertsCompiledSQL):
         # this doesn't evaluate anything because it's a net-negative
         eq_(q[-2:-5], [])
 
+    def test_deprecated_select_coercion_join_target(self):
+        User = self.classes.User
+        addresses = self.tables.addresses
+
+        s = addresses.select()
+        sess = fixture_session()
+        with testing.expect_deprecated(
+            "Implicit coercion of SELECT and " "textual SELECT constructs"
+        ):
+            self.assert_compile(
+                sess.query(User).join(s, User.addresses),
+                "SELECT users.id AS users_id, users.name AS users_name "
+                "FROM users JOIN (SELECT addresses.id AS id, "
+                "addresses.user_id AS user_id, addresses.email_address "
+                "AS email_address FROM addresses) AS anon_1 "
+                "ON users.id = anon_1.user_id",
+            )
+
     def test_deprecated_negative_slices_compile(self):
         User = self.classes.User
 
@@ -497,92 +498,6 @@ class DeprecatedQueryTest(_fixtures.FixtureTest, AssertsCompiledSQL):
                 ],
             )
 
-    def test_aliased(self):
-        User = self.classes.User
-
-        s = fixture_session()
-
-        with testing.expect_deprecated_20(join_aliased_dep):
-            q1 = s.query(User).join(User.addresses, aliased=True)
-
-        self.assert_compile(
-            q1,
-            "SELECT users.id AS users_id, users.name AS users_name "
-            "FROM users JOIN addresses AS addresses_1 "
-            "ON users.id = addresses_1.user_id",
-        )
-
-    def test_from_joinpoint(self):
-        User = self.classes.User
-        Address = self.classes.Address
-
-        s = fixture_session()
-
-        u1 = aliased(User)
-
-        with testing.expect_deprecated_20(join_aliased_dep):
-            q1 = (
-                s.query(u1)
-                .join(u1.addresses)
-                .join(Address.user, from_joinpoint=True)
-            )
-
-        self.assert_compile(
-            q1,
-            "SELECT users_1.id AS users_1_id, users_1.name AS users_1_name "
-            "FROM users AS users_1 JOIN addresses ON users_1.id = "
-            "addresses.user_id JOIN users ON users.id = addresses.user_id",
-        )
-
-    def test_multiple_joins(self):
-        User = self.classes.User
-        Address = self.classes.Address
-
-        s = fixture_session()
-
-        u1 = aliased(User)
-
-        with testing.expect_deprecated_20(join_chain_dep):
-            q1 = s.query(u1).join(u1.addresses, Address.user)
-
-        self.assert_compile(
-            q1,
-            "SELECT users_1.id AS users_1_id, users_1.name AS users_1_name "
-            "FROM users AS users_1 JOIN addresses ON users_1.id = "
-            "addresses.user_id JOIN users ON users.id = addresses.user_id",
-        )
-
-    def test_multiple_entities(self):
-        User = self.classes.User
-        Address = self.classes.Address
-        Dingaling = self.classes.Dingaling
-
-        s = fixture_session()
-
-        u1 = aliased(User)
-
-        with testing.expect_deprecated_20(join_chain_dep):
-            q1 = s.query(u1).join(Address, User)
-
-        self.assert_compile(
-            q1,
-            "SELECT users_1.id AS users_1_id, users_1.name AS users_1_name "
-            "FROM users AS users_1 JOIN addresses ON users_1.id = "
-            "addresses.user_id JOIN users ON users.id = addresses.user_id",
-        )
-
-        with testing.expect_deprecated_20(join_chain_dep):
-            q1 = s.query(u1).join(Address, User, Dingaling)
-
-        self.assert_compile(
-            q1,
-            "SELECT users_1.id AS users_1_id, users_1.name AS users_1_name "
-            "FROM users AS users_1 JOIN addresses "
-            "ON users_1.id = addresses.user_id "
-            "JOIN users ON users.id = addresses.user_id "
-            "JOIN dingalings ON addresses.id = dingalings.address_id",
-        )
-
     def test_invalid_column(self):
         User = self.classes.User
 
@@ -597,21 +512,6 @@ class DeprecatedQueryTest(_fixtures.FixtureTest, AssertsCompiledSQL):
             "SELECT users.id AS users_id, users.name AS users_name FROM users",
         )
 
-    def test_via_textasfrom_select_from(self):
-        User = self.classes.User
-        s = fixture_session()
-
-        with self._expect_implicit_subquery():
-            eq_(
-                s.query(User)
-                .select_entity_from(
-                    text("select * from users").columns(User.id, User.name)
-                )
-                .order_by(User.id)
-                .all(),
-                [User(id=7), User(id=8), User(id=9), User(id=10)],
-            )
-
     def test_text_as_column(self):
         User = self.classes.User
 
@@ -646,50 +546,6 @@ class DeprecatedQueryTest(_fixtures.FixtureTest, AssertsCompiledSQL):
         ):
             s.query(User).as_scalar()
 
-    def test_select_entity_from_crit(self):
-        User, users = self.classes.User, self.tables.users
-
-        sel = users.select()
-        sess = fixture_session()
-
-        with self._expect_implicit_subquery():
-            eq_(
-                sess.query(User)
-                .select_entity_from(sel)
-                .filter(User.id.in_([7, 8]))
-                .all(),
-                [User(name="jack", id=7), User(name="ed", id=8)],
-            )
-
-    def test_select_entity_from_select(self):
-        User, users = self.classes.User, self.tables.users
-
-        sess = fixture_session()
-        with self._expect_implicit_subquery():
-            self.assert_compile(
-                sess.query(User.name).select_entity_from(
-                    users.select().where(users.c.id > 5)
-                ),
-                "SELECT anon_1.name AS anon_1_name FROM "
-                "(SELECT users.id AS id, users.name AS name FROM users "
-                "WHERE users.id > :id_1) AS anon_1",
-            )
-
-    def test_select_entity_from_q_statement(self):
-        User = self.classes.User
-
-        sess = fixture_session()
-
-        q = sess.query(User)
-        with self._expect_implicit_subquery():
-            q = sess.query(User).select_entity_from(q.statement)
-        self.assert_compile(
-            q.filter(User.name == "ed"),
-            "SELECT anon_1.id AS anon_1_id, anon_1.name AS anon_1_name "
-            "FROM (SELECT users.id AS id, users.name AS name FROM "
-            "users) AS anon_1 WHERE anon_1.name = :name_1",
-        )
-
     def test_select_from_q_statement_no_aliasing(self):
         User = self.classes.User
         sess = fixture_session()
@@ -704,90 +560,6 @@ class DeprecatedQueryTest(_fixtures.FixtureTest, AssertsCompiledSQL):
             "users) AS anon_1 WHERE users.name = :name_1",
         )
 
-    def test_from_alias_three(self):
-        User, addresses, users = (
-            self.classes.User,
-            self.tables.addresses,
-            self.tables.users,
-        )
-
-        query = (
-            users.select()
-            .where(users.c.id == 7)
-            .union(users.select().where(users.c.id > 7))
-            .alias("ulist")
-            .outerjoin(addresses)
-            .select()
-            .order_by(text("ulist.id"), addresses.c.id)
-        )
-        sess = fixture_session()
-
-        # better way.  use select_entity_from()
-        def go():
-            with self._expect_implicit_subquery():
-                result = (
-                    sess.query(User)
-                    .select_entity_from(query)
-                    .options(contains_eager(User.addresses))
-                    .all()
-                )
-            assert self.static.user_address_result == result
-
-        self.assert_sql_count(testing.db, go, 1)
-
-    def test_from_alias_four(self):
-        User, addresses, users = (
-            self.classes.User,
-            self.tables.addresses,
-            self.tables.users,
-        )
-
-        sess = fixture_session()
-
-        # same thing, but alias addresses, so that the adapter
-        # generated by select_entity_from() is wrapped within
-        # the adapter created by contains_eager()
-        adalias = addresses.alias()
-        query = (
-            users.select()
-            .where(users.c.id == 7)
-            .union(users.select().where(users.c.id > 7))
-            .alias("ulist")
-            .outerjoin(adalias)
-            .select()
-            .order_by(text("ulist.id"), adalias.c.id)
-        )
-
-        def go():
-            with self._expect_implicit_subquery():
-                result = (
-                    sess.query(User)
-                    .select_entity_from(query)
-                    .options(contains_eager(User.addresses, alias=adalias))
-                    .all()
-                )
-            assert self.static.user_address_result == result
-
-        self.assert_sql_count(testing.db, go, 1)
-
-    def test_select(self):
-        users = self.tables.users
-
-        sess = fixture_session()
-
-        with self._expect_implicit_subquery():
-            stmt = sess.query(users).select_entity_from(users.select())
-
-        with testing.expect_deprecated_20(r"The Query.with_labels\(\)"):
-            stmt = stmt.apply_labels().statement
-        self.assert_compile(
-            stmt,
-            "SELECT users.id AS users_id, users.name AS users_name "
-            "FROM users, "
-            "(SELECT users.id AS id, users.name AS name FROM users) "
-            "AS anon_1",
-        )
-
     def test_apply_labels(self):
         User = self.classes.User
 
@@ -816,5137 +588,1955 @@ class DeprecatedQueryTest(_fixtures.FixtureTest, AssertsCompiledSQL):
             "SELECT users.id AS users_id, users.name AS users_name FROM users",
         )
 
-    def test_join(self):
-        users, Address, User = (
-            self.tables.users,
-            self.classes.Address,
-            self.classes.User,
-        )
-
-        # mapper(User, users, properties={"addresses": relationship(Address)})
-        # mapper(Address, addresses)
 
-        sel = users.select().where(users.c.id.in_([7, 8]))
-        sess = fixture_session()
+class LazyLoadOptSpecificityTest(fixtures.DeclarativeMappedTest):
+    """test for [ticket:3963]"""
 
-        with self._expect_implicit_subquery():
-            result = (
-                sess.query(User)
-                .select_entity_from(sel)
-                .join(User.addresses)
-                .add_entity(Address)
-                .order_by(User.id)
-                .order_by(Address.id)
-                .all()
-            )
+    @classmethod
+    def setup_classes(cls):
+        Base = cls.DeclarativeBasic
 
-        eq_(
-            result,
-            [
-                (
-                    User(name="jack", id=7),
-                    Address(user_id=7, email_address="jack@bean.com", id=1),
-                ),
-                (
-                    User(name="ed", id=8),
-                    Address(user_id=8, email_address="ed@wood.com", id=2),
-                ),
-                (
-                    User(name="ed", id=8),
-                    Address(user_id=8, email_address="ed@bettyboop.com", id=3),
-                ),
-                (
-                    User(name="ed", id=8),
-                    Address(user_id=8, email_address="ed@lala.com", id=4),
-                ),
-            ],
-        )
+        class A(Base):
+            __tablename__ = "a"
+            id = Column(Integer, primary_key=True)
+            bs = relationship("B")
 
-        adalias = aliased(Address)
-        with self._expect_implicit_subquery():
-            result = (
-                sess.query(User)
-                .select_entity_from(sel)
-                .join(adalias, User.addresses)
-                .add_entity(adalias)
-                .order_by(User.id)
-                .order_by(adalias.id)
-                .all()
-            )
-        eq_(
-            result,
-            [
-                (
-                    User(name="jack", id=7),
-                    Address(user_id=7, email_address="jack@bean.com", id=1),
-                ),
-                (
-                    User(name="ed", id=8),
-                    Address(user_id=8, email_address="ed@wood.com", id=2),
-                ),
-                (
-                    User(name="ed", id=8),
-                    Address(user_id=8, email_address="ed@bettyboop.com", id=3),
-                ),
-                (
-                    User(name="ed", id=8),
-                    Address(user_id=8, email_address="ed@lala.com", id=4),
-                ),
-            ],
-        )
+        class B(Base):
+            __tablename__ = "b"
+            id = Column(Integer, primary_key=True)
+            a_id = Column(ForeignKey("a.id"))
+            cs = relationship("C")
 
-    def test_more_joins(self):
-        (users, Keyword, User) = (
-            self.tables.users,
-            self.classes.Keyword,
-            self.classes.User,
-        )
-        Order, Item = self.classes("Order", "Item")
+        class C(Base):
+            __tablename__ = "c"
+            id = Column(Integer, primary_key=True)
+            b_id = Column(ForeignKey("b.id"))
 
-        sess = fixture_session()
-        sel = users.select().where(users.c.id.in_([7, 8]))
+    @classmethod
+    def insert_data(cls, connection):
+        A, B, C = cls.classes("A", "B", "C")
+        s = Session(connection)
+        s.add(A(id=1, bs=[B(cs=[C()])]))
+        s.add(A(id=2))
+        s.commit()
 
-        with self._expect_implicit_subquery():
-            eq_(
-                sess.query(User)
-                .select_entity_from(sel)
-                .join(User.orders)
-                .join(Order.items)
-                .join(Item.keywords)
-                .filter(Keyword.name.in_(["red", "big", "round"]))
-                .all(),
-                [User(name="jack", id=7)],
-            )
+    def _run_tests(self, query, expected):
+        def go():
+            for a, _ in query:
+                for b in a.bs:
+                    b.cs
 
-        with self._expect_implicit_subquery():
-            eq_(
-                sess.query(User)
-                .select_entity_from(sel)
-                .join(User.orders, aliased=True)
-                .join(Order.items, aliased=True)
-                .join(Item.keywords, aliased=True)
-                .filter(Keyword.name.in_(["red", "big", "round"]))
-                .all(),
-                [User(name="jack", id=7)],
-            )
+        self.assert_sql_count(testing.db, go, expected)
 
-    def test_join_no_order_by(self):
-        User, users = self.classes.User, self.tables.users
 
-        sel = users.select().where(users.c.id.in_([7, 8]))
+class DynamicTest(_DynamicFixture, _fixtures.FixtureTest):
+    def test_negative_slice_access_raises(self):
+        User, Address = self._user_address_fixture()
         sess = fixture_session()
+        u1 = sess.get(User, 8)
 
-        with self._expect_implicit_subquery():
-            eq_(
-                sess.query(User).select_entity_from(sel).all(),
-                [User(name="jack", id=7), User(name="ed", id=8)],
-            )
+        with testing.expect_deprecated_20(
+            "Support for negative indexes for SQL index / slice"
+        ):
+            eq_(u1.addresses[-1], Address(id=4))
 
-    def test_replace_with_eager(self):
-        users, Address, User = (
-            self.tables.users,
-            self.classes.Address,
-            self.classes.User,
-        )
+        with testing.expect_deprecated_20(
+            "Support for negative indexes for SQL index / slice"
+        ):
+            eq_(u1.addresses[-5:-2], [Address(id=2)])
 
-        sel = users.select().where(users.c.id.in_([7, 8]))
-        sess = fixture_session()
+        with testing.expect_deprecated_20(
+            "Support for negative indexes for SQL index / slice"
+        ):
+            eq_(u1.addresses[-2], Address(id=3))
 
-        def go():
-            with self._expect_implicit_subquery():
-                eq_(
-                    sess.query(User)
-                    .options(joinedload(User.addresses))
-                    .select_entity_from(sel)
-                    .order_by(User.id)
-                    .all(),
-                    [
-                        User(id=7, addresses=[Address(id=1)]),
-                        User(
-                            id=8,
-                            addresses=[
-                                Address(id=2),
-                                Address(id=3),
-                                Address(id=4),
-                            ],
-                        ),
-                    ],
-                )
+        with testing.expect_deprecated_20(
+            "Support for negative indexes for SQL index / slice"
+        ):
+            eq_(u1.addresses[:-2], [Address(id=2)])
 
-        self.assert_sql_count(testing.db, go, 1)
-        sess.expunge_all()
 
-        def go():
-            with self._expect_implicit_subquery():
+class SessionTest(fixtures.RemovesEvents, _LocalFixture):
+    def test_transaction_attr(self):
+        s1 = Session(testing.db)
+
+        with testing.expect_deprecated_20(
+            "The Session.transaction attribute is considered legacy as "
+            "of the 1.x series"
+        ):
+            s1.transaction
+
+    def test_textual_execute(self, connection):
+        """test that Session.execute() converts to text()"""
+
+        users = self.tables.users
+
+        with Session(bind=connection) as sess:
+            sess.execute(users.insert(), dict(id=7, name="jack"))
+
+            with testing.expect_deprecated_20(
+                "Using plain strings to indicate SQL statements "
+                "without using the text"
+            ):
+                # use :bindparam style
                 eq_(
-                    sess.query(User)
-                    .options(joinedload(User.addresses))
-                    .select_entity_from(sel)
-                    .filter(User.id == 8)
-                    .order_by(User.id)
-                    .all(),
-                    [
-                        User(
-                            id=8,
-                            addresses=[
-                                Address(id=2),
-                                Address(id=3),
-                                Address(id=4),
-                            ],
-                        )
-                    ],
+                    sess.execute(
+                        "select * from users where id=:id", {"id": 7}
+                    ).fetchall(),
+                    [(7, "jack")],
                 )
 
-        self.assert_sql_count(testing.db, go, 1)
-        sess.expunge_all()
-
-        def go():
-            with self._expect_implicit_subquery():
+            with testing.expect_deprecated_20(
+                "Using plain strings to indicate SQL statements "
+                "without using the text"
+            ):
+                # use :bindparam style
                 eq_(
-                    sess.query(User)
-                    .options(joinedload(User.addresses))
-                    .select_entity_from(sel)
-                    .order_by(User.id)[1],
-                    User(
-                        id=8,
-                        addresses=[
-                            Address(id=2),
-                            Address(id=3),
-                            Address(id=4),
-                        ],
+                    sess.scalar(
+                        "select id from users where id=:id", {"id": 7}
                     ),
+                    7,
                 )
 
-        self.assert_sql_count(testing.db, go, 1)
-
-    def test_onclause_conditional_adaption(self):
-        Item, Order, orders, order_items, User = (
-            self.classes.Item,
-            self.classes.Order,
-            self.tables.orders,
-            self.tables.order_items,
-            self.classes.User,
-        )
+    def test_session_str(self):
+        s1 = Session(testing.db)
+        str(s1)
 
-        sess = fixture_session()
+    @testing.combinations(
+        {"mapper": None},
+        {"clause": None},
+        {"bind_arguments": {"mapper": None}, "clause": None},
+        {"bind_arguments": {}, "clause": None},
+    )
+    def test_bind_kwarg_deprecated(self, kw):
+        s1 = Session(testing.db)
 
-        oalias = orders.select()
+        for meth in s1.execute, s1.scalar:
+            m1 = mock.Mock(side_effect=s1.get_bind)
+            with mock.patch.object(s1, "get_bind", m1):
+                expr = text("select 1")
 
-        with self._expect_implicit_subquery(), _aliased_join_warning():
-            self.assert_compile(
-                sess.query(User)
-                .join(oalias, User.orders)
-                .join(
-                    Item,
-                    and_(
-                        Order.id == order_items.c.order_id,
-                        order_items.c.item_id == Item.id,
-                    ),
-                    from_joinpoint=True,
-                ),
-                "SELECT users.id AS users_id, users.name AS users_name "
-                "FROM users JOIN "
-                "(SELECT orders.id AS id, orders.user_id AS user_id, "
-                "orders.address_id AS address_id, orders.description "
-                "AS description, orders.isopen AS isopen FROM orders) "
-                "AS anon_1 ON users.id = anon_1.user_id JOIN items "
-                "ON anon_1.id = order_items.order_id "
-                "AND order_items.item_id = items.id",
-                use_default_dialect=True,
-            )
+                with testing.expect_deprecated_20(
+                    r"Passing bind arguments to Session.execute\(\) as "
+                    "keyword "
+                    "arguments is deprecated and will be removed SQLAlchemy "
+                    "2.0"
+                ):
+                    meth(expr, **kw)
 
+                bind_arguments = kw.pop("bind_arguments", None)
+                if bind_arguments:
+                    bind_arguments.update(kw)
 
-class SelfRefFromSelfTest(fixtures.MappedTest, AssertsCompiledSQL):
-    run_setup_mappers = "once"
-    run_inserts = "once"
-    run_deletes = None
-    __dialect__ = "default"
+                    if "clause" not in kw:
+                        bind_arguments["clause"] = expr
+                    eq_(m1.mock_calls, [call(**bind_arguments)])
+                else:
+                    if "clause" not in kw:
+                        kw["clause"] = expr
+                    eq_(m1.mock_calls, [call(**kw)])
 
-    @classmethod
-    def define_tables(cls, metadata):
-        Table(
-            "nodes",
-            metadata,
-            Column(
-                "id", Integer, primary_key=True, test_needs_autoincrement=True
-            ),
-            Column("parent_id", Integer, ForeignKey("nodes.id")),
-            Column("data", String(30)),
-        )
 
-    @classmethod
-    def setup_classes(cls):
-        class Node(cls.Comparable):
-            def append(self, node):
-                self.children.append(node)
+class DeprecatedInhTest(_poly_fixtures._Polymorphic):
+    def test_with_polymorphic(self):
+        Person = _poly_fixtures.Person
+        Engineer = _poly_fixtures.Engineer
 
-    @classmethod
-    def setup_mappers(cls):
-        Node, nodes = cls.classes.Node, cls.tables.nodes
+        with DeprecatedQueryTest._expect_implicit_subquery():
+            p_poly = with_polymorphic(Person, [Engineer], select(Person))
 
-        cls.mapper_registry.map_imperatively(
-            Node,
-            nodes,
-            properties={
-                "children": relationship(
-                    Node,
-                    lazy="select",
-                    join_depth=3,
-                    backref=backref("parent", remote_side=[nodes.c.id]),
-                )
-            },
+        is_true(
+            sa.inspect(p_poly).selectable.compare(select(Person).subquery())
         )
 
-    @classmethod
-    def insert_data(cls, connection):
-        Node = cls.classes.Node
-
-        sess = Session(connection)
-        n1 = Node(data="n1")
-        n1.append(Node(data="n11"))
-        n1.append(Node(data="n12"))
-        n1.append(Node(data="n13"))
-        n1.children[1].append(Node(data="n121"))
-        n1.children[1].append(Node(data="n122"))
-        n1.children[1].append(Node(data="n123"))
-        sess.add(n1)
-        sess.flush()
-        sess.close()
-
-    def test_from_self_inside_excludes_outside(self):
-        """test the propagation of aliased() from inside to outside
-        on a from_self()..
-        """
 
-        Node = self.classes.Node
+class DeprecatedMapperTest(_fixtures.FixtureTest, AssertsCompiledSQL):
+    __dialect__ = "default"
 
-        sess = fixture_session()
+    def test_deferred_scalar_loader_name_change(self):
+        class Foo:
+            pass
 
-        n1 = aliased(Node)
+        def myloader(*arg, **kw):
+            pass
 
-        # n1 is not inside the from_self(), so all cols must be maintained
-        # on the outside
-        with self._from_self_deprecated():
-            self.assert_compile(
-                sess.query(Node)
-                .filter(Node.data == "n122")
-                .from_self(n1, Node.id),
-                "SELECT nodes_1.id AS nodes_1_id, "
-                "nodes_1.parent_id AS nodes_1_parent_id, "
-                "nodes_1.data AS nodes_1_data, anon_1.nodes_id "
-                "AS anon_1_nodes_id "
-                "FROM nodes AS nodes_1, (SELECT nodes.id AS nodes_id, "
-                "nodes.parent_id AS nodes_parent_id, "
-                "nodes.data AS nodes_data FROM "
-                "nodes WHERE nodes.data = :data_1) AS anon_1",
-                use_default_dialect=True,
-            )
+        instrumentation.register_class(Foo)
+        manager = instrumentation.manager_of_class(Foo)
 
-        parent = aliased(Node)
-        grandparent = aliased(Node)
-        with self._from_self_deprecated():
-            q = (
-                sess.query(Node, parent, grandparent)
-                .join(parent, Node.parent)
-                .join(grandparent, parent.parent)
-                .filter(Node.data == "n122")
-                .filter(parent.data == "n12")
-                .filter(grandparent.data == "n1")
-                .from_self()
-                .limit(1)
-            )
+        with testing.expect_deprecated(
+            "The ClassManager.deferred_scalar_loader attribute is now named "
+            "expired_attribute_loader"
+        ):
+            manager.deferred_scalar_loader = myloader
 
-        # parent, grandparent *are* inside the from_self(), so they
-        # should get aliased to the outside.
-        self.assert_compile(
-            q,
-            "SELECT anon_1.nodes_id AS anon_1_nodes_id, "
-            "anon_1.nodes_parent_id AS anon_1_nodes_parent_id, "
-            "anon_1.nodes_data AS anon_1_nodes_data, "
-            "anon_1.nodes_1_id AS anon_1_nodes_1_id, "
-            "anon_1.nodes_1_parent_id AS anon_1_nodes_1_parent_id, "
-            "anon_1.nodes_1_data AS anon_1_nodes_1_data, "
-            "anon_1.nodes_2_id AS anon_1_nodes_2_id, "
-            "anon_1.nodes_2_parent_id AS anon_1_nodes_2_parent_id, "
-            "anon_1.nodes_2_data AS anon_1_nodes_2_data "
-            "FROM (SELECT nodes.id AS nodes_id, nodes.parent_id "
-            "AS nodes_parent_id, nodes.data AS nodes_data, "
-            "nodes_1.id AS nodes_1_id, "
-            "nodes_1.parent_id AS nodes_1_parent_id, "
-            "nodes_1.data AS nodes_1_data, nodes_2.id AS nodes_2_id, "
-            "nodes_2.parent_id AS nodes_2_parent_id, nodes_2.data AS "
-            "nodes_2_data FROM nodes JOIN nodes AS nodes_1 ON "
-            "nodes_1.id = nodes.parent_id JOIN nodes AS nodes_2 "
-            "ON nodes_2.id = nodes_1.parent_id "
-            "WHERE nodes.data = :data_1 AND nodes_1.data = :data_2 AND "
-            "nodes_2.data = :data_3) AS anon_1 LIMIT :param_1",
-            {"param_1": 1},
-            use_default_dialect=True,
-        )
+        is_(manager.expired_attribute_loader, myloader)
 
-    def test_multiple_explicit_entities_two(self):
-        Node = self.classes.Node
+        with testing.expect_deprecated(
+            "The ClassManager.deferred_scalar_loader attribute is now named "
+            "expired_attribute_loader"
+        ):
+            is_(manager.deferred_scalar_loader, myloader)
 
-        sess = fixture_session()
+    def test_polymorphic_union_w_select(self):
+        users, addresses = self.tables.users, self.tables.addresses
 
-        parent = aliased(Node)
-        grandparent = aliased(Node)
-        with self._from_self_deprecated():
-            eq_(
-                sess.query(Node, parent, grandparent)
-                .join(parent, Node.parent)
-                .join(grandparent, parent.parent)
-                .filter(Node.data == "n122")
-                .filter(parent.data == "n12")
-                .filter(grandparent.data == "n1")
-                .from_self()
-                .first(),
-                (Node(data="n122"), Node(data="n12"), Node(data="n1")),
+        with DeprecatedQueryTest._expect_implicit_subquery():
+            dep = polymorphic_union(
+                {"u": users.select(), "a": addresses.select()},
+                "type",
+                "bcjoin",
             )
 
-    def test_multiple_explicit_entities_three(self):
-        Node = self.classes.Node
-
-        sess = fixture_session()
+        subq_version = polymorphic_union(
+            {
+                "u": users.select().subquery(),
+                "a": addresses.select().subquery(),
+            },
+            "type",
+            "bcjoin",
+        )
+        is_true(dep.compare(subq_version))
 
-        parent = aliased(Node)
-        grandparent = aliased(Node)
-        # same, change order around
-        with self._from_self_deprecated():
-            eq_(
-                sess.query(parent, grandparent, Node)
-                .join(parent, Node.parent)
-                .join(grandparent, parent.parent)
-                .filter(Node.data == "n122")
-                .filter(parent.data == "n12")
-                .filter(grandparent.data == "n1")
-                .from_self()
-                .first(),
-                (Node(data="n12"), Node(data="n1"), Node(data="n122")),
-            )
+    def test_comparable_column(self):
+        users, User = self.tables.users, self.classes.User
 
-    def test_multiple_explicit_entities_five(self):
-        Node = self.classes.Node
+        class MyComparator(sa.orm.properties.ColumnProperty.Comparator):
+            __hash__ = None
 
-        sess = fixture_session()
+            def __eq__(self, other):
+                # lower case comparison
+                return func.lower(self.__clause_element__()) == func.lower(
+                    other
+                )
 
-        parent = aliased(Node)
-        grandparent = aliased(Node)
-        with self._from_self_deprecated():
-            eq_(
-                sess.query(Node, parent, grandparent)
-                .join(parent, Node.parent)
-                .join(grandparent, parent.parent)
-                .filter(Node.data == "n122")
-                .filter(parent.data == "n12")
-                .filter(grandparent.data == "n1")
-                .from_self()
-                .options(joinedload(Node.children))
-                .first(),
-                (Node(data="n122"), Node(data="n12"), Node(data="n1")),
-            )
+            def intersects(self, other):
+                # non-standard comparator
+                return self.__clause_element__().op("&=")(other)
 
-    def _from_self_deprecated(self):
-        return testing.expect_deprecated_20(r"The Query.from_self\(\) method")
+        self.mapper_registry.map_imperatively(
+            User,
+            users,
+            properties={
+                "name": sa.orm.column_property(
+                    users.c.name, comparator_factory=MyComparator
+                )
+            },
+        )
 
+        assert_raises_message(
+            AttributeError,
+            "Neither 'InstrumentedAttribute' object nor "
+            "'MyComparator' object associated with User.name has "
+            "an attribute 'nonexistent'",
+            getattr,
+            User.name,
+            "nonexistent",
+        )
 
-class SelfReferentialEagerTest(fixtures.MappedTest):
-    @classmethod
-    def define_tables(cls, metadata):
-        Table(
-            "nodes",
-            metadata,
-            Column(
-                "id", Integer, primary_key=True, test_needs_autoincrement=True
+        eq_(
+            str(
+                (User.name == "ed").compile(
+                    dialect=sa.engine.default.DefaultDialect()
+                )
             ),
-            Column("parent_id", Integer, ForeignKey("nodes.id")),
-            Column("data", String(30)),
+            "lower(users.name) = lower(:lower_1)",
+        )
+        eq_(
+            str(
+                (User.name.intersects("ed")).compile(
+                    dialect=sa.engine.default.DefaultDialect()
+                )
+            ),
+            "users.name &= :name_1",
         )
 
+    def test_add_property(self):
+        users = self.tables.users
 
-class LazyLoadOptSpecificityTest(fixtures.DeclarativeMappedTest):
-    """test for [ticket:3963]"""
-
-    @classmethod
-    def setup_classes(cls):
-        Base = cls.DeclarativeBasic
+        assert_col = []
 
-        class A(Base):
-            __tablename__ = "a"
-            id = Column(Integer, primary_key=True)
-            bs = relationship("B")
-
-        class B(Base):
-            __tablename__ = "b"
-            id = Column(Integer, primary_key=True)
-            a_id = Column(ForeignKey("a.id"))
-            cs = relationship("C")
-
-        class C(Base):
-            __tablename__ = "c"
-            id = Column(Integer, primary_key=True)
-            b_id = Column(ForeignKey("b.id"))
+        class User(fixtures.ComparableEntity):
+            def _get_name(self):
+                assert_col.append(("get", self._name))
+                return self._name
 
-    @classmethod
-    def insert_data(cls, connection):
-        A, B, C = cls.classes("A", "B", "C")
-        s = Session(connection)
-        s.add(A(id=1, bs=[B(cs=[C()])]))
-        s.add(A(id=2))
-        s.commit()
+            def _set_name(self, name):
+                assert_col.append(("set", name))
+                self._name = name
 
-    def _run_tests(self, query, expected):
-        def go():
-            for a, _ in query:
-                for b in a.bs:
-                    b.cs
+            name = property(_get_name, _set_name)
 
-        self.assert_sql_count(testing.db, go, expected)
+        m = self.mapper_registry.map_imperatively(User, users)
 
+        m.add_property("_name", deferred(users.c.name))
+        m.add_property("name", synonym("_name"))
 
-class DynamicTest(_DynamicFixture, _fixtures.FixtureTest):
-    def test_negative_slice_access_raises(self):
-        User, Address = self._user_address_fixture()
         sess = fixture_session()
-        u1 = sess.get(User, 8)
-
-        with testing.expect_deprecated_20(
-            "Support for negative indexes for SQL index / slice"
-        ):
-            eq_(u1.addresses[-1], Address(id=4))
-
-        with testing.expect_deprecated_20(
-            "Support for negative indexes for SQL index / slice"
-        ):
-            eq_(u1.addresses[-5:-2], [Address(id=2)])
-
-        with testing.expect_deprecated_20(
-            "Support for negative indexes for SQL index / slice"
-        ):
-            eq_(u1.addresses[-2], Address(id=3))
-
-        with testing.expect_deprecated_20(
-            "Support for negative indexes for SQL index / slice"
-        ):
-            eq_(u1.addresses[:-2], [Address(id=2)])
-
+        assert sess.get(User, 7)
 
-class FromSelfTest(QueryTest, AssertsCompiledSQL):
-    __dialect__ = "default"
+        u = sess.query(User).filter_by(name="jack").one()
 
-    def _from_self_deprecated(self):
-        return testing.expect_deprecated_20(r"The Query.from_self\(\) method")
+        def go():
+            eq_(u.name, "jack")
+            eq_(assert_col, [("get", "jack")], str(assert_col))
 
-    def test_illegal_operations(self):
+        self.sql_count_(1, go)
 
-        User = self.classes.User
 
-        s = fixture_session()
+class DeprecatedOptionAllTest(OptionsPathTest, _fixtures.FixtureTest):
+    run_inserts = "once"
+    run_deletes = None
 
-        with self._from_self_deprecated():
-            q = s.query(User).from_self()
-        assert_raises_message(
-            sa.exc.InvalidRequestError,
-            r"Can't call Query.update\(\) or Query.delete\(\)",
-            q.update,
-            {},
+    def _mapper_fixture_one(self):
+        users, User, addresses, Address, orders, Order = (
+            self.tables.users,
+            self.classes.User,
+            self.tables.addresses,
+            self.classes.Address,
+            self.tables.orders,
+            self.classes.Order,
+        )
+        keywords, items, item_keywords, Keyword, Item = (
+            self.tables.keywords,
+            self.tables.items,
+            self.tables.item_keywords,
+            self.classes.Keyword,
+            self.classes.Item,
+        )
+        self.mapper_registry.map_imperatively(
+            User,
+            users,
+            properties={
+                "addresses": relationship(Address),
+                "orders": relationship(Order),
+            },
+        )
+        self.mapper_registry.map_imperatively(Address, addresses)
+        self.mapper_registry.map_imperatively(
+            Order,
+            orders,
+            properties={
+                "items": relationship(Item, secondary=self.tables.order_items)
+            },
+        )
+        self.mapper_registry.map_imperatively(
+            Keyword,
+            keywords,
+            properties={
+                "keywords": column_property(keywords.c.name + "some keyword")
+            },
+        )
+        self.mapper_registry.map_imperatively(
+            Item,
+            items,
+            properties=dict(
+                keywords=relationship(Keyword, secondary=item_keywords)
+            ),
         )
 
+    def _assert_eager_with_entity_exception(
+        self, entity_list, options, message
+    ):
         assert_raises_message(
-            sa.exc.InvalidRequestError,
-            r"Can't call Query.update\(\) or Query.delete\(\)",
-            q.delete,
-            {},
+            sa.exc.ArgumentError,
+            message,
+            fixture_session()
+            .query(*entity_list)
+            .options(*options)
+            ._compile_context,
         )
 
-    def test_basic_filter_by(self):
-        """test #7239"""
-
-        User = self.classes.User
-
-        s = fixture_session()
-
-        with self._from_self_deprecated():
-            q = s.query(User).from_self()
-
-        self.assert_compile(
-            q.filter_by(id=5),
-            "SELECT anon_1.users_id AS anon_1_users_id, anon_1.users_name "
-            "AS anon_1_users_name FROM (SELECT users.id AS users_id, "
-            "users.name AS users_name FROM users) AS anon_1 "
-            "WHERE anon_1.users_id = :id_1",
+    def test_defer_addtl_attrs(self):
+        users, User, Address, addresses = (
+            self.tables.users,
+            self.classes.User,
+            self.classes.Address,
+            self.tables.addresses,
         )
 
-    def test_columns_augmented_distinct_on(self):
-        User, Address = self.classes.User, self.classes.Address
-
-        sess = fixture_session()
-
-        with self._from_self_deprecated():
-            q = (
-                sess.query(
-                    User.id,
-                    User.name.label("foo"),
-                    Address.id,
-                    Address.email_address,
+        self.mapper_registry.map_imperatively(Address, addresses)
+        self.mapper_registry.map_imperatively(
+            User,
+            users,
+            properties={
+                "addresses": relationship(
+                    Address, lazy="selectin", order_by=addresses.c.id
                 )
-                .distinct(Address.email_address)
-                .order_by(User.id, User.name, Address.email_address)
-                .from_self(User.id, User.name.label("foo"), Address.id)
-            )
-
-        # Address.email_address is added because of DISTINCT,
-        # however User.id, User.name are not b.c. they're already there,
-        # even though User.name is labeled
-        self.assert_compile(
-            q,
-            "SELECT anon_1.users_id AS anon_1_users_id, anon_1.foo AS foo, "
-            "anon_1.addresses_id AS anon_1_addresses_id "
-            "FROM ("
-            "SELECT DISTINCT ON (addresses.email_address) "
-            "users.id AS users_id, users.name AS foo, "
-            "addresses.id AS addresses_id, addresses.email_address AS "
-            "addresses_email_address FROM users, addresses ORDER BY "
-            "users.id, users.name, addresses.email_address"
-            ") AS anon_1",
-            dialect="postgresql",
+            },
         )
 
-    def test_columns_augmented_roundtrip_one_from_self(self):
-        """Test workaround for legacy style DISTINCT on extra column.
-
-        See #5134
-
-        """
-        User, Address = self.classes.User, self.classes.Address
-
         sess = fixture_session()
-        with self._from_self_deprecated():
-            q = (
-                sess.query(User, Address.email_address)
-                .join(User.addresses)
-                .distinct()
-                .from_self(User)
-                .order_by(desc(Address.email_address))
-            )
-
-        eq_([User(id=7), User(id=9), User(id=8)], q.all())
 
-    def test_columns_augmented_roundtrip_three_from_self(self):
-        """Test workaround for legacy style DISTINCT on extra column.
+        with testing.expect_deprecated(undefer_needs_chaining):
+            sess.query(User).options(
+                defer(User.addresses, Address.email_address)
+            )
 
-        See #5134
+        with testing.expect_deprecated(undefer_needs_chaining):
+            sess.query(User).options(
+                undefer(User.addresses, Address.email_address)
+            )
 
-        """
 
-        User, Address = self.classes.User, self.classes.Address
+class InstrumentationTest(fixtures.ORMTest):
+    def test_dict_subclass4(self):
+        # tests #2654
+        with testing.expect_deprecated(
+            r"The collection.converter\(\) handler is deprecated and will "
+            "be removed in a future release.  Please refer to the "
+            "AttributeEvents"
+        ):
 
-        sess = fixture_session()
+            class MyDict(collections.MappedCollection):
+                def __init__(self):
+                    super(MyDict, self).__init__(lambda value: "k%d" % value)
 
-        with self._from_self_deprecated():
-            q = (
-                sess.query(
-                    User.id,
-                    User.name.label("foo"),
-                    Address.id,
-                    Address.email_address,
-                )
-                .join(Address, true())
-                .filter(User.name == "jack")
-                .filter(User.id + Address.user_id > 0)
-                .distinct()
-                .from_self(User.id, User.name.label("foo"), Address.id)
-                .order_by(User.id, User.name, Address.email_address)
-            )
+                @collection.converter
+                def _convert(self, dictlike):
+                    for key, value in dictlike.items():
+                        yield value + 5
 
-        eq_(
-            q.all(),
-            [
-                (7, "jack", 3),
-                (7, "jack", 4),
-                (7, "jack", 2),
-                (7, "jack", 5),
-                (7, "jack", 1),
-            ],
-        )
-        for row in q:
-            eq_(row._mapping.keys(), ["id", "foo", "id"])
+        class Foo:
+            pass
 
-    def test_clause_onclause(self):
-        Order, User = (
-            self.classes.Order,
-            self.classes.User,
+        instrumentation.register_class(Foo)
+        attributes.register_attribute(
+            Foo, "attr", uselist=True, typecallable=MyDict, useobject=True
         )
 
-        sess = fixture_session()
-        # explicit onclause with from_self(), means
-        # the onclause must be aliased against the query's custom
-        # FROM object
-        with self._from_self_deprecated():
-            eq_(
-                sess.query(User)
-                .order_by(User.id)
-                .offset(2)
-                .from_self()
-                .join(Order, User.id == Order.user_id)
-                .all(),
-                [User(name="fred")],
-            )
+        f = Foo()
+        f.attr = {"k1": 1, "k2": 2}
 
-    def test_from_self_resets_joinpaths(self):
-        """test a join from from_self() doesn't confuse joins inside the subquery
-        with the outside.
-        """
+        eq_(f.attr, {"k7": 7, "k6": 6})
 
-        Item, Keyword = self.classes.Item, self.classes.Keyword
+    def test_name_setup(self):
+        with testing.expect_deprecated(
+            r"The collection.converter\(\) handler is deprecated and will "
+            "be removed in a future release.  Please refer to the "
+            "AttributeEvents"
+        ):
 
-        sess = fixture_session()
+            class Base:
+                @collection.iterator
+                def base_iterate(self, x):
+                    return "base_iterate"
 
-        with self._from_self_deprecated():
-            self.assert_compile(
-                sess.query(Item)
-                .join(Item.keywords)
-                .from_self(Keyword)
-                .join(Item.keywords),
-                "SELECT keywords.id AS keywords_id, "
-                "keywords.name AS keywords_name "
-                "FROM (SELECT items.id AS items_id, "
-                "items.description AS items_description "
-                "FROM items JOIN item_keywords AS item_keywords_1 "
-                "ON items.id = "
-                "item_keywords_1.item_id JOIN keywords "
-                "ON keywords.id = item_keywords_1.keyword_id) "
-                "AS anon_1 JOIN item_keywords AS item_keywords_2 ON "
-                "anon_1.items_id = item_keywords_2.item_id "
-                "JOIN keywords ON "
-                "keywords.id = item_keywords_2.keyword_id",
-                use_default_dialect=True,
-            )
+                @collection.appender
+                def base_append(self, x):
+                    return "base_append"
 
-    def test_single_prop_9(self):
-        User = self.classes.User
+                @collection.converter
+                def base_convert(self, x):
+                    return "base_convert"
 
-        sess = fixture_session()
-        with self._from_self_deprecated():
-            self.assert_compile(
-                sess.query(User)
-                .filter(User.name == "ed")
-                .from_self()
-                .join(User.orders),
-                "SELECT anon_1.users_id AS anon_1_users_id, "
-                "anon_1.users_name AS anon_1_users_name "
-                "FROM (SELECT users.id AS users_id, users.name AS users_name "
-                "FROM users "
-                "WHERE users.name = :name_1) AS anon_1 JOIN orders "
-                "ON anon_1.users_id = orders.user_id",
-            )
+                @collection.remover
+                def base_remove(self, x):
+                    return "base_remove"
 
-    def test_anonymous_expression_from_self_twice_oldstyle(self):
-        # relies upon _orm_only_from_obj_alias setting
+        from sqlalchemy.orm.collections import _instrument_class
 
-        sess = fixture_session()
-        c1, c2 = column("c1"), column("c2")
-        q1 = sess.query(c1, c2).filter(c1 == "dog")
-        with self._from_self_deprecated():
-            q1 = q1.from_self().from_self()
-        self.assert_compile(
-            q1.order_by(c1),
-            "SELECT anon_1.anon_2_c1 AS anon_1_anon_2_c1, anon_1.anon_2_c2 AS "
-            "anon_1_anon_2_c2 FROM (SELECT anon_2.c1 AS anon_2_c1, anon_2.c2 "
-            "AS anon_2_c2 "
-            "FROM (SELECT c1, c2 WHERE c1 = :c1_1) AS "
-            "anon_2) AS anon_1 ORDER BY anon_1.anon_2_c1",
-        )
+        _instrument_class(Base)
 
-    def test_anonymous_expression_plus_flag_aliased_join(self):
-        """test that the 'dont alias non-ORM' rule remains for other
-        kinds of aliasing when _from_selectable() is used."""
-
-        User = self.classes.User
-        Address = self.classes.Address
-        addresses = self.tables.addresses
+        eq_(Base._sa_remover(Base(), 5), "base_remove")
+        eq_(Base._sa_appender(Base(), 5), "base_append")
+        eq_(Base._sa_iterator(Base(), 5), "base_iterate")
+        eq_(Base._sa_converter(Base(), 5), "base_convert")
 
-        sess = fixture_session()
-        q1 = sess.query(User.id).filter(User.id > 5)
-        with self._from_self_deprecated():
-            q1 = q1.from_self()
+        with testing.expect_deprecated(
+            r"The collection.converter\(\) handler is deprecated and will "
+            "be removed in a future release.  Please refer to the "
+            "AttributeEvents"
+        ):
 
-        with testing.expect_deprecated_20(join_aliased_dep):
-            q1 = q1.join(User.addresses, aliased=True).order_by(
-                User.id, Address.id, addresses.c.id
-            )
+            class Sub(Base):
+                @collection.converter
+                def base_convert(self, x):
+                    return "sub_convert"
 
-        self.assert_compile(
-            q1,
-            "SELECT anon_1.users_id AS anon_1_users_id "
-            "FROM (SELECT users.id AS users_id FROM users "
-            "WHERE users.id > :id_1) AS anon_1 JOIN addresses AS addresses_1 "
-            "ON anon_1.users_id = addresses_1.user_id "
-            "ORDER BY anon_1.users_id, addresses_1.id, addresses.id",
-        )
+                @collection.remover
+                def sub_remove(self, x):
+                    return "sub_remove"
 
-    def test_anonymous_expression_plus_explicit_aliased_join(self):
-        """test that the 'dont alias non-ORM' rule remains for other
-        kinds of aliasing when _from_selectable() is used."""
+        _instrument_class(Sub)
 
-        User = self.classes.User
-        Address = self.classes.Address
-        addresses = self.tables.addresses
+        eq_(Sub._sa_appender(Sub(), 5), "base_append")
+        eq_(Sub._sa_remover(Sub(), 5), "sub_remove")
+        eq_(Sub._sa_iterator(Sub(), 5), "base_iterate")
+        eq_(Sub._sa_converter(Sub(), 5), "sub_convert")
 
-        sess = fixture_session()
-        q1 = sess.query(User.id).filter(User.id > 5)
-        with self._from_self_deprecated():
-            q1 = q1.from_self()
 
-        aa = aliased(Address)
-        q1 = q1.join(aa, User.addresses).order_by(
-            User.id, aa.id, addresses.c.id
-        )
-        self.assert_compile(
-            q1,
-            "SELECT anon_1.users_id AS anon_1_users_id "
-            "FROM (SELECT users.id AS users_id FROM users "
-            "WHERE users.id > :id_1) AS anon_1 JOIN addresses AS addresses_1 "
-            "ON anon_1.users_id = addresses_1.user_id "
-            "ORDER BY anon_1.users_id, addresses_1.id, addresses.id",
-        )
+class NonPrimaryRelationshipLoaderTest(_fixtures.FixtureTest):
+    run_inserts = "once"
+    run_deletes = None
 
-    def test_table_anonymous_expression_from_self_twice_oldstyle(self):
-        # relies upon _orm_only_from_obj_alias setting
-        from sqlalchemy.sql import column
+    def test_selectload(self):
+        """tests lazy loading with two relationships simultaneously,
+        from the same table, using aliases."""
 
-        sess = fixture_session()
-        t1 = table("t1", column("c1"), column("c2"))
-        q1 = sess.query(t1.c.c1, t1.c.c2).filter(t1.c.c1 == "dog")
-        with self._from_self_deprecated():
-            q1 = q1.from_self().from_self()
-        self.assert_compile(
-            q1.order_by(t1.c.c1),
-            "SELECT anon_1.anon_2_t1_c1 "
-            "AS anon_1_anon_2_t1_c1, anon_1.anon_2_t1_c2 "
-            "AS anon_1_anon_2_t1_c2 "
-            "FROM (SELECT anon_2.t1_c1 AS anon_2_t1_c1, "
-            "anon_2.t1_c2 AS anon_2_t1_c2 FROM (SELECT t1.c1 AS t1_c1, t1.c2 "
-            "AS t1_c2 FROM t1 WHERE t1.c1 = :c1_1) AS anon_2) AS anon_1 "
-            "ORDER BY anon_1.anon_2_t1_c1",
+        users, orders, User, Address, Order, addresses = (
+            self.tables.users,
+            self.tables.orders,
+            self.classes.User,
+            self.classes.Address,
+            self.classes.Order,
+            self.tables.addresses,
         )
 
-    def test_self_referential(self):
-        Order = self.classes.Order
-
-        sess = fixture_session()
-        oalias = aliased(Order)
-
-        with self._from_self_deprecated():
-            for q in [
-                sess.query(Order, oalias)
-                .filter(Order.user_id == oalias.user_id)
-                .filter(Order.user_id == 7)
-                .filter(Order.id > oalias.id)
-                .order_by(Order.id, oalias.id),
-                sess.query(Order, oalias)
-                .filter(Order.id > oalias.id)
-                .from_self()
-                .filter(Order.user_id == oalias.user_id)
-                .filter(Order.user_id == 7)
-                .order_by(Order.id, oalias.id),
-                # same thing, but reversed.
-                sess.query(oalias, Order)
-                .filter(Order.id < oalias.id)
-                .from_self()
-                .filter(oalias.user_id == Order.user_id)
-                .filter(oalias.user_id == 7)
-                .order_by(oalias.id, Order.id),
-                # here we go....two layers of aliasing
-                sess.query(Order, oalias)
-                .filter(Order.user_id == oalias.user_id)
-                .filter(Order.user_id == 7)
-                .filter(Order.id > oalias.id)
-                .from_self()
-                .order_by(Order.id, oalias.id)
-                .limit(10)
-                .options(joinedload(Order.items)),
-                # gratuitous four layers
-                sess.query(Order, oalias)
-                .filter(Order.user_id == oalias.user_id)
-                .filter(Order.user_id == 7)
-                .filter(Order.id > oalias.id)
-                .from_self()
-                .from_self()
-                .from_self()
-                .order_by(Order.id, oalias.id)
-                .limit(10)
-                .options(joinedload(Order.items)),
-            ]:
-
-                eq_(
-                    q.all(),
-                    [
-                        (
-                            Order(
-                                address_id=1,
-                                description="order 3",
-                                isopen=1,
-                                user_id=7,
-                                id=3,
-                            ),
-                            Order(
-                                address_id=1,
-                                description="order 1",
-                                isopen=0,
-                                user_id=7,
-                                id=1,
-                            ),
-                        ),
-                        (
-                            Order(
-                                address_id=None,
-                                description="order 5",
-                                isopen=0,
-                                user_id=7,
-                                id=5,
-                            ),
-                            Order(
-                                address_id=1,
-                                description="order 1",
-                                isopen=0,
-                                user_id=7,
-                                id=1,
-                            ),
-                        ),
-                        (
-                            Order(
-                                address_id=None,
-                                description="order 5",
-                                isopen=0,
-                                user_id=7,
-                                id=5,
-                            ),
-                            Order(
-                                address_id=1,
-                                description="order 3",
-                                isopen=1,
-                                user_id=7,
-                                id=3,
-                            ),
-                        ),
-                    ],
-                )
+        openorders = sa.alias(orders, "openorders")
+        closedorders = sa.alias(orders, "closedorders")
 
-    def test_from_self_internal_literals_oldstyle(self):
-        # relies upon _orm_only_from_obj_alias setting
-        Order = self.classes.Order
+        self.mapper_registry.map_imperatively(Address, addresses)
 
-        sess = fixture_session()
+        self.mapper_registry.map_imperatively(Order, orders)
 
-        # ensure column expressions are taken from inside the subquery, not
-        # restated at the top
-        with self._from_self_deprecated():
-            q = (
-                sess.query(
-                    Order.id,
-                    Order.description,
-                    literal_column("'q'").label("foo"),
-                )
-                .filter(Order.description == "order 3")
-                .from_self()
+        with testing.expect_deprecated(
+            "The mapper.non_primary parameter is deprecated"
+        ):
+            open_mapper = self.mapper_registry.map_imperatively(
+                Order, openorders, non_primary=True
             )
-        self.assert_compile(
-            q,
-            "SELECT anon_1.orders_id AS "
-            "anon_1_orders_id, "
-            "anon_1.orders_description AS anon_1_orders_description, "
-            "anon_1.foo AS anon_1_foo FROM (SELECT "
-            "orders.id AS orders_id, "
-            "orders.description AS orders_description, "
-            "'q' AS foo FROM orders WHERE "
-            "orders.description = :description_1) AS "
-            "anon_1",
-        )
-        eq_(q.all(), [(3, "order 3", "q")])
-
-    def test_column_access_from_self(self):
-        User = self.classes.User
-        sess = fixture_session()
-
-        with self._from_self_deprecated():
-            q = sess.query(User).from_self()
-        self.assert_compile(
-            q.filter(User.name == "ed"),
-            "SELECT anon_1.users_id AS anon_1_users_id, anon_1.users_name AS "
-            "anon_1_users_name FROM (SELECT users.id AS users_id, users.name "
-            "AS users_name FROM users) AS anon_1 WHERE anon_1.users_name = "
-            ":name_1",
+            closed_mapper = self.mapper_registry.map_imperatively(
+                Order, closedorders, non_primary=True
+            )
+        self.mapper_registry.map_imperatively(
+            User,
+            users,
+            properties=dict(
+                addresses=relationship(Address, lazy=True),
+                open_orders=relationship(
+                    open_mapper,
+                    primaryjoin=sa.and_(
+                        openorders.c.isopen == 1,
+                        users.c.id == openorders.c.user_id,
+                    ),
+                    lazy="select",
+                ),
+                closed_orders=relationship(
+                    closed_mapper,
+                    primaryjoin=sa.and_(
+                        closedorders.c.isopen == 0,
+                        users.c.id == closedorders.c.user_id,
+                    ),
+                    lazy="select",
+                ),
+            ),
         )
 
-    def test_column_access_from_self_twice(self):
-        User = self.classes.User
-        sess = fixture_session()
+        self._run_double_test(10)
 
-        with self._from_self_deprecated():
-            q = sess.query(User).from_self(User.id, User.name).from_self()
-        self.assert_compile(
-            q.filter(User.name == "ed"),
-            "SELECT anon_1.anon_2_users_id AS anon_1_anon_2_users_id, "
-            "anon_1.anon_2_users_name AS anon_1_anon_2_users_name FROM "
-            "(SELECT anon_2.users_id AS anon_2_users_id, anon_2.users_name "
-            "AS anon_2_users_name FROM (SELECT users.id AS users_id, "
-            "users.name AS users_name FROM users) AS anon_2) AS anon_1 "
-            "WHERE anon_1.anon_2_users_name = :name_1",
-        )
+    def test_joinedload(self):
+        """Eager loading with two relationships simultaneously,
+        from the same table, using aliases."""
 
-    def test_column_queries_nine(self):
-        Address, User = (
-            self.classes.Address,
+        users, orders, User, Address, Order, addresses = (
+            self.tables.users,
+            self.tables.orders,
             self.classes.User,
+            self.classes.Address,
+            self.classes.Order,
+            self.tables.addresses,
         )
 
-        sess = fixture_session()
+        openorders = sa.alias(orders, "openorders")
+        closedorders = sa.alias(orders, "closedorders")
 
-        adalias = aliased(Address)
-        # select from aliasing + explicit aliasing
-        with self._from_self_deprecated():
-            eq_(
-                sess.query(User, adalias.email_address, adalias.id)
-                .outerjoin(adalias, User.addresses)
-                .from_self(User, adalias.email_address)
-                .order_by(User.id, adalias.id)
-                .all(),
-                [
-                    (User(name="jack", id=7), "jack@bean.com"),
-                    (User(name="ed", id=8), "ed@wood.com"),
-                    (User(name="ed", id=8), "ed@bettyboop.com"),
-                    (User(name="ed", id=8), "ed@lala.com"),
-                    (User(name="fred", id=9), "fred@fred.com"),
-                    (User(name="chuck", id=10), None),
-                ],
+        self.mapper_registry.map_imperatively(Address, addresses)
+        self.mapper_registry.map_imperatively(Order, orders)
+
+        with testing.expect_deprecated(
+            "The mapper.non_primary parameter is deprecated"
+        ):
+            open_mapper = self.mapper_registry.map_imperatively(
+                Order, openorders, non_primary=True
+            )
+            closed_mapper = self.mapper_registry.map_imperatively(
+                Order, closedorders, non_primary=True
             )
 
-    def test_column_queries_ten(self):
-        Address, User = (
-            self.classes.Address,
-            self.classes.User,
+        self.mapper_registry.map_imperatively(
+            User,
+            users,
+            properties=dict(
+                addresses=relationship(
+                    Address, lazy="joined", order_by=addresses.c.id
+                ),
+                open_orders=relationship(
+                    open_mapper,
+                    primaryjoin=sa.and_(
+                        openorders.c.isopen == 1,
+                        users.c.id == openorders.c.user_id,
+                    ),
+                    lazy="joined",
+                    order_by=openorders.c.id,
+                ),
+                closed_orders=relationship(
+                    closed_mapper,
+                    primaryjoin=sa.and_(
+                        closedorders.c.isopen == 0,
+                        users.c.id == closedorders.c.user_id,
+                    ),
+                    lazy="joined",
+                    order_by=closedorders.c.id,
+                ),
+            ),
         )
+        self._run_double_test(1)
 
-        sess = fixture_session()
-
-        # anon + select from aliasing
-        aa = aliased(Address)
-        with self._from_self_deprecated():
-            eq_(
-                sess.query(User)
-                .join(aa, User.addresses)
-                .filter(aa.email_address.like("%ed%"))
-                .from_self()
-                .all(),
-                [User(name="ed", id=8), User(name="fred", id=9)],
-            )
+    def test_selectin(self):
 
-    def test_column_queries_eleven(self):
-        Address, User = (
-            self.classes.Address,
+        users, orders, User, Address, Order, addresses = (
+            self.tables.users,
+            self.tables.orders,
             self.classes.User,
+            self.classes.Address,
+            self.classes.Order,
+            self.tables.addresses,
         )
 
-        sess = fixture_session()
-
-        adalias = aliased(Address)
-        # test eager aliasing, with/without select_entity_from aliasing
-        with self._from_self_deprecated():
-            for q in [
-                sess.query(User, adalias.email_address)
-                .outerjoin(adalias, User.addresses)
-                .options(joinedload(User.addresses))
-                .order_by(User.id, adalias.id)
-                .limit(10),
-                sess.query(User, adalias.email_address, adalias.id)
-                .outerjoin(adalias, User.addresses)
-                .from_self(User, adalias.email_address)
-                .options(joinedload(User.addresses))
-                .order_by(User.id, adalias.id)
-                .limit(10),
-            ]:
-                eq_(
-                    q.all(),
-                    [
-                        (
-                            User(
-                                addresses=[
-                                    Address(
-                                        user_id=7,
-                                        email_address="jack@bean.com",
-                                        id=1,
-                                    )
-                                ],
-                                name="jack",
-                                id=7,
-                            ),
-                            "jack@bean.com",
-                        ),
-                        (
-                            User(
-                                addresses=[
-                                    Address(
-                                        user_id=8,
-                                        email_address="ed@wood.com",
-                                        id=2,
-                                    ),
-                                    Address(
-                                        user_id=8,
-                                        email_address="ed@bettyboop.com",
-                                        id=3,
-                                    ),
-                                    Address(
-                                        user_id=8,
-                                        email_address="ed@lala.com",
-                                        id=4,
-                                    ),
-                                ],
-                                name="ed",
-                                id=8,
-                            ),
-                            "ed@wood.com",
-                        ),
-                        (
-                            User(
-                                addresses=[
-                                    Address(
-                                        user_id=8,
-                                        email_address="ed@wood.com",
-                                        id=2,
-                                    ),
-                                    Address(
-                                        user_id=8,
-                                        email_address="ed@bettyboop.com",
-                                        id=3,
-                                    ),
-                                    Address(
-                                        user_id=8,
-                                        email_address="ed@lala.com",
-                                        id=4,
-                                    ),
-                                ],
-                                name="ed",
-                                id=8,
-                            ),
-                            "ed@bettyboop.com",
-                        ),
-                        (
-                            User(
-                                addresses=[
-                                    Address(
-                                        user_id=8,
-                                        email_address="ed@wood.com",
-                                        id=2,
-                                    ),
-                                    Address(
-                                        user_id=8,
-                                        email_address="ed@bettyboop.com",
-                                        id=3,
-                                    ),
-                                    Address(
-                                        user_id=8,
-                                        email_address="ed@lala.com",
-                                        id=4,
-                                    ),
-                                ],
-                                name="ed",
-                                id=8,
-                            ),
-                            "ed@lala.com",
-                        ),
-                        (
-                            User(
-                                addresses=[
-                                    Address(
-                                        user_id=9,
-                                        email_address="fred@fred.com",
-                                        id=5,
-                                    )
-                                ],
-                                name="fred",
-                                id=9,
-                            ),
-                            "fred@fred.com",
-                        ),
-                        (User(addresses=[], name="chuck", id=10), None),
-                    ],
-                )
+        openorders = sa.alias(orders, "openorders")
+        closedorders = sa.alias(orders, "closedorders")
 
-    def test_filter(self):
-        User = self.classes.User
+        self.mapper_registry.map_imperatively(Address, addresses)
+        self.mapper_registry.map_imperatively(Order, orders)
 
-        with self._from_self_deprecated():
-            eq_(
-                [User(id=8), User(id=9)],
-                fixture_session()
-                .query(User)
-                .filter(User.id.in_([8, 9]))
-                .from_self()
-                .all(),
+        with testing.expect_deprecated(
+            "The mapper.non_primary parameter is deprecated"
+        ):
+            open_mapper = self.mapper_registry.map_imperatively(
+                Order, openorders, non_primary=True
             )
-
-        with self._from_self_deprecated():
-            eq_(
-                [User(id=8), User(id=9)],
-                fixture_session()
-                .query(User)
-                .order_by(User.id)
-                .slice(1, 3)
-                .from_self()
-                .all(),
+            closed_mapper = self.mapper_registry.map_imperatively(
+                Order, closedorders, non_primary=True
             )
 
-        with self._from_self_deprecated():
-            eq_(
-                [User(id=8)],
-                list(
-                    fixture_session()
-                    .query(User)
-                    .filter(User.id.in_([8, 9]))
-                    .from_self()
-                    .order_by(User.id)[0:1]
+        self.mapper_registry.map_imperatively(
+            User,
+            users,
+            properties=dict(
+                addresses=relationship(
+                    Address, lazy="selectin", order_by=addresses.c.id
                 ),
-            )
+                open_orders=relationship(
+                    open_mapper,
+                    primaryjoin=sa.and_(
+                        openorders.c.isopen == 1,
+                        users.c.id == openorders.c.user_id,
+                    ),
+                    lazy="selectin",
+                    order_by=openorders.c.id,
+                ),
+                closed_orders=relationship(
+                    closed_mapper,
+                    primaryjoin=sa.and_(
+                        closedorders.c.isopen == 0,
+                        users.c.id == closedorders.c.user_id,
+                    ),
+                    lazy="selectin",
+                    order_by=closedorders.c.id,
+                ),
+            ),
+        )
 
-    def test_join(self):
-        User, Address = self.classes.User, self.classes.Address
+        self._run_double_test(4)
 
-        with self._from_self_deprecated():
-            eq_(
-                [
-                    (User(id=8), Address(id=2)),
-                    (User(id=8), Address(id=3)),
-                    (User(id=8), Address(id=4)),
-                    (User(id=9), Address(id=5)),
-                ],
-                fixture_session()
-                .query(User)
-                .filter(User.id.in_([8, 9]))
-                .from_self()
-                .join(User.addresses)
-                .add_entity(Address)
-                .order_by(User.id, Address.id)
-                .all(),
-            )
+    def test_subqueryload(self):
 
-    def test_group_by(self):
-        Address = self.classes.Address
-
-        eq_(
-            fixture_session()
-            .query(Address.user_id, func.count(Address.id).label("count"))
-            .group_by(Address.user_id)
-            .order_by(Address.user_id)
-            .all(),
-            [(7, 1), (8, 3), (9, 1)],
+        users, orders, User, Address, Order, addresses = (
+            self.tables.users,
+            self.tables.orders,
+            self.classes.User,
+            self.classes.Address,
+            self.classes.Order,
+            self.tables.addresses,
         )
 
-        with self._from_self_deprecated():
-            eq_(
-                fixture_session()
-                .query(Address.user_id, Address.id)
-                .from_self(Address.user_id, func.count(Address.id))
-                .group_by(Address.user_id)
-                .order_by(Address.user_id)
-                .all(),
-                [(7, 1), (8, 3), (9, 1)],
-            )
-
-    def test_having(self):
-        User = self.classes.User
+        openorders = sa.alias(orders, "openorders")
+        closedorders = sa.alias(orders, "closedorders")
 
-        s = fixture_session()
+        self.mapper_registry.map_imperatively(Address, addresses)
+        self.mapper_registry.map_imperatively(Order, orders)
 
-        with self._from_self_deprecated():
-            self.assert_compile(
-                s.query(User.id)
-                .group_by(User.id)
-                .having(User.id > 5)
-                .from_self(),
-                "SELECT anon_1.users_id AS anon_1_users_id FROM "
-                "(SELECT users.id AS users_id FROM users GROUP "
-                "BY users.id HAVING users.id > :id_1) AS anon_1",
+        with testing.expect_deprecated(
+            "The mapper.non_primary parameter is deprecated"
+        ):
+            open_mapper = self.mapper_registry.map_imperatively(
+                Order, openorders, non_primary=True
+            )
+            closed_mapper = self.mapper_registry.map_imperatively(
+                Order, closedorders, non_primary=True
             )
 
-    def test_no_joinedload(self):
-        """test that joinedloads are pushed outwards and not rendered in
-        subqueries."""
-
-        User = self.classes.User
-
-        s = fixture_session()
-
-        with self._from_self_deprecated():
-            q = s.query(User).options(joinedload(User.addresses)).from_self()
-
-        self.assert_compile(
-            q.statement,
-            "SELECT anon_1.users_id, anon_1.users_name, addresses_1.id, "
-            "addresses_1.user_id, addresses_1.email_address FROM "
-            "(SELECT users.id AS users_id, users.name AS "
-            "users_name FROM users) AS anon_1 LEFT OUTER JOIN "
-            "addresses AS addresses_1 ON anon_1.users_id = "
-            "addresses_1.user_id ORDER BY addresses_1.id",
+        self.mapper_registry.map_imperatively(
+            User,
+            users,
+            properties=dict(
+                addresses=relationship(
+                    Address, lazy="subquery", order_by=addresses.c.id
+                ),
+                open_orders=relationship(
+                    open_mapper,
+                    primaryjoin=sa.and_(
+                        openorders.c.isopen == 1,
+                        users.c.id == openorders.c.user_id,
+                    ),
+                    lazy="subquery",
+                    order_by=openorders.c.id,
+                ),
+                closed_orders=relationship(
+                    closed_mapper,
+                    primaryjoin=sa.and_(
+                        closedorders.c.isopen == 0,
+                        users.c.id == closedorders.c.user_id,
+                    ),
+                    lazy="subquery",
+                    order_by=closedorders.c.id,
+                ),
+            ),
         )
 
-    def test_aliases(self):
-        """test that aliased objects are accessible externally to a from_self()
-        call."""
-
-        User, Address = self.classes.User, self.classes.Address
-
-        s = fixture_session()
-
-        ualias = aliased(User)
-
-        with self._from_self_deprecated():
-            eq_(
-                s.query(User, ualias)
-                .filter(User.id > ualias.id)
-                .from_self(User.name, ualias.name)
-                .order_by(User.name, ualias.name)
-                .all(),
-                [
-                    ("chuck", "ed"),
-                    ("chuck", "fred"),
-                    ("chuck", "jack"),
-                    ("ed", "jack"),
-                    ("fred", "ed"),
-                    ("fred", "jack"),
-                ],
-            )
+        self._run_double_test(4)
 
-        with self._from_self_deprecated():
-            eq_(
-                s.query(User, ualias)
-                .filter(User.id > ualias.id)
-                .from_self(User.name, ualias.name)
-                .filter(ualias.name == "ed")
-                .order_by(User.name, ualias.name)
-                .all(),
-                [("chuck", "ed"), ("fred", "ed")],
-            )
+    def _run_double_test(self, count):
+        User, Address, Order, Item = self.classes(
+            "User", "Address", "Order", "Item"
+        )
+        q = fixture_session().query(User).order_by(User.id)
 
-        with self._from_self_deprecated():
+        def go():
             eq_(
-                s.query(User, ualias)
-                .filter(User.id > ualias.id)
-                .from_self(ualias.name, Address.email_address)
-                .join(ualias.addresses)
-                .order_by(ualias.name, Address.email_address)
-                .all(),
                 [
-                    ("ed", "fred@fred.com"),
-                    ("jack", "ed@bettyboop.com"),
-                    ("jack", "ed@lala.com"),
-                    ("jack", "ed@wood.com"),
-                    ("jack", "fred@fred.com"),
+                    User(
+                        id=7,
+                        addresses=[Address(id=1)],
+                        open_orders=[Order(id=3)],
+                        closed_orders=[Order(id=1), Order(id=5)],
+                    ),
+                    User(
+                        id=8,
+                        addresses=[
+                            Address(id=2),
+                            Address(id=3),
+                            Address(id=4),
+                        ],
+                        open_orders=[],
+                        closed_orders=[],
+                    ),
+                    User(
+                        id=9,
+                        addresses=[Address(id=5)],
+                        open_orders=[Order(id=4)],
+                        closed_orders=[Order(id=2)],
+                    ),
+                    User(id=10),
                 ],
+                q.all(),
             )
 
-    def test_multiple_entities(self):
-        User, Address = self.classes.User, self.classes.Address
+        self.assert_sql_count(testing.db, go, count)
 
         sess = fixture_session()
+        user = sess.get(User, 7)
 
-        with self._from_self_deprecated():
-            eq_(
-                sess.query(User, Address)
-                .filter(User.id == Address.user_id)
-                .filter(Address.id.in_([2, 5]))
-                .from_self()
-                .all(),
-                [(User(id=8), Address(id=2)), (User(id=9), Address(id=5))],
-            )
-
-        with self._from_self_deprecated():
-            eq_(
-                sess.query(User, Address)
-                .filter(User.id == Address.user_id)
-                .filter(Address.id.in_([2, 5]))
-                .from_self()
-                .options(joinedload(User.addresses))
-                .first(),
-                (
-                    User(id=8, addresses=[Address(), Address(), Address()]),
-                    Address(id=2),
-                ),
-            )
+        closed_mapper = User.closed_orders.entity
+        open_mapper = User.open_orders.entity
+        eq_(
+            [Order(id=1), Order(id=5)],
+            fixture_session()
+            .query(closed_mapper)
+            .filter(with_parent(user, User.closed_orders))
+            .all(),
+        )
+        eq_(
+            [Order(id=3)],
+            fixture_session()
+            .query(open_mapper)
+            .filter(with_parent(user, User.open_orders))
+            .all(),
+        )
 
-    def test_multiple_with_column_entities_oldstyle(self):
-        # relies upon _orm_only_from_obj_alias setting
-        User = self.classes.User
 
-        sess = fixture_session()
+class ViewonlyFlagWarningTest(fixtures.MappedTest):
+    """test for #4993.
 
-        with self._from_self_deprecated():
-            eq_(
-                sess.query(User.id)
-                .from_self()
-                .add_columns(func.count().label("foo"))
-                .group_by(User.id)
-                .order_by(User.id)
-                .from_self()
-                .all(),
-                [(7, 1), (8, 1), (9, 1), (10, 1)],
-            )
+    In 1.4, this moves to test/orm/test_cascade, deprecation warnings
+    become errors, will then be for #4994.
 
+    """
 
-class SubqRelationsFromSelfTest(fixtures.DeclarativeMappedTest):
-    def _from_self_deprecated(self):
-        return testing.expect_deprecated_20(r"The Query.from_self\(\) method")
+    @classmethod
+    def define_tables(cls, metadata):
+        Table(
+            "users",
+            metadata,
+            Column("id", Integer, primary_key=True),
+            Column("name", String(30)),
+        )
+        Table(
+            "orders",
+            metadata,
+            Column("id", Integer, primary_key=True),
+            Column("user_id", Integer),
+            Column("description", String(30)),
+        )
 
     @classmethod
     def setup_classes(cls):
-        Base = cls.DeclarativeBasic
+        class User(cls.Comparable):
+            pass
 
-        class A(Base, ComparableEntity):
-            __tablename__ = "a"
+        class Order(cls.Comparable):
+            pass
 
-            id = Column(Integer, primary_key=True)
-            cs = relationship("C", order_by="C.id")
+    @testing.combinations(
+        ("passive_deletes", True),
+        ("passive_updates", False),
+        ("enable_typechecks", False),
+        ("active_history", True),
+        ("cascade_backrefs", False),
+    )
+    def test_viewonly_warning(self, flag, value):
+        Order = self.classes.Order
 
-        class B(Base, ComparableEntity):
-            __tablename__ = "b"
-            id = Column(Integer, primary_key=True)
-            a_id = Column(ForeignKey("a.id"))
-            a = relationship("A")
-            ds = relationship("D", order_by="D.id")
+        with testing.expect_warnings(
+            r"Setting %s on relationship\(\) while also setting "
+            "viewonly=True does not make sense" % flag
+        ):
+            kw = {
+                "viewonly": True,
+                "primaryjoin": self.tables.users.c.id
+                == foreign(self.tables.orders.c.user_id),
+            }
+            kw[flag] = value
+            rel = relationship(Order, **kw)
 
-        class C(Base, ComparableEntity):
-            __tablename__ = "c"
-            id = Column(Integer, primary_key=True)
-            a_id = Column(ForeignKey("a.id"))
+            eq_(getattr(rel, flag), value)
 
-        class D(Base, ComparableEntity):
-            __tablename__ = "d"
-            id = Column(Integer, primary_key=True)
-            b_id = Column(ForeignKey("b.id"))
 
-    @classmethod
-    def insert_data(cls, connection):
-        A, B, C, D = cls.classes("A", "B", "C", "D")
+class NonPrimaryMapperTest(_fixtures.FixtureTest, AssertsCompiledSQL):
+    __dialect__ = "default"
 
-        s = Session(connection)
+    def teardown_test(self):
+        clear_mappers()
+
+    def test_non_primary_identity_class(self):
+        User = self.classes.User
+        users, addresses = self.tables.users, self.tables.addresses
 
-        as_ = [
-            A(
-                id=i,
-                cs=[C(), C()],
+        class AddressUser(User):
+            pass
+
+        self.mapper_registry.map_imperatively(
+            User, users, polymorphic_identity="user"
+        )
+        m2 = self.mapper_registry.map_imperatively(
+            AddressUser,
+            addresses,
+            inherits=User,
+            polymorphic_identity="address",
+            properties={"address_id": addresses.c.id},
+        )
+        with testing.expect_deprecated(
+            "The mapper.non_primary parameter is deprecated"
+        ):
+            m3 = self.mapper_registry.map_imperatively(
+                AddressUser, addresses, non_primary=True
             )
-            for i in range(1, 5)
-        ]
+        assert m3._identity_class is m2._identity_class
+        eq_(
+            m2.identity_key_from_instance(AddressUser()),
+            m3.identity_key_from_instance(AddressUser()),
+        )
 
-        s.add_all(
-            [
-                B(a=as_[0], ds=[D()]),
-                B(a=as_[1], ds=[D()]),
-                B(a=as_[2]),
-                B(a=as_[3]),
-            ]
+    def test_illegal_non_primary(self):
+        users, Address, addresses, User = (
+            self.tables.users,
+            self.classes.Address,
+            self.tables.addresses,
+            self.classes.User,
         )
 
-        s.commit()
+        self.mapper_registry.map_imperatively(User, users)
+        self.mapper_registry.map_imperatively(Address, addresses)
+        with testing.expect_deprecated(
+            "The mapper.non_primary parameter is deprecated"
+        ):
+            m = self.mapper_registry.map_imperatively(  # noqa F841
+                User,
+                users,
+                non_primary=True,
+                properties={"addresses": relationship(Address)},
+            )
+        assert_raises_message(
+            sa.exc.ArgumentError,
+            "Attempting to assign a new relationship 'addresses' "
+            "to a non-primary mapper on class 'User'",
+            configure_mappers,
+        )
 
-    def test_subq_w_from_self_one(self):
-        A, B, C = self.classes("A", "B", "C")
-
-        s = fixture_session()
-
-        cache = {}
-
-        for i in range(3):
-            with self._from_self_deprecated():
-                q = (
-                    s.query(B)
-                    .execution_options(compiled_cache=cache)
-                    .join(B.a)
-                    .filter(B.id < 4)
-                    .filter(A.id > 1)
-                    .from_self()
-                    .options(subqueryload(B.a).subqueryload(A.cs))
-                    .from_self()
-                )
-
-            def go():
-                results = q.all()
-                eq_(
-                    results,
-                    [
-                        B(
-                            a=A(cs=[C(a_id=2, id=3), C(a_id=2, id=4)], id=2),
-                            a_id=2,
-                            id=2,
-                        ),
-                        B(
-                            a=A(cs=[C(a_id=3, id=5), C(a_id=3, id=6)], id=3),
-                            a_id=3,
-                            id=3,
-                        ),
-                    ],
-                )
-
-            self.assert_sql_execution(
-                testing.db,
-                go,
-                CompiledSQL(
-                    "SELECT anon_1.anon_2_b_id AS anon_1_anon_2_b_id, "
-                    "anon_1.anon_2_b_a_id AS anon_1_anon_2_b_a_id FROM "
-                    "(SELECT anon_2.b_id AS anon_2_b_id, anon_2.b_a_id "
-                    "AS anon_2_b_a_id FROM (SELECT b.id AS b_id, b.a_id "
-                    "AS b_a_id FROM b JOIN a ON a.id = b.a_id "
-                    "WHERE b.id < :id_1 AND a.id > :id_2) AS anon_2) AS anon_1"
-                ),
-                CompiledSQL(
-                    "SELECT a.id AS a_id, anon_1.anon_2_anon_3_b_a_id AS "
-                    "anon_1_anon_2_anon_3_b_a_id FROM (SELECT DISTINCT "
-                    "anon_2.anon_3_b_a_id AS anon_2_anon_3_b_a_id FROM "
-                    "(SELECT anon_3.b_id AS anon_3_b_id, anon_3.b_a_id "
-                    "AS anon_3_b_a_id FROM (SELECT b.id AS b_id, b.a_id "
-                    "AS b_a_id FROM b JOIN a ON a.id = b.a_id "
-                    "WHERE b.id < :id_1 AND a.id > :id_2) AS anon_3) "
-                    "AS anon_2) AS anon_1 JOIN a "
-                    "ON a.id = anon_1.anon_2_anon_3_b_a_id"
-                ),
-                CompiledSQL(
-                    "SELECT c.id AS c_id, c.a_id AS c_a_id, a_1.id "
-                    "AS a_1_id FROM (SELECT DISTINCT anon_2.anon_3_b_a_id AS "
-                    "anon_2_anon_3_b_a_id FROM "
-                    "(SELECT anon_3.b_id AS anon_3_b_id, anon_3.b_a_id "
-                    "AS anon_3_b_a_id FROM (SELECT b.id AS b_id, b.a_id "
-                    "AS b_a_id FROM b JOIN a ON a.id = b.a_id "
-                    "WHERE b.id < :id_1 AND a.id > :id_2) AS anon_3) "
-                    "AS anon_2) AS anon_1 JOIN a AS a_1 ON a_1.id = "
-                    "anon_1.anon_2_anon_3_b_a_id JOIN c ON a_1.id = c.a_id "
-                    "ORDER BY c.id"
-                ),
-            )
-
-            s.close()
-
-    def test_subq_w_from_self_two(self):
-
-        A, B, C = self.classes("A", "B", "C")
-
-        s = fixture_session()
-        cache = {}
-
-        for i in range(3):
-
-            def go():
-                with self._from_self_deprecated():
-                    q = (
-                        s.query(B)
-                        .execution_options(compiled_cache=cache)
-                        .join(B.a)
-                        .from_self()
-                    )
-                q = q.options(subqueryload(B.ds))
-
-                q.all()
-
-            self.assert_sql_execution(
-                testing.db,
-                go,
-                CompiledSQL(
-                    "SELECT anon_1.b_id AS anon_1_b_id, anon_1.b_a_id AS "
-                    "anon_1_b_a_id FROM (SELECT b.id AS b_id, b.a_id "
-                    "AS b_a_id FROM b JOIN a ON a.id = b.a_id) AS anon_1"
-                ),
-                CompiledSQL(
-                    "SELECT d.id AS d_id, d.b_id AS d_b_id, "
-                    "anon_1.anon_2_b_id AS anon_1_anon_2_b_id "
-                    "FROM (SELECT anon_2.b_id AS anon_2_b_id FROM "
-                    "(SELECT b.id AS b_id, b.a_id AS b_a_id FROM b "
-                    "JOIN a ON a.id = b.a_id) AS anon_2) AS anon_1 "
-                    "JOIN d ON anon_1.anon_2_b_id = d.b_id ORDER BY d.id"
-                ),
-            )
-            s.close()
-
-
-class SessionTest(fixtures.RemovesEvents, _LocalFixture):
-    def test_transaction_attr(self):
-        s1 = Session(testing.db)
-
-        with testing.expect_deprecated_20(
-            "The Session.transaction attribute is considered legacy as "
-            "of the 1.x series"
-        ):
-            s1.transaction
-
-    def test_textual_execute(self, connection):
-        """test that Session.execute() converts to text()"""
-
-        users = self.tables.users
-
-        with Session(bind=connection) as sess:
-            sess.execute(users.insert(), dict(id=7, name="jack"))
-
-            with testing.expect_deprecated_20(
-                "Using plain strings to indicate SQL statements "
-                "without using the text"
-            ):
-                # use :bindparam style
-                eq_(
-                    sess.execute(
-                        "select * from users where id=:id", {"id": 7}
-                    ).fetchall(),
-                    [(7, "jack")],
-                )
-
-            with testing.expect_deprecated_20(
-                "Using plain strings to indicate SQL statements "
-                "without using the text"
-            ):
-                # use :bindparam style
-                eq_(
-                    sess.scalar(
-                        "select id from users where id=:id", {"id": 7}
-                    ),
-                    7,
-                )
-
-    def test_session_str(self):
-        s1 = Session(testing.db)
-        str(s1)
-
-    @testing.combinations(
-        {"mapper": None},
-        {"clause": None},
-        {"bind_arguments": {"mapper": None}, "clause": None},
-        {"bind_arguments": {}, "clause": None},
-    )
-    def test_bind_kwarg_deprecated(self, kw):
-        s1 = Session(testing.db)
-
-        for meth in s1.execute, s1.scalar:
-            m1 = mock.Mock(side_effect=s1.get_bind)
-            with mock.patch.object(s1, "get_bind", m1):
-                expr = text("select 1")
-
-                with testing.expect_deprecated_20(
-                    r"Passing bind arguments to Session.execute\(\) as "
-                    "keyword "
-                    "arguments is deprecated and will be removed SQLAlchemy "
-                    "2.0"
-                ):
-                    meth(expr, **kw)
-
-                bind_arguments = kw.pop("bind_arguments", None)
-                if bind_arguments:
-                    bind_arguments.update(kw)
-
-                    if "clause" not in kw:
-                        bind_arguments["clause"] = expr
-                    eq_(m1.mock_calls, [call(**bind_arguments)])
-                else:
-                    if "clause" not in kw:
-                        kw["clause"] = expr
-                    eq_(m1.mock_calls, [call(**kw)])
-
-
-class DeprecatedInhTest(_poly_fixtures._Polymorphic):
-    def test_with_polymorphic(self):
-        Person = _poly_fixtures.Person
-        Engineer = _poly_fixtures.Engineer
-
-        with DeprecatedQueryTest._expect_implicit_subquery():
-            p_poly = with_polymorphic(Person, [Engineer], select(Person))
+    def test_illegal_non_primary_2(self):
+        User, users = self.classes.User, self.tables.users
 
-        is_true(
-            sa.inspect(p_poly).selectable.compare(select(Person).subquery())
+        assert_raises_message(
+            sa.exc.InvalidRequestError,
+            "Configure a primary mapper first",
+            self.mapper_registry.map_imperatively,
+            User,
+            users,
+            non_primary=True,
         )
 
-    def test_multiple_adaption(self):
-        """test that multiple filter() adapters get chained together "
-        and work correctly within a multiple-entry join()."""
-
-        Company = _poly_fixtures.Company
-        Machine = _poly_fixtures.Machine
-        Engineer = _poly_fixtures.Engineer
-
-        people = self.tables.people
-        engineers = self.tables.engineers
-        machines = self.tables.machines
-
-        sess = fixture_session()
-
-        mach_alias = machines.select()
-
-        # note python 2 does not allow parens here; reformat in py3 only
-        with DeprecatedQueryTest._expect_implicit_subquery(), _aliased_join_warning(  # noqa E501
-            r"\[Person\(people\)\]"
-        ):
-            self.assert_compile(
-                sess.query(Company)
-                .join(people.join(engineers), Company.employees)
-                .join(mach_alias, Engineer.machines, from_joinpoint=True)
-                .filter(Engineer.name == "dilbert")
-                .filter(Machine.name == "foo"),
-                "SELECT companies.company_id AS companies_company_id, "
-                "companies.name AS companies_name "
-                "FROM companies JOIN (people "
-                "JOIN engineers ON people.person_id = "
-                "engineers.person_id) ON companies.company_id = "
-                "people.company_id JOIN "
-                "(SELECT machines.machine_id AS machine_id, "
-                "machines.name AS name, "
-                "machines.engineer_id AS engineer_id "
-                "FROM machines) AS anon_1 "
-                "ON engineers.person_id = anon_1.engineer_id "
-                "WHERE people.name = :name_1 AND anon_1.name = :name_2",
-                use_default_dialect=True,
-            )
-
-
-class DeprecatedMapperTest(_fixtures.FixtureTest, AssertsCompiledSQL):
-    __dialect__ = "default"
+    def test_illegal_non_primary_3(self):
+        users, addresses = self.tables.users, self.tables.addresses
 
-    def test_deferred_scalar_loader_name_change(self):
-        class Foo:
+        class Base:
             pass
 
-        def myloader(*arg, **kw):
+        class Sub(Base):
             pass
 
-        instrumentation.register_class(Foo)
-        manager = instrumentation.manager_of_class(Foo)
+        self.mapper_registry.map_imperatively(Base, users)
+        assert_raises_message(
+            sa.exc.InvalidRequestError,
+            "Configure a primary mapper first",
+            self.mapper_registry.map_imperatively,
+            Sub,
+            addresses,
+            non_primary=True,
+        )
+
+    def test_illegal_non_primary_legacy(self):
+        users, Address, addresses, User = (
+            self.tables.users,
+            self.classes.Address,
+            self.tables.addresses,
+            self.classes.User,
+        )
 
         with testing.expect_deprecated(
-            "The ClassManager.deferred_scalar_loader attribute is now named "
-            "expired_attribute_loader"
+            "Calling the mapper.* function directly outside of a declarative "
         ):
-            manager.deferred_scalar_loader = myloader
-
-        is_(manager.expired_attribute_loader, myloader)
-
+            mapper(User, users)
         with testing.expect_deprecated(
-            "The ClassManager.deferred_scalar_loader attribute is now named "
-            "expired_attribute_loader"
+            "Calling the mapper.* function directly outside of a declarative "
         ):
-            is_(manager.deferred_scalar_loader, myloader)
-
-    def test_polymorphic_union_w_select(self):
-        users, addresses = self.tables.users, self.tables.addresses
-
-        with DeprecatedQueryTest._expect_implicit_subquery():
-            dep = polymorphic_union(
-                {"u": users.select(), "a": addresses.select()},
-                "type",
-                "bcjoin",
+            mapper(Address, addresses)
+        with testing.expect_deprecated(
+            "The mapper.non_primary parameter is deprecated"
+        ):
+            m = mapper(  # noqa F841
+                User,
+                users,
+                non_primary=True,
+                properties={"addresses": relationship(Address)},
             )
-
-        subq_version = polymorphic_union(
-            {
-                "u": users.select().subquery(),
-                "a": addresses.select().subquery(),
-            },
-            "type",
-            "bcjoin",
+        assert_raises_message(
+            sa.exc.ArgumentError,
+            "Attempting to assign a new relationship 'addresses' "
+            "to a non-primary mapper on class 'User'",
+            configure_mappers,
         )
-        is_true(dep.compare(subq_version))
-
-    def test_comparable_column(self):
-        users, User = self.tables.users, self.classes.User
-
-        class MyComparator(sa.orm.properties.ColumnProperty.Comparator):
-            __hash__ = None
-
-            def __eq__(self, other):
-                # lower case comparison
-                return func.lower(self.__clause_element__()) == func.lower(
-                    other
-                )
-
-            def intersects(self, other):
-                # non-standard comparator
-                return self.__clause_element__().op("&=")(other)
 
-        self.mapper_registry.map_imperatively(
-            User,
-            users,
-            properties={
-                "name": sa.orm.column_property(
-                    users.c.name, comparator_factory=MyComparator
-                )
-            },
-        )
+    def test_illegal_non_primary_2_legacy(self):
+        User, users = self.classes.User, self.tables.users
 
-        assert_raises_message(
-            AttributeError,
-            "Neither 'InstrumentedAttribute' object nor "
-            "'MyComparator' object associated with User.name has "
-            "an attribute 'nonexistent'",
-            getattr,
-            User.name,
-            "nonexistent",
-        )
-
-        eq_(
-            str(
-                (User.name == "ed").compile(
-                    dialect=sa.engine.default.DefaultDialect()
-                )
-            ),
-            "lower(users.name) = lower(:lower_1)",
-        )
-        eq_(
-            str(
-                (User.name.intersects("ed")).compile(
-                    dialect=sa.engine.default.DefaultDialect()
-                )
-            ),
-            "users.name &= :name_1",
-        )
-
-    def test_add_property(self):
-        users = self.tables.users
-
-        assert_col = []
-
-        class User(fixtures.ComparableEntity):
-            def _get_name(self):
-                assert_col.append(("get", self._name))
-                return self._name
-
-            def _set_name(self, name):
-                assert_col.append(("set", name))
-                self._name = name
-
-            name = property(_get_name, _set_name)
-
-        m = self.mapper_registry.map_imperatively(User, users)
-
-        m.add_property("_name", deferred(users.c.name))
-        m.add_property("name", synonym("_name"))
-
-        sess = fixture_session()
-        assert sess.get(User, 7)
-
-        u = sess.query(User).filter_by(name="jack").one()
-
-        def go():
-            eq_(u.name, "jack")
-            eq_(assert_col, [("get", "jack")], str(assert_col))
-
-        self.sql_count_(1, go)
-
-
-class DeprecatedOptionAllTest(OptionsPathTest, _fixtures.FixtureTest):
-    run_inserts = "once"
-    run_deletes = None
-
-    def _mapper_fixture_one(self):
-        users, User, addresses, Address, orders, Order = (
-            self.tables.users,
-            self.classes.User,
-            self.tables.addresses,
-            self.classes.Address,
-            self.tables.orders,
-            self.classes.Order,
-        )
-        keywords, items, item_keywords, Keyword, Item = (
-            self.tables.keywords,
-            self.tables.items,
-            self.tables.item_keywords,
-            self.classes.Keyword,
-            self.classes.Item,
-        )
-        self.mapper_registry.map_imperatively(
-            User,
-            users,
-            properties={
-                "addresses": relationship(Address),
-                "orders": relationship(Order),
-            },
-        )
-        self.mapper_registry.map_imperatively(Address, addresses)
-        self.mapper_registry.map_imperatively(
-            Order,
-            orders,
-            properties={
-                "items": relationship(Item, secondary=self.tables.order_items)
-            },
-        )
-        self.mapper_registry.map_imperatively(
-            Keyword,
-            keywords,
-            properties={
-                "keywords": column_property(keywords.c.name + "some keyword")
-            },
-        )
-        self.mapper_registry.map_imperatively(
-            Item,
-            items,
-            properties=dict(
-                keywords=relationship(Keyword, secondary=item_keywords)
-            ),
-        )
-
-    def _assert_eager_with_entity_exception(
-        self, entity_list, options, message
-    ):
-        assert_raises_message(
-            sa.exc.ArgumentError,
-            message,
-            fixture_session()
-            .query(*entity_list)
-            .options(*options)
-            ._compile_context,
-        )
-
-    def test_defer_addtl_attrs(self):
-        users, User, Address, addresses = (
-            self.tables.users,
-            self.classes.User,
-            self.classes.Address,
-            self.tables.addresses,
-        )
-
-        self.mapper_registry.map_imperatively(Address, addresses)
-        self.mapper_registry.map_imperatively(
-            User,
-            users,
-            properties={
-                "addresses": relationship(
-                    Address, lazy="selectin", order_by=addresses.c.id
-                )
-            },
-        )
-
-        sess = fixture_session()
-
-        with testing.expect_deprecated(undefer_needs_chaining):
-            sess.query(User).options(
-                defer(User.addresses, Address.email_address)
-            )
-
-        with testing.expect_deprecated(undefer_needs_chaining):
-            sess.query(User).options(
-                undefer(User.addresses, Address.email_address)
-            )
-
-
-class InstrumentationTest(fixtures.ORMTest):
-    def test_dict_subclass4(self):
-        # tests #2654
-        with testing.expect_deprecated(
-            r"The collection.converter\(\) handler is deprecated and will "
-            "be removed in a future release.  Please refer to the "
-            "AttributeEvents"
-        ):
-
-            class MyDict(collections.MappedCollection):
-                def __init__(self):
-                    super(MyDict, self).__init__(lambda value: "k%d" % value)
-
-                @collection.converter
-                def _convert(self, dictlike):
-                    for key, value in dictlike.items():
-                        yield value + 5
-
-        class Foo:
-            pass
-
-        instrumentation.register_class(Foo)
-        attributes.register_attribute(
-            Foo, "attr", uselist=True, typecallable=MyDict, useobject=True
-        )
-
-        f = Foo()
-        f.attr = {"k1": 1, "k2": 2}
-
-        eq_(f.attr, {"k7": 7, "k6": 6})
-
-    def test_name_setup(self):
-        with testing.expect_deprecated(
-            r"The collection.converter\(\) handler is deprecated and will "
-            "be removed in a future release.  Please refer to the "
-            "AttributeEvents"
-        ):
-
-            class Base:
-                @collection.iterator
-                def base_iterate(self, x):
-                    return "base_iterate"
-
-                @collection.appender
-                def base_append(self, x):
-                    return "base_append"
-
-                @collection.converter
-                def base_convert(self, x):
-                    return "base_convert"
-
-                @collection.remover
-                def base_remove(self, x):
-                    return "base_remove"
-
-        from sqlalchemy.orm.collections import _instrument_class
-
-        _instrument_class(Base)
-
-        eq_(Base._sa_remover(Base(), 5), "base_remove")
-        eq_(Base._sa_appender(Base(), 5), "base_append")
-        eq_(Base._sa_iterator(Base(), 5), "base_iterate")
-        eq_(Base._sa_converter(Base(), 5), "base_convert")
-
-        with testing.expect_deprecated(
-            r"The collection.converter\(\) handler is deprecated and will "
-            "be removed in a future release.  Please refer to the "
-            "AttributeEvents"
-        ):
-
-            class Sub(Base):
-                @collection.converter
-                def base_convert(self, x):
-                    return "sub_convert"
-
-                @collection.remover
-                def sub_remove(self, x):
-                    return "sub_remove"
-
-        _instrument_class(Sub)
-
-        eq_(Sub._sa_appender(Sub(), 5), "base_append")
-        eq_(Sub._sa_remover(Sub(), 5), "sub_remove")
-        eq_(Sub._sa_iterator(Sub(), 5), "base_iterate")
-        eq_(Sub._sa_converter(Sub(), 5), "sub_convert")
-
-
-class NonPrimaryRelationshipLoaderTest(_fixtures.FixtureTest):
-    run_inserts = "once"
-    run_deletes = None
-
-    def test_selectload(self):
-        """tests lazy loading with two relationships simultaneously,
-        from the same table, using aliases."""
-
-        users, orders, User, Address, Order, addresses = (
-            self.tables.users,
-            self.tables.orders,
-            self.classes.User,
-            self.classes.Address,
-            self.classes.Order,
-            self.tables.addresses,
-        )
-
-        openorders = sa.alias(orders, "openorders")
-        closedorders = sa.alias(orders, "closedorders")
-
-        self.mapper_registry.map_imperatively(Address, addresses)
-
-        self.mapper_registry.map_imperatively(Order, orders)
-
-        with testing.expect_deprecated(
-            "The mapper.non_primary parameter is deprecated"
-        ):
-            open_mapper = self.mapper_registry.map_imperatively(
-                Order, openorders, non_primary=True
-            )
-            closed_mapper = self.mapper_registry.map_imperatively(
-                Order, closedorders, non_primary=True
-            )
-        self.mapper_registry.map_imperatively(
-            User,
-            users,
-            properties=dict(
-                addresses=relationship(Address, lazy=True),
-                open_orders=relationship(
-                    open_mapper,
-                    primaryjoin=sa.and_(
-                        openorders.c.isopen == 1,
-                        users.c.id == openorders.c.user_id,
-                    ),
-                    lazy="select",
-                ),
-                closed_orders=relationship(
-                    closed_mapper,
-                    primaryjoin=sa.and_(
-                        closedorders.c.isopen == 0,
-                        users.c.id == closedorders.c.user_id,
-                    ),
-                    lazy="select",
-                ),
-            ),
-        )
-
-        self._run_double_test(10)
-
-    def test_joinedload(self):
-        """Eager loading with two relationships simultaneously,
-        from the same table, using aliases."""
-
-        users, orders, User, Address, Order, addresses = (
-            self.tables.users,
-            self.tables.orders,
-            self.classes.User,
-            self.classes.Address,
-            self.classes.Order,
-            self.tables.addresses,
-        )
-
-        openorders = sa.alias(orders, "openorders")
-        closedorders = sa.alias(orders, "closedorders")
-
-        self.mapper_registry.map_imperatively(Address, addresses)
-        self.mapper_registry.map_imperatively(Order, orders)
-
-        with testing.expect_deprecated(
-            "The mapper.non_primary parameter is deprecated"
-        ):
-            open_mapper = self.mapper_registry.map_imperatively(
-                Order, openorders, non_primary=True
-            )
-            closed_mapper = self.mapper_registry.map_imperatively(
-                Order, closedorders, non_primary=True
-            )
-
-        self.mapper_registry.map_imperatively(
-            User,
-            users,
-            properties=dict(
-                addresses=relationship(
-                    Address, lazy="joined", order_by=addresses.c.id
-                ),
-                open_orders=relationship(
-                    open_mapper,
-                    primaryjoin=sa.and_(
-                        openorders.c.isopen == 1,
-                        users.c.id == openorders.c.user_id,
-                    ),
-                    lazy="joined",
-                    order_by=openorders.c.id,
-                ),
-                closed_orders=relationship(
-                    closed_mapper,
-                    primaryjoin=sa.and_(
-                        closedorders.c.isopen == 0,
-                        users.c.id == closedorders.c.user_id,
-                    ),
-                    lazy="joined",
-                    order_by=closedorders.c.id,
-                ),
-            ),
-        )
-        self._run_double_test(1)
-
-    def test_selectin(self):
-
-        users, orders, User, Address, Order, addresses = (
-            self.tables.users,
-            self.tables.orders,
-            self.classes.User,
-            self.classes.Address,
-            self.classes.Order,
-            self.tables.addresses,
-        )
-
-        openorders = sa.alias(orders, "openorders")
-        closedorders = sa.alias(orders, "closedorders")
-
-        self.mapper_registry.map_imperatively(Address, addresses)
-        self.mapper_registry.map_imperatively(Order, orders)
-
-        with testing.expect_deprecated(
-            "The mapper.non_primary parameter is deprecated"
-        ):
-            open_mapper = self.mapper_registry.map_imperatively(
-                Order, openorders, non_primary=True
-            )
-            closed_mapper = self.mapper_registry.map_imperatively(
-                Order, closedorders, non_primary=True
-            )
-
-        self.mapper_registry.map_imperatively(
-            User,
-            users,
-            properties=dict(
-                addresses=relationship(
-                    Address, lazy="selectin", order_by=addresses.c.id
-                ),
-                open_orders=relationship(
-                    open_mapper,
-                    primaryjoin=sa.and_(
-                        openorders.c.isopen == 1,
-                        users.c.id == openorders.c.user_id,
-                    ),
-                    lazy="selectin",
-                    order_by=openorders.c.id,
-                ),
-                closed_orders=relationship(
-                    closed_mapper,
-                    primaryjoin=sa.and_(
-                        closedorders.c.isopen == 0,
-                        users.c.id == closedorders.c.user_id,
-                    ),
-                    lazy="selectin",
-                    order_by=closedorders.c.id,
-                ),
-            ),
-        )
-
-        self._run_double_test(4)
-
-    def test_subqueryload(self):
-
-        users, orders, User, Address, Order, addresses = (
-            self.tables.users,
-            self.tables.orders,
-            self.classes.User,
-            self.classes.Address,
-            self.classes.Order,
-            self.tables.addresses,
-        )
-
-        openorders = sa.alias(orders, "openorders")
-        closedorders = sa.alias(orders, "closedorders")
-
-        self.mapper_registry.map_imperatively(Address, addresses)
-        self.mapper_registry.map_imperatively(Order, orders)
-
-        with testing.expect_deprecated(
-            "The mapper.non_primary parameter is deprecated"
-        ):
-            open_mapper = self.mapper_registry.map_imperatively(
-                Order, openorders, non_primary=True
-            )
-            closed_mapper = self.mapper_registry.map_imperatively(
-                Order, closedorders, non_primary=True
-            )
-
-        self.mapper_registry.map_imperatively(
-            User,
-            users,
-            properties=dict(
-                addresses=relationship(
-                    Address, lazy="subquery", order_by=addresses.c.id
-                ),
-                open_orders=relationship(
-                    open_mapper,
-                    primaryjoin=sa.and_(
-                        openorders.c.isopen == 1,
-                        users.c.id == openorders.c.user_id,
-                    ),
-                    lazy="subquery",
-                    order_by=openorders.c.id,
-                ),
-                closed_orders=relationship(
-                    closed_mapper,
-                    primaryjoin=sa.and_(
-                        closedorders.c.isopen == 0,
-                        users.c.id == closedorders.c.user_id,
-                    ),
-                    lazy="subquery",
-                    order_by=closedorders.c.id,
-                ),
-            ),
-        )
-
-        self._run_double_test(4)
-
-    def _run_double_test(self, count):
-        User, Address, Order, Item = self.classes(
-            "User", "Address", "Order", "Item"
-        )
-        q = fixture_session().query(User).order_by(User.id)
-
-        def go():
-            eq_(
-                [
-                    User(
-                        id=7,
-                        addresses=[Address(id=1)],
-                        open_orders=[Order(id=3)],
-                        closed_orders=[Order(id=1), Order(id=5)],
-                    ),
-                    User(
-                        id=8,
-                        addresses=[
-                            Address(id=2),
-                            Address(id=3),
-                            Address(id=4),
-                        ],
-                        open_orders=[],
-                        closed_orders=[],
-                    ),
-                    User(
-                        id=9,
-                        addresses=[Address(id=5)],
-                        open_orders=[Order(id=4)],
-                        closed_orders=[Order(id=2)],
-                    ),
-                    User(id=10),
-                ],
-                q.all(),
-            )
-
-        self.assert_sql_count(testing.db, go, count)
-
-        sess = fixture_session()
-        user = sess.get(User, 7)
-
-        closed_mapper = User.closed_orders.entity
-        open_mapper = User.open_orders.entity
-        eq_(
-            [Order(id=1), Order(id=5)],
-            fixture_session()
-            .query(closed_mapper)
-            .filter(with_parent(user, User.closed_orders))
-            .all(),
-        )
-        eq_(
-            [Order(id=3)],
-            fixture_session()
-            .query(open_mapper)
-            .filter(with_parent(user, User.open_orders))
-            .all(),
-        )
-
-
-class ViewonlyFlagWarningTest(fixtures.MappedTest):
-    """test for #4993.
-
-    In 1.4, this moves to test/orm/test_cascade, deprecation warnings
-    become errors, will then be for #4994.
-
-    """
-
-    @classmethod
-    def define_tables(cls, metadata):
-        Table(
-            "users",
-            metadata,
-            Column("id", Integer, primary_key=True),
-            Column("name", String(30)),
-        )
-        Table(
-            "orders",
-            metadata,
-            Column("id", Integer, primary_key=True),
-            Column("user_id", Integer),
-            Column("description", String(30)),
-        )
-
-    @classmethod
-    def setup_classes(cls):
-        class User(cls.Comparable):
-            pass
-
-        class Order(cls.Comparable):
-            pass
-
-    @testing.combinations(
-        ("passive_deletes", True),
-        ("passive_updates", False),
-        ("enable_typechecks", False),
-        ("active_history", True),
-        ("cascade_backrefs", False),
-    )
-    def test_viewonly_warning(self, flag, value):
-        Order = self.classes.Order
-
-        with testing.expect_warnings(
-            r"Setting %s on relationship\(\) while also setting "
-            "viewonly=True does not make sense" % flag
-        ):
-            kw = {
-                "viewonly": True,
-                "primaryjoin": self.tables.users.c.id
-                == foreign(self.tables.orders.c.user_id),
-            }
-            kw[flag] = value
-            rel = relationship(Order, **kw)
-
-            eq_(getattr(rel, flag), value)
-
-
-class NonPrimaryMapperTest(_fixtures.FixtureTest, AssertsCompiledSQL):
-    __dialect__ = "default"
-
-    def teardown_test(self):
-        clear_mappers()
-
-    def test_non_primary_identity_class(self):
-        User = self.classes.User
-        users, addresses = self.tables.users, self.tables.addresses
-
-        class AddressUser(User):
-            pass
-
-        self.mapper_registry.map_imperatively(
-            User, users, polymorphic_identity="user"
-        )
-        m2 = self.mapper_registry.map_imperatively(
-            AddressUser,
-            addresses,
-            inherits=User,
-            polymorphic_identity="address",
-            properties={"address_id": addresses.c.id},
-        )
-        with testing.expect_deprecated(
-            "The mapper.non_primary parameter is deprecated"
-        ):
-            m3 = self.mapper_registry.map_imperatively(
-                AddressUser, addresses, non_primary=True
-            )
-        assert m3._identity_class is m2._identity_class
-        eq_(
-            m2.identity_key_from_instance(AddressUser()),
-            m3.identity_key_from_instance(AddressUser()),
-        )
-
-    def test_illegal_non_primary(self):
-        users, Address, addresses, User = (
-            self.tables.users,
-            self.classes.Address,
-            self.tables.addresses,
-            self.classes.User,
-        )
-
-        self.mapper_registry.map_imperatively(User, users)
-        self.mapper_registry.map_imperatively(Address, addresses)
-        with testing.expect_deprecated(
-            "The mapper.non_primary parameter is deprecated"
-        ):
-            m = self.mapper_registry.map_imperatively(  # noqa F841
-                User,
-                users,
-                non_primary=True,
-                properties={"addresses": relationship(Address)},
-            )
-        assert_raises_message(
-            sa.exc.ArgumentError,
-            "Attempting to assign a new relationship 'addresses' "
-            "to a non-primary mapper on class 'User'",
-            configure_mappers,
-        )
-
-    def test_illegal_non_primary_2(self):
-        User, users = self.classes.User, self.tables.users
-
-        assert_raises_message(
-            sa.exc.InvalidRequestError,
-            "Configure a primary mapper first",
-            self.mapper_registry.map_imperatively,
-            User,
-            users,
-            non_primary=True,
-        )
-
-    def test_illegal_non_primary_3(self):
-        users, addresses = self.tables.users, self.tables.addresses
-
-        class Base:
-            pass
-
-        class Sub(Base):
-            pass
-
-        self.mapper_registry.map_imperatively(Base, users)
-        assert_raises_message(
-            sa.exc.InvalidRequestError,
-            "Configure a primary mapper first",
-            self.mapper_registry.map_imperatively,
-            Sub,
-            addresses,
-            non_primary=True,
-        )
-
-    def test_illegal_non_primary_legacy(self):
-        users, Address, addresses, User = (
-            self.tables.users,
-            self.classes.Address,
-            self.tables.addresses,
-            self.classes.User,
-        )
-
-        with testing.expect_deprecated(
-            "Calling the mapper.* function directly outside of a declarative "
-        ):
-            mapper(User, users)
-        with testing.expect_deprecated(
-            "Calling the mapper.* function directly outside of a declarative "
-        ):
-            mapper(Address, addresses)
-        with testing.expect_deprecated(
-            "The mapper.non_primary parameter is deprecated"
-        ):
-            m = mapper(  # noqa F841
-                User,
-                users,
-                non_primary=True,
-                properties={"addresses": relationship(Address)},
-            )
-        assert_raises_message(
-            sa.exc.ArgumentError,
-            "Attempting to assign a new relationship 'addresses' "
-            "to a non-primary mapper on class 'User'",
-            configure_mappers,
-        )
-
-    def test_illegal_non_primary_2_legacy(self):
-        User, users = self.classes.User, self.tables.users
-
-        with testing.expect_deprecated(
-            "The mapper.non_primary parameter is deprecated"
-        ):
-            assert_raises_message(
-                sa.exc.InvalidRequestError,
-                "Configure a primary mapper first",
-                mapper,
-                User,
-                users,
-                non_primary=True,
-            )
-
-    def test_illegal_non_primary_3_legacy(self):
-        users, addresses = self.tables.users, self.tables.addresses
-
-        class Base:
-            pass
-
-        class Sub(Base):
-            pass
-
-        with testing.expect_deprecated(
-            "Calling the mapper.* function directly outside of a declarative "
-        ):
-            mapper(Base, users)
-        with testing.expect_deprecated(
-            "The mapper.non_primary parameter is deprecated",
-        ):
-            assert_raises_message(
-                sa.exc.InvalidRequestError,
-                "Configure a primary mapper first",
-                mapper,
-                Sub,
-                addresses,
-                non_primary=True,
-            )
-
-
-class InstancesTest(QueryTest, AssertsCompiledSQL):
-    @testing.fails(
-        "ORM refactor not allowing this yet, "
-        "we may just abandon this use case"
-    )
-    def test_from_alias_one(self):
-        User, addresses, users = (
-            self.classes.User,
-            self.tables.addresses,
-            self.tables.users,
-        )
-
-        query = (
-            users.select(users.c.id == 7)
-            .union(users.select(users.c.id > 7))
-            .alias("ulist")
-            .outerjoin(addresses)
-            .select(order_by=[text("ulist.id"), addresses.c.id])
-        )
-        sess = fixture_session()
-        q = sess.query(User)
-
-        # note this has multiple problems because we aren't giving Query
-        # the statement where it would be able to create an adapter
-        def go():
-            with testing.expect_deprecated(
-                r"Using the Query.instances\(\) method without a context",
-                "Retrieving row values using Column objects with only "
-                "matching names",
-            ):
-                result = list(
-                    q.options(
-                        contains_alias("ulist"), contains_eager("addresses")
-                    ).instances(query.execute())
-                )
-            assert self.static.user_address_result == result
-
-        self.assert_sql_count(testing.db, go, 1)
-
-    def test_from_alias_two_old_way(self):
-        User, addresses, users = (
-            self.classes.User,
-            self.tables.addresses,
-            self.tables.users,
-        )
-
-        query = (
-            users.select()
-            .where(users.c.id == 7)
-            .union(users.select().where(users.c.id > 7))
-            .alias("ulist")
-            .outerjoin(addresses)
-            .select()
-            .order_by(text("ulist.id"), addresses.c.id)
-        )
-        sess = fixture_session()
-        q = sess.query(User)
-
-        def go():
-            with testing.expect_deprecated(
-                "The AliasOption is not necessary for entities to be "
-                "matched up to a query"
-            ):
-                result = (
-                    q.options(
-                        contains_alias("ulist"), contains_eager(User.addresses)
-                    )
-                    .from_statement(query)
-                    .all()
-                )
-            assert self.static.user_address_result == result
-
-        self.assert_sql_count(testing.db, go, 1)
-
-    def test_contains_eager(self):
-        users, addresses, User = (
-            self.tables.users,
-            self.tables.addresses,
-            self.classes.User,
-        )
-
-        sess = fixture_session()
-
-        selectquery = (
-            users.outerjoin(addresses)
-            .select()
-            .where(users.c.id < 10)
-            .order_by(users.c.id, addresses.c.id)
-        )
-        q = sess.query(User)
-
-        def go():
-            with testing.expect_deprecated(
-                r"Using the Query.instances\(\) method without a context"
-            ):
-                result = list(
-                    q.options(contains_eager(User.addresses)).instances(
-                        sess.execute(selectquery)
-                    )
-                )
-            assert self.static.user_address_result[0:3] == result
-
-        self.assert_sql_count(testing.db, go, 1)
-
-        sess.expunge_all()
-
-        def go():
-            with testing.expect_deprecated(
-                r"Using the Query.instances\(\) method without a context"
-            ):
-                result = list(
-                    q.options(contains_eager(User.addresses)).instances(
-                        sess.connection().execute(selectquery)
-                    )
-                )
-            assert self.static.user_address_result[0:3] == result
-
-        self.assert_sql_count(testing.db, go, 1)
-
-    def test_contains_eager_aliased_instances(self):
-        addresses, users, User = (
-            self.tables.addresses,
-            self.tables.users,
-            self.classes.User,
-        )
-
-        sess = fixture_session()
-        q = sess.query(User)
-
-        adalias = addresses.alias("adalias")
-        selectquery = (
-            users.outerjoin(adalias)
-            .select()
-            .order_by(users.c.id, adalias.c.id)
-        )
-
-        # note this has multiple problems because we aren't giving Query
-        # the statement where it would be able to create an adapter
-        def go():
-            with testing.expect_deprecated(
-                r"Using the Query.instances\(\) method without a context"
-            ):
-                result = list(
-                    q.options(
-                        contains_eager(User.addresses, alias=adalias)
-                    ).instances(sess.connection().execute(selectquery))
-                )
-            assert self.static.user_address_result == result
-
-        self.assert_sql_count(testing.db, go, 1)
-
-    def test_contains_eager_multi_alias(self):
-        orders, items, users, order_items, User = (
-            self.tables.orders,
-            self.tables.items,
-            self.tables.users,
-            self.tables.order_items,
-            self.classes.User,
-        )
-
-        Order = self.classes.Order
-
-        sess = fixture_session()
-        q = sess.query(User)
-
-        oalias = orders.alias("o1")
-        ialias = items.alias("i1")
-        query = (
-            users.outerjoin(oalias)
-            .outerjoin(order_items)
-            .outerjoin(ialias)
-            .select()
-            .order_by(users.c.id, oalias.c.id, ialias.c.id)
-        )
-
-        # test using Alias with more than one level deep
-
-        # new way:
-        # from sqlalchemy.orm.strategy_options import Load
-        # opt = Load(User).contains_eager('orders', alias=oalias).
-        #     contains_eager('items', alias=ialias)
-
-        def go():
-            with testing.expect_deprecated(
-                r"Using the Query.instances\(\) method without a context"
-            ):
-                result = list(
-                    q.options(
-                        contains_eager(User.orders, alias=oalias),
-                        defaultload(User.orders).contains_eager(
-                            Order.items, alias=ialias
-                        ),
-                    ).instances(sess.connection().execute(query))
-                )
-            assert self.static.user_order_result == result
-
-        self.assert_sql_count(testing.db, go, 1)
-
-
-class TextTest(QueryTest):
-    def test_via_textasfrom_select_from(self):
-        User = self.classes.User
-        s = fixture_session()
-
-        with assertions.expect_deprecated_20(sef_dep):
-            eq_(
-                s.query(User)
-                .select_entity_from(
-                    text("select * from users")
-                    .columns(User.id, User.name)
-                    .subquery()
-                )
-                .order_by(User.id)
-                .all(),
-                [User(id=7), User(id=8), User(id=9), User(id=10)],
-            )
-
-
-class TestDeprecation20(QueryTest):
-    def test_relation(self):
-        User = self.classes.User
-        with testing.expect_deprecated_20(".*relationship"):
-            relation(User.addresses)
-
-    def test_eagerloading(self):
-        User = self.classes.User
-        with testing.expect_deprecated_20(".*joinedload"):
-            eagerload(User.addresses)
-
-
-class DistinctOrderByImplicitTest(QueryTest, AssertsCompiledSQL):
-    __dialect__ = "default"
-
-    def test_columns_augmented_roundtrip_three(self):
-        User, Address = self.classes.User, self.classes.Address
-
-        sess = fixture_session()
-
-        q = (
-            sess.query(User.id, User.name.label("foo"), Address.id)
-            .join(Address, true())
-            .filter(User.name == "jack")
-            .filter(User.id + Address.user_id > 0)
-            .distinct()
-            .order_by(User.id, User.name, Address.email_address)
-        )
-
-        # even though columns are added, they aren't in the result
-        with testing.expect_deprecated(
-            "ORDER BY columns added implicitly due to "
-        ):
-            eq_(
-                q.all(),
-                [
-                    (7, "jack", 3),
-                    (7, "jack", 4),
-                    (7, "jack", 2),
-                    (7, "jack", 5),
-                    (7, "jack", 1),
-                ],
-            )
-            for row in q:
-                eq_(row._mapping.keys(), ["id", "foo", "id"])
-
-    def test_columns_augmented_sql_one(self):
-        User, Address = self.classes.User, self.classes.Address
-
-        sess = fixture_session()
-
-        q = (
-            sess.query(User.id, User.name.label("foo"), Address.id)
-            .distinct()
-            .order_by(User.id, User.name, Address.email_address)
-        )
-
-        # Address.email_address is added because of DISTINCT,
-        # however User.id, User.name are not b.c. they're already there,
-        # even though User.name is labeled
-        with testing.expect_deprecated(
-            "ORDER BY columns added implicitly due to "
-        ):
-            self.assert_compile(
-                q,
-                "SELECT DISTINCT users.id AS users_id, users.name AS foo, "
-                "addresses.id AS addresses_id, addresses.email_address AS "
-                "addresses_email_address FROM users, addresses "
-                "ORDER BY users.id, users.name, addresses.email_address",
-            )
-
-
-class SessionEventsTest(_RemoveListeners, _fixtures.FixtureTest):
-    run_inserts = None
-
-    def test_on_bulk_update_hook(self):
-        User, users = self.classes.User, self.tables.users
-
-        sess = fixture_session()
-        canary = Mock()
-
-        event.listen(sess, "after_bulk_update", canary.after_bulk_update)
-
-        def legacy(ses, qry, ctx, res):
-            canary.after_bulk_update_legacy(ses, qry, ctx, res)
-
-        event.listen(sess, "after_bulk_update", legacy)
-
-        self.mapper_registry.map_imperatively(User, users)
-
-        with testing.expect_deprecated(
-            'The argument signature for the "SessionEvents.after_bulk_update" '
-            "event listener"
-        ):
-            sess.query(User).update({"name": "foo"})
-
-        eq_(canary.after_bulk_update.call_count, 1)
-
-        upd = canary.after_bulk_update.mock_calls[0][1][0]
-        eq_(upd.session, sess)
-        eq_(
-            canary.after_bulk_update_legacy.mock_calls,
-            [call(sess, upd.query, None, upd.result)],
-        )
-
-    def test_on_bulk_delete_hook(self):
-        User, users = self.classes.User, self.tables.users
-
-        sess = fixture_session()
-        canary = Mock()
-
-        event.listen(sess, "after_bulk_delete", canary.after_bulk_delete)
-
-        def legacy(ses, qry, ctx, res):
-            canary.after_bulk_delete_legacy(ses, qry, ctx, res)
-
-        event.listen(sess, "after_bulk_delete", legacy)
-
-        self.mapper_registry.map_imperatively(User, users)
-
-        with testing.expect_deprecated(
-            'The argument signature for the "SessionEvents.after_bulk_delete" '
-            "event listener"
-        ):
-            sess.query(User).delete()
-
-        eq_(canary.after_bulk_delete.call_count, 1)
-
-        upd = canary.after_bulk_delete.mock_calls[0][1][0]
-        eq_(upd.session, sess)
-        eq_(
-            canary.after_bulk_delete_legacy.mock_calls,
-            [call(sess, upd.query, None, upd.result)],
-        )
-
-
-class ImmediateTest(_fixtures.FixtureTest):
-    run_inserts = "once"
-    run_deletes = None
-
-    @classmethod
-    def setup_mappers(cls):
-        Address, addresses, users, User = (
-            cls.classes.Address,
-            cls.tables.addresses,
-            cls.tables.users,
-            cls.classes.User,
-        )
-
-        cls.mapper_registry.map_imperatively(Address, addresses)
-
-        cls.mapper_registry.map_imperatively(
-            User, users, properties=dict(addresses=relationship(Address))
-        )
-
-    def test_value(self):
-        User = self.classes.User
-
-        sess = fixture_session()
-
-        with testing.expect_deprecated(r"Query.value\(\) is deprecated"):
-            eq_(sess.query(User).filter_by(id=7).value(User.id), 7)
-        with testing.expect_deprecated(r"Query.value\(\) is deprecated"):
-            eq_(
-                sess.query(User.id, User.name).filter_by(id=7).value(User.id),
-                7,
-            )
-        with testing.expect_deprecated(r"Query.value\(\) is deprecated"):
-            eq_(sess.query(User).filter_by(id=0).value(User.id), None)
-
-        sess.bind = testing.db
-        with testing.expect_deprecated(r"Query.value\(\) is deprecated"):
-            eq_(sess.query().value(sa.literal_column("1").label("x")), 1)
-
-    def test_value_cancels_loader_opts(self):
-        User = self.classes.User
-
-        sess = fixture_session()
-
-        q = (
-            sess.query(User)
-            .filter(User.name == "ed")
-            .options(joinedload(User.addresses))
-        )
-
-        with testing.expect_deprecated(r"Query.value\(\) is deprecated"):
-            q = q.value(func.count(literal_column("*")))
-
-
-class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
-    __dialect__ = "default"
-
-    def test_values(self):
-        Address, users, User = (
-            self.classes.Address,
-            self.tables.users,
-            self.classes.User,
-        )
-
-        sess = fixture_session()
-
-        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
-            assert list(sess.query(User).values()) == list()
-
-        sel = users.select().where(User.id.in_([7, 8])).alias()
-        q = sess.query(User)
-        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
-            q2 = q.select_entity_from(sel).values(User.name)
-        eq_(list(q2), [("jack",), ("ed",)])
-
-        q = sess.query(User)
-
-        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
-            q2 = q.order_by(User.id).values(
-                User.name, User.name + " " + cast(User.id, String(50))
-            )
-        eq_(
-            list(q2),
-            [
-                ("jack", "jack 7"),
-                ("ed", "ed 8"),
-                ("fred", "fred 9"),
-                ("chuck", "chuck 10"),
-            ],
-        )
-
-        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
-            q2 = (
-                q.join(User.addresses)
-                .filter(User.name.like("%e%"))
-                .order_by(User.id, Address.id)
-                .values(User.name, Address.email_address)
-            )
-        eq_(
-            list(q2),
-            [
-                ("ed", "ed@wood.com"),
-                ("ed", "ed@bettyboop.com"),
-                ("ed", "ed@lala.com"),
-                ("fred", "fred@fred.com"),
-            ],
-        )
-
-        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
-            q2 = (
-                q.join(User.addresses)
-                .filter(User.name.like("%e%"))
-                .order_by(desc(Address.email_address))
-                .slice(1, 3)
-                .values(User.name, Address.email_address)
-            )
-        eq_(list(q2), [("ed", "ed@wood.com"), ("ed", "ed@lala.com")])
-
-        adalias = aliased(Address)
-        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
-            q2 = (
-                q.join(adalias, User.addresses)
-                .filter(User.name.like("%e%"))
-                .order_by(adalias.email_address)
-                .values(User.name, adalias.email_address)
-            )
-        eq_(
-            list(q2),
-            [
-                ("ed", "ed@bettyboop.com"),
-                ("ed", "ed@lala.com"),
-                ("ed", "ed@wood.com"),
-                ("fred", "fred@fred.com"),
-            ],
-        )
-
-        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
-            q2 = q.values(func.count(User.name))
-        assert next(q2) == (4,)
-
-        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
-            q2 = (
-                q.select_entity_from(sel)
-                .filter(User.id == 8)
-                .values(User.name, sel.c.name, User.name)
-            )
-        eq_(list(q2), [("ed", "ed", "ed")])
-
-        # using User.xxx is alised against "sel", so this query returns nothing
-        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
-            q2 = (
-                q.select_entity_from(sel)
-                .filter(User.id == 8)
-                .filter(User.id > sel.c.id)
-                .values(User.name, sel.c.name, User.name)
-            )
-        eq_(list(q2), [])
-
-        # whereas this uses users.c.xxx, is not aliased and creates a new join
-        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
-            q2 = (
-                q.select_entity_from(sel)
-                .filter(users.c.id == 8)
-                .filter(users.c.id > sel.c.id)
-                .values(users.c.name, sel.c.name, User.name)
-            )
-            eq_(list(q2), [("ed", "jack", "jack")])
-
-    @testing.fails_on("mssql", "FIXME: unknown")
-    def test_values_specific_order_by(self):
-        users, User = self.tables.users, self.classes.User
-
-        sess = fixture_session()
-
-        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
-            assert list(sess.query(User).values()) == list()
-
-        sel = users.select().where(User.id.in_([7, 8])).alias()
-        q = sess.query(User)
-        u2 = aliased(User)
-        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
-            q2 = (
-                q.select_entity_from(sel)
-                .filter(u2.id > 1)
-                .filter(or_(u2.id == User.id, u2.id != User.id))
-                .order_by(User.id, sel.c.id, u2.id)
-                .values(User.name, sel.c.name, u2.name)
-            )
-        eq_(
-            list(q2),
-            [
-                ("jack", "jack", "jack"),
-                ("jack", "jack", "ed"),
-                ("jack", "jack", "fred"),
-                ("jack", "jack", "chuck"),
-                ("ed", "ed", "jack"),
-                ("ed", "ed", "ed"),
-                ("ed", "ed", "fred"),
-                ("ed", "ed", "chuck"),
-            ],
-        )
-
-    @testing.fails_on("mssql", "FIXME: unknown")
-    @testing.fails_on(
-        "oracle", "Oracle doesn't support boolean expressions as " "columns"
-    )
-    @testing.fails_on(
-        "postgresql+pg8000",
-        "pg8000 parses the SQL itself before passing on "
-        "to PG, doesn't parse this",
-    )
-    @testing.fails_on(
-        "postgresql+asyncpg",
-        "Asyncpg uses preprated statements that are not compatible with how "
-        "sqlalchemy passes the query. Fails with "
-        'ERROR:  column "users.name" must appear in the GROUP BY clause'
-        " or be used in an aggregate function",
-    )
-    def test_values_with_boolean_selects(self):
-        """Tests a values clause that works with select boolean
-        evaluations"""
-
-        User = self.classes.User
-
-        sess = fixture_session()
-
-        q = sess.query(User)
-        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
-            q2 = (
-                q.group_by(User.name.like("%j%"))
-                .order_by(desc(User.name.like("%j%")))
-                .values(
-                    User.name.like("%j%"), func.count(User.name.like("%j%"))
-                )
-            )
-        eq_(list(q2), [(True, 1), (False, 3)])
-
-        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
-            q2 = q.order_by(desc(User.name.like("%j%"))).values(
-                User.name.like("%j%")
-            )
-        eq_(list(q2), [(True,), (False,), (False,), (False,)])
-
-
-class JoinTest(QueryTest, AssertsCompiledSQL):
-    __dialect__ = "default"
-
-    def test_orderby_arg_bug(self):
-        User, users, Order = (
-            self.classes.User,
-            self.tables.users,
-            self.classes.Order,
-        )
-
-        sess = fixture_session()
-        # no arg error
-        with testing.expect_deprecated_20(join_aliased_dep):
-            (
-                sess.query(User)
-                .join(User.orders, aliased=True)
-                .order_by(Order.id)
-                .reset_joinpoint()
-                .order_by(users.c.id)
-                .all()
-            )
-
-    def test_does_filter_aliasing_work(self):
-        User, Address = self.classes("User", "Address")
-
-        s = fixture_session()
-
-        # aliased=True is to be deprecated, other filter lambdas
-        # that go into effect include polymorphic filtering.
-        with testing.expect_deprecated(join_aliased_dep):
-            q = (
-                s.query(lambda: User)
-                .join(lambda: User.addresses, aliased=True)
-                .filter(lambda: Address.email_address == "foo")
-            )
-        self.assert_compile(
-            q,
-            "SELECT users.id AS users_id, users.name AS users_name "
-            "FROM users JOIN addresses AS addresses_1 "
-            "ON users.id = addresses_1.user_id "
-            "WHERE addresses_1.email_address = :email_address_1",
-        )
-
-    def test_multi_tuple_form_legacy_one(self):
-        """test the 'tuple' form of join, now superseded
-        by the two-element join() form.
-
-
-        """
-
-        Order, User = (
-            self.classes.Order,
-            self.classes.User,
-        )
-
-        sess = fixture_session()
-
-        with testing.expect_deprecated(join_tuple_form):
-            q = (
-                sess.query(User)
-                .join((Order, User.id == Order.user_id))
-                .filter_by(description="foo")
-            )
-        self.assert_compile(
-            q,
-            "SELECT users.id AS users_id, users.name AS users_name "
-            "FROM users JOIN orders ON users.id = orders.user_id "
-            "WHERE orders.description = :description_1",
-        )
-
-    def test_multi_tuple_form_legacy_two(self):
-        """test the 'tuple' form of join, now superseded
-        by the two-element join() form.
-
-
-        """
-
-        Item, Order, User = (
-            self.classes.Item,
-            self.classes.Order,
-            self.classes.User,
-        )
-
-        sess = fixture_session()
-
-        with testing.expect_deprecated_20(join_tuple_form):
-            q = (
-                sess.query(User)
-                .join((Order, User.id == Order.user_id), (Item, Order.items))
-                .filter_by(description="foo")
-            )
-        self.assert_compile(
-            q,
-            "SELECT users.id AS users_id, users.name AS users_name "
-            "FROM users JOIN orders ON users.id = orders.user_id "
-            "JOIN order_items AS order_items_1 ON orders.id = "
-            "order_items_1.order_id JOIN items ON items.id = "
-            "order_items_1.item_id WHERE items.description = :description_1",
-        )
-
-    def test_single_prop_5(self):
-        (
-            Order,
-            User,
-        ) = (self.classes.Order, self.classes.User)
-
-        sess = fixture_session()
-        with testing.expect_deprecated_20(join_chain_dep):
-            self.assert_compile(
-                sess.query(User).join(User.orders, Order.items),
-                "SELECT users.id AS users_id, users.name AS users_name "
-                "FROM users "
-                "JOIN orders ON users.id = orders.user_id "
-                "JOIN order_items AS order_items_1 "
-                "ON orders.id = order_items_1.order_id JOIN items "
-                "ON items.id = order_items_1.item_id",
-            )
-
-    def test_single_prop_7(self):
-        Order, User = (self.classes.Order, self.classes.User)
-
-        sess = fixture_session()
-        # this query is somewhat nonsensical.  the old system didn't render a
-        # correct query for this. In this case its the most faithful to what
-        # was asked - there's no linkage between User.orders and "oalias",
-        # so two FROM elements are generated.
-        oalias = aliased(Order)
-        with testing.expect_deprecated_20(join_chain_dep):
-            self.assert_compile(
-                sess.query(User).join(User.orders, oalias.items),
-                "SELECT users.id AS users_id, users.name AS users_name "
-                "FROM users "
-                "JOIN orders ON users.id = orders.user_id, "
-                "orders AS orders_1 JOIN order_items AS order_items_1 "
-                "ON orders_1.id = order_items_1.order_id "
-                "JOIN items ON items.id = order_items_1.item_id",
-            )
-
-    def test_single_prop_8(self):
-        (
-            Order,
-            User,
-        ) = (self.classes.Order, self.classes.User)
-
-        sess = fixture_session()
-        # same as before using an aliased() for User as well
-        ualias = aliased(User)
-        oalias = aliased(Order)
-        with testing.expect_deprecated_20(join_chain_dep):
-            self.assert_compile(
-                sess.query(ualias).join(ualias.orders, oalias.items),
-                "SELECT users_1.id AS users_1_id, users_1.name "
-                "AS users_1_name "
-                "FROM users AS users_1 "
-                "JOIN orders ON users_1.id = orders.user_id, "
-                "orders AS orders_1 JOIN order_items AS order_items_1 "
-                "ON orders_1.id = order_items_1.order_id "
-                "JOIN items ON items.id = order_items_1.item_id",
-            )
-
-    def test_single_prop_10(self):
-        User, Address = (self.classes.User, self.classes.Address)
-
-        sess = fixture_session()
-        with testing.expect_deprecated_20(join_aliased_dep):
-            self.assert_compile(
-                sess.query(User)
-                .join(User.addresses, aliased=True)
-                .filter(Address.email_address == "foo"),
-                "SELECT users.id AS users_id, users.name AS users_name "
-                "FROM users JOIN addresses AS addresses_1 "
-                "ON users.id = addresses_1.user_id "
-                "WHERE addresses_1.email_address = :email_address_1",
-            )
-
-    def test_single_prop_11(self):
-        Item, Order, User, = (
-            self.classes.Item,
-            self.classes.Order,
-            self.classes.User,
-        )
-
-        sess = fixture_session()
-        with testing.expect_deprecated_20(join_aliased_dep, join_chain_dep):
-            self.assert_compile(
-                sess.query(User)
-                .join(User.orders, Order.items, aliased=True)
-                .filter(Item.id == 10),
-                "SELECT users.id AS users_id, users.name AS users_name "
-                "FROM users JOIN orders AS orders_1 "
-                "ON users.id = orders_1.user_id "
-                "JOIN order_items AS order_items_1 "
-                "ON orders_1.id = order_items_1.order_id "
-                "JOIN items AS items_1 ON items_1.id = order_items_1.item_id "
-                "WHERE items_1.id = :id_1",
-            )
-
-    def test_multiple_adaption(self):
-        Item, Order, User = (
-            self.classes.Item,
-            self.classes.Order,
-            self.classes.User,
-        )
-
-        sess = fixture_session()
-
-        with testing.expect_deprecated_20(join_chain_dep, join_aliased_dep):
-            self.assert_compile(
-                sess.query(User)
-                .join(User.orders, Order.items, aliased=True)
-                .filter(Order.id == 7)
-                .filter(Item.id == 8),
-                "SELECT users.id AS users_id, users.name AS users_name "
-                "FROM users "
-                "JOIN orders AS orders_1 "
-                "ON users.id = orders_1.user_id JOIN order_items AS "
-                "order_items_1 "
-                "ON orders_1.id = order_items_1.order_id "
-                "JOIN items AS items_1 ON items_1.id = order_items_1.item_id "
-                "WHERE orders_1.id = :id_1 AND items_1.id = :id_2",
-                use_default_dialect=True,
-            )
-
-    def test_onclause_conditional_adaption(self):
-        Item, Order, orders, order_items, User = (
-            self.classes.Item,
-            self.classes.Order,
-            self.tables.orders,
-            self.tables.order_items,
-            self.classes.User,
-        )
-
-        sess = fixture_session()
-
-        # this is now a very weird test, nobody should really
-        # be using the aliased flag in this way.
-        with testing.expect_deprecated_20(join_aliased_dep):
-            self.assert_compile(
-                sess.query(User)
-                .join(User.orders, aliased=True)
-                .join(
-                    Item,
-                    and_(
-                        Order.id == order_items.c.order_id,
-                        order_items.c.item_id == Item.id,
-                    ),
-                    from_joinpoint=True,
-                    aliased=True,
-                ),
-                "SELECT users.id AS users_id, users.name AS users_name "
-                "FROM users "
-                "JOIN orders AS orders_1 ON users.id = orders_1.user_id "
-                "JOIN items AS items_1 "
-                "ON orders_1.id = order_items.order_id "
-                "AND order_items.item_id = items_1.id",
-                use_default_dialect=True,
-            )
-
-        # nothing is deprecated here but it is comparing to the above
-        # that nothing is adapted.
-        oalias = aliased(Order, orders.select().subquery())
-        self.assert_compile(
-            sess.query(User)
-            .join(oalias, User.orders)
-            .join(
-                Item,
-                and_(
-                    oalias.id == order_items.c.order_id,
-                    order_items.c.item_id == Item.id,
-                ),
-            ),
-            "SELECT users.id AS users_id, users.name AS users_name "
-            "FROM users JOIN "
-            "(SELECT orders.id AS id, orders.user_id AS user_id, "
-            "orders.address_id AS address_id, orders.description "
-            "AS description, orders.isopen AS isopen FROM orders) AS anon_1 "
-            "ON users.id = anon_1.user_id JOIN items "
-            "ON anon_1.id = order_items.order_id "
-            "AND order_items.item_id = items.id",
-            use_default_dialect=True,
-        )
-
-        # query.join(<stuff>, aliased=True).join(target, sql_expression)
-        # or: query.join(path_to_some_joined_table_mapper).join(target,
-        # sql_expression)
-
-
-class AliasFromCorrectLeftTest(
-    fixtures.DeclarativeMappedTest, AssertsCompiledSQL
-):
-    run_create_tables = None
-    __dialect__ = "default"
-
-    @classmethod
-    def setup_classes(cls):
-        Base = cls.DeclarativeBasic
-
-        class Object(Base):
-            __tablename__ = "object"
-
-            type = Column(String(30))
-            __mapper_args__ = {
-                "polymorphic_identity": "object",
-                "polymorphic_on": type,
-            }
-
-            id = Column(Integer, primary_key=True)
-            name = Column(String(256))
-
-        class A(Object):
-            __tablename__ = "a"
-
-            __mapper_args__ = {"polymorphic_identity": "a"}
-
-            id = Column(Integer, ForeignKey("object.id"), primary_key=True)
-
-            b_list = relationship(
-                "B", secondary="a_b_association", backref="a_list"
-            )
-
-        class B(Object):
-            __tablename__ = "b"
-
-            __mapper_args__ = {"polymorphic_identity": "b"}
-
-            id = Column(Integer, ForeignKey("object.id"), primary_key=True)
-
-        class ABAssociation(Base):
-            __tablename__ = "a_b_association"
-
-            a_id = Column(Integer, ForeignKey("a.id"), primary_key=True)
-            b_id = Column(Integer, ForeignKey("b.id"), primary_key=True)
-
-        class X(Base):
-            __tablename__ = "x"
-
-            id = Column(Integer, primary_key=True)
-            name = Column(String(30))
-
-            obj_id = Column(Integer, ForeignKey("object.id"))
-            obj = relationship("Object", backref="x_list")
-
-    def test_join_prop_to_prop(self):
-        A, B, X = self.classes("A", "B", "X")
-
-        s = fixture_session()
-
-        # B -> A, but both are Object.  So when we say A.x_list, make sure
-        # we pick the correct right side
-        with testing.expect_deprecated_20(join_chain_dep):
-            q = s.query(B).join(B.a_list, A.x_list).filter(X.name == "x1")
-
-        with _aliased_join_warning():
-            self.assert_compile(
-                q,
-                "SELECT object.type AS object_type, b.id AS b_id, "
-                "object.id AS object_id, object.name AS object_name "
-                "FROM object JOIN b ON object.id = b.id "
-                "JOIN a_b_association AS a_b_association_1 "
-                "ON b.id = a_b_association_1.b_id "
-                "JOIN ("
-                "object AS object_1 "
-                "JOIN a AS a_1 ON object_1.id = a_1.id"
-                ") ON a_1.id = a_b_association_1.a_id "
-                "JOIN x ON object_1.id = x.obj_id WHERE x.name = :name_1",
-            )
-
-
-class SelfReferentialTest(fixtures.MappedTest, AssertsCompiledSQL):
-    run_setup_mappers = "once"
-    run_inserts = "once"
-    run_deletes = None
-    __dialect__ = "default"
-
-    @classmethod
-    def define_tables(cls, metadata):
-        Table(
-            "nodes",
-            metadata,
-            Column(
-                "id", Integer, primary_key=True, test_needs_autoincrement=True
-            ),
-            Column("parent_id", Integer, ForeignKey("nodes.id")),
-            Column("data", String(30)),
-        )
-
-    @classmethod
-    def setup_classes(cls):
-        class Node(cls.Comparable):
-            def append(self, node):
-                self.children.append(node)
-
-    @classmethod
-    def setup_mappers(cls):
-        Node, nodes = cls.classes.Node, cls.tables.nodes
-
-        cls.mapper_registry.map_imperatively(
-            Node,
-            nodes,
-            properties={
-                "children": relationship(
-                    Node,
-                    lazy="select",
-                    join_depth=3,
-                    backref=backref("parent", remote_side=[nodes.c.id]),
-                )
-            },
-        )
-
-    @classmethod
-    def insert_data(cls, connection):
-        Node = cls.classes.Node
-
-        sess = Session(connection)
-        n1 = Node(data="n1")
-        n1.append(Node(data="n11"))
-        n1.append(Node(data="n12"))
-        n1.append(Node(data="n13"))
-        n1.children[1].append(Node(data="n121"))
-        n1.children[1].append(Node(data="n122"))
-        n1.children[1].append(Node(data="n123"))
-        sess.add(n1)
-        sess.flush()
-        sess.close()
-
-    def test_join_2(self):
-        Node = self.classes.Node
-        sess = fixture_session()
-        with testing.expect_deprecated_20(join_aliased_dep):
-            ret = (
-                sess.query(Node.data)
-                .join(Node.children, aliased=True)
-                .filter_by(data="n122")
-                .all()
-            )
-        assert ret == [("n12",)]
-
-
-class InheritedJoinTest(
-    fixtures.NoCache,
-    _poly_fixtures._Polymorphic,
-    _poly_fixtures._PolymorphicFixtureBase,
-    AssertsCompiledSQL,
-):
-    run_setup_mappers = "once"
-    __dialect__ = "default"
-
-    def test_join_to_selectable(self):
-        people, Company, engineers, Engineer = (
-            self.tables.people,
-            self.classes.Company,
-            self.tables.engineers,
-            self.classes.Engineer,
-        )
-
-        sess = fixture_session()
-
-        with _aliased_join_deprecation():
-            self.assert_compile(
-                sess.query(Company)
-                .join(people.join(engineers), Company.employees)
-                .filter(Engineer.name == "dilbert"),
-                "SELECT companies.company_id AS companies_company_id, "
-                "companies.name AS companies_name "
-                "FROM companies JOIN (people "
-                "JOIN engineers ON people.person_id = "
-                "engineers.person_id) ON companies.company_id = "
-                "people.company_id WHERE people.name = :name_1",
-                use_default_dialect=True,
-            )
-
-    def test_multiple_adaption(self):
-        """test that multiple filter() adapters get chained together "
-        and work correctly within a multiple-entry join()."""
-
-        people, Company, Machine, engineers, machines, Engineer = (
-            self.tables.people,
-            self.classes.Company,
-            self.classes.Machine,
-            self.tables.engineers,
-            self.tables.machines,
-            self.classes.Engineer,
-        )
-
-        sess = fixture_session()
-
-        mach_alias = aliased(Machine, machines.select().subquery())
-
-        with testing.expect_deprecated_20(
-            join_aliased_dep
-        ), _aliased_join_deprecation():
-            self.assert_compile(
-                sess.query(Company)
-                .join(people.join(engineers), Company.employees)
-                .join(mach_alias, Engineer.machines, from_joinpoint=True)
-                .filter(Engineer.name == "dilbert")
-                .filter(mach_alias.name == "foo"),
-                "SELECT companies.company_id AS companies_company_id, "
-                "companies.name AS companies_name "
-                "FROM companies JOIN (people "
-                "JOIN engineers ON people.person_id = "
-                "engineers.person_id) ON companies.company_id = "
-                "people.company_id JOIN "
-                "(SELECT machines.machine_id AS machine_id, "
-                "machines.name AS name, "
-                "machines.engineer_id AS engineer_id "
-                "FROM machines) AS anon_1 "
-                "ON engineers.person_id = anon_1.engineer_id "
-                "WHERE people.name = :name_1 AND anon_1.name = :name_2",
-                use_default_dialect=True,
-            )
-
-    def test_with_poly_loader_criteria_warning(self):
-        Person, Manager = (
-            self.classes.Person,
-            self.classes.Manager,
-        )
-
-        sess = fixture_session()
-
-        with testing.expect_deprecated_20(w_polymorphic_dep):
-            q = (
-                sess.query(Person)
-                .with_polymorphic(Manager)
-                .options(with_loader_criteria(Person, Person.person_id == 1))
-            )
-
-        with testing.expect_warnings(
-            r"The with_loader_criteria\(\) function may not work "
-            r"correctly with the legacy Query.with_polymorphic\(\)"
+        with testing.expect_deprecated(
+            "The mapper.non_primary parameter is deprecated"
         ):
-            str(q)
-
-    def test_join_to_subclass_selectable_auto_alias(self):
-        Company, Engineer = self.classes("Company", "Engineer")
-        people, engineers = self.tables("people", "engineers")
-
-        sess = fixture_session()
-
-        with _aliased_join_deprecation():
-            eq_(
-                sess.query(Company)
-                .join(people.join(engineers), Company.employees)
-                .filter(Engineer.primary_language == "java")
-                .all(),
-                [self.c1],
-            )
-
-        # occurs for 2.0 style query also
-        with _aliased_join_deprecation():
-            stmt = (
-                select(Company)
-                .join(people.join(engineers), Company.employees)
-                .filter(Engineer.primary_language == "java")
-            )
-            results = sess.scalars(stmt)
-            eq_(results.all(), [self.c1])
-
-    def test_join_to_subclass_two(self):
-        Company, Engineer = self.classes("Company", "Engineer")
-        people, engineers = self.tables("people", "engineers")
-
-        sess = fixture_session()
-
-        with _aliased_join_deprecation():
-            eq_(
-                sess.query(Company)
-                .join(people.join(engineers), Company.employees)
-                .filter(Engineer.primary_language == "java")
-                .all(),
-                [self.c1],
-            )
-
-    def test_join_to_subclass_six_selectable_auto_alias(self):
-        Company, Engineer = self.classes("Company", "Engineer")
-        people, engineers = self.tables("people", "engineers")
-
-        sess = fixture_session()
-
-        with _aliased_join_deprecation():
-            eq_(
-                sess.query(Company)
-                .join(people.join(engineers), Company.employees)
-                .join(Engineer.machines)
-                .all(),
-                [self.c1, self.c2],
-            )
-
-    def test_join_to_subclass_six_point_five_selectable_auto_alias(self):
-        Company, Engineer = self.classes("Company", "Engineer")
-        people, engineers = self.tables("people", "engineers")
-
-        sess = fixture_session()
-
-        with _aliased_join_deprecation():
-            eq_(
-                sess.query(Company)
-                .join(people.join(engineers), Company.employees)
-                .join(Engineer.machines)
-                .filter(Engineer.name == "dilbert")
-                .all(),
-                [self.c1],
-            )
-
-    def test_join_to_subclass_seven_selectable_auto_alias(self):
-        Company, Engineer, Machine = self.classes(
-            "Company", "Engineer", "Machine"
-        )
-        people, engineers = self.tables("people", "engineers")
-
-        sess = fixture_session()
-
-        with _aliased_join_deprecation():
-            eq_(
-                sess.query(Company)
-                .join(people.join(engineers), Company.employees)
-                .join(Engineer.machines)
-                .filter(Machine.name.ilike("%thinkpad%"))
-                .all(),
-                [self.c1],
-            )
-
-
-class JoinFromSelectableTest(fixtures.MappedTest, AssertsCompiledSQL):
-    __dialect__ = "default"
-    run_setup_mappers = "once"
-
-    @classmethod
-    def define_tables(cls, metadata):
-        Table("table1", metadata, Column("id", Integer, primary_key=True))
-        Table(
-            "table2",
-            metadata,
-            Column("id", Integer, primary_key=True),
-            Column("t1_id", Integer),
-        )
-
-    @classmethod
-    def setup_classes(cls):
-        class T1(cls.Comparable):
-            pass
-
-        class T2(cls.Comparable):
-            pass
-
-    @classmethod
-    def setup_mappers(cls):
-        T1, T2 = cls.classes("T1", "T2")
-        table1, table2 = cls.tables.table1, cls.tables.table2
-        cls.mapper_registry.map_imperatively(T1, table1)
-        cls.mapper_registry.map_imperatively(T2, table2)
-
-    def test_mapped_to_select_implicit_left_w_aliased(self):
-        T1, T2 = self.classes.T1, self.classes.T2
-
-        sess = fixture_session()
-        subq = (
-            sess.query(T2.t1_id, func.count(T2.id).label("count"))
-            .group_by(T2.t1_id)
-            .subquery()
-        )
-
-        with testing.expect_deprecated_20(join_aliased_dep):
             assert_raises_message(
-                sa_exc.InvalidRequestError,
-                r"The aliased=True parameter on query.join\(\) only works "
-                "with "
-                "an ORM entity, not a plain selectable, as the target.",
-                # this doesn't work, so have it raise an error
-                sess.query(T1.id)
-                .join(subq, subq.c.t1_id == T1.id, aliased=True)
-                ._compile_context,
-            )
-
-
-class MultiplePathTest(fixtures.MappedTest, AssertsCompiledSQL):
-    @classmethod
-    def define_tables(cls, metadata):
-        Table(
-            "t1",
-            metadata,
-            Column(
-                "id", Integer, primary_key=True, test_needs_autoincrement=True
-            ),
-            Column("data", String(30)),
-        )
-        Table(
-            "t2",
-            metadata,
-            Column(
-                "id", Integer, primary_key=True, test_needs_autoincrement=True
-            ),
-            Column("data", String(30)),
-        )
-
-        Table(
-            "t1t2_1",
-            metadata,
-            Column("t1id", Integer, ForeignKey("t1.id")),
-            Column("t2id", Integer, ForeignKey("t2.id")),
-        )
-
-        Table(
-            "t1t2_2",
-            metadata,
-            Column("t1id", Integer, ForeignKey("t1.id")),
-            Column("t2id", Integer, ForeignKey("t2.id")),
-        )
-
-
-class BindSensitiveStringifyTest(fixtures.MappedTest):
-    def _fixture(self):
-        # building a totally separate metadata /mapping here
-        # because we need to control if the MetaData is bound or not
-
-        class User:
-            pass
-
-        m = MetaData()
-        user_table = Table(
-            "users",
-            m,
-            Column("id", Integer, primary_key=True),
-            Column("name", String(50)),
-        )
-
-        clear_mappers()
-        self.mapper_registry.map_imperatively(User, user_table)
-        return User
-
-    def _dialect_fixture(self):
-        class MyDialect(default.DefaultDialect):
-            default_paramstyle = "qmark"
+                sa.exc.InvalidRequestError,
+                "Configure a primary mapper first",
+                mapper,
+                User,
+                users,
+                non_primary=True,
+            )
 
-        from sqlalchemy.engine import base
+    def test_illegal_non_primary_3_legacy(self):
+        users, addresses = self.tables.users, self.tables.addresses
 
-        return base.Engine(mock.Mock(), MyDialect(), mock.Mock())
+        class Base:
+            pass
 
-    def _test(self, bound_session, session_present, expect_bound):
-        if bound_session:
-            eng = self._dialect_fixture()
-        else:
-            eng = None
+        class Sub(Base):
+            pass
 
-        User = self._fixture()
+        with testing.expect_deprecated(
+            "Calling the mapper.* function directly outside of a declarative "
+        ):
+            mapper(Base, users)
+        with testing.expect_deprecated(
+            "The mapper.non_primary parameter is deprecated",
+        ):
+            assert_raises_message(
+                sa.exc.InvalidRequestError,
+                "Configure a primary mapper first",
+                mapper,
+                Sub,
+                addresses,
+                non_primary=True,
+            )
 
-        s = Session(eng if bound_session else None)
-        q = s.query(User).filter(User.id == 7)
-        if not session_present:
-            q = q.with_session(None)
 
-        eq_ignore_whitespace(
-            str(q),
-            "SELECT users.id AS users_id, users.name AS users_name "
-            "FROM users WHERE users.id = ?"
-            if expect_bound
-            else "SELECT users.id AS users_id, users.name AS users_name "
-            "FROM users WHERE users.id = :id_1",
+class InstancesTest(QueryTest, AssertsCompiledSQL):
+    @testing.fails(
+        "ORM refactor not allowing this yet, "
+        "we may just abandon this use case"
+    )
+    def test_from_alias_one(self):
+        User, addresses, users = (
+            self.classes.User,
+            self.tables.addresses,
+            self.tables.users,
         )
 
-    def test_query_bound_session(self):
-        self._test(True, True, True)
+        query = (
+            users.select(users.c.id == 7)
+            .union(users.select(users.c.id > 7))
+            .alias("ulist")
+            .outerjoin(addresses)
+            .select(order_by=[text("ulist.id"), addresses.c.id])
+        )
+        sess = fixture_session()
+        q = sess.query(User)
 
-    def test_query_no_session(self):
-        self._test(False, False, False)
+        # note this has multiple problems because we aren't giving Query
+        # the statement where it would be able to create an adapter
+        def go():
+            with testing.expect_deprecated(
+                r"Using the Query.instances\(\) method without a context",
+                "Retrieving row values using Column objects with only "
+                "matching names",
+            ):
+                result = list(
+                    q.options(
+                        contains_alias("ulist"), contains_eager("addresses")
+                    ).instances(query.execute())
+                )
+            assert self.static.user_address_result == result
 
-    def test_query_unbound_session(self):
-        self._test(False, True, False)
+        self.assert_sql_count(testing.db, go, 1)
 
+    def test_from_alias_two_old_way(self):
+        User, addresses, users = (
+            self.classes.User,
+            self.tables.addresses,
+            self.tables.users,
+        )
 
-class DeprecationScopedSessionTest(fixtures.MappedTest):
-    def test_config_errors(self):
-        sm = sessionmaker()
+        query = (
+            users.select()
+            .where(users.c.id == 7)
+            .union(users.select().where(users.c.id > 7))
+            .alias("ulist")
+            .outerjoin(addresses)
+            .select()
+            .order_by(text("ulist.id"), addresses.c.id)
+        )
+        sess = fixture_session()
+        q = sess.query(User)
 
         def go():
-            s = sm()
-            s._is_asyncio = True
-            return s
+            with testing.expect_deprecated(
+                "The AliasOption is not necessary for entities to be "
+                "matched up to a query"
+            ):
+                result = (
+                    q.options(
+                        contains_alias("ulist"), contains_eager(User.addresses)
+                    )
+                    .from_statement(query)
+                    .all()
+                )
+            assert self.static.user_address_result == result
 
-        Session = scoped_session(go)
+        self.assert_sql_count(testing.db, go, 1)
 
-        with expect_deprecated(
-            "Using `scoped_session` with asyncio is deprecated and "
-            "will raise an error in a future version. "
-            "Please use `async_scoped_session` instead."
-        ):
-            Session()
-        Session.remove()
+    def test_contains_eager(self):
+        users, addresses, User = (
+            self.tables.users,
+            self.tables.addresses,
+            self.classes.User,
+        )
 
+        sess = fixture_session()
 
-@testing.combinations(
-    (
-        "inline",
-        True,
-    ),
-    (
-        "separate",
-        False,
-    ),
-    argnames="inline",
-    id_="sa",
-)
-@testing.combinations(
-    (
-        "string",
-        True,
-    ),
-    (
-        "literal",
-        False,
-    ),
-    argnames="stringbased",
-    id_="sa",
-)
-class ExplicitJoinTest(fixtures.MappedTest):
-    @classmethod
-    def define_tables(cls, metadata):
-        global User, Address
-        Base = declarative_base(metadata=metadata)
+        selectquery = (
+            users.outerjoin(addresses)
+            .select()
+            .where(users.c.id < 10)
+            .order_by(users.c.id, addresses.c.id)
+        )
+        q = sess.query(User)
 
-        class User(Base, fixtures.ComparableEntity):
+        def go():
+            with testing.expect_deprecated(
+                r"Using the Query.instances\(\) method without a context"
+            ):
+                result = list(
+                    q.options(contains_eager(User.addresses)).instances(
+                        sess.execute(selectquery)
+                    )
+                )
+            assert self.static.user_address_result[0:3] == result
 
-            __tablename__ = "users"
-            id = Column(
-                Integer, primary_key=True, test_needs_autoincrement=True
-            )
-            name = Column(String(50))
+        self.assert_sql_count(testing.db, go, 1)
 
-        class Address(Base, fixtures.ComparableEntity):
+        sess.expunge_all()
 
-            __tablename__ = "addresses"
-            id = Column(
-                Integer, primary_key=True, test_needs_autoincrement=True
-            )
-            email = Column(String(50))
-            user_id = Column(Integer, ForeignKey("users.id"))
-            if cls.inline:
-                if cls.stringbased:
-                    user = relationship(
-                        "User",
-                        primaryjoin="User.id==Address.user_id",
-                        backref="addresses",
-                    )
-                else:
-                    user = relationship(
-                        User,
-                        primaryjoin=User.id == user_id,
-                        backref="addresses",
+        def go():
+            with testing.expect_deprecated(
+                r"Using the Query.instances\(\) method without a context"
+            ):
+                result = list(
+                    q.options(contains_eager(User.addresses)).instances(
+                        sess.connection().execute(selectquery)
                     )
-
-        if not cls.inline:
-            configure_mappers()
-            if cls.stringbased:
-                Address.user = relationship(
-                    "User",
-                    primaryjoin="User.id==Address.user_id",
-                    backref="addresses",
-                )
-            else:
-                Address.user = relationship(
-                    User,
-                    primaryjoin=User.id == Address.user_id,
-                    backref="addresses",
                 )
+            assert self.static.user_address_result[0:3] == result
 
-    @classmethod
-    def insert_data(cls, connection):
-        params = [
-            dict(list(zip(("id", "name"), column_values)))
-            for column_values in [
-                (7, "jack"),
-                (8, "ed"),
-                (9, "fred"),
-                (10, "chuck"),
-            ]
-        ]
-
-        connection.execute(User.__table__.insert(), params)
-        connection.execute(
-            Address.__table__.insert(),
-            [
-                dict(list(zip(("id", "user_id", "email"), column_values)))
-                for column_values in [
-                    (1, 7, "jack@bean.com"),
-                    (2, 8, "ed@wood.com"),
-                    (3, 8, "ed@bettyboop.com"),
-                    (4, 8, "ed@lala.com"),
-                    (5, 9, "fred@fred.com"),
-                ]
-            ],
-        )
-
-    def test_aliased_join(self):
+        self.assert_sql_count(testing.db, go, 1)
 
-        # this query will screw up if the aliasing enabled in
-        # query.join() gets applied to the right half of the join
-        # condition inside the any(). the join condition inside of
-        # any() comes from the "primaryjoin" of the relationship,
-        # and should not be annotated with _orm_adapt.
-        # PropertyLoader.Comparator will annotate the left side with
-        # _orm_adapt, though.
+    def test_contains_eager_aliased_instances(self):
+        addresses, users, User = (
+            self.tables.addresses,
+            self.tables.users,
+            self.classes.User,
+        )
 
         sess = fixture_session()
+        q = sess.query(User)
 
-        with testing.expect_deprecated_20(join_aliased_dep):
-            eq_(
-                sess.query(User)
-                .join(User.addresses, aliased=True)
-                .filter(Address.email == "ed@wood.com")
-                .filter(User.addresses.any(Address.email == "jack@bean.com"))
-                .all(),
-                [],
-            )
-
-
-class RequirementsTest(fixtures.MappedTest):
-
-    """Tests the contract for user classes."""
-
-    @classmethod
-    def define_tables(cls, metadata):
-        Table(
-            "ht1",
-            metadata,
-            Column(
-                "id", Integer, primary_key=True, test_needs_autoincrement=True
-            ),
-            Column("value", String(10)),
-        )
-        Table(
-            "ht2",
-            metadata,
-            Column(
-                "id", Integer, primary_key=True, test_needs_autoincrement=True
-            ),
-            Column("ht1_id", Integer, ForeignKey("ht1.id")),
-            Column("value", String(10)),
-        )
-        Table(
-            "ht3",
-            metadata,
-            Column(
-                "id", Integer, primary_key=True, test_needs_autoincrement=True
-            ),
-            Column("value", String(10)),
-        )
-        Table(
-            "ht4",
-            metadata,
-            Column("ht1_id", Integer, ForeignKey("ht1.id"), primary_key=True),
-            Column("ht3_id", Integer, ForeignKey("ht3.id"), primary_key=True),
-        )
-        Table(
-            "ht5",
-            metadata,
-            Column("ht1_id", Integer, ForeignKey("ht1.id"), primary_key=True),
-        )
-        Table(
-            "ht6",
-            metadata,
-            Column("ht1a_id", Integer, ForeignKey("ht1.id"), primary_key=True),
-            Column("ht1b_id", Integer, ForeignKey("ht1.id"), primary_key=True),
-            Column("value", String(10)),
+        adalias = addresses.alias("adalias")
+        selectquery = (
+            users.outerjoin(adalias)
+            .select()
+            .order_by(users.c.id, adalias.c.id)
         )
 
+        # note this has multiple problems because we aren't giving Query
+        # the statement where it would be able to create an adapter
+        def go():
+            with testing.expect_deprecated(
+                r"Using the Query.instances\(\) method without a context"
+            ):
+                result = list(
+                    q.options(
+                        contains_eager(User.addresses, alias=adalias)
+                    ).instances(sess.connection().execute(selectquery))
+                )
+            assert self.static.user_address_result == result
 
-class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest):
-    __dialect__ = "default"
+        self.assert_sql_count(testing.db, go, 1)
 
-    def test_deep_options(self):
-        users, items, order_items, Order, Item, User, orders = (
-            self.tables.users,
+    def test_contains_eager_multi_alias(self):
+        orders, items, users, order_items, User = (
+            self.tables.orders,
             self.tables.items,
+            self.tables.users,
             self.tables.order_items,
-            self.classes.Order,
-            self.classes.Item,
             self.classes.User,
-            self.tables.orders,
         )
 
-        self.mapper_registry.map_imperatively(
-            Item,
-            items,
-            properties=dict(description=deferred(items.c.description)),
-        )
-        self.mapper_registry.map_imperatively(
-            Order,
-            orders,
-            properties=dict(items=relationship(Item, secondary=order_items)),
-        )
-        self.mapper_registry.map_imperatively(
-            User,
-            users,
-            properties=dict(orders=relationship(Order, order_by=orders.c.id)),
-        )
+        Order = self.classes.Order
 
         sess = fixture_session()
-        q = sess.query(User).order_by(User.id)
-        result = q.all()
-        item = result[0].orders[1].items[1]
+        q = sess.query(User)
 
-        def go():
-            eq_(item.description, "item 4")
+        oalias = orders.alias("o1")
+        ialias = items.alias("i1")
+        query = (
+            users.outerjoin(oalias)
+            .outerjoin(order_items)
+            .outerjoin(ialias)
+            .select()
+            .order_by(users.c.id, oalias.c.id, ialias.c.id)
+        )
 
-        self.sql_count_(1, go)
-        eq_(item.description, "item 4")
+        # test using Alias with more than one level deep
 
-        sess.expunge_all()
-        with assertions.expect_deprecated(undefer_needs_chaining):
-            result = q.options(
-                undefer(User.orders, Order.items, Item.description)
-            ).all()
-        item = result[0].orders[1].items[1]
+        # new way:
+        # from sqlalchemy.orm.strategy_options import Load
+        # opt = Load(User).contains_eager('orders', alias=oalias).
+        #     contains_eager('items', alias=ialias)
 
         def go():
-            eq_(item.description, "item 4")
+            with testing.expect_deprecated(
+                r"Using the Query.instances\(\) method without a context"
+            ):
+                result = list(
+                    q.options(
+                        contains_eager(User.orders, alias=oalias),
+                        defaultload(User.orders).contains_eager(
+                            Order.items, alias=ialias
+                        ),
+                    ).instances(sess.connection().execute(query))
+                )
+            assert self.static.user_order_result == result
 
-        self.sql_count_(0, go)
-        eq_(item.description, "item 4")
+        self.assert_sql_count(testing.db, go, 1)
 
 
-class SubOptionsTest(PathTest, OptionsQueryTest):
-    run_create_tables = False
-    run_inserts = None
-    run_deletes = None
+class TestDeprecation20(QueryTest):
+    def test_relation(self):
+        User = self.classes.User
+        with testing.expect_deprecated_20(".*relationship"):
+            relation(User.addresses)
 
-    def _assert_opts(self, q, sub_opt, non_sub_opts):
-        attr_a = {}
+    def test_eagerloading(self):
+        User = self.classes.User
+        with testing.expect_deprecated_20(".*joinedload"):
+            eagerload(User.addresses)
 
-        for val in sub_opt._to_bind:
-            val._bind_loader(
+
+class DistinctOrderByImplicitTest(QueryTest, AssertsCompiledSQL):
+    __dialect__ = "default"
+
+    def test_columns_augmented_roundtrip_three(self):
+        User, Address = self.classes.User, self.classes.Address
+
+        sess = fixture_session()
+
+        q = (
+            sess.query(User.id, User.name.label("foo"), Address.id)
+            .join(Address, true())
+            .filter(User.name == "jack")
+            .filter(User.id + Address.user_id > 0)
+            .distinct()
+            .order_by(User.id, User.name, Address.email_address)
+        )
+
+        # even though columns are added, they aren't in the result
+        with testing.expect_deprecated(
+            "ORDER BY columns added implicitly due to "
+        ):
+            eq_(
+                q.all(),
                 [
-                    ent.entity_zero
-                    for ent in q._compile_state()._lead_mapper_entities
+                    (7, "jack", 3),
+                    (7, "jack", 4),
+                    (7, "jack", 2),
+                    (7, "jack", 5),
+                    (7, "jack", 1),
                 ],
-                q._compile_options._current_path,
-                attr_a,
-                False,
             )
+            for row in q:
+                eq_(row._mapping.keys(), ["id", "foo", "id"])
 
-        attr_b = {}
+    def test_columns_augmented_sql_one(self):
+        User, Address = self.classes.User, self.classes.Address
 
-        for opt in non_sub_opts:
-            for val in opt._to_bind:
-                val._bind_loader(
-                    [
-                        ent.entity_zero
-                        for ent in q._compile_state()._lead_mapper_entities
-                    ],
-                    q._compile_options._current_path,
-                    attr_b,
-                    False,
-                )
+        sess = fixture_session()
 
-        for k, l in attr_b.items():
-            if not l.strategy:
-                del attr_b[k]
+        q = (
+            sess.query(User.id, User.name.label("foo"), Address.id)
+            .distinct()
+            .order_by(User.id, User.name, Address.email_address)
+        )
 
-        def strat_as_tuple(strat):
-            return (
-                strat.strategy,
-                strat.local_opts,
-                strat.propagate_to_loaders,
-                strat._of_type,
-                strat.is_class_strategy,
-                strat.is_opts_only,
+        # Address.email_address is added because of DISTINCT,
+        # however User.id, User.name are not b.c. they're already there,
+        # even though User.name is labeled
+        with testing.expect_deprecated(
+            "ORDER BY columns added implicitly due to "
+        ):
+            self.assert_compile(
+                q,
+                "SELECT DISTINCT users.id AS users_id, users.name AS foo, "
+                "addresses.id AS addresses_id, addresses.email_address AS "
+                "addresses_email_address FROM users, addresses "
+                "ORDER BY users.id, users.name, addresses.email_address",
             )
 
-        eq_(
-            {path: strat_as_tuple(load) for path, load in attr_a.items()},
-            {path: strat_as_tuple(load) for path, load in attr_b.items()},
-        )
-
 
-class PolyCacheKeyTest(CacheKeyFixture, _poly_fixtures._Polymorphic):
-    run_setup_mappers = "once"
+class SessionEventsTest(_RemoveListeners, _fixtures.FixtureTest):
     run_inserts = None
-    run_deletes = None
 
-    def _stmt_20(self, *elements):
-        return tuple(
-            elem._statement_20() if isinstance(elem, sa.orm.Query) else elem
-            for elem in elements
-        )
+    def test_on_bulk_update_hook(self):
+        User, users = self.classes.User, self.tables.users
 
-    def test_wp_queries(self):
-        Person, Manager, Engineer, Boss = self.classes(
-            "Person", "Manager", "Engineer", "Boss"
-        )
+        sess = fixture_session()
+        canary = Mock()
 
-        def one():
-            with assertions.expect_deprecated_20(w_polymorphic_dep):
-                return (
-                    fixture_session()
-                    .query(Person)
-                    .with_polymorphic([Manager, Engineer])
-                )
+        event.listen(sess, "after_bulk_update", canary.after_bulk_update)
 
-        def two():
-            wp = with_polymorphic(Person, [Manager, Engineer])
+        def legacy(ses, qry, ctx, res):
+            canary.after_bulk_update_legacy(ses, qry, ctx, res)
 
-            return fixture_session().query(wp)
+        event.listen(sess, "after_bulk_update", legacy)
 
-        def three():
-            wp = with_polymorphic(Person, [Manager, Engineer])
+        self.mapper_registry.map_imperatively(User, users)
 
-            return fixture_session().query(wp).filter(wp.name == "asdfo")
+        with testing.expect_deprecated(
+            'The argument signature for the "SessionEvents.after_bulk_update" '
+            "event listener"
+        ):
+            sess.query(User).update({"name": "foo"})
 
-        def three_a():
-            wp = with_polymorphic(Person, [Manager, Engineer], flat=True)
+        eq_(canary.after_bulk_update.call_count, 1)
 
-            return fixture_session().query(wp).filter(wp.name == "asdfo")
+        upd = canary.after_bulk_update.mock_calls[0][1][0]
+        eq_(upd.session, sess)
+        eq_(
+            canary.after_bulk_update_legacy.mock_calls,
+            [call(sess, upd.query, None, upd.result)],
+        )
 
-        def four():
-            with assertions.expect_deprecated_20(w_polymorphic_dep):
-                return (
-                    fixture_session()
-                    .query(Person)
-                    .with_polymorphic([Manager, Engineer])
-                    .filter(Person.name == "asdf")
-                )
+    def test_on_bulk_delete_hook(self):
+        User, users = self.classes.User, self.tables.users
 
-        def five():
-            subq = (
-                select(Person)
-                .outerjoin(Manager)
-                .outerjoin(Engineer)
-                .subquery()
-            )
-            wp = with_polymorphic(Person, [Manager, Engineer], subq)
+        sess = fixture_session()
+        canary = Mock()
 
-            return fixture_session().query(wp).filter(wp.name == "asdfo")
+        event.listen(sess, "after_bulk_delete", canary.after_bulk_delete)
 
-        def six():
-            subq = (
-                select(Person)
-                .outerjoin(Manager)
-                .outerjoin(Engineer)
-                .subquery()
-            )
+        def legacy(ses, qry, ctx, res):
+            canary.after_bulk_delete_legacy(ses, qry, ctx, res)
+
+        event.listen(sess, "after_bulk_delete", legacy)
+
+        self.mapper_registry.map_imperatively(User, users)
+
+        with testing.expect_deprecated(
+            'The argument signature for the "SessionEvents.after_bulk_delete" '
+            "event listener"
+        ):
+            sess.query(User).delete()
 
-            with assertions.expect_deprecated_20(w_polymorphic_dep):
-                return (
-                    fixture_session()
-                    .query(Person)
-                    .with_polymorphic([Manager, Engineer], subq)
-                    .filter(Person.name == "asdfo")
-                )
+        eq_(canary.after_bulk_delete.call_count, 1)
 
-        self._run_cache_key_fixture(
-            lambda: self._stmt_20(
-                one(), two(), three(), three_a(), four(), five(), six()
-            ),
-            compare_values=True,
+        upd = canary.after_bulk_delete.mock_calls[0][1][0]
+        eq_(upd.session, sess)
+        eq_(
+            canary.after_bulk_delete_legacy.mock_calls,
+            [call(sess, upd.query, None, upd.result)],
         )
 
 
-class ColumnAccessTest(QueryTest, AssertsCompiledSQL):
-    """test access of columns after _from_selectable has been applied"""
+class ImmediateTest(_fixtures.FixtureTest):
+    run_inserts = "once"
+    run_deletes = None
 
-    __dialect__ = "default"
+    @classmethod
+    def setup_mappers(cls):
+        Address, addresses, users, User = (
+            cls.classes.Address,
+            cls.tables.addresses,
+            cls.tables.users,
+            cls.classes.User,
+        )
 
-    def test_select_entity_from(self):
-        User = self.classes.User
-        sess = fixture_session()
+        cls.mapper_registry.map_imperatively(Address, addresses)
 
-        q = sess.query(User)
-        with assertions.expect_deprecated_20(sef_dep):
-            q = sess.query(User).select_entity_from(q.statement.subquery())
-        self.assert_compile(
-            q.filter(User.name == "ed"),
-            "SELECT anon_1.id AS anon_1_id, anon_1.name AS anon_1_name "
-            "FROM (SELECT users.id AS id, users.name AS name FROM "
-            "users) AS anon_1 WHERE anon_1.name = :name_1",
+        cls.mapper_registry.map_imperatively(
+            User, users, properties=dict(addresses=relationship(Address))
         )
 
-    def test_select_entity_from_no_entities(self):
+    def test_value(self):
         User = self.classes.User
+
         sess = fixture_session()
 
-        with assertions.expect_deprecated_20(sef_dep):
-            assert_raises_message(
-                sa.exc.ArgumentError,
-                r"A selectable \(FromClause\) instance is "
-                "expected when the base alias is being set",
-                sess.query(User).select_entity_from(User)._compile_context,
+        with testing.expect_deprecated(r"Query.value\(\) is deprecated"):
+            eq_(sess.query(User).filter_by(id=7).value(User.id), 7)
+        with testing.expect_deprecated(r"Query.value\(\) is deprecated"):
+            eq_(
+                sess.query(User.id, User.name).filter_by(id=7).value(User.id),
+                7,
             )
+        with testing.expect_deprecated(r"Query.value\(\) is deprecated"):
+            eq_(sess.query(User).filter_by(id=0).value(User.id), None)
 
+        sess.bind = testing.db
+        with testing.expect_deprecated(r"Query.value\(\) is deprecated"):
+            eq_(sess.query().value(sa.literal_column("1").label("x")), 1)
 
-class SelectFromTest(QueryTest, AssertsCompiledSQL):
-    run_setup_mappers = None
-    __dialect__ = "default"
-
-    def test_aliased_class_vs_nonaliased(self):
-        User, users = self.classes.User, self.tables.users
-        self.mapper_registry.map_imperatively(User, users)
+    def test_value_cancels_loader_opts(self):
+        User = self.classes.User
 
         sess = fixture_session()
-        with assertions.expect_deprecated_20(sef_dep):
-            self.assert_compile(
-                sess.query(User.name).select_entity_from(
-                    users.select().where(users.c.id > 5).subquery()
-                ),
-                "SELECT anon_1.name AS anon_1_name FROM "
-                "(SELECT users.id AS id, "
-                "users.name AS name FROM users WHERE users.id > :id_1) "
-                "AS anon_1",
-            )
 
-    @testing.combinations(
-        (
-            lambda users: users.select().where(users.c.id.in_([7, 8])),
-            "SELECT anon_1.id AS anon_1_id, anon_1.name AS anon_1_name "
-            "FROM (SELECT users.id AS id, users.name AS name "
-            "FROM users WHERE users.id IN (__[POSTCOMPILE_id_1])) AS anon_1 "
-            "WHERE anon_1.name = :name_1",
-        ),
-        (
-            lambda users: users.select()
-            .where(users.c.id.in_([7, 8]))
-            .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL),
-            "SELECT anon_1.users_id AS anon_1_users_id, anon_1.users_name "
-            "AS anon_1_users_name FROM (SELECT users.id AS users_id, "
-            "users.name AS users_name FROM users "
-            "WHERE users.id IN (__[POSTCOMPILE_id_1])) AS anon_1 "
-            "WHERE anon_1.users_name = :name_1",
-        ),
-        (
-            lambda User, sess: sess.query(User).where(User.id.in_([7, 8])),
-            "SELECT anon_1.id AS anon_1_id, anon_1.name AS anon_1_name "
-            "FROM (SELECT users.id AS id, users.name AS name "
-            "FROM users WHERE users.id IN (__[POSTCOMPILE_id_1])) AS anon_1 "
-            "WHERE anon_1.name = :name_1",
-        ),
-    )
-    def test_filter_by(self, query_fn, expected):
-        """test #7239"""
+        q = (
+            sess.query(User)
+            .filter(User.name == "ed")
+            .options(joinedload(User.addresses))
+        )
 
-        User = self.classes.User
-        sess = fixture_session()
+        with testing.expect_deprecated(r"Query.value\(\) is deprecated"):
+            q = q.value(func.count(literal_column("*")))
 
-        User, users = self.classes.User, self.tables.users
 
-        self.mapper_registry.map_imperatively(User, users)
+class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
+    __dialect__ = "default"
 
-        sel = resolve_lambda(query_fn, User=User, users=users, sess=sess)
+    def test_values(self):
+        Address, User = (
+            self.classes.Address,
+            self.classes.User,
+        )
 
         sess = fixture_session()
 
-        with assertions.expect_deprecated_20(sef_dep):
-            q = sess.query(User).select_entity_from(sel.subquery())
-
-        self.assert_compile(q.filter_by(name="ed"), expected)
-        eq_(q.filter_by(name="ed").all(), [User(name="ed")])
+        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
+            assert list(sess.query(User).values()) == list()
 
-    def test_join_no_order_by(self):
-        User, users = self.classes.User, self.tables.users
+        q = sess.query(User)
 
-        self.mapper_registry.map_imperatively(User, users)
+        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
+            q2 = q.order_by(User.id).values(
+                User.name, User.name + " " + cast(User.id, String(50))
+            )
+        eq_(
+            list(q2),
+            [
+                ("jack", "jack 7"),
+                ("ed", "ed 8"),
+                ("fred", "fred 9"),
+                ("chuck", "chuck 10"),
+            ],
+        )
 
-        sel = users.select().where(users.c.id.in_([7, 8]))
-        sess = fixture_session()
+        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
+            q2 = (
+                q.join(User.addresses)
+                .filter(User.name.like("%e%"))
+                .order_by(User.id, Address.id)
+                .values(User.name, Address.email_address)
+            )
+        eq_(
+            list(q2),
+            [
+                ("ed", "ed@wood.com"),
+                ("ed", "ed@bettyboop.com"),
+                ("ed", "ed@lala.com"),
+                ("fred", "fred@fred.com"),
+            ],
+        )
 
-        with assertions.expect_deprecated_20(sef_dep):
-            eq_(
-                sess.query(User).select_entity_from(sel.subquery()).all(),
-                [User(name="jack", id=7), User(name="ed", id=8)],
+        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
+            q2 = (
+                q.join(User.addresses)
+                .filter(User.name.like("%e%"))
+                .order_by(desc(Address.email_address))
+                .slice(1, 3)
+                .values(User.name, Address.email_address)
             )
+        eq_(list(q2), [("ed", "ed@wood.com"), ("ed", "ed@lala.com")])
 
-    def test_join(self):
-        users, Address, addresses, User = (
-            self.tables.users,
-            self.classes.Address,
-            self.tables.addresses,
-            self.classes.User,
+        adalias = aliased(Address)
+        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
+            q2 = (
+                q.join(adalias, User.addresses)
+                .filter(User.name.like("%e%"))
+                .order_by(adalias.email_address)
+                .values(User.name, adalias.email_address)
+            )
+        eq_(
+            list(q2),
+            [
+                ("ed", "ed@bettyboop.com"),
+                ("ed", "ed@lala.com"),
+                ("ed", "ed@wood.com"),
+                ("fred", "fred@fred.com"),
+            ],
         )
 
-        self.mapper_registry.map_imperatively(
-            User, users, properties={"addresses": relationship(Address)}
-        )
-        self.mapper_registry.map_imperatively(Address, addresses)
+        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
+            q2 = q.values(func.count(User.name))
+        assert next(q2) == (4,)
+
+    @testing.fails_on("mssql", "FIXME: unknown")
+    def test_values_specific_order_by(self):
+        User = self.classes.User
 
-        sel = users.select().where(users.c.id.in_([7, 8]))
         sess = fixture_session()
 
-        with assertions.expect_deprecated_20(sef_dep):
-            eq_(
-                sess.query(User)
-                .select_entity_from(sel.subquery())
-                .join(User.addresses)
-                .add_entity(Address)
-                .order_by(User.id)
-                .order_by(Address.id)
-                .all(),
-                [
-                    (
-                        User(name="jack", id=7),
-                        Address(
-                            user_id=7, email_address="jack@bean.com", id=1
-                        ),
-                    ),
-                    (
-                        User(name="ed", id=8),
-                        Address(user_id=8, email_address="ed@wood.com", id=2),
-                    ),
-                    (
-                        User(name="ed", id=8),
-                        Address(
-                            user_id=8, email_address="ed@bettyboop.com", id=3
-                        ),
-                    ),
-                    (
-                        User(name="ed", id=8),
-                        Address(user_id=8, email_address="ed@lala.com", id=4),
-                    ),
-                ],
-            )
+        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
+            assert list(sess.query(User).values()) == list()
 
-        adalias = aliased(Address)
-        with assertions.expect_deprecated_20(sef_dep):
-            eq_(
-                sess.query(User)
-                .select_entity_from(sel.subquery())
-                .join(adalias, User.addresses)
-                .add_entity(adalias)
-                .order_by(User.id)
-                .order_by(adalias.id)
-                .all(),
-                [
-                    (
-                        User(name="jack", id=7),
-                        Address(
-                            user_id=7, email_address="jack@bean.com", id=1
-                        ),
-                    ),
-                    (
-                        User(name="ed", id=8),
-                        Address(user_id=8, email_address="ed@wood.com", id=2),
-                    ),
-                    (
-                        User(name="ed", id=8),
-                        Address(
-                            user_id=8, email_address="ed@bettyboop.com", id=3
-                        ),
-                    ),
-                    (
-                        User(name="ed", id=8),
-                        Address(user_id=8, email_address="ed@lala.com", id=4),
-                    ),
-                ],
-            )
+    @testing.fails_on("mssql", "FIXME: unknown")
+    @testing.fails_on(
+        "oracle", "Oracle doesn't support boolean expressions as " "columns"
+    )
+    @testing.fails_on(
+        "postgresql+pg8000",
+        "pg8000 parses the SQL itself before passing on "
+        "to PG, doesn't parse this",
+    )
+    @testing.fails_on(
+        "postgresql+asyncpg",
+        "Asyncpg uses preprated statements that are not compatible with how "
+        "sqlalchemy passes the query. Fails with "
+        'ERROR:  column "users.name" must appear in the GROUP BY clause'
+        " or be used in an aggregate function",
+    )
+    def test_values_with_boolean_selects(self):
+        """Tests a values clause that works with select boolean
+        evaluations"""
 
-    def test_more_joins(self):
-        (
-            users,
-            Keyword,
-            orders,
-            items,
-            order_items,
-            Order,
-            Item,
-            User,
-            keywords,
-            item_keywords,
-        ) = (
-            self.tables.users,
-            self.classes.Keyword,
-            self.tables.orders,
-            self.tables.items,
-            self.tables.order_items,
-            self.classes.Order,
-            self.classes.Item,
-            self.classes.User,
-            self.tables.keywords,
-            self.tables.item_keywords,
-        )
+        User = self.classes.User
 
-        self.mapper_registry.map_imperatively(
-            User,
-            users,
-            properties={"orders": relationship(Order, backref="user")},
-        )  # o2m, m2o
-        self.mapper_registry.map_imperatively(
-            Order,
-            orders,
-            properties={
-                "items": relationship(
-                    Item, secondary=order_items, order_by=items.c.id
-                )
-            },
-        )  # m2m
+        sess = fixture_session()
 
-        self.mapper_registry.map_imperatively(
-            Item,
-            items,
-            properties={
-                "keywords": relationship(
-                    Keyword, secondary=item_keywords, order_by=keywords.c.id
+        q = sess.query(User)
+        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
+            q2 = (
+                q.group_by(User.name.like("%j%"))
+                .order_by(desc(User.name.like("%j%")))
+                .values(
+                    User.name.like("%j%"), func.count(User.name.like("%j%"))
                 )
-            },
-        )  # m2m
-        self.mapper_registry.map_imperatively(Keyword, keywords)
-
-        sess = fixture_session()
-        sel = users.select().where(users.c.id.in_([7, 8]))
+            )
+        eq_(list(q2), [(True, 1), (False, 3)])
 
-        with assertions.expect_deprecated_20(sef_dep):
-            eq_(
-                sess.query(User)
-                .select_entity_from(sel.subquery())
-                .join(User.orders, Order.items, Item.keywords)
-                .filter(Keyword.name.in_(["red", "big", "round"]))
-                .all(),
-                [User(name="jack", id=7)],
+        with testing.expect_deprecated(r"Query.values?\(\) is deprecated"):
+            q2 = q.order_by(desc(User.name.like("%j%"))).values(
+                User.name.like("%j%")
             )
+        eq_(list(q2), [(True,), (False,), (False,), (False,)])
 
-    def test_very_nested_joins_with_joinedload(self):
-        (
-            users,
-            Keyword,
-            orders,
-            items,
-            order_items,
-            Order,
-            Item,
-            User,
-            keywords,
-            item_keywords,
-        ) = (
-            self.tables.users,
-            self.classes.Keyword,
-            self.tables.orders,
-            self.tables.items,
-            self.tables.order_items,
-            self.classes.Order,
-            self.classes.Item,
-            self.classes.User,
-            self.tables.keywords,
-            self.tables.item_keywords,
-        )
 
-        self.mapper_registry.map_imperatively(
-            User,
-            users,
-            properties={"orders": relationship(Order, backref="user")},
-        )  # o2m, m2o
-        self.mapper_registry.map_imperatively(
-            Order,
-            orders,
-            properties={
-                "items": relationship(
-                    Item, secondary=order_items, order_by=items.c.id
-                )
-            },
-        )  # m2m
-        self.mapper_registry.map_imperatively(
-            Item,
-            items,
-            properties={
-                "keywords": relationship(
-                    Keyword, secondary=item_keywords, order_by=keywords.c.id
-                )
-            },
-        )  # m2m
-        self.mapper_registry.map_imperatively(Keyword, keywords)
+class InheritedJoinTest(
+    fixtures.NoCache,
+    _poly_fixtures._Polymorphic,
+    _poly_fixtures._PolymorphicFixtureBase,
+    AssertsCompiledSQL,
+):
+    run_setup_mappers = "once"
+    __dialect__ = "default"
+
+    def test_join_to_selectable(self):
+        people, Company, engineers, Engineer = (
+            self.tables.people,
+            self.classes.Company,
+            self.tables.engineers,
+            self.classes.Engineer,
+        )
 
         sess = fixture_session()
 
-        sel = users.select().where(users.c.id.in_([7, 8]))
+        with _aliased_join_deprecation():
+            self.assert_compile(
+                sess.query(Company)
+                .join(people.join(engineers), Company.employees)
+                .filter(Engineer.name == "dilbert"),
+                "SELECT companies.company_id AS companies_company_id, "
+                "companies.name AS companies_name "
+                "FROM companies JOIN (people "
+                "JOIN engineers ON people.person_id = "
+                "engineers.person_id) ON companies.company_id = "
+                "people.company_id WHERE people.name = :name_1",
+                use_default_dialect=True,
+            )
 
-        def go():
-            with assertions.expect_deprecated_20(sef_dep):
-                eq_(
-                    sess.query(User)
-                    .select_entity_from(sel.subquery())
-                    .options(
-                        joinedload(User.orders)
-                        .joinedload(Order.items)
-                        .joinedload(Item.keywords)
-                    )
-                    .join(User.orders, Order.items, Item.keywords)
-                    .filter(Keyword.name.in_(["red", "big", "round"]))
-                    .all(),
-                    [
-                        User(
-                            name="jack",
-                            orders=[
-                                Order(
-                                    description="order 1",
-                                    items=[
-                                        Item(
-                                            description="item 1",
-                                            keywords=[
-                                                Keyword(name="red"),
-                                                Keyword(name="big"),
-                                                Keyword(name="round"),
-                                            ],
-                                        ),
-                                        Item(
-                                            description="item 2",
-                                            keywords=[
-                                                Keyword(name="red", id=2),
-                                                Keyword(name="small", id=5),
-                                                Keyword(name="square"),
-                                            ],
-                                        ),
-                                        Item(
-                                            description="item 3",
-                                            keywords=[
-                                                Keyword(name="green", id=3),
-                                                Keyword(name="big", id=4),
-                                                Keyword(name="round", id=6),
-                                            ],
-                                        ),
-                                    ],
-                                ),
-                                Order(
-                                    description="order 3",
-                                    items=[
-                                        Item(
-                                            description="item 3",
-                                            keywords=[
-                                                Keyword(name="green", id=3),
-                                                Keyword(name="big", id=4),
-                                                Keyword(name="round", id=6),
-                                            ],
-                                        ),
-                                        Item(
-                                            description="item 4",
-                                            keywords=[],
-                                            id=4,
-                                        ),
-                                        Item(
-                                            description="item 5",
-                                            keywords=[],
-                                            id=5,
-                                        ),
-                                    ],
-                                ),
-                                Order(
-                                    description="order 5",
-                                    items=[
-                                        Item(description="item 5", keywords=[])
-                                    ],
-                                ),
-                            ],
-                        )
-                    ],
-                )
+    def test_join_to_subclass_selectable_auto_alias(self):
+        Company, Engineer = self.classes("Company", "Engineer")
+        people, engineers = self.tables("people", "engineers")
 
-        self.assert_sql_count(testing.db, go, 1)
+        sess = fixture_session()
 
-        sess.expunge_all()
-        sel2 = orders.select().where(orders.c.id.in_([1, 2, 3]))
-        with assertions.expect_deprecated_20(sef_dep):
+        with _aliased_join_deprecation():
             eq_(
-                sess.query(Order)
-                .select_entity_from(sel2.subquery())
-                .join(Order.items)
-                .join(Item.keywords)
-                .filter(Keyword.name == "red")
-                .order_by(Order.id)
+                sess.query(Company)
+                .join(people.join(engineers), Company.employees)
+                .filter(Engineer.primary_language == "java")
                 .all(),
-                [
-                    Order(description="order 1", id=1),
-                    Order(description="order 2", id=2),
-                ],
+                [self.c1],
             )
 
-    def test_replace_with_eager(self):
-        users, Address, addresses, User = (
-            self.tables.users,
-            self.classes.Address,
-            self.tables.addresses,
-            self.classes.User,
-        )
+        # occurs for 2.0 style query also
+        with _aliased_join_deprecation():
+            stmt = (
+                select(Company)
+                .join(people.join(engineers), Company.employees)
+                .filter(Engineer.primary_language == "java")
+            )
+            results = sess.scalars(stmt)
+        eq_(results.all(), [self.c1])
 
-        self.mapper_registry.map_imperatively(
-            User,
-            users,
-            properties={
-                "addresses": relationship(Address, order_by=addresses.c.id)
-            },
-        )
-        self.mapper_registry.map_imperatively(Address, addresses)
+    def test_join_to_subclass_two(self):
+        Company, Engineer = self.classes("Company", "Engineer")
+        people, engineers = self.tables("people", "engineers")
 
-        sel = users.select().where(users.c.id.in_([7, 8]))
         sess = fixture_session()
 
-        def go():
-            with assertions.expect_deprecated_20(sef_dep):
-                eq_(
-                    sess.query(User)
-                    .options(joinedload(User.addresses))
-                    .select_entity_from(sel.subquery())
-                    .order_by(User.id)
-                    .all(),
-                    [
-                        User(id=7, addresses=[Address(id=1)]),
-                        User(
-                            id=8,
-                            addresses=[
-                                Address(id=2),
-                                Address(id=3),
-                                Address(id=4),
-                            ],
-                        ),
-                    ],
-                )
+        with _aliased_join_deprecation():
+            eq_(
+                sess.query(Company)
+                .join(people.join(engineers), Company.employees)
+                .filter(Engineer.primary_language == "java")
+                .all(),
+                [self.c1],
+            )
 
-        self.assert_sql_count(testing.db, go, 1)
-        sess.expunge_all()
+    def test_join_to_subclass_six_selectable_auto_alias(self):
+        Company, Engineer = self.classes("Company", "Engineer")
+        people, engineers = self.tables("people", "engineers")
 
-        def go():
-            with assertions.expect_deprecated_20(sef_dep):
-                eq_(
-                    sess.query(User)
-                    .options(joinedload(User.addresses))
-                    .select_entity_from(sel.subquery())
-                    .filter(User.id == 8)
-                    .order_by(User.id)
-                    .all(),
-                    [
-                        User(
-                            id=8,
-                            addresses=[
-                                Address(id=2),
-                                Address(id=3),
-                                Address(id=4),
-                            ],
-                        )
-                    ],
-                )
+        sess = fixture_session()
 
-        self.assert_sql_count(testing.db, go, 1)
-        sess.expunge_all()
+        with _aliased_join_deprecation():
+            eq_(
+                sess.query(Company)
+                .join(people.join(engineers), Company.employees)
+                .join(Engineer.machines)
+                .all(),
+                [self.c1, self.c2],
+            )
 
-        def go():
-            with assertions.expect_deprecated_20(sef_dep):
-                eq_(
-                    sess.query(User)
-                    .options(joinedload(User.addresses))
-                    .select_entity_from(sel.subquery())
-                    .order_by(User.id)[1],
-                    User(
-                        id=8,
-                        addresses=[
-                            Address(id=2),
-                            Address(id=3),
-                            Address(id=4),
-                        ],
-                    ),
-                )
+    def test_join_to_subclass_six_point_five_selectable_auto_alias(self):
+        Company, Engineer = self.classes("Company", "Engineer")
+        people, engineers = self.tables("people", "engineers")
 
-        self.assert_sql_count(testing.db, go, 1)
+        sess = fixture_session()
 
-    def test_select_from_aliased_one(self):
-        User, users = self.classes.User, self.tables.users
+        with _aliased_join_deprecation():
+            eq_(
+                sess.query(Company)
+                .join(people.join(engineers), Company.employees)
+                .join(Engineer.machines)
+                .filter(Engineer.name == "dilbert")
+                .all(),
+                [self.c1],
+            )
 
-        self.mapper_registry.map_imperatively(User, users)
+    def test_join_to_subclass_seven_selectable_auto_alias(self):
+        Company, Engineer, Machine = self.classes(
+            "Company", "Engineer", "Machine"
+        )
+        people, engineers = self.tables("people", "engineers")
 
         sess = fixture_session()
 
-        not_users = table("users", column("id"), column("name"))
-        ua = aliased(User, select(not_users).alias(), adapt_on_names=True)
-
-        with assertions.expect_deprecated_20(sef_dep):
-            q = (
-                sess.query(User.name)
-                .select_entity_from(ua)
-                .order_by(User.name)
+        with _aliased_join_deprecation():
+            eq_(
+                sess.query(Company)
+                .join(people.join(engineers), Company.employees)
+                .join(Engineer.machines)
+                .filter(Machine.name.ilike("%thinkpad%"))
+                .all(),
+                [self.c1],
             )
-        self.assert_compile(
-            q,
-            "SELECT anon_1.name AS anon_1_name FROM (SELECT users.id AS id, "
-            "users.name AS name FROM users) AS anon_1 ORDER BY anon_1.name",
+
+
+class MultiplePathTest(fixtures.MappedTest, AssertsCompiledSQL):
+    @classmethod
+    def define_tables(cls, metadata):
+        Table(
+            "t1",
+            metadata,
+            Column(
+                "id", Integer, primary_key=True, test_needs_autoincrement=True
+            ),
+            Column("data", String(30)),
+        )
+        Table(
+            "t2",
+            metadata,
+            Column(
+                "id", Integer, primary_key=True, test_needs_autoincrement=True
+            ),
+            Column("data", String(30)),
         )
-        eq_(q.all(), [("chuck",), ("ed",), ("fred",), ("jack",)])
 
-    def test_select_from_aliased_two(self):
-        User, users = self.classes.User, self.tables.users
+        Table(
+            "t1t2_1",
+            metadata,
+            Column("t1id", Integer, ForeignKey("t1.id")),
+            Column("t2id", Integer, ForeignKey("t2.id")),
+        )
 
-        self.mapper_registry.map_imperatively(User, users)
+        Table(
+            "t1t2_2",
+            metadata,
+            Column("t1id", Integer, ForeignKey("t1.id")),
+            Column("t2id", Integer, ForeignKey("t2.id")),
+        )
 
-        sess = fixture_session()
 
-        ua = aliased(User)
+class BindSensitiveStringifyTest(fixtures.MappedTest):
+    def _fixture(self):
+        # building a totally separate metadata /mapping here
+        # because we need to control if the MetaData is bound or not
 
-        with assertions.expect_deprecated_20(sef_dep):
-            q = (
-                sess.query(User.name)
-                .select_entity_from(ua)
-                .order_by(User.name)
-            )
-        self.assert_compile(
-            q,
-            "SELECT users_1.name AS users_1_name FROM users AS users_1 "
-            "ORDER BY users_1.name",
+        class User:
+            pass
+
+        m = MetaData()
+        user_table = Table(
+            "users",
+            m,
+            Column("id", Integer, primary_key=True),
+            Column("name", String(50)),
         )
-        eq_(q.all(), [("chuck",), ("ed",), ("fred",), ("jack",)])
 
-    def test_select_from_core_alias_one(self):
-        User, users = self.classes.User, self.tables.users
+        clear_mappers()
+        self.mapper_registry.map_imperatively(User, user_table)
+        return User
 
-        self.mapper_registry.map_imperatively(User, users)
+    def _dialect_fixture(self):
+        class MyDialect(default.DefaultDialect):
+            default_paramstyle = "qmark"
 
-        sess = fixture_session()
+        from sqlalchemy.engine import base
 
-        ua = users.alias()
+        return base.Engine(mock.Mock(), MyDialect(), mock.Mock())
 
-        with assertions.expect_deprecated_20(sef_dep):
-            q = (
-                sess.query(User.name)
-                .select_entity_from(ua)
-                .order_by(User.name)
-            )
-        self.assert_compile(
-            q,
-            "SELECT users_1.name AS users_1_name FROM users AS users_1 "
-            "ORDER BY users_1.name",
-        )
-        eq_(q.all(), [("chuck",), ("ed",), ("fred",), ("jack",)])
+    def _test(self, bound_session, session_present, expect_bound):
+        if bound_session:
+            eng = self._dialect_fixture()
+        else:
+            eng = None
 
-    def test_differentiate_self_external(self):
-        """test some different combinations of joining a table to a subquery of
-        itself."""
+        User = self._fixture()
 
-        users, User = self.tables.users, self.classes.User
+        s = Session(eng if bound_session else None)
+        q = s.query(User).filter(User.id == 7)
+        if not session_present:
+            q = q.with_session(None)
 
-        self.mapper_registry.map_imperatively(User, users)
+        eq_ignore_whitespace(
+            str(q),
+            "SELECT users.id AS users_id, users.name AS users_name "
+            "FROM users WHERE users.id = ?"
+            if expect_bound
+            else "SELECT users.id AS users_id, users.name AS users_name "
+            "FROM users WHERE users.id = :id_1",
+        )
 
-        sess = fixture_session()
+    def test_query_bound_session(self):
+        self._test(True, True, True)
 
-        sel = sess.query(User).filter(User.id.in_([7, 8])).subquery()
-        ualias = aliased(User)
+    def test_query_no_session(self):
+        self._test(False, False, False)
 
-        with assertions.expect_deprecated_20(sef_dep):
-            self.assert_compile(
-                sess.query(ualias)
-                .select_entity_from(sel)
-                .filter(ualias.id > sel.c.id),
-                "SELECT users_1.id AS users_1_id, "
-                "users_1.name AS users_1_name "
-                "FROM users AS users_1, ("
-                "SELECT users.id AS id, users.name AS name FROM users "
-                "WHERE users.id IN (__[POSTCOMPILE_id_1])) AS anon_1 "
-                "WHERE users_1.id > anon_1.id",
-                check_post_param={"id_1": [7, 8]},
-            )
+    def test_query_unbound_session(self):
+        self._test(False, True, False)
 
-        with assertions.expect_deprecated_20(sef_dep):
-            self.assert_compile(
-                sess.query(ualias)
-                .select_entity_from(sel)
-                .join(ualias, ualias.id > sel.c.id),
-                "SELECT users_1.id AS users_1_id, "
-                "users_1.name AS users_1_name "
-                "FROM (SELECT users.id AS id, users.name AS name "
-                "FROM users WHERE users.id IN "
-                "(__[POSTCOMPILE_id_1])) AS anon_1 "
-                "JOIN users AS users_1 ON users_1.id > anon_1.id",
-                check_post_param={"id_1": [7, 8]},
-            )
 
-        with assertions.expect_deprecated_20(sef_dep):
-            self.assert_compile(
-                sess.query(ualias)
-                .select_entity_from(sel)
-                .join(ualias, ualias.id > User.id),
-                "SELECT users_1.id AS users_1_id, "
-                "users_1.name AS users_1_name "
-                "FROM (SELECT users.id AS id, users.name AS name FROM "
-                "users WHERE users.id IN (__[POSTCOMPILE_id_1])) AS anon_1 "
-                "JOIN users AS users_1 ON users_1.id > anon_1.id",
-                check_post_param={"id_1": [7, 8]},
-            )
+class DeprecationScopedSessionTest(fixtures.MappedTest):
+    def test_config_errors(self):
+        sm = sessionmaker()
 
-        with assertions.expect_deprecated_20(sef_dep):
-            self.assert_compile(
-                sess.query(ualias).select_entity_from(
-                    join(sel, ualias, ualias.id > sel.c.id)
-                ),
-                "SELECT users_1.id AS users_1_id, "
-                "users_1.name AS users_1_name "
-                "FROM "
-                "(SELECT users.id AS id, users.name AS name "
-                "FROM users WHERE users.id "
-                "IN (__[POSTCOMPILE_id_1])) AS anon_1 "
-                "JOIN users AS users_1 ON users_1.id > anon_1.id",
-                check_post_param={"id_1": [7, 8]},
-            )
+        def go():
+            s = sm()
+            s._is_asyncio = True
+            return s
+
+        Session = scoped_session(go)
 
+        with expect_deprecated(
+            "Using `scoped_session` with asyncio is deprecated and "
+            "will raise an error in a future version. "
+            "Please use `async_scoped_session` instead."
+        ):
+            Session()
+        Session.remove()
 
-class JoinLateralTest(fixtures.MappedTest, AssertsCompiledSQL):
-    __dialect__ = default.DefaultDialect(supports_native_boolean=True)
 
-    run_setup_bind = None
-    run_setup_mappers = "once"
+class RequirementsTest(fixtures.MappedTest):
 
-    run_create_tables = None
+    """Tests the contract for user classes."""
 
     @classmethod
     def define_tables(cls, metadata):
         Table(
-            "people",
+            "ht1",
             metadata,
-            Column("people_id", Integer, primary_key=True),
-            Column("age", Integer),
-            Column("name", String(30)),
+            Column(
+                "id", Integer, primary_key=True, test_needs_autoincrement=True
+            ),
+            Column("value", String(10)),
         )
         Table(
-            "bookcases",
+            "ht2",
             metadata,
-            Column("bookcase_id", Integer, primary_key=True),
             Column(
-                "bookcase_owner_id", Integer, ForeignKey("people.people_id")
+                "id", Integer, primary_key=True, test_needs_autoincrement=True
             ),
-            Column("bookcase_shelves", Integer),
-            Column("bookcase_width", Integer),
+            Column("ht1_id", Integer, ForeignKey("ht1.id")),
+            Column("value", String(10)),
         )
         Table(
-            "books",
+            "ht3",
             metadata,
-            Column("book_id", Integer, primary_key=True),
             Column(
-                "bookcase_id", Integer, ForeignKey("bookcases.bookcase_id")
+                "id", Integer, primary_key=True, test_needs_autoincrement=True
             ),
-            Column("book_owner_id", Integer, ForeignKey("people.people_id")),
-            Column("book_weight", Integer),
+            Column("value", String(10)),
+        )
+        Table(
+            "ht4",
+            metadata,
+            Column("ht1_id", Integer, ForeignKey("ht1.id"), primary_key=True),
+            Column("ht3_id", Integer, ForeignKey("ht3.id"), primary_key=True),
+        )
+        Table(
+            "ht5",
+            metadata,
+            Column("ht1_id", Integer, ForeignKey("ht1.id"), primary_key=True),
+        )
+        Table(
+            "ht6",
+            metadata,
+            Column("ht1a_id", Integer, ForeignKey("ht1.id"), primary_key=True),
+            Column("ht1b_id", Integer, ForeignKey("ht1.id"), primary_key=True),
+            Column("value", String(10)),
         )
 
-    @classmethod
-    def setup_classes(cls):
-        class Person(cls.Comparable):
-            pass
-
-        class Bookcase(cls.Comparable):
-            pass
 
-        class Book(cls.Comparable):
-            pass
+class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest):
+    __dialect__ = "default"
 
-    @classmethod
-    def setup_mappers(cls):
-        Person, Bookcase, Book = cls.classes("Person", "Bookcase", "Book")
-        people, bookcases, books = cls.tables("people", "bookcases", "books")
-        cls.mapper_registry.map_imperatively(Person, people)
-        cls.mapper_registry.map_imperatively(
-            Bookcase,
-            bookcases,
-            properties={
-                "owner": relationship(Person),
-                "books": relationship(Book),
-            },
+    def test_deep_options(self):
+        users, items, order_items, Order, Item, User, orders = (
+            self.tables.users,
+            self.tables.items,
+            self.tables.order_items,
+            self.classes.Order,
+            self.classes.Item,
+            self.classes.User,
+            self.tables.orders,
         )
-        cls.mapper_registry.map_imperatively(Book, books)
 
-    # "sef" == "select entity from"
-    def test_select_subquery_sef_implicit_correlate(self):
-        Person, Book = self.classes("Person", "Book")
+        self.mapper_registry.map_imperatively(
+            Item,
+            items,
+            properties=dict(description=deferred(items.c.description)),
+        )
+        self.mapper_registry.map_imperatively(
+            Order,
+            orders,
+            properties=dict(items=relationship(Item, secondary=order_items)),
+        )
+        self.mapper_registry.map_imperatively(
+            User,
+            users,
+            properties=dict(orders=relationship(Order, order_by=orders.c.id)),
+        )
 
-        s = fixture_session()
+        sess = fixture_session()
+        q = sess.query(User).order_by(User.id)
+        result = q.all()
+        item = result[0].orders[1].items[1]
 
-        stmt = s.query(Person).subquery()
+        def go():
+            eq_(item.description, "item 4")
 
-        subq = (
-            s.query(Book.book_id)
-            .filter(Person.people_id == Book.book_owner_id)
-            .subquery()
-            .lateral()
-        )
+        self.sql_count_(1, go)
+        eq_(item.description, "item 4")
 
-        with assertions.expect_deprecated_20(sef_dep):
-            stmt = (
-                s.query(Person, subq.c.book_id)
-                .select_entity_from(stmt)
-                .join(subq, true())
-            )
+        sess.expunge_all()
+        with assertions.expect_deprecated(undefer_needs_chaining):
+            result = q.options(
+                undefer(User.orders, Order.items, Item.description)
+            ).all()
+        item = result[0].orders[1].items[1]
 
-        self.assert_compile(
-            stmt,
-            "SELECT anon_1.people_id AS anon_1_people_id, "
-            "anon_1.age AS anon_1_age, anon_1.name AS anon_1_name, "
-            "anon_2.book_id AS anon_2_book_id "
-            "FROM "
-            "(SELECT people.people_id AS people_id, people.age AS age, "
-            "people.name AS name FROM people) AS anon_1 "
-            "JOIN LATERAL "
-            "(SELECT books.book_id AS book_id FROM books "
-            "WHERE anon_1.people_id = books.book_owner_id) AS anon_2 ON true",
-        )
+        def go():
+            eq_(item.description, "item 4")
 
-    def test_select_subquery_sef_implicit_correlate_coreonly(self):
-        Person, Book = self.classes("Person", "Book")
+        self.sql_count_(0, go)
+        eq_(item.description, "item 4")
 
-        s = fixture_session()
 
-        stmt = s.query(Person).subquery()
+class SubOptionsTest(PathTest, OptionsQueryTest):
+    run_create_tables = False
+    run_inserts = None
+    run_deletes = None
 
-        subq = (
-            select(Book.book_id)
-            .where(Person.people_id == Book.book_owner_id)
-            .subquery()
-            .lateral()
-        )
+    def _assert_opts(self, q, sub_opt, non_sub_opts):
+        attr_a = {}
 
-        with assertions.expect_deprecated_20(sef_dep):
-            stmt = (
-                s.query(Person, subq.c.book_id)
-                .select_entity_from(stmt)
-                .join(subq, true())
+        for val in sub_opt._to_bind:
+            val._bind_loader(
+                [
+                    ent.entity_zero
+                    for ent in q._compile_state()._lead_mapper_entities
+                ],
+                q._compile_options._current_path,
+                attr_a,
+                False,
             )
 
-        self.assert_compile(
-            stmt,
-            "SELECT anon_1.people_id AS anon_1_people_id, "
-            "anon_1.age AS anon_1_age, anon_1.name AS anon_1_name, "
-            "anon_2.book_id AS anon_2_book_id "
-            "FROM "
-            "(SELECT people.people_id AS people_id, people.age AS age, "
-            "people.name AS name FROM people) AS anon_1 "
-            "JOIN LATERAL "
-            "(SELECT books.book_id AS book_id FROM books "
-            "WHERE anon_1.people_id = books.book_owner_id) AS anon_2 ON true",
-        )
-
-    def test_select_subquery_sef_explicit_correlate_coreonly(self):
-        Person, Book = self.classes("Person", "Book")
-
-        s = fixture_session()
+        attr_b = {}
 
-        stmt = s.query(Person).subquery()
+        for opt in non_sub_opts:
+            for val in opt._to_bind:
+                val._bind_loader(
+                    [
+                        ent.entity_zero
+                        for ent in q._compile_state()._lead_mapper_entities
+                    ],
+                    q._compile_options._current_path,
+                    attr_b,
+                    False,
+                )
 
-        subq = (
-            select(Book.book_id)
-            .correlate(Person)
-            .where(Person.people_id == Book.book_owner_id)
-            .subquery()
-            .lateral()
-        )
+        for k, l in attr_b.items():
+            if not l.strategy:
+                del attr_b[k]
 
-        with assertions.expect_deprecated_20(sef_dep):
-            stmt = (
-                s.query(Person, subq.c.book_id)
-                .select_entity_from(stmt)
-                .join(subq, true())
+        def strat_as_tuple(strat):
+            return (
+                strat.strategy,
+                strat.local_opts,
+                strat.propagate_to_loaders,
+                strat._of_type,
+                strat.is_class_strategy,
+                strat.is_opts_only,
             )
 
-        self.assert_compile(
-            stmt,
-            "SELECT anon_1.people_id AS anon_1_people_id, "
-            "anon_1.age AS anon_1_age, anon_1.name AS anon_1_name, "
-            "anon_2.book_id AS anon_2_book_id "
-            "FROM "
-            "(SELECT people.people_id AS people_id, people.age AS age, "
-            "people.name AS name FROM people) AS anon_1 "
-            "JOIN LATERAL "
-            "(SELECT books.book_id AS book_id FROM books "
-            "WHERE anon_1.people_id = books.book_owner_id) AS anon_2 ON true",
+        eq_(
+            {path: strat_as_tuple(load) for path, load in attr_a.items()},
+            {path: strat_as_tuple(load) for path, load in attr_b.items()},
         )
 
-    def test_select_subquery_sef_explicit_correlate(self):
-        Person, Book = self.classes("Person", "Book")
 
-        s = fixture_session()
+class PolyCacheKeyTest(CacheKeyFixture, _poly_fixtures._Polymorphic):
+    run_setup_mappers = "once"
+    run_inserts = None
+    run_deletes = None
 
-        stmt = s.query(Person).subquery()
+    def _stmt_20(self, *elements):
+        return tuple(
+            elem._statement_20() if isinstance(elem, sa.orm.Query) else elem
+            for elem in elements
+        )
 
-        subq = (
-            s.query(Book.book_id)
-            .correlate(Person)
-            .filter(Person.people_id == Book.book_owner_id)
-            .subquery()
-            .lateral()
+    def test_wp_queries(self):
+        Person, Manager, Engineer, Boss = self.classes(
+            "Person", "Manager", "Engineer", "Boss"
         )
 
-        with assertions.expect_deprecated_20(sef_dep):
-            stmt = (
-                s.query(Person, subq.c.book_id)
-                .select_entity_from(stmt)
-                .join(subq, true())
-            )
+        def two():
+            wp = with_polymorphic(Person, [Manager, Engineer])
 
-        self.assert_compile(
-            stmt,
-            "SELECT anon_1.people_id AS anon_1_people_id, "
-            "anon_1.age AS anon_1_age, anon_1.name AS anon_1_name, "
-            "anon_2.book_id AS anon_2_book_id "
-            "FROM "
-            "(SELECT people.people_id AS people_id, people.age AS age, "
-            "people.name AS name FROM people) AS anon_1 "
-            "JOIN LATERAL "
-            "(SELECT books.book_id AS book_id FROM books "
-            "WHERE anon_1.people_id = books.book_owner_id) AS anon_2 ON true",
-        )
+            return fixture_session().query(wp)
+
+        def three():
+            wp = with_polymorphic(Person, [Manager, Engineer])
 
-    def test_from_function_sef(self):
-        Bookcase = self.classes.Bookcase
+            return fixture_session().query(wp).filter(wp.name == "asdfo")
 
-        s = fixture_session()
+        def three_a():
+            wp = with_polymorphic(Person, [Manager, Engineer], flat=True)
 
-        subq = s.query(Bookcase).subquery()
+            return fixture_session().query(wp).filter(wp.name == "asdfo")
 
-        srf = lateral(func.generate_series(1, Bookcase.bookcase_shelves))
+        def five():
+            subq = (
+                select(Person)
+                .outerjoin(Manager)
+                .outerjoin(Engineer)
+                .subquery()
+            )
+            wp = with_polymorphic(Person, [Manager, Engineer], subq)
 
-        with assertions.expect_deprecated_20(sef_dep):
-            q = s.query(Bookcase).select_entity_from(subq).join(srf, true())
+            return fixture_session().query(wp).filter(wp.name == "asdfo")
 
-        self.assert_compile(
-            q,
-            "SELECT anon_1.bookcase_id AS anon_1_bookcase_id, "
-            "anon_1.bookcase_owner_id AS anon_1_bookcase_owner_id, "
-            "anon_1.bookcase_shelves AS anon_1_bookcase_shelves, "
-            "anon_1.bookcase_width AS anon_1_bookcase_width "
-            "FROM (SELECT bookcases.bookcase_id AS bookcase_id, "
-            "bookcases.bookcase_owner_id AS bookcase_owner_id, "
-            "bookcases.bookcase_shelves AS bookcase_shelves, "
-            "bookcases.bookcase_width AS bookcase_width FROM bookcases) "
-            "AS anon_1 "
-            "JOIN LATERAL "
-            "generate_series(:generate_series_1, anon_1.bookcase_shelves) "
-            "AS anon_2 ON true",
+        self._run_cache_key_fixture(
+            lambda: self._stmt_20(two(), three(), three_a(), five()),
+            compare_values=True,
         )
 
 
index 58b09b67d781339edb3a20dd36caeb00133bc2e2..07420bacca3b645cd56ecd12ef158871d4102896 100644 (file)
@@ -411,7 +411,7 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
         sess = fixture_session()
         assert_raises_message(
             TypeError,
-            "unknown arguments: bar, foob",
+            r"Query.join\(\) got an unexpected keyword argument 'foob'",
             sess.query(User).join,
             "address",
             foob="bar",
@@ -419,7 +419,7 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
         )
         assert_raises_message(
             TypeError,
-            "unknown arguments: bar, foob",
+            r"Query.outerjoin\(\) got an unexpected keyword argument 'foob'",
             sess.query(User).outerjoin,
             "address",
             foob="bar",
@@ -606,7 +606,7 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
             "ON users_1.id = orders.user_id",
         )
 
-    def test_overlapping_paths_one(self):
+    def test_overlapping_paths_one_legacy(self):
         User = self.classes.User
         Order = self.classes.Order
 
@@ -629,7 +629,7 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
             "ON addresses.id = orders.address_id",
         )
 
-    def test_overlapping_paths_multilevel(self):
+    def test_overlapping_paths_multilevel_legacy(self):
         User = self.classes.User
         Order = self.classes.Order
         Address = self.classes.Address
@@ -655,6 +655,58 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
             "JOIN dingalings ON addresses.id = dingalings.address_id",
         )
 
+    def test_overlapping_paths_one_modern(self):
+        User = self.classes.User
+        Order = self.classes.Order
+
+        # test overlapping paths.   User->orders is used by both joins, but
+        # rendered once.
+        # label style is for comparison to legacy version.  1.4 version
+        # of select().join() did not behave the same as Query.join()
+        self.assert_compile(
+            select(User)
+            .join(User.orders)
+            .join(Order.items)
+            .join(User.orders)
+            .join(Order.address)
+            .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL),
+            "SELECT users.id AS users_id, users.name AS users_name FROM users "
+            "JOIN orders "
+            "ON users.id = orders.user_id "
+            "JOIN order_items AS order_items_1 "
+            "ON orders.id = order_items_1.order_id "
+            "JOIN items ON items.id = order_items_1.item_id JOIN addresses "
+            "ON addresses.id = orders.address_id",
+        )
+
+    def test_overlapping_paths_multilevel_modern(self):
+        User = self.classes.User
+        Order = self.classes.Order
+        Address = self.classes.Address
+
+        # label style is for comparison to legacy version.  1.4 version
+        # of select().join() did not behave the same as Query.join()
+        q = (
+            select(User)
+            .join(User.orders)
+            .join(User.addresses)
+            .join(User.orders)
+            .join(Order.items)
+            .join(User.addresses)
+            .join(Address.dingaling)
+            .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
+        )
+        self.assert_compile(
+            q,
+            "SELECT users.id AS users_id, users.name AS users_name "
+            "FROM users JOIN orders ON users.id = orders.user_id "
+            "JOIN addresses ON users.id = addresses.user_id "
+            "JOIN order_items AS order_items_1 ON orders.id = "
+            "order_items_1.order_id "
+            "JOIN items ON items.id = order_items_1.item_id "
+            "JOIN dingalings ON addresses.id = dingalings.address_id",
+        )
+
     def test_join_nonmapped_column(self):
         """test that the search for a 'left' doesn't trip on non-mapped cols"""
 
@@ -1659,11 +1711,13 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
 
         # coercions does not catch this due to the
         # legacy=True flag for JoinTargetRole
-        assert_raises_message(
+
+        with expect_raises_message(
             sa_exc.ArgumentError,
-            "Expected mapped entity or selectable/table as join target",
-            sess.query(User).join(User.id == Address.user_id)._compile_context,
-        )
+            "Join target, typically a FROM expression, or ORM relationship "
+            "attribute expected, got",
+        ):
+            sess.query(User).join(User.id == Address.user_id)
 
     def test_on_clause_no_right_side_one_future(self):
         User = self.classes.User
@@ -1678,6 +1732,46 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
             User.id == Address.user_id,
         )
 
+    def test_no_legacy_multi_join_two_element(self):
+        User = self.classes.User
+        Order = self.classes.Order
+
+        sess = fixture_session()
+
+        with expect_raises_message(
+            sa_exc.InvalidRequestError,
+            "No 'on clause' argument may be passed when joining to a "
+            "relationship path as a target",
+        ):
+            sess.query(User).join(User.orders, Order.items)._compile_context()
+
+    def test_no_modern_multi_join_two_element(self):
+        User = self.classes.User
+        Order = self.classes.Order
+
+        sess = fixture_session()
+
+        with expect_raises_message(
+            sa_exc.InvalidRequestError,
+            "No 'on clause' argument may be passed when joining to a "
+            "relationship path as a target",
+        ):
+            sess.execute(select(User).join(User.orders, Order.items))
+
+    def test_kw_only_blocks_legacy_multi_join(self):
+        User = self.classes.User
+        Order = self.classes.Order
+        Item = self.classes.Item
+
+        sess = fixture_session()
+
+        with expect_raises_message(
+            TypeError,
+            r"Query.join\(\) takes from 2 to 3 positional arguments but "
+            "4 were given",
+        ):
+            sess.query(User).join(User.orders, Order.items, Item.keywords)
+
     def test_on_clause_no_right_side_two(self):
         User = self.classes.User
         Address = self.classes.Address
@@ -1731,7 +1825,7 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
 
         with expect_raises_message(
             sa_exc.ArgumentError,
-            "Join target, typically a FROM expression, or ORM relationship "
+            "ON clause, typically a SQL expression or ORM relationship "
             "attribute expected, got 'addresses'",
         ):
             sess.query(User).join(Address, "addresses")
@@ -1742,8 +1836,8 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
 
         with expect_raises_message(
             sa_exc.ArgumentError,
-            r"Textual SQL expression 'addresses' should be explicitly "
-            r"declared as text\('addresses'\)",
+            "ON clause, typically a SQL expression or ORM relationship "
+            "attribute expected, got 'addresses'.",
         ):
             select(User).join(Address, "addresses")
 
index ceef252ba37401645ef165b43ff511ac27f3a639..9bfeb36d81c17b8254dd0116e18323e336d09ded 100644 (file)
@@ -1449,10 +1449,6 @@ class InvalidGenerationsTest(QueryTest, AssertsCompiledSQL):
 
         q.enable_assertions(False).select_from(users)
 
-        with testing.expect_deprecated("The Query.from_self"):
-            # this is fine, however
-            q.from_self()
-
     def test_invalid_select_from(self):
         User = self.classes.User
 
@@ -1503,7 +1499,6 @@ class InvalidGenerationsTest(QueryTest, AssertsCompiledSQL):
             q.from_statement,
             text("select * from table"),
         )
-        assert_raises(sa_exc.InvalidRequestError, q.with_polymorphic, User)
 
     def test_order_by(self):
         """test that an order_by() call is not valid before 'clauseelement'
@@ -1519,7 +1514,6 @@ class InvalidGenerationsTest(QueryTest, AssertsCompiledSQL):
             q.from_statement,
             text("select * from table"),
         )
-        assert_raises(sa_exc.InvalidRequestError, q.with_polymorphic, User)
 
     def test_entity_or_mapper_zero_from_context(self):
         User, Address = self.classes.User, self.classes.Address