diff --git a/piccolo/engine/sqlite.py b/piccolo/engine/sqlite.py index 3f7649d76..c8183e336 100644 --- a/piccolo/engine/sqlite.py +++ b/piccolo/engine/sqlite.py @@ -173,9 +173,21 @@ def convert_numeric_out(value: str) -> Decimal: @decode_to_string def convert_int_out(value: str) -> int: """ - Make sure Integer values are actually of type int. + Make sure INTEGER values are actually of type ``int``. + + SQLite doesn't enforce that the values in INTEGER columns are actually + integers - they could be strings ('hello'), or floats (1.0). + + There's not much we can do if the value is something like 'hello' - a + ``ValueError`` is appropriate in this situation. + + For a value like ``1.0``, it seems reasonable to handle this, and return a + value of ``1``. + """ - return int(float(value)) + # We used to use int(float(value)), but it was incorrect, because float has + # limited precision for large numbers. + return int(Decimal(value)) @decode_to_string diff --git a/requirements/test-requirements.txt b/requirements/test-requirements.txt index 006d8f2d9..6b8be1348 100644 --- a/requirements/test-requirements.txt +++ b/requirements/test-requirements.txt @@ -1,5 +1,5 @@ coveralls==3.3.1 -httpx==0.27.2 +httpx==0.28.0 pytest-cov==3.0.0 pytest==6.2.5 python-dateutil==2.8.2 diff --git a/tests/apps/asgi/commands/files/dummy_server.py b/tests/apps/asgi/commands/files/dummy_server.py index 9b83470a3..a4807aa66 100644 --- a/tests/apps/asgi/commands/files/dummy_server.py +++ b/tests/apps/asgi/commands/files/dummy_server.py @@ -3,7 +3,7 @@ import sys import typing as t -from httpx import AsyncClient +from httpx import ASGITransport, AsyncClient async def dummy_server(app: t.Union[str, t.Callable] = "app:app"): @@ -24,7 +24,7 @@ async def dummy_server(app: t.Union[str, t.Callable] = "app:app"): module = importlib.import_module(path) app = t.cast(t.Callable, getattr(module, app_name)) - async with AsyncClient(app=app) as client: + async with AsyncClient(transport=ASGITransport(app=app)) as client: response = await client.get("http://localhost:8000") if response.status_code != 200: sys.exit("The app isn't callable!") diff --git a/tests/columns/test_integer.py b/tests/columns/test_integer.py new file mode 100644 index 000000000..fe42aaf18 --- /dev/null +++ b/tests/columns/test_integer.py @@ -0,0 +1,32 @@ +from piccolo.columns.column_types import Integer +from piccolo.table import Table +from piccolo.testing.test_case import AsyncTableTest +from tests.base import sqlite_only + + +class MyTable(Table): + integer = Integer() + + +@sqlite_only +class TestInteger(AsyncTableTest): + tables = [MyTable] + + async def test_large_integer(self): + """ + Make sure large integers can be inserted and retrieved correctly. + + There was a bug with this in SQLite: + + https://github.com/piccolo-orm/piccolo/issues/1127 + + """ + integer = 625757527765811240 + + row = MyTable(integer=integer) + await row.save() + + _row = MyTable.objects().first().run_sync() + assert _row is not None + + self.assertEqual(_row.integer, integer)