From 3c03f34f5b55570198d036a2a8ee4fbf4aaca5a1 Mon Sep 17 00:00:00 2001 From: maumar Date: Mon, 24 Oct 2022 18:39:02 -0700 Subject: [PATCH] Fix to #28881 - Consider removing unnecessary CASTs around JSON_VALUE We can remove cast for string scalars, other types maybe cause conversion issues in the generated sql. Fixes #28881 --- .../Internal/SqlServerQuerySqlGenerator.cs | 7 +- .../Query/JsonQueryFixtureBase.cs | 4 + .../Query/JsonQueryTestBase.cs | 235 ++++++++++- .../TestModels/JsonQuery/JsonOwnedAllTypes.cs | 2 + .../TestModels/JsonQuery/JsonQueryData.cs | 8 + .../Query/JsonQuerySqlServerTest.cs | 397 ++++++++++++++++-- .../Update/JsonUpdateSqlServerTest.cs | 4 +- 7 files changed, 628 insertions(+), 29 deletions(-) diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs index 39a3ec2731a..bfac93e291b 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs @@ -313,14 +313,17 @@ protected override Expression VisitJsonScalar(JsonScalarExpression jsonScalarExp } else { - Sql.Append("CAST(JSON_VALUE("); + // JSON_VALUE always returns nvarchar(4000) (https://learn.microsoft.com/sql/t-sql/functions/json-value-transact-sql), + // so we cast the result to the expected type - except if it's a string (since the cast interferes with indexes over + // the JSON property). + Sql.Append(jsonScalarExpression.TypeMapping is StringTypeMapping ? "JSON_VALUE(" : "CAST(JSON_VALUE("); } Visit(jsonScalarExpression.JsonColumn); Sql.Append($",'{string.Join("", jsonScalarExpression.Path.Select(e => e.ToString()))}')"); - if (jsonScalarExpression.Type != typeof(JsonElement)) + if (jsonScalarExpression.TypeMapping is not SqlServerJsonTypeMapping and not StringTypeMapping) { Sql.Append(" AS "); Sql.Append(jsonScalarExpression.TypeMapping!.StoreType); diff --git a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs index 6e9d16bba1d..9e284455c8f 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryFixtureBase.cs @@ -331,6 +331,8 @@ public static void AssertCustomNameBranch(JsonOwnedCustomNameBranch expected, Js public static void AssertAllTypes(JsonOwnedAllTypes expected, JsonOwnedAllTypes actual) { + Assert.Equal(expected.TestDefaultString, actual.TestDefaultString); + Assert.Equal(expected.TestMaxLengthString, actual.TestMaxLengthString); Assert.Equal(expected.TestBoolean, actual.TestBoolean); Assert.Equal(expected.TestCharacter, actual.TestCharacter); Assert.Equal(expected.TestDateTime, actual.TestDateTime); @@ -502,6 +504,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con x => x.Reference, b => { b.ToJson(); + b.Property(x => x.TestMaxLengthString).HasMaxLength(5); b.Property(x => x.TestDecimal).HasPrecision(18, 3); b.Property(x => x.TestEnumWithIntConverter).HasConversion(); b.Property(x => x.TestNullableEnumWithIntConverter).HasConversion(); @@ -529,6 +532,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con x => x.Collection, b => { b.ToJson(); + b.Property(x => x.TestMaxLengthString).HasMaxLength(5); b.Property(x => x.TestDecimal).HasPrecision(18, 3); }); } diff --git a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs index 01076a3e50e..dc1ff2e3076 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/JsonQueryTestBase.cs @@ -779,6 +779,8 @@ public virtual Task Json_all_types_projection_individual_properties(bool async) ss => ss.Set().Select( x => new { + x.Reference.TestDefaultString, + x.Reference.TestMaxLengthString, x.Reference.TestBoolean, x.Reference.TestByte, x.Reference.TestCharacter, @@ -796,7 +798,6 @@ public virtual Task Json_all_types_projection_individual_properties(bool async) x.Reference.TestUnsignedInt16, x.Reference.TestUnsignedInt32, x.Reference.TestUnsignedInt64, - x.Reference.TestNullableInt32, x.Reference.TestEnum, x.Reference.TestEnumWithIntConverter, x.Reference.TestNullableEnum, @@ -834,6 +835,238 @@ public virtual Task Json_boolean_projection_negated(bool async) async, ss => ss.Set().Select(x => !x.Reference.TestBoolean)); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_default_string(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestDefaultString != "MyDefaultStringInReference1"), + entryCount: 3); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_max_length_string(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestMaxLengthString != "Foo"), + entryCount: 3); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_string_condition(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => (!x.Reference.TestBoolean ? x.Reference.TestMaxLengthString : x.Reference.TestDefaultString) == "MyDefaultStringInReference1"), + entryCount: 3); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_byte(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestByte != 3), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_character(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestCharacter != 'z'), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_datetime(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestDateTime != new DateTime(2000, 1, 3)), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_datetimeoffset(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestDateTimeOffset != new DateTimeOffset(new DateTime(2000, 1, 4), new TimeSpan(3, 2, 0))), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_decimal(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestDecimal != 1.35M), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_double(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestDouble != 33.25), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_guid(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestGuid != new Guid()), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_int16(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestInt16 != 3), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_int32(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestInt32 != 33), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_int64(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestInt64 != 333), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_signedbyte(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestSignedByte != 100), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_single(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestSingle != 10.4f), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_timespan(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestTimeSpan != new TimeSpan(3, 2, 0)), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_unisgnedint16(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestUnsignedInt16 != 100), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_unsignedint32(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestUnsignedInt32 != 1000), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_unsignedint64(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestUnsignedInt64 != 10000), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_enum(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestEnum != JsonEnum.Two), + entryCount: 3); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_enumwithintconverter(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestEnumWithIntConverter != JsonEnum.Three), + entryCount: 3); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_nullableenum1(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestNullableEnum != JsonEnum.One), + entryCount: 3); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_nullableenum2(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestNullableEnum != null), + entryCount: 3); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_nullableenumwithconverterthathandlesnulls1(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestNullableEnumWithConverterThatHandlesNulls != JsonEnum.One), + entryCount: 6); + + [ConditionalTheory(Skip = "issue #29416")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_nullableenumwithconverterthathandlesnulls2(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestNullableEnumWithConverterThatHandlesNulls != null), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_nullableenumwithconverter1(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestNullableEnumWithIntConverter != JsonEnum.Two), + entryCount: 3); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_nullableenumwithconverter2(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestNullableEnumWithIntConverter != null), + entryCount: 3); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_nullableint321(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestNullableInt32 != 100), + entryCount: 6); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Json_predicate_on_nullableint322(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(x => x.Reference.TestNullableInt32 != null), + entryCount: 3); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task FromSql_on_entity_with_json_basic(bool async) diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonOwnedAllTypes.cs b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonOwnedAllTypes.cs index be1b3a41fc3..42bee012a4c 100644 --- a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonOwnedAllTypes.cs +++ b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonOwnedAllTypes.cs @@ -5,6 +5,8 @@ namespace Microsoft.EntityFrameworkCore.TestModels.JsonQuery; public class JsonOwnedAllTypes { + public string TestDefaultString { get; set; } + public string TestMaxLengthString { get; set; } public short TestInt16 { get; set; } public int TestInt32 { get; set; } public long TestInt64 { get; set; } diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs index 588701d60ac..9dcaf19e0bc 100644 --- a/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs +++ b/test/EFCore.Relational.Specification.Tests/TestModels/JsonQuery/JsonQueryData.cs @@ -632,6 +632,8 @@ public static IReadOnlyList CreateJsonEntitiesAllTypes() { var r1 = new JsonOwnedAllTypes { + TestDefaultString = "MyDefaultStringInReference1", + TestMaxLengthString = "Foo", TestInt16 = -1234, TestInt32 = -123456789, TestInt64 = -1234567890123456789L, @@ -659,6 +661,8 @@ public static IReadOnlyList CreateJsonEntitiesAllTypes() var r2 = new JsonOwnedAllTypes { + TestDefaultString = "MyDefaultStringInReference2", + TestMaxLengthString = "Bar", TestInt16 = -123, TestInt32 = -12356789, TestInt64 = -123567890123456789L, @@ -686,6 +690,8 @@ public static IReadOnlyList CreateJsonEntitiesAllTypes() var c1 = new JsonOwnedAllTypes { + TestDefaultString = "MyDefaultStringInCollection1", + TestMaxLengthString = "Baz", TestInt16 = -12, TestInt32 = -12345, TestInt64 = -1234567890L, @@ -713,6 +719,8 @@ public static IReadOnlyList CreateJsonEntitiesAllTypes() var c2 = new JsonOwnedAllTypes { + TestDefaultString = "MyDefaultStringInCollection2", + TestMaxLengthString = "Qux", TestInt16 = -1, TestInt32 = -1245, TestInt64 = -123567890L, diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs index 2af33cb7814..2470fcd8b92 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs @@ -110,7 +110,7 @@ public override async Task Basic_json_projection_scalar(bool async) AssertSql( """ -SELECT CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) +SELECT JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') FROM [JsonEntitiesBasic] AS [j] """); } @@ -123,7 +123,7 @@ public override async Task Json_scalar_length(bool async) """ SELECT [j].[Name] FROM [JsonEntitiesBasic] AS [j] -WHERE CAST(LEN(CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS nvarchar(max))) AS int) > 2 +WHERE CAST(LEN(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name')) AS int) > 2 """); } @@ -133,7 +133,7 @@ public override async Task Basic_json_projection_enum_inside_json_entity(bool as AssertSql( """ -SELECT [j].[Id], CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.OwnedReferenceBranch.Enum') AS nvarchar(max)) AS [Enum] +SELECT [j].[Id], JSON_VALUE([j].[OwnedReferenceRoot],'$.OwnedReferenceBranch.Enum') AS [Enum] FROM [JsonEntitiesBasic] AS [j] """); } @@ -155,7 +155,7 @@ public override async Task Json_projection_with_deduplication(bool async) AssertSql( """ -SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething') AS nvarchar(max)) +SELECT [j].[Id], [j].[EntityBasicId], [j].[Name], [j].[OwnedCollectionRoot], [j].[OwnedReferenceRoot], JSON_VALUE([j].[OwnedReferenceRoot],'$.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething') FROM [JsonEntitiesBasic] AS [j] """); } @@ -195,7 +195,7 @@ SELECT CAST(LEN([t0].[c]) AS int) FROM ( SELECT DISTINCT [t].[c] FROM ( - SELECT TOP(@__p_0) CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething') AS nvarchar(max)) AS [c] + SELECT TOP(@__p_0) JSON_VALUE([j].[OwnedReferenceRoot],'$.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething') AS [c] FROM [JsonEntitiesBasic] AS [j] ORDER BY [j].[Id] ) AS [t] @@ -291,7 +291,7 @@ FROM [JsonEntitiesBasic] AS [j] ORDER BY [j].[Id] ) AS [t] ) AS [t0] - ORDER BY CAST(JSON_VALUE([t0].[c0],'$.Name') AS nvarchar(max)) + ORDER BY JSON_VALUE([t0].[c0],'$.Name') ) AS [t1] ) AS [t2] """); @@ -318,7 +318,7 @@ FROM [JsonEntitiesBasic] AS [j] ORDER BY [j].[Id] ) AS [t] ) AS [t0] - ORDER BY CAST(JSON_VALUE([t0].[c0],'$.Name') AS nvarchar(max)) + ORDER BY JSON_VALUE([t0].[c0],'$.Name') ) AS [t1] ) AS [t2] """); @@ -332,7 +332,7 @@ public override async Task Json_subquery_reference_pushdown_property(bool async) """ @__p_0='10' -SELECT CAST(JSON_VALUE([t0].[c],'$.SomethingSomething') AS nvarchar(max)) +SELECT JSON_VALUE([t0].[c],'$.SomethingSomething') FROM ( SELECT DISTINCT [t].[c] AS [c], [t].[Id] FROM ( @@ -395,7 +395,7 @@ public override async Task Custom_naming_projection_everything(bool async) AssertSql( """ -SELECT [j].[Id], [j].[Title], [j].[json_collection_custom_naming], [j].[json_reference_custom_naming], CAST(JSON_VALUE([j].[json_reference_custom_naming],'$.CustomName') AS nvarchar(max)), CAST(JSON_VALUE([j].[json_reference_custom_naming],'$.CustomOwnedReferenceBranch.CustomFraction') AS float) +SELECT [j].[Id], [j].[Title], [j].[json_collection_custom_naming], [j].[json_reference_custom_naming], JSON_VALUE([j].[json_reference_custom_naming],'$.CustomName'), CAST(JSON_VALUE([j].[json_reference_custom_naming],'$.CustomOwnedReferenceBranch.CustomFraction') AS float) FROM [JsonEntitiesCustomNaming] AS [j] """); } @@ -484,7 +484,7 @@ public override async Task Project_json_entity_FirstOrDefault_subquery_deduplica SELECT [t].[c], [t].[Id], [t].[c0], [t].[Id0], [t].[c1], [t].[c2], [t].[c3], [t].[c4] FROM [JsonEntitiesBasic] AS [j] OUTER APPLY ( - SELECT TOP(1) JSON_QUERY([j].[OwnedReferenceRoot],'$.OwnedCollectionBranch') AS [c], [j].[Id], [j0].[OwnedReferenceRoot] AS [c0], [j0].[Id] AS [Id0], JSON_QUERY([j0].[OwnedReferenceRoot],'$.OwnedReferenceBranch') AS [c1], CAST(JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [c2], CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.OwnedReferenceBranch.Enum') AS nvarchar(max)) AS [c3], 1 AS [c4] + SELECT TOP(1) JSON_QUERY([j].[OwnedReferenceRoot],'$.OwnedCollectionBranch') AS [c], [j].[Id], [j0].[OwnedReferenceRoot] AS [c0], [j0].[Id] AS [Id0], JSON_QUERY([j0].[OwnedReferenceRoot],'$.OwnedReferenceBranch') AS [c1], JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS [c2], JSON_VALUE([j].[OwnedReferenceRoot],'$.OwnedReferenceBranch.Enum') AS [c3], 1 AS [c4] FROM [JsonEntitiesBasic] AS [j0] ORDER BY [j0].[Id] ) AS [t] @@ -501,7 +501,7 @@ public override async Task Project_json_entity_FirstOrDefault_subquery_deduplica SELECT [t].[c], [t].[Id], [t].[c0], [t].[Id0], [t].[c1], [t].[c2], [t].[c3], [t].[c4] FROM [JsonEntitiesBasic] AS [j] OUTER APPLY ( - SELECT TOP(1) JSON_QUERY([j].[OwnedReferenceRoot],'$.OwnedCollectionBranch') AS [c], [j].[Id], [j0].[OwnedReferenceRoot] AS [c0], [j0].[Id] AS [Id0], JSON_QUERY([j0].[OwnedReferenceRoot],'$.OwnedReferenceBranch') AS [c1], CAST(JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [c2], CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.OwnedReferenceBranch.Enum') AS nvarchar(max)) AS [c3], 1 AS [c4] + SELECT TOP(1) JSON_QUERY([j].[OwnedReferenceRoot],'$.OwnedCollectionBranch') AS [c], [j].[Id], [j0].[OwnedReferenceRoot] AS [c0], [j0].[Id] AS [Id0], JSON_QUERY([j0].[OwnedReferenceRoot],'$.OwnedReferenceBranch') AS [c1], JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS [c2], JSON_VALUE([j].[OwnedReferenceRoot],'$.OwnedReferenceBranch.Enum') AS [c3], 1 AS [c4] FROM [JsonEntitiesBasic] AS [j0] ORDER BY [j0].[Id] ) AS [t] @@ -608,7 +608,7 @@ public override async Task Json_scalar_required_null_semantics(bool async) """ SELECT [j].[Name] FROM [JsonEntitiesBasic] AS [j] -WHERE CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Number') AS int) <> CAST(LEN(CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS nvarchar(max))) AS int) OR (CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) IS NULL) +WHERE CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Number') AS int) <> CAST(LEN(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name')) AS int) OR (JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') IS NULL) """); } @@ -620,7 +620,7 @@ public override async Task Json_scalar_optional_null_semantics(bool async) """ SELECT [j].[Name] FROM [JsonEntitiesBasic] AS [j] -WHERE CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) = CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething') AS nvarchar(max)) OR ((CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) IS NULL) AND (CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething') AS nvarchar(max)) IS NULL)) +WHERE JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') = JSON_VALUE([j].[OwnedReferenceRoot],'$.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething') OR ((JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') IS NULL) AND (JSON_VALUE([j].[OwnedReferenceRoot],'$.OwnedReferenceBranch.OwnedReferenceLeaf.SomethingSomething') IS NULL)) """); } @@ -632,7 +632,7 @@ public override async Task Group_by_on_json_scalar(bool async) """ SELECT [t].[Key], COUNT(*) AS [Count] FROM ( - SELECT CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key] + SELECT JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS [Key] FROM [JsonEntitiesBasic] AS [j] ) AS [t] GROUP BY [t].[Key] @@ -649,7 +649,7 @@ public override async Task Group_by_First_on_json_scalar(bool async) FROM ( SELECT [t].[Key] FROM ( - SELECT CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key] + SELECT JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS [Key] FROM [JsonEntitiesBasic] AS [j] ) AS [t] GROUP BY [t].[Key] @@ -659,7 +659,7 @@ LEFT JOIN ( FROM ( SELECT [t3].[Id], [t3].[EntityBasicId], [t3].[Name], [t3].[c] AS [c], [t3].[c0] AS [c0], [t3].[Key], ROW_NUMBER() OVER(PARTITION BY [t3].[Key] ORDER BY [t3].[Id]) AS [row] FROM ( - SELECT [j0].[Id], [j0].[EntityBasicId], [j0].[Name], [j0].[OwnedCollectionRoot] AS [c], [j0].[OwnedReferenceRoot] AS [c0], CAST(JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key] + SELECT [j0].[Id], [j0].[EntityBasicId], [j0].[Name], [j0].[OwnedCollectionRoot] AS [c], [j0].[OwnedReferenceRoot] AS [c0], JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS [Key] FROM [JsonEntitiesBasic] AS [j0] ) AS [t3] ) AS [t2] @@ -678,7 +678,7 @@ public override async Task Group_by_FirstOrDefault_on_json_scalar(bool async) FROM ( SELECT [t].[Key] FROM ( - SELECT CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key] + SELECT JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS [Key] FROM [JsonEntitiesBasic] AS [j] ) AS [t] GROUP BY [t].[Key] @@ -688,7 +688,7 @@ LEFT JOIN ( FROM ( SELECT [t3].[Id], [t3].[EntityBasicId], [t3].[Name], [t3].[c] AS [c], [t3].[c0] AS [c0], [t3].[Key], ROW_NUMBER() OVER(PARTITION BY [t3].[Key] ORDER BY [t3].[Id]) AS [row] FROM ( - SELECT [j0].[Id], [j0].[EntityBasicId], [j0].[Name], [j0].[OwnedCollectionRoot] AS [c], [j0].[OwnedReferenceRoot] AS [c0], CAST(JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key] + SELECT [j0].[Id], [j0].[EntityBasicId], [j0].[Name], [j0].[OwnedCollectionRoot] AS [c], [j0].[OwnedReferenceRoot] AS [c0], JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS [Key] FROM [JsonEntitiesBasic] AS [j0] ) AS [t3] ) AS [t2] @@ -707,7 +707,7 @@ public override async Task Group_by_Skip_Take_on_json_scalar(bool async) FROM ( SELECT [t].[Key] FROM ( - SELECT CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key] + SELECT JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS [Key] FROM [JsonEntitiesBasic] AS [j] ) AS [t] GROUP BY [t].[Key] @@ -717,7 +717,7 @@ LEFT JOIN ( FROM ( SELECT [t3].[Id], [t3].[EntityBasicId], [t3].[Name], [t3].[c] AS [c], [t3].[c0] AS [c0], [t3].[Key], ROW_NUMBER() OVER(PARTITION BY [t3].[Key] ORDER BY [t3].[Id]) AS [row] FROM ( - SELECT [j0].[Id], [j0].[EntityBasicId], [j0].[Name], [j0].[OwnedCollectionRoot] AS [c], [j0].[OwnedReferenceRoot] AS [c0], CAST(JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key] + SELECT [j0].[Id], [j0].[EntityBasicId], [j0].[Name], [j0].[OwnedCollectionRoot] AS [c], [j0].[OwnedReferenceRoot] AS [c0], JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS [Key] FROM [JsonEntitiesBasic] AS [j0] ) AS [t3] ) AS [t2] @@ -742,14 +742,14 @@ public override async Task Group_by_json_scalar_Skip_First_project_json_scalar(b AssertSql( """ SELECT ( - SELECT TOP(1) CAST(JSON_VALUE([t0].[c0],'$.OwnedReferenceBranch.Enum') AS nvarchar(max)) + SELECT TOP(1) JSON_VALUE([t0].[c0],'$.OwnedReferenceBranch.Enum') FROM ( - SELECT [j0].[Id], [j0].[EntityBasicId], [j0].[Name], [j0].[OwnedCollectionRoot] AS [c], [j0].[OwnedReferenceRoot] AS [c0], CAST(JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key] + SELECT [j0].[Id], [j0].[EntityBasicId], [j0].[Name], [j0].[OwnedCollectionRoot] AS [c], [j0].[OwnedReferenceRoot] AS [c0], JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS [Key] FROM [JsonEntitiesBasic] AS [j0] ) AS [t0] WHERE [t].[Key] = [t0].[Key] OR (([t].[Key] IS NULL) AND ([t0].[Key] IS NULL))) FROM ( - SELECT CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key] + SELECT JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS [Key] FROM [JsonEntitiesBasic] AS [j] ) AS [t] GROUP BY [t].[Key] @@ -836,7 +836,7 @@ public override async Task Json_all_types_projection_individual_properties(bool AssertSql( """ -SELECT CAST(JSON_VALUE([j].[Reference],'$.TestBoolean') AS bit) AS [TestBoolean], CAST(JSON_VALUE([j].[Reference],'$.TestByte') AS tinyint) AS [TestByte], CAST(JSON_VALUE([j].[Reference],'$.TestCharacter') AS nvarchar(1)) AS [TestCharacter], CAST(JSON_VALUE([j].[Reference],'$.TestDateTime') AS datetime2) AS [TestDateTime], CAST(JSON_VALUE([j].[Reference],'$.TestDateTimeOffset') AS datetimeoffset) AS [TestDateTimeOffset], CAST(JSON_VALUE([j].[Reference],'$.TestDecimal') AS decimal(18,3)) AS [TestDecimal], CAST(JSON_VALUE([j].[Reference],'$.TestDouble') AS float) AS [TestDouble], CAST(JSON_VALUE([j].[Reference],'$.TestGuid') AS uniqueidentifier) AS [TestGuid], CAST(JSON_VALUE([j].[Reference],'$.TestInt16') AS smallint) AS [TestInt16], CAST(JSON_VALUE([j].[Reference],'$.TestInt32') AS int) AS [TestInt32], CAST(JSON_VALUE([j].[Reference],'$.TestInt64') AS bigint) AS [TestInt64], CAST(JSON_VALUE([j].[Reference],'$.TestSignedByte') AS smallint) AS [TestSignedByte], CAST(JSON_VALUE([j].[Reference],'$.TestSingle') AS real) AS [TestSingle], CAST(JSON_VALUE([j].[Reference],'$.TestTimeSpan') AS time) AS [TestTimeSpan], CAST(JSON_VALUE([j].[Reference],'$.TestUnsignedInt16') AS int) AS [TestUnsignedInt16], CAST(JSON_VALUE([j].[Reference],'$.TestUnsignedInt32') AS bigint) AS [TestUnsignedInt32], CAST(JSON_VALUE([j].[Reference],'$.TestUnsignedInt64') AS decimal(20,0)) AS [TestUnsignedInt64], CAST(JSON_VALUE([j].[Reference],'$.TestNullableInt32') AS int) AS [TestNullableInt32], CAST(JSON_VALUE([j].[Reference],'$.TestEnum') AS nvarchar(max)) AS [TestEnum], CAST(JSON_VALUE([j].[Reference],'$.TestEnumWithIntConverter') AS int) AS [TestEnumWithIntConverter], CAST(JSON_VALUE([j].[Reference],'$.TestNullableEnum') AS nvarchar(max)) AS [TestNullableEnum], CAST(JSON_VALUE([j].[Reference],'$.TestNullableEnumWithIntConverter') AS int) AS [TestNullableEnumWithIntConverter], CAST(JSON_VALUE([j].[Reference],'$.TestNullableEnumWithConverterThatHandlesNulls') AS nvarchar(max)) AS [TestNullableEnumWithConverterThatHandlesNulls] +SELECT JSON_VALUE([j].[Reference],'$.TestDefaultString') AS [TestDefaultString], JSON_VALUE([j].[Reference],'$.TestMaxLengthString') AS [TestMaxLengthString], CAST(JSON_VALUE([j].[Reference],'$.TestBoolean') AS bit) AS [TestBoolean], CAST(JSON_VALUE([j].[Reference],'$.TestByte') AS tinyint) AS [TestByte], JSON_VALUE([j].[Reference],'$.TestCharacter') AS [TestCharacter], CAST(JSON_VALUE([j].[Reference],'$.TestDateTime') AS datetime2) AS [TestDateTime], CAST(JSON_VALUE([j].[Reference],'$.TestDateTimeOffset') AS datetimeoffset) AS [TestDateTimeOffset], CAST(JSON_VALUE([j].[Reference],'$.TestDecimal') AS decimal(18,3)) AS [TestDecimal], CAST(JSON_VALUE([j].[Reference],'$.TestDouble') AS float) AS [TestDouble], CAST(JSON_VALUE([j].[Reference],'$.TestGuid') AS uniqueidentifier) AS [TestGuid], CAST(JSON_VALUE([j].[Reference],'$.TestInt16') AS smallint) AS [TestInt16], CAST(JSON_VALUE([j].[Reference],'$.TestInt32') AS int) AS [TestInt32], CAST(JSON_VALUE([j].[Reference],'$.TestInt64') AS bigint) AS [TestInt64], CAST(JSON_VALUE([j].[Reference],'$.TestSignedByte') AS smallint) AS [TestSignedByte], CAST(JSON_VALUE([j].[Reference],'$.TestSingle') AS real) AS [TestSingle], CAST(JSON_VALUE([j].[Reference],'$.TestTimeSpan') AS time) AS [TestTimeSpan], CAST(JSON_VALUE([j].[Reference],'$.TestUnsignedInt16') AS int) AS [TestUnsignedInt16], CAST(JSON_VALUE([j].[Reference],'$.TestUnsignedInt32') AS bigint) AS [TestUnsignedInt32], CAST(JSON_VALUE([j].[Reference],'$.TestUnsignedInt64') AS decimal(20,0)) AS [TestUnsignedInt64], JSON_VALUE([j].[Reference],'$.TestEnum') AS [TestEnum], CAST(JSON_VALUE([j].[Reference],'$.TestEnumWithIntConverter') AS int) AS [TestEnumWithIntConverter], JSON_VALUE([j].[Reference],'$.TestNullableEnum') AS [TestNullableEnum], CAST(JSON_VALUE([j].[Reference],'$.TestNullableEnumWithIntConverter') AS int) AS [TestNullableEnumWithIntConverter], JSON_VALUE([j].[Reference],'$.TestNullableEnumWithConverterThatHandlesNulls') AS [TestNullableEnumWithConverterThatHandlesNulls] FROM [JsonEntitiesAllTypes] AS [j] """); } @@ -890,6 +890,355 @@ FROM [JsonEntitiesAllTypes] AS [j] """); } + public override async Task Json_predicate_on_default_string(bool async) + { + await base.Json_predicate_on_default_string(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE JSON_VALUE([j].[Reference],'$.TestDefaultString') <> N'MyDefaultStringInReference1' OR (JSON_VALUE([j].[Reference],'$.TestDefaultString') IS NULL) +"""); + } + + public override async Task Json_predicate_on_max_length_string(bool async) + { + await base.Json_predicate_on_max_length_string(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE JSON_VALUE([j].[Reference],'$.TestMaxLengthString') <> N'Foo' OR (JSON_VALUE([j].[Reference],'$.TestMaxLengthString') IS NULL) +"""); + } + + public override async Task Json_predicate_on_string_condition(bool async) + { + await base.Json_predicate_on_string_condition(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CASE + WHEN CAST(JSON_VALUE([j].[Reference],'$.TestBoolean') AS bit) = CAST(0 AS bit) THEN JSON_VALUE([j].[Reference],'$.TestMaxLengthString') + ELSE JSON_VALUE([j].[Reference],'$.TestDefaultString') +END = N'MyDefaultStringInReference1' +"""); + } + + public override async Task Json_predicate_on_byte(bool async) + { + await base.Json_predicate_on_byte(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestByte') AS tinyint) <> CAST(3 AS tinyint) OR (CAST(JSON_VALUE([j].[Reference],'$.TestByte') AS tinyint) IS NULL) +"""); + } + + public override async Task Json_predicate_on_character(bool async) + { + await base.Json_predicate_on_character(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE JSON_VALUE([j].[Reference],'$.TestCharacter') <> N'z' OR (JSON_VALUE([j].[Reference],'$.TestCharacter') IS NULL) +"""); + } + + public override async Task Json_predicate_on_datetime(bool async) + { + await base.Json_predicate_on_datetime(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestDateTime') AS datetime2) <> '2000-01-03T00:00:00.0000000' OR (CAST(JSON_VALUE([j].[Reference],'$.TestDateTime') AS datetime2) IS NULL) +"""); + } + + public override async Task Json_predicate_on_datetimeoffset(bool async) + { + await base.Json_predicate_on_datetimeoffset(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestDateTimeOffset') AS datetimeoffset) <> '2000-01-04T00:00:00.0000000+03:02' OR (CAST(JSON_VALUE([j].[Reference],'$.TestDateTimeOffset') AS datetimeoffset) IS NULL) +"""); + } + + public override async Task Json_predicate_on_decimal(bool async) + { + await base.Json_predicate_on_decimal(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestDecimal') AS decimal(18,3)) <> 1.35 OR (CAST(JSON_VALUE([j].[Reference],'$.TestDecimal') AS decimal(18,3)) IS NULL) +"""); + } + + public override async Task Json_predicate_on_double(bool async) + { + await base.Json_predicate_on_double(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestDouble') AS float) <> 33.25E0 OR (CAST(JSON_VALUE([j].[Reference],'$.TestDouble') AS float) IS NULL) +"""); + } + + public override async Task Json_predicate_on_enum(bool async) + { + await base.Json_predicate_on_enum(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE JSON_VALUE([j].[Reference],'$.TestEnum') <> N'Two' OR (JSON_VALUE([j].[Reference],'$.TestEnum') IS NULL) +"""); + } + + public override async Task Json_predicate_on_enumwithintconverter(bool async) + { + await base.Json_predicate_on_enumwithintconverter(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestEnumWithIntConverter') AS int) <> 2 OR (CAST(JSON_VALUE([j].[Reference],'$.TestEnumWithIntConverter') AS int) IS NULL) +"""); + } + + public override async Task Json_predicate_on_guid(bool async) + { + await base.Json_predicate_on_guid(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestGuid') AS uniqueidentifier) <> '00000000-0000-0000-0000-000000000000' OR (CAST(JSON_VALUE([j].[Reference],'$.TestGuid') AS uniqueidentifier) IS NULL) +"""); + } + + public override async Task Json_predicate_on_int16(bool async) + { + await base.Json_predicate_on_int16(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestInt16') AS smallint) <> CAST(3 AS smallint) OR (CAST(JSON_VALUE([j].[Reference],'$.TestInt16') AS smallint) IS NULL) +"""); + } + + public override async Task Json_predicate_on_int32(bool async) + { + await base.Json_predicate_on_int32(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestInt32') AS int) <> 33 OR (CAST(JSON_VALUE([j].[Reference],'$.TestInt32') AS int) IS NULL) +"""); + } + + public override async Task Json_predicate_on_int64(bool async) + { + await base.Json_predicate_on_int64(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestInt64') AS bigint) <> CAST(333 AS bigint) OR (CAST(JSON_VALUE([j].[Reference],'$.TestInt64') AS bigint) IS NULL) +"""); + } + + public override async Task Json_predicate_on_nullableenum1(bool async) + { + await base.Json_predicate_on_nullableenum1(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE JSON_VALUE([j].[Reference],'$.TestNullableEnum') <> N'One' OR (JSON_VALUE([j].[Reference],'$.TestNullableEnum') IS NULL) +"""); + } + + public override async Task Json_predicate_on_nullableenum2(bool async) + { + await base.Json_predicate_on_nullableenum2(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE JSON_VALUE([j].[Reference],'$.TestNullableEnum') IS NOT NULL +"""); + } + + public override async Task Json_predicate_on_nullableenumwithconverter1(bool async) + { + await base.Json_predicate_on_nullableenumwithconverter1(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestNullableEnumWithIntConverter') AS int) <> 1 OR (CAST(JSON_VALUE([j].[Reference],'$.TestNullableEnumWithIntConverter') AS int) IS NULL) +"""); + } + + public override async Task Json_predicate_on_nullableenumwithconverter2(bool async) + { + await base.Json_predicate_on_nullableenumwithconverter2(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestNullableEnumWithIntConverter') AS int) IS NOT NULL +"""); + } + + public override async Task Json_predicate_on_nullableenumwithconverterthathandlesnulls1(bool async) + { + await base.Json_predicate_on_nullableenumwithconverterthathandlesnulls1(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE JSON_VALUE([j].[Reference],'$.TestNullableEnumWithConverterThatHandlesNulls') <> N'One' OR (JSON_VALUE([j].[Reference],'$.TestNullableEnumWithConverterThatHandlesNulls') IS NULL) +"""); + } + + public override async Task Json_predicate_on_nullableenumwithconverterthathandlesnulls2(bool async) + { + await base.Json_predicate_on_nullableenumwithconverterthathandlesnulls2(async); + + AssertSql( +""" +x +"""); + } + + public override async Task Json_predicate_on_nullableint321(bool async) + { + await base.Json_predicate_on_nullableint321(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestNullableInt32') AS int) <> 100 OR (CAST(JSON_VALUE([j].[Reference],'$.TestNullableInt32') AS int) IS NULL) +"""); + } + + public override async Task Json_predicate_on_nullableint322(bool async) + { + await base.Json_predicate_on_nullableint322(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestNullableInt32') AS int) IS NOT NULL +"""); + } + + public override async Task Json_predicate_on_signedbyte(bool async) + { + await base.Json_predicate_on_signedbyte(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestSignedByte') AS smallint) <> CAST(100 AS smallint) OR (CAST(JSON_VALUE([j].[Reference],'$.TestSignedByte') AS smallint) IS NULL) +"""); + } + + public override async Task Json_predicate_on_single(bool async) + { + await base.Json_predicate_on_single(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestSingle') AS real) <> CAST(10.4 AS real) OR (CAST(JSON_VALUE([j].[Reference],'$.TestSingle') AS real) IS NULL) +"""); + } + + public override async Task Json_predicate_on_timespan(bool async) + { + await base.Json_predicate_on_timespan(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestTimeSpan') AS time) <> '03:02:00' OR (CAST(JSON_VALUE([j].[Reference],'$.TestTimeSpan') AS time) IS NULL) +"""); + } + + public override async Task Json_predicate_on_unisgnedint16(bool async) + { + await base.Json_predicate_on_unisgnedint16(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestUnsignedInt16') AS int) <> 100 OR (CAST(JSON_VALUE([j].[Reference],'$.TestUnsignedInt16') AS int) IS NULL) +"""); + } + + public override async Task Json_predicate_on_unsignedint32(bool async) + { + await base.Json_predicate_on_unsignedint32(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestUnsignedInt32') AS bigint) <> CAST(1000 AS bigint) OR (CAST(JSON_VALUE([j].[Reference],'$.TestUnsignedInt32') AS bigint) IS NULL) +"""); + } + + public override async Task Json_predicate_on_unsignedint64(bool async) + { + await base.Json_predicate_on_unsignedint64(async); + + AssertSql( +""" +SELECT [j].[Id], [j].[Collection], [j].[Reference] +FROM [JsonEntitiesAllTypes] AS [j] +WHERE CAST(JSON_VALUE([j].[Reference],'$.TestUnsignedInt64') AS decimal(20,0)) <> 10000.0 OR (CAST(JSON_VALUE([j].[Reference],'$.TestUnsignedInt64') AS decimal(20,0)) IS NULL) +"""); + } + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public override async Task FromSql_on_entity_with_json_basic(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs index bad9b4472ba..9a56ee1b8b3 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Update/JsonUpdateSqlServerTest.cs @@ -1152,8 +1152,8 @@ public override async Task Edit_two_properties_on_same_entity_updates_the_entire AssertSql( """ -@p0='{"TestBoolean":false,"TestByte":25,"TestCharacter":"h","TestDateTime":"2100-11-11T12:34:56","TestDateTimeOffset":"2200-11-11T12:34:56-05:00","TestDecimal":-123450.01,"TestDouble":-1.2345,"TestEnum":"One","TestEnumWithIntConverter":"Two","TestGuid":"00000000-0000-0000-0000-000000000000","TestInt16":-12,"TestInt32":32,"TestInt64":64,"TestNullableEnum":"One","TestNullableEnumWithConverterThatHandlesNulls":"Two","TestNullableEnumWithIntConverter":"Three","TestNullableInt32":90,"TestSignedByte":-18,"TestSingle":-1.4,"TestTimeSpan":"06:05:04.0030000","TestUnsignedInt16":12,"TestUnsignedInt32":12345,"TestUnsignedInt64":1234567867}' (Nullable = false) (Size = 631) -@p1='{"TestBoolean":true,"TestByte":255,"TestCharacter":"a","TestDateTime":"2000-01-01T12:34:56","TestDateTimeOffset":"2000-01-01T12:34:56-08:00","TestDecimal":-1234567890.01,"TestDouble":-1.23456789,"TestEnum":"One","TestEnumWithIntConverter":"Two","TestGuid":"12345678-1234-4321-7777-987654321000","TestInt16":-1234,"TestInt32":32,"TestInt64":64,"TestNullableEnum":"One","TestNullableEnumWithConverterThatHandlesNulls":"Three","TestNullableEnumWithIntConverter":"Two","TestNullableInt32":78,"TestSignedByte":-128,"TestSingle":-1.234,"TestTimeSpan":"10:09:08.0070000","TestUnsignedInt16":1234,"TestUnsignedInt32":1234565789,"TestUnsignedInt64":1234567890123456789}' (Nullable = false) (Size = 660) +@p0='{"TestBoolean":false,"TestByte":25,"TestCharacter":"h","TestDateTime":"2100-11-11T12:34:56","TestDateTimeOffset":"2200-11-11T12:34:56-05:00","TestDecimal":-123450.01,"TestDefaultString":"MyDefaultStringInCollection1","TestDouble":-1.2345,"TestEnum":"One","TestEnumWithIntConverter":"Two","TestGuid":"00000000-0000-0000-0000-000000000000","TestInt16":-12,"TestInt32":32,"TestInt64":64,"TestMaxLengthString":"Baz","TestNullableEnum":"One","TestNullableEnumWithConverterThatHandlesNulls":"Two","TestNullableEnumWithIntConverter":"Three","TestNullableInt32":90,"TestSignedByte":-18,"TestSingle":-1.4,"TestTimeSpan":"06:05:04.0030000","TestUnsignedInt16":12,"TestUnsignedInt32":12345,"TestUnsignedInt64":1234567867}' (Nullable = false) (Size = 710) +@p1='{"TestBoolean":true,"TestByte":255,"TestCharacter":"a","TestDateTime":"2000-01-01T12:34:56","TestDateTimeOffset":"2000-01-01T12:34:56-08:00","TestDecimal":-1234567890.01,"TestDefaultString":"MyDefaultStringInReference1","TestDouble":-1.23456789,"TestEnum":"One","TestEnumWithIntConverter":"Two","TestGuid":"12345678-1234-4321-7777-987654321000","TestInt16":-1234,"TestInt32":32,"TestInt64":64,"TestMaxLengthString":"Foo","TestNullableEnum":"One","TestNullableEnumWithConverterThatHandlesNulls":"Three","TestNullableEnumWithIntConverter":"Two","TestNullableInt32":78,"TestSignedByte":-128,"TestSingle":-1.234,"TestTimeSpan":"10:09:08.0070000","TestUnsignedInt16":1234,"TestUnsignedInt32":1234565789,"TestUnsignedInt64":1234567890123456789}' (Nullable = false) (Size = 738) @p2='1' SET IMPLICIT_TRANSACTIONS OFF;