Skip to content

Commit 7b78dec

Browse files
committed
Convert IEEE decimal parameter values in FbParameter instead of DbValue
Every parameter value reaches DbValue through FbParameter.InternalValue (FbCommand and FbBatchCommand, sync and async), so Decimal32/64/128 values are converted to FbDecFloat there. DbValue no longer checks the type of every value it stores: both clients store decoded row values in DbValue (the managed client through the constructors, the native client through SetValue), and those values are never IEEE decimals. DbValue's constructors and SetValue are back to their previous code. Adds a batch command test (Decimal64/Decimal128 parameters, bit-exact round trip), as FbBatchCommand takes the same path.
1 parent 73685e5 commit 7b78dec

3 files changed

Lines changed: 65 additions & 19 deletions

File tree

‎src/FirebirdSql.Data.FirebirdClient.Tests/FbBatchCommandTests.cs‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,59 @@ public override async Task TearDown()
6666
await base.TearDown();
6767
}
6868

69+
#if NET11_0_OR_GREATER
70+
[Test]
71+
public async Task Decimal128AndDecimal64ProperlyInDatabase()
72+
{
73+
await using (var cmd = Connection.CreateCommand())
74+
{
75+
cmd.CommandText = "recreate table batch_decfloat (id int, d16 decfloat(16), d34 decfloat(34))";
76+
await cmd.ExecuteNonQueryAsync();
77+
}
78+
try
79+
{
80+
var d34 = new[] { Decimal128.Parse("1234567890123456789012345678.901234", System.Globalization.CultureInfo.InvariantCulture), Decimal128.Parse("-1.10", System.Globalization.CultureInfo.InvariantCulture) };
81+
var d16 = new[] { Decimal64.Parse("1.10", System.Globalization.CultureInfo.InvariantCulture), Decimal64.Parse("-9.999999999999999E+384", System.Globalization.CultureInfo.InvariantCulture) };
82+
await using (var cmd = Connection.CreateBatchCommand())
83+
{
84+
cmd.CommandText = "insert into batch_decfloat values (@id, @d16, @d34)";
85+
for (var i = 0; i < d34.Length; i++)
86+
{
87+
var batch = cmd.AddBatchParameters();
88+
batch.Add("id", i);
89+
batch.Add("d16", d16[i]);
90+
batch.Add("d34", d34[i]);
91+
}
92+
var result = await cmd.ExecuteNonQueryAsync();
93+
Assert.IsTrue(result.AllSuccess);
94+
}
95+
await using (var cmd = Connection.CreateCommand())
96+
{
97+
cmd.CommandText = "select d16, d34 from batch_decfloat order by id";
98+
await using (var reader = await cmd.ExecuteReaderAsync())
99+
{
100+
for (var i = 0; i < d34.Length; i++)
101+
{
102+
Assert.IsTrue(await reader.ReadAsync());
103+
// same bits: same value and same exponent
104+
Assert.AreEqual(Decimal64.EncodeDecimal(d16[i]), Decimal64.EncodeDecimal(reader.GetFieldValue<Decimal64>(0)));
105+
Assert.AreEqual(Decimal128.EncodeDecimal(d34[i]), Decimal128.EncodeDecimal(reader.GetFieldValue<Decimal128>(1)));
106+
}
107+
Assert.IsFalse(await reader.ReadAsync());
108+
}
109+
}
110+
}
111+
finally
112+
{
113+
await using (var cmd = Connection.CreateCommand())
114+
{
115+
cmd.CommandText = "drop table batch_decfloat";
116+
await cmd.ExecuteNonQueryAsync();
117+
}
118+
}
119+
}
120+
#endif
121+
69122
[Test]
70123
public async Task DataProperlyInDatabase()
71124
{

‎src/FirebirdSql.Data.FirebirdClient/Common/DbValue.cs‎

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,30 +40,14 @@ public DbField Field
4040
public DbValue(DbField field, object value)
4141
{
4242
_field = field;
43-
_value = Normalize(value) ?? DBNull.Value;
43+
_value = value ?? DBNull.Value;
4444
}
4545

4646
public DbValue(StatementBase statement, DbField field, object value)
4747
{
4848
_statement = statement;
4949
_field = field;
50-
_value = Normalize(value) ?? DBNull.Value;
51-
}
52-
53-
// IEEE 754 decimal values are kept as FbDecFloat (exact), so every conversion that handles FbDecFloat handles them too.
54-
static object Normalize(object value)
55-
{
56-
#if NET11_0_OR_GREATER
57-
return value switch
58-
{
59-
Decimal128 d => (FbDecFloat)d,
60-
Decimal64 d => (FbDecFloat)d,
61-
Decimal32 d => (FbDecFloat)d,
62-
_ => value,
63-
};
64-
#else
65-
return value;
66-
#endif
50+
_value = value ?? DBNull.Value;
6751
}
6852

6953
public bool IsDBNull()
@@ -160,7 +144,7 @@ public async ValueTask<object> GetValueAsync(CancellationToken cancellationToken
160144

161145
public void SetValue(object value)
162146
{
163-
_value = Normalize(value);
147+
_value = value;
164148
}
165149

166150
public string GetString()

‎src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbParameter.cs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,15 @@ internal object InternalValue
242242
var result = new byte[Math.Min(Size, bvalue.Length)];
243243
Array.Copy(bvalue, result, result.Length);
244244
return result;
245+
#if NET11_0_OR_GREATER
246+
// IEEE 754 decimal values are passed on as FbDecFloat (exact), so every conversion that handles FbDecFloat handles them too.
247+
case System.Numerics.Decimal128 d128:
248+
return (Types.FbDecFloat)d128;
249+
case System.Numerics.Decimal64 d64:
250+
return (Types.FbDecFloat)d64;
251+
case System.Numerics.Decimal32 d32:
252+
return (Types.FbDecFloat)d32;
253+
#endif
245254
default:
246255
return _value;
247256
}

0 commit comments

Comments
 (0)