]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
fixes #12596
authorViolet Folino Gallo <48537601+galloviolet@users.noreply.github.com>
Mon, 23 Jun 2025 20:30:08 +0000 (13:30 -0700)
committerViolet Folino Gallo <48537601+galloviolet@users.noreply.github.com>
Mon, 23 Jun 2025 20:34:46 +0000 (13:34 -0700)
lib/sqlalchemy/sql/compiler.py
lib/sqlalchemy/sql/elements.py
test/sql/test_compare.py
test/sql/test_compiler.py

index b123acbff1461a48f42bb26437e6d8fd3095d407..1b2e9b2f6203fd3b376033f61bcbe79c857ce7d3 100644 (file)
@@ -2849,7 +2849,7 @@ class SQLCompiler(Compiled):
         elif frameclause.lower_type is elements._FrameClauseType.RANGE_CURRENT:
             left = "CURRENT ROW"
         else:
-            val = self.process(frameclause.lower_integer_bind, **kw)
+            val = self.process(frameclause.lower_bind, **kw)
             if (
                 frameclause.lower_type
                 is elements._FrameClauseType.RANGE_PRECEDING
@@ -2863,7 +2863,7 @@ class SQLCompiler(Compiled):
         elif frameclause.upper_type is elements._FrameClauseType.RANGE_CURRENT:
             right = "CURRENT ROW"
         else:
-            val = self.process(frameclause.upper_integer_bind, **kw)
+            val = self.process(frameclause.upper_bind, **kw)
             if (
                 frameclause.upper_type
                 is elements._FrameClauseType.RANGE_PRECEDING
index 42dfe6110647f1636bbf3649a9ddc6429c69d422..691d28afac9710efd6cd4af7c13609b7a23d31f4 100644 (file)
@@ -4231,7 +4231,7 @@ class Over(ColumnElement[_T]):
         element: ColumnElement[_T],
         partition_by: Optional[_ByArgument] = None,
         order_by: Optional[_ByArgument] = None,
-        range_: Optional[typing_Tuple[Optional[int], Optional[int]]] = None,
+        range_: Optional[typing_Tuple[Optional[typing.Any], Optional[typing.Any]]] = None,
         rows: Optional[typing_Tuple[Optional[int], Optional[int]]] = None,
         groups: Optional[typing_Tuple[Optional[int], Optional[int]]] = None,
     ):
@@ -4252,8 +4252,8 @@ class Over(ColumnElement[_T]):
             )
         else:
             self.range_ = _FrameClause(range_) if range_ else None
-            self.rows = _FrameClause(rows) if rows else None
-            self.groups = _FrameClause(groups) if groups else None
+            self.rows = _FrameClause(self._interpret_int_range(rows)) if rows else None
+            self.groups = _FrameClause(self._interpret_int_range(groups)) if groups else None
 
     if not TYPE_CHECKING:
 
@@ -4272,6 +4272,15 @@ class Over(ColumnElement[_T]):
                 ]
             )
         )
+    
+    def _interpret_int_range(self, rows):
+        try:
+            lower = rows[0] if rows[0] is None else int(rows[0])
+            upper = rows[1] if rows[1] is None else int(rows[1])
+        except ValueError as ve:
+            raise exc.ArgumentError("Integer or None expected for rows value and groups value") from ve
+
+        return lower, upper
 
 
 class _FrameClauseType(Enum):
@@ -4292,70 +4301,48 @@ class _FrameClause(ClauseElement):
     __visit_name__ = "frame_clause"
 
     _traverse_internals: _TraverseInternalsType = [
-        ("lower_integer_bind", InternalTraversal.dp_clauseelement),
-        ("upper_integer_bind", InternalTraversal.dp_clauseelement),
+        ("lower_bind", InternalTraversal.dp_clauseelement),
+        ("upper_bind", InternalTraversal.dp_clauseelement),
         ("lower_type", InternalTraversal.dp_plain_obj),
         ("upper_type", InternalTraversal.dp_plain_obj),
     ]
 
     def __init__(
         self,
-        range_: typing_Tuple[Optional[int], Optional[int]],
+        range_: typing_Tuple[Optional[typing.Any], Optional[typing.Any]],
     ):
         try:
-            r0, r1 = range_
+            lower_value, upper_value = range_
         except (ValueError, TypeError) as ve:
             raise exc.ArgumentError("2-tuple expected for range/rows") from ve
 
