|
2 | 2 | using System.Data; |
3 | 3 | using System.Data.Common; |
4 | 4 | using System.Diagnostics.CodeAnalysis; |
| 5 | +using System.Globalization; |
| 6 | +using System.Numerics; |
5 | 7 | using Ydb.Sdk.Ado.YdbType; |
6 | 8 | using Ydb.Sdk.Value; |
7 | 9 |
|
@@ -270,10 +272,55 @@ internal TypedValue TypedValue |
270 | 272 | $"Writing value of '{value.GetType()}' is not supported without explicit mapping to the YdbDbType") |
271 | 273 | }; |
272 | 274 |
|
273 | | - private TypedValue Decimal(decimal value) => |
274 | | - Precision == 0 && Scale == 0 |
275 | | - ? value.Decimal(22, 9) |
276 | | - : value.Decimal(Precision, Scale); |
| 275 | + private TypedValue Decimal(decimal value) |
| 276 | + { |
| 277 | + var p = (Precision == 0 && Scale == 0) ? 22 : Precision; |
| 278 | + var s = (Precision == 0 && Scale == 0) ? 9 : Scale; |
| 279 | + |
| 280 | + EnsureDecimalFits(value, p, s); |
| 281 | + |
| 282 | + return value.Decimal((byte)p, (byte)s); |
| 283 | + } |
| 284 | + |
| 285 | + private static void EnsureDecimalFits(decimal value, int precision, int scale) |
| 286 | + { |
| 287 | + if (precision <= 0 || scale < 0 || scale > precision) |
| 288 | + throw new ArgumentOutOfRangeException( |
| 289 | + $"Invalid DECIMAL({precision},{scale}) — must have precision>0 and 0<=scale<=precision."); |
| 290 | + |
| 291 | + GetTrimmedMantissaAndScale(value, out BigInteger mantissa, out int fracDigits /* = scale' */); |
| 292 | + |
| 293 | + var totalDigits = mantissa.IsZero ? 1 : mantissa.ToString(CultureInfo.InvariantCulture).Length; |
| 294 | + |
| 295 | + var intDigits = Math.Max(1, totalDigits - fracDigits); |
| 296 | + |
| 297 | + if (fracDigits > scale) |
| 298 | + throw new OverflowException( |
| 299 | + $"Decimal scale overflow: fractional digits {fracDigits} exceed allowed {scale} for DECIMAL({precision},{scale}). Value={value}"); |
| 300 | + |
| 301 | + if (intDigits > (precision - scale)) |
| 302 | + throw new OverflowException( |
| 303 | + $"Decimal precision overflow: integer digits {intDigits} exceed allowed {precision - scale} for DECIMAL({precision},{scale}). Value={value}"); |
| 304 | + } |
| 305 | + |
| 306 | + private static void GetTrimmedMantissaAndScale(decimal value, out BigInteger mantissa, out int scale) |
| 307 | + { |
| 308 | + var bits = decimal.GetBits(value); |
| 309 | + scale = (bits[3] >> 16) & 0xFF; |
| 310 | + |
| 311 | + var lo = (uint)bits[0]; |
| 312 | + var mid = (uint)bits[1]; |
| 313 | + var hi = (uint)bits[2]; |
| 314 | + |
| 315 | + mantissa = ((BigInteger)hi << 64) | ((BigInteger)mid << 32) | lo; |
| 316 | + mantissa = BigInteger.Abs(mantissa); |
| 317 | + |
| 318 | + while (scale > 0 && !mantissa.IsZero && mantissa % 10 == 0) |
| 319 | + { |
| 320 | + mantissa /= 10; |
| 321 | + scale--; |
| 322 | + } |
| 323 | + } |
277 | 324 |
|
278 | 325 | private TypedValue NullTypedValue() |
279 | 326 | { |
|
0 commit comments