Skip to content

Commit 50a9daa

Browse files
author
Davoud Eshtehari
committed
fix & add test units
1 parent 68fc259 commit 50a9daa

File tree

3 files changed

+174
-2
lines changed

3 files changed

+174
-2
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBuffer.cs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,63 @@ internal decimal Decimal
215215
{
216216
if (_value._numericInfo._data4 != 0 || _value._numericInfo._scale > 28)
217217
{
218-
throw new OverflowException(SQLResource.ConversionOverflowMessage);
218+
if (_value._numericInfo._scale > 0)
219+
{
220+
SqlDecimal sqlValue = new(_value._numericInfo._precision,
221+
_value._numericInfo._scale,
222+
_value._numericInfo._positive,
223+
_value._numericInfo._data1,
224+
_value._numericInfo._data2,
225+
_value._numericInfo._data3,
226+
_value._numericInfo._data4);
227+
228+
// Remove trail zeros after the point from the end to calculate the valuable floating part
229+
string[] valueParts = sqlValue.ToString().TrimEnd('0').Split('.');
230+
int scale = valueParts[1].Length;
231+
int precision = valueParts[0].Length + scale + (sqlValue.IsPositive ? 0 : -1);
232+
233+
// Out of range
234+
if ((precision > 29 || scale > 28) && !(precision == 29 && scale == 28))
235+
{
236+
throw new OverflowException(SQLResource.ConversionOverflowMessage);
237+
}
238+
else
239+
{
240+
// Precision could be 28 or 29
241+
// ex: (precision == 29 && scale == 28)
242+
// valid: (+/-)7.1234567890123456789012345678
243+
// invalid: (+/-)8.1234567890123456789012345678
244+
bool tryMaxPrec = (valueParts[0].Length == 1 || (valueParts[0].Length == 2 && !_value._numericInfo._positive));
245+
int precConvert = 29;
246+
int scaleConvert;
247+
248+
if (!tryMaxPrec && precision != 29)
249+
{
250+
precConvert = 28;
251+
}
252+
scaleConvert = precConvert - (precision - scale);
253+
254+
try
255+
{
256+
return SqlDecimal.ConvertToPrecScale(sqlValue, precConvert, scaleConvert).Value;
257+
}
258+
catch (OverflowException)
259+
{
260+
if (tryMaxPrec && scale < 28)
261+
{
262+
return SqlDecimal.ConvertToPrecScale(sqlValue, precConvert - 1, scaleConvert).Value;
263+
}
264+
else
265+
{
266+
throw;
267+
}
268+
}
269+
}
270+
}
271+
else
272+
{
273+
throw new OverflowException(SQLResource.ConversionOverflowMessage);
274+
}
219275
}
220276
return new decimal(_value._numericInfo._data1, _value._numericInfo._data2, _value._numericInfo._data3, !_value._numericInfo._positive, _value._numericInfo._scale);
221277
}

src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBuffer.cs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,63 @@ internal decimal Decimal
212212
{
213213
if (_value._numericInfo._data4 != 0 || _value._numericInfo._scale > 28)
214214
{
215-
throw new OverflowException(SQLResource.ConversionOverflowMessage);
215+
if (_value._numericInfo._scale > 0)
216+
{
217+
SqlDecimal sqlValue = new(_value._numericInfo._precision,
218+
_value._numericInfo._scale,
219+
_value._numericInfo._positive,
220+
_value._numericInfo._data1,
221+
_value._numericInfo._data2,
222+
_value._numericInfo._data3,
223+
_value._numericInfo._data4);
224+
225+
// Remove trail zeros after the point from the end to calculate the valuable floating part
226+
string[] valueParts = sqlValue.ToString().TrimEnd('0').Split('.');
227+
int scale = valueParts[1].Length;
228+
int precision = valueParts[0].Length + scale + (sqlValue.IsPositive ? 0 : -1);
229+
230+
// Out of range
231+
if ((precision > 29 || scale > 28) && !(precision == 29 && scale == 28))
232+
{
233+
throw new OverflowException(SQLResource.ConversionOverflowMessage);
234+
}
235+
else
236+
{
237+
// Precision could be 28 or 29
238+
// ex: (precision == 29 && scale == 28)
239+
// valid: (+/-)7.1234567890123456789012345678
240+
// invalid: (+/-)8.1234567890123456789012345678
241+
bool tryMaxPrec = (valueParts[0].Length == 1 || (valueParts[0].Length == 2 && !_value._numericInfo._positive));
242+
int precConvert = 29;
243+
int scaleConvert;
244+
245+
if (!tryMaxPrec && precision != 29)
246+
{
247+
precConvert = 28;
248+
}
249+
scaleConvert = precConvert - (precision - scale);
250+
251+
try
252+
{
253+
return SqlDecimal.ConvertToPrecScale(sqlValue, precConvert, scaleConvert).Value;
254+
}
255+
catch (OverflowException)
256+
{
257+
if (tryMaxPrec && scale < 28)
258+
{
259+
return SqlDecimal.ConvertToPrecScale(sqlValue, precConvert - 1, scaleConvert).Value;
260+
}
261+
else
262+
{
263+
throw;
264+
}
265+
}
266+
}
267+
}
268+
else
269+
{
270+
throw new OverflowException(SQLResource.ConversionOverflowMessage);
271+
}
216272
}
217273
return new decimal(_value._numericInfo._data1, _value._numericInfo._data2, _value._numericInfo._data3, !_value._numericInfo._positive, _value._numericInfo._scale);
218274
}

