From 5c378803a65c67b0e3df0ca415ce504304044d17 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Thu, 2 Jan 2025 17:27:19 -0800 Subject: [PATCH 1/2] Add `TranslationIssue_IndexOutOfRange` test to reproduce translation bug --- .../TranslationIssue_IndexOutOfRange.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Orm/Xtensive.Orm.Tests/Issues/TranslationIssue_IndexOutOfRange.cs diff --git a/Orm/Xtensive.Orm.Tests/Issues/TranslationIssue_IndexOutOfRange.cs b/Orm/Xtensive.Orm.Tests/Issues/TranslationIssue_IndexOutOfRange.cs new file mode 100644 index 000000000..a7c6845cc --- /dev/null +++ b/Orm/Xtensive.Orm.Tests/Issues/TranslationIssue_IndexOutOfRange.cs @@ -0,0 +1,52 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using NUnit.Framework; +using Xtensive.Orm.Configuration; +using Xtensive.Orm.Tests.Issues.IssueJira0440_CustomCompilerLoosesImplicitCastToNullableModel; + +namespace Xtensive.Orm.Tests.Issues +{ + namespace TranslationIssue_IndexOutOfRangeModel + { + [HierarchyRoot] + public class TestEntity1 : Entity + { + [Key, Field] + public long Id { get; private set; } + + [Field] + public decimal Split { get; set; } + } + + } + + [TestFixture] + public class TranslationIssue_IndexOutOfRange : AutoBuildTest + { + protected override DomainConfiguration BuildConfiguration() + { + var configuration = base.BuildConfiguration(); + configuration.Types.Register(typeof(TranslationIssue_IndexOutOfRangeModel.TestEntity1)); + return configuration; + } + + [Test] + public void MainTest() + { + using var session = Domain.OpenSession(); + using var tx = session.OpenTransaction(); + + _ = ( + from e in session.Query.All() + group new { + Split = e.Split * 0.01M + } by e.Id into g + select g + .Select(x => x.Split) + .Distinct() + .Sum() + ).ToList(); + } + } +} From 5079c7ee92990c69871c726f4889643bdd443345 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Thu, 2 Jan 2025 17:36:57 -0800 Subject: [PATCH 2/2] Revert "Guess possible decimal parameters by columns, used in aggregated expression" This reverts commit 3a6660c528b33d105396b4d1fdfef0ed1ac7b71d. --- .../Orm/Linq/Translator.Queryable.cs | 63 +------------------ 1 file changed, 2 insertions(+), 61 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs b/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs index af312dd94..8e1c3c636 100644 --- a/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs +++ b/Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs @@ -771,21 +771,11 @@ private Expression VisitAggregate(Expression source, MethodInfo method, LambdaEx MethodCallExpression expressionPart) { var aggregateType = ExtractAggregateType(expressionPart); - var origin = VisitAggregateSource(source, argument, aggregateType, expressionPart); var originProjection = origin.Item1; var originColumnIndex = origin.Item2; - - var headerColumns = originProjection.ItemProjector.DataSource.Header.Columns; - var aggregatedColumn = headerColumns[originColumnIndex]; - - // For decimal type we try to guess result precision and scale to avoid - // usage of general values which can create some issues result reading - (sbyte precision, sbyte scale)? aggregateTypeHints = TryGuessDecimalPrecisionAndSclale(aggregatedColumn, headerColumns, context.Model); - - var aggregateDescriptor = aggregateTypeHints.HasValue - ? new AggregateColumnDescriptor(context.GetNextColumnAlias(), originColumnIndex, aggregateType, aggregateTypeHints.Value) - : new AggregateColumnDescriptor(context.GetNextColumnAlias(), originColumnIndex, aggregateType); + var aggregateDescriptor = new AggregateColumnDescriptor( + context.GetNextColumnAlias(), originColumnIndex, aggregateType); var originDataSource = originProjection.ItemProjector.DataSource; var resultDataSource = originDataSource.Aggregate(null, [aggregateDescriptor]); @@ -867,55 +857,6 @@ private Expression VisitAggregate(Expression source, MethodInfo method, LambdaEx return Expression.Convert(result, resultType); } return result; - - - static (sbyte, sbyte)? TryGuessDecimalPrecisionAndSclale(Column aggregatedColumn, Rse.ColumnCollection headerColumns, Orm.Model.DomainModel domainModel) - { - if (aggregatedColumn.Type != WellKnownTypes.Decimal) - return null; - - if (aggregatedColumn is MappedColumn mColumn) { - var resolvedColumn = mColumn.ColumnInfoRef.Resolve(domainModel); - if (resolvedColumn.Precision.HasValue && resolvedColumn.Scale.HasValue) - return (resolvedColumn.Precision.Value, resolvedColumn.Scale.Value); - } - else if (aggregatedColumn is CalculatedColumn cColumn) { - var expression = cColumn.Expression; - var usedColumns = new Rse.Transformation.TupleAccessGatherer().Gather(expression); - - sbyte maxFloorDigits = -1; - sbyte maxScaleDigits = -1; - foreach (var cIndex in usedColumns.Distinct()) { - var usedColumn = headerColumns[cIndex]; - if (usedColumn is MappedColumn mmColumn) { - var resolvedColumn = mmColumn.ColumnInfoRef.Resolve(domainModel); - - (sbyte? p, sbyte? s) @params = Type.GetTypeCode(resolvedColumn.ValueType) switch { - TypeCode.Decimal => (resolvedColumn.Precision, resolvedColumn.Scale), - TypeCode.Int32 or TypeCode.UInt32 => (19, 8), - TypeCode.Int64 or TypeCode.UInt64 => (28, 8), - TypeCode.Byte or TypeCode.SByte => (8, 5), - TypeCode.Int16 or TypeCode.UInt16 => (10, 5), - _ => (null, null), - }; - - if (@params.p.HasValue && @params.s.HasValue) { - if (maxScaleDigits < @params.s.Value) - maxScaleDigits = @params.s.Value; - sbyte floorDigits = (sbyte)(@params.p.Value - @params.s.Value); - if (maxFloorDigits < floorDigits) - maxFloorDigits = floorDigits; - } - } - } - if (maxFloorDigits == -1 || maxScaleDigits == -1) - return null; - if (maxFloorDigits + maxScaleDigits <= 28) - return ((sbyte)(maxFloorDigits + maxScaleDigits), maxScaleDigits); - } - - return null; - } } private CompilableProvider ChooseSourceForAggregate(CompilableProvider left, CompilableProvider right,