mssql-jdbc: Arithmetic operations with decimals return incorrect results
Driver version
12.4.0.jre11
SQL Server version
Microsoft SQL Server 2019 (RTM-CU15) (KB5008996) - 15.0.4198.2 (X64)
Jan 12 2022 22:30:08
Copyright (C) 2019 Microsoft Corporation
Express Edition (64-bit) on Linux (Ubuntu 20.04.3 LTS) <X64>
Client Operating System
Ubuntu on Windows version 2004.2022.1.0
JAVA/JVM version
1.17
Table schema
CREATE TABLE dbo.df_test_all_types
(
ID INT PRIMARY KEY,
COL_TINYINT TINYINT,
COL_SMALLINT SMALLINT,
COL_INT INT,
COL_BIGINT BIGINT,
COL_FLOAT FLOAT,
COL_NUMERIC NUMERIC,
COL_DECIMAL DECIMAL(19, 8),
COL_BOOLEAN BIT,
COL_DATE DATE,
COL_TIME TIME,
COL_DATETIME DATETIME,
COL_VARCHAR VARCHAR(100),
COL_NVARCHAR NVARCHAR(100),
COL_TEXT TEXT,
COL_BLOB VARBINARY(5000)
);
INSERT INTO dbo.df_test_all_types (ID, COL_INT, COL_BIGINT, COL_DECIMAL, COL_BOOLEAN,
COL_VARCHAR, COL_NVARCHAR, COL_TEXT)
VALUES (1, 1, 1000, 48.9, 0, 'Some string', 'Some other string', 'Some Text');
INSERT INTO dbo.df_test_all_types (ID, COL_INT, COL_BIGINT, COL_DECIMAL, COL_BOOLEAN,
COL_TEXT)
VALUES (2, 2, 2000, -48.999, 1, ' TrimText ');
INSERT INTO dbo.df_test_all_types (ID, COL_INT, COL_BIGINT, COL_DECIMAL, COL_BOOLEAN,
COL_VARCHAR, COL_NVARCHAR, COL_TEXT)
VALUES (3, 3, 3000, 25, 1, '1234', '1234', '1234');
INSERT INTO dbo.df_test_all_types (ID, COL_INT, COL_BIGINT, COL_DECIMAL, COL_BOOLEAN,
COL_VARCHAR, COL_NVARCHAR, COL_TEXT)
VALUES (4, 4, 4000, 12.34, 1, '2147483650', '2147483650', '2147483650');
INSERT INTO dbo.df_test_all_types (ID, COL_INT, COL_BIGINT, COL_DECIMAL)
VALUES (5, 5, 10, 2.5);
INSERT INTO dbo.df_test_all_types (ID, COL_TINYINT)
VALUES (6, 19);
Problem description
This new version of the driver introduces some changes on how arithmetic operations with decimals work. For a better understanding consider the follwing examples.
Sum of 3 big decimals with different precision rounds the value to the closest integer:
Query: SELECT ? + ? + ? TABLE_EXAMPLE WHERE "ID" = 4
Dynamic Parameters: new BigDecimal("4"), new BigDecimal("5") and new BigDecimal("3.14")
Expected Result: 5.86
Actual Result: 6
Multiplication followed by a Subtraction of 3 big decimals with different precision returns a decimal without precision:
Query: SELECT ? * ? - ? FROM TABLE_EXAMPLE WHERE "ID" = 4
Dynamic Parameters: new BigDecimal("4"), new BigDecimal("2") and new BigDecimal("1.00")
Expected Result: 7.00
Actual Result: 7
Subtraction of 2 big decimals rounds the result to the closest integer:
Query: SELECT ? - ? FROM TABLE_EXAMPLE WHERE "ID" = 4
Dynamic Parameters: new BigDecimal("4.0") and new BigDecimal("3.14")
Expected Result: 0.86
Actual Result: 1
Subtraction of 2 big decimals round the result to a decimal with the lowest precision:
Example 1
Query: SELECT ? - ? TABLE_EXAMPLE WHERE "ID" = 4
Dynamic Parameters: new BigDecimal("4.0") and new BigDecimal("3.14")
Expected Result: 0.86
Actual Result: 0.9
Example 2
Query: SELECT ? - ? TABLE_EXAMPLE WHERE "ID" = 4]
Dynamic Parameters: new BigDecimal("1.23") and new BigDecimal("1.2")
Expected Result: 0.03
Actual Result: 0.0
Incorrect precision when using CAST
Example 1
Query: SELECT ? - CAST("COL_INT" AS DECIMAL(38, 2) FROM TABLE_EXAMPLE WHERE "ID" = 4
Dynamic Parameters: new BigDecimal("0.00")
Expected Result: 0.00
Actual Result: 0
Example 2
Query: SELECT ? - CAST("COL_BIGINT" AS DECIMAL(38, 2) FROM TABLE_EXAMPLE WHERE "ID" = 4
Dynamic Parameters: new BigDecimal("-3996.00")
Expected Result: -3996.00
Actual Result: -3996
Example 3
Query: SELECT ? - "COL_DECIMAL" FROM TABLE_EXAMPLE WHERE "ID" = 4
Dynamic Parameters: new BigDecimal("-3996.00")
Expected Result: -8.34000000
Actual Result: -8
Error message/stack trace
Not applicable. The driver returns no error. The result though is no the expected one.
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 18 (10 by maintainers)
Hi @Jeffery-Wasty, @tkyc,
Adding the mentioned property to the connection string works as expected. Thank you for your help.
Hi @OS-veracardoso,
In addition to the testing we did previously with our own driver, we looked into jTDS and the MySQL Java connector.
For jTDS, it behaves the same as the JDBC driver, producing similar arithmetic errors. For the MySQL connector, they are using a similar approach to what we did in 12.2, being able to parse the BigDecimal to fetch precision and scale from there. We’re not sure what kind of performance impact this introduces to the driver, but the same approach in the JDBC driver caused a noticeable performance impact. We tried to find a way that precision could be updated for the parameter type definition without recreating the whole execution plan (the reason for the performance impact) but were unable to do so.
For the time being, we ask that you consider using the 12.2 version of the driver, as that version has the BigDecimal fixes you are interested in. A potential enhancement to the driver could include a connection property to restore previous BigDecimal behavior, at the cost of performance. We’ll keep this issue open while that option is still in consideration/development.