Skip to content

Commit

Permalink
SqlServer: date constant in DateAdd method is always datetime (#23215)
Browse files Browse the repository at this point in the history
Resolves #17507
  • Loading branch information
smitpatel authored Nov 6, 2020
1 parent dbf26f8 commit 95c9e31
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;

#nullable enable
Expand Down Expand Up @@ -41,16 +42,20 @@ public class SqlServerDateTimeMethodTranslator : IMethodCallTranslator
};

private readonly ISqlExpressionFactory _sqlExpressionFactory;
private readonly IRelationalTypeMappingSource _typeMappingSource;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public SqlServerDateTimeMethodTranslator([NotNull] ISqlExpressionFactory sqlExpressionFactory)
public SqlServerDateTimeMethodTranslator(
[NotNull] ISqlExpressionFactory sqlExpressionFactory,
[NotNull] IRelationalTypeMappingSource typeMappingSource)
{
_sqlExpressionFactory = sqlExpressionFactory;
_typeMappingSource = typeMappingSource;
}

/// <summary>
Expand All @@ -74,24 +79,32 @@ public SqlServerDateTimeMethodTranslator([NotNull] ISqlExpressionFactory sqlExpr
{
// DateAdd does not accept number argument outside of int range
// AddYears/AddMonths take int argument so no need to check for range
return datePart != "year"
if (datePart != "year"
&& datePart != "month"
&& arguments[0] is SqlConstantExpression sqlConstant
&& ((double)sqlConstant.Value >= int.MaxValue
|| (double)sqlConstant.Value <= int.MinValue)
? null
: _sqlExpressionFactory.Function(
"DATEADD",
new[]
{
_sqlExpressionFactory.Fragment(datePart),
_sqlExpressionFactory.Convert(arguments[0], typeof(int)),
instance
},
nullable: true,
argumentsPropagateNullability: new[] { false, true, true },
instance.Type,
instance.TypeMapping);
|| (double)sqlConstant.Value <= int.MinValue))
{
return null;
}

if (instance is SqlConstantExpression instanceConstant)
{
instance = instanceConstant.ApplyTypeMapping(_typeMappingSource.FindMapping(typeof(DateTime), "datetime"));
}

return _sqlExpressionFactory.Function(
"DATEADD",
new[]
{
_sqlExpressionFactory.Fragment(datePart),
_sqlExpressionFactory.Convert(arguments[0], typeof(int)),
instance
},
nullable: true,
argumentsPropagateNullability: new[] { false, true, true },
instance.Type,
instance.TypeMapping);
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public SqlServerMethodCallTranslatorProvider([NotNull] RelationalMethodCallTrans
new SqlServerConvertTranslator(sqlExpressionFactory),
new SqlServerDataLengthFunctionTranslator(sqlExpressionFactory),
new SqlServerDateDiffFunctionsTranslator(sqlExpressionFactory),
new SqlServerDateTimeMethodTranslator(sqlExpressionFactory),
new SqlServerDateTimeMethodTranslator(sqlExpressionFactory, typeMappingSource),
new SqlServerFromPartsFunctionTranslator(sqlExpressionFactory, typeMappingSource),
new SqlServerFullTextSearchFunctionsTranslator(sqlExpressionFactory),
new SqlServerIsDateFunctionTranslator(sqlExpressionFactory),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2580,6 +2580,17 @@ FROM root c
WHERE ((c[""Discriminator""] = ""Order"") AND (c[""OrderDate""] != null))");
}

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

AssertSql(
@"SELECT VALUE {""c"" : (c[""OrderID""] % 25)}
FROM root c
WHERE ((c[""Discriminator""] = ""Order"") AND (c[""OrderID""] < 10500))
ORDER BY c[""OrderID""]");
}

[ConditionalTheory(Skip = "Issue #17246")]
public override async Task Select_expression_references_are_updated_correctly_with_subquery(bool async)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4194,6 +4194,19 @@ public virtual Task Select_expression_date_add_milliseconds_large_number_divided
e => e.OrderDate);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Add_minutes_on_constant_value(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Order>().Where(c => c.OrderID < 10500)
.OrderBy(o => o.OrderID)
.Select(o => new { Test = new DateTime(1900, 1, 1).AddMinutes(o.OrderID % 25) }),
assertOrder: true,
elementAsserter: (e, a) => AssertEqual(e.Test, a.Test));
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Select_expression_references_are_updated_correctly_with_subquery(bool async)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3229,6 +3229,17 @@ FROM [Orders] AS [o]
WHERE [o].[OrderDate] IS NOT NULL");
}

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

AssertSql(
@"SELECT DATEADD(minute, CAST(CAST(([o].[OrderID] % 25) AS float) AS int), '1900-01-01T00:00:00.000') AS [Test]
FROM [Orders] AS [o]
WHERE [o].[OrderID] < 10500
ORDER BY [o].[OrderID]");
}

public override async Task Select_expression_references_are_updated_correctly_with_subquery(bool async)
{
await base.Select_expression_references_are_updated_correctly_with_subquery(async);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,17 @@ SELECT rtrim(rtrim(strftime('%Y-%m-%d %H:%M:%f', ""o"".""OrderDate"", COALESCE(C
WHERE ""o"".""OrderDate"" IS NOT NULL");
}

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

AssertSql(
@"SELECT rtrim(rtrim(strftime('%Y-%m-%d %H:%M:%f', '1900-01-01 00:00:00', CAST(CAST((""o"".""OrderID"" % 25) AS REAL) AS TEXT) || ' minutes'), '0'), '.') AS ""Test""
FROM ""Orders"" AS ""o""
WHERE ""o"".""OrderID"" < 10500
ORDER BY ""o"".""OrderID""");
}

public override async Task Select_distinct_long_count(bool async)
{
await base.Select_distinct_long_count(async);
Expand Down

0 comments on commit 95c9e31

Please sign in to comment.