// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Linq; using System.Threading.Tasks; using EntityFrameworkCore.Jet.Internal; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.TestModels.Northwind; using Microsoft.EntityFrameworkCore.TestUtilities; using Xunit; using Xunit.Abstractions; using EF = Microsoft.EntityFrameworkCore.EF; #nullable disable // ReSharper disable InconsistentNaming namespace EntityFrameworkCore.Jet.FunctionalTests.Query { public class DbFunctionsJetTest : NorthwindDbFunctionsQueryRelationalTestBase> { public DbFunctionsJetTest( NorthwindQueryJetFixture fixture, ITestOutputHelper testOutputHelper) : base(fixture) { Fixture.TestSqlLoggerFactory.Clear(); Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); public override async Task Like_literal(bool async) { await base.Like_literal(async); AssertSql( $""" SELECT COUNT(*) FROM `Customers` AS `c` WHERE `c`.`ContactName` LIKE '%M%' """); } public override async Task Like_identity(bool async) { await base.Like_identity(async); AssertSql( $""" SELECT COUNT(*) FROM `Customers` AS `c` WHERE `c`.`ContactName` LIKE `c`.`ContactName` """); } [ConditionalTheory(Skip = "No escape character support in Jet")] public override async Task Like_literal_with_escape(bool async) { await base.Like_literal_with_escape(async); AssertSql( $""" SELECT COUNT(*) FROM `Customers` AS `c` WHERE `c`.`ContactName` LIKE '!%' ESCAPE '!' """); } public override async Task Like_all_literals(bool async) { await base.Like_all_literals(async); AssertSql( """ SELECT COUNT(*) FROM `Customers` AS `c` WHERE 'FOO' LIKE '%O%' """); } [ConditionalTheory(Skip = "No escape character support in Jet")] public override async Task Like_all_literals_with_escape(bool async) { await base.Like_all_literals_with_escape(async); AssertSql( """ SELECT COUNT(*) FROM [Customers] AS [c] WHERE N'%' LIKE N'!%' ESCAPE N'!' """); } [ConditionalTheory(Skip = "No ecollate support in Jet")] public override async Task Collate_case_insensitive(bool async) { await base.Collate_case_insensitive(async); AssertSql( """ SELECT COUNT(*) FROM [Customers] AS [c] WHERE [c].[ContactName] COLLATE Latin1_General_CI_AI = N'maria anders' """); } [ConditionalTheory(Skip = "No collate support in Jet")] public override async Task Collate_case_sensitive(bool async) { await base.Collate_case_sensitive(async); AssertSql( """ SELECT COUNT(*) FROM [Customers] AS [c] WHERE [c].[ContactName] COLLATE Latin1_General_CS_AS = N'maria anders' """); } [ConditionalTheory(Skip = "No collate support in Jet")] public override async Task Collate_case_sensitive_constant(bool async) { await base.Collate_case_sensitive_constant(async); AssertSql( """ SELECT COUNT(*) FROM [Customers] AS [c] WHERE [c].[ContactName] = N'maria anders' COLLATE Latin1_General_CS_AS """); } public override async Task Collate_is_null(bool async) { await base.Collate_is_null(async); AssertSql( """ SELECT COUNT(*) FROM `Customers` AS `c` WHERE `c`.`Region` IS NULL """); } public override async Task Least(bool async) { await base.Least(async); AssertSql( """ SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice` FROM `Order Details` AS `o` WHERE IIF(`o`.`OrderID` < 10251, `o`.`OrderID`, 10251) = 10251 """); } public override async Task Greatest(bool async) { await base.Greatest(async); AssertSql( """ SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice` FROM `Order Details` AS `o` WHERE IIF(`o`.`OrderID` > 10251, `o`.`OrderID`, 10251) = 10251 """); } public override async Task Least_with_nullable_value_type(bool async) { await base.Least_with_nullable_value_type(async); AssertSql( """ SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice` FROM `Order Details` AS `o` WHERE IIF(`o`.`OrderID` < 10251, `o`.`OrderID`, 10251) = 10251 """); } public override async Task Greatest_with_nullable_value_type(bool async) { await base.Greatest_with_nullable_value_type(async); AssertSql( """ SELECT `o`.`OrderID`, `o`.`ProductID`, `o`.`Discount`, `o`.`Quantity`, `o`.`UnitPrice` FROM `Order Details` AS `o` WHERE IIF(`o`.`OrderID` > 10251, `o`.`OrderID`, 10251) = 10251 """); } public override async Task Least_with_parameter_array_is_not_supported(bool async) { await base.Least_with_parameter_array_is_not_supported(async); AssertSql(); } public override async Task Greatest_with_parameter_array_is_not_supported(bool async) { await base.Greatest_with_parameter_array_is_not_supported(async); AssertSql(); } [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task DateDiff_Year(bool async) { await AssertCount( async, ss => ss.Set(), ss => ss.Set(), c => EF.Functions.DateDiffYear(c.OrderDate, DateTime.Now) == 0, c => c.OrderDate.Value.Year - DateTime.Now.Year == 0); AssertSql( """ SELECT COUNT(*) FROM `Orders` AS `o` WHERE DATEDIFF('yyyy', `o`.`OrderDate`, NOW()) = 0 """); } [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task DateDiff_Month(bool async) { var now = DateTime.Now; await AssertCount( async, ss => ss.Set(), ss => ss.Set(), c => EF.Functions.DateDiffMonth(c.OrderDate, DateTime.Now) == 0, c => c.OrderDate.Value.Year * 12 + c.OrderDate.Value.Month - (now.Year * 12 + now.Month) == 0); AssertSql( """ SELECT COUNT(*) FROM `Orders` AS `o` WHERE DATEDIFF('m', `o`.`OrderDate`, NOW()) = 0 """); } [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task DateDiff_Day(bool async) { await AssertCount( async, ss => ss.Set(), ss => ss.Set(), c => EF.Functions.DateDiffDay(c.OrderDate, DateTime.Now) == 0, c => false); AssertSql( """ SELECT COUNT(*) FROM `Orders` AS `o` WHERE DATEDIFF('d', `o`.`OrderDate`, NOW()) = 0 """); } [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task DateDiff_Hour(bool async) { await AssertCount( async, ss => ss.Set(), ss => ss.Set(), c => EF.Functions.DateDiffHour(c.OrderDate, DateTime.Now) == 0, c => false); AssertSql( """ SELECT COUNT(*) FROM `Orders` AS `o` WHERE DATEDIFF('h', `o`.`OrderDate`, NOW()) = 0 """); } [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task DateDiff_Minute(bool async) { await AssertCount( async, ss => ss.Set(), ss => ss.Set(), c => EF.Functions.DateDiffMinute(c.OrderDate, DateTime.Now) == 0, c => false); AssertSql( """ SELECT COUNT(*) FROM `Orders` AS `o` WHERE DATEDIFF('n', `o`.`OrderDate`, NOW()) = 0 """); } [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task DateDiff_Second(bool async) { await AssertCount( async, ss => ss.Set(), ss => ss.Set(), c => EF.Functions.DateDiffSecond(c.OrderDate, DateTime.Now) == 0, c => false); AssertSql( """ SELECT COUNT(*) FROM `Orders` AS `o` WHERE DATEDIFF('s', `o`.`OrderDate`, NOW()) = 0 """); } /*[ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task DateDiff_Millisecond(bool async) { await AssertCount( async, ss => ss.Set(), ss => ss.Set(), c => EF.Functions.DateDiffMillisecond(DateTime.Now, DateTime.Now.AddDays(1)) == 0, c => false); AssertSql( """ SELECT COUNT(*) FROM [Orders] AS [o] WHERE DATEDIFF(millisecond, GETDATE(), DATEADD(day, CAST(1.0E0 AS int), GETDATE())) = 0 """); } [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task DateDiff_Microsecond(bool async) { await AssertCount( async, ss => ss.Set(), ss => ss.Set(), c => EF.Functions.DateDiffMicrosecond(DateTime.Now, DateTime.Now.AddSeconds(1)) == 0, c => false); AssertSql( """ SELECT COUNT(*) FROM [Orders] AS [o] WHERE DATEDIFF(microsecond, GETDATE(), DATEADD(second, CAST(1.0E0 AS int), GETDATE())) = 0 """); } [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task DateDiff_Nanosecond(bool async) { await AssertCount( async, ss => ss.Set(), ss => ss.Set(), c => EF.Functions.DateDiffNanosecond(DateTime.Now, DateTime.Now.AddSeconds(1)) == 0, c => false); AssertSql( """ SELECT COUNT(*) FROM [Orders] AS [o] WHERE DATEDIFF(nanosecond, GETDATE(), DATEADD(second, CAST(1.0E0 AS int), GETDATE())) = 0 """); } [ConditionalFact] public virtual void DateDiff_Week_datetime() { using var context = CreateContext(); var count = context.Orders .Count( c => EF.Functions.DateDiffWeek( c.OrderDate, new DateTime(1998, 5, 6, 0, 0, 0)) == 5); Assert.Equal(16, count); AssertSql( """ SELECT COUNT(*) FROM [Orders] AS [o] WHERE DATEDIFF(week, [o].[OrderDate], '1998-05-06T00:00:00.000') = 5 """); } [ConditionalFact] public virtual void DateDiff_Week_datetimeoffset() { using var context = CreateContext(); var count = context.Orders .Count( c => EF.Functions.DateDiffWeek( c.OrderDate, new DateTimeOffset(1998, 5, 6, 0, 0, 0, TimeSpan.Zero)) == 5); Assert.Equal(16, count); AssertSql( """ SELECT COUNT(*) FROM [Orders] AS [o] WHERE DATEDIFF(week, CAST([o].[OrderDate] AS datetimeoffset), '1998-05-06T00:00:00.0000000+00:00') = 5 """); } [ConditionalFact] public virtual void DateDiff_Week_parameters_null() { using var context = CreateContext(); var count = context.Orders .Count( c => EF.Functions.DateDiffWeek( null, c.OrderDate) == 5); Assert.Equal(0, count); AssertSql( """ SELECT COUNT(*) FROM [Orders] AS [o] WHERE DATEDIFF(week, NULL, [o].[OrderDate]) = 5 """); }*/ [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task IsDate_not_valid(bool async) { await AssertQueryScalar( async, ss => ss.Set().Where(o => !EF.Functions.IsDate(o.CustomerID)).Select(o => EF.Functions.IsDate(o.CustomerID)), ss => ss.Set().Select(c => false)); AssertSql( """ SELECT CBOOL(ISDATE(`o`.`CustomerID`)) * -1 FROM `Orders` AS `o` WHERE NOT (CBOOL(ISDATE(`o`.`CustomerID`)) * -1) """); } [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task IsDate_valid(bool async) { await AssertQueryScalar( async, ss => ss.Set() .Where(o => EF.Functions.IsDate(o.OrderDate.Value.ToString())) .Select(o => EF.Functions.IsDate(o.OrderDate.Value.ToString())), ss => ss.Set().Select(o => true)); AssertSql( """ SELECT CBOOL(ISDATE(IIF((`o`.`OrderDate` & '') IS NULL, '', (`o`.`OrderDate` & '')))) * -1 FROM `Orders` AS `o` WHERE CBOOL(ISDATE(IIF((`o`.`OrderDate` & '') IS NULL, '', (`o`.`OrderDate` & '')))) * -1 """); } [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task IsDate_join_fields(bool async) { await AssertCount( async, ss => ss.Set(), ss => ss.Set(), c => EF.Functions.IsDate(c.CustomerID + c.OrderID), c => false); AssertSql( """ SELECT COUNT(*) FROM `Orders` AS `o` WHERE CBOOL(ISDATE(IIF(`o`.`CustomerID` IS NULL, '', `o`.`CustomerID`) & (`o`.`OrderID` & ''))) * -1 """); } [ConditionalFact] public void IsDate_should_throw_on_client_eval() { var exIsDate = Assert.Throws(() => EF.Functions.IsDate("#ISDATE#")); Assert.Equal( CoreStrings.FunctionOnClient(nameof(JetDbFunctionsExtensions.IsDate)), exIsDate.Message); } private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); protected override string CaseInsensitiveCollation => ""; protected override string CaseSensitiveCollation => ""; } }