]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
resolve large ints to BigInteger
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 10 Jun 2022 16:57:53 +0000 (12:57 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 10 Jun 2022 17:23:20 +0000 (13:23 -0400)
The in-place type detection for Python integers, as occurs with an
expression such as ``literal(25)``, will now apply value-based adaption as
well to accommodate Python large integers, where the datatype determined
will be :class:`.BigInteger` rather than :class:`.Integer`. This
accommodates for dialects such as that of asyncpg which both sends implicit
typing information to the driver as well as is sensitive to numeric scale.

Fixes: #7909
Change-Id: I1cd3ec2676c9bb03ffedb600695252bd0037ba02

doc/build/changelog/unreleased_20/7909.rst [new file with mode: 0644]
lib/sqlalchemy/sql/sqltypes.py
lib/sqlalchemy/testing/suite/test_types.py

diff --git a/doc/build/changelog/unreleased_20/7909.rst b/doc/build/changelog/unreleased_20/7909.rst
new file mode 100644 (file)
index 0000000..e003b85
--- /dev/null
@@ -0,0 +1,10 @@
+.. change::
+    :tags: bug, sql
+    :tickets: 7909
+
+    The in-place type detection for Python integers, as occurs with an
+    expression such as ``literal(25)``, will now apply value-based adaption as
+    well to accommodate Python large integers, where the datatype determined
+    will be :class:`.BigInteger` rather than :class:`.Integer`. This
+    accommodates for dialects such as that of asyncpg which both sends implicit
+    typing information to the driver as well as is sensitive to numeric scale.
index 50cb325033b37752f7f9045349a05fdc3af46c37..2cc46107b056f30b61301f37887d6fc56fe62188 100644 (file)
@@ -366,6 +366,12 @@ class Integer(HasExpressionLookup, TypeEngine[int]):
     def python_type(self):
         return int
 
+    def _resolve_for_literal(self, value):
+        if value.bit_length() >= 32:
+            return _BIGINTEGER
+        else:
+            return self
+
     def literal_processor(self, dialect):
         def process(value):
             return str(int(value))
@@ -3556,6 +3562,7 @@ MATCHTYPE = MatchType()
 TABLEVALUE = TableValueType()
 DATETIME_TIMEZONE = DateTime(timezone=True)
 TIME_TIMEZONE = Time(timezone=True)
+_BIGINTEGER = BigInteger()
 _DATETIME = DateTime()
 _TIME = Time()
 _STRING = String()
index f1e3768377543464ef08335eae0e1cac6f126d8b..3913799569caf510bb565acc0e0a3516e123622f 100644 (file)
@@ -565,8 +565,37 @@ class IntegerTest(_LiteralRoundTripFixture, fixtures.TestBase):
     def test_literal(self, literal_round_trip):
         literal_round_trip(Integer, [5], [5])
 
-    def test_huge_int(self, integer_round_trip):
-        integer_round_trip(BigInteger, 1376537018368127)
+    def _huge_ints():
+
+        return testing.combinations(
+            2147483649,  # 32 bits
+            2147483648,  # 32 bits
+            2147483647,  # 31 bits
+            2147483646,  # 31 bits
+            -2147483649,  # 32 bits
+            -2147483648,  # 32 interestingly, asyncpg accepts this one as int32
+            -2147483647,  # 31
+            -2147483646,  # 31
+            0,
+            1376537018368127,
+            -1376537018368127,
+            argnames="intvalue",
+        )
+
+    @_huge_ints()
+    def test_huge_int_auto_accommodation(self, connection, intvalue):
+        """test #7909"""
+
+        eq_(
+            connection.scalar(
+                select(intvalue).where(literal(intvalue) == intvalue)
+            ),
+            intvalue,
+        )
+
+    @_huge_ints()
+    def test_huge_int(self, integer_round_trip, intvalue):
+        integer_round_trip(BigInteger, intvalue)
 
     @testing.fixture
     def integer_round_trip(self, metadata, connection):