From c09c874a5d355cd8178d1a35a4a0731ed33a1e29 Mon Sep 17 00:00:00 2001 From: Christopher Jolly Date: Wed, 24 Jul 2024 01:04:33 +0800 Subject: [PATCH] Use lowest datetime value possible when doing a firstordefault. Default with Jet (0 value) is not the lowest --- .../JetSqlTranslatingExpressionVisitor.cs | 23 +++++++++++++++++++ .../Internal/JetDateTimeTypeMapping.cs | 4 ++-- .../Query/GearsOfWarQueryJetTest.cs | 22 +++++++++++++++--- .../Query/TPCGearsOfWarQueryJetTest.cs | 22 +++++++++++++++--- .../Query/TPTGearsOfWarQueryJetTest.cs | 22 +++++++++++++++--- 5 files changed, 82 insertions(+), 11 deletions(-) diff --git a/src/EFCore.Jet/Query/Internal/JetSqlTranslatingExpressionVisitor.cs b/src/EFCore.Jet/Query/Internal/JetSqlTranslatingExpressionVisitor.cs index 9ae084b..c54a5d1 100644 --- a/src/EFCore.Jet/Query/Internal/JetSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Jet/Query/Internal/JetSqlTranslatingExpressionVisitor.cs @@ -14,6 +14,9 @@ using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using System.Text; using EntityFrameworkCore.Jet.Internal; using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Storage; + namespace EntityFrameworkCore.Jet.Query.Internal; /// @@ -154,6 +157,26 @@ public class JetSqlTranslatingExpressionVisitor : RelationalSqlTranslatingExpres return base.VisitUnary(unaryExpression); } + protected override Expression VisitExtension(Expression extensionExpression) + { + var result = base.VisitExtension(extensionExpression); + if (extensionExpression is ShapedQueryExpression shapedQueryExpression) + { + var shaperExpression = shapedQueryExpression.ShaperExpression; + if (shapedQueryExpression.ResultCardinality == ResultCardinality.SingleOrDefault + && !shaperExpression.Type.IsNullableType() && result is SqlFunctionExpression { Name: "COALESCE"} sqlFunctionExpression) + { + if (sqlFunctionExpression.Arguments?[1] is SqlConstantExpression { Value: DateTime { Ticks: 0 } }) + { + var newconst = new SqlConstantExpression(new DateTime(100, 1, 1), sqlFunctionExpression.Arguments[1].TypeMapping); + return _sqlExpressionFactory.Coalesce(sqlFunctionExpression.Arguments[0], + (SqlExpression)Visit(newconst)); + } + } + } + return result; + } + protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) { var method = methodCallExpression.Method; diff --git a/src/EFCore.Jet/Storage/Internal/JetDateTimeTypeMapping.cs b/src/EFCore.Jet/Storage/Internal/JetDateTimeTypeMapping.cs index 626e68e..6767689 100644 --- a/src/EFCore.Jet/Storage/Internal/JetDateTimeTypeMapping.cs +++ b/src/EFCore.Jet/Storage/Internal/JetDateTimeTypeMapping.cs @@ -148,14 +148,14 @@ namespace EntityFrameworkCore.Jet.Storage.Internal private static DateTime CheckDateTimeValue(DateTime dateTime) { - if (dateTime < JetConfiguration.TimeSpanOffset) + if (dateTime < new DateTime(100,1,1)) { if (dateTime != default) { throw new InvalidOperationException($"The {nameof(DateTime)} value '{dateTime}' is smaller than the minimum supported value of '{JetConfiguration.TimeSpanOffset}'."); } - dateTime = JetConfiguration.TimeSpanOffset; + //dateTime = JetConfiguration.TimeSpanOffset; } return dateTime; diff --git a/test/EFCore.Jet.FunctionalTests/Query/GearsOfWarQueryJetTest.cs b/test/EFCore.Jet.FunctionalTests/Query/GearsOfWarQueryJetTest.cs index 5cef366..58b186e 100644 --- a/test/EFCore.Jet.FunctionalTests/Query/GearsOfWarQueryJetTest.cs +++ b/test/EFCore.Jet.FunctionalTests/Query/GearsOfWarQueryJetTest.cs @@ -8156,7 +8156,23 @@ ORDER BY `g`.`Nickname`, `g`.`SquadId`, `c`.`Name` public override async Task FirstOrDefault_on_empty_collection_of_DateTime_in_subquery(bool async) { - await base.FirstOrDefault_on_empty_collection_of_DateTime_in_subquery(async); + //await base.FirstOrDefault_on_empty_collection_of_DateTime_in_subquery(async); + await AssertQuery( + async, + ss => from g in ss.Set() + let invalidTagIssueDate = (from t in ss.Set() + where t.GearNickName == g.FullName + orderby t.Id + select t.IssueDate).FirstOrDefault() + where g.Tag.IssueDate > invalidTagIssueDate + select new { g.Nickname, invalidTagIssueDate }, + ss => from g in ss.Set() + let invalidTagIssueDate = (from t in ss.Set() + where t.GearNickName == g.FullName + orderby t.Id + select t.IssueDate).FirstOrDefault(new DateTime(100,1,1)) + where g.Tag.IssueDate > invalidTagIssueDate + select new { g.Nickname, invalidTagIssueDate }); AssertSql( """ @@ -8164,7 +8180,7 @@ SELECT `g`.`Nickname`, IIF(( SELECT TOP 1 `t1`.`IssueDate` FROM `Tags` AS `t1` WHERE `t1`.`GearNickName` = `g`.`FullName` - ORDER BY `t1`.`Id`) IS NULL, #1899-12-30#, ( + ORDER BY `t1`.`Id`) IS NULL, #0100-01-01#, ( SELECT TOP 1 `t1`.`IssueDate` FROM `Tags` AS `t1` WHERE `t1`.`GearNickName` = `g`.`FullName` @@ -8175,7 +8191,7 @@ WHERE `t`.`IssueDate` > IIF(( SELECT TOP 1 `t0`.`IssueDate` FROM `Tags` AS `t0` WHERE `t0`.`GearNickName` = `g`.`FullName` - ORDER BY `t0`.`Id`) IS NULL, #1899-12-30#, ( + ORDER BY `t0`.`Id`) IS NULL, #0100-01-01#, ( SELECT TOP 1 `t0`.`IssueDate` FROM `Tags` AS `t0` WHERE `t0`.`GearNickName` = `g`.`FullName` diff --git a/test/EFCore.Jet.FunctionalTests/Query/TPCGearsOfWarQueryJetTest.cs b/test/EFCore.Jet.FunctionalTests/Query/TPCGearsOfWarQueryJetTest.cs index 706a093..6601569 100644 --- a/test/EFCore.Jet.FunctionalTests/Query/TPCGearsOfWarQueryJetTest.cs +++ b/test/EFCore.Jet.FunctionalTests/Query/TPCGearsOfWarQueryJetTest.cs @@ -12277,7 +12277,23 @@ WHERE `l`.`ServerAddress` = '127.0.0.1' public override async Task FirstOrDefault_on_empty_collection_of_DateTime_in_subquery(bool async) { - await base.FirstOrDefault_on_empty_collection_of_DateTime_in_subquery(async); + //await base.FirstOrDefault_on_empty_collection_of_DateTime_in_subquery(async); + await AssertQuery( + async, + ss => from g in ss.Set() + let invalidTagIssueDate = (from t in ss.Set() + where t.GearNickName == g.FullName + orderby t.Id + select t.IssueDate).FirstOrDefault() + where g.Tag.IssueDate > invalidTagIssueDate + select new { g.Nickname, invalidTagIssueDate }, + ss => from g in ss.Set() + let invalidTagIssueDate = (from t in ss.Set() + where t.GearNickName == g.FullName + orderby t.Id + select t.IssueDate).FirstOrDefault(new DateTime(100, 1, 1)) + where g.Tag.IssueDate > invalidTagIssueDate + select new { g.Nickname, invalidTagIssueDate }); AssertSql( """ @@ -12285,7 +12301,7 @@ SELECT `u`.`Nickname`, IIF(( SELECT TOP 1 `t1`.`IssueDate` FROM `Tags` AS `t1` WHERE `t1`.`GearNickName` = `u`.`FullName` - ORDER BY `t1`.`Id`) IS NULL, #1899-12-30#, ( + ORDER BY `t1`.`Id`) IS NULL, #0100-01-01#, ( SELECT TOP 1 `t1`.`IssueDate` FROM `Tags` AS `t1` WHERE `t1`.`GearNickName` = `u`.`FullName` @@ -12302,7 +12318,7 @@ WHERE `t`.`IssueDate` > IIF(( SELECT TOP 1 `t0`.`IssueDate` FROM `Tags` AS `t0` WHERE `t0`.`GearNickName` = `u`.`FullName` - ORDER BY `t0`.`Id`) IS NULL, #1899-12-30#, ( + ORDER BY `t0`.`Id`) IS NULL, #0100-01-01#, ( SELECT TOP 1 `t0`.`IssueDate` FROM `Tags` AS `t0` WHERE `t0`.`GearNickName` = `u`.`FullName` diff --git a/test/EFCore.Jet.FunctionalTests/Query/TPTGearsOfWarQueryJetTest.cs b/test/EFCore.Jet.FunctionalTests/Query/TPTGearsOfWarQueryJetTest.cs index 8a58286..7c17daf 100644 --- a/test/EFCore.Jet.FunctionalTests/Query/TPTGearsOfWarQueryJetTest.cs +++ b/test/EFCore.Jet.FunctionalTests/Query/TPTGearsOfWarQueryJetTest.cs @@ -9881,7 +9881,23 @@ WHERE `f`.`ServerAddress` = '127.0.0.1' public override async Task FirstOrDefault_on_empty_collection_of_DateTime_in_subquery(bool async) { - await base.FirstOrDefault_on_empty_collection_of_DateTime_in_subquery(async); + //await base.FirstOrDefault_on_empty_collection_of_DateTime_in_subquery(async); + await AssertQuery( + async, + ss => from g in ss.Set() + let invalidTagIssueDate = (from t in ss.Set() + where t.GearNickName == g.FullName + orderby t.Id + select t.IssueDate).FirstOrDefault() + where g.Tag.IssueDate > invalidTagIssueDate + select new { g.Nickname, invalidTagIssueDate }, + ss => from g in ss.Set() + let invalidTagIssueDate = (from t in ss.Set() + where t.GearNickName == g.FullName + orderby t.Id + select t.IssueDate).FirstOrDefault(new DateTime(100, 1, 1)) + where g.Tag.IssueDate > invalidTagIssueDate + select new { g.Nickname, invalidTagIssueDate }); AssertSql( """ @@ -9889,7 +9905,7 @@ SELECT `g`.`Nickname`, IIF(( SELECT TOP 1 `t1`.`IssueDate` FROM `Tags` AS `t1` WHERE `t1`.`GearNickName` = `g`.`FullName` - ORDER BY `t1`.`Id`) IS NULL, #1899-12-30#, ( + ORDER BY `t1`.`Id`) IS NULL, #0100-01-01#, ( SELECT TOP 1 `t1`.`IssueDate` FROM `Tags` AS `t1` WHERE `t1`.`GearNickName` = `g`.`FullName` @@ -9900,7 +9916,7 @@ WHERE `t`.`IssueDate` > IIF(( SELECT TOP 1 `t0`.`IssueDate` FROM `Tags` AS `t0` WHERE `t0`.`GearNickName` = `g`.`FullName` - ORDER BY `t0`.`Id`) IS NULL, #1899-12-30#, ( + ORDER BY `t0`.`Id`) IS NULL, #0100-01-01#, ( SELECT TOP 1 `t0`.`IssueDate` FROM `Tags` AS `t0` WHERE `t0`.`GearNickName` = `g`.`FullName`