Skip to content

Commit

Permalink
Translate TimeOnly.FromDateTime and TimeOnly.FromTimeSpan (#259)
Browse files Browse the repository at this point in the history
* Translate TimeOnly.FromDateTime and TimeOnly.FromTimeSpan
  • Loading branch information
ChrisJollyAU authored Aug 28, 2024
1 parent f30c726 commit 20c42de
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public class JetTimeOnlyMethodTranslator : IMethodCallTranslator
nameof(TimeOnly.AddMinutes), new[] { typeof(double) })!;
private static readonly MethodInfo IsBetweenMethod = typeof(TimeOnly).GetRuntimeMethod(
nameof(TimeOnly.IsBetween), new[] { typeof(TimeOnly), typeof(TimeOnly) })!;
private static readonly MethodInfo FromDateTime = typeof(TimeOnly).GetRuntimeMethod(
nameof(TimeOnly.FromDateTime), [typeof(DateTime)])!;

private static readonly MethodInfo FromTimeSpan = typeof(TimeOnly).GetRuntimeMethod(
nameof(TimeOnly.FromTimeSpan), [typeof(TimeSpan)])!;

private readonly ISqlExpressionFactory _sqlExpressionFactory;

Expand All @@ -52,7 +57,19 @@ public JetTimeOnlyMethodTranslator(ISqlExpressionFactory sqlExpressionFactory)
IReadOnlyList<SqlExpression> arguments,
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
{
if (method.DeclaringType != typeof(TimeOnly) || instance is null)
if (method.DeclaringType != typeof(TimeOnly))
{
return null;
}

if ((method == FromDateTime || method == FromTimeSpan)
&& instance is null
&& arguments.Count == 1)
{
return _sqlExpressionFactory.Convert(arguments[0], typeof(TimeOnly));
}

if (instance is null)
{
return null;
}
Expand Down
11 changes: 10 additions & 1 deletion src/EFCore.Jet/Query/Sql/Internal/JetQuerySqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class JetQuerySqlGenerator : QuerySqlGenerator, IJetExpressionVisitor
{ nameof(Double), "CDBL" },
{ nameof(Decimal), "CDEC" },
{ nameof(DateTime), "CDATE" },
{ nameof(TimeOnly), "TIMEVALUE" },
};

private readonly ITypeMappingSource _typeMappingSource;
Expand Down Expand Up @@ -510,6 +511,14 @@ protected override Expression VisitSqlParameter(SqlParameterExpression sqlParame
Sql.Append(")");
return sqlParameterExpression;
}
if (sqlParameterExpression.Type == typeof(TimeOnly) && sqlParameterExpression.TypeMapping is JetTimeOnlyTypeMapping)
{
Sql.Append("TIMEVALUE(");
base.VisitSqlParameter(sqlParameterExpression);
Sql.Append(")");
return sqlParameterExpression;
}


//GroupBy_param_Select_Sum_Min_Key_Max_Avg
//Subquery has parameter as a projection with alias
Expand Down Expand Up @@ -742,7 +751,7 @@ protected Expression VisitJetConvertExpression(SqlUnaryExpression convertExpress
return convertExpression;
}

