Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query: Assign correct type/typemapping to result of Round on SqlServer #28068

Merged
1 commit merged into from
May 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ static void UpdateLimit(SelectExpression selectExpression)

var innerExpression = RemoveConvert(innerShaperExpression);
if (!(innerExpression is EntityShaperExpression
|| innerExpression is IncludeExpression))
|| innerExpression is IncludeExpression))
{
var sentinelExpression = innerSelectExpression.Limit!;
var sentinelNullableType = sentinelExpression.Type.MakeNullable();
Expand Down
22 changes: 18 additions & 4 deletions src/EFCore.SqlServer/Query/Internal/SqlServerMathTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,20 @@ public SqlServerMathTranslator(ISqlExpressionFactory sqlExpressionFactory)
if (TruncateMethodInfos.Contains(method))
{
var argument = arguments[0];
// Result of ROUND for float/double is always double in server side
// C# has Round over decimal/double/float only so our argument will be one of those types (compiler puts convert node)
// In database result will be same type except for float which returns double which we need to cast back to float.
var resultType = argument.Type;
if (resultType == typeof(float))
{
resultType = typeof(double);
}

var result = (SqlExpression)_sqlExpressionFactory.Function(
"ROUND",
new[] { argument, _sqlExpressionFactory.Constant(0), _sqlExpressionFactory.Constant(1) },
nullable: true,
argumentsPropagateNullability: new[] { true, false, false },
typeof(double));
resultType);

if (argument.Type == typeof(float))
{
Expand All @@ -154,13 +161,20 @@ public SqlServerMathTranslator(ISqlExpressionFactory sqlExpressionFactory)
{
var argument = arguments[0];
var digits = arguments.Count == 2 ? arguments[1] : _sqlExpressionFactory.Constant(0);
// Result of ROUND for float/double is always double in server side
// C# has Round over decimal/double/float only so our argument will be one of those types (compiler puts convert node)
// In database result will be same type except for float which returns double which we need to cast back to float.
var resultType = argument.Type;
if (resultType == typeof(float))
{
resultType = typeof(double);
}

var result = (SqlExpression)_sqlExpressionFactory.Function(
"ROUND",
new[] { argument, digits },
nullable: true,
argumentsPropagateNullability: new[] { true, true },
typeof(double));
resultType);

if (argument.Type == typeof(float))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,18 @@ FROM root c
WHERE (((c[""Discriminator""] = ""OrderDetail"") AND (c[""Quantity""] < 5)) AND (ROUND(c[""UnitPrice""]) > 10.0))");
}

public override Task Sum_over_round_works_correctly_in_projection(bool async)
=> AssertTranslationFailed(() => base.Sum_over_round_works_correctly_in_projection(async));

public override Task Sum_over_round_works_correctly_in_projection_2(bool async)
=> AssertTranslationFailed(() => base.Sum_over_round_works_correctly_in_projection_2(async));

public override Task Sum_over_truncate_works_correctly_in_projection(bool async)
=> AssertTranslationFailed(() => base.Sum_over_truncate_works_correctly_in_projection(async));

public override Task Sum_over_truncate_works_correctly_in_projection_2(bool async)
=> AssertTranslationFailed(() => base.Sum_over_truncate_works_correctly_in_projection_2(async));

public override async Task Select_math_round_int(bool async)
{
await base.Select_math_round_int(async);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,82 @@ public virtual Task Where_math_round(bool async)
.Where(od => Math.Round(od.UnitPrice) > 10),
entryCount: 137);

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Sum_over_round_works_correctly_in_projection(bool async)
=> AssertQuery(
async,
ss => ss.Set<Order>()
.Where(o => o.OrderID < 10300)
.Select(o => new
{
o.OrderID,
Sum = o.OrderDetails.Sum(i => Math.Round(i.UnitPrice, 2))
}),
elementSorter: e => e.OrderID,
elementAsserter: (e, a) =>
{
Assert.Equal(e.OrderID, a.OrderID);
Assert.Equal(e.Sum, a.Sum);
});

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Sum_over_round_works_correctly_in_projection_2(bool async)
=> AssertQuery(
async,
ss => ss.Set<Order>()
.Where(o => o.OrderID < 10300)
.Select(o => new
{
o.OrderID,
Sum = o.OrderDetails.Select(i => i.UnitPrice * i.UnitPrice).Sum(i => Math.Round(i, 2))
}),
elementSorter: e => e.OrderID,
elementAsserter: (e, a) =>
{
Assert.Equal(e.OrderID, a.OrderID);
Assert.Equal(e.Sum, a.Sum);
});

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Sum_over_truncate_works_correctly_in_projection(bool async)
=> AssertQuery(
async,
ss => ss.Set<Order>()
.Where(o => o.OrderID < 10300)
.Select(o => new
{
o.OrderID,
Sum = o.OrderDetails.Sum(i => Math.Truncate(i.UnitPrice))
}),
elementSorter: e => e.OrderID,
elementAsserter: (e, a) =>
{
Assert.Equal(e.OrderID, a.OrderID);
Assert.Equal(e.Sum, a.Sum);
});

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Sum_over_truncate_works_correctly_in_projection_2(bool async)
=> AssertQuery(
async,
ss => ss.Set<Order>()
.Where(o => o.OrderID < 10300)
.Select(o => new
{
o.OrderID,
Sum = o.OrderDetails.Select(i => i.UnitPrice * i.UnitPrice).Sum(i => Math.Truncate(i))
}),
elementSorter: e => e.OrderID,
elementAsserter: (e, a) =>
{
Assert.Equal(e.OrderID, a.OrderID);
Assert.Equal(e.Sum, a.Sum);
});

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Select_math_round_int(bool async)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,58 @@ FROM [Order Details] AS [o]
WHERE [o].[Quantity] < CAST(5 AS smallint) AND ROUND([o].[UnitPrice], 0) > 10.0");
}

