]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
fill out all distinguising fields for AliasedInsp
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 17 Aug 2022 17:06:51 +0000 (13:06 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 17 Aug 2022 17:06:51 +0000 (13:06 -0400)
Hardened the cache key strategy for the :func:`_orm.aliased` and
:func:`_orm.with_polymorphic` constructs. While no issue involving actual
statements being cached can easily be demonstrated (if at all), these two
constructs were not including enough of what makes them unique in their
cache keys for caching on the aliased construct alone to be accurate.

Fixes: #8401
Change-Id: I13d14985b6965f398edd9494601d8ae89ac641f2

doc/build/changelog/unreleased_14/8401.rst [new file with mode: 0644]
lib/sqlalchemy/orm/util.py
test/orm/test_cache_key.py

diff --git a/doc/build/changelog/unreleased_14/8401.rst b/doc/build/changelog/unreleased_14/8401.rst
new file mode 100644 (file)
index 0000000..119c6cf
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+    :tags: orm, bug
+    :tickets: 8401
+
+    Hardened the cache key strategy for the :func:`_orm.aliased` and
+    :func:`_orm.with_polymorphic` constructs. While no issue involving actual
+    statements being cached can easily be demonstrated (if at all), these two
+    constructs were not including enough of what makes them unique in their
+    cache keys for caching on the aliased construct alone to be accurate.
index 02080a27f9a16a33ceb9750d935555d88b8da88d..9a5399af00d06478a613963e7cf3ee54f56362e5 100644 (file)
@@ -748,6 +748,19 @@ class AliasedInsp(
         "_nest_adapters",
     )
 
+    _cache_key_traversal = [
+        ("name", visitors.ExtendedInternalTraversal.dp_string),
+        ("_adapt_on_names", visitors.ExtendedInternalTraversal.dp_boolean),
+        ("_use_mapper_path", visitors.ExtendedInternalTraversal.dp_boolean),
+        ("_target", visitors.ExtendedInternalTraversal.dp_inspectable),
+        ("selectable", visitors.ExtendedInternalTraversal.dp_clauseelement),
+        (
+            "with_polymorphic_mappers",
+            visitors.InternalTraversal.dp_has_cache_key_list,
+        ),
+        ("polymorphic_on", visitors.InternalTraversal.dp_clauseelement),
+    ]
+
     mapper: Mapper[_O]
     selectable: FromClause
     _adapter: sql_util.ColumnAdapter
@@ -940,12 +953,6 @@ class AliasedInsp(
     def entity_namespace(self) -> AliasedClass[_O]:
         return self.entity
 
-    _cache_key_traversal = [
-        ("name", visitors.ExtendedInternalTraversal.dp_string),
-        ("_adapt_on_names", visitors.ExtendedInternalTraversal.dp_boolean),
-        ("selectable", visitors.ExtendedInternalTraversal.dp_clauseelement),
-    ]
-
     @property
     def class_(self) -> Type[_O]:
         """Return the mapped class ultimately represented by this
index 7beadc08cf99ba15ee345758c6fdb842b233a457..08fd22dc8960d39f31ea2c0fa29ada5032dea0ac 100644 (file)
@@ -5,6 +5,7 @@ from sqlalchemy import Column
 from sqlalchemy import func
 from sqlalchemy import inspect
 from sqlalchemy import Integer
+from sqlalchemy import literal_column
 from sqlalchemy import null
 from sqlalchemy import select
 from sqlalchemy import Table
@@ -63,8 +64,19 @@ class CacheKeyTest(CacheKeyFixture, _fixtures.FixtureTest):
     def test_mapper_and_aliased(self):
         User, Address, Keyword = self.classes("User", "Address", "Keyword")
 
+        addresses_table = self.tables.addresses
+
         self._run_cache_key_fixture(
-            lambda: (inspect(User), inspect(Address), inspect(aliased(User))),
+            lambda: (
+                inspect(User),
+                inspect(Address),
+                inspect(aliased(User)),
+                inspect(aliased(aliased(User, addresses_table))),
+                inspect(aliased(aliased(User), addresses_table.select())),
+                inspect(aliased(Address)),
+                inspect(aliased(Address, addresses_table.select())),
+                inspect(aliased(User, addresses_table.select())),
+            ),
             compare_values=True,
         )
 
@@ -611,6 +623,94 @@ class PolyCacheKeyTest(CacheKeyFixture, _poly_fixtures._Polymorphic):
             compare_values=True,
         )
 
+    def test_wpoly_cache_keys(self):
+        Person, Manager, Engineer, Boss = self.classes(
+            "Person", "Manager", "Engineer", "Boss"
+        )
+
+        meb_stmt = inspect(
+            with_polymorphic(Person, [Manager, Engineer, Boss])
+        ).selectable
+        me_stmt = inspect(
+            with_polymorphic(Person, [Manager, Engineer])
+        ).selectable
+
+        self._run_cache_key_fixture(
+            lambda: (
+                inspect(Person),
+                inspect(
+                    aliased(Person, me_stmt),
+                ),
+                inspect(
+                    aliased(Person, meb_stmt),
+                ),
+                inspect(
+                    with_polymorphic(Person, [Manager, Engineer]),
+                ),
+                # aliased=True is the same as flat=True for default selectable
+                inspect(
+                    with_polymorphic(
+                        Person, [Manager, Engineer], aliased=True
+                    ),
+                ),
+                inspect(
+                    with_polymorphic(Person, [Manager, Engineer], flat=True),
+                ),
+                inspect(
+                    with_polymorphic(
+                        Person, [Manager, Engineer], flat=True, innerjoin=True
+                    ),
+                ),
+                inspect(
+                    with_polymorphic(
+                        Person,
+                        [Manager, Engineer],
+                        flat=True,
+                        _use_mapper_path=True,
+                    ),
+                ),
+                inspect(
+                    with_polymorphic(
+                        Person,
+                        [Manager, Engineer],
+                        flat=True,
+                        adapt_on_names=True,
+                    ),
+                ),
+                inspect(
+                    with_polymorphic(
+                        Person, [Manager, Engineer], selectable=meb_stmt
+                    ),
+                ),
+                inspect(
+                    with_polymorphic(
+                        Person,
+                        [Manager, Engineer],
+                        selectable=meb_stmt,
+                        aliased=True,
+                    ),
+                ),
+                inspect(
+                    with_polymorphic(Person, [Manager, Engineer, Boss]),
+                ),
+                inspect(
+                    with_polymorphic(
+                        Person,
+                        [Manager, Engineer, Boss],
+                        polymorphic_on=literal_column("foo"),
+                    ),
+                ),
+                inspect(
+                    with_polymorphic(
+                        Person,
+                        [Manager, Engineer, Boss],
+                        polymorphic_on=literal_column("bar"),
+                    ),
+                ),
+            ),
+            compare_values=True,
+        )
+
     def test_wp_queries(self):
         Person, Manager, Engineer, Boss = self.classes(
             "Person", "Manager", "Engineer", "Boss"