Skip to content

Commit 3a6660c

Browse files
committed
Guess possible decimal parameters by columns, used in aggregated expression
1 parent b2ba708 commit 3a6660c

1 file changed

Lines changed: 76 additions & 2 deletions

File tree

Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -710,11 +710,23 @@ private Expression VisitAggregate(Expression source, MethodInfo method, LambdaEx
710710
MethodCallExpression expressionPart)
711711
{
712712
var aggregateType = ExtractAggregateType(expressionPart);
713+
713714
var origin = VisitAggregateSource(source, argument, aggregateType, expressionPart);
714715
var originProjection = origin.First;
715716
var originColumnIndex = origin.Second;
716-
var aggregateDescriptor = new AggregateColumnDescriptor(
717-
context.GetNextColumnAlias(), originColumnIndex, aggregateType);
717+
718+
// experiments
719+
720+
var headerColumns = originProjection.ItemProjector.DataSource.Header.Columns;
721+
var aggregatedColumn = headerColumns[originColumnIndex];
722+
723+
// For decimal type we try to guess result precision and scale to avoid
724+
// usage of general values which can create some issues result reading
725+
(int precision, int scale)? aggregateTypeHints = TryGuessDecimalPrecisionAndSclale(aggregatedColumn, headerColumns, context.Model);
726+
727+
var aggregateDescriptor = aggregateTypeHints.HasValue
728+
? new AggregateColumnDescriptor(context.GetNextColumnAlias(), originColumnIndex, aggregateType, aggregateTypeHints.Value)
729+
: new AggregateColumnDescriptor(context.GetNextColumnAlias(), originColumnIndex, aggregateType);
718730
var originDataSource = originProjection.ItemProjector.DataSource;
719731
var resultDataSource = originDataSource.Aggregate(null, aggregateDescriptor);
720732

@@ -796,6 +808,68 @@ private Expression VisitAggregate(Expression source, MethodInfo method, LambdaEx
796808
return Expression.Convert(result, resultType);
797809
}
798810
return result;
811+
812+
813+
static (int, int)? TryGuessDecimalPrecisionAndSclale(Column aggregatedColumn, Rse.ColumnCollection headerColumns, Orm.Model.DomainModel domainModel)
814+
{
815+
if (aggregatedColumn.Type != WellKnownTypes.Decimal)
816+
return null;
817+
818+
if (aggregatedColumn is MappedColumn mColumn) {
819+
var resolvedColumn = mColumn.ColumnInfoRef.Resolve(domainModel);
820+
if (resolvedColumn.Precision.HasValue && resolvedColumn.Scale.HasValue)
821+
return (resolvedColumn.Precision.Value, resolvedColumn.Scale.Value);
822+
}
823+
else if (aggregatedColumn is CalculatedColumn cColumn) {
824+
var expression = cColumn.Expression;
825+
var usedColumns = new Rse.Transformation.TupleAccessGatherer().Gather(expression);
826+
827+
var maxFloorDigits = 0;
828+
var maxScaleDigits = 0;
829+
foreach (var cIndex in usedColumns) {
830+
var usedColumn = headerColumns[cIndex];
831+
if (usedColumn is MappedColumn mmColumn) {
832+
var resolvedColumn = mmColumn.ColumnInfoRef.Resolve(domainModel);
833+
var precision = resolvedColumn.Precision;
834+
var scale = resolvedColumn.Scale;
835+
if (resolvedColumn.ValueType != WellKnownTypes.Decimal) {
836+
if (!precision.HasValue) {
837+
precision = Type.GetTypeCode(resolvedColumn.ValueType) switch {
838+
TypeCode.Byte or TypeCode.SByte => 8,
839+
TypeCode.UInt16 or TypeCode.Int16=> 10,
840+
TypeCode.UInt32 => 18,
841+
TypeCode.UInt64 or TypeCode.Int64 => 28,
842+
TypeCode.Int32 => 19,
843+
_ => null
844+
};
845+
}
846+
847+
if (!scale.HasValue) {
848+
scale = Type.GetTypeCode(resolvedColumn.ValueType) switch {
849+
TypeCode.Byte or TypeCode.SByte or TypeCode.UInt16 or TypeCode.Int16 => 5,
850+
TypeCode.UInt32 or TypeCode.Int32 or TypeCode.UInt64 or TypeCode.Int64 => 8,
851+
_ => null
852+
};
853+
}
854+
}
855+
856+
if (precision.HasValue && scale.HasValue) {
857+
if (maxScaleDigits < scale.Value)
858+
maxScaleDigits = scale.Value;
859+
var floorDigits = precision.Value - scale.Value;
860+
if (maxFloorDigits < floorDigits)
861+
maxFloorDigits = floorDigits;
862+
}
863+
}
864+
}
865+
if (maxFloorDigits == 0 && maxScaleDigits == 0)
866+
return null;
867+
if (maxFloorDigits + maxScaleDigits <= 28)
868+
return (maxFloorDigits + maxScaleDigits, maxScaleDigits);
869+
}
870+
871+
return null;
872+
}
799873
}
800874

801875
private CompilableProvider ChooseSourceForAggregate(CompilableProvider left, CompilableProvider right,

0 commit comments

Comments
 (0)