Numeric value does not fit in a System.Decimal (value less than decimal.MaxValue)
See original GitHub issueI first looked at posts with a similar issues, for example
https://github.com/npgsql/efcore.pg/issues/438 https://github.com/npgsql/npgsql/issues/3238 https://github.com/npgsql/npgsql/issues/448
and didn’t find a solution to my issue
Introductory information
There are many type fields in our database: numeric (56,28) and numeric (55,19), everything worked fine in Npgsql 3.7 But when we updated the Npgsql version to 4.0/4.1/5 then some!!! fields have ceased to be correctly retrieved from the database below is an example of such a value
Steps to reproduce
for example:
- database field: numeric (55,19)
- save the value in the field: 99999999999
- if we make a select from this field, we get: 99999999999.0000000000000000000
simple code for reproduce:
using (var dbConn = new NpgsqlConnection("..."))
{
dbConn.Open();
var cmd = new NpgsqlCommand("select 99999999999.0000000000000000000::numeric", dbConn);
var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess);
reader.Read();
reader.GetValue(0);
}
The issue
We get an exception in any Npgsql above version 3.7 In Npgsql version 3.7 everything works fine
Exception message: System.OverflowException: 'Numeric value does not fit in a System.Decimal'
Stack trace:
Stack trace:
This exception was originally thrown at this call stack:
Npgsql.Internal.TypeHandlers.NumericHandlers.DecimalRaw.Multiply(ref Npgsql.Internal.TypeHandlers.NumericHandlers.DecimalRaw, uint) in DecimalRaw.cs
Npgsql.Internal.TypeHandlers.NumericHandlers.NumericHandler.Read(Npgsql.Internal.NpgsqlReadBuffer, int, Npgsql.BackendMessages.FieldDescription) in NumericHandler.cs
Npgsql.Internal.TypeHandling.NpgsqlSimpleTypeHandler<TDefault>.Read<TAny>(Npgsql.Internal.NpgsqlReadBuffer, int, Npgsql.BackendMessages.FieldDescription) in NpgsqlSimpleTypeHandler.cs
Npgsql.Internal.TypeHandling.NpgsqlSimpleTypeHandler<TDefault>.Read<TAny>(Npgsql.Internal.NpgsqlReadBuffer, int, bool, Npgsql.BackendMessages.FieldDescription) in NpgsqlSimpleTypeHandler.cs
[External Code]
Npgsql.Internal.TypeHandling.NpgsqlTypeHandler<TDefault>.ReadAsObject(Npgsql.Internal.NpgsqlReadBuffer, int, bool, Npgsql.BackendMessages.FieldDescription) in NpgsqlTypeHandler`.cs
[External Code]
Npgsql.NpgsqlDataReader.GetValue(int) in NpgsqlDataReader.cs
ConsoleApp1.Program.Main(string[]) in Program.cs
Further technical details
Npgsql version: Npgsql 4.0/4.1/5.0 PostgreSQL version: PostgreSQL 10.8 Operating system: Windows 10
https://github.com/npgsql/npgsql/blob/v4.1.4/src/Npgsql/TypeHandlers/NumericHandlers/DecimalRaw.cs
Screenshot with error:
Issue Analytics
- State:
- Created 2 years ago
- Comments:8 (7 by maintainers)
Thanks @YohDeadfall!
@SomeSpy, you have to change the following method:
https://github.com/npgsql/npgsql/blob/0f80008ce02415553a0a9b82f449afb45a04acb6/src/Npgsql/Internal/TypeHandlers/NumericHandlers/NumericHandler.cs#L42-L104
As you can see it throws on an overflow, but should not in your case. Therefore, tweak the logic in a such way that reading fractional part after reaching the maximum capacity of
decimal
will just take the first overflowing group, check that the digit at the overflowing position is less or equal to 5 and add 1 to the result in that case. Otherwise, do nothing. Read left groups till the end, but discard values.You should find in other threads how PostgreSQL stores and handles numerics, but to be short, they are stored in groups of 4 decimal digits. Each group is multiplied by
10^(4 * g)
whereg
is the number of groups minus the current group index. So1234567
can be represented as0123
and4567
. The scale complicates the logic a bit, but no so much.