protected override string GetOperator([JetBrains.Annotations.NotNull] SqlBinaryExpression binaryExpression)
protected override string GetOperator(SqlBinaryExpression binaryExpression)
=> binaryExpression.OperatorType switch
{
ExpressionType.Add when binaryExpression.Type == typeof(string) => " & ",
Expand Down
24 changes: 24 additions & 0 deletions test/EFCore.Jet.FunctionalTests/GreenTests/ace_2010_oledb_x86.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13114,6 +13114,14 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Where_TimeO
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Where_TimeOnly_AddHours(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Where_TimeOnly_AddMinutes(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Where_TimeOnly_AddMinutes(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Where_TimeOnly_FromDateTime_compared_to_constant(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Where_TimeOnly_FromDateTime_compared_to_constant(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Where_TimeOnly_FromDateTime_compared_to_property(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Where_TimeOnly_FromDateTime_compared_to_property(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Where_TimeOnly_FromTimeSpan_compared_to_parameter(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Where_TimeOnly_FromTimeSpan_compared_to_parameter(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Where_TimeOnly_FromTimeSpan_compared_to_property(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Where_TimeOnly_FromTimeSpan_compared_to_property(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Where_TimeOnly_Hour(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Where_TimeOnly_Hour(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.GearsOfWarQueryJetTest.Where_TimeOnly_IsBetween(async: False)
Expand Down Expand Up @@ -20188,6 +20196,14 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Where_Ti
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Where_TimeOnly_AddHours(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Where_TimeOnly_AddMinutes(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Where_TimeOnly_AddMinutes(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Where_TimeOnly_FromDateTime_compared_to_constant(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Where_TimeOnly_FromDateTime_compared_to_constant(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Where_TimeOnly_FromDateTime_compared_to_property(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Where_TimeOnly_FromDateTime_compared_to_property(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Where_TimeOnly_FromTimeSpan_compared_to_parameter(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Where_TimeOnly_FromTimeSpan_compared_to_parameter(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Where_TimeOnly_FromTimeSpan_compared_to_property(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Where_TimeOnly_FromTimeSpan_compared_to_property(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Where_TimeOnly_Hour(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Where_TimeOnly_Hour(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPCGearsOfWarQueryJetTest.Where_TimeOnly_IsBetween(async: False)
Expand Down Expand Up @@ -22021,6 +22037,14 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Where_Ti
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Where_TimeOnly_AddHours(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Where_TimeOnly_AddMinutes(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Where_TimeOnly_AddMinutes(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Where_TimeOnly_FromDateTime_compared_to_constant(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Where_TimeOnly_FromDateTime_compared_to_constant(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Where_TimeOnly_FromDateTime_compared_to_property(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Where_TimeOnly_FromDateTime_compared_to_property(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Where_TimeOnly_FromTimeSpan_compared_to_parameter(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Where_TimeOnly_FromTimeSpan_compared_to_parameter(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Where_TimeOnly_FromTimeSpan_compared_to_property(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Where_TimeOnly_FromTimeSpan_compared_to_property(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Where_TimeOnly_Hour(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Where_TimeOnly_Hour(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.TPTGearsOfWarQueryJetTest.Where_TimeOnly_IsBetween(async: False)
Expand Down
36 changes: 18 additions & 18 deletions test/EFCore.Jet.FunctionalTests/Query/GearsOfWarQueryJetTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8925,11 +8925,11 @@ public override async Task Where_TimeOnly_FromDateTime_compared_to_property(bool
await base.Where_TimeOnly_FromDateTime_compared_to_property(async);

AssertSql(
"""
SELECT [t].[Id] AS [TagId], [m].[Id] AS [MissionId]
FROM [Tags] AS [t]
CROSS JOIN [Missions] AS [m]
WHERE CAST([t].[IssueDate] AS time) = [m].[Time]
"""
SELECT `t`.`Id` AS `TagId`, `m`.`Id` AS `MissionId`
FROM `Tags` AS `t`,
`Missions` AS `m`
WHERE TIMEVALUE(`t`.`IssueDate`) = `m`.`Time`
""");
}

Expand All @@ -8953,10 +8953,10 @@ public override async Task Where_TimeOnly_FromDateTime_compared_to_constant(bool
await base.Where_TimeOnly_FromDateTime_compared_to_constant(async);

AssertSql(
"""
SELECT [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[IssueDate], [t].[Note]
FROM [Tags] AS [t]
WHERE CAST(DATEADD(hour, CAST(CAST(CAST(LEN([t].[Note]) AS int) AS float) AS int), [t].[IssueDate]) AS time) > '09:00:00'
"""
SELECT `t`.`Id`, `t`.`GearNickName`, `t`.`GearSquadId`, `t`.`IssueDate`, `t`.`Note`
FROM `Tags` AS `t`
WHERE IIF(DATEADD('h', CDBL(IIF(LEN(`t`.`Note`) IS NULL, NULL, CLNG(LEN(`t`.`Note`)))), `t`.`IssueDate`) IS NULL, NULL, TIMEVALUE(DATEADD('h', CDBL(IIF(LEN(`t`.`Note`) IS NULL, NULL, CLNG(LEN(`t`.`Note`)))), `t`.`IssueDate`))) > TIMEVALUE('09:00:00')
""");
}

Expand All @@ -8965,10 +8965,10 @@ public override async Task Where_TimeOnly_FromTimeSpan_compared_to_property(bool
await base.Where_TimeOnly_FromTimeSpan_compared_to_property(async);

AssertSql(
"""
SELECT [m].[Id], [m].[BriefingDocument], [m].[BriefingDocumentFileExtension], [m].[CodeName], [m].[Date], [m].[Difficulty], [m].[Duration], [m].[Rating], [m].[Time], [m].[Timeline]
FROM [Missions] AS [m]
WHERE CAST([m].[Duration] AS time) < [m].[Time]
"""
SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Difficulty`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
FROM `Missions` AS `m`
WHERE TIMEVALUE(`m`.`Duration`) < `m`.`Time`
""");
}

Expand All @@ -8977,12 +8977,12 @@ public override async Task Where_TimeOnly_FromTimeSpan_compared_to_parameter(boo
await base.Where_TimeOnly_FromTimeSpan_compared_to_parameter(async);

AssertSql(
"""
@__time_0='01:02' (DbType = Time)
"""
@__time_0='01:02:03'

SELECT [m].[Id], [m].[BriefingDocument], [m].[BriefingDocumentFileExtension], [m].[CodeName], [m].[Date], [m].[Difficulty], [m].[Duration], [m].[Rating], [m].[Time], [m].[Timeline]
FROM [Missions] AS [m]
WHERE CAST([m].[Duration] AS time) = @__time_0
SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Difficulty`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
FROM `Missions` AS `m`
WHERE TIMEVALUE(`m`.`Duration`) = TIMEVALUE(@__time_0)
""");
}

Expand Down
36 changes: 18 additions & 18 deletions test/EFCore.Jet.FunctionalTests/Query/TPCGearsOfWarQueryJetTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11821,11 +11821,11 @@ public override async Task Where_TimeOnly_FromDateTime_compared_to_property(bool
await base.Where_TimeOnly_FromDateTime_compared_to_property(async);

AssertSql(
"""
SELECT [t].[Id] AS [TagId], [m].[Id] AS [MissionId]
FROM [Tags] AS [t]
CROSS JOIN [Missions] AS [m]
WHERE CAST([t].[IssueDate] AS time) = [m].[Time]
"""
SELECT `t`.`Id` AS `TagId`, `m`.`Id` AS `MissionId`
FROM `Tags` AS `t`,
`Missions` AS `m`
WHERE TIMEVALUE(`t`.`IssueDate`) = `m`.`Time`
""");
}

Expand Down Expand Up @@ -11855,10 +11855,10 @@ public override async Task Where_TimeOnly_FromDateTime_compared_to_constant(bool
await base.Where_TimeOnly_FromDateTime_compared_to_constant(async);

AssertSql(
"""
SELECT [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[IssueDate], [t].[Note]
FROM [Tags] AS [t]
WHERE CAST(DATEADD(hour, CAST(CAST(CAST(LEN([t].[Note]) AS int) AS float) AS int), [t].[IssueDate]) AS time) > '09:00:00'
"""
SELECT `t`.`Id`, `t`.`GearNickName`, `t`.`GearSquadId`, `t`.`IssueDate`, `t`.`Note`
FROM `Tags` AS `t`
WHERE IIF(DATEADD('h', CDBL(IIF(LEN(`t`.`Note`) IS NULL, NULL, CLNG(LEN(`t`.`Note`)))), `t`.`IssueDate`) IS NULL, NULL, TIMEVALUE(DATEADD('h', CDBL(IIF(LEN(`t`.`Note`) IS NULL, NULL, CLNG(LEN(`t`.`Note`)))), `t`.`IssueDate`))) > TIMEVALUE('09:00:00')
""");
}

Expand All @@ -11867,10 +11867,10 @@ public override async Task Where_TimeOnly_FromTimeSpan_compared_to_property(bool
await base.Where_TimeOnly_FromTimeSpan_compared_to_property(async);

AssertSql(
"""
SELECT [m].[Id], [m].[CodeName], [m].[Date], [m].[Difficulty], [m].[Duration], [m].[Rating], [m].[Time], [m].[Timeline]
FROM [Missions] AS [m]
WHERE CAST([m].[Duration] AS time) < [m].[Time]
"""
SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Difficulty`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
FROM `Missions` AS `m`
WHERE TIMEVALUE(`m`.`Duration`) < `m`.`Time`
""");
}

Expand All @@ -11879,12 +11879,12 @@ public override async Task Where_TimeOnly_FromTimeSpan_compared_to_parameter(boo
await base.Where_TimeOnly_FromTimeSpan_compared_to_parameter(async);

AssertSql(
"""
@__time_0='01:02' (DbType = Time)
"""
@__time_0='01:02:03'

SELECT [m].[Id], [m].[CodeName], [m].[Date], [m].[Difficulty], [m].[Duration], [m].[Rating], [m].[Time], [m].[Timeline]
FROM [Missions] AS [m]
WHERE CAST([m].[Duration] AS time) = @__time_0
SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Difficulty`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
FROM `Missions` AS `m`
WHERE TIMEVALUE(`m`.`Duration`) = TIMEVALUE(@__time_0)
""");
}

Expand Down
36 changes: 18 additions & 18 deletions test/EFCore.Jet.FunctionalTests/Query/TPTGearsOfWarQueryJetTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9522,11 +9522,11 @@ public override async Task Where_TimeOnly_FromDateTime_compared_to_property(bool
await base.Where_TimeOnly_FromDateTime_compared_to_property(async);

AssertSql(
"""
SELECT [t].[Id] AS [TagId], [m].[Id] AS [MissionId]
FROM [Tags] AS [t]
CROSS JOIN [Missions] AS [m]
WHERE CAST([t].[IssueDate] AS time) = [m].[Time]
"""
SELECT `t`.`Id` AS `TagId`, `m`.`Id` AS `MissionId`
FROM `Tags` AS `t`,
`Missions` AS `m`
WHERE TIMEVALUE(`t`.`IssueDate`) = `m`.`Time`
""");
}

Expand All @@ -9553,10 +9553,10 @@ public override async Task Where_TimeOnly_FromDateTime_compared_to_constant(bool
await base.Where_TimeOnly_FromDateTime_compared_to_constant(async);

AssertSql(
"""
SELECT [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[IssueDate], [t].[Note]
FROM [Tags] AS [t]
WHERE CAST(DATEADD(hour, CAST(CAST(CAST(LEN([t].[Note]) AS int) AS float) AS int), [t].[IssueDate]) AS time) > '09:00:00'
"""
SELECT `t`.`Id`, `t`.`GearNickName`, `t`.`GearSquadId`, `t`.`IssueDate`, `t`.`Note`
FROM `Tags` AS `t`
WHERE IIF(DATEADD('h', CDBL(IIF(LEN(`t`.`Note`) IS NULL, NULL, CLNG(LEN(`t`.`Note`)))), `t`.`IssueDate`) IS NULL, NULL, TIMEVALUE(DATEADD('h', CDBL(IIF(LEN(`t`.`Note`) IS NULL, NULL, CLNG(LEN(`t`.`Note`)))), `t`.`IssueDate`))) > TIMEVALUE('09:00:00')
""");
}

Expand All @@ -9565,10 +9565,10 @@ public override async Task Where_TimeOnly_FromTimeSpan_compared_to_property(bool
await base.Where_TimeOnly_FromTimeSpan_compared_to_property(async);

AssertSql(
"""
SELECT [m].[Id], [m].[CodeName], [m].[Date], [m].[Difficulty], [m].[Duration], [m].[Rating], [m].[Time], [m].[Timeline]
FROM [Missions] AS [m]
WHERE CAST([m].[Duration] AS time) < [m].[Time]
"""
SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Difficulty`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
FROM `Missions` AS `m`
WHERE TIMEVALUE(`m`.`Duration`) < `m`.`Time`
""");
}

Expand All @@ -9577,12 +9577,12 @@ public override async Task Where_TimeOnly_FromTimeSpan_compared_to_parameter(boo
await base.Where_TimeOnly_FromTimeSpan_compared_to_parameter(async);

AssertSql(
"""
@__time_0='01:02' (DbType = Time)
"""
@__time_0='01:02:03'

SELECT [m].[Id], [m].[CodeName], [m].[Date], [m].[Difficulty], [m].[Duration], [m].[Rating], [m].[Time], [m].[Timeline]
FROM [Missions] AS [m]
WHERE CAST([m].[Duration] AS time) = @__time_0
SELECT `m`.`Id`, `m`.`CodeName`, `m`.`Date`, `m`.`Difficulty`, `m`.`Duration`, `m`.`Rating`, `m`.`Time`, `m`.`Timeline`
FROM `Missions` AS `m`
WHERE TIMEVALUE(`m`.`Duration`) = TIMEVALUE(@__time_0)
""");
}

Expand Down

0 comments on commit 20c42de

Please sign in to comment.