Skip to content

Commit

Permalink
Query: Read Avg/Min/Max as nullable from database to throw exception … (
Browse files Browse the repository at this point in the history
#18971)

* Query: Read Avg/Min/Max as nullable from database to throw exception only when it is empty

Resolves #18955

* Update FloatTypeMapping.cs

PR Feedback
  • Loading branch information
smitpatel authored and wtgodbe committed Nov 26, 2019
1 parent 951aca3 commit 2da12e7
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -779,43 +779,33 @@ private ShapedQueryExpression AggregateResultShaper(

selectExpression.ClearOrdering();

Expression shaper = new ProjectionBindingExpression(source.QueryExpression, new ProjectionMember(), projection.Type);
var nullableResultType = resultType.MakeNullable();
Expression shaper = new ProjectionBindingExpression(
source.QueryExpression, new ProjectionMember(), throwOnNullResult ? nullableResultType : projection.Type);

if (throwOnNullResult
&& resultType.IsNullableType())
if (throwOnNullResult)
{
var resultVariable = Expression.Variable(projection.Type, "result");
var resultVariable = Expression.Variable(nullableResultType, "result");
var returnValueForNull = resultType.IsNullableType()
? (Expression)Expression.Constant(null, resultType)
: Expression.Throw(
Expression.New(
typeof(InvalidOperationException).GetConstructors()
.Single(ci => ci.GetParameters().Length == 1),
Expression.Constant(CoreStrings.NoElements)),
resultType);

shaper = Expression.Block(
new[] { resultVariable },
Expression.Assign(resultVariable, shaper),
Expression.Condition(
Expression.Equal(resultVariable, Expression.Default(projection.Type)),
Expression.Constant(null, resultType),
Expression.Equal(resultVariable, Expression.Default(nullableResultType)),
returnValueForNull,
resultType != resultVariable.Type
? Expression.Convert(resultVariable, resultType)
: (Expression)resultVariable));
}
else if (throwOnNullResult)
{
var resultVariable = Expression.Variable(projection.Type, "result");

shaper = Expression.Block(
new[] { resultVariable },
Expression.Assign(resultVariable, shaper),
Expression.Condition(
Expression.Equal(resultVariable, Expression.Default(projection.Type)),
Expression.Throw(
Expression.New(
typeof(InvalidOperationException).GetConstructors()
.Single(ci => ci.GetParameters().Length == 1),
Expression.Constant(CoreStrings.NoElements)),
resultType),
resultType != resultVariable.Type
? Expression.Convert(resultVariable, resultType)
: (Expression)resultVariable));
}
else if (resultType.IsNullableType())
else if (resultType != shaper.Type)
{
shaper = Expression.Convert(shaper, resultType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1143,11 +1143,13 @@ private ShapedQueryExpression AggregateResultShaper(

selectExpression.ClearOrdering();

Expression shaper = new ProjectionBindingExpression(source.QueryExpression, new ProjectionMember(), projection.Type);
var nullableResultType = resultType.MakeNullable();
Expression shaper = new ProjectionBindingExpression(
source.QueryExpression, new ProjectionMember(), throwOnNullResult ? nullableResultType : projection.Type);

if (throwOnNullResult)
{
var resultVariable = Expression.Variable(projection.Type, "result");
var resultVariable = Expression.Variable(nullableResultType, "result");
var returnValueForNull = resultType.IsNullableType()
? (Expression)Expression.Constant(null, resultType)
: Expression.Throw(
Expand All @@ -1161,18 +1163,15 @@ private ShapedQueryExpression AggregateResultShaper(
new[] { resultVariable },
Expression.Assign(resultVariable, shaper),
Expression.Condition(
Expression.Equal(resultVariable, Expression.Default(projection.Type)),
Expression.Equal(resultVariable, Expression.Default(nullableResultType)),
returnValueForNull,
resultType != resultVariable.Type
? Expression.Convert(resultVariable, resultType)
: (Expression)resultVariable));
}
else
else if (resultType != shaper.Type)
{
if (resultType.IsNullableType())
{
shaper = Expression.Convert(shaper, resultType);
}
shaper = Expression.Convert(shaper, resultType);
}

source.ShaperExpression = shaper;
Expand Down
3 changes: 2 additions & 1 deletion src/EFCore.Relational/Storage/FloatTypeMapping.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Data;
using System.Globalization;
using JetBrains.Annotations;
Expand Down Expand Up @@ -55,6 +56,6 @@ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters p
/// The generated string.
/// </returns>
protected override string GenerateNonNullSqlLiteral(object value)
=> ((float)value).ToString("R", CultureInfo.InvariantCulture);
=> Convert.ToSingle(value).ToString("R", CultureInfo.InvariantCulture);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Data;
using System.Data.Common;
using JetBrains.Annotations;
Expand Down Expand Up @@ -58,7 +59,7 @@ protected override string GenerateNonNullSqlLiteral(object value)
{
var literal = base.GenerateNonNullSqlLiteral(value);

var doubleValue = (double)value;
var doubleValue = Convert.ToDouble(value);
return !literal.Contains("E")
&& !literal.Contains("e")
&& !double.IsNaN(doubleValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1885,9 +1885,46 @@ await AssertCount(
ss => ss.Set<Order>().Select(o => new { Id = CodeFormat(o.OrderID) }));
}

private static string CodeFormat(int str)
private static string CodeFormat(int str) => str.ToString();

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Sum_over_empty_returns_zero(bool isAsync)
{
return AssertSum(
isAsync,
ss => ss.Set<Order>().Where(o => o.OrderID == 42),
o => o.OrderID);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Average_over_default_returns_default(bool isAsync)
{
return AssertAverage(
isAsync,
ss => ss.Set<Order>().Where(o => o.OrderID == 10248),
o => o.OrderID - 10248);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Max_over_default_returns_default(bool isAsync)
{
return str.ToString();
return AssertMax(
isAsync,
ss => ss.Set<Order>().Where(o => o.OrderID == 10248),
o => o.OrderID - 10248);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Min_over_default_returns_default(bool isAsync)
{
return AssertMin(
isAsync,
ss => ss.Set<Order>().Where(o => o.OrderID == 10248),
o => o.OrderID - 10248);
}
}
}

0 comments on commit 2da12e7

Please sign in to comment.