Skip to content

Keep Float(asdecimal=True) result conversion with the ibm_db driver; declare DOUBLE as Float - #202

Open
aminghadersohi wants to merge 2 commits into
ibmdb:masterfrom
aminghadersohi:fix-float-asdecimal-results
Open

aminghadersohi wants to merge 2 commits into
ibmdb:masterfrom
aminghadersohi:fix-float-asdecimal-results

Conversation

@aminghadersohi

@aminghadersohi aminghadersohi commented Sep 26, 2026 •

Copy link
Copy Markdown

Problem

Float(asdecimal=True) returns float results, not Decimal, with the ibm_db dialect:

from sqlalchemy import Float
from ibm_db_sa.ibm_db import DB2Dialect_ibm_db

d = DB2Dialect_ibm_db()
p = Float(asdecimal=True).dialect_impl(d).result_processor(d, None)
print(type(p(1.25) if p else 1.25))  # <class 'float'>; expected Decimal

DB2Dialect_ibm_db.colspecs maps Numeric to _IBM_Numeric_ibm_db. Float subclasses Numeric and has no entry of its own, so SQLAlchemy's adapt_type adapts every Float (including REAL, DOUBLE_PRECISION and Float(precision=...)) to _IBM_Numeric_ibm_db. That type returns no result processor when asdecimal is true, so the DBAPI's float passes through and the asdecimal=True contract is broken.

Related: ibm_db_sa.base.DOUBLE subclassed Numeric, so reflected DOUBLE columns declared asdecimal=True and python_type Decimal, although DB2 DOUBLE is binary floating point and the DBAPI returns float.

Fix

Add Float: Float to the ibm_db dialect's colspecs. adapt_type then finds Float first in the MRO and keeps the original type, so SQLAlchemy's own Float.result_processor applies. Numeric/DECIMAL still adapt to _IBM_Numeric_ibm_db; nothing else changes. DDL is unaffected (compilation uses the declared type). This is independent of #201 and works with or without it.

Second commit (2e6f403): DOUBLE is now based on Float (asdecimal=False, python_type float). Returned values don't change and DDL still renders DOUBLE. Compatibility note: the Float constructor has no scale argument, so a caller passing DOUBLE(precision, scale) positionally would need adjusting; nothing in this repository does that.

Regression tests

test/test_float.py, same style as the existing tests: connectionless result-processor checks for Float(asdecimal=True), REAL(asdecimal=True) and default Float(), plus live round trips for Float(asdecimal=True) and Float(), comparing values and Python types exactly (no tolerances). For DOUBLE: test_double_is_float (declaration, compilation and result processing) and a live test_reflected_double (reflected type is DOUBLE, asdecimal is False, python_type is float, values/types round-trip as float).

First commit (Float colspec) — same test file, Python, SQLAlchemy, DBAPI and server; only ibm_db_sa/ibm_db.py differs between upstream master 7f3866e3a827924055323fd5f10ac40b7a9fb2a7 and this patch:

Tests Before After
Connectionless 2 FAIL / 1 PASS 3 PASS
Live DB2 1 FAIL / 1 PASS 2 PASS
Combined 3 FAIL / 2 PASS 5 PASS / 0 FAIL / 0 SKIP

The failures are type mismatches (float where Decimal is expected); the default Float() tests pass before and after, confirming unchanged behaviour there.

Second commit (DOUBLE) — only ibm_db_sa/base.py differing, full test/test_float.py: 2 FAIL / 5 PASS before, 7 PASS after. Both failures are the declaration assertions; value assertions pass before and after. The live reflection test passes the current schema explicitly, because default_schema_name is '' on current master (fixed separately in #203).

python -m pytest -p sqlalchemy.testing.plugin.pytestplugin --db sqlite -q test/test_float.py::TestFloatResults
python -m pytest -p sqlalchemy.testing.plugin.pytestplugin --dburi "$DB2_TEST_URL" -q test/test_float.py

Validation: Python 3.11, SQLAlchemy 2.0.52, ibm-db 3.2.3, pytest 9.1.1, local throwaway DB2 Community Edition 11.5.9.0 container. The connectionless tests also pass on SQLAlchemy 1.4.54 (the live class is skipped there on sqlite). Ruff 0.5.0 check/format on the new test file and git diff --check pass. The entire legacy upstream suite was not run.

The ibm_db dialect maps Numeric to _IBM_Numeric_ibm_db. Float subclasses
Numeric and has no entry of its own, so Float columns are adapted to
_IBM_Numeric_ibm_db, whose result processor returns the DBAPI value
unchanged when asdecimal is true. Float(asdecimal=True) therefore returns
float instead of Decimal.

Map Float to itself so SQLAlchemy's Float result handling applies.

Signed-off-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
ibm_db_sa's DOUBLE subclassed Numeric, so reflected DOUBLE columns
declared asdecimal=True and python_type Decimal, although DB2 DOUBLE is
binary floating point and the ibm_db DBAPI returns float for it. Base
DOUBLE on Float (asdecimal=False, python_type float). Returned values do
not change; DDL still renders DOUBLE.

Signed-off-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
@aminghadersohi

Copy link
Copy Markdown
Author

Added a second commit, 2e6f403 ("Declare DOUBLE as a Float type"), for a related declaration mismatch.

ibm_db_sa.base.DOUBLE subclassed Numeric, so reflected DOUBLE columns declared asdecimal=True and python_type Decimal, although DB2 DOUBLE is binary floating point and the DBAPI returns float. DOUBLE is now based on Float (asdecimal=False, python_type float). Returned values don't change and DDL still renders DOUBLE. One compatibility note: the Float constructor has no scale argument, so any caller passing DOUBLE(precision, scale) positionally would need adjusting. Nothing in this repository does that.

Tests: test_double_is_float (declaration, compilation and result processing) and a live test_reflected_double (reflected type is DOUBLE, asdecimal is False, python_type is float, and values/types round-trip as float). Same local DB2 11.5.9.0 server, only ibm_db_sa/base.py differing: 2 FAIL / 5 PASS before, 7 PASS after. Both failures are the declaration assertions; value assertions pass before and after. Connectionless tests also pass on SQLAlchemy 1.4.54.

Separately observed on current master and not changed here: inspect(connection).default_schema_name is '', so get_columns(table) without an explicit schema returns no columns. The live test passes the current schema explicitly.

@aminghadersohi aminghadersohi changed the title Keep Float(asdecimal=True) result conversion with the ibm_db driver Keep Float(asdecimal=True) result conversion with the ibm_db driver; declare DOUBLE as Float Sep 26, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant