]> 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)
committerMichael Bayer <mike_mp@zzzcomputing.com>
Sat, 22 Jun 2024 16:11:13 +0000 (16:11 +0000)
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
(cherry picked from commit c088b6426f1d73efe7de3e42b3e86f8027076bc3)
(cherry picked from commit 9524e4bffc9c8545fdb8698ef029c420374ac00f)

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 326d9f54fcc5020a11be4968192672fb35f24364..efaec75c5408ac329b2b48792ead059888ee792a 100644 (file)
@@ -1847,6 +1847,10 @@ class MSSQLCompiler(compiler.SQLCompiler):
         self.tablealiases = {}
         super(MSSQLCompiler, self).__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 42369a4e0f03a16b05731525fa6b7f2df7e1d63e..eca2203d58f2a1330c2d70db2ea428bacd56c680 100644 (file)
@@ -1787,3 +1787,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)])