]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
TableValuedAlias generation fixes
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 3 Apr 2022 15:28:57 +0000 (11:28 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 3 Apr 2022 16:31:59 +0000 (12:31 -0400)
Fixed bug in newly implemented
:paramref:`.FunctionElement.table_valued.joins_implicitly` feature where
the parameter would not automatically propagate from the original
:class:`.TableValuedAlias` object to the secondary object produced when
calling upon :meth:`.TableValuedAlias.render_derived` or
:meth:`.TableValuedAlias.alias`.

Additionally repaired these issues in :class:`.TableValuedAlias`:

* repaired a potential memory issue which could occur when
repeatedly calling :meth:`.TableValuedAlias.render_derived` against
successive copies of the same object (for .alias(), we currently
have to still continue chaining from the previous element.  not sure
if this can be improved but this is standard behavior for .alias()
elsewhere)
* repaired issue where the individual element types would be lost when
calling upon :meth:`.TableValuedAlias.render_derived` or
:meth:`.TableValuedAlias.alias`.

Fixes: #7890
Change-Id: Ie5120c7ff1e5c1bba5aaf77c782a51c637860208
(cherry picked from commit c315c7401a2aa00a8a0fa0f7d4189a9976fd7962)

doc/build/changelog/unreleased_14/7890.rst [new file with mode: 0644]
lib/sqlalchemy/sql/selectable.py
test/aaa_profiling/test_memusage.py
test/sql/test_from_linter.py
test/sql/test_functions.py

diff --git a/doc/build/changelog/unreleased_14/7890.rst b/doc/build/changelog/unreleased_14/7890.rst
new file mode 100644 (file)
index 0000000..94a29ab
--- /dev/null
@@ -0,0 +1,22 @@
+.. change::
+    :tags: bug, sql
+    :tickets: 7890
+
+    Fixed bug in newly implemented
+    :paramref:`.FunctionElement.table_valued.joins_implicitly` feature where
+    the parameter would not automatically propagate from the original
+    :class:`.TableValuedAlias` object to the secondary object produced when
+    calling upon :meth:`.TableValuedAlias.render_derived` or
+    :meth:`.TableValuedAlias.alias`.
+
+    Additionally repaired these issues in :class:`.TableValuedAlias`:
+
+    * repaired a potential memory issue which could occur when
+      repeatedly calling :meth:`.TableValuedAlias.render_derived` against
+      successive copies of the same object (for .alias(), we currently
+      have to still continue chaining from the previous element.  not sure
+      if this can be improved but this is standard behavior for .alias()
+      elsewhere)
+    * repaired issue where the individual element types would be lost when
+      calling upon :meth:`.TableValuedAlias.render_derived` or
+      :meth:`.TableValuedAlias.alias`.
index 028ed99a60c5d3f56219e68fbe5d9dece954cec4..ea81ce67058bebd48910399e537bb8ee340c12c5 100644 (file)
@@ -1820,10 +1820,17 @@ class TableValuedAlias(Alias):
 
         """
 
-        tva = TableValuedAlias._construct(self, name=name)
+        tva = TableValuedAlias._construct(
+            self,
+            name=name,
+            table_value_type=self._tableval_type,
+            joins_implicitly=self.joins_implicitly,
+        )
+
         if self._render_derived:
             tva._render_derived = True
             tva._render_derived_w_types = self._render_derived_w_types
+
         return tva
 
     def lateral(self, name=None):
@@ -1884,7 +1891,15 @@ class TableValuedAlias(Alias):
         # python id() of the original which can cause name conflicts if
         # a new anon-name grabs the same identifier as the local anon-name
         # (just saw it happen on CI)
-        new_alias = TableValuedAlias._construct(self, name=name)
+
+        # construct against original to prevent memory growth
+        # for repeated generations
+        new_alias = TableValuedAlias._construct(
+            self.element,
+            name=name,
+            table_value_type=self._tableval_type,
+            joins_implicitly=self.joins_implicitly,
+        )
         new_alias._render_derived = True
         new_alias._render_derived_w_types = with_types
         return new_alias
index c842b593016ece630e75b153ac9a6a0038d885b2..9abc3511a03e2354270dff1cb8af4e0ca187ea7d 100644 (file)
@@ -6,6 +6,7 @@ import weakref
 
 import sqlalchemy as sa
 from sqlalchemy import ForeignKey
+from sqlalchemy import func
 from sqlalchemy import inspect
 from sqlalchemy import Integer
 from sqlalchemy import MetaData
@@ -366,6 +367,16 @@ class MemUsageTest(EnsureZeroed):
 
         go()
 
+    def test_tv_render_derived(self):
+        root_expr = func.some_fn().table_valued()
+        expr = [root_expr]
+
+        @profile_memory()
+        def go():
+            expr[0] = expr[0].render_derived()
+
+        go()
+
 
 class MemUsageWBackendTest(fixtures.MappedTest, EnsureZeroed):
 
index 4a4d907f965dd0cf7132c09ed674f7ee4de06092..1fa3aff360fec284e4368f09e4bb54b824698711 100644 (file)
@@ -165,8 +165,15 @@ class TestFindUnmatchingFroms(fixtures.TablesTest):
         assert start is p3
         assert froms == {p1}
 
+    @testing.combinations(
+        "render_derived", "alias", None, argnames="additional_transformation"
+    )
     @testing.combinations(True, False, argnames="joins_implicitly")
-    def test_table_valued(self, joins_implicitly):
+    def test_table_valued(
+        self,
+        joins_implicitly,
+        additional_transformation,
+    ):
         """test #7845"""
         my_table = table(
             "tbl",
@@ -175,9 +182,16 @@ class TestFindUnmatchingFroms(fixtures.TablesTest):
         )
 
         sub_dict = my_table.c.data["d"]
-        tv = func.json_each(sub_dict).table_valued(
-            "key", joins_implicitly=joins_implicitly
-        )
+
+        tv = func.json_each(sub_dict)
+
+        tv = tv.table_valued("key", joins_implicitly=joins_implicitly)
+
+        if additional_transformation == "render_derived":
+            tv = tv.render_derived(name="tv", with_types=True)
+        elif additional_transformation == "alias":
+            tv = tv.alias()
+
         has_key = tv.c.key == "f"
         stmt = select(my_table.c.id).where(has_key)
         froms, start = find_unmatching_froms(stmt, my_table)
index 27f1b8974208070b787fffc3be9e49f477abe1a3..c4326b8abc863ae97588140332c82f44a5af34c6 100644 (file)
@@ -25,7 +25,6 @@ from sqlalchemy import Table
 from sqlalchemy import testing
 from sqlalchemy import Text
 from sqlalchemy import true
-from sqlalchemy import types as sqltypes
 from sqlalchemy import util
 from sqlalchemy.dialects import mysql
 from sqlalchemy.dialects import oracle
@@ -37,6 +36,7 @@ from sqlalchemy.sql import functions
 from sqlalchemy.sql import LABEL_STYLE_TABLENAME_PLUS_COL
 from sqlalchemy.sql import operators
 from sqlalchemy.sql import quoted_name
+from sqlalchemy.sql import sqltypes
 from sqlalchemy.sql import table
 from sqlalchemy.sql.compiler import BIND_TEMPLATES
 from sqlalchemy.sql.functions import FunctionElement
@@ -1430,6 +1430,30 @@ class TableValuedCompileTest(fixtures.TestBase, AssertsCompiledSQL):
             "LEFT OUTER JOIN b ON unnested.unnested = b.ref",
         )
 
+    def test_render_derived_maintains_tableval_type(self):
+        fn = func.json_something()
+
+        tv = fn.table_valued(column("x", String))
+
+        eq_(tv.column.type, testing.eq_type_affinity(sqltypes.TableValueType))
+        eq_(tv.column.type._elements[0].type, testing.eq_type_affinity(String))
+
+        tv = tv.render_derived()
+        eq_(tv.column.type, testing.eq_type_affinity(sqltypes.TableValueType))
+        eq_(tv.column.type._elements[0].type, testing.eq_type_affinity(String))
+
+    def test_alias_maintains_tableval_type(self):
+        fn = func.json_something()
+
+        tv = fn.table_valued(column("x", String))
+
+        eq_(tv.column.type, testing.eq_type_affinity(sqltypes.TableValueType))
+        eq_(tv.column.type._elements[0].type, testing.eq_type_affinity(String))
+
+        tv = tv.alias()
+        eq_(tv.column.type, testing.eq_type_affinity(sqltypes.TableValueType))
+        eq_(tv.column.type._elements[0].type, testing.eq_type_affinity(String))
+
     def test_star_with_ordinality(self):
         """
         SELECT * FROM generate_series(4,1,-1) WITH ORDINALITY;