public override async Task Sum_over_round_works_correctly_in_projection(bool async)
{
await base.Sum_over_round_works_correctly_in_projection(async);

AssertSql(
@"SELECT [o].[OrderID], (
SELECT COALESCE(SUM(ROUND([o0].[UnitPrice], 2)), 0.0)
FROM [Order Details] AS [o0]
WHERE [o].[OrderID] = [o0].[OrderID]) AS [Sum]
FROM [Orders] AS [o]
WHERE [o].[OrderID] < 10300");
}

public override async Task Sum_over_round_works_correctly_in_projection_2(bool async)
{
await base.Sum_over_round_works_correctly_in_projection_2(async);

AssertSql(
@"SELECT [o].[OrderID], (
SELECT COALESCE(SUM(ROUND([o0].[UnitPrice] * [o0].[UnitPrice], 2)), 0.0)
FROM [Order Details] AS [o0]
WHERE [o].[OrderID] = [o0].[OrderID]) AS [Sum]
FROM [Orders] AS [o]
WHERE [o].[OrderID] < 10300");
}

public override async Task Sum_over_truncate_works_correctly_in_projection(bool async)
{
await base.Sum_over_truncate_works_correctly_in_projection(async);

AssertSql(
@"SELECT [o].[OrderID], (
SELECT COALESCE(SUM(ROUND([o0].[UnitPrice], 0, 1)), 0.0)
FROM [Order Details] AS [o0]
WHERE [o].[OrderID] = [o0].[OrderID]) AS [Sum]
FROM [Orders] AS [o]
WHERE [o].[OrderID] < 10300");
}

public override async Task Sum_over_truncate_works_correctly_in_projection_2(bool async)
{
await base.Sum_over_truncate_works_correctly_in_projection_2(async);

AssertSql(
@"SELECT [o].[OrderID], (
SELECT COALESCE(SUM(ROUND([o0].[UnitPrice] * [o0].[UnitPrice], 0, 1)), 0.0)
FROM [Order Details] AS [o0]
WHERE [o].[OrderID] = [o0].[OrderID]) AS [Sum]
FROM [Orders] AS [o]
WHERE [o].[OrderID] < 10300");
}

public override async Task Select_math_round_int(bool async)
{
await base.Select_math_round_int(async);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ public override Task Where_math_square(bool async)
public override Task Where_math_round(bool async)
=> AssertTranslationFailed(() => base.Where_math_round(async));

public override Task Sum_over_round_works_correctly_in_projection(bool async)
=> AssertTranslationFailed(() => base.Sum_over_round_works_correctly_in_projection(async));

public override Task Sum_over_round_works_correctly_in_projection_2(bool async)
=> AssertTranslationFailed(() => base.Sum_over_round_works_correctly_in_projection_2(async));

public override Task Sum_over_truncate_works_correctly_in_projection(bool async)
=> AssertTranslationFailed(() => base.Sum_over_truncate_works_correctly_in_projection(async));

public override Task Sum_over_truncate_works_correctly_in_projection_2(bool async)
=> AssertTranslationFailed(() => base.Sum_over_truncate_works_correctly_in_projection_2(async));

public override Task Where_math_round2(bool async)
=> AssertTranslationFailed(() => base.Where_math_round2(async));

Expand Down