]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
use literal execute for SQL Server frame parameters
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 19 Jun 2024 15:03:25 +0000 (11:03 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 20 Jun 2024 15:01:05 +0000 (11:01 -0400)
Fixed issue where SQL Server drivers don't support bound parameters when
rendering the "frame specification" for a window function, e.g. "ROWS
BETWEEN", etc.

Fixes: #11514
Change-Id: I0664f4076a2a8266434a4670949b8b44cd261f44

doc/build/changelog/unreleased_14/11514.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/mssql/base.py
lib/sqlalchemy/testing/suite/test_select.py

diff --git a/doc/build/changelog/unreleased_14/11514.rst b/doc/build/changelog/unreleased_14/11514.rst
new file mode 100644 (file)
index 0000000..81f0dde
--- /dev/null
@@ -0,0 +1,8 @@
+.. change::
+    :tags: bug, mssql
+    :tickets: 11514
+
+    Fixed issue where SQL Server drivers don't support bound parameters when
+    rendering the "frame specification" for a window function, e.g. "ROWS
+    BETWEEN", etc.
+
index 872f8584da40c56411e82fcf6ac1534761121c2d..ddee9a5a7398a9c809e36bdb90265deebf40925a 100644 (file)
@@ -1988,6 +1988,10 @@ class MSSQLCompiler(compiler.SQLCompiler):
         self.tablealiases = {}
         super().__init__(*args, **kwargs)
 
+    def _format_frame_clause(self, range_, **kw):
+        kw["literal_execute"] = True
+        return super()._format_frame_clause(range_, **kw)
+
     def _with_legacy_schema_aliasing(fn):
         def decorate(self, *arg, **kw):
             if self.dialect.legacy_schema_aliasing:
index 8ab6d57bbea8db223abe59b42a7a145fb2cf2402..9f2a08d151a6bf938d2fb901561604e9f9515636 100644 (file)
@@ -1886,3 +1886,54 @@ class IsOrIsNotDistinctFromTest(fixtures.TablesTest):
             len(result),
             expected_row_count_for_is_not,
         )
+
+
+class WindowFunctionTest(fixtures.TablesTest):
+    __requires__ = ("window_functions",)
+
+    __backend__ = True
+
+    @classmethod
+    def define_tables(cls, metadata):
+        Table(
+            "some_table",
+            metadata,
+            Column("id", Integer, primary_key=True),
+            Column("col1", Integer),
+            Column("col2", Integer),
+        )
+
+    @classmethod
+    def insert_data(cls, connection):
+        connection.execute(
+            cls.tables.some_table.insert(),
+            [{"id": i, "col1": i, "col2": i * 5} for i in range(1, 50)],
+        )
+
+    def test_window(self, connection):
+        some_table = self.tables.some_table
+        rows = connection.execute(
+            select(
+                func.max(some_table.c.col2).over(
+                    order_by=[some_table.c.col1.desc()]
+                )
+            ).where(some_table.c.col1 < 20)
+        ).all()
+
+        eq_(rows, [(95,) for i in range(19)])
+
+    def test_window_rows_between(self, connection):
+        some_table = self.tables.some_table
+
+        # note the rows are part of the cache key right now, not handled
+        # as binds.  this is issue #11515
+        rows = connection.execute(
+            select(
+                func.max(some_table.c.col2).over(
+                    order_by=[some_table.c.col1],
+                    rows=(-5, 0),
+                )
+            )
+        ).all()
+
+        eq_(rows, [(i,) for i in range(5, 250, 5)])