src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,66 @@ public static void TestParametersWithDatatablesTVPInsert()
319319
}
320320

321321
#region Scaled Decimal Parameter & TVP Test
322+
[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
323+
[InlineData("CAST(7.1234567890123456789012345678 as decimal(38, 35))", "7.1234567890123456789012345678")]
324+
[InlineData("CAST(-7.1234567890123456789012345678 as decimal(38, 35))", "-7.1234567890123456789012345678")]
325+
[InlineData("CAST(-0.1234567890123456789012345678 as decimal(38, 35))", "-0.1234567890123456789012345678")]
326+
[InlineData("CAST(4210862852.86 as decimal(38, 20))", "4210862852.860000000000000000")]
327+
[InlineData("CAST(0 as decimal(38, 36))", "0.0000000000000000000000000000")]
328+
[InlineData("CAST(79228162514264337593543950335 as decimal(38, 9))", "79228162514264337593543950335")]
329+
[InlineData("CAST(-79228162514264337593543950335 as decimal(38, 9))", "-79228162514264337593543950335")]
330+
[InlineData("CAST(0.4210862852 as decimal(38, 38))", "0.4210862852000000000000000000")]
331+
[InlineData("CAST(0.1234567890123456789012345678 as decimal(38, 38))", "0.1234567890123456789012345678")]
332+
[InlineData("CAST(249454727.14678312032280248320 as decimal(38, 20))", "249454727.14678312032280248320")]
333+
[InlineData("CAST(3961408124790879675.7769715711 as decimal(38, 10))", "3961408124790879675.7769715711")]
334+
[InlineData("CAST(3961408124790879675776971571.1 as decimal(38, 1))", "3961408124790879675776971571.1")]
335+
[InlineData("CAST(79228162514264337593543950335 as decimal(38, 0))", "79228162514264337593543950335")]
336+
[InlineData("CAST(-79228162514264337593543950335 as decimal(38, 0))", "-79228162514264337593543950335")]
337+
[InlineData("CAST(0.0000000000000000000000000001 as decimal(38, 38))", "0.0000000000000000000000000001")]
338+
[InlineData("CAST(-0.0000000000000000000000000001 as decimal(38, 38))", "-0.0000000000000000000000000001")]
339+
public static void SqlDecimalConvertToDecimal_TestInRange(string sqlDecimalValue, string expectedDecimalValue)
340+
{
341+
using(SqlConnection cnn = new(s_connString))
342+
{
343+
cnn.Open();
344+
using(SqlCommand cmd = new($"SELECT {sqlDecimalValue} val"))
345+
{
346+
cmd.Connection = cnn;
347+
using (SqlDataReader rdr = cmd.ExecuteReader())
348+
{
349+
Assert.True(rdr.Read(), "SqlDataReader must have a value");
350+
decimal retrunValue = rdr.GetDecimal(0);
351+
Assert.Equal(expectedDecimalValue, retrunValue.ToString());
352+
}
353+
}
354+
}
355+
}
356+
357+
[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))]
358+
[InlineData("CAST(7.9999999999999999999999999999 as decimal(38, 35))")]
359+
[InlineData("CAST(8.1234567890123456789012345678 as decimal(38, 35))")]
360+
[InlineData("CAST(-8.1234567890123456789012345678 as decimal(38, 35))")]
361+
[InlineData("CAST(123456789012345678901234567890 as decimal(38, 0))")]
362+
[InlineData("CAST(7922816251426433759354395.9999 as decimal(38, 8))")]
363+
[InlineData("CAST(-7922816251426433759354395.9999 as decimal(38, 8))")]
364+
[InlineData("CAST(0.123456789012345678901234567890 as decimal(38, 36))")]
365+
public static void SqlDecimalConvertToDecimal_TestOutOfRange(string sqlDecimalValue)
366+
{
367+
using (SqlConnection cnn = new(s_connString))
368+
{
369+
cnn.Open();
370+
using (SqlCommand cmd = new($"SELECT {sqlDecimalValue} val"))
371+
{
372+
cmd.Connection = cnn;
373+
using (SqlDataReader rdr = cmd.ExecuteReader())
374+
{
375+
Assert.True(rdr.Read(), "SqlDataReader must have a value");
376+
Assert.Throws<OverflowException>(() => rdr.GetDecimal(0));
377+
}
378+
}
379+
}
380+
}
381+
322382
[Theory]
323383
[ClassData(typeof(ConnectionStringsProvider))]
324384
public static void TestScaledDecimalParameter_CommandInsert(string connectionString, bool truncateScaledDecimal)

0 commit comments

Comments
 (0)