From 13f87c2bdc2ef95cfc12f46b736fa0c4af26d337 Mon Sep 17 00:00:00 2001 From: Federico Caselli Date: Wed, 29 May 2024 21:39:08 +0200 Subject: [PATCH] Make `FunctionFilter.filter` generative Fixed bug in :meth:`_sql.FunctionFilter.filter` that would mutate the existing function in-place. It now behaves like the rest of the SQLAlchemy API, returning a new instance instead of mutating the original one. Fixes: #11426 Change-Id: I46ffebaed82426cfb1623db066686cfb911055a1 (cherry picked from commit fe2ced9e79b9640f3ca135f8d3782dd41ca16782) --- doc/build/changelog/unreleased_20/11426.rst | 8 ++++++++ lib/sqlalchemy/sql/elements.py | 5 +++-- test/sql/test_functions.py | 11 +++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 doc/build/changelog/unreleased_20/11426.rst diff --git a/doc/build/changelog/unreleased_20/11426.rst b/doc/build/changelog/unreleased_20/11426.rst new file mode 100644 index 0000000000..c9018b02f4 --- /dev/null +++ b/doc/build/changelog/unreleased_20/11426.rst @@ -0,0 +1,8 @@ +.. change:: + :tags: bug, sql + :tickets: 11426 + + Fixed bug in :meth:`_sql.FunctionFilter.filter` that would mutate + the existing function in-place. It now behaves like the rest of the + SQLAlchemy API, returning a new instance instead of mutating the + original one. diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index 0d75318296..3f8f9abbcb 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -4356,7 +4356,7 @@ class WithinGroup(ColumnElement[_T]): ) -class FunctionFilter(ColumnElement[_T]): +class FunctionFilter(Generative, ColumnElement[_T]): """Represent a function FILTER clause. This is a special operator against aggregate and window functions, @@ -4389,8 +4389,9 @@ class FunctionFilter(ColumnElement[_T]): *criterion: _ColumnExpressionArgument[bool], ): self.func = func - self.filter(*criterion) + self.filter.non_generative(self, *criterion) # type: ignore + @_generative def filter(self, *criterion: _ColumnExpressionArgument[bool]) -> Self: """Produce an additional FILTER against the function. diff --git a/test/sql/test_functions.py b/test/sql/test_functions.py index c47601b761..c324c8f33a 100644 --- a/test/sql/test_functions.py +++ b/test/sql/test_functions.py @@ -844,6 +844,17 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL): "AS anon_1 FROM mytable", ) + def test_funcfilter_more_criteria(self): + ff = func.rank().filter(table1.c.name > "foo") + ff2 = ff.filter(table1.c.myid == 1) + self.assert_compile( + select(ff, ff2), + "SELECT rank() FILTER (WHERE mytable.name > :name_1) AS anon_1, " + "rank() FILTER (WHERE mytable.name > :name_1 AND " + "mytable.myid = :myid_1) AS anon_2 FROM mytable", + {"name_1": "foo", "myid_1": 1}, + ) + def test_funcfilter_within_group(self): stmt = select( table1.c.myid, -- 2.47.2