--- /dev/null
+.. 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`.
"""
- 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):
# 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
import sqlalchemy as sa
from sqlalchemy import ForeignKey
+from sqlalchemy import func
from sqlalchemy import inspect
from sqlalchemy import Integer
from sqlalchemy import MetaData
go()
+ def test_tv_render_derived(self):
+ root_expr = func.some_fn().table_valued()
+ expr = root_expr
+
+ @profile_memory()
+ def go():
+ nonlocal expr
+
+ expr = expr.render_derived()
+
+ go()
+
@testing.add_to_marker.memory_intensive
class MemUsageWBackendTest(fixtures.MappedTest, EnsureZeroed):
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",
)
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)
from sqlalchemy import testing
from sqlalchemy import Text
from sqlalchemy import true
-from sqlalchemy import types as sqltypes
from sqlalchemy.dialects import mysql
from sqlalchemy.dialects import oracle
from sqlalchemy.dialects import postgresql
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
"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;