Skip to content
This repository was archived by the owner on Aug 19, 2025. It is now read-only.

Commit 5688129

Browse files
Merge pull request #112 from coobas/feature/use-native-decimal
use native decimal (numeric) type
2 parents 128bd60 + afc5730 commit 5688129

File tree

4 files changed

+40
-0
lines changed

4 files changed

+40
-0
lines changed

databases/backends/mysql.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def __init__(
2323
self._database_url = DatabaseURL(database_url)
2424
self._options = options
2525
self._dialect = pymysql.dialect(paramstyle="pyformat")
26+
self._dialect.supports_native_decimal = True
2627
self._pool = None
2728

2829
def _get_connection_kwargs(self) -> dict:

databases/backends/postgres.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def _get_dialect(self) -> Dialect:
3636
dialect._backslash_escapes = False
3737
dialect.supports_sane_multi_rowcount = True # psycopg 2.0.9+
3838
dialect._has_native_hstore = True
39+
dialect.supports_native_decimal = True
3940

4041
return dialect
4142

databases/backends/sqlite.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ def __init__(
2222
self._database_url = DatabaseURL(database_url)
2323
self._options = options
2424
self._dialect = pysqlite.dialect(paramstyle="qmark")
25+
# aiosqlite does not support decimals
26+
self._dialect.supports_native_decimal = False
2527
self._pool = SQLitePool(self._database_url, **self._options)
2628

2729
async def connect(self) -> None:

tests/test_databases.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
import datetime
3+
import decimal
34
import functools
45
import os
56

@@ -61,6 +62,14 @@ def process_result_value(self, value, dialect):
6162
sqlalchemy.Column("published", MyEpochType),
6263
)
6364

65+
# Used to test Numeric
66+
prices = sqlalchemy.Table(
67+
"prices",
68+
metadata,
69+
sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True),
70+
sqlalchemy.Column("price", sqlalchemy.Numeric(precision=30, scale=20)),
71+
)
72+
6473

6574
@pytest.fixture(autouse=True, scope="module")
6675
def create_test_database():
@@ -456,6 +465,33 @@ async def test_datetime_field(database_url):
456465
assert results[0]["published"] == now
457466

458467

468+
@pytest.mark.parametrize("database_url", DATABASE_URLS)
469+
@async_adapter
470+
async def test_decimal_field(database_url):
471+
"""
472+
Test Decimal (NUMERIC) columns, to ensure records are coerced to/from proper Python types.
473+
"""
474+
475+
async with Database(database_url) as database:
476+
async with database.transaction(force_rollback=True):
477+
price = decimal.Decimal("0.700000000000001")
478+
479+
# execute()
480+
query = prices.insert()
481+
values = {"price": price}
482+
await database.execute(query, values)
483+
484+
# fetch_all()
485+
query = prices.select()
486+
results = await database.fetch_all(query=query)
487+
assert len(results) == 1
488+
if database_url.startswith("sqlite"):
489+
# aiosqlite does not support native decimals --> a roud-off error is expected
490+
assert results[0]["price"] == pytest.approx(price)
491+
else:
492+
assert results[0]["price"] == price
493+
494+
459495
@pytest.mark.parametrize("database_url", DATABASE_URLS)
460496
@async_adapter
461497
async def test_json_field(database_url):

0 commit comments

Comments
 (0)