diff --git a/src/EFCore.Ydb/src/Query/Internal/Translators/YdbQueryableAggregateMethodTranslator.cs b/src/EFCore.Ydb/src/Query/Internal/Translators/YdbQueryableAggregateMethodTranslator.cs index 75e78927..efe895ed 100644 --- a/src/EFCore.Ydb/src/Query/Internal/Translators/YdbQueryableAggregateMethodTranslator.cs +++ b/src/EFCore.Ydb/src/Query/Internal/Translators/YdbQueryableAggregateMethodTranslator.cs @@ -133,7 +133,7 @@ public class YdbQueryableAggregateMethodTranslator( [sumSqlExpression], nullable: true, argumentsPropagateNullability: ArrayUtil.FalseArrays[1], - typeof(decimal)), + typeof(long)), sumInputType, sumSqlExpression.TypeMapping); } diff --git a/src/EFCore.Ydb/src/Query/Internal/YdbSqlExpressionFactory.cs b/src/EFCore.Ydb/src/Query/Internal/YdbSqlExpressionFactory.cs index bdf3eb3c..cac78a38 100644 --- a/src/EFCore.Ydb/src/Query/Internal/YdbSqlExpressionFactory.cs +++ b/src/EFCore.Ydb/src/Query/Internal/YdbSqlExpressionFactory.cs @@ -1,4 +1,7 @@ +using System; +using System.Data; using System.Diagnostics.CodeAnalysis; +using EntityFrameworkCore.Ydb.Storage.Internal.Mapping; using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Storage; @@ -10,4 +13,33 @@ public class YdbSqlExpressionFactory(SqlExpressionFactoryDependencies dependenci [return: NotNullIfNotNull("sqlExpression")] public override SqlExpression? ApplyTypeMapping(SqlExpression? sqlExpression, RelationalTypeMapping? typeMapping) => base.ApplyTypeMapping(sqlExpression, typeMapping); + + public override SqlExpression Coalesce(SqlExpression left, SqlExpression right, + RelationalTypeMapping? typeMapping = null) + { + // For .Sum(x => x.Decimal) EF generates coalesce(sum(x.Decimal), 0.0)) because SUM must have value + + if (left is SqlFunctionExpression funcExpression + && + right is SqlConstantExpression constExpression && constExpression.TypeMapping != null + && + funcExpression.Name.Equals("SUM", StringComparison.OrdinalIgnoreCase) + && + funcExpression.Arguments != null + && + constExpression.TypeMapping.DbType == DbType.Decimal + && + constExpression.Value != null) + { + // get column expression for SUM function expression + var columnExpression = funcExpression.Arguments[0] as ColumnExpression; + + var correctRight = new SqlConstantExpression(constExpression.Value, + YdbDecimalTypeMapping.GetWithMaxPrecision(columnExpression?.TypeMapping?.Scale)); + + return base.Coalesce(left, correctRight, typeMapping); + } + + return base.Coalesce(left, right, typeMapping); + } } diff --git a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDecimalTypeMapping.cs b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDecimalTypeMapping.cs index 2b0d6b2b..bed36dc0 100644 --- a/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDecimalTypeMapping.cs +++ b/src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbDecimalTypeMapping.cs @@ -8,8 +8,20 @@ public class YdbDecimalTypeMapping : DecimalTypeMapping private const byte DefaultPrecision = 22; private const byte DefaultScale = 9; + private const byte MaxPrecision = 35; + public new static YdbDecimalTypeMapping Default => new(); + public static YdbDecimalTypeMapping GetWithMaxPrecision(int? scale) => + new(new RelationalTypeMappingParameters( + new CoreTypeMappingParameters( + typeof(decimal)), + storeType: "Decimal", + dbType: System.Data.DbType.Decimal, + precision: MaxPrecision, + scale: scale ?? DefaultScale) + ); + public YdbDecimalTypeMapping() : this( new RelationalTypeMappingParameters( new CoreTypeMappingParameters(typeof(decimal)), @@ -41,4 +53,7 @@ protected override void ConfigureParameter(DbParameter parameter) if (Scale is { } s) parameter.Scale = (byte)s; } + + protected override string GenerateNonNullSqlLiteral(object value) => + $"Decimal('{base.GenerateNonNullSqlLiteral(value)}', {Precision ?? DefaultPrecision}, {Scale ?? DefaultScale})"; }