-        if r0 is None:
+        if lower_value is None:
             self.lower_type = _FrameClauseType.RANGE_UNBOUNDED
-            self.lower_integer_bind = None
+            self.lower_bind = None
         else:
-            try:
-                lower_integer = int(r0)
-            except ValueError as err:
-                raise exc.ArgumentError(
-                    "Integer or None expected for range value"
-                ) from err
+            if lower_value == 0:
+                self.lower_type = _FrameClauseType.RANGE_CURRENT
+                self.lower_bind = None
+            elif lower_value < 0:
+                self.lower_type = _FrameClauseType.RANGE_PRECEDING
+                self.lower_bind = literal(abs(lower_value), type_api.NULLTYPE)
             else:
-                if lower_integer == 0:
-                    self.lower_type = _FrameClauseType.RANGE_CURRENT
-                    self.lower_integer_bind = None
-                elif lower_integer < 0:
-                    self.lower_type = _FrameClauseType.RANGE_PRECEDING
-                    self.lower_integer_bind = literal(
-                        abs(lower_integer), type_api.INTEGERTYPE
-                    )
-                else:
-                    self.lower_type = _FrameClauseType.RANGE_FOLLOWING
-                    self.lower_integer_bind = literal(
-                        lower_integer, type_api.INTEGERTYPE
-                    )
+                self.lower_type = _FrameClauseType.RANGE_FOLLOWING
+                self.lower_bind = literal(lower_value, type_api.NULLTYPE)
 
-        if r1 is None:
+        if upper_value is None:
             self.upper_type = _FrameClauseType.RANGE_UNBOUNDED
-            self.upper_integer_bind = None
+            self.upper_bind = None
         else:
-            try:
-                upper_integer = int(r1)
-            except ValueError as err:
-                raise exc.ArgumentError(
-                    "Integer or None expected for range value"
-                ) from err
+            if upper_value == 0:
+                self.upper_type = _FrameClauseType.RANGE_CURRENT
+                self.upper_bind = None
+            elif upper_value < 0:
+                self.upper_type = _FrameClauseType.RANGE_PRECEDING
+                self.upper_bind = literal(abs(upper_value), type_api.NULLTYPE)
             else:
-                if upper_integer == 0:
-                    self.upper_type = _FrameClauseType.RANGE_CURRENT
-                    self.upper_integer_bind = None
-                elif upper_integer < 0:
-                    self.upper_type = _FrameClauseType.RANGE_PRECEDING
-                    self.upper_integer_bind = literal(
-                        abs(upper_integer), type_api.INTEGERTYPE
-                    )
-                else:
-                    self.upper_type = _FrameClauseType.RANGE_FOLLOWING
-                    self.upper_integer_bind = literal(
-                        upper_integer, type_api.INTEGERTYPE
-                    )
+                self.upper_type = _FrameClauseType.RANGE_FOLLOWING
+                self.upper_bind = literal(upper_value, type_api.NULLTYPE)
 
 
 class WithinGroup(ColumnElement[_T]):
index 9c9bde1dacfbd50a022add4f74736bab5052ce26..e08c5aeeb4bb9ca4b4cebddd5d8002790e29a77e 100644 (file)
@@ -1665,8 +1665,8 @@ class HasCacheKeySubclass(fixtures.TestBase):
             {"columns", "name", "literal_binds"},
         ),
         "_FrameClause": (
-            {"upper_integer_bind", "upper_type"}
-            | {"lower_type", "lower_integer_bind"},
+            {"upper_bind", "upper_type"}
+            | {"lower_type", "lower_bind"},
             {"range_"},
         ),
         "_MemoizedSelectEntities": (
index 5e86e14db7cb2af2777a7ee524e4a28bd3b087fb..5ab4b67a2d6a871732f92d251e43b4d8a22e8b16 100644 (file)
@@ -3247,16 +3247,16 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL):
     def test_over_invalid_framespecs(self):
         assert_raises_message(
             exc.ArgumentError,
-            "Integer or None expected for range value",
+            "Integer or None expected for rows value and groups value",
             func.row_number().over,
-            range_=("foo", 8),
+            rows=("foo", 8),
         )
 
         assert_raises_message(
             exc.ArgumentError,
-            "Integer or None expected for range value",
+            "Integer or None expected for rows value and groups value",
             func.row_number().over,
-            range_=(-5, "foo"),
+            groups=(-5, "foo"),
         )
 
         assert_raises_message(