From 00d4b5ad7d40685c2802db7b56e19d5f57963394 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 2 Jun 2026 15:19:30 +0200 Subject: [PATCH 1/2] Fix JSON null partial updates Use jsonb_set_lax for JSONB partial updates when targeting PostgreSQL 16 or later so SQL null values are written as JSON null rather than nulling the whole document. Keep older PostgreSQL versions on jsonb_set/json_set with a JSON null fallback for nullable update values. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...yableMethodTranslatingExpressionVisitor.cs | 142 +++++++++++++++--- .../ComplexJsonBulkUpdateNpgsqlTest.cs | 30 ++-- .../Types/Miscellaneous/NpgsqlBoolTypeTest.cs | 6 +- .../Miscellaneous/NpgsqlByteArrayTypeTest.cs | 8 +- .../Types/Miscellaneous/NpgsqlGuidTypeTest.cs | 6 +- .../Miscellaneous/NpgsqlStringTypeTest.cs | 8 +- .../Types/Networking/NpgsqlInetTypeTest.cs | 8 +- .../Types/Networking/NpgsqlMacaddrTypeTest.cs | 8 +- .../Types/Numeric/NpgsqlDecimalTypeTest.cs | 6 +- .../Types/Numeric/NpgsqlDoubleTypeTest.cs | 6 +- .../Types/Numeric/NpgsqlFloatTypeTest.cs | 6 +- .../Types/Numeric/NpgsqlIntTypeTest.cs | 6 +- .../Types/Numeric/NpgsqlLongTypeTest.cs | 6 +- .../Types/Numeric/NpgsqlShortTypeTest.cs | 6 +- .../Types/Temporal/NpgsqlDateOnlyTypeTest.cs | 6 +- .../Temporal/NpgsqlDateTimeOffsetTypeTest.cs | 6 +- .../NpgsqlDateTimeUnspecifiedTypeTest.cs | 6 +- .../Temporal/NpgsqlDateTimeUtcTypeTest.cs | 6 +- .../Types/Temporal/NpgsqlTimeOnlyTypeTest.cs | 6 +- .../Types/Temporal/NpgsqlTimeSpanTypeTest.cs | 6 +- 20 files changed, 192 insertions(+), 96 deletions(-) diff --git a/src/EFCore.PG/Query/Internal/NpgsqlQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.PG/Query/Internal/NpgsqlQueryableMethodTranslatingExpressionVisitor.cs index 5bed034320..0567d6771b 100644 --- a/src/EFCore.PG/Query/Internal/NpgsqlQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.PG/Query/Internal/NpgsqlQueryableMethodTranslatingExpressionVisitor.cs @@ -20,6 +20,7 @@ public class NpgsqlQueryableMethodTranslatingExpressionVisitor : RelationalQuery private readonly RelationalQueryCompilationContext _queryCompilationContext; private readonly NpgsqlTypeMappingSource _typeMappingSource; private readonly NpgsqlSqlExpressionFactory _sqlExpressionFactory; + private readonly Version _postgresVersion; private readonly bool _isRedshift; private RelationalTypeMapping? _ordinalityTypeMapping; @@ -55,6 +56,7 @@ public NpgsqlQueryableMethodTranslatingExpressionVisitor( _queryCompilationContext = queryCompilationContext; _typeMappingSource = (NpgsqlTypeMappingSource)relationalDependencies.TypeMappingSource; _sqlExpressionFactory = (NpgsqlSqlExpressionFactory)relationalDependencies.SqlExpressionFactory; + _postgresVersion = npgsqlSingletonOptions.PostgresVersion; _isRedshift = npgsqlSingletonOptions.UseRedshift; } @@ -70,6 +72,7 @@ protected NpgsqlQueryableMethodTranslatingExpressionVisitor(NpgsqlQueryableMetho _queryCompilationContext = parentVisitor._queryCompilationContext; _typeMappingSource = parentVisitor._typeMappingSource; _sqlExpressionFactory = parentVisitor._sqlExpressionFactory; + _postgresVersion = parentVisitor._postgresVersion; _isRedshift = parentVisitor._isRedshift; } @@ -1274,6 +1277,14 @@ protected override bool TrySerializeScalarToJson( { var jsonTypeMapping = ((ColumnExpression)target.Json).TypeMapping!; + if (IsSqlNull(value)) + { + jsonValue = JsonNull(jsonTypeMapping); + return true; + } + + var coalesceToJsonNull = !SupportsJsonSetLax(jsonTypeMapping) && MayReturnNull(value); + if ( // The base implementation doesn't handle serializing arbitrary SQL expressions to JSON, since that's // database-specific. In PostgreSQL we simply do this by wrapping any expression in to_jsonb(). @@ -1286,7 +1297,7 @@ protected override bool TrySerializeScalarToJson( switch (value.TypeMapping!.StoreType) { case "jsonb" or "json": - jsonValue = value; + jsonValue = coalesceToJsonNull ? CoalesceToJsonNull(value, jsonTypeMapping) : value; return true; case "bytea": @@ -1319,24 +1330,39 @@ protected override bool TrySerializeScalarToJson( jsonScalarValue.Type, jsonTypeMapping, jsonScalarValue.IsNullable); + + if (coalesceToJsonNull) + { + jsonValue = CoalesceToJsonNull(jsonValue, jsonTypeMapping); + } + return true; } - jsonValue = _sqlExpressionFactory.Function( + var valueForJsonScalar = NeedsJsonScalarTypeInference(value) ? CastForJsonScalarTypeInference(value, target) : value; + var toJson = _sqlExpressionFactory.Function( jsonTypeMapping.StoreType switch { "jsonb" => "to_jsonb", "json" => "to_json", _ => throw new UnreachableException() }, - // Make sure PG interprets constant values correctly by adding explicit typing based on the target property's type mapping. + // Make sure PG interprets constant/nullable parameter values correctly by adding explicit typing based on the target property's + // type mapping. // Note that we can only be here for scalar properties, for structural types we always already get a jsonb/json value // and don't need to add to_jsonb/to_json. - [value is SqlConstantExpression ? _sqlExpressionFactory.Convert(value, target.Type, target.TypeMapping) : value], + [valueForJsonScalar], nullable: true, argumentsPropagateNullability: [true], typeof(string), jsonTypeMapping); + + jsonValue = toJson; + } + + if (coalesceToJsonNull) + { + jsonValue = CoalesceToJsonNull(jsonValue, jsonTypeMapping); } return true; @@ -1362,26 +1388,46 @@ protected override bool TrySerializeScalarToJson( _ => throw new UnreachableException(), }; - var jsonSet = _sqlExpressionFactory.Function( - jsonColumn.TypeMapping?.StoreType switch - { - "jsonb" => "jsonb_set", - "json" => "json_set", - _ => throw new UnreachableException() - }, - arguments: - [ - existingSetterValue ?? jsonColumn, - // Hack: Rendering of JSONPATH strings happens in value generation. We can have a special expression for modify to hold the - // IReadOnlyList (just like Json{Scalar,Query}Expression), but instead we do the slight hack of packaging it - // as a constant argument; it will be unpacked and handled in SQL generation. - _sqlExpressionFactory.Constant(path, RelationalTypeMapping.NullMapping), - value - ], - nullable: true, - argumentsPropagateNullability: [true, true, true], - typeof(string), - jsonColumn.TypeMapping); + var jsonTypeMapping = jsonColumn.TypeMapping!; + var isNullConstant = IsJsonNull(value); + var isConstant = value is SqlConstantExpression; + var valueMayBeNull = !isNullConstant && MayReturnNull(value); + var supportsJsonSetLax = SupportsJsonSetLax(jsonTypeMapping); + + value = isNullConstant + ? JsonNull(jsonTypeMapping) + : supportsJsonSetLax || isConstant || !valueMayBeNull + ? value + : CoalesceToJsonNull(value, jsonTypeMapping); + + var useJsonSetLax = supportsJsonSetLax && (valueMayBeNull || isNullConstant); + var jsonSetTarget = existingSetterValue ?? jsonColumn; + + // Hack: Rendering of JSONPATH strings happens in value generation. We can have a special expression for modify to hold the + // IReadOnlyList (just like Json{Scalar,Query}Expression), but instead we do the slight hack of packaging it + // as a constant argument; it will be unpacked and handled in SQL generation. + var jsonPath = _sqlExpressionFactory.Constant(path, RelationalTypeMapping.NullMapping); + + var jsonSet = useJsonSetLax + ? _sqlExpressionFactory.Function( + "jsonb_set_lax", + [jsonSetTarget, jsonPath, value], + nullable: true, + argumentsPropagateNullability: [true, true, false], + typeof(string), + jsonTypeMapping) + : _sqlExpressionFactory.Function( + jsonTypeMapping.StoreType switch + { + "jsonb" => "jsonb_set", + "json" => "json_set", + _ => throw new UnreachableException() + }, + [jsonSetTarget, jsonPath, value], + nullable: true, + argumentsPropagateNullability: [true, true, !isNullConstant], + typeof(string), + jsonTypeMapping); if (existingSetterValue is null) { @@ -1394,6 +1440,54 @@ protected override bool TrySerializeScalarToJson( } } + private SqlExpression JsonNull(RelationalTypeMapping jsonTypeMapping) + => new SqlUnaryExpression( + ExpressionType.Convert, + _sqlExpressionFactory.Constant("null"), + typeof(object), + jsonTypeMapping); + + private SqlExpression CoalesceToJsonNull(SqlExpression value, RelationalTypeMapping jsonTypeMapping) + => _sqlExpressionFactory.Coalesce(value, JsonNull(jsonTypeMapping), jsonTypeMapping); + + private bool SupportsJsonSetLax(RelationalTypeMapping jsonTypeMapping) + => jsonTypeMapping.StoreType == "jsonb" && !_isRedshift && _postgresVersion.AtLeast(16); + + private static bool IsNullConstant(SqlExpression expression) + => expression is SqlConstantExpression { Value: null or DBNull }; + + private static bool IsSqlNull(SqlExpression expression) + => IsNullConstant(expression) + || expression is SqlFragmentExpression { Sql: "NULL" }; + + private static bool IsJsonNull(SqlExpression expression) + => IsSqlNull(expression) + || expression is SqlFunctionExpression + { + Name: "to_jsonb" or "to_json", + Arguments: [var argument] + } && IsSqlNull(argument); + + private static bool NeedsJsonScalarTypeInference(SqlExpression expression) + => expression is SqlConstantExpression; + + private static SqlExpression CastForJsonScalarTypeInference(SqlExpression expression, JsonScalarExpression target) + => new SqlUnaryExpression(ExpressionType.Convert, expression, typeof(object), target.TypeMapping ?? expression.TypeMapping); + + private static bool MayReturnNull(SqlExpression expression) + => expression switch + { + SqlConstantExpression { Value: null or DBNull } => true, + SqlFragmentExpression { Sql: "NULL" } => true, + SqlParameterExpression { IsNullable: true } => true, + ColumnExpression { IsNullable: true } => true, + JsonScalarExpression { IsNullable: true } => true, + PgJsonTraversalExpression => true, + SqlFunctionExpression { IsNullable: true } => true, + + _ => false + }; + #endregion ExecuteUpdate /// diff --git a/test/EFCore.PG.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonBulkUpdateNpgsqlTest.cs b/test/EFCore.PG.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonBulkUpdateNpgsqlTest.cs index 5401fc8682..ddc050ae27 100644 --- a/test/EFCore.PG.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonBulkUpdateNpgsqlTest.cs +++ b/test/EFCore.PG.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonBulkUpdateNpgsqlTest.cs @@ -1,3 +1,5 @@ +using Microsoft.EntityFrameworkCore.Query.Associations; + namespace Microsoft.EntityFrameworkCore.Query.Associations.ComplexJson; public class ComplexJsonBulkUpdateNpgsqlTest( @@ -47,7 +49,7 @@ public override async Task Update_property_inside_associate() @p='foo_updated' UPDATE "RootEntity" AS r -SET "RequiredAssociate" = jsonb_set(r."RequiredAssociate", '{String}', to_jsonb(@p)) +SET "RequiredAssociate" = jsonb_set_lax(r."RequiredAssociate", '{String}', to_jsonb(@p)) """); } @@ -58,7 +60,7 @@ public override async Task Update_property_inside_associate_with_special_chars() AssertExecuteUpdateSql( """ UPDATE "RootEntity" AS r -SET "RequiredAssociate" = jsonb_set(r."RequiredAssociate", '{String}', to_jsonb('{ Some other/JSON:like text though it [isn''t]: ממש ממש לאéèéè }'::text)) +SET "RequiredAssociate" = jsonb_set_lax(r."RequiredAssociate", '{String}', to_jsonb('{ Some other/JSON:like text though it [isn''t]: ממש ממש לאéèéè }'::text)) WHERE (r."RequiredAssociate" ->> 'String') = '{ this may/look:like JSON but it [isn''t]: ממש ממש לאéèéè }' """); } @@ -72,7 +74,7 @@ public override async Task Update_property_inside_nested_associate() @p='foo_updated' UPDATE "RootEntity" AS r -SET "RequiredAssociate" = jsonb_set(r."RequiredAssociate", '{RequiredNestedAssociate,String}', to_jsonb(@p)) +SET "RequiredAssociate" = jsonb_set_lax(r."RequiredAssociate", '{RequiredNestedAssociate,String}', to_jsonb(@p)) """); } @@ -85,7 +87,7 @@ public override async Task Update_property_on_projected_associate() @p='foo_updated' UPDATE "RootEntity" AS r -SET "RequiredAssociate" = jsonb_set(r."RequiredAssociate", '{String}', to_jsonb(@p)) +SET "RequiredAssociate" = jsonb_set_lax(r."RequiredAssociate", '{String}', to_jsonb(@p)) """); } @@ -129,7 +131,7 @@ public override async Task Update_nested_associate_to_parameter() @complex_type_p='{"Id":1000,"Int":80,"Ints":[1,2,4],"Name":"Updated nested name","String":"Updated nested string"}' (DbType = Object) UPDATE "RootEntity" AS r -SET "RequiredAssociate" = jsonb_set(r."RequiredAssociate", '{RequiredNestedAssociate}', @complex_type_p) +SET "RequiredAssociate" = jsonb_set_lax(r."RequiredAssociate", '{RequiredNestedAssociate}', @complex_type_p) """); } @@ -256,7 +258,7 @@ public override async Task Update_nested_collection_to_parameter() @complex_type_p='[{"Id":1000,"Int":80,"Ints":[1,2,4],"Name":"Updated nested name1","String":"Updated nested string1"},{"Id":1001,"Int":81,"Ints":[1,2,4],"Name":"Updated nested name2","String":"Updated nested string2"}]' (DbType = Object) UPDATE "RootEntity" AS r -SET "RequiredAssociate" = jsonb_set(r."RequiredAssociate", '{NestedCollection}', @complex_type_p) +SET "RequiredAssociate" = jsonb_set_lax(r."RequiredAssociate", '{NestedCollection}', @complex_type_p) """); } @@ -278,7 +280,7 @@ public override async Task Update_nested_collection_to_another_nested_collection AssertExecuteUpdateSql( """ UPDATE "RootEntity" AS r -SET "RequiredAssociate" = jsonb_set(r."RequiredAssociate", '{NestedCollection}', r."OptionalAssociate" -> 'NestedCollection') +SET "RequiredAssociate" = jsonb_set_lax(r."RequiredAssociate", '{NestedCollection}', r."OptionalAssociate" -> 'NestedCollection') WHERE (r."OptionalAssociate") IS NOT NULL """); } @@ -321,7 +323,7 @@ public override async Task Update_primitive_collection_to_parameter() @ints='[1,2,4]' (DbType = Object) UPDATE "RootEntity" AS r -SET "RequiredAssociate" = jsonb_set(r."RequiredAssociate", '{Ints}', @ints) +SET "RequiredAssociate" = jsonb_set_lax(r."RequiredAssociate", '{Ints}', @ints) """); } @@ -345,7 +347,7 @@ public override async Task Update_inside_primitive_collection() @p='99' UPDATE "RootEntity" AS r -SET "RequiredAssociate" = jsonb_set(r."RequiredAssociate", '{Ints,1}', to_jsonb(@p)) +SET "RequiredAssociate" = jsonb_set_lax(r."RequiredAssociate", '{Ints,1}', to_jsonb(@p)) WHERE jsonb_array_length(r."RequiredAssociate" -> 'Ints') >= 2 """); } @@ -364,7 +366,7 @@ public override async Task Update_multiple_properties_inside_same_associate() @p1='20' UPDATE "RootEntity" AS r -SET "RequiredAssociate" = jsonb_set(jsonb_set(r."RequiredAssociate", '{String}', to_jsonb(@p)), '{Int}', to_jsonb(@p1)) +SET "RequiredAssociate" = jsonb_set_lax(jsonb_set_lax(r."RequiredAssociate", '{String}', to_jsonb(@p)), '{Int}', to_jsonb(@p1)) """); } @@ -378,8 +380,8 @@ public override async Task Update_multiple_properties_inside_associates_and_on_e UPDATE "RootEntity" AS r SET "Name" = r."Name" || 'Modified', - "RequiredAssociate" = jsonb_set(r."RequiredAssociate", '{String}', r."OptionalAssociate" -> 'String'), - "OptionalAssociate" = jsonb_set(r."OptionalAssociate", '{RequiredNestedAssociate,String}', to_jsonb(@p)) + "RequiredAssociate" = jsonb_set_lax(r."RequiredAssociate", '{String}', r."OptionalAssociate" -> 'String'), + "OptionalAssociate" = jsonb_set_lax(r."OptionalAssociate", '{RequiredNestedAssociate,String}', to_jsonb(@p)) WHERE (r."OptionalAssociate") IS NOT NULL """); } @@ -393,8 +395,8 @@ public override async Task Update_multiple_projected_associates_via_anonymous_ty @p='foo_updated' UPDATE "RootEntity" AS r -SET "RequiredAssociate" = jsonb_set(r."RequiredAssociate", '{String}', r."OptionalAssociate" -> 'String'), - "OptionalAssociate" = jsonb_set(r."OptionalAssociate", '{String}', to_jsonb(@p)) +SET "RequiredAssociate" = jsonb_set_lax(r."RequiredAssociate", '{String}', r."OptionalAssociate" -> 'String'), + "OptionalAssociate" = jsonb_set_lax(r."OptionalAssociate", '{String}', to_jsonb(@p)) WHERE (r."OptionalAssociate") IS NOT NULL """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlBoolTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlBoolTypeTest.cs index d806ac872b..d89e872781 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlBoolTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlBoolTypeTest.cs @@ -85,7 +85,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='False' UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -96,7 +96,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(FALSE::boolean)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(FALSE::boolean)) """); } @@ -118,7 +118,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlByteArrayTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlByteArrayTypeTest.cs index 2307085f99..4beade9c67 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlByteArrayTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlByteArrayTypeTest.cs @@ -85,7 +85,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='0x04050607' UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(encode(@Fixture_OtherValue, 'base64'))) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(encode(@Fixture_OtherValue, 'base64'))) """); } @@ -96,7 +96,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(encode(BYTEA E'\\x04050607', 'base64'))) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(encode(BYTEA E'\\x04050607', 'base64'))) """); } @@ -107,7 +107,7 @@ public override async Task ExecuteUpdate_within_json_to_another_json_property() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(encode(decode(j."JsonContainer" ->> 'OtherValue', 'base64'), 'base64'))) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(encode(decode(j."JsonContainer" ->> 'OtherValue', 'base64'), 'base64'))) """); } @@ -118,7 +118,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(encode(j."OtherValue", 'base64'))) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(encode(j."OtherValue", 'base64'))) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlGuidTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlGuidTypeTest.cs index 97da485f03..0b94d4efd8 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlGuidTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlGuidTypeTest.cs @@ -86,7 +86,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='ae192c36-9004-49b2-b785-8be10d169627' UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -97,7 +97,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb('ae192c36-9004-49b2-b785-8be10d169627'::uuid)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb('ae192c36-9004-49b2-b785-8be10d169627'::uuid)) """); } @@ -119,7 +119,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlStringTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlStringTypeTest.cs index 4f18d82c62..b602d6ae22 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlStringTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Miscellaneous/NpgsqlStringTypeTest.cs @@ -86,7 +86,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='bar' UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -97,7 +97,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb('bar'::text)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb('bar'::text)) """); } @@ -108,7 +108,7 @@ public override async Task ExecuteUpdate_within_json_to_another_json_property() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', j."JsonContainer" -> 'OtherValue') +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', j."JsonContainer" -> 'OtherValue') """); } @@ -119,7 +119,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Networking/NpgsqlInetTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Networking/NpgsqlInetTypeTest.cs index 87b53464e4..cba21d176f 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Networking/NpgsqlInetTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Networking/NpgsqlInetTypeTest.cs @@ -87,7 +87,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='192.168.1.2' (DbType = Object) UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -98,7 +98,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(INET '192.168.1.2'::inet)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(INET '192.168.1.2'::inet)) """); } @@ -109,7 +109,7 @@ public override async Task ExecuteUpdate_within_json_to_another_json_property() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', j."JsonContainer" -> 'OtherValue') +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', j."JsonContainer" -> 'OtherValue') """); } @@ -120,7 +120,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Networking/NpgsqlMacaddrTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Networking/NpgsqlMacaddrTypeTest.cs index 68010f2632..7dd137d4f5 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Networking/NpgsqlMacaddrTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Networking/NpgsqlMacaddrTypeTest.cs @@ -88,7 +88,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='001422012346' (DbType = Object) UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -99,7 +99,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(MACADDR '001422012346'::macaddr)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(MACADDR '001422012346'::macaddr)) """); } @@ -110,7 +110,7 @@ public override async Task ExecuteUpdate_within_json_to_another_json_property() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', j."JsonContainer" -> 'OtherValue') +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', j."JsonContainer" -> 'OtherValue') """); } @@ -121,7 +121,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlDecimalTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlDecimalTypeTest.cs index 8b095e37f0..890cb90d74 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlDecimalTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlDecimalTypeTest.cs @@ -86,7 +86,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='30' UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -97,7 +97,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(30.0::numeric)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(30.0::numeric)) """); } @@ -119,7 +119,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlDoubleTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlDoubleTypeTest.cs index 0d1bc2aa48..e7214ace63 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlDoubleTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlDoubleTypeTest.cs @@ -86,7 +86,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='30' UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -97,7 +97,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(30.0::double precision)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(30.0::double precision)) """); } @@ -119,7 +119,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlFloatTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlFloatTypeTest.cs index 625880c75b..1a4caf7cf6 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlFloatTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlFloatTypeTest.cs @@ -86,7 +86,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='30' UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -97,7 +97,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(30::real)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(30::real)) """); } @@ -119,7 +119,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlIntTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlIntTypeTest.cs index fbc4f09a1b..f9294e9f06 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlIntTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlIntTypeTest.cs @@ -86,7 +86,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='2147483647' UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -97,7 +97,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(2147483647::int)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(2147483647::int)) """); } @@ -119,7 +119,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlLongTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlLongTypeTest.cs index 386ee20a44..ea8110748d 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlLongTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlLongTypeTest.cs @@ -86,7 +86,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='9223372036854775807' UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -97,7 +97,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(9223372036854775807::bigint)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(9223372036854775807::bigint)) """); } @@ -119,7 +119,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlShortTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlShortTypeTest.cs index 23fd28f74c..cb8cb875fb 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlShortTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Numeric/NpgsqlShortTypeTest.cs @@ -85,7 +85,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='32767' UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -96,7 +96,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(32767::smallint)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(32767::smallint)) """); } @@ -118,7 +118,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateOnlyTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateOnlyTypeTest.cs index 4d337a1bbd..d0b67fe235 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateOnlyTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateOnlyTypeTest.cs @@ -85,7 +85,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='05/03/2022' (DbType = Date) UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -96,7 +96,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(DATE '2022-05-03'::date)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(DATE '2022-05-03'::date)) """); } @@ -118,7 +118,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateTimeOffsetTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateTimeOffsetTypeTest.cs index cf4079f53a..b2b801575f 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateTimeOffsetTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateTimeOffsetTypeTest.cs @@ -86,7 +86,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='2020-01-05T13:30:45.0000000+00:00' (DbType = DateTime) UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -97,7 +97,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(TIMESTAMPTZ '2020-01-05T13:30:45+00:00'::timestamptz)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(TIMESTAMPTZ '2020-01-05T13:30:45+00:00'::timestamptz)) """); } @@ -119,7 +119,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateTimeUnspecifiedTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateTimeUnspecifiedTypeTest.cs index d7611dbd70..f3d2be669c 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateTimeUnspecifiedTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateTimeUnspecifiedTypeTest.cs @@ -86,7 +86,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='2022-05-03T00:00:00.0000000' UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -97,7 +97,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(TIMESTAMP '2022-05-03T00:00:00'::timestamp)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(TIMESTAMP '2022-05-03T00:00:00'::timestamp)) """); } @@ -119,7 +119,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateTimeUtcTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateTimeUtcTypeTest.cs index 9256f75453..59505df8b5 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateTimeUtcTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlDateTimeUtcTypeTest.cs @@ -86,7 +86,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='2022-05-03T00:00:00.0000000Z' (DbType = DateTime) UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -97,7 +97,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(TIMESTAMPTZ '2022-05-03T00:00:00Z'::timestamptz)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(TIMESTAMPTZ '2022-05-03T00:00:00Z'::timestamptz)) """); } @@ -119,7 +119,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlTimeOnlyTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlTimeOnlyTypeTest.cs index 2a80d2424b..fdc0bfc4da 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlTimeOnlyTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlTimeOnlyTypeTest.cs @@ -86,7 +86,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='14:00' (DbType = Time) UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -97,7 +97,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(TIME '14:00:00'::time without time zone)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(TIME '14:00:00'::time without time zone)) """); } @@ -119,7 +119,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } diff --git a/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlTimeSpanTypeTest.cs b/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlTimeSpanTypeTest.cs index d36a670485..85a6f3f765 100644 --- a/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlTimeSpanTypeTest.cs +++ b/test/EFCore.PG.FunctionalTests/Types/Temporal/NpgsqlTimeSpanTypeTest.cs @@ -85,7 +85,7 @@ public override async Task ExecuteUpdate_within_json_to_parameter() @Fixture_OtherValue='14:00:00' (DbType = Object) UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(@Fixture_OtherValue)) """); } @@ -96,7 +96,7 @@ public override async Task ExecuteUpdate_within_json_to_constant() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(INTERVAL '14:00:00'::interval)) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(INTERVAL '14:00:00'::interval)) """); } @@ -118,7 +118,7 @@ public override async Task ExecuteUpdate_within_json_to_nonjson_column() AssertSql( """ UPDATE "JsonTypeEntity" AS j -SET "JsonContainer" = jsonb_set(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) +SET "JsonContainer" = jsonb_set_lax(j."JsonContainer", '{Value}', to_jsonb(j."OtherValue")) """); } From c47bd078f897ddc8e0a8c96fe25bca1d35b7ea65 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 2 Jun 2026 16:59:11 +0200 Subject: [PATCH 2/2] Use JSON type for JSON null expressions Keep generated JSON null expressions typed with the JSON type mapping CLR type instead of object, so COALESCE and JSON setter expressions stay well-typed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../NpgsqlQueryableMethodTranslatingExpressionVisitor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EFCore.PG/Query/Internal/NpgsqlQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.PG/Query/Internal/NpgsqlQueryableMethodTranslatingExpressionVisitor.cs index 0567d6771b..ef8cf0f1af 100644 --- a/src/EFCore.PG/Query/Internal/NpgsqlQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.PG/Query/Internal/NpgsqlQueryableMethodTranslatingExpressionVisitor.cs @@ -1444,7 +1444,7 @@ private SqlExpression JsonNull(RelationalTypeMapping jsonTypeMapping) => new SqlUnaryExpression( ExpressionType.Convert, _sqlExpressionFactory.Constant("null"), - typeof(object), + jsonTypeMapping.ClrType, jsonTypeMapping); private SqlExpression CoalesceToJsonNull(SqlExpression value, RelationalTypeMapping jsonTypeMapping)